Automatic sources dropoff on 2020-06-10 18:32:38.095721

The change is generated with prebuilt drop tool.

Change-Id: I24cbf6ba6db262a1ae1445db1427a08fee35b3b4
diff --git a/android/content/AbstractThreadedSyncAdapter.java b/android/content/AbstractThreadedSyncAdapter.java
new file mode 100644
index 0000000..a086a30
--- /dev/null
+++ b/android/content/AbstractThreadedSyncAdapter.java
@@ -0,0 +1,496 @@
+/*
+ * 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.content;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.accounts.Account;
+import android.annotation.MainThread;
+import android.annotation.NonNull;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.Trace;
+import android.util.Log;
+
+import java.util.HashMap;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * An abstract implementation of a SyncAdapter that spawns a thread to invoke a sync operation.
+ * If a sync operation is already in progress when a sync request is received, an error will be
+ * returned to the new request and the existing request will be allowed to continue.
+ * However if there is no sync in progress then a thread will be spawned and {@link #onPerformSync}
+ * will be invoked on that thread.
+ * <p>
+ * Syncs can be cancelled at any time by the framework. For example a sync that was not
+ * user-initiated and lasts longer than 30 minutes will be considered timed-out and cancelled.
+ * Similarly the framework will attempt to determine whether or not an adapter is making progress
+ * by monitoring its network activity over the course of a minute. If the network traffic over this
+ * window is close enough to zero the sync will be cancelled. You can also request the sync be
+ * cancelled via {@link ContentResolver#cancelSync(Account, String)} or
+ * {@link ContentResolver#cancelSync(SyncRequest)}.
+ * <p>
+ * A sync is cancelled by issuing a {@link Thread#interrupt()} on the syncing thread. <strong>Either
+ * your code in {@link #onPerformSync(Account, Bundle, String, ContentProviderClient, SyncResult)}
+ * must check {@link Thread#interrupted()}, or you you must override one of
+ * {@link #onSyncCanceled(Thread)}/{@link #onSyncCanceled()}</strong> (depending on whether or not
+ * your adapter supports syncing of multiple accounts in parallel). If your adapter does not
+ * respect the cancel issued by the framework you run the risk of your app's entire process being
+ * killed.
+ * <p>
+ * In order to be a sync adapter one must extend this class, provide implementations for the
+ * abstract methods and write a service that returns the result of {@link #getSyncAdapterBinder()}
+ * in the service's {@link android.app.Service#onBind(android.content.Intent)} when invoked
+ * with an intent with action <code>android.content.SyncAdapter</code>. This service
+ * must specify the following intent filter and metadata tags in its AndroidManifest.xml file
+ * <pre>
+ *   &lt;intent-filter&gt;
+ *     &lt;action android:name="android.content.SyncAdapter" /&gt;
+ *   &lt;/intent-filter&gt;
+ *   &lt;meta-data android:name="android.content.SyncAdapter"
+ *             android:resource="@xml/syncadapter" /&gt;
+ * </pre>
+ * The <code>android:resource</code> attribute must point to a resource that looks like:
+ * <pre>
+ * &lt;sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
+ *    android:contentAuthority="authority"
+ *    android:accountType="accountType"
+ *    android:userVisible="true|false"
+ *    android:supportsUploading="true|false"
+ *    android:allowParallelSyncs="true|false"
+ *    android:isAlwaysSyncable="true|false"
+ *    android:syncAdapterSettingsAction="ACTION_OF_SETTINGS_ACTIVITY"
+ * /&gt;
+ * </pre>
+ * <ul>
+ * <li>The <code>android:contentAuthority</code> and <code>android:accountType</code> attributes
+ * indicate which content authority and for which account types this sync adapter serves.
+ * <li><code>android:userVisible</code> defaults to true and controls whether or not this sync
+ * adapter shows up in the Sync Settings screen.
+ * <li><code>android:supportsUploading</code> defaults
+ * to true and if true an upload-only sync will be requested for all syncadapters associated
+ * with an authority whenever that authority's content provider does a
+ * {@link ContentResolver#notifyChange(android.net.Uri, android.database.ContentObserver, boolean)}
+ * with syncToNetwork set to true.
+ * <li><code>android:allowParallelSyncs</code> defaults to false and if true indicates that
+ * the sync adapter can handle syncs for multiple accounts at the same time. Otherwise
+ * the SyncManager will wait until the sync adapter is not in use before requesting that
+ * it sync an account's data.
+ * <li><code>android:isAlwaysSyncable</code> defaults to false and if true tells the SyncManager
+ * to initialize the isSyncable state to 1 for that sync adapter for each account that is added.
+ * <li><code>android:syncAdapterSettingsAction</code> defaults to null and if supplied it
+ * specifies an Intent action of an activity that can be used to adjust the sync adapter's
+ * sync settings. The activity must live in the same package as the sync adapter.
+ * </ul>
+ */
+public abstract class AbstractThreadedSyncAdapter {
+    private static final String TAG = "SyncAdapter";
+
+    /**
+     * Kernel event log tag.  Also listed in data/etc/event-log-tags.
+     * @deprecated Private constant.  May go away in the next release.
+     */
+    @Deprecated
+    public static final int LOG_SYNC_DETAILS = 2743;
+
+    private static final boolean ENABLE_LOG = Build.IS_DEBUGGABLE && Log.isLoggable(TAG, Log.DEBUG);
+
+    private final Context mContext;
+    private final AtomicInteger mNumSyncStarts;
+    private final ISyncAdapterImpl mISyncAdapterImpl;
+
+    // all accesses to this member variable must be synchronized on mSyncThreadLock
+    private final HashMap<Account, SyncThread> mSyncThreads = new HashMap<Account, SyncThread>();
+    private final Object mSyncThreadLock = new Object();
+
+    private final boolean mAutoInitialize;
+    private boolean mAllowParallelSyncs;
+
+    /**
+     * Creates an {@link AbstractThreadedSyncAdapter}.
+     * @param context the {@link android.content.Context} that this is running within.
+     * @param autoInitialize if true then sync requests that have
+     * {@link ContentResolver#SYNC_EXTRAS_INITIALIZE} set will be internally handled by
+     * {@link AbstractThreadedSyncAdapter} by calling
+     * {@link ContentResolver#setIsSyncable(android.accounts.Account, String, int)} with 1 if it
+     * is currently set to <0.
+     */
+    public AbstractThreadedSyncAdapter(Context context, boolean autoInitialize) {
+        this(context, autoInitialize, false /* allowParallelSyncs */);
+    }
+
+    /**
+     * Creates an {@link AbstractThreadedSyncAdapter}.
+     * @param context the {@link android.content.Context} that this is running within.
+     * @param autoInitialize if true then sync requests that have
+     * {@link ContentResolver#SYNC_EXTRAS_INITIALIZE} set will be internally handled by
+     * {@link AbstractThreadedSyncAdapter} by calling
+     * {@link ContentResolver#setIsSyncable(android.accounts.Account, String, int)} with 1 if it
+     * is currently set to <0.
+     * @param allowParallelSyncs if true then allow syncs for different accounts to run
+     * at the same time, each in their own thread. This must be consistent with the setting
+     * in the SyncAdapter's configuration file.
+     */
+    public AbstractThreadedSyncAdapter(Context context,
+            boolean autoInitialize, boolean allowParallelSyncs) {
+        mContext = context;
+        mISyncAdapterImpl = new ISyncAdapterImpl();
+        mNumSyncStarts = new AtomicInteger(0);
+        mAutoInitialize = autoInitialize;
+        mAllowParallelSyncs = allowParallelSyncs;
+    }
+
+    public Context getContext() {
+        return mContext;
+    }
+
+    private Account toSyncKey(Account account) {
+        if (mAllowParallelSyncs) {
+            return account;
+        } else {
+            return null;
+        }
+    }
+
+    private class ISyncAdapterImpl extends ISyncAdapter.Stub {
+        @Override
+        public void onUnsyncableAccount(ISyncAdapterUnsyncableAccountCallback cb) {
+            Handler.getMain().sendMessage(obtainMessage(
+                    AbstractThreadedSyncAdapter::handleOnUnsyncableAccount,
+                    AbstractThreadedSyncAdapter.this, cb));
+        }
+
+        @Override
+        public void startSync(ISyncContext syncContext, String authority, Account account,
+                Bundle extras) {
+            if (ENABLE_LOG) {
+                if (extras != null) {
+                    extras.size(); // Unparcel so its toString() will show the contents.
+                }
+                Log.d(TAG, "startSync() start " + authority + " " + account + " " + extras);
+            }
+            try {
+                final SyncContext syncContextClient = new SyncContext(syncContext);
+
+                boolean alreadyInProgress;
+                // synchronize to make sure that mSyncThreads doesn't change between when we
+                // check it and when we use it
+                final Account threadsKey = toSyncKey(account);
+                synchronized (mSyncThreadLock) {
+                    if (!mSyncThreads.containsKey(threadsKey)) {
+                        if (mAutoInitialize
+                                && extras != null
+                                && extras.getBoolean(
+                                        ContentResolver.SYNC_EXTRAS_INITIALIZE, false)) {
+                            try {
+                                if (ContentResolver.getIsSyncable(account, authority) < 0) {
+                                    ContentResolver.setIsSyncable(account, authority, 1);
+                                }
+                            } finally {
+                                syncContextClient.onFinished(new SyncResult());
+                            }
+                            return;
+                        }
+                        SyncThread syncThread = new SyncThread(
+                                "SyncAdapterThread-" + mNumSyncStarts.incrementAndGet(),
+                                syncContextClient, authority, account, extras);
+                        mSyncThreads.put(threadsKey, syncThread);
+                        syncThread.start();
+                        alreadyInProgress = false;
+                    } else {
+                        if (ENABLE_LOG) {
+                            Log.d(TAG, "  alreadyInProgress");
+                        }
+                        alreadyInProgress = true;
+                    }
+                }
+
+                // do this outside since we don't want to call back into the syncContext while
+                // holding the synchronization lock
+                if (alreadyInProgress) {
+                    syncContextClient.onFinished(SyncResult.ALREADY_IN_PROGRESS);
+                }
+            } catch (RuntimeException | Error th) {
+                if (ENABLE_LOG) {
+                    Log.d(TAG, "startSync() caught exception", th);
+                }
+                throw th;
+            } finally {
+                if (ENABLE_LOG) {
+                    Log.d(TAG, "startSync() finishing");
+                }
+            }
+        }
+
+        @Override
+        public void cancelSync(ISyncContext syncContext) {
+            try {
+                // synchronize to make sure that mSyncThreads doesn't change between when we
+                // check it and when we use it
+                SyncThread info = null;
+                synchronized (mSyncThreadLock) {
+                    for (SyncThread current : mSyncThreads.values()) {
+                        if (current.mSyncContext.getSyncContextBinder() == syncContext.asBinder()) {
+                            info = current;
+                            break;
+                        }
+                    }
+                }
+                if (info != null) {
+                    if (ENABLE_LOG) {
+                        Log.d(TAG, "cancelSync() " + info.mAuthority + " " + info.mAccount);
+                    }
+                    if (mAllowParallelSyncs) {
+                        onSyncCanceled(info);
+                    } else {
+                        onSyncCanceled();
+                    }
+                } else {
+                    if (ENABLE_LOG) {
+                        Log.w(TAG, "cancelSync() unknown context");
+                    }
+                }
+            } catch (RuntimeException | Error th) {
+                if (ENABLE_LOG) {
+                    Log.d(TAG, "cancelSync() caught exception", th);
+                }
+                throw th;
+            } finally {
+                if (ENABLE_LOG) {
+                    Log.d(TAG, "cancelSync() finishing");
+                }
+            }
+        }
+    }
+
+    /**
+     * The thread that invokes {@link AbstractThreadedSyncAdapter#onPerformSync}. It also acquires
+     * the provider for this sync before calling onPerformSync and releases it afterwards. Cancel
+     * this thread in order to cancel the sync.
+     */
+    private class SyncThread extends Thread {
+        private final SyncContext mSyncContext;
+        private final String mAuthority;
+        private final Account mAccount;
+        private final Bundle mExtras;
+        private final Account mThreadsKey;
+
+        private SyncThread(String name, SyncContext syncContext, String authority,
+                Account account, Bundle extras) {
+            super(name);
+            mSyncContext = syncContext;
+            mAuthority = authority;
+            mAccount = account;
+            mExtras = extras;
+            mThreadsKey = toSyncKey(account);
+        }
+
+        @Override
+        public void run() {
+            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+
+            if (ENABLE_LOG) {
+                Log.d(TAG, "Thread started");
+            }
+
+            // Trace this sync instance.  Note, conceptually this should be in
+            // SyncStorageEngine.insertStartSyncEvent(), but the trace functions require unique
+            // threads in order to track overlapping operations, so we'll do it here for now.
+            Trace.traceBegin(Trace.TRACE_TAG_SYNC_MANAGER, mAuthority);
+
+            SyncResult syncResult = new SyncResult();
+            ContentProviderClient provider = null;
+            try {
+                if (isCanceled()) {
+                    if (ENABLE_LOG) {
+                        Log.d(TAG, "Already canceled");
+                    }
+                    return;
+                }
+                if (ENABLE_LOG) {
+                    Log.d(TAG, "Calling onPerformSync...");
+                }
+
+                provider = mContext.getContentResolver().acquireContentProviderClient(mAuthority);
+                if (provider != null) {
+                    AbstractThreadedSyncAdapter.this.onPerformSync(mAccount, mExtras,
+                            mAuthority, provider, syncResult);
+                } else {
+                    syncResult.databaseError = true;
+                }
+
+                if (ENABLE_LOG) {
+                    Log.d(TAG, "onPerformSync done");
+                }
+
+            } catch (SecurityException e) {
+                if (ENABLE_LOG) {
+                    Log.d(TAG, "SecurityException", e);
+                }
+                AbstractThreadedSyncAdapter.this.onSecurityException(mAccount, mExtras,
+                        mAuthority, syncResult);
+                syncResult.databaseError = true;
+            } catch (RuntimeException | Error th) {
+                if (ENABLE_LOG) {
+                    Log.d(TAG, "caught exception", th);
+                }
+                throw th;
+            } finally {
+                Trace.traceEnd(Trace.TRACE_TAG_SYNC_MANAGER);
+
+                if (provider != null) {
+                    provider.release();
+                }
+                if (!isCanceled()) {
+                    mSyncContext.onFinished(syncResult);
+                }
+                // synchronize so that the assignment will be seen by other threads
+                // that also synchronize accesses to mSyncThreads
+                synchronized (mSyncThreadLock) {
+                    mSyncThreads.remove(mThreadsKey);
+                }
+
+                if (ENABLE_LOG) {
+                    Log.d(TAG, "Thread finished");
+                }
+            }
+        }
+
+        private boolean isCanceled() {
+            return Thread.currentThread().isInterrupted();
+        }
+    }
+
+    /**
+     * @return a reference to the IBinder of the SyncAdapter service.
+     */
+    public final IBinder getSyncAdapterBinder() {
+        return mISyncAdapterImpl.asBinder();
+    }
+
+    /**
+     * Handle a call of onUnsyncableAccount.
+     *
+     * @param cb The callback to report the return value to
+     */
+    private void handleOnUnsyncableAccount(@NonNull ISyncAdapterUnsyncableAccountCallback cb) {
+        boolean doSync;
+        try {
+            doSync = onUnsyncableAccount();
+        } catch (RuntimeException e) {
+            Log.e(TAG, "Exception while calling onUnsyncableAccount, assuming 'true'", e);
+            doSync = true;
+        }
+
+        try {
+            cb.onUnsyncableAccountDone(doSync);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Could not report result of onUnsyncableAccount", e);
+        }
+    }
+
+    /**
+     * Allows to defer syncing until all accounts are properly set up.
+     *
+     * <p>Called when a account / authority pair
+     * <ul>
+     * <li>that can be handled by this adapter</li>
+     * <li>{@link ContentResolver#requestSync(SyncRequest) is synced}</li>
+     * <li>and the account/provider {@link ContentResolver#getIsSyncable(Account, String) has
+     * unknown state (<0)}.</li>
+     * </ul>
+     *
+     * <p>This might be called on a different service connection as {@link #onPerformSync}.
+     *
+     * <p>The system expects this method to immediately return. If the call stalls the system
+     * behaves as if this method returned {@code true}. If it is required to perform a longer task
+     * (such as interacting with the user), return {@code false} and proceed in a difference
+     * context, such as an {@link android.app.Activity}, or foreground service. The sync can then be
+     * rescheduled once the account becomes syncable.
+     *
+     * @return If {@code false} syncing is deferred. Returns {@code true} by default, i.e. by
+     *         default syncing starts immediately.
+     */
+    @MainThread
+    public boolean onUnsyncableAccount() {
+        return true;
+    }
+
+    /**
+     * Perform a sync for this account. SyncAdapter-specific parameters may
+     * be specified in extras, which is guaranteed to not be null. Invocations
+     * of this method are guaranteed to be serialized.
+     *
+     * @param account the account that should be synced
+     * @param extras SyncAdapter-specific parameters
+     * @param authority the authority of this sync request
+     * @param provider a ContentProviderClient that points to the ContentProvider for this
+     *   authority
+     * @param syncResult SyncAdapter-specific parameters
+     */
+    public abstract void onPerformSync(Account account, Bundle extras,
+            String authority, ContentProviderClient provider, SyncResult syncResult);
+
+    /**
+     * Report that there was a security exception when opening the content provider
+     * prior to calling {@link #onPerformSync}.  This will be treated as a sync
+     * database failure.
+     *
+     * @param account the account that attempted to sync
+     * @param extras SyncAdapter-specific parameters
+     * @param authority the authority of the failed sync request
+     * @param syncResult SyncAdapter-specific parameters
+     */
+    public void onSecurityException(Account account, Bundle extras,
+            String authority, SyncResult syncResult) {
+    }
+
+    /**
+     * Indicates that a sync operation has been canceled. This will be invoked on a separate
+     * thread than the sync thread and so you must consider the multi-threaded implications
+     * of the work that you do in this method.
+     * <p>
+     * This will only be invoked when the SyncAdapter indicates that it doesn't support
+     * parallel syncs.
+     */
+    public void onSyncCanceled() {
+        final SyncThread syncThread;
+        synchronized (mSyncThreadLock) {
+            syncThread = mSyncThreads.get(null);
+        }
+        if (syncThread != null) {
+            syncThread.interrupt();
+        }
+    }
+
+    /**
+     * Indicates that a sync operation has been canceled. This will be invoked on a separate
+     * thread than the sync thread and so you must consider the multi-threaded implications
+     * of the work that you do in this method.
+     * <p>
+     * This will only be invoked when the SyncAdapter indicates that it does support
+     * parallel syncs.
+     * @param thread the Thread of the sync that is to be canceled.
+     */
+    public void onSyncCanceled(Thread thread) {
+        thread.interrupt();
+    }
+}
diff --git a/android/content/ActivityNotFoundException.java b/android/content/ActivityNotFoundException.java
new file mode 100644
index 0000000..16149bb
--- /dev/null
+++ b/android/content/ActivityNotFoundException.java
@@ -0,0 +1,35 @@
+/*
+ * 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.content;
+
+/**
+ * This exception is thrown when a call to {@link Context#startActivity} or
+ * one of its variants fails because an Activity can not be found to execute
+ * the given Intent.
+ */
+public class ActivityNotFoundException extends RuntimeException
+{
+    public ActivityNotFoundException()
+    {
+    }
+
+    public ActivityNotFoundException(String name)
+    {
+        super(name);
+    }
+};
+
diff --git a/android/content/ApexEnvironment.java b/android/content/ApexEnvironment.java
new file mode 100644
index 0000000..b4cc3c2
--- /dev/null
+++ b/android/content/ApexEnvironment.java
@@ -0,0 +1,98 @@
+/*
+ * 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.content;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Environment;
+import android.os.UserHandle;
+
+import java.io.File;
+import java.util.Objects;
+
+/**
+ * Provides information about the environment for a particular APEX.
+ *
+ * @hide
+ */
+@SystemApi
+public class ApexEnvironment {
+
+    private static final String APEX_DATA = "apexdata";
+
+    /**
+     * Returns an ApexEnvironment instance for the APEX with the provided {@code apexModuleName}.
+     *
+     * <p>To preserve the safety and integrity of APEX modules, you must only obtain the
+     * ApexEnvironment for your specific APEX, and you <em>must never</em> attempt to obtain an
+     * ApexEnvironment for another APEX.  Any coordination between APEXs must be performed through
+     * well-defined interfaces; attempting to directly read or write raw files belonging to another
+     * APEX will violate the hermetic storage requirements placed upon each module.
+     */
+    @NonNull
+    public static ApexEnvironment getApexEnvironment(@NonNull String apexModuleName) {
+        Objects.requireNonNull(apexModuleName, "apexModuleName cannot be null");
+        //TODO(b/141148175): Check that apexModuleName is an actual APEX name
+        return new ApexEnvironment(apexModuleName);
+    }
+
+    private final String mApexModuleName;
+
+    private ApexEnvironment(String apexModuleName) {
+        mApexModuleName = apexModuleName;
+    }
+
+    /**
+     * Returns the data directory for the APEX in device-encrypted, non-user-specific storage.
+     *
+     * <p>This directory is automatically created by the system for installed APEXes, and its
+     * contents will be rolled back if the APEX is rolled back.
+     */
+    @NonNull
+    public File getDeviceProtectedDataDir() {
+        return Environment.buildPath(
+                Environment.getDataMiscDirectory(), APEX_DATA, mApexModuleName);
+    }
+
+    /**
+     * Returns the data directory for the APEX in device-encrypted, user-specific storage for the
+     * specified {@code user}.
+     *
+     * <p>This directory is automatically created by the system for each user and for each installed
+     * APEX, and its contents will be rolled back if the APEX is rolled back.
+     */
+    @NonNull
+    public File getDeviceProtectedDataDirForUser(@NonNull UserHandle user) {
+        return Environment.buildPath(
+                Environment.getDataMiscDeDirectory(user.getIdentifier()), APEX_DATA,
+                mApexModuleName);
+    }
+
+    /**
+     * Returns the data directory for the APEX in credential-encrypted, user-specific storage for
+     * the specified {@code user}.
+     *
+     * <p>This directory is automatically created by the system for each user and for each installed
+     * APEX, and its contents will be rolled back if the APEX is rolled back.
+     */
+    @NonNull
+    public File getCredentialProtectedDataDirForUser(@NonNull UserHandle user) {
+        return Environment.buildPath(
+                Environment.getDataMiscCeDirectory(user.getIdentifier()), APEX_DATA,
+                mApexModuleName);
+    }
+}
diff --git a/android/content/AsyncQueryHandler.java b/android/content/AsyncQueryHandler.java
new file mode 100644
index 0000000..07da99d
--- /dev/null
+++ b/android/content/AsyncQueryHandler.java
@@ -0,0 +1,360 @@
+/*
+ * 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.content;
+
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * A helper class to help make handling asynchronous {@link ContentResolver}
+ * queries easier.
+ */
+public abstract class AsyncQueryHandler extends Handler {
+    private static final String TAG = "AsyncQuery";
+    private static final boolean localLOGV = false;
+
+    private static final int EVENT_ARG_QUERY = 1;
+    private static final int EVENT_ARG_INSERT = 2;
+    private static final int EVENT_ARG_UPDATE = 3;
+    private static final int EVENT_ARG_DELETE = 4;
+
+    /* package */ final WeakReference<ContentResolver> mResolver;
+
+    private static Looper sLooper = null;
+
+    private Handler mWorkerThreadHandler;
+
+    protected static final class WorkerArgs {
+        public Uri uri;
+        public Handler handler;
+        public String[] projection;
+        public String selection;
+        public String[] selectionArgs;
+        public String orderBy;
+        public Object result;
+        public Object cookie;
+        public ContentValues values;
+    }
+
+    protected class WorkerHandler extends Handler {
+        public WorkerHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            final ContentResolver resolver = mResolver.get();
+            if (resolver == null) return;
+
+            WorkerArgs args = (WorkerArgs) msg.obj;
+
+            int token = msg.what;
+            int event = msg.arg1;
+
+            switch (event) {
+                case EVENT_ARG_QUERY:
+                    Cursor cursor;
+                    try {
+                        cursor = resolver.query(args.uri, args.projection,
+                                args.selection, args.selectionArgs,
+                                args.orderBy);
+                        // Calling getCount() causes the cursor window to be filled,
+                        // which will make the first access on the main thread a lot faster.
+                        if (cursor != null) {
+                            cursor.getCount();
+                        }
+                    } catch (Exception e) {
+                        Log.w(TAG, "Exception thrown during handling EVENT_ARG_QUERY", e);
+                        cursor = null;
+                    }
+
+                    args.result = cursor;
+                    break;
+
+                case EVENT_ARG_INSERT:
+                    args.result = resolver.insert(args.uri, args.values);
+                    break;
+
+                case EVENT_ARG_UPDATE:
+                    args.result = resolver.update(args.uri, args.values, args.selection,
+                            args.selectionArgs);
+                    break;
+
+                case EVENT_ARG_DELETE:
+                    args.result = resolver.delete(args.uri, args.selection, args.selectionArgs);
+                    break;
+            }
+
+            // passing the original token value back to the caller
+            // on top of the event values in arg1.
+            Message reply = args.handler.obtainMessage(token);
+            reply.obj = args;
+            reply.arg1 = msg.arg1;
+
+            if (localLOGV) {
+                Log.d(TAG, "WorkerHandler.handleMsg: msg.arg1=" + msg.arg1
+                        + ", reply.what=" + reply.what);
+            }
+
+            reply.sendToTarget();
+        }
+    }
+
+    public AsyncQueryHandler(ContentResolver cr) {
+        super();
+        mResolver = new WeakReference<ContentResolver>(cr);
+        synchronized (AsyncQueryHandler.class) {
+            if (sLooper == null) {
+                HandlerThread thread = new HandlerThread("AsyncQueryWorker");
+                thread.start();
+
+                sLooper = thread.getLooper();
+            }
+        }
+        mWorkerThreadHandler = createHandler(sLooper);
+    }
+
+    protected Handler createHandler(Looper looper) {
+        return new WorkerHandler(looper);
+    }
+
+    /**
+     * This method begins an asynchronous query. When the query is done
+     * {@link #onQueryComplete} is called.
+     *
+     * @param token A token passed into {@link #onQueryComplete} to identify
+     *  the query.
+     * @param cookie An object that gets passed into {@link #onQueryComplete}
+     * @param uri The URI, using the content:// scheme, for the content to
+     *         retrieve.
+     * @param projection A list of which columns to return. Passing null will
+     *         return all columns, which is discouraged to prevent reading data
+     *         from storage that isn't going to be used.
+     * @param selection A filter declaring which rows to return, formatted as an
+     *         SQL WHERE clause (excluding the WHERE itself). Passing null will
+     *         return all rows for the given URI.
+     * @param selectionArgs You may include ?s in selection, which will be
+     *         replaced by the values from selectionArgs, in the order that they
+     *         appear in the selection. The values will be bound as Strings.
+     * @param orderBy How to order the rows, formatted as an SQL ORDER BY
+     *         clause (excluding the ORDER BY itself). Passing null will use the
+     *         default sort order, which may be unordered.
+     */
+    public void startQuery(int token, Object cookie, Uri uri,
+            String[] projection, String selection, String[] selectionArgs,
+            String orderBy) {
+        // Use the token as what so cancelOperations works properly
+        Message msg = mWorkerThreadHandler.obtainMessage(token);
+        msg.arg1 = EVENT_ARG_QUERY;
+
+        WorkerArgs args = new WorkerArgs();
+        args.handler = this;
+        args.uri = uri;
+        args.projection = projection;
+        args.selection = selection;
+        args.selectionArgs = selectionArgs;
+        args.orderBy = orderBy;
+        args.cookie = cookie;
+        msg.obj = args;
+
+        mWorkerThreadHandler.sendMessage(msg);
+    }
+
+    /**
+     * Attempts to cancel operation that has not already started. Note that
+     * there is no guarantee that the operation will be canceled. They still may
+     * result in a call to on[Query/Insert/Update/Delete]Complete after this
+     * call has completed.
+     *
+     * @param token The token representing the operation to be canceled.
+     *  If multiple operations have the same token they will all be canceled.
+     */
+    public final void cancelOperation(int token) {
+        mWorkerThreadHandler.removeMessages(token);
+    }
+
+    /**
+     * This method begins an asynchronous insert. When the insert operation is
+     * done {@link #onInsertComplete} is called.
+     *
+     * @param token A token passed into {@link #onInsertComplete} to identify
+     *  the insert operation.
+     * @param cookie An object that gets passed into {@link #onInsertComplete}
+     * @param uri the Uri passed to the insert operation.
+     * @param initialValues the ContentValues parameter passed to the insert operation.
+     */
+    public final void startInsert(int token, Object cookie, Uri uri,
+            ContentValues initialValues) {
+        // Use the token as what so cancelOperations works properly
+        Message msg = mWorkerThreadHandler.obtainMessage(token);
+        msg.arg1 = EVENT_ARG_INSERT;
+
+        WorkerArgs args = new WorkerArgs();
+        args.handler = this;
+        args.uri = uri;
+        args.cookie = cookie;
+        args.values = initialValues;
+        msg.obj = args;
+
+        mWorkerThreadHandler.sendMessage(msg);
+    }
+
+    /**
+     * This method begins an asynchronous update. When the update operation is
+     * done {@link #onUpdateComplete} is called.
+     *
+     * @param token A token passed into {@link #onUpdateComplete} to identify
+     *  the update operation.
+     * @param cookie An object that gets passed into {@link #onUpdateComplete}
+     * @param uri the Uri passed to the update operation.
+     * @param values the ContentValues parameter passed to the update operation.
+     */
+    public final void startUpdate(int token, Object cookie, Uri uri,
+            ContentValues values, String selection, String[] selectionArgs) {
+        // Use the token as what so cancelOperations works properly
+        Message msg = mWorkerThreadHandler.obtainMessage(token);
+        msg.arg1 = EVENT_ARG_UPDATE;
+
+        WorkerArgs args = new WorkerArgs();
+        args.handler = this;
+        args.uri = uri;
+        args.cookie = cookie;
+        args.values = values;
+        args.selection = selection;
+        args.selectionArgs = selectionArgs;
+        msg.obj = args;
+
+        mWorkerThreadHandler.sendMessage(msg);
+    }
+
+    /**
+     * This method begins an asynchronous delete. When the delete operation is
+     * done {@link #onDeleteComplete} is called.
+     *
+     * @param token A token passed into {@link #onDeleteComplete} to identify
+     *  the delete operation.
+     * @param cookie An object that gets passed into {@link #onDeleteComplete}
+     * @param uri the Uri passed to the delete operation.
+     * @param selection the where clause.
+     */
+    public final void startDelete(int token, Object cookie, Uri uri,
+            String selection, String[] selectionArgs) {
+        // Use the token as what so cancelOperations works properly
+        Message msg = mWorkerThreadHandler.obtainMessage(token);
+        msg.arg1 = EVENT_ARG_DELETE;
+
+        WorkerArgs args = new WorkerArgs();
+        args.handler = this;
+        args.uri = uri;
+        args.cookie = cookie;
+        args.selection = selection;
+        args.selectionArgs = selectionArgs;
+        msg.obj = args;
+
+        mWorkerThreadHandler.sendMessage(msg);
+    }
+
+    /**
+     * Called when an asynchronous query is completed.
+     *
+     * @param token the token to identify the query, passed in from
+     *            {@link #startQuery}.
+     * @param cookie the cookie object passed in from {@link #startQuery}.
+     * @param cursor The cursor holding the results from the query.
+     */
+    protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
+        // Empty
+    }
+
+    /**
+     * Called when an asynchronous insert is completed.
+     *
+     * @param token the token to identify the query, passed in from
+     *        {@link #startInsert}.
+     * @param cookie the cookie object that's passed in from
+     *        {@link #startInsert}.
+     * @param uri the uri returned from the insert operation.
+     */
+    protected void onInsertComplete(int token, Object cookie, Uri uri) {
+        // Empty
+    }
+
+    /**
+     * Called when an asynchronous update is completed.
+     *
+     * @param token the token to identify the query, passed in from
+     *        {@link #startUpdate}.
+     * @param cookie the cookie object that's passed in from
+     *        {@link #startUpdate}.
+     * @param result the result returned from the update operation
+     */
+    protected void onUpdateComplete(int token, Object cookie, int result) {
+        // Empty
+    }
+
+    /**
+     * Called when an asynchronous delete is completed.
+     *
+     * @param token the token to identify the query, passed in from
+     *        {@link #startDelete}.
+     * @param cookie the cookie object that's passed in from
+     *        {@link #startDelete}.
+     * @param result the result returned from the delete operation
+     */
+    protected void onDeleteComplete(int token, Object cookie, int result) {
+        // Empty
+    }
+
+    @Override
+    public void handleMessage(Message msg) {
+        WorkerArgs args = (WorkerArgs) msg.obj;
+
+        if (localLOGV) {
+            Log.d(TAG, "AsyncQueryHandler.handleMessage: msg.what=" + msg.what
+                    + ", msg.arg1=" + msg.arg1);
+        }
+
+        int token = msg.what;
+        int event = msg.arg1;
+
+        // pass token back to caller on each callback.
+        switch (event) {
+            case EVENT_ARG_QUERY:
+                onQueryComplete(token, args.cookie, (Cursor) args.result);
+                break;
+
+            case EVENT_ARG_INSERT:
+                onInsertComplete(token, args.cookie, (Uri) args.result);
+                break;
+
+            case EVENT_ARG_UPDATE:
+                onUpdateComplete(token, args.cookie, (Integer) args.result);
+                break;
+
+            case EVENT_ARG_DELETE:
+                onDeleteComplete(token, args.cookie, (Integer) args.result);
+                break;
+        }
+    }
+}
diff --git a/android/content/AsyncTaskLoader.java b/android/content/AsyncTaskLoader.java
new file mode 100644
index 0000000..14c3387
--- /dev/null
+++ b/android/content/AsyncTaskLoader.java
@@ -0,0 +1,387 @@
+/*
+ * 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.content;
+
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.AsyncTask;
+import android.os.Handler;
+import android.os.OperationCanceledException;
+import android.os.SystemClock;
+import android.util.Log;
+import android.util.TimeUtils;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
+
+/**
+ * Abstract Loader that provides an {@link AsyncTask} to do the work.  See
+ * {@link Loader} and {@link android.app.LoaderManager} for more details.
+ *
+ * <p>Here is an example implementation of an AsyncTaskLoader subclass that
+ * loads the currently installed applications from the package manager.  This
+ * implementation takes care of retrieving the application labels and sorting
+ * its result set from them, monitoring for changes to the installed
+ * applications, and rebuilding the list when a change in configuration requires
+ * this (such as a locale change).
+ *
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/LoaderCustom.java
+ *      loader}
+ *
+ * <p>An example implementation of a fragment that uses the above loader to show
+ * the currently installed applications in a list is below.
+ *
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/LoaderCustom.java
+ *      fragment}
+ *
+ * @param <D> the data type to be loaded.
+ *
+ * @deprecated Use the <a href="{@docRoot}tools/extras/support-library.html">Support Library</a>
+ *      {@link android.support.v4.content.AsyncTaskLoader}
+ */
+@Deprecated
+public abstract class AsyncTaskLoader<D> extends Loader<D> {
+    static final String TAG = "AsyncTaskLoader";
+    static final boolean DEBUG = false;
+
+    final class LoadTask extends AsyncTask<Void, Void, D> implements Runnable {
+        private final CountDownLatch mDone = new CountDownLatch(1);
+
+        // Set to true to indicate that the task has been posted to a handler for
+        // execution at a later time.  Used to throttle updates.
+        boolean waiting;
+
+        /* Runs on a worker thread */
+        @Override
+        protected D doInBackground(Void... params) {
+            if (DEBUG) Log.v(TAG, this + " >>> doInBackground");
+            try {
+                D data = AsyncTaskLoader.this.onLoadInBackground();
+                if (DEBUG) Log.v(TAG, this + "  <<< doInBackground");
+                return data;
+            } catch (OperationCanceledException ex) {
+                if (!isCancelled()) {
+                    // onLoadInBackground threw a canceled exception spuriously.
+                    // This is problematic because it means that the LoaderManager did not
+                    // cancel the Loader itself and still expects to receive a result.
+                    // Additionally, the Loader's own state will not have been updated to
+                    // reflect the fact that the task was being canceled.
+                    // So we treat this case as an unhandled exception.
+                    throw ex;
+                }
+                if (DEBUG) Log.v(TAG, this + "  <<< doInBackground (was canceled)", ex);
+                return null;
+            }
+        }
+
+        /* Runs on the UI thread */
+        @Override
+        protected void onPostExecute(D data) {
+            if (DEBUG) Log.v(TAG, this + " onPostExecute");
+            try {
+                AsyncTaskLoader.this.dispatchOnLoadComplete(this, data);
+            } finally {
+                mDone.countDown();
+            }
+        }
+
+        /* Runs on the UI thread */
+        @Override
+        protected void onCancelled(D data) {
+            if (DEBUG) Log.v(TAG, this + " onCancelled");
+            try {
+                AsyncTaskLoader.this.dispatchOnCancelled(this, data);
+            } finally {
+                mDone.countDown();
+            }
+        }
+
+        /* Runs on the UI thread, when the waiting task is posted to a handler.
+         * This method is only executed when task execution was deferred (waiting was true). */
+        @Override
+        public void run() {
+            waiting = false;
+            AsyncTaskLoader.this.executePendingTask();
+        }
+
+        /* Used for testing purposes to wait for the task to complete. */
+        public void waitForLoader() {
+            try {
+                mDone.await();
+            } catch (InterruptedException e) {
+                // Ignore
+            }
+        }
+    }
+
+    @UnsupportedAppUsage
+    private final Executor mExecutor;
+
+    volatile LoadTask mTask;
+    volatile LoadTask mCancellingTask;
+
+    long mUpdateThrottle;
+    long mLastLoadCompleteTime = -10000;
+    Handler mHandler;
+
+    public AsyncTaskLoader(Context context) {
+        this(context, AsyncTask.THREAD_POOL_EXECUTOR);
+    }
+
+    /** {@hide} */
+    public AsyncTaskLoader(Context context, Executor executor) {
+        super(context);
+        mExecutor = executor;
+    }
+
+    /**
+     * Set amount to throttle updates by.  This is the minimum time from
+     * when the last {@link #loadInBackground()} call has completed until
+     * a new load is scheduled.
+     *
+     * @param delayMS Amount of delay, in milliseconds.
+     */
+    public void setUpdateThrottle(long delayMS) {
+        mUpdateThrottle = delayMS;
+        if (delayMS != 0) {
+            mHandler = new Handler();
+        }
+    }
+
+    @Override
+    protected void onForceLoad() {
+        super.onForceLoad();
+        cancelLoad();
+        mTask = new LoadTask();
+        if (DEBUG) Log.v(TAG, "Preparing load: mTask=" + mTask);
+        executePendingTask();
+    }
+
+    @Override
+    protected boolean onCancelLoad() {
+        if (DEBUG) Log.v(TAG, "onCancelLoad: mTask=" + mTask);
+        if (mTask != null) {
+            if (!mStarted) {
+                mContentChanged = true;
+            }
+            if (mCancellingTask != null) {
+                // There was a pending task already waiting for a previous
+                // one being canceled; just drop it.
+                if (DEBUG) Log.v(TAG,
+                        "cancelLoad: still waiting for cancelled task; dropping next");
+                if (mTask.waiting) {
+                    mTask.waiting = false;
+                    mHandler.removeCallbacks(mTask);
+                }
+                mTask = null;
+                return false;
+            } else if (mTask.waiting) {
+                // There is a task, but it is waiting for the time it should
+                // execute.  We can just toss it.
+                if (DEBUG) Log.v(TAG, "cancelLoad: task is waiting, dropping it");
+                mTask.waiting = false;
+                mHandler.removeCallbacks(mTask);
+                mTask = null;
+                return false;
+            } else {
+                boolean cancelled = mTask.cancel(false);
+                if (DEBUG) Log.v(TAG, "cancelLoad: cancelled=" + cancelled);
+                if (cancelled) {
+                    mCancellingTask = mTask;
+                    cancelLoadInBackground();
+                }
+                mTask = null;
+                return cancelled;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Called if the task was canceled before it was completed.  Gives the class a chance
+     * to clean up post-cancellation and to properly dispose of the result.
+     *
+     * @param data The value that was returned by {@link #loadInBackground}, or null
+     * if the task threw {@link OperationCanceledException}.
+     */
+    public void onCanceled(D data) {
+    }
+
+    void executePendingTask() {
+        if (mCancellingTask == null && mTask != null) {
+            if (mTask.waiting) {
+                mTask.waiting = false;
+                mHandler.removeCallbacks(mTask);
+            }
+            if (mUpdateThrottle > 0) {
+                long now = SystemClock.uptimeMillis();
+                if (now < (mLastLoadCompleteTime+mUpdateThrottle)) {
+                    // Not yet time to do another load.
+                    if (DEBUG) Log.v(TAG, "Waiting until "
+                            + (mLastLoadCompleteTime+mUpdateThrottle)
+                            + " to execute: " + mTask);
+                    mTask.waiting = true;
+                    mHandler.postAtTime(mTask, mLastLoadCompleteTime+mUpdateThrottle);
+                    return;
+                }
+            }
+            if (DEBUG) Log.v(TAG, "Executing: " + mTask);
+            mTask.executeOnExecutor(mExecutor, (Void[]) null);
+        }
+    }
+
+    void dispatchOnCancelled(LoadTask task, D data) {
+        onCanceled(data);
+        if (mCancellingTask == task) {
+            if (DEBUG) Log.v(TAG, "Cancelled task is now canceled!");
+            rollbackContentChanged();
+            mLastLoadCompleteTime = SystemClock.uptimeMillis();
+            mCancellingTask = null;
+            if (DEBUG) Log.v(TAG, "Delivering cancellation");
+            deliverCancellation();
+            executePendingTask();
+        }
+    }
+
+    void dispatchOnLoadComplete(LoadTask task, D data) {
+        if (mTask != task) {
+            if (DEBUG) Log.v(TAG, "Load complete of old task, trying to cancel");
+            dispatchOnCancelled(task, data);
+        } else {
+            if (isAbandoned()) {
+                // This cursor has been abandoned; just cancel the new data.
+                onCanceled(data);
+            } else {
+                commitContentChanged();
+                mLastLoadCompleteTime = SystemClock.uptimeMillis();
+                mTask = null;
+                if (DEBUG) Log.v(TAG, "Delivering result");
+                deliverResult(data);
+            }
+        }
+    }
+
+    /**
+     * Called on a worker thread to perform the actual load and to return
+     * the result of the load operation.
+     *
+     * Implementations should not deliver the result directly, but should return them
+     * from this method, which will eventually end up calling {@link #deliverResult} on
+     * the UI thread.  If implementations need to process the results on the UI thread
+     * they may override {@link #deliverResult} and do so there.
+     *
+     * To support cancellation, this method should periodically check the value of
+     * {@link #isLoadInBackgroundCanceled} and terminate when it returns true.
+     * Subclasses may also override {@link #cancelLoadInBackground} to interrupt the load
+     * directly instead of polling {@link #isLoadInBackgroundCanceled}.
+     *
+     * When the load is canceled, this method may either return normally or throw
+     * {@link OperationCanceledException}.  In either case, the {@link Loader} will
+     * call {@link #onCanceled} to perform post-cancellation cleanup and to dispose of the
+     * result object, if any.
+     *
+     * @return The result of the load operation.
+     *
+     * @throws OperationCanceledException if the load is canceled during execution.
+     *
+     * @see #isLoadInBackgroundCanceled
+     * @see #cancelLoadInBackground
+     * @see #onCanceled
+     */
+    public abstract D loadInBackground();
+
+    /**
+     * Calls {@link #loadInBackground()}.
+     *
+     * This method is reserved for use by the loader framework.
+     * Subclasses should override {@link #loadInBackground} instead of this method.
+     *
+     * @return The result of the load operation.
+     *
+     * @throws OperationCanceledException if the load is canceled during execution.
+     *
+     * @see #loadInBackground
+     */
+    protected D onLoadInBackground() {
+        return loadInBackground();
+    }
+
+    /**
+     * Called on the main thread to abort a load in progress.
+     *
+     * Override this method to abort the current invocation of {@link #loadInBackground}
+     * that is running in the background on a worker thread.
+     *
+     * This method should do nothing if {@link #loadInBackground} has not started
+     * running or if it has already finished.
+     *
+     * @see #loadInBackground
+     */
+    public void cancelLoadInBackground() {
+    }
+
+    /**
+     * Returns true if the current invocation of {@link #loadInBackground} is being canceled.
+     *
+     * @return True if the current invocation of {@link #loadInBackground} is being canceled.
+     *
+     * @see #loadInBackground
+     */
+    public boolean isLoadInBackgroundCanceled() {
+        return mCancellingTask != null;
+    }
+
+    /**
+     * Locks the current thread until the loader completes the current load
+     * operation. Returns immediately if there is no load operation running.
+     * Should not be called from the UI thread: calling it from the UI
+     * thread would cause a deadlock.
+     * <p>
+     * Use for testing only.  <b>Never</b> call this from a UI thread.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public void waitForLoader() {
+        LoadTask task = mTask;
+        if (task != null) {
+            task.waitForLoader();
+        }
+    }
+
+    @Override
+    public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+        super.dump(prefix, fd, writer, args);
+        if (mTask != null) {
+            writer.print(prefix); writer.print("mTask="); writer.print(mTask);
+                    writer.print(" waiting="); writer.println(mTask.waiting);
+        }
+        if (mCancellingTask != null) {
+            writer.print(prefix); writer.print("mCancellingTask="); writer.print(mCancellingTask);
+                    writer.print(" waiting="); writer.println(mCancellingTask.waiting);
+        }
+        if (mUpdateThrottle != 0) {
+            writer.print(prefix); writer.print("mUpdateThrottle=");
+                    TimeUtils.formatDuration(mUpdateThrottle, writer);
+                    writer.print(" mLastLoadCompleteTime=");
+                    TimeUtils.formatDuration(mLastLoadCompleteTime,
+                            SystemClock.uptimeMillis(), writer);
+                    writer.println();
+        }
+    }
+}
diff --git a/android/content/AutofillOptions.java b/android/content/AutofillOptions.java
new file mode 100644
index 0000000..97b33b7
--- /dev/null
+++ b/android/content/AutofillOptions.java
@@ -0,0 +1,220 @@
+/*
+ * 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.content;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.TestApi;
+import android.app.ActivityThread;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.SystemClock;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Log;
+import android.view.autofill.AutofillManager;
+import android.view.autofill.AutofillManager.AutofillClient;
+
+import java.io.PrintWriter;
+
+/**
+ * Autofill options for a given package.
+ *
+ * <p>This object is created by the Autofill System Service and passed back to the app when the
+ * application is created.
+ *
+ * @hide
+ */
+@TestApi
+public final class AutofillOptions implements Parcelable {
+
+    private static final String TAG = AutofillOptions.class.getSimpleName();
+
+    /**
+     * Logging level for {@code logcat} statements.
+     */
+    public final int loggingLevel;
+
+    /**
+     * Whether compatibility mode is enabled for the package.
+     */
+    public final boolean compatModeEnabled;
+
+    /**
+     * Whether package is whitelisted for augmented autofill.
+     */
+    public boolean augmentedAutofillEnabled;
+
+    /**
+     * List of whitelisted activities.
+     */
+    @Nullable
+    public ArraySet<ComponentName> whitelistedActivitiesForAugmentedAutofill;
+
+    /**
+     * The package disable expiration by autofill service.
+     */
+    public long appDisabledExpiration;
+
+    /**
+     * The disabled Activities of the package. key is component name string, value is when they
+     * will be enabled.
+     */
+    @Nullable
+    public ArrayMap<String, Long> disabledActivities;
+
+    public AutofillOptions(int loggingLevel, boolean compatModeEnabled) {
+        this.loggingLevel = loggingLevel;
+        this.compatModeEnabled = compatModeEnabled;
+    }
+
+    /**
+     * Returns whether activity is whitelisted for augmented autofill.
+     */
+    public boolean isAugmentedAutofillEnabled(@NonNull Context context) {
+        if (!augmentedAutofillEnabled) return false;
+
+        final AutofillClient autofillClient = context.getAutofillClient();
+        if (autofillClient == null) return false;
+
+        final ComponentName component = autofillClient.autofillClientGetComponentName();
+        return whitelistedActivitiesForAugmentedAutofill == null
+                || whitelistedActivitiesForAugmentedAutofill.contains(component);
+    }
+
+    /**
+     * Returns if autofill is disabled by service to the given activity.
+     *
+     * @hide
+     */
+    public boolean isAutofillDisabledLocked(@NonNull ComponentName componentName) {
+        final long elapsedTime = SystemClock.elapsedRealtime();
+        final String component = componentName.flattenToString();
+        // Check app first.
+        if (appDisabledExpiration >= elapsedTime) return true;
+
+        // Then check activities.
+        if (disabledActivities != null) {
+            final Long expiration = disabledActivities.get(component);
+            if (expiration != null) {
+                if (expiration >= elapsedTime) return true;
+                disabledActivities.remove(component);
+            }
+        }
+        appDisabledExpiration = 0;
+        return false;
+    }
+
+    /**
+     * @hide
+     */
+    @TestApi
+    public static AutofillOptions forWhitelistingItself() {
+        final ActivityThread at = ActivityThread.currentActivityThread();
+        if (at == null) {
+            throw new IllegalStateException("No ActivityThread");
+        }
+
+        final String packageName = at.getApplication().getPackageName();
+
+        if (!"android.autofillservice.cts".equals(packageName)) {
+            Log.e(TAG, "forWhitelistingItself(): called by " + packageName);
+            throw new SecurityException("Thou shall not pass!");
+        }
+
+        final AutofillOptions options = new AutofillOptions(
+                AutofillManager.FLAG_ADD_CLIENT_VERBOSE, /* compatModeAllowed= */ true);
+        options.augmentedAutofillEnabled = true;
+        // Always log, as it's used by test only
+        Log.i(TAG, "forWhitelistingItself(" + packageName + "): " + options);
+
+        return options;
+    }
+
+    @Override
+    public String toString() {
+        return "AutofillOptions [loggingLevel=" + loggingLevel + ", compatMode=" + compatModeEnabled
+                + ", augmentedAutofillEnabled=" + augmentedAutofillEnabled
+                + ", appDisabledExpiration=" + appDisabledExpiration + "]";
+    }
+
+    /** @hide */
+    public void dumpShort(@NonNull PrintWriter pw) {
+        pw.print("logLvl="); pw.print(loggingLevel);
+        pw.print(", compatMode="); pw.print(compatModeEnabled);
+        pw.print(", augmented="); pw.print(augmentedAutofillEnabled);
+        if (whitelistedActivitiesForAugmentedAutofill != null) {
+            pw.print(", whitelistedActivitiesForAugmentedAutofill=");
+            pw.print(whitelistedActivitiesForAugmentedAutofill);
+        }
+        pw.print(", appDisabledExpiration="); pw.print(appDisabledExpiration);
+        if (disabledActivities != null) {
+            pw.print(", disabledActivities=");
+            pw.print(disabledActivities);
+        }
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeInt(loggingLevel);
+        parcel.writeBoolean(compatModeEnabled);
+        parcel.writeBoolean(augmentedAutofillEnabled);
+        parcel.writeArraySet(whitelistedActivitiesForAugmentedAutofill);
+        parcel.writeLong(appDisabledExpiration);
+        final int size = disabledActivities != null ? disabledActivities.size() : 0;
+        parcel.writeInt(size);
+        if (size > 0) {
+            for (int i = 0; i < size; i++) {
+                final String key = disabledActivities.keyAt(i);
+                parcel.writeString(key);
+                parcel.writeLong(disabledActivities.get(key));
+            }
+        }
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<AutofillOptions> CREATOR =
+            new Parcelable.Creator<AutofillOptions>() {
+
+                @Override
+                public AutofillOptions createFromParcel(Parcel parcel) {
+                    final int loggingLevel = parcel.readInt();
+                    final boolean compatMode = parcel.readBoolean();
+                    final AutofillOptions options = new AutofillOptions(loggingLevel, compatMode);
+                    options.augmentedAutofillEnabled = parcel.readBoolean();
+                    options.whitelistedActivitiesForAugmentedAutofill =
+                            (ArraySet<ComponentName>) parcel.readArraySet(null);
+                    options.appDisabledExpiration = parcel.readLong();
+                    final int size = parcel.readInt();
+                    if (size > 0) {
+                        options.disabledActivities = new ArrayMap<>();
+                        for (int i = 0; i < size; i++) {
+                            options.disabledActivities.put(parcel.readString(), parcel.readLong());
+                        }
+                    }
+                    return options;
+                }
+
+                @Override
+                public AutofillOptions[] newArray(int size) {
+                    return new AutofillOptions[size];
+                }
+    };
+}
diff --git a/android/content/BroadcastReceiver.java b/android/content/BroadcastReceiver.java
new file mode 100644
index 0000000..1d4d30d
--- /dev/null
+++ b/android/content/BroadcastReceiver.java
@@ -0,0 +1,687 @@
+/*
+ * 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.content;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.app.ActivityManager;
+import android.app.ActivityThread;
+import android.app.IActivityManager;
+import android.app.QueuedWork;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Log;
+import android.util.Slog;
+
+/**
+ * Base class for code that receives and handles broadcast intents sent by
+ * {@link android.content.Context#sendBroadcast(Intent)}.
+ *
+ * <p>You can either dynamically register an instance of this class with
+ * {@link Context#registerReceiver Context.registerReceiver()}
+ * or statically declare an implementation with the
+ * {@link android.R.styleable#AndroidManifestReceiver &lt;receiver&gt;}
+ * tag in your <code>AndroidManifest.xml</code>.
+ *
+ * <div class="special reference">
+ * <h3>Developer Guides</h3>
+ * <p>For more information about using BroadcastReceiver, read the
+ * <a href="{@docRoot}guide/components/broadcasts.html">Broadcasts</a> developer guide.</p></div>
+ *
+ */
+public abstract class BroadcastReceiver {
+    @UnsupportedAppUsage
+    private PendingResult mPendingResult;
+    private boolean mDebugUnregister;
+
+    /**
+     * State for a result that is pending for a broadcast receiver.  Returned
+     * by {@link BroadcastReceiver#goAsync() goAsync()}
+     * while in {@link BroadcastReceiver#onReceive BroadcastReceiver.onReceive()}.
+     * This allows you to return from onReceive() without having the broadcast
+     * terminate; you must call {@link #finish()} once you are done with the
+     * broadcast.  This allows you to process the broadcast off of the main
+     * thread of your app.
+     *
+     * <p>Note on threading: the state inside of this class is not itself
+     * thread-safe, however you can use it from any thread if you properly
+     * sure that you do not have races.  Typically this means you will hand
+     * the entire object to another thread, which will be solely responsible
+     * for setting any results and finally calling {@link #finish()}.
+     */
+    public static class PendingResult {
+        /** @hide */
+        public static final int TYPE_COMPONENT = 0;
+        /** @hide */
+        public static final int TYPE_REGISTERED = 1;
+        /** @hide */
+        public static final int TYPE_UNREGISTERED = 2;
+
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+        final int mType;
+        @UnsupportedAppUsage
+        final boolean mOrderedHint;
+        @UnsupportedAppUsage
+        final boolean mInitialStickyHint;
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+        final IBinder mToken;
+        @UnsupportedAppUsage
+        final int mSendingUser;
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+        final int mFlags;
+
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+        int mResultCode;
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+        String mResultData;
+        @UnsupportedAppUsage
+        Bundle mResultExtras;
+        @UnsupportedAppUsage
+        boolean mAbortBroadcast;
+        @UnsupportedAppUsage
+        boolean mFinished;
+
+        /** @hide */
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+        public PendingResult(int resultCode, String resultData, Bundle resultExtras, int type,
+                boolean ordered, boolean sticky, IBinder token, int userId, int flags) {
+            mResultCode = resultCode;
+            mResultData = resultData;
+            mResultExtras = resultExtras;
+            mType = type;
+            mOrderedHint = ordered;
+            mInitialStickyHint = sticky;
+            mToken = token;
+            mSendingUser = userId;
+            mFlags = flags;
+        }
+
+        /**
+         * Version of {@link BroadcastReceiver#setResultCode(int)
+         * BroadcastReceiver.setResultCode(int)} for
+         * asynchronous broadcast handling.
+         */
+        public final void setResultCode(int code) {
+            checkSynchronousHint();
+            mResultCode = code;
+        }
+
+        /**
+         * Version of {@link BroadcastReceiver#getResultCode()
+         * BroadcastReceiver.getResultCode()} for
+         * asynchronous broadcast handling.
+         */
+        public final int getResultCode() {
+            return mResultCode;
+        }
+
+        /**
+         * Version of {@link BroadcastReceiver#setResultData(String)
+         * BroadcastReceiver.setResultData(String)} for
+         * asynchronous broadcast handling.
+         */
+        public final void setResultData(String data) {
+            checkSynchronousHint();
+            mResultData = data;
+        }
+
+        /**
+         * Version of {@link BroadcastReceiver#getResultData()
+         * BroadcastReceiver.getResultData()} for
+         * asynchronous broadcast handling.
+         */
+        public final String getResultData() {
+            return mResultData;
+        }
+
+        /**
+         * Version of {@link BroadcastReceiver#setResultExtras(Bundle)
+         * BroadcastReceiver.setResultExtras(Bundle)} for
+         * asynchronous broadcast handling.
+         */
+        public final void setResultExtras(Bundle extras) {
+            checkSynchronousHint();
+            mResultExtras = extras;
+        }
+
+        /**
+         * Version of {@link BroadcastReceiver#getResultExtras(boolean)
+         * BroadcastReceiver.getResultExtras(boolean)} for
+         * asynchronous broadcast handling.
+         */
+        public final Bundle getResultExtras(boolean makeMap) {
+            Bundle e = mResultExtras;
+            if (!makeMap) return e;
+            if (e == null) mResultExtras = e = new Bundle();
+            return e;
+        }
+
+        /**
+         * Version of {@link BroadcastReceiver#setResult(int, String, Bundle)
+         * BroadcastReceiver.setResult(int, String, Bundle)} for
+         * asynchronous broadcast handling.
+         */
+        public final void setResult(int code, String data, Bundle extras) {
+            checkSynchronousHint();
+            mResultCode = code;
+            mResultData = data;
+            mResultExtras = extras;
+        }
+
+        /**
+         * Version of {@link BroadcastReceiver#getAbortBroadcast()
+         * BroadcastReceiver.getAbortBroadcast()} for
+         * asynchronous broadcast handling.
+         */
+        public final boolean getAbortBroadcast() {
+            return mAbortBroadcast;
+        }
+
+        /**
+         * Version of {@link BroadcastReceiver#abortBroadcast()
+         * BroadcastReceiver.abortBroadcast()} for
+         * asynchronous broadcast handling.
+         */
+        public final void abortBroadcast() {
+            checkSynchronousHint();
+            mAbortBroadcast = true;
+        }
+
+        /**
+         * Version of {@link BroadcastReceiver#clearAbortBroadcast()
+         * BroadcastReceiver.clearAbortBroadcast()} for
+         * asynchronous broadcast handling.
+         */
+        public final void clearAbortBroadcast() {
+            mAbortBroadcast = false;
+        }
+
+        /**
+         * Finish the broadcast.  The current result will be sent and the
+         * next broadcast will proceed.
+         */
+        public final void finish() {
+            if (mType == TYPE_COMPONENT) {
+                final IActivityManager mgr = ActivityManager.getService();
+                if (QueuedWork.hasPendingWork()) {
+                    // If this is a broadcast component, we need to make sure any
+                    // queued work is complete before telling AM we are done, so
+                    // we don't have our process killed before that.  We now know
+                    // there is pending work; put another piece of work at the end
+                    // of the list to finish the broadcast, so we don't block this
+                    // thread (which may be the main thread) to have it finished.
+                    //
+                    // Note that we don't need to use QueuedWork.addFinisher() with the
+                    // runnable, since we know the AM is waiting for us until the
+                    // executor gets to it.
+                    QueuedWork.queue(new Runnable() {
+                        @Override public void run() {
+                            if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
+                                    "Finishing broadcast after work to component " + mToken);
+                            sendFinished(mgr);
+                        }
+                    }, false);
+                } else {
+                    if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
+                            "Finishing broadcast to component " + mToken);
+                    sendFinished(mgr);
+                }
+            } else if (mOrderedHint && mType != TYPE_UNREGISTERED) {
+                if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
+                        "Finishing broadcast to " + mToken);
+                final IActivityManager mgr = ActivityManager.getService();
+                sendFinished(mgr);
+            }
+        }
+
+        /** @hide */
+        public void setExtrasClassLoader(ClassLoader cl) {
+            if (mResultExtras != null) {
+                mResultExtras.setClassLoader(cl);
+            }
+        }
+
+        /** @hide */
+        public void sendFinished(IActivityManager am) {
+            synchronized (this) {
+                if (mFinished) {
+                    throw new IllegalStateException("Broadcast already finished");
+                }
+                mFinished = true;
+
+                try {
+                    if (mResultExtras != null) {
+                        mResultExtras.setAllowFds(false);
+                    }
+                    if (mOrderedHint) {
+                        am.finishReceiver(mToken, mResultCode, mResultData, mResultExtras,
+                                mAbortBroadcast, mFlags);
+                    } else {
+                        // This broadcast was sent to a component; it is not ordered,
+                        // but we still need to tell the activity manager we are done.
+                        am.finishReceiver(mToken, 0, null, null, false, mFlags);
+                    }
+                } catch (RemoteException ex) {
+                }
+            }
+        }
+
+        /** @hide */
+        public int getSendingUserId() {
+            return mSendingUser;
+        }
+
+        void checkSynchronousHint() {
+            // Note that we don't assert when receiving the initial sticky value,
+            // since that may have come from an ordered broadcast.  We'll catch
+            // them later when the real broadcast happens again.
+            if (mOrderedHint || mInitialStickyHint) {
+                return;
+            }
+            RuntimeException e = new RuntimeException(
+                    "BroadcastReceiver trying to return result during a non-ordered broadcast");
+            e.fillInStackTrace();
+            Log.e("BroadcastReceiver", e.getMessage(), e);
+        }
+    }
+
+    public BroadcastReceiver() {
+    }
+
+    /**
+     * This method is called when the BroadcastReceiver is receiving an Intent
+     * broadcast.  During this time you can use the other methods on
+     * BroadcastReceiver to view/modify the current result values.  This method
+     * is always called within the main thread of its process, unless you
+     * explicitly asked for it to be scheduled on a different thread using
+     * {@link android.content.Context#registerReceiver(BroadcastReceiver,
+     * IntentFilter, String, android.os.Handler)}. When it runs on the main
+     * thread you should
+     * never perform long-running operations in it (there is a timeout of
+     * 10 seconds that the system allows before considering the receiver to
+     * be blocked and a candidate to be killed). You cannot launch a popup dialog
+     * in your implementation of onReceive().
+     *
+     * <p><b>If this BroadcastReceiver was launched through a &lt;receiver&gt; tag,
+     * then the object is no longer alive after returning from this
+     * function.</b> This means you should not perform any operations that
+     * return a result to you asynchronously. If you need to perform any follow up
+     * background work, schedule a {@link android.app.job.JobService} with
+     * {@link android.app.job.JobScheduler}.
+     *
+     * If you wish to interact with a service that is already running and previously
+     * bound using {@link android.content.Context#bindService(Intent, ServiceConnection, int) bindService()},
+     * you can use {@link #peekService}.
+     *
+     * <p>The Intent filters used in {@link android.content.Context#registerReceiver}
+     * and in application manifests are <em>not</em> guaranteed to be exclusive. They
+     * are hints to the operating system about how to find suitable recipients. It is
+     * possible for senders to force delivery to specific recipients, bypassing filter
+     * resolution.  For this reason, {@link #onReceive(Context, Intent) onReceive()}
+     * implementations should respond only to known actions, ignoring any unexpected
+     * Intents that they may receive.
+     *
+     * @param context The Context in which the receiver is running.
+     * @param intent The Intent being received.
+     */
+    public abstract void onReceive(Context context, Intent intent);
+
+    /**
+     * This can be called by an application in {@link #onReceive} to allow
+     * it to keep the broadcast active after returning from that function.
+     * This does <em>not</em> change the expectation of being relatively
+     * responsive to the broadcast, but does allow
+     * the implementation to move work related to it over to another thread
+     * to avoid glitching the main UI thread due to disk IO.
+     *
+     * <p>As a general rule, broadcast receivers are allowed to run for up to 10 seconds
+     * before they system will consider them non-responsive and ANR the app.  Since these usually
+     * execute on the app's main thread, they are already bound by the ~5 second time limit
+     * of various operations that can happen there (not to mention just avoiding UI jank), so
+     * the receive limit is generally not of concern.  However, once you use {@code goAsync}, though
+     * able to be off the main thread, the broadcast execution limit still applies, and that
+     * includes the time spent between calling this method and ultimately
+     * {@link PendingResult#finish() PendingResult.finish()}.</p>
+     *
+     * <p>If you are taking advantage of this method to have more time to execute, it is useful
+     * to know that the available time can be longer in certain situations.  In particular, if
+     * the broadcast you are receiving is not a foreground broadcast (that is, the sender has not
+     * used {@link Intent#FLAG_RECEIVER_FOREGROUND}), then more time is allowed for the receivers
+     * to run, allowing them to execute for 30 seconds or even a bit more.  This is something that
+     * receivers should rarely take advantage of (long work should be punted to another system
+     * facility such as {@link android.app.job.JobScheduler}, {@link android.app.Service}, or
+     * see especially {@link android.support.v4.app.JobIntentService}), but can be useful in
+     * certain rare cases where it is necessary to do some work as soon as the broadcast is
+     * delivered.  Keep in mind that the work you do here will block further broadcasts until
+     * it completes, so taking advantage of this at all excessively can be counter-productive
+     * and cause later events to be received more slowly.</p>
+     *
+     * @return Returns a {@link PendingResult} representing the result of
+     * the active broadcast.  The BroadcastRecord itself is no longer active;
+     * all data and other interaction must go through {@link PendingResult}
+     * APIs.  The {@link PendingResult#finish PendingResult.finish()} method
+     * must be called once processing of the broadcast is done.
+     */
+    public final PendingResult goAsync() {
+        PendingResult res = mPendingResult;
+        mPendingResult = null;
+        return res;
+    }
+
+    /**
+     * Provide a binder to an already-bound service.  This method is synchronous
+     * and will not start the target service if it is not present, so it is safe
+     * to call from {@link #onReceive}.
+     *
+     * For peekService() to return a non null {@link android.os.IBinder} interface
+     * the service must have published it before. In other words some component
+     * must have called {@link android.content.Context#bindService(Intent, ServiceConnection, int)} on it.
+     *
+     * @param myContext The Context that had been passed to {@link #onReceive(Context, Intent)}
+     * @param service Identifies the already-bound service you wish to use. See
+     * {@link android.content.Context#bindService(Intent, ServiceConnection, int)}
+     * for more information.
+     */
+    public IBinder peekService(Context myContext, Intent service) {
+        IActivityManager am = ActivityManager.getService();
+        IBinder binder = null;
+        try {
+            service.prepareToLeaveProcess(myContext);
+            binder = am.peekService(service, service.resolveTypeIfNeeded(
+                    myContext.getContentResolver()), myContext.getOpPackageName());
+        } catch (RemoteException e) {
+        }
+        return binder;
+    }
+
+    /**
+     * Change the current result code of this broadcast; only works with
+     * broadcasts sent through
+     * {@link Context#sendOrderedBroadcast(Intent, String)
+     * Context.sendOrderedBroadcast}.  Often uses the
+     * Activity {@link android.app.Activity#RESULT_CANCELED} and
+     * {@link android.app.Activity#RESULT_OK} constants, though the
+     * actual meaning of this value is ultimately up to the broadcaster.
+     *
+     * <p class="note">This method does not work with non-ordered broadcasts such
+     * as those sent with {@link Context#sendBroadcast(Intent)
+     * Context.sendBroadcast}</p>
+     *
+     * @param code The new result code.
+     *
+     * @see #setResult(int, String, Bundle)
+     */
+    public final void setResultCode(int code) {
+        checkSynchronousHint();
+        mPendingResult.mResultCode = code;
+    }
+
+    /**
+     * Retrieve the current result code, as set by the previous receiver.
+     *
+     * @return int The current result code.
+     */
+    public final int getResultCode() {
+        return mPendingResult != null ? mPendingResult.mResultCode : 0;
+    }
+
+    /**
+     * Change the current result data of this broadcast; only works with
+     * broadcasts sent through
+     * {@link Context#sendOrderedBroadcast(Intent, String)
+     * Context.sendOrderedBroadcast}.  This is an arbitrary
+     * string whose interpretation is up to the broadcaster.
+     *
+     * <p><strong>This method does not work with non-ordered broadcasts such
+     * as those sent with {@link Context#sendBroadcast(Intent)
+     * Context.sendBroadcast}</strong></p>
+     *
+     * @param data The new result data; may be null.
+     *
+     * @see #setResult(int, String, Bundle)
+     */
+    public final void setResultData(String data) {
+        checkSynchronousHint();
+        mPendingResult.mResultData = data;
+    }
+
+    /**
+     * Retrieve the current result data, as set by the previous receiver.
+     * Often this is null.
+     *
+     * @return String The current result data; may be null.
+     */
+    public final String getResultData() {
+        return mPendingResult != null ? mPendingResult.mResultData : null;
+    }
+
+    /**
+     * Change the current result extras of this broadcast; only works with
+     * broadcasts sent through
+     * {@link Context#sendOrderedBroadcast(Intent, String)
+     * Context.sendOrderedBroadcast}.  This is a Bundle
+     * holding arbitrary data, whose interpretation is up to the
+     * broadcaster.  Can be set to null.  Calling this method completely
+     * replaces the current map (if any).
+     *
+     * <p><strong>This method does not work with non-ordered broadcasts such
+     * as those sent with {@link Context#sendBroadcast(Intent)
+     * Context.sendBroadcast}</strong></p>
+     *
+     * @param extras The new extra data map; may be null.
+     *
+     * @see #setResult(int, String, Bundle)
+     */
+    public final void setResultExtras(Bundle extras) {
+        checkSynchronousHint();
+        mPendingResult.mResultExtras = extras;
+    }
+
+    /**
+     * Retrieve the current result extra data, as set by the previous receiver.
+     * Any changes you make to the returned Map will be propagated to the next
+     * receiver.
+     *
+     * @param makeMap If true then a new empty Map will be made for you if the
+     *                current Map is null; if false you should be prepared to
+     *                receive a null Map.
+     *
+     * @return Map The current extras map.
+     */
+    public final Bundle getResultExtras(boolean makeMap) {
+        if (mPendingResult == null) {
+            return null;
+        }
+        Bundle e = mPendingResult.mResultExtras;
+        if (!makeMap) return e;
+        if (e == null) mPendingResult.mResultExtras = e = new Bundle();
+        return e;
+    }
+
+    /**
+     * Change all of the result data returned from this broadcasts; only works
+     * with broadcasts sent through
+     * {@link Context#sendOrderedBroadcast(Intent, String)
+     * Context.sendOrderedBroadcast}.  All current result data is replaced
+     * by the value given to this method.
+     *
+     * <p><strong>This method does not work with non-ordered broadcasts such
+     * as those sent with {@link Context#sendBroadcast(Intent)
+     * Context.sendBroadcast}</strong></p>
+     *
+     * @param code The new result code.  Often uses the
+     * Activity {@link android.app.Activity#RESULT_CANCELED} and
+     * {@link android.app.Activity#RESULT_OK} constants, though the
+     * actual meaning of this value is ultimately up to the broadcaster.
+     * @param data The new result data.  This is an arbitrary
+     * string whose interpretation is up to the broadcaster; may be null.
+     * @param extras The new extra data map.  This is a Bundle
+     * holding arbitrary data, whose interpretation is up to the
+     * broadcaster.  Can be set to null.  This completely
+     * replaces the current map (if any).
+     */
+    public final void setResult(int code, String data, Bundle extras) {
+        checkSynchronousHint();
+        mPendingResult.mResultCode = code;
+        mPendingResult.mResultData = data;
+        mPendingResult.mResultExtras = extras;
+    }
+
+    /**
+     * Returns the flag indicating whether or not this receiver should
+     * abort the current broadcast.
+     *
+     * @return True if the broadcast should be aborted.
+     */
+    public final boolean getAbortBroadcast() {
+        return mPendingResult != null ? mPendingResult.mAbortBroadcast : false;
+    }
+
+    /**
+     * Sets the flag indicating that this receiver should abort the
+     * current broadcast; only works with broadcasts sent through
+     * {@link Context#sendOrderedBroadcast(Intent, String)
+     * Context.sendOrderedBroadcast}.  This will prevent
+     * any other broadcast receivers from receiving the broadcast. It will still
+     * call {@link #onReceive} of the BroadcastReceiver that the caller of
+     * {@link Context#sendOrderedBroadcast(Intent, String)
+     * Context.sendOrderedBroadcast} passed in.
+     *
+     * <p><strong>This method does not work with non-ordered broadcasts such
+     * as those sent with {@link Context#sendBroadcast(Intent)
+     * Context.sendBroadcast}</strong></p>
+     */
+    public final void abortBroadcast() {
+        checkSynchronousHint();
+        mPendingResult.mAbortBroadcast = true;
+    }
+
+    /**
+     * Clears the flag indicating that this receiver should abort the current
+     * broadcast.
+     */
+    public final void clearAbortBroadcast() {
+        if (mPendingResult != null) {
+            mPendingResult.mAbortBroadcast = false;
+        }
+    }
+
+    /**
+     * Returns true if the receiver is currently processing an ordered
+     * broadcast.
+     */
+    public final boolean isOrderedBroadcast() {
+        return mPendingResult != null ? mPendingResult.mOrderedHint : false;
+    }
+
+    /**
+     * Returns true if the receiver is currently processing the initial
+     * value of a sticky broadcast -- that is, the value that was last
+     * broadcast and is currently held in the sticky cache, so this is
+     * not directly the result of a broadcast right now.
+     */
+    public final boolean isInitialStickyBroadcast() {
+        return mPendingResult != null ? mPendingResult.mInitialStickyHint : false;
+    }
+
+    /**
+     * For internal use, sets the hint about whether this BroadcastReceiver is
+     * running in ordered mode.
+     */
+    public final void setOrderedHint(boolean isOrdered) {
+        // Accidentally left in the SDK.
+    }
+
+    /**
+     * For internal use to set the result data that is active. @hide
+     */
+    @UnsupportedAppUsage
+    public final void setPendingResult(PendingResult result) {
+        mPendingResult = result;
+    }
+
+    /**
+     * For internal use to set the result data that is active. @hide
+     */
+    @UnsupportedAppUsage
+    public final PendingResult getPendingResult() {
+        return mPendingResult;
+    }
+
+    /**
+     * Returns the user that the broadcast was sent to.
+     *
+     * <p>It can be used in a receiver registered by
+     * {@link Context#registerReceiverForAllUsers Context.registerReceiverForAllUsers()}
+     * to determine on which user the broadcast was sent.
+     *
+     * @hide
+     */
+    @SystemApi
+    public final @NonNull UserHandle getSendingUser() {
+        return UserHandle.of(getSendingUserId());
+    }
+
+    /** @hide */
+    public int getSendingUserId() {
+        return mPendingResult.mSendingUser;
+    }
+
+    /**
+     * Control inclusion of debugging help for mismatched
+     * calls to {@link Context#registerReceiver(BroadcastReceiver, IntentFilter)
+     * Context.registerReceiver()}.
+     * If called with true, before given to registerReceiver(), then the
+     * callstack of the following {@link Context#unregisterReceiver(BroadcastReceiver)
+     * Context.unregisterReceiver()} call is retained, to be printed if a later
+     * incorrect unregister call is made.  Note that doing this requires retaining
+     * information about the BroadcastReceiver for the lifetime of the app,
+     * resulting in a leak -- this should only be used for debugging.
+     */
+    public final void setDebugUnregister(boolean debug) {
+        mDebugUnregister = debug;
+    }
+
+    /**
+     * Return the last value given to {@link #setDebugUnregister}.
+     */
+    public final boolean getDebugUnregister() {
+        return mDebugUnregister;
+    }
+
+    void checkSynchronousHint() {
+        if (mPendingResult == null) {
+            throw new IllegalStateException("Call while result is not pending");
+        }
+
+        // Note that we don't assert when receiving the initial sticky value,
+        // since that may have come from an ordered broadcast.  We'll catch
+        // them later when the real broadcast happens again.
+        if (mPendingResult.mOrderedHint || mPendingResult.mInitialStickyHint) {
+            return;
+        }
+        RuntimeException e = new RuntimeException(
+                "BroadcastReceiver trying to return result during a non-ordered broadcast");
+        e.fillInStackTrace();
+        Log.e("BroadcastReceiver", e.getMessage(), e);
+    }
+}
+
diff --git a/android/content/ClipData.java b/android/content/ClipData.java
new file mode 100644
index 0000000..2342165
--- /dev/null
+++ b/android/content/ClipData.java
@@ -0,0 +1,1178 @@
+/**
+ * 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.content;
+
+import static android.content.ContentProvider.maybeAddUserId;
+import static android.content.ContentResolver.SCHEME_ANDROID_RESOURCE;
+import static android.content.ContentResolver.SCHEME_CONTENT;
+import static android.content.ContentResolver.SCHEME_FILE;
+
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.res.AssetFileDescriptor;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.StrictMode;
+import android.text.Html;
+import android.text.Spannable;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
+import android.text.TextUtils;
+import android.text.style.URLSpan;
+import android.util.Log;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.util.ArrayUtils;
+
+import libcore.io.IoUtils;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Representation of a clipped data on the clipboard.
+ *
+ * <p>ClipData is a complex type containing one or more Item instances,
+ * each of which can hold one or more representations of an item of data.
+ * For display to the user, it also has a label.</p>
+ *
+ * <p>A ClipData contains a {@link ClipDescription}, which describes
+ * important meta-data about the clip.  In particular, its
+ * {@link ClipDescription#getMimeType(int) getDescription().getMimeType(int)}
+ * must return correct MIME type(s) describing the data in the clip.  For help
+ * in correctly constructing a clip with the correct MIME type, use
+ * {@link #newPlainText(CharSequence, CharSequence)},
+ * {@link #newUri(ContentResolver, CharSequence, Uri)}, and
+ * {@link #newIntent(CharSequence, Intent)}.
+ *
+ * <p>Each Item instance can be one of three main classes of data: a simple
+ * CharSequence of text, a single Intent object, or a Uri.  See {@link Item}
+ * for more details.
+ *
+ * <div class="special reference">
+ * <h3>Developer Guides</h3>
+ * <p>For more information about using the clipboard framework, read the
+ * <a href="{@docRoot}guide/topics/clipboard/copy-paste.html">Copy and Paste</a>
+ * developer guide.</p>
+ * </div>
+ *
+ * <a name="ImplementingPaste"></a>
+ * <h3>Implementing Paste or Drop</h3>
+ *
+ * <p>To implement a paste or drop of a ClipData object into an application,
+ * the application must correctly interpret the data for its use.  If the {@link Item}
+ * it contains is simple text or an Intent, there is little to be done: text
+ * can only be interpreted as text, and an Intent will typically be used for
+ * creating shortcuts (such as placing icons on the home screen) or other
+ * actions.
+ *
+ * <p>If all you want is the textual representation of the clipped data, you
+ * can use the convenience method {@link Item#coerceToText Item.coerceToText}.
+ * In this case there is generally no need to worry about the MIME types
+ * reported by {@link ClipDescription#getMimeType(int) getDescription().getMimeType(int)},
+ * since any clip item can always be converted to a string.
+ *
+ * <p>More complicated exchanges will be done through URIs, in particular
+ * "content:" URIs.  A content URI allows the recipient of a ClipData item
+ * to interact closely with the ContentProvider holding the data in order to
+ * negotiate the transfer of that data.  The clip must also be filled in with
+ * the available MIME types; {@link #newUri(ContentResolver, CharSequence, Uri)}
+ * will take care of correctly doing this.
+ *
+ * <p>For example, here is the paste function of a simple NotePad application.
+ * When retrieving the data from the clipboard, it can do either two things:
+ * if the clipboard contains a URI reference to an existing note, it copies
+ * the entire structure of the note into a new note; otherwise, it simply
+ * coerces the clip into text and uses that as the new note's contents.
+ *
+ * {@sample development/samples/NotePad/src/com/example/android/notepad/NoteEditor.java
+ *      paste}
+ *
+ * <p>In many cases an application can paste various types of streams of data.  For
+ * example, an e-mail application may want to allow the user to paste an image
+ * or other binary data as an attachment.  This is accomplished through the
+ * ContentResolver {@link ContentResolver#getStreamTypes(Uri, String)} and
+ * {@link ContentResolver#openTypedAssetFileDescriptor(Uri, String, android.os.Bundle)}
+ * methods.  These allow a client to discover the type(s) of data that a particular
+ * content URI can make available as a stream and retrieve the stream of data.
+ *
+ * <p>For example, the implementation of {@link Item#coerceToText Item.coerceToText}
+ * itself uses this to try to retrieve a URI clip as a stream of text:
+ *
+ * {@sample frameworks/base/core/java/android/content/ClipData.java coerceToText}
+ *
+ * <a name="ImplementingCopy"></a>
+ * <h3>Implementing Copy or Drag</h3>
+ *
+ * <p>To be the source of a clip, the application must construct a ClipData
+ * object that any recipient can interpret best for their context.  If the clip
+ * is to contain a simple text, Intent, or URI, this is easy: an {@link Item}
+ * containing the appropriate data type can be constructed and used.
+ *
+ * <p>More complicated data types require the implementation of support in
+ * a ContentProvider for describing and generating the data for the recipient.
+ * A common scenario is one where an application places on the clipboard the
+ * content: URI of an object that the user has copied, with the data at that
+ * URI consisting of a complicated structure that only other applications with
+ * direct knowledge of the structure can use.
+ *
+ * <p>For applications that do not have intrinsic knowledge of the data structure,
+ * the content provider holding it can make the data available as an arbitrary
+ * number of types of data streams.  This is done by implementing the
+ * ContentProvider {@link ContentProvider#getStreamTypes(Uri, String)} and
+ * {@link ContentProvider#openTypedAssetFile(Uri, String, android.os.Bundle)}
+ * methods.
+ *
+ * <p>Going back to our simple NotePad application, this is the implementation
+ * it may have to convert a single note URI (consisting of a title and the note
+ * text) into a stream of plain text data.
+ *
+ * {@sample development/samples/NotePad/src/com/example/android/notepad/NotePadProvider.java
+ *      stream}
+ *
+ * <p>The copy operation in our NotePad application is now just a simple matter
+ * of making a clip containing the URI of the note being copied:
+ *
+ * {@sample development/samples/NotePad/src/com/example/android/notepad/NotesList.java
+ *      copy}
+ *
+ * <p>Note if a paste operation needs this clip as text (for example to paste
+ * into an editor), then {@link Item#coerceToText(Context)} will ask the content
+ * provider for the clip URI as text and successfully paste the entire note.
+ */
+public class ClipData implements Parcelable {
+    static final String[] MIMETYPES_TEXT_PLAIN = new String[] {
+        ClipDescription.MIMETYPE_TEXT_PLAIN };
+    static final String[] MIMETYPES_TEXT_HTML = new String[] {
+        ClipDescription.MIMETYPE_TEXT_HTML };
+    static final String[] MIMETYPES_TEXT_URILIST = new String[] {
+        ClipDescription.MIMETYPE_TEXT_URILIST };
+    static final String[] MIMETYPES_TEXT_INTENT = new String[] {
+        ClipDescription.MIMETYPE_TEXT_INTENT };
+
+    final ClipDescription mClipDescription;
+
+    final Bitmap mIcon;
+
+    final ArrayList<Item> mItems;
+
+    /**
+     * Description of a single item in a ClipData.
+     *
+     * <p>The types than an individual item can currently contain are:</p>
+     *
+     * <ul>
+     * <li> Text: a basic string of text.  This is actually a CharSequence,
+     * so it can be formatted text supported by corresponding Android built-in
+     * style spans.  (Custom application spans are not supported and will be
+     * stripped when transporting through the clipboard.)
+     * <li> Intent: an arbitrary Intent object.  A typical use is the shortcut
+     * to create when pasting a clipped item on to the home screen.
+     * <li> Uri: a URI reference.  This may be any URI (such as an http: URI
+     * representing a bookmark), however it is often a content: URI.  Using
+     * content provider references as clips like this allows an application to
+     * share complex or large clips through the standard content provider
+     * facilities.
+     * </ul>
+     */
+    public static class Item {
+        final CharSequence mText;
+        final String mHtmlText;
+        final Intent mIntent;
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+        Uri mUri;
+
+        /** @hide */
+        public Item(Item other) {
+            mText = other.mText;
+            mHtmlText = other.mHtmlText;
+            mIntent = other.mIntent;
+            mUri = other.mUri;
+        }
+
+        /**
+         * Create an Item consisting of a single block of (possibly styled) text.
+         */
+        public Item(CharSequence text) {
+            mText = text;
+            mHtmlText = null;
+            mIntent = null;
+            mUri = null;
+        }
+
+        /**
+         * Create an Item consisting of a single block of (possibly styled) text,
+         * with an alternative HTML formatted representation.  You <em>must</em>
+         * supply a plain text representation in addition to HTML text; coercion
+         * will not be done from HTML formatted text into plain text.
+         * <p><strong>Warning:</strong> Use content: URI for sharing large clip data.
+         * ClipData.Item doesn't accept an HTML text if it's larger than 800KB.
+         * </p>
+         */
+        public Item(CharSequence text, String htmlText) {
+            mText = text;
+            mHtmlText = htmlText;
+            mIntent = null;
+            mUri = null;
+        }
+
+        /**
+         * Create an Item consisting of an arbitrary Intent.
+         */
+        public Item(Intent intent) {
+            mText = null;
+            mHtmlText = null;
+            mIntent = intent;
+            mUri = null;
+        }
+
+        /**
+         * Create an Item consisting of an arbitrary URI.
+         */
+        public Item(Uri uri) {
+            mText = null;
+            mHtmlText = null;
+            mIntent = null;
+            mUri = uri;
+        }
+
+        /**
+         * Create a complex Item, containing multiple representations of
+         * text, Intent, and/or URI.
+         */
+        public Item(CharSequence text, Intent intent, Uri uri) {
+            mText = text;
+            mHtmlText = null;
+            mIntent = intent;
+            mUri = uri;
+        }
+
+        /**
+         * Create a complex Item, containing multiple representations of
+         * text, HTML text, Intent, and/or URI.  If providing HTML text, you
+         * <em>must</em> supply a plain text representation as well; coercion
+         * will not be done from HTML formatted text into plain text.
+         */
+        public Item(CharSequence text, String htmlText, Intent intent, Uri uri) {
+            if (htmlText != null && text == null) {
+                throw new IllegalArgumentException(
+                        "Plain text must be supplied if HTML text is supplied");
+            }
+            mText = text;
+            mHtmlText = htmlText;
+            mIntent = intent;
+            mUri = uri;
+        }
+
+        /**
+         * Retrieve the raw text contained in this Item.
+         */
+        public CharSequence getText() {
+            return mText;
+        }
+
+        /**
+         * Retrieve the raw HTML text contained in this Item.
+         */
+        public String getHtmlText() {
+            return mHtmlText;
+        }
+
+        /**
+         * Retrieve the raw Intent contained in this Item.
+         */
+        public Intent getIntent() {
+            return mIntent;
+        }
+
+        /**
+         * Retrieve the raw URI contained in this Item.
+         */
+        public Uri getUri() {
+            return mUri;
+        }
+
+        /**
+         * Turn this item into text, regardless of the type of data it
+         * actually contains.
+         *
+         * <p>The algorithm for deciding what text to return is:
+         * <ul>
+         * <li> If {@link #getText} is non-null, return that.
+         * <li> If {@link #getUri} is non-null, try to retrieve its data
+         * as a text stream from its content provider.  If this succeeds, copy
+         * the text into a String and return it.  If it is not a content: URI or
+         * the content provider does not supply a text representation, return
+         * the raw URI as a string.
+         * <li> If {@link #getIntent} is non-null, convert that to an intent:
+         * URI and return it.
+         * <li> Otherwise, return an empty string.
+         * </ul>
+         *
+         * @param context The caller's Context, from which its ContentResolver
+         * and other things can be retrieved.
+         * @return Returns the item's textual representation.
+         */
+//BEGIN_INCLUDE(coerceToText)
+        public CharSequence coerceToText(Context context) {
+            // If this Item has an explicit textual value, simply return that.
+            CharSequence text = getText();
+            if (text != null) {
+                return text;
+            }
+
+            // If this Item has a URI value, try using that.
+            Uri uri = getUri();
+            if (uri != null) {
+                // First see if the URI can be opened as a plain text stream
+                // (of any sub-type).  If so, this is the best textual
+                // representation for it.
+                final ContentResolver resolver = context.getContentResolver();
+                AssetFileDescriptor descr = null;
+                FileInputStream stream = null;
+                InputStreamReader reader = null;
+                try {
+                    try {
+                        // Ask for a stream of the desired type.
+                        descr = resolver.openTypedAssetFileDescriptor(uri, "text/*", null);
+                    } catch (SecurityException e) {
+                        Log.w("ClipData", "Failure opening stream", e);
+                    } catch (FileNotFoundException|RuntimeException e) {
+                        // Unable to open content URI as text...  not really an
+                        // error, just something to ignore.
+                    }
+                    if (descr != null) {
+                        try {
+                            stream = descr.createInputStream();
+                            reader = new InputStreamReader(stream, "UTF-8");
+
+                            // Got it...  copy the stream into a local string and return it.
+                            final StringBuilder builder = new StringBuilder(128);
+                            char[] buffer = new char[8192];
+                            int len;
+                            while ((len=reader.read(buffer)) > 0) {
+                                builder.append(buffer, 0, len);
+                            }
+                            return builder.toString();
+                        } catch (IOException e) {
+                            // Something bad has happened.
+                            Log.w("ClipData", "Failure loading text", e);
+                            return e.toString();
+                        }
+                    }
+                } finally {
+                    IoUtils.closeQuietly(descr);
+                    IoUtils.closeQuietly(stream);
+                    IoUtils.closeQuietly(reader);
+                }
+
+                // If we couldn't open the URI as a stream, use the URI itself as a textual
+                // representation (but not for "content", "android.resource" or "file" schemes).
+                final String scheme = uri.getScheme();
+                if (SCHEME_CONTENT.equals(scheme)
+                        || SCHEME_ANDROID_RESOURCE.equals(scheme)
+                        || SCHEME_FILE.equals(scheme)) {
+                    return "";
+                }
+                return uri.toString();
+            }
+
+            // Finally, if all we have is an Intent, then we can just turn that
+            // into text.  Not the most user-friendly thing, but it's something.
+            Intent intent = getIntent();
+            if (intent != null) {
+                return intent.toUri(Intent.URI_INTENT_SCHEME);
+            }
+
+            // Shouldn't get here, but just in case...
+            return "";
+        }
+//END_INCLUDE(coerceToText)
+
+        /**
+         * Like {@link #coerceToHtmlText(Context)}, but any text that would
+         * be returned as HTML formatting will be returned as text with
+         * style spans.
+         * @param context The caller's Context, from which its ContentResolver
+         * and other things can be retrieved.
+         * @return Returns the item's textual representation.
+         */
+        public CharSequence coerceToStyledText(Context context) {
+            CharSequence text = getText();
+            if (text instanceof Spanned) {
+                return text;
+            }
+            String htmlText = getHtmlText();
+            if (htmlText != null) {
+                try {
+                    CharSequence newText = Html.fromHtml(htmlText);
+                    if (newText != null) {
+                        return newText;
+                    }
+                } catch (RuntimeException e) {
+                    // If anything bad happens, we'll fall back on the plain text.
+                }
+            }
+
+            if (text != null) {
+                return text;
+            }
+            return coerceToHtmlOrStyledText(context, true);
+        }
+
+        /**
+         * Turn this item into HTML text, regardless of the type of data it
+         * actually contains.
+         *
+         * <p>The algorithm for deciding what text to return is:
+         * <ul>
+         * <li> If {@link #getHtmlText} is non-null, return that.
+         * <li> If {@link #getText} is non-null, return that, converting to
+         * valid HTML text.  If this text contains style spans,
+         * {@link Html#toHtml(Spanned) Html.toHtml(Spanned)} is used to
+         * convert them to HTML formatting.
+         * <li> If {@link #getUri} is non-null, try to retrieve its data
+         * as a text stream from its content provider.  If the provider can
+         * supply text/html data, that will be preferred and returned as-is.
+         * Otherwise, any text/* data will be returned and escaped to HTML.
+         * If it is not a content: URI or the content provider does not supply
+         * a text representation, HTML text containing a link to the URI
+         * will be returned.
+         * <li> If {@link #getIntent} is non-null, convert that to an intent:
+         * URI and return as an HTML link.
+         * <li> Otherwise, return an empty string.
+         * </ul>
+         *
+         * @param context The caller's Context, from which its ContentResolver
+         * and other things can be retrieved.
+         * @return Returns the item's representation as HTML text.
+         */
+        public String coerceToHtmlText(Context context) {
+            // If the item has an explicit HTML value, simply return that.
+            String htmlText = getHtmlText();
+            if (htmlText != null) {
+                return htmlText;
+            }
+
+            // If this Item has a plain text value, return it as HTML.
+            CharSequence text = getText();
+            if (text != null) {
+                if (text instanceof Spanned) {
+                    return Html.toHtml((Spanned)text);
+                }
+                return Html.escapeHtml(text);
+            }
+
+            text = coerceToHtmlOrStyledText(context, false);
+            return text != null ? text.toString() : null;
+        }
+
+        private CharSequence coerceToHtmlOrStyledText(Context context, boolean styled) {
+            // If this Item has a URI value, try using that.
+            if (mUri != null) {
+
+                // Check to see what data representations the content
+                // provider supports.  We would like HTML text, but if that
+                // is not possible we'll live with plan text.
+                String[] types = null;
+                try {
+                    types = context.getContentResolver().getStreamTypes(mUri, "text/*");
+                } catch (SecurityException e) {
+                    // No read permission for mUri, assume empty stream types list.
+                }
+                boolean hasHtml = false;
+                boolean hasText = false;
+                if (types != null) {
+                    for (String type : types) {
+                        if ("text/html".equals(type)) {
+                            hasHtml = true;
+                        } else if (type.startsWith("text/")) {
+                            hasText = true;
+                        }
+                    }
+                }
+
+                // If the provider can serve data we can use, open and load it.
+                if (hasHtml || hasText) {
+                    FileInputStream stream = null;
+                    try {
+                        // Ask for a stream of the desired type.
+                        AssetFileDescriptor descr = context.getContentResolver()
+                                .openTypedAssetFileDescriptor(mUri,
+                                        hasHtml ? "text/html" : "text/plain", null);
+                        stream = descr.createInputStream();
+                        InputStreamReader reader = new InputStreamReader(stream, "UTF-8");
+
+                        // Got it...  copy the stream into a local string and return it.
+                        StringBuilder builder = new StringBuilder(128);
+                        char[] buffer = new char[8192];
+                        int len;
+                        while ((len=reader.read(buffer)) > 0) {
+                            builder.append(buffer, 0, len);
+                        }
+                        String text = builder.toString();
+                        if (hasHtml) {
+                            if (styled) {
+                                // We loaded HTML formatted text and the caller
+                                // want styled text, convert it.
+                                try {
+                                    CharSequence newText = Html.fromHtml(text);
+                                    return newText != null ? newText : text;
+                                } catch (RuntimeException e) {
+                                    return text;
+                                }
+                            } else {
+                                // We loaded HTML formatted text and that is what
+                                // the caller wants, just return it.
+                                return text.toString();
+                            }
+                        }
+                        if (styled) {
+                            // We loaded plain text and the caller wants styled
+                            // text, that is all we have so return it.
+                            return text;
+                        } else {
+                            // We loaded plain text and the caller wants HTML
+                            // text, escape it for HTML.
+                            return Html.escapeHtml(text);
+                        }
+
+                    } catch (SecurityException e) {
+                        Log.w("ClipData", "Failure opening stream", e);
+
+                    } catch (FileNotFoundException e) {
+                        // Unable to open content URI as text...  not really an
+                        // error, just something to ignore.
+
+                    } catch (IOException e) {
+                        // Something bad has happened.
+                        Log.w("ClipData", "Failure loading text", e);
+                        return Html.escapeHtml(e.toString());
+
+                    } finally {
+                        if (stream != null) {
+                            try {
+                                stream.close();
+                            } catch (IOException e) {
+                            }
+                        }
+                    }
+                }
+
+                // If we couldn't open the URI as a stream, use the URI itself as a textual
+                // representation (but not for "content", "android.resource" or "file" schemes).
+                final String scheme = mUri.getScheme();
+                if (SCHEME_CONTENT.equals(scheme)
+                        || SCHEME_ANDROID_RESOURCE.equals(scheme)
+                        || SCHEME_FILE.equals(scheme)) {
+                    return "";
+                }
+
+                if (styled) {
+                    return uriToStyledText(mUri.toString());
+                } else {
+                    return uriToHtml(mUri.toString());
+                }
+            }
+
+            // Finally, if all we have is an Intent, then we can just turn that
+            // into text.  Not the most user-friendly thing, but it's something.
+            if (mIntent != null) {
+                if (styled) {
+                    return uriToStyledText(mIntent.toUri(Intent.URI_INTENT_SCHEME));
+                } else {
+                    return uriToHtml(mIntent.toUri(Intent.URI_INTENT_SCHEME));
+                }
+            }
+
+            // Shouldn't get here, but just in case...
+            return "";
+        }
+
+        private String uriToHtml(String uri) {
+            StringBuilder builder = new StringBuilder(256);
+            builder.append("<a href=\"");
+            builder.append(Html.escapeHtml(uri));
+            builder.append("\">");
+            builder.append(Html.escapeHtml(uri));
+            builder.append("</a>");
+            return builder.toString();
+        }
+
+        private CharSequence uriToStyledText(String uri) {
+            SpannableStringBuilder builder = new SpannableStringBuilder();
+            builder.append(uri);
+            builder.setSpan(new URLSpan(uri), 0, builder.length(),
+                    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+            return builder;
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder b = new StringBuilder(128);
+
+            b.append("ClipData.Item { ");
+            toShortString(b);
+            b.append(" }");
+
+            return b.toString();
+        }
+
+        /** @hide */
+        public void toShortString(StringBuilder b) {
+            if (mHtmlText != null) {
+                b.append("H:");
+                b.append(mHtmlText);
+            } else if (mText != null) {
+                b.append("T:");
+                b.append(mText);
+            } else if (mUri != null) {
+                b.append("U:");
+                b.append(mUri);
+            } else if (mIntent != null) {
+                b.append("I:");
+                mIntent.toShortString(b, true, true, true, true);
+            } else {
+                b.append("NULL");
+            }
+        }
+
+        /** @hide */
+        public void toShortSummaryString(StringBuilder b) {
+            if (mHtmlText != null) {
+                b.append("HTML");
+            } else if (mText != null) {
+                b.append("TEXT");
+            } else if (mUri != null) {
+                b.append("U:");
+                b.append(mUri);
+            } else if (mIntent != null) {
+                b.append("I:");
+                mIntent.toShortString(b, true, true, true, true);
+            } else {
+                b.append("NULL");
+            }
+        }
+
+        /** @hide */
+        public void dumpDebug(ProtoOutputStream proto, long fieldId) {
+            final long token = proto.start(fieldId);
+
+            if (mHtmlText != null) {
+                proto.write(ClipDataProto.Item.HTML_TEXT, mHtmlText);
+            } else if (mText != null) {
+                proto.write(ClipDataProto.Item.TEXT, mText.toString());
+            } else if (mUri != null) {
+                proto.write(ClipDataProto.Item.URI, mUri.toString());
+            } else if (mIntent != null) {
+                mIntent.dumpDebug(proto, ClipDataProto.Item.INTENT, true, true, true, true);
+            } else {
+                proto.write(ClipDataProto.Item.NOTHING, true);
+            }
+
+            proto.end(token);
+        }
+    }
+
+    /**
+     * Create a new clip.
+     *
+     * @param label Label to show to the user describing this clip.
+     * @param mimeTypes An array of MIME types this data is available as.
+     * @param item The contents of the first item in the clip.
+     */
+    public ClipData(CharSequence label, String[] mimeTypes, Item item) {
+        mClipDescription = new ClipDescription(label, mimeTypes);
+        if (item == null) {
+            throw new NullPointerException("item is null");
+        }
+        mIcon = null;
+        mItems = new ArrayList<Item>();
+        mItems.add(item);
+    }
+
+    /**
+     * Create a new clip.
+     *
+     * @param description The ClipDescription describing the clip contents.
+     * @param item The contents of the first item in the clip.
+     */
+    public ClipData(ClipDescription description, Item item) {
+        mClipDescription = description;
+        if (item == null) {
+            throw new NullPointerException("item is null");
+        }
+        mIcon = null;
+        mItems = new ArrayList<Item>();
+        mItems.add(item);
+    }
+
+    /**
+     * Create a new clip.
+     *
+     * @param description The ClipDescription describing the clip contents.
+     * @param items The items in the clip. Not that a defensive copy of this
+     *     list is not made, so caution should be taken to ensure the
+     *     list is not available for further modification.
+     * @hide
+     */
+    public ClipData(ClipDescription description, ArrayList<Item> items) {
+        mClipDescription = description;
+        if (items == null) {
+            throw new NullPointerException("item is null");
+        }
+        mIcon = null;
+        mItems = items;
+    }
+
+    /**
+     * Create a new clip that is a copy of another clip.  This does a deep-copy
+     * of all items in the clip.
+     *
+     * @param other The existing ClipData that is to be copied.
+     */
+    public ClipData(ClipData other) {
+        mClipDescription = other.mClipDescription;
+        mIcon = other.mIcon;
+        mItems = new ArrayList<Item>(other.mItems);
+    }
+
+    /**
+     * Create a new ClipData holding data of the type
+     * {@link ClipDescription#MIMETYPE_TEXT_PLAIN}.
+     *
+     * @param label User-visible label for the clip data.
+     * @param text The actual text in the clip.
+     * @return Returns a new ClipData containing the specified data.
+     */
+    static public ClipData newPlainText(CharSequence label, CharSequence text) {
+        Item item = new Item(text);
+        return new ClipData(label, MIMETYPES_TEXT_PLAIN, item);
+    }
+
+    /**
+     * Create a new ClipData holding data of the type
+     * {@link ClipDescription#MIMETYPE_TEXT_HTML}.
+     *
+     * @param label User-visible label for the clip data.
+     * @param text The text of clip as plain text, for receivers that don't
+     * handle HTML.  This is required.
+     * @param htmlText The actual HTML text in the clip.
+     * @return Returns a new ClipData containing the specified data.
+     */
+    static public ClipData newHtmlText(CharSequence label, CharSequence text,
+            String htmlText) {
+        Item item = new Item(text, htmlText);
+        return new ClipData(label, MIMETYPES_TEXT_HTML, item);
+    }
+
+    /**
+     * Create a new ClipData holding an Intent with MIME type
+     * {@link ClipDescription#MIMETYPE_TEXT_INTENT}.
+     *
+     * @param label User-visible label for the clip data.
+     * @param intent The actual Intent in the clip.
+     * @return Returns a new ClipData containing the specified data.
+     */
+    static public ClipData newIntent(CharSequence label, Intent intent) {
+        Item item = new Item(intent);
+        return new ClipData(label, MIMETYPES_TEXT_INTENT, item);
+    }
+
+    /**
+     * Create a new ClipData holding a URI.  If the URI is a content: URI,
+     * this will query the content provider for the MIME type of its data and
+     * use that as the MIME type.  Otherwise, it will use the MIME type
+     * {@link ClipDescription#MIMETYPE_TEXT_URILIST}.
+     *
+     * @param resolver ContentResolver used to get information about the URI.
+     * @param label User-visible label for the clip data.
+     * @param uri The URI in the clip.
+     * @return Returns a new ClipData containing the specified data.
+     */
+    static public ClipData newUri(ContentResolver resolver, CharSequence label,
+            Uri uri) {
+        Item item = new Item(uri);
+        String[] mimeTypes = getMimeTypes(resolver, uri);
+        return new ClipData(label, mimeTypes, item);
+    }
+
+    /**
+     * Finds all applicable MIME types for a given URI.
+     *
+     * @param resolver ContentResolver used to get information about the URI.
+     * @param uri The URI.
+     * @return Returns an array of MIME types.
+     */
+    private static String[] getMimeTypes(ContentResolver resolver, Uri uri) {
+        String[] mimeTypes = null;
+        if (SCHEME_CONTENT.equals(uri.getScheme())) {
+            String realType = resolver.getType(uri);
+            mimeTypes = resolver.getStreamTypes(uri, "*/*");
+            if (realType != null) {
+                if (mimeTypes == null) {
+                    mimeTypes = new String[] { realType };
+                } else if (!ArrayUtils.contains(mimeTypes, realType)) {
+                    String[] tmp = new String[mimeTypes.length + 1];
+                    tmp[0] = realType;
+                    System.arraycopy(mimeTypes, 0, tmp, 1, mimeTypes.length);
+                    mimeTypes = tmp;
+                }
+            }
+        }
+        if (mimeTypes == null) {
+            mimeTypes = MIMETYPES_TEXT_URILIST;
+        }
+        return mimeTypes;
+    }
+
+    /**
+     * Create a new ClipData holding an URI with MIME type
+     * {@link ClipDescription#MIMETYPE_TEXT_URILIST}.
+     * Unlike {@link #newUri(ContentResolver, CharSequence, Uri)}, nothing
+     * is inferred about the URI -- if it is a content: URI holding a bitmap,
+     * the reported type will still be uri-list.  Use this with care!
+     *
+     * @param label User-visible label for the clip data.
+     * @param uri The URI in the clip.
+     * @return Returns a new ClipData containing the specified data.
+     */
+    static public ClipData newRawUri(CharSequence label, Uri uri) {
+        Item item = new Item(uri);
+        return new ClipData(label, MIMETYPES_TEXT_URILIST, item);
+    }
+
+    /**
+     * Return the {@link ClipDescription} associated with this data, describing
+     * what it contains.
+     */
+    public ClipDescription getDescription() {
+        return mClipDescription;
+    }
+
+    /**
+     * Add a new Item to the overall ClipData container.
+     * <p> This method will <em>not</em> update the list of available MIME types in the
+     * {@link ClipDescription}. It should be used only when adding items which do not add new
+     * MIME types to this clip. If this is not the case, use {@link #addItem(ContentResolver, Item)}
+     * or call {@link #ClipData(CharSequence, String[], Item)} with a complete list of MIME types.
+     * @param item Item to be added.
+     */
+    public void addItem(Item item) {
+        if (item == null) {
+            throw new NullPointerException("item is null");
+        }
+        mItems.add(item);
+    }
+
+    /**
+     * Add a new Item to the overall ClipData container.
+     * <p> Unlike {@link #addItem(Item)}, this method will update the list of available MIME types
+     * in the {@link ClipDescription}.
+     * @param resolver ContentResolver used to get information about the URI possibly contained in
+     * the item.
+     * @param item Item to be added.
+     */
+    public void addItem(ContentResolver resolver, Item item) {
+        addItem(item);
+
+        if (item.getHtmlText() != null) {
+            mClipDescription.addMimeTypes(MIMETYPES_TEXT_HTML);
+        } else if (item.getText() != null) {
+            mClipDescription.addMimeTypes(MIMETYPES_TEXT_PLAIN);
+        }
+
+        if (item.getIntent() != null) {
+            mClipDescription.addMimeTypes(MIMETYPES_TEXT_INTENT);
+        }
+
+        if (item.getUri() != null) {
+            mClipDescription.addMimeTypes(getMimeTypes(resolver, item.getUri()));
+        }
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage
+    public Bitmap getIcon() {
+        return mIcon;
+    }
+
+    /**
+     * Return the number of items in the clip data.
+     */
+    public int getItemCount() {
+        return mItems.size();
+    }
+
+    /**
+     * Return a single item inside of the clip data.  The index can range
+     * from 0 to {@link #getItemCount()}-1.
+     */
+    public Item getItemAt(int index) {
+        return mItems.get(index);
+    }
+
+    /** @hide */
+    public void setItemAt(int index, Item item) {
+        mItems.set(index, item);
+    }
+
+    /**
+     * Prepare this {@link ClipData} to leave an app process.
+     *
+     * @hide
+     */
+    public void prepareToLeaveProcess(boolean leavingPackage) {
+        // Assume that callers are going to be granting permissions
+        prepareToLeaveProcess(leavingPackage, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+    }
+
+    /**
+     * Prepare this {@link ClipData} to leave an app process.
+     *
+     * @hide
+     */
+    public void prepareToLeaveProcess(boolean leavingPackage, int intentFlags) {
+        final int size = mItems.size();
+        for (int i = 0; i < size; i++) {
+            final Item item = mItems.get(i);
+            if (item.mIntent != null) {
+                item.mIntent.prepareToLeaveProcess(leavingPackage);
+            }
+            if (item.mUri != null && leavingPackage) {
+                if (StrictMode.vmFileUriExposureEnabled()) {
+                    item.mUri.checkFileUriExposed("ClipData.Item.getUri()");
+                }
+                if (StrictMode.vmContentUriWithoutPermissionEnabled()) {
+                    item.mUri.checkContentUriWithoutPermission("ClipData.Item.getUri()",
+                            intentFlags);
+                }
+            }
+        }
+    }
+
+    /** {@hide} */
+    public void prepareToEnterProcess() {
+        final int size = mItems.size();
+        for (int i = 0; i < size; i++) {
+            final Item item = mItems.get(i);
+            if (item.mIntent != null) {
+                item.mIntent.prepareToEnterProcess();
+            }
+        }
+    }
+
+    /** @hide */
+    public void fixUris(int contentUserHint) {
+        final int size = mItems.size();
+        for (int i = 0; i < size; i++) {
+            final Item item = mItems.get(i);
+            if (item.mIntent != null) {
+                item.mIntent.fixUris(contentUserHint);
+            }
+            if (item.mUri != null) {
+                item.mUri = maybeAddUserId(item.mUri, contentUserHint);
+            }
+        }
+    }
+
+    /**
+     * Only fixing the data field of the intents
+     * @hide
+     */
+    public void fixUrisLight(int contentUserHint) {
+        final int size = mItems.size();
+        for (int i = 0; i < size; i++) {
+            final Item item = mItems.get(i);
+            if (item.mIntent != null) {
+                Uri data = item.mIntent.getData();
+                if (data != null) {
+                    item.mIntent.setData(maybeAddUserId(data, contentUserHint));
+                }
+            }
+            if (item.mUri != null) {
+                item.mUri = maybeAddUserId(item.mUri, contentUserHint);
+            }
+        }
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder b = new StringBuilder(128);
+
+        b.append("ClipData { ");
+        toShortString(b);
+        b.append(" }");
+
+        return b.toString();
+    }
+
+    /** @hide */
+    public void toShortString(StringBuilder b) {
+        boolean first;
+        if (mClipDescription != null) {
+            first = !mClipDescription.toShortString(b);
+        } else {
+            first = true;
+        }
+        if (mIcon != null) {
+            if (!first) {
+                b.append(' ');
+            }
+            first = false;
+            b.append("I:");
+            b.append(mIcon.getWidth());
+            b.append('x');
+            b.append(mIcon.getHeight());
+        }
+        for (int i=0; i<mItems.size(); i++) {
+            if (!first) {
+                b.append(' ');
+            }
+            first = false;
+            b.append('{');
+            mItems.get(i).toShortString(b);
+            b.append('}');
+        }
+    }
+
+    /** @hide */
+    public void toShortStringShortItems(StringBuilder b, boolean first) {
+        if (mItems.size() > 0) {
+            if (!first) {
+                b.append(' ');
+            }
+            for (int i=0; i<mItems.size(); i++) {
+                b.append("{...}");
+            }
+        }
+    }
+
+    /** @hide */
+    public void dumpDebug(ProtoOutputStream proto, long fieldId) {
+        final long token = proto.start(fieldId);
+
+        if (mClipDescription != null) {
+            mClipDescription.dumpDebug(proto, ClipDataProto.DESCRIPTION);
+        }
+        if (mIcon != null) {
+            final long iToken = proto.start(ClipDataProto.ICON);
+            proto.write(ClipDataProto.Icon.WIDTH, mIcon.getWidth());
+            proto.write(ClipDataProto.Icon.HEIGHT, mIcon.getHeight());
+            proto.end(iToken);
+        }
+        for (int i = 0; i < mItems.size(); i++) {
+            mItems.get(i).dumpDebug(proto, ClipDataProto.ITEMS);
+        }
+
+        proto.end(token);
+    }
+
+    /** @hide */
+    public void collectUris(List<Uri> out) {
+        for (int i = 0; i < mItems.size(); ++i) {
+            ClipData.Item item = getItemAt(i);
+
+            if (item.getUri() != null) {
+                out.add(item.getUri());
+            }
+
+            Intent intent = item.getIntent();
+            if (intent != null) {
+                if (intent.getData() != null) {
+                    out.add(intent.getData());
+                }
+                if (intent.getClipData() != null) {
+                    intent.getClipData().collectUris(out);
+                }
+            }
+        }
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        mClipDescription.writeToParcel(dest, flags);
+        if (mIcon != null) {
+            dest.writeInt(1);
+            mIcon.writeToParcel(dest, flags);
+        } else {
+            dest.writeInt(0);
+        }
+        final int N = mItems.size();
+        dest.writeInt(N);
+        for (int i=0; i<N; i++) {
+            Item item = mItems.get(i);
+            TextUtils.writeToParcel(item.mText, dest, flags);
+            dest.writeString8(item.mHtmlText);
+            if (item.mIntent != null) {
+                dest.writeInt(1);
+                item.mIntent.writeToParcel(dest, flags);
+            } else {
+                dest.writeInt(0);
+            }
+            if (item.mUri != null) {
+                dest.writeInt(1);
+                item.mUri.writeToParcel(dest, flags);
+            } else {
+                dest.writeInt(0);
+            }
+        }
+    }
+
+    ClipData(Parcel in) {
+        mClipDescription = new ClipDescription(in);
+        if (in.readInt() != 0) {
+            mIcon = Bitmap.CREATOR.createFromParcel(in);
+        } else {
+            mIcon = null;
+        }
+        mItems = new ArrayList<Item>();
+        final int N = in.readInt();
+        for (int i=0; i<N; i++) {
+            CharSequence text = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+            String htmlText = in.readString8();
+            Intent intent = in.readInt() != 0 ? Intent.CREATOR.createFromParcel(in) : null;
+            Uri uri = in.readInt() != 0 ? Uri.CREATOR.createFromParcel(in) : null;
+            mItems.add(new Item(text, htmlText, intent, uri));
+        }
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<ClipData> CREATOR =
+        new Parcelable.Creator<ClipData>() {
+
+            @Override
+            public ClipData createFromParcel(Parcel source) {
+                return new ClipData(source);
+            }
+
+            @Override
+            public ClipData[] newArray(int size) {
+                return new ClipData[size];
+            }
+        };
+}
diff --git a/android/content/ClipDescription.java b/android/content/ClipDescription.java
new file mode 100644
index 0000000..6739138
--- /dev/null
+++ b/android/content/ClipDescription.java
@@ -0,0 +1,404 @@
+/**
+ * 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.content;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.PersistableBundle;
+import android.text.TextUtils;
+import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * Meta-data describing the contents of a {@link ClipData}.  Provides enough
+ * information to know if you can handle the ClipData, but not the data
+ * itself.
+ *
+ * <div class="special reference">
+ * <h3>Developer Guides</h3>
+ * <p>For more information about using the clipboard framework, read the
+ * <a href="{@docRoot}guide/topics/clipboard/copy-paste.html">Copy and Paste</a>
+ * developer guide.</p>
+ * </div>
+ */
+public class ClipDescription implements Parcelable {
+    /**
+     * The MIME type for a clip holding plain text.
+     */
+    public static final String MIMETYPE_TEXT_PLAIN = "text/plain";
+
+    /**
+     * The MIME type for a clip holding HTML text.
+     */
+    public static final String MIMETYPE_TEXT_HTML = "text/html";
+
+    /**
+     * The MIME type for a clip holding one or more URIs.  This should be
+     * used for URIs that are meaningful to a user (such as an http: URI).
+     * It should <em>not</em> be used for a content: URI that references some
+     * other piece of data; in that case the MIME type should be the type
+     * of the referenced data.
+     */
+    public static final String MIMETYPE_TEXT_URILIST = "text/uri-list";
+
+    /**
+     * The MIME type for a clip holding an Intent.
+     */
+    public static final String MIMETYPE_TEXT_INTENT = "text/vnd.android.intent";
+
+    /**
+     * The MIME type for data whose type is otherwise unknown.
+     * <p>
+     * Per RFC 2046, the "application" media type is to be used for discrete
+     * data which do not fit in any of the other categories, and the
+     * "octet-stream" subtype is used to indicate that a body contains arbitrary
+     * binary data.
+     */
+    public static final String MIMETYPE_UNKNOWN = "application/octet-stream";
+
+    /**
+     * The name of the extra used to define a component name when copying/dragging
+     * an app icon from Launcher.
+     * <p>
+     * Type: String
+     * </p>
+     * <p>
+     * Use {@link ComponentName#unflattenFromString(String)}
+     * and {@link ComponentName#flattenToString()} to convert the extra value
+     * to/from {@link ComponentName}.
+     * </p>
+     * @hide
+     */
+    public static final String EXTRA_TARGET_COMPONENT_NAME =
+            "android.content.extra.TARGET_COMPONENT_NAME";
+
+    /**
+     * The name of the extra used to define a user serial number when copying/dragging
+     * an app icon from Launcher.
+     * <p>
+     * Type: long
+     * </p>
+     * @hide
+     */
+    public static final String EXTRA_USER_SERIAL_NUMBER =
+            "android.content.extra.USER_SERIAL_NUMBER";
+
+
+    final CharSequence mLabel;
+    private final ArrayList<String> mMimeTypes;
+    private PersistableBundle mExtras;
+    private long mTimeStamp;
+
+    /**
+     * Create a new clip.
+     *
+     * @param label Label to show to the user describing this clip.
+     * @param mimeTypes An array of MIME types this data is available as.
+     */
+    public ClipDescription(CharSequence label, String[] mimeTypes) {
+        if (mimeTypes == null) {
+            throw new NullPointerException("mimeTypes is null");
+        }
+        mLabel = label;
+        mMimeTypes = new ArrayList<String>(Arrays.asList(mimeTypes));
+    }
+
+    /**
+     * Create a copy of a ClipDescription.
+     */
+    public ClipDescription(ClipDescription o) {
+        mLabel = o.mLabel;
+        mMimeTypes = new ArrayList<String>(o.mMimeTypes);
+        mTimeStamp = o.mTimeStamp;
+    }
+
+    /**
+     * Helper to compare two MIME types, where one may be a pattern.
+     * @param concreteType A fully-specified MIME type.
+     * @param desiredType A desired MIME type that may be a pattern such as *&#47;*.
+     * @return Returns true if the two MIME types match.
+     */
+    public static boolean compareMimeTypes(String concreteType, String desiredType) {
+        final int typeLength = desiredType.length();
+        if (typeLength == 3 && desiredType.equals("*/*")) {
+            return true;
+        }
+
+        final int slashpos = desiredType.indexOf('/');
+        if (slashpos > 0) {
+            if (typeLength == slashpos+2 && desiredType.charAt(slashpos+1) == '*') {
+                if (desiredType.regionMatches(0, concreteType, 0, slashpos+1)) {
+                    return true;
+                }
+            } else if (desiredType.equals(concreteType)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Used for setting the timestamp at which the associated {@link ClipData} is copied to
+     * global clipboard.
+     *
+     * @param timeStamp at which the associated {@link ClipData} is copied to clipboard in
+     *                  {@link System#currentTimeMillis()} time base.
+     * @hide
+     */
+    public void setTimestamp(long timeStamp) {
+        mTimeStamp = timeStamp;
+    }
+
+    /**
+     * Return the timestamp at which the associated {@link ClipData} is copied to global clipboard
+     * in the {@link System#currentTimeMillis()} time base.
+     *
+     * @return timestamp at which the associated {@link ClipData} is copied to global clipboard
+     *         or {@code 0} if it is not copied to clipboard.
+     */
+    public long getTimestamp() {
+        return mTimeStamp;
+    }
+
+    /**
+     * Return the label for this clip.
+     */
+    public CharSequence getLabel() {
+        return mLabel;
+    }
+
+    /**
+     * Check whether the clip description contains the given MIME type.
+     *
+     * @param mimeType The desired MIME type.  May be a pattern.
+     * @return Returns true if one of the MIME types in the clip description
+     * matches the desired MIME type, else false.
+     */
+    public boolean hasMimeType(String mimeType) {
+        final int size = mMimeTypes.size();
+        for (int i=0; i<size; i++) {
+            if (compareMimeTypes(mMimeTypes.get(i), mimeType)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Filter the clip description MIME types by the given MIME type.  Returns
+     * all MIME types in the clip that match the given MIME type.
+     *
+     * @param mimeType The desired MIME type.  May be a pattern.
+     * @return Returns an array of all matching MIME types.  If there are no
+     * matching MIME types, null is returned.
+     */
+    public String[] filterMimeTypes(String mimeType) {
+        ArrayList<String> array = null;
+        final int size = mMimeTypes.size();
+        for (int i=0; i<size; i++) {
+            if (compareMimeTypes(mMimeTypes.get(i), mimeType)) {
+                if (array == null) {
+                    array = new ArrayList<String>();
+                }
+                array.add(mMimeTypes.get(i));
+            }
+        }
+        if (array == null) {
+            return null;
+        }
+        String[] rawArray = new String[array.size()];
+        array.toArray(rawArray);
+        return rawArray;
+    }
+
+    /**
+     * Return the number of MIME types the clip is available in.
+     */
+    public int getMimeTypeCount() {
+        return mMimeTypes.size();
+    }
+
+    /**
+     * Return one of the possible clip MIME types.
+     */
+    public String getMimeType(int index) {
+        return mMimeTypes.get(index);
+    }
+
+    /**
+     * Add MIME types to the clip description.
+     */
+    void addMimeTypes(String[] mimeTypes) {
+        for (int i=0; i!=mimeTypes.length; i++) {
+            final String mimeType = mimeTypes[i];
+            if (!mMimeTypes.contains(mimeType)) {
+                mMimeTypes.add(mimeType);
+            }
+        }
+    }
+
+    /**
+     * Retrieve extended data from the clip description.
+     *
+     * @return the bundle containing extended data previously set with
+     * {@link #setExtras(PersistableBundle)}, or null if no extras have been set.
+     *
+     * @see #setExtras(PersistableBundle)
+     */
+    public PersistableBundle getExtras() {
+        return mExtras;
+    }
+
+    /**
+     * Add extended data to the clip description.
+     *
+     * @see #getExtras()
+     */
+    public void setExtras(PersistableBundle extras) {
+        mExtras = new PersistableBundle(extras);
+    }
+
+    /** @hide */
+    public void validate() {
+        if (mMimeTypes == null) {
+            throw new NullPointerException("null mime types");
+        }
+        final int size = mMimeTypes.size();
+        if (size <= 0) {
+            throw new IllegalArgumentException("must have at least 1 mime type");
+        }
+        for (int i=0; i<size; i++) {
+            if (mMimeTypes.get(i) == null) {
+                throw new NullPointerException("mime type at " + i + " is null");
+            }
+        }
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder b = new StringBuilder(128);
+
+        b.append("ClipDescription { ");
+        toShortString(b);
+        b.append(" }");
+
+        return b.toString();
+    }
+
+    /** @hide */
+    public boolean toShortString(StringBuilder b) {
+        boolean first = !toShortStringTypesOnly(b);
+        if (mLabel != null) {
+            if (!first) {
+                b.append(' ');
+            }
+            first = false;
+            b.append('"');
+            b.append(mLabel);
+            b.append('"');
+        }
+        if (mExtras != null) {
+            if (!first) {
+                b.append(' ');
+            }
+            first = false;
+            b.append(mExtras.toString());
+        }
+        if (mTimeStamp > 0) {
+            if (!first) {
+                b.append(' ');
+            }
+            first = false;
+            b.append('<');
+            b.append(TimeUtils.logTimeOfDay(mTimeStamp));
+            b.append('>');
+        }
+        return !first;
+    }
+
+    /** @hide */
+    public boolean toShortStringTypesOnly(StringBuilder b) {
+        boolean first = true;
+        final int size = mMimeTypes.size();
+        for (int i=0; i<size; i++) {
+            if (!first) {
+                b.append(' ');
+            }
+            first = false;
+            b.append(mMimeTypes.get(i));
+        }
+        return !first;
+    }
+
+    /** @hide */
+    public void dumpDebug(ProtoOutputStream proto, long fieldId) {
+        final long token = proto.start(fieldId);
+
+        final int size = mMimeTypes.size();
+        for (int i = 0; i < size; i++) {
+            proto.write(ClipDescriptionProto.MIME_TYPES, mMimeTypes.get(i));
+        }
+
+        if (mLabel != null) {
+            proto.write(ClipDescriptionProto.LABEL, mLabel.toString());
+        }
+        if (mExtras != null) {
+            mExtras.dumpDebug(proto, ClipDescriptionProto.EXTRAS);
+        }
+        if (mTimeStamp > 0) {
+            proto.write(ClipDescriptionProto.TIMESTAMP_MS, mTimeStamp);
+        }
+
+        proto.end(token);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        TextUtils.writeToParcel(mLabel, dest, flags);
+        dest.writeStringList(mMimeTypes);
+        dest.writePersistableBundle(mExtras);
+        dest.writeLong(mTimeStamp);
+    }
+
+    ClipDescription(Parcel in) {
+        mLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+        mMimeTypes = in.createStringArrayList();
+        mExtras = in.readPersistableBundle();
+        mTimeStamp = in.readLong();
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<ClipDescription> CREATOR =
+        new Parcelable.Creator<ClipDescription>() {
+
+            public ClipDescription createFromParcel(Parcel source) {
+                return new ClipDescription(source);
+            }
+
+            public ClipDescription[] newArray(int size) {
+                return new ClipDescription[size];
+            }
+        };
+}
diff --git a/android/content/ClipboardManager.java b/android/content/ClipboardManager.java
new file mode 100644
index 0000000..f833175
--- /dev/null
+++ b/android/content/ClipboardManager.java
@@ -0,0 +1,127 @@
+/*
+ * 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.content;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Handler;
+
+public class ClipboardManager extends android.text.ClipboardManager {
+
+    /**
+     * Defines a listener callback that is invoked when the primary clip on the clipboard changes.
+     * Objects that want to register a listener call
+     * {@link android.content.ClipboardManager#addPrimaryClipChangedListener(OnPrimaryClipChangedListener)
+     * addPrimaryClipChangedListener()} with an
+     * object that implements OnPrimaryClipChangedListener.
+     *
+     */
+    public interface OnPrimaryClipChangedListener {
+
+        /**
+         * Callback that is invoked by {@link android.content.ClipboardManager} when the primary
+         * clip changes.
+         */
+        void onPrimaryClipChanged();
+    }
+
+    /** {@hide} */
+    public ClipboardManager(Context context, Handler handler) { }
+
+    /**
+     * Sets the current primary clip on the clipboard.  This is the clip that
+     * is involved in normal cut and paste operations.
+     *
+     * @param clip The clipped data item to set.
+     * @see #getPrimaryClip()
+     * @see #clearPrimaryClip()
+     */
+    public void setPrimaryClip(@NonNull ClipData clip) { }
+
+    /**
+     * Clears any current primary clip on the clipboard.
+     *
+     * @see #setPrimaryClip(ClipData)
+     */
+    public void clearPrimaryClip() { }
+
+    /**
+     * Returns the current primary clip on the clipboard.
+     *
+     * <em>If the application is not the default IME or does not have input focus this return
+     * {@code null}.</em>
+     *
+     * @see #setPrimaryClip(ClipData)
+     */
+    public @Nullable ClipData getPrimaryClip() {
+        return null;
+    }
+
+    /**
+     * Returns a description of the current primary clip on the clipboard
+     * but not a copy of its data.
+     *
+     * <em>If the application is not the default IME or does not have input focus this return
+     * {@code null}.</em>
+     *
+     * @see #setPrimaryClip(ClipData)
+     */
+    public @Nullable ClipDescription getPrimaryClipDescription() {
+        return null;
+    }
+
+    /**
+     * Returns true if there is currently a primary clip on the clipboard.
+     *
+     * <em>If the application is not the default IME or the does not have input focus this will
+     * return {@code false}.</em>
+     */
+    public boolean hasPrimaryClip() {
+        return false;
+    }
+
+    public void addPrimaryClipChangedListener(OnPrimaryClipChangedListener what) { }
+
+    public void removePrimaryClipChangedListener(OnPrimaryClipChangedListener what) { }
+
+    /**
+     * @deprecated Use {@link #getPrimaryClip()} instead.  This retrieves
+     * the primary clip and tries to coerce it to a string.
+     */
+    @Deprecated
+    public CharSequence getText() {
+        return null;
+    }
+
+    /**
+     * @deprecated Use {@link #setPrimaryClip(ClipData)} instead.  This
+     * creates a ClippedItem holding the given text and sets it as the
+     * primary clip.  It has no label or icon.
+     */
+    @Deprecated
+    public void setText(CharSequence text) { }
+
+    /**
+     * @deprecated Use {@link #hasPrimaryClip()} instead.
+     */
+    @Deprecated
+    public boolean hasText() {
+        return false;
+    }
+
+    void reportPrimaryClipChanged() { }
+}
diff --git a/android/content/ComponentCallbacks.java b/android/content/ComponentCallbacks.java
new file mode 100644
index 0000000..428545d
--- /dev/null
+++ b/android/content/ComponentCallbacks.java
@@ -0,0 +1,69 @@
+/*
+ * 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.content;
+
+import android.annotation.NonNull;
+import android.content.res.Configuration;
+
+/**
+ * The set of callback APIs that are common to all application components
+ * ({@link android.app.Activity}, {@link android.app.Service},
+ * {@link ContentProvider}, and {@link android.app.Application}).
+ *
+ * <p class="note"><strong>Note:</strong> You should also implement the {@link
+ * ComponentCallbacks2} interface, which provides the {@link
+ * ComponentCallbacks2#onTrimMemory} callback to help your app manage its memory usage more
+ * effectively.</p>
+ */
+public interface ComponentCallbacks {
+    /**
+     * Called by the system when the device configuration changes while your
+     * component is running.  Note that, unlike activities, other components
+     * are never restarted when a configuration changes: they must always deal
+     * with the results of the change, such as by re-retrieving resources.
+     *
+     * <p>At the time that this function has been called, your Resources
+     * object will have been updated to return resource values matching the
+     * new configuration.
+     *
+     * <p>For more information, read <a href="{@docRoot}guide/topics/resources/runtime-changes.html"
+     * >Handling Runtime Changes</a>.
+     *
+     * @param newConfig The new device configuration.
+     */
+    void onConfigurationChanged(@NonNull Configuration newConfig);
+
+    /**
+     * This is called when the overall system is running low on memory, and
+     * actively running processes should trim their memory usage.  While
+     * the exact point at which this will be called is not defined, generally
+     * it will happen when all background process have been killed.
+     * That is, before reaching the point of killing processes hosting
+     * service and foreground UI that we would like to avoid killing.
+     *
+     * <p>You should implement this method to release
+     * any caches or other unnecessary resources you may be holding on to.
+     * The system will perform a garbage collection for you after returning from this method.
+     * <p>Preferably, you should implement {@link ComponentCallbacks2#onTrimMemory} from
+     * {@link ComponentCallbacks2} to incrementally unload your resources based on various
+     * levels of memory demands.  That API is available for API level 14 and higher, so you should
+     * only use this {@link #onLowMemory} method as a fallback for older versions, which can be
+     * treated the same as {@link ComponentCallbacks2#onTrimMemory} with the {@link
+     * ComponentCallbacks2#TRIM_MEMORY_COMPLETE} level.</p>
+     */
+    void onLowMemory();
+}
diff --git a/android/content/ComponentCallbacks2.java b/android/content/ComponentCallbacks2.java
new file mode 100644
index 0000000..6576c0f
--- /dev/null
+++ b/android/content/ComponentCallbacks2.java
@@ -0,0 +1,178 @@
+/*
+ * 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.content;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Extended {@link ComponentCallbacks} interface with a new callback for
+ * finer-grained memory management. This interface is available in all application components
+ * ({@link android.app.Activity}, {@link android.app.Service},
+ * {@link ContentProvider}, and {@link android.app.Application}).
+ *
+ * <p>You should implement {@link #onTrimMemory} to incrementally release memory based on current
+ * system constraints. Using this callback to release your resources helps provide a more
+ * responsive system overall, but also directly benefits the user experience for
+ * your app by allowing the system to keep your process alive longer. That is,
+ * if you <em>don't</em> trim your resources based on memory levels defined by this callback,
+ * the system is more likely to kill your process while it is cached in the least-recently used
+ * (LRU) list, thus requiring your app to restart and restore all state when the user returns to it.
+ *
+ * <p>The values provided by {@link #onTrimMemory} do not represent a single linear progression of
+ * memory limits, but provide you different types of clues about memory availability:</p>
+ * <ul>
+ * <li>When your app is running:
+ *  <ol>
+ *  <li>{@link #TRIM_MEMORY_RUNNING_MODERATE} <br>The device is beginning to run low on memory.
+ * Your app is running and not killable.
+ *  <li>{@link #TRIM_MEMORY_RUNNING_LOW} <br>The device is running much lower on memory.
+ * Your app is running and not killable, but please release unused resources to improve system
+ * performance (which directly impacts your app's performance).
+ *  <li>{@link #TRIM_MEMORY_RUNNING_CRITICAL} <br>The device is running extremely low on memory.
+ * Your app is not yet considered a killable process, but the system will begin killing
+ * background processes if apps do not release resources, so you should release non-critical
+ * resources now to prevent performance degradation.
+ *  </ol>
+ * </li>
+ * <li>When your app's visibility changes:
+ *  <ol>
+ *  <li>{@link #TRIM_MEMORY_UI_HIDDEN} <br>Your app's UI is no longer visible, so this is a good
+ * time to release large resources that are used only by your UI.
+ *  </ol>
+ * </li>
+ * <li>When your app's process resides in the background LRU list:
+ *  <ol>
+ *  <li>{@link #TRIM_MEMORY_BACKGROUND} <br>The system is running low on memory and your process is
+ * near the beginning of the LRU list. Although your app process is not at a high risk of being
+ * killed, the system may already be killing processes in the LRU list, so you should release
+ * resources that are easy to recover so your process will remain in the list and resume
+ * quickly when the user returns to your app.
+ *  <li>{@link #TRIM_MEMORY_MODERATE} <br>The system is running low on memory and your process is
+ * near the middle of the LRU list. If the system becomes further constrained for memory, there's a
+ * chance your process will be killed.
+ *  <li>{@link #TRIM_MEMORY_COMPLETE} <br>The system is running low on memory and your process is
+ * one of the first to be killed if the system does not recover memory now. You should release
+ * absolutely everything that's not critical to resuming your app state.
+ *   <p>To support API levels lower than 14, you can use the {@link #onLowMemory} method as a
+ * fallback that's roughly equivalent to the {@link ComponentCallbacks2#TRIM_MEMORY_COMPLETE} level.
+ *  </li>
+ *  </ol>
+ * <p class="note"><strong>Note:</strong> When the system begins
+ * killing processes in the LRU list, although it primarily works bottom-up, it does give some
+ * consideration to which processes are consuming more memory and will thus provide more gains in
+ * memory if killed. So the less memory you consume while in the LRU list overall, the better
+ * your chances are to remain in the list and be able to quickly resume.</p>
+ * </li>
+ * </ul>
+ * <p>More information about the different stages of a process lifecycle (such as what it means
+ * to be placed in the background LRU list) is provided in the <a
+ * href="{@docRoot}guide/components/processes-and-threads.html#Lifecycle">Processes and Threads</a>
+ * document.
+ */
+public interface ComponentCallbacks2 extends ComponentCallbacks {
+
+    /** @hide */
+    @IntDef(prefix = { "TRIM_MEMORY_" }, value = {
+            TRIM_MEMORY_COMPLETE,
+            TRIM_MEMORY_MODERATE,
+            TRIM_MEMORY_BACKGROUND,
+            TRIM_MEMORY_UI_HIDDEN,
+            TRIM_MEMORY_RUNNING_CRITICAL,
+            TRIM_MEMORY_RUNNING_LOW,
+            TRIM_MEMORY_RUNNING_MODERATE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface TrimMemoryLevel {}
+
+    /**
+     * Level for {@link #onTrimMemory(int)}: the process is nearing the end
+     * of the background LRU list, and if more memory isn't found soon it will
+     * be killed.
+     */
+    static final int TRIM_MEMORY_COMPLETE = 80;
+    
+    /**
+     * Level for {@link #onTrimMemory(int)}: the process is around the middle
+     * of the background LRU list; freeing memory can help the system keep
+     * other processes running later in the list for better overall performance.
+     */
+    static final int TRIM_MEMORY_MODERATE = 60;
+    
+    /**
+     * Level for {@link #onTrimMemory(int)}: the process has gone on to the
+     * LRU list.  This is a good opportunity to clean up resources that can
+     * efficiently and quickly be re-built if the user returns to the app.
+     */
+    static final int TRIM_MEMORY_BACKGROUND = 40;
+    
+    /**
+     * Level for {@link #onTrimMemory(int)}: the process had been showing
+     * a user interface, and is no longer doing so.  Large allocations with
+     * the UI should be released at this point to allow memory to be better
+     * managed.
+     */
+    static final int TRIM_MEMORY_UI_HIDDEN = 20;
+
+    /**
+     * Level for {@link #onTrimMemory(int)}: the process is not an expendable
+     * background process, but the device is running extremely low on memory
+     * and is about to not be able to keep any background processes running.
+     * Your running process should free up as many non-critical resources as it
+     * can to allow that memory to be used elsewhere.  The next thing that
+     * will happen after this is {@link #onLowMemory()} called to report that
+     * nothing at all can be kept in the background, a situation that can start
+     * to notably impact the user.
+     */
+    static final int TRIM_MEMORY_RUNNING_CRITICAL = 15;
+
+    /**
+     * Level for {@link #onTrimMemory(int)}: the process is not an expendable
+     * background process, but the device is running low on memory.
+     * Your running process should free up unneeded resources to allow that
+     * memory to be used elsewhere.
+     */
+    static final int TRIM_MEMORY_RUNNING_LOW = 10;
+
+    /**
+     * Level for {@link #onTrimMemory(int)}: the process is not an expendable
+     * background process, but the device is running moderately low on memory.
+     * Your running process may want to release some unneeded resources for
+     * use elsewhere.
+     */
+    static final int TRIM_MEMORY_RUNNING_MODERATE = 5;
+
+    /**
+     * Called when the operating system has determined that it is a good
+     * time for a process to trim unneeded memory from its process.  This will
+     * happen for example when it goes in the background and there is not enough
+     * memory to keep as many background processes running as desired.  You
+     * should never compare to exact values of the level, since new intermediate
+     * values may be added -- you will typically want to compare if the value
+     * is greater or equal to a level you are interested in.
+     *
+     * <p>To retrieve the processes current trim level at any point, you can
+     * use {@link android.app.ActivityManager#getMyMemoryState
+     * ActivityManager.getMyMemoryState(RunningAppProcessInfo)}.
+     *
+     * @param level The context of the trim, giving a hint of the amount of
+     * trimming the application may like to perform.
+     */
+    void onTrimMemory(@TrimMemoryLevel int level);
+}
diff --git a/android/content/ComponentName.java b/android/content/ComponentName.java
new file mode 100644
index 0000000..1967a01
--- /dev/null
+++ b/android/content/ComponentName.java
@@ -0,0 +1,431 @@
+/*
+ * 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.content;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+import android.util.proto.ProtoOutputStream;
+
+import java.io.PrintWriter;
+
+/**
+ * Identifier for a specific application component
+ * ({@link android.app.Activity}, {@link android.app.Service},
+ * {@link android.content.BroadcastReceiver}, or
+ * {@link android.content.ContentProvider}) that is available.  Two
+ * pieces of information, encapsulated here, are required to identify
+ * a component: the package (a String) it exists in, and the class (a String)
+ * name inside of that package.
+ *
+ */
+public final class ComponentName implements Parcelable, Cloneable, Comparable<ComponentName> {
+    private final String mPackage;
+    private final String mClass;
+
+    /**
+     * Create a new component identifier where the class name may be specified
+     * as either absolute or relative to the containing package.
+     *
+     * <p>Relative package names begin with a <code>'.'</code> character. For a package
+     * <code>"com.example"</code> and class name <code>".app.MyActivity"</code> this method
+     * will return a ComponentName with the package <code>"com.example"</code>and class name
+     * <code>"com.example.app.MyActivity"</code>. Fully qualified class names are also
+     * permitted.</p>
+     *
+     * @param pkg the name of the package the component exists in
+     * @param cls the name of the class inside of <var>pkg</var> that implements
+     *            the component
+     * @return the new ComponentName
+     */
+    public static @NonNull ComponentName createRelative(@NonNull String pkg, @NonNull String cls) {
+        if (TextUtils.isEmpty(cls)) {
+            throw new IllegalArgumentException("class name cannot be empty");
+        }
+
+        final String fullName;
+        if (cls.charAt(0) == '.') {
+            // Relative to the package. Prepend the package name.
+            fullName = pkg + cls;
+        } else {
+            // Fully qualified package name.
+            fullName = cls;
+        }
+        return new ComponentName(pkg, fullName);
+    }
+
+    /**
+     * Create a new component identifier where the class name may be specified
+     * as either absolute or relative to the containing package.
+     *
+     * <p>Relative package names begin with a <code>'.'</code> character. For a package
+     * <code>"com.example"</code> and class name <code>".app.MyActivity"</code> this method
+     * will return a ComponentName with the package <code>"com.example"</code>and class name
+     * <code>"com.example.app.MyActivity"</code>. Fully qualified class names are also
+     * permitted.</p>
+     *
+     * @param pkg a Context for the package implementing the component
+     * @param cls the name of the class inside of <var>pkg</var> that implements
+     *            the component
+     * @return the new ComponentName
+     */
+    public static @NonNull ComponentName createRelative(@NonNull Context pkg, @NonNull String cls) {
+        return createRelative(pkg.getPackageName(), cls);
+    }
+
+    /**
+     * Create a new component identifier.
+     *
+     * @param pkg The name of the package that the component exists in.  Can
+     * not be null.
+     * @param cls The name of the class inside of <var>pkg</var> that
+     * implements the component.  Can not be null.
+     */
+    public ComponentName(@NonNull String pkg, @NonNull String cls) {
+        if (pkg == null) throw new NullPointerException("package name is null");
+        if (cls == null) throw new NullPointerException("class name is null");
+        mPackage = pkg;
+        mClass = cls;
+    }
+
+    /**
+     * Create a new component identifier from a Context and class name.
+     *
+     * @param pkg A Context for the package implementing the component,
+     * from which the actual package name will be retrieved.
+     * @param cls The name of the class inside of <var>pkg</var> that
+     * implements the component.
+     */
+    public ComponentName(@NonNull Context pkg, @NonNull String cls) {
+        if (cls == null) throw new NullPointerException("class name is null");
+        mPackage = pkg.getPackageName();
+        mClass = cls;
+    }
+
+    /**
+     * Create a new component identifier from a Context and Class object.
+     *
+     * @param pkg A Context for the package implementing the component, from
+     * which the actual package name will be retrieved.
+     * @param cls The Class object of the desired component, from which the
+     * actual class name will be retrieved.
+     */
+    public ComponentName(@NonNull Context pkg, @NonNull Class<?> cls) {
+        mPackage = pkg.getPackageName();
+        mClass = cls.getName();
+    }
+
+    public ComponentName clone() {
+        return new ComponentName(mPackage, mClass);
+    }
+
+    /**
+     * Return the package name of this component.
+     */
+    public @NonNull String getPackageName() {
+        return mPackage;
+    }
+
+    /**
+     * Return the class name of this component.
+     */
+    public @NonNull String getClassName() {
+        return mClass;
+    }
+
+    /**
+     * Return the class name, either fully qualified or in a shortened form
+     * (with a leading '.') if it is a suffix of the package.
+     */
+    public String getShortClassName() {
+        if (mClass.startsWith(mPackage)) {
+            int PN = mPackage.length();
+            int CN = mClass.length();
+            if (CN > PN && mClass.charAt(PN) == '.') {
+                return mClass.substring(PN, CN);
+            }
+        }
+        return mClass;
+    }
+
+    private static void appendShortClassName(StringBuilder sb, String packageName,
+            String className) {
+        if (className.startsWith(packageName)) {
+            int PN = packageName.length();
+            int CN = className.length();
+            if (CN > PN && className.charAt(PN) == '.') {
+                sb.append(className, PN, CN);
+                return;
+            }
+        }
+        sb.append(className);
+    }
+
+    private static void printShortClassName(PrintWriter pw, String packageName,
+            String className) {
+        if (className.startsWith(packageName)) {
+            int PN = packageName.length();
+            int CN = className.length();
+            if (CN > PN && className.charAt(PN) == '.') {
+                pw.write(className, PN, CN-PN);
+                return;
+            }
+        }
+        pw.print(className);
+    }
+
+    /**
+     * Helper to get {@link #flattenToShortString()} in a {@link ComponentName} reference that can
+     * be {@code null}.
+     *
+     * @hide
+     */
+    @Nullable
+    public static String flattenToShortString(@Nullable ComponentName componentName) {
+        return componentName == null ? null : componentName.flattenToShortString();
+    }
+
+    /**
+     * Return a String that unambiguously describes both the package and
+     * class names contained in the ComponentName.  You can later recover
+     * the ComponentName from this string through
+     * {@link #unflattenFromString(String)}.
+     *
+     * @return Returns a new String holding the package and class names.  This
+     * is represented as the package name, concatenated with a '/' and then the
+     * class name.
+     *
+     * @see #unflattenFromString(String)
+     */
+    public @NonNull String flattenToString() {
+        return mPackage + "/" + mClass;
+    }
+
+    /**
+     * The same as {@link #flattenToString()}, but abbreviates the class
+     * name if it is a suffix of the package.  The result can still be used
+     * with {@link #unflattenFromString(String)}.
+     *
+     * @return Returns a new String holding the package and class names.  This
+     * is represented as the package name, concatenated with a '/' and then the
+     * class name.
+     *
+     * @see #unflattenFromString(String)
+     */
+    public @NonNull String flattenToShortString() {
+        StringBuilder sb = new StringBuilder(mPackage.length() + mClass.length());
+        appendShortString(sb, mPackage, mClass);
+        return sb.toString();
+    }
+
+    /** @hide */
+    public void appendShortString(StringBuilder sb) {
+        appendShortString(sb, mPackage, mClass);
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage
+    public static void appendShortString(StringBuilder sb, String packageName, String className) {
+        sb.append(packageName).append('/');
+        appendShortClassName(sb, packageName, className);
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage
+    public static void printShortString(PrintWriter pw, String packageName, String className) {
+        pw.print(packageName);
+        pw.print('/');
+        printShortClassName(pw, packageName, className);
+    }
+
+    /**
+     * Recover a ComponentName from a String that was previously created with
+     * {@link #flattenToString()}.  It splits the string at the first '/',
+     * taking the part before as the package name and the part after as the
+     * class name.  As a special convenience (to use, for example, when
+     * parsing component names on the command line), if the '/' is immediately
+     * followed by a '.' then the final class name will be the concatenation
+     * of the package name with the string following the '/'.  Thus
+     * "com.foo/.Blah" becomes package="com.foo" class="com.foo.Blah".
+     *
+     * @param str The String that was returned by flattenToString().
+     * @return Returns a new ComponentName containing the package and class
+     * names that were encoded in <var>str</var>
+     *
+     * @see #flattenToString()
+     */
+    public static @Nullable ComponentName unflattenFromString(@NonNull String str) {
+        int sep = str.indexOf('/');
+        if (sep < 0 || (sep+1) >= str.length()) {
+            return null;
+        }
+        String pkg = str.substring(0, sep);
+        String cls = str.substring(sep+1);
+        if (cls.length() > 0 && cls.charAt(0) == '.') {
+            cls = pkg + cls;
+        }
+        return new ComponentName(pkg, cls);
+    }
+
+    /**
+     * Return string representation of this class without the class's name
+     * as a prefix.
+     */
+    public String toShortString() {
+        return "{" + mPackage + "/" + mClass + "}";
+    }
+
+    @Override
+    public String toString() {
+        return "ComponentInfo{" + mPackage + "/" + mClass + "}";
+    }
+
+    /** Put this here so that individual services don't have to reimplement this. @hide */
+    public void dumpDebug(ProtoOutputStream proto, long fieldId) {
+        final long token = proto.start(fieldId);
+        proto.write(ComponentNameProto.PACKAGE_NAME, mPackage);
+        proto.write(ComponentNameProto.CLASS_NAME, mClass);
+        proto.end(token);
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>Two components are considered to be equal if the packages in which they reside have the
+     * same name, and if the classes that implement each component also have the same name.
+     */
+    @Override
+    public boolean equals(Object obj) {
+        try {
+            if (obj != null) {
+                ComponentName other = (ComponentName)obj;
+                // Note: no null checks, because mPackage and mClass can
+                // never be null.
+                return mPackage.equals(other.mPackage)
+                        && mClass.equals(other.mClass);
+            }
+        } catch (ClassCastException e) {
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return mPackage.hashCode() + mClass.hashCode();
+    }
+
+    public int compareTo(ComponentName that) {
+        int v;
+        v = this.mPackage.compareTo(that.mPackage);
+        if (v != 0) {
+            return v;
+        }
+        return this.mClass.compareTo(that.mClass);
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel out, int flags) {
+        // WARNING: If you modify this function, also update
+        // frameworks/base/libs/services/src/content/ComponentName.cpp.
+        out.writeString(mPackage);
+        out.writeString(mClass);
+    }
+
+    /**
+     * Write a ComponentName to a Parcel, handling null pointers.  Must be
+     * read with {@link #readFromParcel(Parcel)}.
+     *
+     * @param c The ComponentName to be written.
+     * @param out The Parcel in which the ComponentName will be placed.
+     *
+     * @see #readFromParcel(Parcel)
+     */
+    public static void writeToParcel(ComponentName c, Parcel out) {
+        if (c != null) {
+            c.writeToParcel(out, 0);
+        } else {
+            out.writeString(null);
+        }
+    }
+
+    /**
+     * Read a ComponentName from a Parcel that was previously written
+     * with {@link #writeToParcel(ComponentName, Parcel)}, returning either
+     * a null or new object as appropriate.
+     *
+     * @param in The Parcel from which to read the ComponentName
+     * @return Returns a new ComponentName matching the previously written
+     * object, or null if a null had been written.
+     *
+     * @see #writeToParcel(ComponentName, Parcel)
+     */
+    public static ComponentName readFromParcel(Parcel in) {
+        String pkg = in.readString();
+        return pkg != null ? new ComponentName(pkg, in) : null;
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<ComponentName> CREATOR
+            = new Parcelable.Creator<ComponentName>() {
+        public ComponentName createFromParcel(Parcel in) {
+            return new ComponentName(in);
+        }
+
+        public ComponentName[] newArray(int size) {
+            return new ComponentName[size];
+        }
+    };
+
+    /**
+     * Instantiate a new ComponentName 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(ComponentName, Parcel)} since it is not possible
+     * to handle a null ComponentObject here.
+     *
+     * @param in The Parcel containing the previously written ComponentName,
+     * positioned at the location in the buffer where it was written.
+     */
+    public ComponentName(Parcel in) {
+        mPackage = in.readString();
+        if (mPackage == null) throw new NullPointerException(
+                "package name is null");
+        mClass = in.readString();
+        if (mClass == null) throw new NullPointerException(
+                "class name is null");
+    }
+
+    private ComponentName(String pkg, Parcel in) {
+        mPackage = pkg;
+        mClass = in.readString();
+    }
+
+    /**
+     * Interface for classes associated with a component name.
+     * @hide
+     */
+    @FunctionalInterface
+    public interface WithComponentName {
+        /** Return the associated component name. */
+        ComponentName getComponentName();
+    }
+}
diff --git a/android/content/ContentCaptureOptions.java b/android/content/ContentCaptureOptions.java
new file mode 100644
index 0000000..cb2142c
--- /dev/null
+++ b/android/content/ContentCaptureOptions.java
@@ -0,0 +1,238 @@
+/*
+ * 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.content;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.TestApi;
+import android.app.ActivityThread;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.ArraySet;
+import android.util.Log;
+import android.view.contentcapture.ContentCaptureManager;
+import android.view.contentcapture.ContentCaptureManager.ContentCaptureClient;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.PrintWriter;
+
+/**
+ * Content capture options for a given package.
+ *
+ * <p>This object is created by the Content Capture System Service and passed back to the app when
+ * the application is created.
+ *
+ * @hide
+ */
+@TestApi
+public final class ContentCaptureOptions implements Parcelable {
+
+    private static final String TAG = ContentCaptureOptions.class.getSimpleName();
+
+    /**
+     * Logging level for {@code logcat} statements.
+     */
+    public final int loggingLevel;
+
+    /**
+     * Maximum number of events that are buffered before sent to the app.
+     */
+    public final int maxBufferSize;
+
+    /**
+     * Frequency the buffer is flushed if idle.
+     */
+    public final int idleFlushingFrequencyMs;
+
+    /**
+     * Frequency the buffer is flushed if last event is a text change.
+     */
+    public final int textChangeFlushingFrequencyMs;
+
+    /**
+     * Size of events that are logging on {@code dump}.
+     */
+    public final int logHistorySize;
+
+    /**
+     * List of activities explicitly whitelisted for content capture (or {@code null} if whitelisted
+     * for all acitivites in the package).
+     */
+    @Nullable
+    public final ArraySet<ComponentName> whitelistedComponents;
+
+    /**
+     * Used to enable just a small set of APIs so it can used by activities belonging to the
+     * content capture service APK.
+     */
+    public final boolean lite;
+
+    /**
+     * Constructor for "lite" objects that are just used to enable a {@link ContentCaptureManager}
+     * for contexts belonging to the content capture service app.
+     */
+    public ContentCaptureOptions(int loggingLevel) {
+        this(/* lite= */ true, loggingLevel, /* maxBufferSize= */ 0,
+                /* idleFlushingFrequencyMs= */ 0, /* textChangeFlushingFrequencyMs= */ 0,
+                /* logHistorySize= */ 0, /* whitelistedComponents= */ null);
+    }
+
+    /**
+     * Default constructor.
+     */
+    public ContentCaptureOptions(int loggingLevel, int maxBufferSize, int idleFlushingFrequencyMs,
+            int textChangeFlushingFrequencyMs, int logHistorySize,
+            @Nullable ArraySet<ComponentName> whitelistedComponents) {
+        this(/* lite= */ false, loggingLevel, maxBufferSize, idleFlushingFrequencyMs,
+                textChangeFlushingFrequencyMs, logHistorySize, whitelistedComponents);
+    }
+
+    /** @hide */
+    @VisibleForTesting
+    public ContentCaptureOptions(@Nullable ArraySet<ComponentName> whitelistedComponents) {
+        this(ContentCaptureManager.LOGGING_LEVEL_VERBOSE,
+                ContentCaptureManager.DEFAULT_MAX_BUFFER_SIZE,
+                ContentCaptureManager.DEFAULT_IDLE_FLUSHING_FREQUENCY_MS,
+                ContentCaptureManager.DEFAULT_TEXT_CHANGE_FLUSHING_FREQUENCY_MS,
+                ContentCaptureManager.DEFAULT_LOG_HISTORY_SIZE, whitelistedComponents);
+    }
+
+    private ContentCaptureOptions(boolean lite, int loggingLevel, int maxBufferSize,
+            int idleFlushingFrequencyMs, int textChangeFlushingFrequencyMs, int logHistorySize,
+            @Nullable ArraySet<ComponentName> whitelistedComponents) {
+        this.lite = lite;
+        this.loggingLevel = loggingLevel;
+        this.maxBufferSize = maxBufferSize;
+        this.idleFlushingFrequencyMs = idleFlushingFrequencyMs;
+        this.textChangeFlushingFrequencyMs = textChangeFlushingFrequencyMs;
+        this.logHistorySize = logHistorySize;
+        this.whitelistedComponents = whitelistedComponents;
+    }
+
+    public static ContentCaptureOptions forWhitelistingItself() {
+        final ActivityThread at = ActivityThread.currentActivityThread();
+        if (at == null) {
+            throw new IllegalStateException("No ActivityThread");
+        }
+
+        final String packageName = at.getApplication().getPackageName();
+
+        if (!"android.contentcaptureservice.cts".equals(packageName)) {
+            Log.e(TAG, "forWhitelistingItself(): called by " + packageName);
+            throw new SecurityException("Thou shall not pass!");
+        }
+
+        final ContentCaptureOptions options =
+                new ContentCaptureOptions(/* whitelistedComponents= */ null);
+        // Always log, as it's used by test only
+        Log.i(TAG, "forWhitelistingItself(" + packageName + "): " + options);
+
+        return options;
+    }
+
+    /** @hide */
+    @VisibleForTesting
+    public boolean isWhitelisted(@NonNull Context context) {
+        if (whitelistedComponents == null) return true; // whole package is whitelisted
+        final ContentCaptureClient client = context.getContentCaptureClient();
+        if (client == null) {
+            // Shouldn't happen, but it doesn't hurt to check...
+            Log.w(TAG, "isWhitelisted(): no ContentCaptureClient on " + context);
+            return false;
+        }
+        return whitelistedComponents.contains(client.contentCaptureClientGetComponentName());
+    }
+
+    @Override
+    public String toString() {
+        if (lite) {
+            return "ContentCaptureOptions [loggingLevel=" + loggingLevel + " (lite)]";
+        }
+        final StringBuilder string = new StringBuilder("ContentCaptureOptions [");
+        string.append("loggingLevel=").append(loggingLevel)
+            .append(", maxBufferSize=").append(maxBufferSize)
+            .append(", idleFlushingFrequencyMs=").append(idleFlushingFrequencyMs)
+            .append(", textChangeFlushingFrequencyMs=").append(textChangeFlushingFrequencyMs)
+            .append(", logHistorySize=").append(logHistorySize);
+        if (whitelistedComponents != null) {
+            string.append(", whitelisted=").append(whitelistedComponents);
+        }
+        return string.append(']').toString();
+    }
+
+    /** @hide */
+    public void dumpShort(@NonNull PrintWriter pw) {
+        pw.print("logLvl="); pw.print(loggingLevel);
+        if (lite) {
+            pw.print(", lite");
+            return;
+        }
+        pw.print(", bufferSize="); pw.print(maxBufferSize);
+        pw.print(", idle="); pw.print(idleFlushingFrequencyMs);
+        pw.print(", textIdle="); pw.print(textChangeFlushingFrequencyMs);
+        pw.print(", logSize="); pw.print(logHistorySize);
+        if (whitelistedComponents != null) {
+            pw.print(", whitelisted="); pw.print(whitelistedComponents);
+        }
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeBoolean(lite);
+        parcel.writeInt(loggingLevel);
+        if (lite) return;
+
+        parcel.writeInt(maxBufferSize);
+        parcel.writeInt(idleFlushingFrequencyMs);
+        parcel.writeInt(textChangeFlushingFrequencyMs);
+        parcel.writeInt(logHistorySize);
+        parcel.writeArraySet(whitelistedComponents);
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<ContentCaptureOptions> CREATOR =
+            new Parcelable.Creator<ContentCaptureOptions>() {
+
+                @Override
+                public ContentCaptureOptions createFromParcel(Parcel parcel) {
+                    final boolean lite = parcel.readBoolean();
+                    final int loggingLevel = parcel.readInt();
+                    if (lite) {
+                        return new ContentCaptureOptions(loggingLevel);
+                    }
+                    final int maxBufferSize = parcel.readInt();
+                    final int idleFlushingFrequencyMs = parcel.readInt();
+                    final int textChangeFlushingFrequencyMs = parcel.readInt();
+                    final int logHistorySize = parcel.readInt();
+                    @SuppressWarnings("unchecked")
+                    final ArraySet<ComponentName> whitelistedComponents =
+                            (ArraySet<ComponentName>) parcel.readArraySet(null);
+                    return new ContentCaptureOptions(loggingLevel, maxBufferSize,
+                            idleFlushingFrequencyMs, textChangeFlushingFrequencyMs, logHistorySize,
+                            whitelistedComponents);
+                }
+
+                @Override
+                public ContentCaptureOptions[] newArray(int size) {
+                    return new ContentCaptureOptions[size];
+                }
+    };
+}
diff --git a/android/content/ContentInsertHandler.java b/android/content/ContentInsertHandler.java
new file mode 100644
index 0000000..fbf726e
--- /dev/null
+++ b/android/content/ContentInsertHandler.java
@@ -0,0 +1,50 @@
+/*
+ * 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.content;
+
+
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Interface to insert data to ContentResolver
+ * @hide
+ */
+public interface ContentInsertHandler extends ContentHandler {
+    /**
+     * insert data from InputStream to ContentResolver
+     * @param contentResolver
+     * @param in InputStream
+     * @throws IOException
+     * @throws SAXException
+     */
+    public void insert(ContentResolver contentResolver, InputStream in) 
+        throws IOException, SAXException;
+    
+    /**
+     * insert data from String to ContentResolver
+     * @param contentResolver
+     * @param in input string
+     * @throws SAXException
+     */
+    public void insert(ContentResolver contentResolver, String in) 
+        throws SAXException;
+    
+}
diff --git a/android/content/ContentInterface.java b/android/content/ContentInterface.java
new file mode 100644
index 0000000..5988dd3
--- /dev/null
+++ b/android/content/ContentInterface.java
@@ -0,0 +1,89 @@
+/*
+ * 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.content;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.res.AssetFileDescriptor;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+
+/**
+ * Interface representing calls that can be made to {@link ContentProvider}
+ * instances.
+ * <p>
+ * These methods have been extracted into a general interface so that APIs can
+ * be flexible in accepting either a {@link ContentProvider}, a
+ * {@link ContentResolver}, or a {@link ContentProviderClient}.
+ *
+ * @hide
+ */
+public interface ContentInterface {
+    public @Nullable Cursor query(@NonNull Uri uri, @Nullable String[] projection,
+            @Nullable Bundle queryArgs, @Nullable CancellationSignal cancellationSignal)
+            throws RemoteException;
+
+    public @Nullable String getType(@NonNull Uri uri) throws RemoteException;
+
+    public @Nullable String[] getStreamTypes(@NonNull Uri uri, @NonNull String mimeTypeFilter)
+            throws RemoteException;
+
+    public @Nullable Uri canonicalize(@NonNull Uri uri) throws RemoteException;
+
+    public @Nullable Uri uncanonicalize(@NonNull Uri uri) throws RemoteException;
+
+    public boolean refresh(@NonNull Uri uri, @Nullable Bundle extras,
+            @Nullable CancellationSignal cancellationSignal) throws RemoteException;
+
+    public int checkUriPermission(@NonNull Uri uri, int uid, @Intent.AccessUriMode int modeFlags)
+            throws RemoteException;
+
+    public @Nullable Uri insert(@NonNull Uri uri, @Nullable ContentValues initialValues,
+            @Nullable Bundle extras) throws RemoteException;
+
+    public int bulkInsert(@NonNull Uri uri, @NonNull ContentValues[] initialValues)
+            throws RemoteException;
+
+    public int delete(@NonNull Uri uri, @Nullable Bundle extras) throws RemoteException;
+
+    public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable Bundle extras)
+            throws RemoteException;
+
+    public @Nullable ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode,
+            @Nullable CancellationSignal signal) throws RemoteException, FileNotFoundException;
+
+    public @Nullable AssetFileDescriptor openAssetFile(@NonNull Uri uri, @NonNull String mode,
+            @Nullable CancellationSignal signal) throws RemoteException, FileNotFoundException;
+
+    public @Nullable AssetFileDescriptor openTypedAssetFile(@NonNull Uri uri,
+            @NonNull String mimeTypeFilter, @Nullable Bundle opts,
+            @Nullable CancellationSignal signal) throws RemoteException, FileNotFoundException;
+
+    public @NonNull ContentProviderResult[] applyBatch(@NonNull String authority,
+            @NonNull ArrayList<ContentProviderOperation> operations)
+            throws RemoteException, OperationApplicationException;
+
+    public @Nullable Bundle call(@NonNull String authority, @NonNull String method,
+            @Nullable String arg, @Nullable Bundle extras) throws RemoteException;
+}
diff --git a/android/content/ContentProvider.java b/android/content/ContentProvider.java
new file mode 100644
index 0000000..60ecf64
--- /dev/null
+++ b/android/content/ContentProvider.java
@@ -0,0 +1,2625 @@
+/*
+ * 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.content;
+
+import static android.Manifest.permission.INTERACT_ACROSS_USERS;
+import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.MODE_DEFAULT;
+import static android.app.AppOpsManager.MODE_ERRORED;
+import static android.app.AppOpsManager.MODE_IGNORED;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.os.Trace.TRACE_TAG_DATABASE;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.app.AppOpsManager;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.pm.PackageManager;
+import android.content.pm.PathPermission;
+import android.content.pm.ProviderInfo;
+import android.content.res.AssetFileDescriptor;
+import android.content.res.Configuration;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.database.SQLException;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Binder;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.IBinder;
+import android.os.ICancellationSignal;
+import android.os.ParcelFileDescriptor;
+import android.os.ParcelableException;
+import android.os.Process;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+import android.os.Trace;
+import android.os.UserHandle;
+import android.os.storage.StorageManager;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Pair;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * Content providers are one of the primary building blocks of Android applications, providing
+ * content to applications. They encapsulate data and provide it to applications through the single
+ * {@link ContentResolver} interface. A content provider is only required if you need to share
+ * data between multiple applications. For example, the contacts data is used by multiple
+ * applications and must be stored in a content provider. If you don't need to share data amongst
+ * multiple applications you can use a database directly via
+ * {@link android.database.sqlite.SQLiteDatabase}.
+ *
+ * <p>When a request is made via
+ * a {@link ContentResolver} the system inspects the authority of the given URI and passes the
+ * request to the content provider registered with the authority. The content provider can interpret
+ * the rest of the URI however it wants. The {@link UriMatcher} class is helpful for parsing
+ * URIs.</p>
+ *
+ * <p>The primary methods that need to be implemented are:
+ * <ul>
+ *   <li>{@link #onCreate} which is called to initialize the provider</li>
+ *   <li>{@link #query} which returns data to the caller</li>
+ *   <li>{@link #insert} which inserts new data into the content provider</li>
+ *   <li>{@link #update} which updates existing data in the content provider</li>
+ *   <li>{@link #delete} which deletes data from the content provider</li>
+ *   <li>{@link #getType} which returns the MIME type of data in the content provider</li>
+ * </ul></p>
+ *
+ * <p class="caution">Data access methods (such as {@link #insert} and
+ * {@link #update}) may be called from many threads at once, and must be thread-safe.
+ * Other methods (such as {@link #onCreate}) are only called from the application
+ * main thread, and must avoid performing lengthy operations.  See the method
+ * descriptions for their expected thread behavior.</p>
+ *
+ * <p>Requests to {@link ContentResolver} are automatically forwarded to the appropriate
+ * ContentProvider instance, so subclasses don't have to worry about the details of
+ * cross-process calls.</p>
+ *
+ * <div class="special reference">
+ * <h3>Developer Guides</h3>
+ * <p>For more information about using content providers, read the
+ * <a href="{@docRoot}guide/topics/providers/content-providers.html">Content Providers</a>
+ * developer guide.</p>
+ * </div>
+ */
+public abstract class ContentProvider implements ContentInterface, ComponentCallbacks2 {
+
+    private static final String TAG = "ContentProvider";
+
+    /*
+     * Note: if you add methods to ContentProvider, you must add similar methods to
+     *       MockContentProvider.
+     */
+
+    @UnsupportedAppUsage
+    private Context mContext = null;
+    private int mMyUid;
+
+    // Since most Providers have only one authority, we keep both a String and a String[] to improve
+    // performance.
+    @UnsupportedAppUsage
+    private String mAuthority;
+    @UnsupportedAppUsage
+    private String[] mAuthorities;
+    @UnsupportedAppUsage
+    private String mReadPermission;
+    @UnsupportedAppUsage
+    private String mWritePermission;
+    @UnsupportedAppUsage
+    private PathPermission[] mPathPermissions;
+    private boolean mExported;
+    private boolean mNoPerms;
+    private boolean mSingleUser;
+
+    private ThreadLocal<Pair<String, String>> mCallingPackage;
+
+    private Transport mTransport = new Transport();
+
+    /**
+     * Construct a ContentProvider instance.  Content providers must be
+     * <a href="{@docRoot}guide/topics/manifest/provider-element.html">declared
+     * in the manifest</a>, accessed with {@link ContentResolver}, and created
+     * automatically by the system, so applications usually do not create
+     * ContentProvider instances directly.
+     *
+     * <p>At construction time, the object is uninitialized, and most fields and
+     * methods are unavailable.  Subclasses should initialize themselves in
+     * {@link #onCreate}, not the constructor.
+     *
+     * <p>Content providers are created on the application main thread at
+     * application launch time.  The constructor must not perform lengthy
+     * operations, or application startup will be delayed.
+     */
+    public ContentProvider() {
+    }
+
+    /**
+     * Constructor just for mocking.
+     *
+     * @param context A Context object which should be some mock instance (like the
+     * instance of {@link android.test.mock.MockContext}).
+     * @param readPermission The read permision you want this instance should have in the
+     * test, which is available via {@link #getReadPermission()}.
+     * @param writePermission The write permission you want this instance should have
+     * in the test, which is available via {@link #getWritePermission()}.
+     * @param pathPermissions The PathPermissions you want this instance should have
+     * in the test, which is available via {@link #getPathPermissions()}.
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    public ContentProvider(
+            Context context,
+            String readPermission,
+            String writePermission,
+            PathPermission[] pathPermissions) {
+        mContext = context;
+        mReadPermission = readPermission;
+        mWritePermission = writePermission;
+        mPathPermissions = pathPermissions;
+    }
+
+    /**
+     * Given an IContentProvider, try to coerce it back to the real
+     * ContentProvider object if it is running in the local process.  This can
+     * be used if you know you are running in the same process as a provider,
+     * and want to get direct access to its implementation details.  Most
+     * clients should not nor have a reason to use it.
+     *
+     * @param abstractInterface The ContentProvider interface that is to be
+     *              coerced.
+     * @return If the IContentProvider is non-{@code null} and local, returns its actual
+     * ContentProvider instance.  Otherwise returns {@code null}.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static ContentProvider coerceToLocalContentProvider(
+            IContentProvider abstractInterface) {
+        if (abstractInterface instanceof Transport) {
+            return ((Transport)abstractInterface).getContentProvider();
+        }
+        return null;
+    }
+
+    /**
+     * Binder object that deals with remoting.
+     *
+     * @hide
+     */
+    class Transport extends ContentProviderNative {
+        volatile AppOpsManager mAppOpsManager = null;
+        volatile int mReadOp = AppOpsManager.OP_NONE;
+        volatile int mWriteOp = AppOpsManager.OP_NONE;
+        volatile ContentInterface mInterface = ContentProvider.this;
+
+        ContentProvider getContentProvider() {
+            return ContentProvider.this;
+        }
+
+        @Override
+        public String getProviderName() {
+            return getContentProvider().getClass().getName();
+        }
+
+        @Override
+        public Cursor query(String callingPkg, @Nullable String attributionTag, Uri uri,
+                @Nullable String[] projection, @Nullable Bundle queryArgs,
+                @Nullable ICancellationSignal cancellationSignal) {
+            uri = validateIncomingUri(uri);
+            uri = maybeGetUriWithoutUserId(uri);
+            if (enforceReadPermission(callingPkg, attributionTag, uri, null)
+                    != AppOpsManager.MODE_ALLOWED) {
+                // The caller has no access to the data, so return an empty cursor with
+                // the columns in the requested order. The caller may ask for an invalid
+                // column and we would not catch that but this is not a problem in practice.
+                // We do not call ContentProvider#query with a modified where clause since
+                // the implementation is not guaranteed to be backed by a SQL database, hence
+                // it may not handle properly the tautology where clause we would have created.
+                if (projection != null) {
+                    return new MatrixCursor(projection, 0);
+                }
+
+                // Null projection means all columns but we have no idea which they are.
+                // However, the caller may be expecting to access them my index. Hence,
+                // we have to execute the query as if allowed to get a cursor with the
+                // columns. We then use the column names to return an empty cursor.
+                Cursor cursor;
+                final Pair<String, String> original = setCallingPackage(
+                        new Pair<>(callingPkg, attributionTag));
+                try {
+                    cursor = mInterface.query(
+                            uri, projection, queryArgs,
+                            CancellationSignal.fromTransport(cancellationSignal));
+                } catch (RemoteException e) {
+                    throw e.rethrowAsRuntimeException();
+                } finally {
+                    setCallingPackage(original);
+                }
+                if (cursor == null) {
+                    return null;
+                }
+
+                // Return an empty cursor for all columns.
+                return new MatrixCursor(cursor.getColumnNames(), 0);
+            }
+            Trace.traceBegin(TRACE_TAG_DATABASE, "query");
+            final Pair<String, String> original = setCallingPackage(
+                    new Pair<>(callingPkg, attributionTag));
+            try {
+                return mInterface.query(
+                        uri, projection, queryArgs,
+                        CancellationSignal.fromTransport(cancellationSignal));
+            } catch (RemoteException e) {
+                throw e.rethrowAsRuntimeException();
+            } finally {
+                setCallingPackage(original);
+                Trace.traceEnd(TRACE_TAG_DATABASE);
+            }
+        }
+
+        @Override
+        public String getType(Uri uri) {
+            // getCallingPackage() isn't available in getType(), as the javadoc states.
+            uri = validateIncomingUri(uri);
+            uri = maybeGetUriWithoutUserId(uri);
+            Trace.traceBegin(TRACE_TAG_DATABASE, "getType");
+            try {
+                return mInterface.getType(uri);
+            } catch (RemoteException e) {
+                throw e.rethrowAsRuntimeException();
+            } finally {
+                Trace.traceEnd(TRACE_TAG_DATABASE);
+            }
+        }
+
+        @Override
+        public void getTypeAsync(Uri uri, RemoteCallback callback) {
+            final Bundle result = new Bundle();
+            try {
+                result.putString(ContentResolver.REMOTE_CALLBACK_RESULT, getType(uri));
+            } catch (Exception e) {
+                result.putParcelable(ContentResolver.REMOTE_CALLBACK_ERROR,
+                        new ParcelableException(e));
+            }
+            callback.sendResult(result);
+        }
+
+        @Override
+        public Uri insert(String callingPkg, @Nullable String attributionTag, Uri uri,
+                ContentValues initialValues, Bundle extras) {
+            uri = validateIncomingUri(uri);
+            int userId = getUserIdFromUri(uri);
+            uri = maybeGetUriWithoutUserId(uri);
+            if (enforceWritePermission(callingPkg, attributionTag, uri, null)
+                    != AppOpsManager.MODE_ALLOWED) {
+                final Pair<String, String> original = setCallingPackage(
+                        new Pair<>(callingPkg, attributionTag));
+                try {
+                    return rejectInsert(uri, initialValues);
+                } finally {
+                    setCallingPackage(original);
+                }
+            }
+            Trace.traceBegin(TRACE_TAG_DATABASE, "insert");
+            final Pair<String, String> original = setCallingPackage(
+                    new Pair<>(callingPkg, attributionTag));
+            try {
+                return maybeAddUserId(mInterface.insert(uri, initialValues, extras), userId);
+            } catch (RemoteException e) {
+                throw e.rethrowAsRuntimeException();
+            } finally {
+                setCallingPackage(original);
+                Trace.traceEnd(TRACE_TAG_DATABASE);
+            }
+        }
+
+        @Override
+        public int bulkInsert(String callingPkg, @Nullable String attributionTag, Uri uri,
+                ContentValues[] initialValues) {
+            uri = validateIncomingUri(uri);
+            uri = maybeGetUriWithoutUserId(uri);
+            if (enforceWritePermission(callingPkg, attributionTag, uri, null)
+                    != AppOpsManager.MODE_ALLOWED) {
+                return 0;
+            }
+            Trace.traceBegin(TRACE_TAG_DATABASE, "bulkInsert");
+            final Pair<String, String> original = setCallingPackage(
+                    new Pair<>(callingPkg, attributionTag));
+            try {
+                return mInterface.bulkInsert(uri, initialValues);
+            } catch (RemoteException e) {
+                throw e.rethrowAsRuntimeException();
+            } finally {
+                setCallingPackage(original);
+                Trace.traceEnd(TRACE_TAG_DATABASE);
+            }
+        }
+
+        @Override
+        public ContentProviderResult[] applyBatch(String callingPkg,
+                @Nullable String attributionTag, String authority,
+                ArrayList<ContentProviderOperation> operations)
+                throws OperationApplicationException {
+            validateIncomingAuthority(authority);
+            int numOperations = operations.size();
+            final int[] userIds = new int[numOperations];
+            for (int i = 0; i < numOperations; i++) {
+                ContentProviderOperation operation = operations.get(i);
+                Uri uri = operation.getUri();
+                userIds[i] = getUserIdFromUri(uri);
+                uri = validateIncomingUri(uri);
+                uri = maybeGetUriWithoutUserId(uri);
+                // Rebuild operation if we changed the Uri above
+                if (!Objects.equals(operation.getUri(), uri)) {
+                    operation = new ContentProviderOperation(operation, uri);
+                    operations.set(i, operation);
+                }
+                if (operation.isReadOperation()) {
+                    if (enforceReadPermission(callingPkg, attributionTag, uri, null)
+                            != AppOpsManager.MODE_ALLOWED) {
+                        throw new OperationApplicationException("App op not allowed", 0);
+                    }
+                }
+                if (operation.isWriteOperation()) {
+                    if (enforceWritePermission(callingPkg, attributionTag, uri, null)
+                            != AppOpsManager.MODE_ALLOWED) {
+                        throw new OperationApplicationException("App op not allowed", 0);
+                    }
+                }
+            }
+            Trace.traceBegin(TRACE_TAG_DATABASE, "applyBatch");
+            final Pair<String, String> original = setCallingPackage(
+                    new Pair<>(callingPkg, attributionTag));
+            try {
+                ContentProviderResult[] results = mInterface.applyBatch(authority,
+                        operations);
+                if (results != null) {
+                    for (int i = 0; i < results.length ; i++) {
+                        if (userIds[i] != UserHandle.USER_CURRENT) {
+                            // Adding the userId to the uri.
+                            results[i] = new ContentProviderResult(results[i], userIds[i]);
+                        }
+                    }
+                }
+                return results;
+            } catch (RemoteException e) {
+                throw e.rethrowAsRuntimeException();
+            } finally {
+                setCallingPackage(original);
+                Trace.traceEnd(TRACE_TAG_DATABASE);
+            }
+        }
+
+        @Override
+        public int delete(String callingPkg, @Nullable String attributionTag, Uri uri,
+                Bundle extras) {
+            uri = validateIncomingUri(uri);
+            uri = maybeGetUriWithoutUserId(uri);
+            if (enforceWritePermission(callingPkg, attributionTag, uri, null)
+                    != AppOpsManager.MODE_ALLOWED) {
+                return 0;
+            }
+            Trace.traceBegin(TRACE_TAG_DATABASE, "delete");
+            final Pair<String, String> original = setCallingPackage(
+                    new Pair<>(callingPkg, attributionTag));
+            try {
+                return mInterface.delete(uri, extras);
+            } catch (RemoteException e) {
+                throw e.rethrowAsRuntimeException();
+            } finally {
+                setCallingPackage(original);
+                Trace.traceEnd(TRACE_TAG_DATABASE);
+            }
+        }
+
+        @Override
+        public int update(String callingPkg, @Nullable String attributionTag, Uri uri,
+                ContentValues values, Bundle extras) {
+            uri = validateIncomingUri(uri);
+            uri = maybeGetUriWithoutUserId(uri);
+            if (enforceWritePermission(callingPkg, attributionTag, uri, null)
+                    != AppOpsManager.MODE_ALLOWED) {
+                return 0;
+            }
+            Trace.traceBegin(TRACE_TAG_DATABASE, "update");
+            final Pair<String, String> original = setCallingPackage(
+                    new Pair<>(callingPkg, attributionTag));
+            try {
+                return mInterface.update(uri, values, extras);
+            } catch (RemoteException e) {
+                throw e.rethrowAsRuntimeException();
+            } finally {
+                setCallingPackage(original);
+                Trace.traceEnd(TRACE_TAG_DATABASE);
+            }
+        }
+
+        @Override
+        public ParcelFileDescriptor openFile(String callingPkg, @Nullable String attributionTag,
+                Uri uri, String mode, ICancellationSignal cancellationSignal, IBinder callerToken)
+                throws FileNotFoundException {
+            uri = validateIncomingUri(uri);
+            uri = maybeGetUriWithoutUserId(uri);
+            enforceFilePermission(callingPkg, attributionTag, uri, mode, callerToken);
+            Trace.traceBegin(TRACE_TAG_DATABASE, "openFile");
+            final Pair<String, String> original = setCallingPackage(
+                    new Pair<>(callingPkg, attributionTag));
+            try {
+                return mInterface.openFile(
+                        uri, mode, CancellationSignal.fromTransport(cancellationSignal));
+            } catch (RemoteException e) {
+                throw e.rethrowAsRuntimeException();
+            } finally {
+                setCallingPackage(original);
+                Trace.traceEnd(TRACE_TAG_DATABASE);
+            }
+        }
+
+        @Override
+        public AssetFileDescriptor openAssetFile(String callingPkg, @Nullable String attributionTag,
+                Uri uri, String mode, ICancellationSignal cancellationSignal)
+                throws FileNotFoundException {
+            uri = validateIncomingUri(uri);
+            uri = maybeGetUriWithoutUserId(uri);
+            enforceFilePermission(callingPkg, attributionTag, uri, mode, null);
+            Trace.traceBegin(TRACE_TAG_DATABASE, "openAssetFile");
+            final Pair<String, String> original = setCallingPackage(
+                    new Pair<>(callingPkg, attributionTag));
+            try {
+                return mInterface.openAssetFile(
+                        uri, mode, CancellationSignal.fromTransport(cancellationSignal));
+            } catch (RemoteException e) {
+                throw e.rethrowAsRuntimeException();
+            } finally {
+                setCallingPackage(original);
+                Trace.traceEnd(TRACE_TAG_DATABASE);
+            }
+        }
+
+        @Override
+        public Bundle call(String callingPkg, @Nullable String attributionTag, String authority,
+                String method, @Nullable String arg, @Nullable Bundle extras) {
+            validateIncomingAuthority(authority);
+            Bundle.setDefusable(extras, true);
+            Trace.traceBegin(TRACE_TAG_DATABASE, "call");
+            final Pair<String, String> original = setCallingPackage(
+                    new Pair<>(callingPkg, attributionTag));
+            try {
+                return mInterface.call(authority, method, arg, extras);
+            } catch (RemoteException e) {
+                throw e.rethrowAsRuntimeException();
+            } finally {
+                setCallingPackage(original);
+                Trace.traceEnd(TRACE_TAG_DATABASE);
+            }
+        }
+
+        @Override
+        public String[] getStreamTypes(Uri uri, String mimeTypeFilter) {
+            // getCallingPackage() isn't available in getType(), as the javadoc states.
+            uri = validateIncomingUri(uri);
+            uri = maybeGetUriWithoutUserId(uri);
+            Trace.traceBegin(TRACE_TAG_DATABASE, "getStreamTypes");
+            try {
+                return mInterface.getStreamTypes(uri, mimeTypeFilter);
+            } catch (RemoteException e) {
+                throw e.rethrowAsRuntimeException();
+            } finally {
+                Trace.traceEnd(TRACE_TAG_DATABASE);
+            }
+        }
+
+        @Override
+        public AssetFileDescriptor openTypedAssetFile(String callingPkg,
+                @Nullable String attributionTag, Uri uri, String mimeType, Bundle opts,
+                ICancellationSignal cancellationSignal) throws FileNotFoundException {
+            Bundle.setDefusable(opts, true);
+            uri = validateIncomingUri(uri);
+            uri = maybeGetUriWithoutUserId(uri);
+            enforceFilePermission(callingPkg, attributionTag, uri, "r", null);
+            Trace.traceBegin(TRACE_TAG_DATABASE, "openTypedAssetFile");
+            final Pair<String, String> original = setCallingPackage(
+                    new Pair<>(callingPkg, attributionTag));
+            try {
+                return mInterface.openTypedAssetFile(
+                        uri, mimeType, opts, CancellationSignal.fromTransport(cancellationSignal));
+            } catch (RemoteException e) {
+                throw e.rethrowAsRuntimeException();
+            } finally {
+                setCallingPackage(original);
+                Trace.traceEnd(TRACE_TAG_DATABASE);
+            }
+        }
+
+        @Override
+        public ICancellationSignal createCancellationSignal() {
+            return CancellationSignal.createTransport();
+        }
+
+        @Override
+        public Uri canonicalize(String callingPkg, @Nullable String attributionTag, Uri uri) {
+            uri = validateIncomingUri(uri);
+            int userId = getUserIdFromUri(uri);
+            uri = getUriWithoutUserId(uri);
+            if (enforceReadPermission(callingPkg, attributionTag, uri, null)
+                    != AppOpsManager.MODE_ALLOWED) {
+                return null;
+            }
+            Trace.traceBegin(TRACE_TAG_DATABASE, "canonicalize");
+            final Pair<String, String> original = setCallingPackage(
+                    new Pair<>(callingPkg, attributionTag));
+            try {
+                return maybeAddUserId(mInterface.canonicalize(uri), userId);
+            } catch (RemoteException e) {
+                throw e.rethrowAsRuntimeException();
+            } finally {
+                setCallingPackage(original);
+                Trace.traceEnd(TRACE_TAG_DATABASE);
+            }
+        }
+
+        @Override
+        public void canonicalizeAsync(String callingPkg, @Nullable String attributionTag, Uri uri,
+                RemoteCallback callback) {
+            final Bundle result = new Bundle();
+            try {
+                result.putParcelable(ContentResolver.REMOTE_CALLBACK_RESULT,
+                        canonicalize(callingPkg, attributionTag, uri));
+            } catch (Exception e) {
+                result.putParcelable(ContentResolver.REMOTE_CALLBACK_ERROR,
+                        new ParcelableException(e));
+            }
+            callback.sendResult(result);
+        }
+
+        @Override
+        public Uri uncanonicalize(String callingPkg, String attributionTag,  Uri uri) {
+            uri = validateIncomingUri(uri);
+            int userId = getUserIdFromUri(uri);
+            uri = getUriWithoutUserId(uri);
+            if (enforceReadPermission(callingPkg, attributionTag, uri, null)
+                    != AppOpsManager.MODE_ALLOWED) {
+                return null;
+            }
+            Trace.traceBegin(TRACE_TAG_DATABASE, "uncanonicalize");
+            final Pair<String, String> original = setCallingPackage(
+                    new Pair<>(callingPkg, attributionTag));
+            try {
+                return maybeAddUserId(mInterface.uncanonicalize(uri), userId);
+            } catch (RemoteException e) {
+                throw e.rethrowAsRuntimeException();
+            } finally {
+                setCallingPackage(original);
+                Trace.traceEnd(TRACE_TAG_DATABASE);
+            }
+        }
+
+        @Override
+        public boolean refresh(String callingPkg, String attributionTag, Uri uri, Bundle extras,
+                ICancellationSignal cancellationSignal) throws RemoteException {
+            uri = validateIncomingUri(uri);
+            uri = getUriWithoutUserId(uri);
+            if (enforceReadPermission(callingPkg, attributionTag, uri, null)
+                    != AppOpsManager.MODE_ALLOWED) {
+                return false;
+            }
+            Trace.traceBegin(TRACE_TAG_DATABASE, "refresh");
+            final Pair<String, String> original = setCallingPackage(
+                    new Pair<>(callingPkg, attributionTag));
+            try {
+                return mInterface.refresh(uri, extras,
+                        CancellationSignal.fromTransport(cancellationSignal));
+            } finally {
+                setCallingPackage(original);
+                Trace.traceEnd(TRACE_TAG_DATABASE);
+            }
+        }
+
+        @Override
+        public int checkUriPermission(String callingPkg, @Nullable String attributionTag, Uri uri,
+                int uid, int modeFlags) {
+            uri = validateIncomingUri(uri);
+            uri = maybeGetUriWithoutUserId(uri);
+            Trace.traceBegin(TRACE_TAG_DATABASE, "checkUriPermission");
+            final Pair<String, String> original = setCallingPackage(
+                    new Pair<>(callingPkg, attributionTag));
+            try {
+                return mInterface.checkUriPermission(uri, uid, modeFlags);
+            } catch (RemoteException e) {
+                throw e.rethrowAsRuntimeException();
+            } finally {
+                setCallingPackage(original);
+                Trace.traceEnd(TRACE_TAG_DATABASE);
+            }
+        }
+
+        private void enforceFilePermission(String callingPkg, @Nullable String attributionTag,
+                Uri uri, String mode, IBinder callerToken)
+                throws FileNotFoundException, SecurityException {
+            if (mode != null && mode.indexOf('w') != -1) {
+                if (enforceWritePermission(callingPkg, attributionTag, uri, callerToken)
+                        != AppOpsManager.MODE_ALLOWED) {
+                    throw new FileNotFoundException("App op not allowed");
+                }
+            } else {
+                if (enforceReadPermission(callingPkg, attributionTag, uri, callerToken)
+                        != AppOpsManager.MODE_ALLOWED) {
+                    throw new FileNotFoundException("App op not allowed");
+                }
+            }
+        }
+
+        private int enforceReadPermission(String callingPkg, @Nullable String attributionTag,
+                Uri uri, IBinder callerToken)
+                throws SecurityException {
+            final int mode = enforceReadPermissionInner(uri, callingPkg, attributionTag,
+                    callerToken);
+            if (mode != MODE_ALLOWED) {
+                return mode;
+            }
+
+            return noteProxyOp(callingPkg, attributionTag, mReadOp);
+        }
+
+        private int enforceWritePermission(String callingPkg, String attributionTag, Uri uri,
+                IBinder callerToken)
+                throws SecurityException {
+            final int mode = enforceWritePermissionInner(uri, callingPkg, attributionTag,
+                    callerToken);
+            if (mode != MODE_ALLOWED) {
+                return mode;
+            }
+
+            return noteProxyOp(callingPkg, attributionTag, mWriteOp);
+        }
+
+        private int noteProxyOp(String callingPkg, String attributionTag, int op) {
+            if (op != AppOpsManager.OP_NONE) {
+                int mode = mAppOpsManager.noteProxyOp(op, callingPkg, Binder.getCallingUid(),
+                        attributionTag, null);
+                return mode == MODE_DEFAULT ? MODE_IGNORED : mode;
+            }
+
+            return AppOpsManager.MODE_ALLOWED;
+        }
+    }
+
+    boolean checkUser(int pid, int uid, Context context) {
+        if (UserHandle.getUserId(uid) == context.getUserId() || mSingleUser) {
+            return true;
+        }
+        return context.checkPermission(INTERACT_ACROSS_USERS, pid, uid) == PERMISSION_GRANTED
+                || context.checkPermission(INTERACT_ACROSS_USERS_FULL, pid, uid)
+                == PERMISSION_GRANTED;
+    }
+
+    /**
+     * Verify that calling app holds both the given permission and any app-op
+     * associated with that permission.
+     */
+    private int checkPermissionAndAppOp(String permission, String callingPkg,
+            @Nullable String attributionTag, IBinder callerToken) {
+        if (getContext().checkPermission(permission, Binder.getCallingPid(), Binder.getCallingUid(),
+                callerToken) != PERMISSION_GRANTED) {
+            return MODE_ERRORED;
+        }
+
+        return mTransport.noteProxyOp(callingPkg, attributionTag,
+                AppOpsManager.permissionToOpCode(permission));
+    }
+
+    /** {@hide} */
+    protected int enforceReadPermissionInner(Uri uri, String callingPkg,
+            @Nullable String attributionTag, IBinder callerToken) throws SecurityException {
+        final Context context = getContext();
+        final int pid = Binder.getCallingPid();
+        final int uid = Binder.getCallingUid();
+        String missingPerm = null;
+        int strongestMode = MODE_ALLOWED;
+
+        if (UserHandle.isSameApp(uid, mMyUid)) {
+            return MODE_ALLOWED;
+        }
+
+        if (mExported && checkUser(pid, uid, context)) {
+            final String componentPerm = getReadPermission();
+            if (componentPerm != null) {
+                final int mode = checkPermissionAndAppOp(componentPerm, callingPkg, attributionTag,
+                        callerToken);
+                if (mode == MODE_ALLOWED) {
+                    return MODE_ALLOWED;
+                } else {
+                    missingPerm = componentPerm;
+                    strongestMode = Math.max(strongestMode, mode);
+                }
+            }
+
+            // track if unprotected read is allowed; any denied
+            // <path-permission> below removes this ability
+            boolean allowDefaultRead = (componentPerm == null);
+
+            final PathPermission[] pps = getPathPermissions();
+            if (pps != null) {
+                final String path = uri.getPath();
+                for (PathPermission pp : pps) {
+                    final String pathPerm = pp.getReadPermission();
+                    if (pathPerm != null && pp.match(path)) {
+                        final int mode = checkPermissionAndAppOp(pathPerm, callingPkg,
+                                attributionTag, callerToken);
+                        if (mode == MODE_ALLOWED) {
+                            return MODE_ALLOWED;
+                        } else {
+                            // any denied <path-permission> means we lose
+                            // default <provider> access.
+                            allowDefaultRead = false;
+                            missingPerm = pathPerm;
+                            strongestMode = Math.max(strongestMode, mode);
+                        }
+                    }
+                }
+            }
+
+            // if we passed <path-permission> checks above, and no default
+            // <provider> permission, then allow access.
+            if (allowDefaultRead) return MODE_ALLOWED;
+        }
+
+        // last chance, check against any uri grants
+        final int callingUserId = UserHandle.getUserId(uid);
+        final Uri userUri = (mSingleUser && !UserHandle.isSameUser(mMyUid, uid))
+                ? maybeAddUserId(uri, callingUserId) : uri;
+        if (context.checkUriPermission(userUri, pid, uid, Intent.FLAG_GRANT_READ_URI_PERMISSION,
+                callerToken) == PERMISSION_GRANTED) {
+            return MODE_ALLOWED;
+        }
+
+        // If the worst denial we found above was ignored, then pass that
+        // ignored through; otherwise we assume it should be a real error below.
+        if (strongestMode == MODE_IGNORED) {
+            return MODE_IGNORED;
+        }
+
+        final String suffix;
+        if (android.Manifest.permission.MANAGE_DOCUMENTS.equals(mReadPermission)) {
+            suffix = " requires that you obtain access using ACTION_OPEN_DOCUMENT or related APIs";
+        } else if (mExported) {
+            suffix = " requires " + missingPerm + ", or grantUriPermission()";
+        } else {
+            suffix = " requires the provider be exported, or grantUriPermission()";
+        }
+        throw new SecurityException("Permission Denial: reading "
+                + ContentProvider.this.getClass().getName() + " uri " + uri + " from pid=" + pid
+                + ", uid=" + uid + suffix);
+    }
+
+    /** {@hide} */
+    protected int enforceWritePermissionInner(Uri uri, String callingPkg,
+            @Nullable String attributionTag, IBinder callerToken) throws SecurityException {
+        final Context context = getContext();
+        final int pid = Binder.getCallingPid();
+        final int uid = Binder.getCallingUid();
+        String missingPerm = null;
+        int strongestMode = MODE_ALLOWED;
+
+        if (UserHandle.isSameApp(uid, mMyUid)) {
+            return MODE_ALLOWED;
+        }
+
+        if (mExported && checkUser(pid, uid, context)) {
+            final String componentPerm = getWritePermission();
+            if (componentPerm != null) {
+                final int mode = checkPermissionAndAppOp(componentPerm, callingPkg,
+                        attributionTag, callerToken);
+                if (mode == MODE_ALLOWED) {
+                    return MODE_ALLOWED;
+                } else {
+                    missingPerm = componentPerm;
+                    strongestMode = Math.max(strongestMode, mode);
+                }
+            }
+
+            // track if unprotected write is allowed; any denied
+            // <path-permission> below removes this ability
+            boolean allowDefaultWrite = (componentPerm == null);
+
+            final PathPermission[] pps = getPathPermissions();
+            if (pps != null) {
+                final String path = uri.getPath();
+                for (PathPermission pp : pps) {
+                    final String pathPerm = pp.getWritePermission();
+                    if (pathPerm != null && pp.match(path)) {
+                        final int mode = checkPermissionAndAppOp(pathPerm, callingPkg,
+                                attributionTag, callerToken);
+                        if (mode == MODE_ALLOWED) {
+                            return MODE_ALLOWED;
+                        } else {
+                            // any denied <path-permission> means we lose
+                            // default <provider> access.
+                            allowDefaultWrite = false;
+                            missingPerm = pathPerm;
+                            strongestMode = Math.max(strongestMode, mode);
+                        }
+                    }
+                }
+            }
+
+            // if we passed <path-permission> checks above, and no default
+            // <provider> permission, then allow access.
+            if (allowDefaultWrite) return MODE_ALLOWED;
+        }
+
+        // last chance, check against any uri grants
+        if (context.checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
+                callerToken) == PERMISSION_GRANTED) {
+            return MODE_ALLOWED;
+        }
+
+        // If the worst denial we found above was ignored, then pass that
+        // ignored through; otherwise we assume it should be a real error below.
+        if (strongestMode == MODE_IGNORED) {
+            return MODE_IGNORED;
+        }
+
+        final String failReason = mExported
+                ? " requires " + missingPerm + ", or grantUriPermission()"
+                : " requires the provider be exported, or grantUriPermission()";
+        throw new SecurityException("Permission Denial: writing "
+                + ContentProvider.this.getClass().getName() + " uri " + uri + " from pid=" + pid
+                + ", uid=" + uid + failReason);
+    }
+
+    /**
+     * Retrieves the Context this provider is running in.  Only available once
+     * {@link #onCreate} has been called -- this will return {@code null} in the
+     * constructor.
+     */
+    public final @Nullable Context getContext() {
+        return mContext;
+    }
+
+    /**
+     * Retrieves a Non-Nullable Context this provider is running in, this is intended to be called
+     * after {@link #onCreate}. When called before context was created, an IllegalStateException
+     * will be thrown.
+     * <p>
+     * Note A provider must be declared in the manifest and created automatically by the system,
+     * and context is only available after {@link #onCreate} is called.
+     */
+    @NonNull
+    public final Context requireContext() {
+        final Context ctx = getContext();
+        if (ctx == null) {
+            throw new IllegalStateException("Cannot find context from the provider.");
+        }
+        return ctx;
+    }
+
+    /**
+     * Set the calling package/feature, returning the current value (or {@code null})
+     * which can be used later to restore the previous state.
+     */
+    private Pair<String, String> setCallingPackage(Pair<String, String> callingPackage) {
+        final Pair<String, String> original = mCallingPackage.get();
+        mCallingPackage.set(callingPackage);
+        onCallingPackageChanged();
+        return original;
+    }
+
+    /**
+     * Return the package name of the caller that initiated the request being
+     * processed on the current thread. The returned package will have been
+     * verified to belong to the calling UID. Returns {@code null} if not
+     * currently processing a request.
+     * <p>
+     * This will always return {@code null} when processing
+     * {@link #getType(Uri)} or {@link #getStreamTypes(Uri, String)} requests.
+     *
+     * @see Binder#getCallingUid()
+     * @see Context#grantUriPermission(String, Uri, int)
+     * @throws SecurityException if the calling package doesn't belong to the
+     *             calling UID.
+     */
+    public final @Nullable String getCallingPackage() {
+        final Pair<String, String> pkg = mCallingPackage.get();
+        if (pkg != null) {
+            mTransport.mAppOpsManager.checkPackage(Binder.getCallingUid(), pkg.first);
+            return pkg.first;
+        }
+
+        return null;
+    }
+
+    /**
+     * Return the attribution tag of the caller that initiated the request being
+     * processed on the current thread. Returns {@code null} if not currently processing
+     * a request of the request is for the default attribution.
+     * <p>
+     * This will always return {@code null} when processing
+     * {@link #getType(Uri)} or {@link #getStreamTypes(Uri, String)} requests.
+     *
+     * @see #getCallingPackage
+     */
+    public final @Nullable String getCallingAttributionTag() {
+        final Pair<String, String> pkg = mCallingPackage.get();
+        if (pkg != null) {
+            return pkg.second;
+        }
+
+        return null;
+    }
+
+    /**
+     * @removed
+     */
+    @Deprecated
+    public final @Nullable String getCallingFeatureId() {
+        return getCallingAttributionTag();
+    }
+
+    /**
+     * Return the package name of the caller that initiated the request being
+     * processed on the current thread. The returned package will have
+     * <em>not</em> been verified to belong to the calling UID. Returns
+     * {@code null} if not currently processing a request.
+     * <p>
+     * This will always return {@code null} when processing
+     * {@link #getType(Uri)} or {@link #getStreamTypes(Uri, String)} requests.
+     *
+     * @see Binder#getCallingUid()
+     * @see Context#grantUriPermission(String, Uri, int)
+     */
+    public final @Nullable String getCallingPackageUnchecked() {
+        final Pair<String, String> pkg = mCallingPackage.get();
+        if (pkg != null) {
+            return pkg.first;
+        }
+
+        return null;
+    }
+
+    /**
+     * Called whenever the value of {@link #getCallingPackage()} changes, giving
+     * the provider an opportunity to invalidate any security related caching it
+     * may be performing.
+     * <p>
+     * This typically happens when a {@link ContentProvider} makes a nested call
+     * back into itself when already processing a call from a remote process.
+     */
+    public void onCallingPackageChanged() {
+    }
+
+    /**
+     * Opaque token representing the identity of an incoming IPC.
+     */
+    public final class CallingIdentity {
+        /** {@hide} */
+        public final long binderToken;
+        /** {@hide} */
+        public final Pair<String, String> callingPackage;
+
+        /** {@hide} */
+        public CallingIdentity(long binderToken, Pair<String, String> callingPackage) {
+            this.binderToken = binderToken;
+            this.callingPackage = callingPackage;
+        }
+    }
+
+    /**
+     * Reset the identity of the incoming IPC on the current thread.
+     * <p>
+     * Internally this calls {@link Binder#clearCallingIdentity()} and also
+     * clears any value stored in {@link #getCallingPackage()}.
+     *
+     * @return Returns an opaque token that can be used to restore the original
+     *         calling identity by passing it to
+     *         {@link #restoreCallingIdentity}.
+     */
+    public final @NonNull CallingIdentity clearCallingIdentity() {
+        return new CallingIdentity(Binder.clearCallingIdentity(), setCallingPackage(null));
+    }
+
+    /**
+     * Restore the identity of the incoming IPC on the current thread back to a
+     * previously identity that was returned by {@link #clearCallingIdentity}.
+     * <p>
+     * Internally this calls {@link Binder#restoreCallingIdentity(long)} and
+     * also restores any value stored in {@link #getCallingPackage()}.
+     */
+    public final void restoreCallingIdentity(@NonNull CallingIdentity identity) {
+        Binder.restoreCallingIdentity(identity.binderToken);
+        mCallingPackage.set(identity.callingPackage);
+    }
+
+    /**
+     * Change the authorities of the ContentProvider.
+     * This is normally set for you from its manifest information when the provider is first
+     * created.
+     * @hide
+     * @param authorities the semi-colon separated authorities of the ContentProvider.
+     */
+    protected final void setAuthorities(String authorities) {
+        if (authorities != null) {
+            if (authorities.indexOf(';') == -1) {
+                mAuthority = authorities;
+                mAuthorities = null;
+            } else {
+                mAuthority = null;
+                mAuthorities = authorities.split(";");
+            }
+        }
+    }
+
+    /** @hide */
+    protected final boolean matchesOurAuthorities(String authority) {
+        if (mAuthority != null) {
+            return mAuthority.equals(authority);
+        }
+        if (mAuthorities != null) {
+            int length = mAuthorities.length;
+            for (int i = 0; i < length; i++) {
+                if (mAuthorities[i].equals(authority)) return true;
+            }
+        }
+        return false;
+    }
+
+
+    /**
+     * Change the permission required to read data from the content
+     * provider.  This is normally set for you from its manifest information
+     * when the provider is first created.
+     *
+     * @param permission Name of the permission required for read-only access.
+     */
+    protected final void setReadPermission(@Nullable String permission) {
+        mReadPermission = permission;
+    }
+
+    /**
+     * Return the name of the permission required for read-only access to
+     * this content provider.  This method can be called from multiple
+     * threads, as described in
+     * <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes
+     * and Threads</a>.
+     */
+    public final @Nullable String getReadPermission() {
+        return mReadPermission;
+    }
+
+    /**
+     * Change the permission required to read and write data in the content
+     * provider.  This is normally set for you from its manifest information
+     * when the provider is first created.
+     *
+     * @param permission Name of the permission required for read/write access.
+     */
+    protected final void setWritePermission(@Nullable String permission) {
+        mWritePermission = permission;
+    }
+
+    /**
+     * Return the name of the permission required for read/write access to
+     * this content provider.  This method can be called from multiple
+     * threads, as described in
+     * <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes
+     * and Threads</a>.
+     */
+    public final @Nullable String getWritePermission() {
+        return mWritePermission;
+    }
+
+    /**
+     * Change the path-based permission required to read and/or write data in
+     * the content provider.  This is normally set for you from its manifest
+     * information when the provider is first created.
+     *
+     * @param permissions Array of path permission descriptions.
+     */
+    protected final void setPathPermissions(@Nullable PathPermission[] permissions) {
+        mPathPermissions = permissions;
+    }
+
+    /**
+     * Return the path-based permissions required for read and/or write access to
+     * this content provider.  This method can be called from multiple
+     * threads, as described in
+     * <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes
+     * and Threads</a>.
+     */
+    public final @Nullable PathPermission[] getPathPermissions() {
+        return mPathPermissions;
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage
+    public final void setAppOps(int readOp, int writeOp) {
+        if (!mNoPerms) {
+            mTransport.mReadOp = readOp;
+            mTransport.mWriteOp = writeOp;
+        }
+    }
+
+    /** @hide */
+    public AppOpsManager getAppOpsManager() {
+        return mTransport.mAppOpsManager;
+    }
+
+    /** @hide */
+    public final void setTransportLoggingEnabled(boolean enabled) {
+        if (mTransport == null) {
+            return;
+        }
+        if (enabled) {
+            mTransport.mInterface = new LoggingContentInterface(getClass().getSimpleName(), this);
+        } else {
+            mTransport.mInterface = this;
+        }
+    }
+
+    /**
+     * Implement this to initialize your content provider on startup.
+     * This method is called for all registered content providers on the
+     * application main thread at application launch time.  It must not perform
+     * lengthy operations, or application startup will be delayed.
+     *
+     * <p>You should defer nontrivial initialization (such as opening,
+     * upgrading, and scanning databases) until the content provider is used
+     * (via {@link #query}, {@link #insert}, etc).  Deferred initialization
+     * keeps application startup fast, avoids unnecessary work if the provider
+     * turns out not to be needed, and stops database errors (such as a full
+     * disk) from halting application launch.
+     *
+     * <p>If you use SQLite, {@link android.database.sqlite.SQLiteOpenHelper}
+     * is a helpful utility class that makes it easy to manage databases,
+     * and will automatically defer opening until first use.  If you do use
+     * SQLiteOpenHelper, make sure to avoid calling
+     * {@link android.database.sqlite.SQLiteOpenHelper#getReadableDatabase} or
+     * {@link android.database.sqlite.SQLiteOpenHelper#getWritableDatabase}
+     * from this method.  (Instead, override
+     * {@link android.database.sqlite.SQLiteOpenHelper#onOpen} to initialize the
+     * database when it is first opened.)
+     *
+     * @return true if the provider was successfully loaded, false otherwise
+     */
+    public abstract boolean onCreate();
+
+    /**
+     * {@inheritDoc}
+     * This method is always called on the application main thread, and must
+     * not perform lengthy operations.
+     *
+     * <p>The default content provider implementation does nothing.
+     * Override this method to take appropriate action.
+     * (Content providers do not usually care about things like screen
+     * orientation, but may want to know about locale changes.)
+     */
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+    }
+
+    /**
+     * {@inheritDoc}
+     * This method is always called on the application main thread, and must
+     * not perform lengthy operations.
+     *
+     * <p>The default content provider implementation does nothing.
+     * Subclasses may override this method to take appropriate action.
+     */
+    @Override
+    public void onLowMemory() {
+    }
+
+    @Override
+    public void onTrimMemory(int level) {
+    }
+
+    /**
+     * Implement this to handle query requests from clients.
+     *
+     * <p>Apps targeting {@link android.os.Build.VERSION_CODES#O} or higher should override
+     * {@link #query(Uri, String[], Bundle, CancellationSignal)} and provide a stub
+     * implementation of this method.
+     *
+     * <p>This method can be called from multiple threads, as described in
+     * <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes
+     * and Threads</a>.
+     * <p>
+     * Example client call:<p>
+     * <pre>// Request a specific record.
+     * Cursor managedCursor = managedQuery(
+                ContentUris.withAppendedId(Contacts.People.CONTENT_URI, 2),
+                projection,    // Which columns to return.
+                null,          // WHERE clause.
+                null,          // WHERE clause value substitution
+                People.NAME + " ASC");   // Sort order.</pre>
+     * Example implementation:<p>
+     * <pre>// SQLiteQueryBuilder is a helper class that creates the
+        // proper SQL syntax for us.
+        SQLiteQueryBuilder qBuilder = new SQLiteQueryBuilder();
+
+        // Set the table we're querying.
+        qBuilder.setTables(DATABASE_TABLE_NAME);
+
+        // If the query ends in a specific record number, we're
+        // being asked for a specific record, so set the
+        // WHERE clause in our query.
+        if((URI_MATCHER.match(uri)) == SPECIFIC_MESSAGE){
+            qBuilder.appendWhere("_id=" + uri.getPathLeafId());
+        }
+
+        // Make the query.
+        Cursor c = qBuilder.query(mDb,
+                projection,
+                selection,
+                selectionArgs,
+                groupBy,
+                having,
+                sortOrder);
+        c.setNotificationUri(getContext().getContentResolver(), uri);
+        return c;</pre>
+     *
+     * @param uri The URI to query. This will be the full URI sent by the client;
+     *      if the client is requesting a specific record, the URI will end in a record number
+     *      that the implementation should parse and add to a WHERE or HAVING clause, specifying
+     *      that _id value.
+     * @param projection The list of columns to put into the cursor. If
+     *      {@code null} all columns are included.
+     * @param selection A selection criteria to apply when filtering rows.
+     *      If {@code null} then all rows are included.
+     * @param selectionArgs You may include ?s in selection, which will be replaced by
+     *      the values from selectionArgs, in order that they appear in the selection.
+     *      The values will be bound as Strings.
+     * @param sortOrder How the rows in the cursor should be sorted.
+     *      If {@code null} then the provider is free to define the sort order.
+     * @return a Cursor or {@code null}.
+     */
+    public abstract @Nullable Cursor query(@NonNull Uri uri, @Nullable String[] projection,
+            @Nullable String selection, @Nullable String[] selectionArgs,
+            @Nullable String sortOrder);
+
+    /**
+     * Implement this to handle query requests from clients with support for cancellation.
+     *
+     * <p>Apps targeting {@link android.os.Build.VERSION_CODES#O} or higher should override
+     * {@link #query(Uri, String[], Bundle, CancellationSignal)} instead of this method.
+     *
+     * <p>This method can be called from multiple threads, as described in
+     * <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes
+     * and Threads</a>.
+     * <p>
+     * Example client call:<p>
+     * <pre>// Request a specific record.
+     * Cursor managedCursor = managedQuery(
+                ContentUris.withAppendedId(Contacts.People.CONTENT_URI, 2),
+                projection,    // Which columns to return.
+                null,          // WHERE clause.
+                null,          // WHERE clause value substitution
+                People.NAME + " ASC");   // Sort order.</pre>
+     * Example implementation:<p>
+     * <pre>// SQLiteQueryBuilder is a helper class that creates the
+        // proper SQL syntax for us.
+        SQLiteQueryBuilder qBuilder = new SQLiteQueryBuilder();
+
+        // Set the table we're querying.
+        qBuilder.setTables(DATABASE_TABLE_NAME);
+
+        // If the query ends in a specific record number, we're
+        // being asked for a specific record, so set the
+        // WHERE clause in our query.
+        if((URI_MATCHER.match(uri)) == SPECIFIC_MESSAGE){
+            qBuilder.appendWhere("_id=" + uri.getPathLeafId());
+        }
+
+        // Make the query.
+        Cursor c = qBuilder.query(mDb,
+                projection,
+                selection,
+                selectionArgs,
+                groupBy,
+                having,
+                sortOrder);
+        c.setNotificationUri(getContext().getContentResolver(), uri);
+        return c;</pre>
+     * <p>
+     * If you implement this method then you must also implement the version of
+     * {@link #query(Uri, String[], String, String[], String)} that does not take a cancellation
+     * signal to ensure correct operation on older versions of the Android Framework in
+     * which the cancellation signal overload was not available.
+     *
+     * @param uri The URI to query. This will be the full URI sent by the client;
+     *      if the client is requesting a specific record, the URI will end in a record number
+     *      that the implementation should parse and add to a WHERE or HAVING clause, specifying
+     *      that _id value.
+     * @param projection The list of columns to put into the cursor. If
+     *      {@code null} all columns are included.
+     * @param selection A selection criteria to apply when filtering rows.
+     *      If {@code null} then all rows are included.
+     * @param selectionArgs You may include ?s in selection, which will be replaced by
+     *      the values from selectionArgs, in order that they appear in the selection.
+     *      The values will be bound as Strings.
+     * @param sortOrder How the rows in the cursor should be sorted.
+     *      If {@code null} then the provider is free to define the sort order.
+     * @param cancellationSignal A signal to cancel the operation in progress, or {@code null} if none.
+     * If the operation is canceled, then {@link android.os.OperationCanceledException} will be thrown
+     * when the query is executed.
+     * @return a Cursor or {@code null}.
+     */
+    public @Nullable Cursor query(@NonNull Uri uri, @Nullable String[] projection,
+            @Nullable String selection, @Nullable String[] selectionArgs,
+            @Nullable String sortOrder, @Nullable CancellationSignal cancellationSignal) {
+        return query(uri, projection, selection, selectionArgs, sortOrder);
+    }
+
+    /**
+     * Implement this to handle query requests where the arguments are packed into a {@link Bundle}.
+     * Arguments may include traditional SQL style query arguments. When present these
+     * should be handled  according to the contract established in
+     * {@link #query(Uri, String[], String, String[], String, CancellationSignal)}.
+     *
+     * <p>Traditional SQL arguments can be found in the bundle using the following keys:
+     * <li>{@link android.content.ContentResolver#QUERY_ARG_SQL_SELECTION}
+     * <li>{@link android.content.ContentResolver#QUERY_ARG_SQL_SELECTION_ARGS}
+     * <li>{@link android.content.ContentResolver#QUERY_ARG_SQL_SORT_ORDER}
+     *
+     * <p>This method can be called from multiple threads, as described in
+     * <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes
+     * and Threads</a>.
+     *
+     * <p>
+     * Example client call:<p>
+     * <pre>// Request 20 records starting at row index 30.
+       Bundle queryArgs = new Bundle();
+       queryArgs.putInt(ContentResolver.QUERY_ARG_OFFSET, 30);
+       queryArgs.putInt(ContentResolver.QUERY_ARG_LIMIT, 20);
+
+       Cursor cursor = getContentResolver().query(
+                contentUri,    // Content Uri is specific to individual content providers.
+                projection,    // String[] describing which columns to return.
+                queryArgs,     // Query arguments.
+                null);         // Cancellation signal.</pre>
+     *
+     * Example implementation:<p>
+     * <pre>
+
+        int recordsetSize = 0x1000;  // Actual value is implementation specific.
+        queryArgs = queryArgs != null ? queryArgs : Bundle.EMPTY;  // ensure queryArgs is non-null
+
+        int offset = queryArgs.getInt(ContentResolver.QUERY_ARG_OFFSET, 0);
+        int limit = queryArgs.getInt(ContentResolver.QUERY_ARG_LIMIT, Integer.MIN_VALUE);
+
+        MatrixCursor c = new MatrixCursor(PROJECTION, limit);
+
+        // Calculate the number of items to include in the cursor.
+        int numItems = MathUtils.constrain(recordsetSize - offset, 0, limit);
+
+        // Build the paged result set....
+        for (int i = offset; i < offset + numItems; i++) {
+            // populate row from your data.
+        }
+
+        Bundle extras = new Bundle();
+        c.setExtras(extras);
+
+        // Any QUERY_ARG_* key may be included if honored.
+        // In an actual implementation, include only keys that are both present in queryArgs
+        // and reflected in the Cursor output. For example, if QUERY_ARG_OFFSET were included
+        // in queryArgs, but was ignored because it contained an invalid value (like –273),
+        // then QUERY_ARG_OFFSET should be omitted.
+        extras.putStringArray(ContentResolver.EXTRA_HONORED_ARGS, new String[] {
+            ContentResolver.QUERY_ARG_OFFSET,
+            ContentResolver.QUERY_ARG_LIMIT
+        });
+
+        extras.putInt(ContentResolver.EXTRA_TOTAL_COUNT, recordsetSize);
+
+        cursor.setNotificationUri(getContext().getContentResolver(), uri);
+
+        return cursor;</pre>
+     * <p>
+     * See {@link #query(Uri, String[], String, String[], String, CancellationSignal)}
+     * for implementation details.
+     *
+     * @param uri The URI to query. This will be the full URI sent by the client.
+     * @param projection The list of columns to put into the cursor.
+     *            If {@code null} provide a default set of columns.
+     * @param queryArgs A Bundle containing additional information necessary for
+     *            the operation. Arguments may include SQL style arguments, such
+     *            as {@link ContentResolver#QUERY_ARG_SQL_LIMIT}, but note that
+     *            the documentation for each individual provider will indicate
+     *            which arguments they support.
+     * @param cancellationSignal A signal to cancel the operation in progress,
+     *            or {@code null}.
+     * @return a Cursor or {@code null}.
+     */
+    @Override
+    public @Nullable Cursor query(@NonNull Uri uri, @Nullable String[] projection,
+            @Nullable Bundle queryArgs, @Nullable CancellationSignal cancellationSignal) {
+        queryArgs = queryArgs != null ? queryArgs : Bundle.EMPTY;
+
+        // if client doesn't supply an SQL sort order argument, attempt to build one from
+        // QUERY_ARG_SORT* arguments.
+        String sortClause = queryArgs.getString(ContentResolver.QUERY_ARG_SQL_SORT_ORDER);
+        if (sortClause == null && queryArgs.containsKey(ContentResolver.QUERY_ARG_SORT_COLUMNS)) {
+            sortClause = ContentResolver.createSqlSortClause(queryArgs);
+        }
+
+        return query(
+                uri,
+                projection,
+                queryArgs.getString(ContentResolver.QUERY_ARG_SQL_SELECTION),
+                queryArgs.getStringArray(ContentResolver.QUERY_ARG_SQL_SELECTION_ARGS),
+                sortClause,
+                cancellationSignal);
+    }
+
+    /**
+     * Implement this to handle requests for the MIME type of the data at the
+     * given URI.  The returned MIME type should start with
+     * <code>vnd.android.cursor.item</code> for a single record,
+     * or <code>vnd.android.cursor.dir/</code> for multiple items.
+     * This method can be called from multiple threads, as described in
+     * <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes
+     * and Threads</a>.
+     *
+     * <p>Note that there are no permissions needed for an application to
+     * access this information; if your content provider requires read and/or
+     * write permissions, or is not exported, all applications can still call
+     * this method regardless of their access permissions.  This allows them
+     * to retrieve the MIME type for a URI when dispatching intents.
+     *
+     * @param uri the URI to query.
+     * @return a MIME type string, or {@code null} if there is no type.
+     */
+    @Override
+    public abstract @Nullable String getType(@NonNull Uri uri);
+
+    /**
+     * Implement this to support canonicalization of URIs that refer to your
+     * content provider.  A canonical URI is one that can be transported across
+     * devices, backup/restore, and other contexts, and still be able to refer
+     * to the same data item.  Typically this is implemented by adding query
+     * params to the URI allowing the content provider to verify that an incoming
+     * canonical URI references the same data as it was originally intended for and,
+     * if it doesn't, to find that data (if it exists) in the current environment.
+     *
+     * <p>For example, if the content provider holds people and a normal URI in it
+     * is created with a row index into that people database, the cananical representation
+     * may have an additional query param at the end which specifies the name of the
+     * person it is intended for.  Later calls into the provider with that URI will look
+     * up the row of that URI's base index and, if it doesn't match or its entry's
+     * name doesn't match the name in the query param, perform a query on its database
+     * to find the correct row to operate on.</p>
+     *
+     * <p>If you implement support for canonical URIs, <b>all</b> incoming calls with
+     * URIs (including this one) must perform this verification and recovery of any
+     * canonical URIs they receive.  In addition, you must also implement
+     * {@link #uncanonicalize} to strip the canonicalization of any of these URIs.</p>
+     *
+     * <p>The default implementation of this method returns null, indicating that
+     * canonical URIs are not supported.</p>
+     *
+     * @param url The Uri to canonicalize.
+     *
+     * @return Return the canonical representation of <var>url</var>, or null if
+     * canonicalization of that Uri is not supported.
+     */
+    @Override
+    public @Nullable Uri canonicalize(@NonNull Uri url) {
+        return null;
+    }
+
+    /**
+     * Remove canonicalization from canonical URIs previously returned by
+     * {@link #canonicalize}.  For example, if your implementation is to add
+     * a query param to canonicalize a URI, this method can simply trip any
+     * query params on the URI.  The default implementation always returns the
+     * same <var>url</var> that was passed in.
+     *
+     * @param url The Uri to remove any canonicalization from.
+     *
+     * @return Return the non-canonical representation of <var>url</var>, return
+     * the <var>url</var> as-is if there is nothing to do, or return null if
+     * the data identified by the canonical representation can not be found in
+     * the current environment.
+     */
+    @Override
+    public @Nullable Uri uncanonicalize(@NonNull Uri url) {
+        return url;
+    }
+
+    /**
+     * Implement this to support refresh of content identified by {@code uri}.
+     * By default, this method returns false; providers who wish to implement
+     * this should return true to signal the client that the provider has tried
+     * refreshing with its own implementation.
+     * <p>
+     * This allows clients to request an explicit refresh of content identified
+     * by {@code uri}.
+     * <p>
+     * Client code should only invoke this method when there is a strong
+     * indication (such as a user initiated pull to refresh gesture) that the
+     * content is stale.
+     * <p>
+     * Remember to send
+     * {@link ContentResolver#notifyChange(Uri, android.database.ContentObserver)}
+     * notifications when content changes.
+     *
+     * @param uri The Uri identifying the data to refresh.
+     * @param extras Additional options from the client. The definitions of
+     *            these are specific to the content provider being called.
+     * @param cancellationSignal A signal to cancel the operation in progress,
+     *            or {@code null} if none. For example, if you called refresh on
+     *            a particular uri, you should call
+     *            {@link CancellationSignal#throwIfCanceled()} to check whether
+     *            the client has canceled the refresh request.
+     * @return true if the provider actually tried refreshing.
+     */
+    @Override
+    public boolean refresh(Uri uri, @Nullable Bundle extras,
+            @Nullable CancellationSignal cancellationSignal) {
+        return false;
+    }
+
+    /**
+     * Perform a detailed internal check on a {@link Uri} to determine if a UID
+     * is able to access it with specific mode flags.
+     * <p>
+     * This method is typically used when the provider implements more dynamic
+     * access controls that cannot be expressed with {@code <path-permission>}
+     * style static rules.
+     * <p>
+     * Because validation of these dynamic access controls has significant
+     * system health impact, this feature is only available to providers that
+     * are built into the system.
+     *
+     * @param uri the {@link Uri} to perform an access check on.
+     * @param uid the UID to check the permission for.
+     * @param modeFlags the access flags to use for the access check, such as
+     *            {@link Intent#FLAG_GRANT_READ_URI_PERMISSION}.
+     * @return {@link PackageManager#PERMISSION_GRANTED} if access is allowed,
+     *         otherwise {@link PackageManager#PERMISSION_DENIED}.
+     * @hide
+     */
+    @Override
+    @SystemApi
+    public int checkUriPermission(@NonNull Uri uri, int uid, @Intent.AccessUriMode int modeFlags) {
+        return PackageManager.PERMISSION_DENIED;
+    }
+
+    /**
+     * @hide
+     * Implementation when a caller has performed an insert on the content
+     * provider, but that call has been rejected for the operation given
+     * to {@link #setAppOps(int, int)}.  The default implementation simply
+     * returns a dummy URI that is the base URI with a 0 path element
+     * appended.
+     */
+    public Uri rejectInsert(Uri uri, ContentValues values) {
+        // If not allowed, we need to return some reasonable URI.  Maybe the
+        // content provider should be responsible for this, but for now we
+        // will just return the base URI with a dummy '0' tagged on to it.
+        // You shouldn't be able to read if you can't write, anyway, so it
+        // shouldn't matter much what is returned.
+        return uri.buildUpon().appendPath("0").build();
+    }
+
+    /**
+     * Implement this to handle requests to insert a new row. As a courtesy,
+     * call
+     * {@link ContentResolver#notifyChange(android.net.Uri ,android.database.ContentObserver)
+     * notifyChange()} after inserting. This method can be called from multiple
+     * threads, as described in <a href="
+     * {@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes
+     * and Threads</a>.
+     *
+     * @param uri The content:// URI of the insertion request.
+     * @param values A set of column_name/value pairs to add to the database.
+     * @return The URI for the newly inserted item.
+     */
+    public abstract @Nullable Uri insert(@NonNull Uri uri, @Nullable ContentValues values);
+
+    /**
+     * Implement this to handle requests to insert a new row. As a courtesy,
+     * call
+     * {@link ContentResolver#notifyChange(android.net.Uri ,android.database.ContentObserver)
+     * notifyChange()} after inserting. This method can be called from multiple
+     * threads, as described in <a href="
+     * {@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes
+     * and Threads</a>.
+     *
+     * @param uri The content:// URI of the insertion request.
+     * @param values A set of column_name/value pairs to add to the database.
+     * @param extras A Bundle containing additional information necessary for
+     *            the operation. Arguments may include SQL style arguments, such
+     *            as {@link ContentResolver#QUERY_ARG_SQL_LIMIT}, but note that
+     *            the documentation for each individual provider will indicate
+     *            which arguments they support.
+     * @return The URI for the newly inserted item.
+     * @throws IllegalArgumentException if the provider doesn't support one of
+     *             the requested Bundle arguments.
+     */
+    @Override
+    public @Nullable Uri insert(@NonNull Uri uri, @Nullable ContentValues values,
+            @Nullable Bundle extras) {
+        return insert(uri, values);
+    }
+
+    /**
+     * Override this to handle requests to insert a set of new rows, or the
+     * default implementation will iterate over the values and call
+     * {@link #insert} on each of them.
+     * As a courtesy, call {@link ContentResolver#notifyChange(android.net.Uri ,android.database.ContentObserver) notifyChange()}
+     * after inserting.
+     * This method can be called from multiple threads, as described in
+     * <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes
+     * and Threads</a>.
+     *
+     * @param uri The content:// URI of the insertion request.
+     * @param values An array of sets of column_name/value pairs to add to the database.
+     *    This must not be {@code null}.
+     * @return The number of values that were inserted.
+     */
+    @Override
+    public int bulkInsert(@NonNull Uri uri, @NonNull ContentValues[] values) {
+        int numValues = values.length;
+        for (int i = 0; i < numValues; i++) {
+            insert(uri, values[i]);
+        }
+        return numValues;
+    }
+
+    /**
+     * Implement this to handle requests to delete one or more rows. The
+     * implementation should apply the selection clause when performing
+     * deletion, allowing the operation to affect multiple rows in a directory.
+     * As a courtesy, call
+     * {@link ContentResolver#notifyChange(android.net.Uri ,android.database.ContentObserver)
+     * notifyChange()} after deleting. This method can be called from multiple
+     * threads, as described in <a href="
+     * {@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes
+     * and Threads</a>.
+     * <p>
+     * The implementation is responsible for parsing out a row ID at the end of
+     * the URI, if a specific row is being deleted. That is, the client would
+     * pass in <code>content://contacts/people/22</code> and the implementation
+     * is responsible for parsing the record number (22) when creating a SQL
+     * statement.
+     *
+     * @param uri The full URI to query, including a row ID (if a specific
+     *            record is requested).
+     * @param selection An optional restriction to apply to rows when deleting.
+     * @return The number of rows affected.
+     * @throws SQLException
+     */
+    public abstract int delete(@NonNull Uri uri, @Nullable String selection,
+            @Nullable String[] selectionArgs);
+
+    /**
+     * Implement this to handle requests to delete one or more rows. The
+     * implementation should apply the selection clause when performing
+     * deletion, allowing the operation to affect multiple rows in a directory.
+     * As a courtesy, call
+     * {@link ContentResolver#notifyChange(android.net.Uri ,android.database.ContentObserver)
+     * notifyChange()} after deleting. This method can be called from multiple
+     * threads, as described in <a href="
+     * {@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes
+     * and Threads</a>.
+     * <p>
+     * The implementation is responsible for parsing out a row ID at the end of
+     * the URI, if a specific row is being deleted. That is, the client would
+     * pass in <code>content://contacts/people/22</code> and the implementation
+     * is responsible for parsing the record number (22) when creating a SQL
+     * statement.
+     *
+     * @param uri The full URI to query, including a row ID (if a specific
+     *            record is requested).
+     * @param extras A Bundle containing additional information necessary for
+     *            the operation. Arguments may include SQL style arguments, such
+     *            as {@link ContentResolver#QUERY_ARG_SQL_LIMIT}, but note that
+     *            the documentation for each individual provider will indicate
+     *            which arguments they support.
+     * @throws IllegalArgumentException if the provider doesn't support one of
+     *             the requested Bundle arguments.
+     * @throws SQLException
+     */
+    @Override
+    public int delete(@NonNull Uri uri, @Nullable Bundle extras) {
+        extras = (extras != null) ? extras : Bundle.EMPTY;
+        return delete(uri,
+                extras.getString(ContentResolver.QUERY_ARG_SQL_SELECTION),
+                extras.getStringArray(ContentResolver.QUERY_ARG_SQL_SELECTION_ARGS));
+    }
+
+    /**
+     * Implement this to handle requests to update one or more rows. The
+     * implementation should update all rows matching the selection to set the
+     * columns according to the provided values map. As a courtesy, call
+     * {@link ContentResolver#notifyChange(android.net.Uri ,android.database.ContentObserver)
+     * notifyChange()} after updating. This method can be called from multiple
+     * threads, as described in <a href="
+     * {@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes
+     * and Threads</a>.
+     *
+     * @param uri The URI to query. This can potentially have a record ID if
+     *            this is an update request for a specific record.
+     * @param values A set of column_name/value pairs to update in the database.
+     * @param selection An optional filter to match rows to update.
+     * @return the number of rows affected.
+     */
+    public abstract int update(@NonNull Uri uri, @Nullable ContentValues values,
+            @Nullable String selection, @Nullable String[] selectionArgs);
+
+    /**
+     * Implement this to handle requests to update one or more rows. The
+     * implementation should update all rows matching the selection to set the
+     * columns according to the provided values map. As a courtesy, call
+     * {@link ContentResolver#notifyChange(android.net.Uri ,android.database.ContentObserver)
+     * notifyChange()} after updating. This method can be called from multiple
+     * threads, as described in <a href="
+     * {@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes
+     * and Threads</a>.
+     *
+     * @param uri The URI to query. This can potentially have a record ID if
+     *            this is an update request for a specific record.
+     * @param values A set of column_name/value pairs to update in the database.
+     * @param extras A Bundle containing additional information necessary for
+     *            the operation. Arguments may include SQL style arguments, such
+     *            as {@link ContentResolver#QUERY_ARG_SQL_LIMIT}, but note that
+     *            the documentation for each individual provider will indicate
+     *            which arguments they support.
+     * @return the number of rows affected.
+     * @throws IllegalArgumentException if the provider doesn't support one of
+     *             the requested Bundle arguments.
+     */
+    @Override
+    public int update(@NonNull Uri uri, @Nullable ContentValues values,
+            @Nullable Bundle extras) {
+        extras = (extras != null) ? extras : Bundle.EMPTY;
+        return update(uri, values,
+                extras.getString(ContentResolver.QUERY_ARG_SQL_SELECTION),
+                extras.getStringArray(ContentResolver.QUERY_ARG_SQL_SELECTION_ARGS));
+    }
+
+    /**
+     * Override this to handle requests to open a file blob.
+     * The default implementation always throws {@link FileNotFoundException}.
+     * This method can be called from multiple threads, as described in
+     * <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes
+     * and Threads</a>.
+     *
+     * <p>This method returns a ParcelFileDescriptor, which is returned directly
+     * to the caller.  This way large data (such as images and documents) can be
+     * returned without copying the content.
+     *
+     * <p>The returned ParcelFileDescriptor is owned by the caller, so it is
+     * their responsibility to close it when done.  That is, the implementation
+     * of this method should create a new ParcelFileDescriptor for each call.
+     * <p>
+     * If opened with the exclusive "r" or "w" modes, the returned
+     * ParcelFileDescriptor can be a pipe or socket pair to enable streaming
+     * of data. Opening with the "rw" or "rwt" modes implies a file on disk that
+     * supports seeking.
+     * <p>
+     * If you need to detect when the returned ParcelFileDescriptor has been
+     * closed, or if the remote process has crashed or encountered some other
+     * error, you can use {@link ParcelFileDescriptor#open(File, int,
+     * android.os.Handler, android.os.ParcelFileDescriptor.OnCloseListener)},
+     * {@link ParcelFileDescriptor#createReliablePipe()}, or
+     * {@link ParcelFileDescriptor#createReliableSocketPair()}.
+     * <p>
+     * If you need to return a large file that isn't backed by a real file on
+     * disk, such as a file on a network share or cloud storage service,
+     * consider using
+     * {@link StorageManager#openProxyFileDescriptor(int, android.os.ProxyFileDescriptorCallback, android.os.Handler)}
+     * which will let you to stream the content on-demand.
+     *
+     * <p class="note">For use in Intents, you will want to implement {@link #getType}
+     * to return the appropriate MIME type for the data returned here with
+     * the same URI.  This will allow intent resolution to automatically determine the data MIME
+     * type and select the appropriate matching targets as part of its operation.</p>
+     *
+     * <p class="note">For better interoperability with other applications, it is recommended
+     * that for any URIs that can be opened, you also support queries on them
+     * containing at least the columns specified by {@link android.provider.OpenableColumns}.
+     * You may also want to support other common columns if you have additional meta-data
+     * to supply, such as {@link android.provider.MediaStore.MediaColumns#DATE_ADDED}
+     * in {@link android.provider.MediaStore.MediaColumns}.</p>
+     *
+     * @param uri The URI whose file is to be opened.
+     * @param mode Access mode for the file.  May be "r" for read-only access,
+     * "rw" for read and write access, or "rwt" for read and write access
+     * that truncates any existing file.
+     *
+     * @return Returns a new ParcelFileDescriptor which you can use to access
+     * the file.
+     *
+     * @throws FileNotFoundException Throws FileNotFoundException if there is
+     * no file associated with the given URI or the mode is invalid.
+     * @throws SecurityException Throws SecurityException if the caller does
+     * not have permission to access the file.
+     *
+     * @see #openAssetFile(Uri, String)
+     * @see #openFileHelper(Uri, String)
+     * @see #getType(android.net.Uri)
+     * @see ParcelFileDescriptor#parseMode(String)
+     */
+    public @Nullable ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode)
+            throws FileNotFoundException {
+        throw new FileNotFoundException("No files supported by provider at "
+                + uri);
+    }
+
+    /**
+     * Override this to handle requests to open a file blob.
+     * The default implementation always throws {@link FileNotFoundException}.
+     * This method can be called from multiple threads, as described in
+     * <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes
+     * and Threads</a>.
+     *
+     * <p>This method returns a ParcelFileDescriptor, which is returned directly
+     * to the caller.  This way large data (such as images and documents) can be
+     * returned without copying the content.
+     *
+     * <p>The returned ParcelFileDescriptor is owned by the caller, so it is
+     * their responsibility to close it when done.  That is, the implementation
+     * of this method should create a new ParcelFileDescriptor for each call.
+     * <p>
+     * If opened with the exclusive "r" or "w" modes, the returned
+     * ParcelFileDescriptor can be a pipe or socket pair to enable streaming
+     * of data. Opening with the "rw" or "rwt" modes implies a file on disk that
+     * supports seeking.
+     * <p>
+     * If you need to detect when the returned ParcelFileDescriptor has been
+     * closed, or if the remote process has crashed or encountered some other
+     * error, you can use {@link ParcelFileDescriptor#open(File, int,
+     * android.os.Handler, android.os.ParcelFileDescriptor.OnCloseListener)},
+     * {@link ParcelFileDescriptor#createReliablePipe()}, or
+     * {@link ParcelFileDescriptor#createReliableSocketPair()}.
+     *
+     * <p class="note">For use in Intents, you will want to implement {@link #getType}
+     * to return the appropriate MIME type for the data returned here with
+     * the same URI.  This will allow intent resolution to automatically determine the data MIME
+     * type and select the appropriate matching targets as part of its operation.</p>
+     *
+     * <p class="note">For better interoperability with other applications, it is recommended
+     * that for any URIs that can be opened, you also support queries on them
+     * containing at least the columns specified by {@link android.provider.OpenableColumns}.
+     * You may also want to support other common columns if you have additional meta-data
+     * to supply, such as {@link android.provider.MediaStore.MediaColumns#DATE_ADDED}
+     * in {@link android.provider.MediaStore.MediaColumns}.</p>
+     *
+     * @param uri The URI whose file is to be opened.
+     * @param mode Access mode for the file. May be "r" for read-only access,
+     *            "w" for write-only access, "rw" for read and write access, or
+     *            "rwt" for read and write access that truncates any existing
+     *            file.
+     * @param signal A signal to cancel the operation in progress, or
+     *            {@code null} if none. For example, if you are downloading a
+     *            file from the network to service a "rw" mode request, you
+     *            should periodically call
+     *            {@link CancellationSignal#throwIfCanceled()} to check whether
+     *            the client has canceled the request and abort the download.
+     *
+     * @return Returns a new ParcelFileDescriptor which you can use to access
+     * the file.
+     *
+     * @throws FileNotFoundException Throws FileNotFoundException if there is
+     * no file associated with the given URI or the mode is invalid.
+     * @throws SecurityException Throws SecurityException if the caller does
+     * not have permission to access the file.
+     *
+     * @see #openAssetFile(Uri, String)
+     * @see #openFileHelper(Uri, String)
+     * @see #getType(android.net.Uri)
+     * @see ParcelFileDescriptor#parseMode(String)
+     */
+    @Override
+    public @Nullable ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode,
+            @Nullable CancellationSignal signal) throws FileNotFoundException {
+        return openFile(uri, mode);
+    }
+
+    /**
+     * This is like {@link #openFile}, but can be implemented by providers
+     * that need to be able to return sub-sections of files, often assets
+     * inside of their .apk.
+     * This method can be called from multiple threads, as described in
+     * <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes
+     * and Threads</a>.
+     *
+     * <p>If you implement this, your clients must be able to deal with such
+     * file slices, either directly with
+     * {@link ContentResolver#openAssetFileDescriptor}, or by using the higher-level
+     * {@link ContentResolver#openInputStream ContentResolver.openInputStream}
+     * or {@link ContentResolver#openOutputStream ContentResolver.openOutputStream}
+     * methods.
+     * <p>
+     * The returned AssetFileDescriptor can be a pipe or socket pair to enable
+     * streaming of data.
+     *
+     * <p class="note">If you are implementing this to return a full file, you
+     * should create the AssetFileDescriptor with
+     * {@link AssetFileDescriptor#UNKNOWN_LENGTH} to be compatible with
+     * applications that cannot handle sub-sections of files.</p>
+     *
+     * <p class="note">For use in Intents, you will want to implement {@link #getType}
+     * to return the appropriate MIME type for the data returned here with
+     * the same URI.  This will allow intent resolution to automatically determine the data MIME
+     * type and select the appropriate matching targets as part of its operation.</p>
+     *
+     * <p class="note">For better interoperability with other applications, it is recommended
+     * that for any URIs that can be opened, you also support queries on them
+     * containing at least the columns specified by {@link android.provider.OpenableColumns}.</p>
+     *
+     * @param uri The URI whose file is to be opened.
+     * @param mode Access mode for the file.  May be "r" for read-only access,
+     * "w" for write-only access (erasing whatever data is currently in
+     * the file), "wa" for write-only access to append to any existing data,
+     * "rw" for read and write access on any existing data, and "rwt" for read
+     * and write access that truncates any existing file.
+     *
+     * @return Returns a new AssetFileDescriptor which you can use to access
+     * the file.
+     *
+     * @throws FileNotFoundException Throws FileNotFoundException if there is
+     * no file associated with the given URI or the mode is invalid.
+     * @throws SecurityException Throws SecurityException if the caller does
+     * not have permission to access the file.
+     *
+     * @see #openFile(Uri, String)
+     * @see #openFileHelper(Uri, String)
+     * @see #getType(android.net.Uri)
+     */
+    public @Nullable AssetFileDescriptor openAssetFile(@NonNull Uri uri, @NonNull String mode)
+            throws FileNotFoundException {
+        ParcelFileDescriptor fd = openFile(uri, mode);
+        return fd != null ? new AssetFileDescriptor(fd, 0, -1) : null;
+    }
+
+    /**
+     * This is like {@link #openFile}, but can be implemented by providers
+     * that need to be able to return sub-sections of files, often assets
+     * inside of their .apk.
+     * This method can be called from multiple threads, as described in
+     * <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes
+     * and Threads</a>.
+     *
+     * <p>If you implement this, your clients must be able to deal with such
+     * file slices, either directly with
+     * {@link ContentResolver#openAssetFileDescriptor}, or by using the higher-level
+     * {@link ContentResolver#openInputStream ContentResolver.openInputStream}
+     * or {@link ContentResolver#openOutputStream ContentResolver.openOutputStream}
+     * methods.
+     * <p>
+     * The returned AssetFileDescriptor can be a pipe or socket pair to enable
+     * streaming of data.
+     *
+     * <p class="note">If you are implementing this to return a full file, you
+     * should create the AssetFileDescriptor with
+     * {@link AssetFileDescriptor#UNKNOWN_LENGTH} to be compatible with
+     * applications that cannot handle sub-sections of files.</p>
+     *
+     * <p class="note">For use in Intents, you will want to implement {@link #getType}
+     * to return the appropriate MIME type for the data returned here with
+     * the same URI.  This will allow intent resolution to automatically determine the data MIME
+     * type and select the appropriate matching targets as part of its operation.</p>
+     *
+     * <p class="note">For better interoperability with other applications, it is recommended
+     * that for any URIs that can be opened, you also support queries on them
+     * containing at least the columns specified by {@link android.provider.OpenableColumns}.</p>
+     *
+     * @param uri The URI whose file is to be opened.
+     * @param mode Access mode for the file.  May be "r" for read-only access,
+     * "w" for write-only access (erasing whatever data is currently in
+     * the file), "wa" for write-only access to append to any existing data,
+     * "rw" for read and write access on any existing data, and "rwt" for read
+     * and write access that truncates any existing file.
+     * @param signal A signal to cancel the operation in progress, or
+     *            {@code null} if none. For example, if you are downloading a
+     *            file from the network to service a "rw" mode request, you
+     *            should periodically call
+     *            {@link CancellationSignal#throwIfCanceled()} to check whether
+     *            the client has canceled the request and abort the download.
+     *
+     * @return Returns a new AssetFileDescriptor which you can use to access
+     * the file.
+     *
+     * @throws FileNotFoundException Throws FileNotFoundException if there is
+     * no file associated with the given URI or the mode is invalid.
+     * @throws SecurityException Throws SecurityException if the caller does
+     * not have permission to access the file.
+     *
+     * @see #openFile(Uri, String)
+     * @see #openFileHelper(Uri, String)
+     * @see #getType(android.net.Uri)
+     */
+    @Override
+    public @Nullable AssetFileDescriptor openAssetFile(@NonNull Uri uri, @NonNull String mode,
+            @Nullable CancellationSignal signal) throws FileNotFoundException {
+        return openAssetFile(uri, mode);
+    }
+
+    /**
+     * Convenience for subclasses that wish to implement {@link #openFile}
+     * by looking up a column named "_data" at the given URI.
+     *
+     * @param uri The URI to be opened.
+     * @param mode The file mode.  May be "r" for read-only access,
+     * "w" for write-only access (erasing whatever data is currently in
+     * the file), "wa" for write-only access to append to any existing data,
+     * "rw" for read and write access on any existing data, and "rwt" for read
+     * and write access that truncates any existing file.
+     *
+     * @return Returns a new ParcelFileDescriptor that can be used by the
+     * client to access the file.
+     */
+    protected final @NonNull ParcelFileDescriptor openFileHelper(@NonNull Uri uri,
+            @NonNull String mode) throws FileNotFoundException {
+        Cursor c = query(uri, new String[]{"_data"}, null, null, null);
+        int count = (c != null) ? c.getCount() : 0;
+        if (count != 1) {
+            // If there is not exactly one result, throw an appropriate
+            // exception.
+            if (c != null) {
+                c.close();
+            }
+            if (count == 0) {
+                throw new FileNotFoundException("No entry for " + uri);
+            }
+            throw new FileNotFoundException("Multiple items at " + uri);
+        }
+
+        c.moveToFirst();
+        int i = c.getColumnIndex("_data");
+        String path = (i >= 0 ? c.getString(i) : null);
+        c.close();
+        if (path == null) {
+            throw new FileNotFoundException("Column _data not found.");
+        }
+
+        int modeBits = ParcelFileDescriptor.parseMode(mode);
+        return ParcelFileDescriptor.open(new File(path), modeBits);
+    }
+
+    /**
+     * Called by a client to determine the types of data streams that this
+     * content provider supports for the given URI.  The default implementation
+     * returns {@code null}, meaning no types.  If your content provider stores data
+     * of a particular type, return that MIME type if it matches the given
+     * mimeTypeFilter.  If it can perform type conversions, return an array
+     * of all supported MIME types that match mimeTypeFilter.
+     *
+     * @param uri The data in the content provider being queried.
+     * @param mimeTypeFilter The type of data the client desires.  May be
+     * a pattern, such as *&#47;* to retrieve all possible data types.
+     * @return Returns {@code null} if there are no possible data streams for the
+     * given mimeTypeFilter.  Otherwise returns an array of all available
+     * concrete MIME types.
+     *
+     * @see #getType(Uri)
+     * @see #openTypedAssetFile(Uri, String, Bundle)
+     * @see ClipDescription#compareMimeTypes(String, String)
+     */
+    @Override
+    public @Nullable String[] getStreamTypes(@NonNull Uri uri, @NonNull String mimeTypeFilter) {
+        return null;
+    }
+
+    /**
+     * Called by a client to open a read-only stream containing data of a
+     * particular MIME type.  This is like {@link #openAssetFile(Uri, String)},
+     * except the file can only be read-only and the content provider may
+     * perform data conversions to generate data of the desired type.
+     *
+     * <p>The default implementation compares the given mimeType against the
+     * result of {@link #getType(Uri)} and, if they match, simply calls
+     * {@link #openAssetFile(Uri, String)}.
+     *
+     * <p>See {@link ClipData} for examples of the use and implementation
+     * of this method.
+     * <p>
+     * The returned AssetFileDescriptor can be a pipe or socket pair to enable
+     * streaming of data.
+     *
+     * <p class="note">For better interoperability with other applications, it is recommended
+     * that for any URIs that can be opened, you also support queries on them
+     * containing at least the columns specified by {@link android.provider.OpenableColumns}.
+     * You may also want to support other common columns if you have additional meta-data
+     * to supply, such as {@link android.provider.MediaStore.MediaColumns#DATE_ADDED}
+     * in {@link android.provider.MediaStore.MediaColumns}.</p>
+     *
+     * @param uri The data in the content provider being queried.
+     * @param mimeTypeFilter The type of data the client desires.  May be
+     * a pattern, such as *&#47;*, if the caller does not have specific type
+     * requirements; in this case the content provider will pick its best
+     * type matching the pattern.
+     * @param opts Additional options from the client.  The definitions of
+     * these are specific to the content provider being called.
+     *
+     * @return Returns a new AssetFileDescriptor from which the client can
+     * read data of the desired type.
+     *
+     * @throws FileNotFoundException Throws FileNotFoundException if there is
+     * no file associated with the given URI or the mode is invalid.
+     * @throws SecurityException Throws SecurityException if the caller does
+     * not have permission to access the data.
+     * @throws IllegalArgumentException Throws IllegalArgumentException if the
+     * content provider does not support the requested MIME type.
+     *
+     * @see #getStreamTypes(Uri, String)
+     * @see #openAssetFile(Uri, String)
+     * @see ClipDescription#compareMimeTypes(String, String)
+     */
+    public @Nullable AssetFileDescriptor openTypedAssetFile(@NonNull Uri uri,
+            @NonNull String mimeTypeFilter, @Nullable Bundle opts) throws FileNotFoundException {
+        if ("*/*".equals(mimeTypeFilter)) {
+            // If they can take anything, the untyped open call is good enough.
+            return openAssetFile(uri, "r");
+        }
+        String baseType = getType(uri);
+        if (baseType != null && ClipDescription.compareMimeTypes(baseType, mimeTypeFilter)) {
+            // Use old untyped open call if this provider has a type for this
+            // URI and it matches the request.
+            return openAssetFile(uri, "r");
+        }
+        throw new FileNotFoundException("Can't open " + uri + " as type " + mimeTypeFilter);
+    }
+
+
+    /**
+     * Called by a client to open a read-only stream containing data of a
+     * particular MIME type.  This is like {@link #openAssetFile(Uri, String)},
+     * except the file can only be read-only and the content provider may
+     * perform data conversions to generate data of the desired type.
+     *
+     * <p>The default implementation compares the given mimeType against the
+     * result of {@link #getType(Uri)} and, if they match, simply calls
+     * {@link #openAssetFile(Uri, String)}.
+     *
+     * <p>See {@link ClipData} for examples of the use and implementation
+     * of this method.
+     * <p>
+     * The returned AssetFileDescriptor can be a pipe or socket pair to enable
+     * streaming of data.
+     *
+     * <p class="note">For better interoperability with other applications, it is recommended
+     * that for any URIs that can be opened, you also support queries on them
+     * containing at least the columns specified by {@link android.provider.OpenableColumns}.
+     * You may also want to support other common columns if you have additional meta-data
+     * to supply, such as {@link android.provider.MediaStore.MediaColumns#DATE_ADDED}
+     * in {@link android.provider.MediaStore.MediaColumns}.</p>
+     *
+     * @param uri The data in the content provider being queried.
+     * @param mimeTypeFilter The type of data the client desires.  May be
+     * a pattern, such as *&#47;*, if the caller does not have specific type
+     * requirements; in this case the content provider will pick its best
+     * type matching the pattern.
+     * @param opts Additional options from the client.  The definitions of
+     * these are specific to the content provider being called.
+     * @param signal A signal to cancel the operation in progress, or
+     *            {@code null} if none. For example, if you are downloading a
+     *            file from the network to service a "rw" mode request, you
+     *            should periodically call
+     *            {@link CancellationSignal#throwIfCanceled()} to check whether
+     *            the client has canceled the request and abort the download.
+     *
+     * @return Returns a new AssetFileDescriptor from which the client can
+     * read data of the desired type.
+     *
+     * @throws FileNotFoundException Throws FileNotFoundException if there is
+     * no file associated with the given URI or the mode is invalid.
+     * @throws SecurityException Throws SecurityException if the caller does
+     * not have permission to access the data.
+     * @throws IllegalArgumentException Throws IllegalArgumentException if the
+     * content provider does not support the requested MIME type.
+     *
+     * @see #getStreamTypes(Uri, String)
+     * @see #openAssetFile(Uri, String)
+     * @see ClipDescription#compareMimeTypes(String, String)
+     */
+    @Override
+    public @Nullable AssetFileDescriptor openTypedAssetFile(@NonNull Uri uri,
+            @NonNull String mimeTypeFilter, @Nullable Bundle opts,
+            @Nullable CancellationSignal signal) throws FileNotFoundException {
+        return openTypedAssetFile(uri, mimeTypeFilter, opts);
+    }
+
+    /**
+     * Interface to write a stream of data to a pipe.  Use with
+     * {@link ContentProvider#openPipeHelper}.
+     */
+    public interface PipeDataWriter<T> {
+        /**
+         * Called from a background thread to stream data out to a pipe.
+         * Note that the pipe is blocking, so this thread can block on
+         * writes for an arbitrary amount of time if the client is slow
+         * at reading.
+         *
+         * @param output The pipe where data should be written.  This will be
+         * closed for you upon returning from this function.
+         * @param uri The URI whose data is to be written.
+         * @param mimeType The desired type of data to be written.
+         * @param opts Options supplied by caller.
+         * @param args Your own custom arguments.
+         */
+        public void writeDataToPipe(@NonNull ParcelFileDescriptor output, @NonNull Uri uri,
+                @NonNull String mimeType, @Nullable Bundle opts, @Nullable T args);
+    }
+
+    /**
+     * A helper function for implementing {@link #openTypedAssetFile}, for
+     * creating a data pipe and background thread allowing you to stream
+     * generated data back to the client.  This function returns a new
+     * ParcelFileDescriptor that should be returned to the caller (the caller
+     * is responsible for closing it).
+     *
+     * @param uri The URI whose data is to be written.
+     * @param mimeType The desired type of data to be written.
+     * @param opts Options supplied by caller.
+     * @param args Your own custom arguments.
+     * @param func Interface implementing the function that will actually
+     * stream the data.
+     * @return Returns a new ParcelFileDescriptor holding the read side of
+     * the pipe.  This should be returned to the caller for reading; the caller
+     * is responsible for closing it when done.
+     */
+    public @NonNull <T> ParcelFileDescriptor openPipeHelper(final @NonNull Uri uri,
+            final @NonNull String mimeType, final @Nullable Bundle opts, final @Nullable T args,
+            final @NonNull PipeDataWriter<T> func) throws FileNotFoundException {
+        try {
+            final ParcelFileDescriptor[] fds = ParcelFileDescriptor.createPipe();
+
+            AsyncTask<Object, Object, Object> task = new AsyncTask<Object, Object, Object>() {
+                @Override
+                protected Object doInBackground(Object... params) {
+                    func.writeDataToPipe(fds[1], uri, mimeType, opts, args);
+                    try {
+                        fds[1].close();
+                    } catch (IOException e) {
+                        Log.w(TAG, "Failure closing pipe", e);
+                    }
+                    return null;
+                }
+            };
+            task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Object[])null);
+
+            return fds[0];
+        } catch (IOException e) {
+            throw new FileNotFoundException("failure making pipe");
+        }
+    }
+
+    /**
+     * Returns true if this instance is a temporary content provider.
+     * @return true if this instance is a temporary content provider
+     */
+    protected boolean isTemporary() {
+        return false;
+    }
+
+    /**
+     * Returns the Binder object for this provider.
+     *
+     * @return the Binder object for this provider
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public IContentProvider getIContentProvider() {
+        return mTransport;
+    }
+
+    /**
+     * Like {@link #attachInfo(Context, android.content.pm.ProviderInfo)}, but for use
+     * when directly instantiating the provider for testing.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public void attachInfoForTesting(Context context, ProviderInfo info) {
+        attachInfo(context, info, true);
+    }
+
+    /**
+     * After being instantiated, this is called to tell the content provider
+     * about itself.
+     *
+     * @param context The context this provider is running in
+     * @param info Registered information about this content provider
+     */
+    public void attachInfo(Context context, ProviderInfo info) {
+        attachInfo(context, info, false);
+    }
+
+    private void attachInfo(Context context, ProviderInfo info, boolean testing) {
+        mNoPerms = testing;
+        mCallingPackage = new ThreadLocal<>();
+
+        /*
+         * Only allow it to be set once, so after the content service gives
+         * this to us clients can't change it.
+         */
+        if (mContext == null) {
+            mContext = context;
+            if (context != null && mTransport != null) {
+                mTransport.mAppOpsManager = (AppOpsManager) context.getSystemService(
+                        Context.APP_OPS_SERVICE);
+            }
+            mMyUid = Process.myUid();
+            if (info != null) {
+                setReadPermission(info.readPermission);
+                setWritePermission(info.writePermission);
+                setPathPermissions(info.pathPermissions);
+                mExported = info.exported;
+                mSingleUser = (info.flags & ProviderInfo.FLAG_SINGLE_USER) != 0;
+                setAuthorities(info.authority);
+            }
+            if (Build.IS_DEBUGGABLE) {
+                setTransportLoggingEnabled(Log.isLoggable(getClass().getSimpleName(),
+                        Log.VERBOSE));
+            }
+            ContentProvider.this.onCreate();
+        }
+    }
+
+    /**
+     * Override this to handle requests to perform a batch of operations, or the
+     * default implementation will iterate over the operations and call
+     * {@link ContentProviderOperation#apply} on each of them.
+     * If all calls to {@link ContentProviderOperation#apply} succeed
+     * then a {@link ContentProviderResult} array with as many
+     * elements as there were operations will be returned.  If any of the calls
+     * fail, it is up to the implementation how many of the others take effect.
+     * This method can be called from multiple threads, as described in
+     * <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes
+     * and Threads</a>.
+     *
+     * @param operations the operations to apply
+     * @return the results of the applications
+     * @throws OperationApplicationException thrown if any operation fails.
+     * @see ContentProviderOperation#apply
+     */
+    @Override
+    public @NonNull ContentProviderResult[] applyBatch(@NonNull String authority,
+            @NonNull ArrayList<ContentProviderOperation> operations)
+                    throws OperationApplicationException {
+        return applyBatch(operations);
+    }
+
+    public @NonNull ContentProviderResult[] applyBatch(
+            @NonNull ArrayList<ContentProviderOperation> operations)
+                    throws OperationApplicationException {
+        final int numOperations = operations.size();
+        final ContentProviderResult[] results = new ContentProviderResult[numOperations];
+        for (int i = 0; i < numOperations; i++) {
+            results[i] = operations.get(i).apply(this, results, i);
+        }
+        return results;
+    }
+
+    /**
+     * Call a provider-defined method.  This can be used to implement
+     * interfaces that are cheaper and/or unnatural for a table-like
+     * model.
+     *
+     * <p class="note"><strong>WARNING:</strong> The framework does no permission checking
+     * on this entry into the content provider besides the basic ability for the application
+     * to get access to the provider at all.  For example, it has no idea whether the call
+     * being executed may read or write data in the provider, so can't enforce those
+     * individual permissions.  Any implementation of this method <strong>must</strong>
+     * do its own permission checks on incoming calls to make sure they are allowed.</p>
+     *
+     * @param method method name to call.  Opaque to framework, but should not be {@code null}.
+     * @param arg provider-defined String argument.  May be {@code null}.
+     * @param extras provider-defined Bundle argument.  May be {@code null}.
+     * @return provider-defined return value.  May be {@code null}, which is also
+     *   the default for providers which don't implement any call methods.
+     */
+    @Override
+    public @Nullable Bundle call(@NonNull String authority, @NonNull String method,
+            @Nullable String arg, @Nullable Bundle extras) {
+        return call(method, arg, extras);
+    }
+
+    public @Nullable Bundle call(@NonNull String method, @Nullable String arg,
+            @Nullable Bundle extras) {
+        return null;
+    }
+
+    /**
+     * Implement this to shut down the ContentProvider instance. You can then
+     * invoke this method in unit tests.
+     *
+     * <p>
+     * Android normally handles ContentProvider startup and shutdown
+     * automatically. You do not need to start up or shut down a
+     * ContentProvider. When you invoke a test method on a ContentProvider,
+     * however, a ContentProvider instance is started and keeps running after
+     * the test finishes, even if a succeeding test instantiates another
+     * ContentProvider. A conflict develops because the two instances are
+     * usually running against the same underlying data source (for example, an
+     * sqlite database).
+     * </p>
+     * <p>
+     * Implementing shutDown() avoids this conflict by providing a way to
+     * terminate the ContentProvider. This method can also prevent memory leaks
+     * from multiple instantiations of the ContentProvider, and it can ensure
+     * unit test isolation by allowing you to completely clean up the test
+     * fixture before moving on to the next test.
+     * </p>
+     */
+    public void shutdown() {
+        Log.w(TAG, "implement ContentProvider shutdown() to make sure all database " +
+                "connections are gracefully shutdown");
+    }
+
+    /**
+     * Print the Provider's state into the given stream.  This gets invoked if
+     * you run "adb shell dumpsys activity provider &lt;provider_component_name&gt;".
+     *
+     * @param fd The raw file descriptor that the dump is being sent to.
+     * @param writer The PrintWriter to which you should dump your state.  This will be
+     * closed for you after you return.
+     * @param args additional arguments to the dump request.
+     */
+    public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+        writer.println("nothing to dump");
+    }
+
+    private void validateIncomingAuthority(String authority) throws SecurityException {
+        if (!matchesOurAuthorities(getAuthorityWithoutUserId(authority))) {
+            String message = "The authority " + authority + " does not match the one of the "
+                    + "contentProvider: ";
+            if (mAuthority != null) {
+                message += mAuthority;
+            } else {
+                message += Arrays.toString(mAuthorities);
+            }
+            throw new SecurityException(message);
+        }
+    }
+
+    /** @hide */
+    @VisibleForTesting
+    public Uri validateIncomingUri(Uri uri) throws SecurityException {
+        String auth = uri.getAuthority();
+        if (!mSingleUser) {
+            int userId = getUserIdFromAuthority(auth, UserHandle.USER_CURRENT);
+            if (userId != UserHandle.USER_CURRENT && userId != mContext.getUserId()) {
+                throw new SecurityException("trying to query a ContentProvider in user "
+                        + mContext.getUserId() + " with a uri belonging to user " + userId);
+            }
+        }
+        validateIncomingAuthority(auth);
+
+        // Normalize the path by removing any empty path segments, which can be
+        // a source of security issues.
+        final String encodedPath = uri.getEncodedPath();
+        if (encodedPath != null && encodedPath.indexOf("//") != -1) {
+            final Uri normalized = uri.buildUpon()
+                    .encodedPath(encodedPath.replaceAll("//+", "/")).build();
+            Log.w(TAG, "Normalized " + uri + " to " + normalized
+                    + " to avoid possible security issues");
+            return normalized;
+        } else {
+            return uri;
+        }
+    }
+
+    /** @hide */
+    private Uri maybeGetUriWithoutUserId(Uri uri) {
+        if (mSingleUser) {
+            return uri;
+        }
+        return getUriWithoutUserId(uri);
+    }
+
+    /** @hide */
+    public static int getUserIdFromAuthority(String auth, int defaultUserId) {
+        if (auth == null) return defaultUserId;
+        int end = auth.lastIndexOf('@');
+        if (end == -1) return defaultUserId;
+        String userIdString = auth.substring(0, end);
+        try {
+            return Integer.parseInt(userIdString);
+        } catch (NumberFormatException e) {
+            Log.w(TAG, "Error parsing userId.", e);
+            return UserHandle.USER_NULL;
+        }
+    }
+
+    /** @hide */
+    public static int getUserIdFromAuthority(String auth) {
+        return getUserIdFromAuthority(auth, UserHandle.USER_CURRENT);
+    }
+
+    /** @hide */
+    public static int getUserIdFromUri(Uri uri, int defaultUserId) {
+        if (uri == null) return defaultUserId;
+        return getUserIdFromAuthority(uri.getAuthority(), defaultUserId);
+    }
+
+    /** @hide */
+    public static int getUserIdFromUri(Uri uri) {
+        return getUserIdFromUri(uri, UserHandle.USER_CURRENT);
+    }
+
+    /**
+     * Returns the user associated with the given URI.
+     *
+     * @hide
+     */
+    @TestApi
+    public @NonNull static UserHandle getUserHandleFromUri(@NonNull Uri uri) {
+        return UserHandle.of(getUserIdFromUri(uri, Process.myUserHandle().getIdentifier()));
+    }
+
+    /**
+     * Removes userId part from authority string. Expects format:
+     * [email protected]
+     * If there is no userId in the authority, it symply returns the argument
+     * @hide
+     */
+    public static String getAuthorityWithoutUserId(String auth) {
+        if (auth == null) return null;
+        int end = auth.lastIndexOf('@');
+        return auth.substring(end+1);
+    }
+
+    /** @hide */
+    public static Uri getUriWithoutUserId(Uri uri) {
+        if (uri == null) return null;
+        Uri.Builder builder = uri.buildUpon();
+        builder.authority(getAuthorityWithoutUserId(uri.getAuthority()));
+        return builder.build();
+    }
+
+    /** @hide */
+    public static boolean uriHasUserId(Uri uri) {
+        if (uri == null) return false;
+        return !TextUtils.isEmpty(uri.getUserInfo());
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage
+    public static Uri maybeAddUserId(Uri uri, int userId) {
+        if (uri == null) return null;
+        if (userId != UserHandle.USER_CURRENT
+                && ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
+            if (!uriHasUserId(uri)) {
+                //We don't add the user Id if there's already one
+                Uri.Builder builder = uri.buildUpon();
+                builder.encodedAuthority("" + userId + "@" + uri.getEncodedAuthority());
+                return builder.build();
+            }
+        }
+        return uri;
+    }
+}
diff --git a/android/content/ContentProviderClient.java b/android/content/ContentProviderClient.java
new file mode 100644
index 0000000..d0f5ec4
--- /dev/null
+++ b/android/content/ContentProviderClient.java
@@ -0,0 +1,722 @@
+/*
+ * 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.content;
+
+import android.annotation.DurationMillisLong;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.res.AssetFileDescriptor;
+import android.database.CrossProcessCursorWrapper;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.DeadObjectException;
+import android.os.Handler;
+import android.os.ICancellationSignal;
+import android.os.Looper;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+
+import dalvik.system.CloseGuard;
+
+import libcore.io.IoUtils;
+
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * The public interface object used to interact with a specific
+ * {@link ContentProvider}.
+ * <p>
+ * Instances can be obtained by calling
+ * {@link ContentResolver#acquireContentProviderClient} or
+ * {@link ContentResolver#acquireUnstableContentProviderClient}. Instances must
+ * be released using {@link #close()} in order to indicate to the system that
+ * the underlying {@link ContentProvider} is no longer needed and can be killed
+ * to free up resources.
+ * <p>
+ * Note that you should generally create a new ContentProviderClient instance
+ * for each thread that will be performing operations. Unlike
+ * {@link ContentResolver}, the methods here such as {@link #query} and
+ * {@link #openFile} are not thread safe -- you must not call {@link #close()}
+ * on the ContentProviderClient those calls are made from until you are finished
+ * with the data they have returned.
+ */
+public class ContentProviderClient implements ContentInterface, AutoCloseable {
+    private static final String TAG = "ContentProviderClient";
+
+    @GuardedBy("ContentProviderClient.class")
+    private static Handler sAnrHandler;
+
+    private final ContentResolver mContentResolver;
+    @UnsupportedAppUsage
+    private final IContentProvider mContentProvider;
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    private final String mPackageName;
+    private final @Nullable String mAttributionTag;
+    private final String mAuthority;
+    private final boolean mStable;
+
+    private final AtomicBoolean mClosed = new AtomicBoolean();
+    private final CloseGuard mCloseGuard = CloseGuard.get();
+
+    private long mAnrTimeout;
+    private NotRespondingRunnable mAnrRunnable;
+
+    /** {@hide} */
+    @VisibleForTesting
+    public ContentProviderClient(ContentResolver contentResolver, IContentProvider contentProvider,
+            boolean stable) {
+        // Only used for testing, so use a fake authority
+        this(contentResolver, contentProvider, "unknown", stable);
+    }
+
+    /** {@hide} */
+    public ContentProviderClient(ContentResolver contentResolver, IContentProvider contentProvider,
+            String authority, boolean stable) {
+        mContentResolver = contentResolver;
+        mContentProvider = contentProvider;
+        mPackageName = contentResolver.mPackageName;
+        mAttributionTag = contentResolver.mAttributionTag;
+
+        mAuthority = authority;
+        mStable = stable;
+
+        mCloseGuard.open("close");
+    }
+
+    /**
+     * Configure this client to automatically detect and kill the remote
+     * provider when an "application not responding" event is detected.
+     *
+     * @param timeoutMillis the duration for which a pending call is allowed
+     *            block before the remote provider is considered to be
+     *            unresponsive. Set to {@code 0} to allow pending calls to block
+     *            indefinitely with no action taken.
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.REMOVE_TASKS)
+    public void setDetectNotResponding(@DurationMillisLong long timeoutMillis) {
+        synchronized (ContentProviderClient.class) {
+            mAnrTimeout = timeoutMillis;
+
+            if (timeoutMillis > 0) {
+                if (mAnrRunnable == null) {
+                    mAnrRunnable = new NotRespondingRunnable();
+                }
+                if (sAnrHandler == null) {
+                    sAnrHandler = new Handler(Looper.getMainLooper(), null, true /* async */);
+                }
+
+                // If the remote process hangs, we're going to kill it, so we're
+                // technically okay doing blocking calls.
+                Binder.allowBlocking(mContentProvider.asBinder());
+            } else {
+                mAnrRunnable = null;
+
+                // If we're no longer watching for hangs, revert back to default
+                // blocking behavior.
+                Binder.defaultBlocking(mContentProvider.asBinder());
+            }
+        }
+    }
+
+    private void beforeRemote() {
+        if (mAnrRunnable != null) {
+            sAnrHandler.postDelayed(mAnrRunnable, mAnrTimeout);
+        }
+    }
+
+    private void afterRemote() {
+        if (mAnrRunnable != null) {
+            sAnrHandler.removeCallbacks(mAnrRunnable);
+        }
+    }
+
+    /** See {@link ContentProvider#query ContentProvider.query} */
+    public @Nullable Cursor query(@NonNull Uri url, @Nullable String[] projection,
+            @Nullable String selection, @Nullable String[] selectionArgs,
+            @Nullable String sortOrder) throws RemoteException {
+        return query(url, projection, selection,  selectionArgs, sortOrder, null);
+    }
+
+    /** See {@link ContentProvider#query ContentProvider.query} */
+    public @Nullable Cursor query(@NonNull Uri uri, @Nullable String[] projection,
+            @Nullable String selection, @Nullable String[] selectionArgs,
+            @Nullable String sortOrder, @Nullable CancellationSignal cancellationSignal)
+                    throws RemoteException {
+        Bundle queryArgs =
+                ContentResolver.createSqlQueryBundle(selection, selectionArgs, sortOrder);
+        return query(uri, projection, queryArgs, cancellationSignal);
+    }
+
+    /** See {@link ContentProvider#query ContentProvider.query} */
+    @Override
+    public @Nullable Cursor query(@NonNull Uri uri, @Nullable String[] projection,
+            Bundle queryArgs, @Nullable CancellationSignal cancellationSignal)
+                    throws RemoteException {
+        Objects.requireNonNull(uri, "url");
+
+        beforeRemote();
+        try {
+            ICancellationSignal remoteCancellationSignal = null;
+            if (cancellationSignal != null) {
+                cancellationSignal.throwIfCanceled();
+                remoteCancellationSignal = mContentProvider.createCancellationSignal();
+                cancellationSignal.setRemote(remoteCancellationSignal);
+            }
+            final Cursor cursor = mContentProvider.query(
+                    mPackageName, mAttributionTag, uri, projection, queryArgs,
+                    remoteCancellationSignal);
+            if (cursor == null) {
+                return null;
+            }
+            return new CursorWrapperInner(cursor);
+        } catch (DeadObjectException e) {
+            if (!mStable) {
+                mContentResolver.unstableProviderDied(mContentProvider);
+            }
+            throw e;
+        } finally {
+            afterRemote();
+        }
+    }
+
+    /** See {@link ContentProvider#getType ContentProvider.getType} */
+    @Override
+    public @Nullable String getType(@NonNull Uri url) throws RemoteException {
+        Objects.requireNonNull(url, "url");
+
+        beforeRemote();
+        try {
+            return mContentProvider.getType(url);
+        } catch (DeadObjectException e) {
+            if (!mStable) {
+                mContentResolver.unstableProviderDied(mContentProvider);
+            }
+            throw e;
+        } finally {
+            afterRemote();
+        }
+    }
+
+    /** See {@link ContentProvider#getStreamTypes ContentProvider.getStreamTypes} */
+    @Override
+    public @Nullable String[] getStreamTypes(@NonNull Uri url, @NonNull String mimeTypeFilter)
+            throws RemoteException {
+        Objects.requireNonNull(url, "url");
+        Objects.requireNonNull(mimeTypeFilter, "mimeTypeFilter");
+
+        beforeRemote();
+        try {
+            return mContentProvider.getStreamTypes(url, mimeTypeFilter);
+        } catch (DeadObjectException e) {
+            if (!mStable) {
+                mContentResolver.unstableProviderDied(mContentProvider);
+            }
+            throw e;
+        } finally {
+            afterRemote();
+        }
+    }
+
+    /** See {@link ContentProvider#canonicalize} */
+    @Override
+    public final @Nullable Uri canonicalize(@NonNull Uri url) throws RemoteException {
+        Objects.requireNonNull(url, "url");
+
+        beforeRemote();
+        try {
+            return mContentProvider.canonicalize(mPackageName, mAttributionTag, url);
+        } catch (DeadObjectException e) {
+            if (!mStable) {
+                mContentResolver.unstableProviderDied(mContentProvider);
+            }
+            throw e;
+        } finally {
+            afterRemote();
+        }
+    }
+
+    /** See {@link ContentProvider#uncanonicalize} */
+    @Override
+    public final @Nullable Uri uncanonicalize(@NonNull Uri url) throws RemoteException {
+        Objects.requireNonNull(url, "url");
+
+        beforeRemote();
+        try {
+            return mContentProvider.uncanonicalize(mPackageName, mAttributionTag, url);
+        } catch (DeadObjectException e) {
+            if (!mStable) {
+                mContentResolver.unstableProviderDied(mContentProvider);
+            }
+            throw e;
+        } finally {
+            afterRemote();
+        }
+    }
+
+    /** See {@link ContentProvider#refresh} */
+    @Override
+    public boolean refresh(Uri url, @Nullable Bundle extras,
+            @Nullable CancellationSignal cancellationSignal) throws RemoteException {
+        Objects.requireNonNull(url, "url");
+
+        beforeRemote();
+        try {
+            ICancellationSignal remoteCancellationSignal = null;
+            if (cancellationSignal != null) {
+                cancellationSignal.throwIfCanceled();
+                remoteCancellationSignal = mContentProvider.createCancellationSignal();
+                cancellationSignal.setRemote(remoteCancellationSignal);
+            }
+            return mContentProvider.refresh(mPackageName, mAttributionTag, url, extras,
+                    remoteCancellationSignal);
+        } catch (DeadObjectException e) {
+            if (!mStable) {
+                mContentResolver.unstableProviderDied(mContentProvider);
+            }
+            throw e;
+        } finally {
+            afterRemote();
+        }
+    }
+
+    /** {@hide} */
+    @Override
+    public int checkUriPermission(@NonNull Uri uri, int uid, @Intent.AccessUriMode int modeFlags)
+            throws RemoteException {
+        Objects.requireNonNull(uri, "uri");
+
+        beforeRemote();
+        try {
+            return mContentProvider.checkUriPermission(mPackageName, mAttributionTag, uri, uid,
+                    modeFlags);
+        } catch (DeadObjectException e) {
+            if (!mStable) {
+                mContentResolver.unstableProviderDied(mContentProvider);
+            }
+            throw e;
+        } finally {
+            afterRemote();
+        }
+    }
+
+    /** See {@link ContentProvider#insert ContentProvider.insert} */
+    public @Nullable Uri insert(@NonNull Uri url, @Nullable ContentValues initialValues)
+            throws RemoteException {
+        return insert(url, initialValues, null);
+    }
+
+    /** See {@link ContentProvider#insert ContentProvider.insert} */
+    @Override
+    public @Nullable Uri insert(@NonNull Uri url, @Nullable ContentValues initialValues,
+            @Nullable Bundle extras) throws RemoteException {
+        Objects.requireNonNull(url, "url");
+
+        beforeRemote();
+        try {
+            return mContentProvider.insert(mPackageName, mAttributionTag, url, initialValues,
+                    extras);
+        } catch (DeadObjectException e) {
+            if (!mStable) {
+                mContentResolver.unstableProviderDied(mContentProvider);
+            }
+            throw e;
+        } finally {
+            afterRemote();
+        }
+    }
+
+    /** See {@link ContentProvider#bulkInsert ContentProvider.bulkInsert} */
+    @Override
+    public int bulkInsert(@NonNull Uri url, @NonNull ContentValues[] initialValues)
+            throws RemoteException {
+        Objects.requireNonNull(url, "url");
+        Objects.requireNonNull(initialValues, "initialValues");
+
+        beforeRemote();
+        try {
+            return mContentProvider.bulkInsert(mPackageName, mAttributionTag, url, initialValues);
+        } catch (DeadObjectException e) {
+            if (!mStable) {
+                mContentResolver.unstableProviderDied(mContentProvider);
+            }
+            throw e;
+        } finally {
+            afterRemote();
+        }
+    }
+
+    /** See {@link ContentProvider#delete ContentProvider.delete} */
+    public int delete(@NonNull Uri url, @Nullable String selection,
+            @Nullable String[] selectionArgs) throws RemoteException {
+        return delete(url, ContentResolver.createSqlQueryBundle(selection, selectionArgs));
+    }
+
+    /** See {@link ContentProvider#delete ContentProvider.delete} */
+    @Override
+    public int delete(@NonNull Uri url, @Nullable Bundle extras) throws RemoteException {
+        Objects.requireNonNull(url, "url");
+
+        beforeRemote();
+        try {
+            return mContentProvider.delete(mPackageName, mAttributionTag, url, extras);
+        } catch (DeadObjectException e) {
+            if (!mStable) {
+                mContentResolver.unstableProviderDied(mContentProvider);
+            }
+            throw e;
+        } finally {
+            afterRemote();
+        }
+    }
+
+    /** See {@link ContentProvider#update ContentProvider.update} */
+    public int update(@NonNull Uri url, @Nullable ContentValues values, @Nullable String selection,
+            @Nullable String[] selectionArgs) throws RemoteException {
+        return update(url, values, ContentResolver.createSqlQueryBundle(selection, selectionArgs));
+    }
+
+    /** See {@link ContentProvider#update ContentProvider.update} */
+    @Override
+    public int update(@NonNull Uri url, @Nullable ContentValues values, @Nullable Bundle extras)
+            throws RemoteException {
+        Objects.requireNonNull(url, "url");
+
+        beforeRemote();
+        try {
+            return mContentProvider.update(mPackageName, mAttributionTag, url, values, extras);
+        } catch (DeadObjectException e) {
+            if (!mStable) {
+                mContentResolver.unstableProviderDied(mContentProvider);
+            }
+            throw e;
+        } finally {
+            afterRemote();
+        }
+    }
+
+    /**
+     * See {@link ContentProvider#openFile ContentProvider.openFile}.  Note that
+     * this <em>does not</em>
+     * take care of non-content: URIs such as file:.  It is strongly recommended
+     * you use the {@link ContentResolver#openFileDescriptor
+     * ContentResolver.openFileDescriptor} API instead.
+     */
+    public @Nullable ParcelFileDescriptor openFile(@NonNull Uri url, @NonNull String mode)
+            throws RemoteException, FileNotFoundException {
+        return openFile(url, mode, null);
+    }
+
+    /**
+     * See {@link ContentProvider#openFile ContentProvider.openFile}.  Note that
+     * this <em>does not</em>
+     * take care of non-content: URIs such as file:.  It is strongly recommended
+     * you use the {@link ContentResolver#openFileDescriptor
+     * ContentResolver.openFileDescriptor} API instead.
+     */
+    @Override
+    public @Nullable ParcelFileDescriptor openFile(@NonNull Uri url, @NonNull String mode,
+            @Nullable CancellationSignal signal) throws RemoteException, FileNotFoundException {
+        Objects.requireNonNull(url, "url");
+        Objects.requireNonNull(mode, "mode");
+
+        beforeRemote();
+        try {
+            ICancellationSignal remoteSignal = null;
+            if (signal != null) {
+                signal.throwIfCanceled();
+                remoteSignal = mContentProvider.createCancellationSignal();
+                signal.setRemote(remoteSignal);
+            }
+            return mContentProvider.openFile(mPackageName, mAttributionTag, url, mode,
+                    remoteSignal, null);
+        } catch (DeadObjectException e) {
+            if (!mStable) {
+                mContentResolver.unstableProviderDied(mContentProvider);
+            }
+            throw e;
+        } finally {
+            afterRemote();
+        }
+    }
+
+    /**
+     * See {@link ContentProvider#openAssetFile ContentProvider.openAssetFile}.
+     * Note that this <em>does not</em>
+     * take care of non-content: URIs such as file:.  It is strongly recommended
+     * you use the {@link ContentResolver#openAssetFileDescriptor
+     * ContentResolver.openAssetFileDescriptor} API instead.
+     */
+    public @Nullable AssetFileDescriptor openAssetFile(@NonNull Uri url, @NonNull String mode)
+            throws RemoteException, FileNotFoundException {
+        return openAssetFile(url, mode, null);
+    }
+
+    /**
+     * See {@link ContentProvider#openAssetFile ContentProvider.openAssetFile}.
+     * Note that this <em>does not</em>
+     * take care of non-content: URIs such as file:.  It is strongly recommended
+     * you use the {@link ContentResolver#openAssetFileDescriptor
+     * ContentResolver.openAssetFileDescriptor} API instead.
+     */
+    @Override
+    public @Nullable AssetFileDescriptor openAssetFile(@NonNull Uri url, @NonNull String mode,
+            @Nullable CancellationSignal signal) throws RemoteException, FileNotFoundException {
+        Objects.requireNonNull(url, "url");
+        Objects.requireNonNull(mode, "mode");
+
+        beforeRemote();
+        try {
+            ICancellationSignal remoteSignal = null;
+            if (signal != null) {
+                signal.throwIfCanceled();
+                remoteSignal = mContentProvider.createCancellationSignal();
+                signal.setRemote(remoteSignal);
+            }
+            return mContentProvider.openAssetFile(mPackageName, mAttributionTag, url, mode,
+                    remoteSignal);
+        } catch (DeadObjectException e) {
+            if (!mStable) {
+                mContentResolver.unstableProviderDied(mContentProvider);
+            }
+            throw e;
+        } finally {
+            afterRemote();
+        }
+    }
+
+    /** See {@link ContentProvider#openTypedAssetFile ContentProvider.openTypedAssetFile} */
+    public final @Nullable AssetFileDescriptor openTypedAssetFileDescriptor(@NonNull Uri uri,
+            @NonNull String mimeType, @Nullable Bundle opts)
+                    throws RemoteException, FileNotFoundException {
+        return openTypedAssetFileDescriptor(uri, mimeType, opts, null);
+    }
+
+    /** See {@link ContentProvider#openTypedAssetFile ContentProvider.openTypedAssetFile} */
+    public final @Nullable AssetFileDescriptor openTypedAssetFileDescriptor(@NonNull Uri uri,
+            @NonNull String mimeType, @Nullable Bundle opts, @Nullable CancellationSignal signal)
+                    throws RemoteException, FileNotFoundException {
+        return openTypedAssetFile(uri, mimeType, opts, signal);
+    }
+
+    @Override
+    public final @Nullable AssetFileDescriptor openTypedAssetFile(@NonNull Uri uri,
+            @NonNull String mimeTypeFilter, @Nullable Bundle opts,
+            @Nullable CancellationSignal signal) throws RemoteException, FileNotFoundException {
+        Objects.requireNonNull(uri, "uri");
+        Objects.requireNonNull(mimeTypeFilter, "mimeTypeFilter");
+
+        beforeRemote();
+        try {
+            ICancellationSignal remoteSignal = null;
+            if (signal != null) {
+                signal.throwIfCanceled();
+                remoteSignal = mContentProvider.createCancellationSignal();
+                signal.setRemote(remoteSignal);
+            }
+            return mContentProvider.openTypedAssetFile(
+                    mPackageName, mAttributionTag, uri, mimeTypeFilter, opts, remoteSignal);
+        } catch (DeadObjectException e) {
+            if (!mStable) {
+                mContentResolver.unstableProviderDied(mContentProvider);
+            }
+            throw e;
+        } finally {
+            afterRemote();
+        }
+    }
+
+    /** See {@link ContentProvider#applyBatch ContentProvider.applyBatch} */
+    public @NonNull ContentProviderResult[] applyBatch(
+            @NonNull ArrayList<ContentProviderOperation> operations)
+            throws RemoteException, OperationApplicationException {
+        return applyBatch(mAuthority, operations);
+    }
+
+    /** See {@link ContentProvider#applyBatch ContentProvider.applyBatch} */
+    @Override
+    public @NonNull ContentProviderResult[] applyBatch(@NonNull String authority,
+            @NonNull ArrayList<ContentProviderOperation> operations)
+            throws RemoteException, OperationApplicationException {
+        Objects.requireNonNull(operations, "operations");
+
+        beforeRemote();
+        try {
+            return mContentProvider.applyBatch(mPackageName, mAttributionTag, authority,
+                    operations);
+        } catch (DeadObjectException e) {
+            if (!mStable) {
+                mContentResolver.unstableProviderDied(mContentProvider);
+            }
+            throw e;
+        } finally {
+            afterRemote();
+        }
+    }
+
+    /** See {@link ContentProvider#call(String, String, Bundle)} */
+    public @Nullable Bundle call(@NonNull String method, @Nullable String arg,
+            @Nullable Bundle extras) throws RemoteException {
+        return call(mAuthority, method, arg, extras);
+    }
+
+    /** See {@link ContentProvider#call(String, String, Bundle)} */
+    @Override
+    public @Nullable Bundle call(@NonNull String authority, @NonNull String method,
+            @Nullable String arg, @Nullable Bundle extras) throws RemoteException {
+        Objects.requireNonNull(authority, "authority");
+        Objects.requireNonNull(method, "method");
+
+        beforeRemote();
+        try {
+            return mContentProvider.call(mPackageName, mAttributionTag, authority, method, arg,
+                    extras);
+        } catch (DeadObjectException e) {
+            if (!mStable) {
+                mContentResolver.unstableProviderDied(mContentProvider);
+            }
+            throw e;
+        } finally {
+            afterRemote();
+        }
+    }
+
+    /**
+     * Closes this client connection, indicating to the system that the
+     * underlying {@link ContentProvider} is no longer needed.
+     */
+    @Override
+    public void close() {
+        closeInternal();
+    }
+
+    /**
+     * @deprecated replaced by {@link #close()}.
+     */
+    @Deprecated
+    public boolean release() {
+        return closeInternal();
+    }
+
+    private boolean closeInternal() {
+        mCloseGuard.close();
+        if (mClosed.compareAndSet(false, true)) {
+            // We can't do ANR checks after we cease to exist! Reset any
+            // blocking behavior changes we might have made.
+            setDetectNotResponding(0);
+
+            if (mStable) {
+                return mContentResolver.releaseProvider(mContentProvider);
+            } else {
+                return mContentResolver.releaseUnstableProvider(mContentProvider);
+            }
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            if (mCloseGuard != null) {
+                mCloseGuard.warnIfOpen();
+            }
+
+            close();
+        } finally {
+            super.finalize();
+        }
+    }
+
+    /**
+     * Get a reference to the {@link ContentProvider} that is associated with this
+     * client. If the {@link ContentProvider} is running in a different process then
+     * null will be returned. This can be used if you know you are running in the same
+     * process as a provider, and want to get direct access to its implementation details.
+     *
+     * @return If the associated {@link ContentProvider} is local, returns it.
+     * Otherwise returns null.
+     */
+    public @Nullable ContentProvider getLocalContentProvider() {
+        return ContentProvider.coerceToLocalContentProvider(mContentProvider);
+    }
+
+    /** {@hide} */
+    @Deprecated
+    public static void closeQuietly(ContentProviderClient client) {
+        IoUtils.closeQuietly(client);
+    }
+
+    /** {@hide} */
+    @Deprecated
+    public static void releaseQuietly(ContentProviderClient client) {
+        IoUtils.closeQuietly(client);
+    }
+
+    private class NotRespondingRunnable implements Runnable {
+        @Override
+        public void run() {
+            Log.w(TAG, "Detected provider not responding: " + mContentProvider);
+            mContentResolver.appNotRespondingViaProvider(mContentProvider);
+        }
+    }
+
+    private final class CursorWrapperInner extends CrossProcessCursorWrapper {
+        private final CloseGuard mCloseGuard = CloseGuard.get();
+
+        CursorWrapperInner(Cursor cursor) {
+            super(cursor);
+            mCloseGuard.open("close");
+        }
+
+        @Override
+        public void close() {
+            mCloseGuard.close();
+            super.close();
+        }
+
+        @Override
+        protected void finalize() throws Throwable {
+            try {
+                if (mCloseGuard != null) {
+                    mCloseGuard.warnIfOpen();
+                }
+
+                close();
+            } finally {
+                super.finalize();
+            }
+        }
+    }
+}
diff --git a/android/content/ContentProviderNative.java b/android/content/ContentProviderNative.java
new file mode 100644
index 0000000..7bc5901
--- /dev/null
+++ b/android/content/ContentProviderNative.java
@@ -0,0 +1,928 @@
+/*
+ * 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.content;
+
+import android.annotation.Nullable;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.res.AssetFileDescriptor;
+import android.database.BulkCursorDescriptor;
+import android.database.BulkCursorToCursorAdaptor;
+import android.database.Cursor;
+import android.database.CursorToBulkCursorAdaptor;
+import android.database.DatabaseUtils;
+import android.database.IContentObserver;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.ICancellationSignal;
+import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
+import android.os.Parcelable;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+
+/**
+ * {@hide}
+ */
+abstract public class ContentProviderNative extends Binder implements IContentProvider {
+    public ContentProviderNative()
+    {
+        attachInterface(this, descriptor);
+    }
+
+    /**
+     * Cast a Binder object into a content resolver interface, generating
+     * a proxy if needed.
+     */
+    @UnsupportedAppUsage
+    static public IContentProvider asInterface(IBinder obj)
+    {
+        if (obj == null) {
+            return null;
+        }
+        IContentProvider in =
+            (IContentProvider)obj.queryLocalInterface(descriptor);
+        if (in != null) {
+            return in;
+        }
+
+        return new ContentProviderProxy(obj);
+    }
+
+    /**
+     * Gets the name of the content provider.
+     * Should probably be part of the {@link IContentProvider} interface.
+     * @return The content provider name.
+     */
+    public abstract String getProviderName();
+
+    @Override
+    public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
+            throws RemoteException {
+        try {
+            switch (code) {
+                case QUERY_TRANSACTION:
+                {
+                    data.enforceInterface(IContentProvider.descriptor);
+
+                    String callingPkg = data.readString();
+                    String callingFeatureId = data.readString();
+                    Uri url = Uri.CREATOR.createFromParcel(data);
+
+                    // String[] projection
+                    int num = data.readInt();
+                    String[] projection = null;
+                    if (num > 0) {
+                        projection = new String[num];
+                        for (int i = 0; i < num; i++) {
+                            projection[i] = data.readString();
+                        }
+                    }
+
+                    Bundle queryArgs = data.readBundle();
+                    IContentObserver observer = IContentObserver.Stub.asInterface(
+                            data.readStrongBinder());
+                    ICancellationSignal cancellationSignal = ICancellationSignal.Stub.asInterface(
+                            data.readStrongBinder());
+
+                    Cursor cursor = query(callingPkg, callingFeatureId, url, projection, queryArgs,
+                            cancellationSignal);
+                    if (cursor != null) {
+                        CursorToBulkCursorAdaptor adaptor = null;
+
+                        try {
+                            adaptor = new CursorToBulkCursorAdaptor(cursor, observer,
+                                    getProviderName());
+                            cursor = null;
+
+                            BulkCursorDescriptor d = adaptor.getBulkCursorDescriptor();
+                            adaptor = null;
+
+                            reply.writeNoException();
+                            reply.writeInt(1);
+                            d.writeToParcel(reply, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+                        } finally {
+                            // Close cursor if an exception was thrown while constructing the adaptor.
+                            if (adaptor != null) {
+                                adaptor.close();
+                            }
+                            if (cursor != null) {
+                                cursor.close();
+                            }
+                        }
+                    } else {
+                        reply.writeNoException();
+                        reply.writeInt(0);
+                    }
+
+                    return true;
+                }
+
+                case GET_TYPE_TRANSACTION:
+                {
+                    data.enforceInterface(IContentProvider.descriptor);
+                    Uri url = Uri.CREATOR.createFromParcel(data);
+                    String type = getType(url);
+                    reply.writeNoException();
+                    reply.writeString(type);
+
+                    return true;
+                }
+
+                case GET_TYPE_ASYNC_TRANSACTION: {
+                    data.enforceInterface(IContentProvider.descriptor);
+                    Uri url = Uri.CREATOR.createFromParcel(data);
+                    RemoteCallback callback = RemoteCallback.CREATOR.createFromParcel(data);
+                    getTypeAsync(url, callback);
+                    return true;
+                }
+
+                case INSERT_TRANSACTION:
+                {
+                    data.enforceInterface(IContentProvider.descriptor);
+                    String callingPkg = data.readString();
+                    String featureId = data.readString();
+                    Uri url = Uri.CREATOR.createFromParcel(data);
+                    ContentValues values = ContentValues.CREATOR.createFromParcel(data);
+                    Bundle extras = data.readBundle();
+
+                    Uri out = insert(callingPkg, featureId, url, values, extras);
+                    reply.writeNoException();
+                    Uri.writeToParcel(reply, out);
+                    return true;
+                }
+
+                case BULK_INSERT_TRANSACTION:
+                {
+                    data.enforceInterface(IContentProvider.descriptor);
+                    String callingPkg = data.readString();
+                    String featureId = data.readString();
+                    Uri url = Uri.CREATOR.createFromParcel(data);
+                    ContentValues[] values = data.createTypedArray(ContentValues.CREATOR);
+
+                    int count = bulkInsert(callingPkg, featureId, url, values);
+                    reply.writeNoException();
+                    reply.writeInt(count);
+                    return true;
+                }
+
+                case APPLY_BATCH_TRANSACTION:
+                {
+                    data.enforceInterface(IContentProvider.descriptor);
+                    String callingPkg = data.readString();
+                    String featureId = data.readString();
+                    String authority = data.readString();
+                    final int numOperations = data.readInt();
+                    final ArrayList<ContentProviderOperation> operations =
+                            new ArrayList<>(numOperations);
+                    for (int i = 0; i < numOperations; i++) {
+                        operations.add(i, ContentProviderOperation.CREATOR.createFromParcel(data));
+                    }
+                    final ContentProviderResult[] results = applyBatch(callingPkg, featureId,
+                            authority, operations);
+                    reply.writeNoException();
+                    reply.writeTypedArray(results, 0);
+                    return true;
+                }
+
+                case DELETE_TRANSACTION:
+                {
+                    data.enforceInterface(IContentProvider.descriptor);
+                    String callingPkg = data.readString();
+                    String featureId = data.readString();
+                    Uri url = Uri.CREATOR.createFromParcel(data);
+                    Bundle extras = data.readBundle();
+
+                    int count = delete(callingPkg, featureId, url, extras);
+
+                    reply.writeNoException();
+                    reply.writeInt(count);
+                    return true;
+                }
+
+                case UPDATE_TRANSACTION:
+                {
+                    data.enforceInterface(IContentProvider.descriptor);
+                    String callingPkg = data.readString();
+                    String featureId = data.readString();
+                    Uri url = Uri.CREATOR.createFromParcel(data);
+                    ContentValues values = ContentValues.CREATOR.createFromParcel(data);
+                    Bundle extras = data.readBundle();
+
+                    int count = update(callingPkg, featureId, url, values, extras);
+
+                    reply.writeNoException();
+                    reply.writeInt(count);
+                    return true;
+                }
+
+                case OPEN_FILE_TRANSACTION:
+                {
+                    data.enforceInterface(IContentProvider.descriptor);
+                    String callingPkg = data.readString();
+                    String featureId = data.readString();
+                    Uri url = Uri.CREATOR.createFromParcel(data);
+                    String mode = data.readString();
+                    ICancellationSignal signal = ICancellationSignal.Stub.asInterface(
+                            data.readStrongBinder());
+                    IBinder callerToken = data.readStrongBinder();
+
+                    ParcelFileDescriptor fd;
+                    fd = openFile(callingPkg, featureId, url, mode, signal, callerToken);
+                    reply.writeNoException();
+                    if (fd != null) {
+                        reply.writeInt(1);
+                        fd.writeToParcel(reply,
+                                Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+                    } else {
+                        reply.writeInt(0);
+                    }
+                    return true;
+                }
+
+                case OPEN_ASSET_FILE_TRANSACTION:
+                {
+                    data.enforceInterface(IContentProvider.descriptor);
+                    String callingPkg = data.readString();
+                    String featureId = data.readString();
+                    Uri url = Uri.CREATOR.createFromParcel(data);
+                    String mode = data.readString();
+                    ICancellationSignal signal = ICancellationSignal.Stub.asInterface(
+                            data.readStrongBinder());
+
+                    AssetFileDescriptor fd;
+                    fd = openAssetFile(callingPkg, featureId, url, mode, signal);
+                    reply.writeNoException();
+                    if (fd != null) {
+                        reply.writeInt(1);
+                        fd.writeToParcel(reply,
+                                Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+                    } else {
+                        reply.writeInt(0);
+                    }
+                    return true;
+                }
+
+                case CALL_TRANSACTION:
+                {
+                    data.enforceInterface(IContentProvider.descriptor);
+
+                    String callingPkg = data.readString();
+                    String featureId = data.readString();
+                    String authority = data.readString();
+                    String method = data.readString();
+                    String stringArg = data.readString();
+                    Bundle extras = data.readBundle();
+
+                    Bundle responseBundle = call(callingPkg, featureId, authority, method,
+                            stringArg, extras);
+
+                    reply.writeNoException();
+                    reply.writeBundle(responseBundle);
+                    return true;
+                }
+
+                case GET_STREAM_TYPES_TRANSACTION:
+                {
+                    data.enforceInterface(IContentProvider.descriptor);
+                    Uri url = Uri.CREATOR.createFromParcel(data);
+                    String mimeTypeFilter = data.readString();
+                    String[] types = getStreamTypes(url, mimeTypeFilter);
+                    reply.writeNoException();
+                    reply.writeStringArray(types);
+
+                    return true;
+                }
+
+                case OPEN_TYPED_ASSET_FILE_TRANSACTION:
+                {
+                    data.enforceInterface(IContentProvider.descriptor);
+                    String callingPkg = data.readString();
+                    String featureId = data.readString();
+                    Uri url = Uri.CREATOR.createFromParcel(data);
+                    String mimeType = data.readString();
+                    Bundle opts = data.readBundle();
+                    ICancellationSignal signal = ICancellationSignal.Stub.asInterface(
+                            data.readStrongBinder());
+
+                    AssetFileDescriptor fd;
+                    fd = openTypedAssetFile(callingPkg, featureId, url, mimeType, opts, signal);
+                    reply.writeNoException();
+                    if (fd != null) {
+                        reply.writeInt(1);
+                        fd.writeToParcel(reply,
+                                Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+                    } else {
+                        reply.writeInt(0);
+                    }
+                    return true;
+                }
+
+                case CREATE_CANCELATION_SIGNAL_TRANSACTION:
+                {
+                    data.enforceInterface(IContentProvider.descriptor);
+
+                    ICancellationSignal cancellationSignal = createCancellationSignal();
+                    reply.writeNoException();
+                    reply.writeStrongBinder(cancellationSignal.asBinder());
+                    return true;
+                }
+
+                case CANONICALIZE_TRANSACTION:
+                {
+                    data.enforceInterface(IContentProvider.descriptor);
+                    String callingPkg = data.readString();
+                    String featureId = data.readString();
+                    Uri url = Uri.CREATOR.createFromParcel(data);
+
+                    Uri out = canonicalize(callingPkg, featureId, url);
+                    reply.writeNoException();
+                    Uri.writeToParcel(reply, out);
+                    return true;
+                }
+
+                case CANONICALIZE_ASYNC_TRANSACTION: {
+                    data.enforceInterface(IContentProvider.descriptor);
+                    String callingPkg = data.readString();
+                    String featureId = data.readString();
+                    Uri uri = Uri.CREATOR.createFromParcel(data);
+                    RemoteCallback callback = RemoteCallback.CREATOR.createFromParcel(data);
+                    canonicalizeAsync(callingPkg, featureId, uri, callback);
+                    return true;
+                }
+
+                case UNCANONICALIZE_TRANSACTION:
+                {
+                    data.enforceInterface(IContentProvider.descriptor);
+                    String callingPkg = data.readString();
+                    String featureId = data.readString();
+                    Uri url = Uri.CREATOR.createFromParcel(data);
+
+                    Uri out = uncanonicalize(callingPkg, featureId, url);
+                    reply.writeNoException();
+                    Uri.writeToParcel(reply, out);
+                    return true;
+                }
+
+                case REFRESH_TRANSACTION: {
+                    data.enforceInterface(IContentProvider.descriptor);
+                    String callingPkg = data.readString();
+                    String featureId = data.readString();
+                    Uri url = Uri.CREATOR.createFromParcel(data);
+                    Bundle extras = data.readBundle();
+                    ICancellationSignal signal = ICancellationSignal.Stub.asInterface(
+                            data.readStrongBinder());
+
+                    boolean out = refresh(callingPkg, featureId, url, extras, signal);
+                    reply.writeNoException();
+                    reply.writeInt(out ? 0 : -1);
+                    return true;
+                }
+
+                case CHECK_URI_PERMISSION_TRANSACTION: {
+                    data.enforceInterface(IContentProvider.descriptor);
+                    String callingPkg = data.readString();
+                    String featureId = data.readString();
+                    Uri uri = Uri.CREATOR.createFromParcel(data);
+                    int uid = data.readInt();
+                    int modeFlags = data.readInt();
+
+                    int out = checkUriPermission(callingPkg, featureId, uri, uid, modeFlags);
+                    reply.writeNoException();
+                    reply.writeInt(out);
+                    return true;
+                }
+            }
+        } catch (Exception e) {
+            DatabaseUtils.writeExceptionToParcel(reply, e);
+            return true;
+        }
+
+        return super.onTransact(code, data, reply, flags);
+    }
+
+    @Override
+    public IBinder asBinder()
+    {
+        return this;
+    }
+}
+
+
+final class ContentProviderProxy implements IContentProvider
+{
+    public ContentProviderProxy(IBinder remote)
+    {
+        mRemote = remote;
+    }
+
+    @Override
+    public IBinder asBinder()
+    {
+        return mRemote;
+    }
+
+    @Override
+    public Cursor query(String callingPkg, @Nullable String featureId, Uri url,
+            @Nullable String[] projection, @Nullable Bundle queryArgs,
+            @Nullable ICancellationSignal cancellationSignal)
+            throws RemoteException {
+        BulkCursorToCursorAdaptor adaptor = new BulkCursorToCursorAdaptor();
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        try {
+            data.writeInterfaceToken(IContentProvider.descriptor);
+
+            data.writeString(callingPkg);
+            data.writeString(featureId);
+            url.writeToParcel(data, 0);
+            int length = 0;
+            if (projection != null) {
+                length = projection.length;
+            }
+            data.writeInt(length);
+            for (int i = 0; i < length; i++) {
+                data.writeString(projection[i]);
+            }
+            data.writeBundle(queryArgs);
+            data.writeStrongBinder(adaptor.getObserver().asBinder());
+            data.writeStrongBinder(
+                    cancellationSignal != null ? cancellationSignal.asBinder() : null);
+
+            mRemote.transact(IContentProvider.QUERY_TRANSACTION, data, reply, 0);
+
+            DatabaseUtils.readExceptionFromParcel(reply);
+
+            if (reply.readInt() != 0) {
+                BulkCursorDescriptor d = BulkCursorDescriptor.CREATOR.createFromParcel(reply);
+                Binder.copyAllowBlocking(mRemote, (d.cursor != null) ? d.cursor.asBinder() : null);
+                adaptor.initialize(d);
+            } else {
+                adaptor.close();
+                adaptor = null;
+            }
+            return adaptor;
+        } catch (RemoteException ex) {
+            adaptor.close();
+            throw ex;
+        } catch (RuntimeException ex) {
+            adaptor.close();
+            throw ex;
+        } finally {
+            data.recycle();
+            reply.recycle();
+        }
+    }
+
+    @Override
+    public String getType(Uri url) throws RemoteException
+    {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        try {
+            data.writeInterfaceToken(IContentProvider.descriptor);
+
+            url.writeToParcel(data, 0);
+
+            mRemote.transact(IContentProvider.GET_TYPE_TRANSACTION, data, reply, 0);
+
+            DatabaseUtils.readExceptionFromParcel(reply);
+            String out = reply.readString();
+            return out;
+        } finally {
+            data.recycle();
+            reply.recycle();
+        }
+    }
+
+    @Override
+    /* oneway */ public void getTypeAsync(Uri uri, RemoteCallback callback) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        try {
+            data.writeInterfaceToken(IContentProvider.descriptor);
+
+            uri.writeToParcel(data, 0);
+            callback.writeToParcel(data, 0);
+
+            mRemote.transact(IContentProvider.GET_TYPE_ASYNC_TRANSACTION, data, null,
+                    IBinder.FLAG_ONEWAY);
+        } finally {
+            data.recycle();
+        }
+    }
+
+    @Override
+    public Uri insert(String callingPkg, @Nullable String featureId, Uri url,
+            ContentValues values, Bundle extras) throws RemoteException
+    {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        try {
+            data.writeInterfaceToken(IContentProvider.descriptor);
+
+            data.writeString(callingPkg);
+            data.writeString(featureId);
+            url.writeToParcel(data, 0);
+            values.writeToParcel(data, 0);
+            data.writeBundle(extras);
+
+            mRemote.transact(IContentProvider.INSERT_TRANSACTION, data, reply, 0);
+
+            DatabaseUtils.readExceptionFromParcel(reply);
+            Uri out = Uri.CREATOR.createFromParcel(reply);
+            return out;
+        } finally {
+            data.recycle();
+            reply.recycle();
+        }
+    }
+
+    @Override
+    public int bulkInsert(String callingPkg, @Nullable String featureId, Uri url,
+            ContentValues[] values) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        try {
+            data.writeInterfaceToken(IContentProvider.descriptor);
+
+            data.writeString(callingPkg);
+            data.writeString(featureId);
+            url.writeToParcel(data, 0);
+            data.writeTypedArray(values, 0);
+
+            mRemote.transact(IContentProvider.BULK_INSERT_TRANSACTION, data, reply, 0);
+
+            DatabaseUtils.readExceptionFromParcel(reply);
+            int count = reply.readInt();
+            return count;
+        } finally {
+            data.recycle();
+            reply.recycle();
+        }
+    }
+
+    @Override
+    public ContentProviderResult[] applyBatch(String callingPkg, @Nullable String featureId,
+            String authority, ArrayList<ContentProviderOperation> operations)
+            throws RemoteException, OperationApplicationException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        try {
+            data.writeInterfaceToken(IContentProvider.descriptor);
+            data.writeString(callingPkg);
+            data.writeString(featureId);
+            data.writeString(authority);
+            data.writeInt(operations.size());
+            for (ContentProviderOperation operation : operations) {
+                operation.writeToParcel(data, 0);
+            }
+            mRemote.transact(IContentProvider.APPLY_BATCH_TRANSACTION, data, reply, 0);
+
+            DatabaseUtils.readExceptionWithOperationApplicationExceptionFromParcel(reply);
+            final ContentProviderResult[] results =
+                    reply.createTypedArray(ContentProviderResult.CREATOR);
+            return results;
+        } finally {
+            data.recycle();
+            reply.recycle();
+        }
+    }
+
+    @Override
+    public int delete(String callingPkg, @Nullable String featureId, Uri url, Bundle extras)
+            throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        try {
+            data.writeInterfaceToken(IContentProvider.descriptor);
+
+            data.writeString(callingPkg);
+            data.writeString(featureId);
+            url.writeToParcel(data, 0);
+            data.writeBundle(extras);
+
+            mRemote.transact(IContentProvider.DELETE_TRANSACTION, data, reply, 0);
+
+            DatabaseUtils.readExceptionFromParcel(reply);
+            int count = reply.readInt();
+            return count;
+        } finally {
+            data.recycle();
+            reply.recycle();
+        }
+    }
+
+    @Override
+    public int update(String callingPkg, @Nullable String featureId, Uri url,
+            ContentValues values, Bundle extras) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        try {
+            data.writeInterfaceToken(IContentProvider.descriptor);
+
+            data.writeString(callingPkg);
+            data.writeString(featureId);
+            url.writeToParcel(data, 0);
+            values.writeToParcel(data, 0);
+            data.writeBundle(extras);
+
+            mRemote.transact(IContentProvider.UPDATE_TRANSACTION, data, reply, 0);
+
+            DatabaseUtils.readExceptionFromParcel(reply);
+            int count = reply.readInt();
+            return count;
+        } finally {
+            data.recycle();
+            reply.recycle();
+        }
+    }
+
+    @Override
+    public ParcelFileDescriptor openFile(String callingPkg, @Nullable String featureId, Uri url,
+            String mode, ICancellationSignal signal, IBinder token)
+            throws RemoteException, FileNotFoundException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        try {
+            data.writeInterfaceToken(IContentProvider.descriptor);
+
+            data.writeString(callingPkg);
+            data.writeString(featureId);
+            url.writeToParcel(data, 0);
+            data.writeString(mode);
+            data.writeStrongBinder(signal != null ? signal.asBinder() : null);
+            data.writeStrongBinder(token);
+
+            mRemote.transact(IContentProvider.OPEN_FILE_TRANSACTION, data, reply, 0);
+
+            DatabaseUtils.readExceptionWithFileNotFoundExceptionFromParcel(reply);
+            int has = reply.readInt();
+            ParcelFileDescriptor fd = has != 0 ? ParcelFileDescriptor.CREATOR
+                    .createFromParcel(reply) : null;
+            return fd;
+        } finally {
+            data.recycle();
+            reply.recycle();
+        }
+    }
+
+    @Override
+    public AssetFileDescriptor openAssetFile(String callingPkg, @Nullable String featureId,
+            Uri url, String mode, ICancellationSignal signal)
+            throws RemoteException, FileNotFoundException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        try {
+            data.writeInterfaceToken(IContentProvider.descriptor);
+
+            data.writeString(callingPkg);
+            data.writeString(featureId);
+            url.writeToParcel(data, 0);
+            data.writeString(mode);
+            data.writeStrongBinder(signal != null ? signal.asBinder() : null);
+
+            mRemote.transact(IContentProvider.OPEN_ASSET_FILE_TRANSACTION, data, reply, 0);
+
+            DatabaseUtils.readExceptionWithFileNotFoundExceptionFromParcel(reply);
+            int has = reply.readInt();
+            AssetFileDescriptor fd = has != 0
+                    ? AssetFileDescriptor.CREATOR.createFromParcel(reply) : null;
+            return fd;
+        } finally {
+            data.recycle();
+            reply.recycle();
+        }
+    }
+
+    @Override
+    public Bundle call(String callingPkg, @Nullable String featureId, String authority,
+            String method, String request, Bundle extras) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        try {
+            data.writeInterfaceToken(IContentProvider.descriptor);
+
+            data.writeString(callingPkg);
+            data.writeString(featureId);
+            data.writeString(authority);
+            data.writeString(method);
+            data.writeString(request);
+            data.writeBundle(extras);
+
+            mRemote.transact(IContentProvider.CALL_TRANSACTION, data, reply, 0);
+
+            DatabaseUtils.readExceptionFromParcel(reply);
+            Bundle bundle = reply.readBundle();
+            return bundle;
+        } finally {
+            data.recycle();
+            reply.recycle();
+        }
+    }
+
+    @Override
+    public String[] getStreamTypes(Uri url, String mimeTypeFilter) throws RemoteException
+    {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        try {
+            data.writeInterfaceToken(IContentProvider.descriptor);
+
+            url.writeToParcel(data, 0);
+            data.writeString(mimeTypeFilter);
+
+            mRemote.transact(IContentProvider.GET_STREAM_TYPES_TRANSACTION, data, reply, 0);
+
+            DatabaseUtils.readExceptionFromParcel(reply);
+            String[] out = reply.createStringArray();
+            return out;
+        } finally {
+            data.recycle();
+            reply.recycle();
+        }
+    }
+
+    @Override
+    public AssetFileDescriptor openTypedAssetFile(String callingPkg, @Nullable String featureId,
+            Uri url, String mimeType, Bundle opts, ICancellationSignal signal)
+            throws RemoteException, FileNotFoundException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        try {
+            data.writeInterfaceToken(IContentProvider.descriptor);
+
+            data.writeString(callingPkg);
+            data.writeString(featureId);
+            url.writeToParcel(data, 0);
+            data.writeString(mimeType);
+            data.writeBundle(opts);
+            data.writeStrongBinder(signal != null ? signal.asBinder() : null);
+
+            mRemote.transact(IContentProvider.OPEN_TYPED_ASSET_FILE_TRANSACTION, data, reply, 0);
+
+            DatabaseUtils.readExceptionWithFileNotFoundExceptionFromParcel(reply);
+            int has = reply.readInt();
+            AssetFileDescriptor fd = has != 0
+                    ? AssetFileDescriptor.CREATOR.createFromParcel(reply) : null;
+            return fd;
+        } finally {
+            data.recycle();
+            reply.recycle();
+        }
+    }
+
+    @Override
+    public ICancellationSignal createCancellationSignal() throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        try {
+            data.writeInterfaceToken(IContentProvider.descriptor);
+
+            mRemote.transact(IContentProvider.CREATE_CANCELATION_SIGNAL_TRANSACTION,
+                    data, reply, 0);
+
+            DatabaseUtils.readExceptionFromParcel(reply);
+            ICancellationSignal cancellationSignal = ICancellationSignal.Stub.asInterface(
+                    reply.readStrongBinder());
+            return cancellationSignal;
+        } finally {
+            data.recycle();
+            reply.recycle();
+        }
+    }
+
+    @Override
+    public Uri canonicalize(String callingPkg, @Nullable String featureId, Uri url)
+            throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        try {
+            data.writeInterfaceToken(IContentProvider.descriptor);
+
+            data.writeString(callingPkg);
+            data.writeString(featureId);
+            url.writeToParcel(data, 0);
+
+            mRemote.transact(IContentProvider.CANONICALIZE_TRANSACTION, data, reply, 0);
+
+            DatabaseUtils.readExceptionFromParcel(reply);
+            Uri out = Uri.CREATOR.createFromParcel(reply);
+            return out;
+        } finally {
+            data.recycle();
+            reply.recycle();
+        }
+    }
+
+    @Override
+    /* oneway */ public void canonicalizeAsync(String callingPkg, @Nullable String featureId,
+            Uri uri, RemoteCallback callback) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        try {
+            data.writeInterfaceToken(IContentProvider.descriptor);
+
+            data.writeString(callingPkg);
+            data.writeString(featureId);
+            uri.writeToParcel(data, 0);
+            callback.writeToParcel(data, 0);
+
+            mRemote.transact(IContentProvider.CANONICALIZE_ASYNC_TRANSACTION, data, null,
+                    Binder.FLAG_ONEWAY);
+        } finally {
+            data.recycle();
+        }
+    }
+
+    @Override
+    public Uri uncanonicalize(String callingPkg, @Nullable String featureId, Uri url)
+            throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        try {
+            data.writeInterfaceToken(IContentProvider.descriptor);
+
+            data.writeString(callingPkg);
+            data.writeString(featureId);
+            url.writeToParcel(data, 0);
+
+            mRemote.transact(IContentProvider.UNCANONICALIZE_TRANSACTION, data, reply, 0);
+
+            DatabaseUtils.readExceptionFromParcel(reply);
+            Uri out = Uri.CREATOR.createFromParcel(reply);
+            return out;
+        } finally {
+            data.recycle();
+            reply.recycle();
+        }
+    }
+
+    @Override
+    public boolean refresh(String callingPkg, @Nullable String featureId, Uri url, Bundle extras,
+            ICancellationSignal signal) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        try {
+            data.writeInterfaceToken(IContentProvider.descriptor);
+
+            data.writeString(callingPkg);
+            data.writeString(featureId);
+            url.writeToParcel(data, 0);
+            data.writeBundle(extras);
+            data.writeStrongBinder(signal != null ? signal.asBinder() : null);
+
+            mRemote.transact(IContentProvider.REFRESH_TRANSACTION, data, reply, 0);
+
+            DatabaseUtils.readExceptionFromParcel(reply);
+            int success = reply.readInt();
+            return (success == 0);
+        } finally {
+            data.recycle();
+            reply.recycle();
+        }
+    }
+
+    @Override
+    public int checkUriPermission(String callingPkg, @Nullable String featureId, Uri url, int uid,
+            int modeFlags) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        try {
+            data.writeInterfaceToken(IContentProvider.descriptor);
+
+            data.writeString(callingPkg);
+            data.writeString(featureId);
+            url.writeToParcel(data, 0);
+            data.writeInt(uid);
+            data.writeInt(modeFlags);
+
+            mRemote.transact(IContentProvider.CHECK_URI_PERMISSION_TRANSACTION, data, reply, 0);
+
+            DatabaseUtils.readExceptionFromParcel(reply);
+            return reply.readInt();
+        } finally {
+            data.recycle();
+            reply.recycle();
+        }
+    }
+
+    @UnsupportedAppUsage
+    private IBinder mRemote;
+}
diff --git a/android/content/ContentProviderOperation.java b/android/content/ContentProviderOperation.java
new file mode 100644
index 0000000..1fb426e
--- /dev/null
+++ b/android/content/ContentProviderOperation.java
@@ -0,0 +1,1039 @@
+/*
+ * 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.content;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.SparseArray;
+
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * Represents a single operation to be performed as part of a batch of operations.
+ *
+ * @see ContentProvider#applyBatch(ArrayList)
+ */
+public class ContentProviderOperation implements Parcelable {
+    /** @hide exposed for unit tests */
+    @UnsupportedAppUsage
+    public final static int TYPE_INSERT = 1;
+    /** @hide exposed for unit tests */
+    @UnsupportedAppUsage
+    public final static int TYPE_UPDATE = 2;
+    /** @hide exposed for unit tests */
+    @UnsupportedAppUsage
+    public final static int TYPE_DELETE = 3;
+    /** @hide exposed for unit tests */
+    public final static int TYPE_ASSERT = 4;
+    /** @hide exposed for unit tests */
+    public final static int TYPE_CALL = 5;
+
+    @UnsupportedAppUsage
+    private final int mType;
+    @UnsupportedAppUsage
+    private final Uri mUri;
+    private final String mMethod;
+    private final String mArg;
+    private final ArrayMap<String, Object> mValues;
+    private final ArrayMap<String, Object> mExtras;
+    @UnsupportedAppUsage
+    private final String mSelection;
+    private final SparseArray<Object> mSelectionArgs;
+    private final Integer mExpectedCount;
+    private final boolean mYieldAllowed;
+    private final boolean mExceptionAllowed;
+
+    private final static String TAG = "ContentProviderOperation";
+
+    /**
+     * Creates a {@link ContentProviderOperation} by copying the contents of a
+     * {@link Builder}.
+     */
+    private ContentProviderOperation(Builder builder) {
+        mType = builder.mType;
+        mUri = builder.mUri;
+        mMethod = builder.mMethod;
+        mArg = builder.mArg;
+        mValues = builder.mValues;
+        mExtras = builder.mExtras;
+        mSelection = builder.mSelection;
+        mSelectionArgs = builder.mSelectionArgs;
+        mExpectedCount = builder.mExpectedCount;
+        mYieldAllowed = builder.mYieldAllowed;
+        mExceptionAllowed = builder.mExceptionAllowed;
+    }
+
+    private ContentProviderOperation(Parcel source) {
+        mType = source.readInt();
+        mUri = Uri.CREATOR.createFromParcel(source);
+        mMethod = source.readInt() != 0 ? source.readString8() : null;
+        mArg = source.readInt() != 0 ? source.readString8() : null;
+        final int valuesSize = source.readInt();
+        if (valuesSize != -1) {
+            mValues = new ArrayMap<>(valuesSize);
+            source.readArrayMap(mValues, null);
+        } else {
+            mValues = null;
+        }
+        final int extrasSize = source.readInt();
+        if (extrasSize != -1) {
+            mExtras = new ArrayMap<>(extrasSize);
+            source.readArrayMap(mExtras, null);
+        } else {
+            mExtras = null;
+        }
+        mSelection = source.readInt() != 0 ? source.readString8() : null;
+        mSelectionArgs = source.readSparseArray(null);
+        mExpectedCount = source.readInt() != 0 ? source.readInt() : null;
+        mYieldAllowed = source.readInt() != 0;
+        mExceptionAllowed = source.readInt() != 0;
+    }
+
+    /** @hide */
+    public ContentProviderOperation(ContentProviderOperation cpo, Uri withUri) {
+        mType = cpo.mType;
+        mUri = withUri;
+        mMethod = cpo.mMethod;
+        mArg = cpo.mArg;
+        mValues = cpo.mValues;
+        mExtras = cpo.mExtras;
+        mSelection = cpo.mSelection;
+        mSelectionArgs = cpo.mSelectionArgs;
+        mExpectedCount = cpo.mExpectedCount;
+        mYieldAllowed = cpo.mYieldAllowed;
+        mExceptionAllowed = cpo.mExceptionAllowed;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mType);
+        Uri.writeToParcel(dest, mUri);
+        if (mMethod != null) {
+            dest.writeInt(1);
+            dest.writeString8(mMethod);
+        } else {
+            dest.writeInt(0);
+        }
+        if (mArg != null) {
+            dest.writeInt(1);
+            dest.writeString8(mArg);
+        } else {
+            dest.writeInt(0);
+        }
+        if (mValues != null) {
+            dest.writeInt(mValues.size());
+            dest.writeArrayMap(mValues);
+        } else {
+            dest.writeInt(-1);
+        }
+        if (mExtras != null) {
+            dest.writeInt(mExtras.size());
+            dest.writeArrayMap(mExtras);
+        } else {
+            dest.writeInt(-1);
+        }
+        if (mSelection != null) {
+            dest.writeInt(1);
+            dest.writeString8(mSelection);
+        } else {
+            dest.writeInt(0);
+        }
+        dest.writeSparseArray(mSelectionArgs);
+        if (mExpectedCount != null) {
+            dest.writeInt(1);
+            dest.writeInt(mExpectedCount);
+        } else {
+            dest.writeInt(0);
+        }
+        dest.writeInt(mYieldAllowed ? 1 : 0);
+        dest.writeInt(mExceptionAllowed ? 1 : 0);
+    }
+
+    /**
+     * Create a {@link Builder} suitable for building an operation that will
+     * invoke {@link ContentProvider#insert}.
+     *
+     * @param uri The {@link Uri} that is the target of the operation.
+     */
+    public static @NonNull Builder newInsert(@NonNull Uri uri) {
+        return new Builder(TYPE_INSERT, uri);
+    }
+
+    /**
+     * Create a {@link Builder} suitable for building an operation that will
+     * invoke {@link ContentProvider#update}.
+     *
+     * @param uri The {@link Uri} that is the target of the operation.
+     */
+    public static @NonNull Builder newUpdate(@NonNull Uri uri) {
+        return new Builder(TYPE_UPDATE, uri);
+    }
+
+    /**
+     * Create a {@link Builder} suitable for building an operation that will
+     * invoke {@link ContentProvider#delete}.
+     *
+     * @param uri The {@link Uri} that is the target of the operation.
+     */
+    public static @NonNull Builder newDelete(@NonNull Uri uri) {
+        return new Builder(TYPE_DELETE, uri);
+    }
+
+    /**
+     * Create a {@link Builder} suitable for building a
+     * {@link ContentProviderOperation} to assert a set of values as provided
+     * through {@link Builder#withValues(ContentValues)}.
+     */
+    public static @NonNull Builder newAssertQuery(@NonNull Uri uri) {
+        return new Builder(TYPE_ASSERT, uri);
+    }
+
+    /**
+     * Create a {@link Builder} suitable for building an operation that will
+     * invoke {@link ContentProvider#call}.
+     *
+     * @param uri The {@link Uri} that is the target of the operation.
+     */
+    public static @NonNull Builder newCall(@NonNull Uri uri, @Nullable String method,
+            @Nullable String arg) {
+        return new Builder(TYPE_CALL, uri, method, arg);
+    }
+
+    /**
+     * Gets the Uri for the target of the operation.
+     */
+    public @NonNull Uri getUri() {
+        return mUri;
+    }
+
+    /**
+     * Returns true if the operation allows yielding the database to other transactions
+     * if the database is contended.
+     *
+     * @see android.database.sqlite.SQLiteDatabase#yieldIfContendedSafely()
+     */
+    public boolean isYieldAllowed() {
+        return mYieldAllowed;
+    }
+
+    /**
+     * Returns true if this operation allows subsequent operations to continue
+     * even if this operation throws an exception. When true, any encountered
+     * exception is returned via {@link ContentProviderResult#exception}.
+     */
+    public boolean isExceptionAllowed() {
+        return mExceptionAllowed;
+    }
+
+    /** @hide exposed for unit tests */
+    @UnsupportedAppUsage
+    public int getType() {
+        return mType;
+    }
+
+    /**
+     * Returns true if the operation represents a {@link ContentProvider#insert}
+     * operation.
+     *
+     * @see #newInsert
+     */
+    public boolean isInsert() {
+        return mType == TYPE_INSERT;
+    }
+
+    /**
+     * Returns true if the operation represents a {@link ContentProvider#delete}
+     * operation.
+     *
+     * @see #newDelete
+     */
+    public boolean isDelete() {
+        return mType == TYPE_DELETE;
+    }
+
+    /**
+     * Returns true if the operation represents a {@link ContentProvider#update}
+     * operation.
+     *
+     * @see #newUpdate
+     */
+    public boolean isUpdate() {
+        return mType == TYPE_UPDATE;
+    }
+
+    /**
+     * Returns true if the operation represents an assert query.
+     *
+     * @see #newAssertQuery
+     */
+    public boolean isAssertQuery() {
+        return mType == TYPE_ASSERT;
+    }
+
+    /**
+     * Returns true if the operation represents a {@link ContentProvider#call}
+     * operation.
+     *
+     * @see #newCall
+     */
+    public boolean isCall() {
+        return mType == TYPE_CALL;
+    }
+
+    /**
+     * Returns true if the operation represents an insertion, deletion, or update.
+     *
+     * @see #isInsert
+     * @see #isDelete
+     * @see #isUpdate
+     */
+    public boolean isWriteOperation() {
+        return mType == TYPE_DELETE || mType == TYPE_INSERT || mType == TYPE_UPDATE;
+    }
+
+    /**
+     * Returns true if the operation represents an assert query.
+     *
+     * @see #isAssertQuery
+     */
+    public boolean isReadOperation() {
+        return mType == TYPE_ASSERT;
+    }
+
+    /**
+     * Applies this operation using the given provider. The backRefs array is used to resolve any
+     * back references that were requested using
+     * {@link Builder#withValueBackReferences(ContentValues)} and
+     * {@link Builder#withSelectionBackReference}.
+     * @param provider the {@link ContentProvider} on which this batch is applied
+     * @param backRefs a {@link ContentProviderResult} array that will be consulted
+     * to resolve any requested back references.
+     * @param numBackRefs the number of valid results on the backRefs array.
+     * @return a {@link ContentProviderResult} that contains either the {@link Uri} of the inserted
+     * row if this was an insert otherwise the number of rows affected.
+     * @throws OperationApplicationException thrown if either the insert fails or
+     * if the number of rows affected didn't match the expected count
+     */
+    public @NonNull ContentProviderResult apply(@NonNull ContentProvider provider,
+            @NonNull ContentProviderResult[] backRefs, int numBackRefs)
+            throws OperationApplicationException {
+        if (mExceptionAllowed) {
+            try {
+                return applyInternal(provider, backRefs, numBackRefs);
+            } catch (Exception e) {
+                return new ContentProviderResult(e);
+            }
+        } else {
+            return applyInternal(provider, backRefs, numBackRefs);
+        }
+    }
+
+    private ContentProviderResult applyInternal(ContentProvider provider,
+            ContentProviderResult[] backRefs, int numBackRefs)
+            throws OperationApplicationException {
+        final ContentValues values = resolveValueBackReferences(backRefs, numBackRefs);
+
+        // If the creator requested explicit selection or selectionArgs, it
+        // should take precedence over similar values they defined in extras
+        Bundle extras = resolveExtrasBackReferences(backRefs, numBackRefs);
+        if (mSelection != null) {
+            extras = (extras != null) ? extras : new Bundle();
+            extras.putString(ContentResolver.QUERY_ARG_SQL_SELECTION, mSelection);
+        }
+        if (mSelectionArgs != null) {
+            extras = (extras != null) ? extras : new Bundle();
+            extras.putStringArray(ContentResolver.QUERY_ARG_SQL_SELECTION_ARGS,
+                    resolveSelectionArgsBackReferences(backRefs, numBackRefs));
+        }
+
+        if (mType == TYPE_INSERT) {
+            final Uri newUri = provider.insert(mUri, values, extras);
+            if (newUri != null) {
+                return new ContentProviderResult(newUri);
+            } else {
+                throw new OperationApplicationException(
+                        "Insert into " + mUri + " returned no result");
+            }
+        } else if (mType == TYPE_CALL) {
+            final Bundle res = provider.call(mUri.getAuthority(), mMethod, mArg, extras);
+            return new ContentProviderResult(res);
+        }
+
+        final int numRows;
+        if (mType == TYPE_DELETE) {
+            numRows = provider.delete(mUri, extras);
+        } else if (mType == TYPE_UPDATE) {
+            numRows = provider.update(mUri, values, extras);
+        } else if (mType == TYPE_ASSERT) {
+            // Assert that all rows match expected values
+            String[] projection =  null;
+            if (values != null) {
+                // Build projection map from expected values
+                final ArrayList<String> projectionList = new ArrayList<String>();
+                for (Map.Entry<String, Object> entry : values.valueSet()) {
+                    projectionList.add(entry.getKey());
+                }
+                projection = projectionList.toArray(new String[projectionList.size()]);
+            }
+            final Cursor cursor = provider.query(mUri, projection, extras, null);
+            try {
+                numRows = cursor.getCount();
+                if (projection != null) {
+                    while (cursor.moveToNext()) {
+                        for (int i = 0; i < projection.length; i++) {
+                            final String cursorValue = cursor.getString(i);
+                            final String expectedValue = values.getAsString(projection[i]);
+                            if (!TextUtils.equals(cursorValue, expectedValue)) {
+                                // Throw exception when expected values don't match
+                                throw new OperationApplicationException("Found value " + cursorValue
+                                        + " when expected " + expectedValue + " for column "
+                                        + projection[i]);
+                            }
+                        }
+                    }
+                }
+            } finally {
+                cursor.close();
+            }
+        } else {
+            throw new IllegalStateException("bad type, " + mType);
+        }
+
+        if (mExpectedCount != null && mExpectedCount != numRows) {
+            throw new OperationApplicationException(
+                    "Expected " + mExpectedCount + " rows but actual " + numRows);
+        }
+
+        return new ContentProviderResult(numRows);
+    }
+
+    /**
+     * Return the values for this operation after resolving any requested
+     * back-references using the given results.
+     *
+     * @param backRefs the results to use when resolving any back-references
+     * @param numBackRefs the number of results which are valid
+     */
+    public @Nullable ContentValues resolveValueBackReferences(
+            @NonNull ContentProviderResult[] backRefs, int numBackRefs) {
+        if (mValues != null) {
+            final ContentValues values = new ContentValues();
+            for (int i = 0; i < mValues.size(); i++) {
+                final Object value = mValues.valueAt(i);
+                final Object resolved;
+                if (value instanceof BackReference) {
+                    resolved = ((BackReference) value).resolve(backRefs, numBackRefs);
+                } else {
+                    resolved = value;
+                }
+                values.putObject(mValues.keyAt(i), resolved);
+            }
+            return values;
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Return the extras for this operation after resolving any requested
+     * back-references using the given results.
+     *
+     * @param backRefs the results to use when resolving any back-references
+     * @param numBackRefs the number of results which are valid
+     */
+    public @Nullable Bundle resolveExtrasBackReferences(
+            @NonNull ContentProviderResult[] backRefs, int numBackRefs) {
+        if (mExtras != null) {
+            final Bundle extras = new Bundle();
+            for (int i = 0; i < mExtras.size(); i++) {
+                final Object value = mExtras.valueAt(i);
+                final Object resolved;
+                if (value instanceof BackReference) {
+                    resolved = ((BackReference) value).resolve(backRefs, numBackRefs);
+                } else {
+                    resolved = value;
+                }
+                extras.putObject(mExtras.keyAt(i), resolved);
+            }
+            return extras;
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Return the selection arguments for this operation after resolving any
+     * requested back-references using the given results.
+     *
+     * @param backRefs the results to use when resolving any back-references
+     * @param numBackRefs the number of results which are valid
+     */
+    public @Nullable String[] resolveSelectionArgsBackReferences(
+            @NonNull ContentProviderResult[] backRefs, int numBackRefs) {
+        if (mSelectionArgs != null) {
+            int max = -1;
+            for (int i = 0; i < mSelectionArgs.size(); i++) {
+                max = Math.max(max, mSelectionArgs.keyAt(i));
+            }
+
+            final String[] selectionArgs = new String[max + 1];
+            for (int i = 0; i < mSelectionArgs.size(); i++) {
+                final Object value = mSelectionArgs.valueAt(i);
+                final Object resolved;
+                if (value instanceof BackReference) {
+                    resolved = ((BackReference) value).resolve(backRefs, numBackRefs);
+                } else {
+                    resolved = value;
+                }
+                selectionArgs[mSelectionArgs.keyAt(i)] = String.valueOf(resolved);
+            }
+            return selectionArgs;
+        } else {
+            return null;
+        }
+    }
+
+    /** {@hide} */
+    public static String typeToString(int type) {
+        switch (type) {
+            case TYPE_INSERT: return "insert";
+            case TYPE_UPDATE: return "update";
+            case TYPE_DELETE: return "delete";
+            case TYPE_ASSERT: return "assert";
+            case TYPE_CALL: return "call";
+            default: return Integer.toString(type);
+        }
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder("ContentProviderOperation(");
+        sb.append("type=" + typeToString(mType) + " ");
+        if (mUri != null) {
+            sb.append("uri=" + mUri + " ");
+        }
+        if (mValues != null) {
+            sb.append("values=" + mValues + " ");
+        }
+        if (mSelection != null) {
+            sb.append("selection=" + mSelection + " ");
+        }
+        if (mSelectionArgs != null) {
+            sb.append("selectionArgs=" + mSelectionArgs + " ");
+        }
+        if (mExpectedCount != null) {
+            sb.append("expectedCount=" + mExpectedCount + " ");
+        }
+        if (mYieldAllowed) {
+            sb.append("yieldAllowed ");
+        }
+        if (mExceptionAllowed) {
+            sb.append("exceptionAllowed ");
+        }
+        sb.deleteCharAt(sb.length() - 1);
+        sb.append(")");
+        return sb.toString();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final @android.annotation.NonNull Creator<ContentProviderOperation> CREATOR =
+            new Creator<ContentProviderOperation>() {
+        @Override
+        public ContentProviderOperation createFromParcel(Parcel source) {
+            return new ContentProviderOperation(source);
+        }
+
+        @Override
+        public ContentProviderOperation[] newArray(int size) {
+            return new ContentProviderOperation[size];
+        }
+    };
+
+    /** {@hide} */
+    public static class BackReference implements Parcelable {
+        private final int fromIndex;
+        private final String fromKey;
+
+        private BackReference(int fromIndex, String fromKey) {
+            this.fromIndex = fromIndex;
+            this.fromKey = fromKey;
+        }
+
+        public BackReference(Parcel src) {
+            this.fromIndex = src.readInt();
+            if (src.readInt() != 0) {
+                this.fromKey = src.readString8();
+            } else {
+                this.fromKey = null;
+            }
+        }
+
+        public Object resolve(ContentProviderResult[] backRefs, int numBackRefs) {
+            if (fromIndex >= numBackRefs) {
+                Log.e(TAG, this.toString());
+                throw new ArrayIndexOutOfBoundsException("asked for back ref " + fromIndex
+                        + " but there are only " + numBackRefs + " back refs");
+            }
+            ContentProviderResult backRef = backRefs[fromIndex];
+            Object backRefValue;
+            if (backRef.extras != null) {
+                backRefValue = backRef.extras.get(fromKey);
+            } else if (backRef.uri != null) {
+                backRefValue = ContentUris.parseId(backRef.uri);
+            } else {
+                backRefValue = (long) backRef.count;
+            }
+            return backRefValue;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(fromIndex);
+            if (fromKey != null) {
+                dest.writeInt(1);
+                dest.writeString8(fromKey);
+            } else {
+                dest.writeInt(0);
+            }
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        public static final @android.annotation.NonNull Creator<BackReference> CREATOR =
+                new Creator<BackReference>() {
+            @Override
+            public BackReference createFromParcel(Parcel source) {
+                return new BackReference(source);
+            }
+
+            @Override
+            public BackReference[] newArray(int size) {
+                return new BackReference[size];
+            }
+        };
+    }
+
+    /**
+     * Used to add parameters to a {@link ContentProviderOperation}. The {@link Builder} is
+     * first created by calling {@link ContentProviderOperation#newInsert(android.net.Uri)},
+     * {@link ContentProviderOperation#newUpdate(android.net.Uri)},
+     * {@link ContentProviderOperation#newDelete(android.net.Uri)} or
+     * {@link ContentProviderOperation#newAssertQuery(Uri)}. The withXXX methods
+     * can then be used to add parameters to the builder. See the specific methods to find for
+     * which {@link Builder} type each is allowed. Call {@link #build} to create the
+     * {@link ContentProviderOperation} once all the parameters have been supplied.
+     */
+    public static class Builder {
+        private final int mType;
+        private final Uri mUri;
+        private final String mMethod;
+        private final String mArg;
+        private ArrayMap<String, Object> mValues;
+        private ArrayMap<String, Object> mExtras;
+        private String mSelection;
+        private SparseArray<Object> mSelectionArgs;
+        private Integer mExpectedCount;
+        private boolean mYieldAllowed;
+        private boolean mExceptionAllowed;
+
+        private Builder(int type, Uri uri) {
+            this(type, uri, null, null);
+        }
+
+        private Builder(int type, Uri uri, String method, String arg) {
+            mType = type;
+            mUri = Objects.requireNonNull(uri);
+            mMethod = method;
+            mArg = arg;
+        }
+
+        /** Create a ContentProviderOperation from this {@link Builder}. */
+        public @NonNull ContentProviderOperation build() {
+            if (mType == TYPE_UPDATE) {
+                if ((mValues == null || mValues.isEmpty())) {
+                    throw new IllegalArgumentException("Empty values");
+                }
+            }
+            if (mType == TYPE_ASSERT) {
+                if ((mValues == null || mValues.isEmpty())
+                        && (mExpectedCount == null)) {
+                    throw new IllegalArgumentException("Empty values");
+                }
+            }
+            return new ContentProviderOperation(this);
+        }
+
+        private void ensureValues() {
+            if (mValues == null) {
+                mValues = new ArrayMap<>();
+            }
+        }
+
+        private void ensureExtras() {
+            if (mExtras == null) {
+                mExtras = new ArrayMap<>();
+            }
+        }
+
+        private void ensureSelectionArgs() {
+            if (mSelectionArgs == null) {
+                mSelectionArgs = new SparseArray<>();
+            }
+        }
+
+        private void setValue(@NonNull String key, @NonNull Object value) {
+            ensureValues();
+            final boolean oldReference = mValues.get(key) instanceof BackReference;
+            final boolean newReference = value instanceof BackReference;
+            if (!oldReference || newReference) {
+                mValues.put(key, value);
+            }
+        }
+
+        private void setExtra(@NonNull String key, @NonNull Object value) {
+            ensureExtras();
+            final boolean oldReference = mExtras.get(key) instanceof BackReference;
+            final boolean newReference = value instanceof BackReference;
+            if (!oldReference || newReference) {
+                mExtras.put(key, value);
+            }
+        }
+
+        private void setSelectionArg(int index, @NonNull Object value) {
+            ensureSelectionArgs();
+            final boolean oldReference = mSelectionArgs.get(index) instanceof BackReference;
+            final boolean newReference = value instanceof BackReference;
+            if (!oldReference || newReference) {
+                mSelectionArgs.put(index, value);
+            }
+        }
+
+        /**
+         * Configure the values to use for this operation. This method will
+         * replace any previously defined values for the contained keys, but it
+         * will not replace any back-reference requests.
+         * <p>
+         * Any value may be dynamically overwritten using the result of a
+         * previous operation by using methods such as
+         * {@link #withValueBackReference(String, int)}.
+         */
+        public @NonNull Builder withValues(@NonNull ContentValues values) {
+            assertValuesAllowed();
+            ensureValues();
+            final ArrayMap<String, Object> rawValues = values.getValues();
+            for (int i = 0; i < rawValues.size(); i++) {
+                setValue(rawValues.keyAt(i), rawValues.valueAt(i));
+            }
+            return this;
+        }
+
+        /**
+         * Configure the given value to use for this operation. This method will
+         * replace any previously defined value for this key.
+         *
+         * @param key the key indicating which value to configure
+         */
+        public @NonNull Builder withValue(@NonNull String key, @Nullable Object value) {
+            assertValuesAllowed();
+            if (!ContentValues.isSupportedValue(value)) {
+                throw new IllegalArgumentException("bad value type: " + value.getClass().getName());
+            }
+            setValue(key, value);
+            return this;
+        }
+
+        /**
+         * Configure the given values to be dynamically overwritten using the
+         * result of a previous operation. This method will replace any
+         * previously defined values for these keys.
+         *
+         * @param backReferences set of values where the key indicates which
+         *            value to configure and the value the index indicating
+         *            which historical {@link ContentProviderResult} should
+         *            overwrite the value
+         */
+        public @NonNull Builder withValueBackReferences(@NonNull ContentValues backReferences) {
+            assertValuesAllowed();
+            final ArrayMap<String, Object> rawValues = backReferences.getValues();
+            for (int i = 0; i < rawValues.size(); i++) {
+                setValue(rawValues.keyAt(i),
+                        new BackReference((int) rawValues.valueAt(i), null));
+            }
+            return this;
+        }
+
+        /**
+         * Configure the given value to be dynamically overwritten using the
+         * result of a previous operation. This method will replace any
+         * previously defined value for this key.
+         *
+         * @param key the key indicating which value to configure
+         * @param fromIndex the index indicating which historical
+         *            {@link ContentProviderResult} should overwrite the value
+         */
+        public @NonNull Builder withValueBackReference(@NonNull String key, int fromIndex) {
+            assertValuesAllowed();
+            setValue(key, new BackReference(fromIndex, null));
+            return this;
+        }
+
+        /**
+         * Configure the given value to be dynamically overwritten using the
+         * result of a previous operation. This method will replace any
+         * previously defined value for this key.
+         *
+         * @param key the key indicating which value to configure
+         * @param fromIndex the index indicating which historical
+         *            {@link ContentProviderResult} should overwrite the value
+         * @param fromKey the key of indicating which
+         *            {@link ContentProviderResult#extras} value should
+         *            overwrite the value
+         */
+        public @NonNull Builder withValueBackReference(@NonNull String key, int fromIndex,
+                @NonNull String fromKey) {
+            assertValuesAllowed();
+            setValue(key, new BackReference(fromIndex, fromKey));
+            return this;
+        }
+
+        /**
+         * Configure the extras to use for this operation. This method will
+         * replace any previously defined values for the contained keys, but it
+         * will not replace any back-reference requests.
+         * <p>
+         * Any value may be dynamically overwritten using the result of a
+         * previous operation by using methods such as
+         * {@link #withExtraBackReference(String, int)}.
+         */
+        public @NonNull Builder withExtras(@NonNull Bundle extras) {
+            assertExtrasAllowed();
+            ensureExtras();
+            for (String key : extras.keySet()) {
+                setExtra(key, extras.get(key));
+            }
+            return this;
+        }
+
+        /**
+         * Configure the given extra to use for this operation. This method will
+         * replace any previously defined extras for this key.
+         *
+         * @param key the key indicating which extra to configure
+         */
+        public @NonNull Builder withExtra(@NonNull String key, @Nullable Object value) {
+            assertExtrasAllowed();
+            setExtra(key, value);
+            return this;
+        }
+
+        /**
+         * Configure the given extra to be dynamically overwritten using the
+         * result of a previous operation. This method will replace any
+         * previously defined extras for this key.
+         *
+         * @param key the key indicating which extra to configure
+         * @param fromIndex the index indicating which historical
+         *            {@link ContentProviderResult} should overwrite the extra
+         */
+        public @NonNull Builder withExtraBackReference(@NonNull String key, int fromIndex) {
+            assertExtrasAllowed();
+            setExtra(key, new BackReference(fromIndex, null));
+            return this;
+        }
+
+        /**
+         * Configure the given extra to be dynamically overwritten using the
+         * result of a previous operation. This method will replace any
+         * previously defined extras for this key.
+         *
+         * @param key the key indicating which extra to configure
+         * @param fromIndex the index indicating which historical
+         *            {@link ContentProviderResult} should overwrite the extra
+         * @param fromKey the key of indicating which
+         *            {@link ContentProviderResult#extras} value should
+         *            overwrite the extra
+         */
+        public @NonNull Builder withExtraBackReference(@NonNull String key, int fromIndex,
+                @NonNull String fromKey) {
+            assertExtrasAllowed();
+            setExtra(key, new BackReference(fromIndex, fromKey));
+            return this;
+        }
+
+        /**
+         * Configure the selection and selection arguments to use for this
+         * operation. This method will replace any previously defined selection
+         * and selection arguments, but it will not replace any back-reference
+         * requests.
+         * <p>
+         * An occurrence of {@code ?} in the selection will be replaced with the
+         * corresponding selection argument when the operation is executed.
+         * <p>
+         * Any selection argument may be dynamically overwritten using the
+         * result of a previous operation by using methods such as
+         * {@link #withSelectionBackReference(int, int)}.
+         */
+        public @NonNull Builder withSelection(@Nullable String selection,
+                @Nullable String[] selectionArgs) {
+            assertSelectionAllowed();
+            mSelection = selection;
+            if (selectionArgs != null) {
+                ensureSelectionArgs();
+                for (int i = 0; i < selectionArgs.length; i++) {
+                    setSelectionArg(i, selectionArgs[i]);
+                }
+            }
+            return this;
+        }
+
+        /**
+         * Configure the given selection argument to be dynamically overwritten
+         * using the result of a previous operation. This method will replace
+         * any previously defined selection argument at this index.
+         *
+         * @param index the index indicating which selection argument to
+         *            configure
+         * @param fromIndex the index indicating which historical
+         *            {@link ContentProviderResult} should overwrite the
+         *            selection argument
+         */
+        public @NonNull Builder withSelectionBackReference(int index, int fromIndex) {
+            assertSelectionAllowed();
+            setSelectionArg(index, new BackReference(fromIndex, null));
+            return this;
+        }
+
+        /**
+         * Configure the given selection argument to be dynamically overwritten
+         * using the result of a previous operation. This method will replace
+         * any previously defined selection argument at this index.
+         *
+         * @param index the index indicating which selection argument to
+         *            configure
+         * @param fromIndex the index indicating which historical
+         *            {@link ContentProviderResult} should overwrite the
+         *            selection argument
+         * @param fromKey the key of indicating which
+         *            {@link ContentProviderResult#extras} value should
+         *            overwrite the selection argument
+         */
+        public @NonNull Builder withSelectionBackReference(int index, int fromIndex,
+                @NonNull String fromKey) {
+            assertSelectionAllowed();
+            setSelectionArg(index, new BackReference(fromIndex, fromKey));
+            return this;
+        }
+
+        /**
+         * If set then if the number of rows affected by this operation does not match
+         * this count {@link OperationApplicationException} will be throw.
+         * This can only be used with builders of type update, delete, or assert.
+         * @return this builder, to allow for chaining.
+         */
+        public @NonNull Builder withExpectedCount(int count) {
+            if (mType != TYPE_UPDATE && mType != TYPE_DELETE && mType != TYPE_ASSERT) {
+                throw new IllegalArgumentException(
+                        "only updates, deletes, and asserts can have expected counts");
+            }
+            mExpectedCount = count;
+            return this;
+        }
+
+        /**
+         * If set to true then the operation allows yielding the database to other transactions
+         * if the database is contended.
+         * @return this builder, to allow for chaining.
+         * @see android.database.sqlite.SQLiteDatabase#yieldIfContendedSafely()
+         */
+        public @NonNull Builder withYieldAllowed(boolean yieldAllowed) {
+            mYieldAllowed = yieldAllowed;
+            return this;
+        }
+
+        /**
+         * If set to true, this operation allows subsequent operations to
+         * continue even if this operation throws an exception. When true, any
+         * encountered exception is returned via
+         * {@link ContentProviderResult#exception}.
+         */
+        public @NonNull Builder withExceptionAllowed(boolean exceptionAllowed) {
+            mExceptionAllowed = exceptionAllowed;
+            return this;
+        }
+
+        /** {@hide} */
+        public @NonNull Builder withFailureAllowed(boolean failureAllowed) {
+            return withExceptionAllowed(failureAllowed);
+        }
+
+        private void assertValuesAllowed() {
+            switch (mType) {
+                case TYPE_INSERT:
+                case TYPE_UPDATE:
+                case TYPE_ASSERT:
+                    break;
+                default:
+                    throw new IllegalArgumentException(
+                            "Values not supported for " + typeToString(mType));
+            }
+        }
+
+        private void assertSelectionAllowed() {
+            switch (mType) {
+                case TYPE_UPDATE:
+                case TYPE_DELETE:
+                case TYPE_ASSERT:
+                    break;
+                default:
+                    throw new IllegalArgumentException(
+                            "Selection not supported for " + typeToString(mType));
+            }
+        }
+
+        private void assertExtrasAllowed() {
+            switch (mType) {
+                case TYPE_INSERT:
+                case TYPE_UPDATE:
+                case TYPE_DELETE:
+                case TYPE_ASSERT:
+                case TYPE_CALL:
+                    break;
+                default:
+                    throw new IllegalArgumentException(
+                            "Extras not supported for " + typeToString(mType));
+            }
+        }
+    }
+}
diff --git a/android/content/ContentProviderResult.java b/android/content/ContentProviderResult.java
new file mode 100644
index 0000000..4fb1ddb
--- /dev/null
+++ b/android/content/ContentProviderResult.java
@@ -0,0 +1,161 @@
+/*
+ * 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.content;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.ParcelableException;
+
+import java.util.Objects;
+
+/**
+ * Contains the result of the application of a {@link ContentProviderOperation}.
+ * <p>
+ * It is guaranteed to have exactly one of {@link #uri}, {@link #count},
+ * {@link #extras}, or {@link #exception} set.
+ */
+public class ContentProviderResult implements Parcelable {
+    public final @Nullable Uri uri;
+    public final @Nullable Integer count;
+    public final @Nullable Bundle extras;
+    public final @Nullable Throwable exception;
+
+    public ContentProviderResult(@NonNull Uri uri) {
+        this(Objects.requireNonNull(uri), null, null, null);
+    }
+
+    public ContentProviderResult(int count) {
+        this(null, count, null, null);
+    }
+
+    public ContentProviderResult(@NonNull Bundle extras) {
+        this(null, null, Objects.requireNonNull(extras), null);
+    }
+
+    public ContentProviderResult(@NonNull Throwable exception) {
+        this(null, null, null, exception);
+    }
+
+    /** {@hide} */
+    public ContentProviderResult(Uri uri, Integer count, Bundle extras, Throwable exception) {
+        this.uri = uri;
+        this.count = count;
+        this.extras = extras;
+        this.exception = exception;
+    }
+
+    public ContentProviderResult(Parcel source) {
+        if (source.readInt() != 0) {
+            uri = Uri.CREATOR.createFromParcel(source);
+        } else {
+            uri = null;
+        }
+        if (source.readInt() != 0) {
+            count = source.readInt();
+        } else {
+            count = null;
+        }
+        if (source.readInt() != 0) {
+            extras = source.readBundle();
+        } else {
+            extras = null;
+        }
+        if (source.readInt() != 0) {
+            exception = ParcelableException.readFromParcel(source);
+        } else {
+            exception = null;
+        }
+    }
+
+    /** @hide */
+    public ContentProviderResult(ContentProviderResult cpr, int userId) {
+        uri = ContentProvider.maybeAddUserId(cpr.uri, userId);
+        count = cpr.count;
+        extras = cpr.extras;
+        exception = cpr.exception;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        if (uri != null) {
+            dest.writeInt(1);
+            uri.writeToParcel(dest, flags);
+        } else {
+            dest.writeInt(0);
+        }
+        if (count != null) {
+            dest.writeInt(1);
+            dest.writeInt(count);
+        } else {
+            dest.writeInt(0);
+        }
+        if (extras != null) {
+            dest.writeInt(1);
+            dest.writeBundle(extras);
+        } else {
+            dest.writeInt(0);
+        }
+        if (exception != null) {
+            dest.writeInt(1);
+            ParcelableException.writeToParcel(dest, exception);
+        } else {
+            dest.writeInt(0);
+        }
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final @android.annotation.NonNull Creator<ContentProviderResult> CREATOR =
+            new Creator<ContentProviderResult>() {
+        @Override
+        public ContentProviderResult createFromParcel(Parcel source) {
+            return new ContentProviderResult(source);
+        }
+
+        @Override
+        public ContentProviderResult[] newArray(int size) {
+            return new ContentProviderResult[size];
+        }
+    };
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder("ContentProviderResult(");
+        if (uri != null) {
+            sb.append("uri=" + uri + " ");
+        }
+        if (count != null) {
+            sb.append("count=" + count + " ");
+        }
+        if (extras != null) {
+            sb.append("extras=" + extras + " ");
+        }
+        if (exception != null) {
+            sb.append("exception=" + exception + " ");
+        }
+        sb.deleteCharAt(sb.length() - 1);
+        sb.append(")");
+        return sb.toString();
+    }
+}
diff --git a/android/content/ContentQueryMap.java b/android/content/ContentQueryMap.java
new file mode 100644
index 0000000..8aeaa8f
--- /dev/null
+++ b/android/content/ContentQueryMap.java
@@ -0,0 +1,182 @@
+/*
+ * 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.content;
+
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.os.Handler;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Observable;
+
+/**
+ * Caches the contents of a cursor into a Map of String->ContentValues and optionally
+ * keeps the cache fresh by registering for updates on the content backing the cursor. The column of
+ * the database that is to be used as the key of the map is user-configurable, and the
+ * ContentValues contains all columns other than the one that is designated the key.
+ * <p>
+ * The cursor data is accessed by row key and column name via getValue().
+ */
+public class ContentQueryMap extends Observable {
+    private volatile Cursor mCursor;
+    private String[] mColumnNames;
+    private int mKeyColumn;
+
+    private Handler mHandlerForUpdateNotifications = null;
+    private boolean mKeepUpdated = false;
+
+    private Map<String, ContentValues> mValues = null;
+
+    private ContentObserver mContentObserver;
+
+    /** Set when a cursor change notification is received and is cleared on a call to requery(). */
+    private boolean mDirty = false;
+
+    /**
+     * Creates a ContentQueryMap that caches the content backing the cursor
+     *
+     * @param cursor the cursor whose contents should be cached
+     * @param columnNameOfKey the column that is to be used as the key of the values map
+     * @param keepUpdated true if the cursor's ContentProvider should be monitored for changes and 
+     * the map updated when changes do occur
+     * @param handlerForUpdateNotifications the Handler that should be used to receive
+     *  notifications of changes (if requested). Normally you pass null here, but if
+     *  you know that the thread that is creating this isn't a thread that can receive
+     *  messages then you can create your own handler and use that here.
+     */
+    public ContentQueryMap(Cursor cursor, String columnNameOfKey, boolean keepUpdated,
+            Handler handlerForUpdateNotifications) {
+        mCursor = cursor;
+        mColumnNames = mCursor.getColumnNames();
+        mKeyColumn = mCursor.getColumnIndexOrThrow(columnNameOfKey);
+        mHandlerForUpdateNotifications = handlerForUpdateNotifications;
+        setKeepUpdated(keepUpdated);
+
+        // If we aren't keeping the cache updated with the current state of the cursor's 
+        // ContentProvider then read it once into the cache. Otherwise the cache will be filled 
+        // automatically.
+        if (!keepUpdated) {
+            readCursorIntoCache(cursor);
+        }
+    }
+
+    /**
+     * Change whether or not the ContentQueryMap will register with the cursor's ContentProvider 
+     * for change notifications. If you use a ContentQueryMap in an activity you should call this
+     * with false in onPause(), which means you need to call it with true in onResume()
+     * if want it to be kept updated.
+     * @param keepUpdated if true the ContentQueryMap should be registered with the cursor's
+     * ContentProvider, false otherwise
+     */
+    public void setKeepUpdated(boolean keepUpdated) {
+        if (keepUpdated == mKeepUpdated) return;
+        mKeepUpdated = keepUpdated;
+
+        if (!mKeepUpdated) {
+            mCursor.unregisterContentObserver(mContentObserver);
+            mContentObserver = null;
+        } else {
+            if (mHandlerForUpdateNotifications == null) {
+                mHandlerForUpdateNotifications = new Handler();
+            }
+            if (mContentObserver == null) {
+                mContentObserver = new ContentObserver(mHandlerForUpdateNotifications) {
+                    @Override
+                    public void onChange(boolean selfChange) {
+                        // If anyone is listening, we need to do this now to broadcast
+                        // to the observers.  Otherwise, we'll just set mDirty and
+                        // let it query lazily when they ask for the values.
+                        if (countObservers() != 0) {
+                            requery();
+                        } else {
+                            mDirty = true;
+                        }
+                    }
+                };
+            }
+            mCursor.registerContentObserver(mContentObserver);
+            // mark dirty, since it is possible the cursor's backing data had changed before we 
+            // registered for changes
+            mDirty = true;
+        }
+    }
+
+    /**
+     * Access the ContentValues for the row specified by rowName
+     * @param rowName which row to read
+     * @return the ContentValues for the row, or null if the row wasn't present in the cursor
+     */
+    public synchronized ContentValues getValues(String rowName) {
+        if (mDirty) requery();
+        return mValues.get(rowName);
+    }
+
+    /** Requeries the cursor and reads the contents into the cache */
+    public void requery() {
+        final Cursor cursor = mCursor;
+        if (cursor == null) {
+            // If mCursor is null then it means there was a requery() in flight
+            // while another thread called close(), which nulls out mCursor.
+            // If this happens ignore the requery() since we are closed anyways.
+            return;
+        }
+        mDirty = false;
+        if (!cursor.requery()) {
+            // again, don't do anything if the cursor is already closed
+            return;
+        }
+        readCursorIntoCache(cursor);
+        setChanged();
+        notifyObservers();
+    }
+
+    private synchronized void readCursorIntoCache(Cursor cursor) {
+        // Make a new map so old values returned by getRows() are undisturbed.
+        int capacity = mValues != null ? mValues.size() : 0;
+        mValues = new HashMap<String, ContentValues>(capacity);
+        while (cursor.moveToNext()) {
+            ContentValues values = new ContentValues();
+            for (int i = 0; i < mColumnNames.length; i++) {
+                if (i != mKeyColumn) {
+                    values.put(mColumnNames[i], cursor.getString(i));
+                }
+            }
+            mValues.put(cursor.getString(mKeyColumn), values);
+        }
+    }
+
+    public synchronized Map<String, ContentValues> getRows() {
+        if (mDirty) requery();
+        return mValues;
+    }
+
+    public synchronized void close() {
+        if (mContentObserver != null) {
+            mCursor.unregisterContentObserver(mContentObserver);
+            mContentObserver = null;
+        }
+        mCursor.close();
+        mCursor = null;
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        if (mCursor != null) close();
+        super.finalize();
+    }
+}
diff --git a/android/content/ContentResolver.java b/android/content/ContentResolver.java
new file mode 100644
index 0000000..0a4627d
--- /dev/null
+++ b/android/content/ContentResolver.java
@@ -0,0 +1,4129 @@
+/*
+ * 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.content;
+
+import static android.provider.DocumentsContract.EXTRA_ORIENTATION;
+
+import android.accounts.Account;
+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.TestApi;
+import android.annotation.UserIdInt;
+import android.app.ActivityManager;
+import android.app.ActivityThread;
+import android.app.AppGlobals;
+import android.app.UriGrantsManager;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.AssetFileDescriptor;
+import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.database.CrossProcessCursorWrapper;
+import android.database.Cursor;
+import android.database.IContentObserver;
+import android.graphics.Bitmap;
+import android.graphics.ImageDecoder;
+import android.graphics.ImageDecoder.ImageInfo;
+import android.graphics.ImageDecoder.Source;
+import android.graphics.Matrix;
+import android.graphics.Point;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.DeadObjectException;
+import android.os.IBinder;
+import android.os.ICancellationSignal;
+import android.os.OperationCanceledException;
+import android.os.ParcelFileDescriptor;
+import android.os.ParcelableException;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.os.storage.StorageManager;
+import android.system.Int32Ref;
+import android.text.TextUtils;
+import android.util.EventLog;
+import android.util.Log;
+import android.util.Size;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.MimeIconUtils;
+
+import dalvik.system.CloseGuard;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+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.Collection;
+import java.util.List;
+import java.util.Objects;
+import java.util.Random;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * This class provides applications access to the content model.
+ *
+ * <div class="special reference">
+ * <h3>Developer Guides</h3>
+ * <p>For more information about using a ContentResolver with content providers, read the
+ * <a href="{@docRoot}guide/topics/providers/content-providers.html">Content Providers</a>
+ * developer guide.</p>
+ * </div>
+ */
+public abstract class ContentResolver implements ContentInterface {
+    /**
+     * Enables logic that supports deprecation of {@code _data} columns,
+     * typically by replacing values with fake paths that the OS then offers to
+     * redirect to {@link #openFileDescriptor(Uri, String)}, which developers
+     * should be using directly.
+     *
+     * @hide
+     */
+    public static final boolean DEPRECATE_DATA_COLUMNS = StorageManager.hasIsolatedStorage();
+
+    /**
+     * Special filesystem path prefix which indicates that a path should be
+     * treated as a {@code content://} {@link Uri} when
+     * {@link #DEPRECATE_DATA_COLUMNS} is enabled.
+     * <p>
+     * The remainder of the path after this prefix is a
+     * {@link Uri#getSchemeSpecificPart()} value, which includes authority, path
+     * segments, and query parameters.
+     *
+     * @hide
+     */
+    public static final String DEPRECATE_DATA_PREFIX = "/mnt/content/";
+
+    /**
+     * @deprecated instead use
+     * {@link #requestSync(android.accounts.Account, String, android.os.Bundle)}
+     */
+    @Deprecated
+    public static final String SYNC_EXTRAS_ACCOUNT = "account";
+
+    /**
+     * If this extra is set to true, the sync request will be scheduled
+     * at the front of the sync request queue and without any delay
+     */
+    public static final String SYNC_EXTRAS_EXPEDITED = "expedited";
+
+    /**
+     * If this extra is set to true, the sync request will be scheduled
+     * only when the device is plugged in. This is equivalent to calling
+     * setRequiresCharging(true) on {@link SyncRequest}.
+     */
+    public static final String SYNC_EXTRAS_REQUIRE_CHARGING = "require_charging";
+
+    /**
+     * @deprecated instead use
+     * {@link #SYNC_EXTRAS_MANUAL}
+     */
+    @Deprecated
+    public static final String SYNC_EXTRAS_FORCE = "force";
+
+    /**
+     * If this extra is set to true then the sync settings (like getSyncAutomatically())
+     * are ignored by the sync scheduler.
+     */
+    public static final String SYNC_EXTRAS_IGNORE_SETTINGS = "ignore_settings";
+
+    /**
+     * If this extra is set to true then any backoffs for the initial attempt (e.g. due to retries)
+     * are ignored by the sync scheduler. If this request fails and gets rescheduled then the
+     * retries will still honor the backoff.
+     */
+    public static final String SYNC_EXTRAS_IGNORE_BACKOFF = "ignore_backoff";
+
+    /**
+     * If this extra is set to true then the request will not be retried if it fails.
+     */
+    public static final String SYNC_EXTRAS_DO_NOT_RETRY = "do_not_retry";
+
+    /**
+     * Setting this extra is the equivalent of setting both {@link #SYNC_EXTRAS_IGNORE_SETTINGS}
+     * and {@link #SYNC_EXTRAS_IGNORE_BACKOFF}
+     */
+    public static final String SYNC_EXTRAS_MANUAL = "force";
+
+    /**
+     * Indicates that this sync is intended to only upload local changes to the server.
+     * For example, this will be set to true if the sync is initiated by a call to
+     * {@link ContentResolver#notifyChange(android.net.Uri, android.database.ContentObserver, boolean)}
+     */
+    public static final String SYNC_EXTRAS_UPLOAD = "upload";
+
+    /**
+     * Indicates that the sync adapter should proceed with the delete operations,
+     * even if it determines that there are too many.
+     * See {@link SyncResult#tooManyDeletions}
+     */
+    public static final String SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS = "deletions_override";
+
+    /**
+     * Indicates that the sync adapter should not proceed with the delete operations,
+     * if it determines that there are too many.
+     * See {@link SyncResult#tooManyDeletions}
+     */
+    public static final String SYNC_EXTRAS_DISCARD_LOCAL_DELETIONS = "discard_deletions";
+
+    /* Extensions to API. TODO: Not clear if we will keep these as public flags. */
+    /** {@hide} User-specified flag for expected upload size. */
+    public static final String SYNC_EXTRAS_EXPECTED_UPLOAD = "expected_upload";
+
+    /** {@hide} User-specified flag for expected download size. */
+    public static final String SYNC_EXTRAS_EXPECTED_DOWNLOAD = "expected_download";
+
+    /** {@hide} Priority of this sync with respect to other syncs scheduled for this application. */
+    public static final String SYNC_EXTRAS_PRIORITY = "sync_priority";
+
+    /** {@hide} Flag to allow sync to occur on metered network. */
+    public static final String SYNC_EXTRAS_DISALLOW_METERED = "allow_metered";
+
+    /**
+     * {@hide} Integer extra containing a SyncExemption flag.
+     *
+     * Only the system and the shell user can set it.
+     *
+     * This extra is "virtual". Once passed to the system server, it'll be removed from the bundle.
+     */
+    public static final String SYNC_VIRTUAL_EXTRAS_EXEMPTION_FLAG = "v_exemption";
+
+    /**
+     * Set by the SyncManager to request that the SyncAdapter initialize itself for
+     * the given account/authority pair. One required initialization step is to
+     * ensure that {@link #setIsSyncable(android.accounts.Account, String, int)} has been
+     * called with a >= 0 value. When this flag is set the SyncAdapter does not need to
+     * do a full sync, though it is allowed to do so.
+     */
+    public static final String SYNC_EXTRAS_INITIALIZE = "initialize";
+
+    /** @hide */
+    public static final Intent ACTION_SYNC_CONN_STATUS_CHANGED =
+            new Intent("com.android.sync.SYNC_CONN_STATUS_CHANGED");
+
+    public static final String SCHEME_CONTENT = "content";
+    public static final String SCHEME_ANDROID_RESOURCE = "android.resource";
+    public static final String SCHEME_FILE = "file";
+
+    /**
+     * An extra {@link Point} describing the optimal size for a requested image
+     * resource, in pixels. If a provider has multiple sizes of the image, it
+     * should return the image closest to this size.
+     *
+     * @see #openTypedAssetFileDescriptor(Uri, String, Bundle)
+     * @see #openTypedAssetFileDescriptor(Uri, String, Bundle,
+     *      CancellationSignal)
+     */
+    public static final String EXTRA_SIZE = "android.content.extra.SIZE";
+
+    /**
+     * An extra boolean describing whether a particular provider supports refresh
+     * or not. If a provider supports refresh, it should include this key in its
+     * returned Cursor as part of its query call.
+     *
+     */
+    public static final String EXTRA_REFRESH_SUPPORTED = "android.content.extra.REFRESH_SUPPORTED";
+
+    /**
+     * Key for an SQL style selection string that may be present in the query Bundle argument
+     * passed to {@link ContentProvider#query(Uri, String[], Bundle, CancellationSignal)}
+     * when called by a legacy client.
+     *
+     * <p>Clients should never include user supplied values directly in the selection string,
+     * as this presents an avenue for SQL injection attacks. In lieu of this, a client
+     * should use standard placeholder notation to represent values in a selection string,
+     * then supply a corresponding value in {@value #QUERY_ARG_SQL_SELECTION_ARGS}.
+     *
+     * <p><b>Apps targeting {@link android.os.Build.VERSION_CODES#O} or higher are strongly
+     * encourage to use structured query arguments in lieu of opaque SQL query clauses.</b>
+     *
+     * @see #QUERY_ARG_SORT_COLUMNS
+     * @see #QUERY_ARG_SORT_DIRECTION
+     * @see #QUERY_ARG_SORT_COLLATION
+     * @see #QUERY_ARG_SORT_LOCALE
+     */
+    public static final String QUERY_ARG_SQL_SELECTION = "android:query-arg-sql-selection";
+
+    /**
+     * Key for SQL selection string arguments list.
+     *
+     * <p>Clients should never include user supplied values directly in the selection string,
+     * as this presents an avenue for SQL injection attacks. In lieu of this, a client
+     * should use standard placeholder notation to represent values in a selection string,
+     * then supply a corresponding value in {@value #QUERY_ARG_SQL_SELECTION_ARGS}.
+     *
+     * <p><b>Apps targeting {@link android.os.Build.VERSION_CODES#O} or higher are strongly
+     * encourage to use structured query arguments in lieu of opaque SQL query clauses.</b>
+     *
+     * @see #QUERY_ARG_SORT_COLUMNS
+     * @see #QUERY_ARG_SORT_DIRECTION
+     * @see #QUERY_ARG_SORT_COLLATION
+     * @see #QUERY_ARG_SORT_LOCALE
+     */
+    public static final String QUERY_ARG_SQL_SELECTION_ARGS =
+            "android:query-arg-sql-selection-args";
+
+    /**
+     * Key for an SQL style sort string that may be present in the query Bundle argument
+     * passed to {@link ContentProvider#query(Uri, String[], Bundle, CancellationSignal)}
+     * when called by a legacy client.
+     *
+     * <p><b>Apps targeting {@link android.os.Build.VERSION_CODES#O} or higher are strongly
+     * encourage to use structured query arguments in lieu of opaque SQL query clauses.</b>
+     *
+     * @see #QUERY_ARG_SORT_COLUMNS
+     * @see #QUERY_ARG_SORT_DIRECTION
+     * @see #QUERY_ARG_SORT_COLLATION
+     * @see #QUERY_ARG_SORT_LOCALE
+     */
+    public static final String QUERY_ARG_SQL_SORT_ORDER = "android:query-arg-sql-sort-order";
+
+    /**
+     * Key for an SQL style {@code GROUP BY} string that may be present in the
+     * query Bundle argument passed to
+     * {@link ContentProvider#query(Uri, String[], Bundle, CancellationSignal)}.
+     *
+     * <p><b>Apps targeting {@link android.os.Build.VERSION_CODES#O} or higher are strongly
+     * encourage to use structured query arguments in lieu of opaque SQL query clauses.</b>
+     *
+     * @see #QUERY_ARG_GROUP_COLUMNS
+     */
+    public static final String QUERY_ARG_SQL_GROUP_BY = "android:query-arg-sql-group-by";
+
+    /**
+     * Key for an SQL style {@code HAVING} string that may be present in the
+     * query Bundle argument passed to
+     * {@link ContentProvider#query(Uri, String[], Bundle, CancellationSignal)}.
+     *
+     * <p><b>Apps targeting {@link android.os.Build.VERSION_CODES#O} or higher are strongly
+     * encourage to use structured query arguments in lieu of opaque SQL query clauses.</b>
+     */
+    public static final String QUERY_ARG_SQL_HAVING = "android:query-arg-sql-having";
+
+    /**
+     * Key for an SQL style {@code LIMIT} string that may be present in the
+     * query Bundle argument passed to
+     * {@link ContentProvider#query(Uri, String[], Bundle, CancellationSignal)}.
+     *
+     * <p><b>Apps targeting {@link android.os.Build.VERSION_CODES#O} or higher are strongly
+     * encourage to use structured query arguments in lieu of opaque SQL query clauses.</b>
+     *
+     * @see #QUERY_ARG_LIMIT
+     * @see #QUERY_ARG_OFFSET
+     */
+    public static final String QUERY_ARG_SQL_LIMIT = "android:query-arg-sql-limit";
+
+    /**
+     * Specifies the list of columns (stored as a {@code String[]}) against
+     * which to sort results. When first column values are identical, records
+     * are then sorted based on second column values, and so on.
+     * <p>
+     * Columns present in this list must also be included in the projection
+     * supplied to
+     * {@link ContentResolver#query(Uri, String[], Bundle, CancellationSignal)}.
+     * <p>
+     * Apps targeting {@link android.os.Build.VERSION_CODES#O} or higher:
+     * <li>{@link ContentProvider} implementations: When preparing data in
+     * {@link ContentProvider#query(Uri, String[], Bundle, CancellationSignal)},
+     * if sort columns is reflected in the returned Cursor, it is strongly
+     * recommended that {@link #QUERY_ARG_SORT_COLUMNS} then be included in the
+     * array of honored arguments reflected in {@link Cursor} extras
+     * {@link Bundle} under {@link #EXTRA_HONORED_ARGS}.
+     * <li>When querying a provider, where no QUERY_ARG_SQL* otherwise exists in
+     * the arguments {@link Bundle}, the Content framework will attempt to
+     * synthesize an QUERY_ARG_SQL* argument using the corresponding
+     * QUERY_ARG_SORT* values.
+     */
+    public static final String QUERY_ARG_SORT_COLUMNS = "android:query-arg-sort-columns";
+
+    /**
+     * Specifies desired sort order. When unspecified a provider may provide a default
+     * sort direction, or choose to return unsorted results.
+     *
+     * <p>Apps targeting {@link android.os.Build.VERSION_CODES#O} or higher:
+     *
+     * <li>{@link ContentProvider} implementations: When preparing data in
+     * {@link ContentProvider#query(Uri, String[], Bundle, CancellationSignal)}, if sort direction
+     * is reflected in the returned Cursor, it is  strongly recommended that
+     * {@link #QUERY_ARG_SORT_DIRECTION} then be included in the array of honored arguments
+     * reflected in {@link Cursor} extras {@link Bundle} under {@link #EXTRA_HONORED_ARGS}.
+     *
+     * <li>When querying a provider, where no QUERY_ARG_SQL* otherwise exists in the
+     * arguments {@link Bundle}, the Content framework will attempt to synthesize
+     * a QUERY_ARG_SQL* argument using the corresponding QUERY_ARG_SORT* values.
+     *
+     * @see #QUERY_SORT_DIRECTION_ASCENDING
+     * @see #QUERY_SORT_DIRECTION_DESCENDING
+     */
+    public static final String QUERY_ARG_SORT_DIRECTION = "android:query-arg-sort-direction";
+
+    /**
+     * Allows client to specify a hint to the provider declaring which collation
+     * to use when sorting values.
+     * <p>
+     * Providers may support custom collators. When specifying a custom collator
+     * the value is determined by the Provider.
+     * <p>
+     * {@link ContentProvider} implementations: When preparing data in
+     * {@link ContentProvider#query(Uri, String[], Bundle, CancellationSignal)},
+     * if sort collation is reflected in the returned Cursor, it is strongly
+     * recommended that {@link #QUERY_ARG_SORT_COLLATION} then be included in
+     * the array of honored arguments reflected in {@link Cursor} extras
+     * {@link Bundle} under {@link #EXTRA_HONORED_ARGS}.
+     * <p>
+     * When querying a provider, where no QUERY_ARG_SQL* otherwise exists in the
+     * arguments {@link Bundle}, the Content framework will attempt to
+     * synthesize a QUERY_ARG_SQL* argument using the corresponding
+     * QUERY_ARG_SORT* values.
+     *
+     * @see java.text.Collator#PRIMARY
+     * @see java.text.Collator#SECONDARY
+     * @see java.text.Collator#TERTIARY
+     * @see java.text.Collator#IDENTICAL
+     */
+    public static final String QUERY_ARG_SORT_COLLATION = "android:query-arg-sort-collation";
+
+    /**
+     * Allows client to specify a hint to the provider declaring which locale to
+     * use when sorting values.
+     * <p>
+     * The value is defined as a RFC 3066 locale ID followed by an optional
+     * keyword list, which is the locale format used to configure ICU through
+     * classes like {@link android.icu.util.ULocale}. This supports requesting
+     * advanced sorting options, such as {@code de@collation=phonebook},
+     * {@code zh@collation=pinyin}, etc.
+     * <p>
+     * {@link ContentProvider} implementations: When preparing data in
+     * {@link ContentProvider#query(Uri, String[], Bundle, CancellationSignal)},
+     * if sort locale is reflected in the returned Cursor, it is strongly
+     * recommended that {@link #QUERY_ARG_SORT_LOCALE} then be included in the
+     * array of honored arguments reflected in {@link Cursor} extras
+     * {@link Bundle} under {@link #EXTRA_HONORED_ARGS}.
+     *
+     * @see java.util.Locale#Locale(String)
+     * @see android.icu.util.ULocale#ULocale(String)
+     */
+    public static final String QUERY_ARG_SORT_LOCALE = "android:query-arg-sort-locale";
+
+    /**
+     * Specifies the list of columns (stored as a {@code String[]}) against
+     * which to group results. When column values are identical, multiple
+     * records are collapsed together into a single record.
+     * <p>
+     * Columns present in this list must also be included in the projection
+     * supplied to
+     * {@link ContentResolver#query(Uri, String[], Bundle, CancellationSignal)}.
+     * <p>
+     * Apps targeting {@link android.os.Build.VERSION_CODES#R} or higher:
+     * <li>{@link ContentProvider} implementations: When preparing data in
+     * {@link ContentProvider#query(Uri, String[], Bundle, CancellationSignal)},
+     * if group columns is reflected in the returned Cursor, it is strongly
+     * recommended that {@link #QUERY_ARG_SORT_COLUMNS} then be included in the
+     * array of honored arguments reflected in {@link Cursor} extras
+     * {@link Bundle} under {@link #EXTRA_HONORED_ARGS}.
+     * <li>When querying a provider, where no QUERY_ARG_SQL* otherwise exists in
+     * the arguments {@link Bundle}, the Content framework will attempt to
+     * synthesize an QUERY_ARG_SQL* argument using the corresponding
+     * QUERY_ARG_SORT* values.
+     */
+    public static final String QUERY_ARG_GROUP_COLUMNS = "android:query-arg-group-columns";
+
+    /**
+     * Allows provider to report back to client which query keys are honored in a Cursor.
+     *
+     * <p>Key identifying a {@code String[]} containing all QUERY_ARG_SORT* arguments
+     * honored by the provider. Include this in {@link Cursor} extras {@link Bundle}
+     * when any QUERY_ARG_SORT* value was honored during the preparation of the
+     * results {@link Cursor}.
+     *
+     * <p>If present, ALL honored arguments are enumerated in this extra’s payload.
+     *
+     * @see #QUERY_ARG_SORT_COLUMNS
+     * @see #QUERY_ARG_SORT_DIRECTION
+     * @see #QUERY_ARG_SORT_COLLATION
+     * @see #QUERY_ARG_SORT_LOCALE
+     * @see #QUERY_ARG_GROUP_COLUMNS
+     */
+    public static final String EXTRA_HONORED_ARGS = "android.content.extra.HONORED_ARGS";
+
+    /** @hide */
+    @IntDef(flag = false, prefix = { "QUERY_SORT_DIRECTION_" }, value = {
+            QUERY_SORT_DIRECTION_ASCENDING,
+            QUERY_SORT_DIRECTION_DESCENDING
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SortDirection {}
+    public static final int QUERY_SORT_DIRECTION_ASCENDING = 0;
+    public static final int QUERY_SORT_DIRECTION_DESCENDING = 1;
+
+    /**
+     * @see {@link java.text.Collector} for details on respective collation strength.
+     * @hide
+     */
+    @IntDef(flag = false, value = {
+            java.text.Collator.PRIMARY,
+            java.text.Collator.SECONDARY,
+            java.text.Collator.TERTIARY,
+            java.text.Collator.IDENTICAL
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface QueryCollator {}
+
+    /**
+     * Specifies the offset row index within a Cursor.
+     */
+    public static final String QUERY_ARG_OFFSET = "android:query-arg-offset";
+
+    /**
+     * Specifies the max number of rows to include in a Cursor.
+     */
+    public static final String QUERY_ARG_LIMIT = "android:query-arg-limit";
+
+    /**
+     * Added to {@link Cursor} extras {@link Bundle} to indicate total row count of
+     * recordset when paging is supported. Providers must include this when
+     * implementing paging support.
+     *
+     * <p>A provider may return -1 that row count of the recordset is unknown.
+     *
+     * <p>Providers having returned -1 in a previous query are recommended to
+     * send content change notification once (if) full recordset size becomes
+     * known.
+     */
+    public static final String EXTRA_TOTAL_COUNT = "android.content.extra.TOTAL_COUNT";
+
+    /**
+     * This is the Android platform's base MIME type for a content: URI
+     * containing a Cursor of a single item.  Applications should use this
+     * as the base type along with their own sub-type of their content: URIs
+     * that represent a particular item.  For example, hypothetical IMAP email
+     * client may have a URI
+     * <code>content://com.company.provider.imap/inbox/1</code> for a particular
+     * message in the inbox, whose MIME type would be reported as
+     * <code>CURSOR_ITEM_BASE_TYPE + "/vnd.company.imap-msg"</code>
+     *
+     * <p>Compare with {@link #CURSOR_DIR_BASE_TYPE}.
+     */
+    public static final String CURSOR_ITEM_BASE_TYPE = "vnd.android.cursor.item";
+
+    /**
+     * This is the Android platform's base MIME type for a content: URI
+     * containing a Cursor of zero or more items.  Applications should use this
+     * as the base type along with their own sub-type of their content: URIs
+     * that represent a directory of items.  For example, hypothetical IMAP email
+     * client may have a URI
+     * <code>content://com.company.provider.imap/inbox</code> for all of the
+     * messages in its inbox, whose MIME type would be reported as
+     * <code>CURSOR_DIR_BASE_TYPE + "/vnd.company.imap-msg"</code>
+     *
+     * <p>Note how the base MIME type varies between this and
+     * {@link #CURSOR_ITEM_BASE_TYPE} depending on whether there is
+     * one single item or multiple items in the data set, while the sub-type
+     * remains the same because in either case the data structure contained
+     * in the cursor is the same.
+     */
+    public static final String CURSOR_DIR_BASE_TYPE = "vnd.android.cursor.dir";
+
+    /**
+     * This is the Android platform's generic MIME type to match any MIME
+     * type of the form "{@link #CURSOR_ITEM_BASE_TYPE}/{@code SUB_TYPE}".
+     * {@code SUB_TYPE} is the sub-type of the application-dependent
+     * content, e.g., "audio", "video", "playlist".
+     */
+    public static final String ANY_CURSOR_ITEM_TYPE = "vnd.android.cursor.item/*";
+
+    /** {@hide} */
+    @Deprecated
+    public static final String MIME_TYPE_DEFAULT = ClipDescription.MIMETYPE_UNKNOWN;
+
+    /** @hide */
+    @UnsupportedAppUsage
+    public static final int SYNC_ERROR_SYNC_ALREADY_IN_PROGRESS = 1;
+    /** @hide */
+    public static final int SYNC_ERROR_AUTHENTICATION = 2;
+    /** @hide */
+    public static final int SYNC_ERROR_IO = 3;
+    /** @hide */
+    public static final int SYNC_ERROR_PARSE = 4;
+    /** @hide */
+    public static final int SYNC_ERROR_CONFLICT = 5;
+    /** @hide */
+    public static final int SYNC_ERROR_TOO_MANY_DELETIONS = 6;
+    /** @hide */
+    public static final int SYNC_ERROR_TOO_MANY_RETRIES = 7;
+    /** @hide */
+    public static final int SYNC_ERROR_INTERNAL = 8;
+
+    private static final String[] SYNC_ERROR_NAMES = new String[] {
+          "already-in-progress",
+          "authentication-error",
+          "io-error",
+          "parse-error",
+          "conflict",
+          "too-many-deletions",
+          "too-many-retries",
+          "internal-error",
+    };
+
+    /** @hide */
+    public static String syncErrorToString(int error) {
+        if (error < 1 || error > SYNC_ERROR_NAMES.length) {
+            return String.valueOf(error);
+        }
+        return SYNC_ERROR_NAMES[error - 1];
+    }
+
+    /** @hide */
+    public static int syncErrorStringToInt(String error) {
+        for (int i = 0, n = SYNC_ERROR_NAMES.length; i < n; i++) {
+            if (SYNC_ERROR_NAMES[i].equals(error)) {
+                return i + 1;
+            }
+        }
+        if (error != null) {
+            try {
+                return Integer.parseInt(error);
+            } catch (NumberFormatException e) {
+                Log.d(TAG, "error parsing sync error: " + error);
+            }
+        }
+        return 0;
+    }
+
+    public static final int SYNC_OBSERVER_TYPE_SETTINGS = 1<<0;
+    public static final int SYNC_OBSERVER_TYPE_PENDING = 1<<1;
+    public static final int SYNC_OBSERVER_TYPE_ACTIVE = 1<<2;
+    /** @hide */
+    @UnsupportedAppUsage
+    public static final int SYNC_OBSERVER_TYPE_STATUS = 1<<3;
+    /** @hide */
+    public static final int SYNC_OBSERVER_TYPE_ALL = 0x7fffffff;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "NOTIFY_" }, value = {
+            NOTIFY_SYNC_TO_NETWORK,
+            NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS,
+            NOTIFY_INSERT,
+            NOTIFY_UPDATE,
+            NOTIFY_DELETE
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface NotifyFlags {}
+
+    /**
+     * Flag for {@link #notifyChange(Uri, ContentObserver, int)}: attempt to sync the change
+     * to the network.
+     */
+    public static final int NOTIFY_SYNC_TO_NETWORK = 1<<0;
+
+    /**
+     * Flag for {@link #notifyChange(Uri, ContentObserver, int)}: if set, this notification
+     * will be skipped if it is being delivered to the root URI of a ContentObserver that is
+     * using "notify for descendants."  The purpose of this is to allow the provide to send
+     * a general notification of "something under X" changed that observers of that specific
+     * URI can receive, while also sending a specific URI under X.  It would use this flag
+     * when sending the former, so that observers of "X and descendants" only see the latter.
+     */
+    public static final int NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS = 1<<1;
+
+    /**
+     * Flag for {@link #notifyChange(Uri, ContentObserver, int)}: typically set
+     * by a {@link ContentProvider} to indicate that this notification is the
+     * result of an {@link ContentProvider#insert} call.
+     * <p>
+     * Sending these detailed flags are optional, but providers are strongly
+     * recommended to send them.
+     */
+    public static final int NOTIFY_INSERT = 1 << 2;
+
+    /**
+     * Flag for {@link #notifyChange(Uri, ContentObserver, int)}: typically set
+     * by a {@link ContentProvider} to indicate that this notification is the
+     * result of an {@link ContentProvider#update} call.
+     * <p>
+     * Sending these detailed flags are optional, but providers are strongly
+     * recommended to send them.
+     */
+    public static final int NOTIFY_UPDATE = 1 << 3;
+
+    /**
+     * Flag for {@link #notifyChange(Uri, ContentObserver, int)}: typically set
+     * by a {@link ContentProvider} to indicate that this notification is the
+     * result of a {@link ContentProvider#delete} call.
+     * <p>
+     * Sending these detailed flags are optional, but providers are strongly
+     * recommended to send them.
+     */
+    public static final int NOTIFY_DELETE = 1 << 4;
+
+    /**
+     * Flag for {@link #notifyChange(Uri, ContentObserver, int)}: typically set
+     * by a {@link ContentProvider} to indicate that this notification should
+     * not be subject to any delays when dispatching to apps running in the
+     * background.
+     * <p>
+     * Using this flag may negatively impact system health and performance, and
+     * should be used sparingly.
+     *
+     * @hide
+     */
+    public static final int NOTIFY_NO_DELAY = 1 << 15;
+
+    /**
+     * No exception, throttled by app standby normally.
+     * @hide
+     */
+    public static final int SYNC_EXEMPTION_NONE = 0;
+
+    /**
+     * Exemption given to a sync request made by a foreground app (including
+     * PROCESS_STATE_IMPORTANT_FOREGROUND).
+     *
+     * At the schedule time, we promote the sync adapter app for a higher bucket:
+     * - If the device is not dozing (so the sync will start right away)
+     *   promote to ACTIVE for 1 hour.
+     * - If the device is dozing (so the sync *won't* start right away),
+     * promote to WORKING_SET for 4 hours, so it'll get a higher chance to be started once the
+     * device comes out of doze.
+     * - When the sync actually starts, we promote the sync adapter app to ACTIVE for 10 minutes,
+     * so it can schedule and start more syncs without getting throttled, even when the first
+     * operation was canceled and now we're retrying.
+     *
+     *
+     * @hide
+     */
+    public static final int SYNC_EXEMPTION_PROMOTE_BUCKET = 1;
+
+    /**
+     * In addition to {@link #SYNC_EXEMPTION_PROMOTE_BUCKET}, we put the sync adapter app in the
+     * temp whitelist for 10 minutes, so that even RARE apps can run syncs right away.
+     * @hide
+     */
+    public static final int SYNC_EXEMPTION_PROMOTE_BUCKET_WITH_TEMP = 2;
+
+    /** @hide */
+    @IntDef(flag = false, prefix = { "SYNC_EXEMPTION_" }, value = {
+            SYNC_EXEMPTION_NONE,
+            SYNC_EXEMPTION_PROMOTE_BUCKET,
+            SYNC_EXEMPTION_PROMOTE_BUCKET_WITH_TEMP,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SyncExemption {}
+
+    // Always log queries which take 500ms+; shorter queries are
+    // sampled accordingly.
+    private static final boolean ENABLE_CONTENT_SAMPLE = false;
+    private static final int SLOW_THRESHOLD_MILLIS = 500;
+    private final Random mRandom = new Random();  // guarded by itself
+
+    /** @hide */
+    public static final String REMOTE_CALLBACK_RESULT = "result";
+
+    /** @hide */
+    public static final String REMOTE_CALLBACK_ERROR = "error";
+
+    /**
+     * How long we wait for an attached process to publish its content providers
+     * before we decide it must be hung.
+     * @hide
+     */
+    public static final int CONTENT_PROVIDER_PUBLISH_TIMEOUT_MILLIS = 10 * 1000;
+
+    /**
+     * How long we wait for an provider to be published. Should be longer than
+     * {@link #CONTENT_PROVIDER_PUBLISH_TIMEOUT_MILLIS}.
+     * @hide
+     */
+    public static final int CONTENT_PROVIDER_READY_TIMEOUT_MILLIS =
+            CONTENT_PROVIDER_PUBLISH_TIMEOUT_MILLIS + 10 * 1000;
+
+    // Timeout given a ContentProvider that has already been started and connected to.
+    private static final int CONTENT_PROVIDER_TIMEOUT_MILLIS = 3 * 1000;
+
+    // Should be >= {@link #CONTENT_PROVIDER_WAIT_TIMEOUT_MILLIS}, because that's how
+    // long ActivityManagerService is giving a content provider to get published if a new process
+    // needs to be started for that.
+    private static final int REMOTE_CONTENT_PROVIDER_TIMEOUT_MILLIS =
+            CONTENT_PROVIDER_READY_TIMEOUT_MILLIS + CONTENT_PROVIDER_TIMEOUT_MILLIS;
+
+    public ContentResolver(@Nullable Context context) {
+        this(context, null);
+    }
+
+    /** {@hide} */
+    public ContentResolver(@Nullable Context context, @Nullable ContentInterface wrapped) {
+        mContext = context != null ? context : ActivityThread.currentApplication();
+        mPackageName = mContext.getOpPackageName();
+        mAttributionTag = mContext.getAttributionTag();
+        mTargetSdkVersion = mContext.getApplicationInfo().targetSdkVersion;
+        mWrapped = wrapped;
+    }
+
+    /** {@hide} */
+    public static @NonNull ContentResolver wrap(@NonNull ContentInterface wrapped) {
+        Objects.requireNonNull(wrapped);
+
+        return new ContentResolver(null, wrapped) {
+            @Override
+            public void unstableProviderDied(IContentProvider icp) {
+                throw new UnsupportedOperationException();
+            }
+            @Override
+            public boolean releaseUnstableProvider(IContentProvider icp) {
+                throw new UnsupportedOperationException();
+            }
+            @Override
+            public boolean releaseProvider(IContentProvider icp) {
+                throw new UnsupportedOperationException();
+            }
+            @Override
+            protected IContentProvider acquireUnstableProvider(Context c, String name) {
+                throw new UnsupportedOperationException();
+            }
+            @Override
+            protected IContentProvider acquireProvider(Context c, String name) {
+                throw new UnsupportedOperationException();
+            }
+        };
+    }
+
+    /**
+     * Create a {@link ContentResolver} instance that redirects all its methods
+     * to the given {@link ContentProvider}.
+     */
+    public static @NonNull ContentResolver wrap(@NonNull ContentProvider wrapped) {
+        return wrap((ContentInterface) wrapped);
+    }
+
+    /**
+     * Create a {@link ContentResolver} instance that redirects all its methods
+     * to the given {@link ContentProviderClient}.
+     */
+    public static @NonNull ContentResolver wrap(@NonNull ContentProviderClient wrapped) {
+        return wrap((ContentInterface) wrapped);
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage
+    protected abstract IContentProvider acquireProvider(Context c, String name);
+
+    /**
+     * Providing a default implementation of this, to avoid having to change a
+     * lot of other things, but implementations of ContentResolver should
+     * implement it.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    protected IContentProvider acquireExistingProvider(Context c, String name) {
+        return acquireProvider(c, name);
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage
+    public abstract boolean releaseProvider(IContentProvider icp);
+    /** @hide */
+    @UnsupportedAppUsage
+    protected abstract IContentProvider acquireUnstableProvider(Context c, String name);
+    /** @hide */
+    @UnsupportedAppUsage
+    public abstract boolean releaseUnstableProvider(IContentProvider icp);
+    /** @hide */
+    @UnsupportedAppUsage
+    public abstract void unstableProviderDied(IContentProvider icp);
+
+    /** @hide */
+    public void appNotRespondingViaProvider(IContentProvider icp) {
+        throw new UnsupportedOperationException("appNotRespondingViaProvider");
+    }
+
+    /**
+     * Return the MIME type of the given content URL.
+     *
+     * @param url A Uri identifying content (either a list or specific type),
+     * using the content:// scheme.
+     * @return A MIME type for the content, or null if the URL is invalid or the type is unknown
+     */
+    @Override
+    public final @Nullable String getType(@NonNull Uri url) {
+        Objects.requireNonNull(url, "url");
+
+        try {
+            if (mWrapped != null) return mWrapped.getType(url);
+        } catch (RemoteException e) {
+            return null;
+        }
+
+        // XXX would like to have an acquireExistingUnstableProvider for this.
+        IContentProvider provider = acquireExistingProvider(url);
+        if (provider != null) {
+            try {
+                final StringResultListener resultListener = new StringResultListener();
+                provider.getTypeAsync(url, new RemoteCallback(resultListener));
+                resultListener.waitForResult(CONTENT_PROVIDER_TIMEOUT_MILLIS);
+                if (resultListener.exception != null) {
+                    throw resultListener.exception;
+                }
+                return resultListener.result;
+            } catch (RemoteException e) {
+                // Arbitrary and not worth documenting, as Activity
+                // Manager will kill this process shortly anyway.
+                return null;
+            } catch (java.lang.Exception e) {
+                Log.w(TAG, "Failed to get type for: " + url + " (" + e.getMessage() + ")");
+                return null;
+            } finally {
+                releaseProvider(provider);
+            }
+        }
+
+        if (!SCHEME_CONTENT.equals(url.getScheme())) {
+            return null;
+        }
+
+        try {
+            final StringResultListener resultListener = new StringResultListener();
+            ActivityManager.getService().getProviderMimeTypeAsync(
+                    ContentProvider.getUriWithoutUserId(url),
+                    resolveUserId(url),
+                    new RemoteCallback(resultListener));
+            resultListener.waitForResult(REMOTE_CONTENT_PROVIDER_TIMEOUT_MILLIS);
+            if (resultListener.exception != null) {
+                throw resultListener.exception;
+            }
+            return resultListener.result;
+        } catch (RemoteException e) {
+            // We just failed to send a oneway request to the System Server. Nothing to do.
+            return null;
+        } catch (java.lang.Exception e) {
+            Log.w(TAG, "Failed to get type for: " + url + " (" + e.getMessage() + ")");
+            return null;
+        }
+    }
+
+    private abstract static class ResultListener<T> implements RemoteCallback.OnResultListener {
+        @GuardedBy("this")
+        public boolean done;
+
+        @GuardedBy("this")
+        public T result;
+
+        @GuardedBy("this")
+        public RuntimeException exception;
+
+        @Override
+        public void onResult(Bundle result) {
+            synchronized (this) {
+                ParcelableException e = result.getParcelable(REMOTE_CALLBACK_ERROR);
+                if (e != null) {
+                    Throwable t = e.getCause();
+                    if (t instanceof RuntimeException) {
+                        this.exception = (RuntimeException) t;
+                    } else {
+                        this.exception = new RuntimeException(t);
+                    }
+                } else {
+                    this.result = getResultFromBundle(result);
+                }
+                done = true;
+                notifyAll();
+            }
+        }
+
+        protected abstract T getResultFromBundle(Bundle result);
+
+        public void waitForResult(long timeout) {
+            synchronized (this) {
+                if (!done) {
+                    try {
+                        wait(timeout);
+                    } catch (InterruptedException e) {
+                        // Ignore
+                    }
+                }
+            }
+        }
+    }
+
+    private static class StringResultListener extends ResultListener<String> {
+        @Override
+        protected String getResultFromBundle(Bundle result) {
+            return result.getString(REMOTE_CALLBACK_RESULT);
+        }
+    }
+
+    private static class UriResultListener extends ResultListener<Uri> {
+        @Override
+        protected Uri getResultFromBundle(Bundle result) {
+            return result.getParcelable(REMOTE_CALLBACK_RESULT);
+        }
+    }
+
+    /**
+     * Query for the possible MIME types for the representations the given
+     * content URL can be returned when opened as as stream with
+     * {@link #openTypedAssetFileDescriptor}.  Note that the types here are
+     * not necessarily a superset of the type returned by {@link #getType} --
+     * many content providers cannot return a raw stream for the structured
+     * data that they contain.
+     *
+     * @param url A Uri identifying content (either a list or specific type),
+     * using the content:// scheme.
+     * @param mimeTypeFilter The desired MIME type.  This may be a pattern,
+     * such as *&#47;*, to query for all available MIME types that match the
+     * pattern.
+     * @return Returns an array of MIME type strings for all available
+     * data streams that match the given mimeTypeFilter.  If there are none,
+     * null is returned.
+     */
+    @Override
+    public @Nullable String[] getStreamTypes(@NonNull Uri url, @NonNull String mimeTypeFilter) {
+        Objects.requireNonNull(url, "url");
+        Objects.requireNonNull(mimeTypeFilter, "mimeTypeFilter");
+
+        try {
+            if (mWrapped != null) return mWrapped.getStreamTypes(url, mimeTypeFilter);
+        } catch (RemoteException e) {
+            return null;
+        }
+
+        IContentProvider provider = acquireProvider(url);
+        if (provider == null) {
+            return null;
+        }
+
+        try {
+            return provider.getStreamTypes(url, mimeTypeFilter);
+        } catch (RemoteException e) {
+            // Arbitrary and not worth documenting, as Activity
+            // Manager will kill this process shortly anyway.
+            return null;
+        } finally {
+            releaseProvider(provider);
+        }
+    }
+
+    /**
+     * Query the given URI, returning a {@link Cursor} over the result set.
+     * <p>
+     * For best performance, the caller should follow these guidelines:
+     * <ul>
+     * <li>Provide an explicit projection, to prevent
+     * reading data from storage that aren't going to be used.</li>
+     * <li>Use question mark parameter markers such as 'phone=?' instead of
+     * explicit values in the {@code selection} parameter, so that queries
+     * that differ only by those values will be recognized as the same
+     * for caching purposes.</li>
+     * </ul>
+     * </p>
+     *
+     * @param uri The URI, using the content:// scheme, for the content to
+     *         retrieve.
+     * @param projection A list of which columns to return. Passing null will
+     *         return all columns, which is inefficient.
+     * @param selection A filter declaring which rows to return, formatted as an
+     *         SQL WHERE clause (excluding the WHERE itself). Passing null will
+     *         return all rows for the given URI.
+     * @param selectionArgs You may include ?s in selection, which will be
+     *         replaced by the values from selectionArgs, in the order that they
+     *         appear in the selection. The values will be bound as Strings.
+     * @param sortOrder How to order the rows, formatted as an SQL ORDER BY
+     *         clause (excluding the ORDER BY itself). Passing null will use the
+     *         default sort order, which may be unordered.
+     * @return A Cursor object, which is positioned before the first entry. May return
+     *         <code>null</code> if the underlying content provider returns <code>null</code>,
+     *         or if it crashes.
+     * @see Cursor
+     */
+    public final @Nullable Cursor query(@RequiresPermission.Read @NonNull Uri uri,
+            @Nullable String[] projection, @Nullable String selection,
+            @Nullable String[] selectionArgs, @Nullable String sortOrder) {
+        return query(uri, projection, selection, selectionArgs, sortOrder, null);
+    }
+
+    /**
+     * Query the given URI, returning a {@link Cursor} over the result set
+     * with optional support for cancellation.
+     * <p>
+     * For best performance, the caller should follow these guidelines:
+     * <ul>
+     * <li>Provide an explicit projection, to prevent
+     * reading data from storage that aren't going to be used.</li>
+     * <li>Use question mark parameter markers such as 'phone=?' instead of
+     * explicit values in the {@code selection} parameter, so that queries
+     * that differ only by those values will be recognized as the same
+     * for caching purposes.</li>
+     * </ul>
+     * </p>
+     *
+     * @param uri The URI, using the content:// scheme, for the content to
+     *         retrieve.
+     * @param projection A list of which columns to return. Passing null will
+     *         return all columns, which is inefficient.
+     * @param selection A filter declaring which rows to return, formatted as an
+     *         SQL WHERE clause (excluding the WHERE itself). Passing null will
+     *         return all rows for the given URI.
+     * @param selectionArgs You may include ?s in selection, which will be
+     *         replaced by the values from selectionArgs, in the order that they
+     *         appear in the selection. The values will be bound as Strings.
+     * @param sortOrder How to order the rows, formatted as an SQL ORDER BY
+     *         clause (excluding the ORDER BY itself). Passing null will use the
+     *         default sort order, which may be unordered.
+     * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
+     * If the operation is canceled, then {@link OperationCanceledException} will be thrown
+     * when the query is executed.
+     * @return A Cursor object, which is positioned before the first entry. May return
+     *         <code>null</code> if the underlying content provider returns <code>null</code>,
+     *         or if it crashes.
+     * @see Cursor
+     */
+    public final @Nullable Cursor query(@RequiresPermission.Read @NonNull Uri uri,
+            @Nullable String[] projection, @Nullable String selection,
+            @Nullable String[] selectionArgs, @Nullable String sortOrder,
+            @Nullable CancellationSignal cancellationSignal) {
+        Bundle queryArgs = createSqlQueryBundle(selection, selectionArgs, sortOrder);
+        return query(uri, projection, queryArgs, cancellationSignal);
+    }
+
+    /**
+     * Query the given URI, returning a {@link Cursor} over the result set
+     * with support for cancellation.
+     *
+     * <p>For best performance, the caller should follow these guidelines:
+     *
+     * <li>Provide an explicit projection, to prevent reading data from storage
+     * that aren't going to be used.
+     *
+     * Provider must identify which QUERY_ARG_SORT* arguments were honored during
+     * the preparation of the result set by including the respective argument keys
+     * in the {@link Cursor} extras {@link Bundle}. See {@link #EXTRA_HONORED_ARGS}
+     * for details.
+     *
+     * @see #QUERY_ARG_SORT_COLUMNS
+     * @see #QUERY_ARG_SORT_DIRECTION
+     * @see #QUERY_ARG_SORT_COLLATION
+     *
+     * @param uri The URI, using the content:// scheme, for the content to
+     *         retrieve.
+     * @param projection A list of which columns to return. Passing null will
+     *         return all columns, which is inefficient.
+     * @param queryArgs A Bundle containing additional information necessary for
+     *            the operation. Arguments may include SQL style arguments, such
+     *            as {@link ContentResolver#QUERY_ARG_SQL_LIMIT}, but note that
+     *            the documentation for each individual provider will indicate
+     *            which arguments they support.
+     * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
+     * If the operation is canceled, then {@link OperationCanceledException} will be thrown
+     * when the query is executed.
+     * @return A Cursor object, which is positioned before the first entry. May return
+     *         <code>null</code> if the underlying content provider returns <code>null</code>,
+     *         or if it crashes.
+     * @see Cursor
+     */
+    @Override
+    public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri,
+            @Nullable String[] projection, @Nullable Bundle queryArgs,
+            @Nullable CancellationSignal cancellationSignal) {
+        Objects.requireNonNull(uri, "uri");
+
+        try {
+            if (mWrapped != null) {
+                return mWrapped.query(uri, projection, queryArgs, cancellationSignal);
+            }
+        } catch (RemoteException e) {
+            return null;
+        }
+
+        IContentProvider unstableProvider = acquireUnstableProvider(uri);
+        if (unstableProvider == null) {
+            return null;
+        }
+        IContentProvider stableProvider = null;
+        Cursor qCursor = null;
+        try {
+            long startTime = SystemClock.uptimeMillis();
+
+            ICancellationSignal remoteCancellationSignal = null;
+            if (cancellationSignal != null) {
+                cancellationSignal.throwIfCanceled();
+                remoteCancellationSignal = unstableProvider.createCancellationSignal();
+                cancellationSignal.setRemote(remoteCancellationSignal);
+            }
+            try {
+                qCursor = unstableProvider.query(mPackageName, mAttributionTag, uri, projection,
+                        queryArgs, remoteCancellationSignal);
+            } catch (DeadObjectException e) {
+                // The remote process has died...  but we only hold an unstable
+                // reference though, so we might recover!!!  Let's try!!!!
+                // This is exciting!!1!!1!!!!1
+                unstableProviderDied(unstableProvider);
+                stableProvider = acquireProvider(uri);
+                if (stableProvider == null) {
+                    return null;
+                }
+                qCursor = stableProvider.query(mPackageName, mAttributionTag, uri, projection,
+                        queryArgs, remoteCancellationSignal);
+            }
+            if (qCursor == null) {
+                return null;
+            }
+
+            // Force query execution.  Might fail and throw a runtime exception here.
+            qCursor.getCount();
+            long durationMillis = SystemClock.uptimeMillis() - startTime;
+            maybeLogQueryToEventLog(durationMillis, uri, projection, queryArgs);
+
+            // Wrap the cursor object into CursorWrapperInner object.
+            final IContentProvider provider = (stableProvider != null) ? stableProvider
+                    : acquireProvider(uri);
+            final CursorWrapperInner wrapper = new CursorWrapperInner(qCursor, provider);
+            stableProvider = null;
+            qCursor = null;
+            return wrapper;
+        } catch (RemoteException e) {
+            // Arbitrary and not worth documenting, as Activity
+            // Manager will kill this process shortly anyway.
+            return null;
+        } finally {
+            if (qCursor != null) {
+                qCursor.close();
+            }
+            if (cancellationSignal != null) {
+                cancellationSignal.setRemote(null);
+            }
+            if (unstableProvider != null) {
+                releaseUnstableProvider(unstableProvider);
+            }
+            if (stableProvider != null) {
+                releaseProvider(stableProvider);
+            }
+        }
+    }
+
+    /** {@hide} */
+    public final @NonNull Uri canonicalizeOrElse(@NonNull Uri uri) {
+        final Uri res = canonicalize(uri);
+        return (res != null) ? res : uri;
+    }
+
+    /**
+     * Transform the given <var>url</var> to a canonical representation of
+     * its referenced resource, which can be used across devices, persisted,
+     * backed up and restored, etc.  The returned Uri is still a fully capable
+     * Uri for use with its content provider, allowing you to do all of the
+     * same content provider operations as with the original Uri --
+     * {@link #query}, {@link #openInputStream(android.net.Uri)}, etc.  The
+     * only difference in behavior between the original and new Uris is that
+     * the content provider may need to do some additional work at each call
+     * using it to resolve it to the correct resource, especially if the
+     * canonical Uri has been moved to a different environment.
+     *
+     * <p>If you are moving a canonical Uri between environments, you should
+     * perform another call to {@link #canonicalize} with that original Uri to
+     * re-canonicalize it for the current environment.  Alternatively, you may
+     * want to use {@link #uncanonicalize} to transform it to a non-canonical
+     * Uri that works only in the current environment but potentially more
+     * efficiently than the canonical representation.</p>
+     *
+     * @param url The {@link Uri} that is to be transformed to a canonical
+     * representation.  Like all resolver calls, the input can be either
+     * a non-canonical or canonical Uri.
+     *
+     * @return Returns the official canonical representation of <var>url</var>,
+     * or null if the content provider does not support a canonical representation
+     * of the given Uri.  Many providers may not support canonicalization of some
+     * or all of their Uris.
+     *
+     * @see #uncanonicalize
+     */
+    @Override
+    public final @Nullable Uri canonicalize(@NonNull Uri url) {
+        Objects.requireNonNull(url, "url");
+
+        try {
+            if (mWrapped != null) return mWrapped.canonicalize(url);
+        } catch (RemoteException e) {
+            return null;
+        }
+
+        IContentProvider provider = acquireProvider(url);
+        if (provider == null) {
+            return null;
+        }
+
+        try {
+            final UriResultListener resultListener = new UriResultListener();
+            provider.canonicalizeAsync(mPackageName, mAttributionTag, url,
+                    new RemoteCallback(resultListener));
+            resultListener.waitForResult(CONTENT_PROVIDER_TIMEOUT_MILLIS);
+            if (resultListener.exception != null) {
+                throw resultListener.exception;
+            }
+            return resultListener.result;
+        } catch (RemoteException e) {
+            // Arbitrary and not worth documenting, as Activity
+            // Manager will kill this process shortly anyway.
+            return null;
+        } finally {
+            releaseProvider(provider);
+        }
+    }
+
+    /**
+     * Given a canonical Uri previously generated by {@link #canonicalize}, convert
+     * it to its local non-canonical form.  This can be useful in some cases where
+     * you know that you will only be using the Uri in the current environment and
+     * want to avoid any possible overhead when using it with the content
+     * provider or want to verify that the referenced data exists at all in the
+     * new environment.
+     *
+     * @param url The canonical {@link Uri} that is to be convered back to its
+     * non-canonical form.
+     *
+     * @return Returns the non-canonical representation of <var>url</var>.  This will
+     * return null if data identified by the canonical Uri can not be found in
+     * the current environment; callers must always check for null and deal with
+     * that by appropriately falling back to an alternative.
+     *
+     * @see #canonicalize
+     */
+    @Override
+    public final @Nullable Uri uncanonicalize(@NonNull Uri url) {
+        Objects.requireNonNull(url, "url");
+
+        try {
+            if (mWrapped != null) return mWrapped.uncanonicalize(url);
+        } catch (RemoteException e) {
+            return null;
+        }
+
+        IContentProvider provider = acquireProvider(url);
+        if (provider == null) {
+            return null;
+        }
+
+        try {
+            return provider.uncanonicalize(mPackageName, mAttributionTag, url);
+        } catch (RemoteException e) {
+            // Arbitrary and not worth documenting, as Activity
+            // Manager will kill this process shortly anyway.
+            return null;
+        } finally {
+            releaseProvider(provider);
+        }
+    }
+
+    /**
+     * This allows clients to request an explicit refresh of content identified
+     * by {@code uri}.
+     * <p>
+     * Client code should only invoke this method when there is a strong
+     * indication (such as a user initiated pull to refresh gesture) that the
+     * content is stale.
+     * <p>
+     *
+     * @param url The Uri identifying the data to refresh.
+     * @param extras Additional options from the client. The definitions of
+     *            these are specific to the content provider being called.
+     * @param cancellationSignal A signal to cancel the operation in progress,
+     *            or {@code null} if none. For example, if you called refresh on
+     *            a particular uri, you should call
+     *            {@link CancellationSignal#throwIfCanceled()} to check whether
+     *            the client has canceled the refresh request.
+     * @return true if the provider actually tried refreshing.
+     */
+    @Override
+    public final boolean refresh(@NonNull Uri url, @Nullable Bundle extras,
+            @Nullable CancellationSignal cancellationSignal) {
+        Objects.requireNonNull(url, "url");
+
+        try {
+            if (mWrapped != null) return mWrapped.refresh(url, extras, cancellationSignal);
+        } catch (RemoteException e) {
+            return false;
+        }
+
+        IContentProvider provider = acquireProvider(url);
+        if (provider == null) {
+            return false;
+        }
+
+        try {
+            ICancellationSignal remoteCancellationSignal = null;
+            if (cancellationSignal != null) {
+                cancellationSignal.throwIfCanceled();
+                remoteCancellationSignal = provider.createCancellationSignal();
+                cancellationSignal.setRemote(remoteCancellationSignal);
+            }
+            return provider.refresh(mPackageName, mAttributionTag, url, extras,
+                    remoteCancellationSignal);
+        } catch (RemoteException e) {
+            // Arbitrary and not worth documenting, as Activity
+            // Manager will kill this process shortly anyway.
+            return false;
+        } finally {
+            releaseProvider(provider);
+        }
+    }
+
+    /**
+     * Perform a detailed internal check on a {@link Uri} to determine if a UID
+     * is able to access it with specific mode flags.
+     * <p>
+     * This method is typically used when the provider implements more dynamic
+     * access controls that cannot be expressed with {@code <path-permission>}
+     * style static rules.
+     * <p>
+     * Because validation of these dynamic access controls has significant
+     * system health impact, this feature is only available to providers that
+     * are built into the system.
+     *
+     * @param uri the {@link Uri} to perform an access check on.
+     * @param uid the UID to check the permission for.
+     * @param modeFlags the access flags to use for the access check, such as
+     *            {@link Intent#FLAG_GRANT_READ_URI_PERMISSION}.
+     * @return {@link PackageManager#PERMISSION_GRANTED} if access is allowed,
+     *         otherwise {@link PackageManager#PERMISSION_DENIED}.
+     * @hide
+     */
+    @Override
+    @SystemApi
+    public int checkUriPermission(@NonNull Uri uri, int uid, @Intent.AccessUriMode int modeFlags) {
+        Objects.requireNonNull(uri, "uri");
+
+        try {
+            if (mWrapped != null) return mWrapped.checkUriPermission(uri, uid, modeFlags);
+        } catch (RemoteException e) {
+            return PackageManager.PERMISSION_DENIED;
+        }
+
+        try (ContentProviderClient client = acquireUnstableContentProviderClient(uri)) {
+            return client.checkUriPermission(uri, uid, modeFlags);
+        } catch (RemoteException e) {
+            return PackageManager.PERMISSION_DENIED;
+        }
+    }
+
+    /**
+     * Open a stream on to the content associated with a content URI.  If there
+     * is no data associated with the URI, FileNotFoundException is thrown.
+     *
+     * <h5>Accepts the following URI schemes:</h5>
+     * <ul>
+     * <li>content ({@link #SCHEME_CONTENT})</li>
+     * <li>android.resource ({@link #SCHEME_ANDROID_RESOURCE})</li>
+     * <li>file ({@link #SCHEME_FILE})</li>
+     * </ul>
+     *
+     * <p>See {@link #openAssetFileDescriptor(Uri, String)} for more information
+     * on these schemes.
+     *
+     * @param uri The desired URI.
+     * @return InputStream
+     * @throws FileNotFoundException if the provided URI could not be opened.
+     * @see #openAssetFileDescriptor(Uri, String)
+     */
+    public final @Nullable InputStream openInputStream(@NonNull Uri uri)
+            throws FileNotFoundException {
+        Objects.requireNonNull(uri, "uri");
+        String scheme = uri.getScheme();
+        if (SCHEME_ANDROID_RESOURCE.equals(scheme)) {
+            // Note: left here to avoid breaking compatibility.  May be removed
+            // with sufficient testing.
+            OpenResourceIdResult r = getResourceId(uri);
+            try {
+                InputStream stream = r.r.openRawResource(r.id);
+                return stream;
+            } catch (Resources.NotFoundException ex) {
+                throw new FileNotFoundException("Resource does not exist: " + uri);
+            }
+        } else if (SCHEME_FILE.equals(scheme)) {
+            // Note: left here to avoid breaking compatibility.  May be removed
+            // with sufficient testing.
+            return new FileInputStream(uri.getPath());
+        } else {
+            AssetFileDescriptor fd = openAssetFileDescriptor(uri, "r", null);
+            try {
+                return fd != null ? fd.createInputStream() : null;
+            } catch (IOException e) {
+                throw new FileNotFoundException("Unable to create stream");
+            }
+        }
+    }
+
+    /**
+     * Synonym for {@link #openOutputStream(Uri, String)
+     * openOutputStream(uri, "w")}.
+     * @throws FileNotFoundException if the provided URI could not be opened.
+     */
+    public final @Nullable OutputStream openOutputStream(@NonNull Uri uri)
+            throws FileNotFoundException {
+        return openOutputStream(uri, "w");
+    }
+
+    /**
+     * Open a stream on to the content associated with a content URI.  If there
+     * is no data associated with the URI, FileNotFoundException is thrown.
+     *
+     * <h5>Accepts the following URI schemes:</h5>
+     * <ul>
+     * <li>content ({@link #SCHEME_CONTENT})</li>
+     * <li>file ({@link #SCHEME_FILE})</li>
+     * </ul>
+     *
+     * <p>See {@link #openAssetFileDescriptor(Uri, String)} for more information
+     * on these schemes.
+     *
+     * @param uri The desired URI.
+     * @param mode May be "w", "wa", "rw", or "rwt".
+     * @return OutputStream
+     * @throws FileNotFoundException if the provided URI could not be opened.
+     * @see #openAssetFileDescriptor(Uri, String)
+     */
+    public final @Nullable OutputStream openOutputStream(@NonNull Uri uri, @NonNull String mode)
+            throws FileNotFoundException {
+        AssetFileDescriptor fd = openAssetFileDescriptor(uri, mode, null);
+        try {
+            return fd != null ? fd.createOutputStream() : null;
+        } catch (IOException e) {
+            throw new FileNotFoundException("Unable to create stream");
+        }
+    }
+
+    @Override
+    public final @Nullable ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode,
+            @Nullable CancellationSignal signal) throws FileNotFoundException {
+        try {
+            if (mWrapped != null) return mWrapped.openFile(uri, mode, signal);
+        } catch (RemoteException e) {
+            return null;
+        }
+
+        return openFileDescriptor(uri, mode, signal);
+    }
+
+    /**
+     * Open a raw file descriptor to access data under a URI.  This
+     * is like {@link #openAssetFileDescriptor(Uri, String)}, but uses the
+     * underlying {@link ContentProvider#openFile}
+     * ContentProvider.openFile()} method, so will <em>not</em> work with
+     * providers that return sub-sections of files.  If at all possible,
+     * you should use {@link #openAssetFileDescriptor(Uri, String)}.  You
+     * will receive a FileNotFoundException exception if the provider returns a
+     * sub-section of a file.
+     *
+     * <h5>Accepts the following URI schemes:</h5>
+     * <ul>
+     * <li>content ({@link #SCHEME_CONTENT})</li>
+     * <li>file ({@link #SCHEME_FILE})</li>
+     * </ul>
+     *
+     * <p>See {@link #openAssetFileDescriptor(Uri, String)} for more information
+     * on these schemes.
+     * <p>
+     * If opening with the exclusive "r" or "w" modes, the returned
+     * ParcelFileDescriptor could be a pipe or socket pair to enable streaming
+     * of data. Opening with the "rw" mode implies a file on disk that supports
+     * seeking. If possible, always use an exclusive mode to give the underlying
+     * {@link ContentProvider} the most flexibility.
+     * <p>
+     * If you are writing a file, and need to communicate an error to the
+     * provider, use {@link ParcelFileDescriptor#closeWithError(String)}.
+     *
+     * @param uri The desired URI to open.
+     * @param mode The file mode to use, as per {@link ContentProvider#openFile
+     * ContentProvider.openFile}.
+     * @return Returns a new ParcelFileDescriptor pointing to the file.  You
+     * own this descriptor and are responsible for closing it when done.
+     * @throws FileNotFoundException Throws FileNotFoundException if no
+     * file exists under the URI or the mode is invalid.
+     * @see #openAssetFileDescriptor(Uri, String)
+     */
+    public final @Nullable ParcelFileDescriptor openFileDescriptor(@NonNull Uri uri,
+            @NonNull String mode) throws FileNotFoundException {
+        return openFileDescriptor(uri, mode, null);
+    }
+
+    /**
+     * Open a raw file descriptor to access data under a URI.  This
+     * is like {@link #openAssetFileDescriptor(Uri, String)}, but uses the
+     * underlying {@link ContentProvider#openFile}
+     * ContentProvider.openFile()} method, so will <em>not</em> work with
+     * providers that return sub-sections of files.  If at all possible,
+     * you should use {@link #openAssetFileDescriptor(Uri, String)}.  You
+     * will receive a FileNotFoundException exception if the provider returns a
+     * sub-section of a file.
+     *
+     * <h5>Accepts the following URI schemes:</h5>
+     * <ul>
+     * <li>content ({@link #SCHEME_CONTENT})</li>
+     * <li>file ({@link #SCHEME_FILE})</li>
+     * </ul>
+     *
+     * <p>See {@link #openAssetFileDescriptor(Uri, String)} for more information
+     * on these schemes.
+     * <p>
+     * If opening with the exclusive "r" or "w" modes, the returned
+     * ParcelFileDescriptor could be a pipe or socket pair to enable streaming
+     * of data. Opening with the "rw" mode implies a file on disk that supports
+     * seeking. If possible, always use an exclusive mode to give the underlying
+     * {@link ContentProvider} the most flexibility.
+     * <p>
+     * If you are writing a file, and need to communicate an error to the
+     * provider, use {@link ParcelFileDescriptor#closeWithError(String)}.
+     *
+     * @param uri The desired URI to open.
+     * @param mode The file mode to use, as per {@link ContentProvider#openFile
+     * ContentProvider.openFile}.
+     * @param cancellationSignal A signal to cancel the operation in progress,
+     *         or null if none. If the operation is canceled, then
+     *         {@link OperationCanceledException} will be thrown.
+     * @return Returns a new ParcelFileDescriptor pointing to the file.  You
+     * own this descriptor and are responsible for closing it when done.
+     * @throws FileNotFoundException Throws FileNotFoundException if no
+     * file exists under the URI or the mode is invalid.
+     * @see #openAssetFileDescriptor(Uri, String)
+     */
+    public final @Nullable ParcelFileDescriptor openFileDescriptor(@NonNull Uri uri,
+            @NonNull String mode, @Nullable CancellationSignal cancellationSignal)
+                    throws FileNotFoundException {
+        try {
+            if (mWrapped != null) return mWrapped.openFile(uri, mode, cancellationSignal);
+        } catch (RemoteException e) {
+            return null;
+        }
+
+        AssetFileDescriptor afd = openAssetFileDescriptor(uri, mode, cancellationSignal);
+        if (afd == null) {
+            return null;
+        }
+
+        if (afd.getDeclaredLength() < 0) {
+            // This is a full file!
+            return afd.getParcelFileDescriptor();
+        }
+
+        // Client can't handle a sub-section of a file, so close what
+        // we got and bail with an exception.
+        try {
+            afd.close();
+        } catch (IOException e) {
+        }
+
+        throw new FileNotFoundException("Not a whole file");
+    }
+
+    @Override
+    public final @Nullable AssetFileDescriptor openAssetFile(@NonNull Uri uri, @NonNull String mode,
+            @Nullable CancellationSignal signal) throws FileNotFoundException {
+        try {
+            if (mWrapped != null) return mWrapped.openAssetFile(uri, mode, signal);
+        } catch (RemoteException e) {
+            return null;
+        }
+
+        return openAssetFileDescriptor(uri, mode, signal);
+    }
+
+    /**
+     * Open a raw file descriptor to access data under a URI.  This
+     * interacts with the underlying {@link ContentProvider#openAssetFile}
+     * method of the provider associated with the given URI, to retrieve any file stored there.
+     *
+     * <h5>Accepts the following URI schemes:</h5>
+     * <ul>
+     * <li>content ({@link #SCHEME_CONTENT})</li>
+     * <li>android.resource ({@link #SCHEME_ANDROID_RESOURCE})</li>
+     * <li>file ({@link #SCHEME_FILE})</li>
+     * </ul>
+     * <h5>The android.resource ({@link #SCHEME_ANDROID_RESOURCE}) Scheme</h5>
+     * <p>
+     * A Uri object can be used to reference a resource in an APK file.  The
+     * Uri should be one of the following formats:
+     * <ul>
+     * <li><code>android.resource://package_name/id_number</code><br/>
+     * <code>package_name</code> is your package name as listed in your AndroidManifest.xml.
+     * For example <code>com.example.myapp</code><br/>
+     * <code>id_number</code> is the int form of the ID.<br/>
+     * The easiest way to construct this form is
+     * <pre>Uri uri = Uri.parse("android.resource://com.example.myapp/" + R.raw.my_resource");</pre>
+     * </li>
+     * <li><code>android.resource://package_name/type/name</code><br/>
+     * <code>package_name</code> is your package name as listed in your AndroidManifest.xml.
+     * For example <code>com.example.myapp</code><br/>
+     * <code>type</code> is the string form of the resource type.  For example, <code>raw</code>
+     * or <code>drawable</code>.
+     * <code>name</code> is the string form of the resource name.  That is, whatever the file
+     * name was in your res directory, without the type extension.
+     * The easiest way to construct this form is
+     * <pre>Uri uri = Uri.parse("android.resource://com.example.myapp/raw/my_resource");</pre>
+     * </li>
+     * </ul>
+     *
+     * <p>Note that if this function is called for read-only input (mode is "r")
+     * on a content: URI, it will instead call {@link #openTypedAssetFileDescriptor}
+     * for you with a MIME type of "*&#47;*".  This allows such callers to benefit
+     * from any built-in data conversion that a provider implements.
+     *
+     * @param uri The desired URI to open.
+     * @param mode The file mode to use, as per {@link ContentProvider#openAssetFile
+     * ContentProvider.openAssetFile}.
+     * @return Returns a new ParcelFileDescriptor pointing to the file.  You
+     * own this descriptor and are responsible for closing it when done.
+     * @throws FileNotFoundException Throws FileNotFoundException of no
+     * file exists under the URI or the mode is invalid.
+     */
+    public final @Nullable AssetFileDescriptor openAssetFileDescriptor(@NonNull Uri uri,
+            @NonNull String mode) throws FileNotFoundException {
+        return openAssetFileDescriptor(uri, mode, null);
+    }
+
+    /**
+     * Open a raw file descriptor to access data under a URI.  This
+     * interacts with the underlying {@link ContentProvider#openAssetFile}
+     * method of the provider associated with the given URI, to retrieve any file stored there.
+     *
+     * <h5>Accepts the following URI schemes:</h5>
+     * <ul>
+     * <li>content ({@link #SCHEME_CONTENT})</li>
+     * <li>android.resource ({@link #SCHEME_ANDROID_RESOURCE})</li>
+     * <li>file ({@link #SCHEME_FILE})</li>
+     * </ul>
+     * <h5>The android.resource ({@link #SCHEME_ANDROID_RESOURCE}) Scheme</h5>
+     * <p>
+     * A Uri object can be used to reference a resource in an APK file.  The
+     * Uri should be one of the following formats:
+     * <ul>
+     * <li><code>android.resource://package_name/id_number</code><br/>
+     * <code>package_name</code> is your package name as listed in your AndroidManifest.xml.
+     * For example <code>com.example.myapp</code><br/>
+     * <code>id_number</code> is the int form of the ID.<br/>
+     * The easiest way to construct this form is
+     * <pre>Uri uri = Uri.parse("android.resource://com.example.myapp/" + R.raw.my_resource");</pre>
+     * </li>
+     * <li><code>android.resource://package_name/type/name</code><br/>
+     * <code>package_name</code> is your package name as listed in your AndroidManifest.xml.
+     * For example <code>com.example.myapp</code><br/>
+     * <code>type</code> is the string form of the resource type.  For example, <code>raw</code>
+     * or <code>drawable</code>.
+     * <code>name</code> is the string form of the resource name.  That is, whatever the file
+     * name was in your res directory, without the type extension.
+     * The easiest way to construct this form is
+     * <pre>Uri uri = Uri.parse("android.resource://com.example.myapp/raw/my_resource");</pre>
+     * </li>
+     * </ul>
+     *
+     * <p>Note that if this function is called for read-only input (mode is "r")
+     * on a content: URI, it will instead call {@link #openTypedAssetFileDescriptor}
+     * for you with a MIME type of "*&#47;*".  This allows such callers to benefit
+     * from any built-in data conversion that a provider implements.
+     *
+     * @param uri The desired URI to open.
+     * @param mode The file mode to use, as per {@link ContentProvider#openAssetFile
+     * ContentProvider.openAssetFile}.
+     * @param cancellationSignal A signal to cancel the operation in progress, or null if
+     *            none. If the operation is canceled, then
+     *            {@link OperationCanceledException} will be thrown.
+     * @return Returns a new ParcelFileDescriptor pointing to the file.  You
+     * own this descriptor and are responsible for closing it when done.
+     * @throws FileNotFoundException Throws FileNotFoundException of no
+     * file exists under the URI or the mode is invalid.
+     */
+    public final @Nullable AssetFileDescriptor openAssetFileDescriptor(@NonNull Uri uri,
+            @NonNull String mode, @Nullable CancellationSignal cancellationSignal)
+                    throws FileNotFoundException {
+        Objects.requireNonNull(uri, "uri");
+        Objects.requireNonNull(mode, "mode");
+
+        try {
+            if (mWrapped != null) return mWrapped.openAssetFile(uri, mode, cancellationSignal);
+        } catch (RemoteException e) {
+            return null;
+        }
+
+        String scheme = uri.getScheme();
+        if (SCHEME_ANDROID_RESOURCE.equals(scheme)) {
+            if (!"r".equals(mode)) {
+                throw new FileNotFoundException("Can't write resources: " + uri);
+            }
+            OpenResourceIdResult r = getResourceId(uri);
+            try {
+                return r.r.openRawResourceFd(r.id);
+            } catch (Resources.NotFoundException ex) {
+                throw new FileNotFoundException("Resource does not exist: " + uri);
+            }
+        } else if (SCHEME_FILE.equals(scheme)) {
+            ParcelFileDescriptor pfd = ParcelFileDescriptor.open(
+                    new File(uri.getPath()), ParcelFileDescriptor.parseMode(mode));
+            return new AssetFileDescriptor(pfd, 0, -1);
+        } else {
+            if ("r".equals(mode)) {
+                return openTypedAssetFileDescriptor(uri, "*/*", null, cancellationSignal);
+            } else {
+                IContentProvider unstableProvider = acquireUnstableProvider(uri);
+                if (unstableProvider == null) {
+                    throw new FileNotFoundException("No content provider: " + uri);
+                }
+                IContentProvider stableProvider = null;
+                AssetFileDescriptor fd = null;
+
+                try {
+                    ICancellationSignal remoteCancellationSignal = null;
+                    if (cancellationSignal != null) {
+                        cancellationSignal.throwIfCanceled();
+                        remoteCancellationSignal = unstableProvider.createCancellationSignal();
+                        cancellationSignal.setRemote(remoteCancellationSignal);
+                    }
+
+                    try {
+                        fd = unstableProvider.openAssetFile(
+                                mPackageName, mAttributionTag, uri, mode,
+                                remoteCancellationSignal);
+                        if (fd == null) {
+                            // The provider will be released by the finally{} clause
+                            return null;
+                        }
+                    } catch (DeadObjectException e) {
+                        // The remote process has died...  but we only hold an unstable
+                        // reference though, so we might recover!!!  Let's try!!!!
+                        // This is exciting!!1!!1!!!!1
+                        unstableProviderDied(unstableProvider);
+                        stableProvider = acquireProvider(uri);
+                        if (stableProvider == null) {
+                            throw new FileNotFoundException("No content provider: " + uri);
+                        }
+                        fd = stableProvider.openAssetFile(
+                                mPackageName, mAttributionTag, uri, mode, remoteCancellationSignal);
+                        if (fd == null) {
+                            // The provider will be released by the finally{} clause
+                            return null;
+                        }
+                    }
+
+                    if (stableProvider == null) {
+                        stableProvider = acquireProvider(uri);
+                    }
+                    releaseUnstableProvider(unstableProvider);
+                    unstableProvider = null;
+                    ParcelFileDescriptor pfd = new ParcelFileDescriptorInner(
+                            fd.getParcelFileDescriptor(), stableProvider);
+
+                    // Success!  Don't release the provider when exiting, let
+                    // ParcelFileDescriptorInner do that when it is closed.
+                    stableProvider = null;
+
+                    return new AssetFileDescriptor(pfd, fd.getStartOffset(),
+                            fd.getDeclaredLength());
+
+                } catch (RemoteException e) {
+                    // Whatever, whatever, we'll go away.
+                    throw new FileNotFoundException(
+                            "Failed opening content provider: " + uri);
+                } catch (FileNotFoundException e) {
+                    throw e;
+                } finally {
+                    if (cancellationSignal != null) {
+                        cancellationSignal.setRemote(null);
+                    }
+                    if (stableProvider != null) {
+                        releaseProvider(stableProvider);
+                    }
+                    if (unstableProvider != null) {
+                        releaseUnstableProvider(unstableProvider);
+                    }
+                }
+            }
+        }
+    }
+
+    @Override
+    public final @Nullable AssetFileDescriptor openTypedAssetFile(@NonNull Uri uri,
+            @NonNull String mimeTypeFilter, @Nullable Bundle opts,
+            @Nullable CancellationSignal signal) throws FileNotFoundException {
+        try {
+            if (mWrapped != null) {
+                return mWrapped.openTypedAssetFile(uri, mimeTypeFilter, opts, signal);
+            }
+        } catch (RemoteException e) {
+            return null;
+        }
+
+        return openTypedAssetFileDescriptor(uri, mimeTypeFilter, opts, signal);
+    }
+
+    /**
+     * Open a raw file descriptor to access (potentially type transformed)
+     * data from a "content:" URI.  This interacts with the underlying
+     * {@link ContentProvider#openTypedAssetFile} method of the provider
+     * associated with the given URI, to retrieve retrieve any appropriate
+     * data stream for the data stored there.
+     *
+     * <p>Unlike {@link #openAssetFileDescriptor}, this function only works
+     * with "content:" URIs, because content providers are the only facility
+     * with an associated MIME type to ensure that the returned data stream
+     * is of the desired type.
+     *
+     * <p>All text/* streams are encoded in UTF-8.
+     *
+     * @param uri The desired URI to open.
+     * @param mimeType The desired MIME type of the returned data.  This can
+     * be a pattern such as *&#47;*, which will allow the content provider to
+     * select a type, though there is no way for you to determine what type
+     * it is returning.
+     * @param opts Additional provider-dependent options.
+     * @return Returns a new ParcelFileDescriptor from which you can read the
+     * data stream from the provider.  Note that this may be a pipe, meaning
+     * you can't seek in it.  The only seek you should do is if the
+     * AssetFileDescriptor contains an offset, to move to that offset before
+     * reading.  You own this descriptor and are responsible for closing it when done.
+     * @throws FileNotFoundException Throws FileNotFoundException of no
+     * data of the desired type exists under the URI.
+     */
+    public final @Nullable AssetFileDescriptor openTypedAssetFileDescriptor(@NonNull Uri uri,
+            @NonNull String mimeType, @Nullable Bundle opts) throws FileNotFoundException {
+        return openTypedAssetFileDescriptor(uri, mimeType, opts, null);
+    }
+
+    /**
+     * Open a raw file descriptor to access (potentially type transformed)
+     * data from a "content:" URI.  This interacts with the underlying
+     * {@link ContentProvider#openTypedAssetFile} method of the provider
+     * associated with the given URI, to retrieve retrieve any appropriate
+     * data stream for the data stored there.
+     *
+     * <p>Unlike {@link #openAssetFileDescriptor}, this function only works
+     * with "content:" URIs, because content providers are the only facility
+     * with an associated MIME type to ensure that the returned data stream
+     * is of the desired type.
+     *
+     * <p>All text/* streams are encoded in UTF-8.
+     *
+     * @param uri The desired URI to open.
+     * @param mimeType The desired MIME type of the returned data.  This can
+     * be a pattern such as *&#47;*, which will allow the content provider to
+     * select a type, though there is no way for you to determine what type
+     * it is returning.
+     * @param opts Additional provider-dependent options.
+     * @param cancellationSignal A signal to cancel the operation in progress,
+     *         or null if none. If the operation is canceled, then
+     *         {@link OperationCanceledException} will be thrown.
+     * @return Returns a new ParcelFileDescriptor from which you can read the
+     * data stream from the provider.  Note that this may be a pipe, meaning
+     * you can't seek in it.  The only seek you should do is if the
+     * AssetFileDescriptor contains an offset, to move to that offset before
+     * reading.  You own this descriptor and are responsible for closing it when done.
+     * @throws FileNotFoundException Throws FileNotFoundException of no
+     * data of the desired type exists under the URI.
+     */
+    public final @Nullable AssetFileDescriptor openTypedAssetFileDescriptor(@NonNull Uri uri,
+            @NonNull String mimeType, @Nullable Bundle opts,
+            @Nullable CancellationSignal cancellationSignal) throws FileNotFoundException {
+        Objects.requireNonNull(uri, "uri");
+        Objects.requireNonNull(mimeType, "mimeType");
+
+        try {
+            if (mWrapped != null) return mWrapped.openTypedAssetFile(uri, mimeType, opts, cancellationSignal);
+        } catch (RemoteException e) {
+            return null;
+        }
+
+        IContentProvider unstableProvider = acquireUnstableProvider(uri);
+        if (unstableProvider == null) {
+            throw new FileNotFoundException("No content provider: " + uri);
+        }
+        IContentProvider stableProvider = null;
+        AssetFileDescriptor fd = null;
+
+        try {
+            ICancellationSignal remoteCancellationSignal = null;
+            if (cancellationSignal != null) {
+                cancellationSignal.throwIfCanceled();
+                remoteCancellationSignal = unstableProvider.createCancellationSignal();
+                cancellationSignal.setRemote(remoteCancellationSignal);
+            }
+
+            try {
+                fd = unstableProvider.openTypedAssetFile(
+                        mPackageName, mAttributionTag, uri, mimeType, opts,
+                        remoteCancellationSignal);
+                if (fd == null) {
+                    // The provider will be released by the finally{} clause
+                    return null;
+                }
+            } catch (DeadObjectException e) {
+                // The remote process has died...  but we only hold an unstable
+                // reference though, so we might recover!!!  Let's try!!!!
+                // This is exciting!!1!!1!!!!1
+                unstableProviderDied(unstableProvider);
+                stableProvider = acquireProvider(uri);
+                if (stableProvider == null) {
+                    throw new FileNotFoundException("No content provider: " + uri);
+                }
+                fd = stableProvider.openTypedAssetFile(
+                        mPackageName, mAttributionTag, uri, mimeType, opts,
+                        remoteCancellationSignal);
+                if (fd == null) {
+                    // The provider will be released by the finally{} clause
+                    return null;
+                }
+            }
+
+            if (stableProvider == null) {
+                stableProvider = acquireProvider(uri);
+            }
+            releaseUnstableProvider(unstableProvider);
+            unstableProvider = null;
+            ParcelFileDescriptor pfd = new ParcelFileDescriptorInner(
+                    fd.getParcelFileDescriptor(), stableProvider);
+
+            // Success!  Don't release the provider when exiting, let
+            // ParcelFileDescriptorInner do that when it is closed.
+            stableProvider = null;
+
+            return new AssetFileDescriptor(pfd, fd.getStartOffset(),
+                    fd.getDeclaredLength(), fd.getExtras());
+
+        } catch (RemoteException e) {
+            // Whatever, whatever, we'll go away.
+            throw new FileNotFoundException(
+                    "Failed opening content provider: " + uri);
+        } catch (FileNotFoundException e) {
+            throw e;
+        } finally {
+            if (cancellationSignal != null) {
+                cancellationSignal.setRemote(null);
+            }
+            if (stableProvider != null) {
+                releaseProvider(stableProvider);
+            }
+            if (unstableProvider != null) {
+                releaseUnstableProvider(unstableProvider);
+            }
+        }
+    }
+
+    /**
+     * A resource identified by the {@link Resources} that contains it, and a resource id.
+     *
+     * @hide
+     */
+    public class OpenResourceIdResult {
+        @UnsupportedAppUsage
+        public Resources r;
+        @UnsupportedAppUsage
+        public int id;
+    }
+
+    /**
+     * Resolves an android.resource URI to a {@link Resources} and a resource id.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public OpenResourceIdResult getResourceId(Uri uri) throws FileNotFoundException {
+        String authority = uri.getAuthority();
+        Resources r;
+        if (TextUtils.isEmpty(authority)) {
+            throw new FileNotFoundException("No authority: " + uri);
+        } else {
+            try {
+                r = mContext.getPackageManager().getResourcesForApplication(authority);
+            } catch (NameNotFoundException ex) {
+                throw new FileNotFoundException("No package found for authority: " + uri);
+            }
+        }
+        List<String> path = uri.getPathSegments();
+        if (path == null) {
+            throw new FileNotFoundException("No path: " + uri);
+        }
+        int len = path.size();
+        int id;
+        if (len == 1) {
+            try {
+                id = Integer.parseInt(path.get(0));
+            } catch (NumberFormatException e) {
+                throw new FileNotFoundException("Single path segment is not a resource ID: " + uri);
+            }
+        } else if (len == 2) {
+            id = r.getIdentifier(path.get(1), path.get(0), authority);
+        } else {
+            throw new FileNotFoundException("More than two path segments: " + uri);
+        }
+        if (id == 0) {
+            throw new FileNotFoundException("No resource found for: " + uri);
+        }
+        OpenResourceIdResult res = new OpenResourceIdResult();
+        res.r = r;
+        res.id = id;
+        return res;
+    }
+
+    /**
+     * Inserts a row into a table at the given URL.
+     *
+     * If the content provider supports transactions the insertion will be atomic.
+     *
+     * @param url The URL of the table to insert into.
+     * @param values The initial values for the newly inserted row. The key is the column name for
+     *               the field. Passing an empty ContentValues will create an empty row.
+     * @return the URL of the newly created row. May return <code>null</code> if the underlying
+     *         content provider returns <code>null</code>, or if it crashes.
+     */
+    public final @Nullable Uri insert(@RequiresPermission.Write @NonNull Uri url,
+                @Nullable ContentValues values) {
+        return insert(url, values, null);
+    }
+
+    /**
+     * Inserts a row into a table at the given URL.
+     *
+     * If the content provider supports transactions the insertion will be atomic.
+     *
+     * @param url The URL of the table to insert into.
+     * @param values The initial values for the newly inserted row. The key is the column name for
+     *               the field. Passing an empty ContentValues will create an empty row.
+     * @param extras A Bundle containing additional information necessary for
+     *            the operation. Arguments may include SQL style arguments, such
+     *            as {@link ContentResolver#QUERY_ARG_SQL_LIMIT}, but note that
+     *            the documentation for each individual provider will indicate
+     *            which arguments they support.
+     * @return the URL of the newly created row. May return <code>null</code> if the underlying
+     *         content provider returns <code>null</code>, or if it crashes.
+     * @throws IllegalArgumentException if the provider doesn't support one of
+     *             the requested Bundle arguments.
+     */
+    @Override
+    public final @Nullable Uri insert(@RequiresPermission.Write @NonNull Uri url,
+            @Nullable ContentValues values, @Nullable Bundle extras) {
+        Objects.requireNonNull(url, "url");
+
+        try {
+            if (mWrapped != null) return mWrapped.insert(url, values, extras);
+        } catch (RemoteException e) {
+            return null;
+        }
+
+        IContentProvider provider = acquireProvider(url);
+        if (provider == null) {
+            throw new IllegalArgumentException("Unknown URL " + url);
+        }
+        try {
+            long startTime = SystemClock.uptimeMillis();
+            Uri createdRow = provider.insert(mPackageName, mAttributionTag, url, values, extras);
+            long durationMillis = SystemClock.uptimeMillis() - startTime;
+            maybeLogUpdateToEventLog(durationMillis, url, "insert", null /* where */);
+            return createdRow;
+        } catch (RemoteException e) {
+            // Arbitrary and not worth documenting, as Activity
+            // Manager will kill this process shortly anyway.
+            return null;
+        } finally {
+            releaseProvider(provider);
+        }
+    }
+
+    /**
+     * Applies each of the {@link ContentProviderOperation} objects and returns an array
+     * of their results. Passes through OperationApplicationException, which may be thrown
+     * by the call to {@link ContentProviderOperation#apply}.
+     * If all the applications succeed then a {@link ContentProviderResult} array with the
+     * same number of elements as the operations will be returned. It is implementation-specific
+     * how many, if any, operations will have been successfully applied if a call to
+     * apply results in a {@link OperationApplicationException}.
+     * @param authority the authority of the ContentProvider to which this batch should be applied
+     * @param operations the operations to apply
+     * @return the results of the applications
+     * @throws OperationApplicationException thrown if an application fails.
+     * See {@link ContentProviderOperation#apply} for more information.
+     * @throws RemoteException thrown if a RemoteException is encountered while attempting
+     *   to communicate with a remote provider.
+     */
+    @Override
+    public @NonNull ContentProviderResult[] applyBatch(@NonNull String authority,
+            @NonNull ArrayList<ContentProviderOperation> operations)
+                    throws RemoteException, OperationApplicationException {
+        Objects.requireNonNull(authority, "authority");
+        Objects.requireNonNull(operations, "operations");
+
+        try {
+            if (mWrapped != null) return mWrapped.applyBatch(authority, operations);
+        } catch (RemoteException e) {
+            return null;
+        }
+
+        ContentProviderClient provider = acquireContentProviderClient(authority);
+        if (provider == null) {
+            throw new IllegalArgumentException("Unknown authority " + authority);
+        }
+        try {
+            return provider.applyBatch(operations);
+        } finally {
+            provider.release();
+        }
+    }
+
+    /**
+     * Inserts multiple rows into a table at the given URL.
+     *
+     * This function make no guarantees about the atomicity of the insertions.
+     *
+     * @param url The URL of the table to insert into.
+     * @param values The initial values for the newly inserted rows. The key is the column name for
+     *               the field. Passing null will create an empty row.
+     * @return the number of newly created rows.
+     */
+    @Override
+    public final int bulkInsert(@RequiresPermission.Write @NonNull Uri url,
+                @NonNull ContentValues[] values) {
+        Objects.requireNonNull(url, "url");
+        Objects.requireNonNull(values, "values");
+
+        try {
+            if (mWrapped != null) return mWrapped.bulkInsert(url, values);
+        } catch (RemoteException e) {
+            return 0;
+        }
+
+        IContentProvider provider = acquireProvider(url);
+        if (provider == null) {
+            throw new IllegalArgumentException("Unknown URL " + url);
+        }
+        try {
+            long startTime = SystemClock.uptimeMillis();
+            int rowsCreated = provider.bulkInsert(mPackageName, mAttributionTag, url, values);
+            long durationMillis = SystemClock.uptimeMillis() - startTime;
+            maybeLogUpdateToEventLog(durationMillis, url, "bulkinsert", null /* where */);
+            return rowsCreated;
+        } catch (RemoteException e) {
+            // Arbitrary and not worth documenting, as Activity
+            // Manager will kill this process shortly anyway.
+            return 0;
+        } finally {
+            releaseProvider(provider);
+        }
+    }
+
+    /**
+     * Deletes row(s) specified by a content URI.
+     *
+     * If the content provider supports transactions, the deletion will be atomic.
+     *
+     * @param url The URL of the row to delete.
+     * @param where A filter to apply to rows before deleting, formatted as an SQL WHERE clause
+                    (excluding the WHERE itself).
+     * @return The number of rows deleted.
+     */
+    public final int delete(@RequiresPermission.Write @NonNull Uri url, @Nullable String where,
+            @Nullable String[] selectionArgs) {
+        return delete(url, createSqlQueryBundle(where, selectionArgs));
+    }
+
+    /**
+     * Deletes row(s) specified by a content URI.
+     *
+     * If the content provider supports transactions, the deletion will be atomic.
+     *
+     * @param url The URL of the row to delete.
+     * @param extras A Bundle containing additional information necessary for
+     *            the operation. Arguments may include SQL style arguments, such
+     *            as {@link ContentResolver#QUERY_ARG_SQL_LIMIT}, but note that
+     *            the documentation for each individual provider will indicate
+     *            which arguments they support.
+     * @return The number of rows deleted.
+     * @throws IllegalArgumentException if the provider doesn't support one of
+     *             the requested Bundle arguments.
+     */
+    @Override
+    public final int delete(@RequiresPermission.Write @NonNull Uri url, @Nullable Bundle extras) {
+        Objects.requireNonNull(url, "url");
+
+        try {
+            if (mWrapped != null) return mWrapped.delete(url, extras);
+        } catch (RemoteException e) {
+            return 0;
+        }
+
+        IContentProvider provider = acquireProvider(url);
+        if (provider == null) {
+            throw new IllegalArgumentException("Unknown URL " + url);
+        }
+        try {
+            long startTime = SystemClock.uptimeMillis();
+            int rowsDeleted = provider.delete(mPackageName, mAttributionTag, url, extras);
+            long durationMillis = SystemClock.uptimeMillis() - startTime;
+            maybeLogUpdateToEventLog(durationMillis, url, "delete", null);
+            return rowsDeleted;
+        } catch (RemoteException e) {
+            // Arbitrary and not worth documenting, as Activity
+            // Manager will kill this process shortly anyway.
+            return -1;
+        } finally {
+            releaseProvider(provider);
+        }
+    }
+
+    /**
+     * Update row(s) in a content URI.
+     *
+     * If the content provider supports transactions the update will be atomic.
+     *
+     * @param uri The URI to modify.
+     * @param values The new field values. The key is the column name for the field.
+                     A null value will remove an existing field value.
+     * @param where A filter to apply to rows before updating, formatted as an SQL WHERE clause
+                    (excluding the WHERE itself).
+     * @return the number of rows updated.
+     * @throws NullPointerException if uri or values are null
+     */
+    public final int update(@RequiresPermission.Write @NonNull Uri uri,
+            @Nullable ContentValues values, @Nullable String where,
+            @Nullable String[] selectionArgs) {
+        return update(uri, values, createSqlQueryBundle(where, selectionArgs));
+    }
+
+    /**
+     * Update row(s) in a content URI.
+     *
+     * If the content provider supports transactions the update will be atomic.
+     *
+     * @param uri The URI to modify.
+     * @param values The new field values. The key is the column name for the field.
+                     A null value will remove an existing field value.
+     * @param extras A Bundle containing additional information necessary for
+     *            the operation. Arguments may include SQL style arguments, such
+     *            as {@link ContentResolver#QUERY_ARG_SQL_LIMIT}, but note that
+     *            the documentation for each individual provider will indicate
+     *            which arguments they support.
+     * @return the number of rows updated.
+     * @throws NullPointerException if uri or values are null
+     * @throws IllegalArgumentException if the provider doesn't support one of
+     *             the requested Bundle arguments.
+     */
+    @Override
+    public final int update(@RequiresPermission.Write @NonNull Uri uri,
+            @Nullable ContentValues values, @Nullable Bundle extras) {
+        Objects.requireNonNull(uri, "uri");
+
+        try {
+            if (mWrapped != null) return mWrapped.update(uri, values, extras);
+        } catch (RemoteException e) {
+            return 0;
+        }
+
+        IContentProvider provider = acquireProvider(uri);
+        if (provider == null) {
+            throw new IllegalArgumentException("Unknown URI " + uri);
+        }
+        try {
+            long startTime = SystemClock.uptimeMillis();
+            int rowsUpdated = provider.update(mPackageName, mAttributionTag, uri, values, extras);
+            long durationMillis = SystemClock.uptimeMillis() - startTime;
+            maybeLogUpdateToEventLog(durationMillis, uri, "update", null);
+            return rowsUpdated;
+        } catch (RemoteException e) {
+            // Arbitrary and not worth documenting, as Activity
+            // Manager will kill this process shortly anyway.
+            return -1;
+        } finally {
+            releaseProvider(provider);
+        }
+    }
+
+    /**
+     * Call a provider-defined method.  This can be used to implement
+     * read or write interfaces which are cheaper than using a Cursor and/or
+     * do not fit into the traditional table model.
+     *
+     * @param method provider-defined method name to call.  Opaque to
+     *   framework, but must be non-null.
+     * @param arg provider-defined String argument.  May be null.
+     * @param extras provider-defined Bundle argument.  May be null.
+     * @return a result Bundle, possibly null.  Will be null if the ContentProvider
+     *   does not implement call.
+     * @throws NullPointerException if uri or method is null
+     * @throws IllegalArgumentException if uri is not known
+     */
+    public final @Nullable Bundle call(@NonNull Uri uri, @NonNull String method,
+            @Nullable String arg, @Nullable Bundle extras) {
+        return call(uri.getAuthority(), method, arg, extras);
+    }
+
+    @Override
+    public final @Nullable Bundle call(@NonNull String authority, @NonNull String method,
+            @Nullable String arg, @Nullable Bundle extras) {
+        Objects.requireNonNull(authority, "authority");
+        Objects.requireNonNull(method, "method");
+
+        try {
+            if (mWrapped != null) return mWrapped.call(authority, method, arg, extras);
+        } catch (RemoteException e) {
+            return null;
+        }
+
+        IContentProvider provider = acquireProvider(authority);
+        if (provider == null) {
+            throw new IllegalArgumentException("Unknown authority " + authority);
+        }
+        try {
+            final Bundle res = provider.call(mPackageName, mAttributionTag, authority, method, arg,
+                    extras);
+            Bundle.setDefusable(res, true);
+            return res;
+        } catch (RemoteException e) {
+            // Arbitrary and not worth documenting, as Activity
+            // Manager will kill this process shortly anyway.
+            return null;
+        } finally {
+            releaseProvider(provider);
+        }
+    }
+
+    /**
+     * Returns the content provider for the given content URI.
+     *
+     * @param uri The URI to a content provider
+     * @return The ContentProvider for the given URI, or null if no content provider is found.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public final IContentProvider acquireProvider(Uri uri) {
+        if (!SCHEME_CONTENT.equals(uri.getScheme())) {
+            return null;
+        }
+        final String auth = uri.getAuthority();
+        if (auth != null) {
+            return acquireProvider(mContext, auth);
+        }
+        return null;
+    }
+
+    /**
+     * Returns the content provider for the given content URI if the process
+     * already has a reference on it.
+     *
+     * @param uri The URI to a content provider
+     * @return The ContentProvider for the given URI, or null if no content provider is found.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public final IContentProvider acquireExistingProvider(Uri uri) {
+        if (!SCHEME_CONTENT.equals(uri.getScheme())) {
+            return null;
+        }
+        final String auth = uri.getAuthority();
+        if (auth != null) {
+            return acquireExistingProvider(mContext, auth);
+        }
+        return null;
+    }
+
+    /**
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public final IContentProvider acquireProvider(String name) {
+        if (name == null) {
+            return null;
+        }
+        return acquireProvider(mContext, name);
+    }
+
+    /**
+     * Returns the content provider for the given content URI.
+     *
+     * @param uri The URI to a content provider
+     * @return The ContentProvider for the given URI, or null if no content provider is found.
+     * @hide
+     */
+    public final IContentProvider acquireUnstableProvider(Uri uri) {
+        if (!SCHEME_CONTENT.equals(uri.getScheme())) {
+            return null;
+        }
+        String auth = uri.getAuthority();
+        if (auth != null) {
+            return acquireUnstableProvider(mContext, uri.getAuthority());
+        }
+        return null;
+    }
+
+    /**
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public final IContentProvider acquireUnstableProvider(String name) {
+        if (name == null) {
+            return null;
+        }
+        return acquireUnstableProvider(mContext, name);
+    }
+
+    /**
+     * Returns a {@link ContentProviderClient} that is associated with the {@link ContentProvider}
+     * that services the content at uri, starting the provider if necessary. Returns
+     * null if there is no provider associated wih the uri. The caller must indicate that they are
+     * done with the provider by calling {@link ContentProviderClient#release} which will allow
+     * the system to release the provider if it determines that there is no other reason for
+     * keeping it active.
+     * @param uri specifies which provider should be acquired
+     * @return a {@link ContentProviderClient} that is associated with the {@link ContentProvider}
+     * that services the content at uri or null if there isn't one.
+     */
+    public final @Nullable ContentProviderClient acquireContentProviderClient(@NonNull Uri uri) {
+        Objects.requireNonNull(uri, "uri");
+        IContentProvider provider = acquireProvider(uri);
+        if (provider != null) {
+            return new ContentProviderClient(this, provider, uri.getAuthority(), true);
+        }
+        return null;
+    }
+
+    /**
+     * Returns a {@link ContentProviderClient} that is associated with the {@link ContentProvider}
+     * with the authority of name, starting the provider if necessary. Returns
+     * null if there is no provider associated wih the uri. The caller must indicate that they are
+     * done with the provider by calling {@link ContentProviderClient#release} which will allow
+     * the system to release the provider if it determines that there is no other reason for
+     * keeping it active.
+     * @param name specifies which provider should be acquired
+     * @return a {@link ContentProviderClient} that is associated with the {@link ContentProvider}
+     * with the authority of name or null if there isn't one.
+     */
+    public final @Nullable ContentProviderClient acquireContentProviderClient(
+            @NonNull String name) {
+        Objects.requireNonNull(name, "name");
+        IContentProvider provider = acquireProvider(name);
+        if (provider != null) {
+            return new ContentProviderClient(this, provider, name, true);
+        }
+
+        return null;
+    }
+
+    /**
+     * Like {@link #acquireContentProviderClient(Uri)}, but for use when you do
+     * not trust the stability of the target content provider.  This turns off
+     * the mechanism in the platform clean up processes that are dependent on
+     * a content provider if that content provider's process goes away.  Normally
+     * you can safely assume that once you have acquired a provider, you can freely
+     * use it as needed and it won't disappear, even if your process is in the
+     * background.  If using this method, you need to take care to deal with any
+     * failures when communicating with the provider, and be sure to close it
+     * so that it can be re-opened later.  In particular, catching a
+     * {@link android.os.DeadObjectException} from the calls there will let you
+     * know that the content provider has gone away; at that point the current
+     * ContentProviderClient object is invalid, and you should release it.  You
+     * can acquire a new one if you would like to try to restart the provider
+     * and perform new operations on it.
+     */
+    public final @Nullable ContentProviderClient acquireUnstableContentProviderClient(
+            @NonNull Uri uri) {
+        Objects.requireNonNull(uri, "uri");
+        IContentProvider provider = acquireUnstableProvider(uri);
+        if (provider != null) {
+            return new ContentProviderClient(this, provider, uri.getAuthority(), false);
+        }
+
+        return null;
+    }
+
+    /**
+     * Like {@link #acquireContentProviderClient(String)}, but for use when you do
+     * not trust the stability of the target content provider.  This turns off
+     * the mechanism in the platform clean up processes that are dependent on
+     * a content provider if that content provider's process goes away.  Normally
+     * you can safely assume that once you have acquired a provider, you can freely
+     * use it as needed and it won't disappear, even if your process is in the
+     * background.  If using this method, you need to take care to deal with any
+     * failures when communicating with the provider, and be sure to close it
+     * so that it can be re-opened later.  In particular, catching a
+     * {@link android.os.DeadObjectException} from the calls there will let you
+     * know that the content provider has gone away; at that point the current
+     * ContentProviderClient object is invalid, and you should release it.  You
+     * can acquire a new one if you would like to try to restart the provider
+     * and perform new operations on it.
+     */
+    public final @Nullable ContentProviderClient acquireUnstableContentProviderClient(
+            @NonNull String name) {
+        Objects.requireNonNull(name, "name");
+        IContentProvider provider = acquireUnstableProvider(name);
+        if (provider != null) {
+            return new ContentProviderClient(this, provider, name, false);
+        }
+
+        return null;
+    }
+
+    /**
+     * Register an observer class that gets callbacks when data identified by a
+     * given content URI changes.
+     * <p>
+     * Starting in {@link android.os.Build.VERSION_CODES#O}, all content
+     * notifications must be backed by a valid {@link ContentProvider}.
+     *
+     * @param uri The URI to watch for changes. This can be a specific row URI,
+     *            or a base URI for a whole class of content.
+     * @param notifyForDescendants When false, the observer will be notified
+     *            whenever a change occurs to the exact URI specified by
+     *            <code>uri</code> or to one of the URI's ancestors in the path
+     *            hierarchy. When true, the observer will also be notified
+     *            whenever a change occurs to the URI's descendants in the path
+     *            hierarchy.
+     * @param observer The object that receives callbacks when changes occur.
+     * @see #unregisterContentObserver
+     */
+    public final void registerContentObserver(@NonNull Uri uri, boolean notifyForDescendants,
+            @NonNull ContentObserver observer) {
+        Objects.requireNonNull(uri, "uri");
+        Objects.requireNonNull(observer, "observer");
+        registerContentObserver(
+                ContentProvider.getUriWithoutUserId(uri),
+                notifyForDescendants,
+                observer,
+                ContentProvider.getUserIdFromUri(uri, mContext.getUserId()));
+    }
+
+    /** @hide - designated user version */
+    @UnsupportedAppUsage
+    public final void registerContentObserver(Uri uri, boolean notifyForDescendents,
+            ContentObserver observer, @UserIdInt int userHandle) {
+        try {
+            getContentService().registerContentObserver(uri, notifyForDescendents,
+                    observer.getContentObserver(), userHandle, mTargetSdkVersion);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Unregisters a change observer.
+     *
+     * @param observer The previously registered observer that is no longer needed.
+     * @see #registerContentObserver
+     */
+    public final void unregisterContentObserver(@NonNull ContentObserver observer) {
+        Objects.requireNonNull(observer, "observer");
+        try {
+            IContentObserver contentObserver = observer.releaseContentObserver();
+            if (contentObserver != null) {
+                getContentService().unregisterContentObserver(
+                        contentObserver);
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Notify registered observers that a row was updated and attempt to sync
+     * changes to the network.
+     * <p>
+     * To observe events sent through this call, use
+     * {@link #registerContentObserver(Uri, boolean, ContentObserver)}.
+     * <p>
+     * Starting in {@link android.os.Build.VERSION_CODES#O}, all content
+     * notifications must be backed by a valid {@link ContentProvider}.
+     *
+     * @param uri The uri of the content that was changed.
+     * @param observer The observer that originated the change, may be
+     *            <code>null</null>. The observer that originated the change
+     *            will only receive the notification if it has requested to
+     *            receive self-change notifications by implementing
+     *            {@link ContentObserver#deliverSelfNotifications()} to return
+     *            true.
+     */
+    public void notifyChange(@NonNull Uri uri, @Nullable ContentObserver observer) {
+        notifyChange(uri, observer, true /* sync to network */);
+    }
+
+    /**
+     * Notify registered observers that a row was updated.
+     * <p>
+     * To observe events sent through this call, use
+     * {@link #registerContentObserver(Uri, boolean, ContentObserver)}.
+     * <p>
+     * If syncToNetwork is true, this will attempt to schedule a local sync
+     * using the sync adapter that's registered for the authority of the
+     * provided uri. No account will be passed to the sync adapter, so all
+     * matching accounts will be synchronized.
+     * <p>
+     * Starting in {@link android.os.Build.VERSION_CODES#O}, all content
+     * notifications must be backed by a valid {@link ContentProvider}.
+     *
+     * @param uri The uri of the content that was changed.
+     * @param observer The observer that originated the change, may be
+     *            <code>null</null>. The observer that originated the change
+     *            will only receive the notification if it has requested to
+     *            receive self-change notifications by implementing
+     *            {@link ContentObserver#deliverSelfNotifications()} to return
+     *            true.
+     * @param syncToNetwork If true, same as {@link #NOTIFY_SYNC_TO_NETWORK}.
+     * @see #requestSync(android.accounts.Account, String, android.os.Bundle)
+     * @deprecated callers should consider migrating to
+     *             {@link #notifyChange(Uri, ContentObserver, int)}, as it
+     *             offers support for many more options than just
+     *             {@link #NOTIFY_SYNC_TO_NETWORK}.
+     */
+    @Deprecated
+    public void notifyChange(@NonNull Uri uri, @Nullable ContentObserver observer,
+            boolean syncToNetwork) {
+        notifyChange(uri, observer, syncToNetwork ? NOTIFY_SYNC_TO_NETWORK : 0);
+    }
+
+    /**
+     * Notify registered observers that a row was updated.
+     * <p>
+     * To observe events sent through this call, use
+     * {@link #registerContentObserver(Uri, boolean, ContentObserver)}.
+     * <p>
+     * If {@link #NOTIFY_SYNC_TO_NETWORK} is set, this will attempt to schedule
+     * a local sync using the sync adapter that's registered for the authority
+     * of the provided uri. No account will be passed to the sync adapter, so
+     * all matching accounts will be synchronized.
+     * <p>
+     * Starting in {@link android.os.Build.VERSION_CODES#O}, all content
+     * notifications must be backed by a valid {@link ContentProvider}.
+     *
+     * @param uri The uri of the content that was changed.
+     * @param observer The observer that originated the change, may be
+     *            <code>null</null>. The observer that originated the change
+     *            will only receive the notification if it has requested to
+     *            receive self-change notifications by implementing
+     *            {@link ContentObserver#deliverSelfNotifications()} to return
+     *            true.
+     * @param flags Additional flags: {@link #NOTIFY_SYNC_TO_NETWORK}.
+     * @see #requestSync(android.accounts.Account, String, android.os.Bundle)
+     */
+    public void notifyChange(@NonNull Uri uri, @Nullable ContentObserver observer,
+            @NotifyFlags int flags) {
+        Objects.requireNonNull(uri, "uri");
+        notifyChange(
+                ContentProvider.getUriWithoutUserId(uri),
+                observer,
+                flags,
+                ContentProvider.getUserIdFromUri(uri, mContext.getUserId()));
+    }
+
+    /** @removed */
+    @Deprecated
+    public void notifyChange(@NonNull Iterable<Uri> uris, @Nullable ContentObserver observer,
+            @NotifyFlags int flags) {
+        final Collection<Uri> asCollection = new ArrayList<>();
+        uris.forEach(asCollection::add);
+        notifyChange(asCollection, observer, flags);
+    }
+
+    /**
+     * Notify registered observers that several rows have been updated.
+     * <p>
+     * To observe events sent through this call, use
+     * {@link #registerContentObserver(Uri, boolean, ContentObserver)}.
+     * <p>
+     * If {@link #NOTIFY_SYNC_TO_NETWORK} is set, this will attempt to schedule
+     * a local sync using the sync adapter that's registered for the authority
+     * of the provided uri. No account will be passed to the sync adapter, so
+     * all matching accounts will be synchronized.
+     * <p>
+     * Starting in {@link android.os.Build.VERSION_CODES#O}, all content
+     * notifications must be backed by a valid {@link ContentProvider}.
+     *
+     * @param uris The uris of the content that was changed.
+     * @param observer The observer that originated the change, may be
+     *            <code>null</null>. The observer that originated the change
+     *            will only receive the notification if it has requested to
+     *            receive self-change notifications by implementing
+     *            {@link ContentObserver#deliverSelfNotifications()} to return
+     *            true.
+     * @param flags Flags such as {@link #NOTIFY_SYNC_TO_NETWORK} or
+     *            {@link #NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS}.
+     */
+    public void notifyChange(@NonNull Collection<Uri> uris, @Nullable ContentObserver observer,
+            @NotifyFlags int flags) {
+        Objects.requireNonNull(uris, "uris");
+
+        // Cluster based on user ID
+        final SparseArray<ArrayList<Uri>> clusteredByUser = new SparseArray<>();
+        for (Uri uri : uris) {
+            final int userId = ContentProvider.getUserIdFromUri(uri, mContext.getUserId());
+            ArrayList<Uri> list = clusteredByUser.get(userId);
+            if (list == null) {
+                list = new ArrayList<>();
+                clusteredByUser.put(userId, list);
+            }
+            list.add(ContentProvider.getUriWithoutUserId(uri));
+        }
+
+        for (int i = 0; i < clusteredByUser.size(); i++) {
+            final int userId = clusteredByUser.keyAt(i);
+            final ArrayList<Uri> list = clusteredByUser.valueAt(i);
+            notifyChange(list.toArray(new Uri[list.size()]), observer, flags, userId);
+        }
+    }
+
+    /**
+     * Notify registered observers within the designated user(s) that a row was updated.
+     *
+     * @deprecated callers should consider migrating to
+     *             {@link #notifyChange(Uri, ContentObserver, int)}, as it
+     *             offers support for many more options than just
+     *             {@link #NOTIFY_SYNC_TO_NETWORK}.
+     * @hide
+     */
+    @Deprecated
+    public void notifyChange(@NonNull Uri uri, ContentObserver observer, boolean syncToNetwork,
+            @UserIdInt int userHandle) {
+        notifyChange(uri, observer, syncToNetwork ? NOTIFY_SYNC_TO_NETWORK : 0, userHandle);
+    }
+
+    /** {@hide} */
+    public void notifyChange(@NonNull Uri uri, ContentObserver observer, @NotifyFlags int flags,
+            @UserIdInt int userHandle) {
+        notifyChange(new Uri[] { uri }, observer, flags, userHandle);
+    }
+
+    /**
+     * Notify registered observers within the designated user(s) that a row was updated.
+     *
+     * @hide
+     */
+    public void notifyChange(@NonNull Uri[] uris, ContentObserver observer, @NotifyFlags int flags,
+            @UserIdInt int userHandle) {
+        try {
+            getContentService().notifyChange(
+                    uris, observer == null ? null : observer.getContentObserver(),
+                    observer != null && observer.deliverSelfNotifications(), flags,
+                    userHandle, mTargetSdkVersion, mContext.getPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Take a persistable URI permission grant that has been offered. Once
+     * taken, the permission grant will be remembered across device reboots.
+     * Only URI permissions granted with
+     * {@link Intent#FLAG_GRANT_PERSISTABLE_URI_PERMISSION} can be persisted. If
+     * the grant has already been persisted, taking it again will touch
+     * {@link UriPermission#getPersistedTime()}.
+     *
+     * @see #getPersistedUriPermissions()
+     */
+    public void takePersistableUriPermission(@NonNull Uri uri,
+            @Intent.AccessUriMode int modeFlags) {
+        Objects.requireNonNull(uri, "uri");
+        try {
+            UriGrantsManager.getService().takePersistableUriPermission(
+                    ContentProvider.getUriWithoutUserId(uri), modeFlags, /* toPackage= */ null,
+                    resolveUserId(uri));
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public void takePersistableUriPermission(@NonNull String toPackage, @NonNull Uri uri,
+            @Intent.AccessUriMode int modeFlags) {
+        Objects.requireNonNull(toPackage, "toPackage");
+        Objects.requireNonNull(uri, "uri");
+        try {
+            UriGrantsManager.getService().takePersistableUriPermission(
+                    ContentProvider.getUriWithoutUserId(uri), modeFlags, toPackage,
+                    resolveUserId(uri));
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Relinquish a persisted URI permission grant. The URI must have been
+     * previously made persistent with
+     * {@link #takePersistableUriPermission(Uri, int)}. Any non-persistent
+     * grants to the calling package will remain intact.
+     *
+     * @see #getPersistedUriPermissions()
+     */
+    public void releasePersistableUriPermission(@NonNull Uri uri,
+            @Intent.AccessUriMode int modeFlags) {
+        Objects.requireNonNull(uri, "uri");
+        try {
+            UriGrantsManager.getService().releasePersistableUriPermission(
+                    ContentProvider.getUriWithoutUserId(uri), modeFlags, /* toPackage= */ null,
+                    resolveUserId(uri));
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Return list of all URI permission grants that have been persisted by the
+     * calling app. That is, the returned permissions have been granted
+     * <em>to</em> the calling app. Only persistable grants taken with
+     * {@link #takePersistableUriPermission(Uri, int)} are returned.
+     * <p>Note: Some of the returned URIs may not be usable until after the user is unlocked.
+     *
+     * @see #takePersistableUriPermission(Uri, int)
+     * @see #releasePersistableUriPermission(Uri, int)
+     */
+    public @NonNull List<UriPermission> getPersistedUriPermissions() {
+        try {
+            return UriGrantsManager.getService().getUriPermissions(
+                    mPackageName, true /* incoming */, true /* persistedOnly */).getList();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Return list of all persisted URI permission grants that are hosted by the
+     * calling app. That is, the returned permissions have been granted
+     * <em>from</em> the calling app. Only grants taken with
+     * {@link #takePersistableUriPermission(Uri, int)} are returned.
+     * <p>Note: Some of the returned URIs may not be usable until after the user is unlocked.
+     */
+    public @NonNull List<UriPermission> getOutgoingPersistedUriPermissions() {
+        try {
+            return UriGrantsManager.getService().getUriPermissions(
+                    mPackageName, false /* incoming */, true /* persistedOnly */).getList();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /** @hide */
+    public @NonNull List<UriPermission> getOutgoingUriPermissions() {
+        try {
+            return UriGrantsManager.getService().getUriPermissions(
+                    mPackageName, false /* incoming */, false /* persistedOnly */).getList();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Start an asynchronous sync operation. If you want to monitor the progress
+     * of the sync you may register a SyncObserver. Only values of the following
+     * types may be used in the extras bundle:
+     * <ul>
+     * <li>Integer</li>
+     * <li>Long</li>
+     * <li>Boolean</li>
+     * <li>Float</li>
+     * <li>Double</li>
+     * <li>String</li>
+     * <li>Account</li>
+     * <li>null</li>
+     * </ul>
+     *
+     * @param uri the uri of the provider to sync or null to sync all providers.
+     * @param extras any extras to pass to the SyncAdapter.
+     * @deprecated instead use
+     * {@link #requestSync(android.accounts.Account, String, android.os.Bundle)}
+     */
+    @Deprecated
+    public void startSync(Uri uri, Bundle extras) {
+        Account account = null;
+        if (extras != null) {
+            String accountName = extras.getString(SYNC_EXTRAS_ACCOUNT);
+            if (!TextUtils.isEmpty(accountName)) {
+                // TODO: No references to Google in AOSP
+                account = new Account(accountName, "com.google");
+            }
+            extras.remove(SYNC_EXTRAS_ACCOUNT);
+        }
+        requestSync(account, uri != null ? uri.getAuthority() : null, extras);
+    }
+
+    /**
+     * Start an asynchronous sync operation. If you want to monitor the progress
+     * of the sync you may register a SyncObserver. Only values of the following
+     * types may be used in the extras bundle:
+     * <ul>
+     * <li>Integer</li>
+     * <li>Long</li>
+     * <li>Boolean</li>
+     * <li>Float</li>
+     * <li>Double</li>
+     * <li>String</li>
+     * <li>Account</li>
+     * <li>null</li>
+     * </ul>
+     *
+     * @param account which account should be synced
+     * @param authority which authority should be synced
+     * @param extras any extras to pass to the SyncAdapter.
+     */
+    public static void requestSync(Account account, String authority, Bundle extras) {
+        requestSyncAsUser(account, authority, UserHandle.myUserId(), extras);
+    }
+
+    /**
+     * @see #requestSync(Account, String, Bundle)
+     * @hide
+     */
+    public static void requestSyncAsUser(Account account, String authority, @UserIdInt int userId,
+            Bundle extras) {
+        if (extras == null) {
+            throw new IllegalArgumentException("Must specify extras.");
+        }
+        SyncRequest request =
+            new SyncRequest.Builder()
+                .setSyncAdapter(account, authority)
+                .setExtras(extras)
+                .syncOnce()     // Immediate sync.
+                .build();
+        try {
+            // Note ActivityThread.currentPackageName() may not be accurate in a shared process
+            // case, but it's only for debugging.
+            getContentService().syncAsUser(request, userId, ActivityThread.currentPackageName());
+        } catch(RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Register a sync with the SyncManager. These requests are built using the
+     * {@link SyncRequest.Builder}.
+     */
+    public static void requestSync(SyncRequest request) {
+        try {
+            // Note ActivityThread.currentPackageName() may not be accurate in a shared process
+            // case, but it's only for debugging.
+            getContentService().sync(request, ActivityThread.currentPackageName());
+        } catch(RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Check that only values of the following types are in the Bundle:
+     * <ul>
+     * <li>Integer</li>
+     * <li>Long</li>
+     * <li>Boolean</li>
+     * <li>Float</li>
+     * <li>Double</li>
+     * <li>String</li>
+     * <li>Account</li>
+     * <li>null</li>
+     * </ul>
+     * @param extras the Bundle to check
+     */
+    public static void validateSyncExtrasBundle(Bundle extras) {
+        try {
+            for (String key : extras.keySet()) {
+                Object value = extras.get(key);
+                if (value == null) continue;
+                if (value instanceof Long) continue;
+                if (value instanceof Integer) continue;
+                if (value instanceof Boolean) continue;
+                if (value instanceof Float) continue;
+                if (value instanceof Double) continue;
+                if (value instanceof String) continue;
+                if (value instanceof Account) continue;
+                throw new IllegalArgumentException("unexpected value type: "
+                        + value.getClass().getName());
+            }
+        } catch (IllegalArgumentException e) {
+            throw e;
+        } catch (RuntimeException exc) {
+            throw new IllegalArgumentException("error unparceling Bundle", exc);
+        }
+    }
+
+    /**
+     * Cancel any active or pending syncs that match the Uri. If the uri is null then
+     * all syncs will be canceled.
+     *
+     * @param uri the uri of the provider to sync or null to sync all providers.
+     * @deprecated instead use {@link #cancelSync(android.accounts.Account, String)}
+     */
+    @Deprecated
+    public void cancelSync(Uri uri) {
+        cancelSync(null /* all accounts */, uri != null ? uri.getAuthority() : null);
+    }
+
+    /**
+     * Cancel any active or pending syncs that match account and authority. The account and
+     * authority can each independently be set to null, which means that syncs with any account
+     * or authority, respectively, will match.
+     *
+     * @param account filters the syncs that match by this account
+     * @param authority filters the syncs that match by this authority
+     */
+    public static void cancelSync(Account account, String authority) {
+        try {
+            getContentService().cancelSync(account, authority, null);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @see #cancelSync(Account, String)
+     * @hide
+     */
+    public static void cancelSyncAsUser(Account account, String authority, @UserIdInt int userId) {
+        try {
+            getContentService().cancelSyncAsUser(account, authority, null, userId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Get information about the SyncAdapters that are known to the system.
+     * @return an array of SyncAdapters that have registered with the system
+     */
+    public static SyncAdapterType[] getSyncAdapterTypes() {
+        try {
+            return getContentService().getSyncAdapterTypes();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @see #getSyncAdapterTypes()
+     * @hide
+     */
+    public static SyncAdapterType[] getSyncAdapterTypesAsUser(@UserIdInt int userId) {
+        try {
+            return getContentService().getSyncAdapterTypesAsUser(userId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @hide
+     * Returns the package names of syncadapters that match a given user and authority.
+     */
+    @TestApi
+    public static String[] getSyncAdapterPackagesForAuthorityAsUser(String authority,
+            @UserIdInt int userId) {
+        try {
+            return getContentService().getSyncAdapterPackagesForAuthorityAsUser(authority, userId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Check if the provider should be synced when a network tickle is received
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#READ_SYNC_SETTINGS}.
+     *
+     * @param account the account whose setting we are querying
+     * @param authority the provider whose setting we are querying
+     * @return true if the provider should be synced when a network tickle is received
+     */
+    public static boolean getSyncAutomatically(Account account, String authority) {
+        try {
+            return getContentService().getSyncAutomatically(account, authority);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @see #getSyncAutomatically(Account, String)
+     * @hide
+     */
+    public static boolean getSyncAutomaticallyAsUser(Account account, String authority,
+            @UserIdInt int userId) {
+        try {
+            return getContentService().getSyncAutomaticallyAsUser(account, authority, userId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Set whether or not the provider is synced when it receives a network tickle.
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#WRITE_SYNC_SETTINGS}.
+     *
+     * @param account the account whose setting we are querying
+     * @param authority the provider whose behavior is being controlled
+     * @param sync true if the provider should be synced when tickles are received for it
+     */
+    public static void setSyncAutomatically(Account account, String authority, boolean sync) {
+        setSyncAutomaticallyAsUser(account, authority, sync, UserHandle.myUserId());
+    }
+
+    /**
+     * @see #setSyncAutomatically(Account, String, boolean)
+     * @hide
+     */
+    public static void setSyncAutomaticallyAsUser(Account account, String authority, boolean sync,
+            @UserIdInt int userId) {
+        try {
+            getContentService().setSyncAutomaticallyAsUser(account, authority, sync, userId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Specifies that a sync should be requested with the specified the account, authority,
+     * and extras at the given frequency. If there is already another periodic sync scheduled
+     * with the account, authority and extras then a new periodic sync won't be added, instead
+     * the frequency of the previous one will be updated.
+     * <p>
+     * These periodic syncs honor the "syncAutomatically" and "masterSyncAutomatically" settings.
+     * Although these sync are scheduled at the specified frequency, it may take longer for it to
+     * actually be started if other syncs are ahead of it in the sync operation queue. This means
+     * that the actual start time may drift.
+     * <p>
+     * Periodic syncs are not allowed to have any of {@link #SYNC_EXTRAS_DO_NOT_RETRY},
+     * {@link #SYNC_EXTRAS_IGNORE_BACKOFF}, {@link #SYNC_EXTRAS_IGNORE_SETTINGS},
+     * {@link #SYNC_EXTRAS_INITIALIZE}, {@link #SYNC_EXTRAS_FORCE},
+     * {@link #SYNC_EXTRAS_EXPEDITED}, {@link #SYNC_EXTRAS_MANUAL} set to true.
+     * If any are supplied then an {@link IllegalArgumentException} will be thrown.
+     *
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#WRITE_SYNC_SETTINGS}.
+     * <p>The bundle for a periodic sync can be queried by applications with the correct
+     * permissions using
+     * {@link ContentResolver#getPeriodicSyncs(Account account, String provider)}, so no
+     * sensitive data should be transferred here.
+     *
+     * @param account the account to specify in the sync
+     * @param authority the provider to specify in the sync request
+     * @param extras extra parameters to go along with the sync request
+     * @param pollFrequency how frequently the sync should be performed, in seconds.
+     * On Android API level 24 and above, a minmam interval of 15 minutes is enforced.
+     * On previous versions, the minimum interval is 1 hour.
+     * @throws IllegalArgumentException if an illegal extra was set or if any of the parameters
+     * are null.
+     */
+    public static void addPeriodicSync(Account account, String authority, Bundle extras,
+            long pollFrequency) {
+        validateSyncExtrasBundle(extras);
+        if (invalidPeriodicExtras(extras)) {
+            throw new IllegalArgumentException("illegal extras were set");
+        }
+        try {
+             getContentService().addPeriodicSync(account, authority, extras, pollFrequency);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * {@hide}
+     * Helper function to throw an <code>IllegalArgumentException</code> if any illegal
+     * extras were set for a periodic sync.
+     *
+     * @param extras bundle to validate.
+     */
+    public static boolean invalidPeriodicExtras(Bundle extras) {
+        if (extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false)
+                || extras.getBoolean(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY, false)
+                || extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, false)
+                || extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, false)
+                || extras.getBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, false)
+                || extras.getBoolean(ContentResolver.SYNC_EXTRAS_FORCE, false)
+                || extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false)) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Remove a periodic sync. Has no affect if account, authority and extras don't match
+     * an existing periodic sync.
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#WRITE_SYNC_SETTINGS}.
+     *
+     * @param account the account of the periodic sync to remove
+     * @param authority the provider of the periodic sync to remove
+     * @param extras the extras of the periodic sync to remove
+     */
+    public static void removePeriodicSync(Account account, String authority, Bundle extras) {
+        validateSyncExtrasBundle(extras);
+        try {
+            getContentService().removePeriodicSync(account, authority, extras);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Remove the specified sync. This will cancel any pending or active syncs. If the request is
+     * for a periodic sync, this call will remove any future occurrences.
+     * <p>
+     *     If a periodic sync is specified, the caller must hold the permission
+     *     {@link android.Manifest.permission#WRITE_SYNC_SETTINGS}.
+     *</p>
+     * It is possible to cancel a sync using a SyncRequest object that is not the same object
+     * with which you requested the sync. Do so by building a SyncRequest with the same
+     * adapter, frequency, <b>and</b> extras bundle.
+     *
+     * @param request SyncRequest object containing information about sync to cancel.
+     */
+    public static void cancelSync(SyncRequest request) {
+        if (request == null) {
+            throw new IllegalArgumentException("request cannot be null");
+        }
+        try {
+            getContentService().cancelRequest(request);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Get the list of information about the periodic syncs for the given account and authority.
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#READ_SYNC_SETTINGS}.
+     *
+     * @param account the account whose periodic syncs we are querying
+     * @param authority the provider whose periodic syncs we are querying
+     * @return a list of PeriodicSync objects. This list may be empty but will never be null.
+     */
+    public static List<PeriodicSync> getPeriodicSyncs(Account account, String authority) {
+        try {
+            return getContentService().getPeriodicSyncs(account, authority, null);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Check if this account/provider is syncable.
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#READ_SYNC_SETTINGS}.
+     * @return >0 if it is syncable, 0 if not, and <0 if the state isn't known yet.
+     */
+    public static int getIsSyncable(Account account, String authority) {
+        try {
+            return getContentService().getIsSyncable(account, authority);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @see #getIsSyncable(Account, String)
+     * @hide
+     */
+    public static int getIsSyncableAsUser(Account account, String authority,
+            @UserIdInt int userId) {
+        try {
+            return getContentService().getIsSyncableAsUser(account, authority, userId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Set whether this account/provider is syncable.
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#WRITE_SYNC_SETTINGS}.
+     * @param syncable >0 denotes syncable, 0 means not syncable, <0 means unknown
+     */
+    public static void setIsSyncable(Account account, String authority, int syncable) {
+        try {
+            getContentService().setIsSyncable(account, authority, syncable);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @see #setIsSyncable(Account, String, int)
+     * @hide
+     */
+    public static void setIsSyncableAsUser(Account account, String authority, int syncable,
+            int userId) {
+        try {
+            getContentService().setIsSyncableAsUser(account, authority, syncable, userId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Gets the master auto-sync setting that applies to all the providers and accounts.
+     * If this is false then the per-provider auto-sync setting is ignored.
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#READ_SYNC_SETTINGS}.
+     *
+     * @return the master auto-sync setting that applies to all the providers and accounts
+     */
+    public static boolean getMasterSyncAutomatically() {
+        try {
+            return getContentService().getMasterSyncAutomatically();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @see #getMasterSyncAutomatically()
+     * @hide
+     */
+    public static boolean getMasterSyncAutomaticallyAsUser(@UserIdInt int userId) {
+        try {
+            return getContentService().getMasterSyncAutomaticallyAsUser(userId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Sets the master auto-sync setting that applies to all the providers and accounts.
+     * If this is false then the per-provider auto-sync setting is ignored.
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#WRITE_SYNC_SETTINGS}.
+     *
+     * @param sync the master auto-sync setting that applies to all the providers and accounts
+     */
+    public static void setMasterSyncAutomatically(boolean sync) {
+        setMasterSyncAutomaticallyAsUser(sync, UserHandle.myUserId());
+    }
+
+    /**
+     * @see #setMasterSyncAutomatically(boolean)
+     * @hide
+     */
+    public static void setMasterSyncAutomaticallyAsUser(boolean sync, @UserIdInt int userId) {
+        try {
+            getContentService().setMasterSyncAutomaticallyAsUser(sync, userId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns true if there is currently a sync operation for the given account or authority
+     * actively being processed.
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#READ_SYNC_STATS}.
+     * @param account the account whose setting we are querying
+     * @param authority the provider whose behavior is being queried
+     * @return true if a sync is active for the given account or authority.
+     */
+    public static boolean isSyncActive(Account account, String authority) {
+        if (account == null) {
+            throw new IllegalArgumentException("account must not be null");
+        }
+        if (authority == null) {
+            throw new IllegalArgumentException("authority must not be null");
+        }
+
+        try {
+            return getContentService().isSyncActive(account, authority, null);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * If a sync is active returns the information about it, otherwise returns null.
+     * <p>
+     * This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#READ_SYNC_STATS}.
+     * <p>
+     * @return the SyncInfo for the currently active sync or null if one is not active.
+     * @deprecated
+     * Since multiple concurrent syncs are now supported you should use
+     * {@link #getCurrentSyncs()} to get the accurate list of current syncs.
+     * This method returns the first item from the list of current syncs
+     * or null if there are none.
+     */
+    @Deprecated
+    public static SyncInfo getCurrentSync() {
+        try {
+            final List<SyncInfo> syncs = getContentService().getCurrentSyncs();
+            if (syncs.isEmpty()) {
+                return null;
+            }
+            return syncs.get(0);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns a list with information about all the active syncs. This list will be empty
+     * if there are no active syncs.
+     * <p>
+     * This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#READ_SYNC_STATS}.
+     * <p>
+     * @return a List of SyncInfo objects for the currently active syncs.
+     */
+    public static List<SyncInfo> getCurrentSyncs() {
+        try {
+            return getContentService().getCurrentSyncs();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @see #getCurrentSyncs()
+     * @hide
+     */
+    public static List<SyncInfo> getCurrentSyncsAsUser(@UserIdInt int userId) {
+        try {
+            return getContentService().getCurrentSyncsAsUser(userId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the status that matches the authority.
+     * @param account the account whose setting we are querying
+     * @param authority the provider whose behavior is being queried
+     * @return the SyncStatusInfo for the authority, or null if none exists
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static SyncStatusInfo getSyncStatus(Account account, String authority) {
+        try {
+            return getContentService().getSyncStatus(account, authority, null);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @see #getSyncStatus(Account, String)
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static SyncStatusInfo getSyncStatusAsUser(Account account, String authority,
+            @UserIdInt int userId) {
+        try {
+            return getContentService().getSyncStatusAsUser(account, authority, null, userId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Return true if the pending status is true of any matching authorities.
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#READ_SYNC_STATS}.
+     * @param account the account whose setting we are querying
+     * @param authority the provider whose behavior is being queried
+     * @return true if there is a pending sync with the matching account and authority
+     */
+    public static boolean isSyncPending(Account account, String authority) {
+        return isSyncPendingAsUser(account, authority, UserHandle.myUserId());
+    }
+
+    /**
+     * @see #requestSync(Account, String, Bundle)
+     * @hide
+     */
+    public static boolean isSyncPendingAsUser(Account account, String authority,
+            @UserIdInt int userId) {
+        try {
+            return getContentService().isSyncPendingAsUser(account, authority, null, userId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Request notifications when the different aspects of the SyncManager change. The
+     * different items that can be requested are:
+     * <ul>
+     * <li> {@link #SYNC_OBSERVER_TYPE_PENDING}
+     * <li> {@link #SYNC_OBSERVER_TYPE_ACTIVE}
+     * <li> {@link #SYNC_OBSERVER_TYPE_SETTINGS}
+     * </ul>
+     * The caller can set one or more of the status types in the mask for any
+     * given listener registration.
+     * @param mask the status change types that will cause the callback to be invoked
+     * @param callback observer to be invoked when the status changes
+     * @return a handle that can be used to remove the listener at a later time
+     */
+    public static Object addStatusChangeListener(int mask, final SyncStatusObserver callback) {
+        if (callback == null) {
+            throw new IllegalArgumentException("you passed in a null callback");
+        }
+        try {
+            ISyncStatusObserver.Stub observer = new ISyncStatusObserver.Stub() {
+                @Override
+                public void onStatusChanged(int which) throws RemoteException {
+                    callback.onStatusChanged(which);
+                }
+            };
+            getContentService().addStatusChangeListener(mask, observer);
+            return observer;
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Remove a previously registered status change listener.
+     * @param handle the handle that was returned by {@link #addStatusChangeListener}
+     */
+    public static void removeStatusChangeListener(Object handle) {
+        if (handle == null) {
+            throw new IllegalArgumentException("you passed in a null handle");
+        }
+        try {
+            getContentService().removeStatusChangeListener((ISyncStatusObserver.Stub) handle);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Store the given {@link Bundle} as a long-lived cached object within the
+     * system. This can be useful to avoid expensive re-parsing when apps are
+     * restarted multiple times on low-RAM devices.
+     * <p>
+     * The {@link Bundle} is automatically invalidated when a
+     * {@link #notifyChange(Uri, ContentObserver)} event applies to the key.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.CACHE_CONTENT)
+    public void putCache(@NonNull Uri key, @Nullable Bundle value) {
+        try {
+            getContentService().putCache(mContext.getPackageName(), key, value,
+                    mContext.getUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Retrieve the last {@link Bundle} stored as a long-lived cached object
+     * within the system.
+     *
+     * @return {@code null} if no cached object has been stored, or if the
+     *         stored object has been invalidated due to a
+     *         {@link #notifyChange(Uri, ContentObserver)} event.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.CACHE_CONTENT)
+    public @Nullable Bundle getCache(@NonNull Uri key) {
+        try {
+            final Bundle bundle = getContentService().getCache(mContext.getPackageName(), key,
+                    mContext.getUserId());
+            if (bundle != null) bundle.setClassLoader(mContext.getClassLoader());
+            return bundle;
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /** {@hide} */
+    public int getTargetSdkVersion() {
+        return mTargetSdkVersion;
+    }
+
+    /**
+     * Returns sampling percentage for a given duration.
+     *
+     * Always returns at least 1%.
+     */
+    private int samplePercentForDuration(long durationMillis) {
+        if (durationMillis >= SLOW_THRESHOLD_MILLIS) {
+            return 100;
+        }
+        return (int) (100 * durationMillis / SLOW_THRESHOLD_MILLIS) + 1;
+    }
+
+    private void maybeLogQueryToEventLog(
+            long durationMillis, Uri uri, String[] projection, @Nullable Bundle queryArgs) {
+        if (!ENABLE_CONTENT_SAMPLE) return;
+        int samplePercent = samplePercentForDuration(durationMillis);
+        if (samplePercent < 100) {
+            synchronized (mRandom) {
+                if (mRandom.nextInt(100) >= samplePercent) {
+                    return;
+                }
+            }
+        }
+
+        // Ensure a non-null bundle.
+        queryArgs = (queryArgs != null) ? queryArgs : Bundle.EMPTY;
+
+        StringBuilder projectionBuffer = new StringBuilder(100);
+        if (projection != null) {
+            for (int i = 0; i < projection.length; ++i) {
+                // Note: not using a comma delimiter here, as the
+                // multiple arguments to EventLog.writeEvent later
+                // stringify with a comma delimiter, which would make
+                // parsing uglier later.
+                if (i != 0) projectionBuffer.append('/');
+                projectionBuffer.append(projection[i]);
+            }
+        }
+
+        // ActivityThread.currentPackageName() only returns non-null if the
+        // current thread is an application main thread.  This parameter tells
+        // us whether an event loop is blocked, and if so, which app it is.
+        String blockingPackage = AppGlobals.getInitialPackage();
+
+        EventLog.writeEvent(
+            EventLogTags.CONTENT_QUERY_SAMPLE,
+            uri.toString(),
+            projectionBuffer.toString(),
+            queryArgs.getString(QUERY_ARG_SQL_SELECTION, ""),
+            queryArgs.getString(QUERY_ARG_SQL_SORT_ORDER, ""),
+            durationMillis,
+            blockingPackage != null ? blockingPackage : "",
+            samplePercent);
+    }
+
+    private void maybeLogUpdateToEventLog(
+        long durationMillis, Uri uri, String operation, String selection) {
+        if (!ENABLE_CONTENT_SAMPLE) return;
+        int samplePercent = samplePercentForDuration(durationMillis);
+        if (samplePercent < 100) {
+            synchronized (mRandom) {
+                if (mRandom.nextInt(100) >= samplePercent) {
+                    return;
+                }
+            }
+        }
+        String blockingPackage = AppGlobals.getInitialPackage();
+        EventLog.writeEvent(
+            EventLogTags.CONTENT_UPDATE_SAMPLE,
+            uri.toString(),
+            operation,
+            selection != null ? selection : "",
+            durationMillis,
+            blockingPackage != null ? blockingPackage : "",
+            samplePercent);
+    }
+
+    private final class CursorWrapperInner extends CrossProcessCursorWrapper {
+        private final IContentProvider mContentProvider;
+        private final AtomicBoolean mProviderReleased = new AtomicBoolean();
+
+        private final CloseGuard mCloseGuard = CloseGuard.get();
+
+        CursorWrapperInner(Cursor cursor, IContentProvider contentProvider) {
+            super(cursor);
+            mContentProvider = contentProvider;
+            mCloseGuard.open("close");
+        }
+
+        @Override
+        public void close() {
+            mCloseGuard.close();
+            super.close();
+
+            if (mProviderReleased.compareAndSet(false, true)) {
+                ContentResolver.this.releaseProvider(mContentProvider);
+            }
+        }
+
+        @Override
+        protected void finalize() throws Throwable {
+            try {
+                if (mCloseGuard != null) {
+                    mCloseGuard.warnIfOpen();
+                }
+
+                close();
+            } finally {
+                super.finalize();
+            }
+        }
+    }
+
+    private final class ParcelFileDescriptorInner extends ParcelFileDescriptor {
+        private final IContentProvider mContentProvider;
+        private final AtomicBoolean mProviderReleased = new AtomicBoolean();
+
+        ParcelFileDescriptorInner(ParcelFileDescriptor pfd, IContentProvider icp) {
+            super(pfd);
+            mContentProvider = icp;
+        }
+
+        @Override
+        public void releaseResources() {
+            if (mProviderReleased.compareAndSet(false, true)) {
+                ContentResolver.this.releaseProvider(mContentProvider);
+            }
+        }
+    }
+
+    /** @hide */
+    public static final String CONTENT_SERVICE_NAME = "content";
+
+    /** @hide */
+    @UnsupportedAppUsage
+    public static IContentService getContentService() {
+        if (sContentService != null) {
+            return sContentService;
+        }
+        IBinder b = ServiceManager.getService(CONTENT_SERVICE_NAME);
+        sContentService = IContentService.Stub.asInterface(b);
+        return sContentService;
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage
+    public String getPackageName() {
+        return mPackageName;
+    }
+
+    /** @hide */
+    public @Nullable String getAttributionTag() {
+        return mAttributionTag;
+    }
+
+    @UnsupportedAppUsage
+    private static volatile IContentService sContentService;
+    @UnsupportedAppUsage
+    private final Context mContext;
+
+    @UnsupportedAppUsage
+    final String mPackageName;
+    final @Nullable String mAttributionTag;
+    final int mTargetSdkVersion;
+    final ContentInterface mWrapped;
+
+    private static final String TAG = "ContentResolver";
+
+    /** @hide */
+    public int resolveUserId(Uri uri) {
+        return ContentProvider.getUserIdFromUri(uri, mContext.getUserId());
+    }
+
+    /** @hide */
+    public int getUserId() {
+        return mContext.getUserId();
+    }
+
+    /** {@hide} */
+    @Deprecated
+    public Drawable getTypeDrawable(String mimeType) {
+        return getTypeInfo(mimeType).getIcon().loadDrawable(mContext);
+    }
+
+    /**
+     * Return a detailed description of the given MIME type, including an icon
+     * and label that describe the type.
+     *
+     * @param mimeType Valid, concrete MIME type.
+     */
+    public final @NonNull MimeTypeInfo getTypeInfo(@NonNull String mimeType) {
+        Objects.requireNonNull(mimeType);
+        return MimeIconUtils.getTypeInfo(mimeType);
+    }
+
+    /**
+     * Detailed description of a specific MIME type, including an icon and label
+     * that describe the type.
+     */
+    public static final class MimeTypeInfo {
+        private final Icon mIcon;
+        private final CharSequence mLabel;
+        private final CharSequence mContentDescription;
+
+        /** {@hide} */
+        public MimeTypeInfo(@NonNull Icon icon, @NonNull CharSequence label,
+                @NonNull CharSequence contentDescription) {
+            mIcon = Objects.requireNonNull(icon);
+            mLabel = Objects.requireNonNull(label);
+            mContentDescription = Objects.requireNonNull(contentDescription);
+        }
+
+        /**
+         * Return a visual representation of this MIME type. This can be styled
+         * using {@link Icon#setTint(int)} to match surrounding UI.
+         *
+         * @see Icon#loadDrawable(Context)
+         * @see android.widget.ImageView#setImageDrawable(Drawable)
+         */
+        public @NonNull Icon getIcon() {
+            return mIcon;
+        }
+
+        /**
+         * Return a textual representation of this MIME type.
+         *
+         * @see android.widget.TextView#setText(CharSequence)
+         */
+        public @NonNull CharSequence getLabel() {
+            return mLabel;
+        }
+
+        /**
+         * Return a content description for this MIME type.
+         *
+         * @see android.view.View#setContentDescription(CharSequence)
+         */
+        public @NonNull CharSequence getContentDescription() {
+            return mContentDescription;
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public static @Nullable Bundle createSqlQueryBundle(
+            @Nullable String selection,
+            @Nullable String[] selectionArgs) {
+        return createSqlQueryBundle(selection, selectionArgs, null);
+    }
+
+    /**
+     * @hide
+     */
+    public static @Nullable Bundle createSqlQueryBundle(
+            @Nullable String selection,
+            @Nullable String[] selectionArgs,
+            @Nullable String sortOrder) {
+
+        if (selection == null && selectionArgs == null && sortOrder == null) {
+            return null;
+        }
+
+        Bundle queryArgs = new Bundle();
+        if (selection != null) {
+            queryArgs.putString(QUERY_ARG_SQL_SELECTION, selection);
+        }
+        if (selectionArgs != null) {
+            queryArgs.putStringArray(QUERY_ARG_SQL_SELECTION_ARGS, selectionArgs);
+        }
+        if (sortOrder != null) {
+            queryArgs.putString(QUERY_ARG_SQL_SORT_ORDER, sortOrder);
+        }
+        return queryArgs;
+    }
+
+    /** @hide */
+    public static @NonNull Bundle includeSqlSelectionArgs(@NonNull Bundle queryArgs,
+            @Nullable String selection, @Nullable String[] selectionArgs) {
+        if (selection != null) {
+            queryArgs.putString(QUERY_ARG_SQL_SELECTION, selection);
+        }
+        if (selectionArgs != null) {
+            queryArgs.putStringArray(QUERY_ARG_SQL_SELECTION_ARGS, selectionArgs);
+        }
+        return queryArgs;
+    }
+
+    /**
+     * Returns structured sort args formatted as an SQL sort clause.
+     *
+     * NOTE: Collator clauses are suitable for use with non text fields. We might
+     * choose to omit any collation clause since we don't know the underlying
+     * type of data to be collated. Imperical testing shows that sqlite3 doesn't
+     * appear to care much about the presence of collate clauses in queries
+     * when ordering by numeric fields. For this reason we include collate
+     * clause unilaterally when {@link #QUERY_ARG_SORT_COLLATION} is present
+     * in query args bundle.
+     *
+     * TODO: Would be nice to explicitly validate that colums referenced in
+     * {@link #QUERY_ARG_SORT_COLUMNS} are present in the associated projection.
+     *
+     * @hide
+     */
+    public static String createSqlSortClause(Bundle queryArgs) {
+        String[] columns = queryArgs.getStringArray(QUERY_ARG_SORT_COLUMNS);
+        if (columns == null || columns.length == 0) {
+            throw new IllegalArgumentException("Can't create sort clause without columns.");
+        }
+
+        String query = TextUtils.join(", ", columns);
+
+        // Interpret PRIMARY and SECONDARY collation strength as no-case collation based
+        // on their javadoc descriptions.
+        int collation = queryArgs.getInt(
+                ContentResolver.QUERY_ARG_SORT_COLLATION, java.text.Collator.IDENTICAL);
+        if (collation == java.text.Collator.PRIMARY || collation == java.text.Collator.SECONDARY) {
+            query += " COLLATE NOCASE";
+        }
+
+        int sortDir = queryArgs.getInt(QUERY_ARG_SORT_DIRECTION, Integer.MIN_VALUE);
+        if (sortDir != Integer.MIN_VALUE) {
+            switch (sortDir) {
+                case QUERY_SORT_DIRECTION_ASCENDING:
+                    query += " ASC";
+                    break;
+                case QUERY_SORT_DIRECTION_DESCENDING:
+                    query += " DESC";
+                    break;
+                default:
+                    throw new IllegalArgumentException("Unsupported sort direction value."
+                            + " See ContentResolver documentation for details.");
+            }
+        }
+        return query;
+    }
+
+    /**
+     * Convenience method that efficiently loads a visual thumbnail for the
+     * given {@link Uri}. Internally calls
+     * {@link ContentProvider#openTypedAssetFile} on the remote provider, but
+     * also defensively resizes any returned content to match the requested
+     * target size.
+     *
+     * @param uri The item that should be visualized as a thumbnail.
+     * @param size The target area on the screen where this thumbnail will be
+     *            shown. This is passed to the provider as {@link #EXTRA_SIZE}
+     *            to help it avoid downloading or generating heavy resources.
+     * @param signal A signal to cancel the operation in progress.
+     * @return Valid {@link Bitmap} which is a visual thumbnail.
+     * @throws IOException If any trouble was encountered while generating or
+     *             loading the thumbnail, or if
+     *             {@link CancellationSignal#cancel()} was invoked.
+     */
+    public @NonNull Bitmap loadThumbnail(@NonNull Uri uri, @NonNull Size size,
+            @Nullable CancellationSignal signal) throws IOException {
+        return loadThumbnail(this, uri, size, signal, ImageDecoder.ALLOCATOR_SOFTWARE);
+    }
+
+    /** {@hide} */
+    public static Bitmap loadThumbnail(@NonNull ContentInterface content, @NonNull Uri uri,
+            @NonNull Size size, @Nullable CancellationSignal signal, int allocator)
+            throws IOException {
+        Objects.requireNonNull(content);
+        Objects.requireNonNull(uri);
+        Objects.requireNonNull(size);
+
+        // Convert to Point, since that's what the API is defined as
+        final Bundle opts = new Bundle();
+        opts.putParcelable(EXTRA_SIZE, Point.convert(size));
+        final Int32Ref orientation = new Int32Ref(0);
+
+        Bitmap bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(() -> {
+            final AssetFileDescriptor afd = content.openTypedAssetFile(uri, "image/*", opts,
+                    signal);
+            final Bundle extras = afd.getExtras();
+            orientation.value = (extras != null) ? extras.getInt(EXTRA_ORIENTATION, 0) : 0;
+            return afd;
+        }), (ImageDecoder decoder, ImageInfo info, Source source) -> {
+                decoder.setAllocator(allocator);
+
+                // One last-ditch check to see if we've been canceled.
+                if (signal != null) signal.throwIfCanceled();
+
+                // We requested a rough thumbnail size, but the remote size may have
+                // returned something giant, so defensively scale down as needed.
+                final int widthSample = info.getSize().getWidth() / size.getWidth();
+                final int heightSample = info.getSize().getHeight() / size.getHeight();
+                final int sample = Math.max(widthSample, heightSample);
+                if (sample > 1) {
+                    decoder.setTargetSampleSize(sample);
+                }
+        });
+
+        // Transform the bitmap if requested. We use a side-channel to
+        // communicate the orientation, since EXIF thumbnails don't contain
+        // the rotation flags of the original image.
+        if (orientation.value != 0) {
+            final int width = bitmap.getWidth();
+            final int height = bitmap.getHeight();
+
+            final Matrix m = new Matrix();
+            m.setRotate(orientation.value, width / 2, height / 2);
+            bitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, m, false);
+        }
+
+        return bitmap;
+    }
+
+    /** {@hide} */
+    public static void onDbCorruption(String tag, String message, Throwable stacktrace) {
+        try {
+            getContentService().onDbCorruption(tag, message, Log.getStackTraceString(stacktrace));
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Decode a path generated by {@link #encodeToFile(Uri)} back into
+     * the original {@link Uri}.
+     * <p>
+     * This is used to offer a way to intercept filesystem calls in
+     * {@link ContentProvider} unaware code and redirect them to a
+     * {@link ContentProvider} when they attempt to use {@code _DATA} columns
+     * that are otherwise deprecated.
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    // We can't accept an already-opened FD here, since these methods are
+    // rewriting actual filesystem paths
+    @SuppressLint("StreamFiles")
+    public static @NonNull Uri decodeFromFile(@NonNull File file) {
+        return translateDeprecatedDataPath(file.getAbsolutePath());
+    }
+
+    /**
+     * Encode a {@link Uri} into an opaque filesystem path which can then be
+     * resurrected by {@link #decodeFromFile(File)}.
+     * <p>
+     * This is used to offer a way to intercept filesystem calls in
+     * {@link ContentProvider} unaware code and redirect them to a
+     * {@link ContentProvider} when they attempt to use {@code _DATA} columns
+     * that are otherwise deprecated.
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    // We can't accept an already-opened FD here, since these methods are
+    // rewriting actual filesystem paths
+    @SuppressLint("StreamFiles")
+    public static @NonNull File encodeToFile(@NonNull Uri uri) {
+        return new File(translateDeprecatedDataPath(uri));
+    }
+
+    /** {@hide} */
+    public static @NonNull Uri translateDeprecatedDataPath(@NonNull String path) {
+        final String ssp = "//" + path.substring(DEPRECATE_DATA_PREFIX.length());
+        return Uri.parse(new Uri.Builder().scheme(SCHEME_CONTENT)
+                .encodedOpaquePart(ssp).build().toString());
+    }
+
+    /** {@hide} */
+    public static @NonNull String translateDeprecatedDataPath(@NonNull Uri uri) {
+        return DEPRECATE_DATA_PREFIX + uri.getEncodedSchemeSpecificPart().substring(2);
+    }
+}
diff --git a/android/content/ContentUris.java b/android/content/ContentUris.java
new file mode 100644
index 0000000..767d3f6
--- /dev/null
+++ b/android/content/ContentUris.java
@@ -0,0 +1,141 @@
+/*
+ * 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.content;
+
+import android.annotation.NonNull;
+import android.net.Uri;
+
+import java.util.List;
+
+/**
+* Utility methods useful for working with {@link android.net.Uri} objects
+* that use the &quot;content&quot; (content://) scheme.
+*
+*<p>
+*   Content URIs have the syntax
+*</p>
+*<p>
+*   <code>content://<em>authority</em>/<em>path</em>/<em>id</em></code>
+*</p>
+*<dl>
+*   <dt>
+*       <code>content:</code>
+*   </dt>
+*   <dd>
+*       The scheme portion of the URI. This is always set to {@link
+*       android.content.ContentResolver#SCHEME_CONTENT ContentResolver.SCHEME_CONTENT} (value
+*       <code>content://</code>).
+*   </dd>
+*   <dt>
+*       <em>authority</em>
+*   </dt>
+*   <dd>
+*       A string that identifies the entire content provider. All the content URIs for the provider
+*       start with this string. To guarantee a unique authority, providers should consider
+*       using an authority that is the same as the provider class' package identifier.
+*   </dd>
+*   <dt>
+*       <em>path</em>
+*   </dt>
+*   <dd>
+*       Zero or more segments, separated by a forward slash (<code>/</code>), that identify
+*       some subset of the provider's data. Most providers use the path part to identify
+*       individual tables. Individual segments in the path are often called
+*       &quot;directories&quot; although they do not refer to file directories. The right-most
+*       segment in a path is often called a &quot;twig&quot;
+*   </dd>
+*   <dt>
+*       <em>id</em>
+*   </dt>
+*   <dd>
+*       A unique numeric identifier for a single row in the subset of data identified by the
+*       preceding path part. Most providers recognize content URIs that contain an id part
+*       and give them special handling. A table that contains a column named <code>_ID</code>
+*       often expects the id part to be a particular value for that column.
+*   </dd>
+*</dl>
+*
+*/
+public class ContentUris {
+
+    /**
+     * Converts the last path segment to a long.
+     *
+     * <p>This supports a common convention for content URIs where an ID is
+     * stored in the last segment.
+     *
+     * @throws UnsupportedOperationException if this isn't a hierarchical URI
+     * @throws NumberFormatException if the last segment isn't a number
+     *
+     * @return the long conversion of the last segment or -1 if the path is
+     *  empty
+     */
+    public static long parseId(@NonNull Uri contentUri) {
+        String last = contentUri.getLastPathSegment();
+        return last == null ? -1 : Long.parseLong(last);
+    }
+
+    /**
+     * Appends the given ID to the end of the path.
+     *
+     * @param builder to append the ID to
+     * @param id to append
+     *
+     * @return the given builder
+     */
+    public static @NonNull Uri.Builder appendId(@NonNull Uri.Builder builder, long id) {
+        return builder.appendEncodedPath(String.valueOf(id));
+    }
+
+    /**
+     * Appends the given ID to the end of the path.
+     *
+     * @param contentUri to start with
+     * @param id to append
+     *
+     * @return a new URI with the given ID appended to the end of the path
+     */
+    public static @NonNull Uri withAppendedId(@NonNull Uri contentUri, long id) {
+        return appendId(contentUri.buildUpon(), id).build();
+    }
+
+    /**
+     * Removes any ID from the end of the path.
+     *
+     * @param contentUri that ends with an ID
+     * @return a new URI with the ID removed from the end of the path
+     * @throws IllegalArgumentException when the given URI has no ID to remove
+     *             from the end of the path
+     */
+    public static @NonNull Uri removeId(@NonNull Uri contentUri) {
+        // Verify that we have a valid ID to actually remove
+        final String last = contentUri.getLastPathSegment();
+        if (last == null) {
+            throw new IllegalArgumentException("No path segments to remove");
+        } else {
+            Long.parseLong(last);
+        }
+
+        final List<String> segments = contentUri.getPathSegments();
+        final Uri.Builder builder = contentUri.buildUpon();
+        builder.path(null);
+        for (int i = 0; i < segments.size() - 1; i++) {
+            builder.appendPath(segments.get(i));
+        }
+        return builder.build();
+    }
+}
diff --git a/android/content/ContentValues.java b/android/content/ContentValues.java
new file mode 100644
index 0000000..f9f4c5d
--- /dev/null
+++ b/android/content/ContentValues.java
@@ -0,0 +1,612 @@
+/*
+ * 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.content;
+
+import android.annotation.Nullable;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * This class is used to store a set of values that the {@link ContentResolver}
+ * can process.
+ */
+public final class ContentValues implements Parcelable {
+    public static final String TAG = "ContentValues";
+
+    /**
+     * @hide
+     * @deprecated kept around for lame people doing reflection
+     */
+    @Deprecated
+    @UnsupportedAppUsage
+    private HashMap<String, Object> mValues;
+
+    private final ArrayMap<String, Object> mMap;
+
+    /**
+     * Creates an empty set of values using the default initial size
+     */
+    public ContentValues() {
+        mMap = new ArrayMap<>();
+    }
+
+    /**
+     * Creates an empty set of values using the given initial size
+     *
+     * @param size the initial size of the set of values
+     */
+    public ContentValues(int size) {
+        Preconditions.checkArgumentNonnegative(size);
+        mMap = new ArrayMap<>(size);
+    }
+
+    /**
+     * Creates a set of values copied from the given set
+     *
+     * @param from the values to copy
+     */
+    public ContentValues(ContentValues from) {
+        Objects.requireNonNull(from);
+        mMap = new ArrayMap<>(from.mMap);
+    }
+
+    /**
+     * @hide
+     * @deprecated kept around for lame people doing reflection
+     */
+    @Deprecated
+    @UnsupportedAppUsage
+    private ContentValues(HashMap<String, Object> from) {
+        mMap = new ArrayMap<>();
+        mMap.putAll(from);
+    }
+
+    /** {@hide} */
+    private ContentValues(Parcel in) {
+        mMap = new ArrayMap<>(in.readInt());
+        in.readArrayMap(mMap, null);
+    }
+
+    @Override
+    public boolean equals(Object object) {
+        if (!(object instanceof ContentValues)) {
+            return false;
+        }
+        return mMap.equals(((ContentValues) object).mMap);
+    }
+
+    /** {@hide} */
+    public ArrayMap<String, Object> getValues() {
+        return mMap;
+    }
+
+    @Override
+    public int hashCode() {
+        return mMap.hashCode();
+    }
+
+    /**
+     * Adds a value to the set.
+     *
+     * @param key the name of the value to put
+     * @param value the data for the value to put
+     */
+    public void put(String key, String value) {
+        mMap.put(key, value);
+    }
+
+    /**
+     * Adds all values from the passed in ContentValues.
+     *
+     * @param other the ContentValues from which to copy
+     */
+    public void putAll(ContentValues other) {
+        mMap.putAll(other.mMap);
+    }
+
+    /**
+     * Adds a value to the set.
+     *
+     * @param key the name of the value to put
+     * @param value the data for the value to put
+     */
+    public void put(String key, Byte value) {
+        mMap.put(key, value);
+    }
+
+    /**
+     * Adds a value to the set.
+     *
+     * @param key the name of the value to put
+     * @param value the data for the value to put
+     */
+    public void put(String key, Short value) {
+        mMap.put(key, value);
+    }
+
+    /**
+     * Adds a value to the set.
+     *
+     * @param key the name of the value to put
+     * @param value the data for the value to put
+     */
+    public void put(String key, Integer value) {
+        mMap.put(key, value);
+    }
+
+    /**
+     * Adds a value to the set.
+     *
+     * @param key the name of the value to put
+     * @param value the data for the value to put
+     */
+    public void put(String key, Long value) {
+        mMap.put(key, value);
+    }
+
+    /**
+     * Adds a value to the set.
+     *
+     * @param key the name of the value to put
+     * @param value the data for the value to put
+     */
+    public void put(String key, Float value) {
+        mMap.put(key, value);
+    }
+
+    /**
+     * Adds a value to the set.
+     *
+     * @param key the name of the value to put
+     * @param value the data for the value to put
+     */
+    public void put(String key, Double value) {
+        mMap.put(key, value);
+    }
+
+    /**
+     * Adds a value to the set.
+     *
+     * @param key the name of the value to put
+     * @param value the data for the value to put
+     */
+    public void put(String key, Boolean value) {
+        mMap.put(key, value);
+    }
+
+    /**
+     * Adds a value to the set.
+     *
+     * @param key the name of the value to put
+     * @param value the data for the value to put
+     */
+    public void put(String key, byte[] value) {
+        mMap.put(key, value);
+    }
+
+    /**
+     * Adds a null value to the set.
+     *
+     * @param key the name of the value to make null
+     */
+    public void putNull(String key) {
+        mMap.put(key, null);
+    }
+
+    /** {@hide} */
+    public void putObject(@Nullable String key, @Nullable Object value) {
+        if (value == null) {
+            putNull(key);
+        } else if (value instanceof String) {
+            put(key, (String) value);
+        } else if (value instanceof Byte) {
+            put(key, (Byte) value);
+        } else if (value instanceof Short) {
+            put(key, (Short) value);
+        } else if (value instanceof Integer) {
+            put(key, (Integer) value);
+        } else if (value instanceof Long) {
+            put(key, (Long) value);
+        } else if (value instanceof Float) {
+            put(key, (Float) value);
+        } else if (value instanceof Double) {
+            put(key, (Double) value);
+        } else if (value instanceof Boolean) {
+            put(key, (Boolean) value);
+        } else if (value instanceof byte[]) {
+            put(key, (byte[]) value);
+        } else {
+            throw new IllegalArgumentException("Unsupported type " + value.getClass());
+        }
+    }
+
+    /**
+     * Returns the number of values.
+     *
+     * @return the number of values
+     */
+    public int size() {
+        return mMap.size();
+    }
+
+    /**
+     * Indicates whether this collection is empty.
+     *
+     * @return true iff size == 0
+     */
+    public boolean isEmpty() {
+        return mMap.isEmpty();
+    }
+
+    /**
+     * Remove a single value.
+     *
+     * @param key the name of the value to remove
+     */
+    public void remove(String key) {
+        mMap.remove(key);
+    }
+
+    /**
+     * Removes all values.
+     */
+    public void clear() {
+        mMap.clear();
+    }
+
+    /**
+     * Returns true if this object has the named value.
+     *
+     * @param key the value to check for
+     * @return {@code true} if the value is present, {@code false} otherwise
+     */
+    public boolean containsKey(String key) {
+        return mMap.containsKey(key);
+    }
+
+    /**
+     * Gets a value. Valid value types are {@link String}, {@link Boolean},
+     * {@link Number}, and {@code byte[]} implementations.
+     *
+     * @param key the value to get
+     * @return the data for the value, or {@code null} if the value is missing or if {@code null}
+     *         was previously added with the given {@code key}
+     */
+    public Object get(String key) {
+        return mMap.get(key);
+    }
+
+    /**
+     * Gets a value and converts it to a String.
+     *
+     * @param key the value to get
+     * @return the String for the value
+     */
+    public String getAsString(String key) {
+        Object value = mMap.get(key);
+        return value != null ? value.toString() : null;
+    }
+
+    /**
+     * Gets a value and converts it to a Long.
+     *
+     * @param key the value to get
+     * @return the Long value, or {@code null} if the value is missing or cannot be converted
+     */
+    public Long getAsLong(String key) {
+        Object value = mMap.get(key);
+        try {
+            return value != null ? ((Number) value).longValue() : null;
+        } catch (ClassCastException e) {
+            if (value instanceof CharSequence) {
+                try {
+                    return Long.valueOf(value.toString());
+                } catch (NumberFormatException e2) {
+                    Log.e(TAG, "Cannot parse Long value for " + value + " at key " + key);
+                    return null;
+                }
+            } else {
+                Log.e(TAG, "Cannot cast value for " + key + " to a Long: " + value, e);
+                return null;
+            }
+        }
+    }
+
+    /**
+     * Gets a value and converts it to an Integer.
+     *
+     * @param key the value to get
+     * @return the Integer value, or {@code null} if the value is missing or cannot be converted
+     */
+    public Integer getAsInteger(String key) {
+        Object value = mMap.get(key);
+        try {
+            return value != null ? ((Number) value).intValue() : null;
+        } catch (ClassCastException e) {
+            if (value instanceof CharSequence) {
+                try {
+                    return Integer.valueOf(value.toString());
+                } catch (NumberFormatException e2) {
+                    Log.e(TAG, "Cannot parse Integer value for " + value + " at key " + key);
+                    return null;
+                }
+            } else {
+                Log.e(TAG, "Cannot cast value for " + key + " to a Integer: " + value, e);
+                return null;
+            }
+        }
+    }
+
+    /**
+     * Gets a value and converts it to a Short.
+     *
+     * @param key the value to get
+     * @return the Short value, or {@code null} if the value is missing or cannot be converted
+     */
+    public Short getAsShort(String key) {
+        Object value = mMap.get(key);
+        try {
+            return value != null ? ((Number) value).shortValue() : null;
+        } catch (ClassCastException e) {
+            if (value instanceof CharSequence) {
+                try {
+                    return Short.valueOf(value.toString());
+                } catch (NumberFormatException e2) {
+                    Log.e(TAG, "Cannot parse Short value for " + value + " at key " + key);
+                    return null;
+                }
+            } else {
+                Log.e(TAG, "Cannot cast value for " + key + " to a Short: " + value, e);
+                return null;
+            }
+        }
+    }
+
+    /**
+     * Gets a value and converts it to a Byte.
+     *
+     * @param key the value to get
+     * @return the Byte value, or {@code null} if the value is missing or cannot be converted
+     */
+    public Byte getAsByte(String key) {
+        Object value = mMap.get(key);
+        try {
+            return value != null ? ((Number) value).byteValue() : null;
+        } catch (ClassCastException e) {
+            if (value instanceof CharSequence) {
+                try {
+                    return Byte.valueOf(value.toString());
+                } catch (NumberFormatException e2) {
+                    Log.e(TAG, "Cannot parse Byte value for " + value + " at key " + key);
+                    return null;
+                }
+            } else {
+                Log.e(TAG, "Cannot cast value for " + key + " to a Byte: " + value, e);
+                return null;
+            }
+        }
+    }
+
+    /**
+     * Gets a value and converts it to a Double.
+     *
+     * @param key the value to get
+     * @return the Double value, or {@code null} if the value is missing or cannot be converted
+     */
+    public Double getAsDouble(String key) {
+        Object value = mMap.get(key);
+        try {
+            return value != null ? ((Number) value).doubleValue() : null;
+        } catch (ClassCastException e) {
+            if (value instanceof CharSequence) {
+                try {
+                    return Double.valueOf(value.toString());
+                } catch (NumberFormatException e2) {
+                    Log.e(TAG, "Cannot parse Double value for " + value + " at key " + key);
+                    return null;
+                }
+            } else {
+                Log.e(TAG, "Cannot cast value for " + key + " to a Double: " + value, e);
+                return null;
+            }
+        }
+    }
+
+    /**
+     * Gets a value and converts it to a Float.
+     *
+     * @param key the value to get
+     * @return the Float value, or {@code null} if the value is missing or cannot be converted
+     */
+    public Float getAsFloat(String key) {
+        Object value = mMap.get(key);
+        try {
+            return value != null ? ((Number) value).floatValue() : null;
+        } catch (ClassCastException e) {
+            if (value instanceof CharSequence) {
+                try {
+                    return Float.valueOf(value.toString());
+                } catch (NumberFormatException e2) {
+                    Log.e(TAG, "Cannot parse Float value for " + value + " at key " + key);
+                    return null;
+                }
+            } else {
+                Log.e(TAG, "Cannot cast value for " + key + " to a Float: " + value, e);
+                return null;
+            }
+        }
+    }
+
+    /**
+     * Gets a value and converts it to a Boolean.
+     *
+     * @param key the value to get
+     * @return the Boolean value, or {@code null} if the value is missing or cannot be converted
+     */
+    public Boolean getAsBoolean(String key) {
+        Object value = mMap.get(key);
+        try {
+            return (Boolean) value;
+        } catch (ClassCastException e) {
+            if (value instanceof CharSequence) {
+                // Note that we also check against 1 here because SQLite's internal representation
+                // for booleans is an integer with a value of 0 or 1. Without this check, boolean
+                // values obtained via DatabaseUtils#cursorRowToContentValues will always return
+                // false.
+                return Boolean.valueOf(value.toString()) || "1".equals(value);
+            } else if (value instanceof Number) {
+                return ((Number) value).intValue() != 0;
+            } else {
+                Log.e(TAG, "Cannot cast value for " + key + " to a Boolean: " + value, e);
+                return null;
+            }
+        }
+    }
+
+    /**
+     * Gets a value that is a byte array. Note that this method will not convert
+     * any other types to byte arrays.
+     *
+     * @param key the value to get
+     * @return the {@code byte[]} value, or {@code null} is the value is missing or not a
+     *         {@code byte[]}
+     */
+    public byte[] getAsByteArray(String key) {
+        Object value = mMap.get(key);
+        if (value instanceof byte[]) {
+            return (byte[]) value;
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Returns a set of all of the keys and values
+     *
+     * @return a set of all of the keys and values
+     */
+    public Set<Map.Entry<String, Object>> valueSet() {
+        return mMap.entrySet();
+    }
+
+    /**
+     * Returns a set of all of the keys
+     *
+     * @return a set of all of the keys
+     */
+    public Set<String> keySet() {
+        return mMap.keySet();
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<ContentValues> CREATOR =
+            new Parcelable.Creator<ContentValues>() {
+        @Override
+        public ContentValues createFromParcel(Parcel in) {
+            return new ContentValues(in);
+        }
+
+        @Override
+        public ContentValues[] newArray(int size) {
+            return new ContentValues[size];
+        }
+    };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeInt(mMap.size());
+        parcel.writeArrayMap(mMap);
+    }
+
+    /**
+     * Unsupported, here until we get proper bulk insert APIs.
+     * {@hide}
+     */
+    @Deprecated
+    @UnsupportedAppUsage
+    public void putStringArrayList(String key, ArrayList<String> value) {
+        mMap.put(key, value);
+    }
+
+    /**
+     * Unsupported, here until we get proper bulk insert APIs.
+     * {@hide}
+     */
+    @SuppressWarnings("unchecked")
+    @Deprecated
+    @UnsupportedAppUsage
+    public ArrayList<String> getStringArrayList(String key) {
+        return (ArrayList<String>) mMap.get(key);
+    }
+
+    /**
+     * Returns a string containing a concise, human-readable description of this object.
+     * @return a printable representation of this object.
+     */
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        for (String name : mMap.keySet()) {
+            String value = getAsString(name);
+            if (sb.length() > 0) sb.append(" ");
+            sb.append(name + "=" + value);
+        }
+        return sb.toString();
+    }
+
+    /** {@hide} */
+    public static boolean isSupportedValue(Object value) {
+        if (value == null) {
+            return true;
+        } else if (value instanceof String) {
+            return true;
+        } else if (value instanceof Byte) {
+            return true;
+        } else if (value instanceof Short) {
+            return true;
+        } else if (value instanceof Integer) {
+            return true;
+        } else if (value instanceof Long) {
+            return true;
+        } else if (value instanceof Float) {
+            return true;
+        } else if (value instanceof Double) {
+            return true;
+        } else if (value instanceof Boolean) {
+            return true;
+        } else if (value instanceof byte[]) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+}
diff --git a/android/content/Context.java b/android/content/Context.java
new file mode 100644
index 0000000..8472144
--- /dev/null
+++ b/android/content/Context.java
@@ -0,0 +1,6122 @@
+/*
+ * 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.content;
+
+import android.annotation.AttrRes;
+import android.annotation.CallbackExecutor;
+import android.annotation.CheckResult;
+import android.annotation.ColorInt;
+import android.annotation.ColorRes;
+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.StringRes;
+import android.annotation.StyleRes;
+import android.annotation.StyleableRes;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.annotation.UserIdInt;
+import android.app.ActivityManager;
+import android.app.IApplicationThread;
+import android.app.IServiceConnection;
+import android.app.VrManager;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.res.AssetManager;
+import android.content.res.ColorStateList;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.database.DatabaseErrorHandler;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteDatabase.CursorFactory;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.StatFs;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.os.storage.StorageManager;
+import android.provider.MediaStore;
+import android.telephony.TelephonyRegistryManager;
+import android.util.AttributeSet;
+import android.view.Display;
+import android.view.DisplayAdjustments;
+import android.view.View;
+import android.view.ViewDebug;
+import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams.WindowType;
+import android.view.autofill.AutofillManager.AutofillClient;
+import android.view.contentcapture.ContentCaptureManager.ContentCaptureClient;
+import android.view.textclassifier.TextClassificationManager;
+
+import com.android.internal.compat.IPlatformCompat;
+import com.android.internal.compat.IPlatformCompatNative;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.Executor;
+
+/**
+ * Interface to global information about an application environment.  This is
+ * an abstract class whose implementation is provided by
+ * the Android system.  It
+ * allows access to application-specific resources and classes, as well as
+ * up-calls for application-level operations such as launching activities,
+ * broadcasting and receiving intents, etc.
+ */
+public abstract class Context {
+    /** @hide */
+    @IntDef(flag = true, prefix = { "MODE_" }, value = {
+            MODE_PRIVATE,
+            MODE_WORLD_READABLE,
+            MODE_WORLD_WRITEABLE,
+            MODE_APPEND,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FileMode {}
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "MODE_" }, value = {
+            MODE_PRIVATE,
+            MODE_WORLD_READABLE,
+            MODE_WORLD_WRITEABLE,
+            MODE_MULTI_PROCESS,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface PreferencesMode {}
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "MODE_" }, value = {
+            MODE_PRIVATE,
+            MODE_WORLD_READABLE,
+            MODE_WORLD_WRITEABLE,
+            MODE_ENABLE_WRITE_AHEAD_LOGGING,
+            MODE_NO_LOCALIZED_COLLATORS,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface DatabaseMode {}
+
+    /**
+     * File creation mode: the default mode, where the created file can only
+     * be accessed by the calling application (or all applications sharing the
+     * same user ID).
+     */
+    public static final int MODE_PRIVATE = 0x0000;
+
+    /**
+     * File creation mode: allow all other applications to have read access to
+     * the created file.
+     * <p>
+     * Starting from {@link android.os.Build.VERSION_CODES#N}, attempting to use this
+     * mode throws a {@link SecurityException}.
+     *
+     * @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.
+     * @see android.support.v4.content.FileProvider
+     * @see Intent#FLAG_GRANT_WRITE_URI_PERMISSION
+     */
+    @Deprecated
+    public static final int MODE_WORLD_READABLE = 0x0001;
+
+    /**
+     * File creation mode: allow all other applications to have write access to
+     * the created file.
+     * <p>
+     * Starting from {@link android.os.Build.VERSION_CODES#N}, attempting to use this
+     * mode will throw a {@link SecurityException}.
+     *
+     * @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.
+     * @see android.support.v4.content.FileProvider
+     * @see Intent#FLAG_GRANT_WRITE_URI_PERMISSION
+     */
+    @Deprecated
+    public static final int MODE_WORLD_WRITEABLE = 0x0002;
+
+    /**
+     * File creation mode: for use with {@link #openFileOutput}, if the file
+     * already exists then write data to the end of the existing file
+     * instead of erasing it.
+     * @see #openFileOutput
+     */
+    public static final int MODE_APPEND = 0x8000;
+
+    /**
+     * SharedPreference loading flag: when set, the file on disk will
+     * be checked for modification even if the shared preferences
+     * instance is already loaded in this process.  This behavior is
+     * sometimes desired in cases where the application has multiple
+     * processes, all writing to the same SharedPreferences file.
+     * Generally there are better forms of communication between
+     * processes, though.
+     *
+     * <p>This was the legacy (but undocumented) behavior in and
+     * before Gingerbread (Android 2.3) and this flag is implied when
+     * targeting such releases.  For applications targeting SDK
+     * versions <em>greater than</em> Android 2.3, this flag must be
+     * explicitly set if desired.
+     *
+     * @see #getSharedPreferences
+     *
+     * @deprecated MODE_MULTI_PROCESS does not work reliably in
+     * some versions of Android, and furthermore does not provide any
+     * mechanism for reconciling concurrent modifications across
+     * processes.  Applications should not attempt to use it.  Instead,
+     * they should use an explicit cross-process data management
+     * approach such as {@link android.content.ContentProvider ContentProvider}.
+     */
+    @Deprecated
+    public static final int MODE_MULTI_PROCESS = 0x0004;
+
+    /**
+     * Database open flag: when set, the database is opened with write-ahead
+     * logging enabled by default.
+     *
+     * @see #openOrCreateDatabase(String, int, CursorFactory)
+     * @see #openOrCreateDatabase(String, int, CursorFactory, DatabaseErrorHandler)
+     * @see SQLiteDatabase#enableWriteAheadLogging
+     */
+    public static final int MODE_ENABLE_WRITE_AHEAD_LOGGING = 0x0008;
+
+    /**
+     * Database open flag: when set, the database is opened without support for
+     * localized collators.
+     *
+     * @see #openOrCreateDatabase(String, int, CursorFactory)
+     * @see #openOrCreateDatabase(String, int, CursorFactory, DatabaseErrorHandler)
+     * @see SQLiteDatabase#NO_LOCALIZED_COLLATORS
+     */
+    public static final int MODE_NO_LOCALIZED_COLLATORS = 0x0010;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "BIND_" }, value = {
+            BIND_AUTO_CREATE,
+            BIND_DEBUG_UNBIND,
+            BIND_NOT_FOREGROUND,
+            BIND_ABOVE_CLIENT,
+            BIND_ALLOW_OOM_MANAGEMENT,
+            BIND_WAIVE_PRIORITY,
+            BIND_IMPORTANT,
+            BIND_ADJUST_WITH_ACTIVITY,
+            BIND_NOT_PERCEPTIBLE,
+            BIND_INCLUDE_CAPABILITIES
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface BindServiceFlags {}
+
+    /**
+     * Flag for {@link #bindService}: automatically create the service as long
+     * as the binding exists.  Note that while this will create the service,
+     * its {@link android.app.Service#onStartCommand}
+     * method will still only be called due to an
+     * explicit call to {@link #startService}.  Even without that, though,
+     * this still provides you with access to the service object while the
+     * service is created.
+     *
+     * <p>Note that prior to {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH},
+     * not supplying this flag would also impact how important the system
+     * consider's the target service's process to be.  When set, the only way
+     * for it to be raised was by binding from a service in which case it will
+     * only be important when that activity is in the foreground.  Now to
+     * achieve this behavior you must explicitly supply the new flag
+     * {@link #BIND_ADJUST_WITH_ACTIVITY}.  For compatibility, old applications
+     * that don't specify {@link #BIND_AUTO_CREATE} will automatically have
+     * the flags {@link #BIND_WAIVE_PRIORITY} and
+     * {@link #BIND_ADJUST_WITH_ACTIVITY} set for them in order to achieve
+     * the same result.
+     */
+    public static final int BIND_AUTO_CREATE = 0x0001;
+
+    /**
+     * Flag for {@link #bindService}: include debugging help for mismatched
+     * calls to unbind.  When this flag is set, the callstack of the following
+     * {@link #unbindService} call is retained, to be printed if a later
+     * incorrect unbind call is made.  Note that doing this requires retaining
+     * information about the binding that was made for the lifetime of the app,
+     * resulting in a leak -- this should only be used for debugging.
+     */
+    public static final int BIND_DEBUG_UNBIND = 0x0002;
+
+    /**
+     * Flag for {@link #bindService}: don't allow this binding to raise
+     * the target service's process to the foreground scheduling priority.
+     * It will still be raised to at least the same memory priority
+     * as the client (so that its process will not be killable in any
+     * situation where the client is not killable), but for CPU scheduling
+     * purposes it may be left in the background.  This only has an impact
+     * in the situation where the binding client is a foreground process
+     * and the target service is in a background process.
+     */
+    public static final int BIND_NOT_FOREGROUND = 0x0004;
+
+    /**
+     * Flag for {@link #bindService}: indicates that the client application
+     * binding to this service considers the service to be more important than
+     * the app itself.  When set, the platform will try to have the out of
+     * memory killer kill the app before it kills the service it is bound to, though
+     * this is not guaranteed to be the case.
+     */
+    public static final int BIND_ABOVE_CLIENT = 0x0008;
+
+    /**
+     * Flag for {@link #bindService}: allow the process hosting the bound
+     * service to go through its normal memory management.  It will be
+     * treated more like a running service, allowing the system to
+     * (temporarily) expunge the process if low on memory or for some other
+     * whim it may have, and being more aggressive about making it a candidate
+     * to be killed (and restarted) if running for a long time.
+     */
+    public static final int BIND_ALLOW_OOM_MANAGEMENT = 0x0010;
+
+    /**
+     * Flag for {@link #bindService}: don't impact the scheduling or
+     * memory management priority of the target service's hosting process.
+     * Allows the service's process to be managed on the background LRU list
+     * just like a regular application process in the background.
+     */
+    public static final int BIND_WAIVE_PRIORITY = 0x0020;
+
+    /**
+     * Flag for {@link #bindService}: this service is very important to
+     * the client, so should be brought to the foreground process level
+     * when the client is.  Normally a process can only be raised to the
+     * visibility level by a client, even if that client is in the foreground.
+     */
+    public static final int BIND_IMPORTANT = 0x0040;
+
+    /**
+     * Flag for {@link #bindService}: If binding from an activity, allow the
+     * target service's process importance to be raised based on whether the
+     * activity is visible to the user, regardless whether another flag is
+     * used to reduce the amount that the client process's overall importance
+     * is used to impact it.
+     */
+    public static final int BIND_ADJUST_WITH_ACTIVITY = 0x0080;
+
+    /**
+     * Flag for {@link #bindService}: If binding from an app that is visible or user-perceptible,
+     * lower the target service's importance to below the perceptible level. This allows
+     * the system to (temporarily) expunge the bound process from memory to make room for more
+     * important user-perceptible processes.
+     */
+    public static final int BIND_NOT_PERCEPTIBLE = 0x00000100;
+
+    /**
+     * Flag for {@link #bindService}: If binding from an app that has specific capabilities
+     * due to its foreground state such as an activity or foreground service, then this flag will
+     * allow the bound app to get the same capabilities, as long as it has the required permissions
+     * as well.
+     */
+    public static final int BIND_INCLUDE_CAPABILITIES = 0x000001000;
+
+    /***********    Public flags above this line ***********/
+    /***********    Hidden flags below this line ***********/
+
+    /**
+     * Flag for {@link #bindService}: This flag is intended to be used only by the system to adjust
+     * the scheduling policy for IMEs (and any other out-of-process user-visible components that
+     * work closely with the top app) so that UI hosted in such services can have the same
+     * scheduling policy (e.g. SCHED_FIFO when it is enabled and TOP_APP_PRIORITY_BOOST otherwise)
+     * as the actual top-app.
+     * @hide
+     */
+    public static final int BIND_SCHEDULE_LIKE_TOP_APP = 0x00080000;
+
+    /**
+     * Flag for {@link #bindService}: allow background activity starts from the bound service's
+     * process.
+     * This flag is only respected if the caller is holding
+     * {@link android.Manifest.permission#START_ACTIVITIES_FROM_BACKGROUND}.
+     * @hide
+     */
+    public static final int BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS = 0x00100000;
+
+    /**
+     * @hide Flag for {@link #bindService}: the service being bound to represents a
+     * protected system component, so must have association restrictions applied to it.
+     * That is, a system config must have one or more allow-association tags limiting
+     * which packages it can interact with.  If it does not have any such association
+     * restrictions, a default empty set will be created.
+     */
+    public static final int BIND_RESTRICT_ASSOCIATIONS = 0x00200000;
+
+    /**
+     * @hide Flag for {@link #bindService}: allows binding to a service provided
+     * by an instant app. Note that the caller may not have access to the instant
+     * app providing the service which is a violation of the instant app sandbox.
+     * This flag is intended ONLY for development/testing and should be used with
+     * great care. Only the system is allowed to use this flag.
+     */
+    public static final int BIND_ALLOW_INSTANT = 0x00400000;
+
+    /**
+     * @hide Flag for {@link #bindService}: like {@link #BIND_NOT_FOREGROUND}, but puts it
+     * up in to the important background state (instead of transient).
+     */
+    public static final int BIND_IMPORTANT_BACKGROUND = 0x00800000;
+
+    /**
+     * @hide Flag for {@link #bindService}: allows application hosting service to manage whitelists
+     * such as temporary allowing a {@code PendingIntent} to bypass Power Save mode.
+     */
+    public static final int BIND_ALLOW_WHITELIST_MANAGEMENT = 0x01000000;
+
+    /**
+     * @hide Flag for {@link #bindService}: Like {@link #BIND_FOREGROUND_SERVICE},
+     * but only applies while the device is awake.
+     */
+    public static final int BIND_FOREGROUND_SERVICE_WHILE_AWAKE = 0x02000000;
+
+    /**
+     * @hide Flag for {@link #bindService}: For only the case where the binding
+     * is coming from the system, set the process state to FOREGROUND_SERVICE
+     * instead of the normal maximum of IMPORTANT_FOREGROUND.  That is, this is
+     * saying that the process shouldn't participate in the normal power reduction
+     * modes (removing network access etc).
+     */
+    public static final int BIND_FOREGROUND_SERVICE = 0x04000000;
+
+    /**
+     * @hide Flag for {@link #bindService}: Treat the binding as hosting
+     * an activity, an unbinding as the activity going in the background.
+     * That is, when unbinding, the process when empty will go on the activity
+     * LRU list instead of the regular one, keeping it around more aggressively
+     * than it otherwise would be.  This is intended for use with IMEs to try
+     * to keep IME processes around for faster keyboard switching.
+     */
+    public static final int BIND_TREAT_LIKE_ACTIVITY = 0x08000000;
+
+    /**
+     * @hide An idea that is not yet implemented.
+     * Flag for {@link #bindService}: If binding from an activity, consider
+     * this service to be visible like the binding activity is.  That is,
+     * it will be treated as something more important to keep around than
+     * invisible background activities.  This will impact the number of
+     * recent activities the user can switch between without having them
+     * restart.  There is no guarantee this will be respected, as the system
+     * tries to balance such requests from one app vs. the importance of
+     * keeping other apps around.
+     */
+    public static final int BIND_VISIBLE = 0x10000000;
+
+    /**
+     * @hide
+     * Flag for {@link #bindService}: Consider this binding to be causing the target
+     * process to be showing UI, so it will be do a UI_HIDDEN memory trim when it goes
+     * away.
+     */
+    public static final int BIND_SHOWING_UI = 0x20000000;
+
+    /**
+     * Flag for {@link #bindService}: Don't consider the bound service to be
+     * visible, even if the caller is visible.
+     * @hide
+     */
+    public static final int BIND_NOT_VISIBLE = 0x40000000;
+
+    /**
+     * Flag for {@link #bindService}: The service being bound is an
+     * {@link android.R.attr#isolatedProcess isolated},
+     * {@link android.R.attr#externalService external} service.  This binds the service into the
+     * calling application's package, rather than the package in which the service is declared.
+     * <p>
+     * When using this flag, the code for the service being bound will execute under the calling
+     * application's package name and user ID.  Because the service must be an isolated process,
+     * it will not have direct access to the application's data, though.
+     *
+     * The purpose of this flag is to allow applications to provide services that are attributed
+     * to the app using the service, rather than the application providing the service.
+     * </p>
+     */
+    public static final int BIND_EXTERNAL_SERVICE = 0x80000000;
+
+    /**
+     * These bind flags reduce the strength of the binding such that we shouldn't
+     * consider it as pulling the process up to the level of the one that is bound to it.
+     * @hide
+     */
+    public static final int BIND_REDUCTION_FLAGS =
+            Context.BIND_ALLOW_OOM_MANAGEMENT | Context.BIND_WAIVE_PRIORITY
+                    | Context.BIND_NOT_PERCEPTIBLE | Context.BIND_NOT_VISIBLE;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "RECEIVER_VISIBLE_" }, value = {
+            RECEIVER_VISIBLE_TO_INSTANT_APPS
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface RegisterReceiverFlags {}
+
+    /**
+     * Flag for {@link #registerReceiver}: The receiver can receive broadcasts from Instant Apps.
+     */
+    public static final int RECEIVER_VISIBLE_TO_INSTANT_APPS = 0x1;
+
+    /**
+     * Returns an AssetManager instance for the application's package.
+     * <p>
+     * <strong>Note:</strong> Implementations of this method should return
+     * an AssetManager instance that is consistent with the Resources instance
+     * returned by {@link #getResources()}. For example, they should share the
+     * same {@link Configuration} object.
+     *
+     * @return an AssetManager instance for the application's package
+     * @see #getResources()
+     */
+    public abstract AssetManager getAssets();
+
+    /**
+     * Returns a Resources instance for the application's package.
+     * <p>
+     * <strong>Note:</strong> Implementations of this method should return
+     * a Resources instance that is consistent with the AssetManager instance
+     * returned by {@link #getAssets()}. For example, they should share the
+     * same {@link Configuration} object.
+     *
+     * @return a Resources instance for the application's package
+     * @see #getAssets()
+     */
+    public abstract Resources getResources();
+
+    /** Return PackageManager instance to find global package information. */
+    public abstract PackageManager getPackageManager();
+
+    /** Return a ContentResolver instance for your application's package. */
+    public abstract ContentResolver getContentResolver();
+
+    /**
+     * Return the Looper for the main thread of the current process.  This is
+     * the thread used to dispatch calls to application components (activities,
+     * services, etc).
+     * <p>
+     * By definition, this method returns the same result as would be obtained
+     * by calling {@link Looper#getMainLooper() Looper.getMainLooper()}.
+     * </p>
+     *
+     * @return The main looper.
+     */
+    public abstract Looper getMainLooper();
+
+    /**
+     * Return an {@link Executor} that will run enqueued tasks on the main
+     * thread associated with this context. This is the thread used to dispatch
+     * calls to application components (activities, services, etc).
+     */
+    public Executor getMainExecutor() {
+        // This is pretty inefficient, which is why ContextImpl overrides it
+        return new HandlerExecutor(new Handler(getMainLooper()));
+    }
+
+    /**
+     * Return the context of the single, global Application object of the
+     * current process.  This generally should only be used if you need a
+     * Context whose lifecycle is separate from the current context, that is
+     * tied to the lifetime of the process rather than the current component.
+     *
+     * <p>Consider for example how this interacts with
+     * {@link #registerReceiver(BroadcastReceiver, IntentFilter)}:
+     * <ul>
+     * <li> <p>If used from an Activity context, the receiver is being registered
+     * within that activity.  This means that you are expected to unregister
+     * before the activity is done being destroyed; in fact if you do not do
+     * so, the framework will clean up your leaked registration as it removes
+     * the activity and log an error.  Thus, if you use the Activity context
+     * to register a receiver that is static (global to the process, not
+     * associated with an Activity instance) then that registration will be
+     * removed on you at whatever point the activity you used is destroyed.
+     * <li> <p>If used from the Context returned here, the receiver is being
+     * registered with the global state associated with your application.  Thus
+     * it will never be unregistered for you.  This is necessary if the receiver
+     * is associated with static data, not a particular component.  However
+     * using the ApplicationContext elsewhere can easily lead to serious leaks
+     * if you forget to unregister, unbind, etc.
+     * </ul>
+     */
+    public abstract Context getApplicationContext();
+
+    /** Non-activity related autofill ids are unique in the app */
+    private static int sLastAutofillId = View.NO_ID;
+
+    /**
+     * Gets the next autofill ID.
+     *
+     * <p>All IDs will be smaller or the same as {@link View#LAST_APP_AUTOFILL_ID}. All IDs
+     * returned will be unique.
+     *
+     * @return A ID that is unique in the process
+     *
+     * {@hide}
+     */
+    public int getNextAutofillId() {
+        if (sLastAutofillId == View.LAST_APP_AUTOFILL_ID - 1) {
+            sLastAutofillId = View.NO_ID;
+        }
+
+        sLastAutofillId++;
+
+        return sLastAutofillId;
+    }
+
+    /**
+     * Add a new {@link ComponentCallbacks} to the base application of the
+     * Context, which will be called at the same times as the ComponentCallbacks
+     * methods of activities and other components are called.  Note that you
+     * <em>must</em> be sure to use {@link #unregisterComponentCallbacks} when
+     * appropriate in the future; this will not be removed for you.
+     *
+     * @param callback The interface to call.  This can be either a
+     * {@link ComponentCallbacks} or {@link ComponentCallbacks2} interface.
+     */
+    public void registerComponentCallbacks(ComponentCallbacks callback) {
+        getApplicationContext().registerComponentCallbacks(callback);
+    }
+
+    /**
+     * Remove a {@link ComponentCallbacks} object that was previously registered
+     * with {@link #registerComponentCallbacks(ComponentCallbacks)}.
+     */
+    public void unregisterComponentCallbacks(ComponentCallbacks callback) {
+        getApplicationContext().unregisterComponentCallbacks(callback);
+    }
+
+    /**
+     * Return a localized, styled CharSequence from the application's package's
+     * default string table.
+     *
+     * @param resId Resource id for the CharSequence text
+     */
+    @NonNull
+    public final CharSequence getText(@StringRes int resId) {
+        return getResources().getText(resId);
+    }
+
+    /**
+     * Returns a localized string from the application's package's
+     * default string table.
+     *
+     * @param resId Resource id for the string
+     * @return The string data associated with the resource, stripped of styled
+     *         text information.
+     */
+    @NonNull
+    public final String getString(@StringRes int resId) {
+        return getResources().getString(resId);
+    }
+
+    /**
+     * Returns a localized formatted string from the application's package's
+     * default string table, substituting the format arguments as defined in
+     * {@link java.util.Formatter} and {@link java.lang.String#format}.
+     *
+     * @param resId Resource id for the format string
+     * @param formatArgs The format arguments that will be used for
+     *                   substitution.
+     * @return The string data associated with the resource, formatted and
+     *         stripped of styled text information.
+     */
+    @NonNull
+    public final String getString(@StringRes int resId, Object... formatArgs) {
+        return getResources().getString(resId, formatArgs);
+    }
+
+    /**
+     * Returns a color associated with a particular resource ID and styled for
+     * the current theme.
+     *
+     * @param id The desired resource identifier, as generated by the aapt
+     *           tool. This integer encodes the package, type, and resource
+     *           entry. The value 0 is an invalid identifier.
+     * @return A single color value in the form 0xAARRGGBB.
+     * @throws android.content.res.Resources.NotFoundException if the given ID
+     *         does not exist.
+     */
+    @ColorInt
+    public final int getColor(@ColorRes int id) {
+        return getResources().getColor(id, getTheme());
+    }
+
+    /**
+     * Returns a drawable object associated with a particular resource ID and
+     * styled for the current theme.
+     *
+     * @param id The desired resource identifier, as generated by the aapt
+     *           tool. This integer encodes the package, type, and resource
+     *           entry. The value 0 is an invalid identifier.
+     * @return An object that can be used to draw this resource.
+     * @throws android.content.res.Resources.NotFoundException if the given ID
+     *         does not exist.
+     */
+    @Nullable
+    public final Drawable getDrawable(@DrawableRes int id) {
+        return getResources().getDrawable(id, getTheme());
+    }
+
+    /**
+     * Returns a color state list associated with a particular resource ID and
+     * styled for the current theme.
+     *
+     * @param id The desired resource identifier, as generated by the aapt
+     *           tool. This integer encodes the package, type, and resource
+     *           entry. The value 0 is an invalid identifier.
+     * @return A color state list.
+     * @throws android.content.res.Resources.NotFoundException if the given ID
+     *         does not exist.
+     */
+    @NonNull
+    public final ColorStateList getColorStateList(@ColorRes int id) {
+        return getResources().getColorStateList(id, getTheme());
+    }
+
+     /**
+     * Set the base theme for this context.  Note that this should be called
+     * before any views are instantiated in the Context (for example before
+     * calling {@link android.app.Activity#setContentView} or
+     * {@link android.view.LayoutInflater#inflate}).
+     *
+     * @param resid The style resource describing the theme.
+     */
+    public abstract void setTheme(@StyleRes int resid);
+
+    /** @hide Needed for some internal implementation...  not public because
+     * you can't assume this actually means anything. */
+    @UnsupportedAppUsage
+    public int getThemeResId() {
+        return 0;
+    }
+
+    /**
+     * Return the Theme object associated with this Context.
+     */
+    @ViewDebug.ExportedProperty(deepExport = true)
+    public abstract Resources.Theme getTheme();
+
+    /**
+     * Retrieve styled attribute information in this Context's theme.  See
+     * {@link android.content.res.Resources.Theme#obtainStyledAttributes(int[])}
+     * for more information.
+     *
+     * @see android.content.res.Resources.Theme#obtainStyledAttributes(int[])
+     */
+    @NonNull
+    public final TypedArray obtainStyledAttributes(@NonNull @StyleableRes int[] attrs) {
+        return getTheme().obtainStyledAttributes(attrs);
+    }
+
+    /**
+     * Retrieve styled attribute information in this Context's theme.  See
+     * {@link android.content.res.Resources.Theme#obtainStyledAttributes(int, int[])}
+     * for more information.
+     *
+     * @see android.content.res.Resources.Theme#obtainStyledAttributes(int, int[])
+     */
+    @NonNull
+    public final TypedArray obtainStyledAttributes(@StyleRes int resid,
+            @NonNull @StyleableRes int[] attrs) throws Resources.NotFoundException {
+        return getTheme().obtainStyledAttributes(resid, attrs);
+    }
+
+    /**
+     * Retrieve styled attribute information in this Context's theme.  See
+     * {@link android.content.res.Resources.Theme#obtainStyledAttributes(AttributeSet, int[], int, int)}
+     * for more information.
+     *
+     * @see android.content.res.Resources.Theme#obtainStyledAttributes(AttributeSet, int[], int, int)
+     */
+    @NonNull
+    public final TypedArray obtainStyledAttributes(
+            @Nullable AttributeSet set, @NonNull @StyleableRes int[] attrs) {
+        return getTheme().obtainStyledAttributes(set, attrs, 0, 0);
+    }
+
+    /**
+     * Retrieve styled attribute information in this Context's theme.  See
+     * {@link android.content.res.Resources.Theme#obtainStyledAttributes(AttributeSet, int[], int, int)}
+     * for more information.
+     *
+     * @see android.content.res.Resources.Theme#obtainStyledAttributes(AttributeSet, int[], int, int)
+     */
+    @NonNull
+    public final TypedArray obtainStyledAttributes(@Nullable AttributeSet set,
+            @NonNull @StyleableRes int[] attrs, @AttrRes int defStyleAttr,
+            @StyleRes int defStyleRes) {
+        return getTheme().obtainStyledAttributes(
+            set, attrs, defStyleAttr, defStyleRes);
+    }
+
+    /**
+     * Return a class loader you can use to retrieve classes in this package.
+     */
+    public abstract ClassLoader getClassLoader();
+
+    /** Return the name of this application's package. */
+    public abstract String getPackageName();
+
+    /**
+     * @hide Return the name of the base context this context is derived from.
+     * This is the same as {@link #getOpPackageName()} except in
+     * cases where system components are loaded into other app processes, in which
+     * case {@link #getOpPackageName()} will be the name of the primary package in
+     * that process (so that app ops uid verification will work with the name).
+     */
+    @UnsupportedAppUsage
+    public abstract String getBasePackageName();
+
+    /**
+     * Return the package name that should be used for {@link android.app.AppOpsManager} calls from
+     * this context, so that app ops manager's uid verification will work with the name.
+     * <p>
+     * This is not generally intended for third party application developers.
+     */
+    @NonNull
+    public String getOpPackageName() {
+        throw new RuntimeException("Not implemented. Must override in a subclass.");
+    }
+
+    /**
+     * <p>Attribution can be used in complex apps to logically separate parts of the app. E.g. a
+     * blogging app might also have a instant messaging app built in. In this case two separate tags
+     * can for used each sub-feature.
+     *
+     * @return the attribution tag this context is for or {@code null} if this is the default.
+     */
+    public @Nullable String getAttributionTag() {
+        return null;
+    }
+
+    // TODO moltmann: Remove
+    /**
+     * @removed
+     */
+    @Deprecated
+    public @Nullable String getFeatureId() {
+        return getAttributionTag();
+    }
+
+    /** Return the full application info for this context's package. */
+    public abstract ApplicationInfo getApplicationInfo();
+
+    /**
+     * Return the full path to this context's primary Android package.
+     * The Android package is a ZIP file which contains the application's
+     * primary resources.
+     *
+     * <p>Note: this is not generally useful for applications, since they should
+     * not be directly accessing the file system.
+     *
+     * @return String Path to the resources.
+     */
+    public abstract String getPackageResourcePath();
+
+    /**
+     * Return the full path to this context's primary Android package.
+     * The Android package is a ZIP file which contains application's
+     * primary code and assets.
+     *
+     * <p>Note: this is not generally useful for applications, since they should
+     * not be directly accessing the file system.
+     *
+     * @return String Path to the code and assets.
+     */
+    public abstract String getPackageCodePath();
+
+    /**
+     * @hide
+     * @deprecated use {@link #getSharedPreferencesPath(String)}
+     */
+    @Deprecated
+    @UnsupportedAppUsage
+    public File getSharedPrefsFile(String name) {
+        return getSharedPreferencesPath(name);
+    }
+
+    /**
+     * Retrieve and hold the contents of the preferences file 'name', returning
+     * a SharedPreferences through which you can retrieve and modify its
+     * values.  Only one instance of the SharedPreferences object is returned
+     * to any callers for the same name, meaning they will see each other's
+     * edits as soon as they are made.
+     *
+     * <p>This method is thread-safe.
+     *
+     * <p>If the preferences directory does not already exist, it will be created when this method
+     * is called.
+     *
+     * <p>If a preferences file by this name does not exist, it will be created when you retrieve an
+     * editor ({@link SharedPreferences#edit()}) and then commit changes ({@link
+     * SharedPreferences.Editor#commit()} or {@link SharedPreferences.Editor#apply()}).
+     *
+     * @param name Desired preferences file.
+     * @param mode Operating mode.
+     *
+     * @return The single {@link SharedPreferences} instance that can be used
+     *         to retrieve and modify the preference values.
+     *
+     * @see #MODE_PRIVATE
+     */
+    public abstract SharedPreferences getSharedPreferences(String name, @PreferencesMode int mode);
+
+    /**
+     * Retrieve and hold the contents of the preferences file, returning
+     * a SharedPreferences through which you can retrieve and modify its
+     * values.  Only one instance of the SharedPreferences object is returned
+     * to any callers for the same name, meaning they will see each other's
+     * edits as soon as they are made.
+     *
+     * @param file Desired preferences file. If a preferences file by this name
+     * does not exist, it will be created when you retrieve an
+     * editor (SharedPreferences.edit()) and then commit changes (Editor.commit()).
+     * @param mode Operating mode.
+     *
+     * @return The single {@link SharedPreferences} instance that can be used
+     *         to retrieve and modify the preference values.
+     *
+     * @see #getSharedPreferencesPath(String)
+     * @see #MODE_PRIVATE
+     * @removed
+     */
+    public abstract SharedPreferences getSharedPreferences(File file, @PreferencesMode int mode);
+
+    /**
+     * Move an existing shared preferences file from the given source storage
+     * context to this context. This is typically used to migrate data between
+     * storage locations after an upgrade, such as moving to device protected
+     * storage.
+     *
+     * @param sourceContext The source context which contains the existing
+     *            shared preferences to move.
+     * @param name The name of the shared preferences file.
+     * @return {@code true} if the move was successful or if the shared
+     *         preferences didn't exist in the source context, otherwise
+     *         {@code false}.
+     * @see #createDeviceProtectedStorageContext()
+     */
+    public abstract boolean moveSharedPreferencesFrom(Context sourceContext, String name);
+
+    /**
+     * Delete an existing shared preferences file.
+     *
+     * @param name The name (unique in the application package) of the shared
+     *            preferences file.
+     * @return {@code true} if the shared preferences file was successfully
+     *         deleted; else {@code false}.
+     * @see #getSharedPreferences(String, int)
+     */
+    public abstract boolean deleteSharedPreferences(String name);
+
+    /** @hide */
+    public abstract void reloadSharedPreferences();
+
+    /**
+     * Open a private file associated with this Context's application package
+     * for reading.
+     *
+     * @param name The name of the file to open; can not contain path
+     *             separators.
+     *
+     * @return The resulting {@link FileInputStream}.
+     *
+     * @see #openFileOutput
+     * @see #fileList
+     * @see #deleteFile
+     * @see java.io.FileInputStream#FileInputStream(String)
+     */
+    public abstract FileInputStream openFileInput(String name)
+        throws FileNotFoundException;
+
+    /**
+     * Open a private file associated with this Context's application package
+     * for writing. Creates the file if it doesn't already exist.
+     * <p>
+     * No additional permissions are required for the calling app to read or
+     * write the returned file.
+     *
+     * @param name The name of the file to open; can not contain path
+     *            separators.
+     * @param mode Operating mode.
+     * @return The resulting {@link FileOutputStream}.
+     * @see #MODE_APPEND
+     * @see #MODE_PRIVATE
+     * @see #openFileInput
+     * @see #fileList
+     * @see #deleteFile
+     * @see java.io.FileOutputStream#FileOutputStream(String)
+     */
+    public abstract FileOutputStream openFileOutput(String name, @FileMode int mode)
+        throws FileNotFoundException;
+
+    /**
+     * Delete the given private file associated with this Context's
+     * application package.
+     *
+     * @param name The name of the file to delete; can not contain path
+     *             separators.
+     *
+     * @return {@code true} if the file was successfully deleted; else
+     *         {@code false}.
+     *
+     * @see #openFileInput
+     * @see #openFileOutput
+     * @see #fileList
+     * @see java.io.File#delete()
+     */
+    public abstract boolean deleteFile(String name);
+
+    /**
+     * Returns the absolute path on the filesystem where a file created with
+     * {@link #openFileOutput} is stored.
+     * <p>
+     * The returned path may change over time if the calling app is moved to an
+     * adopted storage device, so only relative paths should be persisted.
+     *
+     * @param name The name of the file for which you would like to get
+     *          its path.
+     *
+     * @return An absolute path to the given file.
+     *
+     * @see #openFileOutput
+     * @see #getFilesDir
+     * @see #getDir
+     */
+    public abstract File getFileStreamPath(String name);
+
+    /**
+     * Returns the absolute path on the filesystem where a file created with
+     * {@link #getSharedPreferences(String, int)} is stored.
+     * <p>
+     * The returned path may change over time if the calling app is moved to an
+     * adopted storage device, so only relative paths should be persisted.
+     *
+     * @param name The name of the shared preferences for which you would like
+     *            to get a path.
+     * @return An absolute path to the given file.
+     * @see #getSharedPreferences(String, int)
+     * @removed
+     */
+    public abstract File getSharedPreferencesPath(String name);
+
+    /**
+     * Returns the absolute path to the directory on the filesystem where all
+     * private files belonging to this app are stored. Apps should not use this
+     * path directly; they should instead use {@link #getFilesDir()},
+     * {@link #getCacheDir()}, {@link #getDir(String, int)}, or other storage
+     * APIs on this class.
+     * <p>
+     * The returned path may change over time if the calling app is moved to an
+     * adopted storage device, so only relative paths should be persisted.
+     * <p>
+     * No additional permissions are required for the calling app to read or
+     * write files under the returned path.
+     *
+     * @see ApplicationInfo#dataDir
+     */
+    public abstract File getDataDir();
+
+    /**
+     * Returns the absolute path to the directory on the filesystem where files
+     * created with {@link #openFileOutput} are stored.
+     * <p>
+     * The returned path may change over time if the calling app is moved to an
+     * adopted storage device, so only relative paths should be persisted.
+     * <p>
+     * No additional permissions are required for the calling app to read or
+     * write files under the returned path.
+     *
+     * @return The path of the directory holding application files.
+     * @see #openFileOutput
+     * @see #getFileStreamPath
+     * @see #getDir
+     */
+    public abstract File getFilesDir();
+
+    /**
+     * Returns the absolute path to the directory that is related to the crate on the filesystem.
+     * <p>
+     *     The crateId require a validated file name. It can't contain any "..", ".",
+     *     {@link File#separatorChar} etc..
+     * </p>
+     * <p>
+     * The returned path may change over time if the calling app is moved to an
+     * adopted storage device, so only relative paths should be persisted.
+     * </p>
+     * <p>
+     * No additional permissions are required for the calling app to read or
+     * write files under the returned path.
+     *</p>
+     *
+     * @param crateId the relative validated file name under {@link Context#getDataDir()}/crates
+     * @return the crate directory file.
+     * @hide
+     */
+    @NonNull
+    @TestApi
+    public File getCrateDir(@NonNull String crateId) {
+        throw new RuntimeException("Not implemented. Must override in a subclass.");
+    }
+
+    /**
+     * Returns the absolute path to the directory on the filesystem similar to
+     * {@link #getFilesDir()}. The difference is that files placed under this
+     * directory will be excluded from automatic backup to remote storage. See
+     * {@link android.app.backup.BackupAgent BackupAgent} for a full discussion
+     * of the automatic backup mechanism in Android.
+     * <p>
+     * The returned path may change over time if the calling app is moved to an
+     * adopted storage device, so only relative paths should be persisted.
+     * <p>
+     * No additional permissions are required for the calling app to read or
+     * write files under the returned path.
+     *
+     * @return The path of the directory holding application files that will not
+     *         be automatically backed up to remote storage.
+     * @see #openFileOutput
+     * @see #getFileStreamPath
+     * @see #getDir
+     * @see android.app.backup.BackupAgent
+     */
+    public abstract File getNoBackupFilesDir();
+
+    /**
+     * Returns the absolute path to the directory on the primary shared/external
+     * storage device where the application can place persistent files it owns.
+     * These files are internal to the applications, and not typically visible
+     * to the user as media.
+     * <p>
+     * This is like {@link #getFilesDir()} in that these files will be deleted
+     * when the application is uninstalled, however there are some important
+     * differences:
+     * <ul>
+     * <li>Shared storage may not always be available, since removable media can
+     * be ejected by the user. Media state can be checked using
+     * {@link Environment#getExternalStorageState(File)}.
+     * <li>There is no security enforced with these files. For example, any
+     * application holding
+     * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to
+     * these files.
+     * </ul>
+     * <p>
+     * If a shared storage device is emulated (as determined by
+     * {@link Environment#isExternalStorageEmulated(File)}), it's contents are
+     * backed by a private user data partition, which means there is little
+     * benefit to storing data here instead of the private directories returned
+     * by {@link #getFilesDir()}, etc.
+     * <p>
+     * Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, no permissions
+     * are required to read or write to the returned path; it's always
+     * accessible to the calling app. This only applies to paths generated for
+     * package name of the calling application. To access paths belonging to
+     * other packages,
+     * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} and/or
+     * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} are required.
+     * <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>
+     * The returned path may change over time if different shared storage media
+     * is inserted, so only relative paths should be persisted.
+     * <p>
+     * Here is an example of typical code to manipulate a file in an
+     * application's shared storage:
+     * </p>
+     * {@sample development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java
+     * private_file}
+     * <p>
+     * If you supply a non-null <var>type</var> to this function, the returned
+     * file will be a path to a sub-directory of the given type. Though these
+     * files are not automatically scanned by the media scanner, you can
+     * explicitly add them to the media database with
+     * {@link android.media.MediaScannerConnection#scanFile(Context, String[], String[], android.media.MediaScannerConnection.OnScanCompletedListener)
+     * MediaScannerConnection.scanFile}. Note that this is not the same as
+     * {@link android.os.Environment#getExternalStoragePublicDirectory
+     * Environment.getExternalStoragePublicDirectory()}, which provides
+     * directories of media shared by all applications. The directories returned
+     * here are owned by the application, and their contents will be removed
+     * when the application is uninstalled. Unlike
+     * {@link android.os.Environment#getExternalStoragePublicDirectory
+     * Environment.getExternalStoragePublicDirectory()}, the directory returned
+     * here will be automatically created for you.
+     * <p>
+     * Here is an example of typical code to manipulate a picture in an
+     * application's shared storage and add it to the media database:
+     * </p>
+     * {@sample development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java
+     * private_picture}
+     *
+     * @param type The type of files directory to return. May be {@code null}
+     *            for the root of the files directory or one of the following
+     *            constants for a subdirectory:
+     *            {@link android.os.Environment#DIRECTORY_MUSIC},
+     *            {@link android.os.Environment#DIRECTORY_PODCASTS},
+     *            {@link android.os.Environment#DIRECTORY_RINGTONES},
+     *            {@link android.os.Environment#DIRECTORY_ALARMS},
+     *            {@link android.os.Environment#DIRECTORY_NOTIFICATIONS},
+     *            {@link android.os.Environment#DIRECTORY_PICTURES}, or
+     *            {@link android.os.Environment#DIRECTORY_MOVIES}.
+     * @return the absolute path to application-specific directory. May return
+     *         {@code null} if shared storage is not currently available.
+     * @see #getFilesDir
+     * @see #getExternalFilesDirs(String)
+     * @see Environment#getExternalStorageState(File)
+     * @see Environment#isExternalStorageEmulated(File)
+     * @see Environment#isExternalStorageRemovable(File)
+     */
+    @Nullable
+    public abstract File getExternalFilesDir(@Nullable String type);
+
+    /**
+     * Returns absolute paths to application-specific directories on all
+     * shared/external storage devices where the application can place
+     * persistent files it owns. These files are internal to the application,
+     * and not typically visible to the user as media.
+     * <p>
+     * This is like {@link #getFilesDir()} in that these files will be deleted
+     * when the application is uninstalled, however there are some important
+     * differences:
+     * <ul>
+     * <li>Shared storage may not always be available, since removable media can
+     * be ejected by the user. Media state can be checked using
+     * {@link Environment#getExternalStorageState(File)}.
+     * <li>There is no security enforced with these files. For example, any
+     * application holding
+     * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to
+     * these files.
+     * </ul>
+     * <p>
+     * If a shared storage device is emulated (as determined by
+     * {@link Environment#isExternalStorageEmulated(File)}), it's contents are
+     * backed by a private user data partition, which means there is little
+     * benefit to storing data here instead of the private directories returned
+     * by {@link #getFilesDir()}, etc.
+     * <p>
+     * Shared storage devices returned here are considered a stable part of the
+     * device, including physical media slots under a protective cover. The
+     * returned paths do not include transient devices, such as USB flash drives
+     * connected to handheld devices.
+     * <p>
+     * An application may store data on any or all of the returned devices. For
+     * example, an app may choose to store large files on the device with the
+     * most available space, as measured by {@link StatFs}.
+     * <p>
+     * No additional permissions are required for the calling app to read or
+     * write files under the returned path. Write access outside of these paths
+     * on secondary external storage devices is not available.
+     * <p>
+     * The returned path may change over time if different shared storage media
+     * is inserted, so only relative paths should be persisted.
+     *
+     * @param type The type of files directory to return. May be {@code null}
+     *            for the root of the files directory or one of the following
+     *            constants for a subdirectory:
+     *            {@link android.os.Environment#DIRECTORY_MUSIC},
+     *            {@link android.os.Environment#DIRECTORY_PODCASTS},
+     *            {@link android.os.Environment#DIRECTORY_RINGTONES},
+     *            {@link android.os.Environment#DIRECTORY_ALARMS},
+     *            {@link android.os.Environment#DIRECTORY_NOTIFICATIONS},
+     *            {@link android.os.Environment#DIRECTORY_PICTURES}, or
+     *            {@link android.os.Environment#DIRECTORY_MOVIES}.
+     * @return the absolute paths to application-specific directories. Some
+     *         individual paths may be {@code null} if that shared storage is
+     *         not currently available. The first path returned is the same as
+     *         {@link #getExternalFilesDir(String)}.
+     * @see #getExternalFilesDir(String)
+     * @see Environment#getExternalStorageState(File)
+     * @see Environment#isExternalStorageEmulated(File)
+     * @see Environment#isExternalStorageRemovable(File)
+     */
+    public abstract File[] getExternalFilesDirs(String type);
+
+    /**
+     * Return the primary shared/external storage directory where this
+     * application's OBB files (if there are any) can be found. Note if the
+     * application does not have any OBB files, this directory may not exist.
+     * <p>
+     * This is like {@link #getFilesDir()} in that these files will be deleted
+     * when the application is uninstalled, however there are some important
+     * differences:
+     * <ul>
+     * <li>Shared storage may not always be available, since removable media can
+     * be ejected by the user. Media state can be checked using
+     * {@link Environment#getExternalStorageState(File)}.
+     * <li>There is no security enforced with these files. For example, any
+     * application holding
+     * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to
+     * these files.
+     * </ul>
+     * <p>
+     * Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, no permissions
+     * are required to read or write to the path that this method returns.
+     * However, starting from {@link android.os.Build.VERSION_CODES#M},
+     * to read the OBB expansion files, you must declare the
+     * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} permission in the app manifest and ask for
+     * permission at runtime as follows:
+     * </p>
+     * <p>
+     * {@code <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
+     * android:maxSdkVersion="23" />}
+     * </p>
+     * <p>
+     * Starting from {@link android.os.Build.VERSION_CODES#N},
+     * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE}
+     * permission is not required, so don’t ask for this
+     * permission at runtime. To handle both cases, your app must first try to read the OBB file,
+     * and if it fails, you must request
+     * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} permission at runtime.
+     * </p>
+     *
+     * <p>
+     * The following code snippet shows how to do this:
+     * </p>
+     *
+     * <pre>
+     * File obb = new File(obb_filename);
+     * boolean open_failed = false;
+     *
+     * try {
+     *     BufferedReader br = new BufferedReader(new FileReader(obb));
+     *     open_failed = false;
+     *     ReadObbFile(br);
+     * } catch (IOException e) {
+     *     open_failed = true;
+     * }
+     *
+     * if (open_failed) {
+     *     // request READ_EXTERNAL_STORAGE permission before reading OBB file
+     *     ReadObbFileWithPermission();
+     * }
+     * </pre>
+     *
+     * On devices with multiple users (as described by {@link UserManager}),
+     * multiple users may share the same OBB storage location. Applications
+     * should ensure that multiple instances running under different users don't
+     * interfere with each other.
+     *
+     * @return the absolute path to application-specific directory. May return
+     *         {@code null} if shared storage is not currently available.
+     * @see #getObbDirs()
+     * @see Environment#getExternalStorageState(File)
+     * @see Environment#isExternalStorageEmulated(File)
+     * @see Environment#isExternalStorageRemovable(File)
+     */
+    public abstract File getObbDir();
+
+    /**
+     * Returns absolute paths to application-specific directories on all
+     * shared/external storage devices where the application's OBB files (if
+     * there are any) can be found. Note if the application does not have any
+     * OBB files, these directories may not exist.
+     * <p>
+     * This is like {@link #getFilesDir()} in that these files will be deleted
+     * when the application is uninstalled, however there are some important
+     * differences:
+     * <ul>
+     * <li>Shared storage may not always be available, since removable media can
+     * be ejected by the user. Media state can be checked using
+     * {@link Environment#getExternalStorageState(File)}.
+     * <li>There is no security enforced with these files. For example, any
+     * application holding
+     * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to
+     * these files.
+     * </ul>
+     * <p>
+     * Shared storage devices returned here are considered a stable part of the
+     * device, including physical media slots under a protective cover. The
+     * returned paths do not include transient devices, such as USB flash drives
+     * connected to handheld devices.
+     * <p>
+     * An application may store data on any or all of the returned devices. For
+     * example, an app may choose to store large files on the device with the
+     * most available space, as measured by {@link StatFs}.
+     * <p>
+     * No additional permissions are required for the calling app to read or
+     * write files under the returned path. Write access outside of these paths
+     * on secondary external storage devices is not available.
+     *
+     * @return the absolute paths to application-specific directories. Some
+     *         individual paths may be {@code null} if that shared storage is
+     *         not currently available. The first path returned is the same as
+     *         {@link #getObbDir()}
+     * @see #getObbDir()
+     * @see Environment#getExternalStorageState(File)
+     * @see Environment#isExternalStorageEmulated(File)
+     * @see Environment#isExternalStorageRemovable(File)
+     */
+    public abstract File[] getObbDirs();
+
+    /**
+     * Returns the absolute path to the application specific cache directory on
+     * the filesystem.
+     * <p>
+     * The system will automatically delete files in this directory as disk
+     * space is needed elsewhere on the device. The system will always delete
+     * older files first, as reported by {@link File#lastModified()}. If
+     * desired, you can exert more control over how files are deleted using
+     * {@link StorageManager#setCacheBehaviorGroup(File, boolean)} and
+     * {@link StorageManager#setCacheBehaviorTombstone(File, boolean)}.
+     * <p>
+     * Apps are strongly encouraged to keep their usage of cache space below the
+     * quota returned by
+     * {@link StorageManager#getCacheQuotaBytes(java.util.UUID)}. 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>
+     * Note that your cache 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>
+     * The returned path may change over time if the calling app is moved to an
+     * adopted storage device, so only relative paths should be persisted.
+     * <p>
+     * Apps require no extra permissions to read or write to the returned path,
+     * since this path lives in their private storage.
+     *
+     * @return The path of the directory holding application cache files.
+     * @see #openFileOutput
+     * @see #getFileStreamPath
+     * @see #getDir
+     * @see #getExternalCacheDir
+     */
+    public abstract File getCacheDir();
+
+    /**
+     * Returns the absolute path to the application specific cache directory on
+     * the filesystem designed for storing cached code.
+     * <p>
+     * The system will delete any files stored in this location both when your
+     * specific application is upgraded, and when the entire platform is
+     * upgraded.
+     * <p>
+     * This location is optimal for storing compiled or optimized code generated
+     * by your application at runtime.
+     * <p>
+     * The returned path may change over time if the calling app is moved to an
+     * adopted storage device, so only relative paths should be persisted.
+     * <p>
+     * Apps require no extra permissions to read or write to the returned path,
+     * since this path lives in their private storage.
+     *
+     * @return The path of the directory holding application code cache files.
+     */
+    public abstract File getCodeCacheDir();
+
+    /**
+     * Returns absolute path to application-specific directory on the primary
+     * shared/external storage device where the application can place cache
+     * files it owns. These files are internal to the application, and not
+     * typically visible to the user as media.
+     * <p>
+     * This is like {@link #getCacheDir()} in that these files will be deleted
+     * when the application is uninstalled, however there are some important
+     * differences:
+     * <ul>
+     * <li>The platform does not always monitor the space available in shared
+     * storage, and thus may not automatically delete these files. Apps should
+     * always manage the maximum space used in this location. Currently the only
+     * time files here will be deleted by the platform is when running on
+     * {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} or later and
+     * {@link Environment#isExternalStorageEmulated(File)} returns true.
+     * <li>Shared storage may not always be available, since removable media can
+     * be ejected by the user. Media state can be checked using
+     * {@link Environment#getExternalStorageState(File)}.
+     * <li>There is no security enforced with these files. For example, any
+     * application holding
+     * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to
+     * these files.
+     * </ul>
+     * <p>
+     * If a shared storage device is emulated (as determined by
+     * {@link Environment#isExternalStorageEmulated(File)}), its contents are
+     * backed by a private user data partition, which means there is little
+     * benefit to storing data here instead of the private directory returned by
+     * {@link #getCacheDir()}.
+     * <p>
+     * Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, no permissions
+     * are required to read or write to the returned path; it's always
+     * accessible to the calling app. This only applies to paths generated for
+     * package name of the calling application. To access paths belonging to
+     * other packages,
+     * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} and/or
+     * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} are required.
+     * <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>
+     * The returned path may change over time if different shared storage media
+     * is inserted, so only relative paths should be persisted.
+     *
+     * @return the absolute path to application-specific directory. May return
+     *         {@code null} if shared storage is not currently available.
+     * @see #getCacheDir
+     * @see #getExternalCacheDirs()
+     * @see Environment#getExternalStorageState(File)
+     * @see Environment#isExternalStorageEmulated(File)
+     * @see Environment#isExternalStorageRemovable(File)
+     */
+    @Nullable
+    public abstract File getExternalCacheDir();
+
+    /**
+     * Returns absolute path to application-specific directory in the preloaded cache.
+     * <p>Files stored in the cache directory can be deleted when the device runs low on storage.
+     * There is no guarantee when these files will be deleted.
+     * @hide
+     */
+    @Nullable
+    @SystemApi
+    public abstract File getPreloadsFileCache();
+
+    /**
+     * Returns absolute paths to application-specific directories on all
+     * shared/external storage devices where the application can place cache
+     * files it owns. These files are internal to the application, and not
+     * typically visible to the user as media.
+     * <p>
+     * This is like {@link #getCacheDir()} in that these files will be deleted
+     * when the application is uninstalled, however there are some important
+     * differences:
+     * <ul>
+     * <li>The platform does not always monitor the space available in shared
+     * storage, and thus may not automatically delete these files. Apps should
+     * always manage the maximum space used in this location. Currently the only
+     * time files here will be deleted by the platform is when running on
+     * {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} or later and
+     * {@link Environment#isExternalStorageEmulated(File)} returns true.
+     * <li>Shared storage may not always be available, since removable media can
+     * be ejected by the user. Media state can be checked using
+     * {@link Environment#getExternalStorageState(File)}.
+     * <li>There is no security enforced with these files. For example, any
+     * application holding
+     * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to
+     * these files.
+     * </ul>
+     * <p>
+     * If a shared storage device is emulated (as determined by
+     * {@link Environment#isExternalStorageEmulated(File)}), it's contents are
+     * backed by a private user data partition, which means there is little
+     * benefit to storing data here instead of the private directory returned by
+     * {@link #getCacheDir()}.
+     * <p>
+     * Shared storage devices returned here are considered a stable part of the
+     * device, including physical media slots under a protective cover. The
+     * returned paths do not include transient devices, such as USB flash drives
+     * connected to handheld devices.
+     * <p>
+     * An application may store data on any or all of the returned devices. For
+     * example, an app may choose to store large files on the device with the
+     * most available space, as measured by {@link StatFs}.
+     * <p>
+     * No additional permissions are required for the calling app to read or
+     * write files under the returned path. Write access outside of these paths
+     * on secondary external storage devices is not available.
+     * <p>
+     * The returned paths may change over time if different shared storage media
+     * is inserted, so only relative paths should be persisted.
+     *
+     * @return the absolute paths to application-specific directories. Some
+     *         individual paths may be {@code null} if that shared storage is
+     *         not currently available. The first path returned is the same as
+     *         {@link #getExternalCacheDir()}.
+     * @see #getExternalCacheDir()
+     * @see Environment#getExternalStorageState(File)
+     * @see Environment#isExternalStorageEmulated(File)
+     * @see Environment#isExternalStorageRemovable(File)
+     */
+    public abstract File[] getExternalCacheDirs();
+
+    /**
+     * Returns absolute paths to application-specific directories on all
+     * shared/external storage devices where the application can place media
+     * files. These files are scanned and made available to other apps through
+     * {@link MediaStore}.
+     * <p>
+     * This is like {@link #getExternalFilesDirs} in that these files will be
+     * deleted when the application is uninstalled, however there are some
+     * important differences:
+     * <ul>
+     * <li>Shared storage may not always be available, since removable media can
+     * be ejected by the user. Media state can be checked using
+     * {@link Environment#getExternalStorageState(File)}.
+     * <li>There is no security enforced with these files. For example, any
+     * application holding
+     * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to
+     * these files.
+     * </ul>
+     * <p>
+     * Shared storage devices returned here are considered a stable part of the
+     * device, including physical media slots under a protective cover. The
+     * returned paths do not include transient devices, such as USB flash drives
+     * connected to handheld devices.
+     * <p>
+     * An application may store data on any or all of the returned devices. For
+     * example, an app may choose to store large files on the device with the
+     * most available space, as measured by {@link StatFs}.
+     * <p>
+     * No additional permissions are required for the calling app to read or
+     * write files under the returned path. Write access outside of these paths
+     * on secondary external storage devices is not available.
+     * <p>
+     * The returned paths may change over time if different shared storage media
+     * is inserted, so only relative paths should be persisted.
+     *
+     * @return the absolute paths to application-specific directories. Some
+     *         individual paths may be {@code null} if that shared storage is
+     *         not currently available.
+     * @see Environment#getExternalStorageState(File)
+     * @see Environment#isExternalStorageEmulated(File)
+     * @see Environment#isExternalStorageRemovable(File)
+     * @deprecated These directories still exist and are scanned, but developers
+     *             are encouraged to migrate to inserting content into a
+     *             {@link MediaStore} collection directly, as any app can
+     *             contribute new media to {@link MediaStore} with no
+     *             permissions required, starting in
+     *             {@link android.os.Build.VERSION_CODES#Q}.
+     */
+    @Deprecated
+    public abstract File[] getExternalMediaDirs();
+
+    /**
+     * Returns an array of strings naming the private files associated with
+     * this Context's application package.
+     *
+     * @return Array of strings naming the private files.
+     *
+     * @see #openFileInput
+     * @see #openFileOutput
+     * @see #deleteFile
+     */
+    public abstract String[] fileList();
+
+    /**
+     * Retrieve, creating if needed, a new directory in which the application
+     * can place its own custom data files.  You can use the returned File
+     * object to create and access files in this directory.  Note that files
+     * created through a File object will only be accessible by your own
+     * application; you can only set the mode of the entire directory, not
+     * of individual files.
+     * <p>
+     * The returned path may change over time if the calling app is moved to an
+     * adopted storage device, so only relative paths should be persisted.
+     * <p>
+     * Apps require no extra permissions to read or write to the returned path,
+     * since this path lives in their private storage.
+     *
+     * @param name Name of the directory to retrieve.  This is a directory
+     * that is created as part of your application data.
+     * @param mode Operating mode.
+     *
+     * @return A {@link File} object for the requested directory.  The directory
+     * will have been created if it does not already exist.
+     *
+     * @see #openFileOutput(String, int)
+     */
+    public abstract File getDir(String name, @FileMode int mode);
+
+    /**
+     * Open a new private SQLiteDatabase associated with this Context's
+     * application package. Create the database file if it doesn't exist.
+     *
+     * @param name The name (unique in the application package) of the database.
+     * @param mode Operating mode.
+     * @param factory An optional factory class that is called to instantiate a
+     *            cursor when query is called.
+     * @return The contents of a newly created database with the given name.
+     * @throws android.database.sqlite.SQLiteException if the database file
+     *             could not be opened.
+     * @see #MODE_PRIVATE
+     * @see #MODE_ENABLE_WRITE_AHEAD_LOGGING
+     * @see #MODE_NO_LOCALIZED_COLLATORS
+     * @see #deleteDatabase
+     */
+    public abstract SQLiteDatabase openOrCreateDatabase(String name,
+            @DatabaseMode int mode, CursorFactory factory);
+
+    /**
+     * Open a new private SQLiteDatabase associated with this Context's
+     * application package. Creates the database file if it doesn't exist.
+     * <p>
+     * Accepts input param: a concrete instance of {@link DatabaseErrorHandler}
+     * to be used to handle corruption when sqlite reports database corruption.
+     * </p>
+     *
+     * @param name The name (unique in the application package) of the database.
+     * @param mode Operating mode.
+     * @param factory An optional factory class that is called to instantiate a
+     *            cursor when query is called.
+     * @param errorHandler the {@link DatabaseErrorHandler} to be used when
+     *            sqlite reports database corruption. if null,
+     *            {@link android.database.DefaultDatabaseErrorHandler} is
+     *            assumed.
+     * @return The contents of a newly created database with the given name.
+     * @throws android.database.sqlite.SQLiteException if the database file
+     *             could not be opened.
+     * @see #MODE_PRIVATE
+     * @see #MODE_ENABLE_WRITE_AHEAD_LOGGING
+     * @see #MODE_NO_LOCALIZED_COLLATORS
+     * @see #deleteDatabase
+     */
+    public abstract SQLiteDatabase openOrCreateDatabase(String name,
+            @DatabaseMode int mode, CursorFactory factory,
+            @Nullable DatabaseErrorHandler errorHandler);
+
+    /**
+     * Move an existing database file from the given source storage context to
+     * this context. This is typically used to migrate data between storage
+     * locations after an upgrade, such as migrating to device protected
+     * storage.
+     * <p>
+     * The database must be closed before being moved.
+     *
+     * @param sourceContext The source context which contains the existing
+     *            database to move.
+     * @param name The name of the database file.
+     * @return {@code true} if the move was successful or if the database didn't
+     *         exist in the source context, otherwise {@code false}.
+     * @see #createDeviceProtectedStorageContext()
+     */
+    public abstract boolean moveDatabaseFrom(Context sourceContext, String name);
+
+    /**
+     * Delete an existing private SQLiteDatabase associated with this Context's
+     * application package.
+     *
+     * @param name The name (unique in the application package) of the
+     *             database.
+     *
+     * @return {@code true} if the database was successfully deleted; else {@code false}.
+     *
+     * @see #openOrCreateDatabase
+     */
+    public abstract boolean deleteDatabase(String name);
+
+    /**
+     * Returns the absolute path on the filesystem where a database created with
+     * {@link #openOrCreateDatabase} is stored.
+     * <p>
+     * The returned path may change over time if the calling app is moved to an
+     * adopted storage device, so only relative paths should be persisted.
+     *
+     * @param name The name of the database for which you would like to get
+     *          its path.
+     *
+     * @return An absolute path to the given database.
+     *
+     * @see #openOrCreateDatabase
+     */
+    public abstract File getDatabasePath(String name);
+
+    /**
+     * Returns an array of strings naming the private databases associated with
+     * this Context's application package.
+     *
+     * @return Array of strings naming the private databases.
+     *
+     * @see #openOrCreateDatabase
+     * @see #deleteDatabase
+     */
+    public abstract String[] databaseList();
+
+    /**
+     * @deprecated Use {@link android.app.WallpaperManager#getDrawable
+     * WallpaperManager.get()} instead.
+     */
+    @Deprecated
+    public abstract Drawable getWallpaper();
+
+    /**
+     * @deprecated Use {@link android.app.WallpaperManager#peekDrawable
+     * WallpaperManager.peek()} instead.
+     */
+    @Deprecated
+    public abstract Drawable peekWallpaper();
+
+    /**
+     * @deprecated Use {@link android.app.WallpaperManager#getDesiredMinimumWidth()
+     * WallpaperManager.getDesiredMinimumWidth()} instead.
+     */
+    @Deprecated
+    public abstract int getWallpaperDesiredMinimumWidth();
+
+    /**
+     * @deprecated Use {@link android.app.WallpaperManager#getDesiredMinimumHeight()
+     * WallpaperManager.getDesiredMinimumHeight()} instead.
+     */
+    @Deprecated
+    public abstract int getWallpaperDesiredMinimumHeight();
+
+    /**
+     * @deprecated Use {@link android.app.WallpaperManager#setBitmap(Bitmap)
+     * WallpaperManager.set()} instead.
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#SET_WALLPAPER}.
+     */
+    @Deprecated
+    public abstract void setWallpaper(Bitmap bitmap) throws IOException;
+
+    /**
+     * @deprecated Use {@link android.app.WallpaperManager#setStream(InputStream)
+     * WallpaperManager.set()} instead.
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#SET_WALLPAPER}.
+     */
+    @Deprecated
+    public abstract void setWallpaper(InputStream data) throws IOException;
+
+    /**
+     * @deprecated Use {@link android.app.WallpaperManager#clear
+     * WallpaperManager.clear()} instead.
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#SET_WALLPAPER}.
+     */
+    @Deprecated
+    public abstract void clearWallpaper() throws IOException;
+
+    /**
+     * Same as {@link #startActivity(Intent, Bundle)} with no options
+     * specified.
+     *
+     * @param intent The description of the activity to start.
+     *
+     * @throws ActivityNotFoundException &nbsp;
+     *`
+     * @see #startActivity(Intent, Bundle)
+     * @see PackageManager#resolveActivity
+     */
+    public abstract void startActivity(@RequiresPermission Intent intent);
+
+    /**
+     * Version of {@link #startActivity(Intent)} that allows you to specify the
+     * user the activity will be started for.  This is not available to applications
+     * that are not pre-installed on the system image.
+     * @param intent The description of the activity to start.
+     * @param user The UserHandle of the user to start this activity for.
+     * @throws ActivityNotFoundException &nbsp;
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
+    @SystemApi
+    @TestApi
+    public void startActivityAsUser(@RequiresPermission @NonNull Intent intent,
+            @NonNull UserHandle user) {
+        throw new RuntimeException("Not implemented. Must override in a subclass.");
+    }
+
+    /**
+     * Launch a new activity.  You will not receive any information about when
+     * the activity exits.
+     *
+     * <p>Note that if this method is being called from outside of an
+     * {@link android.app.Activity} Context, then the Intent must include
+     * the {@link Intent#FLAG_ACTIVITY_NEW_TASK} launch flag.  This is because,
+     * without being started from an existing Activity, there is no existing
+     * task in which to place the new activity and thus it needs to be placed
+     * in its own separate task.
+     *
+     * <p>This method throws {@link ActivityNotFoundException}
+     * if there was no Activity found to run the given Intent.
+     *
+     * @param intent The description of the activity to start.
+     * @param options Additional options for how the Activity should be started.
+     * May be null if there are no options.  See {@link android.app.ActivityOptions}
+     * for how to build the Bundle supplied here; there are no supported definitions
+     * for building it manually.
+     *
+     * @throws ActivityNotFoundException &nbsp;
+     *
+     * @see #startActivity(Intent)
+     * @see PackageManager#resolveActivity
+     */
+    public abstract void startActivity(@RequiresPermission Intent intent,
+            @Nullable Bundle options);
+
+    /**
+     * Version of {@link #startActivity(Intent, Bundle)} that allows you to specify the
+     * user the activity will be started for.  This is not available to applications
+     * that are not pre-installed on the system image.
+     * @param intent The description of the activity to start.
+     * @param options Additional options for how the Activity should be started.
+     * May be null if there are no options.  See {@link android.app.ActivityOptions}
+     * for how to build the Bundle supplied here; there are no supported definitions
+     * for building it manually.
+     * @param userId The UserHandle of the user to start this activity for.
+     * @throws ActivityNotFoundException &nbsp;
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
+    @UnsupportedAppUsage
+    public void startActivityAsUser(@RequiresPermission Intent intent, @Nullable Bundle options,
+            UserHandle userId) {
+        throw new RuntimeException("Not implemented. Must override in a subclass.");
+    }
+
+    /**
+     * Version of {@link #startActivity(Intent, Bundle)} that returns a result to the caller. This
+     * is only supported for Views and Fragments.
+     * @param who The identifier for the calling element that will receive the result.
+     * @param intent The intent to start.
+     * @param requestCode The code that will be returned with onActivityResult() identifying this
+     *          request.
+     * @param options Additional options for how the Activity should be started.
+     *          May be null if there are no options.  See {@link android.app.ActivityOptions}
+     *          for how to build the Bundle supplied here; there are no supported definitions
+     *          for building it manually.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public void startActivityForResult(
+            @NonNull String who, Intent intent, int requestCode, @Nullable Bundle options) {
+        throw new RuntimeException("This method is only implemented for Activity-based Contexts. "
+                + "Check canStartActivityForResult() before calling.");
+    }
+
+    /**
+     * Identifies whether this Context instance will be able to process calls to
+     * {@link #startActivityForResult(String, Intent, int, Bundle)}.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public boolean canStartActivityForResult() {
+        return false;
+    }
+
+    /**
+     * Same as {@link #startActivities(Intent[], Bundle)} with no options
+     * specified.
+     *
+     * @param intents An array of Intents to be started.
+     *
+     * @throws ActivityNotFoundException &nbsp;
+     *
+     * @see #startActivities(Intent[], Bundle)
+     * @see PackageManager#resolveActivity
+     */
+    public abstract void startActivities(@RequiresPermission Intent[] intents);
+
+    /**
+     * Launch multiple new activities.  This is generally the same as calling
+     * {@link #startActivity(Intent)} for the first Intent in the array,
+     * that activity during its creation calling {@link #startActivity(Intent)}
+     * for the second entry, etc.  Note that unlike that approach, generally
+     * none of the activities except the last in the array will be created
+     * at this point, but rather will be created when the user first visits
+     * them (due to pressing back from the activity on top).
+     *
+     * <p>This method throws {@link ActivityNotFoundException}
+     * if there was no Activity found for <em>any</em> given Intent.  In this
+     * case the state of the activity stack is undefined (some Intents in the
+     * list may be on it, some not), so you probably want to avoid such situations.
+     *
+     * @param intents An array of Intents to be started.
+     * @param options Additional options for how the Activity should be started.
+     * See {@link android.content.Context#startActivity(Intent, Bundle)}
+     * Context.startActivity(Intent, Bundle)} for more details.
+     *
+     * @throws ActivityNotFoundException &nbsp;
+     *
+     * @see #startActivities(Intent[])
+     * @see PackageManager#resolveActivity
+     */
+    public abstract void startActivities(@RequiresPermission Intent[] intents, Bundle options);
+
+    /**
+     * @hide
+     * Launch multiple new activities.  This is generally the same as calling
+     * {@link #startActivity(Intent)} for the first Intent in the array,
+     * that activity during its creation calling {@link #startActivity(Intent)}
+     * for the second entry, etc.  Note that unlike that approach, generally
+     * none of the activities except the last in the array will be created
+     * at this point, but rather will be created when the user first visits
+     * them (due to pressing back from the activity on top).
+     *
+     * <p>This method throws {@link ActivityNotFoundException}
+     * if there was no Activity found for <em>any</em> given Intent.  In this
+     * case the state of the activity stack is undefined (some Intents in the
+     * list may be on it, some not), so you probably want to avoid such situations.
+     *
+     * @param intents An array of Intents to be started.
+     * @param options Additional options for how the Activity should be started.
+     * @param userHandle The user for whom to launch the activities
+     * See {@link android.content.Context#startActivity(Intent, Bundle)}
+     * Context.startActivity(Intent, Bundle)} for more details.
+     *
+     * @return The corresponding flag {@link ActivityManager#START_CANCELED},
+     *         {@link ActivityManager#START_SUCCESS} etc. indicating whether the launch was
+     *         successful.
+     *
+     * @throws ActivityNotFoundException &nbsp;
+     *
+     * @see #startActivities(Intent[])
+     * @see PackageManager#resolveActivity
+     */
+    @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
+    public int startActivitiesAsUser(Intent[] intents, Bundle options, UserHandle userHandle) {
+        throw new RuntimeException("Not implemented. Must override in a subclass.");
+    }
+
+    /**
+     * Same as {@link #startIntentSender(IntentSender, Intent, int, int, int, Bundle)}
+     * with no options specified.
+     *
+     * @param intent The IntentSender to launch.
+     * @param fillInIntent If non-null, this will be provided as the
+     * intent parameter to {@link IntentSender#sendIntent}.
+     * @param flagsMask Intent flags in the original IntentSender that you
+     * would like to change.
+     * @param flagsValues Desired values for any bits set in
+     * <var>flagsMask</var>
+     * @param extraFlags Always set to 0.
+     *
+     * @see #startActivity(Intent)
+     * @see #startIntentSender(IntentSender, Intent, int, int, int, Bundle)
+     */
+    public abstract void startIntentSender(IntentSender intent, @Nullable Intent fillInIntent,
+            @Intent.MutableFlags int flagsMask, @Intent.MutableFlags int flagsValues,
+            int extraFlags) throws IntentSender.SendIntentException;
+
+    /**
+     * Like {@link #startActivity(Intent, Bundle)}, but taking a IntentSender
+     * to start.  If the IntentSender is for an activity, that activity will be started
+     * as if you had called the regular {@link #startActivity(Intent)}
+     * here; otherwise, its associated action will be executed (such as
+     * sending a broadcast) as if you had called
+     * {@link IntentSender#sendIntent IntentSender.sendIntent} on it.
+     *
+     * @param intent The IntentSender to launch.
+     * @param fillInIntent If non-null, this will be provided as the
+     * intent parameter to {@link IntentSender#sendIntent}.
+     * @param flagsMask Intent flags in the original IntentSender that you
+     * would like to change.
+     * @param flagsValues Desired values for any bits set in
+     * <var>flagsMask</var>
+     * @param extraFlags Always set to 0.
+     * @param options Additional options for how the Activity should be started.
+     * See {@link android.content.Context#startActivity(Intent, Bundle)}
+     * Context.startActivity(Intent, Bundle)} for more details.  If options
+     * have also been supplied by the IntentSender, options given here will
+     * override any that conflict with those given by the IntentSender.
+     *
+     * @see #startActivity(Intent, Bundle)
+     * @see #startIntentSender(IntentSender, Intent, int, int, int)
+     */
+    public abstract void startIntentSender(IntentSender intent, @Nullable Intent fillInIntent,
+            @Intent.MutableFlags int flagsMask, @Intent.MutableFlags int flagsValues,
+            int extraFlags, @Nullable Bundle options) throws IntentSender.SendIntentException;
+
+    /**
+     * Broadcast the given intent to all interested BroadcastReceivers.  This
+     * call is asynchronous; it returns immediately, and you will continue
+     * executing while the receivers are run.  No results are propagated from
+     * receivers and receivers can not abort the broadcast. If you want
+     * to allow receivers to propagate results or abort the broadcast, you must
+     * send an ordered broadcast using
+     * {@link #sendOrderedBroadcast(Intent, String)}.
+     *
+     * <p>See {@link BroadcastReceiver} for more information on Intent broadcasts.
+     *
+     * @param intent The Intent to broadcast; all receivers matching this
+     *               Intent will receive the broadcast.
+     *
+     * @see android.content.BroadcastReceiver
+     * @see #registerReceiver
+     * @see #sendBroadcast(Intent, String)
+     * @see #sendOrderedBroadcast(Intent, String)
+     * @see #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle)
+     */
+    public abstract void sendBroadcast(@RequiresPermission Intent intent);
+
+    /**
+     * Broadcast the given intent to all interested BroadcastReceivers, allowing
+     * an optional required permission to be enforced.  This
+     * call is asynchronous; it returns immediately, and you will continue
+     * executing while the receivers are run.  No results are propagated from
+     * receivers and receivers can not abort the broadcast. If you want
+     * to allow receivers to propagate results or abort the broadcast, you must
+     * send an ordered broadcast using
+     * {@link #sendOrderedBroadcast(Intent, String)}.
+     *
+     * <p>See {@link BroadcastReceiver} for more information on Intent broadcasts.
+     *
+     * @param intent The Intent to broadcast; all receivers matching this
+     *               Intent will receive the broadcast.
+     * @param receiverPermission (optional) String naming a permission that
+     *               a receiver must hold in order to receive your broadcast.
+     *               If null, no permission is required.
+     *
+     * @see android.content.BroadcastReceiver
+     * @see #registerReceiver
+     * @see #sendBroadcast(Intent)
+     * @see #sendOrderedBroadcast(Intent, String)
+     * @see #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle)
+     */
+    public abstract void sendBroadcast(@RequiresPermission Intent intent,
+            @Nullable String receiverPermission);
+
+
+    /**
+     * Broadcast the given intent to all interested BroadcastReceivers, allowing
+     * an array of required permissions to be enforced.  This call is asynchronous; it returns
+     * immediately, and you will continue executing while the receivers are run.  No results are
+     * propagated from receivers and receivers can not abort the broadcast. If you want to allow
+     * receivers to propagate results or abort the broadcast, you must send an ordered broadcast
+     * using {@link #sendOrderedBroadcast(Intent, String)}.
+     *
+     * <p>See {@link BroadcastReceiver} for more information on Intent broadcasts.
+     *
+     * @param intent The Intent to broadcast; all receivers matching this
+     *               Intent will receive the broadcast.
+     * @param receiverPermissions Array of names of permissions that a receiver must hold
+     *                            in order to receive your broadcast.
+     *                            If empty, no permissions are required.
+     *
+     * @see android.content.BroadcastReceiver
+     * @see #registerReceiver
+     * @see #sendBroadcast(Intent)
+     * @see #sendOrderedBroadcast(Intent, String)
+     * @see #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle)
+     * @hide
+     */
+    public void sendBroadcastMultiplePermissions(@NonNull Intent intent,
+            @NonNull String[] receiverPermissions) {
+        throw new RuntimeException("Not implemented. Must override in a subclass.");
+    }
+
+    /**
+     * Broadcast the given intent to all interested BroadcastReceivers, allowing
+     * an array of required permissions to be enforced.  This call is asynchronous; it returns
+     * immediately, and you will continue executing while the receivers are run.  No results are
+     * propagated from receivers and receivers can not abort the broadcast. If you want to allow
+     * receivers to propagate results or abort the broadcast, you must send an ordered broadcast
+     * using {@link #sendOrderedBroadcast(Intent, String)}.
+     *
+     * <p>See {@link BroadcastReceiver} for more information on Intent broadcasts.
+     *
+     * @param intent The Intent to broadcast; all receivers matching this
+     *               Intent will receive the broadcast.
+     * @param receiverPermissions Array of names of permissions that a receiver must hold
+     *                            in order to receive your broadcast.
+     *                            If empty, no permissions are required.
+     *
+     * @see android.content.BroadcastReceiver
+     * @see #registerReceiver
+     * @see #sendBroadcast(Intent)
+     * @see #sendOrderedBroadcast(Intent, String)
+     * @see #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle)
+     */
+    public void sendBroadcastWithMultiplePermissions(@NonNull Intent intent,
+            @NonNull String[] receiverPermissions) {
+        sendBroadcastMultiplePermissions(intent, receiverPermissions);
+    }
+
+    /**
+     * Broadcast the given intent to all interested BroadcastReceivers, allowing
+     * an array of required permissions to be enforced.  This call is asynchronous; it returns
+     * immediately, and you will continue executing while the receivers are run.  No results are
+     * propagated from receivers and receivers can not abort the broadcast. If you want to allow
+     * receivers to propagate results or abort the broadcast, you must send an ordered broadcast
+     * using {@link #sendOrderedBroadcast(Intent, String)}.
+     *
+     * <p>See {@link BroadcastReceiver} for more information on Intent broadcasts.
+     *
+     * @param intent The Intent to broadcast; all receivers matching this
+     *               Intent will receive the broadcast.
+     * @param user The user to send the broadcast to.
+     * @param receiverPermissions Array of names of permissions that a receiver must hold
+     *                            in order to receive your broadcast.
+     *                            If null or empty, no permissions are required.
+     *
+     * @see android.content.BroadcastReceiver
+     * @see #registerReceiver
+     * @see #sendBroadcast(Intent)
+     * @see #sendOrderedBroadcast(Intent, String)
+     * @see #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle)
+     * @hide
+     */
+    public abstract void sendBroadcastAsUserMultiplePermissions(Intent intent, UserHandle user,
+            String[] receiverPermissions);
+
+    /**
+     * Broadcast the given intent to all interested BroadcastReceivers, allowing
+     * an optional required permission to be enforced.  This
+     * call is asynchronous; it returns immediately, and you will continue
+     * executing while the receivers are run.  No results are propagated from
+     * receivers and receivers can not abort the broadcast. If you want
+     * to allow receivers to propagate results or abort the broadcast, you must
+     * send an ordered broadcast using
+     * {@link #sendOrderedBroadcast(Intent, String)}.
+     *
+     * <p>See {@link BroadcastReceiver} for more information on Intent broadcasts.
+     *
+     * @param intent The Intent to broadcast; all receivers matching this
+     *               Intent will receive the broadcast.
+     * @param receiverPermission (optional) String naming a permission that
+     *               a receiver must hold in order to receive your broadcast.
+     *               If null, no permission is required.
+     * @param options (optional) Additional sending options, generated from a
+     * {@link android.app.BroadcastOptions}.
+     *
+     * @see android.content.BroadcastReceiver
+     * @see #registerReceiver
+     * @see #sendBroadcast(Intent)
+     * @see #sendOrderedBroadcast(Intent, String)
+     * @see #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle)
+     * @hide
+     */
+    @SystemApi
+    public abstract void sendBroadcast(Intent intent,
+            @Nullable String receiverPermission,
+            @Nullable Bundle options);
+
+    /**
+     * Like {@link #sendBroadcast(Intent, String)}, but also allows specification
+     * of an associated app op as per {@link android.app.AppOpsManager}.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public abstract void sendBroadcast(Intent intent,
+            String receiverPermission, int appOp);
+
+    /**
+     * Broadcast the given intent to all interested BroadcastReceivers, delivering
+     * them one at a time to allow more preferred receivers to consume the
+     * broadcast before it is delivered to less preferred receivers.  This
+     * call is asynchronous; it returns immediately, and you will continue
+     * executing while the receivers are run.
+     *
+     * <p>See {@link BroadcastReceiver} for more information on Intent broadcasts.
+     *
+     * @param intent The Intent to broadcast; all receivers matching this
+     *               Intent will receive the broadcast.
+     * @param receiverPermission (optional) String naming a permissions that
+     *               a receiver must hold in order to receive your broadcast.
+     *               If null, no permission is required.
+     *
+     * @see android.content.BroadcastReceiver
+     * @see #registerReceiver
+     * @see #sendBroadcast(Intent)
+     * @see #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle)
+     */
+    public abstract void sendOrderedBroadcast(@RequiresPermission Intent intent,
+            @Nullable String receiverPermission);
+
+    /**
+     * Version of {@link #sendBroadcast(Intent)} that allows you to
+     * receive data back from the broadcast.  This is accomplished by
+     * supplying your own BroadcastReceiver when calling, which will be
+     * treated as a final receiver at the end of the broadcast -- its
+     * {@link BroadcastReceiver#onReceive} method will be called with
+     * the result values collected from the other receivers.  The broadcast will
+     * be serialized in the same way as calling
+     * {@link #sendOrderedBroadcast(Intent, String)}.
+     *
+     * <p>Like {@link #sendBroadcast(Intent)}, this method is
+     * asynchronous; it will return before
+     * resultReceiver.onReceive() is called.
+     *
+     * <p>See {@link BroadcastReceiver} for more information on Intent broadcasts.
+     *
+     * @param intent The Intent to broadcast; all receivers matching this
+     *               Intent will receive the broadcast.
+     * @param receiverPermission String naming a permissions that
+     *               a receiver must hold in order to receive your broadcast.
+     *               If null, no permission is required.
+     * @param resultReceiver Your own BroadcastReceiver to treat as the final
+     *                       receiver of the broadcast.
+     * @param scheduler A custom Handler with which to schedule the
+     *                  resultReceiver callback; if null it will be
+     *                  scheduled in the Context's main thread.
+     * @param initialCode An initial value for the result code.  Often
+     *                    Activity.RESULT_OK.
+     * @param initialData An initial value for the result data.  Often
+     *                    null.
+     * @param initialExtras An initial value for the result extras.  Often
+     *                      null.
+     *
+     * @see #sendBroadcast(Intent)
+     * @see #sendBroadcast(Intent, String)
+     * @see #sendOrderedBroadcast(Intent, String)
+     * @see android.content.BroadcastReceiver
+     * @see #registerReceiver
+     * @see android.app.Activity#RESULT_OK
+     */
+    public abstract void sendOrderedBroadcast(@RequiresPermission @NonNull Intent intent,
+            @Nullable String receiverPermission, @Nullable BroadcastReceiver resultReceiver,
+            @Nullable Handler scheduler, int initialCode, @Nullable String initialData,
+            @Nullable Bundle initialExtras);
+
+    /**
+     * Version of {@link #sendBroadcast(Intent)} that allows you to
+     * receive data back from the broadcast.  This is accomplished by
+     * supplying your own BroadcastReceiver when calling, which will be
+     * treated as a final receiver at the end of the broadcast -- its
+     * {@link BroadcastReceiver#onReceive} method will be called with
+     * the result values collected from the other receivers.  The broadcast will
+     * be serialized in the same way as calling
+     * {@link #sendOrderedBroadcast(Intent, String)}.
+     *
+     * <p>Like {@link #sendBroadcast(Intent)}, this method is
+     * asynchronous; it will return before
+     * resultReceiver.onReceive() is called.
+     *
+     * <p>See {@link BroadcastReceiver} for more information on Intent broadcasts.
+     *
+     *
+     * @param intent The Intent to broadcast; all receivers matching this
+     *               Intent will receive the broadcast.
+     * @param receiverPermission String naming a permissions that
+     *               a receiver must hold in order to receive your broadcast.
+     *               If null, no permission is required.
+     * @param options (optional) Additional sending options, generated from a
+     * {@link android.app.BroadcastOptions}.
+     * @param resultReceiver Your own BroadcastReceiver to treat as the final
+     *                       receiver of the broadcast.
+     * @param scheduler A custom Handler with which to schedule the
+     *                  resultReceiver callback; if null it will be
+     *                  scheduled in the Context's main thread.
+     * @param initialCode An initial value for the result code.  Often
+     *                    Activity.RESULT_OK.
+     * @param initialData An initial value for the result data.  Often
+     *                    null.
+     * @param initialExtras An initial value for the result extras.  Often
+     *                      null.
+     * @see #sendBroadcast(Intent)
+     * @see #sendBroadcast(Intent, String)
+     * @see #sendOrderedBroadcast(Intent, String)
+     * @see android.content.BroadcastReceiver
+     * @see #registerReceiver
+     * @see android.app.Activity#RESULT_OK
+     * @hide
+     */
+    @SystemApi
+    public abstract void sendOrderedBroadcast(@NonNull Intent intent,
+            @Nullable String receiverPermission, @Nullable Bundle options,
+            @Nullable BroadcastReceiver resultReceiver, @Nullable Handler scheduler,
+            int initialCode, @Nullable String initialData, @Nullable Bundle initialExtras);
+
+    /**
+     * Like {@link #sendOrderedBroadcast(Intent, String, BroadcastReceiver, android.os.Handler,
+     * int, String, android.os.Bundle)}, but also allows specification
+     * of an associated app op as per {@link android.app.AppOpsManager}.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public abstract void sendOrderedBroadcast(Intent intent,
+            String receiverPermission, int appOp, BroadcastReceiver resultReceiver,
+            Handler scheduler, int initialCode, String initialData,
+            Bundle initialExtras);
+
+    /**
+     * Version of {@link #sendBroadcast(Intent)} that allows you to specify the
+     * user the broadcast will be sent to.  This is not available to applications
+     * that are not pre-installed on the system image.
+     * @param intent The intent to broadcast
+     * @param user UserHandle to send the intent to.
+     * @see #sendBroadcast(Intent)
+     */
+    @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
+    public abstract void sendBroadcastAsUser(@RequiresPermission Intent intent,
+            UserHandle user);
+
+    /**
+     * Version of {@link #sendBroadcast(Intent, String)} that allows you to specify the
+     * user the broadcast will be sent to.  This is not available to applications
+     * that are not pre-installed on the system image.
+     *
+     * @param intent The Intent to broadcast; all receivers matching this
+     *               Intent will receive the broadcast.
+     * @param user UserHandle to send the intent to.
+     * @param receiverPermission (optional) String naming a permission that
+     *               a receiver must hold in order to receive your broadcast.
+     *               If null, no permission is required.
+     *
+     * @see #sendBroadcast(Intent, String)
+     */
+    @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
+    public abstract void sendBroadcastAsUser(@RequiresPermission Intent intent,
+            UserHandle user, @Nullable String receiverPermission);
+
+    /**
+     * Version of {@link #sendBroadcast(Intent, String, Bundle)} that allows you to specify the
+     * user the broadcast will be sent to.  This is not available to applications
+     * that are not pre-installed on the system image.
+     *
+     * @param intent The Intent to broadcast; all receivers matching this
+     *               Intent will receive the broadcast.
+     * @param user UserHandle to send the intent to.
+     * @param receiverPermission (optional) String naming a permission that
+     *               a receiver must hold in order to receive your broadcast.
+     *               If null, no permission is required.
+     * @param options (optional) Additional sending options, generated from a
+     * {@link android.app.BroadcastOptions}.
+     *
+     * @see #sendBroadcast(Intent, String, Bundle)
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
+    public abstract void sendBroadcastAsUser(@RequiresPermission Intent intent,
+            UserHandle user, @Nullable String receiverPermission, @Nullable Bundle options);
+
+    /**
+     * Version of {@link #sendBroadcast(Intent, String)} that allows you to specify the
+     * user the broadcast will be sent to.  This is not available to applications
+     * that are not pre-installed on the system image.
+     *
+     * @param intent The Intent to broadcast; all receivers matching this
+     *               Intent will receive the broadcast.
+     * @param user UserHandle to send the intent to.
+     * @param receiverPermission (optional) String naming a permission that
+     *               a receiver must hold in order to receive your broadcast.
+     *               If null, no permission is required.
+     * @param appOp The app op associated with the broadcast.
+     *
+     * @see #sendBroadcast(Intent, String)
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
+    @UnsupportedAppUsage
+    public abstract void sendBroadcastAsUser(@RequiresPermission Intent intent,
+            UserHandle user, @Nullable String receiverPermission, int appOp);
+
+    /**
+     * Version of
+     * {@link #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle)}
+     * that allows you to specify the
+     * user the broadcast will be sent to.  This is not available to applications
+     * that are not pre-installed on the system image.
+     *
+     * <p>See {@link BroadcastReceiver} for more information on Intent broadcasts.
+     *
+     * @param intent The Intent to broadcast; all receivers matching this
+     *               Intent will receive the broadcast.
+     * @param user UserHandle to send the intent to.
+     * @param receiverPermission String naming a permissions that
+     *               a receiver must hold in order to receive your broadcast.
+     *               If null, no permission is required.
+     * @param resultReceiver Your own BroadcastReceiver to treat as the final
+     *                       receiver of the broadcast.
+     * @param scheduler A custom Handler with which to schedule the
+     *                  resultReceiver callback; if null it will be
+     *                  scheduled in the Context's main thread.
+     * @param initialCode An initial value for the result code.  Often
+     *                    Activity.RESULT_OK.
+     * @param initialData An initial value for the result data.  Often
+     *                    null.
+     * @param initialExtras An initial value for the result extras.  Often
+     *                      null.
+     *
+     * @see #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle)
+     */
+    @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
+    public abstract void sendOrderedBroadcastAsUser(@RequiresPermission Intent intent,
+            UserHandle user, @Nullable String receiverPermission, BroadcastReceiver resultReceiver,
+            @Nullable Handler scheduler, int initialCode, @Nullable String initialData,
+            @Nullable  Bundle initialExtras);
+
+    /**
+     * Similar to above but takes an appOp as well, to enforce restrictions.
+     * @see #sendOrderedBroadcastAsUser(Intent, UserHandle, String,
+     *       BroadcastReceiver, Handler, int, String, Bundle)
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
+    @UnsupportedAppUsage
+    public abstract void sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
+            @Nullable String receiverPermission, int appOp, BroadcastReceiver resultReceiver,
+            @Nullable Handler scheduler, int initialCode, @Nullable String initialData,
+            @Nullable  Bundle initialExtras);
+
+    /**
+     * Similar to above but takes an appOp as well, to enforce restrictions, and an options Bundle.
+     * @see #sendOrderedBroadcastAsUser(Intent, UserHandle, String,
+     *       BroadcastReceiver, Handler, int, String, Bundle)
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
+    @UnsupportedAppUsage
+    public abstract void sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
+            @Nullable String receiverPermission, int appOp, @Nullable Bundle options,
+            BroadcastReceiver resultReceiver, @Nullable Handler scheduler, int initialCode,
+            @Nullable String initialData, @Nullable  Bundle initialExtras);
+
+    /**
+     * Version of
+     * {@link #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String,
+     * Bundle)} that allows you to specify the App Op to enforce restrictions on which receivers
+     * the broadcast will be sent to.
+     *
+     * <p>See {@link BroadcastReceiver} for more information on Intent broadcasts.
+     *
+     * @param intent The Intent to broadcast; all receivers matching this
+     *               Intent will receive the broadcast.
+     * @param receiverPermission String naming a permissions that
+     *               a receiver must hold in order to receive your broadcast.
+     *               If null, no permission is required.
+     * @param receiverAppOp The app op associated with the broadcast. If null, no appOp is
+     *                      required. If both receiverAppOp and receiverPermission are non-null,
+     *                      a receiver must have both of them to
+     *                      receive the broadcast
+     * @param resultReceiver Your own BroadcastReceiver to treat as the final
+     *                       receiver of the broadcast.
+     * @param scheduler A custom Handler with which to schedule the
+     *                  resultReceiver callback; if null it will be
+     *                  scheduled in the Context's main thread.
+     * @param initialCode An initial value for the result code.  Often
+     *                    Activity.RESULT_OK.
+     * @param initialData An initial value for the result data.  Often
+     *                    null.
+     * @param initialExtras An initial value for the result extras.  Often
+     *                      null.
+     *
+     * @see #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle)
+     */
+    public void sendOrderedBroadcast(@NonNull Intent intent, @Nullable String receiverPermission,
+            @Nullable String receiverAppOp, @Nullable BroadcastReceiver resultReceiver,
+            @Nullable Handler scheduler, int initialCode, @Nullable String initialData,
+            @Nullable Bundle initialExtras) {
+        throw new RuntimeException("Not implemented. Must override in a subclass.");
+    }
+
+    /**
+     * Version of
+     * {@link #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String,
+     * Bundle)} that allows you to specify the App Op to enforce restrictions on which receivers
+     * the broadcast will be sent to as well as supply an optional sending options
+     *
+     * <p>See {@link BroadcastReceiver} for more information on Intent broadcasts.
+     *
+     * @param intent The Intent to broadcast; all receivers matching this
+     *               Intent will receive the broadcast.
+     * @param receiverPermission String naming a permissions that
+     *               a receiver must hold in order to receive your broadcast.
+     *               If null, no permission is required.
+     * @param receiverAppOp The app op associated with the broadcast. If null, no appOp is
+     *                      required. If both receiverAppOp and receiverPermission are non-null,
+     *                      a receiver must have both of them to
+     *                      receive the broadcast
+     * @param options (optional) Additional sending options, generated from a
+     * {@link android.app.BroadcastOptions}.
+     * @param resultReceiver Your own BroadcastReceiver to treat as the final
+     *                       receiver of the broadcast.
+     * @param scheduler A custom Handler with which to schedule the
+     *                  resultReceiver callback; if null it will be
+     *                  scheduled in the Context's main thread.
+     * @param initialCode An initial value for the result code.  Often
+     *                    Activity.RESULT_OK.
+     * @param initialData An initial value for the result data.  Often
+     *                    null.
+     * @param initialExtras An initial value for the result extras.  Often
+     *                      null.
+     *
+     * @see #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle)
+     * @see android.app.BroadcastOptions
+     * @hide
+     */
+    public void sendOrderedBroadcast(@RequiresPermission @NonNull Intent intent, int initialCode,
+            @Nullable String receiverPermission, @Nullable String receiverAppOp,
+            @Nullable BroadcastReceiver resultReceiver, @Nullable Handler scheduler,
+            @Nullable String initialData, @Nullable Bundle initialExtras,
+            @Nullable Bundle options) {
+        throw new RuntimeException("Not implemented. Must override in a subclass.");
+    }
+
+    /**
+     * <p>Perform a {@link #sendBroadcast(Intent)} that is "sticky," meaning the
+     * Intent you are sending stays around after the broadcast is complete,
+     * so that others can quickly retrieve that data through the return
+     * value of {@link #registerReceiver(BroadcastReceiver, IntentFilter)}.  In
+     * all other ways, this behaves the same as
+     * {@link #sendBroadcast(Intent)}.
+     *
+     * @deprecated Sticky broadcasts should not be used.  They provide no security (anyone
+     * can access them), no protection (anyone can modify them), and many other problems.
+     * The recommended pattern is to use a non-sticky broadcast to report that <em>something</em>
+     * has changed, with another mechanism for apps to retrieve the current value whenever
+     * desired.
+     *
+     * @param intent The Intent to broadcast; all receivers matching this
+     * Intent will receive the broadcast, and the Intent will be held to
+     * be re-broadcast to future receivers.
+     *
+     * @see #sendBroadcast(Intent)
+     * @see #sendStickyOrderedBroadcast(Intent, BroadcastReceiver, Handler, int, String, Bundle)
+     */
+    @Deprecated
+    @RequiresPermission(android.Manifest.permission.BROADCAST_STICKY)
+    public abstract void sendStickyBroadcast(@RequiresPermission Intent intent);
+
+    /**
+     * <p>Version of {@link #sendStickyBroadcast} that allows you to
+     * receive data back from the broadcast.  This is accomplished by
+     * supplying your own BroadcastReceiver when calling, which will be
+     * treated as a final receiver at the end of the broadcast -- its
+     * {@link BroadcastReceiver#onReceive} method will be called with
+     * the result values collected from the other receivers.  The broadcast will
+     * be serialized in the same way as calling
+     * {@link #sendOrderedBroadcast(Intent, String)}.
+     *
+     * <p>Like {@link #sendBroadcast(Intent)}, this method is
+     * asynchronous; it will return before
+     * resultReceiver.onReceive() is called.  Note that the sticky data
+     * stored is only the data you initially supply to the broadcast, not
+     * the result of any changes made by the receivers.
+     *
+     * <p>See {@link BroadcastReceiver} for more information on Intent broadcasts.
+     *
+     * @deprecated Sticky broadcasts should not be used.  They provide no security (anyone
+     * can access them), no protection (anyone can modify them), and many other problems.
+     * The recommended pattern is to use a non-sticky broadcast to report that <em>something</em>
+     * has changed, with another mechanism for apps to retrieve the current value whenever
+     * desired.
+     *
+     * @param intent The Intent to broadcast; all receivers matching this
+     *               Intent will receive the broadcast.
+     * @param resultReceiver Your own BroadcastReceiver to treat as the final
+     *                       receiver of the broadcast.
+     * @param scheduler A custom Handler with which to schedule the
+     *                  resultReceiver callback; if null it will be
+     *                  scheduled in the Context's main thread.
+     * @param initialCode An initial value for the result code.  Often
+     *                    Activity.RESULT_OK.
+     * @param initialData An initial value for the result data.  Often
+     *                    null.
+     * @param initialExtras An initial value for the result extras.  Often
+     *                      null.
+     *
+     * @see #sendBroadcast(Intent)
+     * @see #sendBroadcast(Intent, String)
+     * @see #sendOrderedBroadcast(Intent, String)
+     * @see #sendStickyBroadcast(Intent)
+     * @see android.content.BroadcastReceiver
+     * @see #registerReceiver
+     * @see android.app.Activity#RESULT_OK
+     */
+    @Deprecated
+    @RequiresPermission(android.Manifest.permission.BROADCAST_STICKY)
+    public abstract void sendStickyOrderedBroadcast(@RequiresPermission Intent intent,
+            BroadcastReceiver resultReceiver,
+            @Nullable Handler scheduler, int initialCode, @Nullable String initialData,
+            @Nullable Bundle initialExtras);
+
+    /**
+     * <p>Remove the data previously sent with {@link #sendStickyBroadcast},
+     * so that it is as if the sticky broadcast had never happened.
+     *
+     * @deprecated Sticky broadcasts should not be used.  They provide no security (anyone
+     * can access them), no protection (anyone can modify them), and many other problems.
+     * The recommended pattern is to use a non-sticky broadcast to report that <em>something</em>
+     * has changed, with another mechanism for apps to retrieve the current value whenever
+     * desired.
+     *
+     * @param intent The Intent that was previously broadcast.
+     *
+     * @see #sendStickyBroadcast
+     */
+    @Deprecated
+    @RequiresPermission(android.Manifest.permission.BROADCAST_STICKY)
+    public abstract void removeStickyBroadcast(@RequiresPermission Intent intent);
+
+    /**
+     * <p>Version of {@link #sendStickyBroadcast(Intent)} that allows you to specify the
+     * user the broadcast will be sent to.  This is not available to applications
+     * that are not pre-installed on the system image.
+     *
+     * @deprecated Sticky broadcasts should not be used.  They provide no security (anyone
+     * can access them), no protection (anyone can modify them), and many other problems.
+     * The recommended pattern is to use a non-sticky broadcast to report that <em>something</em>
+     * has changed, with another mechanism for apps to retrieve the current value whenever
+     * desired.
+     *
+     * @param intent The Intent to broadcast; all receivers matching this
+     * Intent will receive the broadcast, and the Intent will be held to
+     * be re-broadcast to future receivers.
+     * @param user UserHandle to send the intent to.
+     *
+     * @see #sendBroadcast(Intent)
+     */
+    @Deprecated
+    @RequiresPermission(allOf = {
+            android.Manifest.permission.INTERACT_ACROSS_USERS,
+            android.Manifest.permission.BROADCAST_STICKY
+    })
+    public abstract void sendStickyBroadcastAsUser(@RequiresPermission Intent intent,
+            UserHandle user);
+
+    /**
+     * @hide
+     * This is just here for sending CONNECTIVITY_ACTION.
+     */
+    @Deprecated
+    @RequiresPermission(allOf = {
+            android.Manifest.permission.INTERACT_ACROSS_USERS,
+            android.Manifest.permission.BROADCAST_STICKY
+    })
+    public abstract void sendStickyBroadcastAsUser(@RequiresPermission Intent intent,
+            UserHandle user, Bundle options);
+
+    /**
+     * <p>Version of
+     * {@link #sendStickyOrderedBroadcast(Intent, BroadcastReceiver, Handler, int, String, Bundle)}
+     * that allows you to specify the
+     * user the broadcast will be sent to.  This is not available to applications
+     * that are not pre-installed on the system image.
+     *
+     * <p>See {@link BroadcastReceiver} for more information on Intent broadcasts.
+     *
+     * @deprecated Sticky broadcasts should not be used.  They provide no security (anyone
+     * can access them), no protection (anyone can modify them), and many other problems.
+     * The recommended pattern is to use a non-sticky broadcast to report that <em>something</em>
+     * has changed, with another mechanism for apps to retrieve the current value whenever
+     * desired.
+     *
+     * @param intent The Intent to broadcast; all receivers matching this
+     *               Intent will receive the broadcast.
+     * @param user UserHandle to send the intent to.
+     * @param resultReceiver Your own BroadcastReceiver to treat as the final
+     *                       receiver of the broadcast.
+     * @param scheduler A custom Handler with which to schedule the
+     *                  resultReceiver callback; if null it will be
+     *                  scheduled in the Context's main thread.
+     * @param initialCode An initial value for the result code.  Often
+     *                    Activity.RESULT_OK.
+     * @param initialData An initial value for the result data.  Often
+     *                    null.
+     * @param initialExtras An initial value for the result extras.  Often
+     *                      null.
+     *
+     * @see #sendStickyOrderedBroadcast(Intent, BroadcastReceiver, Handler, int, String, Bundle)
+     */
+    @Deprecated
+    @RequiresPermission(allOf = {
+            android.Manifest.permission.INTERACT_ACROSS_USERS,
+            android.Manifest.permission.BROADCAST_STICKY
+    })
+    public abstract void sendStickyOrderedBroadcastAsUser(@RequiresPermission Intent intent,
+            UserHandle user, BroadcastReceiver resultReceiver,
+            @Nullable Handler scheduler, int initialCode, @Nullable String initialData,
+            @Nullable Bundle initialExtras);
+
+    /**
+     * <p>Version of {@link #removeStickyBroadcast(Intent)} that allows you to specify the
+     * user the broadcast will be sent to.  This is not available to applications
+     * that are not pre-installed on the system image.
+     *
+     * <p>You must hold the {@link android.Manifest.permission#BROADCAST_STICKY}
+     * permission in order to use this API.  If you do not hold that
+     * permission, {@link SecurityException} will be thrown.
+     *
+     * @deprecated Sticky broadcasts should not be used.  They provide no security (anyone
+     * can access them), no protection (anyone can modify them), and many other problems.
+     * The recommended pattern is to use a non-sticky broadcast to report that <em>something</em>
+     * has changed, with another mechanism for apps to retrieve the current value whenever
+     * desired.
+     *
+     * @param intent The Intent that was previously broadcast.
+     * @param user UserHandle to remove the sticky broadcast from.
+     *
+     * @see #sendStickyBroadcastAsUser
+     */
+    @Deprecated
+    @RequiresPermission(allOf = {
+            android.Manifest.permission.INTERACT_ACROSS_USERS,
+            android.Manifest.permission.BROADCAST_STICKY
+    })
+    public abstract void removeStickyBroadcastAsUser(@RequiresPermission Intent intent,
+            UserHandle user);
+
+    /**
+     * Register a BroadcastReceiver to be run in the main activity thread.  The
+     * <var>receiver</var> will be called with any broadcast Intent that
+     * matches <var>filter</var>, in the main application thread.
+     *
+     * <p>The system may broadcast Intents that are "sticky" -- these stay
+     * around after the broadcast has finished, to be sent to any later
+     * registrations. If your IntentFilter matches one of these sticky
+     * Intents, that Intent will be returned by this function
+     * <strong>and</strong> sent to your <var>receiver</var> as if it had just
+     * been broadcast.
+     *
+     * <p>There may be multiple sticky Intents that match <var>filter</var>,
+     * in which case each of these will be sent to <var>receiver</var>.  In
+     * this case, only one of these can be returned directly by the function;
+     * which of these that is returned is arbitrarily decided by the system.
+     *
+     * <p>If you know the Intent your are registering for is sticky, you can
+     * supply null for your <var>receiver</var>.  In this case, no receiver is
+     * registered -- the function simply returns the sticky Intent that
+     * matches <var>filter</var>.  In the case of multiple matches, the same
+     * rules as described above apply.
+     *
+     * <p>See {@link BroadcastReceiver} for more information on Intent broadcasts.
+     *
+     * <p>As of {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH}, receivers
+     * registered with this method will correctly respect the
+     * {@link Intent#setPackage(String)} specified for an Intent being broadcast.
+     * Prior to that, it would be ignored and delivered to all matching registered
+     * receivers.  Be careful if using this for security.</p>
+     *
+     * <p class="note">Note: this method <em>cannot be called from a
+     * {@link BroadcastReceiver} component;</em> that is, from a BroadcastReceiver
+     * that is declared in an application's manifest.  It is okay, however, to call
+     * this method from another BroadcastReceiver that has itself been registered
+     * at run time with {@link #registerReceiver}, since the lifetime of such a
+     * registered BroadcastReceiver is tied to the object that registered it.</p>
+     *
+     * @param receiver The BroadcastReceiver to handle the broadcast.
+     * @param filter Selects the Intent broadcasts to be received.
+     *
+     * @return The first sticky intent found that matches <var>filter</var>,
+     *         or null if there are none.
+     *
+     * @see #registerReceiver(BroadcastReceiver, IntentFilter, String, Handler)
+     * @see #sendBroadcast
+     * @see #unregisterReceiver
+     */
+    @Nullable
+    public abstract Intent registerReceiver(@Nullable BroadcastReceiver receiver,
+                                            IntentFilter filter);
+
+    /**
+     * Register to receive intent broadcasts, with the receiver optionally being
+     * exposed to Instant Apps. See
+     * {@link #registerReceiver(BroadcastReceiver, IntentFilter)} for more
+     * information. By default Instant Apps cannot interact with receivers in other
+     * applications, this allows you to expose a receiver that Instant Apps can
+     * interact with.
+     *
+     * <p>See {@link BroadcastReceiver} for more information on Intent broadcasts.
+     *
+     * <p>As of {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH}, receivers
+     * registered with this method will correctly respect the
+     * {@link Intent#setPackage(String)} specified for an Intent being broadcast.
+     * Prior to that, it would be ignored and delivered to all matching registered
+     * receivers.  Be careful if using this for security.</p>
+     *
+     * @param receiver The BroadcastReceiver to handle the broadcast.
+     * @param filter Selects the Intent broadcasts to be received.
+     * @param flags Additional options for the receiver. May be 0 or
+     *      {@link #RECEIVER_VISIBLE_TO_INSTANT_APPS}.
+     *
+     * @return The first sticky intent found that matches <var>filter</var>,
+     *         or null if there are none.
+     *
+     * @see #registerReceiver(BroadcastReceiver, IntentFilter)
+     * @see #sendBroadcast
+     * @see #unregisterReceiver
+     */
+    @Nullable
+    public abstract Intent registerReceiver(@Nullable BroadcastReceiver receiver,
+                                            IntentFilter filter,
+                                            @RegisterReceiverFlags int flags);
+
+    /**
+     * Register to receive intent broadcasts, to run in the context of
+     * <var>scheduler</var>.  See
+     * {@link #registerReceiver(BroadcastReceiver, IntentFilter)} for more
+     * information.  This allows you to enforce permissions on who can
+     * broadcast intents to your receiver, or have the receiver run in
+     * a different thread than the main application thread.
+     *
+     * <p>See {@link BroadcastReceiver} for more information on Intent broadcasts.
+     *
+     * <p>As of {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH}, receivers
+     * registered with this method will correctly respect the
+     * {@link Intent#setPackage(String)} specified for an Intent being broadcast.
+     * Prior to that, it would be ignored and delivered to all matching registered
+     * receivers.  Be careful if using this for security.</p>
+     *
+     * @param receiver The BroadcastReceiver to handle the broadcast.
+     * @param filter Selects the Intent broadcasts to be received.
+     * @param broadcastPermission String naming a permissions that a
+     *      broadcaster must hold in order to send an Intent to you.  If null,
+     *      no permission is required.
+     * @param scheduler Handler identifying the thread that will receive
+     *      the Intent.  If null, the main thread of the process will be used.
+     *
+     * @return The first sticky intent found that matches <var>filter</var>,
+     *         or null if there are none.
+     *
+     * @see #registerReceiver(BroadcastReceiver, IntentFilter)
+     * @see #sendBroadcast
+     * @see #unregisterReceiver
+     */
+    @Nullable
+    public abstract Intent registerReceiver(BroadcastReceiver receiver,
+            IntentFilter filter, @Nullable String broadcastPermission,
+            @Nullable Handler scheduler);
+
+    /**
+     * Register to receive intent broadcasts, to run in the context of
+     * <var>scheduler</var>. See
+     * {@link #registerReceiver(BroadcastReceiver, IntentFilter, int)} and
+     * {@link #registerReceiver(BroadcastReceiver, IntentFilter, String, Handler)}
+     * for more information.
+     *
+     * <p>See {@link BroadcastReceiver} for more information on Intent broadcasts.
+     *
+     * <p>As of {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH}, receivers
+     * registered with this method will correctly respect the
+     * {@link Intent#setPackage(String)} specified for an Intent being broadcast.
+     * Prior to that, it would be ignored and delivered to all matching registered
+     * receivers.  Be careful if using this for security.</p>
+     *
+     * @param receiver The BroadcastReceiver to handle the broadcast.
+     * @param filter Selects the Intent broadcasts to be received.
+     * @param broadcastPermission String naming a permissions that a
+     *      broadcaster must hold in order to send an Intent to you.  If null,
+     *      no permission is required.
+     * @param scheduler Handler identifying the thread that will receive
+     *      the Intent.  If null, the main thread of the process will be used.
+     * @param flags Additional options for the receiver. May be 0 or
+     *      {@link #RECEIVER_VISIBLE_TO_INSTANT_APPS}.
+     *
+     * @return The first sticky intent found that matches <var>filter</var>,
+     *         or null if there are none.
+     *
+     * @see #registerReceiver(BroadcastReceiver, IntentFilter, int)
+     * @see #registerReceiver(BroadcastReceiver, IntentFilter, String, Handler)
+     * @see #sendBroadcast
+     * @see #unregisterReceiver
+     */
+    @Nullable
+    public abstract Intent registerReceiver(BroadcastReceiver receiver,
+            IntentFilter filter, @Nullable String broadcastPermission,
+            @Nullable Handler scheduler, @RegisterReceiverFlags int flags);
+
+    /**
+     * Same as {@link #registerReceiver(BroadcastReceiver, IntentFilter, String, Handler)}
+     * but this receiver will receive broadcasts that are sent to all users. The receiver can
+     * use {@link BroadcastReceiver#getSendingUser} to determine on which user the broadcast
+     * was sent.
+     *
+     * @param receiver The BroadcastReceiver to handle the broadcast.
+     * @param filter Selects the Intent broadcasts to be received.
+     * @param broadcastPermission String naming a permissions that a
+     *      broadcaster must hold in order to send an Intent to you. If {@code null},
+     *      no permission is required.
+     * @param scheduler Handler identifying the thread that will receive
+     *      the Intent. If {@code null}, the main thread of the process will be used.
+     *
+     * @return The first sticky intent found that matches <var>filter</var>,
+     *         or {@code null} if there are none.
+     *
+     * @see #registerReceiver(BroadcastReceiver, IntentFilter, String, Handler)
+     * @see #sendBroadcast
+     * @see #unregisterReceiver
+     * @hide
+     */
+    @Nullable
+    @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+    @SystemApi
+    public Intent registerReceiverForAllUsers(@Nullable BroadcastReceiver receiver,
+            @NonNull IntentFilter filter, @Nullable String broadcastPermission,
+            @Nullable Handler scheduler) {
+        throw new RuntimeException("Not implemented. Must override in a subclass.");
+    }
+
+    /**
+     * @hide
+     * Same as {@link #registerReceiver(BroadcastReceiver, IntentFilter, String, Handler)
+     * but for a specific user.  This receiver will receiver broadcasts that
+     * are sent to the requested user.
+     *
+     * @param receiver The BroadcastReceiver to handle the broadcast.
+     * @param user UserHandle to send the intent to.
+     * @param filter Selects the Intent broadcasts to be received.
+     * @param broadcastPermission String naming a permissions that a
+     *      broadcaster must hold in order to send an Intent to you.  If null,
+     *      no permission is required.
+     * @param scheduler Handler identifying the thread that will receive
+     *      the Intent.  If null, the main thread of the process will be used.
+     *
+     * @return The first sticky intent found that matches <var>filter</var>,
+     *         or null if there are none.
+     *
+     * @see #registerReceiver(BroadcastReceiver, IntentFilter, String, Handler)
+     * @see #sendBroadcast
+     * @see #unregisterReceiver
+     */
+    @Nullable
+    @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+    @UnsupportedAppUsage
+    public abstract Intent registerReceiverAsUser(BroadcastReceiver receiver,
+            UserHandle user, IntentFilter filter, @Nullable String broadcastPermission,
+            @Nullable Handler scheduler);
+
+    /**
+     * Unregister a previously registered BroadcastReceiver.  <em>All</em>
+     * filters that have been registered for this BroadcastReceiver will be
+     * removed.
+     *
+     * @param receiver The BroadcastReceiver to unregister.
+     *
+     * @see #registerReceiver
+     */
+    public abstract void unregisterReceiver(BroadcastReceiver receiver);
+
+    /**
+     * Request that a given application service be started.  The Intent
+     * should either contain the complete class name of a specific service
+     * implementation to start, or a specific package name to target.  If the
+     * Intent is less specified, it logs a warning about this.  In this case any of the
+     * multiple matching services may be used.  If this service
+     * is not already running, it will be instantiated and started (creating a
+     * process for it if needed); if it is running then it remains running.
+     *
+     * <p>Every call to this method will result in a corresponding call to
+     * the target service's {@link android.app.Service#onStartCommand} method,
+     * with the <var>intent</var> given here.  This provides a convenient way
+     * to submit jobs to a service without having to bind and call on to its
+     * interface.
+     *
+     * <p>Using startService() overrides the default service lifetime that is
+     * managed by {@link #bindService}: it requires the service to remain
+     * running until {@link #stopService} is called, regardless of whether
+     * any clients are connected to it.  Note that calls to startService()
+     * do not nest: no matter how many times you call startService(),
+     * a single call to {@link #stopService} will stop it.
+     *
+     * <p>The system attempts to keep running services around as much as
+     * possible.  The only time they should be stopped is if the current
+     * foreground application is using so many resources that the service needs
+     * to be killed.  If any errors happen in the service's process, it will
+     * automatically be restarted.
+     *
+     * <p>This function will throw {@link SecurityException} if you do not
+     * have permission to start the given service.
+     *
+     * <p class="note"><strong>Note:</strong> Each call to startService()
+     * results in significant work done by the system to manage service
+     * lifecycle surrounding the processing of the intent, which can take
+     * multiple milliseconds of CPU time. Due to this cost, startService()
+     * should not be used for frequent intent delivery to a service, and only
+     * for scheduling significant work. Use {@link #bindService bound services}
+     * for high frequency calls.
+     * </p>
+     *
+     * @param service Identifies the service to be started.  The Intent must be
+     *      fully explicit (supplying a component name).  Additional values
+     *      may be included in the Intent extras to supply arguments along with
+     *      this specific start call.
+     *
+     * @return If the service is being started or is already running, the
+     * {@link ComponentName} of the actual service that was started is
+     * returned; else if the service does not exist null is returned.
+     *
+     * @throws SecurityException If the caller does not have permission to access the service
+     * or the service can not be found.
+     * @throws IllegalStateException If the application is in a state where the service
+     * can not be started (such as not in the foreground in a state when services are allowed).
+     *
+     * @see #stopService
+     * @see #bindService
+     */
+    @Nullable
+    public abstract ComponentName startService(Intent service);
+
+    /**
+     * Similar to {@link #startService(Intent)}, but with an implicit promise that the
+     * Service will call {@link android.app.Service#startForeground(int, android.app.Notification)
+     * startForeground(int, android.app.Notification)} once it begins running.  The service is given
+     * an amount of time comparable to the ANR interval to do this, otherwise the system
+     * will automatically stop the service and declare the app ANR.
+     *
+     * <p>Unlike the ordinary {@link #startService(Intent)}, this method can be used
+     * at any time, regardless of whether the app hosting the service is in a foreground
+     * state.
+     *
+     * @param service Identifies the service to be started.  The Intent must be
+     *      fully explicit (supplying a component name).  Additional values
+     *      may be included in the Intent extras to supply arguments along with
+     *      this specific start call.
+     *
+     * @return If the service is being started or is already running, the
+     * {@link ComponentName} of the actual service that was started is
+     * returned; else if the service does not exist null is returned.
+     *
+     * @throws SecurityException If the caller does not have permission to access the service
+     * or the service can not be found.
+     *
+     * @see #stopService
+     * @see android.app.Service#startForeground(int, android.app.Notification)
+     */
+    @Nullable
+    public abstract ComponentName startForegroundService(Intent service);
+
+    /**
+     * @hide like {@link #startForegroundService(Intent)} but for a specific user.
+     */
+    @Nullable
+    @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
+    public abstract ComponentName startForegroundServiceAsUser(Intent service, UserHandle user);
+
+    /**
+     * Request that a given application service be stopped.  If the service is
+     * not running, nothing happens.  Otherwise it is stopped.  Note that calls
+     * to startService() are not counted -- this stops the service no matter
+     * how many times it was started.
+     *
+     * <p>Note that if a stopped service still has {@link ServiceConnection}
+     * objects bound to it with the {@link #BIND_AUTO_CREATE} set, it will
+     * not be destroyed until all of these bindings are removed.  See
+     * the {@link android.app.Service} documentation for more details on a
+     * service's lifecycle.
+     *
+     * <p>This function will throw {@link SecurityException} if you do not
+     * have permission to stop the given service.
+     *
+     * @param service Description of the service to be stopped.  The Intent must be either
+     *      fully explicit (supplying a component name) or specify a specific package
+     *      name it is targeted to.
+     *
+     * @return If there is a service matching the given Intent that is already
+     * running, then it is stopped and {@code true} is returned; else {@code false} is returned.
+     *
+     * @throws SecurityException If the caller does not have permission to access the service
+     * or the service can not be found.
+     * @throws IllegalStateException If the application is in a state where the service
+     * can not be started (such as not in the foreground in a state when services are allowed).
+     *
+     * @see #startService
+     */
+    public abstract boolean stopService(Intent service);
+
+    /**
+     * @hide like {@link #startService(Intent)} but for a specific user.
+     */
+    @Nullable
+    @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
+    @UnsupportedAppUsage
+    public abstract ComponentName startServiceAsUser(Intent service, UserHandle user);
+
+    /**
+     * @hide like {@link #stopService(Intent)} but for a specific user.
+     */
+    @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
+    public abstract boolean stopServiceAsUser(Intent service, UserHandle user);
+
+    /**
+     * Connect to an application service, creating it if needed.  This defines
+     * a dependency between your application and the service.  The given
+     * <var>conn</var> will receive the service object when it is created and be
+     * told if it dies and restarts.  The service will be considered required
+     * by the system only for as long as the calling context exists.  For
+     * example, if this Context is an Activity that is stopped, the service will
+     * not be required to continue running until the Activity is resumed.
+     *
+     * <p>If the service does not support binding, it may return {@code null} from
+     * its {@link android.app.Service#onBind(Intent) onBind()} method.  If it does, then
+     * the ServiceConnection's
+     * {@link ServiceConnection#onNullBinding(ComponentName) onNullBinding()} method
+     * will be invoked instead of
+     * {@link ServiceConnection#onServiceConnected(ComponentName, IBinder) onServiceConnected()}.
+     *
+     * <p>This method will throw {@link SecurityException} if the calling app does not
+     * have permission to bind to the given service.
+     *
+     * <p class="note">Note: this method <em>cannot be called from a
+     * {@link BroadcastReceiver} component</em>.  A pattern you can use to
+     * communicate from a BroadcastReceiver to a Service is to call
+     * {@link #startService} with the arguments containing the command to be
+     * sent, with the service calling its
+     * {@link android.app.Service#stopSelf(int)} method when done executing
+     * that command.  See the API demo App/Service/Service Start Arguments
+     * Controller for an illustration of this.  It is okay, however, to use
+     * this method from a BroadcastReceiver that has been registered with
+     * {@link #registerReceiver}, since the lifetime of this BroadcastReceiver
+     * is tied to another object (the one that registered it).</p>
+     *
+     * @param service Identifies the service to connect to.  The Intent must
+     *      specify an explicit component name.
+     * @param conn Receives information as the service is started and stopped.
+     *      This must be a valid ServiceConnection object; it must not be null.
+     * @param flags Operation options for the binding.  May be 0,
+     *          {@link #BIND_AUTO_CREATE}, {@link #BIND_DEBUG_UNBIND},
+     *          {@link #BIND_NOT_FOREGROUND}, {@link #BIND_ABOVE_CLIENT},
+     *          {@link #BIND_ALLOW_OOM_MANAGEMENT}, {@link #BIND_WAIVE_PRIORITY}.
+     *          {@link #BIND_IMPORTANT}, or
+     *          {@link #BIND_ADJUST_WITH_ACTIVITY}.
+     * @return {@code true} if the system is in the process of bringing up a
+     *         service that your client has permission to bind to; {@code false}
+     *         if the system couldn't find the service or if your client doesn't
+     *         have permission to bind to it. If this value is {@code true}, you
+     *         should later call {@link #unbindService} to release the
+     *         connection.
+     *
+     * @throws SecurityException If the caller does not have permission to access the service
+     * or the service can not be found.
+     *
+     * @see #unbindService
+     * @see #startService
+     * @see #BIND_AUTO_CREATE
+     * @see #BIND_DEBUG_UNBIND
+     * @see #BIND_NOT_FOREGROUND
+     * @see #BIND_ABOVE_CLIENT
+     * @see #BIND_ALLOW_OOM_MANAGEMENT
+     * @see #BIND_WAIVE_PRIORITY
+     * @see #BIND_IMPORTANT
+     * @see #BIND_ADJUST_WITH_ACTIVITY
+     */
+    public abstract boolean bindService(@RequiresPermission Intent service,
+            @NonNull ServiceConnection conn, @BindServiceFlags int flags);
+
+    /**
+     * Same as {@link #bindService(Intent, ServiceConnection, int)} with executor to control
+     * ServiceConnection callbacks.
+     * @param executor Callbacks on ServiceConnection will be called on executor. Must use same
+     *      instance for the same instance of ServiceConnection.
+     */
+    public boolean bindService(@RequiresPermission @NonNull Intent service,
+            @BindServiceFlags int flags, @NonNull @CallbackExecutor Executor executor,
+            @NonNull ServiceConnection conn) {
+        throw new RuntimeException("Not implemented. Must override in a subclass.");
+    }
+
+    /**
+     * Variation of {@link #bindService} that, in the specific case of isolated
+     * services, allows the caller to generate multiple instances of a service
+     * from a single component declaration.  In other words, you can use this to bind
+     * to a service that has specified {@link android.R.attr#isolatedProcess} and, in
+     * addition to the existing behavior of running in an isolated process, you can
+     * also through the arguments here have the system bring up multiple concurrent
+     * processes hosting their own instances of that service.  The <var>instanceName</var>
+     * you provide here identifies the different instances, and you can use
+     * {@link #updateServiceGroup(ServiceConnection, int, int)} to tell the system how it
+     * should manage each of these instances.
+     *
+     * @param service Identifies the service to connect to.  The Intent must
+     *      specify an explicit component name.
+     * @param flags Operation options for the binding as per {@link #bindService}.
+     * @param instanceName Unique identifier for the service instance.  Each unique
+     *      name here will result in a different service instance being created.  Identifiers
+     *      must only contain ASCII letters, digits, underscores, and periods.
+     * @return Returns success of binding as per {@link #bindService}.
+     * @param executor Callbacks on ServiceConnection will be called on executor.
+     *      Must use same instance for the same instance of ServiceConnection.
+     * @param conn Receives information as the service is started and stopped.
+     *      This must be a valid ServiceConnection object; it must not be null.
+     *
+     * @throws SecurityException If the caller does not have permission to access the service
+     * @throws IllegalArgumentException If the instanceName is invalid.
+     *
+     * @see #bindService
+     * @see #updateServiceGroup
+     * @see android.R.attr#isolatedProcess
+     */
+    public boolean bindIsolatedService(@RequiresPermission @NonNull Intent service,
+            @BindServiceFlags int flags, @NonNull String instanceName,
+            @NonNull @CallbackExecutor Executor executor, @NonNull ServiceConnection conn) {
+        throw new RuntimeException("Not implemented. Must override in a subclass.");
+    }
+
+    /**
+     * Binds to a service in the given {@code user} in the same manner as
+     * {@link #bindService(Intent, ServiceConnection, int)}.
+     *
+     * <p>If the given {@code user} is in the same profile group and the target package is the
+     * same as the caller, {@code android.Manifest.permission.INTERACT_ACROSS_PROFILES} is
+     * sufficient. Otherwise, requires {@code android.Manifest.permission.INTERACT_ACROSS_USERS}
+     * for interacting with other users.
+     *
+     * @param service Identifies the service to connect to.  The Intent must
+     *      specify an explicit component name.
+     * @param conn Receives information as the service is started and stopped.
+     *      This must be a valid ServiceConnection object; it must not be null.
+     * @param flags Operation options for the binding.  May be 0,
+     *          {@link #BIND_AUTO_CREATE}, {@link #BIND_DEBUG_UNBIND},
+     *          {@link #BIND_NOT_FOREGROUND}, {@link #BIND_ABOVE_CLIENT},
+     *          {@link #BIND_ALLOW_OOM_MANAGEMENT}, {@link #BIND_WAIVE_PRIORITY}.
+     *          {@link #BIND_IMPORTANT}, or
+     *          {@link #BIND_ADJUST_WITH_ACTIVITY}.
+     * @return {@code true} if the system is in the process of bringing up a
+     *         service that your client has permission to bind to; {@code false}
+     *         if the system couldn't find the service. If this value is {@code true}, you
+     *         should later call {@link #unbindService} to release the
+     *         connection.
+     *
+     * @throws SecurityException if the client does not have the required permission to bind.
+     */
+    @SuppressWarnings("unused")
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.INTERACT_ACROSS_USERS,
+            android.Manifest.permission.INTERACT_ACROSS_PROFILES
+    })
+    public boolean bindServiceAsUser(
+            @NonNull @RequiresPermission Intent service, @NonNull ServiceConnection conn, int flags,
+            @NonNull UserHandle user) {
+        throw new RuntimeException("Not implemented. Must override in a subclass.");
+    }
+
+    /**
+     * Same as {@link #bindServiceAsUser(Intent, ServiceConnection, int, UserHandle)}, but with an
+     * explicit non-null Handler to run the ServiceConnection callbacks on.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
+    @UnsupportedAppUsage(trackingBug = 136728678)
+    public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags,
+            Handler handler, UserHandle user) {
+        throw new RuntimeException("Not implemented. Must override in a subclass.");
+    }
+
+    /**
+     * For a service previously bound with {@link #bindService} or a related method, change
+     * how the system manages that service's process in relation to other processes.  This
+     * doesn't modify the original bind flags that were passed in when binding, but adjusts
+     * how the process will be managed in some cases based on those flags.  Currently only
+     * works on isolated processes (will be ignored for non-isolated processes).
+     *
+     * <p>Note that this call does not take immediate effect, but will be applied the next
+     * time the impacted process is adjusted for some other reason.  Typically you would
+     * call this before then calling a new {@link #bindIsolatedService} on the service
+     * of interest, with that binding causing the process to be shuffled accordingly.</p>
+     *
+     * @param conn The connection interface previously supplied to bindService().  This
+     *             parameter must not be null.
+     * @param group A group to put this connection's process in.  Upon calling here, this
+     *              will override any previous group that was set for that process.  The group
+     *              tells the system about processes that are logically grouped together, so
+     *              should be managed as one unit of importance (such as when being considered
+     *              a recently used app).  All processes in the same app with the same group
+     *              are considered to be related.  Supplying 0 reverts to the default behavior
+     *              of not grouping.
+     * @param importance Additional importance of the processes within a group.  Upon calling
+     *                   here, this will override any previous importance that was set for that
+     *                   process.  The most important process is 0, and higher values are
+     *                   successively less important.  You can view this as describing how
+     *                   to order the processes in an array, with the processes at the end of
+     *                   the array being the least important.  This value has no meaning besides
+     *                   indicating how processes should be ordered in that array one after the
+     *                   other.  This provides a way to fine-tune the system's process killing,
+     *                   guiding it to kill processes at the end of the array first.
+     *
+     * @see #bindIsolatedService
+     */
+    public void updateServiceGroup(@NonNull ServiceConnection conn, int group,
+            int importance) {
+        throw new RuntimeException("Not implemented. Must override in a subclass.");
+    }
+
+    /**
+     * Disconnect from an application service.  You will no longer receive
+     * calls as the service is restarted, and the service is now allowed to
+     * stop at any time.
+     *
+     * @param conn The connection interface previously supplied to
+     *             bindService().  This parameter must not be null.
+     *
+     * @see #bindService
+     */
+    public abstract void unbindService(@NonNull ServiceConnection conn);
+
+    /**
+     * Start executing an {@link android.app.Instrumentation} class.  The given
+     * Instrumentation component will be run by killing its target application
+     * (if currently running), starting the target process, instantiating the
+     * instrumentation component, and then letting it drive the application.
+     *
+     * <p>This function is not synchronous -- it returns as soon as the
+     * instrumentation has started and while it is running.
+     *
+     * <p>Instrumentation is normally only allowed to run against a package
+     * that is either unsigned or signed with a signature that the
+     * the instrumentation package is also signed with (ensuring the target
+     * trusts the instrumentation).
+     *
+     * @param className Name of the Instrumentation component to be run.
+     * @param profileFile Optional path to write profiling data as the
+     * instrumentation runs, or null for no profiling.
+     * @param arguments Additional optional arguments to pass to the
+     * instrumentation, or null.
+     *
+     * @return {@code true} if the instrumentation was successfully started,
+     * else {@code false} if it could not be found.
+     */
+    public abstract boolean startInstrumentation(@NonNull ComponentName className,
+            @Nullable String profileFile, @Nullable Bundle arguments);
+
+    /** @hide */
+    @StringDef(suffix = { "_SERVICE" }, value = {
+            POWER_SERVICE,
+            WINDOW_SERVICE,
+            LAYOUT_INFLATER_SERVICE,
+            ACCOUNT_SERVICE,
+            ACTIVITY_SERVICE,
+            ALARM_SERVICE,
+            NOTIFICATION_SERVICE,
+            ACCESSIBILITY_SERVICE,
+            CAPTIONING_SERVICE,
+            KEYGUARD_SERVICE,
+            LOCATION_SERVICE,
+            //@hide: COUNTRY_DETECTOR,
+            SEARCH_SERVICE,
+            SENSOR_SERVICE,
+            SENSOR_PRIVACY_SERVICE,
+            STORAGE_SERVICE,
+            STORAGE_STATS_SERVICE,
+            WALLPAPER_SERVICE,
+            TIME_ZONE_RULES_MANAGER_SERVICE,
+            VIBRATOR_SERVICE,
+            //@hide: STATUS_BAR_SERVICE,
+            CONNECTIVITY_SERVICE,
+            //@hide: IP_MEMORY_STORE_SERVICE,
+            IPSEC_SERVICE,
+            VPN_MANAGEMENT_SERVICE,
+            TEST_NETWORK_SERVICE,
+            //@hide: UPDATE_LOCK_SERVICE,
+            //@hide: NETWORKMANAGEMENT_SERVICE,
+            NETWORK_STATS_SERVICE,
+            //@hide: NETWORK_POLICY_SERVICE,
+            WIFI_SERVICE,
+            WIFI_AWARE_SERVICE,
+            WIFI_P2P_SERVICE,
+            WIFI_SCANNING_SERVICE,
+            //@hide: LOWPAN_SERVICE,
+            //@hide: WIFI_RTT_SERVICE,
+            //@hide: ETHERNET_SERVICE,
+            WIFI_RTT_RANGING_SERVICE,
+            NSD_SERVICE,
+            AUDIO_SERVICE,
+            AUTH_SERVICE,
+            FINGERPRINT_SERVICE,
+            //@hide: FACE_SERVICE,
+            BIOMETRIC_SERVICE,
+            MEDIA_ROUTER_SERVICE,
+            TELEPHONY_SERVICE,
+            TELEPHONY_SUBSCRIPTION_SERVICE,
+            CARRIER_CONFIG_SERVICE,
+            EUICC_SERVICE,
+            //@hide: MMS_SERVICE,
+            TELECOM_SERVICE,
+            CLIPBOARD_SERVICE,
+            INPUT_METHOD_SERVICE,
+            TEXT_SERVICES_MANAGER_SERVICE,
+            TEXT_CLASSIFICATION_SERVICE,
+            APPWIDGET_SERVICE,
+            //@hide: VOICE_INTERACTION_MANAGER_SERVICE,
+            //@hide: BACKUP_SERVICE,
+            ROLLBACK_SERVICE,
+            DROPBOX_SERVICE,
+            //@hide: DEVICE_IDLE_CONTROLLER,
+            //@hide: POWER_WHITELIST_MANAGER,
+            DEVICE_POLICY_SERVICE,
+            UI_MODE_SERVICE,
+            DOWNLOAD_SERVICE,
+            NFC_SERVICE,
+            BLUETOOTH_SERVICE,
+            //@hide: SIP_SERVICE,
+            USB_SERVICE,
+            LAUNCHER_APPS_SERVICE,
+            //@hide: SERIAL_SERVICE,
+            //@hide: HDMI_CONTROL_SERVICE,
+            INPUT_SERVICE,
+            DISPLAY_SERVICE,
+            //@hide COLOR_DISPLAY_SERVICE,
+            USER_SERVICE,
+            RESTRICTIONS_SERVICE,
+            APP_OPS_SERVICE,
+            ROLE_SERVICE,
+            //@hide ROLE_CONTROLLER_SERVICE,
+            CAMERA_SERVICE,
+            //@hide: PLATFORM_COMPAT_SERVICE,
+            //@hide: PLATFORM_COMPAT_NATIVE_SERVICE,
+            PRINT_SERVICE,
+            CONSUMER_IR_SERVICE,
+            //@hide: TRUST_SERVICE,
+            TV_INPUT_SERVICE,
+            //@hide: TV_TUNER_RESOURCE_MGR_SERVICE,
+            //@hide: NETWORK_SCORE_SERVICE,
+            USAGE_STATS_SERVICE,
+            MEDIA_SESSION_SERVICE,
+            BATTERY_SERVICE,
+            JOB_SCHEDULER_SERVICE,
+            //@hide: PERSISTENT_DATA_BLOCK_SERVICE,
+            //@hide: OEM_LOCK_SERVICE,
+            MEDIA_PROJECTION_SERVICE,
+            MIDI_SERVICE,
+            RADIO_SERVICE,
+            HARDWARE_PROPERTIES_SERVICE,
+            //@hide: SOUND_TRIGGER_SERVICE,
+            SHORTCUT_SERVICE,
+            //@hide: CONTEXTHUB_SERVICE,
+            SYSTEM_HEALTH_SERVICE,
+            //@hide: INCIDENT_SERVICE,
+            //@hide: INCIDENT_COMPANION_SERVICE,
+            //@hide: STATS_COMPANION_SERVICE,
+            COMPANION_DEVICE_SERVICE,
+            CROSS_PROFILE_APPS_SERVICE,
+            //@hide: SYSTEM_UPDATE_SERVICE,
+            //@hide: TIME_DETECTOR_SERVICE,
+            //@hide: TIME_ZONE_DETECTOR_SERVICE,
+            PERMISSION_SERVICE,
+            LIGHTS_SERVICE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ServiceName {}
+
+    /**
+     * Return the handle to a system-level service by name. The class of the
+     * returned object varies by the requested name. Currently available names
+     * are:
+     *
+     * <dl>
+     *  <dt> {@link #WINDOW_SERVICE} ("window")
+     *  <dd> The top-level window manager in which you can place custom
+     *  windows.  The returned object is a {@link android.view.WindowManager}. Must only be obtained
+     *  from a visual context such as Activity or a Context created with
+     *  {@link #createWindowContext(int, Bundle)}, which are adjusted to the configuration and
+     *  visual bounds of an area on screen.
+     *  <dt> {@link #LAYOUT_INFLATER_SERVICE} ("layout_inflater")
+     *  <dd> A {@link android.view.LayoutInflater} for inflating layout resources
+     *  in this context. Must only be obtained from a visual context such as Activity or a Context
+     *  created with {@link #createWindowContext(int, Bundle)}, which are adjusted to the
+     *  configuration and visual bounds of an area on screen.
+     *  <dt> {@link #ACTIVITY_SERVICE} ("activity")
+     *  <dd> A {@link android.app.ActivityManager} for interacting with the
+     *  global activity state of the system.
+     *  <dt> {@link #WALLPAPER_SERVICE} ("wallpaper")
+     *  <dd> A {@link android.service.wallpaper.WallpaperService} for accessing wallpapers in this
+     *  context. Must only be obtained from a visual context such as Activity or a Context created
+     *  with {@link #createWindowContext(int, Bundle)}, which are adjusted to the configuration and
+     *  visual bounds of an area on screen.
+     *  <dt> {@link #POWER_SERVICE} ("power")
+     *  <dd> A {@link android.os.PowerManager} for controlling power
+     *  management.
+     *  <dt> {@link #ALARM_SERVICE} ("alarm")
+     *  <dd> A {@link android.app.AlarmManager} for receiving intents at the
+     *  time of your choosing.
+     *  <dt> {@link #NOTIFICATION_SERVICE} ("notification")
+     *  <dd> A {@link android.app.NotificationManager} for informing the user
+     *   of background events.
+     *  <dt> {@link #KEYGUARD_SERVICE} ("keyguard")
+     *  <dd> A {@link android.app.KeyguardManager} for controlling keyguard.
+     *  <dt> {@link #LOCATION_SERVICE} ("location")
+     *  <dd> A {@link android.location.LocationManager} for controlling location
+     *   (e.g., GPS) updates.
+     *  <dt> {@link #SEARCH_SERVICE} ("search")
+     *  <dd> A {@link android.app.SearchManager} for handling search.
+     *  <dt> {@link #VIBRATOR_SERVICE} ("vibrator")
+     *  <dd> A {@link android.os.Vibrator} for interacting with the vibrator
+     *  hardware.
+     *  <dt> {@link #CONNECTIVITY_SERVICE} ("connectivity")
+     *  <dd> A {@link android.net.ConnectivityManager ConnectivityManager} for
+     *  handling management of network connections.
+     *  <dt> {@link #IPSEC_SERVICE} ("ipsec")
+     *  <dd> A {@link android.net.IpSecManager IpSecManager} for managing IPSec on
+     *  sockets and networks.
+     *  <dt> {@link #WIFI_SERVICE} ("wifi")
+     *  <dd> A {@link android.net.wifi.WifiManager WifiManager} for management of Wi-Fi
+     *  connectivity.  On releases before NYC, it should only be obtained from an application
+     *  context, and not from any other derived context to avoid memory leaks within the calling
+     *  process.
+     *  <dt> {@link #WIFI_AWARE_SERVICE} ("wifiaware")
+     *  <dd> A {@link android.net.wifi.aware.WifiAwareManager WifiAwareManager} for management of
+     * Wi-Fi Aware discovery and connectivity.
+     *  <dt> {@link #WIFI_P2P_SERVICE} ("wifip2p")
+     *  <dd> A {@link android.net.wifi.p2p.WifiP2pManager WifiP2pManager} for management of
+     * Wi-Fi Direct connectivity.
+     * <dt> {@link #INPUT_METHOD_SERVICE} ("input_method")
+     * <dd> An {@link android.view.inputmethod.InputMethodManager InputMethodManager}
+     * for management of input methods.
+     * <dt> {@link #UI_MODE_SERVICE} ("uimode")
+     * <dd> An {@link android.app.UiModeManager} for controlling UI modes.
+     * <dt> {@link #DOWNLOAD_SERVICE} ("download")
+     * <dd> A {@link android.app.DownloadManager} for requesting HTTP downloads
+     * <dt> {@link #BATTERY_SERVICE} ("batterymanager")
+     * <dd> A {@link android.os.BatteryManager} for managing battery state
+     * <dt> {@link #JOB_SCHEDULER_SERVICE} ("taskmanager")
+     * <dd>  A {@link android.app.job.JobScheduler} for managing scheduled tasks
+     * <dt> {@link #NETWORK_STATS_SERVICE} ("netstats")
+     * <dd> A {@link android.app.usage.NetworkStatsManager NetworkStatsManager} for querying network
+     * usage statistics.
+     * <dt> {@link #HARDWARE_PROPERTIES_SERVICE} ("hardware_properties")
+     * <dd> A {@link android.os.HardwarePropertiesManager} for accessing hardware properties.
+     * </dl>
+     *
+     * <p>Note:  System services obtained via this API may be closely associated with
+     * the Context in which they are obtained from.  In general, do not share the
+     * service objects between various different contexts (Activities, Applications,
+     * Services, Providers, etc.)
+     *
+     * <p>Note: Instant apps, for which {@link PackageManager#isInstantApp()} returns true,
+     * don't have access to the following system services: {@link #DEVICE_POLICY_SERVICE},
+     * {@link #FINGERPRINT_SERVICE}, {@link #KEYGUARD_SERVICE}, {@link #SHORTCUT_SERVICE},
+     * {@link #USB_SERVICE}, {@link #WALLPAPER_SERVICE}, {@link #WIFI_P2P_SERVICE},
+     * {@link #WIFI_SERVICE}, {@link #WIFI_AWARE_SERVICE}. For these services this method will
+     * return <code>null</code>.  Generally, if you are running as an instant app you should always
+     * check whether the result of this method is {@code null}.
+     *
+     * <p>Note: When implementing this method, keep in mind that new services can be added on newer
+     * Android releases, so if you're looking for just the explicit names mentioned above, make sure
+     * to return {@code null} when you don't recognize the name &mdash; if you throw a
+     * {@link RuntimeException} exception instead, you're app might break on new Android releases.
+     *
+     * @param name The name of the desired service.
+     *
+     * @return The service or {@code null} if the name does not exist.
+     *
+     * @see #WINDOW_SERVICE
+     * @see android.view.WindowManager
+     * @see #LAYOUT_INFLATER_SERVICE
+     * @see android.view.LayoutInflater
+     * @see #ACTIVITY_SERVICE
+     * @see android.app.ActivityManager
+     * @see #POWER_SERVICE
+     * @see android.os.PowerManager
+     * @see #ALARM_SERVICE
+     * @see android.app.AlarmManager
+     * @see #NOTIFICATION_SERVICE
+     * @see android.app.NotificationManager
+     * @see #KEYGUARD_SERVICE
+     * @see android.app.KeyguardManager
+     * @see #LOCATION_SERVICE
+     * @see android.location.LocationManager
+     * @see #SEARCH_SERVICE
+     * @see android.app.SearchManager
+     * @see #SENSOR_SERVICE
+     * @see android.hardware.SensorManager
+     * @see #STORAGE_SERVICE
+     * @see android.os.storage.StorageManager
+     * @see #VIBRATOR_SERVICE
+     * @see android.os.Vibrator
+     * @see #CONNECTIVITY_SERVICE
+     * @see android.net.ConnectivityManager
+     * @see #WIFI_SERVICE
+     * @see android.net.wifi.WifiManager
+     * @see #AUDIO_SERVICE
+     * @see android.media.AudioManager
+     * @see #MEDIA_ROUTER_SERVICE
+     * @see android.media.MediaRouter
+     * @see #TELEPHONY_SERVICE
+     * @see android.telephony.TelephonyManager
+     * @see #TELEPHONY_SUBSCRIPTION_SERVICE
+     * @see android.telephony.SubscriptionManager
+     * @see #CARRIER_CONFIG_SERVICE
+     * @see android.telephony.CarrierConfigManager
+     * @see #EUICC_SERVICE
+     * @see android.telephony.euicc.EuiccManager
+     * @see android.telephony.MmsManager
+     * @see #INPUT_METHOD_SERVICE
+     * @see android.view.inputmethod.InputMethodManager
+     * @see #UI_MODE_SERVICE
+     * @see android.app.UiModeManager
+     * @see #DOWNLOAD_SERVICE
+     * @see android.app.DownloadManager
+     * @see #BATTERY_SERVICE
+     * @see android.os.BatteryManager
+     * @see #JOB_SCHEDULER_SERVICE
+     * @see android.app.job.JobScheduler
+     * @see #NETWORK_STATS_SERVICE
+     * @see android.app.usage.NetworkStatsManager
+     * @see android.os.HardwarePropertiesManager
+     * @see #HARDWARE_PROPERTIES_SERVICE
+     */
+    public abstract @Nullable Object getSystemService(@ServiceName @NonNull String name);
+
+    /**
+     * Return the handle to a system-level service by class.
+     * <p>
+     * Currently available classes are:
+     * {@link android.view.WindowManager}, {@link android.view.LayoutInflater},
+     * {@link android.app.ActivityManager}, {@link android.os.PowerManager},
+     * {@link android.app.AlarmManager}, {@link android.app.NotificationManager},
+     * {@link android.app.KeyguardManager}, {@link android.location.LocationManager},
+     * {@link android.app.SearchManager}, {@link android.os.Vibrator},
+     * {@link android.net.ConnectivityManager},
+     * {@link android.net.wifi.WifiManager},
+     * {@link android.media.AudioManager}, {@link android.media.MediaRouter},
+     * {@link android.telephony.TelephonyManager}, {@link android.telephony.SubscriptionManager},
+     * {@link android.view.inputmethod.InputMethodManager},
+     * {@link android.app.UiModeManager}, {@link android.app.DownloadManager},
+     * {@link android.os.BatteryManager}, {@link android.app.job.JobScheduler},
+     * {@link android.app.usage.NetworkStatsManager}.
+     * </p>
+     *
+     * <p>
+     * Note: System services obtained via this API may be closely associated with
+     * the Context in which they are obtained from.  In general, do not share the
+     * service objects between various different contexts (Activities, Applications,
+     * Services, Providers, etc.)
+     * </p>
+     *
+     * <p>Note: Instant apps, for which {@link PackageManager#isInstantApp()} returns true,
+     * don't have access to the following system services: {@link #DEVICE_POLICY_SERVICE},
+     * {@link #FINGERPRINT_SERVICE}, {@link #KEYGUARD_SERVICE}, {@link #SHORTCUT_SERVICE},
+     * {@link #USB_SERVICE}, {@link #WALLPAPER_SERVICE}, {@link #WIFI_P2P_SERVICE},
+     * {@link #WIFI_SERVICE}, {@link #WIFI_AWARE_SERVICE}. For these services this method will
+     * return {@code null}. Generally, if you are running as an instant app you should always
+     * check whether the result of this method is {@code null}.
+     * </p>
+     *
+     * @param serviceClass The class of the desired service.
+     * @return The service or {@code null} if the class is not a supported system service. Note:
+     * <b>never</b> throw a {@link RuntimeException} if the name is not supported.
+     */
+    @SuppressWarnings("unchecked")
+    public final @Nullable <T> T getSystemService(@NonNull Class<T> serviceClass) {
+        // Because subclasses may override getSystemService(String) we cannot
+        // perform a lookup by class alone.  We must first map the class to its
+        // service name then invoke the string-based method.
+        String serviceName = getSystemServiceName(serviceClass);
+        return serviceName != null ? (T)getSystemService(serviceName) : null;
+    }
+
+    /**
+     * Gets the name of the system-level service that is represented by the specified class.
+     *
+     * @param serviceClass The class of the desired service.
+     * @return The service name or null if the class is not a supported system service.
+     */
+    public abstract @Nullable String getSystemServiceName(@NonNull Class<?> serviceClass);
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.os.PowerManager} for controlling power management,
+     * including "wake locks," which let you keep the device on while
+     * you're running long tasks.
+     */
+    public static final String POWER_SERVICE = "power";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.os.RecoverySystem} for accessing the recovery system
+     * service.
+     *
+     * @see #getSystemService(String)
+     * @hide
+     */
+    public static final String RECOVERY_SERVICE = "recovery";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.os.SystemUpdateManager} for accessing the system update
+     * manager service.
+     *
+     * @see #getSystemService(String)
+     * @hide
+     */
+    @SystemApi
+    public static final String SYSTEM_UPDATE_SERVICE = "system_update";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.view.WindowManager} for accessing the system's window
+     * manager.
+     *
+     * @see #getSystemService(String)
+     * @see android.view.WindowManager
+     */
+    public static final String WINDOW_SERVICE = "window";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.view.LayoutInflater} for inflating layout resources in this
+     * context.
+     *
+     * @see #getSystemService(String)
+     * @see android.view.LayoutInflater
+     */
+    public static final String LAYOUT_INFLATER_SERVICE = "layout_inflater";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.accounts.AccountManager} for receiving intents at a
+     * time of your choosing.
+     *
+     * @see #getSystemService(String)
+     * @see android.accounts.AccountManager
+     */
+    public static final String ACCOUNT_SERVICE = "account";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.app.ActivityManager} for interacting with the global
+     * system state.
+     *
+     * @see #getSystemService(String)
+     * @see android.app.ActivityManager
+     */
+    public static final String ACTIVITY_SERVICE = "activity";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.app.ActivityTaskManager} for interacting with the global system state.
+     *
+     * @see #getSystemService(String)
+     * @see android.app.ActivityTaskManager
+     * @hide
+     */
+    public static final String ACTIVITY_TASK_SERVICE = "activity_task";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.app.UriGrantsManager} for interacting with the global system state.
+     *
+     * @see #getSystemService(String)
+     * @see android.app.UriGrantsManager
+     * @hide
+     */
+    public static final String URI_GRANTS_SERVICE = "uri_grants";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.app.AlarmManager} for receiving intents at a
+     * time of your choosing.
+     *
+     * @see #getSystemService(String)
+     * @see android.app.AlarmManager
+     */
+    public static final String ALARM_SERVICE = "alarm";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.app.NotificationManager} for informing the user of
+     * background events.
+     *
+     * @see #getSystemService(String)
+     * @see android.app.NotificationManager
+     */
+    public static final String NOTIFICATION_SERVICE = "notification";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.view.accessibility.AccessibilityManager} for giving the user
+     * feedback for UI events through the registered event listeners.
+     *
+     * @see #getSystemService(String)
+     * @see android.view.accessibility.AccessibilityManager
+     */
+    public static final String ACCESSIBILITY_SERVICE = "accessibility";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.view.accessibility.CaptioningManager} for obtaining
+     * captioning properties and listening for changes in captioning
+     * preferences.
+     *
+     * @see #getSystemService(String)
+     * @see android.view.accessibility.CaptioningManager
+     */
+    public static final String CAPTIONING_SERVICE = "captioning";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.app.KeyguardManager} for controlling keyguard.
+     *
+     * @see #getSystemService(String)
+     * @see android.app.KeyguardManager
+     */
+    public static final String KEYGUARD_SERVICE = "keyguard";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a {@link
+     * android.location.LocationManager} for controlling location
+     * updates.
+     *
+     * @see #getSystemService(String)
+     * @see android.location.LocationManager
+     */
+    public static final String LOCATION_SERVICE = "location";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.location.CountryDetector} for detecting the country that
+     * the user is in.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    public static final String COUNTRY_DETECTOR = "country_detector";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a {@link
+     * android.app.SearchManager} for handling searches.
+     *
+     * <p>
+     * {@link Configuration#UI_MODE_TYPE_WATCH} does not support
+     * {@link android.app.SearchManager}.
+     *
+     * @see #getSystemService
+     * @see android.app.SearchManager
+     */
+    public static final String SEARCH_SERVICE = "search";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a {@link
+     * android.hardware.SensorManager} for accessing sensors.
+     *
+     * @see #getSystemService(String)
+     * @see android.hardware.SensorManager
+     */
+    public static final String SENSOR_SERVICE = "sensor";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a {@link
+     * android.hardware.SensorPrivacyManager} for accessing sensor privacy
+     * functions.
+     *
+     * @see #getSystemService(String)
+     * @see android.hardware.SensorPrivacyManager
+     *
+     * @hide
+     */
+    public static final String SENSOR_PRIVACY_SERVICE = "sensor_privacy";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a {@link
+     * android.os.storage.StorageManager} for accessing system storage
+     * functions.
+     *
+     * @see #getSystemService(String)
+     * @see android.os.storage.StorageManager
+     */
+    public static final String STORAGE_SERVICE = "storage";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a {@link
+     * android.app.usage.StorageStatsManager} for accessing system storage
+     * statistics.
+     *
+     * @see #getSystemService(String)
+     * @see android.app.usage.StorageStatsManager
+     */
+    public static final String STORAGE_STATS_SERVICE = "storagestats";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * com.android.server.WallpaperService for accessing wallpapers.
+     *
+     * @see #getSystemService(String)
+     */
+    public static final String WALLPAPER_SERVICE = "wallpaper";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a {@link
+     * android.os.Vibrator} for interacting with the vibration hardware.
+     *
+     * @see #getSystemService(String)
+     * @see android.os.Vibrator
+     */
+    public static final String VIBRATOR_SERVICE = "vibrator";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a {@link
+     * android.app.StatusBarManager} for interacting with the status bar.
+     *
+     * @see #getSystemService(String)
+     * @see android.app.StatusBarManager
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    @SuppressLint("ServiceName")
+    public static final String STATUS_BAR_SERVICE = "statusbar";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a {@link
+     * android.net.ConnectivityManager} for handling management of
+     * network connections.
+     *
+     * @see #getSystemService(String)
+     * @see android.net.ConnectivityManager
+     */
+    public static final String CONNECTIVITY_SERVICE = "connectivity";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.net.INetd} for communicating with the network stack
+     * @hide
+     * @see #getSystemService(String)
+     * @hide
+     */
+    @SystemApi
+    public static final String NETD_SERVICE = "netd";
+
+    /**
+     * Use with {@link android.os.ServiceManager.getService()} to retrieve a
+     * {@link INetworkStackConnector} IBinder for communicating with the network stack
+     * @hide
+     * @see NetworkStackClient
+     */
+    public static final String NETWORK_STACK_SERVICE = "network_stack";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a {@link android.net.TetheringManager}
+     * for managing tethering functions.
+     * @hide
+     * @see android.net.TetheringManager
+     */
+    @SystemApi
+    public static final String TETHERING_SERVICE = "tethering";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.net.IpSecManager} for encrypting Sockets or Networks with
+     * IPSec.
+     *
+     * @see #getSystemService(String)
+     */
+    public static final String IPSEC_SERVICE = "ipsec";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a {@link android.net.VpnManager} to
+     * manage profiles for the platform built-in VPN.
+     *
+     * @see #getSystemService(String)
+     */
+    public static final String VPN_MANAGEMENT_SERVICE = "vpn_management";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a {@link
+     * android.net.ConnectivityDiagnosticsManager} for performing network connectivity diagnostics
+     * as well as receiving network connectivity information from the system.
+     *
+     * @see #getSystemService(String)
+     * @see android.net.ConnectivityDiagnosticsManager
+     */
+    public static final String CONNECTIVITY_DIAGNOSTICS_SERVICE = "connectivity_diagnostics";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a {@link
+     * android.net.TestNetworkManager} for building TUNs and limited-use Networks
+     *
+     * @see #getSystemService(String)
+     * @hide
+     */
+    @TestApi public static final String TEST_NETWORK_SERVICE = "test_network";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a {@link
+     * android.os.IUpdateLock} for managing runtime sequences that
+     * must not be interrupted by headless OTA application or similar.
+     *
+     * @hide
+     * @see #getSystemService(String)
+     * @see android.os.UpdateLock
+     */
+    public static final String UPDATE_LOCK_SERVICE = "updatelock";
+
+    /**
+     * Constant for the internal network management service, not really a Context service.
+     * @hide
+     */
+    public static final String NETWORKMANAGEMENT_SERVICE = "network_management";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link com.android.server.slice.SliceManagerService} for managing slices.
+     * @hide
+     * @see #getSystemService(String)
+     */
+    public static final String SLICE_SERVICE = "slice";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a {@link
+     * android.app.usage.NetworkStatsManager} for querying network usage stats.
+     *
+     * @see #getSystemService(String)
+     * @see android.app.usage.NetworkStatsManager
+     */
+    public static final String NETWORK_STATS_SERVICE = "netstats";
+    /** {@hide} */
+    public static final String NETWORK_POLICY_SERVICE = "netpolicy";
+    /** {@hide} */
+    public static final String NETWORK_WATCHLIST_SERVICE = "network_watchlist";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a {@link
+     * android.net.wifi.WifiManager} for handling management of
+     * Wi-Fi access.
+     *
+     * @see #getSystemService(String)
+     * @see android.net.wifi.WifiManager
+     */
+    public static final String WIFI_SERVICE = "wifi";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.net.wifi.wificond.WifiNl80211Manager} for handling management of the
+     * Wi-Fi nl802.11 daemon (wificond).
+     *
+     * @see #getSystemService(String)
+     * @see android.net.wifi.wificond.WifiNl80211Manager
+     * @hide
+     */
+    @SystemApi
+    @SuppressLint("ServiceName")
+    public static final String WIFI_NL80211_SERVICE = "wifinl80211";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a {@link
+     * android.net.wifi.p2p.WifiP2pManager} for handling management of
+     * Wi-Fi peer-to-peer connections.
+     *
+     * @see #getSystemService(String)
+     * @see android.net.wifi.p2p.WifiP2pManager
+     */
+    public static final String WIFI_P2P_SERVICE = "wifip2p";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.net.wifi.aware.WifiAwareManager} for handling management of
+     * Wi-Fi Aware.
+     *
+     * @see #getSystemService(String)
+     * @see android.net.wifi.aware.WifiAwareManager
+     */
+    public static final String WIFI_AWARE_SERVICE = "wifiaware";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a {@link
+     * android.net.wifi.WifiScanner} for scanning the wifi universe
+     *
+     * @see #getSystemService(String)
+     * @see android.net.wifi.WifiScanner
+     * @hide
+     */
+    @SystemApi
+    public static final String WIFI_SCANNING_SERVICE = "wifiscanner";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a {@link
+     * android.net.wifi.RttManager} for ranging devices with wifi
+     *
+     * @see #getSystemService(String)
+     * @see android.net.wifi.RttManager
+     * @hide
+     */
+    @SystemApi
+    @Deprecated
+    public static final String WIFI_RTT_SERVICE = "rttmanager";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a {@link
+     * android.net.wifi.rtt.WifiRttManager} for ranging devices with wifi.
+     *
+     * @see #getSystemService(String)
+     * @see android.net.wifi.rtt.WifiRttManager
+     */
+    public static final String WIFI_RTT_RANGING_SERVICE = "wifirtt";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a {@link
+     * android.net.lowpan.LowpanManager} for handling management of
+     * LoWPAN access.
+     *
+     * @see #getSystemService(String)
+     * @see android.net.lowpan.LowpanManager
+     *
+     * @hide
+     */
+    public static final String LOWPAN_SERVICE = "lowpan";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a {@link android.net.EthernetManager}
+     * for handling management of Ethernet access.
+     *
+     * @see #getSystemService(String)
+     * @see android.net.EthernetManager
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    public static final String ETHERNET_SERVICE = "ethernet";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a {@link
+     * android.net.nsd.NsdManager} for handling management of network service
+     * discovery
+     *
+     * @see #getSystemService(String)
+     * @see android.net.nsd.NsdManager
+     */
+    public static final String NSD_SERVICE = "servicediscovery";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.media.AudioManager} for handling management of volume,
+     * ringer modes and audio routing.
+     *
+     * @see #getSystemService(String)
+     * @see android.media.AudioManager
+     */
+    public static final String AUDIO_SERVICE = "audio";
+
+    /**
+     * AuthService orchestrates biometric and PIN/pattern/password authentication.
+     *
+     * BiometricService was split into two services, AuthService and BiometricService, where
+     * AuthService is the high level service that orchestrates all types of authentication, and
+     * BiometricService is a lower layer responsible only for biometric authentication.
+     *
+     * Ideally we should have renamed BiometricManager to AuthManager, because it logically
+     * corresponds to AuthService. However, because BiometricManager is a public API, we kept
+     * the old name but changed the internal implementation to use AuthService.
+     *
+     * As of now, the AUTH_SERVICE constant is only used to identify the service in
+     * SystemServiceRegistry and SELinux. To obtain the manager for AUTH_SERVICE, one should use
+     * BIOMETRIC_SERVICE with {@link #getSystemService(String)} to retrieve a
+     * {@link android.hardware.biometrics.BiometricManager}
+     *
+     * Map of the two services and their managers:
+     * [Service]            [Manager]
+     * AuthService          BiometricManager
+     * BiometricService     N/A
+     *
+     * @hide
+     */
+    public static final String AUTH_SERVICE = "auth";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.hardware.fingerprint.FingerprintManager} for handling management
+     * of fingerprints.
+     *
+     * @see #getSystemService(String)
+     * @see android.hardware.fingerprint.FingerprintManager
+     */
+    public static final String FINGERPRINT_SERVICE = "fingerprint";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.hardware.face.FaceManager} for handling management
+     * of face authentication.
+     *
+     * @hide
+     * @see #getSystemService
+     * @see android.hardware.face.FaceManager
+     */
+    public static final String FACE_SERVICE = "face";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.hardware.iris.IrisManager} for handling management
+     * of iris authentication.
+     *
+     * @hide
+     * @see #getSystemService
+     * @see android.hardware.iris.IrisManager
+     */
+    public static final String IRIS_SERVICE = "iris";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.hardware.biometrics.BiometricManager} for handling
+     * biometric and PIN/pattern/password authentication.
+     *
+     * @see #getSystemService
+     * @see android.hardware.biometrics.BiometricManager
+     */
+    public static final String BIOMETRIC_SERVICE = "biometric";
+
+    /**
+     * Use with {@link #getSystemService} to retrieve a
+     * {@link android.media.MediaRouter} for controlling and managing
+     * routing of media.
+     *
+     * @see #getSystemService(String)
+     * @see android.media.MediaRouter
+     */
+    public static final String MEDIA_ROUTER_SERVICE = "media_router";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.media.session.MediaSessionManager} for managing media Sessions.
+     *
+     * @see #getSystemService(String)
+     * @see android.media.session.MediaSessionManager
+     */
+    public static final String MEDIA_SESSION_SERVICE = "media_session";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.telephony.TelephonyManager} for handling management the
+     * telephony features of the device.
+     *
+     * @see #getSystemService(String)
+     * @see android.telephony.TelephonyManager
+     */
+    public static final String TELEPHONY_SERVICE = "phone";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.telephony.SubscriptionManager} for handling management the
+     * telephony subscriptions of the device.
+     *
+     * @see #getSystemService(String)
+     * @see android.telephony.SubscriptionManager
+     */
+    public static final String TELEPHONY_SUBSCRIPTION_SERVICE = "telephony_subscription_service";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.telecom.TelecomManager} to manage telecom-related features
+     * of the device.
+     *
+     * @see #getSystemService(String)
+     * @see android.telecom.TelecomManager
+     */
+    public static final String TELECOM_SERVICE = "telecom";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.telephony.CarrierConfigManager} for reading carrier configuration values.
+     *
+     * @see #getSystemService(String)
+     * @see android.telephony.CarrierConfigManager
+     */
+    public static final String CARRIER_CONFIG_SERVICE = "carrier_config";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.telephony.euicc.EuiccManager} to manage the device eUICC (embedded SIM).
+     *
+     * @see #getSystemService(String)
+     * @see android.telephony.euicc.EuiccManager
+     */
+    public static final String EUICC_SERVICE = "euicc";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.telephony.euicc.EuiccCardManager} to access the device eUICC (embedded SIM).
+     *
+     * @see #getSystemService(String)
+     * @see android.telephony.euicc.EuiccCardManager
+     * @hide
+     */
+    @SystemApi
+    public static final String EUICC_CARD_SERVICE = "euicc_card";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.telephony.MmsManager} to send/receive MMS messages.
+     *
+     * @see #getSystemService(String)
+     * @see android.telephony.MmsManager
+     * @hide
+     */
+    public static final String MMS_SERVICE = "mms";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.content.ClipboardManager} for accessing and modifying
+     * the contents of the global clipboard.
+     *
+     * @see #getSystemService(String)
+     * @see android.content.ClipboardManager
+     */
+    public static final String CLIPBOARD_SERVICE = "clipboard";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link TextClassificationManager} for text classification services.
+     *
+     * @see #getSystemService(String)
+     * @see TextClassificationManager
+     */
+    public static final String TEXT_CLASSIFICATION_SERVICE = "textclassification";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link com.android.server.attention.AttentionManagerService} for attention services.
+     *
+     * @see #getSystemService(String)
+     * @see android.server.attention.AttentionManagerService
+     * @hide
+     */
+    public static final String ATTENTION_SERVICE = "attention";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.view.inputmethod.InputMethodManager} for accessing input
+     * methods.
+     *
+     * @see #getSystemService(String)
+     */
+    public static final String INPUT_METHOD_SERVICE = "input_method";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.view.textservice.TextServicesManager} for accessing
+     * text services.
+     *
+     * @see #getSystemService(String)
+     */
+    public static final String TEXT_SERVICES_MANAGER_SERVICE = "textservices";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.appwidget.AppWidgetManager} for accessing AppWidgets.
+     *
+     * @see #getSystemService(String)
+     */
+    public static final String APPWIDGET_SERVICE = "appwidget";
+
+    /**
+     * Official published name of the (internal) voice interaction manager service.
+     *
+     * @hide
+     * @see #getSystemService(String)
+     */
+    public static final String VOICE_INTERACTION_MANAGER_SERVICE = "voiceinteraction";
+
+    /**
+     * Official published name of the (internal) autofill service.
+     *
+     * @hide
+     * @see #getSystemService(String)
+     */
+    public static final String AUTOFILL_MANAGER_SERVICE = "autofill";
+
+    /**
+     * Official published name of the content capture service.
+     *
+     * @hide
+     * @see #getSystemService(String)
+     */
+    @TestApi
+    @SuppressLint("ServiceName")  // TODO: This should be renamed to CONTENT_CAPTURE_SERVICE
+    public static final String CONTENT_CAPTURE_MANAGER_SERVICE = "content_capture";
+
+    /**
+     * Used for getting content selections and classifications for task snapshots.
+     *
+     * @hide
+     * @see #getSystemService(String)
+     */
+    @SystemApi
+    public static final String CONTENT_SUGGESTIONS_SERVICE = "content_suggestions";
+
+    /**
+     * Official published name of the app prediction service.
+     *
+     * <p><b>NOTE: </b> this service is optional; callers of
+     * {@code Context.getSystemServiceName(APP_PREDICTION_SERVICE)} should check for {@code null}.
+     *
+     * @hide
+     * @see #getSystemService(String)
+     */
+    @SystemApi
+    public static final String APP_PREDICTION_SERVICE = "app_prediction";
+
+    /**
+     * Use with {@link #getSystemService(String)} to access the
+     * {@link com.android.server.voiceinteraction.SoundTriggerService}.
+     *
+     * @hide
+     * @see #getSystemService(String)
+     */
+    public static final String SOUND_TRIGGER_SERVICE = "soundtrigger";
+
+    /**
+     * Use with {@link #getSystemService(String)} to access the
+     * {@link com.android.server.soundtrigger_middleware.SoundTriggerMiddlewareService}.
+     *
+     * @hide
+     * @see #getSystemService(String)
+     */
+    public static final String SOUND_TRIGGER_MIDDLEWARE_SERVICE = "soundtrigger_middleware";
+
+    /**
+     * Official published name of the (internal) permission service.
+     *
+     * @see #getSystemService(String)
+     * @hide
+     */
+    @TestApi
+    @SystemApi
+    public static final String PERMISSION_SERVICE = "permission";
+
+    /**
+     * Official published name of the (internal) permission controller service.
+     *
+     * @see #getSystemService(String)
+     * @hide
+     */
+    public static final String PERMISSION_CONTROLLER_SERVICE = "permission_controller";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve an
+     * {@link android.app.backup.IBackupManager IBackupManager} for communicating
+     * with the backup mechanism.
+     * @hide
+     *
+     * @see #getSystemService(String)
+     */
+    @SystemApi
+    public static final String BACKUP_SERVICE = "backup";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve an
+     * {@link android.content.rollback.RollbackManager} for communicating
+     * with the rollback manager
+     *
+     * @see #getSystemService(String)
+     * @hide
+     */
+    @SystemApi @TestApi
+    public static final String ROLLBACK_SERVICE = "rollback";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.os.DropBoxManager} instance for recording
+     * diagnostic logs.
+     * @see #getSystemService(String)
+     */
+    public static final String DROPBOX_SERVICE = "dropbox";
+
+    /**
+     * System service name for the DeviceIdleManager.
+     * @see #getSystemService(String)
+     * @hide
+     */
+    @TestApi
+    @SuppressLint("ServiceName")  // TODO: This should be renamed to DEVICE_IDLE_SERVICE
+    public static final String DEVICE_IDLE_CONTROLLER = "deviceidle";
+
+    /**
+     * System service name for the PowerWhitelistManager.
+     *
+     * @see #getSystemService(String)
+     * @hide
+     */
+    @TestApi
+    @SuppressLint("ServiceName")  // TODO: This should be renamed to POWER_WHITELIST_SERVICE
+    public static final String POWER_WHITELIST_MANAGER = "power_whitelist";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.app.admin.DevicePolicyManager} for working with global
+     * device policy management.
+     *
+     * @see #getSystemService(String)
+     */
+    public static final String DEVICE_POLICY_SERVICE = "device_policy";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.app.UiModeManager} for controlling UI modes.
+     *
+     * @see #getSystemService(String)
+     */
+    public static final String UI_MODE_SERVICE = "uimode";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.app.DownloadManager} for requesting HTTP downloads.
+     *
+     * @see #getSystemService(String)
+     */
+    public static final String DOWNLOAD_SERVICE = "download";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.os.BatteryManager} for managing battery state.
+     *
+     * @see #getSystemService(String)
+     */
+    public static final String BATTERY_SERVICE = "batterymanager";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.nfc.NfcManager} for using NFC.
+     *
+     * @see #getSystemService(String)
+     */
+    public static final String NFC_SERVICE = "nfc";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.bluetooth.BluetoothManager} for using Bluetooth.
+     *
+     * @see #getSystemService(String)
+     */
+    public static final String BLUETOOTH_SERVICE = "bluetooth";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.net.sip.SipManager} for accessing the SIP related service.
+     *
+     * @see #getSystemService(String)
+     */
+    /** @hide */
+    public static final String SIP_SERVICE = "sip";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a {@link
+     * android.hardware.usb.UsbManager} for access to USB devices (as a USB host)
+     * and for controlling this device's behavior as a USB device.
+     *
+     * @see #getSystemService(String)
+     * @see android.hardware.usb.UsbManager
+     */
+    public static final String USB_SERVICE = "usb";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a {@link
+     * Use with {@link #getSystemService} to retrieve a {@link
+     * android.debug.AdbManager} for access to ADB debug functions.
+     *
+     * @see #getSystemService(String)
+     * @see android.debug.AdbManager
+     *
+     * @hide
+     */
+    public static final String ADB_SERVICE = "adb";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a {@link
+     * android.hardware.SerialManager} for access to serial ports.
+     *
+     * @see #getSystemService(String)
+     * @see android.hardware.SerialManager
+     *
+     * @hide
+     */
+    public static final String SERIAL_SERVICE = "serial";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.hardware.hdmi.HdmiControlManager} for controlling and managing
+     * HDMI-CEC protocol.
+     *
+     * @see #getSystemService(String)
+     * @see android.hardware.hdmi.HdmiControlManager
+     * @hide
+     */
+    @SystemApi
+    public static final String HDMI_CONTROL_SERVICE = "hdmi_control";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.hardware.input.InputManager} for interacting with input devices.
+     *
+     * @see #getSystemService(String)
+     * @see android.hardware.input.InputManager
+     */
+    public static final String INPUT_SERVICE = "input";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.hardware.display.DisplayManager} for interacting with display devices.
+     *
+     * @see #getSystemService(String)
+     * @see android.hardware.display.DisplayManager
+     */
+    public static final String DISPLAY_SERVICE = "display";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.hardware.display.ColorDisplayManager} for controlling color transforms.
+     *
+     * @see #getSystemService(String)
+     * @see android.hardware.display.ColorDisplayManager
+     * @hide
+     */
+    public static final String COLOR_DISPLAY_SERVICE = "color_display";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.os.UserManager} for managing users on devices that support multiple users.
+     *
+     * @see #getSystemService(String)
+     * @see android.os.UserManager
+     */
+    public static final String USER_SERVICE = "user";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.content.pm.LauncherApps} for querying and monitoring launchable apps across
+     * profiles of a user.
+     *
+     * @see #getSystemService(String)
+     * @see android.content.pm.LauncherApps
+     */
+    public static final String LAUNCHER_APPS_SERVICE = "launcherapps";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.content.RestrictionsManager} for retrieving application restrictions
+     * and requesting permissions for restricted operations.
+     * @see #getSystemService(String)
+     * @see android.content.RestrictionsManager
+     */
+    public static final String RESTRICTIONS_SERVICE = "restrictions";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.app.AppOpsManager} for tracking application operations
+     * on the device.
+     *
+     * @see #getSystemService(String)
+     * @see android.app.AppOpsManager
+     */
+    public static final String APP_OPS_SERVICE = "appops";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a {@link android.app.role.RoleManager}
+     * for managing roles.
+     *
+     * @see #getSystemService(String)
+     * @see android.app.role.RoleManager
+     */
+    public static final String ROLE_SERVICE = "role";
+
+    /**
+     * Official published name of the (internal) role controller service.
+     *
+     * @see #getSystemService(String)
+     * @see android.app.role.RoleControllerService
+     *
+     * @hide
+     */
+    public static final String ROLE_CONTROLLER_SERVICE = "role_controller";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.hardware.camera2.CameraManager} for interacting with
+     * camera devices.
+     *
+     * @see #getSystemService(String)
+     * @see android.hardware.camera2.CameraManager
+     */
+    public static final String CAMERA_SERVICE = "camera";
+
+    /**
+     * {@link android.print.PrintManager} for printing and managing
+     * printers and print tasks.
+     *
+     * @see #getSystemService(String)
+     * @see android.print.PrintManager
+     */
+    public static final String PRINT_SERVICE = "print";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.companion.CompanionDeviceManager} for managing companion devices
+     *
+     * @see #getSystemService(String)
+     * @see android.companion.CompanionDeviceManager
+     */
+    public static final String COMPANION_DEVICE_SERVICE = "companiondevice";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.hardware.ConsumerIrManager} for transmitting infrared
+     * signals from the device.
+     *
+     * @see #getSystemService(String)
+     * @see android.hardware.ConsumerIrManager
+     */
+    public static final String CONSUMER_IR_SERVICE = "consumer_ir";
+
+    /**
+     * {@link android.app.trust.TrustManager} for managing trust agents.
+     * @see #getSystemService(String)
+     * @see android.app.trust.TrustManager
+     * @hide
+     */
+    public static final String TRUST_SERVICE = "trust";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.media.tv.TvInputManager} for interacting with TV inputs
+     * on the device.
+     *
+     * @see #getSystemService(String)
+     * @see android.media.tv.TvInputManager
+     */
+    public static final String TV_INPUT_SERVICE = "tv_input";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.media.tv.TunerResourceManager} for interacting with TV
+     * tuner resources on the device.
+     *
+     * @see #getSystemService(String)
+     * @see android.media.tv.TunerResourceManager
+     * @hide
+     */
+    public static final String TV_TUNER_RESOURCE_MGR_SERVICE = "tv_tuner_resource_mgr";
+
+    /**
+     * {@link android.net.NetworkScoreManager} for managing network scoring.
+     * @see #getSystemService(String)
+     * @see android.net.NetworkScoreManager
+     * @hide
+     */
+    @SystemApi
+    public static final String NETWORK_SCORE_SERVICE = "network_score";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a {@link
+     * android.app.usage.UsageStatsManager} for querying device usage stats.
+     *
+     * @see #getSystemService(String)
+     * @see android.app.usage.UsageStatsManager
+     */
+    public static final String USAGE_STATS_SERVICE = "usagestats";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a {@link
+     * android.app.job.JobScheduler} instance for managing occasional
+     * background tasks.
+     * @see #getSystemService(String)
+     * @see android.app.job.JobScheduler
+     */
+    public static final String JOB_SCHEDULER_SERVICE = "jobscheduler";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a {@link
+     * android.service.persistentdata.PersistentDataBlockManager} instance
+     * for interacting with a storage device that lives across factory resets.
+     *
+     * @see #getSystemService(String)
+     * @see android.service.persistentdata.PersistentDataBlockManager
+     * @hide
+     */
+    @SystemApi
+    public static final String PERSISTENT_DATA_BLOCK_SERVICE = "persistent_data_block";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a {@link
+     * android.service.oemlock.OemLockManager} instance for managing the OEM lock.
+     *
+     * @see #getSystemService(String)
+     * @see android.service.oemlock.OemLockManager
+     * @hide
+     */
+    @SystemApi
+    public static final String OEM_LOCK_SERVICE = "oem_lock";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a {@link
+     * android.media.projection.MediaProjectionManager} instance for managing
+     * media projection sessions.
+     * @see #getSystemService(String)
+     * @see android.media.projection.MediaProjectionManager
+     */
+    public static final String MEDIA_PROJECTION_SERVICE = "media_projection";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.media.midi.MidiManager} for accessing the MIDI service.
+     *
+     * @see #getSystemService(String)
+     */
+    public static final String MIDI_SERVICE = "midi";
+
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.hardware.radio.RadioManager} for accessing the broadcast radio service.
+     *
+     * @see #getSystemService(String)
+     * @hide
+     */
+    public static final String RADIO_SERVICE = "broadcastradio";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.os.HardwarePropertiesManager} for accessing the hardware properties service.
+     *
+     * @see #getSystemService(String)
+     */
+    public static final String HARDWARE_PROPERTIES_SERVICE = "hardware_properties";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.os.ThermalService} for accessing the thermal service.
+     *
+     * @see #getSystemService(String)
+     * @hide
+     */
+    public static final String THERMAL_SERVICE = "thermalservice";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.content.pm.ShortcutManager} for accessing the launcher shortcut service.
+     *
+     * @see #getSystemService(String)
+     * @see android.content.pm.ShortcutManager
+     */
+    public static final String SHORTCUT_SERVICE = "shortcut";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a {@link
+     * android.hardware.location.ContextHubManager} for accessing context hubs.
+     *
+     * @see #getSystemService(String)
+     * @see android.hardware.location.ContextHubManager
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String CONTEXTHUB_SERVICE = "contexthub";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.os.health.SystemHealthManager} for accessing system health (battery, power,
+     * memory, etc) metrics.
+     *
+     * @see #getSystemService(String)
+     */
+    public static final String SYSTEM_HEALTH_SERVICE = "systemhealth";
+
+    /**
+     * Gatekeeper Service.
+     * @hide
+     */
+    public static final String GATEKEEPER_SERVICE = "android.service.gatekeeper.IGateKeeperService";
+
+    /**
+     * Service defining the policy for access to device identifiers.
+     * @hide
+     */
+    public static final String DEVICE_IDENTIFIERS_SERVICE = "device_identifiers";
+
+    /**
+     * Service to report a system health "incident"
+     * @hide
+     */
+    public static final String INCIDENT_SERVICE = "incident";
+
+    /**
+     * Service to assist incidentd and dumpstated in reporting status to the user
+     * and in confirming authorization to take an incident report or bugreport
+     * @hide
+     */
+    public static final String INCIDENT_COMPANION_SERVICE = "incidentcompanion";
+
+    /**
+     * Service to assist {@link android.app.StatsManager} that lives in system server.
+     * @hide
+     */
+    public static final String STATS_MANAGER_SERVICE = "statsmanager";
+
+    /**
+     * Service to assist statsd in obtaining general stats.
+     * @hide
+     */
+    public static final String STATS_COMPANION_SERVICE = "statscompanion";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve an {@link android.app.StatsManager}.
+     * @hide
+     */
+    @SystemApi
+    public static final String STATS_MANAGER = "stats";
+
+    /**
+     * Use with {@link android.os.ServiceManager.getService()} to retrieve a
+     * {@link IPlatformCompat} IBinder for communicating with the platform compat service.
+     * @hide
+     */
+    public static final String PLATFORM_COMPAT_SERVICE = "platform_compat";
+
+    /**
+     * Use with {@link android.os.ServiceManager.getService()} to retrieve a
+     * {@link IPlatformCompatNative} IBinder for native code communicating with the platform compat
+     * service.
+     * @hide
+     */
+    public static final String PLATFORM_COMPAT_NATIVE_SERVICE = "platform_compat_native";
+
+    /**
+     * Service to capture a bugreport.
+     * @see #getSystemService(String)
+     * @see android.os.BugreportManager
+     * @hide
+     */
+    @SystemApi @TestApi
+    public static final String BUGREPORT_SERVICE = "bugreport";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a {@link
+     * android.content.om.OverlayManager} for managing overlay packages.
+     *
+     * @see #getSystemService(String)
+     * @see android.content.om.OverlayManager
+     * @hide
+     */
+    public static final String OVERLAY_SERVICE = "overlay";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {android.os.IIdmap2} for managing idmap files (used by overlay
+     * packages).
+     *
+     * @see #getSystemService(String)
+     * @hide
+     */
+    public static final String IDMAP_SERVICE = "idmap";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link VrManager} for accessing the VR service.
+     *
+     * @see #getSystemService(String)
+     * @hide
+     */
+    @SystemApi
+    public static final String VR_SERVICE = "vrmanager";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve an
+     * {@link android.app.timezone.ITimeZoneRulesManager}.
+     * @hide
+     *
+     * @see #getSystemService(String)
+     */
+    public static final String TIME_ZONE_RULES_MANAGER_SERVICE = "timezone";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.content.pm.CrossProfileApps} for cross profile operations.
+     *
+     * @see #getSystemService(String)
+     */
+    public static final String CROSS_PROFILE_APPS_SERVICE = "crossprofileapps";
+
+    /**
+     * Use with {@link #getSystemService} to retrieve a
+     * {@link android.se.omapi.ISecureElementService}
+     * for accessing the SecureElementService.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String SECURE_ELEMENT_SERVICE = "secure_element";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve an
+     * {@link android.app.timedetector.TimeDetector}.
+     * @hide
+     *
+     * @see #getSystemService(String)
+     */
+    public static final String TIME_DETECTOR_SERVICE = "time_detector";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve an
+     * {@link android.app.timezonedetector.TimeZoneDetector}.
+     * @hide
+     *
+     * @see #getSystemService(String)
+     */
+    public static final String TIME_ZONE_DETECTOR_SERVICE = "time_zone_detector";
+
+    /**
+     * Binder service name for {@link AppBindingService}.
+     * @hide
+     */
+    public static final String APP_BINDING_SERVICE = "app_binding";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve an
+     * {@link android.telephony.ims.ImsManager}.
+     */
+    public static final String TELEPHONY_IMS_SERVICE = "telephony_ims";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve an
+     * {@link android.os.SystemConfigManager}.
+     * @hide
+     */
+    @SystemApi
+    public static final String SYSTEM_CONFIG_SERVICE = "system_config";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve an
+     * {@link android.telephony.ims.RcsMessageManager}.
+     * @hide
+     */
+    public static final String TELEPHONY_RCS_MESSAGE_SERVICE = "ircsmessage";
+
+     /**
+     * Use with {@link #getSystemService(String)} to retrieve an
+     * {@link android.os.image.DynamicSystemManager}.
+     * @hide
+     */
+    public static final String DYNAMIC_SYSTEM_SERVICE = "dynamic_system";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a {@link
+     * android.app.blob.BlobStoreManager} for contributing and accessing data blobs
+     * from the blob store maintained by the system.
+     *
+     * @see #getSystemService(String)
+     * @see android.app.blob.BlobStoreManager
+     */
+    public static final String BLOB_STORE_SERVICE = "blob_store";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve an
+     * {@link TelephonyRegistryManager}.
+     * @hide
+     */
+    public static final String TELEPHONY_REGISTRY_SERVICE = "telephony_registry";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve an
+     * {@link android.os.BatteryStatsManager}.
+     * @hide
+     */
+    @SystemApi
+    @SuppressLint("ServiceName")
+    public static final String BATTERY_STATS_SERVICE = "batterystats";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve an
+     * {@link android.content.integrity.AppIntegrityManager}.
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    public static final String APP_INTEGRITY_SERVICE = "app_integrity";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve an
+     * {@link android.content.pm.DataLoaderManager}.
+     * @hide
+     */
+    public static final String DATA_LOADER_MANAGER_SERVICE = "dataloader_manager";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve an
+     * {@link android.os.incremental.IncrementalManager}.
+     * @hide
+     */
+    public static final String INCREMENTAL_SERVICE = "incremental";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve an
+     * {@link android.security.FileIntegrityManager}.
+     * @see #getSystemService(String)
+     * @see android.security.FileIntegrityManager
+     */
+    public static final String FILE_INTEGRITY_SERVICE = "file_integrity";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.hardware.lights.LightsManager} for controlling device lights.
+     *
+     * @see #getSystemService(String)
+     * @hide
+     */
+    public static final String LIGHTS_SERVICE = "lights";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.app.DreamManager} for controlling Dream states.
+     *
+     * @see #getSystemService(String)
+
+     * @hide
+     */
+    @TestApi
+    public static final String DREAM_SERVICE = "dream";
+
+    /**
+     * Determine whether the given permission is allowed for a particular
+     * process and user ID running in the system.
+     *
+     * @param permission The name of the permission being checked.
+     * @param pid The process ID being checked against.  Must be > 0.
+     * @param uid The user ID being checked against.  A uid of 0 is the root
+     * user, which will pass every permission check.
+     *
+     * @return {@link PackageManager#PERMISSION_GRANTED} if the given
+     * pid/uid is allowed that permission, or
+     * {@link PackageManager#PERMISSION_DENIED} if it is not.
+     *
+     * @see PackageManager#checkPermission(String, String)
+     * @see #checkCallingPermission
+     */
+    @CheckResult(suggest="#enforcePermission(String,int,int,String)")
+    @PackageManager.PermissionResult
+    public abstract int checkPermission(@NonNull String permission, int pid, int uid);
+
+    /** @hide */
+    @PackageManager.PermissionResult
+    @UnsupportedAppUsage
+    public abstract int checkPermission(@NonNull String permission, int pid, int uid,
+            IBinder callerToken);
+
+    /**
+     * Determine whether the calling process of an IPC you are handling has been
+     * granted a particular permission.  This is basically the same as calling
+     * {@link #checkPermission(String, int, int)} with the pid and uid returned
+     * by {@link android.os.Binder#getCallingPid} and
+     * {@link android.os.Binder#getCallingUid}.  One important difference
+     * is that if you are not currently processing an IPC, this function
+     * will always fail.  This is done to protect against accidentally
+     * leaking permissions; you can use {@link #checkCallingOrSelfPermission}
+     * to avoid this protection.
+     *
+     * @param permission The name of the permission being checked.
+     *
+     * @return {@link PackageManager#PERMISSION_GRANTED} if the calling
+     * pid/uid is allowed that permission, or
+     * {@link PackageManager#PERMISSION_DENIED} if it is not.
+     *
+     * @see PackageManager#checkPermission(String, String)
+     * @see #checkPermission
+     * @see #checkCallingOrSelfPermission
+     */
+    @CheckResult(suggest="#enforceCallingPermission(String,String)")
+    @PackageManager.PermissionResult
+    public abstract int checkCallingPermission(@NonNull String permission);
+
+    /**
+     * Determine whether the calling process of an IPC <em>or you</em> have been
+     * granted a particular permission.  This is the same as
+     * {@link #checkCallingPermission}, except it grants your own permissions
+     * if you are not currently processing an IPC.  Use with care!
+     *
+     * @param permission The name of the permission being checked.
+     *
+     * @return {@link PackageManager#PERMISSION_GRANTED} if the calling
+     * pid/uid is allowed that permission, or
+     * {@link PackageManager#PERMISSION_DENIED} if it is not.
+     *
+     * @see PackageManager#checkPermission(String, String)
+     * @see #checkPermission
+     * @see #checkCallingPermission
+     */
+    @CheckResult(suggest="#enforceCallingOrSelfPermission(String,String)")
+    @PackageManager.PermissionResult
+    public abstract int checkCallingOrSelfPermission(@NonNull String permission);
+
+    /**
+     * Determine whether <em>you</em> have been granted a particular permission.
+     *
+     * @param permission The name of the permission being checked.
+     *
+     * @return {@link PackageManager#PERMISSION_GRANTED} if you have the
+     * permission, or {@link PackageManager#PERMISSION_DENIED} if not.
+     *
+     * @see PackageManager#checkPermission(String, String)
+     * @see #checkCallingPermission(String)
+     */
+    @PackageManager.PermissionResult
+    public abstract int checkSelfPermission(@NonNull String permission);
+
+    /**
+     * If the given permission is not allowed for a particular process
+     * and user ID running in the system, throw a {@link SecurityException}.
+     *
+     * @param permission The name of the permission being checked.
+     * @param pid The process ID being checked against.  Must be &gt; 0.
+     * @param uid The user ID being checked against.  A uid of 0 is the root
+     * user, which will pass every permission check.
+     * @param message A message to include in the exception if it is thrown.
+     *
+     * @see #checkPermission(String, int, int)
+     */
+    public abstract void enforcePermission(
+            @NonNull String permission, int pid, int uid, @Nullable String message);
+
+    /**
+     * If the calling process of an IPC you are handling has not been
+     * granted a particular permission, throw a {@link
+     * SecurityException}.  This is basically the same as calling
+     * {@link #enforcePermission(String, int, int, String)} with the
+     * pid and uid returned by {@link android.os.Binder#getCallingPid}
+     * and {@link android.os.Binder#getCallingUid}.  One important
+     * difference is that if you are not currently processing an IPC,
+     * this function will always throw the SecurityException.  This is
+     * done to protect against accidentally leaking permissions; you
+     * can use {@link #enforceCallingOrSelfPermission} to avoid this
+     * protection.
+     *
+     * @param permission The name of the permission being checked.
+     * @param message A message to include in the exception if it is thrown.
+     *
+     * @see #checkCallingPermission(String)
+     */
+    public abstract void enforceCallingPermission(
+            @NonNull String permission, @Nullable String message);
+
+    /**
+     * If neither you nor the calling process of an IPC you are
+     * handling has been granted a particular permission, throw a
+     * {@link SecurityException}.  This is the same as {@link
+     * #enforceCallingPermission}, except it grants your own
+     * permissions if you are not currently processing an IPC.  Use
+     * with care!
+     *
+     * @param permission The name of the permission being checked.
+     * @param message A message to include in the exception if it is thrown.
+     *
+     * @see #checkCallingOrSelfPermission(String)
+     */
+    public abstract void enforceCallingOrSelfPermission(
+            @NonNull String permission, @Nullable String message);
+
+    /**
+     * Grant permission to access a specific Uri to another package, regardless
+     * of whether that package has general permission to access the Uri's
+     * content provider.  This can be used to grant specific, temporary
+     * permissions, typically in response to user interaction (such as the
+     * user opening an attachment that you would like someone else to
+     * display).
+     *
+     * <p>Normally you should use {@link Intent#FLAG_GRANT_READ_URI_PERMISSION
+     * Intent.FLAG_GRANT_READ_URI_PERMISSION} or
+     * {@link Intent#FLAG_GRANT_WRITE_URI_PERMISSION
+     * Intent.FLAG_GRANT_WRITE_URI_PERMISSION} with the Intent being used to
+     * start an activity instead of this function directly.  If you use this
+     * function directly, you should be sure to call
+     * {@link #revokeUriPermission} when the target should no longer be allowed
+     * to access it.
+     *
+     * <p>To succeed, the content provider owning the Uri must have set the
+     * {@link android.R.styleable#AndroidManifestProvider_grantUriPermissions
+     * grantUriPermissions} attribute in its manifest or included the
+     * {@link android.R.styleable#AndroidManifestGrantUriPermission
+     * &lt;grant-uri-permissions&gt;} tag.
+     *
+     * @param toPackage The package you would like to allow to access the Uri.
+     * @param uri The Uri you would like to grant access to.
+     * @param modeFlags The desired access modes.
+     *
+     * @see #revokeUriPermission
+     */
+    public abstract void grantUriPermission(String toPackage, Uri uri,
+            @Intent.GrantUriMode int modeFlags);
+
+    /**
+     * Remove all permissions to access a particular content provider Uri
+     * that were previously added with {@link #grantUriPermission} or <em>any other</em> mechanism.
+     * The given Uri will match all previously granted Uris that are the same or a
+     * sub-path of the given Uri.  That is, revoking "content://foo/target" will
+     * revoke both "content://foo/target" and "content://foo/target/sub", but not
+     * "content://foo".  It will not remove any prefix grants that exist at a
+     * higher level.
+     *
+     * <p>Prior to {@link android.os.Build.VERSION_CODES#LOLLIPOP}, if you did not have
+     * regular permission access to a Uri, but had received access to it through
+     * a specific Uri permission grant, you could not revoke that grant with this
+     * function and a {@link SecurityException} would be thrown.  As of
+     * {@link android.os.Build.VERSION_CODES#LOLLIPOP}, this function will not throw a security
+     * exception, but will remove whatever permission grants to the Uri had been given to the app
+     * (or none).</p>
+     *
+     * <p>Unlike {@link #revokeUriPermission(String, Uri, int)}, this method impacts all permission
+     * grants matching the given Uri, for any package they had been granted to, through any
+     * mechanism this had happened (such as indirectly through the clipboard, activity launch,
+     * service start, etc).  That means this can be potentially dangerous to use, as it can
+     * revoke grants that another app could be strongly expecting to stick around.</p>
+     *
+     * @param uri The Uri you would like to revoke access to.
+     * @param modeFlags The access modes to revoke.
+     *
+     * @see #grantUriPermission
+     */
+    public abstract void revokeUriPermission(Uri uri, @Intent.AccessUriMode int modeFlags);
+
+    /**
+     * Remove permissions to access a particular content provider Uri
+     * that were previously added with {@link #grantUriPermission} for a specific target
+     * package.  The given Uri will match all previously granted Uris that are the same or a
+     * sub-path of the given Uri.  That is, revoking "content://foo/target" will
+     * revoke both "content://foo/target" and "content://foo/target/sub", but not
+     * "content://foo".  It will not remove any prefix grants that exist at a
+     * higher level.
+     *
+     * <p>Unlike {@link #revokeUriPermission(Uri, int)}, this method will <em>only</em>
+     * revoke permissions that had been explicitly granted through {@link #grantUriPermission}
+     * and only for the package specified.  Any matching grants that have happened through
+     * other mechanisms (clipboard, activity launching, service starting, etc) will not be
+     * removed.</p>
+     *
+     * @param toPackage The package you had previously granted access to.
+     * @param uri The Uri you would like to revoke access to.
+     * @param modeFlags The access modes to revoke.
+     *
+     * @see #grantUriPermission
+     */
+    public abstract void revokeUriPermission(String toPackage, Uri uri,
+            @Intent.AccessUriMode int modeFlags);
+
+    /**
+     * Determine whether a particular process and user ID has been granted
+     * permission to access a specific URI.  This only checks for permissions
+     * that have been explicitly granted -- if the given process/uid has
+     * more general access to the URI's content provider then this check will
+     * always fail.
+     *
+     * @param uri The uri that is being checked.
+     * @param pid The process ID being checked against.  Must be &gt; 0.
+     * @param uid The user ID being checked against.  A uid of 0 is the root
+     * user, which will pass every permission check.
+     * @param modeFlags The access modes to check.
+     *
+     * @return {@link PackageManager#PERMISSION_GRANTED} if the given
+     * pid/uid is allowed to access that uri, or
+     * {@link PackageManager#PERMISSION_DENIED} if it is not.
+     *
+     * @see #checkCallingUriPermission
+     */
+    @CheckResult(suggest="#enforceUriPermission(Uri,int,int,String)")
+    @PackageManager.PermissionResult
+    public abstract int checkUriPermission(Uri uri, int pid, int uid,
+            @Intent.AccessUriMode int modeFlags);
+
+    /** @hide */
+    @PackageManager.PermissionResult
+    public abstract int checkUriPermission(Uri uri, int pid, int uid,
+            @Intent.AccessUriMode int modeFlags, IBinder callerToken);
+
+    /**
+     * Determine whether the calling process and user ID has been
+     * granted permission to access a specific URI.  This is basically
+     * the same as calling {@link #checkUriPermission(Uri, int, int,
+     * int)} with the pid and uid returned by {@link
+     * android.os.Binder#getCallingPid} and {@link
+     * android.os.Binder#getCallingUid}.  One important difference is
+     * that if you are not currently processing an IPC, this function
+     * will always fail.
+     *
+     * @param uri The uri that is being checked.
+     * @param modeFlags The access modes to check.
+     *
+     * @return {@link PackageManager#PERMISSION_GRANTED} if the caller
+     * is allowed to access that uri, or
+     * {@link PackageManager#PERMISSION_DENIED} if it is not.
+     *
+     * @see #checkUriPermission(Uri, int, int, int)
+     */
+    @CheckResult(suggest="#enforceCallingUriPermission(Uri,int,String)")
+    @PackageManager.PermissionResult
+    public abstract int checkCallingUriPermission(Uri uri, @Intent.AccessUriMode int modeFlags);
+
+    /**
+     * Determine whether the calling process of an IPC <em>or you</em> has been granted
+     * permission to access a specific URI.  This is the same as
+     * {@link #checkCallingUriPermission}, except it grants your own permissions
+     * if you are not currently processing an IPC.  Use with care!
+     *
+     * @param uri The uri that is being checked.
+     * @param modeFlags The access modes to check.
+     *
+     * @return {@link PackageManager#PERMISSION_GRANTED} if the caller
+     * is allowed to access that uri, or
+     * {@link PackageManager#PERMISSION_DENIED} if it is not.
+     *
+     * @see #checkCallingUriPermission
+     */
+    @CheckResult(suggest="#enforceCallingOrSelfUriPermission(Uri,int,String)")
+    @PackageManager.PermissionResult
+    public abstract int checkCallingOrSelfUriPermission(Uri uri,
+            @Intent.AccessUriMode int modeFlags);
+
+    /**
+     * Check both a Uri and normal permission.  This allows you to perform
+     * both {@link #checkPermission} and {@link #checkUriPermission} in one
+     * call.
+     *
+     * @param uri The Uri whose permission is to be checked, or null to not
+     * do this check.
+     * @param readPermission The permission that provides overall read access,
+     * or null to not do this check.
+     * @param writePermission The permission that provides overall write
+     * access, or null to not do this check.
+     * @param pid The process ID being checked against.  Must be &gt; 0.
+     * @param uid The user ID being checked against.  A uid of 0 is the root
+     * user, which will pass every permission check.
+     * @param modeFlags The access modes to check.
+     *
+     * @return {@link PackageManager#PERMISSION_GRANTED} if the caller
+     * is allowed to access that uri or holds one of the given permissions, or
+     * {@link PackageManager#PERMISSION_DENIED} if it is not.
+     */
+    @CheckResult(suggest="#enforceUriPermission(Uri,String,String,int,int,int,String)")
+    @PackageManager.PermissionResult
+    public abstract int checkUriPermission(@Nullable Uri uri, @Nullable String readPermission,
+            @Nullable String writePermission, int pid, int uid,
+            @Intent.AccessUriMode int modeFlags);
+
+    /**
+     * If a particular process and user ID has not been granted
+     * permission to access a specific URI, throw {@link
+     * SecurityException}.  This only checks for permissions that have
+     * been explicitly granted -- if the given process/uid has more
+     * general access to the URI's content provider then this check
+     * will always fail.
+     *
+     * @param uri The uri that is being checked.
+     * @param pid The process ID being checked against.  Must be &gt; 0.
+     * @param uid The user ID being checked against.  A uid of 0 is the root
+     * user, which will pass every permission check.
+     * @param modeFlags The access modes to enforce.
+     * @param message A message to include in the exception if it is thrown.
+     *
+     * @see #checkUriPermission(Uri, int, int, int)
+     */
+    public abstract void enforceUriPermission(
+            Uri uri, int pid, int uid, @Intent.AccessUriMode int modeFlags, String message);
+
+    /**
+     * If the calling process and user ID has not been granted
+     * permission to access a specific URI, throw {@link
+     * SecurityException}.  This is basically the same as calling
+     * {@link #enforceUriPermission(Uri, int, int, int, String)} with
+     * the pid and uid returned by {@link
+     * android.os.Binder#getCallingPid} and {@link
+     * android.os.Binder#getCallingUid}.  One important difference is
+     * that if you are not currently processing an IPC, this function
+     * will always throw a SecurityException.
+     *
+     * @param uri The uri that is being checked.
+     * @param modeFlags The access modes to enforce.
+     * @param message A message to include in the exception if it is thrown.
+     *
+     * @see #checkCallingUriPermission(Uri, int)
+     */
+    public abstract void enforceCallingUriPermission(
+            Uri uri, @Intent.AccessUriMode int modeFlags, String message);
+
+    /**
+     * If the calling process of an IPC <em>or you</em> has not been
+     * granted permission to access a specific URI, throw {@link
+     * SecurityException}.  This is the same as {@link
+     * #enforceCallingUriPermission}, except it grants your own
+     * permissions if you are not currently processing an IPC.  Use
+     * with care!
+     *
+     * @param uri The uri that is being checked.
+     * @param modeFlags The access modes to enforce.
+     * @param message A message to include in the exception if it is thrown.
+     *
+     * @see #checkCallingOrSelfUriPermission(Uri, int)
+     */
+    public abstract void enforceCallingOrSelfUriPermission(
+            Uri uri, @Intent.AccessUriMode int modeFlags, String message);
+
+    /**
+     * Enforce both a Uri and normal permission.  This allows you to perform
+     * both {@link #enforcePermission} and {@link #enforceUriPermission} in one
+     * call.
+     *
+     * @param uri The Uri whose permission is to be checked, or null to not
+     * do this check.
+     * @param readPermission The permission that provides overall read access,
+     * or null to not do this check.
+     * @param writePermission The permission that provides overall write
+     * access, or null to not do this check.
+     * @param pid The process ID being checked against.  Must be &gt; 0.
+     * @param uid The user ID being checked against.  A uid of 0 is the root
+     * user, which will pass every permission check.
+     * @param modeFlags The access modes to enforce.
+     * @param message A message to include in the exception if it is thrown.
+     *
+     * @see #checkUriPermission(Uri, String, String, int, int, int)
+     */
+    public abstract void enforceUriPermission(
+            @Nullable Uri uri, @Nullable String readPermission,
+            @Nullable String writePermission, int pid, int uid, @Intent.AccessUriMode int modeFlags,
+            @Nullable String message);
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "CONTEXT_" }, value = {
+            CONTEXT_INCLUDE_CODE,
+            CONTEXT_IGNORE_SECURITY,
+            CONTEXT_RESTRICTED,
+            CONTEXT_DEVICE_PROTECTED_STORAGE,
+            CONTEXT_CREDENTIAL_PROTECTED_STORAGE,
+            CONTEXT_REGISTER_PACKAGE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface CreatePackageOptions {}
+
+    /**
+     * Flag for use with {@link #createPackageContext}: include the application
+     * code with the context.  This means loading code into the caller's
+     * process, so that {@link #getClassLoader()} can be used to instantiate
+     * the application's classes.  Setting this flags imposes security
+     * restrictions on what application context you can access; if the
+     * requested application can not be safely loaded into your process,
+     * java.lang.SecurityException will be thrown.  If this flag is not set,
+     * there will be no restrictions on the packages that can be loaded,
+     * but {@link #getClassLoader} will always return the default system
+     * class loader.
+     */
+    public static final int CONTEXT_INCLUDE_CODE = 0x00000001;
+
+    /**
+     * Flag for use with {@link #createPackageContext}: ignore any security
+     * restrictions on the Context being requested, allowing it to always
+     * be loaded.  For use with {@link #CONTEXT_INCLUDE_CODE} to allow code
+     * to be loaded into a process even when it isn't safe to do so.  Use
+     * with extreme care!
+     */
+    public static final int CONTEXT_IGNORE_SECURITY = 0x00000002;
+
+    /**
+     * Flag for use with {@link #createPackageContext}: a restricted context may
+     * disable specific features. For instance, a View associated with a restricted
+     * context would ignore particular XML attributes.
+     */
+    public static final int CONTEXT_RESTRICTED = 0x00000004;
+
+    /**
+     * Flag for use with {@link #createPackageContext}: point all file APIs at
+     * device-protected storage.
+     *
+     * @hide
+     */
+    public static final int CONTEXT_DEVICE_PROTECTED_STORAGE = 0x00000008;
+
+    /**
+     * Flag for use with {@link #createPackageContext}: point all file APIs at
+     * credential-protected storage.
+     *
+     * @hide
+     */
+    public static final int CONTEXT_CREDENTIAL_PROTECTED_STORAGE = 0x00000010;
+
+    /**
+     * @hide Used to indicate we should tell the activity manager about the process
+     * loading this code.
+     */
+    public static final int CONTEXT_REGISTER_PACKAGE = 0x40000000;
+
+    /**
+     * Return a new Context object for the given application name.  This
+     * Context is the same as what the named application gets when it is
+     * launched, containing the same resources and class loader.  Each call to
+     * this method returns a new instance of a Context object; Context objects
+     * are not shared, however they share common state (Resources, ClassLoader,
+     * etc) so the Context instance itself is fairly lightweight.
+     *
+     * <p>Throws {@link android.content.pm.PackageManager.NameNotFoundException} if there is no
+     * application with the given package name.
+     *
+     * <p>Throws {@link java.lang.SecurityException} if the Context requested
+     * can not be loaded into the caller's process for security reasons (see
+     * {@link #CONTEXT_INCLUDE_CODE} for more information}.
+     *
+     * @param packageName Name of the application's package.
+     * @param flags Option flags.
+     *
+     * @return A {@link Context} for the application.
+     *
+     * @throws SecurityException &nbsp;
+     * @throws PackageManager.NameNotFoundException if there is no application with
+     * the given package name.
+     */
+    public abstract Context createPackageContext(String packageName,
+            @CreatePackageOptions int flags) throws PackageManager.NameNotFoundException;
+
+    /**
+     * Similar to {@link #createPackageContext(String, int)}, but with a
+     * different {@link UserHandle}. For example, {@link #getContentResolver()}
+     * will open any {@link Uri} as the given user.
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    @NonNull
+    public Context createPackageContextAsUser(
+            @NonNull String packageName, @CreatePackageOptions int flags, @NonNull UserHandle user)
+            throws PackageManager.NameNotFoundException {
+        if (Build.IS_ENG) {
+            throw new IllegalStateException("createPackageContextAsUser not overridden!");
+        }
+        return this;
+    }
+
+    /**
+     * Similar to {@link #createPackageContext(String, int)}, but for the own package with a
+     * different {@link UserHandle}. For example, {@link #getContentResolver()}
+     * will open any {@link Uri} as the given user.
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    @NonNull
+    public Context createContextAsUser(@NonNull UserHandle user, @CreatePackageOptions int flags) {
+        if (Build.IS_ENG) {
+            throw new IllegalStateException("createContextAsUser not overridden!");
+        }
+        return this;
+    }
+
+    /**
+     * Creates a context given an {@link android.content.pm.ApplicationInfo}.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public abstract Context createApplicationContext(ApplicationInfo application,
+            @CreatePackageOptions int flags) throws PackageManager.NameNotFoundException;
+
+    /**
+     * Return a new Context object for the given split name. The new Context has a ClassLoader and
+     * Resources object that can access the split's and all of its dependencies' code/resources.
+     * Each call to this method returns a new instance of a Context object;
+     * Context objects are not shared, however common state (ClassLoader, other Resources for
+     * the same split) may be so the Context itself can be fairly lightweight.
+     *
+     * @param splitName The name of the split to include, as declared in the split's
+     *                  <code>AndroidManifest.xml</code>.
+     * @return A {@link Context} with the given split's code and/or resources loaded.
+     */
+    public abstract Context createContextForSplit(String splitName)
+            throws PackageManager.NameNotFoundException;
+
+    /**
+     * Get the user associated with this context
+     * @hide
+     */
+    @TestApi
+    public UserHandle getUser() {
+        return android.os.Process.myUserHandle();
+    }
+
+    /**
+     * Get the user associated with this context
+     * @hide
+     */
+    @UnsupportedAppUsage
+    @TestApi
+    public @UserIdInt int getUserId() {
+        return android.os.UserHandle.myUserId();
+    }
+
+    /**
+     * Return a new Context object for the current Context but whose resources
+     * are adjusted to match the given Configuration.  Each call to this method
+     * returns a new instance of a Context object; Context objects are not
+     * shared, however common state (ClassLoader, other Resources for the
+     * same configuration) may be so the Context itself can be fairly lightweight.
+     *
+     * @param overrideConfiguration A {@link Configuration} specifying what
+     * values to modify in the base Configuration of the original Context's
+     * resources.  If the base configuration changes (such as due to an
+     * orientation change), the resources of this context will also change except
+     * for those that have been explicitly overridden with a value here.
+     *
+     * @return A {@link Context} with the given configuration override.
+     */
+    public abstract Context createConfigurationContext(
+            @NonNull Configuration overrideConfiguration);
+
+    /**
+     * Return a new Context object for the current Context but whose resources
+     * are adjusted to match the metrics of the given Display.  Each call to this method
+     * returns a new instance of a Context object; Context objects are not
+     * shared, however common state (ClassLoader, other Resources for the
+     * same configuration) may be so the Context itself can be fairly lightweight.
+     *
+     * To obtain an instance of a {@link WindowManager} (see {@link #getSystemService(String)}) that
+     * is configured to show windows on the given display call
+     * {@link #createWindowContext(int, Bundle)} on the returned display Context or use an
+     * {@link android.app.Activity}.
+     *
+     * @param display A {@link Display} object specifying the display for whose metrics the
+     * Context's resources should be tailored.
+     *
+     * @return A {@link Context} for the display.
+     */
+    public abstract Context createDisplayContext(@NonNull Display display);
+
+    /**
+     * Creates a Context for a non-activity window.
+     *
+     * <p>
+     * A window context is a context that can be used to add non-activity windows, such as
+     * {@link android.view.WindowManager.LayoutParams#TYPE_APPLICATION_OVERLAY}. A window context
+     * must be created from a context that has an associated {@link Display}, such as
+     * {@link android.app.Activity Activity} or a context created with
+     * {@link #createDisplayContext(Display)}.
+     *
+     * <p>
+     * The window context is created with the appropriate {@link Configuration} for the area of the
+     * display that the windows created with it can occupy; it must be used when
+     * {@link android.view.LayoutInflater inflating} views, such that they can be inflated with
+     * proper {@link Resources}.
+     *
+     * Below is a sample code to <b>add an application overlay window on the primary display:</b>
+     * <pre class="prettyprint">
+     * ...
+     * final DisplayManager dm = anyContext.getSystemService(DisplayManager.class);
+     * final Display primaryDisplay = dm.getDisplay(DEFAULT_DISPLAY);
+     * final Context windowContext = anyContext.createDisplayContext(primaryDisplay)
+     *         .createWindowContext(TYPE_APPLICATION_OVERLAY, null);
+     * final View overlayView = Inflater.from(windowContext).inflate(someLayoutXml, null);
+     *
+     * // WindowManager.LayoutParams initialization
+     * ...
+     * mParams.type = TYPE_APPLICATION_OVERLAY;
+     * ...
+     *
+     * mWindowContext.getSystemService(WindowManager.class).addView(overlayView, mParams);
+     * </pre>
+     *
+     * <p>
+     * This context's configuration and resources are adjusted to a display area where the windows
+     * with provided type will be added. <b>Note that all windows associated with the same context
+     * will have an affinity and can only be moved together between different displays or areas on a
+     * display.</b> If there is a need to add different window types, or non-associated windows,
+     * separate Contexts should be used.
+     * </p>
+     * <p>
+     * Creating a window context is an expensive operation. Misuse of this API may lead to a huge
+     * performance drop. The best practice is to use the same window context when possible.
+     * An approach is to create one window context with specific window type and display and
+     * use it everywhere it's needed..
+     * </p>
+     *
+     * @param type Window type in {@link WindowManager.LayoutParams}
+     * @param options Bundle used to pass window-related options.
+     * @return A {@link Context} that can be used to create windows.
+     * @throws UnsupportedOperationException if this is called on a non-UI context, such as
+     *         {@link android.app.Application Application} or {@link android.app.Service Service}.
+     *
+     * @see #getSystemService(String)
+     * @see #getSystemService(Class)
+     * @see #WINDOW_SERVICE
+     * @see #LAYOUT_INFLATER_SERVICE
+     * @see #WALLPAPER_SERVICE
+     * @throws UnsupportedOperationException if this {@link Context} does not attach to a display or
+     * the current number of window contexts without adding any view by
+     * {@link WindowManager#addView} <b>exceeds five</b>.
+     */
+    public @NonNull Context createWindowContext(@WindowType int type, @Nullable Bundle options)  {
+        throw new RuntimeException("Not implemented. Must override in a subclass.");
+    }
+
+    /**
+     * Return a new Context object for the current Context but attribute to a different tag.
+     * In complex apps attribution tagging can be used to distinguish between separate logical
+     * parts.
+     *
+     * @param attributionTag The tag or {@code null} to create a context for the default.
+     *
+     * @return A {@link Context} that is tagged for the new attribution
+     *
+     * @see #getAttributionTag()
+     */
+    public @NonNull Context createAttributionContext(@Nullable String attributionTag) {
+        throw new RuntimeException("Not implemented. Must override in a subclass.");
+    }
+
+    // TODO moltmann: remove
+    /**
+     * @removed
+     */
+    @Deprecated
+    public @NonNull Context createFeatureContext(@Nullable String featureId) {
+        return createAttributionContext(featureId);
+    }
+
+    /**
+     * Return a new Context object for the current Context but whose storage
+     * APIs are backed by device-protected storage.
+     * <p>
+     * On devices with direct boot, data stored in this location is encrypted
+     * with a key tied to the physical device, and it can be accessed
+     * immediately after the device has booted successfully, both
+     * <em>before and after</em> the user has authenticated with their
+     * credentials (such as a lock pattern or PIN).
+     * <p>
+     * Because device-protected data is available without user authentication,
+     * you should carefully limit the data you store using this Context. For
+     * example, storing sensitive authentication tokens or passwords in the
+     * device-protected area is strongly discouraged.
+     * <p>
+     * If the underlying device does not have the ability to store
+     * device-protected and credential-protected data using different keys, then
+     * both storage areas will become available at the same time. They remain as
+     * two distinct storage locations on disk, and only the window of
+     * availability changes.
+     * <p>
+     * Each call to this method returns a new instance of a Context object;
+     * Context objects are not shared, however common state (ClassLoader, other
+     * Resources for the same configuration) may be so the Context itself can be
+     * fairly lightweight.
+     *
+     * @see #isDeviceProtectedStorage()
+     */
+    public abstract Context createDeviceProtectedStorageContext();
+
+    /**
+     * Return a new Context object for the current Context but whose storage
+     * APIs are backed by credential-protected storage. This is the default
+     * storage area for apps unless
+     * {@link android.R.attr#defaultToDeviceProtectedStorage} was requested.
+     * <p>
+     * On devices with direct boot, data stored in this location is encrypted
+     * with a key tied to user credentials, which can be accessed
+     * <em>only after</em> the user has entered their credentials (such as a
+     * lock pattern or PIN).
+     * <p>
+     * If the underlying device does not have the ability to store
+     * device-protected and credential-protected data using different keys, then
+     * both storage areas will become available at the same time. They remain as
+     * two distinct storage locations on disk, and only the window of
+     * availability changes.
+     * <p>
+     * Each call to this method returns a new instance of a Context object;
+     * Context objects are not shared, however common state (ClassLoader, other
+     * Resources for the same configuration) may be so the Context itself can be
+     * fairly lightweight.
+     *
+     * @see #isCredentialProtectedStorage()
+     * @hide
+     */
+    @SystemApi
+    public abstract Context createCredentialProtectedStorageContext();
+
+    /**
+     * Gets the display adjustments holder for this context.  This information
+     * is provided on a per-application or activity basis and is used to simulate lower density
+     * display metrics for legacy applications and restricted screen sizes.
+     *
+     * @param displayId The display id for which to get compatibility info.
+     * @return The compatibility info holder, or null if not required by the application.
+     * @hide
+     */
+    public abstract DisplayAdjustments getDisplayAdjustments(int displayId);
+
+    /**
+     * Get the display this context is associated with. Applications should use this method with
+     * {@link android.app.Activity} or a context associated with a {@link Display} via
+     * {@link #createDisplayContext(Display)} to get a display object associated with a Context, or
+     * {@link android.hardware.display.DisplayManager#getDisplay} to get a display object by id.
+     * @return Returns the {@link Display} object this context is associated with.
+     * @throws UnsupportedOperationException if the method is called on an instance that is not
+     *         associated with any display.
+     */
+    @Nullable
+    public Display getDisplay() {
+        throw new RuntimeException("Not implemented. Must override in a subclass.");
+    }
+
+    /**
+     * A version of {@link #getDisplay()} that does not perform a Context misuse check to be used by
+     * legacy APIs.
+     * TODO(b/149790106): Fix usages and remove.
+     * @hide
+     */
+    @Nullable
+    public Display getDisplayNoVerify() {
+        throw new RuntimeException("Not implemented. Must override in a subclass.");
+    }
+
+    /**
+     * Gets the ID of the display this context is associated with.
+     *
+     * @return display ID associated with this {@link Context}.
+     * @see #getDisplay()
+     * @hide
+     */
+    @TestApi
+    public abstract int getDisplayId();
+
+    /**
+     * @hide
+     */
+    public abstract void updateDisplay(int displayId);
+
+    /**
+     * Indicates whether this Context is restricted.
+     *
+     * @return {@code true} if this Context is restricted, {@code false} otherwise.
+     *
+     * @see #CONTEXT_RESTRICTED
+     */
+    public boolean isRestricted() {
+        return false;
+    }
+
+    /**
+     * Indicates if the storage APIs of this Context are backed by
+     * device-protected storage.
+     *
+     * @see #createDeviceProtectedStorageContext()
+     */
+    public abstract boolean isDeviceProtectedStorage();
+
+    /**
+     * Indicates if the storage APIs of this Context are backed by
+     * credential-protected storage.
+     *
+     * @see #createCredentialProtectedStorageContext()
+     * @hide
+     */
+    @SystemApi
+    public abstract boolean isCredentialProtectedStorage();
+
+    /**
+     * Returns true if the context can load unsafe resources, e.g. fonts.
+     * @hide
+     */
+    public abstract boolean canLoadUnsafeResources();
+
+    /**
+     * @hide
+     */
+    public IBinder getActivityToken() {
+        throw new RuntimeException("Not implemented. Must override in a subclass.");
+    }
+
+    /**
+     * @hide
+     */
+    @Nullable
+    public IServiceConnection getServiceDispatcher(ServiceConnection conn, Handler handler,
+            int flags) {
+        throw new RuntimeException("Not implemented. Must override in a subclass.");
+    }
+
+    /**
+     * @hide
+     */
+    public IApplicationThread getIApplicationThread() {
+        throw new RuntimeException("Not implemented. Must override in a subclass.");
+    }
+
+    /**
+     * @hide
+     */
+    public Handler getMainThreadHandler() {
+        throw new RuntimeException("Not implemented. Must override in a subclass.");
+    }
+
+    /**
+     * @hide
+     */
+    public AutofillClient getAutofillClient() {
+        return null;
+    }
+
+    /**
+     * @hide
+     */
+    public void setAutofillClient(@SuppressWarnings("unused") AutofillClient client) {
+    }
+
+    /**
+     * @hide
+     */
+    @Nullable
+    public ContentCaptureClient getContentCaptureClient() {
+        return null;
+    }
+
+    /**
+     * @hide
+     */
+    public final boolean isAutofillCompatibilityEnabled() {
+        final AutofillOptions options = getAutofillOptions();
+        return options != null && options.compatModeEnabled;
+    }
+
+    /**
+     * @hide
+     */
+    @Nullable
+    public AutofillOptions getAutofillOptions() {
+        return null;
+    }
+
+    /**
+     * @hide
+     */
+    @TestApi
+    public void setAutofillOptions(@SuppressWarnings("unused") @Nullable AutofillOptions options) {
+    }
+
+    /**
+     * Gets the Content Capture options for this context, or {@code null} if it's not whitelisted.
+     *
+     * @hide
+     */
+    @Nullable
+    public ContentCaptureOptions getContentCaptureOptions() {
+        return null;
+    }
+
+    /**
+     * @hide
+     */
+    @TestApi
+    public void setContentCaptureOptions(
+            @SuppressWarnings("unused") @Nullable ContentCaptureOptions options) {
+    }
+
+    /**
+     * Throws an exception if the Context is using system resources,
+     * which are non-runtime-overlay-themable and may show inconsistent UI.
+     * @hide
+     */
+    public void assertRuntimeOverlayThemable() {
+        // Resources.getSystem() is a singleton and the only Resources not managed by
+        // ResourcesManager; therefore Resources.getSystem() is not themable.
+        if (getResources() == Resources.getSystem()) {
+            throw new IllegalArgumentException("Non-UI context used to display UI; "
+                    + "get a UI context from ActivityThread#getSystemUiContext()");
+        }
+    }
+
+    /**
+     * Indicates if this context is a visual context such as {@link android.app.Activity} or
+     * a context created from {@link #createWindowContext(int, Bundle)}.
+     * @hide
+     */
+    public boolean isUiContext() {
+        throw new RuntimeException("Not implemented. Must override in a subclass.");
+    }
+}
diff --git a/android/content/ContextWrapper.java b/android/content/ContextWrapper.java
new file mode 100644
index 0000000..5dc41e4
--- /dev/null
+++ b/android/content/ContextWrapper.java
@@ -0,0 +1,1156 @@
+/*
+ * 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.content;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.app.IApplicationThread;
+import android.app.IServiceConnection;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.res.AssetManager;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.database.DatabaseErrorHandler;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteDatabase.CursorFactory;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.view.Display;
+import android.view.DisplayAdjustments;
+import android.view.WindowManager.LayoutParams.WindowType;
+import android.view.autofill.AutofillManager.AutofillClient;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.concurrent.Executor;
+
+/**
+ * Proxying implementation of Context that simply delegates all of its calls to
+ * another Context.  Can be subclassed to modify behavior without changing
+ * the original Context.
+ */
+public class ContextWrapper extends Context {
+    @UnsupportedAppUsage
+    Context mBase;
+
+    public ContextWrapper(Context base) {
+        mBase = base;
+    }
+
+    /**
+     * Set the base context for this ContextWrapper.  All calls will then be
+     * delegated to the base context.  Throws
+     * IllegalStateException if a base context has already been set.
+     *
+     * @param base The new base context for this wrapper.
+     */
+    protected void attachBaseContext(Context base) {
+        if (mBase != null) {
+            throw new IllegalStateException("Base context already set");
+        }
+        mBase = base;
+    }
+
+    /**
+     * @return the base context as set by the constructor or setBaseContext
+     */
+    public Context getBaseContext() {
+        return mBase;
+    }
+
+    @Override
+    public AssetManager getAssets() {
+        return mBase.getAssets();
+    }
+
+    @Override
+    public Resources getResources() {
+        return mBase.getResources();
+    }
+
+    @Override
+    public PackageManager getPackageManager() {
+        return mBase.getPackageManager();
+    }
+
+    @Override
+    public ContentResolver getContentResolver() {
+        return mBase.getContentResolver();
+    }
+
+    @Override
+    public Looper getMainLooper() {
+        return mBase.getMainLooper();
+    }
+
+    @Override
+    public Executor getMainExecutor() {
+        return mBase.getMainExecutor();
+    }
+
+    @Override
+    public Context getApplicationContext() {
+        return mBase.getApplicationContext();
+    }
+
+    @Override
+    public void setTheme(int resid) {
+        mBase.setTheme(resid);
+    }
+
+    /** @hide */
+    @Override
+    @UnsupportedAppUsage
+    public int getThemeResId() {
+        return mBase.getThemeResId();
+    }
+
+    @Override
+    public Resources.Theme getTheme() {
+        return mBase.getTheme();
+    }
+
+    @Override
+    public ClassLoader getClassLoader() {
+        return mBase.getClassLoader();
+    }
+
+    @Override
+    public String getPackageName() {
+        return mBase.getPackageName();
+    }
+
+    /** @hide */
+    @Override
+    @UnsupportedAppUsage
+    public String getBasePackageName() {
+        return mBase.getBasePackageName();
+    }
+
+    /** @hide */
+    @Override
+    public String getOpPackageName() {
+        return mBase.getOpPackageName();
+    }
+
+    /** @hide */
+    @Override
+    public @Nullable String getAttributionTag() {
+        return mBase.getAttributionTag();
+    }
+
+    @Override
+    public ApplicationInfo getApplicationInfo() {
+        return mBase.getApplicationInfo();
+    }
+
+    @Override
+    public String getPackageResourcePath() {
+        return mBase.getPackageResourcePath();
+    }
+
+    @Override
+    public String getPackageCodePath() {
+        return mBase.getPackageCodePath();
+    }
+
+    @Override
+    public SharedPreferences getSharedPreferences(String name, int mode) {
+        return mBase.getSharedPreferences(name, mode);
+    }
+
+    /** @removed */
+    @Override
+    public SharedPreferences getSharedPreferences(File file, int mode) {
+        return mBase.getSharedPreferences(file, mode);
+    }
+
+    /** @hide */
+    @Override
+    public void reloadSharedPreferences() {
+        mBase.reloadSharedPreferences();
+    }
+
+    @Override
+    public boolean moveSharedPreferencesFrom(Context sourceContext, String name) {
+        return mBase.moveSharedPreferencesFrom(sourceContext, name);
+    }
+
+    @Override
+    public boolean deleteSharedPreferences(String name) {
+        return mBase.deleteSharedPreferences(name);
+    }
+
+    @Override
+    public FileInputStream openFileInput(String name)
+            throws FileNotFoundException {
+        return mBase.openFileInput(name);
+    }
+
+    @Override
+    public FileOutputStream openFileOutput(String name, int mode)
+            throws FileNotFoundException {
+        return mBase.openFileOutput(name, mode);
+    }
+
+    @Override
+    public boolean deleteFile(String name) {
+        return mBase.deleteFile(name);
+    }
+
+    @Override
+    public File getFileStreamPath(String name) {
+        return mBase.getFileStreamPath(name);
+    }
+
+    /** @removed */
+    @Override
+    public File getSharedPreferencesPath(String name) {
+        return mBase.getSharedPreferencesPath(name);
+    }
+
+    @Override
+    public String[] fileList() {
+        return mBase.fileList();
+    }
+
+    @Override
+    public File getDataDir() {
+        return mBase.getDataDir();
+    }
+
+    @Override
+    public File getFilesDir() {
+        return mBase.getFilesDir();
+    }
+
+    /**
+     * {@inheritDoc Context#getCrateDir()}
+     * @hide
+     */
+    @NonNull
+    @Override
+    public File getCrateDir(@NonNull String cratedId) {
+        return mBase.getCrateDir(cratedId);
+    }
+
+    @Override
+    public File getNoBackupFilesDir() {
+        return mBase.getNoBackupFilesDir();
+    }
+
+    @Override
+    public File getExternalFilesDir(String type) {
+        return mBase.getExternalFilesDir(type);
+    }
+
+    @Override
+    public File[] getExternalFilesDirs(String type) {
+        return mBase.getExternalFilesDirs(type);
+    }
+
+    @Override
+    public File getObbDir() {
+        return mBase.getObbDir();
+    }
+
+    @Override
+    public File[] getObbDirs() {
+        return mBase.getObbDirs();
+    }
+
+    @Override
+    public File getCacheDir() {
+        return mBase.getCacheDir();
+    }
+
+    @Override
+    public File getCodeCacheDir() {
+        return mBase.getCodeCacheDir();
+    }
+
+    @Override
+    public File getExternalCacheDir() {
+        return mBase.getExternalCacheDir();
+    }
+
+    @Override
+    public File[] getExternalCacheDirs() {
+        return mBase.getExternalCacheDirs();
+    }
+
+    @Override
+    public File[] getExternalMediaDirs() {
+        return mBase.getExternalMediaDirs();
+    }
+
+    @Override
+    public File getDir(String name, int mode) {
+        return mBase.getDir(name, mode);
+    }
+
+
+    /** @hide **/
+    @Override
+    public File getPreloadsFileCache() {
+        return mBase.getPreloadsFileCache();
+    }
+
+    @Override
+    public SQLiteDatabase openOrCreateDatabase(String name, int mode, CursorFactory factory) {
+        return mBase.openOrCreateDatabase(name, mode, factory);
+    }
+
+    @Override
+    public SQLiteDatabase openOrCreateDatabase(String name, int mode, CursorFactory factory,
+            DatabaseErrorHandler errorHandler) {
+        return mBase.openOrCreateDatabase(name, mode, factory, errorHandler);
+    }
+
+    @Override
+    public boolean moveDatabaseFrom(Context sourceContext, String name) {
+        return mBase.moveDatabaseFrom(sourceContext, name);
+    }
+
+    @Override
+    public boolean deleteDatabase(String name) {
+        return mBase.deleteDatabase(name);
+    }
+
+    @Override
+    public File getDatabasePath(String name) {
+        return mBase.getDatabasePath(name);
+    }
+
+    @Override
+    public String[] databaseList() {
+        return mBase.databaseList();
+    }
+
+    @Override
+    @Deprecated
+    public Drawable getWallpaper() {
+        return mBase.getWallpaper();
+    }
+
+    @Override
+    @Deprecated
+    public Drawable peekWallpaper() {
+        return mBase.peekWallpaper();
+    }
+
+    @Override
+    @Deprecated
+    public int getWallpaperDesiredMinimumWidth() {
+        return mBase.getWallpaperDesiredMinimumWidth();
+    }
+
+    @Override
+    @Deprecated
+    public int getWallpaperDesiredMinimumHeight() {
+        return mBase.getWallpaperDesiredMinimumHeight();
+    }
+
+    @Override
+    @Deprecated
+    public void setWallpaper(Bitmap bitmap) throws IOException {
+        mBase.setWallpaper(bitmap);
+    }
+
+    @Override
+    @Deprecated
+    public void setWallpaper(InputStream data) throws IOException {
+        mBase.setWallpaper(data);
+    }
+
+    @Override
+    @Deprecated
+    public void clearWallpaper() throws IOException {
+        mBase.clearWallpaper();
+    }
+
+    @Override
+    public void startActivity(Intent intent) {
+        mBase.startActivity(intent);
+    }
+
+    /** @hide */
+    @Override
+    public void startActivityAsUser(Intent intent, UserHandle user) {
+        mBase.startActivityAsUser(intent, user);
+    }
+
+    /** @hide **/
+    public void startActivityForResult(
+            String who, Intent intent, int requestCode, Bundle options) {
+        mBase.startActivityForResult(who, intent, requestCode, options);
+    }
+
+    /** @hide **/
+    public boolean canStartActivityForResult() {
+        return mBase.canStartActivityForResult();
+    }
+
+    @Override
+    public void startActivity(Intent intent, Bundle options) {
+        mBase.startActivity(intent, options);
+    }
+
+    /** @hide */
+    @Override
+    public void startActivityAsUser(Intent intent, Bundle options, UserHandle user) {
+        mBase.startActivityAsUser(intent, options, user);
+    }
+
+    @Override
+    public void startActivities(Intent[] intents) {
+        mBase.startActivities(intents);
+    }
+
+    @Override
+    public void startActivities(Intent[] intents, Bundle options) {
+        mBase.startActivities(intents, options);
+    }
+
+    /** @hide */
+    @Override
+    public int startActivitiesAsUser(Intent[] intents, Bundle options, UserHandle userHandle) {
+        return mBase.startActivitiesAsUser(intents, options, userHandle);
+    }
+
+    @Override
+    public void startIntentSender(IntentSender intent,
+            Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags)
+            throws IntentSender.SendIntentException {
+        mBase.startIntentSender(intent, fillInIntent, flagsMask,
+                flagsValues, extraFlags);
+    }
+
+    @Override
+    public void startIntentSender(IntentSender intent,
+            Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags,
+            Bundle options) throws IntentSender.SendIntentException {
+        mBase.startIntentSender(intent, fillInIntent, flagsMask,
+                flagsValues, extraFlags, options);
+    }
+
+    @Override
+    public void sendBroadcast(Intent intent) {
+        mBase.sendBroadcast(intent);
+    }
+
+    @Override
+    public void sendBroadcast(Intent intent, String receiverPermission) {
+        mBase.sendBroadcast(intent, receiverPermission);
+    }
+
+    /** @hide */
+    @Override
+    public void sendBroadcastMultiplePermissions(@NonNull Intent intent,
+            @NonNull String[] receiverPermissions) {
+        mBase.sendBroadcastMultiplePermissions(intent, receiverPermissions);
+    }
+
+    /** @hide */
+    @Override
+    public void sendBroadcastAsUserMultiplePermissions(Intent intent, UserHandle user,
+            String[] receiverPermissions) {
+        mBase.sendBroadcastAsUserMultiplePermissions(intent, user, receiverPermissions);
+    }
+
+    /** @hide */
+    @SystemApi
+    @Override
+    public void sendBroadcast(Intent intent, String receiverPermission, Bundle options) {
+        mBase.sendBroadcast(intent, receiverPermission, options);
+    }
+
+    /** @hide */
+    @Override
+    public void sendBroadcast(Intent intent, String receiverPermission, int appOp) {
+        mBase.sendBroadcast(intent, receiverPermission, appOp);
+    }
+
+    @Override
+    public void sendOrderedBroadcast(Intent intent,
+            String receiverPermission) {
+        mBase.sendOrderedBroadcast(intent, receiverPermission);
+    }
+
+    @Override
+    public void sendOrderedBroadcast(
+            Intent intent, String receiverPermission, BroadcastReceiver resultReceiver,
+            Handler scheduler, int initialCode, String initialData,
+            Bundle initialExtras) {
+        mBase.sendOrderedBroadcast(intent, receiverPermission,
+                resultReceiver, scheduler, initialCode,
+                initialData, initialExtras);
+    }
+
+    /** @hide */
+    @SystemApi
+    @Override
+    public void sendOrderedBroadcast(
+            Intent intent, String receiverPermission, Bundle options,
+            BroadcastReceiver resultReceiver,
+            Handler scheduler, int initialCode, String initialData,
+            Bundle initialExtras) {
+        mBase.sendOrderedBroadcast(intent, receiverPermission,
+                options, resultReceiver, scheduler, initialCode,
+                initialData, initialExtras);
+    }
+
+    /** @hide */
+    @Override
+    public void sendOrderedBroadcast(
+            Intent intent, String receiverPermission, int appOp, BroadcastReceiver resultReceiver,
+            Handler scheduler, int initialCode, String initialData,
+            Bundle initialExtras) {
+        mBase.sendOrderedBroadcast(intent, receiverPermission, appOp,
+                resultReceiver, scheduler, initialCode,
+                initialData, initialExtras);
+    }
+
+    @Override
+    public void sendBroadcastAsUser(Intent intent, UserHandle user) {
+        mBase.sendBroadcastAsUser(intent, user);
+    }
+
+    @Override
+    public void sendBroadcastAsUser(Intent intent, UserHandle user,
+            String receiverPermission) {
+        mBase.sendBroadcastAsUser(intent, user, receiverPermission);
+    }
+
+    /** @hide */
+    @Override
+    public void sendBroadcastAsUser(Intent intent, UserHandle user,
+            String receiverPermission, Bundle options) {
+        mBase.sendBroadcastAsUser(intent, user, receiverPermission, options);
+    }
+
+    /** @hide */
+    @Override
+    public void sendBroadcastAsUser(Intent intent, UserHandle user,
+            String receiverPermission, int appOp) {
+        mBase.sendBroadcastAsUser(intent, user, receiverPermission, appOp);
+    }
+
+    @Override
+    public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
+            String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler,
+            int initialCode, String initialData, Bundle initialExtras) {
+        mBase.sendOrderedBroadcastAsUser(intent, user, receiverPermission, resultReceiver,
+                scheduler, initialCode, initialData, initialExtras);
+    }
+
+    /** @hide */
+    @Override
+    public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
+            String receiverPermission, int appOp, BroadcastReceiver resultReceiver,
+            Handler scheduler, int initialCode, String initialData, Bundle initialExtras) {
+        mBase.sendOrderedBroadcastAsUser(intent, user, receiverPermission, appOp, resultReceiver,
+                scheduler, initialCode, initialData, initialExtras);
+    }
+
+    /** @hide */
+    @Override
+    public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
+            String receiverPermission, int appOp, Bundle options, BroadcastReceiver resultReceiver,
+            Handler scheduler, int initialCode, String initialData, Bundle initialExtras) {
+        mBase.sendOrderedBroadcastAsUser(intent, user, receiverPermission, appOp, options,
+                resultReceiver, scheduler, initialCode, initialData, initialExtras);
+    }
+
+    @Override
+    public void sendOrderedBroadcast(@RequiresPermission @NonNull Intent intent,
+            @Nullable String receiverPermission, @Nullable String receiverAppOp,
+            @Nullable BroadcastReceiver resultReceiver, @Nullable Handler scheduler,
+            int initialCode, @Nullable String initialData, @Nullable Bundle initialExtras) {
+        mBase.sendOrderedBroadcast(intent, receiverPermission, receiverAppOp, resultReceiver,
+                scheduler, initialCode, initialData, initialExtras);
+    }
+
+    @Override
+    public void sendOrderedBroadcast(@RequiresPermission @NonNull Intent intent, int initialCode,
+            @Nullable String receiverPermission, @Nullable String receiverAppOp,
+            @Nullable BroadcastReceiver resultReceiver, @Nullable Handler scheduler,
+            @Nullable String initialData, @Nullable Bundle initialExtras,
+            @Nullable Bundle options) {
+        mBase.sendOrderedBroadcast(intent, initialCode, receiverPermission, receiverAppOp,
+                resultReceiver, scheduler, initialData, initialExtras, options);
+    }
+
+    @Override
+    @Deprecated
+    public void sendStickyBroadcast(Intent intent) {
+        mBase.sendStickyBroadcast(intent);
+    }
+
+    @Override
+    @Deprecated
+    public void sendStickyOrderedBroadcast(
+        Intent intent, BroadcastReceiver resultReceiver,
+        Handler scheduler, int initialCode, String initialData,
+        Bundle initialExtras) {
+        mBase.sendStickyOrderedBroadcast(intent,
+                resultReceiver, scheduler, initialCode,
+                initialData, initialExtras);
+    }
+
+    @Override
+    @Deprecated
+    public void removeStickyBroadcast(Intent intent) {
+        mBase.removeStickyBroadcast(intent);
+    }
+
+    @Override
+    @Deprecated
+    public void sendStickyBroadcastAsUser(Intent intent, UserHandle user) {
+        mBase.sendStickyBroadcastAsUser(intent, user);
+    }
+
+    /** @hide */
+    @Override
+    @Deprecated
+    public void sendStickyBroadcastAsUser(Intent intent, UserHandle user, Bundle options) {
+        mBase.sendStickyBroadcastAsUser(intent, user, options);
+    }
+
+    @Override
+    @Deprecated
+    public void sendStickyOrderedBroadcastAsUser(Intent intent,
+            UserHandle user, BroadcastReceiver resultReceiver,
+            Handler scheduler, int initialCode, String initialData,
+            Bundle initialExtras) {
+        mBase.sendStickyOrderedBroadcastAsUser(intent, user, resultReceiver,
+                scheduler, initialCode, initialData, initialExtras);
+    }
+
+    @Override
+    @Deprecated
+    public void removeStickyBroadcastAsUser(Intent intent, UserHandle user) {
+        mBase.removeStickyBroadcastAsUser(intent, user);
+    }
+
+    @Override
+    public Intent registerReceiver(
+        BroadcastReceiver receiver, IntentFilter filter) {
+        return mBase.registerReceiver(receiver, filter);
+    }
+
+    @Override
+    public Intent registerReceiver(
+        BroadcastReceiver receiver, IntentFilter filter, int flags) {
+        return mBase.registerReceiver(receiver, filter, flags);
+    }
+
+    @Override
+    public Intent registerReceiver(
+        BroadcastReceiver receiver, IntentFilter filter,
+        String broadcastPermission, Handler scheduler) {
+        return mBase.registerReceiver(receiver, filter, broadcastPermission,
+                scheduler);
+    }
+
+    @Override
+    public Intent registerReceiver(
+        BroadcastReceiver receiver, IntentFilter filter,
+        String broadcastPermission, Handler scheduler, int flags) {
+        return mBase.registerReceiver(receiver, filter, broadcastPermission,
+                scheduler, flags);
+    }
+
+    /** @hide */
+    @Override
+    @Nullable
+    public Intent registerReceiverForAllUsers(@Nullable BroadcastReceiver receiver,
+            @NonNull IntentFilter filter, @Nullable String broadcastPermission,
+            @Nullable Handler scheduler) {
+        return mBase.registerReceiverForAllUsers(receiver, filter, broadcastPermission,
+                scheduler);
+    }
+
+    /** @hide */
+    @Override
+    @UnsupportedAppUsage
+    public Intent registerReceiverAsUser(
+        BroadcastReceiver receiver, UserHandle user, IntentFilter filter,
+        String broadcastPermission, Handler scheduler) {
+        return mBase.registerReceiverAsUser(receiver, user, filter, broadcastPermission,
+                scheduler);
+    }
+
+    @Override
+    public void unregisterReceiver(BroadcastReceiver receiver) {
+        mBase.unregisterReceiver(receiver);
+    }
+
+    @Override
+    public ComponentName startService(Intent service) {
+        return mBase.startService(service);
+    }
+
+    @Override
+    public ComponentName startForegroundService(Intent service) {
+        return mBase.startForegroundService(service);
+    }
+
+    @Override
+    public boolean stopService(Intent name) {
+        return mBase.stopService(name);
+    }
+
+    /** @hide */
+    @Override
+    @UnsupportedAppUsage
+    public ComponentName startServiceAsUser(Intent service, UserHandle user) {
+        return mBase.startServiceAsUser(service, user);
+    }
+
+    /** @hide */
+    @Override
+    @UnsupportedAppUsage
+    public ComponentName startForegroundServiceAsUser(Intent service, UserHandle user) {
+        return mBase.startForegroundServiceAsUser(service, user);
+    }
+
+    /** @hide */
+    @Override
+    public boolean stopServiceAsUser(Intent name, UserHandle user) {
+        return mBase.stopServiceAsUser(name, user);
+    }
+
+    @Override
+    public boolean bindService(Intent service, ServiceConnection conn,
+            int flags) {
+        return mBase.bindService(service, conn, flags);
+    }
+
+    @Override
+    public boolean bindService(Intent service, int flags, Executor executor,
+            ServiceConnection conn) {
+        return mBase.bindService(service, flags, executor, conn);
+    }
+
+    @Override
+    public boolean bindIsolatedService(Intent service, int flags, String instanceName,
+            Executor executor, ServiceConnection conn) {
+        return mBase.bindIsolatedService(service, flags, instanceName, executor, conn);
+    }
+
+    /** @hide */
+    @Override
+    public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags,
+            UserHandle user) {
+        return mBase.bindServiceAsUser(service, conn, flags, user);
+    }
+
+    /** @hide */
+    @Override
+    public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags,
+            Handler handler, UserHandle user) {
+        return mBase.bindServiceAsUser(service, conn, flags, handler, user);
+    }
+
+    @Override
+    public void updateServiceGroup(ServiceConnection conn, int group, int importance) {
+        mBase.updateServiceGroup(conn, group, importance);
+    }
+
+    @Override
+    public void unbindService(ServiceConnection conn) {
+        mBase.unbindService(conn);
+    }
+
+    @Override
+    public boolean startInstrumentation(ComponentName className,
+            String profileFile, Bundle arguments) {
+        return mBase.startInstrumentation(className, profileFile, arguments);
+    }
+
+    @Override
+    public Object getSystemService(String name) {
+        return mBase.getSystemService(name);
+    }
+
+    @Override
+    public String getSystemServiceName(Class<?> serviceClass) {
+        return mBase.getSystemServiceName(serviceClass);
+    }
+
+    @Override
+    public int checkPermission(String permission, int pid, int uid) {
+        return mBase.checkPermission(permission, pid, uid);
+    }
+
+    /** @hide */
+    @Override
+    public int checkPermission(String permission, int pid, int uid, IBinder callerToken) {
+        return mBase.checkPermission(permission, pid, uid, callerToken);
+    }
+
+    @Override
+    public int checkCallingPermission(String permission) {
+        return mBase.checkCallingPermission(permission);
+    }
+
+    @Override
+    public int checkCallingOrSelfPermission(String permission) {
+        return mBase.checkCallingOrSelfPermission(permission);
+    }
+
+    @Override
+    public int checkSelfPermission(String permission) {
+       return mBase.checkSelfPermission(permission);
+    }
+
+    @Override
+    public void enforcePermission(
+            String permission, int pid, int uid, String message) {
+        mBase.enforcePermission(permission, pid, uid, message);
+    }
+
+    @Override
+    public void enforceCallingPermission(String permission, String message) {
+        mBase.enforceCallingPermission(permission, message);
+    }
+
+    @Override
+    public void enforceCallingOrSelfPermission(
+            String permission, String message) {
+        mBase.enforceCallingOrSelfPermission(permission, message);
+    }
+
+    @Override
+    public void grantUriPermission(String toPackage, Uri uri, int modeFlags) {
+        mBase.grantUriPermission(toPackage, uri, modeFlags);
+    }
+
+    @Override
+    public void revokeUriPermission(Uri uri, int modeFlags) {
+        mBase.revokeUriPermission(uri, modeFlags);
+    }
+
+    @Override
+    public void revokeUriPermission(String targetPackage, Uri uri, int modeFlags) {
+        mBase.revokeUriPermission(targetPackage, uri, modeFlags);
+    }
+
+    @Override
+    public int checkUriPermission(Uri uri, int pid, int uid, int modeFlags) {
+        return mBase.checkUriPermission(uri, pid, uid, modeFlags);
+    }
+
+    /** @hide */
+    @Override
+    public int checkUriPermission(Uri uri, int pid, int uid, int modeFlags, IBinder callerToken) {
+        return mBase.checkUriPermission(uri, pid, uid, modeFlags, callerToken);
+    }
+
+    @Override
+    public int checkCallingUriPermission(Uri uri, int modeFlags) {
+        return mBase.checkCallingUriPermission(uri, modeFlags);
+    }
+
+    @Override
+    public int checkCallingOrSelfUriPermission(Uri uri, int modeFlags) {
+        return mBase.checkCallingOrSelfUriPermission(uri, modeFlags);
+    }
+
+    @Override
+    public int checkUriPermission(Uri uri, String readPermission,
+            String writePermission, int pid, int uid, int modeFlags) {
+        return mBase.checkUriPermission(uri, readPermission, writePermission,
+                pid, uid, modeFlags);
+    }
+
+    @Override
+    public void enforceUriPermission(
+            Uri uri, int pid, int uid, int modeFlags, String message) {
+        mBase.enforceUriPermission(uri, pid, uid, modeFlags, message);
+    }
+
+    @Override
+    public void enforceCallingUriPermission(
+            Uri uri, int modeFlags, String message) {
+        mBase.enforceCallingUriPermission(uri, modeFlags, message);
+    }
+
+    @Override
+    public void enforceCallingOrSelfUriPermission(
+            Uri uri, int modeFlags, String message) {
+        mBase.enforceCallingOrSelfUriPermission(uri, modeFlags, message);
+    }
+
+    @Override
+    public void enforceUriPermission(
+            Uri uri, String readPermission, String writePermission,
+            int pid, int uid, int modeFlags, String message) {
+        mBase.enforceUriPermission(
+                uri, readPermission, writePermission, pid, uid, modeFlags,
+                message);
+    }
+
+    @Override
+    public Context createPackageContext(String packageName, int flags)
+        throws PackageManager.NameNotFoundException {
+        return mBase.createPackageContext(packageName, flags);
+    }
+
+    /** @hide */
+    @Override
+    public Context createPackageContextAsUser(String packageName, int flags, UserHandle user)
+            throws PackageManager.NameNotFoundException {
+        return mBase.createPackageContextAsUser(packageName, flags, user);
+    }
+
+    /** @hide */
+    @Override
+    public Context createContextAsUser(UserHandle user, @CreatePackageOptions int flags) {
+        return mBase.createContextAsUser(user, flags);
+    }
+
+    /** @hide */
+    @Override
+    @UnsupportedAppUsage
+    public Context createApplicationContext(ApplicationInfo application,
+            int flags) throws PackageManager.NameNotFoundException {
+        return mBase.createApplicationContext(application, flags);
+    }
+
+    /** @hide */
+    @Override
+    public Context createContextForSplit(String splitName)
+            throws PackageManager.NameNotFoundException {
+        return mBase.createContextForSplit(splitName);
+    }
+
+    /** @hide */
+    @Override
+    public int getUserId() {
+        return mBase.getUserId();
+    }
+
+    /** @hide */
+    @Override
+    public UserHandle getUser() {
+        return mBase.getUser();
+    }
+
+    @Override
+    public Context createConfigurationContext(Configuration overrideConfiguration) {
+        return mBase.createConfigurationContext(overrideConfiguration);
+    }
+
+    @Override
+    public Context createDisplayContext(Display display) {
+        return mBase.createDisplayContext(display);
+    }
+
+    @Override
+    @NonNull
+    public Context createWindowContext(@WindowType int type, @Nullable Bundle options) {
+        return mBase.createWindowContext(type, options);
+    }
+
+    @Override
+    public @NonNull Context createAttributionContext(@Nullable String attributionTag) {
+        return mBase.createAttributionContext(attributionTag);
+    }
+
+    @Override
+    public boolean isRestricted() {
+        return mBase.isRestricted();
+    }
+
+    /** @hide */
+    @Override
+    public DisplayAdjustments getDisplayAdjustments(int displayId) {
+        return mBase.getDisplayAdjustments(displayId);
+    }
+
+    @Override
+    public @Nullable Display getDisplay() {
+        return mBase.getDisplay();
+    }
+
+    /** @hide */
+    @Override
+    public @Nullable Display getDisplayNoVerify() {
+        return mBase.getDisplayNoVerify();
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public int getDisplayId() {
+        return mBase.getDisplayId();
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public void updateDisplay(int displayId) {
+        mBase.updateDisplay(displayId);
+    }
+
+    @Override
+    public Context createDeviceProtectedStorageContext() {
+        return mBase.createDeviceProtectedStorageContext();
+    }
+
+    /** {@hide} */
+    @SystemApi
+    @Override
+    public Context createCredentialProtectedStorageContext() {
+        return mBase.createCredentialProtectedStorageContext();
+    }
+
+    @Override
+    public boolean isDeviceProtectedStorage() {
+        return mBase.isDeviceProtectedStorage();
+    }
+
+    /** {@hide} */
+    @SystemApi
+    @Override
+    public boolean isCredentialProtectedStorage() {
+        return mBase.isCredentialProtectedStorage();
+    }
+
+    /** {@hide} */
+    @Override
+    public boolean canLoadUnsafeResources() {
+        return mBase.canLoadUnsafeResources();
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public IBinder getActivityToken() {
+        return mBase.getActivityToken();
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public IServiceConnection getServiceDispatcher(ServiceConnection conn, Handler handler,
+            int flags) {
+        return mBase.getServiceDispatcher(conn, handler, flags);
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public IApplicationThread getIApplicationThread() {
+        return mBase.getIApplicationThread();
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public Handler getMainThreadHandler() {
+        return mBase.getMainThreadHandler();
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public int getNextAutofillId() {
+        return mBase.getNextAutofillId();
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public AutofillClient getAutofillClient() {
+        return mBase.getAutofillClient();
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public void setAutofillClient(AutofillClient client) {
+        mBase.setAutofillClient(client);
+    }
+
+    /** @hide */
+    @Override
+    public AutofillOptions getAutofillOptions() {
+        return mBase == null ? null : mBase.getAutofillOptions();
+    }
+
+    /** @hide */
+    @Override
+    public void setAutofillOptions(AutofillOptions options) {
+        if (mBase != null) {
+            mBase.setAutofillOptions(options);
+        }
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public ContentCaptureOptions getContentCaptureOptions() {
+        return mBase == null ? null : mBase.getContentCaptureOptions();
+    }
+
+    /**
+     * @hide
+     */
+    @TestApi
+    @Override
+    public void setContentCaptureOptions(ContentCaptureOptions options) {
+        if (mBase != null) {
+            mBase.setContentCaptureOptions(options);
+        }
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public boolean isUiContext() {
+        return mBase.isUiContext();
+    }
+}
diff --git a/android/content/CursorEntityIterator.java b/android/content/CursorEntityIterator.java
new file mode 100644
index 0000000..952366d
--- /dev/null
+++ b/android/content/CursorEntityIterator.java
@@ -0,0 +1,114 @@
+/*
+ * 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.content;
+
+import android.compat.annotation.UnsupportedAppUsage;
+import android.database.Cursor;
+import android.os.RemoteException;
+
+/**
+ * Abstract implementation of EntityIterator that makes it easy to wrap a cursor
+ * that can contain several consecutive rows for an entity.
+ * @hide
+ */
+public abstract class CursorEntityIterator implements EntityIterator {
+    private final Cursor mCursor;
+    private boolean mIsClosed;
+
+    /**
+     * Constructor that makes initializes the cursor such that the iterator points to the
+     * first Entity, if there are any.
+     * @param cursor the cursor that contains the rows that make up the entities
+     */
+    @UnsupportedAppUsage
+    public CursorEntityIterator(Cursor cursor) {
+        mIsClosed = false;
+        mCursor = cursor;
+        mCursor.moveToFirst();
+    }
+
+    /**
+     * Returns the entity that the cursor is currently pointing to. This must take care to advance
+     * the cursor past this entity. This will never be called if the cursor is at the end.
+     * @param cursor the cursor that contains the entity rows
+     * @return the entity that the cursor is currently pointing to
+     * @throws RemoteException if a RemoteException is caught while attempting to build the Entity
+     */
+    public abstract Entity getEntityAndIncrementCursor(Cursor cursor) throws RemoteException;
+
+    /**
+     * Returns whether there are more elements to iterate, i.e. whether the
+     * iterator is positioned in front of an element.
+     *
+     * @return {@code true} if there are more elements, {@code false} otherwise.
+     * @see EntityIterator#next()
+     */
+    public final boolean hasNext() {
+        if (mIsClosed) {
+            throw new IllegalStateException("calling hasNext() when the iterator is closed");
+        }
+
+        return !mCursor.isAfterLast();
+    }
+
+    /**
+     * Returns the next object in the iteration, i.e. returns the element in
+     * front of the iterator and advances the iterator by one position.
+     *
+     * @return the next object.
+     * @throws java.util.NoSuchElementException
+     *             if there are no more elements.
+     * @see EntityIterator#hasNext()
+     */
+    public Entity next() {
+        if (mIsClosed) {
+            throw new IllegalStateException("calling next() when the iterator is closed");
+        }
+        if (!hasNext()) {
+            throw new IllegalStateException("you may only call next() if hasNext() is true");
+        }
+
+        try {
+            return getEntityAndIncrementCursor(mCursor);
+        } catch (RemoteException e) {
+            throw new RuntimeException("caught a remote exception, this process will die soon", e);
+        }
+    }
+
+    public void remove() {
+        throw new UnsupportedOperationException("remove not supported by EntityIterators");
+    }
+
+    public final void reset() {
+        if (mIsClosed) {
+            throw new IllegalStateException("calling reset() when the iterator is closed");
+        }
+        mCursor.moveToFirst();
+    }
+
+    /**
+     * Indicates that this iterator is no longer needed and that any associated resources
+     * may be released (such as a SQLite cursor).
+     */
+    public final void close() {
+        if (mIsClosed) {
+            throw new IllegalStateException("closing when already closed");
+        }
+        mIsClosed = true;
+        mCursor.close();
+    }
+}
diff --git a/android/content/CursorLoader.java b/android/content/CursorLoader.java
new file mode 100644
index 0000000..4ff5cca
--- /dev/null
+++ b/android/content/CursorLoader.java
@@ -0,0 +1,250 @@
+/*
+ * 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.content;
+
+import android.compat.annotation.UnsupportedAppUsage;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.CancellationSignal;
+import android.os.OperationCanceledException;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Arrays;
+
+/**
+ * A loader that queries the {@link ContentResolver} and returns a {@link Cursor}.
+ * This class implements the {@link Loader} protocol in a standard way for
+ * querying cursors, building on {@link AsyncTaskLoader} to perform the cursor
+ * query on a background thread so that it does not block the application's UI.
+ * 
+ * <p>A CursorLoader must be built with the full information for the query to
+ * perform, either through the
+ * {@link #CursorLoader(Context, Uri, String[], String, String[], String)} or
+ * creating an empty instance with {@link #CursorLoader(Context)} and filling
+ * in the desired parameters with {@link #setUri(Uri)}, {@link #setSelection(String)},
+ * {@link #setSelectionArgs(String[])}, {@link #setSortOrder(String)},
+ * and {@link #setProjection(String[])}.
+ *
+ * @deprecated Use the <a href="{@docRoot}tools/extras/support-library.html">Support Library</a>
+ *      {@link android.support.v4.content.CursorLoader}
+ */
+@Deprecated
+public class CursorLoader extends AsyncTaskLoader<Cursor> {
+    @UnsupportedAppUsage
+    final ForceLoadContentObserver mObserver;
+
+    Uri mUri;
+    String[] mProjection;
+    String mSelection;
+    String[] mSelectionArgs;
+    String mSortOrder;
+
+    Cursor mCursor;
+    @UnsupportedAppUsage
+    CancellationSignal mCancellationSignal;
+
+    /* Runs on a worker thread */
+    @Override
+    public Cursor loadInBackground() {
+        synchronized (this) {
+            if (isLoadInBackgroundCanceled()) {
+                throw new OperationCanceledException();
+            }
+            mCancellationSignal = new CancellationSignal();
+        }
+        try {
+            Cursor cursor = getContext().getContentResolver().query(mUri, mProjection, mSelection,
+                    mSelectionArgs, mSortOrder, mCancellationSignal);
+            if (cursor != null) {
+                try {
+                    // Ensure the cursor window is filled.
+                    cursor.getCount();
+                    cursor.registerContentObserver(mObserver);
+                } catch (RuntimeException ex) {
+                    cursor.close();
+                    throw ex;
+                }
+            }
+            return cursor;
+        } finally {
+            synchronized (this) {
+                mCancellationSignal = null;
+            }
+        }
+    }
+
+    @Override
+    public void cancelLoadInBackground() {
+        super.cancelLoadInBackground();
+
+        synchronized (this) {
+            if (mCancellationSignal != null) {
+                mCancellationSignal.cancel();
+            }
+        }
+    }
+
+    /* Runs on the UI thread */
+    @Override
+    public void deliverResult(Cursor cursor) {
+        if (isReset()) {
+            // An async query came in while the loader is stopped
+            if (cursor != null) {
+                cursor.close();
+            }
+            return;
+        }
+        Cursor oldCursor = mCursor;
+        mCursor = cursor;
+
+        if (isStarted()) {
+            super.deliverResult(cursor);
+        }
+
+        if (oldCursor != null && oldCursor != cursor && !oldCursor.isClosed()) {
+            oldCursor.close();
+        }
+    }
+
+    /**
+     * Creates an empty unspecified CursorLoader.  You must follow this with
+     * calls to {@link #setUri(Uri)}, {@link #setSelection(String)}, etc
+     * to specify the query to perform.
+     */
+    public CursorLoader(Context context) {
+        super(context);
+        mObserver = new ForceLoadContentObserver();
+    }
+
+    /**
+     * Creates a fully-specified CursorLoader.  See
+     * {@link ContentResolver#query(Uri, String[], String, String[], String)
+     * ContentResolver.query()} for documentation on the meaning of the
+     * parameters.  These will be passed as-is to that call.
+     */
+    public CursorLoader(Context context, Uri uri, String[] projection, String selection,
+            String[] selectionArgs, String sortOrder) {
+        super(context);
+        mObserver = new ForceLoadContentObserver();
+        mUri = uri;
+        mProjection = projection;
+        mSelection = selection;
+        mSelectionArgs = selectionArgs;
+        mSortOrder = sortOrder;
+    }
+
+    /**
+     * Starts an asynchronous load of the data. When the result is ready the callbacks
+     * will be called on the UI thread. If a previous load has been completed and is still valid
+     * the result may be passed to the callbacks immediately.
+     *
+     * Must be called from the UI thread
+     */
+    @Override
+    protected void onStartLoading() {
+        if (mCursor != null) {
+            deliverResult(mCursor);
+        }
+        if (takeContentChanged() || mCursor == null) {
+            forceLoad();
+        }
+    }
+
+    /**
+     * Must be called from the UI thread
+     */
+    @Override
+    protected void onStopLoading() {
+        // Attempt to cancel the current load task if possible.
+        cancelLoad();
+    }
+
+    @Override
+    public void onCanceled(Cursor cursor) {
+        if (cursor != null && !cursor.isClosed()) {
+            cursor.close();
+        }
+    }
+
+    @Override
+    protected void onReset() {
+        super.onReset();
+        
+        // Ensure the loader is stopped
+        onStopLoading();
+
+        if (mCursor != null && !mCursor.isClosed()) {
+            mCursor.close();
+        }
+        mCursor = null;
+    }
+
+    public Uri getUri() {
+        return mUri;
+    }
+
+    public void setUri(Uri uri) {
+        mUri = uri;
+    }
+
+    public String[] getProjection() {
+        return mProjection;
+    }
+
+    public void setProjection(String[] projection) {
+        mProjection = projection;
+    }
+
+    public String getSelection() {
+        return mSelection;
+    }
+
+    public void setSelection(String selection) {
+        mSelection = selection;
+    }
+
+    public String[] getSelectionArgs() {
+        return mSelectionArgs;
+    }
+
+    public void setSelectionArgs(String[] selectionArgs) {
+        mSelectionArgs = selectionArgs;
+    }
+
+    public String getSortOrder() {
+        return mSortOrder;
+    }
+
+    public void setSortOrder(String sortOrder) {
+        mSortOrder = sortOrder;
+    }
+
+    @Override
+    public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+        super.dump(prefix, fd, writer, args);
+        writer.print(prefix); writer.print("mUri="); writer.println(mUri);
+        writer.print(prefix); writer.print("mProjection=");
+                writer.println(Arrays.toString(mProjection));
+        writer.print(prefix); writer.print("mSelection="); writer.println(mSelection);
+        writer.print(prefix); writer.print("mSelectionArgs=");
+                writer.println(Arrays.toString(mSelectionArgs));
+        writer.print(prefix); writer.print("mSortOrder="); writer.println(mSortOrder);
+        writer.print(prefix); writer.print("mCursor="); writer.println(mCursor);
+        writer.print(prefix); writer.print("mContentChanged="); writer.println(mContentChanged);
+    }
+}
diff --git a/android/content/DefaultDataHandler.java b/android/content/DefaultDataHandler.java
new file mode 100644
index 0000000..863c9f6
--- /dev/null
+++ b/android/content/DefaultDataHandler.java
@@ -0,0 +1,262 @@
+/*
+ * 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.content;
+
+import android.net.Uri;
+import android.util.Xml;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Stack;
+
+/**
+ * Inserts default data from InputStream, should be in XML format.
+ * If the provider syncs data to the server, the imported data will be synced to the server.
+ * <p>Samples:</p>
+ * <br/>
+ *  Insert one row:
+ * <pre>
+ * &lt;row uri="content://contacts/people">
+ *  &lt;Col column = "name" value = "foo feebe "/>
+ *  &lt;Col column = "addr" value = "Tx"/>
+ * &lt;/row></pre>
+ * <br/>
+ * Delete, it must be in order of uri, select, and arg:
+ * <pre>
+ * &lt;del uri="content://contacts/people" select="name=? and addr=?" 
+ *  arg1 = "foo feebe" arg2 ="Tx"/></pre>
+ * <br/>
+ *  Use first row's uri to insert into another table,
+ *  content://contacts/people/1/phones:
+ * <pre>
+ * &lt;row uri="content://contacts/people">
+ *  &lt;col column = "name" value = "foo feebe"/>
+ *  &lt;col column = "addr" value = "Tx"/>
+ *  &lt;row postfix="phones">
+ *    &lt;col column="number" value="512-514-6535"/>
+ *  &lt;/row>
+ *  &lt;row postfix="phones">
+ *    &lt;col column="cell" value="512-514-6535"/>
+ *  &lt;/row>  
+ * &lt;/row></pre>
+ * <br/>
+ *  Insert multiple rows in to same table and same attributes:
+ * <pre>
+ * &lt;row uri="content://contacts/people" >
+ *  &lt;row>
+ *   &lt;col column= "name" value = "foo feebe"/>
+ *   &lt;col column= "addr" value = "Tx"/>
+ *  &lt;/row>
+ *  &lt;row>
+ *  &lt;/row>
+ * &lt;/row></pre>
+ *
+ * @hide
+ */ 
+public class DefaultDataHandler implements ContentInsertHandler {
+    private final static String ROW = "row";
+    private final static String COL = "col";
+    private final static String URI_STR = "uri";
+    private final static String POSTFIX = "postfix";
+    private final static String DEL = "del";
+    private final static String SELECT = "select";
+    private final static String ARG = "arg";   
+   
+    private Stack<Uri> mUris = new Stack<Uri>();
+    private ContentValues mValues;
+    private ContentResolver mContentResolver;   
+    
+    public void insert(ContentResolver contentResolver, InputStream in)
+            throws IOException, SAXException {
+        mContentResolver = contentResolver;
+        Xml.parse(in, Xml.Encoding.UTF_8, this);
+    }
+    
+    public void insert(ContentResolver contentResolver, String in)
+        throws SAXException {
+        mContentResolver = contentResolver;
+        Xml.parse(in, this);
+    }
+    
+    private void parseRow(Attributes atts) throws SAXException {
+        String uriStr = atts.getValue(URI_STR);
+        Uri uri;
+        if (uriStr != null) {
+            // case 1
+            uri = Uri.parse(uriStr);
+            if (uri == null) {
+                throw new SAXException("attribute " +
+                        atts.getValue(URI_STR) + " parsing failure"); 
+            }
+            
+        } else if (mUris.size() > 0){
+            // case 2
+            String postfix = atts.getValue(POSTFIX);
+            if (postfix != null) {
+                uri = Uri.withAppendedPath(mUris.lastElement(),
+                        postfix);
+            } else {
+                uri = mUris.lastElement();
+            } 
+        } else {
+            throw new SAXException("attribute parsing failure"); 
+        }
+        
+        mUris.push(uri);
+        
+    }
+    
+    private Uri insertRow() {
+        Uri u = mContentResolver.insert(mUris.lastElement(), mValues);
+        mValues = null;
+        return u;
+    }
+    
+    public void startElement(String uri, String localName, String name,
+            Attributes atts) throws SAXException {
+        if (ROW.equals(localName)) {            
+            if (mValues != null) {
+                // case 2, <Col> before <Row> insert last uri
+                if (mUris.empty()) {
+                    throw new SAXException("uri is empty");
+                }
+                Uri nextUri = insertRow();
+                if (nextUri == null) {
+                    throw new SAXException("insert to uri " + 
+                            mUris.lastElement().toString() + " failure");
+                } else {
+                    // make sure the stack lastElement save uri for more than one row
+                    mUris.pop();
+                    mUris.push(nextUri);
+                    parseRow(atts);
+                }
+            } else {
+                int attrLen = atts.getLength();
+                if (attrLen == 0) {
+                    // case 3, share same uri as last level
+                    mUris.push(mUris.lastElement());
+                } else {
+                    parseRow(atts);
+                }
+            }                
+        } else if (COL.equals(localName)) {
+            int attrLen = atts.getLength();
+            if (attrLen != 2) {
+                throw new SAXException("illegal attributes number " + attrLen);
+            }
+            String key = atts.getValue(0);
+            String value = atts.getValue(1);
+            if (key != null && key.length() > 0 && value != null && value.length() > 0) {
+                if (mValues == null) {
+                    mValues = new ContentValues();
+                }
+                mValues.put(key, value);
+            } else {
+                throw new SAXException("illegal attributes value");
+            }            
+        } else if (DEL.equals(localName)){
+            Uri u = Uri.parse(atts.getValue(URI_STR));
+            if (u == null) {
+                throw new SAXException("attribute " +
+                        atts.getValue(URI_STR) + " parsing failure"); 
+            }
+            int attrLen = atts.getLength() - 2;
+            if (attrLen > 0) {
+                String[] selectionArgs = new String[attrLen];
+                for (int i = 0; i < attrLen; i++) {
+                    selectionArgs[i] = atts.getValue(i+2);
+                }
+                mContentResolver.delete(u, atts.getValue(1), selectionArgs);
+            } else if (attrLen == 0){
+                mContentResolver.delete(u, atts.getValue(1), null);
+            } else {
+                mContentResolver.delete(u, null, null);
+            }
+            
+        } else {
+            throw new SAXException("unknown element: " + localName);
+        }
+    }
+    
+    public void endElement(String uri, String localName, String name)
+            throws SAXException {
+        if (ROW.equals(localName)) {
+            if (mUris.empty()) {
+                throw new SAXException("uri mismatch"); 
+            }
+            if (mValues != null) {
+                insertRow();
+            }
+            mUris.pop();                
+        } 
+    }
+
+
+    public void characters(char[] ch, int start, int length)
+            throws SAXException {
+        // TODO Auto-generated method stub
+
+    }
+
+    public void endDocument() throws SAXException {
+        // TODO Auto-generated method stub
+
+    }
+
+    public void endPrefixMapping(String prefix) throws SAXException {
+        // TODO Auto-generated method stub
+
+    }
+
+    public void ignorableWhitespace(char[] ch, int start, int length)
+            throws SAXException {
+        // TODO Auto-generated method stub
+
+    }
+
+    public void processingInstruction(String target, String data)
+            throws SAXException {
+        // TODO Auto-generated method stub
+
+    }
+
+    public void setDocumentLocator(Locator locator) {
+        // TODO Auto-generated method stub
+
+    }
+
+    public void skippedEntity(String name) throws SAXException {
+        // TODO Auto-generated method stub
+
+    }
+
+    public void startDocument() throws SAXException {
+        // TODO Auto-generated method stub
+
+    }
+
+    public void startPrefixMapping(String prefix, String uri)
+            throws SAXException {
+        // TODO Auto-generated method stub
+
+    }
+    
+}
diff --git a/android/content/DialogInterface.java b/android/content/DialogInterface.java
new file mode 100644
index 0000000..511f356
--- /dev/null
+++ b/android/content/DialogInterface.java
@@ -0,0 +1,157 @@
+/*
+ * 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.content;
+
+import android.view.KeyEvent;
+
+/**
+ * Interface that defines a dialog-type class that can be shown, dismissed, or
+ * canceled, and may have buttons that can be clicked.
+ */
+public interface DialogInterface {
+    /** The identifier for the positive button. */
+    int BUTTON_POSITIVE = -1;
+
+    /** The identifier for the negative button. */
+    int BUTTON_NEGATIVE = -2;
+
+    /** The identifier for the neutral button. */
+    int BUTTON_NEUTRAL = -3;
+
+    /** @deprecated Use {@link #BUTTON_POSITIVE} */
+    @Deprecated
+    int BUTTON1 = BUTTON_POSITIVE;
+
+    /** @deprecated Use {@link #BUTTON_NEGATIVE} */
+    @Deprecated
+    int BUTTON2 = BUTTON_NEGATIVE;
+
+    /** @deprecated Use {@link #BUTTON_NEUTRAL} */
+    @Deprecated
+    int BUTTON3 = BUTTON_NEUTRAL;
+
+    /**
+     * Cancels the dialog, invoking the {@link OnCancelListener}.
+     * <p>
+     * The {@link OnDismissListener} may also be called if cancellation
+     * dismisses the dialog.
+     */
+    void cancel();
+
+    /**
+     * Dismisses the dialog, invoking the {@link OnDismissListener}.
+     */
+    void dismiss();
+
+    /**
+     * Interface used to allow the creator of a dialog to run some code when the
+     * dialog is canceled.
+     * <p>
+     * This will only be called when the dialog is canceled, if the creator
+     * needs to know when it is dismissed in general, use
+     * {@link DialogInterface.OnDismissListener}.
+     */
+    interface OnCancelListener {
+        /**
+         * This method will be invoked when the dialog is canceled.
+         *
+         * @param dialog the dialog that was canceled will be passed into the
+         *               method
+         */
+        void onCancel(DialogInterface dialog);
+    }
+
+    /**
+     * Interface used to allow the creator of a dialog to run some code when the
+     * dialog is dismissed.
+     */
+    interface OnDismissListener {
+        /**
+         * This method will be invoked when the dialog is dismissed.
+         *
+         * @param dialog the dialog that was dismissed will be passed into the
+         *               method
+         */
+        void onDismiss(DialogInterface dialog);
+    }
+
+    /**
+     * Interface used to allow the creator of a dialog to run some code when the
+     * dialog is shown.
+     */
+    interface OnShowListener {
+        /**
+         * This method will be invoked when the dialog is shown.
+         *
+         * @param dialog the dialog that was shown will be passed into the
+         *               method
+         */
+        void onShow(DialogInterface dialog);
+    }
+
+    /**
+     * Interface used to allow the creator of a dialog to run some code when an
+     * item on the dialog is clicked.
+     */
+    interface OnClickListener {
+        /**
+         * This method will be invoked when a button in the dialog is clicked.
+         *
+         * @param dialog the dialog that received the click
+         * @param which the button that was clicked (ex.
+         *              {@link DialogInterface#BUTTON_POSITIVE}) or the position
+         *              of the item clicked
+         */
+        void onClick(DialogInterface dialog, int which);
+    }
+
+    /**
+     * Interface used to allow the creator of a dialog to run some code when an
+     * item in a multi-choice dialog is clicked.
+     */
+    interface OnMultiChoiceClickListener {
+        /**
+         * This method will be invoked when an item in the dialog is clicked.
+         *
+         * @param dialog the dialog where the selection was made
+         * @param which the position of the item in the list that was clicked
+         * @param isChecked {@code true} if the click checked the item, else
+         *                  {@code false}
+         */
+        void onClick(DialogInterface dialog, int which, boolean isChecked);
+    }
+
+    /**
+     * Interface definition for a callback to be invoked when a key event is
+     * dispatched to this dialog. The callback will be invoked before the key
+     * event is given to the dialog.
+     */
+    interface OnKeyListener {
+        /**
+         * Called when a key is dispatched to a dialog. This allows listeners to
+         * get a chance to respond before the dialog.
+         *
+         * @param dialog the dialog the key has been dispatched to
+         * @param keyCode the code for the physical key that was pressed
+         * @param event the KeyEvent object containing full information about
+         *              the event
+         * @return {@code true} if the listener has consumed the event,
+         *         {@code false} otherwise
+         */
+        boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event);
+    }
+}
diff --git a/android/content/Entity.java b/android/content/Entity.java
new file mode 100644
index 0000000..13137c4
--- /dev/null
+++ b/android/content/Entity.java
@@ -0,0 +1,74 @@
+/*
+ * 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.content;
+
+import android.compat.annotation.UnsupportedAppUsage;
+import android.net.Uri;
+import android.os.Build;
+
+import java.util.ArrayList;
+
+/**
+ * A representation of a item using ContentValues. It contains one top level ContentValue
+ * plus a collection of Uri, ContentValues tuples as subvalues. One example of its use
+ * is in Contacts, where the top level ContentValue contains the columns from the RawContacts
+ * table and the subvalues contain a ContentValues object for each row from the Data table that
+ * corresponds to that RawContact. The uri refers to the Data table uri for each row.
+ */
+public final class Entity {
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    final private ContentValues mValues;
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    final private ArrayList<NamedContentValues> mSubValues;
+
+    public Entity(ContentValues values) {
+        mValues = values;
+        mSubValues = new ArrayList<NamedContentValues>();
+    }
+
+    public ContentValues getEntityValues() {
+        return mValues;
+    }
+
+    public ArrayList<NamedContentValues> getSubValues() {
+        return mSubValues;
+    }
+
+    public void addSubValue(Uri uri, ContentValues values) {
+        mSubValues.add(new Entity.NamedContentValues(uri, values));
+    }
+
+    public static class NamedContentValues {
+        public final Uri uri;
+        public final ContentValues values;
+
+        public NamedContentValues(Uri uri, ContentValues values) {
+            this.uri = uri;
+            this.values = values;
+        }
+    }
+
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("Entity: ").append(getEntityValues());
+        for (Entity.NamedContentValues namedValue : getSubValues()) {
+            sb.append("\n  ").append(namedValue.uri);
+            sb.append("\n  -> ").append(namedValue.values);
+        }
+        return sb.toString();
+    }
+}
diff --git a/android/content/EntityIterator.java b/android/content/EntityIterator.java
new file mode 100644
index 0000000..55c47ba
--- /dev/null
+++ b/android/content/EntityIterator.java
@@ -0,0 +1,39 @@
+/*
+ * 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.content;
+
+import java.util.Iterator;
+
+/**
+ * A specialization of {@link Iterator} that allows iterating over a collection of
+ * {@link Entity} objects. In addition to the iteration functionality it also allows
+ * resetting the iterator back to the beginning and provides for an explicit {@link #close()}
+ * method to indicate that the iterator is no longer needed and that its resources
+ * can be released.
+ */
+public interface EntityIterator extends Iterator<Entity> {
+    /**
+     * Reset the iterator back to the beginning.
+     */
+    public void reset();
+
+    /**
+     * Indicates that this iterator is no longer needed and that any associated resources
+     * may be released (such as a SQLite cursor).
+     */
+    public void close();
+}
diff --git a/android/content/IContentProvider.java b/android/content/IContentProvider.java
new file mode 100644
index 0000000..84b0f0e
--- /dev/null
+++ b/android/content/IContentProvider.java
@@ -0,0 +1,175 @@
+/*
+ * 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.content;
+
+import android.annotation.Nullable;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.res.AssetFileDescriptor;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.ICancellationSignal;
+import android.os.IInterface;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+
+/**
+ * The ipc interface to talk to a content provider.
+ * @hide
+ */
+public interface IContentProvider extends IInterface {
+    public Cursor query(String callingPkg, @Nullable String attributionTag, Uri url,
+            @Nullable String[] projection,
+            @Nullable Bundle queryArgs, @Nullable ICancellationSignal cancellationSignal)
+            throws RemoteException;
+    public String getType(Uri url) throws RemoteException;
+
+    /**
+     * A oneway version of getType. The functionality is exactly the same, except that the
+     * call returns immediately, and the resulting type is returned when available via
+     * a binder callback.
+     */
+    void getTypeAsync(Uri uri, RemoteCallback callback) throws RemoteException;
+
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "Use {@link "
+            + "ContentProviderClient#insert(android.net.Uri, android.content.ContentValues)} "
+            + "instead")
+    public default Uri insert(String callingPkg, Uri url, ContentValues initialValues)
+            throws RemoteException {
+        return insert(callingPkg, null, url, initialValues, null);
+    }
+    public Uri insert(String callingPkg, String attributionTag, Uri url,
+            ContentValues initialValues, Bundle extras) throws RemoteException;
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "Use {@link "
+            + "ContentProviderClient#bulkInsert(android.net.Uri, android.content.ContentValues[])"
+            + "} instead")
+    public default int bulkInsert(String callingPkg, Uri url, ContentValues[] initialValues)
+            throws RemoteException {
+        return bulkInsert(callingPkg, null, url, initialValues);
+    }
+    public int bulkInsert(String callingPkg, String attributionTag, Uri url,
+            ContentValues[] initialValues) throws RemoteException;
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "Use {@link "
+            + "ContentProviderClient#delete(android.net.Uri, java.lang.String, java.lang"
+            + ".String[])} instead")
+    public default int delete(String callingPkg, Uri url, String selection, String[] selectionArgs)
+            throws RemoteException {
+        return delete(callingPkg, null, url,
+                ContentResolver.createSqlQueryBundle(selection, selectionArgs));
+    }
+    public int delete(String callingPkg, String attributionTag, Uri url, Bundle extras)
+            throws RemoteException;
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "Use {@link "
+            + "ContentProviderClient#update(android.net.Uri, android.content.ContentValues, java"
+            + ".lang.String, java.lang.String[])} instead")
+    public default int update(String callingPkg, Uri url, ContentValues values, String selection,
+            String[] selectionArgs) throws RemoteException {
+        return update(callingPkg, null, url, values,
+                ContentResolver.createSqlQueryBundle(selection, selectionArgs));
+    }
+    public int update(String callingPkg, String attributionTag, Uri url, ContentValues values,
+            Bundle extras) throws RemoteException;
+
+    public ParcelFileDescriptor openFile(String callingPkg, @Nullable String attributionTag,
+            Uri url, String mode, ICancellationSignal signal, IBinder callerToken)
+            throws RemoteException, FileNotFoundException;
+
+    public AssetFileDescriptor openAssetFile(String callingPkg, @Nullable String attributionTag,
+            Uri url, String mode, ICancellationSignal signal)
+            throws RemoteException, FileNotFoundException;
+
+    public ContentProviderResult[] applyBatch(String callingPkg, @Nullable String attributionTag,
+            String authority, ArrayList<ContentProviderOperation> operations)
+            throws RemoteException, OperationApplicationException;
+
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "Use {@link "
+            + "ContentProviderClient#call(java.lang.String, java.lang.String, android.os.Bundle)} "
+            + "instead")
+    public default Bundle call(String callingPkg, String method,
+            @Nullable String arg, @Nullable Bundle extras) throws RemoteException {
+        return call(callingPkg, null, "unknown", method, arg, extras);
+    }
+
+    public Bundle call(String callingPkg, @Nullable String attributionTag, String authority,
+            String method, @Nullable String arg, @Nullable Bundle extras) throws RemoteException;
+
+    public int checkUriPermission(String callingPkg, @Nullable String attributionTag, Uri uri,
+            int uid, int modeFlags) throws RemoteException;
+
+    public ICancellationSignal createCancellationSignal() throws RemoteException;
+
+    public Uri canonicalize(String callingPkg, @Nullable String attributionTag, Uri uri)
+            throws RemoteException;
+
+    /**
+     * A oneway version of canonicalize. The functionality is exactly the same, except that the
+     * call returns immediately, and the resulting type is returned when available via
+     * a binder callback.
+     */
+    void canonicalizeAsync(String callingPkg, @Nullable String attributionTag, Uri uri,
+            RemoteCallback callback) throws RemoteException;
+
+    public Uri uncanonicalize(String callingPkg, @Nullable String attributionTag, Uri uri)
+            throws RemoteException;
+
+    public boolean refresh(String callingPkg, @Nullable String attributionTag, Uri url,
+            @Nullable Bundle extras, ICancellationSignal cancellationSignal) throws RemoteException;
+
+    // Data interchange.
+    public String[] getStreamTypes(Uri url, String mimeTypeFilter) throws RemoteException;
+
+    public AssetFileDescriptor openTypedAssetFile(String callingPkg,
+            @Nullable String attributionTag, Uri url, String mimeType, Bundle opts,
+            ICancellationSignal signal)
+            throws RemoteException, FileNotFoundException;
+
+    /* IPC constants */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    static final String descriptor = "android.content.IContentProvider";
+
+    @UnsupportedAppUsage
+    static final int QUERY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION;
+    static final int GET_TYPE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 1;
+    static final int INSERT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 2;
+    static final int DELETE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 3;
+    static final int UPDATE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 9;
+    static final int BULK_INSERT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 12;
+    static final int OPEN_FILE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 13;
+    static final int OPEN_ASSET_FILE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 14;
+    static final int APPLY_BATCH_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 19;
+    static final int CALL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 20;
+    static final int GET_STREAM_TYPES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 21;
+    static final int OPEN_TYPED_ASSET_FILE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 22;
+    static final int CREATE_CANCELATION_SIGNAL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 23;
+    static final int CANONICALIZE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 24;
+    static final int UNCANONICALIZE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 25;
+    static final int REFRESH_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 26;
+    static final int CHECK_URI_PERMISSION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 27;
+    int GET_TYPE_ASYNC_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 28;
+    int CANONICALIZE_ASYNC_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 29;
+}
diff --git a/android/content/Intent.java b/android/content/Intent.java
new file mode 100644
index 0000000..baaf8f7
--- /dev/null
+++ b/android/content/Intent.java
@@ -0,0 +1,11414 @@
+/*
+ * 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.content;
+
+import static android.content.ContentProvider.maybeAddUserId;
+
+import android.annotation.AnyRes;
+import android.annotation.BroadcastBehavior;
+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.SuppressLint;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.app.AppGlobals;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ComponentInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ShortcutInfo;
+import android.content.pm.SuspendDialogInfo;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Rect;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.IncidentManager;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.PersistableBundle;
+import android.os.Process;
+import android.os.ResultReceiver;
+import android.os.ShellCommand;
+import android.os.StrictMode;
+import android.os.UserHandle;
+import android.os.storage.StorageManager;
+import android.provider.ContactsContract.QuickContact;
+import android.provider.DocumentsContract;
+import android.provider.DocumentsProvider;
+import android.provider.MediaStore;
+import android.provider.OpenableColumns;
+import android.telecom.PhoneAccount;
+import android.telecom.TelecomManager;
+import android.text.TextUtils;
+import android.util.ArraySet;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.Serializable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Objects;
+import java.util.Set;
+import java.util.TimeZone;
+
+/**
+ * An intent is an abstract description of an operation to be performed.  It
+ * can be used with {@link Context#startActivity(Intent) startActivity} to
+ * launch an {@link android.app.Activity},
+ * {@link android.content.Context#sendBroadcast(Intent) broadcastIntent} to
+ * send it to any interested {@link BroadcastReceiver BroadcastReceiver} components,
+ * and {@link android.content.Context#startService} or
+ * {@link android.content.Context#bindService} to communicate with a
+ * background {@link android.app.Service}.
+ *
+ * <p>An Intent provides a facility for performing late runtime binding between the code in
+ * different applications. Its most significant use is in the launching of activities, where it
+ * can be thought of as the glue between activities. It is basically a passive data structure
+ * holding an abstract description of an action to be performed.</p>
+ *
+ * <div class="special reference">
+ * <h3>Developer Guides</h3>
+ * <p>For information about how to create and resolve intents, read the
+ * <a href="{@docRoot}guide/topics/intents/intents-filters.html">Intents and Intent Filters</a>
+ * developer guide.</p>
+ * </div>
+ *
+ * <a name="IntentStructure"></a>
+ * <h3>Intent Structure</h3>
+ * <p>The primary pieces of information in an intent are:</p>
+ *
+ * <ul>
+ *   <li> <p><b>action</b> -- The general action to be performed, such as
+ *     {@link #ACTION_VIEW}, {@link #ACTION_EDIT}, {@link #ACTION_MAIN},
+ *     etc.</p>
+ *   </li>
+ *   <li> <p><b>data</b> -- The data to operate on, such as a person record
+ *     in the contacts database, expressed as a {@link android.net.Uri}.</p>
+ *   </li>
+ * </ul>
+ *
+ *
+ * <p>Some examples of action/data pairs are:</p>
+ *
+ * <ul>
+ *   <li> <p><b>{@link #ACTION_VIEW} <i>content://contacts/people/1</i></b> -- Display
+ *     information about the person whose identifier is "1".</p>
+ *   </li>
+ *   <li> <p><b>{@link #ACTION_DIAL} <i>content://contacts/people/1</i></b> -- Display
+ *     the phone dialer with the person filled in.</p>
+ *   </li>
+ *   <li> <p><b>{@link #ACTION_VIEW} <i>tel:123</i></b> -- Display
+ *     the phone dialer with the given number filled in.  Note how the
+ *     VIEW action does what is considered the most reasonable thing for
+ *     a particular URI.</p>
+ *   </li>
+ *   <li> <p><b>{@link #ACTION_DIAL} <i>tel:123</i></b> -- Display
+ *     the phone dialer with the given number filled in.</p>
+ *   </li>
+ *   <li> <p><b>{@link #ACTION_EDIT} <i>content://contacts/people/1</i></b> -- Edit
+ *     information about the person whose identifier is "1".</p>
+ *   </li>
+ *   <li> <p><b>{@link #ACTION_VIEW} <i>content://contacts/people/</i></b> -- Display
+ *     a list of people, which the user can browse through.  This example is a
+ *     typical top-level entry into the Contacts application, showing you the
+ *     list of people. Selecting a particular person to view would result in a
+ *     new intent { <b>{@link #ACTION_VIEW} <i>content://contacts/people/N</i></b> }
+ *     being used to start an activity to display that person.</p>
+ *   </li>
+ * </ul>
+ *
+ * <p>In addition to these primary attributes, there are a number of secondary
+ * attributes that you can also include with an intent:</p>
+ *
+ * <ul>
+ *     <li> <p><b>category</b> -- Gives additional information about the action
+ *         to execute.  For example, {@link #CATEGORY_LAUNCHER} means it should
+ *         appear in the Launcher as a top-level application, while
+ *         {@link #CATEGORY_ALTERNATIVE} means it should be included in a list
+ *         of alternative actions the user can perform on a piece of data.</p>
+ *     <li> <p><b>type</b> -- Specifies an explicit type (a MIME type) of the
+ *         intent data.  Normally the type is inferred from the data itself.
+ *         By setting this attribute, you disable that evaluation and force
+ *         an explicit type.</p>
+ *     <li> <p><b>component</b> -- Specifies an explicit name of a component
+ *         class to use for the intent.  Normally this is determined by looking
+ *         at the other information in the intent (the action, data/type, and
+ *         categories) and matching that with a component that can handle it.
+ *         If this attribute is set then none of the evaluation is performed,
+ *         and this component is used exactly as is.  By specifying this attribute,
+ *         all of the other Intent attributes become optional.</p>
+ *     <li> <p><b>extras</b> -- This is a {@link Bundle} of any additional information.
+ *         This can be used to provide extended information to the component.
+ *         For example, if we have a action to send an e-mail message, we could
+ *         also include extra pieces of data here to supply a subject, body,
+ *         etc.</p>
+ * </ul>
+ *
+ * <p>Here are some examples of other operations you can specify as intents
+ * using these additional parameters:</p>
+ *
+ * <ul>
+ *   <li> <p><b>{@link #ACTION_MAIN} with category {@link #CATEGORY_HOME}</b> --
+ *     Launch the home screen.</p>
+ *   </li>
+ *   <li> <p><b>{@link #ACTION_GET_CONTENT} with MIME type
+ *     <i>{@link android.provider.Contacts.Phones#CONTENT_URI
+ *     vnd.android.cursor.item/phone}</i></b>
+ *     -- Display the list of people's phone numbers, allowing the user to
+ *     browse through them and pick one and return it to the parent activity.</p>
+ *   </li>
+ *   <li> <p><b>{@link #ACTION_GET_CONTENT} with MIME type
+ *     <i>*{@literal /}*</i> and category {@link #CATEGORY_OPENABLE}</b>
+ *     -- Display all pickers for data that can be opened with
+ *     {@link ContentResolver#openInputStream(Uri) ContentResolver.openInputStream()},
+ *     allowing the user to pick one of them and then some data inside of it
+ *     and returning the resulting URI to the caller.  This can be used,
+ *     for example, in an e-mail application to allow the user to pick some
+ *     data to include as an attachment.</p>
+ *   </li>
+ * </ul>
+ *
+ * <p>There are a variety of standard Intent action and category constants
+ * defined in the Intent class, but applications can also define their own.
+ * These strings use Java-style scoping, to ensure they are unique -- for
+ * example, the standard {@link #ACTION_VIEW} is called
+ * "android.intent.action.VIEW".</p>
+ *
+ * <p>Put together, the set of actions, data types, categories, and extra data
+ * defines a language for the system allowing for the expression of phrases
+ * such as "call john smith's cell".  As applications are added to the system,
+ * they can extend this language by adding new actions, types, and categories, or
+ * they can modify the behavior of existing phrases by supplying their own
+ * activities that handle them.</p>
+ *
+ * <a name="IntentResolution"></a>
+ * <h3>Intent Resolution</h3>
+ *
+ * <p>There are two primary forms of intents you will use.
+ *
+ * <ul>
+ *     <li> <p><b>Explicit Intents</b> have specified a component (via
+ *     {@link #setComponent} or {@link #setClass}), which provides the exact
+ *     class to be run.  Often these will not include any other information,
+ *     simply being a way for an application to launch various internal
+ *     activities it has as the user interacts with the application.
+ *
+ *     <li> <p><b>Implicit Intents</b> have not specified a component;
+ *     instead, they must include enough information for the system to
+ *     determine which of the available components is best to run for that
+ *     intent.
+ * </ul>
+ *
+ * <p>When using implicit intents, given such an arbitrary intent we need to
+ * know what to do with it. This is handled by the process of <em>Intent
+ * resolution</em>, which maps an Intent to an {@link android.app.Activity},
+ * {@link BroadcastReceiver}, or {@link android.app.Service} (or sometimes two or
+ * more activities/receivers) that can handle it.</p>
+ *
+ * <p>The intent resolution mechanism basically revolves around matching an
+ * Intent against all of the &lt;intent-filter&gt; descriptions in the
+ * installed application packages.  (Plus, in the case of broadcasts, any {@link BroadcastReceiver}
+ * objects explicitly registered with {@link Context#registerReceiver}.)  More
+ * details on this can be found in the documentation on the {@link
+ * IntentFilter} class.</p>
+ *
+ * <p>There are three pieces of information in the Intent that are used for
+ * resolution: the action, type, and category.  Using this information, a query
+ * is done on the {@link PackageManager} for a component that can handle the
+ * intent. The appropriate component is determined based on the intent
+ * information supplied in the <code>AndroidManifest.xml</code> file as
+ * follows:</p>
+ *
+ * <ul>
+ *     <li> <p>The <b>action</b>, if given, must be listed by the component as
+ *         one it handles.</p>
+ *     <li> <p>The <b>type</b> is retrieved from the Intent's data, if not
+ *         already supplied in the Intent.  Like the action, if a type is
+ *         included in the intent (either explicitly or implicitly in its
+ *         data), then this must be listed by the component as one it handles.</p>
+ *     <li> For data that is not a <code>content:</code> URI and where no explicit
+ *         type is included in the Intent, instead the <b>scheme</b> of the
+ *         intent data (such as <code>http:</code> or <code>mailto:</code>) is
+ *         considered. Again like the action, if we are matching a scheme it
+ *         must be listed by the component as one it can handle.
+ *     <li> <p>The <b>categories</b>, if supplied, must <em>all</em> be listed
+ *         by the activity as categories it handles.  That is, if you include
+ *         the categories {@link #CATEGORY_LAUNCHER} and
+ *         {@link #CATEGORY_ALTERNATIVE}, then you will only resolve to components
+ *         with an intent that lists <em>both</em> of those categories.
+ *         Activities will very often need to support the
+ *         {@link #CATEGORY_DEFAULT} so that they can be found by
+ *         {@link Context#startActivity Context.startActivity()}.</p>
+ * </ul>
+ *
+ * <p>For example, consider the Note Pad sample application that
+ * allows a user to browse through a list of notes data and view details about
+ * individual items.  Text in italics indicates places where you would replace a
+ * name with one specific to your own package.</p>
+ *
+ * <pre> &lt;manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ *       package="<i>com.android.notepad</i>"&gt;
+ *     &lt;application android:icon="@drawable/app_notes"
+ *             android:label="@string/app_name"&gt;
+ *
+ *         &lt;provider class=".NotePadProvider"
+ *                 android:authorities="<i>com.google.provider.NotePad</i>" /&gt;
+ *
+ *         &lt;activity class=".NotesList" android:label="@string/title_notes_list"&gt;
+ *             &lt;intent-filter&gt;
+ *                 &lt;action android:name="android.intent.action.MAIN" /&gt;
+ *                 &lt;category android:name="android.intent.category.LAUNCHER" /&gt;
+ *             &lt;/intent-filter&gt;
+ *             &lt;intent-filter&gt;
+ *                 &lt;action android:name="android.intent.action.VIEW" /&gt;
+ *                 &lt;action android:name="android.intent.action.EDIT" /&gt;
+ *                 &lt;action android:name="android.intent.action.PICK" /&gt;
+ *                 &lt;category android:name="android.intent.category.DEFAULT" /&gt;
+ *                 &lt;data android:mimeType="vnd.android.cursor.dir/<i>vnd.google.note</i>" /&gt;
+ *             &lt;/intent-filter&gt;
+ *             &lt;intent-filter&gt;
+ *                 &lt;action android:name="android.intent.action.GET_CONTENT" /&gt;
+ *                 &lt;category android:name="android.intent.category.DEFAULT" /&gt;
+ *                 &lt;data android:mimeType="vnd.android.cursor.item/<i>vnd.google.note</i>" /&gt;
+ *             &lt;/intent-filter&gt;
+ *         &lt;/activity&gt;
+ *
+ *         &lt;activity class=".NoteEditor" android:label="@string/title_note"&gt;
+ *             &lt;intent-filter android:label="@string/resolve_edit"&gt;
+ *                 &lt;action android:name="android.intent.action.VIEW" /&gt;
+ *                 &lt;action android:name="android.intent.action.EDIT" /&gt;
+ *                 &lt;category android:name="android.intent.category.DEFAULT" /&gt;
+ *                 &lt;data android:mimeType="vnd.android.cursor.item/<i>vnd.google.note</i>" /&gt;
+ *             &lt;/intent-filter&gt;
+ *
+ *             &lt;intent-filter&gt;
+ *                 &lt;action android:name="android.intent.action.INSERT" /&gt;
+ *                 &lt;category android:name="android.intent.category.DEFAULT" /&gt;
+ *                 &lt;data android:mimeType="vnd.android.cursor.dir/<i>vnd.google.note</i>" /&gt;
+ *             &lt;/intent-filter&gt;
+ *
+ *         &lt;/activity&gt;
+ *
+ *         &lt;activity class=".TitleEditor" android:label="@string/title_edit_title"
+ *                 android:theme="@android:style/Theme.Dialog"&gt;
+ *             &lt;intent-filter android:label="@string/resolve_title"&gt;
+ *                 &lt;action android:name="<i>com.android.notepad.action.EDIT_TITLE</i>" /&gt;
+ *                 &lt;category android:name="android.intent.category.DEFAULT" /&gt;
+ *                 &lt;category android:name="android.intent.category.ALTERNATIVE" /&gt;
+ *                 &lt;category android:name="android.intent.category.SELECTED_ALTERNATIVE" /&gt;
+ *                 &lt;data android:mimeType="vnd.android.cursor.item/<i>vnd.google.note</i>" /&gt;
+ *             &lt;/intent-filter&gt;
+ *         &lt;/activity&gt;
+ *
+ *     &lt;/application&gt;
+ * &lt;/manifest&gt;</pre>
+ *
+ * <p>The first activity,
+ * <code>com.android.notepad.NotesList</code>, serves as our main
+ * entry into the app.  It can do three things as described by its three intent
+ * templates:
+ * <ol>
+ * <li><pre>
+ * &lt;intent-filter&gt;
+ *     &lt;action android:name="{@link #ACTION_MAIN android.intent.action.MAIN}" /&gt;
+ *     &lt;category android:name="{@link #CATEGORY_LAUNCHER android.intent.category.LAUNCHER}" /&gt;
+ * &lt;/intent-filter&gt;</pre>
+ * <p>This provides a top-level entry into the NotePad application: the standard
+ * MAIN action is a main entry point (not requiring any other information in
+ * the Intent), and the LAUNCHER category says that this entry point should be
+ * listed in the application launcher.</p>
+ * <li><pre>
+ * &lt;intent-filter&gt;
+ *     &lt;action android:name="{@link #ACTION_VIEW android.intent.action.VIEW}" /&gt;
+ *     &lt;action android:name="{@link #ACTION_EDIT android.intent.action.EDIT}" /&gt;
+ *     &lt;action android:name="{@link #ACTION_PICK android.intent.action.PICK}" /&gt;
+ *     &lt;category android:name="{@link #CATEGORY_DEFAULT android.intent.category.DEFAULT}" /&gt;
+ *     &lt;data android:mimeType="vnd.android.cursor.dir/<i>vnd.google.note</i>" /&gt;
+ * &lt;/intent-filter&gt;</pre>
+ * <p>This declares the things that the activity can do on a directory of
+ * notes.  The type being supported is given with the &lt;type&gt; tag, where
+ * <code>vnd.android.cursor.dir/vnd.google.note</code> is a URI from which
+ * a Cursor of zero or more items (<code>vnd.android.cursor.dir</code>) can
+ * be retrieved which holds our note pad data (<code>vnd.google.note</code>).
+ * The activity allows the user to view or edit the directory of data (via
+ * the VIEW and EDIT actions), or to pick a particular note and return it
+ * to the caller (via the PICK action).  Note also the DEFAULT category
+ * supplied here: this is <em>required</em> for the
+ * {@link Context#startActivity Context.startActivity} method to resolve your
+ * activity when its component name is not explicitly specified.</p>
+ * <li><pre>
+ * &lt;intent-filter&gt;
+ *     &lt;action android:name="{@link #ACTION_GET_CONTENT android.intent.action.GET_CONTENT}" /&gt;
+ *     &lt;category android:name="{@link #CATEGORY_DEFAULT android.intent.category.DEFAULT}" /&gt;
+ *     &lt;data android:mimeType="vnd.android.cursor.item/<i>vnd.google.note</i>" /&gt;
+ * &lt;/intent-filter&gt;</pre>
+ * <p>This filter describes the ability to return to the caller a note selected by
+ * the user without needing to know where it came from.  The data type
+ * <code>vnd.android.cursor.item/vnd.google.note</code> is a URI from which
+ * a Cursor of exactly one (<code>vnd.android.cursor.item</code>) item can
+ * be retrieved which contains our note pad data (<code>vnd.google.note</code>).
+ * The GET_CONTENT action is similar to the PICK action, where the activity
+ * will return to its caller a piece of data selected by the user.  Here,
+ * however, the caller specifies the type of data they desire instead of
+ * the type of data the user will be picking from.</p>
+ * </ol>
+ *
+ * <p>Given these capabilities, the following intents will resolve to the
+ * NotesList activity:</p>
+ *
+ * <ul>
+ *     <li> <p><b>{ action=android.app.action.MAIN }</b> matches all of the
+ *         activities that can be used as top-level entry points into an
+ *         application.</p>
+ *     <li> <p><b>{ action=android.app.action.MAIN,
+ *         category=android.app.category.LAUNCHER }</b> is the actual intent
+ *         used by the Launcher to populate its top-level list.</p>
+ *     <li> <p><b>{ action=android.intent.action.VIEW
+ *          data=content://com.google.provider.NotePad/notes }</b>
+ *         displays a list of all the notes under
+ *         "content://com.google.provider.NotePad/notes", which
+ *         the user can browse through and see the details on.</p>
+ *     <li> <p><b>{ action=android.app.action.PICK
+ *          data=content://com.google.provider.NotePad/notes }</b>
+ *         provides a list of the notes under
+ *         "content://com.google.provider.NotePad/notes", from which
+ *         the user can pick a note whose data URL is returned back to the caller.</p>
+ *     <li> <p><b>{ action=android.app.action.GET_CONTENT
+ *          type=vnd.android.cursor.item/vnd.google.note }</b>
+ *         is similar to the pick action, but allows the caller to specify the
+ *         kind of data they want back so that the system can find the appropriate
+ *         activity to pick something of that data type.</p>
+ * </ul>
+ *
+ * <p>The second activity,
+ * <code>com.android.notepad.NoteEditor</code>, shows the user a single
+ * note entry and allows them to edit it.  It can do two things as described
+ * by its two intent templates:
+ * <ol>
+ * <li><pre>
+ * &lt;intent-filter android:label="@string/resolve_edit"&gt;
+ *     &lt;action android:name="{@link #ACTION_VIEW android.intent.action.VIEW}" /&gt;
+ *     &lt;action android:name="{@link #ACTION_EDIT android.intent.action.EDIT}" /&gt;
+ *     &lt;category android:name="{@link #CATEGORY_DEFAULT android.intent.category.DEFAULT}" /&gt;
+ *     &lt;data android:mimeType="vnd.android.cursor.item/<i>vnd.google.note</i>" /&gt;
+ * &lt;/intent-filter&gt;</pre>
+ * <p>The first, primary, purpose of this activity is to let the user interact
+ * with a single note, as decribed by the MIME type
+ * <code>vnd.android.cursor.item/vnd.google.note</code>.  The activity can
+ * either VIEW a note or allow the user to EDIT it.  Again we support the
+ * DEFAULT category to allow the activity to be launched without explicitly
+ * specifying its component.</p>
+ * <li><pre>
+ * &lt;intent-filter&gt;
+ *     &lt;action android:name="{@link #ACTION_INSERT android.intent.action.INSERT}" /&gt;
+ *     &lt;category android:name="{@link #CATEGORY_DEFAULT android.intent.category.DEFAULT}" /&gt;
+ *     &lt;data android:mimeType="vnd.android.cursor.dir/<i>vnd.google.note</i>" /&gt;
+ * &lt;/intent-filter&gt;</pre>
+ * <p>The secondary use of this activity is to insert a new note entry into
+ * an existing directory of notes.  This is used when the user creates a new
+ * note: the INSERT action is executed on the directory of notes, causing
+ * this activity to run and have the user create the new note data which
+ * it then adds to the content provider.</p>
+ * </ol>
+ *
+ * <p>Given these capabilities, the following intents will resolve to the
+ * NoteEditor activity:</p>
+ *
+ * <ul>
+ *     <li> <p><b>{ action=android.intent.action.VIEW
+ *          data=content://com.google.provider.NotePad/notes/<var>{ID}</var> }</b>
+ *         shows the user the content of note <var>{ID}</var>.</p>
+ *     <li> <p><b>{ action=android.app.action.EDIT
+ *          data=content://com.google.provider.NotePad/notes/<var>{ID}</var> }</b>
+ *         allows the user to edit the content of note <var>{ID}</var>.</p>
+ *     <li> <p><b>{ action=android.app.action.INSERT
+ *          data=content://com.google.provider.NotePad/notes }</b>
+ *         creates a new, empty note in the notes list at
+ *         "content://com.google.provider.NotePad/notes"
+ *         and allows the user to edit it.  If they keep their changes, the URI
+ *         of the newly created note is returned to the caller.</p>
+ * </ul>
+ *
+ * <p>The last activity,
+ * <code>com.android.notepad.TitleEditor</code>, allows the user to
+ * edit the title of a note.  This could be implemented as a class that the
+ * application directly invokes (by explicitly setting its component in
+ * the Intent), but here we show a way you can publish alternative
+ * operations on existing data:</p>
+ *
+ * <pre>
+ * &lt;intent-filter android:label="@string/resolve_title"&gt;
+ *     &lt;action android:name="<i>com.android.notepad.action.EDIT_TITLE</i>" /&gt;
+ *     &lt;category android:name="{@link #CATEGORY_DEFAULT android.intent.category.DEFAULT}" /&gt;
+ *     &lt;category android:name="{@link #CATEGORY_ALTERNATIVE android.intent.category.ALTERNATIVE}" /&gt;
+ *     &lt;category android:name="{@link #CATEGORY_SELECTED_ALTERNATIVE android.intent.category.SELECTED_ALTERNATIVE}" /&gt;
+ *     &lt;data android:mimeType="vnd.android.cursor.item/<i>vnd.google.note</i>" /&gt;
+ * &lt;/intent-filter&gt;</pre>
+ *
+ * <p>In the single intent template here, we
+ * have created our own private action called
+ * <code>com.android.notepad.action.EDIT_TITLE</code> which means to
+ * edit the title of a note.  It must be invoked on a specific note
+ * (data type <code>vnd.android.cursor.item/vnd.google.note</code>) like the previous
+ * view and edit actions, but here displays and edits the title contained
+ * in the note data.
+ *
+ * <p>In addition to supporting the default category as usual, our title editor
+ * also supports two other standard categories: ALTERNATIVE and
+ * SELECTED_ALTERNATIVE.  Implementing
+ * these categories allows others to find the special action it provides
+ * without directly knowing about it, through the
+ * {@link android.content.pm.PackageManager#queryIntentActivityOptions} method, or
+ * more often to build dynamic menu items with
+ * {@link android.view.Menu#addIntentOptions}.  Note that in the intent
+ * template here was also supply an explicit name for the template
+ * (via <code>android:label="@string/resolve_title"</code>) to better control
+ * what the user sees when presented with this activity as an alternative
+ * action to the data they are viewing.
+ *
+ * <p>Given these capabilities, the following intent will resolve to the
+ * TitleEditor activity:</p>
+ *
+ * <ul>
+ *     <li> <p><b>{ action=com.android.notepad.action.EDIT_TITLE
+ *          data=content://com.google.provider.NotePad/notes/<var>{ID}</var> }</b>
+ *         displays and allows the user to edit the title associated
+ *         with note <var>{ID}</var>.</p>
+ * </ul>
+ *
+ * <h3>Standard Activity Actions</h3>
+ *
+ * <p>These are the current standard actions that Intent defines for launching
+ * activities (usually through {@link Context#startActivity}.  The most
+ * important, and by far most frequently used, are {@link #ACTION_MAIN} and
+ * {@link #ACTION_EDIT}.
+ *
+ * <ul>
+ *     <li> {@link #ACTION_MAIN}
+ *     <li> {@link #ACTION_VIEW}
+ *     <li> {@link #ACTION_ATTACH_DATA}
+ *     <li> {@link #ACTION_EDIT}
+ *     <li> {@link #ACTION_PICK}
+ *     <li> {@link #ACTION_CHOOSER}
+ *     <li> {@link #ACTION_GET_CONTENT}
+ *     <li> {@link #ACTION_DIAL}
+ *     <li> {@link #ACTION_CALL}
+ *     <li> {@link #ACTION_SEND}
+ *     <li> {@link #ACTION_SENDTO}
+ *     <li> {@link #ACTION_ANSWER}
+ *     <li> {@link #ACTION_INSERT}
+ *     <li> {@link #ACTION_DELETE}
+ *     <li> {@link #ACTION_RUN}
+ *     <li> {@link #ACTION_SYNC}
+ *     <li> {@link #ACTION_PICK_ACTIVITY}
+ *     <li> {@link #ACTION_SEARCH}
+ *     <li> {@link #ACTION_WEB_SEARCH}
+ *     <li> {@link #ACTION_FACTORY_TEST}
+ * </ul>
+ *
+ * <h3>Standard Broadcast Actions</h3>
+ *
+ * <p>These are the current standard actions that Intent defines for receiving
+ * broadcasts (usually through {@link Context#registerReceiver} or a
+ * &lt;receiver&gt; tag in a manifest).
+ *
+ * <ul>
+ *     <li> {@link #ACTION_TIME_TICK}
+ *     <li> {@link #ACTION_TIME_CHANGED}
+ *     <li> {@link #ACTION_TIMEZONE_CHANGED}
+ *     <li> {@link #ACTION_BOOT_COMPLETED}
+ *     <li> {@link #ACTION_PACKAGE_ADDED}
+ *     <li> {@link #ACTION_PACKAGE_CHANGED}
+ *     <li> {@link #ACTION_PACKAGE_REMOVED}
+ *     <li> {@link #ACTION_PACKAGE_RESTARTED}
+ *     <li> {@link #ACTION_PACKAGE_DATA_CLEARED}
+ *     <li> {@link #ACTION_PACKAGES_SUSPENDED}
+ *     <li> {@link #ACTION_PACKAGES_UNSUSPENDED}
+ *     <li> {@link #ACTION_UID_REMOVED}
+ *     <li> {@link #ACTION_BATTERY_CHANGED}
+ *     <li> {@link #ACTION_POWER_CONNECTED}
+ *     <li> {@link #ACTION_POWER_DISCONNECTED}
+ *     <li> {@link #ACTION_SHUTDOWN}
+ * </ul>
+ *
+ * <h3>Standard Categories</h3>
+ *
+ * <p>These are the current standard categories that can be used to further
+ * clarify an Intent via {@link #addCategory}.
+ *
+ * <ul>
+ *     <li> {@link #CATEGORY_DEFAULT}
+ *     <li> {@link #CATEGORY_BROWSABLE}
+ *     <li> {@link #CATEGORY_TAB}
+ *     <li> {@link #CATEGORY_ALTERNATIVE}
+ *     <li> {@link #CATEGORY_SELECTED_ALTERNATIVE}
+ *     <li> {@link #CATEGORY_LAUNCHER}
+ *     <li> {@link #CATEGORY_INFO}
+ *     <li> {@link #CATEGORY_HOME}
+ *     <li> {@link #CATEGORY_PREFERENCE}
+ *     <li> {@link #CATEGORY_TEST}
+ *     <li> {@link #CATEGORY_CAR_DOCK}
+ *     <li> {@link #CATEGORY_DESK_DOCK}
+ *     <li> {@link #CATEGORY_LE_DESK_DOCK}
+ *     <li> {@link #CATEGORY_HE_DESK_DOCK}
+ *     <li> {@link #CATEGORY_CAR_MODE}
+ *     <li> {@link #CATEGORY_APP_MARKET}
+ *     <li> {@link #CATEGORY_VR_HOME}
+ * </ul>
+ *
+ * <h3>Standard Extra Data</h3>
+ *
+ * <p>These are the current standard fields that can be used as extra data via
+ * {@link #putExtra}.
+ *
+ * <ul>
+ *     <li> {@link #EXTRA_ALARM_COUNT}
+ *     <li> {@link #EXTRA_BCC}
+ *     <li> {@link #EXTRA_CC}
+ *     <li> {@link #EXTRA_CHANGED_COMPONENT_NAME}
+ *     <li> {@link #EXTRA_DATA_REMOVED}
+ *     <li> {@link #EXTRA_DOCK_STATE}
+ *     <li> {@link #EXTRA_DOCK_STATE_HE_DESK}
+ *     <li> {@link #EXTRA_DOCK_STATE_LE_DESK}
+ *     <li> {@link #EXTRA_DOCK_STATE_CAR}
+ *     <li> {@link #EXTRA_DOCK_STATE_DESK}
+ *     <li> {@link #EXTRA_DOCK_STATE_UNDOCKED}
+ *     <li> {@link #EXTRA_DONT_KILL_APP}
+ *     <li> {@link #EXTRA_EMAIL}
+ *     <li> {@link #EXTRA_INITIAL_INTENTS}
+ *     <li> {@link #EXTRA_INTENT}
+ *     <li> {@link #EXTRA_KEY_EVENT}
+ *     <li> {@link #EXTRA_ORIGINATING_URI}
+ *     <li> {@link #EXTRA_PHONE_NUMBER}
+ *     <li> {@link #EXTRA_REFERRER}
+ *     <li> {@link #EXTRA_REMOTE_INTENT_TOKEN}
+ *     <li> {@link #EXTRA_REPLACING}
+ *     <li> {@link #EXTRA_SHORTCUT_ICON}
+ *     <li> {@link #EXTRA_SHORTCUT_ICON_RESOURCE}
+ *     <li> {@link #EXTRA_SHORTCUT_INTENT}
+ *     <li> {@link #EXTRA_STREAM}
+ *     <li> {@link #EXTRA_SHORTCUT_NAME}
+ *     <li> {@link #EXTRA_SUBJECT}
+ *     <li> {@link #EXTRA_TEMPLATE}
+ *     <li> {@link #EXTRA_TEXT}
+ *     <li> {@link #EXTRA_TITLE}
+ *     <li> {@link #EXTRA_UID}
+ * </ul>
+ *
+ * <h3>Flags</h3>
+ *
+ * <p>These are the possible flags that can be used in the Intent via
+ * {@link #setFlags} and {@link #addFlags}.  See {@link #setFlags} for a list
+ * of all possible flags.
+ */
+public class Intent implements Parcelable, Cloneable {
+    private static final String TAG = "Intent";
+
+    private static final String ATTR_ACTION = "action";
+    private static final String TAG_CATEGORIES = "categories";
+    private static final String ATTR_CATEGORY = "category";
+    private static final String TAG_EXTRA = "extra";
+    private static final String ATTR_TYPE = "type";
+    private static final String ATTR_IDENTIFIER = "ident";
+    private static final String ATTR_COMPONENT = "component";
+    private static final String ATTR_DATA = "data";
+    private static final String ATTR_FLAGS = "flags";
+
+    // ---------------------------------------------------------------------
+    // ---------------------------------------------------------------------
+    // Standard intent activity actions (see action variable).
+
+    /**
+     *  Activity Action: Start as a main entry point, does not expect to
+     *  receive data.
+     *  <p>Input: nothing
+     *  <p>Output: nothing
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_MAIN = "android.intent.action.MAIN";
+
+    /**
+     * Activity Action: Display the data to the user.  This is the most common
+     * action performed on data -- it is the generic action you can use on
+     * a piece of data to get the most reasonable thing to occur.  For example,
+     * when used on a contacts entry it will view the entry; when used on a
+     * mailto: URI it will bring up a compose window filled with the information
+     * supplied by the URI; when used with a tel: URI it will invoke the
+     * dialer.
+     * <p>Input: {@link #getData} is URI from which to retrieve data.
+     * <p>Output: nothing.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_VIEW = "android.intent.action.VIEW";
+
+    /**
+     * Extra that can be included on activity intents coming from the storage UI
+     * when it launches sub-activities to manage various types of storage.  For example,
+     * it may use {@link #ACTION_VIEW} with a "image/*" MIME type to have an app show
+     * the images on the device, and in that case also include this extra to tell the
+     * app it is coming from the storage UI so should help the user manage storage of
+     * this type.
+     */
+    public static final String EXTRA_FROM_STORAGE = "android.intent.extra.FROM_STORAGE";
+
+    /**
+     * A synonym for {@link #ACTION_VIEW}, the "standard" action that is
+     * performed on a piece of data.
+     */
+    public static final String ACTION_DEFAULT = ACTION_VIEW;
+
+    /**
+     * Activity Action: Quick view the data. Launches a quick viewer for
+     * a URI or a list of URIs.
+     * <p>Activities handling this intent action should handle the vast majority of
+     * MIME types rather than only specific ones.
+     * <p>Quick viewers must render the quick view image locally, and must not send
+     * file content outside current device.
+     * <p>Input: {@link #getData} is a mandatory content URI of the item to
+     * preview. {@link #getClipData} contains an optional list of content URIs
+     * if there is more than one item to preview. {@link #EXTRA_INDEX} is an
+     * optional index of the URI in the clip data to show first.
+     * {@link #EXTRA_QUICK_VIEW_FEATURES} is an optional extra indicating the features
+     * that can be shown in the quick view UI.
+     * <p>Output: nothing.
+     * @see #EXTRA_INDEX
+     * @see #EXTRA_QUICK_VIEW_FEATURES
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_QUICK_VIEW = "android.intent.action.QUICK_VIEW";
+
+    /**
+     * Used to indicate that some piece of data should be attached to some other
+     * place.  For example, image data could be attached to a contact.  It is up
+     * to the recipient to decide where the data should be attached; the intent
+     * does not specify the ultimate destination.
+     * <p>Input: {@link #getData} is URI of data to be attached.
+     * <p>Output: nothing.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_ATTACH_DATA = "android.intent.action.ATTACH_DATA";
+
+    /**
+     * Activity Action: Provide explicit editable access to the given data.
+     * <p>Input: {@link #getData} is URI of data to be edited.
+     * <p>Output: nothing.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_EDIT = "android.intent.action.EDIT";
+
+    /**
+     * Activity Action: Pick an existing item, or insert a new item, and then edit it.
+     * <p>Input: {@link #getType} is the desired MIME type of the item to create or edit.
+     * The extras can contain type specific data to pass through to the editing/creating
+     * activity.
+     * <p>Output: The URI of the item that was picked.  This must be a content:
+     * URI so that any receiver can access it.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_INSERT_OR_EDIT = "android.intent.action.INSERT_OR_EDIT";
+
+    /**
+     * Activity Action: Pick an item from the data, returning what was selected.
+     * <p>Input: {@link #getData} is URI containing a directory of data
+     * (vnd.android.cursor.dir/*) from which to pick an item.
+     * <p>Output: The URI of the item that was picked.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_PICK = "android.intent.action.PICK";
+
+    /**
+     * Activity Action: Creates a reminder.
+     * <p>Input: {@link #EXTRA_TITLE} The title of the reminder that will be shown to the user.
+     * {@link #EXTRA_TEXT} The reminder text that will be shown to the user. The intent should at
+     * least specify a title or a text. {@link #EXTRA_TIME} The time when the reminder will be shown
+     * to the user. The time is specified in milliseconds since the Epoch (optional).
+     * </p>
+     * <p>Output: Nothing.</p>
+     *
+     * @see #EXTRA_TITLE
+     * @see #EXTRA_TEXT
+     * @see #EXTRA_TIME
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_CREATE_REMINDER = "android.intent.action.CREATE_REMINDER";
+
+    /**
+     * Activity Action: Creates a shortcut.
+     * <p>Input: Nothing.</p>
+     * <p>Output: An Intent representing the {@link android.content.pm.ShortcutInfo} result.</p>
+     * <p>For compatibility with older versions of android the intent may also contain three
+     * extras: SHORTCUT_INTENT (value: Intent), SHORTCUT_NAME (value: String),
+     * and SHORTCUT_ICON (value: Bitmap) or SHORTCUT_ICON_RESOURCE
+     * (value: ShortcutIconResource).</p>
+     *
+     * @see android.content.pm.ShortcutManager#createShortcutResultIntent
+     * @see #EXTRA_SHORTCUT_INTENT
+     * @see #EXTRA_SHORTCUT_NAME
+     * @see #EXTRA_SHORTCUT_ICON
+     * @see #EXTRA_SHORTCUT_ICON_RESOURCE
+     * @see android.content.Intent.ShortcutIconResource
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_CREATE_SHORTCUT = "android.intent.action.CREATE_SHORTCUT";
+
+    /**
+     * The name of the extra used to define the Intent of a shortcut.
+     *
+     * @see #ACTION_CREATE_SHORTCUT
+     * @deprecated Replaced with {@link android.content.pm.ShortcutManager#createShortcutResultIntent}
+     */
+    @Deprecated
+    public static final String EXTRA_SHORTCUT_INTENT = "android.intent.extra.shortcut.INTENT";
+    /**
+     * The name of the extra used to define the name of a shortcut.
+     *
+     * @see #ACTION_CREATE_SHORTCUT
+     * @deprecated Replaced with {@link android.content.pm.ShortcutManager#createShortcutResultIntent}
+     */
+    @Deprecated
+    public static final String EXTRA_SHORTCUT_NAME = "android.intent.extra.shortcut.NAME";
+    /**
+     * The name of the extra used to define the icon, as a Bitmap, of a shortcut.
+     *
+     * @see #ACTION_CREATE_SHORTCUT
+     * @deprecated Replaced with {@link android.content.pm.ShortcutManager#createShortcutResultIntent}
+     */
+    @Deprecated
+    public static final String EXTRA_SHORTCUT_ICON = "android.intent.extra.shortcut.ICON";
+    /**
+     * The name of the extra used to define the icon, as a ShortcutIconResource, of a shortcut.
+     *
+     * @see #ACTION_CREATE_SHORTCUT
+     * @see android.content.Intent.ShortcutIconResource
+     * @deprecated Replaced with {@link android.content.pm.ShortcutManager#createShortcutResultIntent}
+     */
+    @Deprecated
+    public static final String EXTRA_SHORTCUT_ICON_RESOURCE =
+            "android.intent.extra.shortcut.ICON_RESOURCE";
+
+    /**
+     * An activity that provides a user interface for adjusting application preferences.
+     * Optional but recommended settings for all applications which have settings.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_APPLICATION_PREFERENCES
+            = "android.intent.action.APPLICATION_PREFERENCES";
+
+    /**
+     * Activity Action: Launch an activity showing the app information.
+     * For applications which install other applications (such as app stores), it is recommended
+     * to handle this action for providing the app information to the user.
+     *
+     * <p>Input: {@link #EXTRA_PACKAGE_NAME} specifies the package whose information needs
+     * to be displayed.
+     * <p>Output: Nothing.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_SHOW_APP_INFO
+            = "android.intent.action.SHOW_APP_INFO";
+
+    /**
+     * Represents a shortcut/live folder icon resource.
+     *
+     * @see Intent#ACTION_CREATE_SHORTCUT
+     * @see Intent#EXTRA_SHORTCUT_ICON_RESOURCE
+     * @see android.provider.LiveFolders#ACTION_CREATE_LIVE_FOLDER
+     * @see android.provider.LiveFolders#EXTRA_LIVE_FOLDER_ICON
+     */
+    public static class ShortcutIconResource implements Parcelable {
+        /**
+         * The package name of the application containing the icon.
+         */
+        public String packageName;
+
+        /**
+         * The resource name of the icon, including package, name and type.
+         */
+        public String resourceName;
+
+        /**
+         * Creates a new ShortcutIconResource for the specified context and resource
+         * identifier.
+         *
+         * @param context The context of the application.
+         * @param resourceId The resource identifier for the icon.
+         * @return A new ShortcutIconResource with the specified's context package name
+         *         and icon resource identifier.``
+         */
+        public static ShortcutIconResource fromContext(Context context, @AnyRes int resourceId) {
+            ShortcutIconResource icon = new ShortcutIconResource();
+            icon.packageName = context.getPackageName();
+            icon.resourceName = context.getResources().getResourceName(resourceId);
+            return icon;
+        }
+
+        /**
+         * Used to read a ShortcutIconResource from a Parcel.
+         */
+        public static final @android.annotation.NonNull Parcelable.Creator<ShortcutIconResource> CREATOR =
+            new Parcelable.Creator<ShortcutIconResource>() {
+
+                public ShortcutIconResource createFromParcel(Parcel source) {
+                    ShortcutIconResource icon = new ShortcutIconResource();
+                    icon.packageName = source.readString8();
+                    icon.resourceName = source.readString8();
+                    return icon;
+                }
+
+                public ShortcutIconResource[] newArray(int size) {
+                    return new ShortcutIconResource[size];
+                }
+            };
+
+        /**
+         * No special parcel contents.
+         */
+        public int describeContents() {
+            return 0;
+        }
+
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeString8(packageName);
+            dest.writeString8(resourceName);
+        }
+
+        @Override
+        public String toString() {
+            return resourceName;
+        }
+    }
+
+    /**
+     * Activity Action: Display an activity chooser, allowing the user to pick
+     * what they want to before proceeding.  This can be used as an alternative
+     * to the standard activity picker that is displayed by the system when
+     * you try to start an activity with multiple possible matches, with these
+     * differences in behavior:
+     * <ul>
+     * <li>You can specify the title that will appear in the activity chooser.
+     * <li>The user does not have the option to make one of the matching
+     * activities a preferred activity, and all possible activities will
+     * always be shown even if one of them is currently marked as the
+     * preferred activity.
+     * </ul>
+     * <p>
+     * This action should be used when the user will naturally expect to
+     * select an activity in order to proceed.  An example if when not to use
+     * it is when the user clicks on a "mailto:" link.  They would naturally
+     * expect to go directly to their mail app, so startActivity() should be
+     * called directly: it will
+     * either launch the current preferred app, or put up a dialog allowing the
+     * user to pick an app to use and optionally marking that as preferred.
+     * <p>
+     * In contrast, if the user is selecting a menu item to send a picture
+     * they are viewing to someone else, there are many different things they
+     * may want to do at this point: send it through e-mail, upload it to a
+     * web service, etc.  In this case the CHOOSER action should be used, to
+     * always present to the user a list of the things they can do, with a
+     * nice title given by the caller such as "Send this photo with:".
+     * <p>
+     * If you need to grant URI permissions through a chooser, you must specify
+     * the permissions to be granted on the ACTION_CHOOSER Intent
+     * <em>in addition</em> to the EXTRA_INTENT inside.  This means using
+     * {@link #setClipData} to specify the URIs to be granted as well as
+     * {@link #FLAG_GRANT_READ_URI_PERMISSION} and/or
+     * {@link #FLAG_GRANT_WRITE_URI_PERMISSION} as appropriate.
+     * <p>
+     * As a convenience, an Intent of this form can be created with the
+     * {@link #createChooser} function.
+     * <p>
+     * Input: No data should be specified.  get*Extra must have
+     * a {@link #EXTRA_INTENT} field containing the Intent being executed,
+     * and can optionally have a {@link #EXTRA_TITLE} field containing the
+     * title text to display in the chooser.
+     * <p>
+     * Output: Depends on the protocol of {@link #EXTRA_INTENT}.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_CHOOSER = "android.intent.action.CHOOSER";
+
+    /**
+     * Convenience function for creating a {@link #ACTION_CHOOSER} Intent.
+     *
+     * <p>Builds a new {@link #ACTION_CHOOSER} Intent that wraps the given
+     * target intent, also optionally supplying a title.  If the target
+     * intent has specified {@link #FLAG_GRANT_READ_URI_PERMISSION} or
+     * {@link #FLAG_GRANT_WRITE_URI_PERMISSION}, then these flags will also be
+     * set in the returned chooser intent, with its ClipData set appropriately:
+     * either a direct reflection of {@link #getClipData()} if that is non-null,
+     * or a new ClipData built from {@link #getData()}.
+     *
+     * @param target The Intent that the user will be selecting an activity
+     * to perform.
+     * @param title Optional title that will be displayed in the chooser,
+     * only when the target action is not ACTION_SEND or ACTION_SEND_MULTIPLE.
+     * @return Return a new Intent object that you can hand to
+     * {@link Context#startActivity(Intent) Context.startActivity()} and
+     * related methods.
+     */
+    public static Intent createChooser(Intent target, CharSequence title) {
+        return createChooser(target, title, null);
+    }
+
+    /**
+     * Convenience function for creating a {@link #ACTION_CHOOSER} Intent.
+     *
+     * <p>Builds a new {@link #ACTION_CHOOSER} Intent that wraps the given
+     * target intent, also optionally supplying a title.  If the target
+     * intent has specified {@link #FLAG_GRANT_READ_URI_PERMISSION} or
+     * {@link #FLAG_GRANT_WRITE_URI_PERMISSION}, then these flags will also be
+     * set in the returned chooser intent, with its ClipData set appropriately:
+     * either a direct reflection of {@link #getClipData()} if that is non-null,
+     * or a new ClipData built from {@link #getData()}.</p>
+     *
+     * <p>The caller may optionally supply an {@link IntentSender} to receive a callback
+     * when the user makes a choice. This can be useful if the calling application wants
+     * to remember the last chosen target and surface it as a more prominent or one-touch
+     * affordance elsewhere in the UI for next time.</p>
+     *
+     * @param target The Intent that the user will be selecting an activity
+     * to perform.
+     * @param title Optional title that will be displayed in the chooser,
+     * only when the target action is not ACTION_SEND or ACTION_SEND_MULTIPLE.
+     * @param sender Optional IntentSender to be called when a choice is made.
+     * @return Return a new Intent object that you can hand to
+     * {@link Context#startActivity(Intent) Context.startActivity()} and
+     * related methods.
+     */
+    public static Intent createChooser(Intent target, CharSequence title, IntentSender sender) {
+        Intent intent = new Intent(ACTION_CHOOSER);
+        intent.putExtra(EXTRA_INTENT, target);
+        if (title != null) {
+            intent.putExtra(EXTRA_TITLE, title);
+        }
+
+        if (sender != null) {
+            intent.putExtra(EXTRA_CHOSEN_COMPONENT_INTENT_SENDER, sender);
+        }
+
+        // Migrate any clip data and flags from target.
+        int permFlags = target.getFlags() & (FLAG_GRANT_READ_URI_PERMISSION
+                | FLAG_GRANT_WRITE_URI_PERMISSION | FLAG_GRANT_PERSISTABLE_URI_PERMISSION
+                | FLAG_GRANT_PREFIX_URI_PERMISSION);
+        if (permFlags != 0) {
+            ClipData targetClipData = target.getClipData();
+            if (targetClipData == null && target.getData() != null) {
+                ClipData.Item item = new ClipData.Item(target.getData());
+                String[] mimeTypes;
+                if (target.getType() != null) {
+                    mimeTypes = new String[] { target.getType() };
+                } else {
+                    mimeTypes = new String[] { };
+                }
+                targetClipData = new ClipData(null, mimeTypes, item);
+            }
+            if (targetClipData != null) {
+                intent.setClipData(targetClipData);
+                intent.addFlags(permFlags);
+            }
+        }
+
+        return intent;
+    }
+
+    /**
+     * Activity Action: Allow the user to select a particular kind of data and
+     * return it.  This is different than {@link #ACTION_PICK} in that here we
+     * just say what kind of data is desired, not a URI of existing data from
+     * which the user can pick.  An ACTION_GET_CONTENT could allow the user to
+     * create the data as it runs (for example taking a picture or recording a
+     * sound), let them browse over the web and download the desired data,
+     * etc.
+     * <p>
+     * There are two main ways to use this action: if you want a specific kind
+     * of data, such as a person contact, you set the MIME type to the kind of
+     * data you want and launch it with {@link Context#startActivity(Intent)}.
+     * The system will then launch the best application to select that kind
+     * of data for you.
+     * <p>
+     * You may also be interested in any of a set of types of content the user
+     * can pick.  For example, an e-mail application that wants to allow the
+     * user to add an attachment to an e-mail message can use this action to
+     * bring up a list of all of the types of content the user can attach.
+     * <p>
+     * In this case, you should wrap the GET_CONTENT intent with a chooser
+     * (through {@link #createChooser}), which will give the proper interface
+     * for the user to pick how to send your data and allow you to specify
+     * a prompt indicating what they are doing.  You will usually specify a
+     * broad MIME type (such as image/* or {@literal *}/*), resulting in a
+     * broad range of content types the user can select from.
+     * <p>
+     * When using such a broad GET_CONTENT action, it is often desirable to
+     * only pick from data that can be represented as a stream.  This is
+     * accomplished by requiring the {@link #CATEGORY_OPENABLE} in the Intent.
+     * <p>
+     * Callers can optionally specify {@link #EXTRA_LOCAL_ONLY} to request that
+     * the launched content chooser only returns results representing data that
+     * is locally available on the device.  For example, if this extra is set
+     * to true then an image picker should not show any pictures that are available
+     * from a remote server but not already on the local device (thus requiring
+     * they be downloaded when opened).
+     * <p>
+     * If the caller can handle multiple returned items (the user performing
+     * multiple selection), then it can specify {@link #EXTRA_ALLOW_MULTIPLE}
+     * to indicate this.
+     * <p>
+     * Input: {@link #getType} is the desired MIME type to retrieve.  Note
+     * that no URI is supplied in the intent, as there are no constraints on
+     * where the returned data originally comes from.  You may also include the
+     * {@link #CATEGORY_OPENABLE} if you can only accept data that can be
+     * opened as a stream.  You may use {@link #EXTRA_LOCAL_ONLY} to limit content
+     * selection to local data.  You may use {@link #EXTRA_ALLOW_MULTIPLE} to
+     * allow the user to select multiple items.
+     * <p>
+     * Output: The URI of the item that was picked.  This must be a content:
+     * URI so that any receiver can access it.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_GET_CONTENT = "android.intent.action.GET_CONTENT";
+    /**
+     * Activity Action: Dial a number as specified by the data.  This shows a
+     * UI with the number being dialed, allowing the user to explicitly
+     * initiate the call.
+     * <p>Input: If nothing, an empty dialer is started; else {@link #getData}
+     * is URI of a phone number to be dialed or a tel: URI of an explicit phone
+     * number.
+     * <p>Output: nothing.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_DIAL = "android.intent.action.DIAL";
+    /**
+     * Activity Action: Perform a call to someone specified by the data.
+     * <p>Input: If nothing, an empty dialer is started; else {@link #getData}
+     * is URI of a phone number to be dialed or a tel: URI of an explicit phone
+     * number.
+     * <p>Output: nothing.
+     *
+     * <p>Note: there will be restrictions on which applications can initiate a
+     * call; most applications should use the {@link #ACTION_DIAL}.
+     * <p>Note: this Intent <strong>cannot</strong> be used to call emergency
+     * numbers.  Applications can <strong>dial</strong> emergency numbers using
+     * {@link #ACTION_DIAL}, however.
+     *
+     * <p>Note: if you app targets {@link android.os.Build.VERSION_CODES#M M}
+     * and above and declares as using the {@link android.Manifest.permission#CALL_PHONE}
+     * permission which is not granted, then attempting to use this action will
+     * result in a {@link java.lang.SecurityException}.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_CALL = "android.intent.action.CALL";
+    /**
+     * Activity Action: Perform a call to an emergency number specified by the
+     * data.
+     * <p>Input: {@link #getData} is URI of a phone number to be dialed or a
+     * tel: URI of an explicit phone number.
+     * <p>Output: nothing.
+     *
+     * <p class="note"><strong>Note:</strong> It is not guaranteed that the call will be placed on
+     * the {@link PhoneAccount} provided in the {@link TelecomManager#EXTRA_PHONE_ACCOUNT_HANDLE}
+     * extra (if specified) and may be placed on another {@link PhoneAccount} with the
+     * {@link PhoneAccount#CAPABILITY_PLACE_EMERGENCY_CALLS} capability, depending on external
+     * factors, such as network conditions and Modem/SIM status.
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_CALL_EMERGENCY = "android.intent.action.CALL_EMERGENCY";
+    /**
+     * Activity Action: Dial a emergency number specified by the data.  This shows a
+     * UI with the number being dialed, allowing the user to explicitly
+     * initiate the call.
+     * <p>Input: If nothing, an empty emergency dialer is started; else {@link #getData}
+     * is a tel: URI of an explicit emergency phone number.
+     * <p>Output: nothing.
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_DIAL_EMERGENCY = "android.intent.action.DIAL_EMERGENCY";
+    /**
+     * Activity action: Perform a call to any number (emergency or not)
+     * specified by the data.
+     * <p>Input: {@link #getData} is URI of a phone number to be dialed or a
+     * tel: URI of an explicit phone number.
+     * <p>Output: nothing.
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_CALL_PRIVILEGED = "android.intent.action.CALL_PRIVILEGED";
+
+    /**
+     * Activity Action: Main entry point for carrier setup apps.
+     * <p>Carrier apps that provide an implementation for this action may be invoked to configure
+     * carrier service and typically require
+     * {@link android.telephony.TelephonyManager#hasCarrierPrivileges() carrier privileges} to
+     * fulfill their duties.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_CARRIER_SETUP = "android.intent.action.CARRIER_SETUP";
+    /**
+     * Activity Action: Send a message to someone specified by the data.
+     * <p>Input: {@link #getData} is URI describing the target.
+     * <p>Output: nothing.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_SENDTO = "android.intent.action.SENDTO";
+    /**
+     * Activity Action: Deliver some data to someone else.  Who the data is
+     * being delivered to is not specified; it is up to the receiver of this
+     * action to ask the user where the data should be sent.
+     * <p>
+     * When launching a SEND intent, you should usually wrap it in a chooser
+     * (through {@link #createChooser}), which will give the proper interface
+     * for the user to pick how to send your data and allow you to specify
+     * a prompt indicating what they are doing.
+     * <p>
+     * Input: {@link #getType} is the MIME type of the data being sent.
+     * get*Extra can have either a {@link #EXTRA_TEXT}
+     * or {@link #EXTRA_STREAM} field, containing the data to be sent.  If
+     * using EXTRA_TEXT, the MIME type should be "text/plain"; otherwise it
+     * should be the MIME type of the data in EXTRA_STREAM.  Use {@literal *}/*
+     * if the MIME type is unknown (this will only allow senders that can
+     * handle generic data streams).  If using {@link #EXTRA_TEXT}, you can
+     * also optionally supply {@link #EXTRA_HTML_TEXT} for clients to retrieve
+     * your text with HTML formatting.
+     * <p>
+     * As of {@link android.os.Build.VERSION_CODES#JELLY_BEAN}, the data
+     * being sent can be supplied through {@link #setClipData(ClipData)}.  This
+     * allows you to use {@link #FLAG_GRANT_READ_URI_PERMISSION} when sharing
+     * content: URIs and other advanced features of {@link ClipData}.  If
+     * using this approach, you still must supply the same data through the
+     * {@link #EXTRA_TEXT} or {@link #EXTRA_STREAM} fields described below
+     * for compatibility with old applications.  If you don't set a ClipData,
+     * it will be copied there for you when calling {@link Context#startActivity(Intent)}.
+     * <p>
+     * Starting from {@link android.os.Build.VERSION_CODES#O}, if
+     * {@link #CATEGORY_TYPED_OPENABLE} is passed, then the Uris passed in
+     * either {@link #EXTRA_STREAM} or via {@link #setClipData(ClipData)} may
+     * be openable only as asset typed files using
+     * {@link ContentResolver#openTypedAssetFileDescriptor(Uri, String, Bundle)}.
+     * <p>
+     * Optional standard extras, which may be interpreted by some recipients as
+     * appropriate, are: {@link #EXTRA_EMAIL}, {@link #EXTRA_CC},
+     * {@link #EXTRA_BCC}, {@link #EXTRA_SUBJECT}.
+     * <p>
+     * Output: nothing.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_SEND = "android.intent.action.SEND";
+    /**
+     * Activity Action: Deliver multiple data to someone else.
+     * <p>
+     * Like {@link #ACTION_SEND}, except the data is multiple.
+     * <p>
+     * Input: {@link #getType} is the MIME type of the data being sent.
+     * get*ArrayListExtra can have either a {@link #EXTRA_TEXT} or {@link
+     * #EXTRA_STREAM} field, containing the data to be sent.  If using
+     * {@link #EXTRA_TEXT}, you can also optionally supply {@link #EXTRA_HTML_TEXT}
+     * for clients to retrieve your text with HTML formatting.
+     * <p>
+     * Multiple types are supported, and receivers should handle mixed types
+     * whenever possible. The right way for the receiver to check them is to
+     * use the content resolver on each URI. The intent sender should try to
+     * put the most concrete mime type in the intent type, but it can fall
+     * back to {@literal <type>/*} or {@literal *}/* as needed.
+     * <p>
+     * e.g. if you are sending image/jpg and image/jpg, the intent's type can
+     * be image/jpg, but if you are sending image/jpg and image/png, then the
+     * intent's type should be image/*.
+     * <p>
+     * As of {@link android.os.Build.VERSION_CODES#JELLY_BEAN}, the data
+     * being sent can be supplied through {@link #setClipData(ClipData)}.  This
+     * allows you to use {@link #FLAG_GRANT_READ_URI_PERMISSION} when sharing
+     * content: URIs and other advanced features of {@link ClipData}.  If
+     * using this approach, you still must supply the same data through the
+     * {@link #EXTRA_TEXT} or {@link #EXTRA_STREAM} fields described below
+     * for compatibility with old applications.  If you don't set a ClipData,
+     * it will be copied there for you when calling {@link Context#startActivity(Intent)}.
+     * <p>
+     * Starting from {@link android.os.Build.VERSION_CODES#O}, if
+     * {@link #CATEGORY_TYPED_OPENABLE} is passed, then the Uris passed in
+     * either {@link #EXTRA_STREAM} or via {@link #setClipData(ClipData)} may
+     * be openable only as asset typed files using
+     * {@link ContentResolver#openTypedAssetFileDescriptor(Uri, String, Bundle)}.
+     * <p>
+     * Optional standard extras, which may be interpreted by some recipients as
+     * appropriate, are: {@link #EXTRA_EMAIL}, {@link #EXTRA_CC},
+     * {@link #EXTRA_BCC}, {@link #EXTRA_SUBJECT}.
+     * <p>
+     * Output: nothing.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_SEND_MULTIPLE = "android.intent.action.SEND_MULTIPLE";
+    /**
+     * Activity Action: Handle an incoming phone call.
+     * <p>Input: nothing.
+     * <p>Output: nothing.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_ANSWER = "android.intent.action.ANSWER";
+    /**
+     * Activity Action: Insert an empty item into the given container.
+     * <p>Input: {@link #getData} is URI of the directory (vnd.android.cursor.dir/*)
+     * in which to place the data.
+     * <p>Output: URI of the new data that was created.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_INSERT = "android.intent.action.INSERT";
+    /**
+     * Activity Action: Create a new item in the given container, initializing it
+     * from the current contents of the clipboard.
+     * <p>Input: {@link #getData} is URI of the directory (vnd.android.cursor.dir/*)
+     * in which to place the data.
+     * <p>Output: URI of the new data that was created.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_PASTE = "android.intent.action.PASTE";
+    /**
+     * Activity Action: Delete the given data from its container.
+     * <p>Input: {@link #getData} is URI of data to be deleted.
+     * <p>Output: nothing.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_DELETE = "android.intent.action.DELETE";
+    /**
+     * Activity Action: Run the data, whatever that means.
+     * <p>Input: ?  (Note: this is currently specific to the test harness.)
+     * <p>Output: nothing.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_RUN = "android.intent.action.RUN";
+    /**
+     * Activity Action: Perform a data synchronization.
+     * <p>Input: ?
+     * <p>Output: ?
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_SYNC = "android.intent.action.SYNC";
+    /**
+     * Activity Action: Pick an activity given an intent, returning the class
+     * selected.
+     * <p>Input: get*Extra field {@link #EXTRA_INTENT} is an Intent
+     * used with {@link PackageManager#queryIntentActivities} to determine the
+     * set of activities from which to pick.
+     * <p>Output: Class name of the activity that was selected.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_PICK_ACTIVITY = "android.intent.action.PICK_ACTIVITY";
+    /**
+     * Activity Action: Perform a search.
+     * <p>Input: {@link android.app.SearchManager#QUERY getStringExtra(SearchManager.QUERY)}
+     * is the text to search for.  If empty, simply
+     * enter your search results Activity with the search UI activated.
+     * <p>Output: nothing.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_SEARCH = "android.intent.action.SEARCH";
+    /**
+     * Activity Action: Start the platform-defined tutorial
+     * <p>Input: {@link android.app.SearchManager#QUERY getStringExtra(SearchManager.QUERY)}
+     * is the text to search for.  If empty, simply
+     * enter your search results Activity with the search UI activated.
+     * <p>Output: nothing.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_SYSTEM_TUTORIAL = "android.intent.action.SYSTEM_TUTORIAL";
+    /**
+     * Activity Action: Perform a web search.
+     * <p>
+     * Input: {@link android.app.SearchManager#QUERY
+     * getStringExtra(SearchManager.QUERY)} is the text to search for. If it is
+     * a url starts with http or https, the site will be opened. If it is plain
+     * text, Google search will be applied.
+     * <p>
+     * Output: nothing.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_WEB_SEARCH = "android.intent.action.WEB_SEARCH";
+
+    /**
+     * Activity Action: Perform assist action.
+     * <p>
+     * Input: {@link #EXTRA_ASSIST_PACKAGE}, {@link #EXTRA_ASSIST_CONTEXT}, can provide
+     * additional optional contextual information about where the user was when they
+     * requested the assist; {@link #EXTRA_REFERRER} may be set with additional referrer
+     * information.
+     * Output: nothing.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_ASSIST = "android.intent.action.ASSIST";
+
+    /**
+     * Activity Action: Perform voice assist action.
+     * <p>
+     * Input: {@link #EXTRA_ASSIST_PACKAGE}, {@link #EXTRA_ASSIST_CONTEXT}, can provide
+     * additional optional contextual information about where the user was when they
+     * requested the voice assist.
+     * Output: nothing.
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_VOICE_ASSIST = "android.intent.action.VOICE_ASSIST";
+
+    /**
+     * An optional field on {@link #ACTION_ASSIST} containing the name of the current foreground
+     * application package at the time the assist was invoked.
+     */
+    public static final String EXTRA_ASSIST_PACKAGE
+            = "android.intent.extra.ASSIST_PACKAGE";
+
+    /**
+     * An optional field on {@link #ACTION_ASSIST} containing the uid of the current foreground
+     * application package at the time the assist was invoked.
+     */
+    public static final String EXTRA_ASSIST_UID
+            = "android.intent.extra.ASSIST_UID";
+
+    /**
+     * An optional field on {@link #ACTION_ASSIST} and containing additional contextual
+     * information supplied by the current foreground app at the time of the assist request.
+     * This is a {@link Bundle} of additional data.
+     */
+    public static final String EXTRA_ASSIST_CONTEXT
+            = "android.intent.extra.ASSIST_CONTEXT";
+
+    /**
+     * An optional field on {@link #ACTION_ASSIST} suggesting that the user will likely use a
+     * keyboard as the primary input device for assistance.
+     */
+    public static final String EXTRA_ASSIST_INPUT_HINT_KEYBOARD =
+            "android.intent.extra.ASSIST_INPUT_HINT_KEYBOARD";
+
+    /**
+     * An optional field on {@link #ACTION_ASSIST} containing the InputDevice id
+     * that was used to invoke the assist.
+     */
+    public static final String EXTRA_ASSIST_INPUT_DEVICE_ID =
+            "android.intent.extra.ASSIST_INPUT_DEVICE_ID";
+
+    /**
+     * Activity Action: List all available applications.
+     * <p>Input: Nothing.
+     * <p>Output: nothing.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_ALL_APPS = "android.intent.action.ALL_APPS";
+    /**
+     * Activity Action: Show settings for choosing wallpaper.
+     * <p>Input: Nothing.
+     * <p>Output: Nothing.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_SET_WALLPAPER = "android.intent.action.SET_WALLPAPER";
+
+    /**
+     * Activity Action: Show activity for reporting a bug.
+     * <p>Input: Nothing.
+     * <p>Output: Nothing.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_BUG_REPORT = "android.intent.action.BUG_REPORT";
+
+    /**
+     *  Activity Action: Main entry point for factory tests.  Only used when
+     *  the device is booting in factory test node.  The implementing package
+     *  must be installed in the system image.
+     *  <p>Input: nothing
+     *  <p>Output: nothing
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_FACTORY_TEST = "android.intent.action.FACTORY_TEST";
+
+    /**
+     * Activity Action: The user pressed the "call" button to go to the dialer
+     * or other appropriate UI for placing a call.
+     * <p>Input: Nothing.
+     * <p>Output: Nothing.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_CALL_BUTTON = "android.intent.action.CALL_BUTTON";
+
+    /**
+     * Activity Action: Start Voice Command.
+     * <p>Input: Nothing.
+     * <p>Output: Nothing.
+     * <p class="note">
+     * In some cases, a matching Activity may not exist, so ensure you
+     * safeguard against this.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_VOICE_COMMAND = "android.intent.action.VOICE_COMMAND";
+
+    /**
+     * Activity Action: Start action associated with long pressing on the
+     * search key.
+     * <p>Input: Nothing.
+     * <p>Output: Nothing.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_SEARCH_LONG_PRESS = "android.intent.action.SEARCH_LONG_PRESS";
+
+    /**
+     * Activity Action: The user pressed the "Report" button in the crash/ANR dialog.
+     * This intent is delivered to the package which installed the application, usually
+     * Google Play.
+     * <p>Input: No data is specified. The bug report is passed in using
+     * an {@link #EXTRA_BUG_REPORT} field.
+     * <p>Output: Nothing.
+     *
+     * @see #EXTRA_BUG_REPORT
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_APP_ERROR = "android.intent.action.APP_ERROR";
+
+    /**
+     * An incident or bug report has been taken, and a system app has requested it to be shared,
+     * so trigger the confirmation screen.
+     *
+     * This will be sent directly to the registered receiver with the
+     * android.permission.APPROVE_INCIDENT_REPORTS permission.
+     * @hide
+     */
+    @SystemApi
+    public static final String ACTION_PENDING_INCIDENT_REPORTS_CHANGED =
+            "android.intent.action.PENDING_INCIDENT_REPORTS_CHANGED";
+
+    /**
+     * An incident report has been taken, and the user has approved it for sharing.
+     * <p>
+     * This will be sent directly to the registered receiver, which must have
+     * both the DUMP and USAGE_STATS permissions.
+     * <p>
+     * After receiving this, the application should wait until a suitable time
+     * (e.g. network available), get the list of available reports with
+     * {@link IncidentManager#getIncidentReportList IncidentManager.getIncidentReportList(String)}
+     * and then when the reports have been successfully uploaded, call
+     * {@link IncidentManager#deleteIncidentReport IncidentManager.deleteIncidentReport(Uri)}.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String ACTION_INCIDENT_REPORT_READY =
+            "android.intent.action.INCIDENT_REPORT_READY";
+
+    /**
+     * Activity Action: Show power usage information to the user.
+     * <p>Input: Nothing.
+     * <p>Output: Nothing.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_POWER_USAGE_SUMMARY = "android.intent.action.POWER_USAGE_SUMMARY";
+
+    /**
+     * Activity Action: Setup wizard action provided for OTA provisioning to determine if it needs
+     * to run.
+     * <p>Input: Nothing.
+     * <p>Output: Nothing.
+     * @deprecated As of {@link android.os.Build.VERSION_CODES#M}, setup wizard can be identified
+     * using {@link #ACTION_MAIN} and {@link #CATEGORY_SETUP_WIZARD}
+     * @hide
+     * @removed
+     */
+    @Deprecated
+    @SystemApi
+    public static final String ACTION_DEVICE_INITIALIZATION_WIZARD =
+            "android.intent.action.DEVICE_INITIALIZATION_WIZARD";
+
+    /**
+     * Activity Action: Setup wizard to launch after a platform update.  This
+     * activity should have a string meta-data field associated with it,
+     * {@link #METADATA_SETUP_VERSION}, which defines the current version of
+     * the platform for setup.  The activity will be launched only if
+     * {@link android.provider.Settings.Secure#LAST_SETUP_SHOWN} is not the
+     * same value.
+     * <p>Input: Nothing.
+     * <p>Output: Nothing.
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_UPGRADE_SETUP = "android.intent.action.UPGRADE_SETUP";
+
+    /**
+     * Activity Action: Start the Keyboard Shortcuts Helper screen.
+     * <p>Input: Nothing.
+     * <p>Output: Nothing.
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_SHOW_KEYBOARD_SHORTCUTS =
+            "com.android.intent.action.SHOW_KEYBOARD_SHORTCUTS";
+
+    /**
+     * Activity Action: Dismiss the Keyboard Shortcuts Helper screen.
+     * <p>Input: Nothing.
+     * <p>Output: Nothing.
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_DISMISS_KEYBOARD_SHORTCUTS =
+            "com.android.intent.action.DISMISS_KEYBOARD_SHORTCUTS";
+
+    /**
+     * Activity Action: Show settings for managing network data usage of a
+     * specific application. Applications should define an activity that offers
+     * options to control data usage.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_MANAGE_NETWORK_USAGE =
+            "android.intent.action.MANAGE_NETWORK_USAGE";
+
+    /**
+     * Activity Action: Launch application installer.
+     * <p>
+     * Input: The data must be a content: URI at which the application
+     * can be retrieved.  As of {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1},
+     * you can also use "package:<package-name>" to install an application for the
+     * current user that is already installed for another user. You can optionally supply
+     * {@link #EXTRA_INSTALLER_PACKAGE_NAME}, {@link #EXTRA_NOT_UNKNOWN_SOURCE},
+     * {@link #EXTRA_ALLOW_REPLACE}, and {@link #EXTRA_RETURN_RESULT}.
+     * <p>
+     * Output: If {@link #EXTRA_RETURN_RESULT}, returns whether the install
+     * succeeded.
+     * <p>
+     * <strong>Note:</strong>If your app is targeting API level higher than 25 you
+     * need to hold {@link android.Manifest.permission#REQUEST_INSTALL_PACKAGES}
+     * in order to launch the application installer.
+     * </p>
+     *
+     * @see #EXTRA_INSTALLER_PACKAGE_NAME
+     * @see #EXTRA_NOT_UNKNOWN_SOURCE
+     * @see #EXTRA_RETURN_RESULT
+     *
+     * @deprecated use {@link android.content.pm.PackageInstaller} instead
+     */
+    @Deprecated
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_INSTALL_PACKAGE = "android.intent.action.INSTALL_PACKAGE";
+
+    /**
+     * Activity Action: Activity to handle split installation failures.
+     * <p>Splits may be installed dynamically. This happens when an Activity is launched,
+     * but the split that contains the application isn't installed. When a split is
+     * installed in this manner, the containing package usually doesn't know this is
+     * happening. However, if an error occurs during installation, the containing
+     * package can define a single activity handling this action to deal with such
+     * failures.
+     * <p>The activity handling this action must be in the base package.
+     * <p>
+     * Input: {@link #EXTRA_INTENT} the original intent that started split installation.
+     * {@link #EXTRA_SPLIT_NAME} the name of the split that failed to be installed.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_INSTALL_FAILURE = "android.intent.action.INSTALL_FAILURE";
+
+    /**
+     * Activity Action: Launch instant application installer.
+     * <p class="note">
+     * This is a protected intent that can only be sent by the system.
+     * </p>
+     *
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_INSTALL_INSTANT_APP_PACKAGE
+            = "android.intent.action.INSTALL_INSTANT_APP_PACKAGE";
+
+    /**
+     * Service Action: Resolve instant application.
+     * <p>
+     * The system will have a persistent connection to this service.
+     * This is a protected intent that can only be sent by the system.
+     * </p>
+     *
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.SERVICE_ACTION)
+    public static final String ACTION_RESOLVE_INSTANT_APP_PACKAGE
+            = "android.intent.action.RESOLVE_INSTANT_APP_PACKAGE";
+
+    /**
+     * Activity Action: Launch instant app settings.
+     *
+     * <p class="note">
+     * This is a protected intent that can only be sent by the system.
+     * </p>
+     *
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_INSTANT_APP_RESOLVER_SETTINGS
+            = "android.intent.action.INSTANT_APP_RESOLVER_SETTINGS";
+
+    /**
+     * Used as a string extra field with {@link #ACTION_INSTALL_PACKAGE} to install a
+     * package.  Specifies the installer package name; this package will receive the
+     * {@link #ACTION_APP_ERROR} intent.
+     */
+    public static final String EXTRA_INSTALLER_PACKAGE_NAME
+            = "android.intent.extra.INSTALLER_PACKAGE_NAME";
+
+    /**
+     * Used as a boolean extra field with {@link #ACTION_INSTALL_PACKAGE} to install a
+     * package.  Specifies that the application being installed should not be
+     * treated as coming from an unknown source, but as coming from the app
+     * invoking the Intent.  For this to work you must start the installer with
+     * startActivityForResult().
+     */
+    public static final String EXTRA_NOT_UNKNOWN_SOURCE
+            = "android.intent.extra.NOT_UNKNOWN_SOURCE";
+
+    /**
+     * Used as a URI extra field with {@link #ACTION_INSTALL_PACKAGE} and
+     * {@link #ACTION_VIEW} to indicate the URI from which the local APK in the Intent
+     * data field originated from.
+     */
+    public static final String EXTRA_ORIGINATING_URI
+            = "android.intent.extra.ORIGINATING_URI";
+
+    /**
+     * This extra can be used with any Intent used to launch an activity, supplying information
+     * about who is launching that activity.  This field contains a {@link android.net.Uri}
+     * object, typically an http: or https: URI of the web site that the referral came from;
+     * it can also use the {@link #URI_ANDROID_APP_SCHEME android-app:} scheme to identify
+     * a native application that it came from.
+     *
+     * <p>To retrieve this value in a client, use {@link android.app.Activity#getReferrer}
+     * instead of directly retrieving the extra.  It is also valid for applications to
+     * instead supply {@link #EXTRA_REFERRER_NAME} for cases where they can only create
+     * a string, not a Uri; the field here, if supplied, will always take precedence,
+     * however.</p>
+     *
+     * @see #EXTRA_REFERRER_NAME
+     */
+    public static final String EXTRA_REFERRER
+            = "android.intent.extra.REFERRER";
+
+    /**
+     * Alternate version of {@link #EXTRA_REFERRER} that supplies the URI as a String rather
+     * than a {@link android.net.Uri} object.  Only for use in cases where Uri objects can
+     * not be created, in particular when Intent extras are supplied through the
+     * {@link #URI_INTENT_SCHEME intent:} or {@link #URI_ANDROID_APP_SCHEME android-app:}
+     * schemes.
+     *
+     * @see #EXTRA_REFERRER
+     */
+    public static final String EXTRA_REFERRER_NAME
+            = "android.intent.extra.REFERRER_NAME";
+
+    /**
+     * Used as an int extra field with {@link #ACTION_INSTALL_PACKAGE} and
+     * {@link #ACTION_VIEW} to indicate the uid of the package that initiated the install
+     * Currently only a system app that hosts the provider authority "downloads" or holds the
+     * permission {@link android.Manifest.permission.MANAGE_DOCUMENTS} can use this.
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    public static final String EXTRA_ORIGINATING_UID
+            = "android.intent.extra.ORIGINATING_UID";
+
+    /**
+     * Used as a boolean extra field with {@link #ACTION_INSTALL_PACKAGE} to install a
+     * package.  Tells the installer UI to skip the confirmation with the user
+     * if the .apk is replacing an existing one.
+     * @deprecated As of {@link android.os.Build.VERSION_CODES#JELLY_BEAN}, Android
+     * will no longer show an interstitial message about updating existing
+     * applications so this is no longer needed.
+     */
+    @Deprecated
+    public static final String EXTRA_ALLOW_REPLACE
+            = "android.intent.extra.ALLOW_REPLACE";
+
+    /**
+     * Used as a boolean extra field with {@link #ACTION_INSTALL_PACKAGE} or
+     * {@link #ACTION_UNINSTALL_PACKAGE}.  Specifies that the installer UI should
+     * return to the application the result code of the install/uninstall.  The returned result
+     * code will be {@link android.app.Activity#RESULT_OK} on success or
+     * {@link android.app.Activity#RESULT_FIRST_USER} on failure.
+     */
+    public static final String EXTRA_RETURN_RESULT
+            = "android.intent.extra.RETURN_RESULT";
+
+    /**
+     * Package manager install result code.  @hide because result codes are not
+     * yet ready to be exposed.
+     */
+    public static final String EXTRA_INSTALL_RESULT
+            = "android.intent.extra.INSTALL_RESULT";
+
+    /**
+     * Activity Action: Launch application uninstaller.
+     * <p>
+     * Input: The data must be a package: URI whose scheme specific part is
+     * the package name of the current installed package to be uninstalled.
+     * You can optionally supply {@link #EXTRA_RETURN_RESULT}.
+     * <p>
+     * Output: If {@link #EXTRA_RETURN_RESULT}, returns whether the install
+     * succeeded.
+     * <p>
+     * Requires {@link android.Manifest.permission#REQUEST_DELETE_PACKAGES}
+     * since {@link Build.VERSION_CODES#P}.
+     *
+     * @deprecated Use {@link android.content.pm.PackageInstaller#uninstall(String, IntentSender)}
+     *             instead
+     */
+    @Deprecated
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_UNINSTALL_PACKAGE = "android.intent.action.UNINSTALL_PACKAGE";
+
+    /**
+     * Specify whether the package should be uninstalled for all users.
+     * @hide because these should not be part of normal application flow.
+     */
+    public static final String EXTRA_UNINSTALL_ALL_USERS
+            = "android.intent.extra.UNINSTALL_ALL_USERS";
+
+    /**
+     * A string that associates with a metadata entry, indicating the last run version of the
+     * platform that was setup.
+     *
+     * @see #ACTION_UPGRADE_SETUP
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String METADATA_SETUP_VERSION = "android.SETUP_VERSION";
+
+    /**
+     * Activity action: Launch UI to manage the permissions of an app.
+     * <p>
+     * Input: {@link #EXTRA_PACKAGE_NAME} specifies the package whose permissions
+     * will be managed by the launched UI.
+     * </p>
+     * <p>
+     * Output: Nothing.
+     * </p>
+     *
+     * @see #EXTRA_PACKAGE_NAME
+     *
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_MANAGE_APP_PERMISSIONS =
+            "android.intent.action.MANAGE_APP_PERMISSIONS";
+
+    /**
+     * Activity action: Launch UI to manage a specific permissions of an app.
+     * <p>
+     * Input: {@link #EXTRA_PACKAGE_NAME} specifies the package whose permission
+     * will be managed by the launched UI.
+     * </p>
+     * <p>
+     * Input: {@link #EXTRA_PERMISSION_NAME} specifies the (individual) permission
+     * that should be managed by the launched UI.
+     * </p>
+     * <p>
+     * <li> {@link #EXTRA_USER} specifies the UserHandle of the user that owns the app.
+     * </p>
+     * <p>
+     * Output: Nothing.
+     * </p>
+     *
+     * @see #EXTRA_PACKAGE_NAME
+     * @see #EXTRA_PERMISSION_NAME
+     * @see #EXTRA_USER
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS)
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_MANAGE_APP_PERMISSION =
+            "android.intent.action.MANAGE_APP_PERMISSION";
+
+    /**
+     * Activity action: Launch UI to manage permissions.
+     * <p>
+     * Input: Nothing.
+     * </p>
+     * <p>
+     * Output: Nothing.
+     * </p>
+     *
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_MANAGE_PERMISSIONS =
+            "android.intent.action.MANAGE_PERMISSIONS";
+
+    /**
+     * Activity action: Launch UI to manage auto-revoke state.
+     * <p>
+     * Input: {@link Intent#setData data} should be a {@code package}-scheme {@link Uri} with
+     * a package name, whose auto-revoke state will be reviewed (mandatory).
+     * E.g. {@code Uri.fromParts("package", packageName, null) }
+     * </p>
+     * <p>
+     * Output: Nothing.
+     * </p>
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_AUTO_REVOKE_PERMISSIONS =
+            "android.intent.action.AUTO_REVOKE_PERMISSIONS";
+
+    /**
+     * Activity action: Launch UI to review permissions for an app.
+     * The system uses this intent if permission review for apps not
+     * supporting the new runtime permissions model is enabled. In
+     * this mode a permission review is required before any of the
+     * app components can run.
+     * <p>
+     * Input: {@link #EXTRA_PACKAGE_NAME} specifies the package whose
+     * permissions will be reviewed (mandatory).
+     * </p>
+     * <p>
+     * Input: {@link #EXTRA_INTENT} specifies a pending intent to
+     * be fired after the permission review (optional).
+     * </p>
+     * <p>
+     * Input: {@link #EXTRA_REMOTE_CALLBACK} specifies a callback to
+     * be invoked after the permission review (optional).
+     * </p>
+     * <p>
+     * Input: {@link #EXTRA_RESULT_NEEDED} specifies whether the intent
+     * passed via {@link #EXTRA_INTENT} needs a result (optional).
+     * </p>
+     * <p>
+     * Output: Nothing.
+     * </p>
+     *
+     * @see #EXTRA_PACKAGE_NAME
+     * @see #EXTRA_INTENT
+     * @see #EXTRA_REMOTE_CALLBACK
+     * @see #EXTRA_RESULT_NEEDED
+     *
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_REVIEW_PERMISSIONS =
+            "android.intent.action.REVIEW_PERMISSIONS";
+
+    /**
+     * Activity action: Launch UI to show information about the usage
+     * of a given permission. This action would be handled by apps that
+     * want to show details about how and why given permission is being
+     * used.
+     * <p>
+     * <strong>Important:</strong>You must protect the activity that handles
+     * this action with the {@link android.Manifest.permission#START_VIEW_PERMISSION_USAGE
+     *  START_VIEW_PERMISSION_USAGE} permission to ensure that only the
+     * system can launch this activity. The system will not launch
+     * activities that are not properly protected.
+     *
+     * <p>
+     * Input: {@code android.intent.extra.PERMISSION_NAME} specifies the permission
+     * for which the launched UI would be targeted.
+     * </p>
+     * <p>
+     * Output: Nothing.
+     * </p>
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    @RequiresPermission(android.Manifest.permission.START_VIEW_PERMISSION_USAGE)
+    public static final String ACTION_VIEW_PERMISSION_USAGE =
+            "android.intent.action.VIEW_PERMISSION_USAGE";
+
+    /**
+     * Activity action: Launch UI to manage a default app.
+     * <p>
+     * Input: {@link #EXTRA_ROLE_NAME} specifies the role of the default app which will be managed
+     * by the launched UI.
+     * </p>
+     * <p>
+     * Output: Nothing.
+     * </p>
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS)
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    @SystemApi
+    @TestApi
+    public static final String ACTION_MANAGE_DEFAULT_APP =
+            "android.intent.action.MANAGE_DEFAULT_APP";
+
+    /**
+     * Intent extra: A role name.
+     * <p>
+     * Type: String
+     * </p>
+     *
+     * @see android.app.role.RoleManager
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    public static final String EXTRA_ROLE_NAME = "android.intent.extra.ROLE_NAME";
+
+    /**
+     * Activity action: Launch UI to manage special app accesses.
+     * <p>
+     * Input: Nothing.
+     * </p>
+     * <p>
+     * Output: Nothing.
+     * </p>
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS)
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    @SystemApi
+    public static final String ACTION_MANAGE_SPECIAL_APP_ACCESSES =
+            "android.intent.action.MANAGE_SPECIAL_APP_ACCESSES";
+
+    /**
+     * Intent extra: A callback for reporting remote result as a bundle.
+     * <p>
+     * Type: IRemoteCallback
+     * </p>
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_REMOTE_CALLBACK = "android.intent.extra.REMOTE_CALLBACK";
+
+    /**
+     * Intent extra: An app package name.
+     * <p>
+     * Type: String
+     * </p>
+     *
+     */
+    public static final String EXTRA_PACKAGE_NAME = "android.intent.extra.PACKAGE_NAME";
+
+    /**
+     * Intent extra: A {@link Bundle} of extras for a package being suspended. Will be sent as an
+     * extra with {@link #ACTION_MY_PACKAGE_SUSPENDED}.
+     *
+     * <p>The contents of this {@link Bundle} are a contract between the suspended app and the
+     * suspending app, i.e. any app with the permission {@code android.permission.SUSPEND_APPS}.
+     * This is meant to enable the suspended app to better handle the state of being suspended.
+     *
+     * @see #ACTION_MY_PACKAGE_SUSPENDED
+     * @see #ACTION_MY_PACKAGE_UNSUSPENDED
+     * @see PackageManager#isPackageSuspended()
+     * @see PackageManager#getSuspendedPackageAppExtras()
+     */
+    public static final String EXTRA_SUSPENDED_PACKAGE_EXTRAS = "android.intent.extra.SUSPENDED_PACKAGE_EXTRAS";
+
+    /**
+     * Intent extra: An app split name.
+     * <p>
+     * Type: String
+     * </p>
+     */
+    public static final String EXTRA_SPLIT_NAME = "android.intent.extra.SPLIT_NAME";
+
+    /**
+     * Intent extra: A {@link ComponentName} value.
+     * <p>
+     * Type: String
+     * </p>
+     */
+    public static final String EXTRA_COMPONENT_NAME = "android.intent.extra.COMPONENT_NAME";
+
+    /**
+     * Intent extra: An extra for specifying whether a result is needed.
+     * <p>
+     * Type: boolean
+     * </p>
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_RESULT_NEEDED = "android.intent.extra.RESULT_NEEDED";
+
+    /**
+     * Intent extra: ID of the shortcut used to send the share intent. Will be sent with
+     * {@link #ACTION_SEND}.
+     *
+     * @see ShortcutInfo#getId()
+     *
+     * <p>
+     * Type: String
+     * </p>
+     */
+    public static final String EXTRA_SHORTCUT_ID = "android.intent.extra.shortcut.ID";
+
+    /**
+     * Activity action: Launch UI to manage which apps have a given permission.
+     * <p>
+     * Input: {@link #EXTRA_PERMISSION_NAME} specifies the permission group
+     * which will be managed by the launched UI.
+     * </p>
+     * <p>
+     * Output: Nothing.
+     * </p>
+     *
+     * @see #EXTRA_PERMISSION_NAME
+     *
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_MANAGE_PERMISSION_APPS =
+            "android.intent.action.MANAGE_PERMISSION_APPS";
+
+    /**
+     * Intent extra: The name of a permission.
+     * <p>
+     * Type: String
+     * </p>
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_PERMISSION_NAME = "android.intent.extra.PERMISSION_NAME";
+
+    /**
+     * Intent extra: The name of a permission group.
+     * <p>
+     * Type: String
+     * </p>
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_PERMISSION_GROUP_NAME =
+            "android.intent.extra.PERMISSION_GROUP_NAME";
+
+    /**
+     * Intent extra: The number of milliseconds.
+     * <p>
+     * Type: long
+     * </p>
+     */
+    public static final String EXTRA_DURATION_MILLIS =
+            "android.intent.extra.DURATION_MILLIS";
+
+    /**
+     * Activity action: Launch UI to review app uses of permissions.
+     * <p>
+     * Input: {@link #EXTRA_PERMISSION_NAME} specifies the permission name
+     * that will be displayed by the launched UI.  Do not pass both this and
+     * {@link #EXTRA_PERMISSION_GROUP_NAME} .
+     * </p>
+     * <p>
+     * Input: {@link #EXTRA_PERMISSION_GROUP_NAME} specifies the permission group name
+     * that will be displayed by the launched UI.  Do not pass both this and
+     * {@link #EXTRA_PERMISSION_NAME}.
+     * </p>
+     * <p>
+     * Input: {@link #EXTRA_DURATION_MILLIS} specifies the minimum number of milliseconds of recent
+     * activity to show (optional).  Must be non-negative.
+     * </p>
+     * <p>
+     * Output: Nothing.
+     * </p>
+     * <p class="note">
+     * This requires {@link android.Manifest.permission#GRANT_RUNTIME_PERMISSIONS} permission.
+     * </p>
+     *
+     * @see #EXTRA_PERMISSION_NAME
+     * @see #EXTRA_PERMISSION_GROUP_NAME
+     * @see #EXTRA_DURATION_MILLIS
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS)
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_REVIEW_PERMISSION_USAGE =
+            "android.intent.action.REVIEW_PERMISSION_USAGE";
+
+    /**
+     * Activity action: Launch UI to review ongoing app uses of permissions.
+     * <p>
+     * Input: {@link #EXTRA_DURATION_MILLIS} specifies the minimum number of milliseconds of recent
+     * activity to show (optional).  Must be non-negative.
+     * </p>
+     * <p>
+     * Output: Nothing.
+     * </p>
+     * <p class="note">
+     * This requires {@link android.Manifest.permission#GRANT_RUNTIME_PERMISSIONS} permission.
+     * </p>
+     *
+     * @see #EXTRA_DURATION_MILLIS
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS)
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_REVIEW_ONGOING_PERMISSION_USAGE =
+            "android.intent.action.REVIEW_ONGOING_PERMISSION_USAGE";
+
+    /**
+     * Activity action: Launch UI to review running accessibility services.
+     * <p>
+     * Input: Nothing.
+     * </p>
+     * <p>
+     * Output: Nothing.
+     * </p>
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.REVIEW_ACCESSIBILITY_SERVICES)
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_REVIEW_ACCESSIBILITY_SERVICES =
+            "android.intent.action.REVIEW_ACCESSIBILITY_SERVICES";
+
+    // ---------------------------------------------------------------------
+    // ---------------------------------------------------------------------
+    // Standard intent broadcast actions (see action variable).
+
+    /**
+     * Broadcast Action: Sent when the device goes to sleep and becomes non-interactive.
+     * <p>
+     * For historical reasons, the name of this broadcast action refers to the power
+     * state of the screen but it is actually sent in response to changes in the
+     * overall interactive state of the device.
+     * </p><p>
+     * This broadcast is sent when the device becomes non-interactive which may have
+     * nothing to do with the screen turning off.  To determine the
+     * actual state of the screen, use {@link android.view.Display#getState}.
+     * </p><p>
+     * See {@link android.os.PowerManager#isInteractive} for details.
+     * </p>
+     * You <em>cannot</em> receive this through components declared in
+     * manifests, only by explicitly registering for it with
+     * {@link Context#registerReceiver(BroadcastReceiver, IntentFilter)
+     * Context.registerReceiver()}.
+     *
+     * <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_SCREEN_OFF = "android.intent.action.SCREEN_OFF";
+
+    /**
+     * Broadcast Action: Sent when the device wakes up and becomes interactive.
+     * <p>
+     * For historical reasons, the name of this broadcast action refers to the power
+     * state of the screen but it is actually sent in response to changes in the
+     * overall interactive state of the device.
+     * </p><p>
+     * This broadcast is sent when the device becomes interactive which may have
+     * nothing to do with the screen turning on.  To determine the
+     * actual state of the screen, use {@link android.view.Display#getState}.
+     * </p><p>
+     * See {@link android.os.PowerManager#isInteractive} for details.
+     * </p>
+     * You <em>cannot</em> receive this through components declared in
+     * manifests, only by explicitly registering for it with
+     * {@link Context#registerReceiver(BroadcastReceiver, IntentFilter)
+     * Context.registerReceiver()}.
+     *
+     * <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_SCREEN_ON = "android.intent.action.SCREEN_ON";
+
+    /**
+     * Broadcast Action: Sent after the system stops dreaming.
+     *
+     * <p class="note">This is a protected intent that can only be sent by the system.
+     * It is only sent to registered receivers.</p>
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_DREAMING_STOPPED = "android.intent.action.DREAMING_STOPPED";
+
+    /**
+     * Broadcast Action: Sent after the system starts dreaming.
+     *
+     * <p class="note">This is a protected intent that can only be sent by the system.
+     * It is only sent to registered receivers.</p>
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_DREAMING_STARTED = "android.intent.action.DREAMING_STARTED";
+
+    /**
+     * Broadcast Action: Sent when the user is present after device wakes up (e.g when the
+     * keyguard is gone).
+     *
+     * <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_USER_PRESENT = "android.intent.action.USER_PRESENT";
+
+    /**
+     * Broadcast Action: The current time has changed.  Sent every
+     * minute.  You <em>cannot</em> receive this through components declared
+     * in manifests, only by explicitly registering for it with
+     * {@link Context#registerReceiver(BroadcastReceiver, IntentFilter)
+     * Context.registerReceiver()}.
+     *
+     * <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_TIME_TICK = "android.intent.action.TIME_TICK";
+    /**
+     * Broadcast Action: The time was set.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_TIME_CHANGED = "android.intent.action.TIME_SET";
+    /**
+     * Broadcast Action: The date has changed.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_DATE_CHANGED = "android.intent.action.DATE_CHANGED";
+    /**
+     * Broadcast Action: The timezone has changed. The intent will have the following extra values:</p>
+     * <ul>
+     *   <li>{@link #EXTRA_TIMEZONE} - The java.util.TimeZone.getID() value identifying the new
+     *   time zone.</li>
+     * </ul>
+     *
+     * <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_TIMEZONE_CHANGED = "android.intent.action.TIMEZONE_CHANGED";
+    /**
+     * Clear DNS Cache Action: This is broadcast when networks have changed and old
+     * DNS entries should be tossed.
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_CLEAR_DNS_CACHE = "android.intent.action.CLEAR_DNS_CACHE";
+    /**
+     * Alarm Changed Action: This is broadcast when the AlarmClock
+     * application's alarm is set or unset.  It is used by the
+     * AlarmClock application and the StatusBar service.
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @UnsupportedAppUsage
+    public static final String ACTION_ALARM_CHANGED = "android.intent.action.ALARM_CHANGED";
+
+    /**
+     * Broadcast Action: This is broadcast once, after the user has finished
+     * booting, but while still in the "locked" state. It can be used to perform
+     * application-specific initialization, such as installing alarms. You must
+     * hold the {@link android.Manifest.permission#RECEIVE_BOOT_COMPLETED}
+     * permission in order to receive this broadcast.
+     * <p>
+     * This broadcast is sent immediately at boot by all devices (regardless of
+     * direct boot support) running {@link android.os.Build.VERSION_CODES#N} or
+     * higher. Upon receipt of this broadcast, the user is still locked and only
+     * device-protected storage can be accessed safely. If you want to access
+     * credential-protected storage, you need to wait for the user to be
+     * unlocked (typically by entering their lock pattern or PIN for the first
+     * time), after which the {@link #ACTION_USER_UNLOCKED} and
+     * {@link #ACTION_BOOT_COMPLETED} broadcasts are sent.
+     * <p>
+     * To receive this broadcast, your receiver component must be marked as
+     * being {@link ComponentInfo#directBootAware}.
+     * <p class="note">
+     * This is a protected intent that can only be sent by the system.
+     *
+     * @see Context#createDeviceProtectedStorageContext()
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_LOCKED_BOOT_COMPLETED = "android.intent.action.LOCKED_BOOT_COMPLETED";
+
+    /**
+     * Broadcast Action: This is broadcast once, after the user has finished
+     * booting. It can be used to perform application-specific initialization,
+     * such as installing alarms. You must hold the
+     * {@link android.Manifest.permission#RECEIVE_BOOT_COMPLETED} permission in
+     * order to receive this broadcast.
+     * <p>
+     * This broadcast is sent at boot by all devices (both with and without
+     * direct boot support). Upon receipt of this broadcast, the user is
+     * unlocked and both device-protected and credential-protected storage can
+     * accessed safely.
+     * <p>
+     * If you need to run while the user is still locked (before they've entered
+     * their lock pattern or PIN for the first time), you can listen for the
+     * {@link #ACTION_LOCKED_BOOT_COMPLETED} broadcast.
+     * <p class="note">
+     * This is a protected intent that can only be sent by the system.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @BroadcastBehavior(includeBackground = true)
+    public static final String ACTION_BOOT_COMPLETED = "android.intent.action.BOOT_COMPLETED";
+
+    /**
+     * Broadcast Action: This is broadcast when a user action should request a
+     * temporary system dialog to dismiss.  Some examples of temporary system
+     * dialogs are the notification window-shade and the recent tasks dialog.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_CLOSE_SYSTEM_DIALOGS = "android.intent.action.CLOSE_SYSTEM_DIALOGS";
+    /**
+     * Broadcast Action: Trigger the download and eventual installation
+     * of a package.
+     * <p>Input: {@link #getData} is the URI of the package file to download.
+     *
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
+     *
+     * @deprecated This constant has never been used.
+     */
+    @Deprecated
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_PACKAGE_INSTALL = "android.intent.action.PACKAGE_INSTALL";
+    /**
+     * Broadcast Action: A new application package has been installed on the
+     * device. The data contains the name of the package.  Note that the
+     * newly installed package does <em>not</em> receive this broadcast.
+     * <p>May include the following extras:
+     * <ul>
+     * <li> {@link #EXTRA_UID} containing the integer uid assigned to the new package.
+     * <li> {@link #EXTRA_REPLACING} is set to true if this is following
+     * an {@link #ACTION_PACKAGE_REMOVED} broadcast for the same package.
+     * </ul>
+     *
+     * <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_PACKAGE_ADDED = "android.intent.action.PACKAGE_ADDED";
+    /**
+     * Broadcast Action: A new version of an application package has been
+     * installed, replacing an existing version that was previously installed.
+     * The data contains the name of the package.
+     * <p>May include the following extras:
+     * <ul>
+     * <li> {@link #EXTRA_UID} containing the integer uid assigned to the new package.
+     * </ul>
+     *
+     * <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_PACKAGE_REPLACED = "android.intent.action.PACKAGE_REPLACED";
+    /**
+     * Broadcast Action: A new version of your application has been installed
+     * over an existing one.  This is only sent to the application that was
+     * replaced.  It does not contain any additional data; to receive it, just
+     * use an intent filter for this action.
+     *
+     * <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_MY_PACKAGE_REPLACED = "android.intent.action.MY_PACKAGE_REPLACED";
+    /**
+     * Broadcast Action: An existing application package has been removed from
+     * the device.  The data contains the name of the package.  The package
+     * that is being removed does <em>not</em> receive this Intent.
+     * <ul>
+     * <li> {@link #EXTRA_UID} containing the integer uid previously assigned
+     * to the package.
+     * <li> {@link #EXTRA_DATA_REMOVED} is set to true if the entire
+     * application -- data and code -- is being removed.
+     * <li> {@link #EXTRA_REPLACING} is set to true if this will be followed
+     * by an {@link #ACTION_PACKAGE_ADDED} broadcast for the same package.
+     * </ul>
+     *
+     * <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_PACKAGE_REMOVED = "android.intent.action.PACKAGE_REMOVED";
+    /**
+     * Broadcast Action: An existing application package has been completely
+     * removed from the device.  The data contains the name of the package.
+     * This is like {@link #ACTION_PACKAGE_REMOVED}, but only set when
+     * {@link #EXTRA_DATA_REMOVED} is true and
+     * {@link #EXTRA_REPLACING} is false of that broadcast.
+     *
+     * <ul>
+     * <li> {@link #EXTRA_UID} containing the integer uid previously assigned
+     * to the package.
+     * </ul>
+     *
+     * <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_PACKAGE_FULLY_REMOVED
+            = "android.intent.action.PACKAGE_FULLY_REMOVED";
+    /**
+     * Broadcast Action: An existing application package has been changed (for
+     * example, a component has been enabled or disabled).  The data contains
+     * the name of the package.
+     * <ul>
+     * <li> {@link #EXTRA_UID} containing the integer uid assigned to the package.
+     * <li> {@link #EXTRA_CHANGED_COMPONENT_NAME_LIST} containing the class name
+     * of the changed components (or the package name itself).
+     * <li> {@link #EXTRA_DONT_KILL_APP} containing boolean field to override the
+     * default action of restarting the application.
+     * </ul>
+     *
+     * <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_PACKAGE_CHANGED = "android.intent.action.PACKAGE_CHANGED";
+    /**
+     * Broadcast Action: Sent to the system rollback manager when a package
+     * needs to have rollback enabled.
+     * <p class="note">
+     * This is a protected intent that can only be sent by the system.
+     * </p>
+     *
+     * @hide This broadcast is used internally by the system.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_PACKAGE_ENABLE_ROLLBACK =
+            "android.intent.action.PACKAGE_ENABLE_ROLLBACK";
+    /**
+     * Broadcast Action: Sent to the system rollback manager when the rollback for a certain
+     * package needs to be cancelled.
+     *
+     * <p class="note">This intent is sent by PackageManagerService to notify RollbackManager
+     * that enabling a specific rollback has timed out.
+     *
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_CANCEL_ENABLE_ROLLBACK =
+            "android.intent.action.CANCEL_ENABLE_ROLLBACK";
+    /**
+     * Broadcast Action: A rollback has been committed.
+     *
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system. The receiver must hold MANAGE_ROLLBACK permission.
+     *
+     * @hide
+     */
+    @SystemApi @TestApi
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_ROLLBACK_COMMITTED =
+            "android.intent.action.ROLLBACK_COMMITTED";
+    /**
+     * @hide
+     * Broadcast Action: Ask system services if there is any reason to
+     * restart the given package.  The data contains the name of the
+     * package.
+     * <ul>
+     * <li> {@link #EXTRA_UID} containing the integer uid assigned to the package.
+     * <li> {@link #EXTRA_PACKAGES} String array of all packages to check.
+     * </ul>
+     *
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_QUERY_PACKAGE_RESTART = "android.intent.action.QUERY_PACKAGE_RESTART";
+    /**
+     * Broadcast Action: The user has restarted a package, and all of its
+     * processes have been killed.  All runtime state
+     * associated with it (processes, alarms, notifications, etc) should
+     * be removed.  Note that the restarted package does <em>not</em>
+     * receive this broadcast.
+     * The data contains the name of the package.
+     * <ul>
+     * <li> {@link #EXTRA_UID} containing the integer uid assigned to the package.
+     * </ul>
+     *
+     * <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_PACKAGE_RESTARTED = "android.intent.action.PACKAGE_RESTARTED";
+    /**
+     * Broadcast Action: The user has cleared the data of a package.  This should
+     * be preceded by {@link #ACTION_PACKAGE_RESTARTED}, after which all of
+     * its persistent data is erased and this broadcast sent.
+     * Note that the cleared package does <em>not</em>
+     * receive this broadcast. The data contains the name of the package.
+     * <ul>
+     * <li> {@link #EXTRA_UID} containing the integer uid assigned to the package. If the
+     *      package whose data was cleared is an uninstalled instant app, then the UID
+     *      will be -1. The platform keeps some meta-data associated with instant apps
+     *      after they are uninstalled.
+     * <li> {@link #EXTRA_PACKAGE_NAME} containing the package name only if the cleared
+     *      data was for an instant app.
+     * </ul>
+     *
+     * <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_PACKAGE_DATA_CLEARED = "android.intent.action.PACKAGE_DATA_CLEARED";
+    /**
+     * Broadcast Action: Packages have been suspended.
+     * <p>Includes the following extras:
+     * <ul>
+     * <li> {@link #EXTRA_CHANGED_PACKAGE_LIST} is the set of packages which have been suspended
+     * <li> {@link #EXTRA_CHANGED_UID_LIST} is the set of uids which have been suspended
+     * </ul>
+     *
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system. It is only sent to registered receivers.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_PACKAGES_SUSPENDED = "android.intent.action.PACKAGES_SUSPENDED";
+    /**
+     * Broadcast Action: Packages have been unsuspended.
+     * <p>Includes the following extras:
+     * <ul>
+     * <li> {@link #EXTRA_CHANGED_PACKAGE_LIST} is the set of packages which have been unsuspended
+     * <li> {@link #EXTRA_CHANGED_UID_LIST} is the set of uids which have been unsuspended
+     * </ul>
+     *
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system. It is only sent to registered receivers.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_PACKAGES_UNSUSPENDED = "android.intent.action.PACKAGES_UNSUSPENDED";
+
+    /**
+     * Broadcast Action: Distracting packages have been changed.
+     * <p>Includes the following extras:
+     * <ul>
+     * <li> {@link #EXTRA_CHANGED_PACKAGE_LIST} is the set of packages which have been changed.
+     * <li> {@link #EXTRA_CHANGED_UID_LIST} is the set of uids which have been changed.
+     * <li> {@link #EXTRA_DISTRACTION_RESTRICTIONS} the new restrictions set on these packages.
+     * </ul>
+     *
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system. It is only sent to registered receivers.
+     *
+     * @see PackageManager#setDistractingPackageRestrictions(String[], int)
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_DISTRACTING_PACKAGES_CHANGED =
+            "android.intent.action.DISTRACTING_PACKAGES_CHANGED";
+
+    /**
+     * Broadcast Action: Sent to a package that has been suspended by the system. This is sent
+     * whenever a package is put into a suspended state or any of its app extras change while in the
+     * suspended state.
+     * <p> Optionally includes the following extras:
+     * <ul>
+     *     <li> {@link #EXTRA_SUSPENDED_PACKAGE_EXTRAS} which is a {@link Bundle} which will contain
+     *     useful information for the app being suspended.
+     * </ul>
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system. <em>This will be delivered to {@link BroadcastReceiver} components declared in
+     * the manifest.</em>
+     *
+     * @see #ACTION_MY_PACKAGE_UNSUSPENDED
+     * @see #EXTRA_SUSPENDED_PACKAGE_EXTRAS
+     * @see PackageManager#isPackageSuspended()
+     * @see PackageManager#getSuspendedPackageAppExtras()
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_MY_PACKAGE_SUSPENDED = "android.intent.action.MY_PACKAGE_SUSPENDED";
+
+    /**
+     * Activity Action: Started to show more details about why an application was suspended.
+     *
+     * <p>Whenever the system detects an activity launch for a suspended app, this action can
+     * be used to show more details about the reason for suspension.
+     *
+     * <p>Apps holding {@link android.Manifest.permission#SUSPEND_APPS} must declare an activity
+     * handling this intent and protect it with
+     * {@link android.Manifest.permission#SEND_SHOW_SUSPENDED_APP_DETAILS}.
+     *
+     * <p>Includes an extra {@link #EXTRA_PACKAGE_NAME} which is the name of the suspended package.
+     *
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
+     *
+     * @see PackageManager#setPackagesSuspended(String[], boolean, PersistableBundle,
+     * PersistableBundle, String)
+     * @see PackageManager#isPackageSuspended()
+     * @see #ACTION_PACKAGES_SUSPENDED
+     *
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_SHOW_SUSPENDED_APP_DETAILS =
+            "android.intent.action.SHOW_SUSPENDED_APP_DETAILS";
+
+    /**
+     * Broadcast Action: Sent to indicate that the user unsuspended a package.
+     *
+     * <p>This can happen when the user taps on the neutral button of the
+     * {@linkplain SuspendDialogInfo suspend-dialog} which was created by using
+     * {@link SuspendDialogInfo#BUTTON_ACTION_UNSUSPEND}. This broadcast is only sent to the
+     * suspending app that originally specified this dialog while calling
+     * {@link PackageManager#setPackagesSuspended(String[], boolean, PersistableBundle,
+     * PersistableBundle, SuspendDialogInfo)}.
+     *
+     * <p>Includes an extra {@link #EXTRA_PACKAGE_NAME} which is the name of the package that just
+     * got unsuspended.
+     *
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system. <em>This will be delivered to {@link BroadcastReceiver} components declared in
+     * the manifest.</em>
+     *
+     * @see PackageManager#setPackagesSuspended(String[], boolean, PersistableBundle,
+     * PersistableBundle, SuspendDialogInfo)
+     * @see PackageManager#isPackageSuspended()
+     * @see SuspendDialogInfo#BUTTON_ACTION_MORE_DETAILS
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_PACKAGE_UNSUSPENDED_MANUALLY =
+            "android.intent.action.PACKAGE_UNSUSPENDED_MANUALLY";
+
+    /**
+     * Broadcast Action: Sent to a package that has been unsuspended.
+     *
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system. <em>This will be delivered to {@link BroadcastReceiver} components declared in
+     * the manifest.</em>
+     *
+     * @see #ACTION_MY_PACKAGE_SUSPENDED
+     * @see #EXTRA_SUSPENDED_PACKAGE_EXTRAS
+     * @see PackageManager#isPackageSuspended()
+     * @see PackageManager#getSuspendedPackageAppExtras()
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_MY_PACKAGE_UNSUSPENDED = "android.intent.action.MY_PACKAGE_UNSUSPENDED";
+
+    /**
+     * Broadcast Action: A user ID has been removed from the system.  The user
+     * ID number is stored in the extra data under {@link #EXTRA_UID}.
+     *
+     * <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_UID_REMOVED = "android.intent.action.UID_REMOVED";
+
+    /**
+     * Broadcast Action: Sent to the installer package of an application when
+     * that application is first launched (that is the first time it is moved
+     * out of the stopped state).  The data contains the name of the package.
+     *
+     * <p>When the application is first launched, the application itself doesn't receive this
+     * broadcast.</p>
+     *
+     * <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_PACKAGE_FIRST_LAUNCH = "android.intent.action.PACKAGE_FIRST_LAUNCH";
+
+    /**
+     * Broadcast Action: Sent to the system package verifier when a package
+     * needs to be verified. The data contains the package URI.
+     * <p class="note">
+     * This is a protected intent that can only be sent by the system.
+     * </p>
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_PACKAGE_NEEDS_VERIFICATION = "android.intent.action.PACKAGE_NEEDS_VERIFICATION";
+
+    /**
+     * Broadcast Action: Sent to the system package verifier when a package is
+     * verified. The data contains the package URI.
+     * <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_PACKAGE_VERIFIED = "android.intent.action.PACKAGE_VERIFIED";
+
+    /**
+     * Broadcast Action: Sent to the system intent filter verifier when an
+     * intent filter needs to be verified. The data contains the filter data
+     * hosts to be verified against.
+     * <p class="note">
+     * This is a protected intent that can only be sent by the system.
+     * </p>
+     *
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_INTENT_FILTER_NEEDS_VERIFICATION = "android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION";
+
+    /**
+     * Broadcast Action: Resources for a set of packages (which were
+     * previously unavailable) are currently
+     * available since the media on which they exist is available.
+     * The extra data {@link #EXTRA_CHANGED_PACKAGE_LIST} contains a
+     * list of packages whose availability changed.
+     * The extra data {@link #EXTRA_CHANGED_UID_LIST} contains a
+     * list of uids of packages whose availability changed.
+     * Note that the
+     * packages in this list do <em>not</em> receive this broadcast.
+     * The specified set of packages are now available on the system.
+     * <p>Includes the following extras:
+     * <ul>
+     * <li> {@link #EXTRA_CHANGED_PACKAGE_LIST} is the set of packages
+     * whose resources(were previously unavailable) are currently available.
+     * {@link #EXTRA_CHANGED_UID_LIST} is the set of uids of the
+     * packages whose resources(were previously unavailable)
+     * are  currently available.
+     * </ul>
+     *
+     * <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_EXTERNAL_APPLICATIONS_AVAILABLE =
+        "android.intent.action.EXTERNAL_APPLICATIONS_AVAILABLE";
+
+    /**
+     * Broadcast Action: Resources for a set of packages are currently
+     * unavailable since the media on which they exist is unavailable.
+     * The extra data {@link #EXTRA_CHANGED_PACKAGE_LIST} contains a
+     * list of packages whose availability changed.
+     * The extra data {@link #EXTRA_CHANGED_UID_LIST} contains a
+     * list of uids of packages whose availability changed.
+     * The specified set of packages can no longer be
+     * launched and are practically unavailable on the system.
+     * <p>Inclues the following extras:
+     * <ul>
+     * <li> {@link #EXTRA_CHANGED_PACKAGE_LIST} is the set of packages
+     * whose resources are no longer available.
+     * {@link #EXTRA_CHANGED_UID_LIST} is the set of packages
+     * whose resources are no longer available.
+     * </ul>
+     *
+     * <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_EXTERNAL_APPLICATIONS_UNAVAILABLE =
+        "android.intent.action.EXTERNAL_APPLICATIONS_UNAVAILABLE";
+
+    /**
+     * Broadcast Action: preferred activities have changed *explicitly*.
+     *
+     * <p>Note there are cases where a preferred activity is invalidated *implicitly*, e.g.
+     * when an app is installed or uninstalled, but in such cases this broadcast will *not*
+     * be sent.
+     *
+     * {@link #EXTRA_USER_HANDLE} contains the user ID in question.
+     *
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_PREFERRED_ACTIVITY_CHANGED =
+            "android.intent.action.ACTION_PREFERRED_ACTIVITY_CHANGED";
+
+
+    /**
+     * Broadcast Action:  The current system wallpaper has changed.  See
+     * {@link android.app.WallpaperManager} for retrieving the new wallpaper.
+     * This should <em>only</em> be used to determine when the wallpaper
+     * has changed to show the new wallpaper to the user.  You should certainly
+     * never, in response to this, change the wallpaper or other attributes of
+     * it such as the suggested size.  That would be crazy, right?  You'd cause
+     * all kinds of loops, especially if other apps are doing similar things,
+     * right?  Of course.  So please don't do this.
+     *
+     * @deprecated Modern applications should use
+     * {@link android.view.WindowManager.LayoutParams#FLAG_SHOW_WALLPAPER
+     * WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER} to have the wallpaper
+     * shown behind their UI, rather than watching for this broadcast and
+     * rendering the wallpaper on their own.
+     */
+    @Deprecated @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_WALLPAPER_CHANGED = "android.intent.action.WALLPAPER_CHANGED";
+    /**
+     * Broadcast Action: The current device {@link android.content.res.Configuration}
+     * (orientation, locale, etc) has changed.  When such a change happens, the
+     * UIs (view hierarchy) will need to be rebuilt based on this new
+     * information; for the most part, applications don't need to worry about
+     * this, because the system will take care of stopping and restarting the
+     * application to make sure it sees the new changes.  Some system code that
+     * can not be restarted will need to watch for this action and handle it
+     * appropriately.
+     *
+     * <p class="note">
+     * You <em>cannot</em> receive this through components declared
+     * in manifests, only by explicitly registering for it with
+     * {@link Context#registerReceiver(BroadcastReceiver, IntentFilter)
+     * Context.registerReceiver()}.
+     *
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
+     *
+     * @see android.content.res.Configuration
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_CONFIGURATION_CHANGED = "android.intent.action.CONFIGURATION_CHANGED";
+
+    /**
+     * Broadcast Action: The current device {@link android.content.res.Configuration} has changed
+     * such that the device may be eligible for the installation of additional configuration splits.
+     * Configuration properties that can trigger this broadcast include locale and display density.
+     *
+     * <p class="note">
+     * Unlike {@link #ACTION_CONFIGURATION_CHANGED}, you <em>can</em> receive this through
+     * components declared in manifests. However, the receiver <em>must</em> hold the
+     * {@link android.Manifest.permission#INSTALL_PACKAGES} permission.
+     *
+     * <p class="note">
+     * This is a protected intent that can only be sent by the system.
+     *
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_SPLIT_CONFIGURATION_CHANGED =
+            "android.intent.action.SPLIT_CONFIGURATION_CHANGED";
+    /**
+     * Broadcast Action: The current device's locale has changed.
+     *
+     * <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_LOCALE_CHANGED = "android.intent.action.LOCALE_CHANGED";
+    /**
+     * Broadcast Action:  This is a <em>sticky broadcast</em> containing the
+     * charging state, level, and other information about the battery.
+     * See {@link android.os.BatteryManager} for documentation on the
+     * contents of the Intent.
+     *
+     * <p class="note">
+     * You <em>cannot</em> receive this through components declared
+     * in manifests, only by explicitly registering for it with
+     * {@link Context#registerReceiver(BroadcastReceiver, IntentFilter)
+     * Context.registerReceiver()}.  See {@link #ACTION_BATTERY_LOW},
+     * {@link #ACTION_BATTERY_OKAY}, {@link #ACTION_POWER_CONNECTED},
+     * and {@link #ACTION_POWER_DISCONNECTED} for distinct battery-related
+     * broadcasts that are sent and can be received through manifest
+     * receivers.
+     *
+     * <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_BATTERY_CHANGED = "android.intent.action.BATTERY_CHANGED";
+
+
+    /**
+     * Broadcast Action: Sent when the current battery level changes.
+     *
+     * It has {@link android.os.BatteryManager#EXTRA_EVENTS} that carries a list of {@link Bundle}
+     * instances representing individual battery level changes with associated
+     * extras from {@link #ACTION_BATTERY_CHANGED}.
+     *
+     * <p class="note">
+     * This broadcast requires {@link android.Manifest.permission#BATTERY_STATS} permission.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String ACTION_BATTERY_LEVEL_CHANGED =
+            "android.intent.action.BATTERY_LEVEL_CHANGED";
+    /**
+     * Broadcast Action:  Indicates low battery condition on the device.
+     * This broadcast corresponds to the "Low battery warning" system dialog.
+     *
+     * <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_BATTERY_LOW = "android.intent.action.BATTERY_LOW";
+    /**
+     * Broadcast Action:  Indicates the battery is now okay after being low.
+     * This will be sent after {@link #ACTION_BATTERY_LOW} once the battery has
+     * gone back up to an okay state.
+     *
+     * <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_BATTERY_OKAY = "android.intent.action.BATTERY_OKAY";
+    /**
+     * Broadcast Action:  External power has been connected to the device.
+     * This is intended for applications that wish to register specifically to this notification.
+     * Unlike ACTION_BATTERY_CHANGED, applications will be woken for this and so do not have to
+     * stay active to receive this notification.  This action can be used to implement actions
+     * that wait until power is available to trigger.
+     *
+     * <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_POWER_CONNECTED = "android.intent.action.ACTION_POWER_CONNECTED";
+    /**
+     * Broadcast Action:  External power has been removed from the device.
+     * This is intended for applications that wish to register specifically to this notification.
+     * Unlike ACTION_BATTERY_CHANGED, applications will be woken for this and so do not have to
+     * stay active to receive this notification.  This action can be used to implement actions
+     * that wait until power is available to trigger.
+     *
+     * <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_POWER_DISCONNECTED =
+            "android.intent.action.ACTION_POWER_DISCONNECTED";
+    /**
+     * Broadcast Action:  Device is shutting down.
+     * This is broadcast when the device is being shut down (completely turned
+     * off, not sleeping).  Once the broadcast is complete, the final shutdown
+     * will proceed and all unsaved data lost.  Apps will not normally need
+     * to handle this, since the foreground activity will be paused as well.
+     * <p>As of {@link Build.VERSION_CODES#P} this broadcast is only sent to receivers registered
+     * through {@link Context#registerReceiver(BroadcastReceiver, IntentFilter)
+     * Context.registerReceiver}.
+     *
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
+     * <p>May include the following extras:
+     * <ul>
+     * <li> {@link #EXTRA_SHUTDOWN_USERSPACE_ONLY} a boolean that is set to true if this
+     * shutdown is only for userspace processes.  If not set, assumed to be false.
+     * </ul>
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_SHUTDOWN = "android.intent.action.ACTION_SHUTDOWN";
+    /**
+     * Activity Action:  Start this activity to request system shutdown.
+     * The optional boolean extra field {@link #EXTRA_KEY_CONFIRM} can be set to true
+     * to request confirmation from the user before shutting down. The optional boolean
+     * extra field {@link #EXTRA_USER_REQUESTED_SHUTDOWN} can be set to true to
+     * indicate that the shutdown is requested by the user.
+     *
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
+     *
+     * {@hide}
+     */
+    public static final String ACTION_REQUEST_SHUTDOWN
+            = "com.android.internal.intent.action.REQUEST_SHUTDOWN";
+    /**
+     * Broadcast Action: A sticky broadcast that indicates low storage space
+     * condition on the device
+     * <p class="note">
+     * This is a protected intent that can only be sent by the system.
+     *
+     * @deprecated if your app targets {@link android.os.Build.VERSION_CODES#O}
+     *             or above, this broadcast will no longer be delivered to any
+     *             {@link BroadcastReceiver} defined in your manifest. Instead,
+     *             apps are strongly encouraged to use the improved
+     *             {@link Context#getCacheDir()} behavior so the system can
+     *             automatically free up storage when needed.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @Deprecated
+    public static final String ACTION_DEVICE_STORAGE_LOW = "android.intent.action.DEVICE_STORAGE_LOW";
+    /**
+     * Broadcast Action: Indicates low storage space condition on the device no
+     * longer exists
+     * <p class="note">
+     * This is a protected intent that can only be sent by the system.
+     *
+     * @deprecated if your app targets {@link android.os.Build.VERSION_CODES#O}
+     *             or above, this broadcast will no longer be delivered to any
+     *             {@link BroadcastReceiver} defined in your manifest. Instead,
+     *             apps are strongly encouraged to use the improved
+     *             {@link Context#getCacheDir()} behavior so the system can
+     *             automatically free up storage when needed.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @Deprecated
+    public static final String ACTION_DEVICE_STORAGE_OK = "android.intent.action.DEVICE_STORAGE_OK";
+    /**
+     * Broadcast Action: A sticky broadcast that indicates a storage space full
+     * condition on the device. This is intended for activities that want to be
+     * able to fill the data partition completely, leaving only enough free
+     * space to prevent system-wide SQLite failures.
+     * <p class="note">
+     * This is a protected intent that can only be sent by the system.
+     *
+     * @deprecated if your app targets {@link android.os.Build.VERSION_CODES#O}
+     *             or above, this broadcast will no longer be delivered to any
+     *             {@link BroadcastReceiver} defined in your manifest. Instead,
+     *             apps are strongly encouraged to use the improved
+     *             {@link Context#getCacheDir()} behavior so the system can
+     *             automatically free up storage when needed.
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @Deprecated
+    public static final String ACTION_DEVICE_STORAGE_FULL = "android.intent.action.DEVICE_STORAGE_FULL";
+    /**
+     * Broadcast Action: Indicates storage space full condition on the device no
+     * longer exists.
+     * <p class="note">
+     * This is a protected intent that can only be sent by the system.
+     *
+     * @deprecated if your app targets {@link android.os.Build.VERSION_CODES#O}
+     *             or above, this broadcast will no longer be delivered to any
+     *             {@link BroadcastReceiver} defined in your manifest. Instead,
+     *             apps are strongly encouraged to use the improved
+     *             {@link Context#getCacheDir()} behavior so the system can
+     *             automatically free up storage when needed.
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @Deprecated
+    public static final String ACTION_DEVICE_STORAGE_NOT_FULL = "android.intent.action.DEVICE_STORAGE_NOT_FULL";
+    /**
+     * Broadcast Action:  Indicates low memory condition notification acknowledged by user
+     * and package management should be started.
+     * This is triggered by the user from the ACTION_DEVICE_STORAGE_LOW
+     * notification.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_MANAGE_PACKAGE_STORAGE = "android.intent.action.MANAGE_PACKAGE_STORAGE";
+    /**
+     * Broadcast Action:  The device has entered USB Mass Storage mode.
+     * This is used mainly for the USB Settings panel.
+     * Apps should listen for ACTION_MEDIA_MOUNTED and ACTION_MEDIA_UNMOUNTED broadcasts to be notified
+     * when the SD card file system is mounted or unmounted
+     * @deprecated replaced by android.os.storage.StorageEventListener
+     */
+    @Deprecated
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_UMS_CONNECTED = "android.intent.action.UMS_CONNECTED";
+
+    /**
+     * Broadcast Action:  The device has exited USB Mass Storage mode.
+     * This is used mainly for the USB Settings panel.
+     * Apps should listen for ACTION_MEDIA_MOUNTED and ACTION_MEDIA_UNMOUNTED broadcasts to be notified
+     * when the SD card file system is mounted or unmounted
+     * @deprecated replaced by android.os.storage.StorageEventListener
+     */
+    @Deprecated
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_UMS_DISCONNECTED = "android.intent.action.UMS_DISCONNECTED";
+
+    /**
+     * Broadcast Action:  External media has been removed.
+     * The path to the mount point for the removed media is contained in the Intent.mData field.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_MEDIA_REMOVED = "android.intent.action.MEDIA_REMOVED";
+
+    /**
+     * Broadcast Action:  External media is present, but not mounted at its mount point.
+     * The path to the mount point for the unmounted media is contained in the Intent.mData field.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_MEDIA_UNMOUNTED = "android.intent.action.MEDIA_UNMOUNTED";
+
+    /**
+     * Broadcast Action:  External media is present, and being disk-checked
+     * The path to the mount point for the checking media is contained in the Intent.mData field.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_MEDIA_CHECKING = "android.intent.action.MEDIA_CHECKING";
+
+    /**
+     * Broadcast Action:  External media is present, but is using an incompatible fs (or is blank)
+     * The path to the mount point for the checking media is contained in the Intent.mData field.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_MEDIA_NOFS = "android.intent.action.MEDIA_NOFS";
+
+    /**
+     * Broadcast Action:  External media is present and mounted at its mount point.
+     * The path to the mount point for the mounted media is contained in the Intent.mData field.
+     * The Intent contains an extra with name "read-only" and Boolean value to indicate if the
+     * media was mounted read only.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_MEDIA_MOUNTED = "android.intent.action.MEDIA_MOUNTED";
+
+    /**
+     * Broadcast Action:  External media is unmounted because it is being shared via USB mass storage.
+     * The path to the mount point for the shared media is contained in the Intent.mData field.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_MEDIA_SHARED = "android.intent.action.MEDIA_SHARED";
+
+    /**
+     * Broadcast Action:  External media is no longer being shared via USB mass storage.
+     * The path to the mount point for the previously shared media is contained in the Intent.mData field.
+     *
+     * @hide
+     */
+    public static final String ACTION_MEDIA_UNSHARED = "android.intent.action.MEDIA_UNSHARED";
+
+    /**
+     * Broadcast Action:  External media was removed from SD card slot, but mount point was not unmounted.
+     * The path to the mount point for the removed media is contained in the Intent.mData field.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_MEDIA_BAD_REMOVAL = "android.intent.action.MEDIA_BAD_REMOVAL";
+
+    /**
+     * Broadcast Action:  External media is present but cannot be mounted.
+     * The path to the mount point for the unmountable media is contained in the Intent.mData field.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_MEDIA_UNMOUNTABLE = "android.intent.action.MEDIA_UNMOUNTABLE";
+
+   /**
+     * Broadcast Action:  User has expressed the desire to remove the external storage media.
+     * Applications should close all files they have open within the mount point when they receive this intent.
+     * The path to the mount point for the media to be ejected is contained in the Intent.mData field.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_MEDIA_EJECT = "android.intent.action.MEDIA_EJECT";
+
+    /**
+     * Broadcast Action:  The media scanner has started scanning a directory.
+     * The path to the directory being scanned is contained in the Intent.mData field.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_MEDIA_SCANNER_STARTED = "android.intent.action.MEDIA_SCANNER_STARTED";
+
+   /**
+     * Broadcast Action:  The media scanner has finished scanning a directory.
+     * The path to the scanned directory is contained in the Intent.mData field.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_MEDIA_SCANNER_FINISHED = "android.intent.action.MEDIA_SCANNER_FINISHED";
+
+    /**
+     * Broadcast Action: Request the media scanner to scan a file and add it to
+     * the media database.
+     * <p>
+     * The path to the file is contained in {@link Intent#getData()}.
+     *
+     * @deprecated Callers should migrate to inserting items directly into
+     *             {@link MediaStore}, where they will be automatically scanned
+     *             after each mutation.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @Deprecated
+    public static final String ACTION_MEDIA_SCANNER_SCAN_FILE = "android.intent.action.MEDIA_SCANNER_SCAN_FILE";
+
+   /**
+     * Broadcast Action:  The "Media Button" was pressed.  Includes a single
+     * extra field, {@link #EXTRA_KEY_EVENT}, containing the key event that
+     * caused the broadcast.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_MEDIA_BUTTON = "android.intent.action.MEDIA_BUTTON";
+
+    /**
+     * Broadcast Action:  The "Camera Button" was pressed.  Includes a single
+     * extra field, {@link #EXTRA_KEY_EVENT}, containing the key event that
+     * caused the broadcast.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_CAMERA_BUTTON = "android.intent.action.CAMERA_BUTTON";
+
+    // *** NOTE: @todo(*) The following really should go into a more domain-specific
+    // location; they are not general-purpose actions.
+
+    /**
+     * Broadcast Action: A GTalk connection has been established.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_GTALK_SERVICE_CONNECTED =
+            "android.intent.action.GTALK_CONNECTED";
+
+    /**
+     * Broadcast Action: A GTalk connection has been disconnected.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_GTALK_SERVICE_DISCONNECTED =
+            "android.intent.action.GTALK_DISCONNECTED";
+
+    /**
+     * Broadcast Action: An input method has been changed.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_INPUT_METHOD_CHANGED =
+            "android.intent.action.INPUT_METHOD_CHANGED";
+
+    /**
+     * <p>Broadcast Action: The user has switched the phone into or out of Airplane Mode. One or
+     * more radios have been turned off or on. The intent will have the following extra value:</p>
+     * <ul>
+     *   <li><em>state</em> - A boolean value indicating whether Airplane Mode is on. If true,
+     *   then cell radio and possibly other radios such as bluetooth or WiFi may have also been
+     *   turned off</li>
+     * </ul>
+     *
+     * <p class="note">This is a protected intent that can only be sent by the system.</p>
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_AIRPLANE_MODE_CHANGED = "android.intent.action.AIRPLANE_MODE";
+
+    /**
+     * Broadcast Action: Some content providers have parts of their namespace
+     * where they publish new events or items that the user may be especially
+     * interested in. For these things, they may broadcast this action when the
+     * set of interesting items change.
+     *
+     * For example, GmailProvider sends this notification when the set of unread
+     * mail in the inbox changes.
+     *
+     * <p>The data of the intent identifies which part of which provider
+     * changed. When queried through the content resolver, the data URI will
+     * return the data set in question.
+     *
+     * <p>The intent will have the following extra values:
+     * <ul>
+     *   <li><em>count</em> - The number of items in the data set. This is the
+     *       same as the number of items in the cursor returned by querying the
+     *       data URI. </li>
+     * </ul>
+     *
+     * This intent will be sent at boot (if the count is non-zero) and when the
+     * data set changes. It is possible for the data set to change without the
+     * count changing (for example, if a new unread message arrives in the same
+     * sync operation in which a message is archived). The phone should still
+     * ring/vibrate/etc as normal in this case.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_PROVIDER_CHANGED =
+            "android.intent.action.PROVIDER_CHANGED";
+
+    /**
+     * Broadcast Action: Wired Headset plugged in or unplugged.
+     *
+     * Same as {@link android.media.AudioManager#ACTION_HEADSET_PLUG}, to be consulted for value
+     *   and documentation.
+     * <p>If the minimum SDK version of your application is
+     * {@link android.os.Build.VERSION_CODES#LOLLIPOP}, it is recommended to refer
+     * to the <code>AudioManager</code> constant in your receiver registration code instead.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_HEADSET_PLUG = android.media.AudioManager.ACTION_HEADSET_PLUG;
+
+    /**
+     * <p>Broadcast Action: The user has switched on advanced settings in the settings app:</p>
+     * <ul>
+     *   <li><em>state</em> - A boolean value indicating whether the settings is on or off.</li>
+     * </ul>
+     *
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
+     *
+     * @hide
+     */
+    //@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_ADVANCED_SETTINGS_CHANGED
+            = "android.intent.action.ADVANCED_SETTINGS";
+
+    /**
+     *  Broadcast Action: Sent after application restrictions are changed.
+     *
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.</p>
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_APPLICATION_RESTRICTIONS_CHANGED =
+            "android.intent.action.APPLICATION_RESTRICTIONS_CHANGED";
+
+    /**
+     * Broadcast Action: An outgoing call is about to be placed.
+     *
+     * <p>The Intent will have the following extra value:</p>
+     * <ul>
+     *   <li><em>{@link android.content.Intent#EXTRA_PHONE_NUMBER}</em> -
+     *       the phone number originally intended to be dialed.</li>
+     * </ul>
+     * <p>Once the broadcast is finished, the resultData is used as the actual
+     * number to call.  If  <code>null</code>, no call will be placed.</p>
+     * <p>It is perfectly acceptable for multiple receivers to process the
+     * outgoing call in turn: for example, a parental control application
+     * might verify that the user is authorized to place the call at that
+     * time, then a number-rewriting application might add an area code if
+     * one was not specified.</p>
+     * <p>For consistency, any receiver whose purpose is to prohibit phone
+     * calls should have a priority of 0, to ensure it will see the final
+     * phone number to be dialed.
+     * Any receiver whose purpose is to rewrite phone numbers to be called
+     * should have a positive priority.
+     * Negative priorities are reserved for the system for this broadcast;
+     * using them may cause problems.</p>
+     * <p>Any BroadcastReceiver receiving this Intent <em>must not</em>
+     * abort the broadcast.</p>
+     * <p>Emergency calls cannot be intercepted using this mechanism, and
+     * other calls cannot be modified to call emergency numbers using this
+     * mechanism.
+     * <p>Some apps (such as VoIP apps) may want to redirect the outgoing
+     * call to use their own service instead. Those apps should first prevent
+     * the call from being placed by setting resultData to <code>null</code>
+     * and then start their own app to make the call.
+     * <p>You must hold the
+     * {@link android.Manifest.permission#PROCESS_OUTGOING_CALLS}
+     * permission to receive this Intent.</p>
+     *
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
+     *
+     * <p class="note">If the user has chosen a {@link android.telecom.CallRedirectionService} to
+     * handle redirection of outgoing calls, this intent will NOT be sent as an ordered broadcast.
+     * This means that attempts to re-write the outgoing call by other apps using this intent will
+     * be ignored.
+     * </p>
+     *
+     * @deprecated Apps that redirect outgoing calls should use the
+     * {@link android.telecom.CallRedirectionService} API.  Apps that perform call screening
+     * should use the {@link android.telecom.CallScreeningService} API.  Apps which need to be
+     * notified of basic call state should use
+     * {@link android.telephony.PhoneStateListener#onCallStateChanged(int, String)} to determine
+     * when a new outgoing call is placed.
+     */
+    @Deprecated
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_NEW_OUTGOING_CALL =
+            "android.intent.action.NEW_OUTGOING_CALL";
+
+    /**
+     * Broadcast Action: Have the device reboot.  This is only for use by
+     * system code.
+     *
+     * <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_REBOOT =
+            "android.intent.action.REBOOT";
+
+    /**
+     * Broadcast Action:  A sticky broadcast for changes in the physical
+     * docking state of the device.
+     *
+     * <p>The intent will have the following extra values:
+     * <ul>
+     *   <li><em>{@link #EXTRA_DOCK_STATE}</em> - the current dock
+     *       state, indicating which dock the device is physically in.</li>
+     * </ul>
+     * <p>This is intended for monitoring the current physical dock state.
+     * See {@link android.app.UiModeManager} for the normal API dealing with
+     * dock mode changes.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_DOCK_EVENT =
+            "android.intent.action.DOCK_EVENT";
+
+    /**
+     * Broadcast Action: A broadcast when idle maintenance can be started.
+     * This means that the user is not interacting with the device and is
+     * not expected to do so soon. Typical use of the idle maintenance is
+     * to perform somehow expensive tasks that can be postponed at a moment
+     * when they will not degrade user experience.
+     * <p>
+     * <p class="note">In order to keep the device responsive in case of an
+     * unexpected user interaction, implementations of a maintenance task
+     * should be interruptible. In such a scenario a broadcast with action
+     * {@link #ACTION_IDLE_MAINTENANCE_END} will be sent. In other words, you
+     * should not do the maintenance work in
+     * {@link BroadcastReceiver#onReceive(Context, Intent)}, rather start a
+     * maintenance service by {@link Context#startService(Intent)}. Also
+     * you should hold a wake lock while your maintenance service is running
+     * to prevent the device going to sleep.
+     * </p>
+     * <p>
+     * <p class="note">This is a protected intent that can only be sent by
+     * the system.
+     * </p>
+     *
+     * @see #ACTION_IDLE_MAINTENANCE_END
+     *
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_IDLE_MAINTENANCE_START =
+            "android.intent.action.ACTION_IDLE_MAINTENANCE_START";
+
+    /**
+     * Broadcast Action:  A broadcast when idle maintenance should be stopped.
+     * This means that the user was not interacting with the device as a result
+     * of which a broadcast with action {@link #ACTION_IDLE_MAINTENANCE_START}
+     * was sent and now the user started interacting with the device. Typical
+     * use of the idle maintenance is to perform somehow expensive tasks that
+     * can be postponed at a moment when they will not degrade user experience.
+     * <p>
+     * <p class="note">In order to keep the device responsive in case of an
+     * unexpected user interaction, implementations of a maintenance task
+     * should be interruptible. Hence, on receiving a broadcast with this
+     * action, the maintenance task should be interrupted as soon as possible.
+     * In other words, you should not do the maintenance work in
+     * {@link BroadcastReceiver#onReceive(Context, Intent)}, rather stop the
+     * maintenance service that was started on receiving of
+     * {@link #ACTION_IDLE_MAINTENANCE_START}.Also you should release the wake
+     * lock you acquired when your maintenance service started.
+     * </p>
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
+     *
+     * @see #ACTION_IDLE_MAINTENANCE_START
+     *
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_IDLE_MAINTENANCE_END =
+            "android.intent.action.ACTION_IDLE_MAINTENANCE_END";
+
+    /**
+     * Broadcast Action: a remote intent is to be broadcasted.
+     *
+     * A remote intent is used for remote RPC between devices. The remote intent
+     * is serialized and sent from one device to another device. The receiving
+     * device parses the remote intent and broadcasts it. Note that anyone can
+     * broadcast a remote intent. However, if the intent receiver of the remote intent
+     * does not trust intent broadcasts from arbitrary intent senders, it should require
+     * the sender to hold certain permissions so only trusted sender's broadcast will be
+     * let through.
+     * @hide
+     */
+    public static final String ACTION_REMOTE_INTENT =
+            "com.google.android.c2dm.intent.RECEIVE";
+
+    /**
+     * Broadcast Action: This is broadcast once when the user is booting after a
+     * system update. It can be used to perform cleanup or upgrades after a
+     * system update.
+     * <p>
+     * This broadcast is sent after the {@link #ACTION_LOCKED_BOOT_COMPLETED}
+     * broadcast but before the {@link #ACTION_BOOT_COMPLETED} broadcast. It's
+     * only sent when the {@link Build#FINGERPRINT} has changed, and it's only
+     * sent to receivers in the system image.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String ACTION_PRE_BOOT_COMPLETED =
+            "android.intent.action.PRE_BOOT_COMPLETED";
+
+    /**
+     * Broadcast to a specific application to query any supported restrictions to impose
+     * on restricted users. The broadcast intent contains an extra
+     * {@link #EXTRA_RESTRICTIONS_BUNDLE} with the currently persisted
+     * restrictions as a Bundle of key/value pairs. The value types can be Boolean, String or
+     * String[] depending on the restriction type.<p/>
+     * The response should contain an extra {@link #EXTRA_RESTRICTIONS_LIST},
+     * which is of type <code>ArrayList&lt;RestrictionEntry&gt;</code>. It can also
+     * contain an extra {@link #EXTRA_RESTRICTIONS_INTENT}, which is of type <code>Intent</code>.
+     * The activity specified by that intent will be launched for a result which must contain
+     * one of the extras {@link #EXTRA_RESTRICTIONS_LIST} or {@link #EXTRA_RESTRICTIONS_BUNDLE}.
+     * The keys and values of the returned restrictions will be persisted.
+     * @see RestrictionEntry
+     */
+    public static final String ACTION_GET_RESTRICTION_ENTRIES =
+            "android.intent.action.GET_RESTRICTION_ENTRIES";
+
+    /**
+     * Sent the first time a user is starting, to allow system apps to
+     * perform one time initialization.  (This will not be seen by third
+     * party applications because a newly initialized user does not have any
+     * third party applications installed for it.)  This is sent early in
+     * starting the user, around the time the home app is started, before
+     * {@link #ACTION_BOOT_COMPLETED} is sent.  This is sent as a foreground
+     * broadcast, since it is part of a visible user interaction; be as quick
+     * as possible when handling it.
+     */
+    public static final String ACTION_USER_INITIALIZE =
+            "android.intent.action.USER_INITIALIZE";
+
+    /**
+     * Sent when a user switch is happening, causing the process's user to be
+     * brought to the foreground.  This is only sent to receivers registered
+     * through {@link Context#registerReceiver(BroadcastReceiver, IntentFilter)
+     * Context.registerReceiver}.  It is sent to the user that is going to the
+     * foreground.  This is sent as a foreground
+     * broadcast, since it is part of a visible user interaction; be as quick
+     * as possible when handling it.
+     */
+    public static final String ACTION_USER_FOREGROUND =
+            "android.intent.action.USER_FOREGROUND";
+
+    /**
+     * Sent when a user switch is happening, causing the process's user to be
+     * sent to the background.  This is only sent to receivers registered
+     * through {@link Context#registerReceiver(BroadcastReceiver, IntentFilter)
+     * Context.registerReceiver}.  It is sent to the user that is going to the
+     * background.  This is sent as a foreground
+     * broadcast, since it is part of a visible user interaction; be as quick
+     * as possible when handling it.
+     */
+    public static final String ACTION_USER_BACKGROUND =
+            "android.intent.action.USER_BACKGROUND";
+
+    /**
+     * Broadcast sent to the system when a user is added. Carries an extra
+     * EXTRA_USER_HANDLE that has the userHandle of the new user.  It is sent to
+     * all running users.  You must hold
+     * {@link android.Manifest.permission#MANAGE_USERS} to receive this broadcast.
+     * @hide
+     */
+    @SystemApi
+    public static final String ACTION_USER_ADDED =
+            "android.intent.action.USER_ADDED";
+
+    /**
+     * Broadcast sent by the system when a user is started. Carries an extra
+     * EXTRA_USER_HANDLE that has the userHandle of the user.  This is only sent to
+     * registered receivers, not manifest receivers.  It is sent to the user
+     * that has been started.  This is sent as a foreground
+     * broadcast, since it is part of a visible user interaction; be as quick
+     * as possible when handling it.
+     * @hide
+     */
+    public static final String ACTION_USER_STARTED =
+            "android.intent.action.USER_STARTED";
+
+    /**
+     * Broadcast sent when a user is in the process of starting.  Carries an extra
+     * EXTRA_USER_HANDLE that has the userHandle of the user.  This is only
+     * sent to registered receivers, not manifest receivers.  It is sent to all
+     * users (including the one that is being started).  You must hold
+     * {@link android.Manifest.permission#INTERACT_ACROSS_USERS} to receive
+     * this broadcast.  This is sent as a background broadcast, since
+     * its result is not part of the primary UX flow; to safely keep track of
+     * started/stopped state of a user you can use this in conjunction with
+     * {@link #ACTION_USER_STOPPING}.  It is <b>not</b> generally safe to use with
+     * other user state broadcasts since those are foreground broadcasts so can
+     * execute in a different order.
+     * @hide
+     */
+    public static final String ACTION_USER_STARTING =
+            "android.intent.action.USER_STARTING";
+
+    /**
+     * Broadcast sent when a user is going to be stopped.  Carries an extra
+     * EXTRA_USER_HANDLE that has the userHandle of the user.  This is only
+     * sent to registered receivers, not manifest receivers.  It is sent to all
+     * users (including the one that is being stopped).  You must hold
+     * {@link android.Manifest.permission#INTERACT_ACROSS_USERS} to receive
+     * this broadcast.  The user will not stop until all receivers have
+     * handled the broadcast.  This is sent as a background broadcast, since
+     * its result is not part of the primary UX flow; to safely keep track of
+     * started/stopped state of a user you can use this in conjunction with
+     * {@link #ACTION_USER_STARTING}.  It is <b>not</b> generally safe to use with
+     * other user state broadcasts since those are foreground broadcasts so can
+     * execute in a different order.
+     * @hide
+     */
+    public static final String ACTION_USER_STOPPING =
+            "android.intent.action.USER_STOPPING";
+
+    /**
+     * Broadcast sent to the system when a user is stopped. Carries an extra
+     * EXTRA_USER_HANDLE that has the userHandle of the user.  This is similar to
+     * {@link #ACTION_PACKAGE_RESTARTED}, but for an entire user instead of a
+     * specific package.  This is only sent to registered receivers, not manifest
+     * receivers.  It is sent to all running users <em>except</em> the one that
+     * has just been stopped (which is no longer running).
+     * @hide
+     */
+    public static final String ACTION_USER_STOPPED =
+            "android.intent.action.USER_STOPPED";
+
+    /**
+     * Broadcast sent to the system when a user is removed. Carries an extra EXTRA_USER_HANDLE that has
+     * the userHandle of the user.  It is sent to all running users except the
+     * one that has been removed. The user will not be completely removed until all receivers have
+     * handled the broadcast. You must hold
+     * {@link android.Manifest.permission#MANAGE_USERS} to receive this broadcast.
+     * @hide
+     */
+    @SystemApi
+    public static final String ACTION_USER_REMOVED =
+            "android.intent.action.USER_REMOVED";
+
+    /**
+     * Broadcast sent to the system when the user switches. Carries an extra EXTRA_USER_HANDLE that has
+     * the userHandle of the user to become the current one. This is only sent to
+     * registered receivers, not manifest receivers.  It is sent to all running users.
+     * You must hold
+     * {@link android.Manifest.permission#MANAGE_USERS} to receive this broadcast.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static final String ACTION_USER_SWITCHED =
+            "android.intent.action.USER_SWITCHED";
+
+    /**
+     * Broadcast Action: Sent when the credential-encrypted private storage has
+     * become unlocked for the target user. This is only sent to registered
+     * receivers, not manifest receivers.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_USER_UNLOCKED = "android.intent.action.USER_UNLOCKED";
+
+    /**
+     * Broadcast sent to the system when a user's information changes. Carries an extra
+     * {@link #EXTRA_USER_HANDLE} to indicate which user's information changed.
+     * This is only sent to registered receivers, not manifest receivers. It is sent to all users.
+     * @hide
+     */
+    public static final String ACTION_USER_INFO_CHANGED =
+            "android.intent.action.USER_INFO_CHANGED";
+
+    /**
+     * Broadcast sent to the primary user when an associated managed profile is added (the profile
+     * was created and is ready to be used). Carries an extra {@link #EXTRA_USER} that specifies
+     * the UserHandle of the profile that was added. Only applications (for example Launchers)
+     * that need to display merged content across both primary and managed profiles need to
+     * worry about this broadcast. This is only sent to registered receivers,
+     * not manifest receivers.
+     */
+    public static final String ACTION_MANAGED_PROFILE_ADDED =
+            "android.intent.action.MANAGED_PROFILE_ADDED";
+
+    /**
+     * Broadcast sent to the primary user when an associated managed profile is removed. Carries an
+     * extra {@link #EXTRA_USER} that specifies the UserHandle of the profile that was removed.
+     * Only applications (for example Launchers) that need to display merged content across both
+     * primary and managed profiles need to worry about this broadcast. This is only sent to
+     * registered receivers, not manifest receivers.
+     */
+    public static final String ACTION_MANAGED_PROFILE_REMOVED =
+            "android.intent.action.MANAGED_PROFILE_REMOVED";
+
+    /**
+     * Broadcast sent to the primary user when the credential-encrypted private storage for
+     * an associated managed profile is unlocked. Carries an extra {@link #EXTRA_USER} that
+     * specifies the UserHandle of the profile that was unlocked. Only applications (for example
+     * Launchers) that need to display merged content across both primary and managed profiles
+     * need to worry about this broadcast. This is only sent to registered receivers,
+     * not manifest receivers.
+     */
+    public static final String ACTION_MANAGED_PROFILE_UNLOCKED =
+            "android.intent.action.MANAGED_PROFILE_UNLOCKED";
+
+    /**
+     * Broadcast sent to the primary user when an associated managed profile has become available.
+     * Currently this includes when the user disables quiet mode for the profile. Carries an extra
+     * {@link #EXTRA_USER} that specifies the UserHandle of the profile. When quiet mode is changed,
+     * this broadcast will carry a boolean extra {@link #EXTRA_QUIET_MODE} indicating the new state
+     * of quiet mode. This is only sent to registered receivers, not manifest receivers.
+     */
+    public static final String ACTION_MANAGED_PROFILE_AVAILABLE =
+            "android.intent.action.MANAGED_PROFILE_AVAILABLE";
+
+    /**
+     * Broadcast sent to the primary user when an associated managed profile has become unavailable.
+     * Currently this includes when the user enables quiet mode for the profile. Carries an extra
+     * {@link #EXTRA_USER} that specifies the UserHandle of the profile. When quiet mode is changed,
+     * this broadcast will carry a boolean extra {@link #EXTRA_QUIET_MODE} indicating the new state
+     * of quiet mode. This is only sent to registered receivers, not manifest receivers.
+     */
+    public static final String ACTION_MANAGED_PROFILE_UNAVAILABLE =
+            "android.intent.action.MANAGED_PROFILE_UNAVAILABLE";
+
+    /**
+     * Broadcast sent to the system user when the 'device locked' state changes for any user.
+     * Carries an extra {@link #EXTRA_USER_HANDLE} that specifies the ID of the user for which
+     * the device was locked or unlocked.
+     *
+     * This is only sent to registered receivers.
+     *
+     * @hide
+     */
+    public static final String ACTION_DEVICE_LOCKED_CHANGED =
+            "android.intent.action.DEVICE_LOCKED_CHANGED";
+
+    /**
+     * Sent when the user taps on the clock widget in the system's "quick settings" area.
+     */
+    public static final String ACTION_QUICK_CLOCK =
+            "android.intent.action.QUICK_CLOCK";
+
+    /**
+     * Activity Action: Shows the brightness setting dialog.
+     * @hide
+     */
+    public static final String ACTION_SHOW_BRIGHTNESS_DIALOG =
+            "com.android.intent.action.SHOW_BRIGHTNESS_DIALOG";
+
+    /**
+     * Broadcast Action:  A global button was pressed.  Includes a single
+     * extra field, {@link #EXTRA_KEY_EVENT}, containing the key event that
+     * caused the broadcast.
+     * @hide
+     */
+    @SystemApi
+    public static final String ACTION_GLOBAL_BUTTON = "android.intent.action.GLOBAL_BUTTON";
+
+    /**
+     * Broadcast Action: Sent when media resource is granted.
+     * <p>
+     * {@link #EXTRA_PACKAGES} specifies the packages on the process holding the media resource
+     * granted.
+     * </p>
+     * <p class="note">
+     * This is a protected intent that can only be sent by the system.
+     * </p>
+     * <p class="note">
+     * This requires {@link android.Manifest.permission#RECEIVE_MEDIA_RESOURCE_USAGE} permission.
+     * </p>
+     *
+     * @hide
+     */
+    public static final String ACTION_MEDIA_RESOURCE_GRANTED =
+            "android.intent.action.MEDIA_RESOURCE_GRANTED";
+
+    /**
+     * Broadcast Action: An overlay package has changed. The data contains the
+     * name of the overlay package which has changed. This is broadcast on all
+     * changes to the OverlayInfo returned by {@link
+     * android.content.om.IOverlayManager#getOverlayInfo(String, int)}. The
+     * most common change is a state change that will change whether the
+     * overlay is enabled or not.
+     * @hide
+     */
+    public static final String ACTION_OVERLAY_CHANGED = "android.intent.action.OVERLAY_CHANGED";
+
+    /**
+     * Activity Action: Allow the user to select and return one or more existing
+     * documents. When invoked, the system will display the various
+     * {@link DocumentsProvider} instances installed on the device, letting the
+     * user interactively navigate through them. These documents include local
+     * media, such as photos and video, and documents provided by installed
+     * cloud storage providers.
+     * <p>
+     * Each document is represented as a {@code content://} URI backed by a
+     * {@link DocumentsProvider}, which can be opened as a stream with
+     * {@link ContentResolver#openFileDescriptor(Uri, String)}, or queried for
+     * {@link android.provider.DocumentsContract.Document} metadata.
+     * <p>
+     * All selected documents are returned to the calling application with
+     * persistable read and write permission grants. If you want to maintain
+     * access to the documents across device reboots, you need to explicitly
+     * take the persistable permissions using
+     * {@link ContentResolver#takePersistableUriPermission(Uri, int)}.
+     * <p>
+     * Callers must indicate the acceptable document MIME types through
+     * {@link #setType(String)}. For example, to select photos, use
+     * {@code image/*}. If multiple disjoint MIME types are acceptable, define
+     * them in {@link #EXTRA_MIME_TYPES} and {@link #setType(String)} to
+     * {@literal *}/*.
+     * <p>
+     * If the caller can handle multiple returned items (the user performing
+     * multiple selection), then you can specify {@link #EXTRA_ALLOW_MULTIPLE}
+     * to indicate this.
+     * <p>
+     * Callers must include {@link #CATEGORY_OPENABLE} in the Intent to obtain
+     * URIs that can be opened with
+     * {@link ContentResolver#openFileDescriptor(Uri, String)}.
+     * <p>
+     * Callers can set a document URI through
+     * {@link DocumentsContract#EXTRA_INITIAL_URI} to indicate the initial
+     * location of documents navigator. System will do its best to launch the
+     * navigator in the specified document if it's a folder, or the folder that
+     * contains the specified document if not.
+     * <p>
+     * Output: The URI of the item that was picked, returned in
+     * {@link #getData()}. This must be a {@code content://} URI so that any
+     * receiver can access it. If multiple documents were selected, they are
+     * returned in {@link #getClipData()}.
+     *
+     * @see DocumentsContract
+     * @see #ACTION_OPEN_DOCUMENT_TREE
+     * @see #ACTION_CREATE_DOCUMENT
+     * @see #FLAG_GRANT_PERSISTABLE_URI_PERMISSION
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_OPEN_DOCUMENT = "android.intent.action.OPEN_DOCUMENT";
+
+    /**
+     * Activity Action: Allow the user to create a new document. When invoked,
+     * the system will display the various {@link DocumentsProvider} instances
+     * installed on the device, letting the user navigate through them. The
+     * returned document may be a newly created document with no content, or it
+     * may be an existing document with the requested MIME type.
+     * <p>
+     * Each document is represented as a {@code content://} URI backed by a
+     * {@link DocumentsProvider}, which can be opened as a stream with
+     * {@link ContentResolver#openFileDescriptor(Uri, String)}, or queried for
+     * {@link android.provider.DocumentsContract.Document} metadata.
+     * <p>
+     * Callers must indicate the concrete MIME type of the document being
+     * created by setting {@link #setType(String)}. This MIME type cannot be
+     * changed after the document is created.
+     * <p>
+     * Callers can provide an initial display name through {@link #EXTRA_TITLE},
+     * but the user may change this value before creating the file.
+     * <p>
+     * Callers must include {@link #CATEGORY_OPENABLE} in the Intent to obtain
+     * URIs that can be opened with
+     * {@link ContentResolver#openFileDescriptor(Uri, String)}.
+     * <p>
+     * Callers can set a document URI through
+     * {@link DocumentsContract#EXTRA_INITIAL_URI} to indicate the initial
+     * location of documents navigator. System will do its best to launch the
+     * navigator in the specified document if it's a folder, or the folder that
+     * contains the specified document if not.
+     * <p>
+     * Output: The URI of the item that was created. This must be a
+     * {@code content://} URI so that any receiver can access it.
+     *
+     * @see DocumentsContract
+     * @see #ACTION_OPEN_DOCUMENT
+     * @see #ACTION_OPEN_DOCUMENT_TREE
+     * @see #FLAG_GRANT_PERSISTABLE_URI_PERMISSION
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_CREATE_DOCUMENT = "android.intent.action.CREATE_DOCUMENT";
+
+    /**
+     * Activity Action: Allow the user to pick a directory subtree. When
+     * invoked, the system will display the various {@link DocumentsProvider}
+     * instances installed on the device, letting the user navigate through
+     * them. Apps can fully manage documents within the returned directory.
+     * <p>
+     * To gain access to descendant (child, grandchild, etc) documents, use
+     * {@link DocumentsContract#buildDocumentUriUsingTree(Uri, String)} and
+     * {@link DocumentsContract#buildChildDocumentsUriUsingTree(Uri, String)}
+     * with the returned URI.
+     * <p>
+     * Callers can set a document URI through
+     * {@link DocumentsContract#EXTRA_INITIAL_URI} to indicate the initial
+     * location of documents navigator. System will do its best to launch the
+     * navigator in the specified document if it's a folder, or the folder that
+     * contains the specified document if not.
+     * <p>
+     * Output: The URI representing the selected directory tree.
+     *
+     * @see DocumentsContract
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String
+            ACTION_OPEN_DOCUMENT_TREE = "android.intent.action.OPEN_DOCUMENT_TREE";
+
+
+    /**
+     * Activity Action: Perform text translation.
+     * <p>
+     * Input: {@link #EXTRA_TEXT getCharSequence(EXTRA_TEXT)} is the text to translate.
+     * <p>
+     * Output: nothing.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_TRANSLATE = "android.intent.action.TRANSLATE";
+
+    /**
+     * Activity Action: Define the meaning of the selected word(s).
+     * <p>
+     * Input: {@link #EXTRA_TEXT getCharSequence(EXTRA_TEXT)} is the text to define.
+     * <p>
+     * Output: nothing.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_DEFINE = "android.intent.action.DEFINE";
+
+    /**
+     * Broadcast Action: List of dynamic sensor is changed due to new sensor being connected or
+     * exisiting sensor being disconnected.
+     *
+     * <p class="note">This is a protected intent that can only be sent by the system.</p>
+     *
+     * {@hide}
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String
+            ACTION_DYNAMIC_SENSOR_CHANGED = "android.intent.action.DYNAMIC_SENSOR_CHANGED";
+
+    /**
+     * Deprecated - use ACTION_FACTORY_RESET instead.
+     * @hide
+     * @removed
+     */
+    @Deprecated
+    @SystemApi
+    public static final String ACTION_MASTER_CLEAR = "android.intent.action.MASTER_CLEAR";
+
+    /**
+     * Broadcast intent sent by the RecoverySystem to inform listeners that a master clear (wipe)
+     * is about to be performed.
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_MASTER_CLEAR_NOTIFICATION
+            = "android.intent.action.MASTER_CLEAR_NOTIFICATION";
+
+    /**
+     * Boolean intent extra to be used with {@link #ACTION_MASTER_CLEAR} in order to force a factory
+     * reset even if {@link android.os.UserManager#DISALLOW_FACTORY_RESET} is set.
+     *
+     * <p>Deprecated - use {@link #EXTRA_FORCE_FACTORY_RESET} instead.
+     *
+     * @hide
+     */
+    @Deprecated
+    public static final String EXTRA_FORCE_MASTER_CLEAR =
+            "android.intent.extra.FORCE_MASTER_CLEAR";
+
+    /**
+     * A broadcast action to trigger a factory reset.
+     *
+     * <p>The sender must hold the {@link android.Manifest.permission#MASTER_CLEAR} permission. The
+     * reason for the factory reset should be specified as {@link #EXTRA_REASON}.
+     *
+     * <p>Not for use by third-party applications.
+     *
+     * @see #EXTRA_FORCE_FACTORY_RESET
+     *
+     * {@hide}
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_FACTORY_RESET = "android.intent.action.FACTORY_RESET";
+
+    /**
+     * Boolean intent extra to be used with {@link #ACTION_MASTER_CLEAR} in order to force a factory
+     * reset even if {@link android.os.UserManager#DISALLOW_FACTORY_RESET} is set.
+     *
+     * <p>Not for use by third-party applications.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_FORCE_FACTORY_RESET =
+            "android.intent.extra.FORCE_FACTORY_RESET";
+
+    /**
+     * Broadcast action: report that a settings element is being restored from backup. The intent
+     * contains four extras: EXTRA_SETTING_NAME is a string naming the restored setting,
+     * EXTRA_SETTING_NEW_VALUE is the value being restored, EXTRA_SETTING_PREVIOUS_VALUE
+     * is the value of that settings entry prior to the restore operation, and
+     * EXTRA_SETTING_RESTORED_FROM_SDK_INT is the version of the SDK that the setting has been
+     * restored from (corresponds to {@link android.os.Build.VERSION#SDK_INT}). The first three
+     * values are represented as strings, the fourth one as int.
+     *
+     * <p>This broadcast is sent only for settings provider entries known to require special handling
+     * around restore time.  These entries are found in the BROADCAST_ON_RESTORE table within
+     * the provider's backup agent implementation.
+     *
+     * @see #EXTRA_SETTING_NAME
+     * @see #EXTRA_SETTING_PREVIOUS_VALUE
+     * @see #EXTRA_SETTING_NEW_VALUE
+     * @see #EXTRA_SETTING_RESTORED_FROM_SDK_INT
+     * {@hide}
+     */
+    public static final String ACTION_SETTING_RESTORED = "android.os.action.SETTING_RESTORED";
+
+    /** {@hide} */
+    public static final String EXTRA_SETTING_NAME = "setting_name";
+    /** {@hide} */
+    public static final String EXTRA_SETTING_PREVIOUS_VALUE = "previous_value";
+    /** {@hide} */
+    public static final String EXTRA_SETTING_NEW_VALUE = "new_value";
+    /** {@hide} */
+    public static final String EXTRA_SETTING_RESTORED_FROM_SDK_INT = "restored_from_sdk_int";
+
+    /**
+     * Activity Action: Process a piece of text.
+     * <p>Input: {@link #EXTRA_PROCESS_TEXT} contains the text to be processed.
+     * {@link #EXTRA_PROCESS_TEXT_READONLY} states if the resulting text will be read-only.</p>
+     * <p>Output: {@link #EXTRA_PROCESS_TEXT} contains the processed text.</p>
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_PROCESS_TEXT = "android.intent.action.PROCESS_TEXT";
+
+    /**
+     * Broadcast Action: The sim card state has changed.
+     * For more details see TelephonyIntents.ACTION_SIM_STATE_CHANGED. This is here
+     * because TelephonyIntents is an internal class.
+     * The intent will have following extras.</p>
+     * <p>
+     * @see #EXTRA_SIM_STATE
+     * @see #EXTRA_SIM_LOCKED_REASON
+     * @see #EXTRA_REBROADCAST_ON_UNLOCK
+     *
+     * @deprecated Use {@link #ACTION_SIM_CARD_STATE_CHANGED} or
+     * {@link #ACTION_SIM_APPLICATION_STATE_CHANGED}
+     *
+     * @hide
+     */
+    @Deprecated
+    @SystemApi
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_SIM_STATE_CHANGED = "android.intent.action.SIM_STATE_CHANGED";
+
+    /**
+     * The extra used with {@link #ACTION_SIM_STATE_CHANGED} for broadcasting SIM STATE.
+     * This will have one of the following intent values.
+     * @see #SIM_STATE_UNKNOWN
+     * @see #SIM_STATE_NOT_READY
+     * @see #SIM_STATE_ABSENT
+     * @see #SIM_STATE_PRESENT
+     * @see #SIM_STATE_CARD_IO_ERROR
+     * @see #SIM_STATE_CARD_RESTRICTED
+     * @see #SIM_STATE_LOCKED
+     * @see #SIM_STATE_READY
+     * @see #SIM_STATE_IMSI
+     * @see #SIM_STATE_LOADED
+     * @hide
+     * @deprecated Use {@link #ACTION_SIM_CARD_STATE_CHANGED}
+     */
+    public static final String EXTRA_SIM_STATE = "ss";
+
+    /**
+     * The intent value UNKNOWN represents the SIM state unknown
+     * @hide
+     * @deprecated Use {@link #ACTION_SIM_CARD_STATE_CHANGED}
+     */
+    public static final String SIM_STATE_UNKNOWN = "UNKNOWN";
+
+    /**
+     * The intent value NOT_READY means that the SIM is not ready eg. radio is off or powering on
+     * @hide
+     * @deprecated Use {@link #ACTION_SIM_CARD_STATE_CHANGED}
+     */
+    public static final String SIM_STATE_NOT_READY = "NOT_READY";
+
+    /**
+     * The intent value ABSENT means the SIM card is missing
+     * @hide
+     * @deprecated Use {@link #ACTION_SIM_CARD_STATE_CHANGED}
+     */
+    public static final String SIM_STATE_ABSENT = "ABSENT";
+
+    /**
+     * The intent value PRESENT means the device has a SIM card inserted
+     * @hide
+     * @deprecated Use {@link #ACTION_SIM_CARD_STATE_CHANGED}
+     */
+    public static final String SIM_STATE_PRESENT = "PRESENT";
+
+    /**
+     * The intent value CARD_IO_ERROR means for three consecutive times there was SIM IO error
+     * @hide
+     * @deprecated Use {@link #ACTION_SIM_CARD_STATE_CHANGED}
+     */
+    static public final String SIM_STATE_CARD_IO_ERROR = "CARD_IO_ERROR";
+
+    /**
+     * The intent value CARD_RESTRICTED means card is present but not usable due to carrier
+     * restrictions
+     * @hide
+     * @deprecated Use {@link #ACTION_SIM_CARD_STATE_CHANGED}
+     */
+    static public final String SIM_STATE_CARD_RESTRICTED = "CARD_RESTRICTED";
+
+    /**
+     * The intent value LOCKED means the SIM is locked by PIN or by network
+     * @hide
+     * @deprecated Use {@link #ACTION_SIM_CARD_STATE_CHANGED}
+     */
+    public static final String SIM_STATE_LOCKED = "LOCKED";
+
+    /**
+     * The intent value READY means the SIM is ready to be accessed
+     * @hide
+     * @deprecated Use {@link #ACTION_SIM_CARD_STATE_CHANGED}
+     */
+    public static final String SIM_STATE_READY = "READY";
+
+    /**
+     * The intent value IMSI means the SIM IMSI is ready in property
+     * @hide
+     * @deprecated Use {@link #ACTION_SIM_CARD_STATE_CHANGED}
+     */
+    public static final String SIM_STATE_IMSI = "IMSI";
+
+    /**
+     * The intent value LOADED means all SIM records, including IMSI, are loaded
+     * @hide
+     * @deprecated Use {@link #ACTION_SIM_CARD_STATE_CHANGED}
+     */
+    public static final String SIM_STATE_LOADED = "LOADED";
+
+    /**
+     * The extra used with {@link #ACTION_SIM_STATE_CHANGED} for broadcasting SIM STATE.
+     * This extra will have one of the following intent values.
+     * <p>
+     * @see #SIM_LOCKED_ON_PIN
+     * @see #SIM_LOCKED_ON_PUK
+     * @see #SIM_LOCKED_NETWORK
+     * @see #SIM_ABSENT_ON_PERM_DISABLED
+     *
+     * @hide
+     * @deprecated Use {@link #ACTION_SIM_APPLICATION_STATE_CHANGED}
+     */
+    public static final String EXTRA_SIM_LOCKED_REASON = "reason";
+
+    /**
+     * The intent value PIN means the SIM is locked on PIN1
+     * @hide
+     * @deprecated Use {@link #ACTION_SIM_APPLICATION_STATE_CHANGED}
+     */
+    public static final String SIM_LOCKED_ON_PIN = "PIN";
+
+    /**
+     * The intent value PUK means the SIM is locked on PUK1
+     * @hide
+     * @deprecated Use {@link #ACTION_SIM_APPLICATION_STATE_CHANGED}
+     */
+    /* PUK means ICC is locked on PUK1 */
+    public static final String SIM_LOCKED_ON_PUK = "PUK";
+
+    /**
+     * The intent value NETWORK means the SIM is locked on NETWORK PERSONALIZATION
+     * @hide
+     * @deprecated Use {@link #ACTION_SIM_APPLICATION_STATE_CHANGED}
+     */
+    public static final String SIM_LOCKED_NETWORK = "NETWORK";
+
+    /**
+     * The intent value PERM_DISABLED means SIM is permanently disabled due to puk fails
+     * @hide
+     * @deprecated Use {@link #ACTION_SIM_APPLICATION_STATE_CHANGED}
+     */
+    public static final String SIM_ABSENT_ON_PERM_DISABLED = "PERM_DISABLED";
+
+    /**
+     * The extra used with {@link #ACTION_SIM_STATE_CHANGED} for indicating whether this broadcast
+     * is a rebroadcast on unlock. Defaults to {@code false} if not specified.
+     *
+     * @hide
+     * @deprecated Use {@link #ACTION_SIM_CARD_STATE_CHANGED} or
+     * {@link #ACTION_SIM_APPLICATION_STATE_CHANGED}
+     */
+    public static final String EXTRA_REBROADCAST_ON_UNLOCK = "rebroadcastOnUnlock";
+
+    /**
+     * Broadcast Action: indicate that the phone service state has changed.
+     * The intent will have the following extra values:</p>
+     * <p>
+     * @see #EXTRA_VOICE_REG_STATE
+     * @see #EXTRA_DATA_REG_STATE
+     * @see #EXTRA_VOICE_ROAMING_TYPE
+     * @see #EXTRA_DATA_ROAMING_TYPE
+     * @see #EXTRA_OPERATOR_ALPHA_LONG
+     * @see #EXTRA_OPERATOR_ALPHA_SHORT
+     * @see #EXTRA_OPERATOR_NUMERIC
+     * @see #EXTRA_DATA_OPERATOR_ALPHA_LONG
+     * @see #EXTRA_DATA_OPERATOR_ALPHA_SHORT
+     * @see #EXTRA_DATA_OPERATOR_NUMERIC
+     * @see #EXTRA_MANUAL
+     * @see #EXTRA_VOICE_RADIO_TECH
+     * @see #EXTRA_DATA_RADIO_TECH
+     * @see #EXTRA_CSS_INDICATOR
+     * @see #EXTRA_NETWORK_ID
+     * @see #EXTRA_SYSTEM_ID
+     * @see #EXTRA_CDMA_ROAMING_INDICATOR
+     * @see #EXTRA_CDMA_DEFAULT_ROAMING_INDICATOR
+     * @see #EXTRA_EMERGENCY_ONLY
+     * @see #EXTRA_IS_DATA_ROAMING_FROM_REGISTRATION
+     * @see #EXTRA_IS_USING_CARRIER_AGGREGATION
+     * @see #EXTRA_LTE_EARFCN_RSRP_BOOST
+     *
+     * <p class="note">
+     * Requires the READ_PHONE_STATE permission.
+     *
+     * <p class="note">This is a protected intent that can only be sent by the system.
+     * @hide
+     * @removed
+     * @deprecated Use {@link android.provider.Telephony.ServiceStateTable} and the helper
+     * functions {@code ServiceStateTable.getUriForSubscriptionIdAndField} and
+     * {@code ServiceStateTable.getUriForSubscriptionId} to subscribe to changes to the ServiceState
+     * for a given subscription id and field with a ContentObserver or using JobScheduler.
+     */
+    @Deprecated
+    @SystemApi
+    @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_SERVICE_STATE = "android.intent.action.SERVICE_STATE";
+
+    /**
+     * Used by {@link services.core.java.com.android.server.pm.DataLoaderManagerService}
+     * for querying Data Loader Service providers. Data loader service providers register this
+     * intent filter in their manifests, so that they can be looked up and bound to by
+     * {@code DataLoaderManagerService}.
+     *
+     * <p class="note">This is a protected intent that can only be sent by the system.
+     *
+     * Data loader service providers must be privileged apps.
+     * See {@link com.android.server.pm.PackageManagerShellCommandDataLoader} as an example of such
+     * data loader service provider.
+     *
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+    public static final String ACTION_LOAD_DATA = "android.intent.action.LOAD_DATA";
+
+    /**
+     * An int extra used with {@link #ACTION_SERVICE_STATE} which indicates voice registration
+     * state.
+     * @see android.telephony.ServiceState#STATE_EMERGENCY_ONLY
+     * @see android.telephony.ServiceState#STATE_IN_SERVICE
+     * @see android.telephony.ServiceState#STATE_OUT_OF_SERVICE
+     * @see android.telephony.ServiceState#STATE_POWER_OFF
+     * @hide
+     * @removed
+     * @deprecated Use {@link android.provider.Telephony.ServiceStateTable#VOICE_REG_STATE}.
+     */
+    @Deprecated
+    @SystemApi
+    public static final String EXTRA_VOICE_REG_STATE = "voiceRegState";
+
+    /**
+     * An int extra used with {@link #ACTION_SERVICE_STATE} which indicates data registration state.
+     * @see android.telephony.ServiceState#STATE_EMERGENCY_ONLY
+     * @see android.telephony.ServiceState#STATE_IN_SERVICE
+     * @see android.telephony.ServiceState#STATE_OUT_OF_SERVICE
+     * @see android.telephony.ServiceState#STATE_POWER_OFF
+     * @hide
+     * @removed
+     * @deprecated Use {@link android.provider.Telephony.ServiceStateTable#DATA_REG_STATE}.
+     */
+    @Deprecated
+    @SystemApi
+    public static final String EXTRA_DATA_REG_STATE = "dataRegState";
+
+    /**
+     * An integer extra used with {@link #ACTION_SERVICE_STATE} which indicates the voice roaming
+     * type.
+     * @hide
+     * @removed
+     * @deprecated Use {@link android.provider.Telephony.ServiceStateTable#VOICE_ROAMING_TYPE}.
+     */
+    @Deprecated
+    @SystemApi
+    public static final String EXTRA_VOICE_ROAMING_TYPE = "voiceRoamingType";
+
+    /**
+     * An integer extra used with {@link #ACTION_SERVICE_STATE} which indicates the data roaming
+     * type.
+     * @hide
+     * @removed
+     * @deprecated Use {@link android.provider.Telephony.ServiceStateTable#DATA_ROAMING_TYPE}.
+     */
+    @Deprecated
+    @SystemApi
+    public static final String EXTRA_DATA_ROAMING_TYPE = "dataRoamingType";
+
+    /**
+     * A string extra used with {@link #ACTION_SERVICE_STATE} which represents the current
+     * registered voice operator name in long alphanumeric format.
+     * {@code null} if the operator name is not known or unregistered.
+     * @hide
+     * @removed
+     * @deprecated Use
+     * {@link android.provider.Telephony.ServiceStateTable#VOICE_OPERATOR_ALPHA_LONG}.
+     */
+    @Deprecated
+    @SystemApi
+    public static final String EXTRA_OPERATOR_ALPHA_LONG = "operator-alpha-long";
+
+    /**
+     * A string extra used with {@link #ACTION_SERVICE_STATE} which represents the current
+     * registered voice operator name in short alphanumeric format.
+     * {@code null} if the operator name is not known or unregistered.
+     * @hide
+     * @removed
+     * @deprecated Use
+     * {@link android.provider.Telephony.ServiceStateTable#VOICE_OPERATOR_ALPHA_SHORT}.
+     */
+    @Deprecated
+    @SystemApi
+    public static final String EXTRA_OPERATOR_ALPHA_SHORT = "operator-alpha-short";
+
+    /**
+     * A string extra used with {@link #ACTION_SERVICE_STATE} containing the MCC
+     * (Mobile Country Code, 3 digits) and MNC (Mobile Network code, 2-3 digits) for the mobile
+     * network.
+     * @hide
+     * @removed
+     * @deprecated Use {@link android.provider.Telephony.ServiceStateTable#VOICE_OPERATOR_NUMERIC}.
+     */
+    @Deprecated
+    @SystemApi
+    public static final String EXTRA_OPERATOR_NUMERIC = "operator-numeric";
+
+    /**
+     * A string extra used with {@link #ACTION_SERVICE_STATE} which represents the current
+     * registered data operator name in long alphanumeric format.
+     * {@code null} if the operator name is not known or unregistered.
+     * @hide
+     * @removed
+     * @deprecated Use
+     * {@link android.provider.Telephony.ServiceStateTable#DATA_OPERATOR_ALPHA_LONG}.
+     */
+    @Deprecated
+    @SystemApi
+    public static final String EXTRA_DATA_OPERATOR_ALPHA_LONG = "data-operator-alpha-long";
+
+    /**
+     * A string extra used with {@link #ACTION_SERVICE_STATE} which represents the current
+     * registered data operator name in short alphanumeric format.
+     * {@code null} if the operator name is not known or unregistered.
+     * @hide
+     * @removed
+     * @deprecated Use
+     * {@link android.provider.Telephony.ServiceStateTable#DATA_OPERATOR_ALPHA_SHORT}.
+     */
+    @Deprecated
+    @SystemApi
+    public static final String EXTRA_DATA_OPERATOR_ALPHA_SHORT = "data-operator-alpha-short";
+
+    /**
+     * A string extra used with {@link #ACTION_SERVICE_STATE} containing the MCC
+     * (Mobile Country Code, 3 digits) and MNC (Mobile Network code, 2-3 digits) for the
+     * data operator.
+     * @hide
+     * @removed
+     * @deprecated Use {@link android.provider.Telephony.ServiceStateTable#DATA_OPERATOR_NUMERIC}.
+     */
+    @Deprecated
+    @SystemApi
+    public static final String EXTRA_DATA_OPERATOR_NUMERIC = "data-operator-numeric";
+
+    /**
+     * A boolean extra used with {@link #ACTION_SERVICE_STATE} which indicates whether the current
+     * network selection mode is manual.
+     * Will be {@code true} if manual mode, {@code false} if automatic mode.
+     * @hide
+     * @removed
+     * @deprecated Use
+     * {@link android.provider.Telephony.ServiceStateTable#IS_MANUAL_NETWORK_SELECTION}.
+     */
+    @Deprecated
+    @SystemApi
+    public static final String EXTRA_MANUAL = "manual";
+
+    /**
+     * An integer extra used with {@link #ACTION_SERVICE_STATE} which represents the current voice
+     * radio technology.
+     * @hide
+     * @removed
+     * @deprecated Use
+     * {@link android.provider.Telephony.ServiceStateTable#RIL_VOICE_RADIO_TECHNOLOGY}.
+     */
+    @Deprecated
+    @SystemApi
+    public static final String EXTRA_VOICE_RADIO_TECH = "radioTechnology";
+
+    /**
+     * An integer extra used with {@link #ACTION_SERVICE_STATE} which represents the current data
+     * radio technology.
+     * @hide
+     * @removed
+     * @deprecated Use
+     * {@link android.provider.Telephony.ServiceStateTable#RIL_DATA_RADIO_TECHNOLOGY}.
+     */
+    @Deprecated
+    @SystemApi
+    public static final String EXTRA_DATA_RADIO_TECH = "dataRadioTechnology";
+
+    /**
+     * A boolean extra used with {@link #ACTION_SERVICE_STATE} which represents concurrent service
+     * support on CDMA network.
+     * Will be {@code true} if support, {@code false} otherwise.
+     * @hide
+     * @removed
+     * @deprecated Use {@link android.provider.Telephony.ServiceStateTable#CSS_INDICATOR}.
+     */
+    @Deprecated
+    @SystemApi
+    public static final String EXTRA_CSS_INDICATOR = "cssIndicator";
+
+    /**
+     * An integer extra used with {@link #ACTION_SERVICE_STATE} which represents the CDMA network
+     * id. {@code Integer.MAX_VALUE} if unknown.
+     * @hide
+     * @removed
+     * @deprecated Use {@link android.provider.Telephony.ServiceStateTable#NETWORK_ID}.
+     */
+    @Deprecated
+    @SystemApi
+    public static final String EXTRA_NETWORK_ID = "networkId";
+
+    /**
+     * An integer extra used with {@link #ACTION_SERVICE_STATE} which represents the CDMA system id.
+     * {@code Integer.MAX_VALUE} if unknown.
+     * @hide
+     * @removed
+     * @deprecated Use {@link android.provider.Telephony.ServiceStateTable#SYSTEM_ID}.
+     */
+    @Deprecated
+    @SystemApi
+    public static final String EXTRA_SYSTEM_ID = "systemId";
+
+    /**
+     * An integer extra used with {@link #ACTION_SERVICE_STATE} represents the TSB-58 roaming
+     * indicator if registered on a CDMA or EVDO system or {@code -1} if not.
+     * @hide
+     * @removed
+     * @deprecated Use {@link android.provider.Telephony.ServiceStateTable#CDMA_ROAMING_INDICATOR}.
+     */
+    @Deprecated
+    @SystemApi
+    public static final String EXTRA_CDMA_ROAMING_INDICATOR = "cdmaRoamingIndicator";
+
+    /**
+     * An integer extra used with {@link #ACTION_SERVICE_STATE} represents the default roaming
+     * indicator from the PRL if registered on a CDMA or EVDO system {@code -1} if not.
+     * @hide
+     * @removed
+     * @deprecated Use
+     * {@link android.provider.Telephony.ServiceStateTable#CDMA_DEFAULT_ROAMING_INDICATOR}.
+     */
+    @Deprecated
+    @SystemApi
+    public static final String EXTRA_CDMA_DEFAULT_ROAMING_INDICATOR = "cdmaDefaultRoamingIndicator";
+
+    /**
+     * A boolean extra used with {@link #ACTION_SERVICE_STATE} which indicates if under emergency
+     * only mode.
+     * {@code true} if in emergency only mode, {@code false} otherwise.
+     * @hide
+     * @removed
+     * @deprecated Use {@link android.provider.Telephony.ServiceStateTable#IS_EMERGENCY_ONLY}.
+     */
+    @Deprecated
+    @SystemApi
+    public static final String EXTRA_EMERGENCY_ONLY = "emergencyOnly";
+
+    /**
+     * A boolean extra used with {@link #ACTION_SERVICE_STATE} which indicates whether data network
+     * registration state is roaming.
+     * {@code true} if registration indicates roaming, {@code false} otherwise
+     * @hide
+     * @removed
+     * @deprecated Use
+     * {@link android.provider.Telephony.ServiceStateTable#IS_DATA_ROAMING_FROM_REGISTRATION}.
+     */
+    @Deprecated
+    @SystemApi
+    public static final String EXTRA_IS_DATA_ROAMING_FROM_REGISTRATION =
+            "isDataRoamingFromRegistration";
+
+    /**
+     * A boolean extra used with {@link #ACTION_SERVICE_STATE} which indicates if carrier
+     * aggregation is in use.
+     * {@code true} if carrier aggregation is in use, {@code false} otherwise.
+     * @hide
+     * @removed
+     * @deprecated Use
+     * {@link android.provider.Telephony.ServiceStateTable#IS_USING_CARRIER_AGGREGATION}.
+     */
+    @Deprecated
+    @SystemApi
+    public static final String EXTRA_IS_USING_CARRIER_AGGREGATION = "isUsingCarrierAggregation";
+
+    /**
+     * An integer extra used with {@link #ACTION_SERVICE_STATE} representing the offset which
+     * is reduced from the rsrp threshold while calculating signal strength level.
+     * @hide
+     * @removed
+     */
+    @Deprecated
+    @SystemApi
+    public static final String EXTRA_LTE_EARFCN_RSRP_BOOST = "LteEarfcnRsrpBoost";
+
+    /**
+     * The name of the extra used to define the text to be processed, as a
+     * CharSequence. Note that this may be a styled CharSequence, so you must use
+     * {@link Bundle#getCharSequence(String) Bundle.getCharSequence()} to retrieve it.
+     */
+    public static final String EXTRA_PROCESS_TEXT = "android.intent.extra.PROCESS_TEXT";
+    /**
+     * The name of the boolean extra used to define if the processed text will be used as read-only.
+     */
+    public static final String EXTRA_PROCESS_TEXT_READONLY =
+            "android.intent.extra.PROCESS_TEXT_READONLY";
+
+    /**
+     * Broadcast action: reports when a new thermal event has been reached. When the device
+     * is reaching its maximum temperatue, the thermal level reported
+     * {@hide}
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_THERMAL_EVENT = "android.intent.action.THERMAL_EVENT";
+
+    /** {@hide} */
+    public static final String EXTRA_THERMAL_STATE = "android.intent.extra.THERMAL_STATE";
+
+    /**
+     * Thermal state when the device is normal. This state is sent in the
+     * {@link #ACTION_THERMAL_EVENT} broadcast as {@link #EXTRA_THERMAL_STATE}.
+     * {@hide}
+     */
+    public static final int EXTRA_THERMAL_STATE_NORMAL = 0;
+
+    /**
+     * Thermal state where the device is approaching its maximum threshold. This state is sent in
+     * the {@link #ACTION_THERMAL_EVENT} broadcast as {@link #EXTRA_THERMAL_STATE}.
+     * {@hide}
+     */
+    public static final int EXTRA_THERMAL_STATE_WARNING = 1;
+
+    /**
+     * Thermal state where the device has reached its maximum threshold. This state is sent in the
+     * {@link #ACTION_THERMAL_EVENT} broadcast as {@link #EXTRA_THERMAL_STATE}.
+     * {@hide}
+     */
+    public static final int EXTRA_THERMAL_STATE_EXCEEDED = 2;
+
+    /**
+     * Broadcast Action: Indicates the dock in idle state while device is docked.
+     *
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
+     *
+     * @hide
+     */
+    public static final String ACTION_DOCK_IDLE = "android.intent.action.DOCK_IDLE";
+
+    /**
+     * Broadcast Action: Indicates the dock in active state while device is docked.
+     *
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
+     *
+     * @hide
+     */
+    public static final String ACTION_DOCK_ACTIVE = "android.intent.action.DOCK_ACTIVE";
+
+    /**
+     * Broadcast Action: Indicates that a new device customization has been
+     * downloaded and applied (packages installed, runtime resource overlays
+     * enabled, xml files copied, ...), and that it is time for components that
+     * need to for example clear their caches to do so now.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String ACTION_DEVICE_CUSTOMIZATION_READY =
+            "android.intent.action.DEVICE_CUSTOMIZATION_READY";
+
+
+    /**
+     * Activity Action: Display an activity state associated with an unique {@link LocusId}.
+     *
+     * <p>For example, a chat app could use the context to resume a conversation between 2 users.
+     *
+     * <p>Input: {@link #EXTRA_LOCUS_ID} specifies the unique identifier of the locus in the
+     * app domain. Should be stable across reboots and backup / restore.
+     * <p>Output: nothing.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_VIEW_LOCUS = "android.intent.action.VIEW_LOCUS";
+
+    /**
+     * Broadcast Action: Sent to the integrity component when a package
+     * needs to be verified. The data contains the package URI along with other relevant
+     * information.
+     *
+     * <p class="note">
+     * This is a protected intent that can only be sent by the system.
+     * </p>
+     *
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION =
+            "android.intent.action.PACKAGE_NEEDS_INTEGRITY_VERIFICATION";
+
+    // ---------------------------------------------------------------------
+    // ---------------------------------------------------------------------
+    // Standard intent categories (see addCategory()).
+
+    /**
+     * Set if the activity should be an option for the default action
+     * (center press) to perform on a piece of data.  Setting this will
+     * hide from the user any activities without it set when performing an
+     * action on some data.  Note that this is normally -not- set in the
+     * Intent when initiating an action -- it is for use in intent filters
+     * specified in packages.
+     */
+    @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+    public static final String CATEGORY_DEFAULT = "android.intent.category.DEFAULT";
+    /**
+     * Activities that can be safely invoked from a browser must support this
+     * category.  For example, if the user is viewing a web page or an e-mail
+     * and clicks on a link in the text, the Intent generated execute that
+     * link will require the BROWSABLE category, so that only activities
+     * supporting this category will be considered as possible actions.  By
+     * supporting this category, you are promising that there is nothing
+     * damaging (without user intervention) that can happen by invoking any
+     * matching Intent.
+     */
+    @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+    public static final String CATEGORY_BROWSABLE = "android.intent.category.BROWSABLE";
+    /**
+     * Categories for activities that can participate in voice interaction.
+     * An activity that supports this category must be prepared to run with
+     * no UI shown at all (though in some case it may have a UI shown), and
+     * rely on {@link android.app.VoiceInteractor} to interact with the user.
+     */
+    @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+    public static final String CATEGORY_VOICE = "android.intent.category.VOICE";
+    /**
+     * Set if the activity should be considered as an alternative action to
+     * the data the user is currently viewing.  See also
+     * {@link #CATEGORY_SELECTED_ALTERNATIVE} for an alternative action that
+     * applies to the selection in a list of items.
+     *
+     * <p>Supporting this category means that you would like your activity to be
+     * displayed in the set of alternative things the user can do, usually as
+     * part of the current activity's options menu.  You will usually want to
+     * include a specific label in the &lt;intent-filter&gt; of this action
+     * describing to the user what it does.
+     *
+     * <p>The action of IntentFilter with this category is important in that it
+     * describes the specific action the target will perform.  This generally
+     * should not be a generic action (such as {@link #ACTION_VIEW}, but rather
+     * a specific name such as "com.android.camera.action.CROP.  Only one
+     * alternative of any particular action will be shown to the user, so using
+     * a specific action like this makes sure that your alternative will be
+     * displayed while also allowing other applications to provide their own
+     * overrides of that particular action.
+     */
+    @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+    public static final String CATEGORY_ALTERNATIVE = "android.intent.category.ALTERNATIVE";
+    /**
+     * Set if the activity should be considered as an alternative selection
+     * action to the data the user has currently selected.  This is like
+     * {@link #CATEGORY_ALTERNATIVE}, but is used in activities showing a list
+     * of items from which the user can select, giving them alternatives to the
+     * default action that will be performed on it.
+     */
+    @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+    public static final String CATEGORY_SELECTED_ALTERNATIVE = "android.intent.category.SELECTED_ALTERNATIVE";
+    /**
+     * Intended to be used as a tab inside of a containing TabActivity.
+     */
+    @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+    public static final String CATEGORY_TAB = "android.intent.category.TAB";
+    /**
+     * Should be displayed in the top-level launcher.
+     */
+    @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+    public static final String CATEGORY_LAUNCHER = "android.intent.category.LAUNCHER";
+    /**
+     * Indicates an activity optimized for Leanback mode, and that should
+     * be displayed in the Leanback launcher.
+     */
+    @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+    public static final String CATEGORY_LEANBACK_LAUNCHER = "android.intent.category.LEANBACK_LAUNCHER";
+    /**
+     * Indicates the preferred entry-point activity when an application is launched from a Car
+     * launcher. If not present, Car launcher can optionally use {@link #CATEGORY_LAUNCHER} as a
+     * fallback, or exclude the application entirely.
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+    public static final String CATEGORY_CAR_LAUNCHER = "android.intent.category.CAR_LAUNCHER";
+    /**
+     * Indicates a Leanback settings activity to be displayed in the Leanback launcher.
+     * @hide
+     */
+    @SystemApi
+    public static final String CATEGORY_LEANBACK_SETTINGS = "android.intent.category.LEANBACK_SETTINGS";
+    /**
+     * Provides information about the package it is in; typically used if
+     * a package does not contain a {@link #CATEGORY_LAUNCHER} to provide
+     * a front-door to the user without having to be shown in the all apps list.
+     */
+    @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+    public static final String CATEGORY_INFO = "android.intent.category.INFO";
+    /**
+     * This is the home activity, that is the first activity that is displayed
+     * when the device boots.
+     */
+    @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+    public static final String CATEGORY_HOME = "android.intent.category.HOME";
+    /**
+     * This is the home activity that is displayed when the device is finished setting up and ready
+     * for use.
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+    public static final String CATEGORY_HOME_MAIN = "android.intent.category.HOME_MAIN";
+    /**
+     * The home activity shown on secondary displays that support showing home activities.
+     */
+    @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+    public static final String CATEGORY_SECONDARY_HOME = "android.intent.category.SECONDARY_HOME";
+    /**
+     * This is the setup wizard activity, that is the first activity that is displayed
+     * when the user sets up the device for the first time.
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+    public static final String CATEGORY_SETUP_WIZARD = "android.intent.category.SETUP_WIZARD";
+    /**
+     * This is the home activity, that is the activity that serves as the launcher app
+     * from there the user can start other apps. Often components with lower/higher
+     * priority intent filters handle the home intent, for example SetupWizard, to
+     * setup the device and we need to be able to distinguish the home app from these
+     * setup helpers.
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+    public static final String CATEGORY_LAUNCHER_APP = "android.intent.category.LAUNCHER_APP";
+    /**
+     * This activity is a preference panel.
+     */
+    @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+    public static final String CATEGORY_PREFERENCE = "android.intent.category.PREFERENCE";
+    /**
+     * This activity is a development preference panel.
+     */
+    @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+    public static final String CATEGORY_DEVELOPMENT_PREFERENCE = "android.intent.category.DEVELOPMENT_PREFERENCE";
+    /**
+     * Capable of running inside a parent activity container.
+     */
+    @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+    public static final String CATEGORY_EMBED = "android.intent.category.EMBED";
+    /**
+     * This activity allows the user to browse and download new applications.
+     */
+    @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+    public static final String CATEGORY_APP_MARKET = "android.intent.category.APP_MARKET";
+    /**
+     * This activity may be exercised by the monkey or other automated test tools.
+     */
+    @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+    public static final String CATEGORY_MONKEY = "android.intent.category.MONKEY";
+    /**
+     * To be used as a test (not part of the normal user experience).
+     */
+    public static final String CATEGORY_TEST = "android.intent.category.TEST";
+    /**
+     * To be used as a unit test (run through the Test Harness).
+     */
+    public static final String CATEGORY_UNIT_TEST = "android.intent.category.UNIT_TEST";
+    /**
+     * To be used as a sample code example (not part of the normal user
+     * experience).
+     */
+    public static final String CATEGORY_SAMPLE_CODE = "android.intent.category.SAMPLE_CODE";
+
+    /**
+     * Used to indicate that an intent only wants URIs that can be opened with
+     * {@link ContentResolver#openFileDescriptor(Uri, String)}. Openable URIs
+     * must support at least the columns defined in {@link OpenableColumns} when
+     * queried.
+     *
+     * @see #ACTION_GET_CONTENT
+     * @see #ACTION_OPEN_DOCUMENT
+     * @see #ACTION_CREATE_DOCUMENT
+     */
+    @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+    public static final String CATEGORY_OPENABLE = "android.intent.category.OPENABLE";
+
+    /**
+     * Used to indicate that an intent filter can accept files which are not necessarily
+     * openable by {@link ContentResolver#openFileDescriptor(Uri, String)}, but
+     * at least streamable via
+     * {@link ContentResolver#openTypedAssetFileDescriptor(Uri, String, Bundle)}
+     * using one of the stream types exposed via
+     * {@link ContentResolver#getStreamTypes(Uri, String)}.
+     *
+     * @see #ACTION_SEND
+     * @see #ACTION_SEND_MULTIPLE
+     */
+    @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+    public static final String CATEGORY_TYPED_OPENABLE  =
+            "android.intent.category.TYPED_OPENABLE";
+
+    /**
+     * To be used as code under test for framework instrumentation tests.
+     */
+    public static final String CATEGORY_FRAMEWORK_INSTRUMENTATION_TEST =
+            "android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST";
+    /**
+     * An activity to run when device is inserted into a car dock.
+     * Used with {@link #ACTION_MAIN} to launch an activity.  For more
+     * information, see {@link android.app.UiModeManager}.
+     */
+    @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+    public static final String CATEGORY_CAR_DOCK = "android.intent.category.CAR_DOCK";
+    /**
+     * An activity to run when device is inserted into a car dock.
+     * Used with {@link #ACTION_MAIN} to launch an activity.  For more
+     * information, see {@link android.app.UiModeManager}.
+     */
+    @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+    public static final String CATEGORY_DESK_DOCK = "android.intent.category.DESK_DOCK";
+    /**
+     * An activity to run when device is inserted into a analog (low end) dock.
+     * Used with {@link #ACTION_MAIN} to launch an activity.  For more
+     * information, see {@link android.app.UiModeManager}.
+     */
+    @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+    public static final String CATEGORY_LE_DESK_DOCK = "android.intent.category.LE_DESK_DOCK";
+
+    /**
+     * An activity to run when device is inserted into a digital (high end) dock.
+     * Used with {@link #ACTION_MAIN} to launch an activity.  For more
+     * information, see {@link android.app.UiModeManager}.
+     */
+    @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+    public static final String CATEGORY_HE_DESK_DOCK = "android.intent.category.HE_DESK_DOCK";
+
+    /**
+     * Used to indicate that the activity can be used in a car environment.
+     */
+    @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+    public static final String CATEGORY_CAR_MODE = "android.intent.category.CAR_MODE";
+
+    /**
+     * An activity to use for the launcher when the device is placed in a VR Headset viewer.
+     * Used with {@link #ACTION_MAIN} to launch an activity.  For more
+     * information, see {@link android.app.UiModeManager}.
+     */
+    @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+    public static final String CATEGORY_VR_HOME = "android.intent.category.VR_HOME";
+
+    /**
+     * The accessibility shortcut is a global gesture for users with disabilities to trigger an
+     * important for them accessibility feature to help developers determine whether they want to
+     * make their activity a shortcut target.
+     * <p>
+     * An activity of interest to users with accessibility needs may request to be the target of
+     * the accessibility shortcut. It handles intent {@link #ACTION_MAIN} with this category,
+     * which will be dispatched by the system when the user activates the shortcut when it is
+     * configured to point at this target.
+     * </p>
+     * <p>
+     * An activity declared itself to be a target of the shortcut in AndroidManifest.xml. It must
+     * also do two things:
+     * <ul>
+     *     <ol>
+     *         Specify that it handles the <code>android.intent.action.MAIN</code>
+     *         {@link android.content.Intent}
+     *         with category <code>android.intent.category.ACCESSIBILITY_SHORTCUT_TARGET</code>.
+     *     </ol>
+     *     <ol>
+     *         Provide a meta-data entry <code>android.accessibilityshortcut.target</code> in the
+     *         manifest when declaring the activity.
+     *     </ol>
+     * </ul>
+     * If either of these items is missing, the system will ignore the accessibility shortcut
+     * target. Following is an example declaration:
+     * </p>
+     * <pre>
+     * &lt;activity android:name=".MainActivity"
+     * . . .
+     *   &lt;intent-filter&gt;
+     *       &lt;action android:name="android.intent.action.MAIN" /&gt;
+     *       &lt;category android:name="android.intent.category.ACCESSIBILITY_SHORTCUT_TARGET" /&gt;
+     *   &lt;/intent-filter&gt;
+     *   &lt;meta-data android:name="android.accessibilityshortcut.target"
+     *                   android:resource="@xml/accessibilityshortcut" /&gt;
+     * &lt;/activity&gt;
+     * </pre>
+     * <p> This is a sample XML file configuring a accessibility shortcut target: </p>
+     * <pre>
+     * &lt;accessibility-shortcut-target
+     *     android:description="@string/shortcut_target_description"
+     *     android:summary="@string/shortcut_target_summary"
+     *     android:animatedImageDrawable="@drawable/shortcut_target_animated_image"
+     *     android:htmlDescription="@string/shortcut_target_html_description"
+     *     android:settingsActivity="com.example.android.shortcut.target.SettingsActivity" /&gt;
+     * </pre>
+     * <p>
+     * Both description and summary are necessary. The system will ignore the accessibility
+     * shortcut target if they are missing. The animated image and html description are supported
+     * to help users understand how to use the shortcut target. The settings activity is a
+     * component name that allows the user to modify the settings for this accessibility shortcut
+     * target.
+     * </p>
+     */
+    @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+    public static final String CATEGORY_ACCESSIBILITY_SHORTCUT_TARGET =
+            "android.intent.category.ACCESSIBILITY_SHORTCUT_TARGET";
+    // ---------------------------------------------------------------------
+    // ---------------------------------------------------------------------
+    // Application launch intent categories (see addCategory()).
+
+    /**
+     * Used with {@link #ACTION_MAIN} to launch the browser application.
+     * The activity should be able to browse the Internet.
+     * <p>NOTE: This should not be used as the primary key of an Intent,
+     * since it will not result in the app launching with the correct
+     * action and category.  Instead, use this with
+     * {@link #makeMainSelectorActivity(String, String)} to generate a main
+     * Intent with this category in the selector.</p>
+     */
+    @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+    public static final String CATEGORY_APP_BROWSER = "android.intent.category.APP_BROWSER";
+
+    /**
+     * Used with {@link #ACTION_MAIN} to launch the calculator application.
+     * The activity should be able to perform standard arithmetic operations.
+     * <p>NOTE: This should not be used as the primary key of an Intent,
+     * since it will not result in the app launching with the correct
+     * action and category.  Instead, use this with
+     * {@link #makeMainSelectorActivity(String, String)} to generate a main
+     * Intent with this category in the selector.</p>
+     */
+    @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+    public static final String CATEGORY_APP_CALCULATOR = "android.intent.category.APP_CALCULATOR";
+
+    /**
+     * Used with {@link #ACTION_MAIN} to launch the calendar application.
+     * The activity should be able to view and manipulate calendar entries.
+     * <p>NOTE: This should not be used as the primary key of an Intent,
+     * since it will not result in the app launching with the correct
+     * action and category.  Instead, use this with
+     * {@link #makeMainSelectorActivity(String, String)} to generate a main
+     * Intent with this category in the selector.</p>
+     */
+    @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+    public static final String CATEGORY_APP_CALENDAR = "android.intent.category.APP_CALENDAR";
+
+    /**
+     * Used with {@link #ACTION_MAIN} to launch the contacts application.
+     * The activity should be able to view and manipulate address book entries.
+     * <p>NOTE: This should not be used as the primary key of an Intent,
+     * since it will not result in the app launching with the correct
+     * action and category.  Instead, use this with
+     * {@link #makeMainSelectorActivity(String, String)} to generate a main
+     * Intent with this category in the selector.</p>
+     */
+    @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+    public static final String CATEGORY_APP_CONTACTS = "android.intent.category.APP_CONTACTS";
+
+    /**
+     * Used with {@link #ACTION_MAIN} to launch the email application.
+     * The activity should be able to send and receive email.
+     * <p>NOTE: This should not be used as the primary key of an Intent,
+     * since it will not result in the app launching with the correct
+     * action and category.  Instead, use this with
+     * {@link #makeMainSelectorActivity(String, String)} to generate a main
+     * Intent with this category in the selector.</p>
+     */
+    @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+    public static final String CATEGORY_APP_EMAIL = "android.intent.category.APP_EMAIL";
+
+    /**
+     * Used with {@link #ACTION_MAIN} to launch the gallery application.
+     * The activity should be able to view and manipulate image and video files
+     * stored on the device.
+     * <p>NOTE: This should not be used as the primary key of an Intent,
+     * since it will not result in the app launching with the correct
+     * action and category.  Instead, use this with
+     * {@link #makeMainSelectorActivity(String, String)} to generate a main
+     * Intent with this category in the selector.</p>
+     */
+    @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+    public static final String CATEGORY_APP_GALLERY = "android.intent.category.APP_GALLERY";
+
+    /**
+     * Used with {@link #ACTION_MAIN} to launch the maps application.
+     * The activity should be able to show the user's current location and surroundings.
+     * <p>NOTE: This should not be used as the primary key of an Intent,
+     * since it will not result in the app launching with the correct
+     * action and category.  Instead, use this with
+     * {@link #makeMainSelectorActivity(String, String)} to generate a main
+     * Intent with this category in the selector.</p>
+     */
+    @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+    public static final String CATEGORY_APP_MAPS = "android.intent.category.APP_MAPS";
+
+    /**
+     * Used with {@link #ACTION_MAIN} to launch the messaging application.
+     * The activity should be able to send and receive text messages.
+     * <p>NOTE: This should not be used as the primary key of an Intent,
+     * since it will not result in the app launching with the correct
+     * action and category.  Instead, use this with
+     * {@link #makeMainSelectorActivity(String, String)} to generate a main
+     * Intent with this category in the selector.</p>
+     */
+    @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+    public static final String CATEGORY_APP_MESSAGING = "android.intent.category.APP_MESSAGING";
+
+    /**
+     * Used with {@link #ACTION_MAIN} to launch the music application.
+     * The activity should be able to play, browse, or manipulate music files
+     * stored on the device.
+     * <p>NOTE: This should not be used as the primary key of an Intent,
+     * since it will not result in the app launching with the correct
+     * action and category.  Instead, use this with
+     * {@link #makeMainSelectorActivity(String, String)} to generate a main
+     * Intent with this category in the selector.</p>
+     */
+    @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+    public static final String CATEGORY_APP_MUSIC = "android.intent.category.APP_MUSIC";
+
+    /**
+     * Used with {@link #ACTION_MAIN} to launch the files application.
+     * The activity should be able to browse and manage files stored on the device.
+     * <p>NOTE: This should not be used as the primary key of an Intent,
+     * since it will not result in the app launching with the correct
+     * action and category.  Instead, use this with
+     * {@link #makeMainSelectorActivity(String, String)} to generate a main
+     * Intent with this category in the selector.</p>
+     */
+    @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+    public static final String CATEGORY_APP_FILES = "android.intent.category.APP_FILES";
+
+    // ---------------------------------------------------------------------
+    // ---------------------------------------------------------------------
+    // Standard extra data keys.
+
+    /**
+     * The initial data to place in a newly created record.  Use with
+     * {@link #ACTION_INSERT}.  The data here is a Map containing the same
+     * fields as would be given to the underlying ContentProvider.insert()
+     * call.
+     */
+    public static final String EXTRA_TEMPLATE = "android.intent.extra.TEMPLATE";
+
+    /**
+     * A constant CharSequence that is associated with the Intent, used with
+     * {@link #ACTION_SEND} to supply the literal data to be sent.  Note that
+     * this may be a styled CharSequence, so you must use
+     * {@link Bundle#getCharSequence(String) Bundle.getCharSequence()} to
+     * retrieve it.
+     */
+    public static final String EXTRA_TEXT = "android.intent.extra.TEXT";
+
+    /**
+     * A constant String that is associated with the Intent, used with
+     * {@link #ACTION_SEND} to supply an alternative to {@link #EXTRA_TEXT}
+     * as HTML formatted text.  Note that you <em>must</em> also supply
+     * {@link #EXTRA_TEXT}.
+     */
+    public static final String EXTRA_HTML_TEXT = "android.intent.extra.HTML_TEXT";
+
+    /**
+     * A content: URI holding a stream of data associated with the Intent,
+     * used with {@link #ACTION_SEND} to supply the data being sent.
+     */
+    public static final String EXTRA_STREAM = "android.intent.extra.STREAM";
+
+    /**
+     * A String[] holding e-mail addresses that should be delivered to.
+     */
+    public static final String EXTRA_EMAIL       = "android.intent.extra.EMAIL";
+
+    /**
+     * A String[] holding e-mail addresses that should be carbon copied.
+     */
+    public static final String EXTRA_CC       = "android.intent.extra.CC";
+
+    /**
+     * A String[] holding e-mail addresses that should be blind carbon copied.
+     */
+    public static final String EXTRA_BCC      = "android.intent.extra.BCC";
+
+    /**
+     * A constant string holding the desired subject line of a message.
+     */
+    public static final String EXTRA_SUBJECT  = "android.intent.extra.SUBJECT";
+
+    /**
+     * An Intent describing the choices you would like shown with
+     * {@link #ACTION_PICK_ACTIVITY} or {@link #ACTION_CHOOSER}.
+     */
+    public static final String EXTRA_INTENT = "android.intent.extra.INTENT";
+
+    /**
+     * An int representing the user id to be used.
+     *
+     * @hide
+     */
+    public static final String EXTRA_USER_ID = "android.intent.extra.USER_ID";
+
+    /**
+     * An int representing the task id to be retrieved. This is used when a launch from recents is
+     * intercepted by another action such as credentials confirmation to remember which task should
+     * be resumed when complete.
+     *
+     * @hide
+     */
+    public static final String EXTRA_TASK_ID = "android.intent.extra.TASK_ID";
+
+    /**
+     * An Intent[] describing additional, alternate choices you would like shown with
+     * {@link #ACTION_CHOOSER}.
+     *
+     * <p>An app may be capable of providing several different payload types to complete a
+     * user's intended action. For example, an app invoking {@link #ACTION_SEND} to share photos
+     * with another app may use EXTRA_ALTERNATE_INTENTS to have the chooser transparently offer
+     * several different supported sending mechanisms for sharing, such as the actual "image/*"
+     * photo data or a hosted link where the photos can be viewed.</p>
+     *
+     * <p>The intent present in {@link #EXTRA_INTENT} will be treated as the
+     * first/primary/preferred intent in the set. Additional intents specified in
+     * this extra are ordered; by default intents that appear earlier in the array will be
+     * preferred over intents that appear later in the array as matches for the same
+     * target component. To alter this preference, a calling app may also supply
+     * {@link #EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER}.</p>
+     */
+    public static final String EXTRA_ALTERNATE_INTENTS = "android.intent.extra.ALTERNATE_INTENTS";
+
+    /**
+     * A {@link ComponentName ComponentName[]} describing components that should be filtered out
+     * and omitted from a list of components presented to the user.
+     *
+     * <p>When used with {@link #ACTION_CHOOSER}, the chooser will omit any of the components
+     * in this array if it otherwise would have shown them. Useful for omitting specific targets
+     * from your own package or other apps from your organization if the idea of sending to those
+     * targets would be redundant with other app functionality. Filtered components will not
+     * be able to present targets from an associated <code>ChooserTargetService</code>.</p>
+     */
+    public static final String EXTRA_EXCLUDE_COMPONENTS
+            = "android.intent.extra.EXCLUDE_COMPONENTS";
+
+    /**
+     * A {@link android.service.chooser.ChooserTarget ChooserTarget[]} for {@link #ACTION_CHOOSER}
+     * describing additional high-priority deep-link targets for the chooser to present to the user.
+     *
+     * <p>Targets provided in this way will be presented inline with all other targets provided
+     * by services from other apps. They will be prioritized before other service targets, but
+     * after those targets provided by sources that the user has manually pinned to the front.</p>
+     *
+     * @see #ACTION_CHOOSER
+     */
+    public static final String EXTRA_CHOOSER_TARGETS = "android.intent.extra.CHOOSER_TARGETS";
+
+    /**
+     * An {@link IntentSender} for an Activity that will be invoked when the user makes a selection
+     * from the chooser activity presented by {@link #ACTION_CHOOSER}.
+     *
+     * <p>An app preparing an action for another app to complete may wish to allow the user to
+     * disambiguate between several options for completing the action based on the chosen target
+     * or otherwise refine the action before it is invoked.
+     * </p>
+     *
+     * <p>When sent, this IntentSender may be filled in with the following extras:</p>
+     * <ul>
+     *     <li>{@link #EXTRA_INTENT} The first intent that matched the user's chosen target</li>
+     *     <li>{@link #EXTRA_ALTERNATE_INTENTS} Any additional intents that also matched the user's
+     *     chosen target beyond the first</li>
+     *     <li>{@link #EXTRA_RESULT_RECEIVER} A {@link ResultReceiver} that the refinement activity
+     *     should fill in and send once the disambiguation is complete</li>
+     * </ul>
+     */
+    public static final String EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER
+            = "android.intent.extra.CHOOSER_REFINEMENT_INTENT_SENDER";
+
+    /**
+     * An {@code ArrayList} of {@code String} annotations describing content for
+     * {@link #ACTION_CHOOSER}.
+     *
+     * <p>If {@link #EXTRA_CONTENT_ANNOTATIONS} is present in an intent used to start a
+     * {@link #ACTION_CHOOSER} activity, the first three annotations will be used to rank apps.</p>
+     *
+     * <p>Annotations should describe the major components or topics of the content. It is up to
+     * apps initiating {@link #ACTION_CHOOSER} to learn and add annotations. Annotations should be
+     * learned in advance, e.g., when creating or saving content, to avoid increasing latency to
+     * start {@link #ACTION_CHOOSER}. Names of customized annotations should not contain the colon
+     * character. Performance on customized annotations can suffer, if they are rarely used for
+     * {@link #ACTION_CHOOSER} in the past 14 days. Therefore, it is recommended to use the
+     * following annotations when applicable.</p>
+     * <ul>
+     *     <li>"product" represents that the topic of the content is mainly about products, e.g.,
+     *     health & beauty, and office supplies.</li>
+     *     <li>"emotion" represents that the topic of the content is mainly about emotions, e.g.,
+     *     happy, and sad.</li>
+     *     <li>"person" represents that the topic of the content is mainly about persons, e.g.,
+     *     face, finger, standing, and walking.</li>
+     *     <li>"child" represents that the topic of the content is mainly about children, e.g.,
+     *     child, and baby.</li>
+     *     <li>"selfie" represents that the topic of the content is mainly about selfies.</li>
+     *     <li>"crowd" represents that the topic of the content is mainly about crowds.</li>
+     *     <li>"party" represents that the topic of the content is mainly about parties.</li>
+     *     <li>"animal" represent that the topic of the content is mainly about animals.</li>
+     *     <li>"plant" represents that the topic of the content is mainly about plants, e.g.,
+     *     flowers.</li>
+     *     <li>"vacation" represents that the topic of the content is mainly about vacations.</li>
+     *     <li>"fashion" represents that the topic of the content is mainly about fashion, e.g.
+     *     sunglasses, jewelry, handbags and clothing.</li>
+     *     <li>"material" represents that the topic of the content is mainly about materials, e.g.,
+     *     paper, and silk.</li>
+     *     <li>"vehicle" represents that the topic of the content is mainly about vehicles, like
+     *     cars, and boats.</li>
+     *     <li>"document" represents that the topic of the content is mainly about documents, e.g.
+     *     posters.</li>
+     *     <li>"design" represents that the topic of the content is mainly about design, e.g. arts
+     *     and designs of houses.</li>
+     *     <li>"holiday" represents that the topic of the content is mainly about holidays, e.g.,
+     *     Christmas and Thanksgiving.</li>
+     * </ul>
+     */
+    public static final String EXTRA_CONTENT_ANNOTATIONS
+            = "android.intent.extra.CONTENT_ANNOTATIONS";
+
+    /**
+     * A {@link ResultReceiver} used to return data back to the sender.
+     *
+     * <p>Used to complete an app-specific
+     * {@link #EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER refinement} for {@link #ACTION_CHOOSER}.</p>
+     *
+     * <p>If {@link #EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER} is present in the intent
+     * used to start a {@link #ACTION_CHOOSER} activity this extra will be
+     * {@link #fillIn(Intent, int) filled in} to that {@link IntentSender} and sent
+     * when the user selects a target component from the chooser. It is up to the recipient
+     * to send a result to this ResultReceiver to signal that disambiguation is complete
+     * and that the chooser should invoke the user's choice.</p>
+     *
+     * <p>The disambiguator should provide a Bundle to the ResultReceiver with an intent
+     * assigned to the key {@link #EXTRA_INTENT}. This supplied intent will be used by the chooser
+     * to match and fill in the final Intent or ChooserTarget before starting it.
+     * The supplied intent must {@link #filterEquals(Intent) match} one of the intents from
+     * {@link #EXTRA_INTENT} or {@link #EXTRA_ALTERNATE_INTENTS} passed to
+     * {@link #EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER} to be accepted.</p>
+     *
+     * <p>The result code passed to the ResultReceiver should be
+     * {@link android.app.Activity#RESULT_OK} if the refinement succeeded and the supplied intent's
+     * target in the chooser should be started, or {@link android.app.Activity#RESULT_CANCELED} if
+     * the chooser should finish without starting a target.</p>
+     */
+    public static final String EXTRA_RESULT_RECEIVER
+            = "android.intent.extra.RESULT_RECEIVER";
+
+    /**
+     * A CharSequence dialog title to provide to the user when used with a
+     * {@link #ACTION_CHOOSER}.
+     */
+    public static final String EXTRA_TITLE = "android.intent.extra.TITLE";
+
+    /**
+     * A Parcelable[] of {@link Intent} or
+     * {@link android.content.pm.LabeledIntent} objects as set with
+     * {@link #putExtra(String, Parcelable[])} of additional activities to place
+     * a the front of the list of choices, when shown to the user with a
+     * {@link #ACTION_CHOOSER}.
+     */
+    public static final String EXTRA_INITIAL_INTENTS = "android.intent.extra.INITIAL_INTENTS";
+
+    /**
+     * A {@link IntentSender} to start after instant app installation success.
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_INSTANT_APP_SUCCESS =
+            "android.intent.extra.INSTANT_APP_SUCCESS";
+
+    /**
+     * A {@link IntentSender} to start after instant app installation failure.
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_INSTANT_APP_FAILURE =
+            "android.intent.extra.INSTANT_APP_FAILURE";
+
+    /**
+     * The host name that triggered an instant app resolution.
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_INSTANT_APP_HOSTNAME =
+            "android.intent.extra.INSTANT_APP_HOSTNAME";
+
+    /**
+     * An opaque token to track instant app resolution.
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_INSTANT_APP_TOKEN =
+            "android.intent.extra.INSTANT_APP_TOKEN";
+
+    /**
+     * The action that triggered an instant application resolution.
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_INSTANT_APP_ACTION = "android.intent.extra.INSTANT_APP_ACTION";
+
+    /**
+     * An array of {@link Bundle}s containing details about resolved instant apps..
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_INSTANT_APP_BUNDLES =
+            "android.intent.extra.INSTANT_APP_BUNDLES";
+
+    /**
+     * A {@link Bundle} of metadata that describes the instant application that needs to be
+     * installed. This data is populated from the response to
+     * {@link android.content.pm.InstantAppResolveInfo#getExtras()} as provided by the registered
+     * instant application resolver.
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_INSTANT_APP_EXTRAS =
+            "android.intent.extra.INSTANT_APP_EXTRAS";
+
+    /**
+     * A boolean value indicating that the instant app resolver was unable to state with certainty
+     * that it did or did not have an app for the sanitized {@link Intent} defined at
+     * {@link #EXTRA_INTENT}.
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_UNKNOWN_INSTANT_APP =
+            "android.intent.extra.UNKNOWN_INSTANT_APP";
+
+    /**
+     * The version code of the app to install components from.
+     * @deprecated Use {@link #EXTRA_LONG_VERSION_CODE).
+     * @hide
+     */
+    @Deprecated
+    public static final String EXTRA_VERSION_CODE = "android.intent.extra.VERSION_CODE";
+
+    /**
+     * The version code of the app to install components from.
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_LONG_VERSION_CODE = "android.intent.extra.LONG_VERSION_CODE";
+
+    /**
+     * The app that triggered the instant app installation.
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_CALLING_PACKAGE
+            = "android.intent.extra.CALLING_PACKAGE";
+
+    /**
+     * Optional calling app provided bundle containing additional launch information the
+     * installer may use.
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_VERIFICATION_BUNDLE
+            = "android.intent.extra.VERIFICATION_BUNDLE";
+
+    /**
+     * A Bundle forming a mapping of potential target package names to different extras Bundles
+     * to add to the default intent extras in {@link #EXTRA_INTENT} when used with
+     * {@link #ACTION_CHOOSER}. Each key should be a package name. The package need not
+     * be currently installed on the device.
+     *
+     * <p>An application may choose to provide alternate extras for the case where a user
+     * selects an activity from a predetermined set of target packages. If the activity
+     * the user selects from the chooser belongs to a package with its package name as
+     * a key in this bundle, the corresponding extras for that package will be merged with
+     * the extras already present in the intent at {@link #EXTRA_INTENT}. If a replacement
+     * extra has the same key as an extra already present in the intent it will overwrite
+     * the extra from the intent.</p>
+     *
+     * <p><em>Examples:</em>
+     * <ul>
+     *     <li>An application may offer different {@link #EXTRA_TEXT} to an application
+     *     when sharing with it via {@link #ACTION_SEND}, augmenting a link with additional query
+     *     parameters for that target.</li>
+     *     <li>An application may offer additional metadata for known targets of a given intent
+     *     to pass along information only relevant to that target such as account or content
+     *     identifiers already known to that application.</li>
+     * </ul></p>
+     */
+    public static final String EXTRA_REPLACEMENT_EXTRAS =
+            "android.intent.extra.REPLACEMENT_EXTRAS";
+
+    /**
+     * An {@link IntentSender} that will be notified if a user successfully chooses a target
+     * component to handle an action in an {@link #ACTION_CHOOSER} activity. The IntentSender
+     * will have the extra {@link #EXTRA_CHOSEN_COMPONENT} appended to it containing the
+     * {@link ComponentName} of the chosen component.
+     *
+     * <p>In some situations this callback may never come, for example if the user abandons
+     * the chooser, switches to another task or any number of other reasons. Apps should not
+     * be written assuming that this callback will always occur.</p>
+     */
+    public static final String EXTRA_CHOSEN_COMPONENT_INTENT_SENDER =
+            "android.intent.extra.CHOSEN_COMPONENT_INTENT_SENDER";
+
+    /**
+     * The {@link ComponentName} chosen by the user to complete an action.
+     *
+     * @see #EXTRA_CHOSEN_COMPONENT_INTENT_SENDER
+     */
+    public static final String EXTRA_CHOSEN_COMPONENT = "android.intent.extra.CHOSEN_COMPONENT";
+
+    /**
+     * A {@link android.view.KeyEvent} object containing the event that
+     * triggered the creation of the Intent it is in.
+     */
+    public static final String EXTRA_KEY_EVENT = "android.intent.extra.KEY_EVENT";
+
+    /**
+     * Set to true in {@link #ACTION_REQUEST_SHUTDOWN} to request confirmation from the user
+     * before shutting down.
+     *
+     * {@hide}
+     */
+    public static final String EXTRA_KEY_CONFIRM = "android.intent.extra.KEY_CONFIRM";
+
+    /**
+     * Set to true in {@link #ACTION_REQUEST_SHUTDOWN} to indicate that the shutdown is
+     * requested by the user.
+     *
+     * {@hide}
+     */
+    public static final String EXTRA_USER_REQUESTED_SHUTDOWN =
+            "android.intent.extra.USER_REQUESTED_SHUTDOWN";
+
+    /**
+     * Used as a boolean extra field in {@link android.content.Intent#ACTION_PACKAGE_REMOVED} or
+     * {@link android.content.Intent#ACTION_PACKAGE_CHANGED} intents to override the default action
+     * of restarting the application.
+     */
+    public static final String EXTRA_DONT_KILL_APP = "android.intent.extra.DONT_KILL_APP";
+
+    /**
+     * A String holding the phone number originally entered in
+     * {@link android.content.Intent#ACTION_NEW_OUTGOING_CALL}, or the actual
+     * number to call in a {@link android.content.Intent#ACTION_CALL}.
+     */
+    public static final String EXTRA_PHONE_NUMBER = "android.intent.extra.PHONE_NUMBER";
+
+    /**
+     * Used as an int extra field in {@link android.content.Intent#ACTION_UID_REMOVED}
+     * intents to supply the uid the package had been assigned.  Also an optional
+     * extra in {@link android.content.Intent#ACTION_PACKAGE_REMOVED} or
+     * {@link android.content.Intent#ACTION_PACKAGE_CHANGED} for the same
+     * purpose.
+     */
+    public static final String EXTRA_UID = "android.intent.extra.UID";
+
+    /**
+     * @hide String array of package names.
+     */
+    @SystemApi
+    public static final String EXTRA_PACKAGES = "android.intent.extra.PACKAGES";
+
+    /**
+     * Used as a boolean extra field in {@link android.content.Intent#ACTION_PACKAGE_REMOVED}
+     * intents to indicate whether this represents a full uninstall (removing
+     * both the code and its data) or a partial uninstall (leaving its data,
+     * implying that this is an update).
+     */
+    public static final String EXTRA_DATA_REMOVED = "android.intent.extra.DATA_REMOVED";
+
+    /**
+     * @hide
+     * Used as a boolean extra field in {@link android.content.Intent#ACTION_PACKAGE_REMOVED}
+     * intents to indicate that at this point the package has been removed for
+     * all users on the device.
+     */
+    public static final String EXTRA_REMOVED_FOR_ALL_USERS
+            = "android.intent.extra.REMOVED_FOR_ALL_USERS";
+
+    /**
+     * Used as a boolean extra field in {@link android.content.Intent#ACTION_PACKAGE_REMOVED}
+     * intents to indicate that this is a replacement of the package, so this
+     * broadcast will immediately be followed by an add broadcast for a
+     * different version of the same package.
+     */
+    public static final String EXTRA_REPLACING = "android.intent.extra.REPLACING";
+
+    /**
+     * Used as an int extra field in {@link android.app.AlarmManager} intents
+     * to tell the application being invoked how many pending alarms are being
+     * delievered with the intent.  For one-shot alarms this will always be 1.
+     * For recurring alarms, this might be greater than 1 if the device was
+     * asleep or powered off at the time an earlier alarm would have been
+     * delivered.
+     */
+    public static final String EXTRA_ALARM_COUNT = "android.intent.extra.ALARM_COUNT";
+
+    /**
+     * Used as an int extra field in {@link android.content.Intent#ACTION_DOCK_EVENT}
+     * intents to request the dock state.  Possible values are
+     * {@link android.content.Intent#EXTRA_DOCK_STATE_UNDOCKED},
+     * {@link android.content.Intent#EXTRA_DOCK_STATE_DESK}, or
+     * {@link android.content.Intent#EXTRA_DOCK_STATE_CAR}, or
+     * {@link android.content.Intent#EXTRA_DOCK_STATE_LE_DESK}, or
+     * {@link android.content.Intent#EXTRA_DOCK_STATE_HE_DESK}.
+     */
+    public static final String EXTRA_DOCK_STATE = "android.intent.extra.DOCK_STATE";
+
+    /**
+     * Used as an int value for {@link android.content.Intent#EXTRA_DOCK_STATE}
+     * to represent that the phone is not in any dock.
+     */
+    public static final int EXTRA_DOCK_STATE_UNDOCKED = 0;
+
+    /**
+     * Used as an int value for {@link android.content.Intent#EXTRA_DOCK_STATE}
+     * to represent that the phone is in a desk dock.
+     */
+    public static final int EXTRA_DOCK_STATE_DESK = 1;
+
+    /**
+     * Used as an int value for {@link android.content.Intent#EXTRA_DOCK_STATE}
+     * to represent that the phone is in a car dock.
+     */
+    public static final int EXTRA_DOCK_STATE_CAR = 2;
+
+    /**
+     * Used as an int value for {@link android.content.Intent#EXTRA_DOCK_STATE}
+     * to represent that the phone is in a analog (low end) dock.
+     */
+    public static final int EXTRA_DOCK_STATE_LE_DESK = 3;
+
+    /**
+     * Used as an int value for {@link android.content.Intent#EXTRA_DOCK_STATE}
+     * to represent that the phone is in a digital (high end) dock.
+     */
+    public static final int EXTRA_DOCK_STATE_HE_DESK = 4;
+
+    /**
+     * Boolean that can be supplied as meta-data with a dock activity, to
+     * indicate that the dock should take over the home key when it is active.
+     */
+    public static final String METADATA_DOCK_HOME = "android.dock_home";
+
+    /**
+     * Used as a parcelable extra field in {@link #ACTION_APP_ERROR}, containing
+     * the bug report.
+     */
+    public static final String EXTRA_BUG_REPORT = "android.intent.extra.BUG_REPORT";
+
+    /**
+     * Used in the extra field in the remote intent. It's astring token passed with the
+     * remote intent.
+     */
+    public static final String EXTRA_REMOTE_INTENT_TOKEN =
+            "android.intent.extra.remote_intent_token";
+
+    /**
+     * @deprecated See {@link #EXTRA_CHANGED_COMPONENT_NAME_LIST}; this field
+     * will contain only the first name in the list.
+     */
+    @Deprecated public static final String EXTRA_CHANGED_COMPONENT_NAME =
+            "android.intent.extra.changed_component_name";
+
+    /**
+     * This field is part of {@link android.content.Intent#ACTION_PACKAGE_CHANGED},
+     * and contains a string array of all of the components that have changed.  If
+     * the state of the overall package has changed, then it will contain an entry
+     * with the package name itself.
+     */
+    public static final String EXTRA_CHANGED_COMPONENT_NAME_LIST =
+            "android.intent.extra.changed_component_name_list";
+
+    /**
+     * This field is part of
+     * {@link android.content.Intent#ACTION_EXTERNAL_APPLICATIONS_AVAILABLE},
+     * {@link android.content.Intent#ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE},
+     * {@link android.content.Intent#ACTION_PACKAGES_SUSPENDED},
+     * {@link android.content.Intent#ACTION_PACKAGES_UNSUSPENDED}
+     * and contains a string array of all of the components that have changed.
+     */
+    public static final String EXTRA_CHANGED_PACKAGE_LIST =
+            "android.intent.extra.changed_package_list";
+
+    /**
+     * This field is part of
+     * {@link android.content.Intent#ACTION_EXTERNAL_APPLICATIONS_AVAILABLE},
+     * {@link android.content.Intent#ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE}
+     * and contains an integer array of uids of all of the components
+     * that have changed.
+     */
+    public static final String EXTRA_CHANGED_UID_LIST =
+            "android.intent.extra.changed_uid_list";
+
+    /**
+     * An integer denoting a bitwise combination of restrictions set on distracting packages via
+     * {@link PackageManager#setDistractingPackageRestrictions(String[], int)}
+     *
+     * @hide
+     * @see PackageManager.DistractionRestriction
+     * @see PackageManager#setDistractingPackageRestrictions(String[], int)
+     */
+    public static final String EXTRA_DISTRACTION_RESTRICTIONS =
+            "android.intent.extra.distraction_restrictions";
+
+    /**
+     * @hide
+     * Magic extra system code can use when binding, to give a label for
+     * who it is that has bound to a service.  This is an integer giving
+     * a framework string resource that can be displayed to the user.
+     */
+    public static final String EXTRA_CLIENT_LABEL =
+            "android.intent.extra.client_label";
+
+    /**
+     * @hide
+     * Magic extra system code can use when binding, to give a PendingIntent object
+     * that can be launched for the user to disable the system's use of this
+     * service.
+     */
+    public static final String EXTRA_CLIENT_INTENT =
+            "android.intent.extra.client_intent";
+
+    /**
+     * Extra used to indicate that an intent should only return data that is on
+     * the local device. This is a boolean extra; the default is false. If true,
+     * an implementation should only allow the user to select data that is
+     * already on the device, not requiring it be downloaded from a remote
+     * service when opened.
+     *
+     * @see #ACTION_GET_CONTENT
+     * @see #ACTION_OPEN_DOCUMENT
+     * @see #ACTION_OPEN_DOCUMENT_TREE
+     * @see #ACTION_CREATE_DOCUMENT
+     */
+    public static final String EXTRA_LOCAL_ONLY =
+            "android.intent.extra.LOCAL_ONLY";
+
+    /**
+     * Extra used to indicate that an intent can allow the user to select and
+     * return multiple items. This is a boolean extra; the default is false. If
+     * true, an implementation is allowed to present the user with a UI where
+     * they can pick multiple items that are all returned to the caller. When
+     * this happens, they should be returned as the {@link #getClipData()} part
+     * of the result Intent.
+     *
+     * @see #ACTION_GET_CONTENT
+     * @see #ACTION_OPEN_DOCUMENT
+     */
+    public static final String EXTRA_ALLOW_MULTIPLE =
+            "android.intent.extra.ALLOW_MULTIPLE";
+
+    /**
+     * The integer userHandle (i.e. userId) carried with broadcast intents related to addition,
+     * removal and switching of users and managed profiles - {@link #ACTION_USER_ADDED},
+     * {@link #ACTION_USER_REMOVED} and {@link #ACTION_USER_SWITCHED}.
+     *
+     * @hide
+     */
+    public static final String EXTRA_USER_HANDLE =
+            "android.intent.extra.user_handle";
+
+    /**
+     * The UserHandle carried with intents.
+     */
+    public static final String EXTRA_USER =
+            "android.intent.extra.USER";
+
+    /**
+     * Extra used in the response from a BroadcastReceiver that handles
+     * {@link #ACTION_GET_RESTRICTION_ENTRIES}. The type of the extra is
+     * <code>ArrayList&lt;RestrictionEntry&gt;</code>.
+     */
+    public static final String EXTRA_RESTRICTIONS_LIST = "android.intent.extra.restrictions_list";
+
+    /**
+     * Extra sent in the intent to the BroadcastReceiver that handles
+     * {@link #ACTION_GET_RESTRICTION_ENTRIES}. The type of the extra is a Bundle containing
+     * the restrictions as key/value pairs.
+     */
+    public static final String EXTRA_RESTRICTIONS_BUNDLE =
+            "android.intent.extra.restrictions_bundle";
+
+    /**
+     * Extra used in the response from a BroadcastReceiver that handles
+     * {@link #ACTION_GET_RESTRICTION_ENTRIES}.
+     */
+    public static final String EXTRA_RESTRICTIONS_INTENT =
+            "android.intent.extra.restrictions_intent";
+
+    /**
+     * Extra used to communicate a set of acceptable MIME types. The type of the
+     * extra is {@code String[]}. Values may be a combination of concrete MIME
+     * types (such as "image/png") and/or partial MIME types (such as
+     * "audio/*").
+     *
+     * @see #ACTION_GET_CONTENT
+     * @see #ACTION_OPEN_DOCUMENT
+     */
+    public static final String EXTRA_MIME_TYPES = "android.intent.extra.MIME_TYPES";
+
+    /**
+     * Optional extra for {@link #ACTION_SHUTDOWN} that allows the sender to qualify that
+     * this shutdown is only for the user space of the system, not a complete shutdown.
+     * When this is true, hardware devices can use this information to determine that
+     * they shouldn't do a complete shutdown of their device since this is not a
+     * complete shutdown down to the kernel, but only user space restarting.
+     * The default if not supplied is false.
+     */
+    public static final String EXTRA_SHUTDOWN_USERSPACE_ONLY
+            = "android.intent.extra.SHUTDOWN_USERSPACE_ONLY";
+
+    /**
+     * Optional extra specifying a time in milliseconds since the Epoch. The value must be
+     * non-negative.
+     * <p>
+     * Type: long
+     * </p>
+     */
+    public static final String EXTRA_TIME = "android.intent.extra.TIME";
+
+    /**
+     * Extra sent with {@link #ACTION_TIMEZONE_CHANGED} specifying the new time zone of the device.
+     *
+     * <p>Type: String, the same as returned by {@link TimeZone#getID()} to identify time zones.
+     */
+    @SuppressLint("ActionValue")
+    public static final String EXTRA_TIMEZONE = "time-zone";
+
+    /**
+     * Optional int extra for {@link #ACTION_TIME_CHANGED} that indicates the
+     * user has set their time format preference. See {@link #EXTRA_TIME_PREF_VALUE_USE_12_HOUR},
+     * {@link #EXTRA_TIME_PREF_VALUE_USE_24_HOUR} and
+     * {@link #EXTRA_TIME_PREF_VALUE_USE_LOCALE_DEFAULT}. The value must not be negative.
+     *
+     * @hide for internal use only.
+     */
+    public static final String EXTRA_TIME_PREF_24_HOUR_FORMAT =
+            "android.intent.extra.TIME_PREF_24_HOUR_FORMAT";
+    /** @hide */
+    public static final int EXTRA_TIME_PREF_VALUE_USE_12_HOUR = 0;
+    /** @hide */
+    public static final int EXTRA_TIME_PREF_VALUE_USE_24_HOUR = 1;
+    /** @hide */
+    public static final int EXTRA_TIME_PREF_VALUE_USE_LOCALE_DEFAULT = 2;
+
+    /**
+     * Intent extra: the reason that the operation associated with this intent is being performed.
+     *
+     * <p>Type: String
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_REASON = "android.intent.extra.REASON";
+
+    /**
+     * {@hide}
+     * This extra will be send together with {@link #ACTION_FACTORY_RESET}
+     */
+    public static final String EXTRA_WIPE_EXTERNAL_STORAGE = "android.intent.extra.WIPE_EXTERNAL_STORAGE";
+
+    /**
+     * {@hide}
+     * This extra will be set to true when the user choose to wipe the data on eSIM during factory
+     * reset for the device with eSIM. This extra will be sent together with
+     * {@link #ACTION_FACTORY_RESET}
+     */
+    public static final String EXTRA_WIPE_ESIMS = "com.android.internal.intent.extra.WIPE_ESIMS";
+
+    /**
+     * Optional {@link android.app.PendingIntent} extra used to deliver the result of the SIM
+     * activation request.
+     * TODO: Add information about the structure and response data used with the pending intent.
+     * @hide
+     */
+    public static final String EXTRA_SIM_ACTIVATION_RESPONSE =
+            "android.intent.extra.SIM_ACTIVATION_RESPONSE";
+
+    /**
+     * Optional index with semantics depending on the intent action.
+     *
+     * <p>The value must be an integer greater or equal to 0.
+     * @see #ACTION_QUICK_VIEW
+     */
+    public static final String EXTRA_INDEX = "android.intent.extra.INDEX";
+
+    /**
+     * Tells the quick viewer to show additional UI actions suitable for the passed Uris,
+     * such as opening in other apps, sharing, opening, editing, printing, deleting,
+     * casting, etc.
+     *
+     * <p>The value is boolean. By default false.
+     * @see #ACTION_QUICK_VIEW
+     * @removed
+     */
+    @Deprecated
+    public static final String EXTRA_QUICK_VIEW_ADVANCED =
+            "android.intent.extra.QUICK_VIEW_ADVANCED";
+
+    /**
+     * An optional extra of {@code String[]} indicating which quick view features should be made
+     * available to the user in the quick view UI while handing a
+     * {@link Intent#ACTION_QUICK_VIEW} intent.
+     * <li>Enumeration of features here is not meant to restrict capabilities of the quick viewer.
+     * Quick viewer can implement features not listed below.
+     * <li>Features included at this time are: {@link QuickViewConstants#FEATURE_VIEW},
+     * {@link QuickViewConstants#FEATURE_EDIT}, {@link QuickViewConstants#FEATURE_DELETE},
+     * {@link QuickViewConstants#FEATURE_DOWNLOAD}, {@link QuickViewConstants#FEATURE_SEND},
+     * {@link QuickViewConstants#FEATURE_PRINT}.
+     * <p>
+     * Requirements:
+     * <li>Quick viewer shouldn't show a feature if the feature is absent in
+     * {@link #EXTRA_QUICK_VIEW_FEATURES}.
+     * <li>When {@link #EXTRA_QUICK_VIEW_FEATURES} is not present, quick viewer should follow
+     * internal policies.
+     * <li>Presence of an feature in {@link #EXTRA_QUICK_VIEW_FEATURES}, does not constitute a
+     * requirement that the feature be shown. Quick viewer may, according to its own policies,
+     * disable or hide features.
+     *
+     * @see #ACTION_QUICK_VIEW
+     */
+    public static final String EXTRA_QUICK_VIEW_FEATURES =
+            "android.intent.extra.QUICK_VIEW_FEATURES";
+
+    /**
+     * Optional boolean extra indicating whether quiet mode has been switched on or off.
+     * When a profile goes into quiet mode, all apps in the profile are killed and the
+     * profile user is stopped. Widgets originating from the profile are masked, and app
+     * launcher icons are grayed out.
+     */
+    public static final String EXTRA_QUIET_MODE = "android.intent.extra.QUIET_MODE";
+
+    /**
+     * Optional CharSequence extra to provide a search query.
+     * The format of this query is dependent on the receiving application.
+     *
+     * <p>Applicable to {@link Intent} with actions:
+     * <ul>
+     *      <li>{@link Intent#ACTION_GET_CONTENT}</li>
+     *      <li>{@link Intent#ACTION_OPEN_DOCUMENT}</li>
+     * </ul>
+     */
+    public static final String EXTRA_CONTENT_QUERY = "android.intent.extra.CONTENT_QUERY";
+
+    /**
+     * Used as an int extra field in {@link #ACTION_MEDIA_RESOURCE_GRANTED}
+     * intents to specify the resource type granted. Possible values are
+     * {@link #EXTRA_MEDIA_RESOURCE_TYPE_VIDEO_CODEC} or
+     * {@link #EXTRA_MEDIA_RESOURCE_TYPE_AUDIO_CODEC}.
+     *
+     * @hide
+     */
+    public static final String EXTRA_MEDIA_RESOURCE_TYPE =
+            "android.intent.extra.MEDIA_RESOURCE_TYPE";
+
+    /**
+     * Used as a boolean extra field in {@link #ACTION_CHOOSER} intents to specify
+     * whether to show the chooser or not when there is only one application available
+     * to choose from.
+     */
+    public static final String EXTRA_AUTO_LAUNCH_SINGLE_CHOICE =
+            "android.intent.extra.AUTO_LAUNCH_SINGLE_CHOICE";
+
+    /**
+     * Used as an int value for {@link #EXTRA_MEDIA_RESOURCE_TYPE}
+     * to represent that a video codec is allowed to use.
+     *
+     * @hide
+     */
+    public static final int EXTRA_MEDIA_RESOURCE_TYPE_VIDEO_CODEC = 0;
+
+    /**
+     * Used as an int value for {@link #EXTRA_MEDIA_RESOURCE_TYPE}
+     * to represent that a audio codec is allowed to use.
+     *
+     * @hide
+     */
+    public static final int EXTRA_MEDIA_RESOURCE_TYPE_AUDIO_CODEC = 1;
+
+    /**
+     * Intent extra: ID of the context used on {@link #ACTION_VIEW_LOCUS}.
+     *
+     * <p>
+     * Type: {@link LocusId}
+     * </p>
+     */
+    public static final String EXTRA_LOCUS_ID = "android.intent.extra.LOCUS_ID";
+
+    // ---------------------------------------------------------------------
+    // ---------------------------------------------------------------------
+    // Intent flags (see mFlags variable).
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "FLAG_GRANT_" }, value = {
+            FLAG_GRANT_READ_URI_PERMISSION, FLAG_GRANT_WRITE_URI_PERMISSION,
+            FLAG_GRANT_PERSISTABLE_URI_PERMISSION, FLAG_GRANT_PREFIX_URI_PERMISSION })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface GrantUriMode {}
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "FLAG_GRANT_" }, value = {
+            FLAG_GRANT_READ_URI_PERMISSION, FLAG_GRANT_WRITE_URI_PERMISSION })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AccessUriMode {}
+
+    /**
+     * Test if given mode flags specify an access mode, which must be at least
+     * read and/or write.
+     *
+     * @hide
+     */
+    public static boolean isAccessUriMode(int modeFlags) {
+        return (modeFlags & (Intent.FLAG_GRANT_READ_URI_PERMISSION
+                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION)) != 0;
+    }
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "FLAG_" }, value = {
+            FLAG_GRANT_READ_URI_PERMISSION,
+            FLAG_GRANT_WRITE_URI_PERMISSION,
+            FLAG_FROM_BACKGROUND,
+            FLAG_DEBUG_LOG_RESOLUTION,
+            FLAG_EXCLUDE_STOPPED_PACKAGES,
+            FLAG_INCLUDE_STOPPED_PACKAGES,
+            FLAG_GRANT_PERSISTABLE_URI_PERMISSION,
+            FLAG_GRANT_PREFIX_URI_PERMISSION,
+            FLAG_DEBUG_TRIAGED_MISSING,
+            FLAG_IGNORE_EPHEMERAL,
+            FLAG_ACTIVITY_MATCH_EXTERNAL,
+            FLAG_ACTIVITY_NO_HISTORY,
+            FLAG_ACTIVITY_SINGLE_TOP,
+            FLAG_ACTIVITY_NEW_TASK,
+            FLAG_ACTIVITY_MULTIPLE_TASK,
+            FLAG_ACTIVITY_CLEAR_TOP,
+            FLAG_ACTIVITY_FORWARD_RESULT,
+            FLAG_ACTIVITY_PREVIOUS_IS_TOP,
+            FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS,
+            FLAG_ACTIVITY_BROUGHT_TO_FRONT,
+            FLAG_ACTIVITY_RESET_TASK_IF_NEEDED,
+            FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY,
+            FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET,
+            FLAG_ACTIVITY_NEW_DOCUMENT,
+            FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET,
+            FLAG_ACTIVITY_NO_USER_ACTION,
+            FLAG_ACTIVITY_REORDER_TO_FRONT,
+            FLAG_ACTIVITY_NO_ANIMATION,
+            FLAG_ACTIVITY_CLEAR_TASK,
+            FLAG_ACTIVITY_TASK_ON_HOME,
+            FLAG_ACTIVITY_RETAIN_IN_RECENTS,
+            FLAG_ACTIVITY_LAUNCH_ADJACENT,
+            FLAG_ACTIVITY_REQUIRE_NON_BROWSER,
+            FLAG_ACTIVITY_REQUIRE_DEFAULT,
+            FLAG_RECEIVER_REGISTERED_ONLY,
+            FLAG_RECEIVER_REPLACE_PENDING,
+            FLAG_RECEIVER_FOREGROUND,
+            FLAG_RECEIVER_NO_ABORT,
+            FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT,
+            FLAG_RECEIVER_BOOT_UPGRADE,
+            FLAG_RECEIVER_INCLUDE_BACKGROUND,
+            FLAG_RECEIVER_EXCLUDE_BACKGROUND,
+            FLAG_RECEIVER_FROM_SHELL,
+            FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS,
+            FLAG_RECEIVER_OFFLOAD,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Flags {}
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "FLAG_" }, value = {
+            FLAG_FROM_BACKGROUND,
+            FLAG_DEBUG_LOG_RESOLUTION,
+            FLAG_EXCLUDE_STOPPED_PACKAGES,
+            FLAG_INCLUDE_STOPPED_PACKAGES,
+            FLAG_DEBUG_TRIAGED_MISSING,
+            FLAG_IGNORE_EPHEMERAL,
+            FLAG_ACTIVITY_MATCH_EXTERNAL,
+            FLAG_ACTIVITY_NO_HISTORY,
+            FLAG_ACTIVITY_SINGLE_TOP,
+            FLAG_ACTIVITY_NEW_TASK,
+            FLAG_ACTIVITY_MULTIPLE_TASK,
+            FLAG_ACTIVITY_CLEAR_TOP,
+            FLAG_ACTIVITY_FORWARD_RESULT,
+            FLAG_ACTIVITY_PREVIOUS_IS_TOP,
+            FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS,
+            FLAG_ACTIVITY_BROUGHT_TO_FRONT,
+            FLAG_ACTIVITY_RESET_TASK_IF_NEEDED,
+            FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY,
+            FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET,
+            FLAG_ACTIVITY_NEW_DOCUMENT,
+            FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET,
+            FLAG_ACTIVITY_NO_USER_ACTION,
+            FLAG_ACTIVITY_REORDER_TO_FRONT,
+            FLAG_ACTIVITY_NO_ANIMATION,
+            FLAG_ACTIVITY_CLEAR_TASK,
+            FLAG_ACTIVITY_TASK_ON_HOME,
+            FLAG_ACTIVITY_RETAIN_IN_RECENTS,
+            FLAG_ACTIVITY_LAUNCH_ADJACENT,
+            FLAG_RECEIVER_REGISTERED_ONLY,
+            FLAG_RECEIVER_REPLACE_PENDING,
+            FLAG_RECEIVER_FOREGROUND,
+            FLAG_RECEIVER_NO_ABORT,
+            FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT,
+            FLAG_RECEIVER_BOOT_UPGRADE,
+            FLAG_RECEIVER_INCLUDE_BACKGROUND,
+            FLAG_RECEIVER_EXCLUDE_BACKGROUND,
+            FLAG_RECEIVER_FROM_SHELL,
+            FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS,
+            FLAG_RECEIVER_OFFLOAD,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface MutableFlags {}
+
+    /**
+     * If set, the recipient of this Intent will be granted permission to
+     * perform read operations on the URI in the Intent's data and any URIs
+     * specified in its ClipData.  When applying to an Intent's ClipData,
+     * all URIs as well as recursive traversals through data or other ClipData
+     * in Intent items will be granted; only the grant flags of the top-level
+     * Intent are used.
+     */
+    public static final int FLAG_GRANT_READ_URI_PERMISSION = 0x00000001;
+    /**
+     * If set, the recipient of this Intent will be granted permission to
+     * perform write operations on the URI in the Intent's data and any URIs
+     * specified in its ClipData.  When applying to an Intent's ClipData,
+     * all URIs as well as recursive traversals through data or other ClipData
+     * in Intent items will be granted; only the grant flags of the top-level
+     * Intent are used.
+     */
+    public static final int FLAG_GRANT_WRITE_URI_PERMISSION = 0x00000002;
+    /**
+     * Can be set by the caller to indicate that this Intent is coming from
+     * a background operation, not from direct user interaction.
+     */
+    public static final int FLAG_FROM_BACKGROUND = 0x00000004;
+    /**
+     * A flag you can enable for debugging: when set, log messages will be
+     * printed during the resolution of this intent to show you what has
+     * been found to create the final resolved list.
+     */
+    public static final int FLAG_DEBUG_LOG_RESOLUTION = 0x00000008;
+    /**
+     * If set, this intent will not match any components in packages that
+     * are currently stopped.  If this is not set, then the default behavior
+     * is to include such applications in the result.
+     */
+    public static final int FLAG_EXCLUDE_STOPPED_PACKAGES = 0x00000010;
+    /**
+     * If set, this intent will always match any components in packages that
+     * are currently stopped.  This is the default behavior when
+     * {@link #FLAG_EXCLUDE_STOPPED_PACKAGES} is not set.  If both of these
+     * flags are set, this one wins (it allows overriding of exclude for
+     * places where the framework may automatically set the exclude flag).
+     */
+    public static final int FLAG_INCLUDE_STOPPED_PACKAGES = 0x00000020;
+
+    /**
+     * When combined with {@link #FLAG_GRANT_READ_URI_PERMISSION} and/or
+     * {@link #FLAG_GRANT_WRITE_URI_PERMISSION}, the URI permission grant can be
+     * persisted across device reboots until explicitly revoked with
+     * {@link Context#revokeUriPermission(Uri, int)}. This flag only offers the
+     * grant for possible persisting; the receiving application must call
+     * {@link ContentResolver#takePersistableUriPermission(Uri, int)} to
+     * actually persist.
+     *
+     * @see ContentResolver#takePersistableUriPermission(Uri, int)
+     * @see ContentResolver#releasePersistableUriPermission(Uri, int)
+     * @see ContentResolver#getPersistedUriPermissions()
+     * @see ContentResolver#getOutgoingPersistedUriPermissions()
+     */
+    public static final int FLAG_GRANT_PERSISTABLE_URI_PERMISSION = 0x00000040;
+
+    /**
+     * When combined with {@link #FLAG_GRANT_READ_URI_PERMISSION} and/or
+     * {@link #FLAG_GRANT_WRITE_URI_PERMISSION}, the URI permission grant
+     * applies to any URI that is a prefix match against the original granted
+     * URI. (Without this flag, the URI must match exactly for access to be
+     * granted.) Another URI is considered a prefix match only when scheme,
+     * authority, and all path segments defined by the prefix are an exact
+     * match.
+     */
+    public static final int FLAG_GRANT_PREFIX_URI_PERMISSION = 0x00000080;
+
+    /**
+     * Flag used to automatically match intents based on their Direct Boot
+     * awareness and the current user state.
+     * <p>
+     * Since the default behavior is to automatically apply the current user
+     * state, this is effectively a sentinel value that doesn't change the
+     * output of any queries based on its presence or absence.
+     * <p>
+     * Instead, this value can be useful in conjunction with
+     * {@link android.os.StrictMode.VmPolicy.Builder#detectImplicitDirectBoot()}
+     * to detect when a caller is relying on implicit automatic matching,
+     * instead of confirming the explicit behavior they want.
+     */
+    public static final int FLAG_DIRECT_BOOT_AUTO = 0x00000100;
+
+    /** {@hide} */
+    @Deprecated
+    public static final int FLAG_DEBUG_TRIAGED_MISSING = FLAG_DIRECT_BOOT_AUTO;
+
+    /**
+     * Internal flag used to indicate ephemeral applications should not be
+     * considered when resolving the intent.
+     *
+     * @hide
+     */
+    public static final int FLAG_IGNORE_EPHEMERAL = 0x00000200;
+
+    /**
+     * If set, the new activity is not kept in the history stack.  As soon as
+     * the user navigates away from it, the activity is finished.  This may also
+     * be set with the {@link android.R.styleable#AndroidManifestActivity_noHistory
+     * noHistory} attribute.
+     *
+     * <p>If set, {@link android.app.Activity#onActivityResult onActivityResult()}
+     * is never invoked when the current activity starts a new activity which
+     * sets a result and finishes.
+     */
+    public static final int FLAG_ACTIVITY_NO_HISTORY = 0x40000000;
+    /**
+     * If set, the activity will not be launched if it is already running
+     * at the top of the history stack.
+     */
+    public static final int FLAG_ACTIVITY_SINGLE_TOP = 0x20000000;
+    /**
+     * If set, this activity will become the start of a new task on this
+     * history stack.  A task (from the activity that started it to the
+     * next task activity) defines an atomic group of activities that the
+     * user can move to.  Tasks can be moved to the foreground and background;
+     * all of the activities inside of a particular task always remain in
+     * the same order.  See
+     * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
+     * Stack</a> for more information about tasks.
+     *
+     * <p>This flag is generally used by activities that want
+     * to present a "launcher" style behavior: they give the user a list of
+     * separate things that can be done, which otherwise run completely
+     * independently of the activity launching them.
+     *
+     * <p>When using this flag, if a task is already running for the activity
+     * you are now starting, then a new activity will not be started; instead,
+     * the current task will simply be brought to the front of the screen with
+     * the state it was last in.  See {@link #FLAG_ACTIVITY_MULTIPLE_TASK} for a flag
+     * to disable this behavior.
+     *
+     * <p>This flag can not be used when the caller is requesting a result from
+     * the activity being launched.
+     */
+    public static final int FLAG_ACTIVITY_NEW_TASK = 0x10000000;
+    /**
+     * This flag is used to create a new task and launch an activity into it.
+     * This flag is always paired with either {@link #FLAG_ACTIVITY_NEW_DOCUMENT}
+     * or {@link #FLAG_ACTIVITY_NEW_TASK}. In both cases these flags alone would
+     * search through existing tasks for ones matching this Intent. Only if no such
+     * task is found would a new task be created. When paired with
+     * FLAG_ACTIVITY_MULTIPLE_TASK both of these behaviors are modified to skip
+     * the search for a matching task and unconditionally start a new task.
+     *
+     * <strong>When used with {@link #FLAG_ACTIVITY_NEW_TASK} do not use this
+     * flag unless you are implementing your own
+     * top-level application launcher.</strong>  Used in conjunction with
+     * {@link #FLAG_ACTIVITY_NEW_TASK} to disable the
+     * behavior of bringing an existing task to the foreground.  When set,
+     * a new task is <em>always</em> started to host the Activity for the
+     * Intent, regardless of whether there is already an existing task running
+     * the same thing.
+     *
+     * <p><strong>Because the default system does not include graphical task management,
+     * you should not use this flag unless you provide some way for a user to
+     * return back to the tasks you have launched.</strong>
+     *
+     * See {@link #FLAG_ACTIVITY_NEW_DOCUMENT} for details of this flag's use for
+     * creating new document tasks.
+     *
+     * <p>This flag is ignored if one of {@link #FLAG_ACTIVITY_NEW_TASK} or
+     * {@link #FLAG_ACTIVITY_NEW_DOCUMENT} is not also set.
+     *
+     * <p>See
+     * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
+     * Stack</a> for more information about tasks.
+     *
+     * @see #FLAG_ACTIVITY_NEW_DOCUMENT
+     * @see #FLAG_ACTIVITY_NEW_TASK
+     */
+    public static final int FLAG_ACTIVITY_MULTIPLE_TASK = 0x08000000;
+    /**
+     * If set, and the activity being launched is already running in the
+     * current task, then instead of launching a new instance of that activity,
+     * all of the other activities on top of it will be closed and this Intent
+     * will be delivered to the (now on top) old activity as a new Intent.
+     *
+     * <p>For example, consider a task consisting of the activities: A, B, C, D.
+     * If D calls startActivity() with an Intent that resolves to the component
+     * of activity B, then C and D will be finished and B receive the given
+     * Intent, resulting in the stack now being: A, B.
+     *
+     * <p>The currently running instance of activity B in the above example will
+     * either receive the new intent you are starting here in its
+     * onNewIntent() method, or be itself finished and restarted with the
+     * new intent.  If it has declared its launch mode to be "multiple" (the
+     * default) and you have not set {@link #FLAG_ACTIVITY_SINGLE_TOP} in
+     * the same intent, then it will be finished and re-created; for all other
+     * launch modes or if {@link #FLAG_ACTIVITY_SINGLE_TOP} is set then this
+     * Intent will be delivered to the current instance's onNewIntent().
+     *
+     * <p>This launch mode can also be used to good effect in conjunction with
+     * {@link #FLAG_ACTIVITY_NEW_TASK}: if used to start the root activity
+     * of a task, it will bring any currently running instance of that task
+     * to the foreground, and then clear it to its root state.  This is
+     * especially useful, for example, when launching an activity from the
+     * notification manager.
+     *
+     * <p>See
+     * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
+     * Stack</a> for more information about tasks.
+     */
+    public static final int FLAG_ACTIVITY_CLEAR_TOP = 0x04000000;
+    /**
+     * If set and this intent is being used to launch a new activity from an
+     * existing one, then the reply target of the existing activity will be
+     * transferred to the new activity.  This way, the new activity can call
+     * {@link android.app.Activity#setResult} and have that result sent back to
+     * the reply target of the original activity.
+     */
+    public static final int FLAG_ACTIVITY_FORWARD_RESULT = 0x02000000;
+    /**
+     * If set and this intent is being used to launch a new activity from an
+     * existing one, the current activity will not be counted as the top
+     * activity for deciding whether the new intent should be delivered to
+     * the top instead of starting a new one.  The previous activity will
+     * be used as the top, with the assumption being that the current activity
+     * will finish itself immediately.
+     */
+    public static final int FLAG_ACTIVITY_PREVIOUS_IS_TOP = 0x01000000;
+    /**
+     * If set, the new activity is not kept in the list of recently launched
+     * activities.
+     */
+    public static final int FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS = 0x00800000;
+    /**
+     * This flag is not normally set by application code, but set for you by
+     * the system as described in the
+     * {@link android.R.styleable#AndroidManifestActivity_launchMode
+     * launchMode} documentation for the singleTask mode.
+     */
+    public static final int FLAG_ACTIVITY_BROUGHT_TO_FRONT = 0x00400000;
+    /**
+     * If set, and this activity is either being started in a new task or
+     * bringing to the top an existing task, then it will be launched as
+     * the front door of the task.  This will result in the application of
+     * any affinities needed to have that task in the proper state (either
+     * moving activities to or from it), or simply resetting that task to
+     * its initial state if needed.
+     */
+    public static final int FLAG_ACTIVITY_RESET_TASK_IF_NEEDED = 0x00200000;
+    /**
+     * This flag is not normally set by application code, but set for you by
+     * the system if this activity is being launched from history
+     * (longpress home key).
+     */
+    public static final int FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY = 0x00100000;
+    /**
+     * @deprecated As of API 21 this performs identically to
+     * {@link #FLAG_ACTIVITY_NEW_DOCUMENT} which should be used instead of this.
+     */
+    @Deprecated
+    public static final int FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET = 0x00080000;
+    /**
+     * This flag is used to open a document into a new task rooted at the activity launched
+     * by this Intent. Through the use of this flag, or its equivalent attribute,
+     * {@link android.R.attr#documentLaunchMode} multiple instances of the same activity
+     * containing different documents will appear in the recent tasks list.
+     *
+     * <p>The use of the activity attribute form of this,
+     * {@link android.R.attr#documentLaunchMode}, is
+     * preferred over the Intent flag described here. The attribute form allows the
+     * Activity to specify multiple document behavior for all launchers of the Activity
+     * whereas using this flag requires each Intent that launches the Activity to specify it.
+     *
+     * <p>Note that the default semantics of this flag w.r.t. whether the recents entry for
+     * it is kept after the activity is finished is different than the use of
+     * {@link #FLAG_ACTIVITY_NEW_TASK} and {@link android.R.attr#documentLaunchMode} -- if
+     * this flag is being used to create a new recents entry, then by default that entry
+     * will be removed once the activity is finished.  You can modify this behavior with
+     * {@link #FLAG_ACTIVITY_RETAIN_IN_RECENTS}.
+     *
+     * <p>FLAG_ACTIVITY_NEW_DOCUMENT may be used in conjunction with {@link
+     * #FLAG_ACTIVITY_MULTIPLE_TASK}. When used alone it is the
+     * equivalent of the Activity manifest specifying {@link
+     * android.R.attr#documentLaunchMode}="intoExisting". When used with
+     * FLAG_ACTIVITY_MULTIPLE_TASK it is the equivalent of the Activity manifest specifying
+     * {@link android.R.attr#documentLaunchMode}="always".
+     *
+     * Refer to {@link android.R.attr#documentLaunchMode} for more information.
+     *
+     * @see android.R.attr#documentLaunchMode
+     * @see #FLAG_ACTIVITY_MULTIPLE_TASK
+     */
+    public static final int FLAG_ACTIVITY_NEW_DOCUMENT = FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET;
+    /**
+     * If set, this flag will prevent the normal {@link android.app.Activity#onUserLeaveHint}
+     * callback from occurring on the current frontmost activity before it is
+     * paused as the newly-started activity is brought to the front.
+     *
+     * <p>Typically, an activity can rely on that callback to indicate that an
+     * explicit user action has caused their activity to be moved out of the
+     * foreground. The callback marks an appropriate point in the activity's
+     * lifecycle for it to dismiss any notifications that it intends to display
+     * "until the user has seen them," such as a blinking LED.
+     *
+     * <p>If an activity is ever started via any non-user-driven events such as
+     * phone-call receipt or an alarm handler, this flag should be passed to {@link
+     * Context#startActivity Context.startActivity}, ensuring that the pausing
+     * activity does not think the user has acknowledged its notification.
+     */
+    public static final int FLAG_ACTIVITY_NO_USER_ACTION = 0x00040000;
+    /**
+     * If set in an Intent passed to {@link Context#startActivity Context.startActivity()},
+     * this flag will cause the launched activity to be brought to the front of its
+     * task's history stack if it is already running.
+     *
+     * <p>For example, consider a task consisting of four activities: A, B, C, D.
+     * If D calls startActivity() with an Intent that resolves to the component
+     * of activity B, then B will be brought to the front of the history stack,
+     * with this resulting order:  A, C, D, B.
+     *
+     * This flag will be ignored if {@link #FLAG_ACTIVITY_CLEAR_TOP} is also
+     * specified.
+     */
+    public static final int FLAG_ACTIVITY_REORDER_TO_FRONT = 0X00020000;
+    /**
+     * If set in an Intent passed to {@link Context#startActivity Context.startActivity()},
+     * this flag will prevent the system from applying an activity transition
+     * animation to go to the next activity state.  This doesn't mean an
+     * animation will never run -- if another activity change happens that doesn't
+     * specify this flag before the activity started here is displayed, then
+     * that transition will be used.  This flag can be put to good use
+     * when you are going to do a series of activity operations but the
+     * animation seen by the user shouldn't be driven by the first activity
+     * change but rather a later one.
+     */
+    public static final int FLAG_ACTIVITY_NO_ANIMATION = 0X00010000;
+    /**
+     * If set in an Intent passed to {@link Context#startActivity Context.startActivity()},
+     * this flag will cause any existing task that would be associated with the
+     * activity to be cleared before the activity is started.  That is, the activity
+     * becomes the new root of an otherwise empty task, and any old activities
+     * are finished.  This can only be used in conjunction with {@link #FLAG_ACTIVITY_NEW_TASK}.
+     */
+    public static final int FLAG_ACTIVITY_CLEAR_TASK = 0X00008000;
+    /**
+     * If set in an Intent passed to {@link Context#startActivity Context.startActivity()},
+     * this flag will cause a newly launching task to be placed on top of the current
+     * home activity task (if there is one).  That is, pressing back from the task
+     * will always return the user to home even if that was not the last activity they
+     * saw.   This can only be used in conjunction with {@link #FLAG_ACTIVITY_NEW_TASK}.
+     */
+    public static final int FLAG_ACTIVITY_TASK_ON_HOME = 0X00004000;
+    /**
+     * By default a document created by {@link #FLAG_ACTIVITY_NEW_DOCUMENT} will
+     * have its entry in recent tasks removed when the user closes it (with back
+     * or however else it may finish()). If you would like to instead allow the
+     * document to be kept in recents so that it can be re-launched, you can use
+     * this flag. When set and the task's activity is finished, the recents
+     * entry will remain in the interface for the user to re-launch it, like a
+     * recents entry for a top-level application.
+     * <p>
+     * The receiving activity can override this request with
+     * {@link android.R.attr#autoRemoveFromRecents} or by explcitly calling
+     * {@link android.app.Activity#finishAndRemoveTask()
+     * Activity.finishAndRemoveTask()}.
+     */
+    public static final int FLAG_ACTIVITY_RETAIN_IN_RECENTS = 0x00002000;
+
+    /**
+     * This flag is only used in split-screen multi-window mode. The new activity will be displayed
+     * adjacent to the one launching it. This can only be used in conjunction with
+     * {@link #FLAG_ACTIVITY_NEW_TASK}. Also, setting {@link #FLAG_ACTIVITY_MULTIPLE_TASK} is
+     * required if you want a new instance of an existing activity to be created.
+     */
+    public static final int FLAG_ACTIVITY_LAUNCH_ADJACENT = 0x00001000;
+
+
+    /**
+     * If set in an Intent passed to {@link Context#startActivity Context.startActivity()},
+     * this flag will attempt to launch an instant app if no full app on the device can already
+     * handle the intent.
+     * <p>
+     * When attempting to resolve instant apps externally, the following {@link Intent} properties
+     * are supported:
+     * <ul>
+     *     <li>{@link Intent#setAction(String)}</li>
+     *     <li>{@link Intent#addCategory(String)}</li>
+     *     <li>{@link Intent#setData(Uri)}</li>
+     *     <li>{@link Intent#setType(String)}</li>
+     *     <li>{@link Intent#setPackage(String)}</li>
+     *     <li>{@link Intent#addFlags(int)}</li>
+     * </ul>
+     * <p>
+     * In the case that no instant app can be found, the installer will be launched to notify the
+     * user that the intent could not be resolved. On devices that do not support instant apps,
+     * the flag will be ignored.
+     */
+    public static final int FLAG_ACTIVITY_MATCH_EXTERNAL = 0x00000800;
+
+    /**
+     * If set in an intent passed to {@link Context#startActivity Context.startActivity()}, this
+     * flag will only launch the intent if it resolves to a result that is not a browser. If no such
+     * result exists, an {@link ActivityNotFoundException} will be thrown.
+     */
+    public static final int FLAG_ACTIVITY_REQUIRE_NON_BROWSER = 0x00000400;
+
+    /**
+     * If set in an intent passed to {@link Context#startActivity Context.startActivity()}, this
+     * flag will only launch the intent if it resolves to a single result. If no such result exists
+     * or if the system chooser would otherwise be displayed, an {@link ActivityNotFoundException}
+     * will be thrown.
+     */
+    public static final int FLAG_ACTIVITY_REQUIRE_DEFAULT = 0x00000200;
+
+    /**
+     * If set, when sending a broadcast only registered receivers will be
+     * called -- no BroadcastReceiver components will be launched.
+     */
+    public static final int FLAG_RECEIVER_REGISTERED_ONLY = 0x40000000;
+    /**
+     * If set, when sending a broadcast the new broadcast will replace
+     * any existing pending broadcast that matches it.  Matching is defined
+     * by {@link Intent#filterEquals(Intent) Intent.filterEquals} returning
+     * true for the intents of the two broadcasts.  When a match is found,
+     * the new broadcast (and receivers associated with it) will replace the
+     * existing one in the pending broadcast list, remaining at the same
+     * position in the list.
+     *
+     * <p>This flag is most typically used with sticky broadcasts, which
+     * only care about delivering the most recent values of the broadcast
+     * to their receivers.
+     */
+    public static final int FLAG_RECEIVER_REPLACE_PENDING = 0x20000000;
+    /**
+     * If set, when sending a broadcast the recipient is allowed to run at
+     * foreground priority, with a shorter timeout interval.  During normal
+     * broadcasts the receivers are not automatically hoisted out of the
+     * background priority class.
+     */
+    public static final int FLAG_RECEIVER_FOREGROUND = 0x10000000;
+    /**
+     * If set, when sending a broadcast the recipient will be run on the offload queue.
+     *
+     * @hide
+     */
+    public static final int FLAG_RECEIVER_OFFLOAD = 0x80000000;
+    /**
+     * If this is an ordered broadcast, don't allow receivers to abort the broadcast.
+     * They can still propagate results through to later receivers, but they can not prevent
+     * later receivers from seeing the broadcast.
+     */
+    public static final int FLAG_RECEIVER_NO_ABORT = 0x08000000;
+    /**
+     * If set, when sending a broadcast <i>before the system has fully booted up
+     * (which is even before {@link #ACTION_LOCKED_BOOT_COMPLETED} has been sent)"</i> only
+     * registered receivers will be called -- no BroadcastReceiver components
+     * will be launched.  Sticky intent state will be recorded properly even
+     * if no receivers wind up being called.  If {@link #FLAG_RECEIVER_REGISTERED_ONLY}
+     * is specified in the broadcast intent, this flag is unnecessary.
+     *
+     * <p>This flag is only for use by system services (even services from mainline modules) as a
+     * convenience to avoid having to implement a more complex mechanism around detection
+     * of boot completion.
+     *
+     * <p>This is useful to system server mainline modules
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT = 0x04000000;
+    /**
+     * Set when this broadcast is for a boot upgrade, a special mode that
+     * allows the broadcast to be sent before the system is ready and launches
+     * the app process with no providers running in it.
+     * @hide
+     */
+    public static final int FLAG_RECEIVER_BOOT_UPGRADE = 0x02000000;
+    /**
+     * If set, the broadcast will always go to manifest receivers in background (cached
+     * or not running) apps, regardless of whether that would be done by default.  By
+     * default they will only receive broadcasts if the broadcast has specified an
+     * explicit component or package name.
+     *
+     * NOTE: dumpstate uses this flag numerically, so when its value is changed
+     * the broadcast code there must also be changed to match.
+     *
+     * @hide
+     */
+    public static final int FLAG_RECEIVER_INCLUDE_BACKGROUND = 0x01000000;
+    /**
+     * If set, the broadcast will never go to manifest receivers in background (cached
+     * or not running) apps, regardless of whether that would be done by default.  By
+     * default they will receive broadcasts if the broadcast has specified an
+     * explicit component or package name.
+     * @hide
+     */
+    public static final int FLAG_RECEIVER_EXCLUDE_BACKGROUND = 0x00800000;
+    /**
+     * If set, this broadcast is being sent from the shell.
+     * @hide
+     */
+    public static final int FLAG_RECEIVER_FROM_SHELL = 0x00400000;
+
+    /**
+     * If set, the broadcast will be visible to receivers in Instant Apps. By default Instant Apps
+     * will not receive broadcasts.
+     *
+     * <em>This flag has no effect when used by an Instant App.</em>
+     */
+    public static final int FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS = 0x00200000;
+
+    /**
+     * @hide Flags that can't be changed with PendingIntent.
+     */
+    public static final int IMMUTABLE_FLAGS = FLAG_GRANT_READ_URI_PERMISSION
+            | FLAG_GRANT_WRITE_URI_PERMISSION | FLAG_GRANT_PERSISTABLE_URI_PERMISSION
+            | FLAG_GRANT_PREFIX_URI_PERMISSION;
+
+    // ---------------------------------------------------------------------
+    // ---------------------------------------------------------------------
+    // toUri() and parseUri() options.
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "URI_" }, value = {
+            URI_ALLOW_UNSAFE,
+            URI_ANDROID_APP_SCHEME,
+            URI_INTENT_SCHEME,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface UriFlags {}
+
+    /**
+     * Flag for use with {@link #toUri} and {@link #parseUri}: the URI string
+     * always has the "intent:" scheme.  This syntax can be used when you want
+     * to later disambiguate between URIs that are intended to describe an
+     * Intent vs. all others that should be treated as raw URIs.  When used
+     * with {@link #parseUri}, any other scheme will result in a generic
+     * VIEW action for that raw URI.
+     */
+    public static final int URI_INTENT_SCHEME = 1<<0;
+
+    /**
+     * Flag for use with {@link #toUri} and {@link #parseUri}: the URI string
+     * always has the "android-app:" scheme.  This is a variation of
+     * {@link #URI_INTENT_SCHEME} whose format is simpler for the case of an
+     * http/https URI being delivered to a specific package name.  The format
+     * is:
+     *
+     * <pre class="prettyprint">
+     * android-app://{package_id}[/{scheme}[/{host}[/{path}]]][#Intent;{...}]</pre>
+     *
+     * <p>In this scheme, only the <code>package_id</code> is required.  If you include a host,
+     * you must also include a scheme; including a path also requires both a host and a scheme.
+     * The final #Intent; fragment can be used without a scheme, host, or path.
+     * Note that this can not be
+     * used with intents that have a {@link #setSelector}, since the base intent
+     * will always have an explicit package name.</p>
+     *
+     * <p>Some examples of how this scheme maps to Intent objects:</p>
+     * <table border="2" width="85%" align="center" frame="hsides" rules="rows">
+     *     <colgroup align="left" />
+     *     <colgroup align="left" />
+     *     <thead>
+     *     <tr><th>URI</th> <th>Intent</th></tr>
+     *     </thead>
+     *
+     *     <tbody>
+     *     <tr><td><code>android-app://com.example.app</code></td>
+     *         <td><table style="margin:0;border:0;cellpadding:0;cellspacing:0">
+     *             <tr><td>Action: </td><td>{@link #ACTION_MAIN}</td></tr>
+     *             <tr><td>Package: </td><td><code>com.example.app</code></td></tr>
+     *         </table></td>
+     *     </tr>
+     *     <tr><td><code>android-app://com.example.app/http/example.com</code></td>
+     *         <td><table style="margin:0;border:0;cellpadding:0;cellspacing:0">
+     *             <tr><td>Action: </td><td>{@link #ACTION_VIEW}</td></tr>
+     *             <tr><td>Data: </td><td><code>http://example.com/</code></td></tr>
+     *             <tr><td>Package: </td><td><code>com.example.app</code></td></tr>
+     *         </table></td>
+     *     </tr>
+     *     <tr><td><code>android-app://com.example.app/http/example.com/foo?1234</code></td>
+     *         <td><table style="margin:0;border:0;cellpadding:0;cellspacing:0">
+     *             <tr><td>Action: </td><td>{@link #ACTION_VIEW}</td></tr>
+     *             <tr><td>Data: </td><td><code>http://example.com/foo?1234</code></td></tr>
+     *             <tr><td>Package: </td><td><code>com.example.app</code></td></tr>
+     *         </table></td>
+     *     </tr>
+     *     <tr><td><code>android-app://com.example.app/<br />#Intent;action=com.example.MY_ACTION;end</code></td>
+     *         <td><table style="margin:0;border:0;cellpadding:0;cellspacing:0">
+     *             <tr><td>Action: </td><td><code>com.example.MY_ACTION</code></td></tr>
+     *             <tr><td>Package: </td><td><code>com.example.app</code></td></tr>
+     *         </table></td>
+     *     </tr>
+     *     <tr><td><code>android-app://com.example.app/http/example.com/foo?1234<br />#Intent;action=com.example.MY_ACTION;end</code></td>
+     *         <td><table style="margin:0;border:0;cellpadding:0;cellspacing:0">
+     *             <tr><td>Action: </td><td><code>com.example.MY_ACTION</code></td></tr>
+     *             <tr><td>Data: </td><td><code>http://example.com/foo?1234</code></td></tr>
+     *             <tr><td>Package: </td><td><code>com.example.app</code></td></tr>
+     *         </table></td>
+     *     </tr>
+     *     <tr><td><code>android-app://com.example.app/<br />#Intent;action=com.example.MY_ACTION;<br />i.some_int=100;S.some_str=hello;end</code></td>
+     *         <td><table border="" style="margin:0" >
+     *             <tr><td>Action: </td><td><code>com.example.MY_ACTION</code></td></tr>
+     *             <tr><td>Package: </td><td><code>com.example.app</code></td></tr>
+     *             <tr><td>Extras: </td><td><code>some_int=(int)100<br />some_str=(String)hello</code></td></tr>
+     *         </table></td>
+     *     </tr>
+     *     </tbody>
+     * </table>
+     */
+    public static final int URI_ANDROID_APP_SCHEME = 1<<1;
+
+    /**
+     * Flag for use with {@link #toUri} and {@link #parseUri}: allow parsing
+     * of unsafe information.  In particular, the flags {@link #FLAG_GRANT_READ_URI_PERMISSION},
+     * {@link #FLAG_GRANT_WRITE_URI_PERMISSION}, {@link #FLAG_GRANT_PERSISTABLE_URI_PERMISSION},
+     * and {@link #FLAG_GRANT_PREFIX_URI_PERMISSION} flags can not be set, so that the
+     * generated Intent can not cause unexpected data access to happen.
+     *
+     * <p>If you do not trust the source of the URI being parsed, you should still do further
+     * processing to protect yourself from it.  In particular, when using it to start an
+     * activity you should usually add in {@link #CATEGORY_BROWSABLE} to limit the activities
+     * that can handle it.</p>
+     */
+    public static final int URI_ALLOW_UNSAFE = 1<<2;
+
+    // ---------------------------------------------------------------------
+
+    private String mAction;
+    private Uri mData;
+    private String mType;
+    private String mIdentifier;
+    private String mPackage;
+    private ComponentName mComponent;
+    private int mFlags;
+    private ArraySet<String> mCategories;
+    @UnsupportedAppUsage
+    private Bundle mExtras;
+    private Rect mSourceBounds;
+    private Intent mSelector;
+    private ClipData mClipData;
+    private int mContentUserHint = UserHandle.USER_CURRENT;
+    /** Token to track instant app launches. Local only; do not copy cross-process. */
+    private String mLaunchToken;
+
+    // ---------------------------------------------------------------------
+
+    private static final int COPY_MODE_ALL = 0;
+    private static final int COPY_MODE_FILTER = 1;
+    private static final int COPY_MODE_HISTORY = 2;
+
+    /** @hide */
+    @IntDef(prefix = { "COPY_MODE_" }, value = {
+            COPY_MODE_ALL,
+            COPY_MODE_FILTER,
+            COPY_MODE_HISTORY
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface CopyMode {}
+
+    /**
+     * Create an empty intent.
+     */
+    public Intent() {
+    }
+
+    /**
+     * Copy constructor.
+     */
+    public Intent(Intent o) {
+        this(o, COPY_MODE_ALL);
+    }
+
+    private Intent(Intent o, @CopyMode int copyMode) {
+        this.mAction = o.mAction;
+        this.mData = o.mData;
+        this.mType = o.mType;
+        this.mIdentifier = o.mIdentifier;
+        this.mPackage = o.mPackage;
+        this.mComponent = o.mComponent;
+
+        if (o.mCategories != null) {
+            this.mCategories = new ArraySet<>(o.mCategories);
+        }
+
+        if (copyMode != COPY_MODE_FILTER) {
+            this.mFlags = o.mFlags;
+            this.mContentUserHint = o.mContentUserHint;
+            this.mLaunchToken = o.mLaunchToken;
+            if (o.mSourceBounds != null) {
+                this.mSourceBounds = new Rect(o.mSourceBounds);
+            }
+            if (o.mSelector != null) {
+                this.mSelector = new Intent(o.mSelector);
+            }
+
+            if (copyMode != COPY_MODE_HISTORY) {
+                if (o.mExtras != null) {
+                    this.mExtras = new Bundle(o.mExtras);
+                }
+                if (o.mClipData != null) {
+                    this.mClipData = new ClipData(o.mClipData);
+                }
+            } else {
+                if (o.mExtras != null && !o.mExtras.isDefinitelyEmpty()) {
+                    this.mExtras = Bundle.STRIPPED;
+                }
+
+                // Also set "stripped" clip data when we ever log mClipData in the (broadcast)
+                // history.
+            }
+        }
+    }
+
+    @Override
+    public Object clone() {
+        return new Intent(this);
+    }
+
+    /**
+     * Make a clone of only the parts of the Intent that are relevant for
+     * filter matching: the action, data, type, component, and categories.
+     */
+    public @NonNull Intent cloneFilter() {
+        return new Intent(this, COPY_MODE_FILTER);
+    }
+
+    /**
+     * Create an intent with a given action.  All other fields (data, type,
+     * class) are null.  Note that the action <em>must</em> be in a
+     * namespace because Intents are used globally in the system -- for
+     * example the system VIEW action is android.intent.action.VIEW; an
+     * application's custom action would be something like
+     * com.google.app.myapp.CUSTOM_ACTION.
+     *
+     * @param action The Intent action, such as ACTION_VIEW.
+     */
+    public Intent(String action) {
+        setAction(action);
+    }
+
+    /**
+     * Create an intent with a given action and for a given data url.  Note
+     * that the action <em>must</em> be in a namespace because Intents are
+     * used globally in the system -- for example the system VIEW action is
+     * android.intent.action.VIEW; an application's custom action would be
+     * something like com.google.app.myapp.CUSTOM_ACTION.
+     *
+     * <p><em>Note: scheme and host name matching in the Android framework is
+     * case-sensitive, unlike the formal RFC.  As a result,
+     * you should always ensure that you write your Uri with these elements
+     * using lower case letters, and normalize any Uris you receive from
+     * outside of Android to ensure the scheme and host is lower case.</em></p>
+     *
+     * @param action The Intent action, such as ACTION_VIEW.
+     * @param uri The Intent data URI.
+     */
+    public Intent(String action, Uri uri) {
+        setAction(action);
+        mData = uri;
+    }
+
+    /**
+     * Create an intent for a specific component.  All other fields (action, data,
+     * type, class) are null, though they can be modified later with explicit
+     * calls.  This provides a convenient way to create an intent that is
+     * intended to execute a hard-coded class name, rather than relying on the
+     * system to find an appropriate class for you; see {@link #setComponent}
+     * for more information on the repercussions of this.
+     *
+     * @param packageContext A Context of the application package implementing
+     * this class.
+     * @param cls The component class that is to be used for the intent.
+     *
+     * @see #setClass
+     * @see #setComponent
+     * @see #Intent(String, android.net.Uri , Context, Class)
+     */
+    public Intent(Context packageContext, Class<?> cls) {
+        mComponent = new ComponentName(packageContext, cls);
+    }
+
+    /**
+     * Create an intent for a specific component with a specified action and data.
+     * This is equivalent to using {@link #Intent(String, android.net.Uri)} to
+     * construct the Intent and then calling {@link #setClass} to set its
+     * class.
+     *
+     * <p><em>Note: scheme and host name matching in the Android framework is
+     * case-sensitive, unlike the formal RFC.  As a result,
+     * you should always ensure that you write your Uri with these elements
+     * using lower case letters, and normalize any Uris you receive from
+     * outside of Android to ensure the scheme and host is lower case.</em></p>
+     *
+     * @param action The Intent action, such as ACTION_VIEW.
+     * @param uri The Intent data URI.
+     * @param packageContext A Context of the application package implementing
+     * this class.
+     * @param cls The component class that is to be used for the intent.
+     *
+     * @see #Intent(String, android.net.Uri)
+     * @see #Intent(Context, Class)
+     * @see #setClass
+     * @see #setComponent
+     */
+    public Intent(String action, Uri uri,
+            Context packageContext, Class<?> cls) {
+        setAction(action);
+        mData = uri;
+        mComponent = new ComponentName(packageContext, cls);
+    }
+
+    /**
+     * Create an intent to launch the main (root) activity of a task.  This
+     * is the Intent that is started when the application's is launched from
+     * Home.  For anything else that wants to launch an application in the
+     * same way, it is important that they use an Intent structured the same
+     * way, and can use this function to ensure this is the case.
+     *
+     * <p>The returned Intent has the given Activity component as its explicit
+     * component, {@link #ACTION_MAIN} as its action, and includes the
+     * category {@link #CATEGORY_LAUNCHER}.  This does <em>not</em> have
+     * {@link #FLAG_ACTIVITY_NEW_TASK} set, though typically you will want
+     * to do that through {@link #addFlags(int)} on the returned Intent.
+     *
+     * @param mainActivity The main activity component that this Intent will
+     * launch.
+     * @return Returns a newly created Intent that can be used to launch the
+     * activity as a main application entry.
+     *
+     * @see #setClass
+     * @see #setComponent
+     */
+    public static Intent makeMainActivity(ComponentName mainActivity) {
+        Intent intent = new Intent(ACTION_MAIN);
+        intent.setComponent(mainActivity);
+        intent.addCategory(CATEGORY_LAUNCHER);
+        return intent;
+    }
+
+    /**
+     * Make an Intent for the main activity of an application, without
+     * specifying a specific activity to run but giving a selector to find
+     * the activity.  This results in a final Intent that is structured
+     * the same as when the application is launched from
+     * Home.  For anything else that wants to launch an application in the
+     * same way, it is important that they use an Intent structured the same
+     * way, and can use this function to ensure this is the case.
+     *
+     * <p>The returned Intent has {@link #ACTION_MAIN} as its action, and includes the
+     * category {@link #CATEGORY_LAUNCHER}.  This does <em>not</em> have
+     * {@link #FLAG_ACTIVITY_NEW_TASK} set, though typically you will want
+     * to do that through {@link #addFlags(int)} on the returned Intent.
+     *
+     * @param selectorAction The action name of the Intent's selector.
+     * @param selectorCategory The name of a category to add to the Intent's
+     * selector.
+     * @return Returns a newly created Intent that can be used to launch the
+     * activity as a main application entry.
+     *
+     * @see #setSelector(Intent)
+     */
+    public static Intent makeMainSelectorActivity(String selectorAction,
+            String selectorCategory) {
+        Intent intent = new Intent(ACTION_MAIN);
+        intent.addCategory(CATEGORY_LAUNCHER);
+        Intent selector = new Intent();
+        selector.setAction(selectorAction);
+        selector.addCategory(selectorCategory);
+        intent.setSelector(selector);
+        return intent;
+    }
+
+    /**
+     * Make an Intent that can be used to re-launch an application's task
+     * in its base state.  This is like {@link #makeMainActivity(ComponentName)},
+     * but also sets the flags {@link #FLAG_ACTIVITY_NEW_TASK} and
+     * {@link #FLAG_ACTIVITY_CLEAR_TASK}.
+     *
+     * @param mainActivity The activity component that is the root of the
+     * task; this is the activity that has been published in the application's
+     * manifest as the main launcher icon.
+     *
+     * @return Returns a newly created Intent that can be used to relaunch the
+     * activity's task in its root state.
+     */
+    public static Intent makeRestartActivityTask(ComponentName mainActivity) {
+        Intent intent = makeMainActivity(mainActivity);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+        return intent;
+    }
+
+    /**
+     * Call {@link #parseUri} with 0 flags.
+     * @deprecated Use {@link #parseUri} instead.
+     */
+    @Deprecated
+    public static Intent getIntent(String uri) throws URISyntaxException {
+        return parseUri(uri, 0);
+    }
+
+    /**
+     * Create an intent from a URI.  This URI may encode the action,
+     * category, and other intent fields, if it was returned by
+     * {@link #toUri}.  If the Intent was not generate by toUri(), its data
+     * will be the entire URI and its action will be ACTION_VIEW.
+     *
+     * <p>The URI given here must not be relative -- that is, it must include
+     * the scheme and full path.
+     *
+     * @param uri The URI to turn into an Intent.
+     * @param flags Additional processing flags.
+     *
+     * @return Intent The newly created Intent object.
+     *
+     * @throws URISyntaxException Throws URISyntaxError if the basic URI syntax
+     * it bad (as parsed by the Uri class) or the Intent data within the
+     * URI is invalid.
+     *
+     * @see #toUri
+     */
+    public static Intent parseUri(String uri, @UriFlags int flags) throws URISyntaxException {
+        int i = 0;
+        try {
+            final boolean androidApp = uri.startsWith("android-app:");
+
+            // Validate intent scheme if requested.
+            if ((flags&(URI_INTENT_SCHEME|URI_ANDROID_APP_SCHEME)) != 0) {
+                if (!uri.startsWith("intent:") && !androidApp) {
+                    Intent intent = new Intent(ACTION_VIEW);
+                    try {
+                        intent.setData(Uri.parse(uri));
+                    } catch (IllegalArgumentException e) {
+                        throw new URISyntaxException(uri, e.getMessage());
+                    }
+                    return intent;
+                }
+            }
+
+            i = uri.lastIndexOf("#");
+            // simple case
+            if (i == -1) {
+                if (!androidApp) {
+                    return new Intent(ACTION_VIEW, Uri.parse(uri));
+                }
+
+            // old format Intent URI
+            } else if (!uri.startsWith("#Intent;", i)) {
+                if (!androidApp) {
+                    return getIntentOld(uri, flags);
+                } else {
+                    i = -1;
+                }
+            }
+
+            // new format
+            Intent intent = new Intent(ACTION_VIEW);
+            Intent baseIntent = intent;
+            boolean explicitAction = false;
+            boolean inSelector = false;
+
+            // fetch data part, if present
+            String scheme = null;
+            String data;
+            if (i >= 0) {
+                data = uri.substring(0, i);
+                i += 8; // length of "#Intent;"
+            } else {
+                data = uri;
+            }
+
+            // loop over contents of Intent, all name=value;
+            while (i >= 0 && !uri.startsWith("end", i)) {
+                int eq = uri.indexOf('=', i);
+                if (eq < 0) eq = i-1;
+                int semi = uri.indexOf(';', i);
+                String value = eq < semi ? Uri.decode(uri.substring(eq + 1, semi)) : "";
+
+                // action
+                if (uri.startsWith("action=", i)) {
+                    intent.setAction(value);
+                    if (!inSelector) {
+                        explicitAction = true;
+                    }
+                }
+
+                // categories
+                else if (uri.startsWith("category=", i)) {
+                    intent.addCategory(value);
+                }
+
+                // type
+                else if (uri.startsWith("type=", i)) {
+                    intent.mType = value;
+                }
+
+                // identifier
+                else if (uri.startsWith("identifier=", i)) {
+                    intent.mIdentifier = value;
+                }
+
+                // launch flags
+                else if (uri.startsWith("launchFlags=", i)) {
+                    intent.mFlags = Integer.decode(value).intValue();
+                    if ((flags& URI_ALLOW_UNSAFE) == 0) {
+                        intent.mFlags &= ~IMMUTABLE_FLAGS;
+                    }
+                }
+
+                // package
+                else if (uri.startsWith("package=", i)) {
+                    intent.mPackage = value;
+                }
+
+                // component
+                else if (uri.startsWith("component=", i)) {
+                    intent.mComponent = ComponentName.unflattenFromString(value);
+                }
+
+                // scheme
+                else if (uri.startsWith("scheme=", i)) {
+                    if (inSelector) {
+                        intent.mData = Uri.parse(value + ":");
+                    } else {
+                        scheme = value;
+                    }
+                }
+
+                // source bounds
+                else if (uri.startsWith("sourceBounds=", i)) {
+                    intent.mSourceBounds = Rect.unflattenFromString(value);
+                }
+
+                // selector
+                else if (semi == (i+3) && uri.startsWith("SEL", i)) {
+                    intent = new Intent();
+                    inSelector = true;
+                }
+
+                // extra
+                else {
+                    String key = Uri.decode(uri.substring(i + 2, eq));
+                    // create Bundle if it doesn't already exist
+                    if (intent.mExtras == null) intent.mExtras = new Bundle();
+                    Bundle b = intent.mExtras;
+                    // add EXTRA
+                    if      (uri.startsWith("S.", i)) b.putString(key, value);
+                    else if (uri.startsWith("B.", i)) b.putBoolean(key, Boolean.parseBoolean(value));
+                    else if (uri.startsWith("b.", i)) b.putByte(key, Byte.parseByte(value));
+                    else if (uri.startsWith("c.", i)) b.putChar(key, value.charAt(0));
+                    else if (uri.startsWith("d.", i)) b.putDouble(key, Double.parseDouble(value));
+                    else if (uri.startsWith("f.", i)) b.putFloat(key, Float.parseFloat(value));
+                    else if (uri.startsWith("i.", i)) b.putInt(key, Integer.parseInt(value));
+                    else if (uri.startsWith("l.", i)) b.putLong(key, Long.parseLong(value));
+                    else if (uri.startsWith("s.", i)) b.putShort(key, Short.parseShort(value));
+                    else throw new URISyntaxException(uri, "unknown EXTRA type", i);
+                }
+
+                // move to the next item
+                i = semi + 1;
+            }
+
+            if (inSelector) {
+                // The Intent had a selector; fix it up.
+                if (baseIntent.mPackage == null) {
+                    baseIntent.setSelector(intent);
+                }
+                intent = baseIntent;
+            }
+
+            if (data != null) {
+                if (data.startsWith("intent:")) {
+                    data = data.substring(7);
+                    if (scheme != null) {
+                        data = scheme + ':' + data;
+                    }
+                } else if (data.startsWith("android-app:")) {
+                    if (data.charAt(12) == '/' && data.charAt(13) == '/') {
+                        // Correctly formed android-app, first part is package name.
+                        int end = data.indexOf('/', 14);
+                        if (end < 0) {
+                            // All we have is a package name.
+                            intent.mPackage = data.substring(14);
+                            if (!explicitAction) {
+                                intent.setAction(ACTION_MAIN);
+                            }
+                            data = "";
+                        } else {
+                            // Target the Intent at the given package name always.
+                            String authority = null;
+                            intent.mPackage = data.substring(14, end);
+                            int newEnd;
+                            if ((end+1) < data.length()) {
+                                if ((newEnd=data.indexOf('/', end+1)) >= 0) {
+                                    // Found a scheme, remember it.
+                                    scheme = data.substring(end+1, newEnd);
+                                    end = newEnd;
+                                    if (end < data.length() && (newEnd=data.indexOf('/', end+1)) >= 0) {
+                                        // Found a authority, remember it.
+                                        authority = data.substring(end+1, newEnd);
+                                        end = newEnd;
+                                    }
+                                } else {
+                                    // All we have is a scheme.
+                                    scheme = data.substring(end+1);
+                                }
+                            }
+                            if (scheme == null) {
+                                // If there was no scheme, then this just targets the package.
+                                if (!explicitAction) {
+                                    intent.setAction(ACTION_MAIN);
+                                }
+                                data = "";
+                            } else if (authority == null) {
+                                data = scheme + ":";
+                            } else {
+                                data = scheme + "://" + authority + data.substring(end);
+                            }
+                        }
+                    } else {
+                        data = "";
+                    }
+                }
+
+                if (data.length() > 0) {
+                    try {
+                        intent.mData = Uri.parse(data);
+                    } catch (IllegalArgumentException e) {
+                        throw new URISyntaxException(uri, e.getMessage());
+                    }
+                }
+            }
+
+            return intent;
+
+        } catch (IndexOutOfBoundsException e) {
+            throw new URISyntaxException(uri, "illegal Intent URI format", i);
+        }
+    }
+
+    public static Intent getIntentOld(String uri) throws URISyntaxException {
+        return getIntentOld(uri, 0);
+    }
+
+    private static Intent getIntentOld(String uri, int flags) throws URISyntaxException {
+        Intent intent;
+
+        int i = uri.lastIndexOf('#');
+        if (i >= 0) {
+            String action = null;
+            final int intentFragmentStart = i;
+            boolean isIntentFragment = false;
+
+            i++;
+
+            if (uri.regionMatches(i, "action(", 0, 7)) {
+                isIntentFragment = true;
+                i += 7;
+                int j = uri.indexOf(')', i);
+                action = uri.substring(i, j);
+                i = j + 1;
+            }
+
+            intent = new Intent(action);
+
+            if (uri.regionMatches(i, "categories(", 0, 11)) {
+                isIntentFragment = true;
+                i += 11;
+                int j = uri.indexOf(')', i);
+                while (i < j) {
+                    int sep = uri.indexOf('!', i);
+                    if (sep < 0 || sep > j) sep = j;
+                    if (i < sep) {
+                        intent.addCategory(uri.substring(i, sep));
+                    }
+                    i = sep + 1;
+                }
+                i = j + 1;
+            }
+
+            if (uri.regionMatches(i, "type(", 0, 5)) {
+                isIntentFragment = true;
+                i += 5;
+                int j = uri.indexOf(')', i);
+                intent.mType = uri.substring(i, j);
+                i = j + 1;
+            }
+
+            if (uri.regionMatches(i, "launchFlags(", 0, 12)) {
+                isIntentFragment = true;
+                i += 12;
+                int j = uri.indexOf(')', i);
+                intent.mFlags = Integer.decode(uri.substring(i, j)).intValue();
+                if ((flags& URI_ALLOW_UNSAFE) == 0) {
+                    intent.mFlags &= ~IMMUTABLE_FLAGS;
+                }
+                i = j + 1;
+            }
+
+            if (uri.regionMatches(i, "component(", 0, 10)) {
+                isIntentFragment = true;
+                i += 10;
+                int j = uri.indexOf(')', i);
+                int sep = uri.indexOf('!', i);
+                if (sep >= 0 && sep < j) {
+                    String pkg = uri.substring(i, sep);
+                    String cls = uri.substring(sep + 1, j);
+                    intent.mComponent = new ComponentName(pkg, cls);
+                }
+                i = j + 1;
+            }
+
+            if (uri.regionMatches(i, "extras(", 0, 7)) {
+                isIntentFragment = true;
+                i += 7;
+
+                final int closeParen = uri.indexOf(')', i);
+                if (closeParen == -1) throw new URISyntaxException(uri,
+                        "EXTRA missing trailing ')'", i);
+
+                while (i < closeParen) {
+                    // fetch the key value
+                    int j = uri.indexOf('=', i);
+                    if (j <= i + 1 || i >= closeParen) {
+                        throw new URISyntaxException(uri, "EXTRA missing '='", i);
+                    }
+                    char type = uri.charAt(i);
+                    i++;
+                    String key = uri.substring(i, j);
+                    i = j + 1;
+
+                    // get type-value
+                    j = uri.indexOf('!', i);
+                    if (j == -1 || j >= closeParen) j = closeParen;
+                    if (i >= j) throw new URISyntaxException(uri, "EXTRA missing '!'", i);
+                    String value = uri.substring(i, j);
+                    i = j;
+
+                    // create Bundle if it doesn't already exist
+                    if (intent.mExtras == null) intent.mExtras = new Bundle();
+
+                    // add item to bundle
+                    try {
+                        switch (type) {
+                            case 'S':
+                                intent.mExtras.putString(key, Uri.decode(value));
+                                break;
+                            case 'B':
+                                intent.mExtras.putBoolean(key, Boolean.parseBoolean(value));
+                                break;
+                            case 'b':
+                                intent.mExtras.putByte(key, Byte.parseByte(value));
+                                break;
+                            case 'c':
+                                intent.mExtras.putChar(key, Uri.decode(value).charAt(0));
+                                break;
+                            case 'd':
+                                intent.mExtras.putDouble(key, Double.parseDouble(value));
+                                break;
+                            case 'f':
+                                intent.mExtras.putFloat(key, Float.parseFloat(value));
+                                break;
+                            case 'i':
+                                intent.mExtras.putInt(key, Integer.parseInt(value));
+                                break;
+                            case 'l':
+                                intent.mExtras.putLong(key, Long.parseLong(value));
+                                break;
+                            case 's':
+                                intent.mExtras.putShort(key, Short.parseShort(value));
+                                break;
+                            default:
+                                throw new URISyntaxException(uri, "EXTRA has unknown type", i);
+                        }
+                    } catch (NumberFormatException e) {
+                        throw new URISyntaxException(uri, "EXTRA value can't be parsed", i);
+                    }
+
+                    char ch = uri.charAt(i);
+                    if (ch == ')') break;
+                    if (ch != '!') throw new URISyntaxException(uri, "EXTRA missing '!'", i);
+                    i++;
+                }
+            }
+
+            if (isIntentFragment) {
+                intent.mData = Uri.parse(uri.substring(0, intentFragmentStart));
+            } else {
+                intent.mData = Uri.parse(uri);
+            }
+
+            if (intent.mAction == null) {
+                // By default, if no action is specified, then use VIEW.
+                intent.mAction = ACTION_VIEW;
+            }
+
+        } else {
+            intent = new Intent(ACTION_VIEW, Uri.parse(uri));
+        }
+
+        return intent;
+    }
+
+    /** @hide */
+    public interface CommandOptionHandler {
+        boolean handleOption(String opt, ShellCommand cmd);
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage
+    public static Intent parseCommandArgs(ShellCommand cmd, CommandOptionHandler optionHandler)
+            throws URISyntaxException {
+        Intent intent = new Intent();
+        Intent baseIntent = intent;
+        boolean hasIntentInfo = false;
+
+        Uri data = null;
+        String type = null;
+
+        String opt;
+        while ((opt=cmd.getNextOption()) != null) {
+            switch (opt) {
+                case "-a":
+                    intent.setAction(cmd.getNextArgRequired());
+                    if (intent == baseIntent) {
+                        hasIntentInfo = true;
+                    }
+                    break;
+                case "-d":
+                    data = Uri.parse(cmd.getNextArgRequired());
+                    if (intent == baseIntent) {
+                        hasIntentInfo = true;
+                    }
+                    break;
+                case "-t":
+                    type = cmd.getNextArgRequired();
+                    if (intent == baseIntent) {
+                        hasIntentInfo = true;
+                    }
+                    break;
+                case "-i":
+                    intent.setIdentifier(cmd.getNextArgRequired());
+                    if (intent == baseIntent) {
+                        hasIntentInfo = true;
+                    }
+                    break;
+                case "-c":
+                    intent.addCategory(cmd.getNextArgRequired());
+                    if (intent == baseIntent) {
+                        hasIntentInfo = true;
+                    }
+                    break;
+                case "-e":
+                case "--es": {
+                    String key = cmd.getNextArgRequired();
+                    String value = cmd.getNextArgRequired();
+                    intent.putExtra(key, value);
+                }
+                break;
+                case "--esn": {
+                    String key = cmd.getNextArgRequired();
+                    intent.putExtra(key, (String) null);
+                }
+                break;
+                case "--ei": {
+                    String key = cmd.getNextArgRequired();
+                    String value = cmd.getNextArgRequired();
+                    intent.putExtra(key, Integer.decode(value));
+                }
+                break;
+                case "--eu": {
+                    String key = cmd.getNextArgRequired();
+                    String value = cmd.getNextArgRequired();
+                    intent.putExtra(key, Uri.parse(value));
+                }
+                break;
+                case "--ecn": {
+                    String key = cmd.getNextArgRequired();
+                    String value = cmd.getNextArgRequired();
+                    ComponentName cn = ComponentName.unflattenFromString(value);
+                    if (cn == null)
+                        throw new IllegalArgumentException("Bad component name: " + value);
+                    intent.putExtra(key, cn);
+                }
+                break;
+                case "--eia": {
+                    String key = cmd.getNextArgRequired();
+                    String value = cmd.getNextArgRequired();
+                    String[] strings = value.split(",");
+                    int[] list = new int[strings.length];
+                    for (int i = 0; i < strings.length; i++) {
+                        list[i] = Integer.decode(strings[i]);
+                    }
+                    intent.putExtra(key, list);
+                }
+                break;
+                case "--eial": {
+                    String key = cmd.getNextArgRequired();
+                    String value = cmd.getNextArgRequired();
+                    String[] strings = value.split(",");
+                    ArrayList<Integer> list = new ArrayList<>(strings.length);
+                    for (int i = 0; i < strings.length; i++) {
+                        list.add(Integer.decode(strings[i]));
+                    }
+                    intent.putExtra(key, list);
+                }
+                break;
+                case "--el": {
+                    String key = cmd.getNextArgRequired();
+                    String value = cmd.getNextArgRequired();
+                    intent.putExtra(key, Long.valueOf(value));
+                }
+                break;
+                case "--ela": {
+                    String key = cmd.getNextArgRequired();
+                    String value = cmd.getNextArgRequired();
+                    String[] strings = value.split(",");
+                    long[] list = new long[strings.length];
+                    for (int i = 0; i < strings.length; i++) {
+                        list[i] = Long.valueOf(strings[i]);
+                    }
+                    intent.putExtra(key, list);
+                    hasIntentInfo = true;
+                }
+                break;
+                case "--elal": {
+                    String key = cmd.getNextArgRequired();
+                    String value = cmd.getNextArgRequired();
+                    String[] strings = value.split(",");
+                    ArrayList<Long> list = new ArrayList<>(strings.length);
+                    for (int i = 0; i < strings.length; i++) {
+                        list.add(Long.valueOf(strings[i]));
+                    }
+                    intent.putExtra(key, list);
+                    hasIntentInfo = true;
+                }
+                break;
+                case "--ef": {
+                    String key = cmd.getNextArgRequired();
+                    String value = cmd.getNextArgRequired();
+                    intent.putExtra(key, Float.valueOf(value));
+                    hasIntentInfo = true;
+                }
+                break;
+                case "--efa": {
+                    String key = cmd.getNextArgRequired();
+                    String value = cmd.getNextArgRequired();
+                    String[] strings = value.split(",");
+                    float[] list = new float[strings.length];
+                    for (int i = 0; i < strings.length; i++) {
+                        list[i] = Float.valueOf(strings[i]);
+                    }
+                    intent.putExtra(key, list);
+                    hasIntentInfo = true;
+                }
+                break;
+                case "--efal": {
+                    String key = cmd.getNextArgRequired();
+                    String value = cmd.getNextArgRequired();
+                    String[] strings = value.split(",");
+                    ArrayList<Float> list = new ArrayList<>(strings.length);
+                    for (int i = 0; i < strings.length; i++) {
+                        list.add(Float.valueOf(strings[i]));
+                    }
+                    intent.putExtra(key, list);
+                    hasIntentInfo = true;
+                }
+                break;
+                case "--esa": {
+                    String key = cmd.getNextArgRequired();
+                    String value = cmd.getNextArgRequired();
+                    // Split on commas unless they are preceeded by an escape.
+                    // The escape character must be escaped for the string and
+                    // again for the regex, thus four escape characters become one.
+                    String[] strings = value.split("(?<!\\\\),");
+                    intent.putExtra(key, strings);
+                    hasIntentInfo = true;
+                }
+                break;
+                case "--esal": {
+                    String key = cmd.getNextArgRequired();
+                    String value = cmd.getNextArgRequired();
+                    // Split on commas unless they are preceeded by an escape.
+                    // The escape character must be escaped for the string and
+                    // again for the regex, thus four escape characters become one.
+                    String[] strings = value.split("(?<!\\\\),");
+                    ArrayList<String> list = new ArrayList<>(strings.length);
+                    for (int i = 0; i < strings.length; i++) {
+                        list.add(strings[i]);
+                    }
+                    intent.putExtra(key, list);
+                    hasIntentInfo = true;
+                }
+                break;
+                case "--ez": {
+                    String key = cmd.getNextArgRequired();
+                    String value = cmd.getNextArgRequired().toLowerCase();
+                    // Boolean.valueOf() results in false for anything that is not "true", which is
+                    // error-prone in shell commands
+                    boolean arg;
+                    if ("true".equals(value) || "t".equals(value)) {
+                        arg = true;
+                    } else if ("false".equals(value) || "f".equals(value)) {
+                        arg = false;
+                    } else {
+                        try {
+                            arg = Integer.decode(value) != 0;
+                        } catch (NumberFormatException ex) {
+                            throw new IllegalArgumentException("Invalid boolean value: " + value);
+                        }
+                    }
+
+                    intent.putExtra(key, arg);
+                }
+                break;
+                case "-n": {
+                    String str = cmd.getNextArgRequired();
+                    ComponentName cn = ComponentName.unflattenFromString(str);
+                    if (cn == null)
+                        throw new IllegalArgumentException("Bad component name: " + str);
+                    intent.setComponent(cn);
+                    if (intent == baseIntent) {
+                        hasIntentInfo = true;
+                    }
+                }
+                break;
+                case "-p": {
+                    String str = cmd.getNextArgRequired();
+                    intent.setPackage(str);
+                    if (intent == baseIntent) {
+                        hasIntentInfo = true;
+                    }
+                }
+                break;
+                case "-f":
+                    String str = cmd.getNextArgRequired();
+                    intent.setFlags(Integer.decode(str).intValue());
+                    break;
+                case "--grant-read-uri-permission":
+                    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+                    break;
+                case "--grant-write-uri-permission":
+                    intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+                    break;
+                case "--grant-persistable-uri-permission":
+                    intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
+                    break;
+                case "--grant-prefix-uri-permission":
+                    intent.addFlags(Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
+                    break;
+                case "--exclude-stopped-packages":
+                    intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);
+                    break;
+                case "--include-stopped-packages":
+                    intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
+                    break;
+                case "--debug-log-resolution":
+                    intent.addFlags(Intent.FLAG_DEBUG_LOG_RESOLUTION);
+                    break;
+                case "--activity-brought-to-front":
+                    intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
+                    break;
+                case "--activity-clear-top":
+                    intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+                    break;
+                case "--activity-clear-when-task-reset":
+                    intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
+                    break;
+                case "--activity-exclude-from-recents":
+                    intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+                    break;
+                case "--activity-launched-from-history":
+                    intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY);
+                    break;
+                case "--activity-multiple-task":
+                    intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+                    break;
+                case "--activity-no-animation":
+                    intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
+                    break;
+                case "--activity-no-history":
+                    intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
+                    break;
+                case "--activity-no-user-action":
+                    intent.addFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION);
+                    break;
+                case "--activity-previous-is-top":
+                    intent.addFlags(Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
+                    break;
+                case "--activity-reorder-to-front":
+                    intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
+                    break;
+                case "--activity-reset-task-if-needed":
+                    intent.addFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+                    break;
+                case "--activity-single-top":
+                    intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
+                    break;
+                case "--activity-clear-task":
+                    intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
+                    break;
+                case "--activity-task-on-home":
+                    intent.addFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME);
+                    break;
+                case "--activity-match-external":
+                    intent.addFlags(Intent.FLAG_ACTIVITY_MATCH_EXTERNAL);
+                    break;
+                case "--receiver-registered-only":
+                    intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+                    break;
+                case "--receiver-replace-pending":
+                    intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+                    break;
+                case "--receiver-foreground":
+                    intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+                    break;
+                case "--receiver-no-abort":
+                    intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT);
+                    break;
+                case "--receiver-include-background":
+                    intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+                    break;
+                case "--selector":
+                    intent.setDataAndType(data, type);
+                    intent = new Intent();
+                    break;
+                default:
+                    if (optionHandler != null && optionHandler.handleOption(opt, cmd)) {
+                        // Okay, caller handled this option.
+                    } else {
+                        throw new IllegalArgumentException("Unknown option: " + opt);
+                    }
+                    break;
+            }
+        }
+        intent.setDataAndType(data, type);
+
+        final boolean hasSelector = intent != baseIntent;
+        if (hasSelector) {
+            // A selector was specified; fix up.
+            baseIntent.setSelector(intent);
+            intent = baseIntent;
+        }
+
+        String arg = cmd.getNextArg();
+        baseIntent = null;
+        if (arg == null) {
+            if (hasSelector) {
+                // If a selector has been specified, and no arguments
+                // have been supplied for the main Intent, then we can
+                // assume it is ACTION_MAIN CATEGORY_LAUNCHER; we don't
+                // need to have a component name specified yet, the
+                // selector will take care of that.
+                baseIntent = new Intent(Intent.ACTION_MAIN);
+                baseIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+            }
+        } else if (arg.indexOf(':') >= 0) {
+            // The argument is a URI.  Fully parse it, and use that result
+            // to fill in any data not specified so far.
+            baseIntent = Intent.parseUri(arg, Intent.URI_INTENT_SCHEME
+                    | Intent.URI_ANDROID_APP_SCHEME | Intent.URI_ALLOW_UNSAFE);
+        } else if (arg.indexOf('/') >= 0) {
+            // The argument is a component name.  Build an Intent to launch
+            // it.
+            baseIntent = new Intent(Intent.ACTION_MAIN);
+            baseIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+            baseIntent.setComponent(ComponentName.unflattenFromString(arg));
+        } else {
+            // Assume the argument is a package name.
+            baseIntent = new Intent(Intent.ACTION_MAIN);
+            baseIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+            baseIntent.setPackage(arg);
+        }
+        if (baseIntent != null) {
+            Bundle extras = intent.getExtras();
+            intent.replaceExtras((Bundle)null);
+            Bundle uriExtras = baseIntent.getExtras();
+            baseIntent.replaceExtras((Bundle)null);
+            if (intent.getAction() != null && baseIntent.getCategories() != null) {
+                HashSet<String> cats = new HashSet<String>(baseIntent.getCategories());
+                for (String c : cats) {
+                    baseIntent.removeCategory(c);
+                }
+            }
+            intent.fillIn(baseIntent, Intent.FILL_IN_COMPONENT | Intent.FILL_IN_SELECTOR);
+            if (extras == null) {
+                extras = uriExtras;
+            } else if (uriExtras != null) {
+                uriExtras.putAll(extras);
+                extras = uriExtras;
+            }
+            intent.replaceExtras(extras);
+            hasIntentInfo = true;
+        }
+
+        if (!hasIntentInfo) throw new IllegalArgumentException("No intent supplied");
+        return intent;
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage
+    public static void printIntentArgsHelp(PrintWriter pw, String prefix) {
+        final String[] lines = new String[] {
+                "<INTENT> specifications include these flags and arguments:",
+                "    [-a <ACTION>] [-d <DATA_URI>] [-t <MIME_TYPE>] [-i <IDENTIFIER>]",
+                "    [-c <CATEGORY> [-c <CATEGORY>] ...]",
+                "    [-n <COMPONENT_NAME>]",
+                "    [-e|--es <EXTRA_KEY> <EXTRA_STRING_VALUE> ...]",
+                "    [--esn <EXTRA_KEY> ...]",
+                "    [--ez <EXTRA_KEY> <EXTRA_BOOLEAN_VALUE> ...]",
+                "    [--ei <EXTRA_KEY> <EXTRA_INT_VALUE> ...]",
+                "    [--el <EXTRA_KEY> <EXTRA_LONG_VALUE> ...]",
+                "    [--ef <EXTRA_KEY> <EXTRA_FLOAT_VALUE> ...]",
+                "    [--eu <EXTRA_KEY> <EXTRA_URI_VALUE> ...]",
+                "    [--ecn <EXTRA_KEY> <EXTRA_COMPONENT_NAME_VALUE>]",
+                "    [--eia <EXTRA_KEY> <EXTRA_INT_VALUE>[,<EXTRA_INT_VALUE...]]",
+                "        (mutiple extras passed as Integer[])",
+                "    [--eial <EXTRA_KEY> <EXTRA_INT_VALUE>[,<EXTRA_INT_VALUE...]]",
+                "        (mutiple extras passed as List<Integer>)",
+                "    [--ela <EXTRA_KEY> <EXTRA_LONG_VALUE>[,<EXTRA_LONG_VALUE...]]",
+                "        (mutiple extras passed as Long[])",
+                "    [--elal <EXTRA_KEY> <EXTRA_LONG_VALUE>[,<EXTRA_LONG_VALUE...]]",
+                "        (mutiple extras passed as List<Long>)",
+                "    [--efa <EXTRA_KEY> <EXTRA_FLOAT_VALUE>[,<EXTRA_FLOAT_VALUE...]]",
+                "        (mutiple extras passed as Float[])",
+                "    [--efal <EXTRA_KEY> <EXTRA_FLOAT_VALUE>[,<EXTRA_FLOAT_VALUE...]]",
+                "        (mutiple extras passed as List<Float>)",
+                "    [--esa <EXTRA_KEY> <EXTRA_STRING_VALUE>[,<EXTRA_STRING_VALUE...]]",
+                "        (mutiple extras passed as String[]; to embed a comma into a string,",
+                "         escape it using \"\\,\")",
+                "    [--esal <EXTRA_KEY> <EXTRA_STRING_VALUE>[,<EXTRA_STRING_VALUE...]]",
+                "        (mutiple extras passed as List<String>; to embed a comma into a string,",
+                "         escape it using \"\\,\")",
+                "    [-f <FLAG>]",
+                "    [--grant-read-uri-permission] [--grant-write-uri-permission]",
+                "    [--grant-persistable-uri-permission] [--grant-prefix-uri-permission]",
+                "    [--debug-log-resolution] [--exclude-stopped-packages]",
+                "    [--include-stopped-packages]",
+                "    [--activity-brought-to-front] [--activity-clear-top]",
+                "    [--activity-clear-when-task-reset] [--activity-exclude-from-recents]",
+                "    [--activity-launched-from-history] [--activity-multiple-task]",
+                "    [--activity-no-animation] [--activity-no-history]",
+                "    [--activity-no-user-action] [--activity-previous-is-top]",
+                "    [--activity-reorder-to-front] [--activity-reset-task-if-needed]",
+                "    [--activity-single-top] [--activity-clear-task]",
+                "    [--activity-task-on-home] [--activity-match-external]",
+                "    [--receiver-registered-only] [--receiver-replace-pending]",
+                "    [--receiver-foreground] [--receiver-no-abort]",
+                "    [--receiver-include-background]",
+                "    [--selector]",
+                "    [<URI> | <PACKAGE> | <COMPONENT>]"
+        };
+        for (String line : lines) {
+            pw.print(prefix);
+            pw.println(line);
+        }
+    }
+
+    /**
+     * Retrieve the general action to be performed, such as
+     * {@link #ACTION_VIEW}.  The action describes the general way the rest of
+     * the information in the intent should be interpreted -- most importantly,
+     * what to do with the data returned by {@link #getData}.
+     *
+     * @return The action of this intent or null if none is specified.
+     *
+     * @see #setAction
+     */
+    public @Nullable String getAction() {
+        return mAction;
+    }
+
+    /**
+     * Retrieve data this intent is operating on.  This URI specifies the name
+     * of the data; often it uses the content: scheme, specifying data in a
+     * content provider.  Other schemes may be handled by specific activities,
+     * such as http: by the web browser.
+     *
+     * @return The URI of the data this intent is targeting or null.
+     *
+     * @see #getScheme
+     * @see #setData
+     */
+    public @Nullable Uri getData() {
+        return mData;
+    }
+
+    /**
+     * The same as {@link #getData()}, but returns the URI as an encoded
+     * String.
+     */
+    public @Nullable String getDataString() {
+        return mData != null ? mData.toString() : null;
+    }
+
+    /**
+     * Return the scheme portion of the intent's data.  If the data is null or
+     * does not include a scheme, null is returned.  Otherwise, the scheme
+     * prefix without the final ':' is returned, i.e. "http".
+     *
+     * <p>This is the same as calling getData().getScheme() (and checking for
+     * null data).
+     *
+     * @return The scheme of this intent.
+     *
+     * @see #getData
+     */
+    public @Nullable String getScheme() {
+        return mData != null ? mData.getScheme() : null;
+    }
+
+    /**
+     * Retrieve any explicit MIME type included in the intent.  This is usually
+     * null, as the type is determined by the intent data.
+     *
+     * @return If a type was manually set, it is returned; else null is
+     *         returned.
+     *
+     * @see #resolveType(ContentResolver)
+     * @see #setType
+     */
+    public @Nullable String getType() {
+        return mType;
+    }
+
+    /**
+     * Return the MIME data type of this intent.  If the type field is
+     * explicitly set, that is simply returned.  Otherwise, if the data is set,
+     * the type of that data is returned.  If neither fields are set, a null is
+     * returned.
+     *
+     * @return The MIME type of this intent.
+     *
+     * @see #getType
+     * @see #resolveType(ContentResolver)
+     */
+    public @Nullable String resolveType(@NonNull Context context) {
+        return resolveType(context.getContentResolver());
+    }
+
+    /**
+     * Return the MIME data type of this intent.  If the type field is
+     * explicitly set, that is simply returned.  Otherwise, if the data is set,
+     * the type of that data is returned.  If neither fields are set, a null is
+     * returned.
+     *
+     * @param resolver A ContentResolver that can be used to determine the MIME
+     *                 type of the intent's data.
+     *
+     * @return The MIME type of this intent.
+     *
+     * @see #getType
+     * @see #resolveType(Context)
+     */
+    public @Nullable String resolveType(@NonNull ContentResolver resolver) {
+        if (mType != null) {
+            return mType;
+        }
+        if (mData != null) {
+            if ("content".equals(mData.getScheme())) {
+                return resolver.getType(mData);
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Return the MIME data type of this intent, only if it will be needed for
+     * intent resolution.  This is not generally useful for application code;
+     * it is used by the frameworks for communicating with back-end system
+     * services.
+     *
+     * @param resolver A ContentResolver that can be used to determine the MIME
+     *                 type of the intent's data.
+     *
+     * @return The MIME type of this intent, or null if it is unknown or not
+     *         needed.
+     */
+    public @Nullable String resolveTypeIfNeeded(@NonNull ContentResolver resolver) {
+        if (mComponent != null) {
+            return mType;
+        }
+        return resolveType(resolver);
+    }
+
+    /**
+     * Retrieve the identifier for this Intent.  If non-null, this is an arbitrary identity
+     * of the Intent to distinguish it from other Intents.
+     *
+     * @return The identifier of this intent or null if none is specified.
+     *
+     * @see #setIdentifier
+     */
+    public @Nullable String getIdentifier() {
+        return mIdentifier;
+    }
+
+    /**
+     * Check if a category exists in the intent.
+     *
+     * @param category The category to check.
+     *
+     * @return boolean True if the intent contains the category, else false.
+     *
+     * @see #getCategories
+     * @see #addCategory
+     */
+    public boolean hasCategory(String category) {
+        return mCategories != null && mCategories.contains(category);
+    }
+
+    /**
+     * Return the set of all categories in the intent.  If there are no categories,
+     * returns NULL.
+     *
+     * @return The set of categories you can examine.  Do not modify!
+     *
+     * @see #hasCategory
+     * @see #addCategory
+     */
+    public Set<String> getCategories() {
+        return mCategories;
+    }
+
+    /**
+     * Return the specific selector associated with this Intent.  If there is
+     * none, returns null.  See {@link #setSelector} for more information.
+     *
+     * @see #setSelector
+     */
+    public @Nullable Intent getSelector() {
+        return mSelector;
+    }
+
+    /**
+     * Return the {@link ClipData} associated with this Intent.  If there is
+     * none, returns null.  See {@link #setClipData} for more information.
+     *
+     * @see #setClipData
+     */
+    public @Nullable ClipData getClipData() {
+        return mClipData;
+    }
+
+    /** @hide */
+    public int getContentUserHint() {
+        return mContentUserHint;
+    }
+
+    /** @hide */
+    public String getLaunchToken() {
+        return mLaunchToken;
+    }
+
+    /** @hide */
+    public void setLaunchToken(String launchToken) {
+        mLaunchToken = launchToken;
+    }
+
+    /**
+     * Sets the ClassLoader that will be used when unmarshalling
+     * any Parcelable values from the extras of this Intent.
+     *
+     * @param loader a ClassLoader, or null to use the default loader
+     * at the time of unmarshalling.
+     */
+    public void setExtrasClassLoader(@Nullable ClassLoader loader) {
+        if (mExtras != null) {
+            mExtras.setClassLoader(loader);
+        }
+    }
+
+    /**
+     * Returns true if an extra value is associated with the given name.
+     * @param name the extra's name
+     * @return true if the given extra is present.
+     */
+    public boolean hasExtra(String name) {
+        return mExtras != null && mExtras.containsKey(name);
+    }
+
+    /**
+     * Returns true if the Intent's extras contain a parcelled file descriptor.
+     * @return true if the Intent contains a parcelled file descriptor.
+     */
+    public boolean hasFileDescriptors() {
+        return mExtras != null && mExtras.hasFileDescriptors();
+    }
+
+    /** {@hide} */
+    @UnsupportedAppUsage
+    public void setAllowFds(boolean allowFds) {
+        if (mExtras != null) {
+            mExtras.setAllowFds(allowFds);
+        }
+    }
+
+    /** {@hide} */
+    public void setDefusable(boolean defusable) {
+        if (mExtras != null) {
+            mExtras.setDefusable(defusable);
+        }
+    }
+
+    /**
+     * Retrieve extended data from the intent.
+     *
+     * @param name The name of the desired item.
+     *
+     * @return the value of an item previously added with putExtra(),
+     * or null if none was found.
+     *
+     * @deprecated
+     * @hide
+     */
+    @Deprecated
+    @UnsupportedAppUsage
+    public Object getExtra(String name) {
+        return getExtra(name, null);
+    }
+
+    /**
+     * Retrieve extended data from the intent.
+     *
+     * @param name The name of the desired item.
+     * @param defaultValue the value to be returned if no value of the desired
+     * type is stored with the given name.
+     *
+     * @return the value of an item previously added with putExtra(),
+     * or the default value if none was found.
+     *
+     * @see #putExtra(String, boolean)
+     */
+    public boolean getBooleanExtra(String name, boolean defaultValue) {
+        return mExtras == null ? defaultValue :
+            mExtras.getBoolean(name, defaultValue);
+    }
+
+    /**
+     * Retrieve extended data from the intent.
+     *
+     * @param name The name of the desired item.
+     * @param defaultValue the value to be returned if no value of the desired
+     * type is stored with the given name.
+     *
+     * @return the value of an item previously added with putExtra(),
+     * or the default value if none was found.
+     *
+     * @see #putExtra(String, byte)
+     */
+    public byte getByteExtra(String name, byte defaultValue) {
+        return mExtras == null ? defaultValue :
+            mExtras.getByte(name, defaultValue);
+    }
+
+    /**
+     * Retrieve extended data from the intent.
+     *
+     * @param name The name of the desired item.
+     * @param defaultValue the value to be returned if no value of the desired
+     * type is stored with the given name.
+     *
+     * @return the value of an item previously added with putExtra(),
+     * or the default value if none was found.
+     *
+     * @see #putExtra(String, short)
+     */
+    public short getShortExtra(String name, short defaultValue) {
+        return mExtras == null ? defaultValue :
+            mExtras.getShort(name, defaultValue);
+    }
+
+    /**
+     * Retrieve extended data from the intent.
+     *
+     * @param name The name of the desired item.
+     * @param defaultValue the value to be returned if no value of the desired
+     * type is stored with the given name.
+     *
+     * @return the value of an item previously added with putExtra(),
+     * or the default value if none was found.
+     *
+     * @see #putExtra(String, char)
+     */
+    public char getCharExtra(String name, char defaultValue) {
+        return mExtras == null ? defaultValue :
+            mExtras.getChar(name, defaultValue);
+    }
+
+    /**
+     * Retrieve extended data from the intent.
+     *
+     * @param name The name of the desired item.
+     * @param defaultValue the value to be returned if no value of the desired
+     * type is stored with the given name.
+     *
+     * @return the value of an item previously added with putExtra(),
+     * or the default value if none was found.
+     *
+     * @see #putExtra(String, int)
+     */
+    public int getIntExtra(String name, int defaultValue) {
+        return mExtras == null ? defaultValue :
+            mExtras.getInt(name, defaultValue);
+    }
+
+    /**
+     * Retrieve extended data from the intent.
+     *
+     * @param name The name of the desired item.
+     * @param defaultValue the value to be returned if no value of the desired
+     * type is stored with the given name.
+     *
+     * @return the value of an item previously added with putExtra(),
+     * or the default value if none was found.
+     *
+     * @see #putExtra(String, long)
+     */
+    public long getLongExtra(String name, long defaultValue) {
+        return mExtras == null ? defaultValue :
+            mExtras.getLong(name, defaultValue);
+    }
+
+    /**
+     * Retrieve extended data from the intent.
+     *
+     * @param name The name of the desired item.
+     * @param defaultValue the value to be returned if no value of the desired
+     * type is stored with the given name.
+     *
+     * @return the value of an item previously added with putExtra(),
+     * or the default value if no such item is present
+     *
+     * @see #putExtra(String, float)
+     */
+    public float getFloatExtra(String name, float defaultValue) {
+        return mExtras == null ? defaultValue :
+            mExtras.getFloat(name, defaultValue);
+    }
+
+    /**
+     * Retrieve extended data from the intent.
+     *
+     * @param name The name of the desired item.
+     * @param defaultValue the value to be returned if no value of the desired
+     * type is stored with the given name.
+     *
+     * @return the value of an item previously added with putExtra(),
+     * or the default value if none was found.
+     *
+     * @see #putExtra(String, double)
+     */
+    public double getDoubleExtra(String name, double defaultValue) {
+        return mExtras == null ? defaultValue :
+            mExtras.getDouble(name, defaultValue);
+    }
+
+    /**
+     * Retrieve extended data from the intent.
+     *
+     * @param name The name of the desired item.
+     *
+     * @return the value of an item previously added with putExtra(),
+     * or null if no String value was found.
+     *
+     * @see #putExtra(String, String)
+     */
+    public @Nullable String getStringExtra(String name) {
+        return mExtras == null ? null : mExtras.getString(name);
+    }
+
+    /**
+     * Retrieve extended data from the intent.
+     *
+     * @param name The name of the desired item.
+     *
+     * @return the value of an item previously added with putExtra(),
+     * or null if no CharSequence value was found.
+     *
+     * @see #putExtra(String, CharSequence)
+     */
+    public @Nullable CharSequence getCharSequenceExtra(String name) {
+        return mExtras == null ? null : mExtras.getCharSequence(name);
+    }
+
+    /**
+     * Retrieve extended data from the intent.
+     *
+     * @param name The name of the desired item.
+     *
+     * @return the value of an item previously added with putExtra(),
+     * or null if no Parcelable value was found.
+     *
+     * @see #putExtra(String, Parcelable)
+     */
+    public @Nullable <T extends Parcelable> T getParcelableExtra(String name) {
+        return mExtras == null ? null : mExtras.<T>getParcelable(name);
+    }
+
+    /**
+     * Retrieve extended data from the intent.
+     *
+     * @param name The name of the desired item.
+     *
+     * @return the value of an item previously added with putExtra(),
+     * or null if no Parcelable[] value was found.
+     *
+     * @see #putExtra(String, Parcelable[])
+     */
+    public @Nullable Parcelable[] getParcelableArrayExtra(String name) {
+        return mExtras == null ? null : mExtras.getParcelableArray(name);
+    }
+
+    /**
+     * Retrieve extended data from the intent.
+     *
+     * @param name The name of the desired item.
+     *
+     * @return the value of an item previously added with
+     * putParcelableArrayListExtra(), or null if no
+     * ArrayList<Parcelable> value was found.
+     *
+     * @see #putParcelableArrayListExtra(String, ArrayList)
+     */
+    public @Nullable <T extends Parcelable> ArrayList<T> getParcelableArrayListExtra(String name) {
+        return mExtras == null ? null : mExtras.<T>getParcelableArrayList(name);
+    }
+
+    /**
+     * Retrieve extended data from the intent.
+     *
+     * @param name The name of the desired item.
+     *
+     * @return the value of an item previously added with putExtra(),
+     * or null if no Serializable value was found.
+     *
+     * @see #putExtra(String, Serializable)
+     */
+    public @Nullable Serializable getSerializableExtra(String name) {
+        return mExtras == null ? null : mExtras.getSerializable(name);
+    }
+
+    /**
+     * Retrieve extended data from the intent.
+     *
+     * @param name The name of the desired item.
+     *
+     * @return the value of an item previously added with
+     * putIntegerArrayListExtra(), or null if no
+     * ArrayList<Integer> value was found.
+     *
+     * @see #putIntegerArrayListExtra(String, ArrayList)
+     */
+    public @Nullable ArrayList<Integer> getIntegerArrayListExtra(String name) {
+        return mExtras == null ? null : mExtras.getIntegerArrayList(name);
+    }
+
+    /**
+     * Retrieve extended data from the intent.
+     *
+     * @param name The name of the desired item.
+     *
+     * @return the value of an item previously added with
+     * putStringArrayListExtra(), or null if no
+     * ArrayList<String> value was found.
+     *
+     * @see #putStringArrayListExtra(String, ArrayList)
+     */
+    public @Nullable ArrayList<String> getStringArrayListExtra(String name) {
+        return mExtras == null ? null : mExtras.getStringArrayList(name);
+    }
+
+    /**
+     * Retrieve extended data from the intent.
+     *
+     * @param name The name of the desired item.
+     *
+     * @return the value of an item previously added with
+     * putCharSequenceArrayListExtra, or null if no
+     * ArrayList<CharSequence> value was found.
+     *
+     * @see #putCharSequenceArrayListExtra(String, ArrayList)
+     */
+    public @Nullable ArrayList<CharSequence> getCharSequenceArrayListExtra(String name) {
+        return mExtras == null ? null : mExtras.getCharSequenceArrayList(name);
+    }
+
+    /**
+     * Retrieve extended data from the intent.
+     *
+     * @param name The name of the desired item.
+     *
+     * @return the value of an item previously added with putExtra(),
+     * or null if no boolean array value was found.
+     *
+     * @see #putExtra(String, boolean[])
+     */
+    public @Nullable boolean[] getBooleanArrayExtra(String name) {
+        return mExtras == null ? null : mExtras.getBooleanArray(name);
+    }
+
+    /**
+     * Retrieve extended data from the intent.
+     *
+     * @param name The name of the desired item.
+     *
+     * @return the value of an item previously added with putExtra(),
+     * or null if no byte array value was found.
+     *
+     * @see #putExtra(String, byte[])
+     */
+    public @Nullable byte[] getByteArrayExtra(String name) {
+        return mExtras == null ? null : mExtras.getByteArray(name);
+    }
+
+    /**
+     * Retrieve extended data from the intent.
+     *
+     * @param name The name of the desired item.
+     *
+     * @return the value of an item previously added with putExtra(),
+     * or null if no short array value was found.
+     *
+     * @see #putExtra(String, short[])
+     */
+    public @Nullable short[] getShortArrayExtra(String name) {
+        return mExtras == null ? null : mExtras.getShortArray(name);
+    }
+
+    /**
+     * Retrieve extended data from the intent.
+     *
+     * @param name The name of the desired item.
+     *
+     * @return the value of an item previously added with putExtra(),
+     * or null if no char array value was found.
+     *
+     * @see #putExtra(String, char[])
+     */
+    public @Nullable char[] getCharArrayExtra(String name) {
+        return mExtras == null ? null : mExtras.getCharArray(name);
+    }
+
+    /**
+     * Retrieve extended data from the intent.
+     *
+     * @param name The name of the desired item.
+     *
+     * @return the value of an item previously added with putExtra(),
+     * or null if no int array value was found.
+     *
+     * @see #putExtra(String, int[])
+     */
+    public @Nullable int[] getIntArrayExtra(String name) {
+        return mExtras == null ? null : mExtras.getIntArray(name);
+    }
+
+    /**
+     * Retrieve extended data from the intent.
+     *
+     * @param name The name of the desired item.
+     *
+     * @return the value of an item previously added with putExtra(),
+     * or null if no long array value was found.
+     *
+     * @see #putExtra(String, long[])
+     */
+    public @Nullable long[] getLongArrayExtra(String name) {
+        return mExtras == null ? null : mExtras.getLongArray(name);
+    }
+
+    /**
+     * Retrieve extended data from the intent.
+     *
+     * @param name The name of the desired item.
+     *
+     * @return the value of an item previously added with putExtra(),
+     * or null if no float array value was found.
+     *
+     * @see #putExtra(String, float[])
+     */
+    public @Nullable float[] getFloatArrayExtra(String name) {
+        return mExtras == null ? null : mExtras.getFloatArray(name);
+    }
+
+    /**
+     * Retrieve extended data from the intent.
+     *
+     * @param name The name of the desired item.
+     *
+     * @return the value of an item previously added with putExtra(),
+     * or null if no double array value was found.
+     *
+     * @see #putExtra(String, double[])
+     */
+    public @Nullable double[] getDoubleArrayExtra(String name) {
+        return mExtras == null ? null : mExtras.getDoubleArray(name);
+    }
+
+    /**
+     * Retrieve extended data from the intent.
+     *
+     * @param name The name of the desired item.
+     *
+     * @return the value of an item previously added with putExtra(),
+     * or null if no String array value was found.
+     *
+     * @see #putExtra(String, String[])
+     */
+    public @Nullable String[] getStringArrayExtra(String name) {
+        return mExtras == null ? null : mExtras.getStringArray(name);
+    }
+
+    /**
+     * Retrieve extended data from the intent.
+     *
+     * @param name The name of the desired item.
+     *
+     * @return the value of an item previously added with putExtra(),
+     * or null if no CharSequence array value was found.
+     *
+     * @see #putExtra(String, CharSequence[])
+     */
+    public @Nullable CharSequence[] getCharSequenceArrayExtra(String name) {
+        return mExtras == null ? null : mExtras.getCharSequenceArray(name);
+    }
+
+    /**
+     * Retrieve extended data from the intent.
+     *
+     * @param name The name of the desired item.
+     *
+     * @return the value of an item previously added with putExtra(),
+     * or null if no Bundle value was found.
+     *
+     * @see #putExtra(String, Bundle)
+     */
+    public @Nullable Bundle getBundleExtra(String name) {
+        return mExtras == null ? null : mExtras.getBundle(name);
+    }
+
+    /**
+     * Retrieve extended data from the intent.
+     *
+     * @param name The name of the desired item.
+     *
+     * @return the value of an item previously added with putExtra(),
+     * or null if no IBinder value was found.
+     *
+     * @see #putExtra(String, IBinder)
+     *
+     * @deprecated
+     * @hide
+     */
+    @Deprecated
+    @UnsupportedAppUsage
+    public IBinder getIBinderExtra(String name) {
+        return mExtras == null ? null : mExtras.getIBinder(name);
+    }
+
+    /**
+     * Retrieve extended data from the intent.
+     *
+     * @param name The name of the desired item.
+     * @param defaultValue The default value to return in case no item is
+     * associated with the key 'name'
+     *
+     * @return the value of an item previously added with putExtra(),
+     * or defaultValue if none was found.
+     *
+     * @see #putExtra
+     *
+     * @deprecated
+     * @hide
+     */
+    @Deprecated
+    @UnsupportedAppUsage
+    public Object getExtra(String name, Object defaultValue) {
+        Object result = defaultValue;
+        if (mExtras != null) {
+            Object result2 = mExtras.get(name);
+            if (result2 != null) {
+                result = result2;
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * Retrieves a map of extended data from the intent.
+     *
+     * @return the map of all extras previously added with putExtra(),
+     * or null if none have been added.
+     */
+    public @Nullable Bundle getExtras() {
+        return (mExtras != null)
+                ? new Bundle(mExtras)
+                : null;
+    }
+
+    /**
+     * Filter extras to only basic types.
+     * @hide
+     */
+    public void removeUnsafeExtras() {
+        if (mExtras != null) {
+            mExtras = mExtras.filterValues();
+        }
+    }
+
+    /**
+     * @return Whether {@link #maybeStripForHistory} will return an lightened intent or
+     * return itself as-is.
+     * @hide
+     */
+    public boolean canStripForHistory() {
+        return ((mExtras != null) && mExtras.isParcelled()) || (mClipData != null);
+    }
+
+    /**
+     * Call it when the system needs to keep an intent for logging purposes to remove fields
+     * that are not needed for logging.
+     * @hide
+     */
+    public Intent maybeStripForHistory() {
+        // TODO Scan and remove possibly heavy instances like Bitmaps from unparcelled extras?
+
+        if (!canStripForHistory()) {
+            return this;
+        }
+        return new Intent(this, COPY_MODE_HISTORY);
+    }
+
+    /**
+     * Retrieve any special flags associated with this intent.  You will
+     * normally just set them with {@link #setFlags} and let the system
+     * take the appropriate action with them.
+     *
+     * @return The currently set flags.
+     * @see #setFlags
+     * @see #addFlags
+     * @see #removeFlags
+     */
+    public @Flags int getFlags() {
+        return mFlags;
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage
+    public boolean isExcludingStopped() {
+        return (mFlags&(FLAG_EXCLUDE_STOPPED_PACKAGES|FLAG_INCLUDE_STOPPED_PACKAGES))
+                == FLAG_EXCLUDE_STOPPED_PACKAGES;
+    }
+
+    /**
+     * Retrieve the application package name this Intent is limited to.  When
+     * resolving an Intent, if non-null this limits the resolution to only
+     * components in the given application package.
+     *
+     * @return The name of the application package for the Intent.
+     *
+     * @see #resolveActivity
+     * @see #setPackage
+     */
+    public @Nullable String getPackage() {
+        return mPackage;
+    }
+
+    /**
+     * Retrieve the concrete component associated with the intent.  When receiving
+     * an intent, this is the component that was found to best handle it (that is,
+     * yourself) and will always be non-null; in all other cases it will be
+     * null unless explicitly set.
+     *
+     * @return The name of the application component to handle the intent.
+     *
+     * @see #resolveActivity
+     * @see #setComponent
+     */
+    public @Nullable ComponentName getComponent() {
+        return mComponent;
+    }
+
+    /**
+     * Get the bounds of the sender of this intent, in screen coordinates.  This can be
+     * used as a hint to the receiver for animations and the like.  Null means that there
+     * is no source bounds.
+     */
+    public @Nullable Rect getSourceBounds() {
+        return mSourceBounds;
+    }
+
+    /**
+     * Return the Activity component that should be used to handle this intent.
+     * The appropriate component is determined based on the information in the
+     * intent, evaluated as follows:
+     *
+     * <p>If {@link #getComponent} returns an explicit class, that is returned
+     * without any further consideration.
+     *
+     * <p>The activity must handle the {@link Intent#CATEGORY_DEFAULT} Intent
+     * category to be considered.
+     *
+     * <p>If {@link #getAction} is non-NULL, the activity must handle this
+     * action.
+     *
+     * <p>If {@link #resolveType} returns non-NULL, the activity must handle
+     * this type.
+     *
+     * <p>If {@link #addCategory} has added any categories, the activity must
+     * handle ALL of the categories specified.
+     *
+     * <p>If {@link #getPackage} is non-NULL, only activity components in
+     * that application package will be considered.
+     *
+     * <p>If there are no activities that satisfy all of these conditions, a
+     * null string is returned.
+     *
+     * <p>If multiple activities are found to satisfy the intent, the one with
+     * the highest priority will be used.  If there are multiple activities
+     * with the same priority, the system will either pick the best activity
+     * based on user preference, or resolve to a system class that will allow
+     * the user to pick an activity and forward from there.
+     *
+     * <p>This method is implemented simply by calling
+     * {@link PackageManager#resolveActivity} with the "defaultOnly" parameter
+     * true.</p>
+     * <p> This API is called for you as part of starting an activity from an
+     * intent.  You do not normally need to call it yourself.</p>
+     *
+     * @param pm The package manager with which to resolve the Intent.
+     *
+     * @return Name of the component implementing an activity that can
+     *         display the intent.
+     *
+     * @see #setComponent
+     * @see #getComponent
+     * @see #resolveActivityInfo
+     */
+    public ComponentName resolveActivity(@NonNull PackageManager pm) {
+        if (mComponent != null) {
+            return mComponent;
+        }
+
+        ResolveInfo info = pm.resolveActivity(
+            this, PackageManager.MATCH_DEFAULT_ONLY);
+        if (info != null) {
+            return new ComponentName(
+                    info.activityInfo.applicationInfo.packageName,
+                    info.activityInfo.name);
+        }
+
+        return null;
+    }
+
+    /**
+     * Resolve the Intent into an {@link ActivityInfo}
+     * describing the activity that should execute the intent.  Resolution
+     * follows the same rules as described for {@link #resolveActivity}, but
+     * you get back the completely information about the resolved activity
+     * instead of just its class name.
+     *
+     * @param pm The package manager with which to resolve the Intent.
+     * @param flags Addition information to retrieve as per
+     * {@link PackageManager#getActivityInfo(ComponentName, int)
+     * PackageManager.getActivityInfo()}.
+     *
+     * @return PackageManager.ActivityInfo
+     *
+     * @see #resolveActivity
+     */
+    public ActivityInfo resolveActivityInfo(@NonNull PackageManager pm,
+            @PackageManager.ComponentInfoFlags int flags) {
+        ActivityInfo ai = null;
+        if (mComponent != null) {
+            try {
+                ai = pm.getActivityInfo(mComponent, flags);
+            } catch (PackageManager.NameNotFoundException e) {
+                // ignore
+            }
+        } else {
+            ResolveInfo info = pm.resolveActivity(
+                this, PackageManager.MATCH_DEFAULT_ONLY | flags);
+            if (info != null) {
+                ai = info.activityInfo;
+            }
+        }
+
+        return ai;
+    }
+
+    /**
+     * Special function for use by the system to resolve service
+     * intents to system apps.  Throws an exception if there are
+     * multiple potential matches to the Intent.  Returns null if
+     * there are no matches.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public @Nullable ComponentName resolveSystemService(@NonNull PackageManager pm,
+            @PackageManager.ComponentInfoFlags int flags) {
+        if (mComponent != null) {
+            return mComponent;
+        }
+
+        List<ResolveInfo> results = pm.queryIntentServices(this, flags);
+        if (results == null) {
+            return null;
+        }
+        ComponentName comp = null;
+        for (int i=0; i<results.size(); i++) {
+            ResolveInfo ri = results.get(i);
+            if ((ri.serviceInfo.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) == 0) {
+                continue;
+            }
+            ComponentName foundComp = new ComponentName(ri.serviceInfo.applicationInfo.packageName,
+                    ri.serviceInfo.name);
+            if (comp != null) {
+                throw new IllegalStateException("Multiple system services handle " + this
+                        + ": " + comp + ", " + foundComp);
+            }
+            comp = foundComp;
+        }
+        return comp;
+    }
+
+    /**
+     * Set the general action to be performed.
+     *
+     * @param action An action name, such as ACTION_VIEW.  Application-specific
+     *               actions should be prefixed with the vendor's package name.
+     *
+     * @return Returns the same Intent object, for chaining multiple calls
+     * into a single statement.
+     *
+     * @see #getAction
+     */
+    public @NonNull Intent setAction(@Nullable String action) {
+        mAction = action != null ? action.intern() : null;
+        return this;
+    }
+
+    /**
+     * Set the data this intent is operating on.  This method automatically
+     * clears any type that was previously set by {@link #setType} or
+     * {@link #setTypeAndNormalize}.
+     *
+     * <p><em>Note: scheme matching in the Android framework is
+     * case-sensitive, unlike the formal RFC. As a result,
+     * you should always write your Uri with a lower case scheme,
+     * or use {@link Uri#normalizeScheme} or
+     * {@link #setDataAndNormalize}
+     * to ensure that the scheme is converted to lower case.</em>
+     *
+     * @param data The Uri of the data this intent is now targeting.
+     *
+     * @return Returns the same Intent object, for chaining multiple calls
+     * into a single statement.
+     *
+     * @see #getData
+     * @see #setDataAndNormalize
+     * @see android.net.Uri#normalizeScheme()
+     */
+    public @NonNull Intent setData(@Nullable Uri data) {
+        mData = data;
+        mType = null;
+        return this;
+    }
+
+    /**
+     * Normalize and set the data this intent is operating on.
+     *
+     * <p>This method automatically clears any type that was
+     * previously set (for example, by {@link #setType}).
+     *
+     * <p>The data Uri is normalized using
+     * {@link android.net.Uri#normalizeScheme} before it is set,
+     * so really this is just a convenience method for
+     * <pre>
+     * setData(data.normalize())
+     * </pre>
+     *
+     * @param data The Uri of the data this intent is now targeting.
+     *
+     * @return Returns the same Intent object, for chaining multiple calls
+     * into a single statement.
+     *
+     * @see #getData
+     * @see #setType
+     * @see android.net.Uri#normalizeScheme
+     */
+    public @NonNull Intent setDataAndNormalize(@NonNull Uri data) {
+        return setData(data.normalizeScheme());
+    }
+
+    /**
+     * Set an explicit MIME data type.
+     *
+     * <p>This is used to create intents that only specify a type and not data,
+     * for example to indicate the type of data to return.
+     *
+     * <p>This method automatically clears any data that was
+     * previously set (for example by {@link #setData}).
+     *
+     * <p><em>Note: MIME type matching in the Android framework is
+     * case-sensitive, unlike formal RFC MIME types.  As a result,
+     * you should always write your MIME types with lower case letters,
+     * or use {@link #normalizeMimeType} or {@link #setTypeAndNormalize}
+     * to ensure that it is converted to lower case.</em>
+     *
+     * @param type The MIME type of the data being handled by this intent.
+     *
+     * @return Returns the same Intent object, for chaining multiple calls
+     * into a single statement.
+     *
+     * @see #getType
+     * @see #setTypeAndNormalize
+     * @see #setDataAndType
+     * @see #normalizeMimeType
+     */
+    public @NonNull Intent setType(@Nullable String type) {
+        mData = null;
+        mType = type;
+        return this;
+    }
+
+    /**
+     * Normalize and set an explicit MIME data type.
+     *
+     * <p>This is used to create intents that only specify a type and not data,
+     * for example to indicate the type of data to return.
+     *
+     * <p>This method automatically clears any data that was
+     * previously set (for example by {@link #setData}).
+     *
+     * <p>The MIME type is normalized using
+     * {@link #normalizeMimeType} before it is set,
+     * so really this is just a convenience method for
+     * <pre>
+     * setType(Intent.normalizeMimeType(type))
+     * </pre>
+     *
+     * @param type The MIME type of the data being handled by this intent.
+     *
+     * @return Returns the same Intent object, for chaining multiple calls
+     * into a single statement.
+     *
+     * @see #getType
+     * @see #setData
+     * @see #normalizeMimeType
+     */
+    public @NonNull Intent setTypeAndNormalize(@Nullable String type) {
+        return setType(normalizeMimeType(type));
+    }
+
+    /**
+     * (Usually optional) Set the data for the intent along with an explicit
+     * MIME data type.  This method should very rarely be used -- it allows you
+     * to override the MIME type that would ordinarily be inferred from the
+     * data with your own type given here.
+     *
+     * <p><em>Note: MIME type and Uri scheme matching in the
+     * Android framework is case-sensitive, unlike the formal RFC definitions.
+     * As a result, you should always write these elements with lower case letters,
+     * or use {@link #normalizeMimeType} or {@link android.net.Uri#normalizeScheme} or
+     * {@link #setDataAndTypeAndNormalize}
+     * to ensure that they are converted to lower case.</em>
+     *
+     * @param data The Uri of the data this intent is now targeting.
+     * @param type The MIME type of the data being handled by this intent.
+     *
+     * @return Returns the same Intent object, for chaining multiple calls
+     * into a single statement.
+     *
+     * @see #setType
+     * @see #setData
+     * @see #normalizeMimeType
+     * @see android.net.Uri#normalizeScheme
+     * @see #setDataAndTypeAndNormalize
+     */
+    public @NonNull Intent setDataAndType(@Nullable Uri data, @Nullable String type) {
+        mData = data;
+        mType = type;
+        return this;
+    }
+
+    /**
+     * (Usually optional) Normalize and set both the data Uri and an explicit
+     * MIME data type.  This method should very rarely be used -- it allows you
+     * to override the MIME type that would ordinarily be inferred from the
+     * data with your own type given here.
+     *
+     * <p>The data Uri and the MIME type are normalize using
+     * {@link android.net.Uri#normalizeScheme} and {@link #normalizeMimeType}
+     * before they are set, so really this is just a convenience method for
+     * <pre>
+     * setDataAndType(data.normalize(), Intent.normalizeMimeType(type))
+     * </pre>
+     *
+     * @param data The Uri of the data this intent is now targeting.
+     * @param type The MIME type of the data being handled by this intent.
+     *
+     * @return Returns the same Intent object, for chaining multiple calls
+     * into a single statement.
+     *
+     * @see #setType
+     * @see #setData
+     * @see #setDataAndType
+     * @see #normalizeMimeType
+     * @see android.net.Uri#normalizeScheme
+     */
+    public @NonNull Intent setDataAndTypeAndNormalize(@NonNull Uri data, @Nullable String type) {
+        return setDataAndType(data.normalizeScheme(), normalizeMimeType(type));
+    }
+
+    /**
+     * Set an identifier for this Intent.  If set, this provides a unique identity for this Intent,
+     * allowing it to be unique from other Intents that would otherwise look the same.  In
+     * particular, this will be used by {@link #filterEquals(Intent)} to determine if two
+     * Intents are the same as with other fields like {@link #setAction}.  However, unlike those
+     * fields, the identifier is <em>never</em> used for matching against an {@link IntentFilter};
+     * it is as if the identifier has not been set on the Intent.
+     *
+     * <p>This can be used, for example, to make this Intent unique from other Intents that
+     * are otherwise the same, for use in creating a {@link android.app.PendingIntent}.  (Be aware
+     * however that the receiver of the PendingIntent will see whatever you put in here.)  The
+     * structure of this string is completely undefined by the platform, however if you are going
+     * to be exposing identifier strings across different applications you may need to define
+     * your own structure if there is no central party defining the contents of this field.</p>
+     *
+     * @param identifier The identifier for this Intent.  The contents of the string have no
+     *                   meaning to the system, except whether they are exactly the same as
+     *                   another identifier.
+     *
+     * @return Returns the same Intent object, for chaining multiple calls
+     * into a single statement.
+     *
+     * @see #getIdentifier
+     */
+    public @NonNull Intent setIdentifier(@Nullable String identifier) {
+        mIdentifier = identifier;
+        return this;
+    }
+
+    /**
+     * Add a new category to the intent.  Categories provide additional detail
+     * about the action the intent performs.  When resolving an intent, only
+     * activities that provide <em>all</em> of the requested categories will be
+     * used.
+     *
+     * @param category The desired category.  This can be either one of the
+     *               predefined Intent categories, or a custom category in your own
+     *               namespace.
+     *
+     * @return Returns the same Intent object, for chaining multiple calls
+     * into a single statement.
+     *
+     * @see #hasCategory
+     * @see #removeCategory
+     */
+    public @NonNull Intent addCategory(String category) {
+        if (mCategories == null) {
+            mCategories = new ArraySet<String>();
+        }
+        mCategories.add(category.intern());
+        return this;
+    }
+
+    /**
+     * Remove a category from an intent.
+     *
+     * @param category The category to remove.
+     *
+     * @see #addCategory
+     */
+    public void removeCategory(String category) {
+        if (mCategories != null) {
+            mCategories.remove(category);
+            if (mCategories.size() == 0) {
+                mCategories = null;
+            }
+        }
+    }
+
+    /**
+     * Set a selector for this Intent.  This is a modification to the kinds of
+     * things the Intent will match.  If the selector is set, it will be used
+     * when trying to find entities that can handle the Intent, instead of the
+     * main contents of the Intent.  This allows you build an Intent containing
+     * a generic protocol while targeting it more specifically.
+     *
+     * <p>An example of where this may be used is with things like
+     * {@link #CATEGORY_APP_BROWSER}.  This category allows you to build an
+     * Intent that will launch the Browser application.  However, the correct
+     * main entry point of an application is actually {@link #ACTION_MAIN}
+     * {@link #CATEGORY_LAUNCHER} with {@link #setComponent(ComponentName)}
+     * used to specify the actual Activity to launch.  If you launch the browser
+     * with something different, undesired behavior may happen if the user has
+     * previously or later launches it the normal way, since they do not match.
+     * Instead, you can build an Intent with the MAIN action (but no ComponentName
+     * yet specified) and set a selector with {@link #ACTION_MAIN} and
+     * {@link #CATEGORY_APP_BROWSER} to point it specifically to the browser activity.
+     *
+     * <p>Setting a selector does not impact the behavior of
+     * {@link #filterEquals(Intent)} and {@link #filterHashCode()}.  This is part of the
+     * desired behavior of a selector -- it does not impact the base meaning
+     * of the Intent, just what kinds of things will be matched against it
+     * when determining who can handle it.</p>
+     *
+     * <p>You can not use both a selector and {@link #setPackage(String)} on
+     * the same base Intent.</p>
+     *
+     * @param selector The desired selector Intent; set to null to not use
+     * a special selector.
+     */
+    public void setSelector(@Nullable Intent selector) {
+        if (selector == this) {
+            throw new IllegalArgumentException(
+                    "Intent being set as a selector of itself");
+        }
+        if (selector != null && mPackage != null) {
+            throw new IllegalArgumentException(
+                    "Can't set selector when package name is already set");
+        }
+        mSelector = selector;
+    }
+
+    /**
+     * Set a {@link ClipData} associated with this Intent.  This replaces any
+     * previously set ClipData.
+     *
+     * <p>The ClipData in an intent is not used for Intent matching or other
+     * such operations.  Semantically it is like extras, used to transmit
+     * additional data with the Intent.  The main feature of using this over
+     * the extras for data is that {@link #FLAG_GRANT_READ_URI_PERMISSION}
+     * and {@link #FLAG_GRANT_WRITE_URI_PERMISSION} will operate on any URI
+     * items included in the clip data.  This is useful, in particular, if
+     * you want to transmit an Intent containing multiple <code>content:</code>
+     * URIs for which the recipient may not have global permission to access the
+     * content provider.
+     *
+     * <p>If the ClipData contains items that are themselves Intents, any
+     * grant flags in those Intents will be ignored.  Only the top-level flags
+     * of the main Intent are respected, and will be applied to all Uri or
+     * Intent items in the clip (or sub-items of the clip).
+     *
+     * <p>The MIME type, label, and icon in the ClipData object are not
+     * directly used by Intent.  Applications should generally rely on the
+     * MIME type of the Intent itself, not what it may find in the ClipData.
+     * A common practice is to construct a ClipData for use with an Intent
+     * with a MIME type of "*&#47;*".
+     *
+     * @param clip The new clip to set.  May be null to clear the current clip.
+     */
+    public void setClipData(@Nullable ClipData clip) {
+        mClipData = clip;
+    }
+
+    /**
+     * This is NOT a secure mechanism to identify the user who sent the intent.
+     * When the intent is sent to a different user, it is used to fix uris by adding the userId
+     * who sent the intent.
+     * @hide
+     */
+    public void prepareToLeaveUser(int userId) {
+        // If mContentUserHint is not UserHandle.USER_CURRENT, the intent has already left a user.
+        // We want mContentUserHint to refer to the original user, so don't do anything.
+        if (mContentUserHint == UserHandle.USER_CURRENT) {
+            mContentUserHint = userId;
+        }
+    }
+
+    /**
+     * Add extended data to the intent.  The name must include a package
+     * prefix, for example the app com.android.contacts would use names
+     * like "com.android.contacts.ShowAll".
+     *
+     * @param name The name of the extra data, with package prefix.
+     * @param value The boolean data value.
+     *
+     * @return Returns the same Intent object, for chaining multiple calls
+     * into a single statement.
+     *
+     * @see #putExtras
+     * @see #removeExtra
+     * @see #getBooleanExtra(String, boolean)
+     */
+    public @NonNull Intent putExtra(String name, boolean value) {
+        if (mExtras == null) {
+            mExtras = new Bundle();
+        }
+        mExtras.putBoolean(name, value);
+        return this;
+    }
+
+    /**
+     * Add extended data to the intent.  The name must include a package
+     * prefix, for example the app com.android.contacts would use names
+     * like "com.android.contacts.ShowAll".
+     *
+     * @param name The name of the extra data, with package prefix.
+     * @param value The byte data value.
+     *
+     * @return Returns the same Intent object, for chaining multiple calls
+     * into a single statement.
+     *
+     * @see #putExtras
+     * @see #removeExtra
+     * @see #getByteExtra(String, byte)
+     */
+    public @NonNull Intent putExtra(String name, byte value) {
+        if (mExtras == null) {
+            mExtras = new Bundle();
+        }
+        mExtras.putByte(name, value);
+        return this;
+    }
+
+    /**
+     * Add extended data to the intent.  The name must include a package
+     * prefix, for example the app com.android.contacts would use names
+     * like "com.android.contacts.ShowAll".
+     *
+     * @param name The name of the extra data, with package prefix.
+     * @param value The char data value.
+     *
+     * @return Returns the same Intent object, for chaining multiple calls
+     * into a single statement.
+     *
+     * @see #putExtras
+     * @see #removeExtra
+     * @see #getCharExtra(String, char)
+     */
+    public @NonNull Intent putExtra(String name, char value) {
+        if (mExtras == null) {
+            mExtras = new Bundle();
+        }
+        mExtras.putChar(name, value);
+        return this;
+    }
+
+    /**
+     * Add extended data to the intent.  The name must include a package
+     * prefix, for example the app com.android.contacts would use names
+     * like "com.android.contacts.ShowAll".
+     *
+     * @param name The name of the extra data, with package prefix.
+     * @param value The short data value.
+     *
+     * @return Returns the same Intent object, for chaining multiple calls
+     * into a single statement.
+     *
+     * @see #putExtras
+     * @see #removeExtra
+     * @see #getShortExtra(String, short)
+     */
+    public @NonNull Intent putExtra(String name, short value) {
+        if (mExtras == null) {
+            mExtras = new Bundle();
+        }
+        mExtras.putShort(name, value);
+        return this;
+    }
+
+    /**
+     * Add extended data to the intent.  The name must include a package
+     * prefix, for example the app com.android.contacts would use names
+     * like "com.android.contacts.ShowAll".
+     *
+     * @param name The name of the extra data, with package prefix.
+     * @param value The integer data value.
+     *
+     * @return Returns the same Intent object, for chaining multiple calls
+     * into a single statement.
+     *
+     * @see #putExtras
+     * @see #removeExtra
+     * @see #getIntExtra(String, int)
+     */
+    public @NonNull Intent putExtra(String name, int value) {
+        if (mExtras == null) {
+            mExtras = new Bundle();
+        }
+        mExtras.putInt(name, value);
+        return this;
+    }
+
+    /**
+     * Add extended data to the intent.  The name must include a package
+     * prefix, for example the app com.android.contacts would use names
+     * like "com.android.contacts.ShowAll".
+     *
+     * @param name The name of the extra data, with package prefix.
+     * @param value The long data value.
+     *
+     * @return Returns the same Intent object, for chaining multiple calls
+     * into a single statement.
+     *
+     * @see #putExtras
+     * @see #removeExtra
+     * @see #getLongExtra(String, long)
+     */
+    public @NonNull Intent putExtra(String name, long value) {
+        if (mExtras == null) {
+            mExtras = new Bundle();
+        }
+        mExtras.putLong(name, value);
+        return this;
+    }
+
+    /**
+     * Add extended data to the intent.  The name must include a package
+     * prefix, for example the app com.android.contacts would use names
+     * like "com.android.contacts.ShowAll".
+     *
+     * @param name The name of the extra data, with package prefix.
+     * @param value The float data value.
+     *
+     * @return Returns the same Intent object, for chaining multiple calls
+     * into a single statement.
+     *
+     * @see #putExtras
+     * @see #removeExtra
+     * @see #getFloatExtra(String, float)
+     */
+    public @NonNull Intent putExtra(String name, float value) {
+        if (mExtras == null) {
+            mExtras = new Bundle();
+        }
+        mExtras.putFloat(name, value);
+        return this;
+    }
+
+    /**
+     * Add extended data to the intent.  The name must include a package
+     * prefix, for example the app com.android.contacts would use names
+     * like "com.android.contacts.ShowAll".
+     *
+     * @param name The name of the extra data, with package prefix.
+     * @param value The double data value.
+     *
+     * @return Returns the same Intent object, for chaining multiple calls
+     * into a single statement.
+     *
+     * @see #putExtras
+     * @see #removeExtra
+     * @see #getDoubleExtra(String, double)
+     */
+    public @NonNull Intent putExtra(String name, double value) {
+        if (mExtras == null) {
+            mExtras = new Bundle();
+        }
+        mExtras.putDouble(name, value);
+        return this;
+    }
+
+    /**
+     * Add extended data to the intent.  The name must include a package
+     * prefix, for example the app com.android.contacts would use names
+     * like "com.android.contacts.ShowAll".
+     *
+     * @param name The name of the extra data, with package prefix.
+     * @param value The String data value.
+     *
+     * @return Returns the same Intent object, for chaining multiple calls
+     * into a single statement.
+     *
+     * @see #putExtras
+     * @see #removeExtra
+     * @see #getStringExtra(String)
+     */
+    public @NonNull Intent putExtra(String name, @Nullable String value) {
+        if (mExtras == null) {
+            mExtras = new Bundle();
+        }
+        mExtras.putString(name, value);
+        return this;
+    }
+
+    /**
+     * Add extended data to the intent.  The name must include a package
+     * prefix, for example the app com.android.contacts would use names
+     * like "com.android.contacts.ShowAll".
+     *
+     * @param name The name of the extra data, with package prefix.
+     * @param value The CharSequence data value.
+     *
+     * @return Returns the same Intent object, for chaining multiple calls
+     * into a single statement.
+     *
+     * @see #putExtras
+     * @see #removeExtra
+     * @see #getCharSequenceExtra(String)
+     */
+    public @NonNull Intent putExtra(String name, @Nullable CharSequence value) {
+        if (mExtras == null) {
+            mExtras = new Bundle();
+        }
+        mExtras.putCharSequence(name, value);
+        return this;
+    }
+
+    /**
+     * Add extended data to the intent.  The name must include a package
+     * prefix, for example the app com.android.contacts would use names
+     * like "com.android.contacts.ShowAll".
+     *
+     * @param name The name of the extra data, with package prefix.
+     * @param value The Parcelable data value.
+     *
+     * @return Returns the same Intent object, for chaining multiple calls
+     * into a single statement.
+     *
+     * @see #putExtras
+     * @see #removeExtra
+     * @see #getParcelableExtra(String)
+     */
+    public @NonNull Intent putExtra(String name, @Nullable Parcelable value) {
+        if (mExtras == null) {
+            mExtras = new Bundle();
+        }
+        mExtras.putParcelable(name, value);
+        return this;
+    }
+
+    /**
+     * Add extended data to the intent.  The name must include a package
+     * prefix, for example the app com.android.contacts would use names
+     * like "com.android.contacts.ShowAll".
+     *
+     * @param name The name of the extra data, with package prefix.
+     * @param value The Parcelable[] data value.
+     *
+     * @return Returns the same Intent object, for chaining multiple calls
+     * into a single statement.
+     *
+     * @see #putExtras
+     * @see #removeExtra
+     * @see #getParcelableArrayExtra(String)
+     */
+    public @NonNull Intent putExtra(String name, @Nullable Parcelable[] value) {
+        if (mExtras == null) {
+            mExtras = new Bundle();
+        }
+        mExtras.putParcelableArray(name, value);
+        return this;
+    }
+
+    /**
+     * Add extended data to the intent.  The name must include a package
+     * prefix, for example the app com.android.contacts would use names
+     * like "com.android.contacts.ShowAll".
+     *
+     * @param name The name of the extra data, with package prefix.
+     * @param value The ArrayList<Parcelable> data value.
+     *
+     * @return Returns the same Intent object, for chaining multiple calls
+     * into a single statement.
+     *
+     * @see #putExtras
+     * @see #removeExtra
+     * @see #getParcelableArrayListExtra(String)
+     */
+    public @NonNull Intent putParcelableArrayListExtra(String name,
+            @Nullable ArrayList<? extends Parcelable> value) {
+        if (mExtras == null) {
+            mExtras = new Bundle();
+        }
+        mExtras.putParcelableArrayList(name, value);
+        return this;
+    }
+
+    /**
+     * Add extended data to the intent.  The name must include a package
+     * prefix, for example the app com.android.contacts would use names
+     * like "com.android.contacts.ShowAll".
+     *
+     * @param name The name of the extra data, with package prefix.
+     * @param value The ArrayList<Integer> data value.
+     *
+     * @return Returns the same Intent object, for chaining multiple calls
+     * into a single statement.
+     *
+     * @see #putExtras
+     * @see #removeExtra
+     * @see #getIntegerArrayListExtra(String)
+     */
+    public @NonNull Intent putIntegerArrayListExtra(String name,
+            @Nullable ArrayList<Integer> value) {
+        if (mExtras == null) {
+            mExtras = new Bundle();
+        }
+        mExtras.putIntegerArrayList(name, value);
+        return this;
+    }
+
+    /**
+     * Add extended data to the intent.  The name must include a package
+     * prefix, for example the app com.android.contacts would use names
+     * like "com.android.contacts.ShowAll".
+     *
+     * @param name The name of the extra data, with package prefix.
+     * @param value The ArrayList<String> data value.
+     *
+     * @return Returns the same Intent object, for chaining multiple calls
+     * into a single statement.
+     *
+     * @see #putExtras
+     * @see #removeExtra
+     * @see #getStringArrayListExtra(String)
+     */
+    public @NonNull Intent putStringArrayListExtra(String name, @Nullable ArrayList<String> value) {
+        if (mExtras == null) {
+            mExtras = new Bundle();
+        }
+        mExtras.putStringArrayList(name, value);
+        return this;
+    }
+
+    /**
+     * Add extended data to the intent.  The name must include a package
+     * prefix, for example the app com.android.contacts would use names
+     * like "com.android.contacts.ShowAll".
+     *
+     * @param name The name of the extra data, with package prefix.
+     * @param value The ArrayList<CharSequence> data value.
+     *
+     * @return Returns the same Intent object, for chaining multiple calls
+     * into a single statement.
+     *
+     * @see #putExtras
+     * @see #removeExtra
+     * @see #getCharSequenceArrayListExtra(String)
+     */
+    public @NonNull Intent putCharSequenceArrayListExtra(String name,
+            @Nullable ArrayList<CharSequence> value) {
+        if (mExtras == null) {
+            mExtras = new Bundle();
+        }
+        mExtras.putCharSequenceArrayList(name, value);
+        return this;
+    }
+
+    /**
+     * Add extended data to the intent.  The name must include a package
+     * prefix, for example the app com.android.contacts would use names
+     * like "com.android.contacts.ShowAll".
+     *
+     * @param name The name of the extra data, with package prefix.
+     * @param value The Serializable data value.
+     *
+     * @return Returns the same Intent object, for chaining multiple calls
+     * into a single statement.
+     *
+     * @see #putExtras
+     * @see #removeExtra
+     * @see #getSerializableExtra(String)
+     */
+    public @NonNull Intent putExtra(String name, @Nullable Serializable value) {
+        if (mExtras == null) {
+            mExtras = new Bundle();
+        }
+        mExtras.putSerializable(name, value);
+        return this;
+    }
+
+    /**
+     * Add extended data to the intent.  The name must include a package
+     * prefix, for example the app com.android.contacts would use names
+     * like "com.android.contacts.ShowAll".
+     *
+     * @param name The name of the extra data, with package prefix.
+     * @param value The boolean array data value.
+     *
+     * @return Returns the same Intent object, for chaining multiple calls
+     * into a single statement.
+     *
+     * @see #putExtras
+     * @see #removeExtra
+     * @see #getBooleanArrayExtra(String)
+     */
+    public @NonNull Intent putExtra(String name, @Nullable boolean[] value) {
+        if (mExtras == null) {
+            mExtras = new Bundle();
+        }
+        mExtras.putBooleanArray(name, value);
+        return this;
+    }
+
+    /**
+     * Add extended data to the intent.  The name must include a package
+     * prefix, for example the app com.android.contacts would use names
+     * like "com.android.contacts.ShowAll".
+     *
+     * @param name The name of the extra data, with package prefix.
+     * @param value The byte array data value.
+     *
+     * @return Returns the same Intent object, for chaining multiple calls
+     * into a single statement.
+     *
+     * @see #putExtras
+     * @see #removeExtra
+     * @see #getByteArrayExtra(String)
+     */
+    public @NonNull Intent putExtra(String name, @Nullable byte[] value) {
+        if (mExtras == null) {
+            mExtras = new Bundle();
+        }
+        mExtras.putByteArray(name, value);
+        return this;
+    }
+
+    /**
+     * Add extended data to the intent.  The name must include a package
+     * prefix, for example the app com.android.contacts would use names
+     * like "com.android.contacts.ShowAll".
+     *
+     * @param name The name of the extra data, with package prefix.
+     * @param value The short array data value.
+     *
+     * @return Returns the same Intent object, for chaining multiple calls
+     * into a single statement.
+     *
+     * @see #putExtras
+     * @see #removeExtra
+     * @see #getShortArrayExtra(String)
+     */
+    public @NonNull Intent putExtra(String name, @Nullable short[] value) {
+        if (mExtras == null) {
+            mExtras = new Bundle();
+        }
+        mExtras.putShortArray(name, value);
+        return this;
+    }
+
+    /**
+     * Add extended data to the intent.  The name must include a package
+     * prefix, for example the app com.android.contacts would use names
+     * like "com.android.contacts.ShowAll".
+     *
+     * @param name The name of the extra data, with package prefix.
+     * @param value The char array data value.
+     *
+     * @return Returns the same Intent object, for chaining multiple calls
+     * into a single statement.
+     *
+     * @see #putExtras
+     * @see #removeExtra
+     * @see #getCharArrayExtra(String)
+     */
+    public @NonNull Intent putExtra(String name, @Nullable char[] value) {
+        if (mExtras == null) {
+            mExtras = new Bundle();
+        }
+        mExtras.putCharArray(name, value);
+        return this;
+    }
+
+    /**
+     * Add extended data to the intent.  The name must include a package
+     * prefix, for example the app com.android.contacts would use names
+     * like "com.android.contacts.ShowAll".
+     *
+     * @param name The name of the extra data, with package prefix.
+     * @param value The int array data value.
+     *
+     * @return Returns the same Intent object, for chaining multiple calls
+     * into a single statement.
+     *
+     * @see #putExtras
+     * @see #removeExtra
+     * @see #getIntArrayExtra(String)
+     */
+    public @NonNull Intent putExtra(String name, @Nullable int[] value) {
+        if (mExtras == null) {
+            mExtras = new Bundle();
+        }
+        mExtras.putIntArray(name, value);
+        return this;
+    }
+
+    /**
+     * Add extended data to the intent.  The name must include a package
+     * prefix, for example the app com.android.contacts would use names
+     * like "com.android.contacts.ShowAll".
+     *
+     * @param name The name of the extra data, with package prefix.
+     * @param value The byte array data value.
+     *
+     * @return Returns the same Intent object, for chaining multiple calls
+     * into a single statement.
+     *
+     * @see #putExtras
+     * @see #removeExtra
+     * @see #getLongArrayExtra(String)
+     */
+    public @NonNull Intent putExtra(String name, @Nullable long[] value) {
+        if (mExtras == null) {
+            mExtras = new Bundle();
+        }
+        mExtras.putLongArray(name, value);
+        return this;
+    }
+
+    /**
+     * Add extended data to the intent.  The name must include a package
+     * prefix, for example the app com.android.contacts would use names
+     * like "com.android.contacts.ShowAll".
+     *
+     * @param name The name of the extra data, with package prefix.
+     * @param value The float array data value.
+     *
+     * @return Returns the same Intent object, for chaining multiple calls
+     * into a single statement.
+     *
+     * @see #putExtras
+     * @see #removeExtra
+     * @see #getFloatArrayExtra(String)
+     */
+    public @NonNull Intent putExtra(String name, @Nullable float[] value) {
+        if (mExtras == null) {
+            mExtras = new Bundle();
+        }
+        mExtras.putFloatArray(name, value);
+        return this;
+    }
+
+    /**
+     * Add extended data to the intent.  The name must include a package
+     * prefix, for example the app com.android.contacts would use names
+     * like "com.android.contacts.ShowAll".
+     *
+     * @param name The name of the extra data, with package prefix.
+     * @param value The double array data value.
+     *
+     * @return Returns the same Intent object, for chaining multiple calls
+     * into a single statement.
+     *
+     * @see #putExtras
+     * @see #removeExtra
+     * @see #getDoubleArrayExtra(String)
+     */
+    public @NonNull Intent putExtra(String name, @Nullable double[] value) {
+        if (mExtras == null) {
+            mExtras = new Bundle();
+        }
+        mExtras.putDoubleArray(name, value);
+        return this;
+    }
+
+    /**
+     * Add extended data to the intent.  The name must include a package
+     * prefix, for example the app com.android.contacts would use names
+     * like "com.android.contacts.ShowAll".
+     *
+     * @param name The name of the extra data, with package prefix.
+     * @param value The String array data value.
+     *
+     * @return Returns the same Intent object, for chaining multiple calls
+     * into a single statement.
+     *
+     * @see #putExtras
+     * @see #removeExtra
+     * @see #getStringArrayExtra(String)
+     */
+    public @NonNull Intent putExtra(String name, @Nullable String[] value) {
+        if (mExtras == null) {
+            mExtras = new Bundle();
+        }
+        mExtras.putStringArray(name, value);
+        return this;
+    }
+
+    /**
+     * Add extended data to the intent.  The name must include a package
+     * prefix, for example the app com.android.contacts would use names
+     * like "com.android.contacts.ShowAll".
+     *
+     * @param name The name of the extra data, with package prefix.
+     * @param value The CharSequence array data value.
+     *
+     * @return Returns the same Intent object, for chaining multiple calls
+     * into a single statement.
+     *
+     * @see #putExtras
+     * @see #removeExtra
+     * @see #getCharSequenceArrayExtra(String)
+     */
+    public @NonNull Intent putExtra(String name, @Nullable CharSequence[] value) {
+        if (mExtras == null) {
+            mExtras = new Bundle();
+        }
+        mExtras.putCharSequenceArray(name, value);
+        return this;
+    }
+
+    /**
+     * Add extended data to the intent.  The name must include a package
+     * prefix, for example the app com.android.contacts would use names
+     * like "com.android.contacts.ShowAll".
+     *
+     * @param name The name of the extra data, with package prefix.
+     * @param value The Bundle data value.
+     *
+     * @return Returns the same Intent object, for chaining multiple calls
+     * into a single statement.
+     *
+     * @see #putExtras
+     * @see #removeExtra
+     * @see #getBundleExtra(String)
+     */
+    public @NonNull Intent putExtra(String name, @Nullable Bundle value) {
+        if (mExtras == null) {
+            mExtras = new Bundle();
+        }
+        mExtras.putBundle(name, value);
+        return this;
+    }
+
+    /**
+     * Add extended data to the intent.  The name must include a package
+     * prefix, for example the app com.android.contacts would use names
+     * like "com.android.contacts.ShowAll".
+     *
+     * @param name The name of the extra data, with package prefix.
+     * @param value The IBinder data value.
+     *
+     * @return Returns the same Intent object, for chaining multiple calls
+     * into a single statement.
+     *
+     * @see #putExtras
+     * @see #removeExtra
+     * @see #getIBinderExtra(String)
+     *
+     * @deprecated
+     * @hide
+     */
+    @Deprecated
+    @UnsupportedAppUsage
+    public @NonNull Intent putExtra(String name, IBinder value) {
+        if (mExtras == null) {
+            mExtras = new Bundle();
+        }
+        mExtras.putIBinder(name, value);
+        return this;
+    }
+
+    /**
+     * Copy all extras in 'src' in to this intent.
+     *
+     * @param src Contains the extras to copy.
+     *
+     * @see #putExtra
+     */
+    public @NonNull Intent putExtras(@NonNull Intent src) {
+        if (src.mExtras != null) {
+            if (mExtras == null) {
+                mExtras = new Bundle(src.mExtras);
+            } else {
+                mExtras.putAll(src.mExtras);
+            }
+        }
+        return this;
+    }
+
+    /**
+     * Add a set of extended data to the intent.  The keys must include a package
+     * prefix, for example the app com.android.contacts would use names
+     * like "com.android.contacts.ShowAll".
+     *
+     * @param extras The Bundle of extras to add to this intent.
+     *
+     * @see #putExtra
+     * @see #removeExtra
+     */
+    public @NonNull Intent putExtras(@NonNull Bundle extras) {
+        if (mExtras == null) {
+            mExtras = new Bundle();
+        }
+        mExtras.putAll(extras);
+        return this;
+    }
+
+    /**
+     * Completely replace the extras in the Intent with the extras in the
+     * given Intent.
+     *
+     * @param src The exact extras contained in this Intent are copied
+     * into the target intent, replacing any that were previously there.
+     */
+    public @NonNull Intent replaceExtras(@NonNull Intent src) {
+        mExtras = src.mExtras != null ? new Bundle(src.mExtras) : null;
+        return this;
+    }
+
+    /**
+     * Completely replace the extras in the Intent with the given Bundle of
+     * extras.
+     *
+     * @param extras The new set of extras in the Intent, or null to erase
+     * all extras.
+     */
+    public @NonNull Intent replaceExtras(@Nullable Bundle extras) {
+        mExtras = extras != null ? new Bundle(extras) : null;
+        return this;
+    }
+
+    /**
+     * Remove extended data from the intent.
+     *
+     * @see #putExtra
+     */
+    public void removeExtra(String name) {
+        if (mExtras != null) {
+            mExtras.remove(name);
+            if (mExtras.size() == 0) {
+                mExtras = null;
+            }
+        }
+    }
+
+    /**
+     * Set special flags controlling how this intent is handled.  Most values
+     * here depend on the type of component being executed by the Intent,
+     * specifically the FLAG_ACTIVITY_* flags are all for use with
+     * {@link Context#startActivity Context.startActivity()} and the
+     * FLAG_RECEIVER_* flags are all for use with
+     * {@link Context#sendBroadcast(Intent) Context.sendBroadcast()}.
+     *
+     * <p>See the
+     * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
+     * Stack</a> documentation for important information on how some of these options impact
+     * the behavior of your application.
+     *
+     * @param flags The desired flags.
+     * @return Returns the same Intent object, for chaining multiple calls
+     * into a single statement.
+     * @see #getFlags
+     * @see #addFlags
+     * @see #removeFlags
+     */
+    public @NonNull Intent setFlags(@Flags int flags) {
+        mFlags = flags;
+        return this;
+    }
+
+    /**
+     * Add additional flags to the intent (or with existing flags value).
+     *
+     * @param flags The new flags to set.
+     * @return Returns the same Intent object, for chaining multiple calls into
+     *         a single statement.
+     * @see #setFlags
+     * @see #getFlags
+     * @see #removeFlags
+     */
+    public @NonNull Intent addFlags(@Flags int flags) {
+        mFlags |= flags;
+        return this;
+    }
+
+    /**
+     * Remove these flags from the intent.
+     *
+     * @param flags The flags to remove.
+     * @see #setFlags
+     * @see #getFlags
+     * @see #addFlags
+     */
+    public void removeFlags(@Flags int flags) {
+        mFlags &= ~flags;
+    }
+
+    /**
+     * (Usually optional) Set an explicit application package name that limits
+     * the components this Intent will resolve to.  If left to the default
+     * value of null, all components in all applications will considered.
+     * If non-null, the Intent can only match the components in the given
+     * application package.
+     *
+     * @param packageName The name of the application package to handle the
+     * intent, or null to allow any application package.
+     *
+     * @return Returns the same Intent object, for chaining multiple calls
+     * into a single statement.
+     *
+     * @see #getPackage
+     * @see #resolveActivity
+     */
+    public @NonNull Intent setPackage(@Nullable String packageName) {
+        if (packageName != null && mSelector != null) {
+            throw new IllegalArgumentException(
+                    "Can't set package name when selector is already set");
+        }
+        mPackage = packageName;
+        return this;
+    }
+
+    /**
+     * (Usually optional) Explicitly set the component to handle the intent.
+     * If left with the default value of null, the system will determine the
+     * appropriate class to use based on the other fields (action, data,
+     * type, categories) in the Intent.  If this class is defined, the
+     * specified class will always be used regardless of the other fields.  You
+     * should only set this value when you know you absolutely want a specific
+     * class to be used; otherwise it is better to let the system find the
+     * appropriate class so that you will respect the installed applications
+     * and user preferences.
+     *
+     * @param component The name of the application component to handle the
+     * intent, or null to let the system find one for you.
+     *
+     * @return Returns the same Intent object, for chaining multiple calls
+     * into a single statement.
+     *
+     * @see #setClass
+     * @see #setClassName(Context, String)
+     * @see #setClassName(String, String)
+     * @see #getComponent
+     * @see #resolveActivity
+     */
+    public @NonNull Intent setComponent(@Nullable ComponentName component) {
+        mComponent = component;
+        return this;
+    }
+
+    /**
+     * Convenience for calling {@link #setComponent} with an
+     * explicit class name.
+     *
+     * @param packageContext A Context of the application package implementing
+     * this class.
+     * @param className The name of a class inside of the application package
+     * that will be used as the component for this Intent.
+     *
+     * @return Returns the same Intent object, for chaining multiple calls
+     * into a single statement.
+     *
+     * @see #setComponent
+     * @see #setClass
+     */
+    public @NonNull Intent setClassName(@NonNull Context packageContext,
+            @NonNull String className) {
+        mComponent = new ComponentName(packageContext, className);
+        return this;
+    }
+
+    /**
+     * Convenience for calling {@link #setComponent} with an
+     * explicit application package name and class name.
+     *
+     * @param packageName The name of the package implementing the desired
+     * component.
+     * @param className The name of a class inside of the application package
+     * that will be used as the component for this Intent.
+     *
+     * @return Returns the same Intent object, for chaining multiple calls
+     * into a single statement.
+     *
+     * @see #setComponent
+     * @see #setClass
+     */
+    public @NonNull Intent setClassName(@NonNull String packageName, @NonNull String className) {
+        mComponent = new ComponentName(packageName, className);
+        return this;
+    }
+
+    /**
+     * Convenience for calling {@link #setComponent(ComponentName)} with the
+     * name returned by a {@link Class} object.
+     *
+     * @param packageContext A Context of the application package implementing
+     * this class.
+     * @param cls The class name to set, equivalent to
+     *            <code>setClassName(context, cls.getName())</code>.
+     *
+     * @return Returns the same Intent object, for chaining multiple calls
+     * into a single statement.
+     *
+     * @see #setComponent
+     */
+    public @NonNull Intent setClass(@NonNull Context packageContext, @NonNull Class<?> cls) {
+        mComponent = new ComponentName(packageContext, cls);
+        return this;
+    }
+
+    /**
+     * Set the bounds of the sender of this intent, in screen coordinates.  This can be
+     * used as a hint to the receiver for animations and the like.  Null means that there
+     * is no source bounds.
+     */
+    public void setSourceBounds(@Nullable Rect r) {
+        if (r != null) {
+            mSourceBounds = new Rect(r);
+        } else {
+            mSourceBounds = null;
+        }
+    }
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "FILL_IN_" }, value = {
+            FILL_IN_ACTION,
+            FILL_IN_DATA,
+            FILL_IN_CATEGORIES,
+            FILL_IN_COMPONENT,
+            FILL_IN_PACKAGE,
+            FILL_IN_SOURCE_BOUNDS,
+            FILL_IN_SELECTOR,
+            FILL_IN_CLIP_DATA
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FillInFlags {}
+
+    /**
+     * Use with {@link #fillIn} to allow the current action value to be
+     * overwritten, even if it is already set.
+     */
+    public static final int FILL_IN_ACTION = 1<<0;
+
+    /**
+     * Use with {@link #fillIn} to allow the current data or type value
+     * overwritten, even if it is already set.
+     */
+    public static final int FILL_IN_DATA = 1<<1;
+
+    /**
+     * Use with {@link #fillIn} to allow the current categories to be
+     * overwritten, even if they are already set.
+     */
+    public static final int FILL_IN_CATEGORIES = 1<<2;
+
+    /**
+     * Use with {@link #fillIn} to allow the current component value to be
+     * overwritten, even if it is already set.
+     */
+    public static final int FILL_IN_COMPONENT = 1<<3;
+
+    /**
+     * Use with {@link #fillIn} to allow the current package value to be
+     * overwritten, even if it is already set.
+     */
+    public static final int FILL_IN_PACKAGE = 1<<4;
+
+    /**
+     * Use with {@link #fillIn} to allow the current bounds rectangle to be
+     * overwritten, even if it is already set.
+     */
+    public static final int FILL_IN_SOURCE_BOUNDS = 1<<5;
+
+    /**
+     * Use with {@link #fillIn} to allow the current selector to be
+     * overwritten, even if it is already set.
+     */
+    public static final int FILL_IN_SELECTOR = 1<<6;
+
+    /**
+     * Use with {@link #fillIn} to allow the current ClipData to be
+     * overwritten, even if it is already set.
+     */
+    public static final int FILL_IN_CLIP_DATA = 1<<7;
+
+    /**
+     * Use with {@link #fillIn} to allow the current identifier value to be
+     * overwritten, even if it is already set.
+     */
+    public static final int FILL_IN_IDENTIFIER = 1<<8;
+
+    /**
+     * Copy the contents of <var>other</var> in to this object, but only
+     * where fields are not defined by this object.  For purposes of a field
+     * being defined, the following pieces of data in the Intent are
+     * considered to be separate fields:
+     *
+     * <ul>
+     * <li> action, as set by {@link #setAction}.
+     * <li> data Uri and MIME type, as set by {@link #setData(Uri)},
+     * {@link #setType(String)}, or {@link #setDataAndType(Uri, String)}.
+     * <li> identifier, as set by {@link #setIdentifier}.
+     * <li> categories, as set by {@link #addCategory}.
+     * <li> package, as set by {@link #setPackage}.
+     * <li> component, as set by {@link #setComponent(ComponentName)} or
+     * related methods.
+     * <li> source bounds, as set by {@link #setSourceBounds}.
+     * <li> selector, as set by {@link #setSelector(Intent)}.
+     * <li> clip data, as set by {@link #setClipData(ClipData)}.
+     * <li> each top-level name in the associated extras.
+     * </ul>
+     *
+     * <p>In addition, you can use the {@link #FILL_IN_ACTION},
+     * {@link #FILL_IN_DATA}, {@link #FILL_IN_IDENTIFIER}, {@link #FILL_IN_CATEGORIES},
+     * {@link #FILL_IN_PACKAGE}, {@link #FILL_IN_COMPONENT}, {@link #FILL_IN_SOURCE_BOUNDS},
+     * {@link #FILL_IN_SELECTOR}, and {@link #FILL_IN_CLIP_DATA} to override
+     * the restriction where the corresponding field will not be replaced if
+     * it is already set.
+     *
+     * <p>Note: The component field will only be copied if {@link #FILL_IN_COMPONENT}
+     * is explicitly specified.  The selector will only be copied if
+     * {@link #FILL_IN_SELECTOR} is explicitly specified.
+     *
+     * <p>For example, consider Intent A with {data="foo", categories="bar"}
+     * and Intent B with {action="gotit", data-type="some/thing",
+     * categories="one","two"}.
+     *
+     * <p>Calling A.fillIn(B, Intent.FILL_IN_DATA) will result in A now
+     * containing: {action="gotit", data-type="some/thing",
+     * categories="bar"}.
+     *
+     * @param other Another Intent whose values are to be used to fill in
+     * the current one.
+     * @param flags Options to control which fields can be filled in.
+     *
+     * @return Returns a bit mask of {@link #FILL_IN_ACTION},
+     * {@link #FILL_IN_DATA}, {@link #FILL_IN_CATEGORIES}, {@link #FILL_IN_PACKAGE},
+     * {@link #FILL_IN_COMPONENT}, {@link #FILL_IN_SOURCE_BOUNDS},
+     * {@link #FILL_IN_SELECTOR} and {@link #FILL_IN_CLIP_DATA} indicating which fields were
+     * changed.
+     */
+    @FillInFlags
+    public int fillIn(@NonNull Intent other, @FillInFlags int flags) {
+        int changes = 0;
+        boolean mayHaveCopiedUris = false;
+        if (other.mAction != null
+                && (mAction == null || (flags&FILL_IN_ACTION) != 0)) {
+            mAction = other.mAction;
+            changes |= FILL_IN_ACTION;
+        }
+        if ((other.mData != null || other.mType != null)
+                && ((mData == null && mType == null)
+                        || (flags&FILL_IN_DATA) != 0)) {
+            mData = other.mData;
+            mType = other.mType;
+            changes |= FILL_IN_DATA;
+            mayHaveCopiedUris = true;
+        }
+        if (other.mIdentifier != null
+                && (mIdentifier == null || (flags&FILL_IN_IDENTIFIER) != 0)) {
+            mIdentifier = other.mIdentifier;
+            changes |= FILL_IN_IDENTIFIER;
+        }
+        if (other.mCategories != null
+                && (mCategories == null || (flags&FILL_IN_CATEGORIES) != 0)) {
+            if (other.mCategories != null) {
+                mCategories = new ArraySet<String>(other.mCategories);
+            }
+            changes |= FILL_IN_CATEGORIES;
+        }
+        if (other.mPackage != null
+                && (mPackage == null || (flags&FILL_IN_PACKAGE) != 0)) {
+            // Only do this if mSelector is not set.
+            if (mSelector == null) {
+                mPackage = other.mPackage;
+                changes |= FILL_IN_PACKAGE;
+            }
+        }
+        // Selector is special: it can only be set if explicitly allowed,
+        // for the same reason as the component name.
+        if (other.mSelector != null && (flags&FILL_IN_SELECTOR) != 0) {
+            if (mPackage == null) {
+                mSelector = new Intent(other.mSelector);
+                mPackage = null;
+                changes |= FILL_IN_SELECTOR;
+            }
+        }
+        if (other.mClipData != null
+                && (mClipData == null || (flags&FILL_IN_CLIP_DATA) != 0)) {
+            mClipData = other.mClipData;
+            changes |= FILL_IN_CLIP_DATA;
+            mayHaveCopiedUris = true;
+        }
+        // Component is special: it can -only- be set if explicitly allowed,
+        // since otherwise the sender could force the intent somewhere the
+        // originator didn't intend.
+        if (other.mComponent != null && (flags&FILL_IN_COMPONENT) != 0) {
+            mComponent = other.mComponent;
+            changes |= FILL_IN_COMPONENT;
+        }
+        mFlags |= other.mFlags;
+        if (other.mSourceBounds != null
+                && (mSourceBounds == null || (flags&FILL_IN_SOURCE_BOUNDS) != 0)) {
+            mSourceBounds = new Rect(other.mSourceBounds);
+            changes |= FILL_IN_SOURCE_BOUNDS;
+        }
+        if (mExtras == null) {
+            if (other.mExtras != null) {
+                mExtras = new Bundle(other.mExtras);
+                mayHaveCopiedUris = true;
+            }
+        } else if (other.mExtras != null) {
+            try {
+                Bundle newb = new Bundle(other.mExtras);
+                newb.putAll(mExtras);
+                mExtras = newb;
+                mayHaveCopiedUris = true;
+            } catch (RuntimeException e) {
+                // Modifying the extras can cause us to unparcel the contents
+                // of the bundle, and if we do this in the system process that
+                // may fail.  We really should handle this (i.e., the Bundle
+                // impl shouldn't be on top of a plain map), but for now just
+                // ignore it and keep the original contents. :(
+                Log.w(TAG, "Failure filling in extras", e);
+            }
+        }
+        if (mayHaveCopiedUris && mContentUserHint == UserHandle.USER_CURRENT
+                && other.mContentUserHint != UserHandle.USER_CURRENT) {
+            mContentUserHint = other.mContentUserHint;
+        }
+        return changes;
+    }
+
+    /**
+     * Wrapper class holding an Intent and implementing comparisons on it for
+     * the purpose of filtering.  The class implements its
+     * {@link #equals equals()} and {@link #hashCode hashCode()} methods as
+     * simple calls to {@link Intent#filterEquals(Intent)}  filterEquals()} and
+     * {@link android.content.Intent#filterHashCode()}  filterHashCode()}
+     * on the wrapped Intent.
+     */
+    public static final class FilterComparison {
+        private final Intent mIntent;
+        private final int mHashCode;
+
+        public FilterComparison(Intent intent) {
+            mIntent = intent;
+            mHashCode = intent.filterHashCode();
+        }
+
+        /**
+         * Return the Intent that this FilterComparison represents.
+         * @return Returns the Intent held by the FilterComparison.  Do
+         * not modify!
+         */
+        public Intent getIntent() {
+            return mIntent;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj instanceof FilterComparison) {
+                Intent other = ((FilterComparison)obj).mIntent;
+                return mIntent.filterEquals(other);
+            }
+            return false;
+        }
+
+        @Override
+        public int hashCode() {
+            return mHashCode;
+        }
+    }
+
+    /**
+     * Determine if two intents are the same for the purposes of intent
+     * resolution (filtering). That is, if their action, data, type, identity,
+     * class, and categories are the same.  This does <em>not</em> compare
+     * any extra data included in the intents.  Note that technically when actually
+     * matching against an {@link IntentFilter} the identifier is ignored, while here
+     * it is directly compared for equality like the other fields.
+     *
+     * @param other The other Intent to compare against.
+     *
+     * @return Returns true if action, data, type, class, and categories
+     *         are the same.
+     */
+    public boolean filterEquals(Intent other) {
+        if (other == null) {
+            return false;
+        }
+        if (!Objects.equals(this.mAction, other.mAction)) return false;
+        if (!Objects.equals(this.mData, other.mData)) return false;
+        if (!Objects.equals(this.mType, other.mType)) return false;
+        if (!Objects.equals(this.mIdentifier, other.mIdentifier)) return false;
+        if (!(this.hasPackageEquivalentComponent() && other.hasPackageEquivalentComponent())
+                && !Objects.equals(this.mPackage, other.mPackage)) {
+            return false;
+        }
+        if (!Objects.equals(this.mComponent, other.mComponent)) return false;
+        if (!Objects.equals(this.mCategories, other.mCategories)) return false;
+
+        return true;
+    }
+
+    /**
+     * Return {@code true} if the component name is not null and is in the same package that this
+     * intent limited to. otherwise return {@code false}.
+     */
+    private boolean hasPackageEquivalentComponent() {
+        return mComponent != null
+            && (mPackage == null || mPackage.equals(mComponent.getPackageName()));
+    }
+
+    /**
+     * Generate hash code that matches semantics of filterEquals().
+     *
+     * @return Returns the hash value of the action, data, type, class, and
+     *         categories.
+     *
+     * @see #filterEquals
+     */
+    public int filterHashCode() {
+        int code = 0;
+        if (mAction != null) {
+            code += mAction.hashCode();
+        }
+        if (mData != null) {
+            code += mData.hashCode();
+        }
+        if (mType != null) {
+            code += mType.hashCode();
+        }
+        if (mIdentifier != null) {
+            code += mIdentifier.hashCode();
+        }
+        if (mPackage != null) {
+            code += mPackage.hashCode();
+        }
+        if (mComponent != null) {
+            code += mComponent.hashCode();
+        }
+        if (mCategories != null) {
+            code += mCategories.hashCode();
+        }
+        return code;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder b = new StringBuilder(128);
+
+        b.append("Intent { ");
+        toShortString(b, true, true, true, false);
+        b.append(" }");
+
+        return b.toString();
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage
+    public String toInsecureString() {
+        StringBuilder b = new StringBuilder(128);
+
+        b.append("Intent { ");
+        toShortString(b, false, true, true, false);
+        b.append(" }");
+
+        return b.toString();
+    }
+
+    /** @hide */
+    public String toInsecureStringWithClip() {
+        StringBuilder b = new StringBuilder(128);
+
+        b.append("Intent { ");
+        toShortString(b, false, true, true, true);
+        b.append(" }");
+
+        return b.toString();
+    }
+
+    /** @hide */
+    public String toShortString(boolean secure, boolean comp, boolean extras, boolean clip) {
+        StringBuilder b = new StringBuilder(128);
+        toShortString(b, secure, comp, extras, clip);
+        return b.toString();
+    }
+
+    /** @hide */
+    public void toShortString(StringBuilder b, boolean secure, boolean comp, boolean extras,
+            boolean clip) {
+        boolean first = true;
+        if (mAction != null) {
+            b.append("act=").append(mAction);
+            first = false;
+        }
+        if (mCategories != null) {
+            if (!first) {
+                b.append(' ');
+            }
+            first = false;
+            b.append("cat=[");
+            for (int i=0; i<mCategories.size(); i++) {
+                if (i > 0) b.append(',');
+                b.append(mCategories.valueAt(i));
+            }
+            b.append("]");
+        }
+        if (mData != null) {
+            if (!first) {
+                b.append(' ');
+            }
+            first = false;
+            b.append("dat=");
+            if (secure) {
+                b.append(mData.toSafeString());
+            } else {
+                b.append(mData);
+            }
+        }
+        if (mType != null) {
+            if (!first) {
+                b.append(' ');
+            }
+            first = false;
+            b.append("typ=").append(mType);
+        }
+        if (mIdentifier != null) {
+            if (!first) {
+                b.append(' ');
+            }
+            first = false;
+            b.append("id=").append(mIdentifier);
+        }
+        if (mFlags != 0) {
+            if (!first) {
+                b.append(' ');
+            }
+            first = false;
+            b.append("flg=0x").append(Integer.toHexString(mFlags));
+        }
+        if (mPackage != null) {
+            if (!first) {
+                b.append(' ');
+            }
+            first = false;
+            b.append("pkg=").append(mPackage);
+        }
+        if (comp && mComponent != null) {
+            if (!first) {
+                b.append(' ');
+            }
+            first = false;
+            b.append("cmp=").append(mComponent.flattenToShortString());
+        }
+        if (mSourceBounds != null) {
+            if (!first) {
+                b.append(' ');
+            }
+            first = false;
+            b.append("bnds=").append(mSourceBounds.toShortString());
+        }
+        if (mClipData != null) {
+            if (!first) {
+                b.append(' ');
+            }
+            b.append("clip={");
+            if (clip) {
+                mClipData.toShortString(b);
+            } else {
+                if (mClipData.getDescription() != null) {
+                    first = !mClipData.getDescription().toShortStringTypesOnly(b);
+                } else {
+                    first = true;
+                }
+                mClipData.toShortStringShortItems(b, first);
+            }
+            first = false;
+            b.append('}');
+        }
+        if (extras && mExtras != null) {
+            if (!first) {
+                b.append(' ');
+            }
+            first = false;
+            b.append("(has extras)");
+        }
+        if (mContentUserHint != UserHandle.USER_CURRENT) {
+            if (!first) {
+                b.append(' ');
+            }
+            first = false;
+            b.append("u=").append(mContentUserHint);
+        }
+        if (mSelector != null) {
+            b.append(" sel=");
+            mSelector.toShortString(b, secure, comp, extras, clip);
+            b.append("}");
+        }
+    }
+
+    /** @hide */
+    public void dumpDebug(ProtoOutputStream proto, long fieldId) {
+        // Same input parameters that toString() gives to toShortString().
+        dumpDebug(proto, fieldId, true, true, true, false);
+    }
+
+    /** @hide */
+    public void dumpDebug(ProtoOutputStream proto) {
+        // Same input parameters that toString() gives to toShortString().
+        dumpDebugWithoutFieldId(proto, true, true, true, false);
+    }
+
+    /** @hide */
+    public void dumpDebug(ProtoOutputStream proto, long fieldId, boolean secure, boolean comp,
+            boolean extras, boolean clip) {
+        long token = proto.start(fieldId);
+        dumpDebugWithoutFieldId(proto, secure, comp, extras, clip);
+        proto.end(token);
+    }
+
+    private void dumpDebugWithoutFieldId(ProtoOutputStream proto, boolean secure, boolean comp,
+            boolean extras, boolean clip) {
+        if (mAction != null) {
+            proto.write(IntentProto.ACTION, mAction);
+        }
+        if (mCategories != null)  {
+            for (String category : mCategories) {
+                proto.write(IntentProto.CATEGORIES, category);
+            }
+        }
+        if (mData != null) {
+            proto.write(IntentProto.DATA, secure ? mData.toSafeString() : mData.toString());
+        }
+        if (mType != null) {
+            proto.write(IntentProto.TYPE, mType);
+        }
+        if (mIdentifier != null) {
+            proto.write(IntentProto.IDENTIFIER, mIdentifier);
+        }
+        if (mFlags != 0) {
+            proto.write(IntentProto.FLAG, "0x" + Integer.toHexString(mFlags));
+        }
+        if (mPackage != null) {
+            proto.write(IntentProto.PACKAGE, mPackage);
+        }
+        if (comp && mComponent != null) {
+            mComponent.dumpDebug(proto, IntentProto.COMPONENT);
+        }
+        if (mSourceBounds != null) {
+            proto.write(IntentProto.SOURCE_BOUNDS, mSourceBounds.toShortString());
+        }
+        if (mClipData != null) {
+            StringBuilder b = new StringBuilder();
+            if (clip) {
+                mClipData.toShortString(b);
+            } else {
+                mClipData.toShortStringShortItems(b, false);
+            }
+            proto.write(IntentProto.CLIP_DATA, b.toString());
+        }
+        if (extras && mExtras != null) {
+            proto.write(IntentProto.EXTRAS, mExtras.toShortString());
+        }
+        if (mContentUserHint != 0) {
+            proto.write(IntentProto.CONTENT_USER_HINT, mContentUserHint);
+        }
+        if (mSelector != null) {
+            proto.write(IntentProto.SELECTOR, mSelector.toShortString(secure, comp, extras, clip));
+        }
+    }
+
+    /**
+     * Call {@link #toUri} with 0 flags.
+     * @deprecated Use {@link #toUri} instead.
+     */
+    @Deprecated
+    public String toURI() {
+        return toUri(0);
+    }
+
+    /**
+     * Convert this Intent into a String holding a URI representation of it.
+     * The returned URI string has been properly URI encoded, so it can be
+     * used with {@link Uri#parse Uri.parse(String)}.  The URI contains the
+     * Intent's data as the base URI, with an additional fragment describing
+     * the action, categories, type, flags, package, component, and extras.
+     *
+     * <p>You can convert the returned string back to an Intent with
+     * {@link #getIntent}.
+     *
+     * @param flags Additional operating flags.
+     *
+     * @return Returns a URI encoding URI string describing the entire contents
+     * of the Intent.
+     */
+    public String toUri(@UriFlags int flags) {
+        StringBuilder uri = new StringBuilder(128);
+        if ((flags&URI_ANDROID_APP_SCHEME) != 0) {
+            if (mPackage == null) {
+                throw new IllegalArgumentException(
+                        "Intent must include an explicit package name to build an android-app: "
+                        + this);
+            }
+            uri.append("android-app://");
+            uri.append(mPackage);
+            String scheme = null;
+            if (mData != null) {
+                scheme = mData.getScheme();
+                if (scheme != null) {
+                    uri.append('/');
+                    uri.append(scheme);
+                    String authority = mData.getEncodedAuthority();
+                    if (authority != null) {
+                        uri.append('/');
+                        uri.append(authority);
+                        String path = mData.getEncodedPath();
+                        if (path != null) {
+                            uri.append(path);
+                        }
+                        String queryParams = mData.getEncodedQuery();
+                        if (queryParams != null) {
+                            uri.append('?');
+                            uri.append(queryParams);
+                        }
+                        String fragment = mData.getEncodedFragment();
+                        if (fragment != null) {
+                            uri.append('#');
+                            uri.append(fragment);
+                        }
+                    }
+                }
+            }
+            toUriFragment(uri, null, scheme == null ? Intent.ACTION_MAIN : Intent.ACTION_VIEW,
+                    mPackage, flags);
+            return uri.toString();
+        }
+        String scheme = null;
+        if (mData != null) {
+            String data = mData.toString();
+            if ((flags&URI_INTENT_SCHEME) != 0) {
+                final int N = data.length();
+                for (int i=0; i<N; i++) {
+                    char c = data.charAt(i);
+                    if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
+                            || (c >= '0' && c <= '9') || c == '.' || c == '-' || c == '+') {
+                        continue;
+                    }
+                    if (c == ':' && i > 0) {
+                        // Valid scheme.
+                        scheme = data.substring(0, i);
+                        uri.append("intent:");
+                        data = data.substring(i+1);
+                        break;
+                    }
+
+                    // No scheme.
+                    break;
+                }
+            }
+            uri.append(data);
+
+        } else if ((flags&URI_INTENT_SCHEME) != 0) {
+            uri.append("intent:");
+        }
+
+        toUriFragment(uri, scheme, Intent.ACTION_VIEW, null, flags);
+
+        return uri.toString();
+    }
+
+    private void toUriFragment(StringBuilder uri, String scheme, String defAction,
+            String defPackage, int flags) {
+        StringBuilder frag = new StringBuilder(128);
+
+        toUriInner(frag, scheme, defAction, defPackage, flags);
+        if (mSelector != null) {
+            frag.append("SEL;");
+            // Note that for now we are not going to try to handle the
+            // data part; not clear how to represent this as a URI, and
+            // not much utility in it.
+            mSelector.toUriInner(frag, mSelector.mData != null ? mSelector.mData.getScheme() : null,
+                    null, null, flags);
+        }
+
+        if (frag.length() > 0) {
+            uri.append("#Intent;");
+            uri.append(frag);
+            uri.append("end");
+        }
+    }
+
+    private void toUriInner(StringBuilder uri, String scheme, String defAction,
+            String defPackage, int flags) {
+        if (scheme != null) {
+            uri.append("scheme=").append(scheme).append(';');
+        }
+        if (mAction != null && !mAction.equals(defAction)) {
+            uri.append("action=").append(Uri.encode(mAction)).append(';');
+        }
+        if (mCategories != null) {
+            for (int i=0; i<mCategories.size(); i++) {
+                uri.append("category=").append(Uri.encode(mCategories.valueAt(i))).append(';');
+            }
+        }
+        if (mType != null) {
+            uri.append("type=").append(Uri.encode(mType, "/")).append(';');
+        }
+        if (mIdentifier != null) {
+            uri.append("identifier=").append(Uri.encode(mIdentifier, "/")).append(';');
+        }
+        if (mFlags != 0) {
+            uri.append("launchFlags=0x").append(Integer.toHexString(mFlags)).append(';');
+        }
+        if (mPackage != null && !mPackage.equals(defPackage)) {
+            uri.append("package=").append(Uri.encode(mPackage)).append(';');
+        }
+        if (mComponent != null) {
+            uri.append("component=").append(Uri.encode(
+                    mComponent.flattenToShortString(), "/")).append(';');
+        }
+        if (mSourceBounds != null) {
+            uri.append("sourceBounds=")
+                    .append(Uri.encode(mSourceBounds.flattenToString()))
+                    .append(';');
+        }
+        if (mExtras != null) {
+            for (String key : mExtras.keySet()) {
+                final Object value = mExtras.get(key);
+                char entryType =
+                        value instanceof String    ? 'S' :
+                        value instanceof Boolean   ? 'B' :
+                        value instanceof Byte      ? 'b' :
+                        value instanceof Character ? 'c' :
+                        value instanceof Double    ? 'd' :
+                        value instanceof Float     ? 'f' :
+                        value instanceof Integer   ? 'i' :
+                        value instanceof Long      ? 'l' :
+                        value instanceof Short     ? 's' :
+                        '\0';
+
+                if (entryType != '\0') {
+                    uri.append(entryType);
+                    uri.append('.');
+                    uri.append(Uri.encode(key));
+                    uri.append('=');
+                    uri.append(Uri.encode(value.toString()));
+                    uri.append(';');
+                }
+            }
+        }
+    }
+
+    public int describeContents() {
+        return (mExtras != null) ? mExtras.describeContents() : 0;
+    }
+
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeString8(mAction);
+        Uri.writeToParcel(out, mData);
+        out.writeString8(mType);
+        out.writeString8(mIdentifier);
+        out.writeInt(mFlags);
+        out.writeString8(mPackage);
+        ComponentName.writeToParcel(mComponent, out);
+
+        if (mSourceBounds != null) {
+            out.writeInt(1);
+            mSourceBounds.writeToParcel(out, flags);
+        } else {
+            out.writeInt(0);
+        }
+
+        if (mCategories != null) {
+            final int N = mCategories.size();
+            out.writeInt(N);
+            for (int i=0; i<N; i++) {
+                out.writeString8(mCategories.valueAt(i));
+            }
+        } else {
+            out.writeInt(0);
+        }
+
+        if (mSelector != null) {
+            out.writeInt(1);
+            mSelector.writeToParcel(out, flags);
+        } else {
+            out.writeInt(0);
+        }
+
+        if (mClipData != null) {
+            out.writeInt(1);
+            mClipData.writeToParcel(out, flags);
+        } else {
+            out.writeInt(0);
+        }
+        out.writeInt(mContentUserHint);
+        out.writeBundle(mExtras);
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<Intent> CREATOR
+            = new Parcelable.Creator<Intent>() {
+        public Intent createFromParcel(Parcel in) {
+            return new Intent(in);
+        }
+        public Intent[] newArray(int size) {
+            return new Intent[size];
+        }
+    };
+
+    /** @hide */
+    protected Intent(Parcel in) {
+        readFromParcel(in);
+    }
+
+    public void readFromParcel(Parcel in) {
+        setAction(in.readString8());
+        mData = Uri.CREATOR.createFromParcel(in);
+        mType = in.readString8();
+        mIdentifier = in.readString8();
+        mFlags = in.readInt();
+        mPackage = in.readString8();
+        mComponent = ComponentName.readFromParcel(in);
+
+        if (in.readInt() != 0) {
+            mSourceBounds = Rect.CREATOR.createFromParcel(in);
+        }
+
+        int N = in.readInt();
+        if (N > 0) {
+            mCategories = new ArraySet<String>();
+            int i;
+            for (i=0; i<N; i++) {
+                mCategories.add(in.readString8().intern());
+            }
+        } else {
+            mCategories = null;
+        }
+
+        if (in.readInt() != 0) {
+            mSelector = new Intent(in);
+        }
+
+        if (in.readInt() != 0) {
+            mClipData = new ClipData(in);
+        }
+        mContentUserHint = in.readInt();
+        mExtras = in.readBundle();
+    }
+
+    /**
+     * Parses the "intent" element (and its children) from XML and instantiates
+     * an Intent object.  The given XML parser should be located at the tag
+     * where parsing should start (often named "intent"), from which the
+     * basic action, data, type, and package and class name will be
+     * retrieved.  The function will then parse in to any child elements,
+     * looking for <category android:name="xxx"> tags to add categories and
+     * <extra android:name="xxx" android:value="yyy"> to attach extra data
+     * to the intent.
+     *
+     * @param resources The Resources to use when inflating resources.
+     * @param parser The XML parser pointing at an "intent" tag.
+     * @param attrs The AttributeSet interface for retrieving extended
+     * attribute data at the current <var>parser</var> location.
+     * @return An Intent object matching the XML data.
+     * @throws XmlPullParserException If there was an XML parsing error.
+     * @throws IOException If there was an I/O error.
+     */
+    public static @NonNull Intent parseIntent(@NonNull Resources resources,
+            @NonNull XmlPullParser parser, AttributeSet attrs)
+            throws XmlPullParserException, IOException {
+        Intent intent = new Intent();
+
+        TypedArray sa = resources.obtainAttributes(attrs,
+                com.android.internal.R.styleable.Intent);
+
+        intent.setAction(sa.getString(com.android.internal.R.styleable.Intent_action));
+
+        String data = sa.getString(com.android.internal.R.styleable.Intent_data);
+        String mimeType = sa.getString(com.android.internal.R.styleable.Intent_mimeType);
+        intent.setDataAndType(data != null ? Uri.parse(data) : null, mimeType);
+
+        intent.setIdentifier(sa.getString(com.android.internal.R.styleable.Intent_identifier));
+
+        String packageName = sa.getString(com.android.internal.R.styleable.Intent_targetPackage);
+        String className = sa.getString(com.android.internal.R.styleable.Intent_targetClass);
+        if (packageName != null && className != null) {
+            intent.setComponent(new ComponentName(packageName, className));
+        }
+
+        sa.recycle();
+
+        int outerDepth = parser.getDepth();
+        int type;
+        while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
+               && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+
+            String nodeName = parser.getName();
+            if (nodeName.equals(TAG_CATEGORIES)) {
+                sa = resources.obtainAttributes(attrs,
+                        com.android.internal.R.styleable.IntentCategory);
+                String cat = sa.getString(com.android.internal.R.styleable.IntentCategory_name);
+                sa.recycle();
+
+                if (cat != null) {
+                    intent.addCategory(cat);
+                }
+                XmlUtils.skipCurrentTag(parser);
+
+            } else if (nodeName.equals(TAG_EXTRA)) {
+                if (intent.mExtras == null) {
+                    intent.mExtras = new Bundle();
+                }
+                resources.parseBundleExtra(TAG_EXTRA, attrs, intent.mExtras);
+                XmlUtils.skipCurrentTag(parser);
+
+            } else {
+                XmlUtils.skipCurrentTag(parser);
+            }
+        }
+
+        return intent;
+    }
+
+    /** @hide */
+    public void saveToXml(XmlSerializer out) throws IOException {
+        if (mAction != null) {
+            out.attribute(null, ATTR_ACTION, mAction);
+        }
+        if (mData != null) {
+            out.attribute(null, ATTR_DATA, mData.toString());
+        }
+        if (mType != null) {
+            out.attribute(null, ATTR_TYPE, mType);
+        }
+        if (mIdentifier != null) {
+            out.attribute(null, ATTR_IDENTIFIER, mIdentifier);
+        }
+        if (mComponent != null) {
+            out.attribute(null, ATTR_COMPONENT, mComponent.flattenToShortString());
+        }
+        out.attribute(null, ATTR_FLAGS, Integer.toHexString(getFlags()));
+
+        if (mCategories != null) {
+            out.startTag(null, TAG_CATEGORIES);
+            for (int categoryNdx = mCategories.size() - 1; categoryNdx >= 0; --categoryNdx) {
+                out.attribute(null, ATTR_CATEGORY, mCategories.valueAt(categoryNdx));
+            }
+            out.endTag(null, TAG_CATEGORIES);
+        }
+    }
+
+    /** @hide */
+    public static Intent restoreFromXml(XmlPullParser in) throws IOException,
+            XmlPullParserException {
+        Intent intent = new Intent();
+        final int outerDepth = in.getDepth();
+
+        int attrCount = in.getAttributeCount();
+        for (int attrNdx = attrCount - 1; attrNdx >= 0; --attrNdx) {
+            final String attrName = in.getAttributeName(attrNdx);
+            final String attrValue = in.getAttributeValue(attrNdx);
+            if (ATTR_ACTION.equals(attrName)) {
+                intent.setAction(attrValue);
+            } else if (ATTR_DATA.equals(attrName)) {
+                intent.setData(Uri.parse(attrValue));
+            } else if (ATTR_TYPE.equals(attrName)) {
+                intent.setType(attrValue);
+            } else if (ATTR_IDENTIFIER.equals(attrName)) {
+                intent.setIdentifier(attrValue);
+            } else if (ATTR_COMPONENT.equals(attrName)) {
+                intent.setComponent(ComponentName.unflattenFromString(attrValue));
+            } else if (ATTR_FLAGS.equals(attrName)) {
+                intent.setFlags(Integer.parseInt(attrValue, 16));
+            } else {
+                Log.e(TAG, "restoreFromXml: unknown attribute=" + attrName);
+            }
+        }
+
+        int event;
+        String name;
+        while (((event = in.next()) != XmlPullParser.END_DOCUMENT) &&
+                (event != XmlPullParser.END_TAG || in.getDepth() < outerDepth)) {
+            if (event == XmlPullParser.START_TAG) {
+                name = in.getName();
+                if (TAG_CATEGORIES.equals(name)) {
+                    attrCount = in.getAttributeCount();
+                    for (int attrNdx = attrCount - 1; attrNdx >= 0; --attrNdx) {
+                        intent.addCategory(in.getAttributeValue(attrNdx));
+                    }
+                } else {
+                    Log.w(TAG, "restoreFromXml: unknown name=" + name);
+                    XmlUtils.skipCurrentTag(in);
+                }
+            }
+        }
+
+        return intent;
+    }
+
+    /**
+     * Normalize a MIME data type.
+     *
+     * <p>A normalized MIME type has white-space trimmed,
+     * content-type parameters removed, and is lower-case.
+     * This aligns the type with Android best practices for
+     * intent filtering.
+     *
+     * <p>For example, "text/plain; charset=utf-8" becomes "text/plain".
+     * "text/x-vCard" becomes "text/x-vcard".
+     *
+     * <p>All MIME types received from outside Android (such as user input,
+     * or external sources like Bluetooth, NFC, or the Internet) should
+     * be normalized before they are used to create an Intent.
+     *
+     * @param type MIME data type to normalize
+     * @return normalized MIME data type, or null if the input was null
+     * @see #setType
+     * @see #setTypeAndNormalize
+     */
+    public static @Nullable String normalizeMimeType(@Nullable String type) {
+        if (type == null) {
+            return null;
+        }
+
+        type = type.trim().toLowerCase(Locale.ROOT);
+
+        final int semicolonIndex = type.indexOf(';');
+        if (semicolonIndex != -1) {
+            type = type.substring(0, semicolonIndex);
+        }
+        return type;
+    }
+
+    /**
+     * Prepare this {@link Intent} to leave an app process.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public void prepareToLeaveProcess(Context context) {
+        final boolean leavingPackage = (mComponent == null)
+                || !Objects.equals(mComponent.getPackageName(), context.getPackageName());
+        prepareToLeaveProcess(leavingPackage);
+    }
+
+    /**
+     * Prepare this {@link Intent} to leave an app process.
+     *
+     * @hide
+     */
+    public void prepareToLeaveProcess(boolean leavingPackage) {
+        setAllowFds(false);
+
+        if (mSelector != null) {
+            mSelector.prepareToLeaveProcess(leavingPackage);
+        }
+        if (mClipData != null) {
+            mClipData.prepareToLeaveProcess(leavingPackage, getFlags());
+        }
+
+        if (mExtras != null && !mExtras.isParcelled()) {
+            final Object intent = mExtras.get(Intent.EXTRA_INTENT);
+            if (intent instanceof Intent) {
+                ((Intent) intent).prepareToLeaveProcess(leavingPackage);
+            }
+        }
+
+        if (mAction != null && mData != null && StrictMode.vmFileUriExposureEnabled()
+                && leavingPackage) {
+            switch (mAction) {
+                case ACTION_MEDIA_REMOVED:
+                case ACTION_MEDIA_UNMOUNTED:
+                case ACTION_MEDIA_CHECKING:
+                case ACTION_MEDIA_NOFS:
+                case ACTION_MEDIA_MOUNTED:
+                case ACTION_MEDIA_SHARED:
+                case ACTION_MEDIA_UNSHARED:
+                case ACTION_MEDIA_BAD_REMOVAL:
+                case ACTION_MEDIA_UNMOUNTABLE:
+                case ACTION_MEDIA_EJECT:
+                case ACTION_MEDIA_SCANNER_STARTED:
+                case ACTION_MEDIA_SCANNER_FINISHED:
+                case ACTION_MEDIA_SCANNER_SCAN_FILE:
+                case ACTION_PACKAGE_NEEDS_VERIFICATION:
+                case ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION:
+                case ACTION_PACKAGE_VERIFIED:
+                case ACTION_PACKAGE_ENABLE_ROLLBACK:
+                    // Ignore legacy actions
+                    break;
+                default:
+                    mData.checkFileUriExposed("Intent.getData()");
+            }
+        }
+
+        if (mAction != null && mData != null && StrictMode.vmContentUriWithoutPermissionEnabled()
+                && leavingPackage) {
+            switch (mAction) {
+                case ACTION_PROVIDER_CHANGED:
+                case QuickContact.ACTION_QUICK_CONTACT:
+                    // Ignore actions that don't need to grant
+                    break;
+                default:
+                    mData.checkContentUriWithoutPermission("Intent.getData()", getFlags());
+            }
+        }
+
+        // Translate raw filesystem paths out of storage sandbox
+        if (ACTION_MEDIA_SCANNER_SCAN_FILE.equals(mAction) && mData != null
+                && ContentResolver.SCHEME_FILE.equals(mData.getScheme()) && leavingPackage) {
+            final StorageManager sm = AppGlobals.getInitialApplication()
+                    .getSystemService(StorageManager.class);
+            final File before = new File(mData.getPath());
+            final File after = sm.translateAppToSystem(before,
+                    android.os.Process.myPid(), android.os.Process.myUid());
+            if (!Objects.equals(before, after)) {
+                Log.v(TAG, "Translated " + before + " to " + after);
+                mData = Uri.fromFile(after);
+            }
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public void prepareToEnterProcess() {
+        // We just entered destination process, so we should be able to read all
+        // parcelables inside.
+        setDefusable(true);
+
+        if (mSelector != null) {
+            mSelector.prepareToEnterProcess();
+        }
+        if (mClipData != null) {
+            mClipData.prepareToEnterProcess();
+        }
+
+        if (mContentUserHint != UserHandle.USER_CURRENT) {
+            if (UserHandle.getAppId(Process.myUid()) != Process.SYSTEM_UID) {
+                fixUris(mContentUserHint);
+                mContentUserHint = UserHandle.USER_CURRENT;
+            }
+        }
+    }
+
+    /** @hide */
+    public boolean hasWebURI() {
+        if (getData() == null) {
+            return false;
+        }
+        final String scheme = getScheme();
+        if (TextUtils.isEmpty(scheme)) {
+            return false;
+        }
+        return scheme.equals(IntentFilter.SCHEME_HTTP) || scheme.equals(IntentFilter.SCHEME_HTTPS);
+    }
+
+    /** @hide */
+    public boolean isWebIntent() {
+        return ACTION_VIEW.equals(mAction)
+                && hasWebURI();
+    }
+
+    private boolean isImageCaptureIntent() {
+        return (MediaStore.ACTION_IMAGE_CAPTURE.equals(mAction)
+                || MediaStore.ACTION_IMAGE_CAPTURE_SECURE.equals(mAction)
+                || MediaStore.ACTION_VIDEO_CAPTURE.equals(mAction));
+    }
+
+    /** @hide */
+    public boolean isImplicitImageCaptureIntent() {
+        return mPackage == null && mComponent == null && isImageCaptureIntent();
+    }
+
+    /**
+     * @hide
+     */
+     public void fixUris(int contentUserHint) {
+        Uri data = getData();
+        if (data != null) {
+            mData = maybeAddUserId(data, contentUserHint);
+        }
+        if (mClipData != null) {
+            mClipData.fixUris(contentUserHint);
+        }
+        String action = getAction();
+        if (ACTION_SEND.equals(action)) {
+            final Uri stream = getParcelableExtra(EXTRA_STREAM);
+            if (stream != null) {
+                putExtra(EXTRA_STREAM, maybeAddUserId(stream, contentUserHint));
+            }
+        } else if (ACTION_SEND_MULTIPLE.equals(action)) {
+            final ArrayList<Uri> streams = getParcelableArrayListExtra(EXTRA_STREAM);
+            if (streams != null) {
+                ArrayList<Uri> newStreams = new ArrayList<Uri>();
+                for (int i = 0; i < streams.size(); i++) {
+                    newStreams.add(maybeAddUserId(streams.get(i), contentUserHint));
+                }
+                putParcelableArrayListExtra(EXTRA_STREAM, newStreams);
+            }
+        } else if (isImageCaptureIntent()) {
+            final Uri output = getParcelableExtra(MediaStore.EXTRA_OUTPUT);
+            if (output != null) {
+                putExtra(MediaStore.EXTRA_OUTPUT, maybeAddUserId(output, contentUserHint));
+            }
+        }
+     }
+
+    /**
+     * Migrate any {@link #EXTRA_STREAM} in {@link #ACTION_SEND} and
+     * {@link #ACTION_SEND_MULTIPLE} to {@link ClipData}. Also inspects nested
+     * intents in {@link #ACTION_CHOOSER}.
+     *
+     * @return Whether any contents were migrated.
+     * @hide
+     */
+    public boolean migrateExtraStreamToClipData() {
+        // Refuse to touch if extras already parcelled
+        if (mExtras != null && mExtras.isParcelled()) return false;
+
+        // Bail when someone already gave us ClipData
+        if (getClipData() != null) return false;
+
+        final String action = getAction();
+        if (ACTION_CHOOSER.equals(action)) {
+            // Inspect contained intents to see if we need to migrate extras. We
+            // don't promote ClipData to the parent, since ChooserActivity will
+            // already start the picked item as the caller, and we can't combine
+            // the flags in a safe way.
+
+            boolean migrated = false;
+            try {
+                final Intent intent = getParcelableExtra(EXTRA_INTENT);
+                if (intent != null) {
+                    migrated |= intent.migrateExtraStreamToClipData();
+                }
+            } catch (ClassCastException e) {
+            }
+            try {
+                final Parcelable[] intents = getParcelableArrayExtra(EXTRA_INITIAL_INTENTS);
+                if (intents != null) {
+                    for (int i = 0; i < intents.length; i++) {
+                        final Intent intent = (Intent) intents[i];
+                        if (intent != null) {
+                            migrated |= intent.migrateExtraStreamToClipData();
+                        }
+                    }
+                }
+            } catch (ClassCastException e) {
+            }
+            return migrated;
+
+        } else if (ACTION_SEND.equals(action)) {
+            try {
+                final Uri stream = getParcelableExtra(EXTRA_STREAM);
+                final CharSequence text = getCharSequenceExtra(EXTRA_TEXT);
+                final String htmlText = getStringExtra(EXTRA_HTML_TEXT);
+                if (stream != null || text != null || htmlText != null) {
+                    final ClipData clipData = new ClipData(
+                            null, new String[] { getType() },
+                            new ClipData.Item(text, htmlText, null, stream));
+                    setClipData(clipData);
+                    addFlags(FLAG_GRANT_READ_URI_PERMISSION);
+                    return true;
+                }
+            } catch (ClassCastException e) {
+            }
+
+        } else if (ACTION_SEND_MULTIPLE.equals(action)) {
+            try {
+                final ArrayList<Uri> streams = getParcelableArrayListExtra(EXTRA_STREAM);
+                final ArrayList<CharSequence> texts = getCharSequenceArrayListExtra(EXTRA_TEXT);
+                final ArrayList<String> htmlTexts = getStringArrayListExtra(EXTRA_HTML_TEXT);
+                int num = -1;
+                if (streams != null) {
+                    num = streams.size();
+                }
+                if (texts != null) {
+                    if (num >= 0 && num != texts.size()) {
+                        // Wha...!  F- you.
+                        return false;
+                    }
+                    num = texts.size();
+                }
+                if (htmlTexts != null) {
+                    if (num >= 0 && num != htmlTexts.size()) {
+                        // Wha...!  F- you.
+                        return false;
+                    }
+                    num = htmlTexts.size();
+                }
+                if (num > 0) {
+                    final ClipData clipData = new ClipData(
+                            null, new String[] { getType() },
+                            makeClipItem(streams, texts, htmlTexts, 0));
+
+                    for (int i = 1; i < num; i++) {
+                        clipData.addItem(makeClipItem(streams, texts, htmlTexts, i));
+                    }
+
+                    setClipData(clipData);
+                    addFlags(FLAG_GRANT_READ_URI_PERMISSION);
+                    return true;
+                }
+            } catch (ClassCastException e) {
+            }
+        } else if (isImageCaptureIntent()) {
+            final Uri output;
+            try {
+                output = getParcelableExtra(MediaStore.EXTRA_OUTPUT);
+            } catch (ClassCastException e) {
+                return false;
+            }
+            if (output != null) {
+                setClipData(ClipData.newRawUri("", output));
+                addFlags(FLAG_GRANT_WRITE_URI_PERMISSION|FLAG_GRANT_READ_URI_PERMISSION);
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Convert the dock state to a human readable format.
+     * @hide
+     */
+    public static String dockStateToString(int dock) {
+        switch (dock) {
+            case EXTRA_DOCK_STATE_HE_DESK:
+                return "EXTRA_DOCK_STATE_HE_DESK";
+            case EXTRA_DOCK_STATE_LE_DESK:
+                return "EXTRA_DOCK_STATE_LE_DESK";
+            case EXTRA_DOCK_STATE_CAR:
+                return "EXTRA_DOCK_STATE_CAR";
+            case EXTRA_DOCK_STATE_DESK:
+                return "EXTRA_DOCK_STATE_DESK";
+            case EXTRA_DOCK_STATE_UNDOCKED:
+                return "EXTRA_DOCK_STATE_UNDOCKED";
+            default:
+                return Integer.toString(dock);
+        }
+    }
+
+    private static ClipData.Item makeClipItem(ArrayList<Uri> streams, ArrayList<CharSequence> texts,
+            ArrayList<String> htmlTexts, int which) {
+        Uri uri = streams != null ? streams.get(which) : null;
+        CharSequence text = texts != null ? texts.get(which) : null;
+        String htmlText = htmlTexts != null ? htmlTexts.get(which) : null;
+        return new ClipData.Item(text, htmlText, null, uri);
+    }
+
+    /** @hide */
+    public boolean isDocument() {
+        return (mFlags & FLAG_ACTIVITY_NEW_DOCUMENT) == FLAG_ACTIVITY_NEW_DOCUMENT;
+    }
+}
diff --git a/android/content/IntentFilter.java b/android/content/IntentFilter.java
new file mode 100644
index 0000000..745add1
--- /dev/null
+++ b/android/content/IntentFilter.java
@@ -0,0 +1,2466 @@
+/*
+ * 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.content;
+
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.PatternMatcher;
+import android.text.TextUtils;
+import android.util.AndroidException;
+import android.util.Log;
+import android.util.Printer;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.function.BiConsumer;
+
+/**
+ * Structured description of Intent values to be matched.  An IntentFilter can
+ * match against actions, categories, and data (either via its type, scheme,
+ * and/or path) in an Intent.  It also includes a "priority" value which is
+ * used to order multiple matching filters.
+ *
+ * <p>IntentFilter objects are often created in XML as part of a package's
+ * {@link android.R.styleable#AndroidManifest AndroidManifest.xml} file,
+ * using {@link android.R.styleable#AndroidManifestIntentFilter intent-filter}
+ * tags.
+ *
+ * <p>There are three Intent characteristics you can filter on: the
+ * <em>action</em>, <em>data</em>, and <em>categories</em>.  For each of these
+ * characteristics you can provide
+ * multiple possible matching values (via {@link #addAction},
+ * {@link #addDataType}, {@link #addDataScheme}, {@link #addDataSchemeSpecificPart},
+ * {@link #addDataAuthority}, {@link #addDataPath}, and {@link #addCategory}, respectively).
+ * For actions, if no data characteristics are specified, then the filter will
+ * only match intents that contain no data.
+ *
+ * <p>The data characteristic is
+ * itself divided into three attributes: type, scheme, authority, and path.
+ * Any that are
+ * specified must match the contents of the Intent.  If you specify a scheme
+ * but no type, only Intent that does not have a type (such as mailto:) will
+ * match; a content: URI will never match because they always have a MIME type
+ * that is supplied by their content provider.  Specifying a type with no scheme
+ * has somewhat special meaning: it will match either an Intent with no URI
+ * field, or an Intent with a content: or file: URI.  If you specify neither,
+ * then only an Intent with no data or type will match.  To specify an authority,
+ * you must also specify one or more schemes that it is associated with.
+ * To specify a path, you also must specify both one or more authorities and
+ * one or more schemes it is associated with.
+ *
+ * <div class="special reference">
+ * <h3>Developer Guides</h3>
+ * <p>For information about how to create and resolve intents, read the
+ * <a href="{@docRoot}guide/topics/intents/intents-filters.html">Intents and Intent Filters</a>
+ * developer guide.</p>
+ * </div>
+ *
+ * <h3>Filter Rules</h3>
+ * <p>A match is based on the following rules.  Note that
+ * for an IntentFilter to match an Intent, three conditions must hold:
+ * the <strong>action</strong> and <strong>category</strong> must match, and
+ * the data (both the <strong>data type</strong> and
+ * <strong>data scheme+authority+path</strong> if specified) must match
+ * (see {@link #match(ContentResolver, Intent, boolean, String)} for more details
+ * on how the data fields match).
+ *
+ * <p><strong>Action</strong> matches if any of the given values match the
+ * Intent action; if the filter specifies no actions, then it will only match
+ * Intents that do not contain an action.
+ *
+ * <p><strong>Data Type</strong> matches if any of the given values match the
+ * Intent type.  The Intent
+ * type is determined by calling {@link Intent#resolveType}.  A wildcard can be
+ * used for the MIME sub-type, in both the Intent and IntentFilter, so that the
+ * type "audio/*" will match "audio/mpeg", "audio/aiff", "audio/*", etc.
+ * <em>Note that MIME type matching here is <b>case sensitive</b>, unlike
+ * formal RFC MIME types!</em>  You should thus always use lower case letters
+ * for your MIME types.
+ *
+ * <p><strong>Data Scheme</strong> matches if any of the given values match the
+ * Intent data's scheme.
+ * The Intent scheme is determined by calling {@link Intent#getData}
+ * and {@link android.net.Uri#getScheme} on that URI.
+ * <em>Note that scheme matching here is <b>case sensitive</b>, unlike
+ * formal RFC schemes!</em>  You should thus always use lower case letters
+ * for your schemes.
+ *
+ * <p><strong>Data Scheme Specific Part</strong> matches if any of the given values match
+ * the Intent's data scheme specific part <em>and</em> one of the data schemes in the filter
+ * has matched the Intent, <em>or</em> no scheme specific parts were supplied in the filter.
+ * The Intent scheme specific part is determined by calling
+ * {@link Intent#getData} and {@link android.net.Uri#getSchemeSpecificPart} on that URI.
+ * <em>Note that scheme specific part matching is <b>case sensitive</b>.</em>
+ *
+ * <p><strong>Data Authority</strong> matches if any of the given values match
+ * the Intent's data authority <em>and</em> one of the data schemes in the filter
+ * has matched the Intent, <em>or</em> no authorities were supplied in the filter.
+ * The Intent authority is determined by calling
+ * {@link Intent#getData} and {@link android.net.Uri#getAuthority} on that URI.
+ * <em>Note that authority matching here is <b>case sensitive</b>, unlike
+ * formal RFC host names!</em>  You should thus always use lower case letters
+ * for your authority.
+ *
+ * <p><strong>Data Path</strong> matches if any of the given values match the
+ * Intent's data path <em>and</em> both a scheme and authority in the filter
+ * has matched against the Intent, <em>or</em> no paths were supplied in the
+ * filter.  The Intent authority is determined by calling
+ * {@link Intent#getData} and {@link android.net.Uri#getPath} on that URI.
+ *
+ * <p><strong>Categories</strong> match if <em>all</em> of the categories in
+ * the Intent match categories given in the filter.  Extra categories in the
+ * filter that are not in the Intent will not cause the match to fail.  Note
+ * that unlike the action, an IntentFilter with no categories
+ * will only match an Intent that does not have any categories.
+ */
+public class IntentFilter implements Parcelable {
+    private static final String AGLOB_STR = "aglob";
+    private static final String SGLOB_STR = "sglob";
+    private static final String PREFIX_STR = "prefix";
+    private static final String LITERAL_STR = "literal";
+    private static final String PATH_STR = "path";
+    private static final String PORT_STR = "port";
+    private static final String HOST_STR = "host";
+    private static final String AUTH_STR = "auth";
+    private static final String SSP_STR = "ssp";
+    private static final String SCHEME_STR = "scheme";
+    private static final String STATIC_TYPE_STR = "staticType";
+    private static final String TYPE_STR = "type";
+    private static final String GROUP_STR = "group";
+    private static final String CAT_STR = "cat";
+    private static final String NAME_STR = "name";
+    private static final String ACTION_STR = "action";
+    private static final String AUTO_VERIFY_STR = "autoVerify";
+
+    /**
+     * The filter {@link #setPriority} value at which system high-priority
+     * receivers are placed; that is, receivers that should execute before
+     * application code. Applications should never use filters with this or
+     * higher priorities.
+     *
+     * @see #setPriority
+     */
+    public static final int SYSTEM_HIGH_PRIORITY = 1000;
+
+    /**
+     * The filter {@link #setPriority} value at which system low-priority
+     * receivers are placed; that is, receivers that should execute after
+     * application code. Applications should never use filters with this or
+     * lower priorities.
+     *
+     * @see #setPriority
+     */
+    public static final int SYSTEM_LOW_PRIORITY = -1000;
+
+    /**
+     * The part of a match constant that describes the category of match
+     * that occurred.  May be either {@link #MATCH_CATEGORY_EMPTY},
+     * {@link #MATCH_CATEGORY_SCHEME}, {@link #MATCH_CATEGORY_SCHEME_SPECIFIC_PART},
+     * {@link #MATCH_CATEGORY_HOST}, {@link #MATCH_CATEGORY_PORT},
+     * {@link #MATCH_CATEGORY_PATH}, or {@link #MATCH_CATEGORY_TYPE}.  Higher
+     * values indicate a better match.
+     */
+    public static final int MATCH_CATEGORY_MASK = 0xfff0000;
+
+    /**
+     * The part of a match constant that applies a quality adjustment to the
+     * basic category of match.  The value {@link #MATCH_ADJUSTMENT_NORMAL}
+     * is no adjustment; higher numbers than that improve the quality, while
+     * lower numbers reduce it.
+     */
+    public static final int MATCH_ADJUSTMENT_MASK = 0x000ffff;
+
+    /**
+     * Quality adjustment applied to the category of match that signifies
+     * the default, base value; higher numbers improve the quality while
+     * lower numbers reduce it.
+     */
+    public static final int MATCH_ADJUSTMENT_NORMAL = 0x8000;
+
+    /**
+     * The filter matched an intent that had no data specified.
+     */
+    public static final int MATCH_CATEGORY_EMPTY = 0x0100000;
+    /**
+     * The filter matched an intent with the same data URI scheme.
+     */
+    public static final int MATCH_CATEGORY_SCHEME = 0x0200000;
+    /**
+     * The filter matched an intent with the same data URI scheme and
+     * authority host.
+     */
+    public static final int MATCH_CATEGORY_HOST = 0x0300000;
+    /**
+     * The filter matched an intent with the same data URI scheme and
+     * authority host and port.
+     */
+    public static final int MATCH_CATEGORY_PORT = 0x0400000;
+    /**
+     * The filter matched an intent with the same data URI scheme,
+     * authority, and path.
+     */
+    public static final int MATCH_CATEGORY_PATH = 0x0500000;
+    /**
+     * The filter matched an intent with the same data URI scheme and
+     * scheme specific part.
+     */
+    public static final int MATCH_CATEGORY_SCHEME_SPECIFIC_PART = 0x0580000;
+    /**
+     * The filter matched an intent with the same data MIME type.
+     */
+    public static final int MATCH_CATEGORY_TYPE = 0x0600000;
+
+    /**
+     * The filter didn't match due to different MIME types.
+     */
+    public static final int NO_MATCH_TYPE = -1;
+    /**
+     * The filter didn't match due to different data URIs.
+     */
+    public static final int NO_MATCH_DATA = -2;
+    /**
+     * The filter didn't match due to different actions.
+     */
+    public static final int NO_MATCH_ACTION = -3;
+    /**
+     * The filter didn't match because it required one or more categories
+     * that were not in the Intent.
+     */
+    public static final int NO_MATCH_CATEGORY = -4;
+
+    /**
+     * HTTP scheme.
+     *
+     * @see #addDataScheme(String)
+     * @hide
+     */
+    public static final String SCHEME_HTTP = "http";
+    /**
+     * HTTPS scheme.
+     *
+     * @see #addDataScheme(String)
+     * @hide
+     */
+    public static final String SCHEME_HTTPS = "https";
+
+    /**
+     * The value to indicate a wildcard for incoming match arguments.
+     * @hide
+     */
+    public static final String WILDCARD = "*";
+    /** @hide */
+    public static final String WILDCARD_PATH = "/" + WILDCARD;
+
+    private int mPriority;
+    @UnsupportedAppUsage
+    private int mOrder;
+    @UnsupportedAppUsage
+    private final ArrayList<String> mActions;
+    private ArrayList<String> mCategories = null;
+    private ArrayList<String> mDataSchemes = null;
+    private ArrayList<PatternMatcher> mDataSchemeSpecificParts = null;
+    private ArrayList<AuthorityEntry> mDataAuthorities = null;
+    private ArrayList<PatternMatcher> mDataPaths = null;
+    private ArrayList<String> mStaticDataTypes = null;
+    private ArrayList<String> mDataTypes = null;
+    private ArrayList<String> mMimeGroups = null;
+    private boolean mHasStaticPartialTypes = false;
+    private boolean mHasDynamicPartialTypes = false;
+
+    private static final int STATE_VERIFY_AUTO         = 0x00000001;
+    private static final int STATE_NEED_VERIFY         = 0x00000010;
+    private static final int STATE_NEED_VERIFY_CHECKED = 0x00000100;
+    private static final int STATE_VERIFIED            = 0x00001000;
+
+    private int mVerifyState;
+    /** @hide */
+    public static final int VISIBILITY_NONE = 0;
+    /** @hide */
+    public static final int VISIBILITY_EXPLICIT = 1;
+    /** @hide */
+    public static final int VISIBILITY_IMPLICIT = 2;
+    /** @hide */
+    @IntDef(prefix = { "VISIBILITY_" }, value = {
+            VISIBILITY_NONE,
+            VISIBILITY_EXPLICIT,
+            VISIBILITY_IMPLICIT,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface InstantAppVisibility {}
+    /** Whether or not the intent filter is visible to instant apps. */
+    private @InstantAppVisibility int mInstantAppVisibility;
+    // These functions are the start of more optimized code for managing
+    // the string sets...  not yet implemented.
+
+    private static int findStringInSet(String[] set, String string,
+            int[] lengths, int lenPos) {
+        if (set == null) return -1;
+        final int N = lengths[lenPos];
+        for (int i=0; i<N; i++) {
+            if (set[i].equals(string)) return i;
+        }
+        return -1;
+    }
+
+    private static String[] addStringToSet(String[] set, String string,
+            int[] lengths, int lenPos) {
+        if (findStringInSet(set, string, lengths, lenPos) >= 0) return set;
+        if (set == null) {
+            set = new String[2];
+            set[0] = string;
+            lengths[lenPos] = 1;
+            return set;
+        }
+        final int N = lengths[lenPos];
+        if (N < set.length) {
+            set[N] = string;
+            lengths[lenPos] = N+1;
+            return set;
+        }
+
+        String[] newSet = new String[(N*3)/2 + 2];
+        System.arraycopy(set, 0, newSet, 0, N);
+        set = newSet;
+        set[N] = string;
+        lengths[lenPos] = N+1;
+        return set;
+    }
+
+    private static String[] removeStringFromSet(String[] set, String string,
+            int[] lengths, int lenPos) {
+        int pos = findStringInSet(set, string, lengths, lenPos);
+        if (pos < 0) return set;
+        final int N = lengths[lenPos];
+        if (N > (set.length/4)) {
+            int copyLen = N-(pos+1);
+            if (copyLen > 0) {
+                System.arraycopy(set, pos+1, set, pos, copyLen);
+            }
+            set[N-1] = null;
+            lengths[lenPos] = N-1;
+            return set;
+        }
+
+        String[] newSet = new String[set.length/3];
+        if (pos > 0) System.arraycopy(set, 0, newSet, 0, pos);
+        if ((pos+1) < N) System.arraycopy(set, pos+1, newSet, pos, N-(pos+1));
+        return newSet;
+    }
+
+    /**
+     * This exception is thrown when a given MIME type does not have a valid
+     * syntax.
+     */
+    public static class MalformedMimeTypeException extends AndroidException {
+        public MalformedMimeTypeException() {
+        }
+
+        public MalformedMimeTypeException(String name) {
+            super(name);
+        }
+    }
+
+    /**
+     * Create a new IntentFilter instance with a specified action and MIME
+     * type, where you know the MIME type is correctly formatted.  This catches
+     * the {@link MalformedMimeTypeException} exception that the constructor
+     * can call and turns it into a runtime exception.
+     *
+     * @param action The action to match, such as Intent.ACTION_VIEW.
+     * @param dataType The type to match, such as "vnd.android.cursor.dir/person".
+     *
+     * @return A new IntentFilter for the given action and type.
+     *
+     * @see #IntentFilter(String, String)
+     */
+    public static IntentFilter create(String action, String dataType) {
+        try {
+            return new IntentFilter(action, dataType);
+        } catch (MalformedMimeTypeException e) {
+            throw new RuntimeException("Bad MIME type", e);
+        }
+    }
+
+    /**
+     * New empty IntentFilter.
+     */
+    public IntentFilter() {
+        mPriority = 0;
+        mActions = new ArrayList<String>();
+    }
+
+    /**
+     * New IntentFilter that matches a single action with no data.  If
+     * no data characteristics are subsequently specified, then the
+     * filter will only match intents that contain no data.
+     *
+     * @param action The action to match, such as Intent.ACTION_MAIN.
+     */
+    public IntentFilter(String action) {
+        mPriority = 0;
+        mActions = new ArrayList<String>();
+        addAction(action);
+    }
+
+    /**
+     * New IntentFilter that matches a single action and data type.
+     *
+     * <p><em>Note: MIME type matching in the Android framework is
+     * case-sensitive, unlike formal RFC MIME types.  As a result,
+     * you should always write your MIME types with lower case letters,
+     * and any MIME types you receive from outside of Android should be
+     * converted to lower case before supplying them here.</em></p>
+     *
+     * <p>Throws {@link MalformedMimeTypeException} if the given MIME type is
+     * not syntactically correct.
+     *
+     * @param action The action to match, such as Intent.ACTION_VIEW.
+     * @param dataType The type to match, such as "vnd.android.cursor.dir/person".
+     *
+     */
+    public IntentFilter(String action, String dataType)
+        throws MalformedMimeTypeException {
+        mPriority = 0;
+        mActions = new ArrayList<String>();
+        addAction(action);
+        addDataType(dataType);
+    }
+
+    /**
+     * New IntentFilter containing a copy of an existing filter.
+     *
+     * @param o The original filter to copy.
+     */
+    public IntentFilter(IntentFilter o) {
+        mPriority = o.mPriority;
+        mOrder = o.mOrder;
+        mActions = new ArrayList<String>(o.mActions);
+        if (o.mCategories != null) {
+            mCategories = new ArrayList<String>(o.mCategories);
+        }
+        if (o.mStaticDataTypes != null) {
+            mStaticDataTypes = new ArrayList<String>(o.mStaticDataTypes);
+        }
+        if (o.mDataTypes != null) {
+            mDataTypes = new ArrayList<String>(o.mDataTypes);
+        }
+        if (o.mDataSchemes != null) {
+            mDataSchemes = new ArrayList<String>(o.mDataSchemes);
+        }
+        if (o.mDataSchemeSpecificParts != null) {
+            mDataSchemeSpecificParts = new ArrayList<PatternMatcher>(o.mDataSchemeSpecificParts);
+        }
+        if (o.mDataAuthorities != null) {
+            mDataAuthorities = new ArrayList<AuthorityEntry>(o.mDataAuthorities);
+        }
+        if (o.mDataPaths != null) {
+            mDataPaths = new ArrayList<PatternMatcher>(o.mDataPaths);
+        }
+        if (o.mMimeGroups != null) {
+            mMimeGroups = new ArrayList<String>(o.mMimeGroups);
+        }
+        mHasStaticPartialTypes = o.mHasStaticPartialTypes;
+        mHasDynamicPartialTypes = o.mHasDynamicPartialTypes;
+        mVerifyState = o.mVerifyState;
+        mInstantAppVisibility = o.mInstantAppVisibility;
+    }
+
+    /**
+     * Modify priority of this filter.  This only affects receiver filters.
+     * The priority of activity filters are set in XML and cannot be changed
+     * programmatically. The default priority is 0. Positive values will be
+     * before the default, lower values will be after it. Applications should
+     * use a value that is larger than {@link #SYSTEM_LOW_PRIORITY} and
+     * smaller than {@link #SYSTEM_HIGH_PRIORITY} .
+     *
+     * @param priority The new priority value.
+     *
+     * @see #getPriority
+     * @see #SYSTEM_LOW_PRIORITY
+     * @see #SYSTEM_HIGH_PRIORITY
+     */
+    public final void setPriority(int priority) {
+        mPriority = priority;
+    }
+
+    /**
+     * Return the priority of this filter.
+     *
+     * @return The priority of the filter.
+     *
+     * @see #setPriority
+     */
+    public final int getPriority() {
+        return mPriority;
+    }
+
+    /** @hide */
+    @SystemApi
+    public final void setOrder(int order) {
+        mOrder = order;
+    }
+
+    /** @hide */
+    @SystemApi
+    public final int getOrder() {
+        return mOrder;
+    }
+
+    /**
+     * Set whether this filter will needs to be automatically verified against its data URIs or not.
+     * The default is false.
+     *
+     * The verification would need to happen only and only if the Intent action is
+     * {@link android.content.Intent#ACTION_VIEW} and the Intent category is
+     * {@link android.content.Intent#CATEGORY_BROWSABLE} and the Intent data scheme
+     * is "http" or "https".
+     *
+     * True means that the filter will need to use its data URIs to be verified.
+     *
+     * @param autoVerify The new autoVerify value.
+     *
+     * @see #getAutoVerify()
+     * @see #addAction(String)
+     * @see #getAction(int)
+     * @see #addCategory(String)
+     * @see #getCategory(int)
+     * @see #addDataScheme(String)
+     * @see #getDataScheme(int)
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public final void setAutoVerify(boolean autoVerify) {
+        mVerifyState &= ~STATE_VERIFY_AUTO;
+        if (autoVerify) mVerifyState |= STATE_VERIFY_AUTO;
+    }
+
+    /**
+     * Return if this filter will needs to be automatically verified again its data URIs or not.
+     *
+     * @return True if the filter will needs to be automatically verified. False otherwise.
+     *
+     * @see #setAutoVerify(boolean)
+     *
+     * @hide
+     */
+    public final boolean getAutoVerify() {
+        return ((mVerifyState & STATE_VERIFY_AUTO) == STATE_VERIFY_AUTO);
+    }
+
+    /**
+     * Return if this filter handle all HTTP or HTTPS data URI or not.  This is the
+     * core check for whether a given activity qualifies as a "browser".
+     *
+     * @return True if the filter handle all HTTP or HTTPS data URI. False otherwise.
+     *
+     * This will check if:
+     *
+     * - either the Intent category is {@link android.content.Intent#CATEGORY_APP_BROWSER}
+     * - either the Intent action is {@link android.content.Intent#ACTION_VIEW} and
+     * the Intent category is {@link android.content.Intent#CATEGORY_BROWSABLE} and the Intent
+     * data scheme is "http" or "https" and that there is no specific host defined.
+     *
+     * @hide
+     */
+    public final boolean handleAllWebDataURI() {
+        return hasCategory(Intent.CATEGORY_APP_BROWSER) ||
+                (handlesWebUris(false) && countDataAuthorities() == 0);
+    }
+
+    /**
+     * Return if this filter handles HTTP or HTTPS data URIs.
+     *
+     * @return True if the filter handles ACTION_VIEW/CATEGORY_BROWSABLE,
+     * has at least one HTTP or HTTPS data URI pattern defined, and optionally
+     * does not define any non-http/https data URI patterns.
+     *
+     * This will check if if the Intent action is {@link android.content.Intent#ACTION_VIEW} and
+     * the Intent category is {@link android.content.Intent#CATEGORY_BROWSABLE} and the Intent
+     * data scheme is "http" or "https".
+     *
+     * @param onlyWebSchemes When true, requires that the intent filter declare
+     *     that it handles *only* http: or https: schemes.  This is a requirement for
+     *     the intent filter's domain linkage being verifiable.
+     * @hide
+     */
+    public final boolean handlesWebUris(boolean onlyWebSchemes) {
+        // Require ACTION_VIEW, CATEGORY_BROWSEABLE, and at least one scheme
+        if (!hasAction(Intent.ACTION_VIEW)
+            || !hasCategory(Intent.CATEGORY_BROWSABLE)
+            || mDataSchemes == null
+            || mDataSchemes.size() == 0) {
+            return false;
+        }
+
+        // Now allow only the schemes "http" and "https"
+        final int N = mDataSchemes.size();
+        for (int i = 0; i < N; i++) {
+            final String scheme = mDataSchemes.get(i);
+            final boolean isWebScheme =
+                    SCHEME_HTTP.equals(scheme) || SCHEME_HTTPS.equals(scheme);
+            if (onlyWebSchemes) {
+                // If we're specifically trying to ensure that there are no non-web schemes
+                // declared in this filter, then if we ever see a non-http/https scheme then
+                // we know it's a failure.
+                if (!isWebScheme) {
+                    return false;
+                }
+            } else {
+                // If we see any http/https scheme declaration in this case then the
+                // filter matches what we're looking for.
+                if (isWebScheme) {
+                    return true;
+                }
+            }
+        }
+
+        // We get here if:
+        //   1) onlyWebSchemes and no non-web schemes were found, i.e success; or
+        //   2) !onlyWebSchemes and no http/https schemes were found, i.e. failure.
+        return onlyWebSchemes;
+    }
+
+    /**
+     * Return if this filter needs to be automatically verified again its data URIs or not.
+     *
+     * @return True if the filter needs to be automatically verified. False otherwise.
+     *
+     * This will check if if the Intent action is {@link android.content.Intent#ACTION_VIEW} and
+     * the Intent category is {@link android.content.Intent#CATEGORY_BROWSABLE} and the Intent
+     * data scheme is "http" or "https".
+     *
+     * @see #setAutoVerify(boolean)
+     *
+     * @hide
+     */
+    public final boolean needsVerification() {
+        return getAutoVerify() && handlesWebUris(true);
+    }
+
+    /**
+     * Return if this filter has been verified
+     *
+     * @return true if the filter has been verified or if autoVerify is false.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    public final boolean isVerified() {
+        if ((mVerifyState & STATE_NEED_VERIFY_CHECKED) == STATE_NEED_VERIFY_CHECKED) {
+            return ((mVerifyState & STATE_NEED_VERIFY) == STATE_NEED_VERIFY);
+        }
+        return false;
+    }
+
+    /**
+     * Set if this filter has been verified
+     *
+     * @param verified true if this filter has been verified. False otherwise.
+     *
+     * @hide
+     */
+    public void setVerified(boolean verified) {
+        mVerifyState |= STATE_NEED_VERIFY_CHECKED;
+        mVerifyState &= ~STATE_VERIFIED;
+        if (verified) mVerifyState |= STATE_VERIFIED;
+    }
+
+    /** @hide */
+    public void setVisibilityToInstantApp(@InstantAppVisibility int visibility) {
+        mInstantAppVisibility = visibility;
+    }
+    /** @hide */
+    public @InstantAppVisibility int getVisibilityToInstantApp() {
+        return mInstantAppVisibility;
+    }
+    /** @hide */
+    public boolean isVisibleToInstantApp() {
+        return mInstantAppVisibility != VISIBILITY_NONE;
+    }
+    /** @hide */
+    public boolean isExplicitlyVisibleToInstantApp() {
+        return mInstantAppVisibility == VISIBILITY_EXPLICIT;
+    }
+    /** @hide */
+    public boolean isImplicitlyVisibleToInstantApp() {
+        return mInstantAppVisibility == VISIBILITY_IMPLICIT;
+    }
+
+    /**
+     * Add a new Intent action to match against.  If any actions are included
+     * in the filter, then an Intent's action must be one of those values for
+     * it to match.  If no actions are included, the Intent action is ignored.
+     *
+     * @param action Name of the action to match, such as Intent.ACTION_VIEW.
+     */
+    public final void addAction(String action) {
+        if (!mActions.contains(action)) {
+            mActions.add(action.intern());
+        }
+    }
+
+    /**
+     * Return the number of actions in the filter.
+     */
+    public final int countActions() {
+        return mActions.size();
+    }
+
+    /**
+     * Return an action in the filter.
+     */
+    public final String getAction(int index) {
+        return mActions.get(index);
+    }
+
+    /**
+     * Is the given action included in the filter?  Note that if the filter
+     * does not include any actions, false will <em>always</em> be returned.
+     *
+     * @param action The action to look for.
+     *
+     * @return True if the action is explicitly mentioned in the filter.
+     */
+    public final boolean hasAction(String action) {
+        return action != null && mActions.contains(action);
+    }
+
+    /**
+     * Match this filter against an Intent's action.  If the filter does not
+     * specify any actions, the match will always fail.
+     *
+     * @param action The desired action to look for.
+     *
+     * @return True if the action is listed in the filter.
+     */
+    public final boolean matchAction(String action) {
+        return matchAction(action, false /*wildcardSupported*/, null /*ignoreActions*/);
+    }
+
+    /**
+     * Variant of {@link #matchAction(String)} that allows a wildcard for the provided action.
+     * @param wildcardSupported if true, will allow action to use wildcards
+     * @param ignoreActions if not null, the set of actions to should not be considered valid while
+     *                      calculating the match
+     */
+    private boolean matchAction(String action, boolean wildcardSupported,
+            @Nullable Collection<String> ignoreActions) {
+        if (wildcardSupported && WILDCARD.equals(action)) {
+            if (ignoreActions == null) {
+                return !mActions.isEmpty();
+            }
+            for (int i = mActions.size() - 1; i >= 0; i--) {
+                if (!ignoreActions.contains(mActions.get(i))) {
+                    return true;
+                }
+            }
+            return false;
+        }
+        if (ignoreActions != null && ignoreActions.contains(action)) {
+            return false;
+        }
+        return hasAction(action);
+    }
+
+    /**
+     * Return an iterator over the filter's actions.  If there are no actions,
+     * returns null.
+     */
+    public final Iterator<String> actionsIterator() {
+        return mActions != null ? mActions.iterator() : null;
+    }
+
+    /**
+     * Add a new Intent data type to match against.  If any types are
+     * included in the filter, then an Intent's data must be <em>either</em>
+     * one of these types <em>or</em> a matching scheme.  If no data types
+     * are included, then an Intent will only match if it specifies no data.
+     *
+     * <p><em>Note: MIME type matching in the Android framework is
+     * case-sensitive, unlike formal RFC MIME types.  As a result,
+     * you should always write your MIME types with lower case letters,
+     * and any MIME types you receive from outside of Android should be
+     * converted to lower case before supplying them here.</em></p>
+     *
+     * <p>Throws {@link MalformedMimeTypeException} if the given MIME type is
+     * not syntactically correct.
+     *
+     * @param type Name of the data type to match, such as "vnd.android.cursor.dir/person".
+     *
+     * @see #matchData
+     */
+    public final void addDataType(String type)
+        throws MalformedMimeTypeException {
+        processMimeType(type, (internalType, isPartial) -> {
+            if (mDataTypes == null) {
+                mDataTypes = new ArrayList<>();
+            }
+            if (mStaticDataTypes == null) {
+                mStaticDataTypes = new ArrayList<>();
+            }
+
+            if (mDataTypes.contains(internalType)) {
+                return;
+            }
+
+            mDataTypes.add(internalType.intern());
+            mStaticDataTypes.add(internalType.intern());
+            mHasStaticPartialTypes = mHasStaticPartialTypes || isPartial;
+        });
+    }
+
+    /**
+     * Add a new Intent data type <em>from MIME group</em> to match against.  If any types are
+     * included in the filter, then an Intent's data must be <em>either</em>
+     * one of these types <em>or</em> a matching scheme.  If no data types
+     * are included, then an Intent will only match if it specifies no data.
+     *
+     * <p><em>Note: MIME type matching in the Android framework is
+     * case-sensitive, unlike formal RFC MIME types.  As a result,
+     * you should always write your MIME types with lower case letters,
+     * and any MIME types you receive from outside of Android should be
+     * converted to lower case before supplying them here.</em></p>
+     *
+     * <p>Throws {@link MalformedMimeTypeException} if the given MIME type is
+     * not syntactically correct.
+     *
+     * @param type Name of the data type to match, such as "vnd.android.cursor.dir/person".
+     *
+     * @see #clearDynamicDataTypes()
+     * @hide
+     */
+    public final void addDynamicDataType(String type)
+            throws MalformedMimeTypeException {
+        processMimeType(type, (internalType, isPartial) -> {
+            if (mDataTypes == null) {
+                mDataTypes = new ArrayList<>();
+            }
+
+            if (!mDataTypes.contains(internalType)) {
+                mDataTypes.add(internalType.intern());
+
+                mHasDynamicPartialTypes = mHasDynamicPartialTypes || isPartial;
+            }
+        });
+    }
+
+    /**
+     * Process mime type - convert to representation used internally and check if type is partial,
+     * and then call provided action
+     */
+    private void processMimeType(String type, BiConsumer<String, Boolean> action)
+            throws MalformedMimeTypeException {
+        final int slashpos = type.indexOf('/');
+        final int typelen = type.length();
+        if (slashpos <= 0 || typelen < slashpos + 2) {
+            throw new MalformedMimeTypeException(type);
+        }
+
+        String internalType = type;
+        boolean isPartialType = false;
+        if (typelen == slashpos + 2 && type.charAt(slashpos + 1) == '*') {
+            internalType = type.substring(0, slashpos);
+            isPartialType = true;
+        }
+
+        action.accept(internalType, isPartialType);
+    }
+
+    /**
+     * Remove all previously added Intent data types from IntentFilter.
+     *
+     * @see #addDynamicDataType(String)
+     * @hide
+     */
+    public final void clearDynamicDataTypes() {
+        if (mDataTypes == null) {
+            return;
+        }
+
+        if (mStaticDataTypes != null) {
+            mDataTypes.clear();
+            mDataTypes.addAll(mStaticDataTypes);
+        } else {
+            mDataTypes = null;
+        }
+
+        mHasDynamicPartialTypes = false;
+    }
+
+    /**
+     * Return the number of static data types in the filter.
+     * @hide
+     */
+    public int countStaticDataTypes() {
+        return mStaticDataTypes != null ? mStaticDataTypes.size() : 0;
+    }
+
+    /**
+     * Is the given data type included in the filter?  Note that if the filter
+     * does not include any type, false will <em>always</em> be returned.
+     *
+     * @param type The data type to look for.
+     *
+     * @return True if the type is explicitly mentioned in the filter.
+     */
+    public final boolean hasDataType(String type) {
+        return mDataTypes != null && findMimeType(type);
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage
+    public final boolean hasExactDataType(String type) {
+        return mDataTypes != null && mDataTypes.contains(type);
+    }
+
+    /** @hide */
+    public final boolean hasExactDynamicDataType(String type) {
+        return hasExactDataType(type) && !hasExactStaticDataType(type);
+    }
+
+    /** @hide */
+    public final boolean hasExactStaticDataType(String type) {
+        return mStaticDataTypes != null && mStaticDataTypes.contains(type);
+    }
+
+    /**
+     * Return the number of data types in the filter.
+     */
+    public final int countDataTypes() {
+        return mDataTypes != null ? mDataTypes.size() : 0;
+    }
+
+    /**
+     * Return a data type in the filter.
+     */
+    public final String getDataType(int index) {
+        return mDataTypes.get(index);
+    }
+
+    /**
+     * Return an iterator over the filter's data types.
+     */
+    public final Iterator<String> typesIterator() {
+        return mDataTypes != null ? mDataTypes.iterator() : null;
+    }
+
+    /**
+     * Return copy of filter's data types.
+     * @hide
+     */
+    public final List<String> dataTypes() {
+        return mDataTypes != null ? new ArrayList<>(mDataTypes) : null;
+    }
+
+    /** @hide */
+    public final void addMimeGroup(String name) {
+        if (mMimeGroups == null) {
+            mMimeGroups = new ArrayList<>();
+        }
+        if (!mMimeGroups.contains(name)) {
+            mMimeGroups.add(name);
+        }
+    }
+
+    /** @hide */
+    public final boolean hasMimeGroup(String name) {
+        return mMimeGroups != null && mMimeGroups.contains(name);
+    }
+
+    /** @hide */
+    public final String getMimeGroup(int index) {
+        return mMimeGroups.get(index);
+    }
+
+    /** @hide */
+    public final int countMimeGroups() {
+        return mMimeGroups != null ? mMimeGroups.size() : 0;
+    }
+
+    /** @hide */
+    public final Iterator<String> mimeGroupsIterator() {
+        return mMimeGroups != null ? mMimeGroups.iterator() : null;
+    }
+
+    /**
+     * Add a new Intent data scheme to match against.  If any schemes are
+     * included in the filter, then an Intent's data must be <em>either</em>
+     * one of these schemes <em>or</em> a matching data type.  If no schemes
+     * are included, then an Intent will match only if it includes no data.
+     *
+     * <p><em>Note: scheme matching in the Android framework is
+     * case-sensitive, unlike formal RFC schemes.  As a result,
+     * you should always write your schemes with lower case letters,
+     * and any schemes you receive from outside of Android should be
+     * converted to lower case before supplying them here.</em></p>
+     *
+     * @param scheme Name of the scheme to match, such as "http".
+     *
+     * @see #matchData
+     */
+    public final void addDataScheme(String scheme) {
+        if (mDataSchemes == null) mDataSchemes = new ArrayList<String>();
+        if (!mDataSchemes.contains(scheme)) {
+            mDataSchemes.add(scheme.intern());
+        }
+    }
+
+    /**
+     * Return the number of data schemes in the filter.
+     */
+    public final int countDataSchemes() {
+        return mDataSchemes != null ? mDataSchemes.size() : 0;
+    }
+
+    /**
+     * Return a data scheme in the filter.
+     */
+    public final String getDataScheme(int index) {
+        return mDataSchemes.get(index);
+    }
+
+    /**
+     * Is the given data scheme included in the filter?  Note that if the
+     * filter does not include any scheme, false will <em>always</em> be
+     * returned.
+     *
+     * @param scheme The data scheme to look for.
+     *
+     * @return True if the scheme is explicitly mentioned in the filter.
+     */
+    public final boolean hasDataScheme(String scheme) {
+        return mDataSchemes != null && mDataSchemes.contains(scheme);
+    }
+
+    /**
+     * Return an iterator over the filter's data schemes.
+     */
+    public final Iterator<String> schemesIterator() {
+        return mDataSchemes != null ? mDataSchemes.iterator() : null;
+    }
+
+    /**
+     * This is an entry for a single authority in the Iterator returned by
+     * {@link #authoritiesIterator()}.
+     */
+    public final static class AuthorityEntry {
+        private final String mOrigHost;
+        private final String mHost;
+        private final boolean mWild;
+        private final int mPort;
+
+        public AuthorityEntry(String host, String port) {
+            mOrigHost = host;
+            mWild = host.length() > 0 && host.charAt(0) == '*';
+            mHost = mWild ? host.substring(1).intern() : host;
+            mPort = port != null ? Integer.parseInt(port) : -1;
+        }
+
+        AuthorityEntry(Parcel src) {
+            mOrigHost = src.readString();
+            mHost = src.readString();
+            mWild = src.readInt() != 0;
+            mPort = src.readInt();
+        }
+
+        void writeToParcel(Parcel dest) {
+            dest.writeString(mOrigHost);
+            dest.writeString(mHost);
+            dest.writeInt(mWild ? 1 : 0);
+            dest.writeInt(mPort);
+        }
+
+        void dumpDebug(ProtoOutputStream proto, long fieldId) {
+            long token = proto.start(fieldId);
+            // The original host information is already contained in host and wild, no output now.
+            proto.write(AuthorityEntryProto.HOST, mHost);
+            proto.write(AuthorityEntryProto.WILD, mWild);
+            proto.write(AuthorityEntryProto.PORT, mPort);
+            proto.end(token);
+        }
+
+        public String getHost() {
+            return mOrigHost;
+        }
+
+        public int getPort() {
+            return mPort;
+        }
+
+        /** @hide */
+        public boolean match(AuthorityEntry other) {
+            if (mWild != other.mWild) {
+                return false;
+            }
+            if (!mHost.equals(other.mHost)) {
+                return false;
+            }
+            if (mPort != other.mPort) {
+                return false;
+            }
+            return true;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj instanceof AuthorityEntry) {
+                final AuthorityEntry other = (AuthorityEntry)obj;
+                return match(other);
+            }
+            return false;
+        }
+
+        /**
+         * Determine whether this AuthorityEntry matches the given data Uri.
+         * <em>Note that this comparison is case-sensitive, unlike formal
+         * RFC host names.  You thus should always normalize to lower-case.</em>
+         *
+         * @param data The Uri to match.
+         * @return Returns either {@link IntentFilter#NO_MATCH_DATA},
+         * {@link IntentFilter#MATCH_CATEGORY_PORT}, or
+         * {@link IntentFilter#MATCH_CATEGORY_HOST}.
+         */
+        public int match(Uri data) {
+            return match(data, false);
+        }
+
+        /**
+         * Variant of {@link #match(Uri)} that supports wildcards on the scheme, host and
+         * path of the provided {@link Uri}
+         *
+         * @param wildcardSupported if true, will allow parameters to use wildcards
+         * @hide
+         */
+        public int match(Uri data, boolean wildcardSupported) {
+            String host = data.getHost();
+            if (host == null) {
+                return NO_MATCH_DATA;
+            }
+            if (false) Log.v("IntentFilter",
+                    "Match host " + host + ": " + mHost);
+            if (!wildcardSupported || !WILDCARD.equals(host)) {
+                if (mWild) {
+                    if (host.length() < mHost.length()) {
+                        return NO_MATCH_DATA;
+                    }
+                    host = host.substring(host.length() - mHost.length());
+                }
+                if (host.compareToIgnoreCase(mHost) != 0) {
+                    return NO_MATCH_DATA;
+                }
+            }
+            if (mPort >= 0) {
+                if (mPort != data.getPort()) {
+                    return NO_MATCH_DATA;
+                }
+                return MATCH_CATEGORY_PORT;
+            }
+            return MATCH_CATEGORY_HOST;
+        }
+    }
+
+    /**
+     * Add a new Intent data "scheme specific part" to match against.  The filter must
+     * include one or more schemes (via {@link #addDataScheme}) for the
+     * scheme specific part to be considered.  If any scheme specific parts are
+     * included in the filter, then an Intent's data must match one of
+     * them.  If no scheme specific parts are included, then only the scheme must match.
+     *
+     * <p>The "scheme specific part" that this matches against is the string returned
+     * by {@link android.net.Uri#getSchemeSpecificPart() Uri.getSchemeSpecificPart}.
+     * For Uris that contain a path, this kind of matching is not generally of interest,
+     * since {@link #addDataAuthority(String, String)} and
+     * {@link #addDataPath(String, int)} can provide a better mechanism for matching
+     * them.  However, for Uris that do not contain a path, the authority and path
+     * are empty, so this is the only way to match against the non-scheme part.</p>
+     *
+     * @param ssp Either a raw string that must exactly match the scheme specific part
+     * path, or a simple pattern, depending on <var>type</var>.
+     * @param type Determines how <var>ssp</var> will be compared to
+     * determine a match: either {@link PatternMatcher#PATTERN_LITERAL},
+     * {@link PatternMatcher#PATTERN_PREFIX}, or
+     * {@link PatternMatcher#PATTERN_SIMPLE_GLOB}.
+     *
+     * @see #matchData
+     * @see #addDataScheme
+     */
+    public final void addDataSchemeSpecificPart(String ssp, int type) {
+        addDataSchemeSpecificPart(new PatternMatcher(ssp, type));
+    }
+
+    /** @hide */
+    public final void addDataSchemeSpecificPart(PatternMatcher ssp) {
+        if (mDataSchemeSpecificParts == null) {
+            mDataSchemeSpecificParts = new ArrayList<PatternMatcher>();
+        }
+        mDataSchemeSpecificParts.add(ssp);
+    }
+
+    /**
+     * Return the number of data scheme specific parts in the filter.
+     */
+    public final int countDataSchemeSpecificParts() {
+        return mDataSchemeSpecificParts != null ? mDataSchemeSpecificParts.size() : 0;
+    }
+
+    /**
+     * Return a data scheme specific part in the filter.
+     */
+    public final PatternMatcher getDataSchemeSpecificPart(int index) {
+        return mDataSchemeSpecificParts.get(index);
+    }
+
+    /**
+     * Is the given data scheme specific part included in the filter?  Note that if the
+     * filter does not include any scheme specific parts, false will <em>always</em> be
+     * returned.
+     *
+     * @param data The scheme specific part that is being looked for.
+     *
+     * @return Returns true if the data string matches a scheme specific part listed in the
+     *         filter.
+     */
+    public final boolean hasDataSchemeSpecificPart(String data) {
+        return hasDataSchemeSpecificPart(data, false);
+    }
+
+    /**
+     * Variant of {@link #hasDataSchemeSpecificPart(String)} that supports wildcards on the provided
+     * ssp.
+     * @param supportWildcards if true, will allow parameters to use wildcards
+     */
+    private boolean hasDataSchemeSpecificPart(String data, boolean supportWildcards) {
+        if (mDataSchemeSpecificParts == null) {
+            return false;
+        }
+        if (supportWildcards && WILDCARD.equals(data) && mDataSchemeSpecificParts.size() > 0) {
+            return true;
+        }
+        final int numDataSchemeSpecificParts = mDataSchemeSpecificParts.size();
+        for (int i = 0; i < numDataSchemeSpecificParts; i++) {
+            final PatternMatcher pe = mDataSchemeSpecificParts.get(i);
+            if (pe.match(data)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage
+    public final boolean hasDataSchemeSpecificPart(PatternMatcher ssp) {
+        if (mDataSchemeSpecificParts == null) {
+            return false;
+        }
+        final int numDataSchemeSpecificParts = mDataSchemeSpecificParts.size();
+        for (int i = 0; i < numDataSchemeSpecificParts; i++) {
+            final PatternMatcher pe = mDataSchemeSpecificParts.get(i);
+            if (pe.getType() == ssp.getType() && pe.getPath().equals(ssp.getPath())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Return an iterator over the filter's data scheme specific parts.
+     */
+    public final Iterator<PatternMatcher> schemeSpecificPartsIterator() {
+        return mDataSchemeSpecificParts != null ? mDataSchemeSpecificParts.iterator() : null;
+    }
+
+    /**
+     * Add a new Intent data authority to match against.  The filter must
+     * include one or more schemes (via {@link #addDataScheme}) for the
+     * authority to be considered.  If any authorities are
+     * included in the filter, then an Intent's data must match one of
+     * them.  If no authorities are included, then only the scheme must match.
+     *
+     * <p><em>Note: host name in the Android framework is
+     * case-sensitive, unlike formal RFC host names.  As a result,
+     * you should always write your host names with lower case letters,
+     * and any host names you receive from outside of Android should be
+     * converted to lower case before supplying them here.</em></p>
+     *
+     * @param host The host part of the authority to match.  May start with a
+     *             single '*' to wildcard the front of the host name.
+     * @param port Optional port part of the authority to match.  If null, any
+     *             port is allowed.
+     *
+     * @see #matchData
+     * @see #addDataScheme
+     */
+    public final void addDataAuthority(String host, String port) {
+        if (port != null) port = port.intern();
+        addDataAuthority(new AuthorityEntry(host.intern(), port));
+    }
+
+    /** @hide */
+    public final void addDataAuthority(AuthorityEntry ent) {
+        if (mDataAuthorities == null) mDataAuthorities =
+                new ArrayList<AuthorityEntry>();
+        mDataAuthorities.add(ent);
+    }
+
+    /**
+     * Return the number of data authorities in the filter.
+     */
+    public final int countDataAuthorities() {
+        return mDataAuthorities != null ? mDataAuthorities.size() : 0;
+    }
+
+    /**
+     * Return a data authority in the filter.
+     */
+    public final AuthorityEntry getDataAuthority(int index) {
+        return mDataAuthorities.get(index);
+    }
+
+    /**
+     * Is the given data authority included in the filter?  Note that if the
+     * filter does not include any authorities, false will <em>always</em> be
+     * returned.
+     *
+     * @param data The data whose authority is being looked for.
+     *
+     * @return Returns true if the data string matches an authority listed in the
+     *         filter.
+     */
+    public final boolean hasDataAuthority(Uri data) {
+        return matchDataAuthority(data) >= 0;
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage
+    public final boolean hasDataAuthority(AuthorityEntry auth) {
+        if (mDataAuthorities == null) {
+            return false;
+        }
+        final int numDataAuthorities = mDataAuthorities.size();
+        for (int i = 0; i < numDataAuthorities; i++) {
+            if (mDataAuthorities.get(i).match(auth)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Return an iterator over the filter's data authorities.
+     */
+    public final Iterator<AuthorityEntry> authoritiesIterator() {
+        return mDataAuthorities != null ? mDataAuthorities.iterator() : null;
+    }
+
+    /**
+     * Add a new Intent data path to match against.  The filter must
+     * include one or more schemes (via {@link #addDataScheme}) <em>and</em>
+     * one or more authorities (via {@link #addDataAuthority}) for the
+     * path to be considered.  If any paths are
+     * included in the filter, then an Intent's data must match one of
+     * them.  If no paths are included, then only the scheme/authority must
+     * match.
+     *
+     * <p>The path given here can either be a literal that must directly
+     * match or match against a prefix, or it can be a simple globbing pattern.
+     * If the latter, you can use '*' anywhere in the pattern to match zero
+     * or more instances of the previous character, '.' as a wildcard to match
+     * any character, and '\' to escape the next character.
+     *
+     * @param path Either a raw string that must exactly match the file
+     * path, or a simple pattern, depending on <var>type</var>.
+     * @param type Determines how <var>path</var> will be compared to
+     * determine a match: either {@link PatternMatcher#PATTERN_LITERAL},
+     * {@link PatternMatcher#PATTERN_PREFIX}, or
+     * {@link PatternMatcher#PATTERN_SIMPLE_GLOB}.
+     *
+     * @see #matchData
+     * @see #addDataScheme
+     * @see #addDataAuthority
+     */
+    public final void addDataPath(String path, int type) {
+        addDataPath(new PatternMatcher(path.intern(), type));
+    }
+
+    /** @hide */
+    public final void addDataPath(PatternMatcher path) {
+        if (mDataPaths == null) mDataPaths = new ArrayList<PatternMatcher>();
+        mDataPaths.add(path);
+    }
+
+    /**
+     * Return the number of data paths in the filter.
+     */
+    public final int countDataPaths() {
+        return mDataPaths != null ? mDataPaths.size() : 0;
+    }
+
+    /**
+     * Return a data path in the filter.
+     */
+    public final PatternMatcher getDataPath(int index) {
+        return mDataPaths.get(index);
+    }
+
+    /**
+     * Is the given data path included in the filter?  Note that if the
+     * filter does not include any paths, false will <em>always</em> be
+     * returned.
+     *
+     * @param data The data path to look for.  This is without the scheme
+     *             prefix.
+     *
+     * @return True if the data string matches a path listed in the
+     *         filter.
+     */
+    public final boolean hasDataPath(String data) {
+        return hasDataPath(data, false);
+    }
+
+    /**
+     * Variant of {@link #hasDataPath(String)} that supports wildcards on the provided scheme, host,
+     * and path.
+     * @param wildcardSupported if true, will allow parameters to use wildcards
+     */
+    private boolean hasDataPath(String data, boolean wildcardSupported) {
+        if (mDataPaths == null) {
+            return false;
+        }
+        if (wildcardSupported && WILDCARD_PATH.equals(data)) {
+            return true;
+        }
+        final int numDataPaths = mDataPaths.size();
+        for (int i = 0; i < numDataPaths; i++) {
+            final PatternMatcher pe = mDataPaths.get(i);
+            if (pe.match(data)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage
+    public final boolean hasDataPath(PatternMatcher path) {
+        if (mDataPaths == null) {
+            return false;
+        }
+        final int numDataPaths = mDataPaths.size();
+        for (int i = 0; i < numDataPaths; i++) {
+            final PatternMatcher pe = mDataPaths.get(i);
+            if (pe.getType() == path.getType() && pe.getPath().equals(path.getPath())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Return an iterator over the filter's data paths.
+     */
+    public final Iterator<PatternMatcher> pathsIterator() {
+        return mDataPaths != null ? mDataPaths.iterator() : null;
+    }
+
+    /**
+     * Match this intent filter against the given Intent data.  This ignores
+     * the data scheme -- unlike {@link #matchData}, the authority will match
+     * regardless of whether there is a matching scheme.
+     *
+     * @param data The data whose authority is being looked for.
+     *
+     * @return Returns either {@link #MATCH_CATEGORY_HOST},
+     * {@link #MATCH_CATEGORY_PORT}, {@link #NO_MATCH_DATA}.
+     */
+    public final int matchDataAuthority(Uri data) {
+        return matchDataAuthority(data, false);
+    }
+
+    /**
+     * Variant of {@link #matchDataAuthority(Uri)} that allows wildcard matching of the provided
+     * authority.
+     *
+     * @param wildcardSupported if true, will allow parameters to use wildcards
+     * @hide
+     */
+    public final int matchDataAuthority(Uri data, boolean wildcardSupported) {
+        if (data == null || mDataAuthorities == null) {
+            return NO_MATCH_DATA;
+        }
+        final int numDataAuthorities = mDataAuthorities.size();
+        for (int i = 0; i < numDataAuthorities; i++) {
+            final AuthorityEntry ae = mDataAuthorities.get(i);
+            int match = ae.match(data, wildcardSupported);
+            if (match >= 0) {
+                return match;
+            }
+        }
+        return NO_MATCH_DATA;
+    }
+
+    /**
+     * Match this filter against an Intent's data (type, scheme and path). If
+     * the filter does not specify any types and does not specify any
+     * schemes/paths, the match will only succeed if the intent does not
+     * also specify a type or data.  If the filter does not specify any schemes,
+     * it will implicitly match intents with no scheme, or the schemes "content:"
+     * or "file:" (basically performing a MIME-type only match).  If the filter
+     * does not specify any MIME types, the Intent also must not specify a MIME
+     * type.
+     *
+     * <p>Be aware that to match against an authority, you must also specify a base
+     * scheme the authority is in.  To match against a data path, both a scheme
+     * and authority must be specified.  If the filter does not specify any
+     * types or schemes that it matches against, it is considered to be empty
+     * (any authority or data path given is ignored, as if it were empty as
+     * well).
+     *
+     * <p><em>Note: MIME type, Uri scheme, and host name matching in the
+     * Android framework is case-sensitive, unlike the formal RFC definitions.
+     * As a result, you should always write these elements with lower case letters,
+     * and normalize any MIME types or Uris you receive from
+     * outside of Android to ensure these elements are lower case before
+     * supplying them here.</em></p>
+     *
+     * @param type The desired data type to look for, as returned by
+     *             Intent.resolveType().
+     * @param scheme The desired data scheme to look for, as returned by
+     *               Intent.getScheme().
+     * @param data The full data string to match against, as supplied in
+     *             Intent.data.
+     *
+     * @return Returns either a valid match constant (a combination of
+     * {@link #MATCH_CATEGORY_MASK} and {@link #MATCH_ADJUSTMENT_MASK}),
+     * or one of the error codes {@link #NO_MATCH_TYPE} if the type didn't match
+     * or {@link #NO_MATCH_DATA} if the scheme/path didn't match.
+     *
+     * @see #match
+     */
+    public final int matchData(String type, String scheme, Uri data) {
+        return matchData(type, scheme, data, false);
+    }
+
+    /**
+     * Variant of {@link #matchData(String, String, Uri)} that allows wildcard matching
+     * of the provided type, scheme, host and path parameters.
+     * @param wildcardSupported if true, will allow parameters to use wildcards
+     */
+    private int matchData(String type, String scheme, Uri data, boolean wildcardSupported) {
+        final ArrayList<String> types = mDataTypes;
+        final ArrayList<String> schemes = mDataSchemes;
+
+        int match = MATCH_CATEGORY_EMPTY;
+
+        if (types == null && schemes == null) {
+            return ((type == null && data == null)
+                ? (MATCH_CATEGORY_EMPTY+MATCH_ADJUSTMENT_NORMAL) : NO_MATCH_DATA);
+        }
+
+        if (schemes != null) {
+            if (schemes.contains(scheme != null ? scheme : "")
+                    || wildcardSupported && WILDCARD.equals(scheme)) {
+                match = MATCH_CATEGORY_SCHEME;
+            } else {
+                return NO_MATCH_DATA;
+            }
+
+            final ArrayList<PatternMatcher> schemeSpecificParts = mDataSchemeSpecificParts;
+            if (schemeSpecificParts != null && data != null) {
+                match = hasDataSchemeSpecificPart(data.getSchemeSpecificPart(), wildcardSupported)
+                        ? MATCH_CATEGORY_SCHEME_SPECIFIC_PART : NO_MATCH_DATA;
+            }
+            if (match != MATCH_CATEGORY_SCHEME_SPECIFIC_PART) {
+                // If there isn't any matching ssp, we need to match an authority.
+                final ArrayList<AuthorityEntry> authorities = mDataAuthorities;
+                if (authorities != null) {
+                    int authMatch = matchDataAuthority(data, wildcardSupported);
+                    if (authMatch >= 0) {
+                        final ArrayList<PatternMatcher> paths = mDataPaths;
+                        if (paths == null) {
+                            match = authMatch;
+                        } else if (hasDataPath(data.getPath(), wildcardSupported)) {
+                            match = MATCH_CATEGORY_PATH;
+                        } else {
+                            return NO_MATCH_DATA;
+                        }
+                    } else {
+                        return NO_MATCH_DATA;
+                    }
+                }
+            }
+            // If neither an ssp nor an authority matched, we're done.
+            if (match == NO_MATCH_DATA) {
+                return NO_MATCH_DATA;
+            }
+        } else {
+            // Special case: match either an Intent with no data URI,
+            // or with a scheme: URI.  This is to give a convenience for
+            // the common case where you want to deal with data in a
+            // content provider, which is done by type, and we don't want
+            // to force everyone to say they handle content: or file: URIs.
+            if (scheme != null && !"".equals(scheme)
+                    && !"content".equals(scheme)
+                    && !"file".equals(scheme)
+                    && !(wildcardSupported && WILDCARD.equals(scheme))) {
+                return NO_MATCH_DATA;
+            }
+        }
+
+        if (types != null) {
+            if (findMimeType(type)) {
+                match = MATCH_CATEGORY_TYPE;
+            } else {
+                return NO_MATCH_TYPE;
+            }
+        } else {
+            // If no MIME types are specified, then we will only match against
+            // an Intent that does not have a MIME type.
+            if (type != null) {
+                return NO_MATCH_TYPE;
+            }
+        }
+
+        return match + MATCH_ADJUSTMENT_NORMAL;
+    }
+
+    /**
+     * Add a new Intent category to match against.  The semantics of
+     * categories is the opposite of actions -- an Intent includes the
+     * categories that it requires, all of which must be included in the
+     * filter in order to match.  In other words, adding a category to the
+     * filter has no impact on matching unless that category is specified in
+     * the intent.
+     *
+     * @param category Name of category to match, such as Intent.CATEGORY_EMBED.
+     */
+    public final void addCategory(String category) {
+        if (mCategories == null) mCategories = new ArrayList<String>();
+        if (!mCategories.contains(category)) {
+            mCategories.add(category.intern());
+        }
+    }
+
+    /**
+     * Return the number of categories in the filter.
+     */
+    public final int countCategories() {
+        return mCategories != null ? mCategories.size() : 0;
+    }
+
+    /**
+     * Return a category in the filter.
+     */
+    public final String getCategory(int index) {
+        return mCategories.get(index);
+    }
+
+    /**
+     * Is the given category included in the filter?
+     *
+     * @param category The category that the filter supports.
+     *
+     * @return True if the category is explicitly mentioned in the filter.
+     */
+    public final boolean hasCategory(String category) {
+        return mCategories != null && mCategories.contains(category);
+    }
+
+    /**
+     * Return an iterator over the filter's categories.
+     *
+     * @return Iterator if this filter has categories or {@code null} if none.
+     */
+    public final Iterator<String> categoriesIterator() {
+        return mCategories != null ? mCategories.iterator() : null;
+    }
+
+    /**
+     * Match this filter against an Intent's categories.  Each category in
+     * the Intent must be specified by the filter; if any are not in the
+     * filter, the match fails.
+     *
+     * @param categories The categories included in the intent, as returned by
+     *                   Intent.getCategories().
+     *
+     * @return If all categories match (success), null; else the name of the
+     *         first category that didn't match.
+     */
+    public final String matchCategories(Set<String> categories) {
+        if (categories == null) {
+            return null;
+        }
+
+        Iterator<String> it = categories.iterator();
+
+        if (mCategories == null) {
+            return it.hasNext() ? it.next() : null;
+        }
+
+        while (it.hasNext()) {
+            final String category = it.next();
+            if (!mCategories.contains(category)) {
+                return category;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Test whether this filter matches the given <var>intent</var>.
+     *
+     * @param intent The Intent to compare against.
+     * @param resolve If true, the intent's type will be resolved by calling
+     *                Intent.resolveType(); otherwise a simple match against
+     *                Intent.type will be performed.
+     * @param logTag Tag to use in debugging messages.
+     *
+     * @return Returns either a valid match constant (a combination of
+     * {@link #MATCH_CATEGORY_MASK} and {@link #MATCH_ADJUSTMENT_MASK}),
+     * or one of the error codes {@link #NO_MATCH_TYPE} if the type didn't match,
+     * {@link #NO_MATCH_DATA} if the scheme/path didn't match,
+     * {@link #NO_MATCH_ACTION} if the action didn't match, or
+     * {@link #NO_MATCH_CATEGORY} if one or more categories didn't match.
+     *
+     * @see #match(String, String, String, android.net.Uri , Set, String)
+     */
+    public final int match(ContentResolver resolver, Intent intent,
+            boolean resolve, String logTag) {
+        String type = resolve ? intent.resolveType(resolver) : intent.getType();
+        return match(intent.getAction(), type, intent.getScheme(),
+                     intent.getData(), intent.getCategories(), logTag);
+    }
+
+    /**
+     * Test whether this filter matches the given intent data.  A match is
+     * only successful if the actions and categories in the Intent match
+     * against the filter, as described in {@link IntentFilter}; in that case,
+     * the match result returned will be as per {@link #matchData}.
+     *
+     * @param action The intent action to match against (Intent.getAction).
+     * @param type The intent type to match against (Intent.resolveType()).
+     * @param scheme The data scheme to match against (Intent.getScheme()).
+     * @param data The data URI to match against (Intent.getData()).
+     * @param categories The categories to match against
+     *                   (Intent.getCategories()).
+     * @param logTag Tag to use in debugging messages.
+     *
+     * @return Returns either a valid match constant (a combination of
+     * {@link #MATCH_CATEGORY_MASK} and {@link #MATCH_ADJUSTMENT_MASK}),
+     * or one of the error codes {@link #NO_MATCH_TYPE} if the type didn't match,
+     * {@link #NO_MATCH_DATA} if the scheme/path didn't match,
+     * {@link #NO_MATCH_ACTION} if the action didn't match, or
+     * {@link #NO_MATCH_CATEGORY} if one or more categories didn't match.
+     *
+     * @see #matchData
+     * @see Intent#getAction
+     * @see Intent#resolveType
+     * @see Intent#getScheme
+     * @see Intent#getData
+     * @see Intent#getCategories
+     */
+    public final int match(String action, String type, String scheme,
+            Uri data, Set<String> categories, String logTag) {
+        return match(action, type, scheme, data, categories, logTag, false /*supportWildcards*/,
+                null /*ignoreActions*/);
+    }
+
+    /**
+     * Variant of {@link #match(ContentResolver, Intent, boolean, String)} that supports wildcards
+     * in the action, type, scheme, host and path.
+     * @param supportWildcards if true, will allow supported parameters to use wildcards to match
+     *                         this filter
+     * @param ignoreActions a collection of actions that, if not null should be ignored and not used
+     *                      if provided as the matching action or the set of actions defined by this
+     *                      filter
+     * @hide
+     */
+    public final int match(String action, String type, String scheme,
+            Uri data, Set<String> categories, String logTag, boolean supportWildcards,
+            @Nullable Collection<String> ignoreActions) {
+        if (action != null && !matchAction(action, supportWildcards, ignoreActions)) {
+            if (false) Log.v(
+                logTag, "No matching action " + action + " for " + this);
+            return NO_MATCH_ACTION;
+        }
+
+        int dataMatch = matchData(type, scheme, data, supportWildcards);
+        if (dataMatch < 0) {
+            if (false) {
+                if (dataMatch == NO_MATCH_TYPE) {
+                    Log.v(logTag, "No matching type " + type
+                          + " for " + this);
+                }
+                if (dataMatch == NO_MATCH_DATA) {
+                    Log.v(logTag, "No matching scheme/path " + data
+                          + " for " + this);
+                }
+            }
+            return dataMatch;
+        }
+
+        String categoryMismatch = matchCategories(categories);
+        if (categoryMismatch != null) {
+            if (false) {
+                Log.v(logTag, "No matching category " + categoryMismatch + " for " + this);
+            }
+            return NO_MATCH_CATEGORY;
+        }
+
+        // It would be nice to treat container activities as more
+        // important than ones that can be embedded, but this is not the way...
+        if (false) {
+            if (categories != null) {
+                dataMatch -= mCategories.size() - categories.size();
+            }
+        }
+
+        return dataMatch;
+    }
+
+    /**
+     * Write the contents of the IntentFilter as an XML stream.
+     */
+    public void writeToXml(XmlSerializer serializer) throws IOException {
+
+        if (getAutoVerify()) {
+            serializer.attribute(null, AUTO_VERIFY_STR, Boolean.toString(true));
+        }
+
+        int N = countActions();
+        for (int i=0; i<N; i++) {
+            serializer.startTag(null, ACTION_STR);
+            serializer.attribute(null, NAME_STR, mActions.get(i));
+            serializer.endTag(null, ACTION_STR);
+        }
+        N = countCategories();
+        for (int i=0; i<N; i++) {
+            serializer.startTag(null, CAT_STR);
+            serializer.attribute(null, NAME_STR, mCategories.get(i));
+            serializer.endTag(null, CAT_STR);
+        }
+        writeDataTypesToXml(serializer);
+        N = countMimeGroups();
+        for (int i=0; i<N; i++) {
+            serializer.startTag(null, GROUP_STR);
+            serializer.attribute(null, NAME_STR, mMimeGroups.get(i));
+            serializer.endTag(null, GROUP_STR);
+        }
+        N = countDataSchemes();
+        for (int i=0; i<N; i++) {
+            serializer.startTag(null, SCHEME_STR);
+            serializer.attribute(null, NAME_STR, mDataSchemes.get(i));
+            serializer.endTag(null, SCHEME_STR);
+        }
+        N = countDataSchemeSpecificParts();
+        for (int i=0; i<N; i++) {
+            serializer.startTag(null, SSP_STR);
+            PatternMatcher pe = mDataSchemeSpecificParts.get(i);
+            switch (pe.getType()) {
+                case PatternMatcher.PATTERN_LITERAL:
+                    serializer.attribute(null, LITERAL_STR, pe.getPath());
+                    break;
+                case PatternMatcher.PATTERN_PREFIX:
+                    serializer.attribute(null, PREFIX_STR, pe.getPath());
+                    break;
+                case PatternMatcher.PATTERN_SIMPLE_GLOB:
+                    serializer.attribute(null, SGLOB_STR, pe.getPath());
+                    break;
+                case PatternMatcher.PATTERN_ADVANCED_GLOB:
+                    serializer.attribute(null, AGLOB_STR, pe.getPath());
+                    break;
+            }
+            serializer.endTag(null, SSP_STR);
+        }
+        N = countDataAuthorities();
+        for (int i=0; i<N; i++) {
+            serializer.startTag(null, AUTH_STR);
+            AuthorityEntry ae = mDataAuthorities.get(i);
+            serializer.attribute(null, HOST_STR, ae.getHost());
+            if (ae.getPort() >= 0) {
+                serializer.attribute(null, PORT_STR, Integer.toString(ae.getPort()));
+            }
+            serializer.endTag(null, AUTH_STR);
+        }
+        N = countDataPaths();
+        for (int i=0; i<N; i++) {
+            serializer.startTag(null, PATH_STR);
+            PatternMatcher pe = mDataPaths.get(i);
+            switch (pe.getType()) {
+                case PatternMatcher.PATTERN_LITERAL:
+                    serializer.attribute(null, LITERAL_STR, pe.getPath());
+                    break;
+                case PatternMatcher.PATTERN_PREFIX:
+                    serializer.attribute(null, PREFIX_STR, pe.getPath());
+                    break;
+                case PatternMatcher.PATTERN_SIMPLE_GLOB:
+                    serializer.attribute(null, SGLOB_STR, pe.getPath());
+                    break;
+                case PatternMatcher.PATTERN_ADVANCED_GLOB:
+                    serializer.attribute(null, AGLOB_STR, pe.getPath());
+                    break;
+            }
+            serializer.endTag(null, PATH_STR);
+        }
+    }
+
+    /**
+     * Write data types (both static and dynamic) to XML.
+     * In implementation we rely on two facts:
+     * - {@link #mStaticDataTypes} is subsequence of {@link #mDataTypes}
+     * - both {@link #mStaticDataTypes} and {@link #mDataTypes} does not contain duplicates
+     */
+    private void writeDataTypesToXml(XmlSerializer serializer) throws IOException {
+        if (mStaticDataTypes == null) {
+            return;
+        }
+
+        int i = 0;
+        for (String staticType: mStaticDataTypes) {
+            while (!mDataTypes.get(i).equals(staticType)) {
+                writeDataTypeToXml(serializer, mDataTypes.get(i), TYPE_STR);
+                i++;
+            }
+
+            writeDataTypeToXml(serializer, staticType, STATIC_TYPE_STR);
+            i++;
+        }
+
+        while (i < mDataTypes.size()) {
+            writeDataTypeToXml(serializer, mDataTypes.get(i), TYPE_STR);
+            i++;
+        }
+    }
+
+    private void writeDataTypeToXml(XmlSerializer serializer, String type, String tag)
+            throws IOException {
+        serializer.startTag(null, tag);
+
+        if (type.indexOf('/') < 0) {
+            type = type + "/*";
+        }
+
+        serializer.attribute(null, NAME_STR, type);
+        serializer.endTag(null, tag);
+    }
+
+    public void readFromXml(XmlPullParser parser) throws XmlPullParserException,
+            IOException {
+        String autoVerify = parser.getAttributeValue(null, AUTO_VERIFY_STR);
+        setAutoVerify(TextUtils.isEmpty(autoVerify) ? false : Boolean.getBoolean(autoVerify));
+
+        int outerDepth = parser.getDepth();
+        int type;
+        while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
+               && (type != XmlPullParser.END_TAG
+                       || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG
+                    || type == XmlPullParser.TEXT) {
+                continue;
+            }
+
+            String tagName = parser.getName();
+            if (tagName.equals(ACTION_STR)) {
+                String name = parser.getAttributeValue(null, NAME_STR);
+                if (name != null) {
+                    addAction(name);
+                }
+            } else if (tagName.equals(CAT_STR)) {
+                String name = parser.getAttributeValue(null, NAME_STR);
+                if (name != null) {
+                    addCategory(name);
+                }
+            } else if (tagName.equals(STATIC_TYPE_STR)) {
+                String name = parser.getAttributeValue(null, NAME_STR);
+                if (name != null) {
+                    try {
+                        addDataType(name);
+                    } catch (MalformedMimeTypeException e) {
+                    }
+                }
+            } else if (tagName.equals(TYPE_STR)) {
+                String name = parser.getAttributeValue(null, NAME_STR);
+                if (name != null) {
+                    try {
+                        addDynamicDataType(name);
+                    } catch (MalformedMimeTypeException e) {
+                    }
+                }
+            } else if (tagName.equals(GROUP_STR)) {
+                String name = parser.getAttributeValue(null, NAME_STR);
+                if (name != null) {
+                    addMimeGroup(name);
+                }
+            } else if (tagName.equals(SCHEME_STR)) {
+                String name = parser.getAttributeValue(null, NAME_STR);
+                if (name != null) {
+                    addDataScheme(name);
+                }
+            } else if (tagName.equals(SSP_STR)) {
+                String ssp = parser.getAttributeValue(null, LITERAL_STR);
+                if (ssp != null) {
+                    addDataSchemeSpecificPart(ssp, PatternMatcher.PATTERN_LITERAL);
+                } else if ((ssp=parser.getAttributeValue(null, PREFIX_STR)) != null) {
+                    addDataSchemeSpecificPart(ssp, PatternMatcher.PATTERN_PREFIX);
+                } else if ((ssp=parser.getAttributeValue(null, SGLOB_STR)) != null) {
+                    addDataSchemeSpecificPart(ssp, PatternMatcher.PATTERN_SIMPLE_GLOB);
+                } else if ((ssp=parser.getAttributeValue(null, AGLOB_STR)) != null) {
+                    addDataSchemeSpecificPart(ssp, PatternMatcher.PATTERN_ADVANCED_GLOB);
+                }
+            } else if (tagName.equals(AUTH_STR)) {
+                String host = parser.getAttributeValue(null, HOST_STR);
+                String port = parser.getAttributeValue(null, PORT_STR);
+                if (host != null) {
+                    addDataAuthority(host, port);
+                }
+            } else if (tagName.equals(PATH_STR)) {
+                String path = parser.getAttributeValue(null, LITERAL_STR);
+                if (path != null) {
+                    addDataPath(path, PatternMatcher.PATTERN_LITERAL);
+                } else if ((path=parser.getAttributeValue(null, PREFIX_STR)) != null) {
+                    addDataPath(path, PatternMatcher.PATTERN_PREFIX);
+                } else if ((path=parser.getAttributeValue(null, SGLOB_STR)) != null) {
+                    addDataPath(path, PatternMatcher.PATTERN_SIMPLE_GLOB);
+                } else if ((path=parser.getAttributeValue(null, AGLOB_STR)) != null) {
+                    addDataPath(path, PatternMatcher.PATTERN_ADVANCED_GLOB);
+                }
+            } else {
+                Log.w("IntentFilter", "Unknown tag parsing IntentFilter: " + tagName);
+            }
+            XmlUtils.skipCurrentTag(parser);
+        }
+    }
+
+    /** @hide */
+    public void dumpDebug(ProtoOutputStream proto, long fieldId) {
+        long token = proto.start(fieldId);
+        if (mActions.size() > 0) {
+            Iterator<String> it = mActions.iterator();
+            while (it.hasNext()) {
+                proto.write(IntentFilterProto.ACTIONS, it.next());
+            }
+        }
+        if (mCategories != null) {
+            Iterator<String> it = mCategories.iterator();
+            while (it.hasNext()) {
+                proto.write(IntentFilterProto.CATEGORIES, it.next());
+            }
+        }
+        if (mDataSchemes != null) {
+            Iterator<String> it = mDataSchemes.iterator();
+            while (it.hasNext()) {
+                proto.write(IntentFilterProto.DATA_SCHEMES, it.next());
+            }
+        }
+        if (mDataSchemeSpecificParts != null) {
+            Iterator<PatternMatcher> it = mDataSchemeSpecificParts.iterator();
+            while (it.hasNext()) {
+                it.next().dumpDebug(proto, IntentFilterProto.DATA_SCHEME_SPECS);
+            }
+        }
+        if (mDataAuthorities != null) {
+            Iterator<AuthorityEntry> it = mDataAuthorities.iterator();
+            while (it.hasNext()) {
+                it.next().dumpDebug(proto, IntentFilterProto.DATA_AUTHORITIES);
+            }
+        }
+        if (mDataPaths != null) {
+            Iterator<PatternMatcher> it = mDataPaths.iterator();
+            while (it.hasNext()) {
+                it.next().dumpDebug(proto, IntentFilterProto.DATA_PATHS);
+            }
+        }
+        if (mDataTypes != null) {
+            Iterator<String> it = mDataTypes.iterator();
+            while (it.hasNext()) {
+                proto.write(IntentFilterProto.DATA_TYPES, it.next());
+            }
+        }
+        if (mMimeGroups != null) {
+            Iterator<String> it = mMimeGroups.iterator();
+            while (it.hasNext()) {
+                proto.write(IntentFilterProto.MIME_GROUPS, it.next());
+            }
+        }
+        if (mPriority != 0 || hasPartialTypes()) {
+            proto.write(IntentFilterProto.PRIORITY, mPriority);
+            proto.write(IntentFilterProto.HAS_PARTIAL_TYPES, hasPartialTypes());
+        }
+        proto.write(IntentFilterProto.GET_AUTO_VERIFY, getAutoVerify());
+        proto.end(token);
+    }
+
+    public void dump(Printer du, String prefix) {
+        StringBuilder sb = new StringBuilder(256);
+        if (mActions.size() > 0) {
+            Iterator<String> it = mActions.iterator();
+            while (it.hasNext()) {
+                sb.setLength(0);
+                sb.append(prefix); sb.append("Action: \"");
+                        sb.append(it.next()); sb.append("\"");
+                du.println(sb.toString());
+            }
+        }
+        if (mCategories != null) {
+            Iterator<String> it = mCategories.iterator();
+            while (it.hasNext()) {
+                sb.setLength(0);
+                sb.append(prefix); sb.append("Category: \"");
+                        sb.append(it.next()); sb.append("\"");
+                du.println(sb.toString());
+            }
+        }
+        if (mDataSchemes != null) {
+            Iterator<String> it = mDataSchemes.iterator();
+            while (it.hasNext()) {
+                sb.setLength(0);
+                sb.append(prefix); sb.append("Scheme: \"");
+                        sb.append(it.next()); sb.append("\"");
+                du.println(sb.toString());
+            }
+        }
+        if (mDataSchemeSpecificParts != null) {
+            Iterator<PatternMatcher> it = mDataSchemeSpecificParts.iterator();
+            while (it.hasNext()) {
+                PatternMatcher pe = it.next();
+                sb.setLength(0);
+                sb.append(prefix); sb.append("Ssp: \"");
+                        sb.append(pe); sb.append("\"");
+                du.println(sb.toString());
+            }
+        }
+        if (mDataAuthorities != null) {
+            Iterator<AuthorityEntry> it = mDataAuthorities.iterator();
+            while (it.hasNext()) {
+                AuthorityEntry ae = it.next();
+                sb.setLength(0);
+                sb.append(prefix); sb.append("Authority: \"");
+                        sb.append(ae.mHost); sb.append("\": ");
+                        sb.append(ae.mPort);
+                if (ae.mWild) sb.append(" WILD");
+                du.println(sb.toString());
+            }
+        }
+        if (mDataPaths != null) {
+            Iterator<PatternMatcher> it = mDataPaths.iterator();
+            while (it.hasNext()) {
+                PatternMatcher pe = it.next();
+                sb.setLength(0);
+                sb.append(prefix); sb.append("Path: \"");
+                        sb.append(pe); sb.append("\"");
+                du.println(sb.toString());
+            }
+        }
+        if (mStaticDataTypes != null) {
+            Iterator<String> it = mStaticDataTypes.iterator();
+            while (it.hasNext()) {
+                sb.setLength(0);
+                sb.append(prefix); sb.append("StaticType: \"");
+                sb.append(it.next()); sb.append("\"");
+                du.println(sb.toString());
+            }
+        }
+        if (mDataTypes != null) {
+            Iterator<String> it = mDataTypes.iterator();
+            while (it.hasNext()) {
+                String dataType = it.next();
+                if (hasExactStaticDataType(dataType)) {
+                    continue;
+                }
+
+                sb.setLength(0);
+                sb.append(prefix); sb.append("Type: \"");
+                sb.append(dataType); sb.append("\"");
+                du.println(sb.toString());
+            }
+        }
+        if (mMimeGroups != null) {
+            Iterator<String> it = mMimeGroups.iterator();
+            while (it.hasNext()) {
+                sb.setLength(0);
+                sb.append(prefix); sb.append("MimeGroup: \"");
+                sb.append(it.next()); sb.append("\"");
+                du.println(sb.toString());
+            }
+        }
+        if (mPriority != 0 || mOrder != 0 || hasPartialTypes()) {
+            sb.setLength(0);
+            sb.append(prefix);
+            sb.append("mPriority="); sb.append(mPriority);
+            sb.append(", mOrder="); sb.append(mOrder);
+            sb.append(", mHasStaticPartialTypes="); sb.append(mHasStaticPartialTypes);
+            sb.append(", mHasDynamicPartialTypes="); sb.append(mHasDynamicPartialTypes);
+            du.println(sb.toString());
+        }
+        if (getAutoVerify()) {
+            sb.setLength(0);
+            sb.append(prefix); sb.append("AutoVerify="); sb.append(getAutoVerify());
+            du.println(sb.toString());
+        }
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<IntentFilter> CREATOR
+            = new Parcelable.Creator<IntentFilter>() {
+        public IntentFilter createFromParcel(Parcel source) {
+            return new IntentFilter(source);
+        }
+
+        public IntentFilter[] newArray(int size) {
+            return new IntentFilter[size];
+        }
+    };
+
+    public final int describeContents() {
+        return 0;
+    }
+
+    public final void writeToParcel(Parcel dest, int flags) {
+        dest.writeStringList(mActions);
+        if (mCategories != null) {
+            dest.writeInt(1);
+            dest.writeStringList(mCategories);
+        } else {
+            dest.writeInt(0);
+        }
+        if (mDataSchemes != null) {
+            dest.writeInt(1);
+            dest.writeStringList(mDataSchemes);
+        } else {
+            dest.writeInt(0);
+        }
+        if (mStaticDataTypes != null) {
+            dest.writeInt(1);
+            dest.writeStringList(mStaticDataTypes);
+        } else {
+            dest.writeInt(0);
+        }
+        if (mDataTypes != null) {
+            dest.writeInt(1);
+            dest.writeStringList(mDataTypes);
+        } else {
+            dest.writeInt(0);
+        }
+        if (mMimeGroups != null) {
+            dest.writeInt(1);
+            dest.writeStringList(mMimeGroups);
+        } else {
+            dest.writeInt(0);
+        }
+        if (mDataSchemeSpecificParts != null) {
+            final int N = mDataSchemeSpecificParts.size();
+            dest.writeInt(N);
+            for (int i=0; i<N; i++) {
+                mDataSchemeSpecificParts.get(i).writeToParcel(dest, flags);
+            }
+        } else {
+            dest.writeInt(0);
+        }
+        if (mDataAuthorities != null) {
+            final int N = mDataAuthorities.size();
+            dest.writeInt(N);
+            for (int i=0; i<N; i++) {
+                mDataAuthorities.get(i).writeToParcel(dest);
+            }
+        } else {
+            dest.writeInt(0);
+        }
+        if (mDataPaths != null) {
+            final int N = mDataPaths.size();
+            dest.writeInt(N);
+            for (int i=0; i<N; i++) {
+                mDataPaths.get(i).writeToParcel(dest, flags);
+            }
+        } else {
+            dest.writeInt(0);
+        }
+        dest.writeInt(mPriority);
+        dest.writeInt(mHasStaticPartialTypes ? 1 : 0);
+        dest.writeInt(mHasDynamicPartialTypes ? 1 : 0);
+        dest.writeInt(getAutoVerify() ? 1 : 0);
+        dest.writeInt(mInstantAppVisibility);
+        dest.writeInt(mOrder);
+    }
+
+    /**
+     * For debugging -- perform a check on the filter, return true if it passed
+     * or false if it failed.
+     *
+     * {@hide}
+     */
+    public boolean debugCheck() {
+        return true;
+
+        // This code looks for intent filters that do not specify data.
+        /*
+        if (mActions != null && mActions.size() == 1
+                && mActions.contains(Intent.ACTION_MAIN)) {
+            return true;
+        }
+
+        if (mDataTypes == null && mDataSchemes == null) {
+            Log.w("IntentFilter", "QUESTIONABLE INTENT FILTER:");
+            dump(Log.WARN, "IntentFilter", "  ");
+            return false;
+        }
+
+        return true;
+        */
+    }
+
+    /** @hide */
+    public IntentFilter(Parcel source) {
+        mActions = new ArrayList<String>();
+        source.readStringList(mActions);
+        if (source.readInt() != 0) {
+            mCategories = new ArrayList<String>();
+            source.readStringList(mCategories);
+        }
+        if (source.readInt() != 0) {
+            mDataSchemes = new ArrayList<String>();
+            source.readStringList(mDataSchemes);
+        }
+        if (source.readInt() != 0) {
+            mStaticDataTypes = new ArrayList<String>();
+            source.readStringList(mStaticDataTypes);
+        }
+        if (source.readInt() != 0) {
+            mDataTypes = new ArrayList<String>();
+            source.readStringList(mDataTypes);
+        }
+        if (source.readInt() != 0) {
+            mMimeGroups = new ArrayList<String>();
+            source.readStringList(mMimeGroups);
+        }
+        int N = source.readInt();
+        if (N > 0) {
+            mDataSchemeSpecificParts = new ArrayList<PatternMatcher>(N);
+            for (int i=0; i<N; i++) {
+                mDataSchemeSpecificParts.add(new PatternMatcher(source));
+            }
+        }
+        N = source.readInt();
+        if (N > 0) {
+            mDataAuthorities = new ArrayList<AuthorityEntry>(N);
+            for (int i=0; i<N; i++) {
+                mDataAuthorities.add(new AuthorityEntry(source));
+            }
+        }
+        N = source.readInt();
+        if (N > 0) {
+            mDataPaths = new ArrayList<PatternMatcher>(N);
+            for (int i=0; i<N; i++) {
+                mDataPaths.add(new PatternMatcher(source));
+            }
+        }
+        mPriority = source.readInt();
+        mHasStaticPartialTypes = source.readInt() > 0;
+        mHasDynamicPartialTypes = source.readInt() > 0;
+        setAutoVerify(source.readInt() > 0);
+        setVisibilityToInstantApp(source.readInt());
+        mOrder = source.readInt();
+    }
+
+    private boolean hasPartialTypes() {
+        return mHasStaticPartialTypes || mHasDynamicPartialTypes;
+    }
+
+    private final boolean findMimeType(String type) {
+        final ArrayList<String> t = mDataTypes;
+
+        if (type == null) {
+            return false;
+        }
+
+        if (t.contains(type)) {
+            return true;
+        }
+
+        // Deal with an Intent wanting to match every type in the IntentFilter.
+        final int typeLength = type.length();
+        if (typeLength == 3 && type.equals("*/*")) {
+            return !t.isEmpty();
+        }
+
+        // Deal with this IntentFilter wanting to match every Intent type.
+        if (hasPartialTypes() && t.contains("*")) {
+            return true;
+        }
+
+        final int slashpos = type.indexOf('/');
+        if (slashpos > 0) {
+            if (hasPartialTypes() && t.contains(type.substring(0, slashpos))) {
+                return true;
+            }
+            if (typeLength == slashpos+2 && type.charAt(slashpos+1) == '*') {
+                // Need to look through all types for one that matches
+                // our base...
+                final int numTypes = t.size();
+                for (int i = 0; i < numTypes; i++) {
+                    final String v = t.get(i);
+                    if (type.regionMatches(0, v, 0, slashpos+1)) {
+                        return true;
+                    }
+                }
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * @hide
+     */
+    public ArrayList<String> getHostsList() {
+        ArrayList<String> result = new ArrayList<>();
+        Iterator<IntentFilter.AuthorityEntry> it = authoritiesIterator();
+        if (it != null) {
+            while (it.hasNext()) {
+                IntentFilter.AuthorityEntry entry = it.next();
+                result.add(entry.getHost());
+            }
+        }
+        return result;
+    }
+
+    /**
+     * @hide
+     */
+    public String[] getHosts() {
+        ArrayList<String> list = getHostsList();
+        return list.toArray(new String[list.size()]);
+    }
+}
diff --git a/android/content/IntentSender.java b/android/content/IntentSender.java
new file mode 100644
index 0000000..f40dc29
--- /dev/null
+++ b/android/content/IntentSender.java
@@ -0,0 +1,387 @@
+/*
+ * 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.content;
+
+import android.app.ActivityManager;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.AndroidException;
+
+
+/**
+ * A description of an Intent and target action to perform with it.
+ * The returned object can be
+ * handed to other applications so that they can perform the action you
+ * described on your behalf at a later time.
+ *
+ * <p>By giving a IntentSender to another application,
+ * you are granting it the right to perform the operation you have specified
+ * as if the other application was yourself (with the same permissions and
+ * identity).  As such, you should be careful about how you build the IntentSender:
+ * often, for example, the base Intent you supply will have the component
+ * name explicitly set to one of your own components, to ensure it is ultimately
+ * sent there and nowhere else.
+ *
+ * <p>A IntentSender itself is simply a reference to a token maintained by
+ * the system describing the original data used to retrieve it.  This means
+ * that, even if its owning application's process is killed, the
+ * IntentSender itself will remain usable from other processes that
+ * have been given it.  If the creating application later re-retrieves the
+ * same kind of IntentSender (same operation, same Intent action, data,
+ * categories, and components, and same flags), it will receive a IntentSender
+ * representing the same token if that is still valid.
+ *
+ * <p>Instances of this class can not be made directly, but rather must be
+ * created from an existing {@link android.app.PendingIntent} with
+ * {@link android.app.PendingIntent#getIntentSender() PendingIntent.getIntentSender()}.
+ */
+public class IntentSender implements Parcelable {
+    @UnsupportedAppUsage
+    private final IIntentSender mTarget;
+    IBinder mWhitelistToken;
+
+    /**
+     * Exception thrown when trying to send through a PendingIntent that
+     * has been canceled or is otherwise no longer able to execute the request.
+     */
+    public static class SendIntentException extends AndroidException {
+        public SendIntentException() {
+        }
+
+        public SendIntentException(String name) {
+            super(name);
+        }
+
+        public SendIntentException(Exception cause) {
+            super(cause);
+        }
+    }
+
+    /**
+     * Callback interface for discovering when a send operation has
+     * completed.  Primarily for use with a IntentSender that is
+     * performing a broadcast, this provides the same information as
+     * calling {@link Context#sendOrderedBroadcast(Intent, String,
+     * android.content.BroadcastReceiver, Handler, int, String, Bundle)
+     * Context.sendBroadcast()} with a final BroadcastReceiver.
+     */
+    public interface OnFinished {
+        /**
+         * Called when a send operation as completed.
+         *
+         * @param IntentSender The IntentSender this operation was sent through.
+         * @param intent The original Intent that was sent.
+         * @param resultCode The final result code determined by the send.
+         * @param resultData The final data collected by a broadcast.
+         * @param resultExtras The final extras collected by a broadcast.
+         */
+        void onSendFinished(IntentSender IntentSender, Intent intent,
+                int resultCode, String resultData, Bundle resultExtras);
+    }
+
+    private static class FinishedDispatcher extends IIntentReceiver.Stub
+            implements Runnable {
+        private final IntentSender mIntentSender;
+        private final OnFinished mWho;
+        private final Handler mHandler;
+        private Intent mIntent;
+        private int mResultCode;
+        private String mResultData;
+        private Bundle mResultExtras;
+        FinishedDispatcher(IntentSender pi, OnFinished who, Handler handler) {
+            mIntentSender = pi;
+            mWho = who;
+            mHandler = handler;
+        }
+        public void performReceive(Intent intent, int resultCode, String data,
+                Bundle extras, boolean serialized, boolean sticky, int sendingUser) {
+            mIntent = intent;
+            mResultCode = resultCode;
+            mResultData = data;
+            mResultExtras = extras;
+            if (mHandler == null) {
+                run();
+            } else {
+                mHandler.post(this);
+            }
+        }
+        public void run() {
+            mWho.onSendFinished(mIntentSender, mIntent, mResultCode,
+                    mResultData, mResultExtras);
+        }
+    }
+
+    /**
+     * Perform the operation associated with this IntentSender, allowing the
+     * caller to specify information about the Intent to use and be notified
+     * when the send has completed.
+     *
+     * @param context The Context of the caller.  This may be null if
+     * <var>intent</var> is also null.
+     * @param code Result code to supply back to the IntentSender's target.
+     * @param intent Additional Intent data.  See {@link Intent#fillIn
+     * Intent.fillIn()} for information on how this is applied to the
+     * original Intent.  Use null to not modify the original Intent.
+     * @param onFinished The object to call back on when the send has
+     * completed, or null for no callback.
+     * @param handler Handler identifying the thread on which the callback
+     * should happen.  If null, the callback will happen from the thread
+     * pool of the process.
+     *
+     *
+     * @throws SendIntentException Throws CanceledIntentException if the IntentSender
+     * is no longer allowing more intents to be sent through it.
+     */
+    public void sendIntent(Context context, int code, Intent intent,
+            OnFinished onFinished, Handler handler) throws SendIntentException {
+        sendIntent(context, code, intent, onFinished, handler, null);
+    }
+
+    /**
+     * Perform the operation associated with this IntentSender, allowing the
+     * caller to specify information about the Intent to use and be notified
+     * when the send has completed.
+     *
+     * @param context The Context of the caller.  This may be null if
+     * <var>intent</var> is also null.
+     * @param code Result code to supply back to the IntentSender's target.
+     * @param intent Additional Intent data.  See {@link Intent#fillIn
+     * Intent.fillIn()} for information on how this is applied to the
+     * original Intent.  Use null to not modify the original Intent.
+     * @param onFinished The object to call back on when the send has
+     * completed, or null for no callback.
+     * @param handler Handler identifying the thread on which the callback
+     * should happen.  If null, the callback will happen from the thread
+     * pool of the process.
+     * @param requiredPermission Name of permission that a recipient of the PendingIntent
+     * is required to hold.  This is only valid for broadcast intents, and
+     * corresponds to the permission argument in
+     * {@link Context#sendBroadcast(Intent, String) Context.sendOrderedBroadcast(Intent, String)}.
+     * If null, no permission is required.
+     *
+     *
+     * @throws SendIntentException Throws CanceledIntentException if the IntentSender
+     * is no longer allowing more intents to be sent through it.
+     */
+    public void sendIntent(Context context, int code, Intent intent,
+            OnFinished onFinished, Handler handler, String requiredPermission)
+            throws SendIntentException {
+        try {
+            String resolvedType = intent != null ?
+                    intent.resolveTypeIfNeeded(context.getContentResolver())
+                    : null;
+            int res = ActivityManager.getService().sendIntentSender(mTarget, mWhitelistToken,
+                    code, intent, resolvedType,
+                    onFinished != null
+                            ? new FinishedDispatcher(this, onFinished, handler)
+                            : null,
+                    requiredPermission, null);
+            if (res < 0) {
+                throw new SendIntentException();
+            }
+        } catch (RemoteException e) {
+            throw new SendIntentException();
+        }
+    }
+
+    /**
+     * @deprecated Renamed to {@link #getCreatorPackage()}.
+     */
+    @Deprecated
+    public String getTargetPackage() {
+        try {
+            return ActivityManager.getService()
+                .getPackageForIntentSender(mTarget);
+        } catch (RemoteException e) {
+            // Should never happen.
+            return null;
+        }
+    }
+
+    /**
+     * Return the package name of the application that created this
+     * IntentSender, that is the identity under which you will actually be
+     * sending the Intent.  The returned string is supplied by the system, so
+     * that an application can not spoof its package.
+     *
+     * @return The package name of the PendingIntent, or null if there is
+     * none associated with it.
+     */
+    public String getCreatorPackage() {
+        try {
+            return ActivityManager.getService()
+                .getPackageForIntentSender(mTarget);
+        } catch (RemoteException e) {
+            // Should never happen.
+            return null;
+        }
+    }
+
+    /**
+     * Return the uid of the application that created this
+     * PendingIntent, that is the identity under which you will actually be
+     * sending the Intent.  The returned integer is supplied by the system, so
+     * that an application can not spoof its uid.
+     *
+     * @return The uid of the PendingIntent, or -1 if there is
+     * none associated with it.
+     */
+    public int getCreatorUid() {
+        try {
+            return ActivityManager.getService()
+                .getUidForIntentSender(mTarget);
+        } catch (RemoteException e) {
+            // Should never happen.
+            return -1;
+        }
+    }
+
+    /**
+     * Return the user handle of the application that created this
+     * PendingIntent, that is the user under which you will actually be
+     * sending the Intent.  The returned UserHandle is supplied by the system, so
+     * that an application can not spoof its user.  See
+     * {@link android.os.Process#myUserHandle() Process.myUserHandle()} for
+     * more explanation of user handles.
+     *
+     * @return The user handle of the PendingIntent, or null if there is
+     * none associated with it.
+     */
+    public UserHandle getCreatorUserHandle() {
+        try {
+            int uid = ActivityManager.getService()
+                .getUidForIntentSender(mTarget);
+            return uid > 0 ? new UserHandle(UserHandle.getUserId(uid)) : null;
+        } catch (RemoteException e) {
+            // Should never happen.
+            return null;
+        }
+    }
+
+    /**
+     * Comparison operator on two IntentSender objects, such that true
+     * is returned then they both represent the same operation from the
+     * same package.
+     */
+    @Override
+    public boolean equals(Object otherObj) {
+        if (otherObj instanceof IntentSender) {
+            return mTarget.asBinder().equals(((IntentSender)otherObj)
+                    .mTarget.asBinder());
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return mTarget.asBinder().hashCode();
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder(128);
+        sb.append("IntentSender{");
+        sb.append(Integer.toHexString(System.identityHashCode(this)));
+        sb.append(": ");
+        sb.append(mTarget != null ? mTarget.asBinder() : null);
+        sb.append('}');
+        return sb.toString();
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeStrongBinder(mTarget.asBinder());
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<IntentSender> CREATOR
+            = new Parcelable.Creator<IntentSender>() {
+        public IntentSender createFromParcel(Parcel in) {
+            IBinder target = in.readStrongBinder();
+            return target != null ? new IntentSender(target) : null;
+        }
+
+        public IntentSender[] newArray(int size) {
+            return new IntentSender[size];
+        }
+    };
+
+    /**
+     * Convenience function for writing either a IntentSender or null pointer to
+     * a Parcel.  You must use this with {@link #readIntentSenderOrNullFromParcel}
+     * for later reading it.
+     *
+     * @param sender The IntentSender to write, or null.
+     * @param out Where to write the IntentSender.
+     */
+    public static void writeIntentSenderOrNullToParcel(IntentSender sender,
+            Parcel out) {
+        out.writeStrongBinder(sender != null ? sender.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 #writeIntentSenderOrNullToParcel}.
+     *
+     * @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 IntentSender readIntentSenderOrNullFromParcel(Parcel in) {
+        IBinder b = in.readStrongBinder();
+        return b != null ? new IntentSender(b) : null;
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage
+    public IIntentSender getTarget() {
+        return mTarget;
+    }
+
+    /** @hide */
+    public IBinder getWhitelistToken() {
+        return mWhitelistToken;
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage
+    public IntentSender(IIntentSender target) {
+        mTarget = target;
+    }
+
+    /** @hide */
+    public IntentSender(IIntentSender target, IBinder whitelistToken) {
+        mTarget = target;
+        mWhitelistToken = whitelistToken;
+    }
+
+    /** @hide */
+    public IntentSender(IBinder target) {
+        mTarget = IIntentSender.Stub.asInterface(target);
+    }
+}
diff --git a/android/content/Loader.java b/android/content/Loader.java
new file mode 100644
index 0000000..b0555d4
--- /dev/null
+++ b/android/content/Loader.java
@@ -0,0 +1,565 @@
+/*
+ * 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.content;
+
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.util.DebugUtils;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * A class that performs asynchronous loading of data. While Loaders are active
+ * they should monitor the source of their data and deliver new results when the contents
+ * change.  See {@link android.app.LoaderManager} for more detail.
+ *
+ * <p><b>Note on threading:</b> Clients of loaders should as a rule perform
+ * any calls on to a Loader from the main thread of their process (that is,
+ * the thread the Activity callbacks and other things occur on).  Subclasses
+ * of Loader (such as {@link AsyncTaskLoader}) will often perform their work
+ * in a separate thread, but when delivering their results this too should
+ * be done on the main thread.</p>
+ *
+ * <p>Subclasses generally must implement at least {@link #onStartLoading()},
+ * {@link #onStopLoading()}, {@link #onForceLoad()}, and {@link #onReset()}.</p>
+ *
+ * <p>Most implementations should not derive directly from this class, but
+ * instead inherit from {@link AsyncTaskLoader}.</p>
+ *
+ * <div class="special reference">
+ * <h3>Developer Guides</h3>
+ * <p>For more information about using loaders, read the
+ * <a href="{@docRoot}guide/components/loaders.html">Loaders</a> developer guide.</p>
+ * </div>
+ *
+ * @param <D> The result returned when the load is complete
+ *
+ * @deprecated Use the <a href="{@docRoot}tools/extras/support-library.html">Support Library</a>
+ *      {@link android.support.v4.content.Loader}
+ */
+@Deprecated
+public class Loader<D> {
+    int mId;
+    OnLoadCompleteListener<D> mListener;
+    OnLoadCanceledListener<D> mOnLoadCanceledListener;
+    Context mContext;
+    boolean mStarted = false;
+    boolean mAbandoned = false;
+    boolean mReset = true;
+    boolean mContentChanged = false;
+    boolean mProcessingChange = false;
+
+    /**
+     * An implementation of a ContentObserver that takes care of connecting
+     * it to the Loader to have the loader re-load its data when the observer
+     * is told it has changed.  You do not normally need to use this yourself;
+     * it is used for you by {@link CursorLoader} to take care of executing
+     * an update when the cursor's backing data changes.
+     *
+     * @deprecated Use {@link android.support.v4.content.Loader.ForceLoadContentObserver}
+     */
+    @Deprecated
+    public final class ForceLoadContentObserver extends ContentObserver {
+        public ForceLoadContentObserver() {
+            super(new Handler());
+        }
+
+        @Override
+        public boolean deliverSelfNotifications() {
+            return true;
+        }
+
+        @Override
+        public void onChange(boolean selfChange) {
+            onContentChanged();
+        }
+    }
+
+    /**
+     * Interface that is implemented to discover when a Loader has finished
+     * loading its data.  You do not normally need to implement this yourself;
+     * it is used in the implementation of {@link android.app.LoaderManager}
+     * to find out when a Loader it is managing has completed so that this can
+     * be reported to its client.  This interface should only be used if a
+     * Loader is not being used in conjunction with LoaderManager.
+     *
+     * @deprecated Use {@link android.support.v4.content.Loader.OnLoadCompleteListener}
+     */
+    @Deprecated
+    public interface OnLoadCompleteListener<D> {
+        /**
+         * Called on the thread that created the Loader when the load is complete.
+         *
+         * @param loader the loader that completed the load
+         * @param data the result of the load
+         */
+        public void onLoadComplete(Loader<D> loader, D data);
+    }
+
+    /**
+     * Interface that is implemented to discover when a Loader has been canceled
+     * before it finished loading its data.  You do not normally need to implement
+     * this yourself; it is used in the implementation of {@link android.app.LoaderManager}
+     * to find out when a Loader it is managing has been canceled so that it
+     * can schedule the next Loader.  This interface should only be used if a
+     * Loader is not being used in conjunction with LoaderManager.
+     *
+     * @deprecated Use {@link android.support.v4.content.Loader.OnLoadCanceledListener}
+     */
+    @Deprecated
+    public interface OnLoadCanceledListener<D> {
+        /**
+         * Called on the thread that created the Loader when the load is canceled.
+         *
+         * @param loader the loader that canceled the load
+         */
+        public void onLoadCanceled(Loader<D> loader);
+    }
+
+    /**
+     * Stores away the application context associated with context.
+     * Since Loaders can be used across multiple activities it's dangerous to
+     * store the context directly; always use {@link #getContext()} to retrieve
+     * the Loader's Context, don't use the constructor argument directly.
+     * The Context returned by {@link #getContext} is safe to use across
+     * Activity instances.
+     *
+     * @param context used to retrieve the application context.
+     */
+    public Loader(Context context) {
+        mContext = context.getApplicationContext();
+    }
+
+    /**
+     * Sends the result of the load to the registered listener. Should only be called by subclasses.
+     *
+     * Must be called from the process's main thread.
+     *
+     * @param data the result of the load
+     */
+    public void deliverResult(D data) {
+        if (mListener != null) {
+            mListener.onLoadComplete(this, data);
+        }
+    }
+
+    /**
+     * Informs the registered {@link OnLoadCanceledListener} that the load has been canceled.
+     * Should only be called by subclasses.
+     *
+     * Must be called from the process's main thread.
+     */
+    public void deliverCancellation() {
+        if (mOnLoadCanceledListener != null) {
+            mOnLoadCanceledListener.onLoadCanceled(this);
+        }
+    }
+
+    /**
+     * @return an application context retrieved from the Context passed to the constructor.
+     */
+    public Context getContext() {
+        return mContext;
+    }
+
+    /**
+     * @return the ID of this loader
+     */
+    public int getId() {
+        return mId;
+    }
+
+    /**
+     * Registers a class that will receive callbacks when a load is complete.
+     * The callback will be called on the process's main thread so it's safe to
+     * pass the results to widgets.
+     *
+     * <p>Must be called from the process's main thread.
+     */
+    public void registerListener(int id, OnLoadCompleteListener<D> listener) {
+        if (mListener != null) {
+            throw new IllegalStateException("There is already a listener registered");
+        }
+        mListener = listener;
+        mId = id;
+    }
+
+    /**
+     * Remove a listener that was previously added with {@link #registerListener}.
+     *
+     * Must be called from the process's main thread.
+     */
+    public void unregisterListener(OnLoadCompleteListener<D> listener) {
+        if (mListener == null) {
+            throw new IllegalStateException("No listener register");
+        }
+        if (mListener != listener) {
+            throw new IllegalArgumentException("Attempting to unregister the wrong listener");
+        }
+        mListener = null;
+    }
+
+    /**
+     * Registers a listener that will receive callbacks when a load is canceled.
+     * The callback will be called on the process's main thread so it's safe to
+     * pass the results to widgets.
+     *
+     * Must be called from the process's main thread.
+     *
+     * @param listener The listener to register.
+     */
+    public void registerOnLoadCanceledListener(OnLoadCanceledListener<D> listener) {
+        if (mOnLoadCanceledListener != null) {
+            throw new IllegalStateException("There is already a listener registered");
+        }
+        mOnLoadCanceledListener = listener;
+    }
+
+    /**
+     * Unregisters a listener that was previously added with
+     * {@link #registerOnLoadCanceledListener}.
+     *
+     * Must be called from the process's main thread.
+     *
+     * @param listener The listener to unregister.
+     */
+    public void unregisterOnLoadCanceledListener(OnLoadCanceledListener<D> listener) {
+        if (mOnLoadCanceledListener == null) {
+            throw new IllegalStateException("No listener register");
+        }
+        if (mOnLoadCanceledListener != listener) {
+            throw new IllegalArgumentException("Attempting to unregister the wrong listener");
+        }
+        mOnLoadCanceledListener = null;
+    }
+
+    /**
+     * Return whether this load has been started.  That is, its {@link #startLoading()}
+     * has been called and no calls to {@link #stopLoading()} or
+     * {@link #reset()} have yet been made.
+     */
+    public boolean isStarted() {
+        return mStarted;
+    }
+
+    /**
+     * Return whether this loader has been abandoned.  In this state, the
+     * loader <em>must not</em> report any new data, and <em>must</em> keep
+     * its last reported data valid until it is finally reset.
+     */
+    public boolean isAbandoned() {
+        return mAbandoned;
+    }
+
+    /**
+     * Return whether this load has been reset.  That is, either the loader
+     * has not yet been started for the first time, or its {@link #reset()}
+     * has been called.
+     */
+    public boolean isReset() {
+        return mReset;
+    }
+
+    /**
+     * This function will normally be called for you automatically by
+     * {@link android.app.LoaderManager} when the associated fragment/activity
+     * is being started.  When using a Loader with {@link android.app.LoaderManager},
+     * you <em>must not</em> call this method yourself, or you will conflict
+     * with its management of the Loader.
+     *
+     * Starts an asynchronous load of the Loader's data. When the result
+     * is ready the callbacks will be called on the process's main thread.
+     * If a previous load has been completed and is still valid
+     * the result may be passed to the callbacks immediately.
+     * The loader will monitor the source of
+     * the data set and may deliver future callbacks if the source changes.
+     * Calling {@link #stopLoading} will stop the delivery of callbacks.
+     *
+     * <p>This updates the Loader's internal state so that
+     * {@link #isStarted()} and {@link #isReset()} will return the correct
+     * values, and then calls the implementation's {@link #onStartLoading()}.
+     *
+     * <p>Must be called from the process's main thread.
+     */
+    public final void startLoading() {
+        mStarted = true;
+        mReset = false;
+        mAbandoned = false;
+        onStartLoading();
+    }
+
+    /**
+     * Subclasses must implement this to take care of loading their data,
+     * as per {@link #startLoading()}.  This is not called by clients directly,
+     * but as a result of a call to {@link #startLoading()}.
+     */
+    protected void onStartLoading() {
+    }
+
+    /**
+     * Attempt to cancel the current load task.
+     * Must be called on the main thread of the process.
+     *
+     * <p>Cancellation is not an immediate operation, since the load is performed
+     * in a background thread.  If there is currently a load in progress, this
+     * method requests that the load be canceled, and notes this is the case;
+     * once the background thread has completed its work its remaining state
+     * will be cleared.  If another load request comes in during this time,
+     * it will be held until the canceled load is complete.
+     *
+     * @return Returns <tt>false</tt> if the task could not be canceled,
+     * typically because it has already completed normally, or
+     * because {@link #startLoading()} hasn't been called; returns
+     * <tt>true</tt> otherwise.  When <tt>true</tt> is returned, the task
+     * is still running and the {@link OnLoadCanceledListener} will be called
+     * when the task completes.
+     */
+    public boolean cancelLoad() {
+        return onCancelLoad();
+    }
+
+    /**
+     * Subclasses must implement this to take care of requests to {@link #cancelLoad()}.
+     * This will always be called from the process's main thread.
+     *
+     * @return Returns <tt>false</tt> if the task could not be canceled,
+     * typically because it has already completed normally, or
+     * because {@link #startLoading()} hasn't been called; returns
+     * <tt>true</tt> otherwise.  When <tt>true</tt> is returned, the task
+     * is still running and the {@link OnLoadCanceledListener} will be called
+     * when the task completes.
+     */
+    protected boolean onCancelLoad() {
+        return false;
+    }
+
+    /**
+     * Force an asynchronous load. Unlike {@link #startLoading()} this will ignore a previously
+     * loaded data set and load a new one.  This simply calls through to the
+     * implementation's {@link #onForceLoad()}.  You generally should only call this
+     * when the loader is started -- that is, {@link #isStarted()} returns true.
+     *
+     * <p>Must be called from the process's main thread.
+     */
+    public void forceLoad() {
+        onForceLoad();
+    }
+
+    /**
+     * Subclasses must implement this to take care of requests to {@link #forceLoad()}.
+     * This will always be called from the process's main thread.
+     */
+    protected void onForceLoad() {
+    }
+
+    /**
+     * This function will normally be called for you automatically by
+     * {@link android.app.LoaderManager} when the associated fragment/activity
+     * is being stopped.  When using a Loader with {@link android.app.LoaderManager},
+     * you <em>must not</em> call this method yourself, or you will conflict
+     * with its management of the Loader.
+     *
+     * <p>Stops delivery of updates until the next time {@link #startLoading()} is called.
+     * Implementations should <em>not</em> invalidate their data at this point --
+     * clients are still free to use the last data the loader reported.  They will,
+     * however, typically stop reporting new data if the data changes; they can
+     * still monitor for changes, but must not report them to the client until and
+     * if {@link #startLoading()} is later called.
+     *
+     * <p>This updates the Loader's internal state so that
+     * {@link #isStarted()} will return the correct
+     * value, and then calls the implementation's {@link #onStopLoading()}.
+     *
+     * <p>Must be called from the process's main thread.
+     */
+    public void stopLoading() {
+        mStarted = false;
+        onStopLoading();
+    }
+
+    /**
+     * Subclasses must implement this to take care of stopping their loader,
+     * as per {@link #stopLoading()}.  This is not called by clients directly,
+     * but as a result of a call to {@link #stopLoading()}.
+     * This will always be called from the process's main thread.
+     */
+    protected void onStopLoading() {
+    }
+
+    /**
+     * This function will normally be called for you automatically by
+     * {@link android.app.LoaderManager} when restarting a Loader.  When using
+     * a Loader with {@link android.app.LoaderManager},
+     * you <em>must not</em> call this method yourself, or you will conflict
+     * with its management of the Loader.
+     *
+     * Tell the Loader that it is being abandoned.  This is called prior
+     * to {@link #reset} to have it retain its current data but not report
+     * any new data.
+     */
+    public void abandon() {
+        mAbandoned = true;
+        onAbandon();
+    }
+    
+    /**
+     * Subclasses implement this to take care of being abandoned.  This is
+     * an optional intermediate state prior to {@link #onReset()} -- it means that
+     * the client is no longer interested in any new data from the loader,
+     * so the loader must not report any further updates.  However, the
+     * loader <em>must</em> keep its last reported data valid until the final
+     * {@link #onReset()} happens.  You can retrieve the current abandoned
+     * state with {@link #isAbandoned}.
+     */
+    protected void onAbandon() {
+    }
+    
+    /**
+     * This function will normally be called for you automatically by
+     * {@link android.app.LoaderManager} when destroying a Loader.  When using
+     * a Loader with {@link android.app.LoaderManager},
+     * you <em>must not</em> call this method yourself, or you will conflict
+     * with its management of the Loader.
+     *
+     * Resets the state of the Loader.  The Loader should at this point free
+     * all of its resources, since it may never be called again; however, its
+     * {@link #startLoading()} may later be called at which point it must be
+     * able to start running again.
+     *
+     * <p>This updates the Loader's internal state so that
+     * {@link #isStarted()} and {@link #isReset()} will return the correct
+     * values, and then calls the implementation's {@link #onReset()}.
+     *
+     * <p>Must be called from the process's main thread.
+     */
+    public void reset() {
+        onReset();
+        mReset = true;
+        mStarted = false;
+        mAbandoned = false;
+        mContentChanged = false;
+        mProcessingChange = false;
+    }
+
+    /**
+     * Subclasses must implement this to take care of resetting their loader,
+     * as per {@link #reset()}.  This is not called by clients directly,
+     * but as a result of a call to {@link #reset()}.
+     * This will always be called from the process's main thread.
+     */
+    protected void onReset() {
+    }
+
+    /**
+     * Take the current flag indicating whether the loader's content had
+     * changed while it was stopped.  If it had, true is returned and the
+     * flag is cleared.
+     */
+    public boolean takeContentChanged() {
+        boolean res = mContentChanged;
+        mContentChanged = false;
+        mProcessingChange |= res;
+        return res;
+    }
+
+    /**
+     * Commit that you have actually fully processed a content change that
+     * was returned by {@link #takeContentChanged}.  This is for use with
+     * {@link #rollbackContentChanged()} to handle situations where a load
+     * is cancelled.  Call this when you have completely processed a load
+     * without it being cancelled.
+     */
+    public void commitContentChanged() {
+        mProcessingChange = false;
+    }
+
+    /**
+     * Report that you have abandoned the processing of a content change that
+     * was returned by {@link #takeContentChanged()} and would like to rollback
+     * to the state where there is again a pending content change.  This is
+     * to handle the case where a data load due to a content change has been
+     * canceled before its data was delivered back to the loader.
+     */
+    public void rollbackContentChanged() {
+        if (mProcessingChange) {
+            onContentChanged();
+        }
+    }
+
+    /**
+     * Called when {@link ForceLoadContentObserver} detects a change.  The
+     * default implementation checks to see if the loader is currently started;
+     * if so, it simply calls {@link #forceLoad()}; otherwise, it sets a flag
+     * so that {@link #takeContentChanged()} returns true.
+     *
+     * <p>Must be called from the process's main thread.
+     */
+    public void onContentChanged() {
+        if (mStarted) {
+            forceLoad();
+        } else {
+            // This loader has been stopped, so we don't want to load
+            // new data right now...  but keep track of it changing to
+            // refresh later if we start again.
+            mContentChanged = true;
+        }
+    }
+
+    /**
+     * For debugging, converts an instance of the Loader's data class to
+     * a string that can be printed.  Must handle a null data.
+     */
+    public String dataToString(D data) {
+        StringBuilder sb = new StringBuilder(64);
+        DebugUtils.buildShortClassTag(data, sb);
+        sb.append("}");
+        return sb.toString();
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder(64);
+        DebugUtils.buildShortClassTag(this, sb);
+        sb.append(" id=");
+        sb.append(mId);
+        sb.append("}");
+        return sb.toString();
+    }
+
+    /**
+     * Print the Loader's state into the given stream.
+     *
+     * @param prefix Text to print at the front of each line.
+     * @param fd The raw file descriptor that the dump is being sent to.
+     * @param writer A PrintWriter to which the dump is to be set.
+     * @param args Additional arguments to the dump request.
+     */
+    public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+        writer.print(prefix); writer.print("mId="); writer.print(mId);
+                writer.print(" mListener="); writer.println(mListener);
+        if (mStarted || mContentChanged || mProcessingChange) {
+            writer.print(prefix); writer.print("mStarted="); writer.print(mStarted);
+                    writer.print(" mContentChanged="); writer.print(mContentChanged);
+                    writer.print(" mProcessingChange="); writer.println(mProcessingChange);
+        }
+        if (mAbandoned || mReset) {
+            writer.print(prefix); writer.print("mAbandoned="); writer.print(mAbandoned);
+                    writer.print(" mReset="); writer.println(mReset);
+        }
+    }
+}
diff --git a/android/content/LocusId.java b/android/content/LocusId.java
new file mode 100644
index 0000000..283cea0
--- /dev/null
+++ b/android/content/LocusId.java
@@ -0,0 +1,149 @@
+/*
+ * 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.content;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.contentcapture.ContentCaptureManager;
+
+import com.android.internal.util.Preconditions;
+
+import java.io.PrintWriter;
+
+/**
+ * An identifier for an unique state (locus) in the application. Should be stable across reboots and
+ * backup / restore.
+ *
+ * <p>Locus is a new concept introduced on
+ * {@link android.os.Build.VERSION_CODES#Q Android Q} and it lets the intelligence service provided
+ * by the Android System to correlate state between different subsystems such as content capture,
+ * shortcuts, and notifications.
+ *
+ * <p>For example, if your app provides an activiy representing a chat between 2 users
+ * (say {@code A} and {@code B}, this chat state could be represented by:
+ *
+ * <pre><code>
+ * LocusId chatId = new LocusId("Chat_A_B");
+ * </code></pre>
+ *
+ * <p>And then you should use that {@code chatId} by:
+ *
+ * <ul>
+ *   <li>Setting it in the chat notification (through
+ *   {@link android.app.Notification.Builder#setLocusId(LocusId)
+ *   Notification.Builder.setLocusId(chatId)}).
+ *   <li>Setting it into the {@link android.content.pm.ShortcutInfo} (through
+ *   {@link android.content.pm.ShortcutInfo.Builder#setLocusId(LocusId)
+ *   ShortcutInfo.Builder.setLocusId(chatId)}), if you provide a launcher shortcut for that chat
+ *   conversation.
+ *   <li>Associating it with the {@link android.view.contentcapture.ContentCaptureContext} of the
+ *   root view of the chat conversation activity (through
+ *   {@link android.view.View#getContentCaptureSession()}, then
+ *   {@link android.view.contentcapture.ContentCaptureContext.Builder
+ *   new ContentCaptureContext.Builder(chatId).build()} and
+ *   {@link android.view.contentcapture.ContentCaptureSession#setContentCaptureContext(
+ *   android.view.contentcapture.ContentCaptureContext)} - see {@link ContentCaptureManager}
+ *   for more info about content capture).
+ *   <li>Configuring your app to launch the chat conversation through the
+ *   {@link Intent#ACTION_VIEW_LOCUS} intent.
+ * </ul>
+ */
+public final class LocusId implements Parcelable {
+
+    private final String mId;
+
+    /**
+     * Default constructor.
+     *
+     * @throws IllegalArgumentException if {@code id} is empty or {@code null}.
+     */
+    public LocusId(@NonNull String id) {
+        mId = Preconditions.checkStringNotEmpty(id, "id cannot be empty");
+    }
+
+    /**
+     * Gets the canonical {@code id} associated with the locus.
+     */
+    @NonNull
+    public String getId() {
+        return mId;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((mId == null) ? 0 : mId.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) return true;
+        if (obj == null) return false;
+        if (getClass() != obj.getClass()) return false;
+        final LocusId other = (LocusId) obj;
+        if (mId == null) {
+            if (other.mId != null) return false;
+        } else {
+            if (!mId.equals(other.mId)) return false;
+        }
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return "LocusId[" + getSanitizedId() + "]";
+    }
+
+    /** @hide */
+    public void dump(@NonNull PrintWriter pw) {
+        pw.print("id:"); pw.println(getSanitizedId());
+    }
+
+    @NonNull
+    private String getSanitizedId() {
+        final int size = mId.length();
+        return size + "_chars";
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeString(mId);
+    }
+
+    public static final @NonNull Parcelable.Creator<LocusId> CREATOR =
+            new Parcelable.Creator<LocusId>() {
+
+        @NonNull
+        @Override
+        public LocusId createFromParcel(Parcel parcel) {
+            return new LocusId(parcel.readString());
+        }
+
+        @NonNull
+        @Override
+        public LocusId[] newArray(int size) {
+            return new LocusId[size];
+        }
+    };
+}
diff --git a/android/content/LoggingContentInterface.java b/android/content/LoggingContentInterface.java
new file mode 100644
index 0000000..3bd0832
--- /dev/null
+++ b/android/content/LoggingContentInterface.java
@@ -0,0 +1,297 @@
+/*
+ * 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.content;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.res.AssetFileDescriptor;
+import android.database.Cursor;
+import android.database.DatabaseUtils;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * Instance of {@link ContentInterface} that logs all inputs and outputs while
+ * delegating to another {@link ContentInterface}.
+ *
+ * @hide
+ */
+public class LoggingContentInterface implements ContentInterface {
+    private final String tag;
+    private final ContentInterface delegate;
+
+    public LoggingContentInterface(String tag, ContentInterface delegate) {
+        this.tag = tag;
+        this.delegate = delegate;
+    }
+
+    private class Logger implements AutoCloseable {
+        private final StringBuilder sb = new StringBuilder();
+
+        public Logger(String method, Object... args) {
+            // First, force-unparcel any bundles so we can log them
+            for (Object arg : args) {
+                if (arg instanceof Bundle) {
+                    ((Bundle) arg).size();
+                }
+            }
+
+            sb.append("callingUid=").append(Binder.getCallingUid()).append(' ');
+            sb.append(method);
+            sb.append('(').append(deepToString(args)).append(')');
+        }
+
+        private String deepToString(Object value) {
+            if (value != null && value.getClass().isArray()) {
+                return Arrays.deepToString((Object[]) value);
+            } else {
+                return String.valueOf(value);
+            }
+        }
+
+        public <T> T setResult(T res) {
+            if (res instanceof Cursor) {
+                sb.append('\n');
+                DatabaseUtils.dumpCursor((Cursor) res, sb);
+            } else {
+                sb.append(" = ").append(deepToString(res));
+            }
+            return res;
+        }
+
+        @Override
+        public void close() {
+            Log.v(tag, sb.toString());
+        }
+    }
+
+    @Override
+    public @Nullable Cursor query(@NonNull Uri uri, @Nullable String[] projection,
+            @Nullable Bundle queryArgs, @Nullable CancellationSignal cancellationSignal)
+            throws RemoteException {
+        try (Logger l = new Logger("query", uri, projection, queryArgs, cancellationSignal)) {
+            try {
+                return l.setResult(delegate.query(uri, projection, queryArgs, cancellationSignal));
+            } catch (Exception res) {
+                l.setResult(res);
+                throw res;
+            }
+        }
+    }
+
+    @Override
+    public @Nullable String getType(@NonNull Uri uri) throws RemoteException {
+        try (Logger l = new Logger("getType", uri)) {
+            try {
+                return l.setResult(delegate.getType(uri));
+            } catch (Exception res) {
+                l.setResult(res);
+                throw res;
+            }
+        }
+    }
+
+    @Override
+    public @Nullable String[] getStreamTypes(@NonNull Uri uri, @NonNull String mimeTypeFilter)
+            throws RemoteException {
+        try (Logger l = new Logger("getStreamTypes", uri, mimeTypeFilter)) {
+            try {
+                return l.setResult(delegate.getStreamTypes(uri, mimeTypeFilter));
+            } catch (Exception res) {
+                l.setResult(res);
+                throw res;
+            }
+        }
+    }
+
+    @Override
+    public @Nullable Uri canonicalize(@NonNull Uri uri) throws RemoteException {
+        try (Logger l = new Logger("canonicalize", uri)) {
+            try {
+                return l.setResult(delegate.canonicalize(uri));
+            } catch (Exception res) {
+                l.setResult(res);
+                throw res;
+            }
+        }
+    }
+
+    @Override
+    public @Nullable Uri uncanonicalize(@NonNull Uri uri) throws RemoteException {
+        try (Logger l = new Logger("uncanonicalize", uri)) {
+            try {
+                return l.setResult(delegate.uncanonicalize(uri));
+            } catch (Exception res) {
+                l.setResult(res);
+                throw res;
+            }
+        }
+    }
+
+    @Override
+    public boolean refresh(@NonNull Uri uri, @Nullable Bundle args,
+            @Nullable CancellationSignal cancellationSignal) throws RemoteException {
+        try (Logger l = new Logger("refresh", uri, args, cancellationSignal)) {
+            try {
+                return l.setResult(delegate.refresh(uri, args, cancellationSignal));
+            } catch (Exception res) {
+                l.setResult(res);
+                throw res;
+            }
+        }
+    }
+
+    @Override
+    public int checkUriPermission(@NonNull Uri uri, int uid, @Intent.AccessUriMode int modeFlags)
+            throws RemoteException {
+        try (Logger l = new Logger("checkUriPermission", uri, uid, modeFlags)) {
+            try {
+                return l.setResult(delegate.checkUriPermission(uri, uid, modeFlags));
+            } catch (Exception res) {
+                l.setResult(res);
+                throw res;
+            }
+        }
+    }
+
+    @Override
+    public @Nullable Uri insert(@NonNull Uri uri, @Nullable ContentValues initialValues,
+            @Nullable Bundle extras) throws RemoteException {
+        try (Logger l = new Logger("insert", uri, initialValues, extras)) {
+            try {
+                return l.setResult(delegate.insert(uri, initialValues, extras));
+            } catch (Exception res) {
+                l.setResult(res);
+                throw res;
+            }
+        }
+    }
+
+    @Override
+    public int bulkInsert(@NonNull Uri uri, @NonNull ContentValues[] initialValues)
+            throws RemoteException {
+        try (Logger l = new Logger("bulkInsert", uri, initialValues)) {
+            try {
+                return l.setResult(delegate.bulkInsert(uri, initialValues));
+            } catch (Exception res) {
+                l.setResult(res);
+                throw res;
+            }
+        }
+    }
+
+    @Override
+    public int delete(@NonNull Uri uri, @Nullable Bundle extras) throws RemoteException {
+        try (Logger l = new Logger("delete", uri, extras)) {
+            try {
+                return l.setResult(delegate.delete(uri, extras));
+            } catch (Exception res) {
+                l.setResult(res);
+                throw res;
+            }
+        }
+    }
+
+    @Override
+    public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable Bundle extras)
+            throws RemoteException {
+        try (Logger l = new Logger("update", uri, values, extras)) {
+            try {
+                return l.setResult(delegate.update(uri, values, extras));
+            } catch (Exception res) {
+                l.setResult(res);
+                throw res;
+            }
+        }
+    }
+
+    @Override
+    public @Nullable ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode,
+            @Nullable CancellationSignal signal) throws RemoteException, FileNotFoundException {
+        try (Logger l = new Logger("openFile", uri, mode, signal)) {
+            try {
+                return l.setResult(delegate.openFile(uri, mode, signal));
+            } catch (Exception res) {
+                l.setResult(res);
+                throw res;
+            }
+        }
+    }
+
+    @Override
+    public @Nullable AssetFileDescriptor openAssetFile(@NonNull Uri uri, @NonNull String mode,
+            @Nullable CancellationSignal signal) throws RemoteException, FileNotFoundException {
+        try (Logger l = new Logger("openAssetFile", uri, mode, signal)) {
+            try {
+                return l.setResult(delegate.openAssetFile(uri, mode, signal));
+            } catch (Exception res) {
+                l.setResult(res);
+                throw res;
+            }
+        }
+    }
+
+    @Override
+    public @Nullable AssetFileDescriptor openTypedAssetFile(@NonNull Uri uri,
+            @NonNull String mimeTypeFilter, @Nullable Bundle opts,
+            @Nullable CancellationSignal signal) throws RemoteException, FileNotFoundException {
+        try (Logger l = new Logger("openTypedAssetFile", uri, mimeTypeFilter, opts, signal)) {
+            try {
+                return l.setResult(delegate.openTypedAssetFile(uri, mimeTypeFilter, opts, signal));
+            } catch (Exception res) {
+                l.setResult(res);
+                throw res;
+            }
+        }
+    }
+
+    @Override
+    public @NonNull ContentProviderResult[] applyBatch(@NonNull String authority,
+            @NonNull ArrayList<ContentProviderOperation> operations)
+            throws RemoteException, OperationApplicationException {
+        try (Logger l = new Logger("applyBatch", authority, operations)) {
+            try {
+                return l.setResult(delegate.applyBatch(authority, operations));
+            } catch (Exception res) {
+                l.setResult(res);
+                throw res;
+            }
+        }
+    }
+
+    @Override
+    public @Nullable Bundle call(@NonNull String authority, @NonNull String method,
+            @Nullable String arg, @Nullable Bundle extras) throws RemoteException {
+        try (Logger l = new Logger("call", authority, method, arg, extras)) {
+            try {
+                return l.setResult(delegate.call(authority, method, arg, extras));
+            } catch (Exception res) {
+                l.setResult(res);
+                throw res;
+            }
+        }
+    }
+}
diff --git a/android/content/MimeTypeFilter.java b/android/content/MimeTypeFilter.java
new file mode 100644
index 0000000..1c26fd9
--- /dev/null
+++ b/android/content/MimeTypeFilter.java
@@ -0,0 +1,154 @@
+/*
+ * 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.content;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.util.ArrayList;
+
+/**
+ * Provides utility methods for matching MIME type filters used in ContentProvider.
+ *
+ * <p>Wildcards are allowed only instead of the entire type or subtype with a tree prefix.
+ * Eg. image\/*, *\/* is a valid filter and will match image/jpeg, but image/j* is invalid and
+ * it will not match image/jpeg. Suffixes and parameters are not supported, and they are treated
+ * as part of the subtype during matching. Neither type nor subtype can be empty.
+ *
+ * <p><em>Note: MIME type matching in the Android framework is case-sensitive, unlike the formal
+ * RFC definitions. As a result, you should always write these elements with lower case letters,
+ * or use {@link android.content.Intent#normalizeMimeType} to ensure that they are converted to
+ * lower case.</em>
+ *
+ * <p>MIME types can be null or ill-formatted. In such case they won't match anything.
+ *
+ * <p>MIME type filters must be correctly formatted, or an exception will be thrown.
+ * Copied from support library.
+ * {@hide}
+ */
+public final class MimeTypeFilter {
+
+    private MimeTypeFilter() {
+    }
+
+    private static boolean mimeTypeAgainstFilter(
+            @NonNull String[] mimeTypeParts, @NonNull String[] filterParts) {
+        if (filterParts.length != 2) {
+            throw new IllegalArgumentException(
+                    "Ill-formatted MIME type filter. Must be type/subtype.");
+        }
+        if (filterParts[0].isEmpty() || filterParts[1].isEmpty()) {
+            throw new IllegalArgumentException(
+                    "Ill-formatted MIME type filter. Type or subtype empty.");
+        }
+        if (mimeTypeParts.length != 2) {
+            return false;
+        }
+        if (!"*".equals(filterParts[0])
+                && !filterParts[0].equals(mimeTypeParts[0])) {
+            return false;
+        }
+        if (!"*".equals(filterParts[1])
+                && !filterParts[1].equals(mimeTypeParts[1])) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Matches one nullable MIME type against one MIME type filter.
+     * @return True if the {@code mimeType} matches the {@code filter}.
+     */
+    public static boolean matches(@Nullable String mimeType, @NonNull String filter) {
+        if (mimeType == null) {
+            return false;
+        }
+
+        final String[] mimeTypeParts = mimeType.split("/");
+        final String[] filterParts = filter.split("/");
+
+        return mimeTypeAgainstFilter(mimeTypeParts, filterParts);
+    }
+
+    /**
+     * Matches one nullable MIME type against an array of MIME type filters.
+     * @return The first matching filter, or null if nothing matches.
+     */
+    @Nullable
+    public static String matches(
+            @Nullable String mimeType, @NonNull String[] filters) {
+        if (mimeType == null) {
+            return null;
+        }
+
+        final String[] mimeTypeParts = mimeType.split("/");
+        for (String filter : filters) {
+            final String[] filterParts = filter.split("/");
+            if (mimeTypeAgainstFilter(mimeTypeParts, filterParts)) {
+                return filter;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Matches multiple MIME types against an array of MIME type filters.
+     * @return The first matching MIME type, or null if nothing matches.
+     */
+    @Nullable
+    public static String matches(
+            @Nullable String[] mimeTypes, @NonNull String filter) {
+        if (mimeTypes == null) {
+            return null;
+        }
+
+        final String[] filterParts = filter.split("/");
+        for (String mimeType : mimeTypes) {
+            final String[] mimeTypeParts = mimeType.split("/");
+            if (mimeTypeAgainstFilter(mimeTypeParts, filterParts)) {
+                return mimeType;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Matches multiple MIME types against an array of MIME type filters.
+     * @return The list of matching MIME types, or empty array if nothing matches.
+     */
+    @NonNull
+    public static String[] matchesMany(
+            @Nullable String[] mimeTypes, @NonNull String filter) {
+        if (mimeTypes == null) {
+            return new String[] {};
+        }
+
+        final ArrayList<String> list = new ArrayList<>();
+        final String[] filterParts = filter.split("/");
+        for (String mimeType : mimeTypes) {
+            final String[] mimeTypeParts = mimeType.split("/");
+            if (mimeTypeAgainstFilter(mimeTypeParts, filterParts)) {
+                list.add(mimeType);
+            }
+        }
+
+        return list.toArray(new String[list.size()]);
+    }
+}
diff --git a/android/content/MutableContextWrapper.java b/android/content/MutableContextWrapper.java
new file mode 100644
index 0000000..820479c
--- /dev/null
+++ b/android/content/MutableContextWrapper.java
@@ -0,0 +1,38 @@
+/*
+ * 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.content;
+
+/**
+ * Special version of {@link ContextWrapper} that allows the base context to
+ * be modified after it is initially set.
+ */
+public class MutableContextWrapper extends ContextWrapper {
+    public MutableContextWrapper(Context base) {
+        super(base);
+    }
+    
+    /**
+     * Change the base context for this ContextWrapper. All calls will then be
+     * delegated to the base context.  Unlike ContextWrapper, the base context
+     * can be changed even after one is already set.
+     * 
+     * @param base The new base context for this wrapper.
+     */
+    public void setBaseContext(Context base) {
+        mBase = base;
+    }
+}
diff --git a/android/content/OperationApplicationException.java b/android/content/OperationApplicationException.java
new file mode 100644
index 0000000..2fc19bb
--- /dev/null
+++ b/android/content/OperationApplicationException.java
@@ -0,0 +1,54 @@
+/*
+ * 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.content;
+
+/**
+ * Thrown when an application of a {@link ContentProviderOperation} fails due the specified
+ * constraints.
+ */
+public class OperationApplicationException extends Exception {
+    private final int mNumSuccessfulYieldPoints;
+
+    public OperationApplicationException() {
+        super();
+        mNumSuccessfulYieldPoints = 0;
+    }
+    public OperationApplicationException(String message) {
+        super(message);
+        mNumSuccessfulYieldPoints = 0;
+    }
+    public OperationApplicationException(String message, Throwable cause) {
+        super(message, cause);
+        mNumSuccessfulYieldPoints = 0;
+    }
+    public OperationApplicationException(Throwable cause) {
+        super(cause);
+        mNumSuccessfulYieldPoints = 0;
+    }
+    public OperationApplicationException(int numSuccessfulYieldPoints) {
+        super();
+        mNumSuccessfulYieldPoints = numSuccessfulYieldPoints;
+    }
+    public OperationApplicationException(String message, int numSuccessfulYieldPoints) {
+        super(message);
+        mNumSuccessfulYieldPoints = numSuccessfulYieldPoints;
+    }
+
+    public int getNumSuccessfulYieldPoints() {
+        return mNumSuccessfulYieldPoints;
+    }
+}
diff --git a/android/content/PeriodicSync.java b/android/content/PeriodicSync.java
new file mode 100644
index 0000000..a075148
--- /dev/null
+++ b/android/content/PeriodicSync.java
@@ -0,0 +1,165 @@
+/*
+ * 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.content;
+
+import android.os.Parcelable;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.accounts.Account;
+
+import java.util.Objects;
+
+/**
+ * Value type that contains information about a periodic sync.
+ */
+public class PeriodicSync implements Parcelable {
+    /** The account to be synced. Can be null. */
+    public final Account account;
+    /** The authority of the sync. Can be null. */
+    public final String authority;
+    /** Any extras that parameters that are to be passed to the sync adapter. */
+    public final Bundle extras;
+    /** How frequently the sync should be scheduled, in seconds. Kept around for API purposes. */
+    public final long period;
+    /**
+     * How much flexibility can be taken in scheduling the sync, in seconds.
+     * {@hide}
+     */
+    public final long flexTime;
+
+      /**
+       * Creates a new PeriodicSync, copying the Bundle. This constructor is no longer used.
+       */
+    public PeriodicSync(Account account, String authority, Bundle extras, long periodInSeconds) {
+        this.account = account;
+        this.authority = authority;
+        if (extras == null) {
+            this.extras = new Bundle();
+        } else {
+            this.extras = new Bundle(extras);
+        }
+        this.period = periodInSeconds;
+        // Old API uses default flex time. No-one should be using this ctor anyway.
+        this.flexTime = 0L;
+    }
+
+    /**
+     * Create a copy of a periodic sync.
+     * {@hide}
+     */
+    public PeriodicSync(PeriodicSync other) {
+        this.account = other.account;
+        this.authority = other.authority;
+        this.extras = new Bundle(other.extras);
+        this.period = other.period;
+        this.flexTime = other.flexTime;
+    }
+
+    /**
+     * A PeriodicSync for a sync with a specified provider.
+     * {@hide}
+     */
+    public PeriodicSync(Account account, String authority, Bundle extras,
+            long period, long flexTime) {
+        this.account = account;
+        this.authority = authority;
+        this.extras = new Bundle(extras);
+        this.period = period;
+        this.flexTime = flexTime;
+    }
+
+    private PeriodicSync(Parcel in) {
+        this.account = in.readParcelable(null);
+        this.authority = in.readString();
+        this.extras = in.readBundle();
+        this.period = in.readLong();
+        this.flexTime = in.readLong();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeParcelable(account, flags);
+        dest.writeString(authority);
+        dest.writeBundle(extras);
+        dest.writeLong(period);
+        dest.writeLong(flexTime);
+    }
+
+    public static final @android.annotation.NonNull Creator<PeriodicSync> CREATOR = new Creator<PeriodicSync>() {
+        @Override
+        public PeriodicSync createFromParcel(Parcel source) {
+            return new PeriodicSync(source);
+        }
+
+        @Override
+        public PeriodicSync[] newArray(int size) {
+            return new PeriodicSync[size];
+        }
+    };
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == this) {
+            return true;
+        }
+        if (!(o instanceof PeriodicSync)) {
+            return false;
+        }
+        final PeriodicSync other = (PeriodicSync) o;
+        return account.equals(other.account)
+                && authority.equals(other.authority)
+                && period == other.period
+                && syncExtrasEquals(extras, other.extras);
+    }
+
+    /**
+     * Periodic sync extra comparison function.
+     * {@hide}
+     */
+    public static boolean syncExtrasEquals(Bundle b1, Bundle b2) {
+        if (b1.size() != b2.size()) {
+            return false;
+        }
+        if (b1.isEmpty()) {
+            return true;
+        }
+        for (String key : b1.keySet()) {
+            if (!b2.containsKey(key)) {
+                return false;
+            }
+            // Null check. According to ContentResolver#validateSyncExtrasBundle null-valued keys
+            // are allowed in the bundle.
+            if (!Objects.equals(b1.get(key), b2.get(key))) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return "account: " + account +
+               ", authority: " + authority +
+               ". period: " + period + "s " +
+               ", flex: " + flexTime;
+    }
+}
diff --git a/android/content/PermissionChecker.java b/android/content/PermissionChecker.java
new file mode 100644
index 0000000..b434072
--- /dev/null
+++ b/android/content/PermissionChecker.java
@@ -0,0 +1,481 @@
+/*
+ * 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.content;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AppOpsManager;
+import android.content.pm.PackageManager;
+import android.content.pm.PermissionInfo;
+import android.os.Binder;
+import android.os.Process;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * This class provides permission check APIs that verify both the
+ * permission and the associated app op for this permission if
+ * such is defined.
+ * <p>
+ * In the new permission model permissions with protection level
+ * dangerous are runtime permissions. For apps targeting {@link android.os.Build.VERSION_CODES#M}
+ * and above the user may not grant such permissions or revoke
+ * them at any time. For apps targeting API lower than {@link android.os.Build.VERSION_CODES#M}
+ * these permissions are always granted as such apps do not expect
+ * permission revocations and would crash. Therefore, when the
+ * user disables a permission for a legacy app in the UI the
+ * platform disables the APIs guarded by this permission making
+ * them a no-op which is doing nothing or returning an empty
+ * result or default error.
+ * </p>
+ * <p>
+ * It is important that when you perform an operation on behalf of
+ * another app you use these APIs to check for permissions as the
+ * app may be a legacy app that does not participate in the new
+ * permission model for which the user had disabled the "permission"
+ * which is achieved by disallowing the corresponding app op.
+ * </p>
+ * <p>
+ * This class has two types of methods and you should be careful which
+ * type to call based on whether permission protected data is being
+ * passed to the app or you are just checking whether the app holds a
+ * permission. The reason is that a permission check requires checking
+ * the runtime permission and if it is granted checking the corresponding
+ * app op as for apps not supporting the runtime mode we never revoke
+ * permissions but disable app ops. Since there are two types of app op
+ * checks, one that does not leave a record an action was performed and
+ * another the does, one needs to call the preflight flavor of the checks
+ * named xxxForPreflight only if no private data is being delivered but
+ * a permission check is what is needed and the xxxForDataDelivery where
+ * the permission check is right before private data delivery.
+ *
+ * @hide
+ */
+public final class PermissionChecker {
+    /** The permission is granted. */
+    public static final int PERMISSION_GRANTED =  PackageManager.PERMISSION_GRANTED;
+
+    /** Returned when:
+     * <ul>
+     * <li>For non app op permissions, returned when the permission is denied.</li>
+     * <li>For app op permissions, returned when the app op is denied or app op is
+     * {@link AppOpsManager#MODE_DEFAULT} and permission is denied.</li>
+     * </ul>
+     *
+     */
+    public static final int PERMISSION_HARD_DENIED =  PackageManager.PERMISSION_DENIED;
+
+    /** Only for runtime permissions, its returned when the runtime permission
+     * is granted, but the corresponding app op is denied. */
+    public static final int PERMISSION_SOFT_DENIED =  PackageManager.PERMISSION_DENIED - 1;
+
+    /** Constant when the PID for which we check permissions is unknown. */
+    public static final int PID_UNKNOWN = -1;
+
+    /** @hide */
+    @IntDef({PERMISSION_GRANTED,
+            PERMISSION_SOFT_DENIED,
+            PERMISSION_HARD_DENIED})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface PermissionResult {}
+
+    private PermissionChecker() {
+        /* do nothing */
+    }
+
+    /**
+     * Checks whether a given package in a UID and PID has a given permission
+     * and whether the app op that corresponds to this permission is allowed.
+     *
+     * <strong>NOTE:</strong> Use this method only for permission checks at the
+     * point where you will deliver the permission protected data to clients.
+     *
+     * <p>For example, if an app registers a location listener it should have the location
+     * permission but no data is actually sent to the app at the moment of registration
+     * and you should use {@link #checkPermissionForPreflight(Context, String, int, int, String)}
+     * to determine if the app has or may have location permission (if app has only foreground
+     * location the grant state depends on the app's fg/gb state) and this check will not
+     * leave a trace that permission protected data was delivered. When you are about to
+     * deliver the location data to a registered listener you should use this method which
+     * will evaluate the permission access based on the current fg/bg state of the app and
+     * leave a record that the data was accessed.
+     *
+     * @param context Context for accessing resources.
+     * @param permission The permission to check.
+     * @param pid The process id for which to check. Use {@link #PID_UNKNOWN} if the PID
+     *    is not known.
+     * @param uid The uid for which to check.
+     * @param packageName The package name for which to check. If null the
+     *     the first package for the calling UID will be used.
+     * @param attributionTag attribution tag
+     * @return The permission check result which is either {@link #PERMISSION_GRANTED}
+     *     or {@link #PERMISSION_SOFT_DENIED} or {@link #PERMISSION_HARD_DENIED}.
+     * @param message A message describing the reason the permission was checked
+     *
+     * @see #checkPermissionForPreflight(Context, String, int, int, String)
+     */
+    @PermissionResult
+    public static int checkPermissionForDataDelivery(@NonNull Context context,
+            @NonNull String permission, int pid, int uid, @Nullable String packageName,
+            @Nullable String attributionTag, @Nullable String message) {
+        return checkPermissionCommon(context, permission, pid, uid, packageName, attributionTag,
+                message, true /*forDataDelivery*/);
+    }
+
+    /**
+     * Checks whether a given package in a UID and PID has a given permission
+     * and whether the app op that corresponds to this permission is allowed.
+     *
+     * <strong>NOTE:</strong> Use this method only for permission checks at the
+     * preflight point where you will not deliver the permission protected data
+     * to clients but schedule permission data delivery, apps register listeners,
+     * etc.
+     *
+     * <p>For example, if an app registers a location listener it should have the location
+     * permission but no data is actually sent to the app at the moment of registration
+     * and you should use this method to determine if the app has or may have location
+     * permission (if app has only foreground location the grant state depends on the app's
+     * fg/gb state) and this check will not leave a trace that permission protected data
+     * was delivered. When you are about to deliver the location data to a registered
+     * listener you should use {@link #checkPermissionForDataDelivery(Context, String,
+     * int, int, String, String)} which will evaluate the permission access based on the current
+     * fg/bg state of the app and leave a record that the data was accessed.
+     *
+     * @param context Context for accessing resources.
+     * @param permission The permission to check.
+     * @param pid The process id for which to check.
+     * @param uid The uid for which to check.
+     * @param packageName The package name for which to check. If null the
+     *     the first package for the calling UID will be used.
+     * @return The permission check result which is either {@link #PERMISSION_GRANTED}
+     *     or {@link #PERMISSION_SOFT_DENIED} or {@link #PERMISSION_HARD_DENIED}.
+     *
+     * @see #checkPermissionForDataDelivery(Context, String, int, int, String, String)
+     */
+    @PermissionResult
+    public static int checkPermissionForPreflight(@NonNull Context context,
+            @NonNull String permission, int pid, int uid, @Nullable String packageName) {
+        return checkPermissionCommon(context, permission, pid, uid, packageName,
+                null /*attributionTag*/, null /*message*/, false /*forDataDelivery*/);
+    }
+
+    /**
+     * Checks whether your app has a given permission and whether the app op
+     * that corresponds to this permission is allowed.
+     *
+     * <strong>NOTE:</strong> Use this method only for permission checks at the
+     * point where you will deliver the permission protected data to clients.
+     *
+     * <p>For example, if an app registers a location listener it should have the location
+     * permission but no data is actually sent to the app at the moment of registration
+     * and you should use {@link #checkSelfPermissionForPreflight(Context, String)}
+     * to determine if the app has or may have location permission (if app has only foreground
+     * location the grant state depends on the app's fg/gb state) and this check will not
+     * leave a trace that permission protected data was delivered. When you are about to
+     * deliver the location data to a registered listener you should use this method
+     * which will evaluate the permission access based on the current fg/bg state of the
+     * app and leave a record that the data was accessed.
+     *
+     * <p>This API assumes the the {@link Binder#getCallingUid()} is the same as
+     * {@link Process#myUid()}.
+     *
+     * @param context Context for accessing resources.
+     * @param permission The permission to check.
+     * @return The permission check result which is either {@link #PERMISSION_GRANTED}
+     *     or {@link #PERMISSION_SOFT_DENIED} or {@link #PERMISSION_HARD_DENIED}.
+     * @param message A message describing the reason the permission was checked
+     *
+     * @see #checkSelfPermissionForPreflight(Context, String)
+     */
+    @PermissionResult
+    public static int checkSelfPermissionForDataDelivery(@NonNull Context context,
+            @NonNull String permission, @Nullable String message) {
+        return checkPermissionForDataDelivery(context, permission, Process.myPid(),
+                Process.myUid(), context.getPackageName(), context.getAttributionTag(), message);
+    }
+
+    /**
+     * Checks whether your app has a given permission and whether the app op
+     * that corresponds to this permission is allowed.
+     *
+     * <strong>NOTE:</strong> Use this method only for permission checks at the
+     * preflight point where you will not deliver the permission protected data
+     * to clients but schedule permission data delivery, apps register listeners,
+     * etc.
+     *
+     * <p>For example, if an app registers a location listener it should have the location
+     * permission but no data is actually sent to the app at the moment of registration
+     * and you should use this method to determine if the app has or may have location
+     * permission (if app has only foreground location the grant state depends on the
+     * app's fg/gb state) and this check will not leave a trace that permission protected
+     * data was delivered. When you are about to deliver the location data to a registered
+     * listener you should use this method which will evaluate the permission access based
+     * on the current fg/bg state of the app and leave a record that the data was accessed.
+     *
+     * <p>This API assumes the the {@link Binder#getCallingUid()} is the same as
+     * {@link Process#myUid()}.
+     *
+     * @param context Context for accessing resources.
+     * @param permission The permission to check.
+     * @return The permission check result which is either {@link #PERMISSION_GRANTED}
+     *     or {@link #PERMISSION_SOFT_DENIED} or {@link #PERMISSION_HARD_DENIED}.
+     *
+     * @see #checkSelfPermissionForDataDelivery(Context, String, String)
+     */
+    @PermissionResult
+    public static int checkSelfPermissionForPreflight(@NonNull Context context,
+            @NonNull String permission) {
+        return checkPermissionForPreflight(context, permission, Process.myPid(),
+                Process.myUid(), context.getPackageName());
+    }
+
+    /**
+     * Checks whether the IPC you are handling has a given permission and whether
+     * the app op that corresponds to this permission is allowed.
+     *
+     * <strong>NOTE:</strong> Use this method only for permission checks at the
+     * point where you will deliver the permission protected data to clients.
+     *
+     * <p>For example, if an app registers a location listener it should have the location
+     * permission but no data is actually sent to the app at the moment of registration
+     * and you should use {@link #checkCallingPermissionForPreflight(Context, String, String)}
+     * to determine if the app has or may have location permission (if app has only foreground
+     * location the grant state depends on the app's fg/gb state) and this check will not
+     * leave a trace that permission protected data was delivered. When you are about to
+     * deliver the location data to a registered listener you should use this method which
+     * will evaluate the permission access based on the current fg/bg state of the app and
+     * leave a record that the data was accessed.
+     *
+     * @param context Context for accessing resources.
+     * @param permission The permission to check.
+     * @param packageName The package name making the IPC. If null the
+     *     the first package for the calling UID will be used.
+     * @param attributionTag attribution tag
+     * @return The permission check result which is either {@link #PERMISSION_GRANTED}
+     *     or {@link #PERMISSION_SOFT_DENIED} or {@link #PERMISSION_HARD_DENIED}.
+     * @param message A message describing the reason the permission was checked
+     *
+     * @see #checkCallingPermissionForPreflight(Context, String, String)
+     */
+    @PermissionResult
+    public static int checkCallingPermissionForDataDelivery(@NonNull Context context,
+            @NonNull String permission, @Nullable String packageName,
+            @Nullable String attributionTag, @Nullable String message) {
+        if (Binder.getCallingPid() == Process.myPid()) {
+            return PERMISSION_HARD_DENIED;
+        }
+        return checkPermissionForDataDelivery(context, permission, Binder.getCallingPid(),
+                Binder.getCallingUid(), packageName, attributionTag, message);
+    }
+
+    /**
+     * Checks whether the IPC you are handling has a given permission and whether
+     * the app op that corresponds to this permission is allowed.
+     *
+     * <strong>NOTE:</strong> Use this method only for permission checks at the
+     * preflight point where you will not deliver the permission protected data
+     * to clients but schedule permission data delivery, apps register listeners,
+     * etc.
+     *
+     * <p>For example, if an app registers a location listener it should have the location
+     * permission but no data is actually sent to the app at the moment of registration
+     * and you should use this method to determine if the app has or may have location
+     * permission (if app has only foreground location the grant state depends on the app's
+     * fg/gb state) and this check will not leave a trace that permission protected data
+     * was delivered. When you are about to deliver the location data to a registered
+     * listener you should use {@link #checkCallingOrSelfPermissionForDataDelivery(Context,
+     * String, String)} which will evaluate the permission access based on the current fg/bg state
+     * of the app and leave a record that the data was accessed.
+     *
+     * @param context Context for accessing resources.
+     * @param permission The permission to check.
+     * @param packageName The package name making the IPC. If null the
+     *     the first package for the calling UID will be used.
+     * @return The permission check result which is either {@link #PERMISSION_GRANTED}
+     *     or {@link #PERMISSION_SOFT_DENIED} or {@link #PERMISSION_HARD_DENIED}.
+     *
+     * @see #checkCallingPermissionForDataDelivery(Context, String, String, String)
+     */
+    @PermissionResult
+    public static int checkCallingPermissionForPreflight(@NonNull Context context,
+            @NonNull String permission, @Nullable String packageName) {
+        if (Binder.getCallingPid() == Process.myPid()) {
+            return PERMISSION_HARD_DENIED;
+        }
+        return checkPermissionForPreflight(context, permission, Binder.getCallingPid(),
+                Binder.getCallingUid(), packageName);
+    }
+
+    /**
+     * Checks whether the IPC you are handling or your app has a given permission
+     * and whether the app op that corresponds to this permission is allowed.
+     *
+     * <strong>NOTE:</strong> Use this method only for permission checks at the
+     * point where you will deliver the permission protected data to clients.
+     *
+     * <p>For example, if an app registers a location listener it should have the location
+     * permission but no data is actually sent to the app at the moment of registration
+     * and you should use {@link #checkCallingOrSelfPermissionForPreflight(Context, String)}
+     * to determine if the app has or may have location permission (if app has only foreground
+     * location the grant state depends on the app's fg/gb state) and this check will not
+     * leave a trace that permission protected data was delivered. When you are about to
+     * deliver the location data to a registered listener you should use this method which
+     * will evaluate the permission access based on the current fg/bg state of the app and
+     * leave a record that the data was accessed.
+     *
+     * @param context Context for accessing resources.
+     * @param permission The permission to check.
+     * @return The permission check result which is either {@link #PERMISSION_GRANTED}
+     *     or {@link #PERMISSION_SOFT_DENIED} or {@link #PERMISSION_HARD_DENIED}.
+     * @param attributionTag attribution tag of caller (if not self)
+     * @param message A message describing the reason the permission was checked
+     *
+     * @see #checkCallingOrSelfPermissionForPreflight(Context, String)
+     */
+    @PermissionResult
+    public static int checkCallingOrSelfPermissionForDataDelivery(@NonNull Context context,
+            @NonNull String permission, @Nullable String attributionTag, @Nullable String message) {
+        String packageName = (Binder.getCallingPid() == Process.myPid())
+                ? context.getPackageName() : null;
+        attributionTag = (Binder.getCallingPid() == Process.myPid())
+                ? context.getAttributionTag() : attributionTag;
+        return checkPermissionForDataDelivery(context, permission, Binder.getCallingPid(),
+                Binder.getCallingUid(), packageName, attributionTag, message);
+    }
+
+    /**
+     * Checks whether the IPC you are handling or your app has a given permission
+     * and whether the app op that corresponds to this permission is allowed.
+     *
+     * <strong>NOTE:</strong> Use this method only for permission checks at the
+     * preflight point where you will not deliver the permission protected data
+     * to clients but schedule permission data delivery, apps register listeners,
+     * etc.
+     *
+     * <p>For example, if an app registers a location listener it should have the location
+     * permission but no data is actually sent to the app at the moment of registration
+     * and you should use this method to determine if the app has or may have location
+     * permission (if app has only foreground location the grant state depends on the
+     * app's fg/gb state) and this check will not leave a trace that permission protected
+     * data was delivered. When you are about to deliver the location data to a registered
+     * listener you should use {@link #checkCallingOrSelfPermissionForDataDelivery(Context,
+     * String, String, String)} which will evaluate the permission access based on the current
+     * fg/bg state of the app and leave a record that the data was accessed.
+     *
+     * @param context Context for accessing resources.
+     * @param permission The permission to check.
+     * @return The permission check result which is either {@link #PERMISSION_GRANTED}
+     *     or {@link #PERMISSION_SOFT_DENIED} or {@link #PERMISSION_HARD_DENIED}.
+     *
+     * @see #checkCallingOrSelfPermissionForDataDelivery(Context, String, String, String)
+     */
+    @PermissionResult
+    public static int checkCallingOrSelfPermissionForPreflight(@NonNull Context context,
+            @NonNull String permission) {
+        String packageName = (Binder.getCallingPid() == Process.myPid())
+                ? context.getPackageName() : null;
+        return checkPermissionForPreflight(context, permission, Binder.getCallingPid(),
+                Binder.getCallingUid(), packageName);
+    }
+
+    static int checkPermissionCommon(@NonNull Context context, @NonNull String permission,
+            int pid, int uid, @Nullable String packageName, @Nullable String attributionTag,
+            @Nullable String message, boolean forDataDelivery) {
+        final PermissionInfo permissionInfo;
+        try {
+            // TODO(b/147869157): Cache platform defined app op and runtime permissions to avoid
+            // calling into the package manager every time.
+            permissionInfo = context.getPackageManager().getPermissionInfo(permission, 0);
+        } catch (PackageManager.NameNotFoundException ignored) {
+            return PERMISSION_HARD_DENIED;
+        }
+
+        if (packageName == null) {
+            String[] packageNames = context.getPackageManager().getPackagesForUid(uid);
+            if (packageNames != null && packageNames.length > 0) {
+                packageName = packageNames[0];
+            }
+        }
+
+        if (permissionInfo.isAppOp()) {
+            return checkAppOpPermission(context, permission, pid, uid, packageName, attributionTag,
+                    message, forDataDelivery);
+        }
+        if (permissionInfo.isRuntime()) {
+            return checkRuntimePermission(context, permission, pid, uid, packageName,
+                    attributionTag, message, forDataDelivery);
+        }
+        return context.checkPermission(permission, pid, uid);
+    }
+
+    private static int checkAppOpPermission(@NonNull Context context, @NonNull String permission,
+            int pid, int uid, @Nullable String packageName, @Nullable String attributionTag,
+            @Nullable String message, boolean forDataDelivery) {
+        final String op = AppOpsManager.permissionToOp(permission);
+        if (op == null || packageName == null) {
+            return PERMISSION_HARD_DENIED;
+        }
+
+        final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
+        final int opMode = (forDataDelivery)
+                ? appOpsManager.noteProxyOpNoThrow(op, packageName, uid, attributionTag, message)
+                : appOpsManager.unsafeCheckOpRawNoThrow(op, uid, packageName);
+
+        switch (opMode) {
+            case AppOpsManager.MODE_ALLOWED:
+            case AppOpsManager.MODE_FOREGROUND: {
+                return PERMISSION_GRANTED;
+            }
+            case AppOpsManager.MODE_DEFAULT: {
+                return context.checkPermission(permission, pid, uid)
+                            == PackageManager.PERMISSION_GRANTED
+                        ? PERMISSION_GRANTED : PERMISSION_HARD_DENIED;
+            }
+            default: {
+                return PERMISSION_HARD_DENIED;
+            }
+        }
+    }
+
+    private static int checkRuntimePermission(@NonNull Context context, @NonNull String permission,
+            int pid, int uid, @Nullable String packageName, @Nullable String attributionTag,
+            @Nullable String message, boolean forDataDelivery) {
+        if (context.checkPermission(permission, pid, uid) == PackageManager.PERMISSION_DENIED) {
+            return PERMISSION_HARD_DENIED;
+        }
+
+        final String op = AppOpsManager.permissionToOp(permission);
+        if (op == null || packageName == null) {
+            return PERMISSION_GRANTED;
+        }
+
+        final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
+        final int opMode = (forDataDelivery)
+                ? appOpsManager.noteProxyOpNoThrow(op, packageName, uid, attributionTag, message)
+                : appOpsManager.unsafeCheckOpRawNoThrow(op, uid, packageName);
+
+        switch (opMode) {
+            case AppOpsManager.MODE_ALLOWED:
+            case AppOpsManager.MODE_FOREGROUND:
+                return PERMISSION_GRANTED;
+            default:
+                return PERMISSION_SOFT_DENIED;
+        }
+    }
+}
diff --git a/android/content/QuickViewConstants.java b/android/content/QuickViewConstants.java
new file mode 100644
index 0000000..ffb131c
--- /dev/null
+++ b/android/content/QuickViewConstants.java
@@ -0,0 +1,77 @@
+/*
+ * 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.content;
+
+/**
+ * Constants for {@link Intent#ACTION_QUICK_VIEW}.
+ */
+public class QuickViewConstants {
+
+    private QuickViewConstants() {}
+
+    /**
+     * Feature to view a document using system standard viewing mechanism, like
+     * {@link Intent#ACTION_VIEW}.
+     *
+     * @see Intent#EXTRA_QUICK_VIEW_FEATURES
+     * @see Intent#ACTION_QUICK_VIEW
+     */
+    public static final String FEATURE_VIEW = "android:view";
+
+    /**
+     * Feature to edit a document using system standard editing mechanism, like
+     * {@link Intent#ACTION_EDIT}.
+     *
+     * @see Intent#EXTRA_QUICK_VIEW_FEATURES
+     * @see Intent#ACTION_QUICK_VIEW
+     */
+    public static final String FEATURE_EDIT = "android:edit";
+
+    /**
+     * Feature to delete an individual document. Quick viewer implementations must use
+     * Storage Access Framework to both verify delete permission and to delete content.
+     *
+     * @see android.provider.DocumentsContract.Document#FLAG_SUPPORTS_DELETE
+     * @see android.provider.DocumentsContract#deleteDocument(ContentResolver, android.net.Uri)
+     */
+    public static final String FEATURE_DELETE = "android:delete";
+
+    /**
+     * Feature to view a document using system standard sending mechanism, like
+     * {@link Intent#ACTION_SEND}.
+     *
+     * @see Intent#EXTRA_QUICK_VIEW_FEATURES
+     * @see Intent#ACTION_QUICK_VIEW
+     */
+    public static final String FEATURE_SEND = "android:send";
+
+    /**
+     * Feature to download a document to the local file system.
+     *
+     * @see Intent#EXTRA_QUICK_VIEW_FEATURES
+     * @see Intent#ACTION_QUICK_VIEW
+     */
+    public static final String FEATURE_DOWNLOAD = "android:download";
+
+    /**
+     * Feature to print a document.
+     *
+     * @see Intent#EXTRA_QUICK_VIEW_FEATURES
+     * @see Intent#ACTION_QUICK_VIEW
+     */
+    public static final String FEATURE_PRINT = "android:print";
+}
diff --git a/android/content/ReceiverCallNotAllowedException.java b/android/content/ReceiverCallNotAllowedException.java
new file mode 100644
index 0000000..96b269c
--- /dev/null
+++ b/android/content/ReceiverCallNotAllowedException.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.content;
+
+import android.util.AndroidRuntimeException;
+
+/**
+ * This exception is thrown from {@link Context#registerReceiver} and
+ * {@link Context#bindService} when these methods are being used from
+ * an {@link BroadcastReceiver} component.  In this case, the component will no
+ * longer be active upon returning from receiving the Intent, so it is
+ * not valid to use asynchronous APIs.
+ */
+public class ReceiverCallNotAllowedException extends AndroidRuntimeException {
+    public ReceiverCallNotAllowedException(String msg) {
+        super(msg);
+    }
+}
diff --git a/android/content/RestrictionEntry.java b/android/content/RestrictionEntry.java
new file mode 100644
index 0000000..010992c
--- /dev/null
+++ b/android/content/RestrictionEntry.java
@@ -0,0 +1,558 @@
+/*
+ * 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.content;
+
+import android.annotation.ArrayRes;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * Applications can expose restrictions for a restricted user on a
+ * multiuser device. The administrator can configure these restrictions that will then be
+ * applied to the restricted user. Each RestrictionsEntry is one configurable restriction.
+ * <p/>
+ * Any application that chooses to expose such restrictions does so by implementing a
+ * receiver that handles the {@link Intent#ACTION_GET_RESTRICTION_ENTRIES} action.
+ * The receiver then returns a result bundle that contains an entry called "restrictions", whose
+ * value is an ArrayList<RestrictionsEntry>.
+ */
+public class RestrictionEntry implements Parcelable {
+
+    /**
+     * Hidden restriction type. Use this type for information that needs to be transferred
+     * across but shouldn't be presented to the user in the UI. Stores a single String value.
+     */
+    public static final int TYPE_NULL         = 0;
+
+    /**
+     * Restriction of type "bool". Use this for storing a boolean value, typically presented as
+     * a checkbox in the UI.
+     */
+    public static final int TYPE_BOOLEAN      = 1;
+
+    /**
+     * Restriction of type "choice". Use this for storing a string value, typically presented as
+     * a single-select list. Call {@link #setChoiceEntries(String[])} and
+     * {@link #setChoiceValues(String[])} to set the localized list entries to present to the user
+     * and the corresponding values, respectively.
+     */
+    public static final int TYPE_CHOICE       = 2;
+
+    /**
+     * Internal restriction type. Use this for storing a string value, typically presented as
+     * a single-select list. Call {@link #setChoiceEntries(String[])} and
+     * {@link #setChoiceValues(String[])} to set the localized list entries to present to the user
+     * and the corresponding values, respectively.
+     * The presentation could imply that values in lower array indices are included when a
+     * particular value is chosen.
+     * @hide
+     */
+    public static final int TYPE_CHOICE_LEVEL = 3;
+
+    /**
+     * Restriction of type "multi-select". Use this for presenting a multi-select list where more
+     * than one entry can be selected, such as for choosing specific titles to white-list.
+     * Call {@link #setChoiceEntries(String[])} and
+     * {@link #setChoiceValues(String[])} to set the localized list entries to present to the user
+     * and the corresponding values, respectively.
+     * Use {@link #getAllSelectedStrings()} and {@link #setAllSelectedStrings(String[])} to
+     * manipulate the selections.
+     */
+    public static final int TYPE_MULTI_SELECT = 4;
+
+    /**
+     * Restriction of type "integer". Use this for storing an integer value. The range of values
+     * is from {@link Integer#MIN_VALUE} to {@link Integer#MAX_VALUE}.
+     */
+    public static final int TYPE_INTEGER = 5;
+
+    /**
+     * Restriction of type "string". Use this for storing a string value.
+     * @see #setSelectedString
+     * @see #getSelectedString
+     */
+    public static final int TYPE_STRING = 6;
+
+    /**
+     * Restriction of type "bundle". Use this for storing {@link android.os.Bundle bundles} of
+     * restrictions
+     */
+    public static final int TYPE_BUNDLE = 7;
+
+    /**
+     * Restriction of type "bundle_array". Use this for storing arrays of
+     * {@link android.os.Bundle bundles} of restrictions
+     */
+    public static final int TYPE_BUNDLE_ARRAY = 8;
+
+    /** The type of restriction. */
+    private int mType;
+
+    /** The unique key that identifies the restriction. */
+    private String mKey;
+
+    /** The user-visible title of the restriction. */
+    private String mTitle;
+
+    /** The user-visible secondary description of the restriction. */
+    private String mDescription;
+
+    /** The user-visible set of choices used for single-select and multi-select lists. */
+    private String[] mChoiceEntries;
+
+    /** The values corresponding to the user-visible choices. The value(s) of this entry will
+     * one or more of these, returned by {@link #getAllSelectedStrings()} and
+     * {@link #getSelectedString()}.
+     */
+    private String[] mChoiceValues;
+
+    /* The chosen value, whose content depends on the type of the restriction. */
+    private String mCurrentValue;
+
+    /* List of selected choices in the multi-select case. */
+    private String[] mCurrentValues;
+
+    /**
+     * List of nested restrictions. Used by {@link #TYPE_BUNDLE bundle} and
+     * {@link #TYPE_BUNDLE_ARRAY bundle_array} restrictions.
+     */
+    private RestrictionEntry[] mRestrictions;
+
+    /**
+     * Constructor for specifying the type and key, with no initial value;
+     *
+     * @param type the restriction type.
+     * @param key the unique key for this restriction
+     */
+    public RestrictionEntry(int type, String key) {
+        mType = type;
+        mKey = key;
+    }
+
+    /**
+     * Constructor for {@link #TYPE_CHOICE} type.
+     * @param key the unique key for this restriction
+     * @param selectedString the current value
+     */
+    public RestrictionEntry(String key, String selectedString) {
+        this.mKey = key;
+        this.mType = TYPE_CHOICE;
+        this.mCurrentValue = selectedString;
+    }
+
+    /**
+     * Constructor for {@link #TYPE_BOOLEAN} type.
+     * @param key the unique key for this restriction
+     * @param selectedState whether this restriction is selected or not
+     */
+    public RestrictionEntry(String key, boolean selectedState) {
+        this.mKey = key;
+        this.mType = TYPE_BOOLEAN;
+        setSelectedState(selectedState);
+    }
+
+    /**
+     * Constructor for {@link #TYPE_MULTI_SELECT} type.
+     * @param key the unique key for this restriction
+     * @param selectedStrings the list of values that are currently selected
+     */
+    public RestrictionEntry(String key, String[] selectedStrings) {
+        this.mKey = key;
+        this.mType = TYPE_MULTI_SELECT;
+        this.mCurrentValues = selectedStrings;
+    }
+
+    /**
+     * Constructor for {@link #TYPE_INTEGER} type.
+     * @param key the unique key for this restriction
+     * @param selectedInt the integer value of the restriction
+     */
+    public RestrictionEntry(String key, int selectedInt) {
+        mKey = key;
+        mType = TYPE_INTEGER;
+        setIntValue(selectedInt);
+    }
+
+    /**
+     * Constructor for {@link #TYPE_BUNDLE}/{@link #TYPE_BUNDLE_ARRAY} type.
+     * @param key the unique key for this restriction
+     * @param restrictionEntries array of nested restriction entries. If the entry, being created
+     * represents a {@link #TYPE_BUNDLE_ARRAY bundle-array}, {@code restrictionEntries} array may
+     * only contain elements of type {@link #TYPE_BUNDLE bundle}.
+     * @param isBundleArray true if this restriction represents
+     * {@link #TYPE_BUNDLE_ARRAY bundle-array} type, otherwise the type will be set to
+     * {@link #TYPE_BUNDLE bundle}.
+     */
+    private RestrictionEntry(String key, RestrictionEntry[] restrictionEntries,
+            boolean isBundleArray) {
+        mKey = key;
+        if (isBundleArray) {
+            mType = TYPE_BUNDLE_ARRAY;
+            if (restrictionEntries != null) {
+                for (RestrictionEntry restriction : restrictionEntries) {
+                    if (restriction.getType() != TYPE_BUNDLE) {
+                        throw new IllegalArgumentException("bundle_array restriction can only have "
+                                + "nested restriction entries of type bundle");
+                    }
+                }
+            }
+        } else {
+            mType = TYPE_BUNDLE;
+        }
+        setRestrictions(restrictionEntries);
+    }
+
+    /**
+     * Creates an entry of type {@link #TYPE_BUNDLE}.
+     * @param key the unique key for this restriction
+     * @param restrictionEntries array of nested restriction entries.
+     * @return the newly created restriction
+     */
+    public static RestrictionEntry createBundleEntry(String key,
+            RestrictionEntry[] restrictionEntries) {
+        return new RestrictionEntry(key, restrictionEntries, false);
+    }
+
+    /**
+     * Creates an entry of type {@link #TYPE_BUNDLE_ARRAY}.
+     * @param key the unique key for this restriction
+     * @param restrictionEntries array of nested restriction entries. The array may only contain
+     * elements of type {@link #TYPE_BUNDLE bundle}.
+     * @return the newly created restriction
+     */
+    public static RestrictionEntry createBundleArrayEntry(String key,
+            RestrictionEntry[] restrictionEntries) {
+        return new RestrictionEntry(key, restrictionEntries, true);
+    }
+
+    /**
+     * Sets the type for this restriction.
+     * @param type the type for this restriction.
+     */
+    public void setType(int type) {
+        this.mType = type;
+    }
+
+    /**
+     * Returns the type for this restriction.
+     * @return the type for this restriction
+     */
+    public int getType() {
+        return mType;
+    }
+
+    /**
+     * Returns the currently selected string value.
+     * @return the currently selected value, which can be null for types that aren't for holding
+     * single string values.
+     */
+    public String getSelectedString() {
+        return mCurrentValue;
+    }
+
+    /**
+     * Returns the list of currently selected values.
+     * @return the list of current selections, if type is {@link #TYPE_MULTI_SELECT},
+     *  null otherwise.
+     */
+    public String[] getAllSelectedStrings() {
+        return mCurrentValues;
+    }
+
+    /**
+     * Returns the current selected state for an entry of type {@link #TYPE_BOOLEAN}.
+     * @return the current selected state of the entry.
+     */
+    public boolean getSelectedState() {
+        return Boolean.parseBoolean(mCurrentValue);
+    }
+
+    /**
+     * Returns the value of the entry as an integer when the type is {@link #TYPE_INTEGER}.
+     * @return the integer value of the entry.
+     */
+    public int getIntValue() {
+        return Integer.parseInt(mCurrentValue);
+    }
+
+    /**
+     * Sets the integer value of the entry when the type is {@link #TYPE_INTEGER}.
+     * @param value the integer value to set.
+     */
+    public void setIntValue(int value) {
+        mCurrentValue = Integer.toString(value);
+    }
+
+    /**
+     * Sets the string value to use as the selected value for this restriction. This value will
+     * be persisted by the system for later use by the application.
+     * @param selectedString the string value to select.
+     */
+    public void setSelectedString(String selectedString) {
+        mCurrentValue = selectedString;
+    }
+
+    /**
+     * Sets the current selected state for an entry of type {@link #TYPE_BOOLEAN}. This value will
+     * be persisted by the system for later use by the application.
+     * @param state the current selected state
+     */
+    public void setSelectedState(boolean state) {
+        mCurrentValue = Boolean.toString(state);
+    }
+
+    /**
+     * Sets the current list of selected values for an entry of type {@link #TYPE_MULTI_SELECT}.
+     * These values will be persisted by the system for later use by the application.
+     * @param allSelectedStrings the current list of selected values.
+     */
+    public void setAllSelectedStrings(String[] allSelectedStrings) {
+        mCurrentValues = allSelectedStrings;
+    }
+
+    /**
+     * Sets a list of string values that can be selected by the user. If no user-visible entries
+     * are set by a call to {@link #setChoiceEntries(String[])}, these values will be the ones
+     * shown to the user. Values will be chosen from this list as the user's selection and the
+     * selected values can be retrieved by a call to {@link #getAllSelectedStrings()}, or
+     * {@link #getSelectedString()}, depending on whether it is a multi-select type or choice type.
+     * This method is not relevant for types other than
+     * {@link #TYPE_CHOICE}, and {@link #TYPE_MULTI_SELECT}.
+     * @param choiceValues an array of Strings which will be the selected values for the user's
+     * selections.
+     * @see #getChoiceValues()
+     * @see #getAllSelectedStrings()
+     */
+    public void setChoiceValues(String[] choiceValues) {
+        mChoiceValues = choiceValues;
+    }
+
+    /**
+     * Sets a list of string values that can be selected by the user, similar to
+     * {@link #setChoiceValues(String[])}.
+     * @param context the application context for retrieving the resources.
+     * @param stringArrayResId the resource id for a string array containing the possible values.
+     * @see #setChoiceValues(String[])
+     */
+    public void setChoiceValues(Context context, @ArrayRes int stringArrayResId) {
+        mChoiceValues = context.getResources().getStringArray(stringArrayResId);
+    }
+
+    /**
+     * Returns array of possible restriction entries that this entry may contain.
+     */
+    public RestrictionEntry[] getRestrictions() {
+        return mRestrictions;
+    }
+
+   /**
+    * Sets an array of possible restriction entries, that this entry may contain.
+    * <p>This method is only relevant for types {@link #TYPE_BUNDLE} and
+    * {@link #TYPE_BUNDLE_ARRAY}
+    */
+    public void setRestrictions(RestrictionEntry[] restrictions) {
+        mRestrictions = restrictions;
+    }
+
+    /**
+     * Returns the list of possible string values set earlier.
+     * @return the list of possible values.
+     */
+    public String[] getChoiceValues() {
+        return mChoiceValues;
+    }
+
+    /**
+     * Sets a list of strings that will be presented as choices to the user. When the
+     * user selects one or more of these choices, the corresponding value from the possible values
+     * are stored as the selected strings. The size of this array must match the size of the array
+     * set in {@link #setChoiceValues(String[])}. This method is not relevant for types other
+     * than {@link #TYPE_CHOICE}, and {@link #TYPE_MULTI_SELECT}.
+     * @param choiceEntries the list of user-visible choices.
+     * @see #setChoiceValues(String[])
+     */
+    public void setChoiceEntries(String[] choiceEntries) {
+        mChoiceEntries = choiceEntries;
+    }
+
+    /** Sets a list of strings that will be presented as choices to the user. This is similar to
+     * {@link #setChoiceEntries(String[])}.
+     * @param context the application context, used for retrieving the resources.
+     * @param stringArrayResId the resource id of a string array containing the possible entries.
+     */
+    public void setChoiceEntries(Context context, @ArrayRes int stringArrayResId) {
+        mChoiceEntries = context.getResources().getStringArray(stringArrayResId);
+    }
+
+    /**
+     * Returns the list of strings, set earlier, that will be presented as choices to the user.
+     * @return the list of choices presented to the user.
+     */
+    public String[] getChoiceEntries() {
+        return mChoiceEntries;
+    }
+
+    /**
+     * Returns the provided user-visible description of the entry, if any.
+     * @return the user-visible description, null if none was set earlier.
+     */
+    public String getDescription() {
+        return mDescription;
+    }
+
+    /**
+     * Sets the user-visible description of the entry, as a possible sub-text for the title.
+     * You can use this to describe the entry in more detail or to display the current state of
+     * the restriction.
+     * @param description the user-visible description string.
+     */
+    public void setDescription(String description) {
+        this.mDescription = description;
+    }
+
+    /**
+     * This is the unique key for the restriction entry.
+     * @return the key for the restriction.
+     */
+    public String getKey() {
+        return mKey;
+    }
+
+    /**
+     * Returns the user-visible title for the entry, if any.
+     * @return the user-visible title for the entry, null if none was set earlier.
+     */
+    public String getTitle() {
+        return mTitle;
+    }
+
+    /**
+     * Sets the user-visible title for the entry.
+     * @param title the user-visible title for the entry.
+     */
+    public void setTitle(String title) {
+        this.mTitle = title;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == this) return true;
+        if (!(o instanceof RestrictionEntry)) return false;
+        final RestrictionEntry other = (RestrictionEntry) o;
+        if (mType != other.mType || !mKey.equals(other.mKey)) {
+            return false;
+        }
+        if (mCurrentValues == null && other.mCurrentValues == null
+                && mRestrictions == null && other.mRestrictions == null
+                && Objects.equals(mCurrentValue, other.mCurrentValue)) {
+            return true;
+        }
+        if (mCurrentValue == null && other.mCurrentValue == null
+                && mRestrictions == null && other.mRestrictions == null
+                && Arrays.equals(mCurrentValues, other.mCurrentValues)) {
+            return true;
+        }
+        if (mCurrentValue == null && other.mCurrentValue == null
+                && mCurrentValue == null && other.mCurrentValue == null
+                && Arrays.equals(mRestrictions, other.mRestrictions)) {
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = 17;
+        result = 31 * result + mKey.hashCode();
+        if (mCurrentValue != null) {
+            result = 31 * result + mCurrentValue.hashCode();
+        } else if (mCurrentValues != null) {
+            for (String value : mCurrentValues) {
+                if (value != null) {
+                    result = 31 * result + value.hashCode();
+                }
+            }
+        } else if (mRestrictions != null) {
+            result = 31 * result + Arrays.hashCode(mRestrictions);
+        }
+        return result;
+    }
+
+    public RestrictionEntry(Parcel in) {
+        mType = in.readInt();
+        mKey = in.readString();
+        mTitle = in.readString();
+        mDescription = in.readString();
+        mChoiceEntries = in.readStringArray();
+        mChoiceValues = in.readStringArray();
+        mCurrentValue = in.readString();
+        mCurrentValues = in.readStringArray();
+        Parcelable[] parcelables = in.readParcelableArray(null);
+        if (parcelables != null) {
+            mRestrictions = new RestrictionEntry[parcelables.length];
+            for (int i = 0; i < parcelables.length; i++) {
+                mRestrictions[i] = (RestrictionEntry) parcelables[i];
+            }
+        }
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mType);
+        dest.writeString(mKey);
+        dest.writeString(mTitle);
+        dest.writeString(mDescription);
+        dest.writeStringArray(mChoiceEntries);
+        dest.writeStringArray(mChoiceValues);
+        dest.writeString(mCurrentValue);
+        dest.writeStringArray(mCurrentValues);
+        dest.writeParcelableArray(mRestrictions, 0);
+    }
+
+    public static final @android.annotation.NonNull Creator<RestrictionEntry> CREATOR = new Creator<RestrictionEntry>() {
+        public RestrictionEntry createFromParcel(Parcel source) {
+            return new RestrictionEntry(source);
+        }
+
+        public RestrictionEntry[] newArray(int size) {
+            return new RestrictionEntry[size];
+        }
+    };
+
+    @Override
+    public String toString() {
+        return "RestrictionEntry{" +
+                "mType=" + mType +
+                ", mKey='" + mKey + '\'' +
+                ", mTitle='" + mTitle + '\'' +
+                ", mDescription='" + mDescription + '\'' +
+                ", mChoiceEntries=" + Arrays.toString(mChoiceEntries) +
+                ", mChoiceValues=" + Arrays.toString(mChoiceValues) +
+                ", mCurrentValue='" + mCurrentValue + '\'' +
+                ", mCurrentValues=" + Arrays.toString(mCurrentValues) +
+                ", mRestrictions=" + Arrays.toString(mRestrictions) +
+                '}';
+    }
+}
diff --git a/android/content/RestrictionsManager.java b/android/content/RestrictionsManager.java
new file mode 100644
index 0000000..885eb70
--- /dev/null
+++ b/android/content/RestrictionsManager.java
@@ -0,0 +1,751 @@
+/*
+ * 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.content;
+
+import android.annotation.SystemService;
+import android.app.Activity;
+import android.app.admin.DevicePolicyManager;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.PersistableBundle;
+import android.os.RemoteException;
+import android.service.restrictions.RestrictionsReceiver;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.Xml;
+
+import com.android.internal.R;
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Provides a mechanism for apps to query restrictions imposed by an entity that
+ * manages the user. Apps can also send permission requests to a local or remote
+ * device administrator to override default app-specific restrictions or any other
+ * operation that needs explicit authorization from the administrator.
+ * <p>
+ * Apps can expose a set of restrictions via an XML file specified in the manifest.
+ * <p>
+ * If the user has an active Restrictions Provider, dynamic requests can be made in
+ * addition to the statically imposed restrictions. Dynamic requests are app-specific
+ * and can be expressed via a predefined set of request types.
+ * <p>
+ * The RestrictionsManager forwards the dynamic requests to the active
+ * Restrictions Provider. The Restrictions Provider can respond back to requests by calling
+ * {@link #notifyPermissionResponse(String, PersistableBundle)}, when
+ * a response is received from the administrator of the device or user.
+ * The response is relayed back to the application via a protected broadcast,
+ * {@link #ACTION_PERMISSION_RESPONSE_RECEIVED}.
+ * <p>
+ * Static restrictions are specified by an XML file referenced by a meta-data attribute
+ * in the manifest. This enables applications as well as any web administration consoles
+ * to be able to read the list of available restrictions from the apk.
+ * <p>
+ * The syntax of the XML format is as follows:
+ * <pre>
+ * &lt;?xml version="1.0" encoding="utf-8"?&gt;
+ * &lt;restrictions xmlns:android="http://schemas.android.com/apk/res/android" &gt;
+ *     &lt;restriction
+ *         android:key="string"
+ *         android:title="string resource"
+ *         android:restrictionType=["bool" | "string" | "integer"
+ *                                         | "choice" | "multi-select" | "hidden"
+ *                                         | "bundle" | "bundle_array"]
+ *         android:description="string resource"
+ *         android:entries="string-array resource"
+ *         android:entryValues="string-array resource"
+ *         android:defaultValue="reference" &gt;
+ *             &lt;restriction ... /&gt;
+ *             ...
+ *     &lt;/restriction&gt;
+ *     &lt;restriction ... /&gt;
+ *     ...
+ * &lt;/restrictions&gt;
+ * </pre>
+ * <p>
+ * The attributes for each restriction depend on the restriction type.
+ * <p>
+ * <ul>
+ * <li><code>key</code>, <code>title</code> and <code>restrictionType</code> are mandatory.</li>
+ * <li><code>entries</code> and <code>entryValues</code> are required if <code>restrictionType
+ * </code> is <code>choice</code> or <code>multi-select</code>.</li>
+ * <li><code>defaultValue</code> is optional and its type depends on the
+ * <code>restrictionType</code></li>
+ * <li><code>hidden</code> type must have a <code>defaultValue</code> and will
+ * not be shown to the administrator. It can be used to pass along data that cannot be modified,
+ * such as a version code.</li>
+ * <li><code>description</code> is meant to describe the restriction in more detail to the
+ * administrator controlling the values, if the title is not sufficient.</li>
+ * </ul>
+ * <p>
+ * Only restrictions of type {@code bundle} and {@code bundle_array} can have one or multiple nested
+ * restriction elements.
+ * <p>
+ * In your manifest's <code>application</code> section, add the meta-data tag to point to
+ * the restrictions XML file as shown below:
+ * <pre>
+ * &lt;application ... &gt;
+ *     &lt;meta-data android:name="android.content.APP_RESTRICTIONS"
+ *                   android:resource="@xml/app_restrictions" /&gt;
+ *     ...
+ * &lt;/application&gt;
+ * </pre>
+ *
+ * @see RestrictionEntry
+ * @see RestrictionsReceiver
+ * @see DevicePolicyManager#setRestrictionsProvider(ComponentName, ComponentName)
+ * @see DevicePolicyManager#setApplicationRestrictions(ComponentName, String, Bundle)
+ */
+@SystemService(Context.RESTRICTIONS_SERVICE)
+public class RestrictionsManager {
+
+    private static final String TAG = "RestrictionsManager";
+
+    /**
+     * Broadcast intent delivered when a response is received for a permission request. The
+     * application should not interrupt the user by coming to the foreground if it isn't
+     * currently in the foreground. It can either post a notification informing
+     * the user of the response or wait until the next time the user launches the app.
+     * <p>
+     * For instance, if the user requested permission to make an in-app purchase,
+     * the app can post a notification that the request had been approved or denied.
+     * <p>
+     * The broadcast Intent carries the following extra:
+     * {@link #EXTRA_RESPONSE_BUNDLE}.
+     */
+    public static final String ACTION_PERMISSION_RESPONSE_RECEIVED =
+            "android.content.action.PERMISSION_RESPONSE_RECEIVED";
+
+    /**
+     * Broadcast intent sent to the Restrictions Provider to handle a permission request from
+     * an app. It will have the following extras: {@link #EXTRA_PACKAGE_NAME},
+     * {@link #EXTRA_REQUEST_TYPE}, {@link #EXTRA_REQUEST_ID} and {@link #EXTRA_REQUEST_BUNDLE}.
+     * The Restrictions Provider will handle the request and respond back to the
+     * RestrictionsManager, when a response is available, by calling
+     * {@link #notifyPermissionResponse}.
+     * <p>
+     * The BroadcastReceiver must require the {@link android.Manifest.permission#BIND_DEVICE_ADMIN}
+     * permission to ensure that only the system can send the broadcast.
+     */
+    public static final String ACTION_REQUEST_PERMISSION =
+            "android.content.action.REQUEST_PERMISSION";
+
+    /**
+     * Activity intent that is optionally implemented by the Restrictions Provider package
+     * to challenge for an administrator PIN or password locally on the device. Apps will
+     * call this intent using {@link Activity#startActivityForResult}. On a successful
+     * response, {@link Activity#onActivityResult} will return a resultCode of
+     * {@link Activity#RESULT_OK}.
+     * <p>
+     * The intent must contain {@link #EXTRA_REQUEST_BUNDLE} as an extra and the bundle must
+     * contain at least {@link #REQUEST_KEY_MESSAGE} for the activity to display.
+     * <p>
+     * @see #createLocalApprovalIntent()
+     */
+    public static final String ACTION_REQUEST_LOCAL_APPROVAL =
+            "android.content.action.REQUEST_LOCAL_APPROVAL";
+
+    /**
+     * The package name of the application making the request.
+     * <p>
+     * Type: String
+     */
+    public static final String EXTRA_PACKAGE_NAME = "android.content.extra.PACKAGE_NAME";
+
+    /**
+     * The request type passed in the {@link #ACTION_REQUEST_PERMISSION} broadcast.
+     * <p>
+     * Type: String
+     */
+    public static final String EXTRA_REQUEST_TYPE = "android.content.extra.REQUEST_TYPE";
+
+    /**
+     * The request ID passed in the {@link #ACTION_REQUEST_PERMISSION} broadcast.
+     * <p>
+     * Type: String
+     */
+    public static final String EXTRA_REQUEST_ID = "android.content.extra.REQUEST_ID";
+
+    /**
+     * The request bundle passed in the {@link #ACTION_REQUEST_PERMISSION} broadcast.
+     * <p>
+     * Type: {@link PersistableBundle}
+     */
+    public static final String EXTRA_REQUEST_BUNDLE = "android.content.extra.REQUEST_BUNDLE";
+
+    /**
+     * Contains a response from the administrator for specific request.
+     * The bundle contains the following information, at least:
+     * <ul>
+     * <li>{@link #REQUEST_KEY_ID}: The request ID.</li>
+     * <li>{@link #RESPONSE_KEY_RESULT}: The response result.</li>
+     * </ul>
+     * <p>
+     * Type: {@link PersistableBundle}
+     */
+    public static final String EXTRA_RESPONSE_BUNDLE = "android.content.extra.RESPONSE_BUNDLE";
+
+    /**
+     * Request type for a simple question, with a possible title and icon.
+     * <p>
+     * Required keys are: {@link #REQUEST_KEY_MESSAGE}
+     * <p>
+     * Optional keys are
+     * {@link #REQUEST_KEY_DATA}, {@link #REQUEST_KEY_ICON}, {@link #REQUEST_KEY_TITLE},
+     * {@link #REQUEST_KEY_APPROVE_LABEL} and {@link #REQUEST_KEY_DENY_LABEL}.
+     */
+    public static final String REQUEST_TYPE_APPROVAL = "android.request.type.approval";
+
+    /**
+     * Key for request ID contained in the request bundle.
+     * <p>
+     * App-generated request ID to identify the specific request when receiving
+     * a response. This value is returned in the {@link #EXTRA_RESPONSE_BUNDLE}.
+     * <p>
+     * Type: String
+     */
+    public static final String REQUEST_KEY_ID = "android.request.id";
+
+    /**
+     * Key for request data contained in the request bundle.
+     * <p>
+     * Optional, typically used to identify the specific data that is being referred to,
+     * such as the unique identifier for a movie or book. This is not used for display
+     * purposes and is more like a cookie. This value is returned in the
+     * {@link #EXTRA_RESPONSE_BUNDLE}.
+     * <p>
+     * Type: String
+     */
+    public static final String REQUEST_KEY_DATA = "android.request.data";
+
+    /**
+     * Key for request title contained in the request bundle.
+     * <p>
+     * Optional, typically used as the title of any notification or dialog presented
+     * to the administrator who approves the request.
+     * <p>
+     * Type: String
+     */
+    public static final String REQUEST_KEY_TITLE = "android.request.title";
+
+    /**
+     * Key for request message contained in the request bundle.
+     * <p>
+     * Required, shown as the actual message in a notification or dialog presented
+     * to the administrator who approves the request.
+     * <p>
+     * Type: String
+     */
+    public static final String REQUEST_KEY_MESSAGE = "android.request.mesg";
+
+    /**
+     * Key for request icon contained in the request bundle.
+     * <p>
+     * Optional, shown alongside the request message presented to the administrator
+     * who approves the request. The content must be a compressed image such as a
+     * PNG or JPEG, as a byte array.
+     * <p>
+     * Type: byte[]
+     */
+    public static final String REQUEST_KEY_ICON = "android.request.icon";
+
+    /**
+     * Key for request approval button label contained in the request bundle.
+     * <p>
+     * Optional, may be shown as a label on the positive button in a dialog or
+     * notification presented to the administrator who approves the request.
+     * <p>
+     * Type: String
+     */
+    public static final String REQUEST_KEY_APPROVE_LABEL = "android.request.approve_label";
+
+    /**
+     * Key for request rejection button label contained in the request bundle.
+     * <p>
+     * Optional, may be shown as a label on the negative button in a dialog or
+     * notification presented to the administrator who approves the request.
+     * <p>
+     * Type: String
+     */
+    public static final String REQUEST_KEY_DENY_LABEL = "android.request.deny_label";
+
+    /**
+     * Key for issuing a new request, contained in the request bundle. If this is set to true,
+     * the Restrictions Provider must make a new request. If it is false or not specified, then
+     * the Restrictions Provider can return a cached response that has the same requestId, if
+     * available. If there's no cached response, it will issue a new one to the administrator.
+     * <p>
+     * Type: boolean
+     */
+    public static final String REQUEST_KEY_NEW_REQUEST = "android.request.new_request";
+
+    /**
+     * Key for the response result in the response bundle sent to the application, for a permission
+     * request. It indicates the status of the request. In some cases an additional message might
+     * be available in {@link #RESPONSE_KEY_MESSAGE}, to be displayed to the user.
+     * <p>
+     * Type: int
+     * <p>
+     * Possible values: {@link #RESULT_APPROVED}, {@link #RESULT_DENIED},
+     * {@link #RESULT_NO_RESPONSE}, {@link #RESULT_UNKNOWN_REQUEST} or
+     * {@link #RESULT_ERROR}.
+     */
+    public static final String RESPONSE_KEY_RESULT = "android.response.result";
+
+    /**
+     * Response result value indicating that the request was approved.
+     */
+    public static final int RESULT_APPROVED = 1;
+
+    /**
+     * Response result value indicating that the request was denied.
+     */
+    public static final int RESULT_DENIED = 2;
+
+    /**
+     * Response result value indicating that the request has not received a response yet.
+     */
+    public static final int RESULT_NO_RESPONSE = 3;
+
+    /**
+     * Response result value indicating that the request is unknown, when it's not a new
+     * request.
+     */
+    public static final int RESULT_UNKNOWN_REQUEST = 4;
+
+    /**
+     * Response result value indicating an error condition. Additional error code might be available
+     * in the response bundle, for the key {@link #RESPONSE_KEY_ERROR_CODE}. There might also be
+     * an associated error message in the response bundle, for the key
+     * {@link #RESPONSE_KEY_MESSAGE}.
+     */
+    public static final int RESULT_ERROR = 5;
+
+    /**
+     * Error code indicating that there was a problem with the request.
+     * <p>
+     * Stored in {@link #RESPONSE_KEY_ERROR_CODE} field in the response bundle.
+     */
+    public static final int RESULT_ERROR_BAD_REQUEST = 1;
+
+    /**
+     * Error code indicating that there was a problem with the network.
+     * <p>
+     * Stored in {@link #RESPONSE_KEY_ERROR_CODE} field in the response bundle.
+     */
+    public static final int RESULT_ERROR_NETWORK = 2;
+
+    /**
+     * Error code indicating that there was an internal error.
+     * <p>
+     * Stored in {@link #RESPONSE_KEY_ERROR_CODE} field in the response bundle.
+     */
+    public static final int RESULT_ERROR_INTERNAL = 3;
+
+    /**
+     * Key for the optional error code in the response bundle sent to the application.
+     * <p>
+     * Type: int
+     * <p>
+     * Possible values: {@link #RESULT_ERROR_BAD_REQUEST}, {@link #RESULT_ERROR_NETWORK} or
+     * {@link #RESULT_ERROR_INTERNAL}.
+     */
+    public static final String RESPONSE_KEY_ERROR_CODE = "android.response.errorcode";
+
+    /**
+     * Key for the optional message in the response bundle sent to the application.
+     * <p>
+     * Type: String
+     */
+    public static final String RESPONSE_KEY_MESSAGE = "android.response.msg";
+
+    /**
+     * Key for the optional timestamp of when the administrator responded to the permission
+     * request. It is an represented in milliseconds since January 1, 1970 00:00:00.0 UTC.
+     * <p>
+     * Type: long
+     */
+    public static final String RESPONSE_KEY_RESPONSE_TIMESTAMP = "android.response.timestamp";
+
+    /**
+     * Name of the meta-data entry in the manifest that points to the XML file containing the
+     * application's available restrictions.
+     * @see #getManifestRestrictions(String)
+     */
+    public static final String META_DATA_APP_RESTRICTIONS = "android.content.APP_RESTRICTIONS";
+
+    private static final String TAG_RESTRICTION = "restriction";
+
+    private final Context mContext;
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    private final IRestrictionsManager mService;
+
+    /**
+     * @hide
+     */
+    public RestrictionsManager(Context context, IRestrictionsManager service) {
+        mContext = context;
+        mService = service;
+    }
+
+    /**
+     * Returns any available set of application-specific restrictions applicable
+     * to this application.
+     * @return the application restrictions as a Bundle. Returns null if there
+     * are no restrictions.
+     */
+    public Bundle getApplicationRestrictions() {
+        try {
+            if (mService != null) {
+                return mService.getApplicationRestrictions(mContext.getPackageName());
+            }
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+        return null;
+    }
+
+    /**
+     * Called by an application to check if there is an active Restrictions Provider. If
+     * there isn't, {@link #requestPermission(String, String, PersistableBundle)} is not available.
+     *
+     * @return whether there is an active Restrictions Provider.
+     */
+    public boolean hasRestrictionsProvider() {
+        try {
+            if (mService != null) {
+                return mService.hasRestrictionsProvider();
+            }
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+        return false;
+    }
+
+    /**
+     * Called by an application to request permission for an operation. The contents of the
+     * request are passed in a Bundle that contains several pieces of data depending on the
+     * chosen request type.
+     *
+     * @param requestType The type of request. The type could be one of the
+     * predefined types specified here or a custom type that the specific
+     * Restrictions Provider might understand. For custom types, the type name should be
+     * namespaced to avoid collisions with predefined types and types specified by
+     * other Restrictions Providers.
+     * @param requestId A unique id generated by the app that contains sufficient information
+     * to identify the parameters of the request when it receives the id in the response.
+     * @param request A PersistableBundle containing the data corresponding to the specified request
+     * type. The keys for the data in the bundle depend on the request type.
+     *
+     * @throws IllegalArgumentException if any of the required parameters are missing.
+     */
+    public void requestPermission(String requestType, String requestId, PersistableBundle request) {
+        if (requestType == null) {
+            throw new NullPointerException("requestType cannot be null");
+        }
+        if (requestId == null) {
+            throw new NullPointerException("requestId cannot be null");
+        }
+        if (request == null) {
+            throw new NullPointerException("request cannot be null");
+        }
+        try {
+            if (mService != null) {
+                mService.requestPermission(mContext.getPackageName(), requestType, requestId,
+                        request);
+            }
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    public Intent createLocalApprovalIntent() {
+        try {
+            if (mService != null) {
+                return mService.createLocalApprovalIntent();
+            }
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+        return null;
+    }
+
+    /**
+     * Called by the Restrictions Provider to deliver a response to an application.
+     *
+     * @param packageName the application to deliver the response to. Cannot be null.
+     * @param response the bundle containing the response status, request ID and other information.
+     *                 Cannot be null.
+     *
+     * @throws IllegalArgumentException if any of the required parameters are missing.
+     */
+    public void notifyPermissionResponse(String packageName, PersistableBundle response) {
+        if (packageName == null) {
+            throw new NullPointerException("packageName cannot be null");
+        }
+        if (response == null) {
+            throw new NullPointerException("request cannot be null");
+        }
+        if (!response.containsKey(REQUEST_KEY_ID)) {
+            throw new IllegalArgumentException("REQUEST_KEY_ID must be specified");
+        }
+        if (!response.containsKey(RESPONSE_KEY_RESULT)) {
+            throw new IllegalArgumentException("RESPONSE_KEY_RESULT must be specified");
+        }
+        try {
+            if (mService != null) {
+                mService.notifyPermissionResponse(packageName, response);
+            }
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Parse and return the list of restrictions defined in the manifest for the specified
+     * package, if any.
+     *
+     * @param packageName The application for which to fetch the restrictions list.
+     * @return The list of RestrictionEntry objects created from the XML file specified
+     * in the manifest, or null if none was specified.
+     */
+    public List<RestrictionEntry> getManifestRestrictions(String packageName) {
+        ApplicationInfo appInfo = null;
+        try {
+            appInfo = mContext.getPackageManager().getApplicationInfo(packageName,
+                    PackageManager.GET_META_DATA);
+        } catch (NameNotFoundException pnfe) {
+            throw new IllegalArgumentException("No such package " + packageName);
+        }
+        if (appInfo == null || !appInfo.metaData.containsKey(META_DATA_APP_RESTRICTIONS)) {
+            return null;
+        }
+
+        XmlResourceParser xml =
+                appInfo.loadXmlMetaData(mContext.getPackageManager(), META_DATA_APP_RESTRICTIONS);
+        return loadManifestRestrictions(packageName, xml);
+    }
+
+    private List<RestrictionEntry> loadManifestRestrictions(String packageName,
+            XmlResourceParser xml) {
+        Context appContext;
+        try {
+            appContext = mContext.createPackageContext(packageName, 0 /* flags */);
+        } catch (NameNotFoundException nnfe) {
+            return null;
+        }
+        ArrayList<RestrictionEntry> restrictions = new ArrayList<>();
+        RestrictionEntry restriction;
+
+        try {
+            int tagType = xml.next();
+            while (tagType != XmlPullParser.END_DOCUMENT) {
+                if (tagType == XmlPullParser.START_TAG) {
+                    restriction = loadRestrictionElement(appContext, xml);
+                    if (restriction != null) {
+                        restrictions.add(restriction);
+                    }
+                }
+                tagType = xml.next();
+            }
+        } catch (XmlPullParserException e) {
+            Log.w(TAG, "Reading restriction metadata for " + packageName, e);
+            return null;
+        } catch (IOException e) {
+            Log.w(TAG, "Reading restriction metadata for " + packageName, e);
+            return null;
+        }
+
+        return restrictions;
+    }
+
+    private RestrictionEntry loadRestrictionElement(Context appContext, XmlResourceParser xml)
+            throws IOException, XmlPullParserException {
+        if (xml.getName().equals(TAG_RESTRICTION)) {
+            AttributeSet attrSet = Xml.asAttributeSet(xml);
+            if (attrSet != null) {
+                TypedArray a = appContext.obtainStyledAttributes(attrSet,
+                        com.android.internal.R.styleable.RestrictionEntry);
+                return loadRestriction(appContext, a, xml);
+            }
+        }
+        return null;
+    }
+
+    private RestrictionEntry loadRestriction(Context appContext, TypedArray a, XmlResourceParser xml)
+            throws IOException, XmlPullParserException {
+        String key = a.getString(R.styleable.RestrictionEntry_key);
+        int restrictionType = a.getInt(
+                R.styleable.RestrictionEntry_restrictionType, -1);
+        String title = a.getString(R.styleable.RestrictionEntry_title);
+        String description = a.getString(R.styleable.RestrictionEntry_description);
+        int entries = a.getResourceId(R.styleable.RestrictionEntry_entries, 0);
+        int entryValues = a.getResourceId(R.styleable.RestrictionEntry_entryValues, 0);
+
+        if (restrictionType == -1) {
+            Log.w(TAG, "restrictionType cannot be omitted");
+            return null;
+        }
+
+        if (key == null) {
+            Log.w(TAG, "key cannot be omitted");
+            return null;
+        }
+
+        RestrictionEntry restriction = new RestrictionEntry(restrictionType, key);
+        restriction.setTitle(title);
+        restriction.setDescription(description);
+        if (entries != 0) {
+            restriction.setChoiceEntries(appContext, entries);
+        }
+        if (entryValues != 0) {
+            restriction.setChoiceValues(appContext, entryValues);
+        }
+        // Extract the default value based on the type
+        switch (restrictionType) {
+            case RestrictionEntry.TYPE_NULL: // hidden
+            case RestrictionEntry.TYPE_STRING:
+            case RestrictionEntry.TYPE_CHOICE:
+                restriction.setSelectedString(
+                        a.getString(R.styleable.RestrictionEntry_defaultValue));
+                break;
+            case RestrictionEntry.TYPE_INTEGER:
+                restriction.setIntValue(
+                        a.getInt(R.styleable.RestrictionEntry_defaultValue, 0));
+                break;
+            case RestrictionEntry.TYPE_MULTI_SELECT:
+                int resId = a.getResourceId(R.styleable.RestrictionEntry_defaultValue, 0);
+                if (resId != 0) {
+                    restriction.setAllSelectedStrings(
+                            appContext.getResources().getStringArray(resId));
+                }
+                break;
+            case RestrictionEntry.TYPE_BOOLEAN:
+                restriction.setSelectedState(
+                        a.getBoolean(R.styleable.RestrictionEntry_defaultValue, false));
+                break;
+            case RestrictionEntry.TYPE_BUNDLE:
+            case RestrictionEntry.TYPE_BUNDLE_ARRAY:
+                final int outerDepth = xml.getDepth();
+                List<RestrictionEntry> restrictionEntries = new ArrayList<>();
+                while (XmlUtils.nextElementWithin(xml, outerDepth)) {
+                    RestrictionEntry childEntry = loadRestrictionElement(appContext, xml);
+                    if (childEntry == null) {
+                        Log.w(TAG, "Child entry cannot be loaded for bundle restriction " + key);
+                    } else {
+                        restrictionEntries.add(childEntry);
+                        if (restrictionType == RestrictionEntry.TYPE_BUNDLE_ARRAY
+                                && childEntry.getType() != RestrictionEntry.TYPE_BUNDLE) {
+                            Log.w(TAG, "bundle_array " + key
+                                    + " can only contain entries of type bundle");
+                        }
+                    }
+                }
+                restriction.setRestrictions(restrictionEntries.toArray(new RestrictionEntry[
+                        restrictionEntries.size()]));
+                break;
+            default:
+                Log.w(TAG, "Unknown restriction type " + restrictionType);
+        }
+        return restriction;
+    }
+
+    /**
+     * Converts a list of restrictions to the corresponding bundle, using the following mapping:
+     * <table>
+     *     <tr><th>RestrictionEntry</th><th>Bundle</th></tr>
+     *     <tr><td>{@link RestrictionEntry#TYPE_BOOLEAN}</td><td>{@link Bundle#putBoolean}</td></tr>
+     *     <tr><td>{@link RestrictionEntry#TYPE_CHOICE},
+     *     {@link RestrictionEntry#TYPE_MULTI_SELECT}</td>
+     *     <td>{@link Bundle#putStringArray}</td></tr>
+     *     <tr><td>{@link RestrictionEntry#TYPE_INTEGER}</td><td>{@link Bundle#putInt}</td></tr>
+     *     <tr><td>{@link RestrictionEntry#TYPE_STRING}</td><td>{@link Bundle#putString}</td></tr>
+     *     <tr><td>{@link RestrictionEntry#TYPE_BUNDLE}</td><td>{@link Bundle#putBundle}</td></tr>
+     *     <tr><td>{@link RestrictionEntry#TYPE_BUNDLE_ARRAY}</td>
+     *     <td>{@link Bundle#putParcelableArray}</td></tr>
+     * </table>
+     * @param entries list of restrictions
+     */
+    public static Bundle convertRestrictionsToBundle(List<RestrictionEntry> entries) {
+        final Bundle bundle = new Bundle();
+        for (RestrictionEntry entry : entries) {
+            addRestrictionToBundle(bundle, entry);
+        }
+        return bundle;
+    }
+
+    private static Bundle addRestrictionToBundle(Bundle bundle, RestrictionEntry entry) {
+        switch (entry.getType()) {
+            case RestrictionEntry.TYPE_BOOLEAN:
+                bundle.putBoolean(entry.getKey(), entry.getSelectedState());
+                break;
+            case RestrictionEntry.TYPE_CHOICE:
+            case RestrictionEntry.TYPE_CHOICE_LEVEL:
+            case RestrictionEntry.TYPE_MULTI_SELECT:
+                bundle.putStringArray(entry.getKey(), entry.getAllSelectedStrings());
+                break;
+            case RestrictionEntry.TYPE_INTEGER:
+                bundle.putInt(entry.getKey(), entry.getIntValue());
+                break;
+            case RestrictionEntry.TYPE_STRING:
+            case RestrictionEntry.TYPE_NULL:
+                bundle.putString(entry.getKey(), entry.getSelectedString());
+                break;
+            case RestrictionEntry.TYPE_BUNDLE:
+                RestrictionEntry[] restrictions = entry.getRestrictions();
+                Bundle childBundle = convertRestrictionsToBundle(Arrays.asList(restrictions));
+                bundle.putBundle(entry.getKey(), childBundle);
+                break;
+            case RestrictionEntry.TYPE_BUNDLE_ARRAY:
+                RestrictionEntry[] bundleRestrictionArray = entry.getRestrictions();
+                Bundle[] bundleArray = new Bundle[bundleRestrictionArray.length];
+                for (int i = 0; i < bundleRestrictionArray.length; i++) {
+                    RestrictionEntry[] bundleRestrictions =
+                            bundleRestrictionArray[i].getRestrictions();
+                    if (bundleRestrictions == null) {
+                        // Non-bundle entry found in bundle array.
+                        Log.w(TAG, "addRestrictionToBundle: " +
+                                "Non-bundle entry found in bundle array");
+                        bundleArray[i] = new Bundle();
+                    } else {
+                        bundleArray[i] = convertRestrictionsToBundle(Arrays.asList(
+                                bundleRestrictions));
+                    }
+                }
+                bundle.putParcelableArray(entry.getKey(), bundleArray);
+                break;
+            default:
+                throw new IllegalArgumentException(
+                        "Unsupported restrictionEntry type: " + entry.getType());
+        }
+        return bundle;
+    }
+
+}
diff --git a/android/content/SearchRecentSuggestionsProvider.java b/android/content/SearchRecentSuggestionsProvider.java
new file mode 100644
index 0000000..fc3ddf6
--- /dev/null
+++ b/android/content/SearchRecentSuggestionsProvider.java
@@ -0,0 +1,399 @@
+/*
+ * 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.content;
+
+import android.app.SearchManager;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.net.Uri;
+import android.text.TextUtils;
+import android.util.Log;
+
+/**
+ * This superclass can be used to create a simple search suggestions provider for your application.
+ * It creates suggestions (as the user types) based on recent queries and/or recent views.
+ * 
+ * <p>In order to use this class, you must do the following.
+ * 
+ * <ul>
+ * <li>Implement and test query search, as described in {@link android.app.SearchManager}.  (This
+ * provider will send any suggested queries via the standard 
+ * {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH} Intent, which you'll already
+ * support once you have implemented and tested basic searchability.)</li>
+ * <li>Create a Content Provider within your application by extending 
+ * {@link android.content.SearchRecentSuggestionsProvider}.  The class you create will be
+ * very simple - typically, it will have only a constructor.  But the constructor has a very 
+ * important responsibility:  When it calls {@link #setupSuggestions(String, int)}, it
+ * <i>configures</i> the provider to match the requirements of your searchable activity.</li>
+ * <li>Create a manifest entry describing your provider.  Typically this would be as simple
+ * as adding the following lines:
+ * <pre class="prettyprint">
+ *     &lt;!-- Content provider for search suggestions --&gt;
+ *     &lt;provider android:name="YourSuggestionProviderClass"
+ *               android:authorities="your.suggestion.authority" /&gt;</pre>
+ * </li>
+ * <li>Please note that you <i>do not</i> instantiate this content provider directly from within
+ * your code.  This is done automatically by the system Content Resolver, when the search dialog
+ * looks for suggestions.</li>
+ * <li>In order for the Content Resolver to do this, you must update your searchable activity's 
+ * XML configuration file with information about your content provider.  The following additions 
+ * are usually sufficient:
+ * <pre class="prettyprint">
+ *     android:searchSuggestAuthority="your.suggestion.authority"
+ *     android:searchSuggestSelection=" ? "</pre>
+ * </li>
+ * <li>In your searchable activities, capture any user-generated queries and record them
+ * for future searches by calling {@link android.provider.SearchRecentSuggestions#saveRecentQuery
+ * SearchRecentSuggestions.saveRecentQuery()}.</li>
+ * </ul>
+ *
+ * <div class="special reference">
+ * <h3>Developer Guides</h3>
+ * <p>For information about using search suggestions in your application, read the
+ * <a href="{@docRoot}guide/topics/search/index.html">Search</a> developer guide.</p>
+ * </div>
+ * 
+ * @see android.provider.SearchRecentSuggestions
+ */
+public class SearchRecentSuggestionsProvider extends ContentProvider {
+    // debugging support
+    private static final String TAG = "SuggestionsProvider";
+    
+    // client-provided configuration values
+    private String mAuthority;
+    private int mMode;
+    private boolean mTwoLineDisplay;
+
+    // general database configuration and tables
+    private SQLiteOpenHelper mOpenHelper;
+    private static final String sDatabaseName = "suggestions.db";
+    private static final String sSuggestions = "suggestions";
+    private static final String ORDER_BY = "date DESC";
+    private static final String NULL_COLUMN = "query";
+    
+    // Table of database versions.  Don't forget to update!
+    // NOTE:  These version values are shifted left 8 bits (x 256) in order to create space for
+    // a small set of mode bitflags in the version int.
+    //
+    // 1      original implementation with queries, and 1 or 2 display columns
+    // 1->2   added UNIQUE constraint to display1 column
+    private static final int DATABASE_VERSION = 2 * 256;
+    
+    /**
+     * This mode bit configures the database to record recent queries.  <i>required</i>
+     * 
+     * @see #setupSuggestions(String, int)
+     */
+    public static final int DATABASE_MODE_QUERIES = 1;
+    /**
+     * This mode bit configures the database to include a 2nd annotation line with each entry.
+     * <i>optional</i>
+     * 
+     * @see #setupSuggestions(String, int)
+     */
+    public static final int DATABASE_MODE_2LINES = 2;
+
+    // Uri and query support
+    private static final int URI_MATCH_SUGGEST = 1;
+    
+    private Uri mSuggestionsUri;
+    private UriMatcher mUriMatcher;
+    
+    private String mSuggestSuggestionClause;
+    @UnsupportedAppUsage
+    private String[] mSuggestionProjection;
+
+    /**
+     * Builds the database.  This version has extra support for using the version field
+     * as a mode flags field, and configures the database columns depending on the mode bits
+     * (features) requested by the extending class.
+     * 
+     * @hide
+     */
+    private static class DatabaseHelper extends SQLiteOpenHelper {
+        
+        private int mNewVersion;
+        
+        public DatabaseHelper(Context context, int newVersion) {
+            super(context, sDatabaseName, null, newVersion);
+            mNewVersion = newVersion;
+        }
+        
+        @Override
+        public void onCreate(SQLiteDatabase db) {
+            StringBuilder builder = new StringBuilder();
+            builder.append("CREATE TABLE suggestions (" +
+                    "_id INTEGER PRIMARY KEY" +
+                    ",display1 TEXT UNIQUE ON CONFLICT REPLACE");
+            if (0 != (mNewVersion & DATABASE_MODE_2LINES)) {
+                builder.append(",display2 TEXT");
+            }
+            builder.append(",query TEXT" +
+                    ",date LONG" +
+                    ");");
+            db.execSQL(builder.toString());
+        }
+
+        @Override
+        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+            Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
+                    + newVersion + ", which will destroy all old data");
+            db.execSQL("DROP TABLE IF EXISTS suggestions");
+            onCreate(db);
+        }
+    }
+    
+    /**
+     * In order to use this class, you must extend it, and call this setup function from your
+     * constructor.  In your application or activities, you must provide the same values when 
+     * you create the {@link android.provider.SearchRecentSuggestions} helper.
+     * 
+     * @param authority This must match the authority that you've declared in your manifest.
+     * @param mode You can use mode flags here to determine certain functional aspects of your
+     * database.  Note, this value should not change from run to run, because when it does change,
+     * your suggestions database may be wiped.
+     * 
+     * @see #DATABASE_MODE_QUERIES
+     * @see #DATABASE_MODE_2LINES
+     */
+    protected void setupSuggestions(String authority, int mode) {
+        if (TextUtils.isEmpty(authority) || 
+                ((mode & DATABASE_MODE_QUERIES) == 0)) {
+            throw new IllegalArgumentException();
+        }
+        // unpack mode flags
+        mTwoLineDisplay = (0 != (mode & DATABASE_MODE_2LINES));
+            
+        // saved values
+        mAuthority = new String(authority);
+        mMode = mode;
+        
+        // derived values
+        mSuggestionsUri = Uri.parse("content://" + mAuthority + "/suggestions");
+        mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
+        mUriMatcher.addURI(mAuthority, SearchManager.SUGGEST_URI_PATH_QUERY, URI_MATCH_SUGGEST);
+        
+        if (mTwoLineDisplay) {
+            mSuggestSuggestionClause = "display1 LIKE ? OR display2 LIKE ?";
+
+            mSuggestionProjection = new String [] {
+                    "0 AS " + SearchManager.SUGGEST_COLUMN_FORMAT,
+                    "'android.resource://system/"
+                            + com.android.internal.R.drawable.ic_menu_recent_history + "' AS "
+                            + SearchManager.SUGGEST_COLUMN_ICON_1,
+                    "display1 AS " + SearchManager.SUGGEST_COLUMN_TEXT_1,
+                    "display2 AS " + SearchManager.SUGGEST_COLUMN_TEXT_2,
+                    "query AS " + SearchManager.SUGGEST_COLUMN_QUERY,
+                    "_id"
+            };
+        } else {
+            mSuggestSuggestionClause = "display1 LIKE ?";
+
+            mSuggestionProjection = new String [] {
+                    "0 AS " + SearchManager.SUGGEST_COLUMN_FORMAT,
+                    "'android.resource://system/"
+                            + com.android.internal.R.drawable.ic_menu_recent_history + "' AS "
+                            + SearchManager.SUGGEST_COLUMN_ICON_1,
+                    "display1 AS " + SearchManager.SUGGEST_COLUMN_TEXT_1,
+                    "query AS " + SearchManager.SUGGEST_COLUMN_QUERY,
+                    "_id"
+            };
+        }
+
+
+    }
+    
+    /**
+     * This method is provided for use by the ContentResolver.  Do not override, or directly
+     * call from your own code.
+     */
+    @Override
+    public int delete(Uri uri, String selection, String[] selectionArgs) {
+        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+
+        final int length = uri.getPathSegments().size();
+        if (length != 1) {
+            throw new IllegalArgumentException("Unknown Uri");
+        }
+
+        final String base = uri.getPathSegments().get(0);
+        int count = 0;
+        if (base.equals(sSuggestions)) {
+            count = db.delete(sSuggestions, selection, selectionArgs);
+        } else {
+            throw new IllegalArgumentException("Unknown Uri");
+        }
+        getContext().getContentResolver().notifyChange(uri, null);
+        return count;
+    }
+
+    /**
+     * This method is provided for use by the ContentResolver.  Do not override, or directly
+     * call from your own code.
+     */
+    @Override
+    public String getType(Uri uri) {
+        if (mUriMatcher.match(uri) == URI_MATCH_SUGGEST) {
+            return SearchManager.SUGGEST_MIME_TYPE;
+        }
+        int length = uri.getPathSegments().size();
+        if (length >= 1) {
+            String base = uri.getPathSegments().get(0);
+            if (base.equals(sSuggestions)) {
+                if (length == 1) {
+                    return "vnd.android.cursor.dir/suggestion";
+                } else if (length == 2) {
+                    return "vnd.android.cursor.item/suggestion";
+                }
+            }
+        }            
+        throw new IllegalArgumentException("Unknown Uri");
+    }
+
+    /**
+     * This method is provided for use by the ContentResolver.  Do not override, or directly
+     * call from your own code.
+     */
+    @Override
+    public Uri insert(Uri uri, ContentValues values) {
+        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+
+        int length = uri.getPathSegments().size();
+        if (length < 1) {
+            throw new IllegalArgumentException("Unknown Uri");
+        }
+        // Note:  This table has on-conflict-replace semantics, so insert() may actually replace()
+        long rowID = -1;
+        String base = uri.getPathSegments().get(0);
+        Uri newUri = null;
+        if (base.equals(sSuggestions)) {
+            if (length == 1) {
+                rowID = db.insert(sSuggestions, NULL_COLUMN, values);
+                if (rowID > 0) {
+                    newUri = Uri.withAppendedPath(mSuggestionsUri, String.valueOf(rowID));
+                }
+            }
+        }
+        if (rowID < 0) {
+            throw new IllegalArgumentException("Unknown Uri");
+        }
+        getContext().getContentResolver().notifyChange(newUri, null);
+        return newUri;
+    }
+
+    /**
+     * This method is provided for use by the ContentResolver.  Do not override, or directly
+     * call from your own code.
+     */
+    @Override
+    public boolean onCreate() {
+        if (mAuthority == null || mMode == 0) {
+            throw new IllegalArgumentException("Provider not configured");
+        }
+        int mWorkingDbVersion = DATABASE_VERSION + mMode;
+        mOpenHelper = new DatabaseHelper(getContext(), mWorkingDbVersion);
+        
+        return true;
+    }
+
+    /**
+     * This method is provided for use by the ContentResolver.  Do not override, or directly
+     * call from your own code.
+     */
+    // TODO: Confirm no injection attacks here, or rewrite.
+    @Override
+    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, 
+            String sortOrder) {
+        SQLiteDatabase db = mOpenHelper.getReadableDatabase();
+        
+        // special case for actual suggestions (from search manager)
+        if (mUriMatcher.match(uri) == URI_MATCH_SUGGEST) {
+            String suggestSelection;
+            String[] myArgs;
+            if (TextUtils.isEmpty(selectionArgs[0])) {
+                suggestSelection = null;
+                myArgs = null;
+            } else {
+                String like = "%" + selectionArgs[0] + "%";
+                if (mTwoLineDisplay) {
+                    myArgs = new String [] { like, like };
+                } else {
+                    myArgs = new String [] { like };
+                }
+                suggestSelection = mSuggestSuggestionClause;
+            }
+            // Suggestions are always performed with the default sort order
+            Cursor c = db.query(sSuggestions, mSuggestionProjection,
+                    suggestSelection, myArgs, null, null, ORDER_BY, null);
+            c.setNotificationUri(getContext().getContentResolver(), uri);
+            return c;
+        }
+
+        // otherwise process arguments and perform a standard query
+        int length = uri.getPathSegments().size();
+        if (length != 1 && length != 2) {
+            throw new IllegalArgumentException("Unknown Uri");
+        }
+
+        String base = uri.getPathSegments().get(0);
+        if (!base.equals(sSuggestions)) {
+            throw new IllegalArgumentException("Unknown Uri");
+        }
+
+        String[] useProjection = null;
+        if (projection != null && projection.length > 0) {
+            useProjection = new String[projection.length + 1];
+            System.arraycopy(projection, 0, useProjection, 0, projection.length);
+            useProjection[projection.length] = "_id AS _id";
+        }
+
+        StringBuilder whereClause = new StringBuilder(256);
+        if (length == 2) {
+            whereClause.append("(_id = ").append(uri.getPathSegments().get(1)).append(")");
+        }
+
+        // Tack on the user's selection, if present
+        if (selection != null && selection.length() > 0) {
+            if (whereClause.length() > 0) {
+                whereClause.append(" AND ");
+            }
+
+            whereClause.append('(');
+            whereClause.append(selection);
+            whereClause.append(')');
+        }
+        
+        // And perform the generic query as requested
+        Cursor c = db.query(base, useProjection, whereClause.toString(),
+                selectionArgs, null, null, sortOrder,
+                null);
+        c.setNotificationUri(getContext().getContentResolver(), uri);
+        return c;
+    }
+
+    /**
+     * This method is provided for use by the ContentResolver.  Do not override, or directly
+     * call from your own code.
+     */
+    @Override
+    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+}
diff --git a/android/content/ServiceConnection.java b/android/content/ServiceConnection.java
new file mode 100644
index 0000000..21398f6
--- /dev/null
+++ b/android/content/ServiceConnection.java
@@ -0,0 +1,88 @@
+/*
+ * 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.content;
+
+import android.os.IBinder;
+
+/**
+ * Interface for monitoring the state of an application service.  See
+ * {@link android.app.Service} and
+ * {@link Context#bindService Context.bindService()} for more information.
+ * <p>Like many callbacks from the system, the methods on this class are called
+ * from the main thread of your process.
+ */
+public interface ServiceConnection {
+    /**
+     * Called when a connection to the Service has been established, with
+     * the {@link android.os.IBinder} of the communication channel to the
+     * Service.
+     *
+     * <p class="note"><b>Note:</b> If the system has started to bind your
+     * client app to a service, it's possible that your app will never receive
+     * this callback. Your app won't receive a callback if there's an issue with
+     * the service, such as the service crashing while being created.
+     *
+     * @param name The concrete component name of the service that has
+     * been connected.
+     *
+     * @param service The IBinder of the Service's communication channel,
+     * which you can now make calls on.
+     */
+    void onServiceConnected(ComponentName name, IBinder service);
+
+    /**
+     * Called when a connection to the Service has been lost.  This typically
+     * happens when the process hosting the service has crashed or been killed.
+     * This does <em>not</em> remove the ServiceConnection itself -- this
+     * binding to the service will remain active, and you will receive a call
+     * to {@link #onServiceConnected} when the Service is next running.
+     *
+     * @param name The concrete component name of the service whose
+     * connection has been lost.
+     */
+    void onServiceDisconnected(ComponentName name);
+
+    /**
+     * Called when the binding to this connection is dead.  This means the
+     * interface will never receive another connection.  The application will
+     * need to unbind and rebind the connection to activate it again.  This may
+     * happen, for example, if the application hosting the service it is bound to
+     * has been updated.
+     *
+     * @param name The concrete component name of the service whose
+     * connection is dead.
+     */
+    default void onBindingDied(ComponentName name) {
+    }
+
+    /**
+     * Called when the service being bound has returned {@code null} from its
+     * {@link android.app.Service#onBind(Intent) onBind()} method.  This indicates
+     * that the attempting service binding represented by this ServiceConnection
+     * will never become usable.
+     *
+     * <p class="note">The app which requested the binding must still call
+     * {@link Context#unbindService(ServiceConnection)} to release the tracking
+     * resources associated with this ServiceConnection even if this callback was
+     * invoked following {@link Context#bindService Context.bindService() bindService()}.
+     *
+     * @param name The concrete component name of the service whose binding
+     *     has been rejected by the Service implementation.
+     */
+    default void onNullBinding(ComponentName name) {
+    }
+}
diff --git a/android/content/SharedPreferences.java b/android/content/SharedPreferences.java
new file mode 100644
index 0000000..c193868
--- /dev/null
+++ b/android/content/SharedPreferences.java
@@ -0,0 +1,392 @@
+/*
+ * 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.content;
+
+import android.annotation.Nullable;
+
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Interface for accessing and modifying preference data returned by {@link
+ * Context#getSharedPreferences}.  For any particular set of preferences,
+ * there is a single instance of this class that all clients share.
+ * Modifications to the preferences must go through an {@link Editor} object
+ * to ensure the preference values remain in a consistent state and control
+ * when they are committed to storage.  Objects that are returned from the
+ * various <code>get</code> methods must be treated as immutable by the application.
+ *
+ * <p>Note: This class provides strong consistency guarantees. It is using expensive operations
+ * which might slow down an app. Frequently changing properties or properties where loss can be
+ * tolerated should use other mechanisms. For more details read the comments on
+ * {@link Editor#commit()} and {@link Editor#apply()}.
+ *
+ * <p><em>Note: This class does not support use across multiple processes.</em>
+ *
+ * <div class="special reference">
+ * <h3>Developer Guides</h3>
+ * <p>For more information about using SharedPreferences, read the
+ * <a href="{@docRoot}guide/topics/data/data-storage.html#pref">Data Storage</a>
+ * developer guide.</p></div>
+ *
+ * @see Context#getSharedPreferences
+ */
+public interface SharedPreferences {
+    /**
+     * Interface definition for a callback to be invoked when a shared
+     * preference is changed.
+     */
+    public interface OnSharedPreferenceChangeListener {
+        /**
+         * Called when a shared preference is changed, added, or removed. This
+         * may be called even if a preference is set to its existing value.
+         *
+         * <p>This callback will be run on your main thread.
+         *
+         * <p><em>Note: This callback will not be triggered when preferences are cleared
+         * via {@link Editor#clear()}, unless targeting {@link android.os.Build.VERSION_CODES#R}
+         * on devices running OS versions {@link android.os.Build.VERSION_CODES#R Android R}
+         * or later.</em>
+         *
+         * @param sharedPreferences The {@link SharedPreferences} that received the change.
+         * @param key The key of the preference that was changed, added, or removed. Apps targeting
+         *            {@link android.os.Build.VERSION_CODES#R} on devices running OS versions
+         *            {@link android.os.Build.VERSION_CODES#R Android R} or later, will receive
+         *            a {@code null} value when preferences are cleared.
+         */
+        void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key);
+    }
+
+    /**
+     * Interface used for modifying values in a {@link SharedPreferences}
+     * object.  All changes you make in an editor are batched, and not copied
+     * back to the original {@link SharedPreferences} until you call {@link #commit}
+     * or {@link #apply}
+     */
+    public interface Editor {
+        /**
+         * Set a String value in the preferences editor, to be written back once
+         * {@link #commit} or {@link #apply} are called.
+         * 
+         * @param key The name of the preference to modify.
+         * @param value The new value for the preference.  Passing {@code null}
+         *    for this argument is equivalent to calling {@link #remove(String)} with
+         *    this key.
+         * 
+         * @return Returns a reference to the same Editor object, so you can
+         * chain put calls together.
+         */
+        Editor putString(String key, @Nullable String value);
+        
+        /**
+         * Set a set of String values in the preferences editor, to be written
+         * back once {@link #commit} or {@link #apply} is called.
+         * 
+         * @param key The name of the preference to modify.
+         * @param values The set of new values for the preference.  Passing {@code null}
+         *    for this argument is equivalent to calling {@link #remove(String)} with
+         *    this key.
+         * @return Returns a reference to the same Editor object, so you can
+         * chain put calls together.
+         */
+        Editor putStringSet(String key, @Nullable Set<String> values);
+        
+        /**
+         * Set an int value in the preferences editor, to be written back once
+         * {@link #commit} or {@link #apply} are called.
+         * 
+         * @param key The name of the preference to modify.
+         * @param value The new value for the preference.
+         * 
+         * @return Returns a reference to the same Editor object, so you can
+         * chain put calls together.
+         */
+        Editor putInt(String key, int value);
+        
+        /**
+         * Set a long value in the preferences editor, to be written back once
+         * {@link #commit} or {@link #apply} are called.
+         * 
+         * @param key The name of the preference to modify.
+         * @param value The new value for the preference.
+         * 
+         * @return Returns a reference to the same Editor object, so you can
+         * chain put calls together.
+         */
+        Editor putLong(String key, long value);
+        
+        /**
+         * Set a float value in the preferences editor, to be written back once
+         * {@link #commit} or {@link #apply} are called.
+         * 
+         * @param key The name of the preference to modify.
+         * @param value The new value for the preference.
+         * 
+         * @return Returns a reference to the same Editor object, so you can
+         * chain put calls together.
+         */
+        Editor putFloat(String key, float value);
+        
+        /**
+         * Set a boolean value in the preferences editor, to be written back
+         * once {@link #commit} or {@link #apply} are called.
+         * 
+         * @param key The name of the preference to modify.
+         * @param value The new value for the preference.
+         * 
+         * @return Returns a reference to the same Editor object, so you can
+         * chain put calls together.
+         */
+        Editor putBoolean(String key, boolean value);
+
+        /**
+         * Mark in the editor that a preference value should be removed, which
+         * will be done in the actual preferences once {@link #commit} is
+         * called.
+         * 
+         * <p>Note that when committing back to the preferences, all removals
+         * are done first, regardless of whether you called remove before
+         * or after put methods on this editor.
+         * 
+         * @param key The name of the preference to remove.
+         * 
+         * @return Returns a reference to the same Editor object, so you can
+         * chain put calls together.
+         */
+        Editor remove(String key);
+
+        /**
+         * Mark in the editor to remove <em>all</em> values from the
+         * preferences.  Once commit is called, the only remaining preferences
+         * will be any that you have defined in this editor.
+         * 
+         * <p>Note that when committing back to the preferences, the clear
+         * is done first, regardless of whether you called clear before
+         * or after put methods on this editor.
+         * 
+         * @return Returns a reference to the same Editor object, so you can
+         * chain put calls together.
+         */
+        Editor clear();
+
+        /**
+         * Commit your preferences changes back from this Editor to the
+         * {@link SharedPreferences} object it is editing.  This atomically
+         * performs the requested modifications, replacing whatever is currently
+         * in the SharedPreferences.
+         *
+         * <p>Note that when two editors are modifying preferences at the same
+         * time, the last one to call commit wins.
+         *
+         * <p>If you don't care about the return value and you're
+         * using this from your application's main thread, consider
+         * using {@link #apply} instead.
+         *
+         * @return Returns true if the new values were successfully written
+         * to persistent storage.
+         */
+        boolean commit();
+
+        /**
+         * Commit your preferences changes back from this Editor to the
+         * {@link SharedPreferences} object it is editing.  This atomically
+         * performs the requested modifications, replacing whatever is currently
+         * in the SharedPreferences.
+         *
+         * <p>Note that when two editors are modifying preferences at the same
+         * time, the last one to call apply wins.
+         *
+         * <p>Unlike {@link #commit}, which writes its preferences out
+         * to persistent storage synchronously, {@link #apply}
+         * commits its changes to the in-memory
+         * {@link SharedPreferences} immediately but starts an
+         * asynchronous commit to disk and you won't be notified of
+         * any failures.  If another editor on this
+         * {@link SharedPreferences} does a regular {@link #commit}
+         * while a {@link #apply} is still outstanding, the
+         * {@link #commit} will block until all async commits are
+         * completed as well as the commit itself.
+         *
+         * <p>As {@link SharedPreferences} instances are singletons within
+         * a process, it's safe to replace any instance of {@link #commit} with
+         * {@link #apply} if you were already ignoring the return value.
+         *
+         * <p>You don't need to worry about Android component
+         * lifecycles and their interaction with <code>apply()</code>
+         * writing to disk.  The framework makes sure in-flight disk
+         * writes from <code>apply()</code> complete before switching
+         * states.
+         *
+         * <p class='note'>The SharedPreferences.Editor interface
+         * isn't expected to be implemented directly.  However, if you
+         * previously did implement it and are now getting errors
+         * about missing <code>apply()</code>, you can simply call
+         * {@link #commit} from <code>apply()</code>.
+         */
+        void apply();
+    }
+
+    /**
+     * Retrieve all values from the preferences.
+     *
+     * <p>Note that you <em>must not</em> modify the collection returned
+     * by this method, or alter any of its contents.  The consistency of your
+     * stored data is not guaranteed if you do.
+     *
+     * @return Returns a map containing a list of pairs key/value representing
+     * the preferences.
+     *
+     * @throws NullPointerException
+     */
+    Map<String, ?> getAll();
+
+    /**
+     * Retrieve a String value from the preferences.
+     * 
+     * @param key The name of the preference to retrieve.
+     * @param defValue Value to return if this preference does not exist.
+     * 
+     * @return Returns the preference value if it exists, or defValue.  Throws
+     * ClassCastException if there is a preference with this name that is not
+     * a String.
+     * 
+     * @throws ClassCastException
+     */
+    @Nullable
+    String getString(String key, @Nullable String defValue);
+    
+    /**
+     * Retrieve a set of String values from the preferences.
+     * 
+     * <p>Note that you <em>must not</em> modify the set instance returned
+     * by this call.  The consistency of the stored data is not guaranteed
+     * if you do, nor is your ability to modify the instance at all.
+     *
+     * @param key The name of the preference to retrieve.
+     * @param defValues Values to return if this preference does not exist.
+     * 
+     * @return Returns the preference values if they exist, or defValues.
+     * Throws ClassCastException if there is a preference with this name
+     * that is not a Set.
+     * 
+     * @throws ClassCastException
+     */
+    @Nullable
+    Set<String> getStringSet(String key, @Nullable Set<String> defValues);
+    
+    /**
+     * Retrieve an int value from the preferences.
+     * 
+     * @param key The name of the preference to retrieve.
+     * @param defValue Value to return if this preference does not exist.
+     * 
+     * @return Returns the preference value if it exists, or defValue.  Throws
+     * ClassCastException if there is a preference with this name that is not
+     * an int.
+     * 
+     * @throws ClassCastException
+     */
+    int getInt(String key, int defValue);
+    
+    /**
+     * Retrieve a long value from the preferences.
+     * 
+     * @param key The name of the preference to retrieve.
+     * @param defValue Value to return if this preference does not exist.
+     * 
+     * @return Returns the preference value if it exists, or defValue.  Throws
+     * ClassCastException if there is a preference with this name that is not
+     * a long.
+     * 
+     * @throws ClassCastException
+     */
+    long getLong(String key, long defValue);
+    
+    /**
+     * Retrieve a float value from the preferences.
+     * 
+     * @param key The name of the preference to retrieve.
+     * @param defValue Value to return if this preference does not exist.
+     * 
+     * @return Returns the preference value if it exists, or defValue.  Throws
+     * ClassCastException if there is a preference with this name that is not
+     * a float.
+     * 
+     * @throws ClassCastException
+     */
+    float getFloat(String key, float defValue);
+    
+    /**
+     * Retrieve a boolean value from the preferences.
+     * 
+     * @param key The name of the preference to retrieve.
+     * @param defValue Value to return if this preference does not exist.
+     * 
+     * @return Returns the preference value if it exists, or defValue.  Throws
+     * ClassCastException if there is a preference with this name that is not
+     * a boolean.
+     * 
+     * @throws ClassCastException
+     */
+    boolean getBoolean(String key, boolean defValue);
+
+    /**
+     * Checks whether the preferences contains a preference.
+     * 
+     * @param key The name of the preference to check.
+     * @return Returns true if the preference exists in the preferences,
+     *         otherwise false.
+     */
+    boolean contains(String key);
+    
+    /**
+     * Create a new Editor for these preferences, through which you can make
+     * modifications to the data in the preferences and atomically commit those
+     * changes back to the SharedPreferences object.
+     * 
+     * <p>Note that you <em>must</em> call {@link Editor#commit} to have any
+     * changes you perform in the Editor actually show up in the
+     * SharedPreferences.
+     * 
+     * @return Returns a new instance of the {@link Editor} interface, allowing
+     * you to modify the values in this SharedPreferences object.
+     */
+    Editor edit();
+    
+    /**
+     * Registers a callback to be invoked when a change happens to a preference.
+     *
+     * <p class="caution"><strong>Caution:</strong> The preference manager does
+     * not currently store a strong reference to the listener. You must store a
+     * strong reference to the listener, or it will be susceptible to garbage
+     * collection. We recommend you keep a reference to the listener in the
+     * instance data of an object that will exist as long as you need the
+     * listener.</p>
+     *
+     * @param listener The callback that will run.
+     * @see #unregisterOnSharedPreferenceChangeListener
+     */
+    void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener);
+
+    /**
+     * Unregisters a previous callback.
+     *
+     * @param listener The callback that should be unregistered.
+     * @see #registerOnSharedPreferenceChangeListener
+     */
+    void unregisterOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener);
+}
diff --git a/android/content/SyncActivityTooManyDeletes.java b/android/content/SyncActivityTooManyDeletes.java
new file mode 100644
index 0000000..093fb08
--- /dev/null
+++ b/android/content/SyncActivityTooManyDeletes.java
@@ -0,0 +1,133 @@
+/*
+ * 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.content;
+
+import com.android.internal.R;
+import android.accounts.Account;
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.LinearLayout;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+/**
+ * Presents multiple options for handling the case where a sync was aborted because there
+ * were too many pending deletes. One option is to force the delete, another is to rollback
+ * the deletes, the third is to do nothing.
+ * @hide
+ */
+public class SyncActivityTooManyDeletes extends Activity
+        implements AdapterView.OnItemClickListener {
+
+    private long mNumDeletes;
+    private Account mAccount;
+    private String mAuthority;
+    private String mProvider;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        Bundle extras = getIntent().getExtras();
+        if (extras == null) {
+            finish();
+            return;
+        }
+
+        mNumDeletes = extras.getLong("numDeletes");
+        mAccount = (Account) extras.getParcelable("account");
+        mAuthority = extras.getString("authority");
+        mProvider = extras.getString("provider");
+
+        // the order of these must match up with the constants for position used in onItemClick
+        CharSequence[] options = new CharSequence[]{
+                getResources().getText(R.string.sync_really_delete),
+                getResources().getText(R.string.sync_undo_deletes),
+                getResources().getText(R.string.sync_do_nothing)
+        };
+
+        ListAdapter adapter = new ArrayAdapter<CharSequence>(this,
+                android.R.layout.simple_list_item_1,
+                android.R.id.text1,
+                options);
+
+        ListView listView = new ListView(this);
+        listView.setAdapter(adapter);
+        listView.setItemsCanFocus(true);
+        listView.setOnItemClickListener(this);
+
+        TextView textView = new TextView(this);
+        CharSequence tooManyDeletesDescFormat =
+                getResources().getText(R.string.sync_too_many_deletes_desc);
+        textView.setText(String.format(tooManyDeletesDescFormat.toString(),
+                mNumDeletes, mProvider, mAccount.name));
+
+        final LinearLayout ll = new LinearLayout(this);
+        ll.setOrientation(LinearLayout.VERTICAL);
+        final LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
+                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT, 0);
+        ll.addView(textView, lp);
+        ll.addView(listView, lp);
+
+        // TODO: consider displaying the icon of the account type
+//        AuthenticatorDescription[] descs = AccountManager.get(this).getAuthenticatorTypes();
+//        for (AuthenticatorDescription desc : descs) {
+//            if (desc.type.equals(mAccount.type)) {
+//                try {
+//                    final Context authContext = createPackageContext(desc.packageName, 0);
+//                    ImageView imageView = new ImageView(this);
+//                    imageView.setImageDrawable(authContext.getDrawable(desc.iconId));
+//                    ll.addView(imageView, lp);
+//                } catch (PackageManager.NameNotFoundException e) {
+//                }
+//                break;
+//            }
+//        }
+
+        setContentView(ll);
+    }
+
+    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+        // the constants for position correspond to the items options array in onCreate()
+        if (position == 0) startSyncReallyDelete();
+        else if (position == 1) startSyncUndoDeletes();
+        finish();
+    }
+
+    private void startSyncReallyDelete() {
+        Bundle extras = new Bundle();
+        extras.putBoolean(ContentResolver.SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS, true);
+        extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
+        extras.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
+        extras.putBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, true);
+        ContentResolver.requestSync(mAccount, mAuthority, extras);
+    }
+
+    private void startSyncUndoDeletes() {
+        Bundle extras = new Bundle();
+        extras.putBoolean(ContentResolver.SYNC_EXTRAS_DISCARD_LOCAL_DELETIONS, true);
+        extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
+        extras.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
+        extras.putBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, true);
+        ContentResolver.requestSync(mAccount, mAuthority, extras);
+    }
+}
diff --git a/android/content/SyncAdapterType.java b/android/content/SyncAdapterType.java
new file mode 100644
index 0000000..7bcdbfd
--- /dev/null
+++ b/android/content/SyncAdapterType.java
@@ -0,0 +1,253 @@
+/*
+ * 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.content;
+
+import android.annotation.Nullable;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+/**
+ * Value type that represents a SyncAdapterType. This object overrides {@link #equals} and
+ * {@link #hashCode}, making it suitable for use as the key of a {@link java.util.Map}
+ */
+public class SyncAdapterType implements Parcelable {
+    public final String authority;
+    public final String accountType;
+    public final boolean isKey;
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    private final boolean userVisible;
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    private final boolean supportsUploading;
+    @UnsupportedAppUsage
+    private final boolean isAlwaysSyncable;
+    @UnsupportedAppUsage
+    private final boolean allowParallelSyncs;
+    @UnsupportedAppUsage
+    private final String settingsActivity;
+    private final String packageName;
+
+    public SyncAdapterType(String authority, String accountType, boolean userVisible,
+            boolean supportsUploading) {
+        if (TextUtils.isEmpty(authority)) {
+            throw new IllegalArgumentException("the authority must not be empty: " + authority);
+        }
+        if (TextUtils.isEmpty(accountType)) {
+            throw new IllegalArgumentException("the accountType must not be empty: " + accountType);
+        }
+        this.authority = authority;
+        this.accountType = accountType;
+        this.userVisible = userVisible;
+        this.supportsUploading = supportsUploading;
+        this.isAlwaysSyncable = false;
+        this.allowParallelSyncs = false;
+        this.settingsActivity = null;
+        this.isKey = false;
+        this.packageName = null;
+    }
+
+    /** @hide */
+    public SyncAdapterType(String authority, String accountType, boolean userVisible,
+            boolean supportsUploading,
+            boolean isAlwaysSyncable,
+            boolean allowParallelSyncs,
+            String settingsActivity,
+            String packageName) {
+        if (TextUtils.isEmpty(authority)) {
+            throw new IllegalArgumentException("the authority must not be empty: " + authority);
+        }
+        if (TextUtils.isEmpty(accountType)) {
+            throw new IllegalArgumentException("the accountType must not be empty: " + accountType);
+        }
+        this.authority = authority;
+        this.accountType = accountType;
+        this.userVisible = userVisible;
+        this.supportsUploading = supportsUploading;
+        this.isAlwaysSyncable = isAlwaysSyncable;
+        this.allowParallelSyncs = allowParallelSyncs;
+        this.settingsActivity = settingsActivity;
+        this.isKey = false;
+        this.packageName = packageName;
+    }
+
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    private SyncAdapterType(String authority, String accountType) {
+        if (TextUtils.isEmpty(authority)) {
+            throw new IllegalArgumentException("the authority must not be empty: " + authority);
+        }
+        if (TextUtils.isEmpty(accountType)) {
+            throw new IllegalArgumentException("the accountType must not be empty: " + accountType);
+        }
+        this.authority = authority;
+        this.accountType = accountType;
+        this.userVisible = true;
+        this.supportsUploading = true;
+        this.isAlwaysSyncable = false;
+        this.allowParallelSyncs = false;
+        this.settingsActivity = null;
+        this.isKey = true;
+        this.packageName = null;
+    }
+
+    public boolean supportsUploading() {
+        if (isKey) {
+            throw new IllegalStateException(
+                    "this method is not allowed to be called when this is a key");
+        }
+        return supportsUploading;
+    }
+
+    public boolean isUserVisible() {
+        if (isKey) {
+            throw new IllegalStateException(
+                    "this method is not allowed to be called when this is a key");
+        }
+        return userVisible;
+    }
+
+    /**
+     * @return True if this SyncAdapter supports syncing multiple accounts simultaneously.
+     * If false then the SyncManager will take care to only start one sync at a time
+     * using this SyncAdapter.
+     */
+    public boolean allowParallelSyncs() {
+        if (isKey) {
+            throw new IllegalStateException(
+                    "this method is not allowed to be called when this is a key");
+        }
+        return allowParallelSyncs;
+    }
+
+    /**
+     * If true then the SyncManager will never issue an initialization sync to the SyncAdapter
+     * and will instead automatically call
+     * {@link ContentResolver#setIsSyncable(android.accounts.Account, String, int)} with a
+     * value of 1 for each account and provider that this sync adapter supports.
+     * @return true if the SyncAdapter does not require initialization and if it is ok for the
+     * SyncAdapter to treat it as syncable automatically.
+     */
+    public boolean isAlwaysSyncable() {
+        if (isKey) {
+            throw new IllegalStateException(
+                    "this method is not allowed to be called when this is a key");
+        }
+        return isAlwaysSyncable;
+    }
+
+    /**
+     * @return The activity to use to invoke this SyncAdapter's settings activity.
+     * May be null.
+     */
+    public String getSettingsActivity() {
+        if (isKey) {
+            throw new IllegalStateException(
+                    "this method is not allowed to be called when this is a key");
+        }
+        return settingsActivity;
+    }
+
+    /**
+     * The package hosting the sync adapter.
+     * @return The package name.
+     *
+     * @hide
+     */
+    public @Nullable String getPackageName() {
+        return packageName;
+    }
+
+    public static SyncAdapterType newKey(String authority, String accountType) {
+        return new SyncAdapterType(authority, accountType);
+    }
+
+    public boolean equals(Object o) {
+        if (o == this) return true;
+        if (!(o instanceof SyncAdapterType)) return false;
+        final SyncAdapterType other = (SyncAdapterType)o;
+        // don't include userVisible or supportsUploading in the equality check
+        return authority.equals(other.authority) && accountType.equals(other.accountType);
+    }
+
+    public int hashCode() {
+        int result = 17;
+        result = 31 * result + authority.hashCode();
+        result = 31 * result + accountType.hashCode();
+        // don't include userVisible or supportsUploading  the hash
+        return result;
+    }
+
+    public String toString() {
+        if (isKey) {
+            return "SyncAdapterType Key {name=" + authority
+                    + ", type=" + accountType
+                    + "}";
+        } else {
+            return "SyncAdapterType {name=" + authority
+                    + ", type=" + accountType
+                    + ", userVisible=" + userVisible
+                    + ", supportsUploading=" + supportsUploading
+                    + ", isAlwaysSyncable=" + isAlwaysSyncable
+                    + ", allowParallelSyncs=" + allowParallelSyncs
+                    + ", settingsActivity=" + settingsActivity
+                    + ", packageName=" + packageName
+                    + "}";
+        }
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel dest, int flags) {
+        if (isKey) {
+            throw new IllegalStateException("keys aren't parcelable");
+        }
+
+        dest.writeString(authority);
+        dest.writeString(accountType);
+        dest.writeInt(userVisible ? 1 : 0);
+        dest.writeInt(supportsUploading ? 1 : 0);
+        dest.writeInt(isAlwaysSyncable ? 1 : 0);
+        dest.writeInt(allowParallelSyncs ? 1 : 0);
+        dest.writeString(settingsActivity);
+        dest.writeString(packageName);
+    }
+
+    public SyncAdapterType(Parcel source) {
+        this(
+                source.readString(),
+                source.readString(),
+                source.readInt() != 0,
+                source.readInt() != 0,
+                source.readInt() != 0,
+                source.readInt() != 0,
+                source.readString(),
+                source.readString());
+    }
+
+    public static final @android.annotation.NonNull Creator<SyncAdapterType> CREATOR = new Creator<SyncAdapterType>() {
+        public SyncAdapterType createFromParcel(Parcel source) {
+            return new SyncAdapterType(source);
+        }
+
+        public SyncAdapterType[] newArray(int size) {
+            return new SyncAdapterType[size];
+        }
+    };
+}
diff --git a/android/content/SyncAdaptersCache.java b/android/content/SyncAdaptersCache.java
new file mode 100644
index 0000000..58445a7
--- /dev/null
+++ b/android/content/SyncAdaptersCache.java
@@ -0,0 +1,157 @@
+/*
+ * 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.content;
+
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.pm.RegisteredServicesCache;
+import android.content.pm.XmlSerializerAndParser;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.AttributeSet;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * A cache of services that export the {@link android.content.ISyncAdapter} interface.
+ * @hide
+ */
+public class SyncAdaptersCache extends RegisteredServicesCache<SyncAdapterType> {
+    private static final String TAG = "Account";
+
+    private static final String SERVICE_INTERFACE = "android.content.SyncAdapter";
+    private static final String SERVICE_META_DATA = "android.content.SyncAdapter";
+    private static final String ATTRIBUTES_NAME = "sync-adapter";
+    private static final MySerializer sSerializer = new MySerializer();
+
+    @GuardedBy("mServicesLock")
+    private SparseArray<ArrayMap<String,String[]>> mAuthorityToSyncAdapters
+            = new SparseArray<>();
+
+    @UnsupportedAppUsage
+    public SyncAdaptersCache(Context context) {
+        super(context, SERVICE_INTERFACE, SERVICE_META_DATA, ATTRIBUTES_NAME, sSerializer);
+    }
+
+    public SyncAdapterType parseServiceAttributes(Resources res,
+            String packageName, AttributeSet attrs) {
+        TypedArray sa = res.obtainAttributes(attrs,
+                com.android.internal.R.styleable.SyncAdapter);
+        try {
+            final String authority =
+                    sa.getString(com.android.internal.R.styleable.SyncAdapter_contentAuthority);
+            final String accountType =
+                    sa.getString(com.android.internal.R.styleable.SyncAdapter_accountType);
+            if (TextUtils.isEmpty(authority) || TextUtils.isEmpty(accountType)) {
+                return null;
+            }
+            final boolean userVisible =
+                    sa.getBoolean(com.android.internal.R.styleable.SyncAdapter_userVisible, true);
+            final boolean supportsUploading =
+                    sa.getBoolean(com.android.internal.R.styleable.SyncAdapter_supportsUploading,
+                            true);
+            final boolean isAlwaysSyncable =
+                    sa.getBoolean(com.android.internal.R.styleable.SyncAdapter_isAlwaysSyncable,
+                            false);
+            final boolean allowParallelSyncs =
+                    sa.getBoolean(com.android.internal.R.styleable.SyncAdapter_allowParallelSyncs,
+                            false);
+            final String settingsActivity =
+                    sa.getString(com.android.internal.R.styleable
+                            .SyncAdapter_settingsActivity);
+            return new SyncAdapterType(authority, accountType, userVisible, supportsUploading,
+                    isAlwaysSyncable, allowParallelSyncs, settingsActivity, packageName);
+        } finally {
+            sa.recycle();
+        }
+    }
+
+    @Override
+    protected void onServicesChangedLocked(int userId) {
+        synchronized (mServicesLock) {
+            ArrayMap<String,String[]> adapterMap = mAuthorityToSyncAdapters.get(userId);
+            if (adapterMap != null) {
+                adapterMap.clear();
+            }
+        }
+
+        super.onServicesChangedLocked(userId);
+    }
+
+    public String[] getSyncAdapterPackagesForAuthority(String authority, int userId) {
+        synchronized (mServicesLock) {
+            ArrayMap<String,String[]> adapterMap = mAuthorityToSyncAdapters.get(userId);
+            if (adapterMap == null) {
+                adapterMap = new ArrayMap<>();
+                mAuthorityToSyncAdapters.put(userId, adapterMap);
+            }
+            // If the mapping exists, return it
+            if (adapterMap.containsKey(authority)) {
+                return adapterMap.get(authority);
+            }
+            // Create the mapping and cache it
+            String[] syncAdapterPackages;
+            final Collection<RegisteredServicesCache.ServiceInfo<SyncAdapterType>> serviceInfos;
+            serviceInfos = getAllServices(userId);
+            ArrayList<String> packages = new ArrayList<>();
+            for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> serviceInfo : serviceInfos) {
+                if (authority.equals(serviceInfo.type.authority)
+                        && serviceInfo.componentName != null) {
+                    packages.add(serviceInfo.componentName.getPackageName());
+                }
+            }
+            syncAdapterPackages = new String[packages.size()];
+            packages.toArray(syncAdapterPackages);
+            adapterMap.put(authority, syncAdapterPackages);
+
+            return syncAdapterPackages;
+        }
+    }
+
+    @Override
+    protected void onUserRemoved(int userId) {
+        synchronized (mServicesLock) {
+            mAuthorityToSyncAdapters.remove(userId);
+        }
+
+        super.onUserRemoved(userId);
+    }
+
+    static class MySerializer implements XmlSerializerAndParser<SyncAdapterType> {
+        public void writeAsXml(SyncAdapterType item, XmlSerializer out) throws IOException {
+            out.attribute(null, "authority", item.authority);
+            out.attribute(null, "accountType", item.accountType);
+        }
+
+        public SyncAdapterType createFromXml(XmlPullParser parser)
+                throws IOException, XmlPullParserException {
+            final String authority = parser.getAttributeValue(null, "authority");
+            final String accountType = parser.getAttributeValue(null, "accountType");
+            return SyncAdapterType.newKey(authority, accountType);
+        }
+    }
+}
diff --git a/android/content/SyncContext.java b/android/content/SyncContext.java
new file mode 100644
index 0000000..4a9f66c
--- /dev/null
+++ b/android/content/SyncContext.java
@@ -0,0 +1,83 @@
+/*
+ * 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.content;
+
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.SystemClock;
+
+public class SyncContext {
+    private ISyncContext mSyncContext;
+    private long mLastHeartbeatSendTime;
+
+    private static final long HEARTBEAT_SEND_INTERVAL_IN_MS = 1000;
+
+    /**
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public SyncContext(ISyncContext syncContextInterface) {
+        mSyncContext = syncContextInterface;
+        mLastHeartbeatSendTime = 0;
+    }
+
+    /**
+     * Call to update the status text for this sync. This internally invokes
+     * {@link #updateHeartbeat}, so it also takes the place of a call to that.
+     *
+     * @param message the current status message for this sync
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public void setStatusText(String message) {
+        updateHeartbeat();
+    }
+
+    /**
+     * Call to indicate that the SyncAdapter is making progress. E.g., if this SyncAdapter
+     * downloads or sends records to/from the server, this may be called after each record
+     * is downloaded or uploaded.
+     */
+    private void updateHeartbeat() {
+        final long now = SystemClock.elapsedRealtime();
+        if (now < mLastHeartbeatSendTime + HEARTBEAT_SEND_INTERVAL_IN_MS) return;
+        try {
+            mLastHeartbeatSendTime = now;
+            if (mSyncContext != null) {
+                mSyncContext.sendHeartbeat();
+            }
+        } catch (RemoteException e) {
+            // this should never happen
+        }
+    }
+
+    public void onFinished(SyncResult result) {
+        try {
+            if (mSyncContext != null) {
+                mSyncContext.onFinished(result);
+            }
+        } catch (RemoteException e) {
+            // this should never happen
+        }
+    }
+
+    public IBinder getSyncContextBinder() {
+        return (mSyncContext == null) ? null : mSyncContext.asBinder();
+    }
+}
diff --git a/android/content/SyncInfo.java b/android/content/SyncInfo.java
new file mode 100644
index 0000000..017a92b
--- /dev/null
+++ b/android/content/SyncInfo.java
@@ -0,0 +1,118 @@
+/*
+ * 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.content;
+
+import android.accounts.Account;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Information about the sync operation that is currently underway.
+ */
+public class SyncInfo implements Parcelable {
+    /**
+     * Used when the caller receiving this object doesn't have permission to access the accounts
+     * on device.
+     * @See Manifest.permission.GET_ACCOUNTS
+     */
+    private static final Account REDACTED_ACCOUNT = new Account("*****", "*****");
+
+    /** @hide */
+    @UnsupportedAppUsage
+    public final int authorityId;
+
+    /**
+     * The {@link Account} that is currently being synced.
+     */
+    public final Account account;
+
+    /**
+     * The authority of the provider that is currently being synced.
+     */
+    public final String authority;
+
+    /**
+     * The start time of the current sync operation in milliseconds since boot.
+     * This is represented in elapsed real time.
+     * See {@link android.os.SystemClock#elapsedRealtime()}.
+     */
+    public final long startTime;
+
+    /**
+     * Creates a SyncInfo object with an unusable Account. Used when the caller receiving this
+     * object doesn't have access to the accounts on the device.
+     * @See Manifest.permission.GET_ACCOUNTS
+     * @hide
+     */
+    public static SyncInfo createAccountRedacted(
+        int authorityId, String authority, long startTime) {
+            return new SyncInfo(authorityId, REDACTED_ACCOUNT, authority, startTime);
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage
+    public SyncInfo(int authorityId, Account account, String authority, long startTime) {
+        this.authorityId = authorityId;
+        this.account = account;
+        this.authority = authority;
+        this.startTime = startTime;
+    }
+
+    /** @hide */
+    public SyncInfo(SyncInfo other) {
+        this.authorityId = other.authorityId;
+        this.account = new Account(other.account.name, other.account.type);
+        this.authority = other.authority;
+        this.startTime = other.startTime;
+    }
+
+    /** @hide */
+    public int describeContents() {
+        return 0;
+    }
+
+    /** @hide */
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeInt(authorityId);
+        parcel.writeParcelable(account, flags);
+        parcel.writeString(authority);
+        parcel.writeLong(startTime);
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    SyncInfo(Parcel parcel) {
+        authorityId = parcel.readInt();
+        account = parcel.readParcelable(Account.class.getClassLoader());
+        authority = parcel.readString();
+        startTime = parcel.readLong();
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage
+    public static final @android.annotation.NonNull Creator<SyncInfo> CREATOR = new Creator<SyncInfo>() {
+        public SyncInfo createFromParcel(Parcel in) {
+            return new SyncInfo(in);
+        }
+
+        public SyncInfo[] newArray(int size) {
+            return new SyncInfo[size];
+        }
+    };
+}
diff --git a/android/content/SyncRequest.java b/android/content/SyncRequest.java
new file mode 100644
index 0000000..9e568a4
--- /dev/null
+++ b/android/content/SyncRequest.java
@@ -0,0 +1,554 @@
+/*
+ * 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.content;
+
+import android.accounts.Account;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Convenience class to construct sync requests. See {@link android.content.SyncRequest.Builder}
+ * for an explanation of the various functions. The resulting object is passed through to the
+ * framework via {@link android.content.ContentResolver#requestSync(SyncRequest)}.
+ */
+public class SyncRequest implements Parcelable {
+    private static final String TAG = "SyncRequest";
+    /** Account to pass to the sync adapter. Can be null. */
+    @UnsupportedAppUsage
+    private final Account mAccountToSync;
+    /** Authority string that corresponds to a ContentProvider. */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    private final String mAuthority;
+    /** Bundle containing user info as well as sync settings. */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    private final Bundle mExtras;
+    /** Don't allow this sync request on metered networks. */
+    private final boolean mDisallowMetered;
+    /**
+     * Amount of time before {@link #mSyncRunTimeSecs} from which the sync may optionally be
+     * started.
+     */
+    private final long mSyncFlexTimeSecs;
+    /**
+     * Specifies a point in the future at which the sync must have been scheduled to run.
+     */
+    @UnsupportedAppUsage
+    private final long mSyncRunTimeSecs;
+    /** Periodic versus one-off. */
+    @UnsupportedAppUsage
+    private final boolean mIsPeriodic;
+    /** Service versus provider. */
+    private final boolean mIsAuthority;
+    /** Sync should be run in lieu of other syncs. */
+    private final boolean mIsExpedited;
+
+    /**
+     * {@hide}
+     * @return whether this sync is periodic or one-time. A Sync Request must be
+     *         either one of these or an InvalidStateException will be thrown in
+     *         Builder.build().
+     */
+    public boolean isPeriodic() {
+        return mIsPeriodic;
+    }
+
+    /**
+     * {@hide}
+     * @return whether this sync is expedited.
+     */
+    public boolean isExpedited() {
+        return mIsExpedited;
+    }
+
+    /**
+     * {@hide}
+     *
+     * @return account object for this sync.
+     * @throws IllegalArgumentException if this function is called for a request that targets a
+     * sync service.
+     */
+    public Account getAccount() {
+        return mAccountToSync;
+    }
+
+    /**
+     * {@hide}
+     *
+     * @return provider for this sync.
+     * @throws IllegalArgumentException if this function is called for a request that targets a
+     * sync service.
+     */
+    public String getProvider() {
+        return mAuthority;
+    }
+
+    /**
+     * {@hide}
+     * Retrieve bundle for this SyncRequest. Will not be null.
+     */
+    public Bundle getBundle() {
+        return mExtras;
+    }
+
+    /**
+     * {@hide}
+     * @return the earliest point in time that this sync can be scheduled.
+     */
+    public long getSyncFlexTime() {
+        return mSyncFlexTimeSecs;
+    }
+    /**
+     * {@hide}
+     * @return the last point in time at which this sync must scheduled.
+     */
+    public long getSyncRunTime() {
+        return mSyncRunTimeSecs;
+    }
+
+    public static final @android.annotation.NonNull Creator<SyncRequest> CREATOR = new Creator<SyncRequest>() {
+
+        @Override
+        public SyncRequest createFromParcel(Parcel in) {
+            return new SyncRequest(in);
+        }
+
+        @Override
+        public SyncRequest[] newArray(int size) {
+            return new SyncRequest[size];
+        }
+    };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeBundle(mExtras);
+        parcel.writeLong(mSyncFlexTimeSecs);
+        parcel.writeLong(mSyncRunTimeSecs);
+        parcel.writeInt((mIsPeriodic ? 1 : 0));
+        parcel.writeInt((mDisallowMetered ? 1 : 0));
+        parcel.writeInt((mIsAuthority ? 1 : 0));
+        parcel.writeInt((mIsExpedited? 1 : 0));
+        parcel.writeParcelable(mAccountToSync, flags);
+        parcel.writeString(mAuthority);
+    }
+
+    private SyncRequest(Parcel in) {
+        mExtras = Bundle.setDefusable(in.readBundle(), true);
+        mSyncFlexTimeSecs = in.readLong();
+        mSyncRunTimeSecs = in.readLong();
+        mIsPeriodic = (in.readInt() != 0);
+        mDisallowMetered = (in.readInt() != 0);
+        mIsAuthority = (in.readInt() != 0);
+        mIsExpedited = (in.readInt() != 0);
+        mAccountToSync = in.readParcelable(null);
+        mAuthority = in.readString();
+    }
+
+    /** {@hide} Protected ctor to instantiate anonymous SyncRequest. */
+    protected SyncRequest(SyncRequest.Builder b) {
+        mSyncFlexTimeSecs = b.mSyncFlexTimeSecs;
+        mSyncRunTimeSecs = b.mSyncRunTimeSecs;
+        mAccountToSync = b.mAccount;
+        mAuthority = b.mAuthority;
+        mIsPeriodic = (b.mSyncType == Builder.SYNC_TYPE_PERIODIC);
+        mIsAuthority = (b.mSyncTarget == Builder.SYNC_TARGET_ADAPTER);
+        mIsExpedited = b.mExpedited;
+        mExtras = new Bundle(b.mCustomExtras);
+        // For now we merge the sync config extras & the custom extras into one bundle.
+        // TODO: pass the configuration extras through separately.
+        mExtras.putAll(b.mSyncConfigExtras);
+        mDisallowMetered = b.mDisallowMetered;
+    }
+
+    /**
+     * Builder class for a {@link SyncRequest}. As you build your SyncRequest this class will also
+     * perform validation.
+     */
+    public static class Builder {
+        /** Unknown sync type. */
+        private static final int SYNC_TYPE_UNKNOWN = 0;
+        /** Specify that this is a periodic sync. */
+        private static final int SYNC_TYPE_PERIODIC = 1;
+        /** Specify that this is a one-time sync. */
+        private static final int SYNC_TYPE_ONCE = 2;
+        /** Unknown sync target. */
+        private static final int SYNC_TARGET_UNKNOWN = 0;
+        /** Specify that this is a sync with a provider. */
+        private static final int SYNC_TARGET_ADAPTER = 2;
+        /**
+         * Earliest point of displacement into the future at which this sync can
+         * occur.
+         */
+        private long mSyncFlexTimeSecs;
+        /** Displacement into the future at which this sync must occur. */
+        private long mSyncRunTimeSecs;
+        /**
+         * Sync configuration information - custom user data explicitly provided by the developer.
+         * This data is handed over to the sync operation.
+         */
+        private Bundle mCustomExtras;
+        /**
+         * Sync system configuration -  used to store system sync configuration. Corresponds to
+         * ContentResolver.SYNC_EXTRAS_* flags.
+         * TODO: Use this instead of dumping into one bundle. Need to decide if these flags should
+         * discriminate between equivalent syncs.
+         */
+        private Bundle mSyncConfigExtras;
+        /** Whether or not this sync can occur on metered networks. Default false. */
+        private boolean mDisallowMetered;
+        /**
+         * Whether this builder is building a periodic sync, or a one-time sync.
+         */
+        private int mSyncType = SYNC_TYPE_UNKNOWN;
+        /** Whether this will go to a sync adapter. */
+        private int mSyncTarget = SYNC_TARGET_UNKNOWN;
+        /** Whether this is a user-activated sync. */
+        private boolean mIsManual;
+        /**
+         * Whether to retry this one-time sync if the sync fails. Not valid for
+         * periodic syncs. See {@link ContentResolver#SYNC_EXTRAS_DO_NOT_RETRY}.
+         */
+        private boolean mNoRetry;
+        /**
+         * Whether to respect back-off for this one-time sync. Not valid for
+         * periodic syncs. See
+         * {@link ContentResolver#SYNC_EXTRAS_IGNORE_BACKOFF};
+         */
+        private boolean mIgnoreBackoff;
+
+        /** Ignore sync system settings and perform sync anyway. */
+        private boolean mIgnoreSettings;
+
+        /** This sync will run in preference to other non-expedited syncs. */
+        private boolean mExpedited;
+
+        /**
+         * The Account object that together with an Authority name define the SyncAdapter (if
+         * this sync is bound to a provider), otherwise null.
+         */
+        private Account mAccount;
+        /**
+         * The Authority name that together with an Account define the SyncAdapter (if
+         * this sync is bound to a provider), otherwise null.
+         */
+        private String mAuthority;
+        /**
+         * Whether the sync requires the phone to be plugged in.
+         */
+        private boolean mRequiresCharging;
+
+        public Builder() {
+        }
+
+        /**
+         * Request that a sync occur immediately.
+         *
+         * Example
+         * <pre>
+         *     SyncRequest.Builder builder = (new SyncRequest.Builder()).syncOnce();
+         * </pre>
+         */
+        public Builder syncOnce() {
+            if (mSyncType != SYNC_TYPE_UNKNOWN) {
+                throw new IllegalArgumentException("Sync type has already been defined.");
+            }
+            mSyncType = SYNC_TYPE_ONCE;
+            setupInterval(0, 0);
+            return this;
+        }
+
+        /**
+         * Build a periodic sync. Either this or syncOnce() <b>must</b> be called for this builder.
+         * Syncs are identified by target {@link android.provider} and by the
+         * contents of the extras bundle.
+         * You cannot reuse the same builder for one-time syncs after having specified a periodic
+         * sync (by calling this function). If you do, an <code>IllegalArgumentException</code>
+         * will be thrown.
+         * <p>The bundle for a periodic sync can be queried by applications with the correct
+         * permissions using
+         * {@link ContentResolver#getPeriodicSyncs(Account account, String provider)}, so no
+         * sensitive data should be transferred here.
+         *
+         * Example usage.
+         *
+         * <pre>
+         *     Request a periodic sync every 5 hours with 20 minutes of flex.
+         *     SyncRequest.Builder builder =
+         *         (new SyncRequest.Builder()).syncPeriodic(5 * HOUR_IN_SECS, 20 * MIN_IN_SECS);
+         *
+         *     Schedule a periodic sync every hour at any point in time during that hour.
+         *     SyncRequest.Builder builder =
+         *         (new SyncRequest.Builder()).syncPeriodic(1 * HOUR_IN_SECS, 1 * HOUR_IN_SECS);
+         * </pre>
+         *
+         * N.B.: Periodic syncs are not allowed to have any of
+         * {@link ContentResolver#SYNC_EXTRAS_DO_NOT_RETRY},
+         * {@link ContentResolver#SYNC_EXTRAS_IGNORE_BACKOFF},
+         * {@link ContentResolver#SYNC_EXTRAS_IGNORE_SETTINGS},
+         * {@link ContentResolver#SYNC_EXTRAS_INITIALIZE},
+         * {@link ContentResolver#SYNC_EXTRAS_FORCE},
+         * {@link ContentResolver#SYNC_EXTRAS_EXPEDITED},
+         * {@link ContentResolver#SYNC_EXTRAS_MANUAL}
+         * set to true. If any are supplied then an <code>IllegalArgumentException</code> will
+         * be thrown.
+         *
+         * @param pollFrequency the amount of time in seconds that you wish
+         *            to elapse between periodic syncs. A minimum period of 1 hour is enforced.
+         * @param beforeSeconds the amount of flex time in seconds before
+         *            {@code pollFrequency} that you permit for the sync to take
+         *            place. Must be less than {@code pollFrequency} and greater than
+         *            MAX(5% of {@code pollFrequency}, 5 minutes)
+         */
+        public Builder syncPeriodic(long pollFrequency, long beforeSeconds) {
+            if (mSyncType != SYNC_TYPE_UNKNOWN) {
+                throw new IllegalArgumentException("Sync type has already been defined.");
+            }
+            mSyncType = SYNC_TYPE_PERIODIC;
+            setupInterval(pollFrequency, beforeSeconds);
+            return this;
+        }
+
+        private void setupInterval(long at, long before) {
+            if (before > at) {
+                throw new IllegalArgumentException("Specified run time for the sync must be" +
+                    " after the specified flex time.");
+            }
+            mSyncRunTimeSecs = at;
+            mSyncFlexTimeSecs = before;
+        }
+
+        /**
+         * Will throw an <code>IllegalArgumentException</code> if called and
+         * {@link #setIgnoreSettings(boolean ignoreSettings)} has already been called.
+         * @param disallow true to allow this transfer on metered networks. Default false.
+         *
+         */
+        public Builder setDisallowMetered(boolean disallow) {
+            if (mIgnoreSettings && disallow) {
+                throw new IllegalArgumentException("setDisallowMetered(true) after having"
+                        + " specified that settings are ignored.");
+            }
+            mDisallowMetered = disallow;
+            return this;
+        }
+
+        /**
+         * Specify whether the sync requires the phone to be plugged in.
+         * @param requiresCharging true if sync requires the phone to be plugged in. Default false.
+         */
+        public Builder setRequiresCharging(boolean requiresCharging) {
+            mRequiresCharging = requiresCharging;
+            return this;
+        }
+
+        /**
+         * Specify an authority and account for this transfer.
+         *
+         * @param authority A String identifying the content provider to be synced.
+         * @param account Account to sync. Can be null unless this is a periodic
+         *            sync, for which verification by the ContentResolver will
+         *            fail. If a sync is performed without an account, the
+         */
+        public Builder setSyncAdapter(Account account, String authority) {
+            if (mSyncTarget != SYNC_TARGET_UNKNOWN) {
+                throw new IllegalArgumentException("Sync target has already been defined.");
+            }
+            if (authority != null && authority.length() == 0) {
+                throw new IllegalArgumentException("Authority must be non-empty");
+            }
+            mSyncTarget = SYNC_TARGET_ADAPTER;
+            mAccount = account;
+            mAuthority = authority;
+            return this;
+        }
+
+        /**
+         * Developer-provided extras handed back when sync actually occurs. This bundle is copied
+         * into the SyncRequest returned by {@link #build()}.
+         *
+         * Example:
+         * <pre>
+         *   String[] syncItems = {"dog", "cat", "frog", "child"};
+         *   SyncRequest.Builder builder =
+         *     new SyncRequest.Builder()
+         *       .setSyncAdapter(dummyAccount, dummyProvider)
+         *       .syncOnce();
+         *
+         *   for (String syncData : syncItems) {
+         *     Bundle extras = new Bundle();
+         *     extras.setString("data", syncData);
+         *     builder.setExtras(extras);
+         *     ContentResolver.sync(builder.build()); // Each sync() request creates a unique sync.
+         *   }
+         * </pre>
+         * Only values of the following types may be used in the extras bundle:
+         * <ul>
+         * <li>Integer</li>
+         * <li>Long</li>
+         * <li>Boolean</li>
+         * <li>Float</li>
+         * <li>Double</li>
+         * <li>String</li>
+         * <li>Account</li>
+         * <li>null</li>
+         * </ul>
+         * If any data is present in the bundle not of this type, build() will
+         * throw a runtime exception.
+         *
+         * @param bundle extras bundle to set.
+         */
+        public Builder setExtras(Bundle bundle) {
+            mCustomExtras = bundle;
+            return this;
+        }
+
+        /**
+         * Convenience function for setting {@link ContentResolver#SYNC_EXTRAS_DO_NOT_RETRY}.
+         *
+         * A one-off sync operation that fails will be retried with exponential back-off unless
+         * this is set to false. Not valid for periodic sync and will throw an
+         * <code>IllegalArgumentException</code> in build().
+         *
+         * @param noRetry true to not retry a failed sync. Default false.
+         */
+        public Builder setNoRetry(boolean noRetry) {
+            mNoRetry = noRetry;
+            return this;
+        }
+
+        /**
+         * Convenience function for setting {@link ContentResolver#SYNC_EXTRAS_IGNORE_SETTINGS}.
+         *
+         * Not valid for periodic sync and will throw an <code>IllegalArgumentException</code> in
+         * {@link #build()}.
+         * <p>Throws <code>IllegalArgumentException</code> if called and
+         * {@link #setDisallowMetered(boolean)} has been set.
+         * 
+         *
+         * @param ignoreSettings true to ignore the sync automatically settings. Default false.
+         */
+        public Builder setIgnoreSettings(boolean ignoreSettings) {
+            if (mDisallowMetered && ignoreSettings) {
+                throw new IllegalArgumentException("setIgnoreSettings(true) after having specified"
+                        + " sync settings with this builder.");
+            }
+            mIgnoreSettings = ignoreSettings;
+            return this;
+        }
+
+        /**
+         * Convenience function for setting {@link ContentResolver#SYNC_EXTRAS_IGNORE_BACKOFF}.
+         *
+         * Ignoring back-off will force the sync scheduling process to ignore any back-off that was
+         * the result of a failed sync, as well as to invalidate any {@link SyncResult#delayUntil}
+         * value that may have been set by the adapter. Successive failures will not honor this
+         * flag. Not valid for periodic sync and will throw an <code>IllegalArgumentException</code>
+         * in {@link #build()}.
+         *
+         * @param ignoreBackoff ignore back off settings. Default false.
+         */
+        public Builder setIgnoreBackoff(boolean ignoreBackoff) {
+            mIgnoreBackoff = ignoreBackoff;
+            return this;
+        }
+
+        /**
+         * Convenience function for setting {@link ContentResolver#SYNC_EXTRAS_MANUAL}.
+         *
+         * Not valid for periodic sync and will throw an <code>IllegalArgumentException</code> in
+         * {@link #build()}.
+         *
+         * @param isManual User-initiated sync or not. Default false.
+         */
+        public Builder setManual(boolean isManual) {
+            mIsManual = isManual;
+            return this;
+        }
+
+        /**
+         * An expedited sync runs immediately and can preempt other non-expedited running syncs.
+         *
+         * Not valid for periodic sync and will throw an <code>IllegalArgumentException</code> in
+         * {@link #build()}.
+         *
+         * @param expedited whether to run expedited. Default false.
+         */
+        public Builder setExpedited(boolean expedited) {
+            mExpedited = expedited;
+            return this;
+        }
+
+        /**
+         * Performs validation over the request and throws the runtime exception
+         * <code>IllegalArgumentException</code> if this validation fails.
+         *
+         * @return a SyncRequest with the information contained within this
+         *         builder.
+         */
+        public SyncRequest build() {
+            // Validate the extras bundle
+            ContentResolver.validateSyncExtrasBundle(mCustomExtras);
+            if (mCustomExtras == null) {
+                mCustomExtras = new Bundle();
+            }
+            // Combine builder extra flags into the config bundle.
+            mSyncConfigExtras = new Bundle();
+            if (mIgnoreBackoff) {
+                mSyncConfigExtras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, true);
+            }
+            if (mDisallowMetered) {
+                mSyncConfigExtras.putBoolean(ContentResolver.SYNC_EXTRAS_DISALLOW_METERED, true);
+            }
+            if (mRequiresCharging) {
+                mSyncConfigExtras.putBoolean(ContentResolver.SYNC_EXTRAS_REQUIRE_CHARGING, true);
+            }
+            if (mIgnoreSettings) {
+                mSyncConfigExtras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, true);
+            }
+            if (mNoRetry) {
+                mSyncConfigExtras.putBoolean(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY, true);
+            }
+            if (mExpedited) {
+                mSyncConfigExtras.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
+            }
+            if (mIsManual) {
+                mSyncConfigExtras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, true);
+                mSyncConfigExtras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, true);
+            }
+            if (mSyncType == SYNC_TYPE_PERIODIC) {
+                // If this is a periodic sync ensure than invalid extras were not set.
+                if (ContentResolver.invalidPeriodicExtras(mCustomExtras) ||
+                        ContentResolver.invalidPeriodicExtras(mSyncConfigExtras)) {
+                    throw new IllegalArgumentException("Illegal extras were set");
+                }
+            }
+            // Ensure that a target for the sync has been set.
+            if (mSyncTarget == SYNC_TARGET_UNKNOWN) {
+                throw new IllegalArgumentException("Must specify an adapter with" +
+                        " setSyncAdapter(Account, String");
+            }
+            return new SyncRequest(this);
+        }
+    }
+}
diff --git a/android/content/SyncResult.java b/android/content/SyncResult.java
new file mode 100644
index 0000000..8280f8e
--- /dev/null
+++ b/android/content/SyncResult.java
@@ -0,0 +1,335 @@
+/*
+ * 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.content;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * This class is used to communicate the results of a sync operation to the SyncManager.
+ * Based on the values here the SyncManager will determine the disposition of the
+ * sync and whether or not a new sync operation needs to be scheduled in the future.
+ *
+ */
+public final class SyncResult implements Parcelable {
+    /**
+     * Used to indicate that the SyncAdapter is already performing a sync operation, though
+     * not necessarily for the requested account and authority and that it wasn't able to
+     * process this request. The SyncManager will reschedule the request to run later.
+     */
+    public final boolean syncAlreadyInProgress;
+
+    /**
+     * Used to indicate that the SyncAdapter determined that it would need to issue
+     * too many delete operations to the server in order to satisfy the request
+     * (as defined by the SyncAdapter). The SyncManager will record
+     * that the sync request failed and will cause a System Notification to be created
+     * asking the user what they want to do about this. It will give the user a chance to
+     * choose between (1) go ahead even with those deletes, (2) revert the deletes,
+     * or (3) take no action. If the user decides (1) or (2) the SyncManager will issue another
+     * sync request with either {@link ContentResolver#SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS}
+     * or {@link ContentResolver#SYNC_EXTRAS_DISCARD_LOCAL_DELETIONS} set in the extras.
+     * It is then up to the SyncAdapter to decide how to honor that request.
+     */
+    public boolean tooManyDeletions;
+
+    /**
+     * Used to indicate that the SyncAdapter experienced a hard error due to trying the same
+     * operation too many times (as defined by the SyncAdapter). The SyncManager will record
+     * that the sync request failed and it will not reschedule the request.
+     */
+    public boolean tooManyRetries;
+
+    /**
+     * Used to indicate that the SyncAdapter experienced a hard error due to an error it
+     * received from interacting with the storage layer. The SyncManager will record that
+     * the sync request failed and it will not reschedule the request.
+     */
+    public boolean databaseError;
+
+    /**
+     * If set the SyncManager will request an immediate sync with the same Account and authority
+     * (but empty extras Bundle) as was used in the sync request.
+     */
+    public boolean fullSyncRequested;
+
+    /**
+     * This field is ignored by the SyncManager.
+     */
+    public boolean partialSyncUnavailable;
+
+    /**
+     * This field is ignored by the SyncManager.
+     */
+    public boolean moreRecordsToGet;
+
+    /**
+     * Used to indicate to the SyncManager that future sync requests that match the request's
+     * Account and authority should be delayed until a time in seconds since Java epoch.
+     *
+     * <p>For example, if you want to delay the next sync for at least 5 minutes, then:
+     * <pre>
+     * result.delayUntil = (System.currentTimeMillis() / 1000) + 5 * 60;
+     * </pre>
+     *
+     * <p>By default, when a sync fails, the system retries later with an exponential back-off
+     * with the system default initial delay time, which always wins over {@link #delayUntil} --
+     * i.e. if the system back-off time is larger than {@link #delayUntil}, {@link #delayUntil}
+     * will essentially be ignored.
+     */
+    public long delayUntil;
+
+    /**
+     * Used to hold extras statistics about the sync operation. Some of these indicate that
+     * the sync request resulted in a hard or soft error, others are for purely informational
+     * purposes.
+     */
+    public final SyncStats stats;
+
+    /**
+     * This instance of a SyncResult is returned by the SyncAdapter in response to a
+     * sync request when a sync is already underway. The SyncManager will reschedule the
+     * sync request to try again later.
+     */
+    public static final SyncResult ALREADY_IN_PROGRESS;
+
+    static {
+        ALREADY_IN_PROGRESS = new SyncResult(true);
+    }
+
+    /**
+     * Create a "clean" SyncResult. If this is returned without any changes then the
+     * SyncManager will consider the sync to have completed successfully. The various fields
+     * can be set by the SyncAdapter in order to give the SyncManager more information as to
+     * the disposition of the sync.
+     * <p>
+     * The errors are classified into two broad categories: hard errors and soft errors.
+     * Soft errors are retried with exponential backoff. Hard errors are not retried (except
+     * when the hard error is for a {@link ContentResolver#SYNC_EXTRAS_UPLOAD} request,
+     * in which the request is retryed without the {@link ContentResolver#SYNC_EXTRAS_UPLOAD}
+     * extra set). The SyncManager checks the type of error by calling
+     * {@link SyncResult#hasHardError()} and  {@link SyncResult#hasSoftError()}. If both are
+     * true then the SyncManager treats it as a hard error, not a soft error.
+     */
+    public SyncResult() {
+        this(false);
+    }
+
+    /**
+     * Internal helper for creating a clean SyncResult or one that indicated that
+     * a sync is already in progress.
+     * @param syncAlreadyInProgress if true then set the {@link #syncAlreadyInProgress} flag
+     */
+    private SyncResult(boolean syncAlreadyInProgress) {
+        this.syncAlreadyInProgress = syncAlreadyInProgress;
+        this.tooManyDeletions = false;
+        this.tooManyRetries = false;
+        this.fullSyncRequested = false;
+        this.partialSyncUnavailable = false;
+        this.moreRecordsToGet = false;
+        this.delayUntil = 0;
+        this.stats = new SyncStats();
+    }
+
+    private SyncResult(Parcel parcel) {
+        syncAlreadyInProgress = parcel.readInt() != 0;
+        tooManyDeletions = parcel.readInt() != 0;
+        tooManyRetries = parcel.readInt() != 0;
+        databaseError = parcel.readInt() != 0;
+        fullSyncRequested = parcel.readInt() != 0;
+        partialSyncUnavailable = parcel.readInt() != 0;
+        moreRecordsToGet = parcel.readInt() != 0;
+        delayUntil = parcel.readLong();
+        stats = new SyncStats(parcel);
+    }
+
+    /**
+     * Convenience method for determining if the SyncResult indicates that a hard error
+     * occurred. See {@link #SyncResult()} for an explanation of what the SyncManager does
+     * when it sees a hard error.
+     * <p>
+     * A hard error is indicated when any of the following is true:
+     * <ul>
+     * <li> {@link SyncStats#numParseExceptions} > 0
+     * <li> {@link SyncStats#numConflictDetectedExceptions} > 0
+     * <li> {@link SyncStats#numAuthExceptions} > 0
+     * <li> {@link #tooManyDeletions}
+     * <li> {@link #tooManyRetries}
+     * <li> {@link #databaseError}
+     * @return true if a hard error is indicated
+     */
+    public boolean hasHardError() {
+        return stats.numParseExceptions > 0
+                || stats.numConflictDetectedExceptions > 0
+                || stats.numAuthExceptions > 0
+                || tooManyDeletions
+                || tooManyRetries
+                || databaseError;
+    }
+
+    /**
+     * Convenience method for determining if the SyncResult indicates that a soft error
+     * occurred. See {@link #SyncResult()} for an explanation of what the SyncManager does
+     * when it sees a soft error.
+     * <p>
+     * A soft error is indicated when any of the following is true:
+     * <ul>
+     * <li> {@link SyncStats#numIoExceptions} > 0
+     * <li> {@link #syncAlreadyInProgress}
+     * </ul>
+     * @return true if a soft error is indicated
+     */
+    public boolean hasSoftError() {
+        return syncAlreadyInProgress || stats.numIoExceptions > 0;
+    }
+
+    /**
+     * A convenience method for determining of the SyncResult indicates that an error occurred.
+     * @return true if either a soft or hard error occurred
+     */
+    public boolean hasError() {
+        return hasSoftError() || hasHardError();
+    }
+
+    /**
+     * Convenience method for determining if the Sync should be rescheduled after failing for some
+     * reason.
+     * @return true if the SyncManager should reschedule this sync.
+     */
+    public boolean madeSomeProgress() {
+        return ((stats.numDeletes > 0) && !tooManyDeletions)
+                || stats.numInserts > 0
+                || stats.numUpdates > 0;
+    }
+
+    /**
+     * Clears the SyncResult to a clean state. Throws an {@link UnsupportedOperationException}
+     * if this is called when {@link #syncAlreadyInProgress} is set.
+     */
+    public void clear() {
+        if (syncAlreadyInProgress) {
+            throw new UnsupportedOperationException(
+                    "you are not allowed to clear the ALREADY_IN_PROGRESS SyncStats");
+        }
+        tooManyDeletions = false;
+        tooManyRetries = false;
+        databaseError = false;
+        fullSyncRequested = false;
+        partialSyncUnavailable = false;
+        moreRecordsToGet = false;
+        delayUntil = 0;
+        stats.clear();
+    }
+
+    public static final @android.annotation.NonNull Creator<SyncResult> CREATOR = new Creator<SyncResult>() {
+        public SyncResult createFromParcel(Parcel in) {
+            return new SyncResult(in);
+        }
+
+        public SyncResult[] newArray(int size) {
+            return new SyncResult[size];
+        }
+    };
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeInt(syncAlreadyInProgress ? 1 : 0);
+        parcel.writeInt(tooManyDeletions ? 1 : 0);
+        parcel.writeInt(tooManyRetries ? 1 : 0);
+        parcel.writeInt(databaseError ? 1 : 0);
+        parcel.writeInt(fullSyncRequested ? 1 : 0);
+        parcel.writeInt(partialSyncUnavailable ? 1 : 0);
+        parcel.writeInt(moreRecordsToGet ? 1 : 0);
+        parcel.writeLong(delayUntil);
+        stats.writeToParcel(parcel, flags);
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("SyncResult:");
+        if (syncAlreadyInProgress) {
+            sb.append(" syncAlreadyInProgress: ").append(syncAlreadyInProgress);
+        }
+        if (tooManyDeletions) sb.append(" tooManyDeletions: ").append(tooManyDeletions);
+        if (tooManyRetries) sb.append(" tooManyRetries: ").append(tooManyRetries);
+        if (databaseError) sb.append(" databaseError: ").append(databaseError);
+        if (fullSyncRequested) sb.append(" fullSyncRequested: ").append(fullSyncRequested);
+        if (partialSyncUnavailable) {
+            sb.append(" partialSyncUnavailable: ").append(partialSyncUnavailable);
+        }
+        if (moreRecordsToGet) sb.append(" moreRecordsToGet: ").append(moreRecordsToGet);
+        if (delayUntil > 0) sb.append(" delayUntil: ").append(delayUntil);
+        sb.append(stats);
+        return sb.toString();
+    }
+
+    /**
+     * Generates a debugging string indicating the status.
+     * The string consist of a sequence of code letter followed by the count.
+     * Code letters are f - fullSyncRequested, r - partialSyncUnavailable,
+     * X - hardError, e - numParseExceptions, c - numConflictDetectedExceptions,
+     * a - numAuthExceptions, D - tooManyDeletions, R - tooManyRetries,
+     * b - databaseError, x - softError, l - syncAlreadyInProgress,
+     * I - numIoExceptions
+     * @return debugging string.
+     */
+    public String toDebugString() {
+        StringBuffer sb = new StringBuffer();
+
+        if (fullSyncRequested) {
+            sb.append("f1");
+        }
+        if (partialSyncUnavailable) {
+            sb.append("r1");
+        }
+        if (hasHardError()) {
+            sb.append("X1");
+        }
+        if (stats.numParseExceptions > 0) {
+            sb.append("e").append(stats.numParseExceptions);
+        }
+        if (stats.numConflictDetectedExceptions > 0) {
+            sb.append("c").append(stats.numConflictDetectedExceptions);
+        }
+        if (stats.numAuthExceptions > 0) {
+            sb.append("a").append(stats.numAuthExceptions);
+        }
+        if (tooManyDeletions) {
+            sb.append("D1");
+        }
+        if (tooManyRetries) {
+            sb.append("R1");
+        }
+        if (databaseError) {
+            sb.append("b1");
+        }
+        if (hasSoftError()) {
+            sb.append("x1");
+        }
+        if (syncAlreadyInProgress) {
+            sb.append("l1");
+        }
+        if (stats.numIoExceptions > 0) {
+            sb.append("I").append(stats.numIoExceptions);
+        }
+        return sb.toString();
+    }
+}
diff --git a/android/content/SyncStats.java b/android/content/SyncStats.java
new file mode 100644
index 0000000..9596a60
--- /dev/null
+++ b/android/content/SyncStats.java
@@ -0,0 +1,178 @@
+/*
+ * 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.content;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+
+/**
+ * Used to record various statistics about the result of a sync operation. The SyncManager
+ * gets access to these via a {@link SyncResult} and uses some of them to determine the
+ * disposition of the sync. See {@link SyncResult} for further dicussion on how the
+ * SyncManager uses these values.
+ */
+public class SyncStats implements Parcelable {
+    /**
+     * The SyncAdapter was unable to authenticate the {@link android.accounts.Account}
+     * that was specified in the request. The user needs to take some action to resolve
+     * before a future request can expect to succeed. This is considered a hard error.
+     */
+    public long numAuthExceptions;
+
+    /**
+     * The SyncAdapter had a problem, most likely with the network connectivity or a timeout
+     * while waiting for a network response. The request may succeed if it is tried again
+     * later. This is considered a soft error.
+     */
+    public long numIoExceptions;
+
+    /**
+     * The SyncAdapter had a problem with the data it received from the server or the storage
+     * later. This problem will likely repeat if the request is tried again. The problem
+     * will need to be cleared up by either the server or the storage layer (likely with help
+     * from the user). If the SyncAdapter cleans up the data itself then it typically won't
+     * increment this value although it may still do so in order to record that it had to
+     * perform some cleanup. E.g., if the SyncAdapter received a bad entry from the server
+     * when processing a feed of entries, it may choose to drop the entry and thus make
+     * progress and still increment this value just so the SyncAdapter can record that an
+     * error occurred. This is considered a hard error.
+     */
+    public long numParseExceptions;
+
+    /**
+     * The SyncAdapter detected that there was an unrecoverable version conflict when it
+     * attempted to update or delete a version of a resource on the server. This is expected
+     * to clear itself automatically once the new state is retrieved from the server,
+     * though it may remain until the user intervenes manually, perhaps by clearing the
+     * local storage and starting over from scratch. This is considered a hard error.
+     */
+    public long numConflictDetectedExceptions;
+
+    /**
+     * Counter for tracking how many inserts were performed by the sync operation, as defined
+     * by the SyncAdapter.
+     */
+    public long numInserts;
+
+    /**
+     * Counter for tracking how many updates were performed by the sync operation, as defined
+     * by the SyncAdapter.
+     */
+    public long numUpdates;
+
+    /**
+     * Counter for tracking how many deletes were performed by the sync operation, as defined
+     * by the SyncAdapter.
+     */
+    public long numDeletes;
+
+    /**
+     * Counter for tracking how many entries were affected by the sync operation, as defined
+     * by the SyncAdapter.
+     */
+    public long numEntries;
+
+    /**
+     * Counter for tracking how many entries, either from the server or the local store, were
+     * ignored during the sync operation. This could happen if the SyncAdapter detected some
+     * unparsable data but decided to skip it and move on rather than failing immediately.
+     */
+    public long numSkippedEntries;
+
+    public SyncStats() {
+        numAuthExceptions = 0;
+        numIoExceptions = 0;
+        numParseExceptions = 0;
+        numConflictDetectedExceptions = 0;
+        numInserts = 0;
+        numUpdates = 0;
+        numDeletes = 0;
+        numEntries = 0;
+        numSkippedEntries = 0;
+    }
+
+    public SyncStats(Parcel in) {
+        numAuthExceptions = in.readLong();
+        numIoExceptions = in.readLong();
+        numParseExceptions = in.readLong();
+        numConflictDetectedExceptions = in.readLong();
+        numInserts = in.readLong();
+        numUpdates = in.readLong();
+        numDeletes = in.readLong();
+        numEntries = in.readLong();
+        numSkippedEntries = in.readLong();
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append(" stats [");
+        if (numAuthExceptions > 0) sb.append(" numAuthExceptions: ").append(numAuthExceptions);
+        if (numIoExceptions > 0) sb.append(" numIoExceptions: ").append(numIoExceptions);
+        if (numParseExceptions > 0) sb.append(" numParseExceptions: ").append(numParseExceptions);
+        if (numConflictDetectedExceptions > 0)
+            sb.append(" numConflictDetectedExceptions: ").append(numConflictDetectedExceptions);
+        if (numInserts > 0) sb.append(" numInserts: ").append(numInserts);
+        if (numUpdates > 0) sb.append(" numUpdates: ").append(numUpdates);
+        if (numDeletes > 0) sb.append(" numDeletes: ").append(numDeletes);
+        if (numEntries > 0) sb.append(" numEntries: ").append(numEntries);
+        if (numSkippedEntries > 0) sb.append(" numSkippedEntries: ").append(numSkippedEntries);
+        sb.append("]");
+        return sb.toString();
+    }
+
+    /**
+     * Reset all the counters to 0.
+     */
+    public void clear() {
+        numAuthExceptions = 0;
+        numIoExceptions = 0;
+        numParseExceptions = 0;
+        numConflictDetectedExceptions = 0;
+        numInserts = 0;
+        numUpdates = 0;
+        numDeletes = 0;
+        numEntries = 0;
+        numSkippedEntries = 0;
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeLong(numAuthExceptions);
+        dest.writeLong(numIoExceptions);
+        dest.writeLong(numParseExceptions);
+        dest.writeLong(numConflictDetectedExceptions);
+        dest.writeLong(numInserts);
+        dest.writeLong(numUpdates);
+        dest.writeLong(numDeletes);
+        dest.writeLong(numEntries);
+        dest.writeLong(numSkippedEntries);
+    }
+
+    public static final @android.annotation.NonNull Creator<SyncStats> CREATOR = new Creator<SyncStats>() {
+        public SyncStats createFromParcel(Parcel in) {
+            return new SyncStats(in);
+        }
+
+        public SyncStats[] newArray(int size) {
+            return new SyncStats[size];
+        }
+    };
+}
diff --git a/android/content/SyncStatusInfo.java b/android/content/SyncStatusInfo.java
new file mode 100644
index 0000000..b72eb04
--- /dev/null
+++ b/android/content/SyncStatusInfo.java
@@ -0,0 +1,512 @@
+/*
+ * 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.content;
+
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+import android.util.Pair;
+
+import com.android.internal.util.ArrayUtils;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+
+/** @hide */
+public class SyncStatusInfo implements Parcelable {
+    private static final String TAG = "Sync";
+
+    static final int VERSION = 6;
+
+    private static final int MAX_EVENT_COUNT = 10;
+
+    /**
+     * Number of sync sources. KEEP THIS AND SyncStorageEngine.SOURCES IN SYNC.
+     */
+    private static final int SOURCE_COUNT = 6;
+
+    @UnsupportedAppUsage
+    public final int authorityId;
+
+    /**
+     * # of syncs for each sync source, etc.
+     */
+    public static class Stats {
+        public long totalElapsedTime;
+        public int numSyncs;
+        public int numSourcePoll;
+        public int numSourceOther;
+        public int numSourceLocal;
+        public int numSourceUser;
+        public int numSourcePeriodic;
+        public int numSourceFeed;
+        public int numFailures;
+        public int numCancels;
+
+        /** Copy all the stats to another instance. */
+        public void copyTo(Stats to) {
+            to.totalElapsedTime = totalElapsedTime;
+            to.numSyncs = numSyncs;
+            to.numSourcePoll = numSourcePoll;
+            to.numSourceOther = numSourceOther;
+            to.numSourceLocal = numSourceLocal;
+            to.numSourceUser = numSourceUser;
+            to.numSourcePeriodic = numSourcePeriodic;
+            to.numSourceFeed = numSourceFeed;
+            to.numFailures = numFailures;
+            to.numCancels = numCancels;
+        }
+
+        /** Clear all the stats. */
+        public void clear() {
+            totalElapsedTime = 0;
+            numSyncs = 0;
+            numSourcePoll = 0;
+            numSourceOther = 0;
+            numSourceLocal = 0;
+            numSourceUser = 0;
+            numSourcePeriodic = 0;
+            numSourceFeed = 0;
+            numFailures = 0;
+            numCancels = 0;
+        }
+
+        /** Write all the stats to a parcel. */
+        public void writeToParcel(Parcel parcel) {
+            parcel.writeLong(totalElapsedTime);
+            parcel.writeInt(numSyncs);
+            parcel.writeInt(numSourcePoll);
+            parcel.writeInt(numSourceOther);
+            parcel.writeInt(numSourceLocal);
+            parcel.writeInt(numSourceUser);
+            parcel.writeInt(numSourcePeriodic);
+            parcel.writeInt(numSourceFeed);
+            parcel.writeInt(numFailures);
+            parcel.writeInt(numCancels);
+        }
+
+        /** Read all the stats from a parcel. */
+        public void readFromParcel(Parcel parcel) {
+            totalElapsedTime = parcel.readLong();
+            numSyncs = parcel.readInt();
+            numSourcePoll = parcel.readInt();
+            numSourceOther = parcel.readInt();
+            numSourceLocal = parcel.readInt();
+            numSourceUser = parcel.readInt();
+            numSourcePeriodic = parcel.readInt();
+            numSourceFeed = parcel.readInt();
+            numFailures = parcel.readInt();
+            numCancels = parcel.readInt();
+        }
+    }
+
+    public long lastTodayResetTime;
+
+    public final Stats totalStats = new Stats();
+    public final Stats todayStats = new Stats();
+    public final Stats yesterdayStats = new Stats();
+
+    @UnsupportedAppUsage
+    public long lastSuccessTime;
+    @UnsupportedAppUsage
+    public int lastSuccessSource;
+    @UnsupportedAppUsage
+    public long lastFailureTime;
+    @UnsupportedAppUsage
+    public int lastFailureSource;
+    @UnsupportedAppUsage
+    public String lastFailureMesg;
+    @UnsupportedAppUsage
+    public long initialFailureTime;
+    @UnsupportedAppUsage
+    public boolean pending;
+    @UnsupportedAppUsage
+    public boolean initialize;
+
+    public final long[] perSourceLastSuccessTimes = new long[SOURCE_COUNT];
+    public final long[] perSourceLastFailureTimes = new long[SOURCE_COUNT];
+
+    // Warning: It is up to the external caller to ensure there are
+    // no race conditions when accessing this list
+    @UnsupportedAppUsage
+    private ArrayList<Long> periodicSyncTimes;
+
+    private final ArrayList<Long> mLastEventTimes = new ArrayList<>();
+    private final ArrayList<String> mLastEvents = new ArrayList<>();
+
+    @UnsupportedAppUsage
+    public SyncStatusInfo(int authorityId) {
+        this.authorityId = authorityId;
+    }
+
+    @UnsupportedAppUsage
+    public int getLastFailureMesgAsInt(int def) {
+        final int i = ContentResolver.syncErrorStringToInt(lastFailureMesg);
+        if (i > 0) {
+            return i;
+        } else {
+            Log.d(TAG, "Unknown lastFailureMesg:" + lastFailureMesg);
+            return def;
+        }
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeInt(VERSION);
+        parcel.writeInt(authorityId);
+
+        // Note we can't use Stats.writeToParcel() here; see the below constructor for the reason.
+        parcel.writeLong(totalStats.totalElapsedTime);
+        parcel.writeInt(totalStats.numSyncs);
+        parcel.writeInt(totalStats.numSourcePoll);
+        parcel.writeInt(totalStats.numSourceOther);
+        parcel.writeInt(totalStats.numSourceLocal);
+        parcel.writeInt(totalStats.numSourceUser);
+
+        parcel.writeLong(lastSuccessTime);
+        parcel.writeInt(lastSuccessSource);
+        parcel.writeLong(lastFailureTime);
+        parcel.writeInt(lastFailureSource);
+        parcel.writeString(lastFailureMesg);
+        parcel.writeLong(initialFailureTime);
+        parcel.writeInt(pending ? 1 : 0);
+        parcel.writeInt(initialize ? 1 : 0);
+        if (periodicSyncTimes != null) {
+            parcel.writeInt(periodicSyncTimes.size());
+            for (long periodicSyncTime : periodicSyncTimes) {
+                parcel.writeLong(periodicSyncTime);
+            }
+        } else {
+            parcel.writeInt(-1);
+        }
+        parcel.writeInt(mLastEventTimes.size());
+        for (int i = 0; i < mLastEventTimes.size(); i++) {
+            parcel.writeLong(mLastEventTimes.get(i));
+            parcel.writeString(mLastEvents.get(i));
+        }
+        // Version 4
+        parcel.writeInt(totalStats.numSourcePeriodic);
+
+        // Version 5
+        parcel.writeInt(totalStats.numSourceFeed);
+        parcel.writeInt(totalStats.numFailures);
+        parcel.writeInt(totalStats.numCancels);
+
+        parcel.writeLong(lastTodayResetTime);
+
+        todayStats.writeToParcel(parcel);
+        yesterdayStats.writeToParcel(parcel);
+
+        // Version 6.
+        parcel.writeLongArray(perSourceLastSuccessTimes);
+        parcel.writeLongArray(perSourceLastFailureTimes);
+    }
+
+    @UnsupportedAppUsage
+    public SyncStatusInfo(Parcel parcel) {
+        int version = parcel.readInt();
+        if (version != VERSION && version != 1) {
+            Log.w("SyncStatusInfo", "Unknown version: " + version);
+        }
+        authorityId = parcel.readInt();
+
+        // Note we can't use Stats.writeToParcel() here because the data is persisted and we need
+        // to be able to read from the old format too.
+        totalStats.totalElapsedTime = parcel.readLong();
+        totalStats.numSyncs = parcel.readInt();
+        totalStats.numSourcePoll = parcel.readInt();
+        totalStats.numSourceOther = parcel.readInt();
+        totalStats.numSourceLocal = parcel.readInt();
+        totalStats.numSourceUser = parcel.readInt();
+        lastSuccessTime = parcel.readLong();
+        lastSuccessSource = parcel.readInt();
+        lastFailureTime = parcel.readLong();
+        lastFailureSource = parcel.readInt();
+        lastFailureMesg = parcel.readString();
+        initialFailureTime = parcel.readLong();
+        pending = parcel.readInt() != 0;
+        initialize = parcel.readInt() != 0;
+        if (version == 1) {
+            periodicSyncTimes = null;
+        } else {
+            final int count = parcel.readInt();
+            if (count < 0) {
+                periodicSyncTimes = null;
+            } else {
+                periodicSyncTimes = new ArrayList<Long>();
+                for (int i = 0; i < count; i++) {
+                    periodicSyncTimes.add(parcel.readLong());
+                }
+            }
+            if (version >= 3) {
+                mLastEventTimes.clear();
+                mLastEvents.clear();
+                final int nEvents = parcel.readInt();
+                for (int i = 0; i < nEvents; i++) {
+                    mLastEventTimes.add(parcel.readLong());
+                    mLastEvents.add(parcel.readString());
+                }
+            }
+        }
+        if (version < 4) {
+            // Before version 4, numSourcePeriodic wasn't persisted.
+            totalStats.numSourcePeriodic =
+                    totalStats.numSyncs - totalStats.numSourceLocal - totalStats.numSourcePoll
+                            - totalStats.numSourceOther
+                            - totalStats.numSourceUser;
+            if (totalStats.numSourcePeriodic < 0) { // Sanity check.
+                totalStats.numSourcePeriodic = 0;
+            }
+        } else {
+            totalStats.numSourcePeriodic = parcel.readInt();
+        }
+        if (version >= 5) {
+            totalStats.numSourceFeed = parcel.readInt();
+            totalStats.numFailures = parcel.readInt();
+            totalStats.numCancels = parcel.readInt();
+
+            lastTodayResetTime = parcel.readLong();
+
+            todayStats.readFromParcel(parcel);
+            yesterdayStats.readFromParcel(parcel);
+        }
+        if (version >= 6) {
+            parcel.readLongArray(perSourceLastSuccessTimes);
+            parcel.readLongArray(perSourceLastFailureTimes);
+        }
+    }
+
+    /**
+     * Copies all data from the given SyncStatusInfo object.
+     *
+     * @param other the SyncStatusInfo object to copy data from
+     */
+    public SyncStatusInfo(SyncStatusInfo other) {
+        authorityId = other.authorityId;
+        copyFrom(other);
+    }
+
+    /**
+     * Copies all data from the given SyncStatusInfo object except for its authority id.
+     *
+     * @param authorityId the new authority id
+     * @param other the SyncStatusInfo object to copy data from
+     */
+    public SyncStatusInfo(int authorityId, SyncStatusInfo other) {
+        this.authorityId = authorityId;
+        copyFrom(other);
+    }
+
+    private void copyFrom(SyncStatusInfo other) {
+        other.totalStats.copyTo(totalStats);
+        other.todayStats.copyTo(todayStats);
+        other.yesterdayStats.copyTo(yesterdayStats);
+
+        lastTodayResetTime = other.lastTodayResetTime;
+
+        lastSuccessTime = other.lastSuccessTime;
+        lastSuccessSource = other.lastSuccessSource;
+        lastFailureTime = other.lastFailureTime;
+        lastFailureSource = other.lastFailureSource;
+        lastFailureMesg = other.lastFailureMesg;
+        initialFailureTime = other.initialFailureTime;
+        pending = other.pending;
+        initialize = other.initialize;
+        if (other.periodicSyncTimes != null) {
+            periodicSyncTimes = new ArrayList<Long>(other.periodicSyncTimes);
+        }
+        mLastEventTimes.addAll(other.mLastEventTimes);
+        mLastEvents.addAll(other.mLastEvents);
+
+        copy(perSourceLastSuccessTimes, other.perSourceLastSuccessTimes);
+        copy(perSourceLastFailureTimes, other.perSourceLastFailureTimes);
+    }
+
+    private static void copy(long[] to, long[] from) {
+        System.arraycopy(from, 0, to, 0, to.length);
+    }
+
+    public int getPeriodicSyncTimesSize() {
+        return periodicSyncTimes == null ? 0 : periodicSyncTimes.size();
+    }
+
+    public void addPeriodicSyncTime(long time) {
+        periodicSyncTimes = ArrayUtils.add(periodicSyncTimes, time);
+    }
+
+    @UnsupportedAppUsage
+    public void setPeriodicSyncTime(int index, long when) {
+        // The list is initialized lazily when scheduling occurs so we need to make sure
+        // we initialize elements < index to zero (zero is ignore for scheduling purposes)
+        ensurePeriodicSyncTimeSize(index);
+        periodicSyncTimes.set(index, when);
+    }
+
+    @UnsupportedAppUsage
+    public long getPeriodicSyncTime(int index) {
+        if (periodicSyncTimes != null && index < periodicSyncTimes.size()) {
+            return periodicSyncTimes.get(index);
+        } else {
+            return 0;
+        }
+    }
+
+    @UnsupportedAppUsage
+    public void removePeriodicSyncTime(int index) {
+        if (periodicSyncTimes != null && index < periodicSyncTimes.size()) {
+            periodicSyncTimes.remove(index);
+        }
+    }
+
+    /**
+     * Populates {@code mLastEventTimes} and {@code mLastEvents} with the given list. <br>
+     * <i>Note: This method is mainly used to repopulate the event info from disk and it will clear
+     * both {@code mLastEventTimes} and {@code mLastEvents} before populating.</i>
+     *
+     * @param lastEventInformation the list to populate with
+     */
+    public void populateLastEventsInformation(ArrayList<Pair<Long, String>> lastEventInformation) {
+        mLastEventTimes.clear();
+        mLastEvents.clear();
+        final int size = lastEventInformation.size();
+        for (int i = 0; i < size; i++) {
+            final Pair<Long, String> lastEventInfo = lastEventInformation.get(i);
+            mLastEventTimes.add(lastEventInfo.first);
+            mLastEvents.add(lastEventInfo.second);
+        }
+    }
+
+    /** */
+    public void addEvent(String message) {
+        if (mLastEventTimes.size() >= MAX_EVENT_COUNT) {
+            mLastEventTimes.remove(MAX_EVENT_COUNT - 1);
+            mLastEvents.remove(MAX_EVENT_COUNT - 1);
+        }
+        mLastEventTimes.add(0, System.currentTimeMillis());
+        mLastEvents.add(0, message);
+    }
+
+    /** */
+    public int getEventCount() {
+        return mLastEventTimes.size();
+    }
+
+    /** */
+    public long getEventTime(int i) {
+        return mLastEventTimes.get(i);
+    }
+
+    /** */
+    public String getEvent(int i) {
+        return mLastEvents.get(i);
+    }
+
+    /** Call this when a sync has succeeded. */
+    public void setLastSuccess(int source, long lastSyncTime) {
+        lastSuccessTime = lastSyncTime;
+        lastSuccessSource = source;
+        lastFailureTime = 0;
+        lastFailureSource = -1;
+        lastFailureMesg = null;
+        initialFailureTime = 0;
+
+        if (0 <= source && source < perSourceLastSuccessTimes.length) {
+            perSourceLastSuccessTimes[source] = lastSyncTime;
+        }
+    }
+
+    /** Call this when a sync has failed. */
+    public void setLastFailure(int source, long lastSyncTime, String failureMessage) {
+        lastFailureTime = lastSyncTime;
+        lastFailureSource = source;
+        lastFailureMesg = failureMessage;
+        if (initialFailureTime == 0) {
+            initialFailureTime = lastSyncTime;
+        }
+
+        if (0 <= source && source < perSourceLastFailureTimes.length) {
+            perSourceLastFailureTimes[source] = lastSyncTime;
+        }
+    }
+
+    @UnsupportedAppUsage
+    public static final @android.annotation.NonNull Creator<SyncStatusInfo> CREATOR = new Creator<SyncStatusInfo>() {
+        public SyncStatusInfo createFromParcel(Parcel in) {
+            return new SyncStatusInfo(in);
+        }
+
+        public SyncStatusInfo[] newArray(int size) {
+            return new SyncStatusInfo[size];
+        }
+    };
+
+    @UnsupportedAppUsage
+    private void ensurePeriodicSyncTimeSize(int index) {
+        if (periodicSyncTimes == null) {
+            periodicSyncTimes = new ArrayList<Long>(0);
+        }
+
+        final int requiredSize = index + 1;
+        if (periodicSyncTimes.size() < requiredSize) {
+            for (int i = periodicSyncTimes.size(); i < requiredSize; i++) {
+                periodicSyncTimes.add((long) 0);
+            }
+        }
+    }
+
+    /**
+     * If the last reset was not today, move today's stats to yesterday's and clear today's.
+     */
+    public void maybeResetTodayStats(boolean clockValid, boolean force) {
+        final long now = System.currentTimeMillis();
+
+        if (!force) {
+            // Last reset was the same day, nothing to do.
+            if (areSameDates(now, lastTodayResetTime)) {
+                return;
+            }
+
+            // Hack -- on devices with no RTC, until the NTP kicks in, the device won't have the
+            // correct time. So if the time goes back, don't reset, unless we're sure the current
+            // time is correct.
+            if (now < lastTodayResetTime && !clockValid) {
+                return;
+            }
+        }
+
+        lastTodayResetTime = now;
+
+        todayStats.copyTo(yesterdayStats);
+        todayStats.clear();
+    }
+
+    private static boolean areSameDates(long time1, long time2) {
+        final Calendar c1 = new GregorianCalendar();
+        final Calendar c2 = new GregorianCalendar();
+
+        c1.setTimeInMillis(time1);
+        c2.setTimeInMillis(time2);
+
+        return c1.get(Calendar.YEAR) == c2.get(Calendar.YEAR)
+                && c1.get(Calendar.DAY_OF_YEAR) == c2.get(Calendar.DAY_OF_YEAR);
+    }
+}
diff --git a/android/content/SyncStatusObserver.java b/android/content/SyncStatusObserver.java
new file mode 100644
index 0000000..663378a
--- /dev/null
+++ b/android/content/SyncStatusObserver.java
@@ -0,0 +1,21 @@
+/*
+ * 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.content;
+
+public interface SyncStatusObserver {
+    void onStatusChanged(int which);
+}
diff --git a/android/content/UndoManager.java b/android/content/UndoManager.java
new file mode 100644
index 0000000..ed9cd86
--- /dev/null
+++ b/android/content/UndoManager.java
@@ -0,0 +1,956 @@
+/*
+ * 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.content;
+
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Parcel;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+
+import java.util.ArrayList;
+
+/**
+ * Top-level class for managing and interacting with the global undo state for
+ * a document or application.  This class supports both undo and redo and has
+ * helpers for merging undoable operations together as they are performed.
+ *
+ * <p>A single undoable operation is represented by {@link UndoOperation} which
+ * apps implement to define their undo/redo behavior.  The UndoManager keeps
+ * a stack of undo states; each state can have one or more undo operations
+ * inside of it.</p>
+ *
+ * <p>Updates to the stack must be done inside of a {@link #beginUpdate}/{@link #endUpdate()}
+ * pair.  During this time you can add new operations to the stack with
+ * {@link #addOperation}, retrieve and modify existing operations with
+ * {@link #getLastOperation}, control the label shown to the user for this operation
+ * with {@link #setUndoLabel} and {@link #suggestUndoLabel}, etc.</p>
+ *
+ * <p>Every {link UndoOperation} is associated with an {@link UndoOwner}, which identifies
+ * the data it belongs to.  The owner is used to indicate how operations are dependent
+ * on each other -- operations with the same owner are dependent on others with the
+ * same owner.  For example, you may have a document with multiple embedded objects.  If the
+ * document itself and each embedded object use different owners, then you
+ * can provide undo semantics appropriate to the user's context: while within
+ * an embedded object, only edits to that object are seen and the user can
+ * undo/redo them without needing to impact edits in other objects; while
+ * within the larger document, all edits can be seen and the user must
+ * undo/redo them as a single stream.</p>
+ *
+ * @hide
+ */
+public class UndoManager {
+    // The common case is a single undo owner (e.g. for a TextView), so default to that capacity.
+    private final ArrayMap<String, UndoOwner> mOwners =
+            new ArrayMap<String, UndoOwner>(1 /* capacity */);
+    private final ArrayList<UndoState> mUndos = new ArrayList<UndoState>();
+    private final ArrayList<UndoState> mRedos = new ArrayList<UndoState>();
+    private int mUpdateCount;
+    private int mHistorySize = 20;
+    private UndoState mWorking;
+    private int mCommitId = 1;
+    private boolean mInUndo;
+    private boolean mMerged;
+
+    private int mStateSeq;
+    private int mNextSavedIdx;
+    private UndoOwner[] mStateOwners;
+
+    /**
+     * Never merge with the last undo state.
+     */
+    public static final int MERGE_MODE_NONE = 0;
+
+    /**
+     * Allow merge with the last undo state only if it contains
+     * operations with the caller's owner.
+     */
+    public static final int MERGE_MODE_UNIQUE = 1;
+
+    /**
+     * Always allow merge with the last undo state, if possible.
+     */
+    public static final int MERGE_MODE_ANY = 2;
+
+    @UnsupportedAppUsage
+    public UndoManager() {
+    }
+
+    @UnsupportedAppUsage
+    public UndoOwner getOwner(String tag, Object data) {
+        if (tag == null) {
+            throw new NullPointerException("tag can't be null");
+        }
+        if (data == null) {
+            throw new NullPointerException("data can't be null");
+        }
+        UndoOwner owner = mOwners.get(tag);
+        if (owner != null) {
+            if (owner.mData != data) {
+                if (owner.mData != null) {
+                    throw new IllegalStateException("Owner " + owner + " already exists with data "
+                            + owner.mData + " but giving different data " + data);
+                }
+                owner.mData = data;
+            }
+            return owner;
+        }
+
+        owner = new UndoOwner(tag, this);
+        owner.mData = data;
+        mOwners.put(tag, owner);
+        return owner;
+    }
+
+    void removeOwner(UndoOwner owner) {
+        // XXX need to figure out how to prune.
+        if (false) {
+            mOwners.remove(owner.mTag);
+        }
+    }
+
+    /**
+     * Flatten the current undo state into a Parcel object, which can later be restored
+     * with {@link #restoreInstanceState(android.os.Parcel, java.lang.ClassLoader)}.
+     */
+    @UnsupportedAppUsage
+    public void saveInstanceState(Parcel p) {
+        if (mUpdateCount > 0) {
+            throw new IllegalStateException("Can't save state while updating");
+        }
+        mStateSeq++;
+        if (mStateSeq <= 0) {
+            mStateSeq = 0;
+        }
+        mNextSavedIdx = 0;
+        p.writeInt(mHistorySize);
+        p.writeInt(mOwners.size());
+        // XXX eventually we need to be smart here about limiting the
+        // number of undo states we write to not exceed X bytes.
+        int i = mUndos.size();
+        while (i > 0) {
+            p.writeInt(1);
+            i--;
+            mUndos.get(i).writeToParcel(p);
+        }
+        i = mRedos.size();
+        while (i > 0) {
+            p.writeInt(2);
+            i--;
+            mRedos.get(i).writeToParcel(p);
+        }
+        p.writeInt(0);
+    }
+
+    void saveOwner(UndoOwner owner, Parcel out) {
+        if (owner.mStateSeq == mStateSeq) {
+            out.writeInt(owner.mSavedIdx);
+        } else {
+            owner.mStateSeq = mStateSeq;
+            owner.mSavedIdx = mNextSavedIdx;
+            out.writeInt(owner.mSavedIdx);
+            out.writeString(owner.mTag);
+            out.writeInt(owner.mOpCount);
+            mNextSavedIdx++;
+        }
+    }
+
+    /**
+     * Restore an undo state previously created with {@link #saveInstanceState(Parcel)}.  This
+     * will restore the UndoManager's state to almost exactly what it was at the point it had
+     * been previously saved; the only information not restored is the data object
+     * associated with each {@link UndoOwner}, which requires separate calls to
+     * {@link #getOwner(String, Object)} to re-associate the owner with its data.
+     */
+    @UnsupportedAppUsage
+    public void restoreInstanceState(Parcel p, ClassLoader loader) {
+        if (mUpdateCount > 0) {
+            throw new IllegalStateException("Can't save state while updating");
+        }
+        forgetUndos(null, -1);
+        forgetRedos(null, -1);
+        mHistorySize = p.readInt();
+        mStateOwners = new UndoOwner[p.readInt()];
+
+        int stype;
+        while ((stype=p.readInt()) != 0) {
+            UndoState ustate = new UndoState(this, p, loader);
+            if (stype == 1) {
+                mUndos.add(0, ustate);
+            } else {
+                mRedos.add(0, ustate);
+            }
+        }
+    }
+
+    UndoOwner restoreOwner(Parcel in) {
+        int idx = in.readInt();
+        UndoOwner owner = mStateOwners[idx];
+        if (owner == null) {
+            String tag = in.readString();
+            int opCount = in.readInt();
+            owner = new UndoOwner(tag, this);
+            owner.mOpCount = opCount;
+            mStateOwners[idx] = owner;
+            mOwners.put(tag, owner);
+        }
+        return owner;
+    }
+
+    /**
+     * Set the maximum number of undo states that will be retained.
+     */
+    public void setHistorySize(int size) {
+        mHistorySize = size;
+        if (mHistorySize >= 0 && countUndos(null) > mHistorySize) {
+            forgetUndos(null, countUndos(null) - mHistorySize);
+        }
+    }
+
+    /**
+     * Return the current maximum number of undo states.
+     */
+    public int getHistorySize() {
+        return mHistorySize;
+    }
+
+    /**
+     * Perform undo of last/top <var>count</var> undo states.  The states impacted
+     * by this can be limited through <var>owners</var>.
+     * @param owners Optional set of owners that should be impacted.  If null, all
+     * undo states will be visible and available for undo.  If non-null, only those
+     * states that contain one of the owners specified here will be visible.
+     * @param count Number of undo states to pop.
+     * @return Returns the number of undo states that were actually popped.
+     */
+    @UnsupportedAppUsage
+    public int undo(UndoOwner[] owners, int count) {
+        if (mWorking != null) {
+            throw new IllegalStateException("Can't be called during an update");
+        }
+
+        int num = 0;
+        int i = -1;
+
+        mInUndo = true;
+
+        UndoState us = getTopUndo(null);
+        if (us != null) {
+            us.makeExecuted();
+        }
+
+        while (count > 0 && (i=findPrevState(mUndos, owners, i)) >= 0) {
+            UndoState state = mUndos.remove(i);
+            state.undo();
+            mRedos.add(state);
+            count--;
+            num++;
+        }
+
+        mInUndo = false;
+
+        return num;
+    }
+
+    /**
+     * Perform redo of last/top <var>count</var> undo states in the transient redo stack.
+     * The states impacted by this can be limited through <var>owners</var>.
+     * @param owners Optional set of owners that should be impacted.  If null, all
+     * undo states will be visible and available for undo.  If non-null, only those
+     * states that contain one of the owners specified here will be visible.
+     * @param count Number of undo states to pop.
+     * @return Returns the number of undo states that were actually redone.
+     */
+    @UnsupportedAppUsage
+    public int redo(UndoOwner[] owners, int count) {
+        if (mWorking != null) {
+            throw new IllegalStateException("Can't be called during an update");
+        }
+
+        int num = 0;
+        int i = -1;
+
+        mInUndo = true;
+
+        while (count > 0 && (i=findPrevState(mRedos, owners, i)) >= 0) {
+            UndoState state = mRedos.remove(i);
+            state.redo();
+            mUndos.add(state);
+            count--;
+            num++;
+        }
+
+        mInUndo = false;
+
+        return num;
+    }
+
+    /**
+     * Returns true if we are currently inside of an undo/redo operation.  This is
+     * useful for editors to know whether they should be generating new undo state
+     * when they see edit operations happening.
+     */
+    @UnsupportedAppUsage
+    public boolean isInUndo() {
+        return mInUndo;
+    }
+
+    @UnsupportedAppUsage
+    public int forgetUndos(UndoOwner[] owners, int count) {
+        if (count < 0) {
+            count = mUndos.size();
+        }
+
+        int removed = 0;
+        int i = 0;
+        while (i < mUndos.size() && removed < count) {
+            UndoState state = mUndos.get(i);
+            if (count > 0 && matchOwners(state, owners)) {
+                state.destroy();
+                mUndos.remove(i);
+                removed++;
+            } else {
+                i++;
+            }
+        }
+
+        return removed;
+    }
+
+    @UnsupportedAppUsage
+    public int forgetRedos(UndoOwner[] owners, int count) {
+        if (count < 0) {
+            count = mRedos.size();
+        }
+
+        int removed = 0;
+        int i = 0;
+        while (i < mRedos.size() && removed < count) {
+            UndoState state = mRedos.get(i);
+            if (count > 0 && matchOwners(state, owners)) {
+                state.destroy();
+                mRedos.remove(i);
+                removed++;
+            } else {
+                i++;
+            }
+        }
+
+        return removed;
+    }
+
+    /**
+     * Return the number of undo states on the undo stack.
+     * @param owners If non-null, only those states containing an operation with one of
+     * the owners supplied here will be counted.
+     */
+    @UnsupportedAppUsage
+    public int countUndos(UndoOwner[] owners) {
+        if (owners == null) {
+            return mUndos.size();
+        }
+
+        int count=0;
+        int i=0;
+        while ((i=findNextState(mUndos, owners, i)) >= 0) {
+            count++;
+            i++;
+        }
+        return count;
+    }
+
+    /**
+     * Return the number of redo states on the undo stack.
+     * @param owners If non-null, only those states containing an operation with one of
+     * the owners supplied here will be counted.
+     */
+    @UnsupportedAppUsage
+    public int countRedos(UndoOwner[] owners) {
+        if (owners == null) {
+            return mRedos.size();
+        }
+
+        int count=0;
+        int i=0;
+        while ((i=findNextState(mRedos, owners, i)) >= 0) {
+            count++;
+            i++;
+        }
+        return count;
+    }
+
+    /**
+     * Return the user-visible label for the top undo state on the stack.
+     * @param owners If non-null, will select the top-most undo state containing an
+     * operation with one of the owners supplied here.
+     */
+    public CharSequence getUndoLabel(UndoOwner[] owners) {
+        UndoState state = getTopUndo(owners);
+        return state != null ? state.getLabel() : null;
+    }
+
+    /**
+     * Return the user-visible label for the top redo state on the stack.
+     * @param owners If non-null, will select the top-most undo state containing an
+     * operation with one of the owners supplied here.
+     */
+    public CharSequence getRedoLabel(UndoOwner[] owners) {
+        UndoState state = getTopRedo(owners);
+        return state != null ? state.getLabel() : null;
+    }
+
+    /**
+     * Start creating a new undo state.  Multiple calls to this function will nest until
+     * they are all matched by a later call to {@link #endUpdate}.
+     * @param label Optional user-visible label for this new undo state.
+     */
+    @UnsupportedAppUsage
+    public void beginUpdate(CharSequence label) {
+        if (mInUndo) {
+            throw new IllegalStateException("Can't being update while performing undo/redo");
+        }
+        if (mUpdateCount <= 0) {
+            createWorkingState();
+            mMerged = false;
+            mUpdateCount = 0;
+        }
+
+        mWorking.updateLabel(label);
+        mUpdateCount++;
+    }
+
+    private void createWorkingState() {
+        mWorking = new UndoState(this, mCommitId++);
+        if (mCommitId < 0) {
+            mCommitId = 1;
+        }
+    }
+
+    /**
+     * Returns true if currently inside of a {@link #beginUpdate}.
+     */
+    public boolean isInUpdate() {
+        return mUpdateCount > 0;
+    }
+
+    /**
+     * Forcibly set a new for the new undo state being built within a {@link #beginUpdate}.
+     * Any existing label will be replaced with this one.
+     */
+    @UnsupportedAppUsage
+    public void setUndoLabel(CharSequence label) {
+        if (mWorking == null) {
+            throw new IllegalStateException("Must be called during an update");
+        }
+        mWorking.setLabel(label);
+    }
+
+    /**
+     * Set a new for the new undo state being built within a {@link #beginUpdate}, but
+     * only if there is not a label currently set for it.
+     */
+    public void suggestUndoLabel(CharSequence label) {
+        if (mWorking == null) {
+            throw new IllegalStateException("Must be called during an update");
+        }
+        mWorking.updateLabel(label);
+    }
+
+    /**
+     * Return the number of times {@link #beginUpdate} has been called without a matching
+     * {@link #endUpdate} call.
+     */
+    public int getUpdateNestingLevel() {
+        return mUpdateCount;
+    }
+
+    /**
+     * Check whether there is an {@link UndoOperation} in the current {@link #beginUpdate}
+     * undo state.
+     * @param owner Optional owner of the operation to look for.  If null, will succeed
+     * if there is any operation; if non-null, will only succeed if there is an operation
+     * with the given owner.
+     * @return Returns true if there is a matching operation in the current undo state.
+     */
+    public boolean hasOperation(UndoOwner owner) {
+        if (mWorking == null) {
+            throw new IllegalStateException("Must be called during an update");
+        }
+        return mWorking.hasOperation(owner);
+    }
+
+    /**
+     * Return the most recent {@link UndoOperation} that was added to the update.
+     * @param mergeMode May be either {@link #MERGE_MODE_NONE} or {@link #MERGE_MODE_ANY}.
+     */
+    public UndoOperation<?> getLastOperation(int mergeMode) {
+        return getLastOperation(null, null, mergeMode);
+    }
+
+    /**
+     * Return the most recent {@link UndoOperation} that was added to the update and
+     * has the given owner.
+     * @param owner Optional owner of last operation to retrieve.  If null, the last
+     * operation regardless of owner will be retrieved; if non-null, the last operation
+     * matching the given owner will be retrieved.
+     * @param mergeMode May be either {@link #MERGE_MODE_NONE}, {@link #MERGE_MODE_UNIQUE},
+     * or {@link #MERGE_MODE_ANY}.
+     */
+    public UndoOperation<?> getLastOperation(UndoOwner owner, int mergeMode) {
+        return getLastOperation(null, owner, mergeMode);
+    }
+
+    /**
+     * Return the most recent {@link UndoOperation} that was added to the update and
+     * has the given owner.
+     * @param clazz Optional class of the last operation to retrieve.  If null, the
+     * last operation regardless of class will be retrieved; if non-null, the last
+     * operation whose class is the same as the given class will be retrieved.
+     * @param owner Optional owner of last operation to retrieve.  If null, the last
+     * operation regardless of owner will be retrieved; if non-null, the last operation
+     * matching the given owner will be retrieved.
+     * @param mergeMode May be either {@link #MERGE_MODE_NONE}, {@link #MERGE_MODE_UNIQUE},
+     * or {@link #MERGE_MODE_ANY}.
+     */
+    @UnsupportedAppUsage
+    public <T extends UndoOperation> T getLastOperation(Class<T> clazz, UndoOwner owner,
+            int mergeMode) {
+        if (mWorking == null) {
+            throw new IllegalStateException("Must be called during an update");
+        }
+        if (mergeMode != MERGE_MODE_NONE && !mMerged && !mWorking.hasData()) {
+            UndoState state = getTopUndo(null);
+            UndoOperation<?> last;
+            if (state != null && (mergeMode == MERGE_MODE_ANY || !state.hasMultipleOwners())
+                    && state.canMerge() && (last=state.getLastOperation(clazz, owner)) != null) {
+                if (last.allowMerge()) {
+                    mWorking.destroy();
+                    mWorking = state;
+                    mUndos.remove(state);
+                    mMerged = true;
+                    return (T)last;
+                }
+            }
+        }
+
+        return mWorking.getLastOperation(clazz, owner);
+    }
+
+    /**
+     * Add a new UndoOperation to the current update.
+     * @param op The new operation to add.
+     * @param mergeMode May be either {@link #MERGE_MODE_NONE}, {@link #MERGE_MODE_UNIQUE},
+     * or {@link #MERGE_MODE_ANY}.
+     */
+    @UnsupportedAppUsage
+    public void addOperation(UndoOperation<?> op, int mergeMode) {
+        if (mWorking == null) {
+            throw new IllegalStateException("Must be called during an update");
+        }
+        UndoOwner owner = op.getOwner();
+        if (owner.mManager != this) {
+            throw new IllegalArgumentException(
+                    "Given operation's owner is not in this undo manager.");
+        }
+        if (mergeMode != MERGE_MODE_NONE && !mMerged && !mWorking.hasData()) {
+            UndoState state = getTopUndo(null);
+            if (state != null && (mergeMode == MERGE_MODE_ANY || !state.hasMultipleOwners())
+                    && state.canMerge() && state.hasOperation(op.getOwner())) {
+                mWorking.destroy();
+                mWorking = state;
+                mUndos.remove(state);
+                mMerged = true;
+            }
+        }
+        mWorking.addOperation(op);
+    }
+
+    /**
+     * Finish the creation of an undo state, matching a previous call to
+     * {@link #beginUpdate}.
+     */
+    @UnsupportedAppUsage
+    public void endUpdate() {
+        if (mWorking == null) {
+            throw new IllegalStateException("Must be called during an update");
+        }
+        mUpdateCount--;
+
+        if (mUpdateCount == 0) {
+            pushWorkingState();
+        }
+    }
+
+    private void pushWorkingState() {
+        int N = mUndos.size() + 1;
+
+        if (mWorking.hasData()) {
+            mUndos.add(mWorking);
+            forgetRedos(null, -1);
+            mWorking.commit();
+            if (N >= 2) {
+                // The state before this one can no longer be merged, ever.
+                // The only way to get back to it is for the user to perform
+                // an undo.
+                mUndos.get(N-2).makeExecuted();
+            }
+        } else {
+            mWorking.destroy();
+        }
+        mWorking = null;
+
+        if (mHistorySize >= 0 && N > mHistorySize) {
+            forgetUndos(null, N - mHistorySize);
+        }
+    }
+
+    /**
+     * Commit the last finished undo state.  This undo state can no longer be
+     * modified with further {@link #MERGE_MODE_UNIQUE} or
+     * {@link #MERGE_MODE_ANY} merge modes.  If called while inside of an update,
+     * this will push any changes in the current update on to the undo stack
+     * and result with a fresh undo state, behaving as if {@link #endUpdate()}
+     * had been called enough to unwind the current update, then the last state
+     * committed, and {@link #beginUpdate} called to restore the update nesting.
+     * @param owner The optional owner to determine whether to perform the commit.
+     * If this is non-null, the commit will only execute if the current top undo
+     * state contains an operation with the given owner.
+     * @return Returns an integer identifier for the committed undo state, which
+     * can later be used to try to uncommit the state to perform further edits on it.
+     */
+    @UnsupportedAppUsage
+    public int commitState(UndoOwner owner) {
+        if (mWorking != null && mWorking.hasData()) {
+            if (owner == null || mWorking.hasOperation(owner)) {
+                mWorking.setCanMerge(false);
+                int commitId = mWorking.getCommitId();
+                pushWorkingState();
+                createWorkingState();
+                mMerged = true;
+                return commitId;
+            }
+        } else {
+            UndoState state = getTopUndo(null);
+            if (state != null && (owner == null || state.hasOperation(owner))) {
+                state.setCanMerge(false);
+                return state.getCommitId();
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * Attempt to undo a previous call to {@link #commitState}.  This will work
+     * if the undo state at the top of the stack has the given id, and has not been
+     * involved in an undo operation.  Otherwise false is returned.
+     * @param commitId The identifier for the state to be uncommitted, as returned
+     * by {@link #commitState}.
+     * @param owner Optional owner that must appear in the committed state.
+     * @return Returns true if the uncommit is successful, else false.
+     */
+    public boolean uncommitState(int commitId, UndoOwner owner) {
+        if (mWorking != null && mWorking.getCommitId() == commitId) {
+            if (owner == null || mWorking.hasOperation(owner)) {
+                return mWorking.setCanMerge(true);
+            }
+        } else {
+            UndoState state = getTopUndo(null);
+            if (state != null && (owner == null || state.hasOperation(owner))) {
+                if (state.getCommitId() == commitId) {
+                    return state.setCanMerge(true);
+                }
+            }
+        }
+        return false;
+    }
+
+    UndoState getTopUndo(UndoOwner[] owners) {
+        if (mUndos.size() <= 0) {
+            return null;
+        }
+        int i = findPrevState(mUndos, owners, -1);
+        return i >= 0 ? mUndos.get(i) : null;
+    }
+
+    UndoState getTopRedo(UndoOwner[] owners) {
+        if (mRedos.size() <= 0) {
+            return null;
+        }
+        int i = findPrevState(mRedos, owners, -1);
+        return i >= 0 ? mRedos.get(i) : null;
+    }
+
+    boolean matchOwners(UndoState state, UndoOwner[] owners) {
+        if (owners == null) {
+            return true;
+        }
+        for (int i=0; i<owners.length; i++) {
+            if (state.matchOwner(owners[i])) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    int findPrevState(ArrayList<UndoState> states, UndoOwner[] owners, int from) {
+        final int N = states.size();
+
+        if (from == -1) {
+            from = N-1;
+        }
+        if (from >= N) {
+            return -1;
+        }
+        if (owners == null) {
+            return from;
+        }
+
+        while (from >= 0) {
+            UndoState state = states.get(from);
+            if (matchOwners(state, owners)) {
+                return from;
+            }
+            from--;
+        }
+
+        return -1;
+    }
+
+    int findNextState(ArrayList<UndoState> states, UndoOwner[] owners, int from) {
+        final int N = states.size();
+
+        if (from < 0) {
+            from = 0;
+        }
+        if (from >= N) {
+            return -1;
+        }
+        if (owners == null) {
+            return from;
+        }
+
+        while (from < N) {
+            UndoState state = states.get(from);
+            if (matchOwners(state, owners)) {
+                return from;
+            }
+            from++;
+        }
+
+        return -1;
+    }
+
+    final static class UndoState {
+        private final UndoManager mManager;
+        private final int mCommitId;
+        private final ArrayList<UndoOperation<?>> mOperations = new ArrayList<UndoOperation<?>>();
+        private ArrayList<UndoOperation<?>> mRecent;
+        private CharSequence mLabel;
+        private boolean mCanMerge = true;
+        private boolean mExecuted;
+
+        UndoState(UndoManager manager, int commitId) {
+            mManager = manager;
+            mCommitId = commitId;
+        }
+
+        UndoState(UndoManager manager, Parcel p, ClassLoader loader) {
+            mManager = manager;
+            mCommitId = p.readInt();
+            mCanMerge = p.readInt() != 0;
+            mExecuted = p.readInt() != 0;
+            mLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(p);
+            final int N = p.readInt();
+            for (int i=0; i<N; i++) {
+                UndoOwner owner = mManager.restoreOwner(p);
+                UndoOperation op = (UndoOperation)p.readParcelable(loader);
+                op.mOwner = owner;
+                mOperations.add(op);
+            }
+        }
+
+        void writeToParcel(Parcel p) {
+            if (mRecent != null) {
+                throw new IllegalStateException("Can't save state before committing");
+            }
+            p.writeInt(mCommitId);
+            p.writeInt(mCanMerge ? 1 : 0);
+            p.writeInt(mExecuted ? 1 : 0);
+            TextUtils.writeToParcel(mLabel, p, 0);
+            final int N = mOperations.size();
+            p.writeInt(N);
+            for (int i=0; i<N; i++) {
+                UndoOperation op = mOperations.get(i);
+                mManager.saveOwner(op.mOwner, p);
+                p.writeParcelable(op, 0);
+            }
+        }
+
+        int getCommitId() {
+            return mCommitId;
+        }
+
+        void setLabel(CharSequence label) {
+            mLabel = label;
+        }
+
+        void updateLabel(CharSequence label) {
+            if (mLabel != null) {
+                mLabel = label;
+            }
+        }
+
+        CharSequence getLabel() {
+            return mLabel;
+        }
+
+        boolean setCanMerge(boolean state) {
+            // Don't allow re-enabling of merging if state has been executed.
+            if (state && mExecuted) {
+                return false;
+            }
+            mCanMerge = state;
+            return true;
+        }
+
+        void makeExecuted() {
+            mExecuted = true;
+        }
+
+        boolean canMerge() {
+            return mCanMerge && !mExecuted;
+        }
+
+        int countOperations() {
+            return mOperations.size();
+        }
+
+        boolean hasOperation(UndoOwner owner) {
+            final int N = mOperations.size();
+            if (owner == null) {
+                return N != 0;
+            }
+            for (int i=0; i<N; i++) {
+                if (mOperations.get(i).getOwner() == owner) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        boolean hasMultipleOwners() {
+            final int N = mOperations.size();
+            if (N <= 1) {
+                return false;
+            }
+            UndoOwner owner = mOperations.get(0).getOwner();
+            for (int i=1; i<N; i++) {
+                if (mOperations.get(i).getOwner() != owner) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        void addOperation(UndoOperation<?> op) {
+            if (mOperations.contains(op)) {
+                throw new IllegalStateException("Already holds " + op);
+            }
+            mOperations.add(op);
+            if (mRecent == null) {
+                mRecent = new ArrayList<UndoOperation<?>>();
+                mRecent.add(op);
+            }
+            op.mOwner.mOpCount++;
+        }
+
+        <T extends UndoOperation> T getLastOperation(Class<T> clazz, UndoOwner owner) {
+            final int N = mOperations.size();
+            if (clazz == null && owner == null) {
+                return N > 0 ? (T)mOperations.get(N-1) : null;
+            }
+            // First look for the top-most operation with the same owner.
+            for (int i=N-1; i>=0; i--) {
+                UndoOperation<?> op = mOperations.get(i);
+                if (owner != null && op.getOwner() != owner) {
+                    continue;
+                }
+                // Return this operation if it has the same class that the caller wants.
+                // Note that we don't search deeper for the class, because we don't want
+                // to end up with a different order of operations for the same owner.
+                if (clazz != null && op.getClass() != clazz) {
+                    return null;
+                }
+                return (T)op;
+            }
+
+            return null;
+        }
+
+        boolean matchOwner(UndoOwner owner) {
+            for (int i=mOperations.size()-1; i>=0; i--) {
+                if (mOperations.get(i).matchOwner(owner)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        boolean hasData() {
+            for (int i=mOperations.size()-1; i>=0; i--) {
+                if (mOperations.get(i).hasData()) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        void commit() {
+            final int N = mRecent != null ? mRecent.size() : 0;
+            for (int i=0; i<N; i++) {
+                mRecent.get(i).commit();
+            }
+            mRecent = null;
+        }
+
+        void undo() {
+            for (int i=mOperations.size()-1; i>=0; i--) {
+                mOperations.get(i).undo();
+            }
+        }
+
+        void redo() {
+            final int N = mOperations.size();
+            for (int i=0; i<N; i++) {
+                mOperations.get(i).redo();
+            }
+        }
+
+        void destroy() {
+            for (int i=mOperations.size()-1; i>=0; i--) {
+                UndoOwner owner = mOperations.get(i).mOwner;
+                owner.mOpCount--;
+                if (owner.mOpCount <= 0) {
+                    if (owner.mOpCount < 0) {
+                        throw new IllegalStateException("Underflow of op count on owner " + owner
+                                + " in op " + mOperations.get(i));
+                    }
+                    mManager.removeOwner(owner);
+                }
+            }
+        }
+    }
+}
diff --git a/android/content/UndoOperation.java b/android/content/UndoOperation.java
new file mode 100644
index 0000000..235d721
--- /dev/null
+++ b/android/content/UndoOperation.java
@@ -0,0 +1,115 @@
+/*
+ * 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.content;
+
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A single undoable operation.  You must subclass this to implement the state
+ * and behavior for your operation.  Instances of this class are placed and
+ * managed in an {@link UndoManager}.
+ *
+ * @hide
+ */
+public abstract class UndoOperation<DATA> implements Parcelable {
+    UndoOwner mOwner;
+
+    /**
+     * Create a new instance of the operation.
+     * @param owner Who owns the data being modified by this undo state; must be
+     * returned by {@link UndoManager#getOwner(String, Object) UndoManager.getOwner}.
+     */
+    @UnsupportedAppUsage
+    public UndoOperation(UndoOwner owner) {
+        mOwner = owner;
+    }
+
+    /**
+     * Construct from a Parcel.
+     */
+    @UnsupportedAppUsage
+    protected UndoOperation(Parcel src, ClassLoader loader) {
+    }
+
+    /**
+     * Owning object as given to {@link #UndoOperation(UndoOwner)}.
+     */
+    public UndoOwner getOwner() {
+        return mOwner;
+    }
+
+    /**
+     * Synonym for {@link #getOwner()}.{@link android.content.UndoOwner#getData()}.
+     */
+    public DATA getOwnerData() {
+        return (DATA)mOwner.getData();
+    }
+
+    /**
+     * Return true if this undo operation is a member of the given owner.
+     * The default implementation is <code>owner == getOwner()</code>.  You
+     * can override this to provide more sophisticated dependencies between
+     * owners.
+     */
+    public boolean matchOwner(UndoOwner owner) {
+        return owner == getOwner();
+    }
+
+    /**
+     * Return true if this operation actually contains modification data.  The
+     * default implementation always returns true.  If you return false, the
+     * operation will be dropped when the final undo state is being built.
+     */
+    public boolean hasData() {
+        return true;
+    }
+
+    /**
+     * Return true if this operation can be merged with a later operation.
+     * The default implementation always returns true.
+     */
+    public boolean allowMerge() {
+        return true;
+    }
+
+    /**
+     * Called when this undo state is being committed to the undo stack.
+     * The implementation should perform the initial edits and save any state that
+     * may be needed to undo them.
+     */
+    public abstract void commit();
+
+    /**
+     * Called when this undo state is being popped off the undo stack (in to
+     * the temporary redo stack).  The implementation should remove the original
+     * edits and thus restore the target object to its prior value.
+     */
+    public abstract void undo();
+
+    /**
+     * Called when this undo state is being pushed back from the transient
+     * redo stack to the main undo stack.  The implementation should re-apply
+     * the edits that were previously removed by {@link #undo}.
+     */
+    public abstract void redo();
+
+    public int describeContents() {
+        return 0;
+    }
+}
diff --git a/android/content/UndoOwner.java b/android/content/UndoOwner.java
new file mode 100644
index 0000000..fd257ab
--- /dev/null
+++ b/android/content/UndoOwner.java
@@ -0,0 +1,75 @@
+/*
+ * 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.content;
+
+/**
+ * Representation of an owner of {@link UndoOperation} objects in an {@link UndoManager}.
+ *
+ * @hide
+ */
+public class UndoOwner {
+    final String mTag;
+    final UndoManager mManager;
+
+    Object mData;
+    int mOpCount;
+
+    // For saving/restoring state.
+    int mStateSeq;
+    int mSavedIdx;
+
+    UndoOwner(String tag, UndoManager manager) {
+        if (tag == null) {
+            throw new NullPointerException("tag can't be null");
+        }
+        if (manager == null) {
+            throw new NullPointerException("manager can't be null");
+        }
+        mTag = tag;
+        mManager = manager;
+    }
+
+    /**
+     * Return the unique tag name identifying this owner.  This is the tag
+     * supplied to {@link UndoManager#getOwner(String, Object) UndoManager.getOwner}
+     * and is immutable.
+     */
+    public String getTag() {
+        return mTag;
+    }
+
+    /**
+     * Return the actual data object of the owner.  This is the data object
+     * supplied to {@link UndoManager#getOwner(String, Object) UndoManager.getOwner}.  An
+     * owner may have a null data if it was restored from a previously saved state with
+     * no getOwner call to associate it with its data.
+     */
+    public Object getData() {
+        return mData;
+    }
+
+    @Override
+    public String toString() {
+        return "UndoOwner:[mTag=" + mTag +
+                " mManager=" + mManager +
+                " mData=" + mData +
+                " mData=" + mData +
+                " mOpCount=" + mOpCount +
+                " mStateSeq=" + mStateSeq +
+                " mSavedIdx=" + mSavedIdx + "]";
+    }
+}
diff --git a/android/content/UriMatcher.java b/android/content/UriMatcher.java
new file mode 100644
index 0000000..7fa48f0
--- /dev/null
+++ b/android/content/UriMatcher.java
@@ -0,0 +1,285 @@
+/*
+ * 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.content;
+
+import android.compat.annotation.UnsupportedAppUsage;
+import android.net.Uri;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+Utility class to aid in matching URIs in content providers.
+
+<p>To use this class, build up a tree of <code>UriMatcher</code> objects.
+For example:
+<pre>
+    private static final int PEOPLE = 1;
+    private static final int PEOPLE_ID = 2;
+    private static final int PEOPLE_PHONES = 3;
+    private static final int PEOPLE_PHONES_ID = 4;
+    private static final int PEOPLE_CONTACTMETHODS = 7;
+    private static final int PEOPLE_CONTACTMETHODS_ID = 8;
+
+    private static final int DELETED_PEOPLE = 20;
+
+    private static final int PHONES = 9;
+    private static final int PHONES_ID = 10;
+    private static final int PHONES_FILTER = 14;
+
+    private static final int CONTACTMETHODS = 18;
+    private static final int CONTACTMETHODS_ID = 19;
+
+    private static final int CALLS = 11;
+    private static final int CALLS_ID = 12;
+    private static final int CALLS_FILTER = 15;
+
+    private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
+
+    static
+    {
+        sURIMatcher.addURI("contacts", "people", PEOPLE);
+        sURIMatcher.addURI("contacts", "people/#", PEOPLE_ID);
+        sURIMatcher.addURI("contacts", "people/#/phones", PEOPLE_PHONES);
+        sURIMatcher.addURI("contacts", "people/#/phones/#", PEOPLE_PHONES_ID);
+        sURIMatcher.addURI("contacts", "people/#/contact_methods", PEOPLE_CONTACTMETHODS);
+        sURIMatcher.addURI("contacts", "people/#/contact_methods/#", PEOPLE_CONTACTMETHODS_ID);
+        sURIMatcher.addURI("contacts", "deleted_people", DELETED_PEOPLE);
+        sURIMatcher.addURI("contacts", "phones", PHONES);
+        sURIMatcher.addURI("contacts", "phones/filter/*", PHONES_FILTER);
+        sURIMatcher.addURI("contacts", "phones/#", PHONES_ID);
+        sURIMatcher.addURI("contacts", "contact_methods", CONTACTMETHODS);
+        sURIMatcher.addURI("contacts", "contact_methods/#", CONTACTMETHODS_ID);
+        sURIMatcher.addURI("call_log", "calls", CALLS);
+        sURIMatcher.addURI("call_log", "calls/filter/*", CALLS_FILTER);
+        sURIMatcher.addURI("call_log", "calls/#", CALLS_ID);
+    }
+</pre>
+<p>Starting from API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}, paths can start
+ with a leading slash.  For example:
+<pre>
+        sURIMatcher.addURI("contacts", "/people", PEOPLE);
+</pre>
+<p>Then when you need to match against a URI, call {@link #match}, providing
+the URL that you have been given.  You can use the result to build a query,
+return a type, insert or delete a row, or whatever you need, without duplicating
+all of the if-else logic that you would otherwise need.  For example:
+<pre>
+    public String getType(Uri url)
+    {
+        int match = sURIMatcher.match(url);
+        switch (match)
+        {
+            case PEOPLE:
+                return "vnd.android.cursor.dir/person";
+            case PEOPLE_ID:
+                return "vnd.android.cursor.item/person";
+... snip ...
+                return "vnd.android.cursor.dir/snail-mail";
+            case PEOPLE_ADDRESS_ID:
+                return "vnd.android.cursor.item/snail-mail";
+            default:
+                return null;
+        }
+    }
+</pre>
+instead of:
+<pre>
+    public String getType(Uri url)
+    {
+        List<String> pathSegments = url.getPathSegments();
+        if (pathSegments.size() >= 2) {
+            if ("people".equals(pathSegments.get(1))) {
+                if (pathSegments.size() == 2) {
+                    return "vnd.android.cursor.dir/person";
+                } else if (pathSegments.size() == 3) {
+                    return "vnd.android.cursor.item/person";
+... snip ...
+                    return "vnd.android.cursor.dir/snail-mail";
+                } else if (pathSegments.size() == 3) {
+                    return "vnd.android.cursor.item/snail-mail";
+                }
+            }
+        }
+        return null;
+    }
+</pre>
+*/
+public class UriMatcher
+{
+    public static final int NO_MATCH = -1;
+    /**
+     * Creates the root node of the URI tree.
+     *
+     * @param code the code to match for the root URI
+     */
+    public UriMatcher(int code)
+    {
+        mCode = code;
+        mWhich = -1;
+        mChildren = new ArrayList<UriMatcher>();
+        mText = null;
+    }
+
+    private UriMatcher(int which, String text)
+    {
+        mCode = NO_MATCH;
+        mWhich = which;
+        mChildren = new ArrayList<UriMatcher>();
+        mText = text;
+    }
+
+    /**
+     * Add a URI to match, and the code to return when this URI is
+     * matched. URI nodes may be exact match string, the token "*"
+     * that matches any text, or the token "#" that matches only
+     * numbers.
+     * <p>
+     * Starting from API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2},
+     * this method will accept a leading slash in the path.
+     *
+     * @param authority the authority to match
+     * @param path the path to match. * may be used as a wild card for
+     * any text, and # may be used as a wild card for numbers.
+     * @param code the code that is returned when a URI is matched
+     * against the given components. Must be positive.
+     */
+    public void addURI(String authority, String path, int code)
+    {
+        if (code < 0) {
+            throw new IllegalArgumentException("code " + code + " is invalid: it must be positive");
+        }
+
+        String[] tokens = null;
+        if (path != null) {
+            String newPath = path;
+            // Strip leading slash if present.
+            if (path.length() > 1 && path.charAt(0) == '/') {
+                newPath = path.substring(1);
+            }
+            tokens = newPath.split("/");
+        }
+
+        int numTokens = tokens != null ? tokens.length : 0;
+        UriMatcher node = this;
+        for (int i = -1; i < numTokens; i++) {
+            String token = i < 0 ? authority : tokens[i];
+            ArrayList<UriMatcher> children = node.mChildren;
+            int numChildren = children.size();
+            UriMatcher child;
+            int j;
+            for (j = 0; j < numChildren; j++) {
+                child = children.get(j);
+                if (token.equals(child.mText)) {
+                    node = child;
+                    break;
+                }
+            }
+            if (j == numChildren) {
+                // Child not found, create it
+                child = createChild(token);
+                node.mChildren.add(child);
+                node = child;
+            }
+        }
+        node.mCode = code;
+    }
+
+    private static UriMatcher createChild(String token) {
+        switch (token) {
+            case "#":
+                return new UriMatcher(NUMBER, "#");
+            case "*":
+                return new UriMatcher(TEXT, "*");
+            default:
+                return new UriMatcher(EXACT, token);
+        }
+    }
+
+    /**
+     * Try to match against the path in a url.
+     *
+     * @param uri       The url whose path we will match against.
+     *
+     * @return  The code for the matched node (added using addURI),
+     * or -1 if there is no matched node.
+     */
+    public int match(Uri uri)
+    {
+        final List<String> pathSegments = uri.getPathSegments();
+        final int li = pathSegments.size();
+
+        UriMatcher node = this;
+
+        if (li == 0 && uri.getAuthority() == null) {
+            return this.mCode;
+        }
+
+        for (int i=-1; i<li; i++) {
+            String u = i < 0 ? uri.getAuthority() : pathSegments.get(i);
+            ArrayList<UriMatcher> list = node.mChildren;
+            if (list == null) {
+                break;
+            }
+            node = null;
+            int lj = list.size();
+            for (int j=0; j<lj; j++) {
+                UriMatcher n = list.get(j);
+          which_switch:
+                switch (n.mWhich) {
+                    case EXACT:
+                        if (n.mText.equals(u)) {
+                            node = n;
+                        }
+                        break;
+                    case NUMBER:
+                        int lk = u.length();
+                        for (int k=0; k<lk; k++) {
+                            char c = u.charAt(k);
+                            if (c < '0' || c > '9') {
+                                break which_switch;
+                            }
+                        }
+                        node = n;
+                        break;
+                    case TEXT:
+                        node = n;
+                        break;
+                }
+                if (node != null) {
+                    break;
+                }
+            }
+            if (node == null) {
+                return NO_MATCH;
+            }
+        }
+
+        return node.mCode;
+    }
+
+    private static final int EXACT = 0;
+    private static final int NUMBER = 1;
+    private static final int TEXT = 2;
+
+    private int mCode;
+    private final int mWhich;
+    @UnsupportedAppUsage
+    private final String mText;
+    @UnsupportedAppUsage
+    private ArrayList<UriMatcher> mChildren;
+}
diff --git a/android/content/UriPermission.java b/android/content/UriPermission.java
new file mode 100644
index 0000000..d3a9cb8
--- /dev/null
+++ b/android/content/UriPermission.java
@@ -0,0 +1,117 @@
+/*
+ * 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.content;
+
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Description of a single Uri permission grant. This grants may have been
+ * created via {@link Intent#FLAG_GRANT_READ_URI_PERMISSION}, etc when sending
+ * an {@link Intent}, or explicitly through
+ * {@link Context#grantUriPermission(String, android.net.Uri, int)}.
+ *
+ * @see ContentResolver#getPersistedUriPermissions()
+ */
+public final class UriPermission implements Parcelable {
+    private final Uri mUri;
+    private final int mModeFlags;
+    private final long mPersistedTime;
+
+    /**
+     * Value returned when a permission has not been persisted.
+     */
+    public static final long INVALID_TIME = Long.MIN_VALUE;
+
+    /** {@hide} */
+    public UriPermission(Uri uri, int modeFlags, long persistedTime) {
+        mUri = uri;
+        mModeFlags = modeFlags;
+        mPersistedTime = persistedTime;
+    }
+
+    /** {@hide} */
+    public UriPermission(Parcel in) {
+        mUri = in.readParcelable(null);
+        mModeFlags = in.readInt();
+        mPersistedTime = in.readLong();
+    }
+
+    /**
+     * Return the Uri this permission pertains to.
+     */
+    public Uri getUri() {
+        return mUri;
+    }
+
+    /**
+     * Returns if this permission offers read access.
+     */
+    public boolean isReadPermission() {
+        return (mModeFlags & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0;
+    }
+
+    /**
+     * Returns if this permission offers write access.
+     */
+    public boolean isWritePermission() {
+        return (mModeFlags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0;
+    }
+
+    /**
+     * Return the time when this permission was first persisted, in milliseconds
+     * since January 1, 1970 00:00:00.0 UTC. Returns {@link #INVALID_TIME} if
+     * not persisted.
+     *
+     * @see ContentResolver#takePersistableUriPermission(Uri, int)
+     * @see System#currentTimeMillis()
+     */
+    public long getPersistedTime() {
+        return mPersistedTime;
+    }
+
+    @Override
+    public String toString() {
+        return "UriPermission {uri=" + mUri + ", modeFlags=" + mModeFlags + ", persistedTime="
+                + mPersistedTime + "}";
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeParcelable(mUri, flags);
+        dest.writeInt(mModeFlags);
+        dest.writeLong(mPersistedTime);
+    }
+
+    public static final @android.annotation.NonNull Creator<UriPermission> CREATOR = new Creator<UriPermission>() {
+        @Override
+        public UriPermission createFromParcel(Parcel source) {
+            return new UriPermission(source);
+        }
+
+        @Override
+        public UriPermission[] newArray(int size) {
+            return new UriPermission[size];
+        }
+    };
+}
diff --git a/android/content/integrity/AppInstallMetadata.java b/android/content/integrity/AppInstallMetadata.java
new file mode 100644
index 0000000..4f38fae
--- /dev/null
+++ b/android/content/integrity/AppInstallMetadata.java
@@ -0,0 +1,300 @@
+/*
+ * 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.content.integrity;
+
+import android.annotation.NonNull;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * The app install metadata.
+ *
+ * <p>The integrity component retrieves metadata for app installs from package manager, passing it
+ * to the rule evaluation engine to evaluate the metadata against the rules.
+ *
+ * <p>Instances of this class are immutable.
+ *
+ * @hide
+ */
+public final class AppInstallMetadata {
+    private final String mPackageName;
+    // Raw string encoding for the SHA-256 hash of the certificate of the app.
+    private final List<String> mAppCertificates;
+    private final String mInstallerName;
+    // Raw string encoding for the SHA-256 hash of the certificate of the installer.
+    private final List<String> mInstallerCertificates;
+    private final long mVersionCode;
+    private final boolean mIsPreInstalled;
+    private final boolean mIsStampPresent;
+    private final boolean mIsStampVerified;
+    private final boolean mIsStampTrusted;
+    // Raw string encoding for the SHA-256 hash of the certificate of the stamp.
+    private final String mStampCertificateHash;
+    private final Map<String, String> mAllowedInstallersAndCertificates;
+
+    private AppInstallMetadata(Builder builder) {
+        this.mPackageName = builder.mPackageName;
+        this.mAppCertificates = builder.mAppCertificates;
+        this.mInstallerName = builder.mInstallerName;
+        this.mInstallerCertificates = builder.mInstallerCertificates;
+        this.mVersionCode = builder.mVersionCode;
+        this.mIsPreInstalled = builder.mIsPreInstalled;
+        this.mIsStampPresent = builder.mIsStampPresent;
+        this.mIsStampVerified = builder.mIsStampVerified;
+        this.mIsStampTrusted = builder.mIsStampTrusted;
+        this.mStampCertificateHash = builder.mStampCertificateHash;
+        this.mAllowedInstallersAndCertificates = builder.mAllowedInstallersAndCertificates;
+    }
+
+    @NonNull
+    public String getPackageName() {
+        return mPackageName;
+    }
+
+    @NonNull
+    public List<String> getAppCertificates() {
+        return mAppCertificates;
+    }
+
+    @NonNull
+    public String getInstallerName() {
+        return mInstallerName;
+    }
+
+    @NonNull
+    public List<String> getInstallerCertificates() {
+        return mInstallerCertificates;
+    }
+
+    /** @see AppInstallMetadata.Builder#setVersionCode(long) */
+    public long getVersionCode() {
+        return mVersionCode;
+    }
+
+    /** @see AppInstallMetadata.Builder#setIsPreInstalled(boolean) */
+    public boolean isPreInstalled() {
+        return mIsPreInstalled;
+    }
+
+    /** @see AppInstallMetadata.Builder#setIsStampPresent(boolean) */
+    public boolean isStampPresent() {
+        return mIsStampPresent;
+    }
+
+    /** @see AppInstallMetadata.Builder#setIsStampVerified(boolean) */
+    public boolean isStampVerified() {
+        return mIsStampVerified;
+    }
+
+    /** @see AppInstallMetadata.Builder#setIsStampTrusted(boolean) */
+    public boolean isStampTrusted() {
+        return mIsStampTrusted;
+    }
+
+    /** @see AppInstallMetadata.Builder#setStampCertificateHash(String) */
+    public String getStampCertificateHash() {
+        return mStampCertificateHash;
+    }
+
+    /** Get the allowed installers and their corresponding cert. */
+    public Map<String, String> getAllowedInstallersAndCertificates() {
+        return mAllowedInstallersAndCertificates;
+    }
+
+    @Override
+    public String toString() {
+        return String.format(
+                "AppInstallMetadata { PackageName = %s, AppCerts = %s, InstallerName = %s,"
+                    + " InstallerCerts = %s, VersionCode = %d, PreInstalled = %b, StampPresent ="
+                    + " %b, StampVerified = %b, StampTrusted = %b, StampCert = %s }",
+                mPackageName,
+                mAppCertificates,
+                mInstallerName == null ? "null" : mInstallerName,
+                mInstallerCertificates == null ? "null" : mInstallerCertificates,
+                mVersionCode,
+                mIsPreInstalled,
+                mIsStampPresent,
+                mIsStampVerified,
+                mIsStampTrusted,
+                mStampCertificateHash == null ? "null" : mStampCertificateHash);
+    }
+
+    /** Builder class for constructing {@link AppInstallMetadata} objects. */
+    public static final class Builder {
+        private String mPackageName;
+        private List<String> mAppCertificates;
+        private String mInstallerName;
+        private List<String> mInstallerCertificates;
+        private long mVersionCode;
+        private boolean mIsPreInstalled;
+        private boolean mIsStampPresent;
+        private boolean mIsStampVerified;
+        private boolean mIsStampTrusted;
+        private String mStampCertificateHash;
+        private Map<String, String> mAllowedInstallersAndCertificates;
+
+        public Builder() {
+            mAllowedInstallersAndCertificates = new HashMap<>();
+        }
+
+        /**
+         * Add allowed installers and cert.
+         *
+         * @see AppInstallMetadata#getAllowedInstallersAndCertificates()
+         */
+        @NonNull
+        public Builder setAllowedInstallersAndCert(
+                @NonNull Map<String, String> allowedInstallersAndCertificates) {
+            this.mAllowedInstallersAndCertificates = allowedInstallersAndCertificates;
+            return this;
+        }
+
+        /**
+         * Set package name of the app to be installed.
+         *
+         * @see AppInstallMetadata#getPackageName()
+         */
+        @NonNull
+        public Builder setPackageName(@NonNull String packageName) {
+            this.mPackageName = Objects.requireNonNull(packageName);
+            return this;
+        }
+
+        /**
+         * Set certificate of the app to be installed.
+         *
+         * <p>It is represented as the raw string encoding for the SHA-256 hash of the certificate
+         * of the app.
+         *
+         * @see AppInstallMetadata#getAppCertificates()
+         */
+        @NonNull
+        public Builder setAppCertificates(@NonNull List<String> appCertificates) {
+            this.mAppCertificates = Objects.requireNonNull(appCertificates);
+            return this;
+        }
+
+        /**
+         * Set name of the installer installing the app.
+         *
+         * @see AppInstallMetadata#getInstallerName()
+         */
+        @NonNull
+        public Builder setInstallerName(@NonNull String installerName) {
+            this.mInstallerName = Objects.requireNonNull(installerName);
+            return this;
+        }
+
+        /**
+         * Set certificate of the installer installing the app.
+         *
+         * <p>It is represented as the raw string encoding for the SHA-256 hash of the certificate
+         * of the installer.
+         *
+         * @see AppInstallMetadata#getInstallerCertificates()
+         */
+        @NonNull
+        public Builder setInstallerCertificates(@NonNull List<String> installerCertificates) {
+            this.mInstallerCertificates = Objects.requireNonNull(installerCertificates);
+            return this;
+        }
+
+        /**
+         * Set version code of the app to be installed.
+         *
+         * @see AppInstallMetadata#getVersionCode()
+         */
+        @NonNull
+        public Builder setVersionCode(long versionCode) {
+            this.mVersionCode = versionCode;
+            return this;
+        }
+
+        /**
+         * Set whether the app is pre-installed on the device or not.
+         *
+         * @see AppInstallMetadata#isPreInstalled()
+         */
+        @NonNull
+        public Builder setIsPreInstalled(boolean isPreInstalled) {
+            this.mIsPreInstalled = isPreInstalled;
+            return this;
+        }
+
+        /**
+         * Set whether the stamp embedded in the APK is present or not.
+         *
+         * @see AppInstallMetadata#isStampPresent()
+         */
+        @NonNull
+        public Builder setIsStampPresent(boolean isStampPresent) {
+            this.mIsStampPresent = isStampPresent;
+            return this;
+        }
+
+        /**
+         * Set whether the stamp embedded in the APK is verified or not.
+         *
+         * @see AppInstallMetadata#isStampVerified()
+         */
+        @NonNull
+        public Builder setIsStampVerified(boolean isStampVerified) {
+            this.mIsStampVerified = isStampVerified;
+            return this;
+        }
+
+        /**
+         * Set whether the stamp embedded in the APK is trusted or not.
+         *
+         * @see AppInstallMetadata#isStampTrusted()
+         */
+        @NonNull
+        public Builder setIsStampTrusted(boolean isStampTrusted) {
+            this.mIsStampTrusted = isStampTrusted;
+            return this;
+        }
+
+        /**
+         * Set certificate hash of the stamp embedded in the APK.
+         *
+         * <p>It is represented as the raw string encoding for the SHA-256 hash of the certificate
+         * of the stamp.
+         *
+         * @see AppInstallMetadata#getStampCertificateHash()
+         */
+        @NonNull
+        public Builder setStampCertificateHash(@NonNull String stampCertificateHash) {
+            this.mStampCertificateHash = Objects.requireNonNull(stampCertificateHash);
+            return this;
+        }
+
+        /**
+         * Build {@link AppInstallMetadata}.
+         *
+         * @throws IllegalArgumentException if package name or app certificate is null
+         */
+        @NonNull
+        public AppInstallMetadata build() {
+            Objects.requireNonNull(mPackageName);
+            Objects.requireNonNull(mAppCertificates);
+            return new AppInstallMetadata(this);
+        }
+    }
+}
diff --git a/android/content/integrity/AppIntegrityManager.java b/android/content/integrity/AppIntegrityManager.java
new file mode 100644
index 0000000..2869abb
--- /dev/null
+++ b/android/content/integrity/AppIntegrityManager.java
@@ -0,0 +1,143 @@
+/*
+ * 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.content.integrity;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.annotation.TestApi;
+import android.content.Context;
+import android.content.IntentSender;
+import android.content.pm.ParceledListSlice;
+import android.os.RemoteException;
+
+import java.util.List;
+
+/**
+ * Class for pushing rules used to check the integrity of app installs.
+ *
+ * <p>Note: applications using methods of this class must be a system app and have their package
+ * name whitelisted as an integrity rule provider. Otherwise a {@link SecurityException} will be
+ * thrown.
+ *
+ * @hide
+ */
+@TestApi
+@SystemApi
+@SystemService(Context.APP_INTEGRITY_SERVICE)
+public class AppIntegrityManager {
+
+    /** The operation succeeded. */
+    public static final int STATUS_SUCCESS = 0;
+
+    /** The operation failed. */
+    public static final int STATUS_FAILURE = 1;
+
+    /**
+     * Current status of an operation. Will be one of {@link #STATUS_SUCCESS}, {@link
+     * #STATUS_FAILURE}.
+     *
+     * <p>More information about a status may be available through additional extras; see the
+     * individual status documentation for details.
+     *
+     * @see android.content.Intent#getIntExtra(String, int)
+     */
+    public static final String EXTRA_STATUS = "android.content.integrity.extra.STATUS";
+
+    IAppIntegrityManager mManager;
+
+    /** @hide */
+    public AppIntegrityManager(IAppIntegrityManager manager) {
+        mManager = manager;
+    }
+
+    /**
+     * Update the rules to evaluate during install time.
+     *
+     * @param updateRequest request containing the data of the rule set update
+     * @param statusReceiver Called when the state of the session changes. Intents sent to this
+     *     receiver contain {@link #EXTRA_STATUS}. Refer to the individual status codes on how to
+     *     handle them.
+     */
+    public void updateRuleSet(
+            @NonNull RuleSet updateRequest, @NonNull IntentSender statusReceiver) {
+        try {
+            mManager.updateRuleSet(
+                    updateRequest.getVersion(),
+                    new ParceledListSlice<>(updateRequest.getRules()),
+                    statusReceiver);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /** Get the current version of the rule set. */
+    @NonNull
+    public String getCurrentRuleSetVersion() {
+        try {
+            return mManager.getCurrentRuleSetVersion();
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /** Get the name of the package that provided the current rule set. */
+    @NonNull
+    public String getCurrentRuleSetProvider() {
+        try {
+            return mManager.getCurrentRuleSetProvider();
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Get current RuleSet on device.
+     *
+     * <p>Warning: this method is only used for tests.
+     *
+     * @hide
+     */
+    @TestApi
+    @NonNull
+    public RuleSet getCurrentRuleSet() {
+        try {
+            ParceledListSlice<Rule> rules = mManager.getCurrentRules();
+            String version = mManager.getCurrentRuleSetVersion();
+            return new RuleSet.Builder().setVersion(version).addRules(rules.getList()).build();
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Get the package names of all whitelisted rule providers.
+     *
+     * <p>Warning: this method is only used for tests.
+     *
+     * @hide
+     */
+    @TestApi
+    @NonNull
+    public List<String> getWhitelistedRuleProviders() {
+        try {
+            return mManager.getWhitelistedRuleProviders();
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+}
diff --git a/android/content/integrity/AtomicFormula.java b/android/content/integrity/AtomicFormula.java
new file mode 100644
index 0000000..f363a54
--- /dev/null
+++ b/android/content/integrity/AtomicFormula.java
@@ -0,0 +1,695 @@
+/*
+ * 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.content.integrity;
+
+import static com.android.internal.util.Preconditions.checkArgument;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Represents a simple formula consisting of an app install metadata field and a value.
+ *
+ * <p>Instances of this class are immutable.
+ *
+ * @hide
+ */
+@VisibleForTesting
+public abstract class AtomicFormula extends IntegrityFormula {
+
+    /** @hide */
+    @IntDef(
+            value = {
+                PACKAGE_NAME,
+                APP_CERTIFICATE,
+                INSTALLER_NAME,
+                INSTALLER_CERTIFICATE,
+                VERSION_CODE,
+                PRE_INSTALLED,
+                STAMP_TRUSTED,
+                STAMP_CERTIFICATE_HASH,
+            })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Key {}
+
+    /** @hide */
+    @IntDef(value = {EQ, GT, GTE})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Operator {}
+
+    /**
+     * Package name of the app.
+     *
+     * <p>Can only be used in {@link StringAtomicFormula}.
+     */
+    public static final int PACKAGE_NAME = 0;
+
+    /**
+     * SHA-256 of the app certificate of the app.
+     *
+     * <p>Can only be used in {@link StringAtomicFormula}.
+     */
+    public static final int APP_CERTIFICATE = 1;
+
+    /**
+     * Package name of the installer. Will be empty string if installed by the system (e.g., adb).
+     *
+     * <p>Can only be used in {@link StringAtomicFormula}.
+     */
+    public static final int INSTALLER_NAME = 2;
+
+    /**
+     * SHA-256 of the cert of the installer. Will be empty string if installed by the system (e.g.,
+     * adb).
+     *
+     * <p>Can only be used in {@link StringAtomicFormula}.
+     */
+    public static final int INSTALLER_CERTIFICATE = 3;
+
+    /**
+     * Version code of the app.
+     *
+     * <p>Can only be used in {@link LongAtomicFormula}.
+     */
+    public static final int VERSION_CODE = 4;
+
+    /**
+     * If the app is pre-installed on the device.
+     *
+     * <p>Can only be used in {@link BooleanAtomicFormula}.
+     */
+    public static final int PRE_INSTALLED = 5;
+
+    /**
+     * If the APK has an embedded trusted stamp.
+     *
+     * <p>Can only be used in {@link BooleanAtomicFormula}.
+     */
+    public static final int STAMP_TRUSTED = 6;
+
+    /**
+     * SHA-256 of the certificate used to sign the stamp embedded in the APK.
+     *
+     * <p>Can only be used in {@link StringAtomicFormula}.
+     */
+    public static final int STAMP_CERTIFICATE_HASH = 7;
+
+    public static final int EQ = 0;
+    public static final int GT = 1;
+    public static final int GTE = 2;
+
+    private final @Key int mKey;
+
+    public AtomicFormula(@Key int key) {
+        checkArgument(isValidKey(key), String.format("Unknown key: %d", key));
+        mKey = key;
+    }
+
+    /** An {@link AtomicFormula} with an key and long value. */
+    public static final class LongAtomicFormula extends AtomicFormula implements Parcelable {
+        private final Long mValue;
+        private final @Operator Integer mOperator;
+
+        /**
+         * Constructs an empty {@link LongAtomicFormula}. This should only be used as a base.
+         *
+         * <p>This formula will always return false.
+         *
+         * @throws IllegalArgumentException if {@code key} cannot be used with long value
+         */
+        public LongAtomicFormula(@Key int key) {
+            super(key);
+            checkArgument(
+                    key == VERSION_CODE,
+                    String.format(
+                            "Key %s cannot be used with LongAtomicFormula", keyToString(key)));
+            mValue = null;
+            mOperator = null;
+        }
+
+        /**
+         * Constructs a new {@link LongAtomicFormula}.
+         *
+         * <p>This formula will hold if and only if the corresponding information of an install
+         * specified by {@code key} is of the correct relationship to {@code value} as specified by
+         * {@code operator}.
+         *
+         * @throws IllegalArgumentException if {@code key} cannot be used with long value
+         */
+        public LongAtomicFormula(@Key int key, @Operator int operator, long value) {
+            super(key);
+            checkArgument(
+                    key == VERSION_CODE,
+                    String.format(
+                            "Key %s cannot be used with LongAtomicFormula", keyToString(key)));
+            checkArgument(
+                    isValidOperator(operator), String.format("Unknown operator: %d", operator));
+            mOperator = operator;
+            mValue = value;
+        }
+
+        LongAtomicFormula(Parcel in) {
+            super(in.readInt());
+            mValue = in.readLong();
+            mOperator = in.readInt();
+        }
+
+        @NonNull
+        public static final Creator<LongAtomicFormula> CREATOR =
+                new Creator<LongAtomicFormula>() {
+                    @Override
+                    public LongAtomicFormula createFromParcel(Parcel in) {
+                        return new LongAtomicFormula(in);
+                    }
+
+                    @Override
+                    public LongAtomicFormula[] newArray(int size) {
+                        return new LongAtomicFormula[size];
+                    }
+                };
+
+        @Override
+        public int getTag() {
+            return IntegrityFormula.LONG_ATOMIC_FORMULA_TAG;
+        }
+
+        @Override
+        public boolean matches(AppInstallMetadata appInstallMetadata) {
+            if (mValue == null || mOperator == null) {
+                return false;
+            }
+
+            long metadataValue = getLongMetadataValue(appInstallMetadata, getKey());
+            switch (mOperator) {
+                case EQ:
+                    return metadataValue == mValue;
+                case GT:
+                    return metadataValue > mValue;
+                case GTE:
+                    return metadataValue >= mValue;
+                default:
+                    throw new IllegalArgumentException(
+                            String.format("Unexpected operator %d", mOperator));
+            }
+        }
+
+        @Override
+        public boolean isAppCertificateFormula() {
+            return false;
+        }
+
+        @Override
+        public boolean isInstallerFormula() {
+            return false;
+        }
+
+        @Override
+        public String toString() {
+            if (mValue == null || mOperator == null) {
+                return String.format("(%s)", keyToString(getKey()));
+            }
+            return String.format(
+                    "(%s %s %s)", keyToString(getKey()), operatorToString(mOperator), mValue);
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+            LongAtomicFormula that = (LongAtomicFormula) o;
+            return getKey() == that.getKey()
+                    && mValue == that.mValue
+                    && mOperator == that.mOperator;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(getKey(), mOperator, mValue);
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
+            if (mValue == null || mOperator == null) {
+                throw new IllegalStateException("Cannot write an empty LongAtomicFormula.");
+            }
+            dest.writeInt(getKey());
+            dest.writeLong(mValue);
+            dest.writeInt(mOperator);
+        }
+
+        public Long getValue() {
+            return mValue;
+        }
+
+        public Integer getOperator() {
+            return mOperator;
+        }
+
+        private static boolean isValidOperator(int operator) {
+            return operator == EQ || operator == GT || operator == GTE;
+        }
+
+        private static long getLongMetadataValue(AppInstallMetadata appInstallMetadata, int key) {
+            switch (key) {
+                case AtomicFormula.VERSION_CODE:
+                    return appInstallMetadata.getVersionCode();
+                default:
+                    throw new IllegalStateException("Unexpected key in IntAtomicFormula" + key);
+            }
+        }
+    }
+
+    /** An {@link AtomicFormula} with a key and string value. */
+    public static final class StringAtomicFormula extends AtomicFormula implements Parcelable {
+        private final String mValue;
+        // Indicates whether the value is the actual value or the hashed value.
+        private final Boolean mIsHashedValue;
+
+        /**
+         * Constructs an empty {@link StringAtomicFormula}. This should only be used as a base.
+         *
+         * <p>An empty formula will always match to false.
+         *
+         * @throws IllegalArgumentException if {@code key} cannot be used with string value
+         */
+        public StringAtomicFormula(@Key int key) {
+            super(key);
+            checkArgument(
+                    key == PACKAGE_NAME
+                            || key == APP_CERTIFICATE
+                            || key == INSTALLER_CERTIFICATE
+                            || key == INSTALLER_NAME
+                            || key == STAMP_CERTIFICATE_HASH,
+                    String.format(
+                            "Key %s cannot be used with StringAtomicFormula", keyToString(key)));
+            mValue = null;
+            mIsHashedValue = null;
+        }
+
+        /**
+         * Constructs a new {@link StringAtomicFormula}.
+         *
+         * <p>This formula will hold if and only if the corresponding information of an install
+         * specified by {@code key} equals {@code value}.
+         *
+         * @throws IllegalArgumentException if {@code key} cannot be used with string value
+         */
+        public StringAtomicFormula(@Key int key, @NonNull String value, boolean isHashed) {
+            super(key);
+            checkArgument(
+                    key == PACKAGE_NAME
+                            || key == APP_CERTIFICATE
+                            || key == INSTALLER_CERTIFICATE
+                            || key == INSTALLER_NAME
+                            || key == STAMP_CERTIFICATE_HASH,
+                    String.format(
+                            "Key %s cannot be used with StringAtomicFormula", keyToString(key)));
+            mValue = value;
+            mIsHashedValue = isHashed;
+        }
+
+        /**
+         * Constructs a new {@link StringAtomicFormula} together with handling the necessary hashing
+         * for the given key.
+         *
+         * <p>The value will be automatically hashed with SHA256 and the hex digest will be computed
+         * when the key is PACKAGE_NAME or INSTALLER_NAME and the value is more than 32 characters.
+         *
+         * <p>The APP_CERTIFICATES, INSTALLER_CERTIFICATES, and STAMP_CERTIFICATE_HASH are always
+         * delivered in hashed form. So the isHashedValue is set to true by default.
+         *
+         * @throws IllegalArgumentException if {@code key} cannot be used with string value.
+         */
+        public StringAtomicFormula(@Key int key, @NonNull String value) {
+            super(key);
+            checkArgument(
+                    key == PACKAGE_NAME
+                            || key == APP_CERTIFICATE
+                            || key == INSTALLER_CERTIFICATE
+                            || key == INSTALLER_NAME
+                            || key == STAMP_CERTIFICATE_HASH,
+                    String.format(
+                            "Key %s cannot be used with StringAtomicFormula", keyToString(key)));
+            mValue = hashValue(key, value);
+            mIsHashedValue =
+                    (key == APP_CERTIFICATE
+                                    || key == INSTALLER_CERTIFICATE
+                                    || key == STAMP_CERTIFICATE_HASH)
+                            || !mValue.equals(value);
+        }
+
+        StringAtomicFormula(Parcel in) {
+            super(in.readInt());
+            mValue = in.readStringNoHelper();
+            mIsHashedValue = in.readByte() != 0;
+        }
+
+        @NonNull
+        public static final Creator<StringAtomicFormula> CREATOR =
+                new Creator<StringAtomicFormula>() {
+                    @Override
+                    public StringAtomicFormula createFromParcel(Parcel in) {
+                        return new StringAtomicFormula(in);
+                    }
+
+                    @Override
+                    public StringAtomicFormula[] newArray(int size) {
+                        return new StringAtomicFormula[size];
+                    }
+                };
+
+        @Override
+        public int getTag() {
+            return IntegrityFormula.STRING_ATOMIC_FORMULA_TAG;
+        }
+
+        @Override
+        public boolean matches(AppInstallMetadata appInstallMetadata) {
+            if (mValue == null || mIsHashedValue == null) {
+                return false;
+            }
+            return getMetadataValue(appInstallMetadata, getKey()).contains(mValue);
+        }
+
+        @Override
+        public boolean isAppCertificateFormula() {
+            return getKey() == APP_CERTIFICATE;
+        }
+
+        @Override
+        public boolean isInstallerFormula() {
+            return getKey() == INSTALLER_NAME || getKey() == INSTALLER_CERTIFICATE;
+        }
+
+        @Override
+        public String toString() {
+            if (mValue == null || mIsHashedValue == null) {
+                return String.format("(%s)", keyToString(getKey()));
+            }
+            return String.format("(%s %s %s)", keyToString(getKey()), operatorToString(EQ), mValue);
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+            StringAtomicFormula that = (StringAtomicFormula) o;
+            return getKey() == that.getKey() && Objects.equals(mValue, that.mValue);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(getKey(), mValue);
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
+            if (mValue == null || mIsHashedValue == null) {
+                throw new IllegalStateException("Cannot write an empty StringAtomicFormula.");
+            }
+            dest.writeInt(getKey());
+            dest.writeStringNoHelper(mValue);
+            dest.writeByte((byte) (mIsHashedValue ? 1 : 0));
+        }
+
+        public String getValue() {
+            return mValue;
+        }
+
+        public Boolean getIsHashedValue() {
+            return mIsHashedValue;
+        }
+
+        private static List<String> getMetadataValue(
+                AppInstallMetadata appInstallMetadata, int key) {
+            switch (key) {
+                case AtomicFormula.PACKAGE_NAME:
+                    return Collections.singletonList(appInstallMetadata.getPackageName());
+                case AtomicFormula.APP_CERTIFICATE:
+                    return appInstallMetadata.getAppCertificates();
+                case AtomicFormula.INSTALLER_CERTIFICATE:
+                    return appInstallMetadata.getInstallerCertificates();
+                case AtomicFormula.INSTALLER_NAME:
+                    return Collections.singletonList(appInstallMetadata.getInstallerName());
+                case AtomicFormula.STAMP_CERTIFICATE_HASH:
+                    return Collections.singletonList(appInstallMetadata.getStampCertificateHash());
+                default:
+                    throw new IllegalStateException(
+                            "Unexpected key in StringAtomicFormula: " + key);
+            }
+        }
+
+        private static String hashValue(@Key int key, String value) {
+            // Hash the string value if it is a PACKAGE_NAME or INSTALLER_NAME and the value is
+            // greater than 32 characters.
+            if (value.length() > 32) {
+                if (key == PACKAGE_NAME || key == INSTALLER_NAME) {
+                    return hash(value);
+                }
+            }
+            return value;
+        }
+
+        private static String hash(String value) {
+            try {
+                MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
+                byte[] hashBytes = messageDigest.digest(value.getBytes(StandardCharsets.UTF_8));
+                return IntegrityUtils.getHexDigest(hashBytes);
+            } catch (NoSuchAlgorithmException e) {
+                throw new RuntimeException("SHA-256 algorithm not found", e);
+            }
+        }
+    }
+
+    /** An {@link AtomicFormula} with a key and boolean value. */
+    public static final class BooleanAtomicFormula extends AtomicFormula implements Parcelable {
+        private final Boolean mValue;
+
+        /**
+         * Constructs an empty {@link BooleanAtomicFormula}. This should only be used as a base.
+         *
+         * <p>An empty formula will always match to false.
+         *
+         * @throws IllegalArgumentException if {@code key} cannot be used with boolean value
+         */
+        public BooleanAtomicFormula(@Key int key) {
+            super(key);
+            checkArgument(
+                    key == PRE_INSTALLED || key == STAMP_TRUSTED,
+                    String.format(
+                            "Key %s cannot be used with BooleanAtomicFormula", keyToString(key)));
+            mValue = null;
+        }
+
+        /**
+         * Constructs a new {@link BooleanAtomicFormula}.
+         *
+         * <p>This formula will hold if and only if the corresponding information of an install
+         * specified by {@code key} equals {@code value}.
+         *
+         * @throws IllegalArgumentException if {@code key} cannot be used with boolean value
+         */
+        public BooleanAtomicFormula(@Key int key, boolean value) {
+            super(key);
+            checkArgument(
+                    key == PRE_INSTALLED || key == STAMP_TRUSTED,
+                    String.format(
+                            "Key %s cannot be used with BooleanAtomicFormula", keyToString(key)));
+            mValue = value;
+        }
+
+        BooleanAtomicFormula(Parcel in) {
+            super(in.readInt());
+            mValue = in.readByte() != 0;
+        }
+
+        @NonNull
+        public static final Creator<BooleanAtomicFormula> CREATOR =
+                new Creator<BooleanAtomicFormula>() {
+                    @Override
+                    public BooleanAtomicFormula createFromParcel(Parcel in) {
+                        return new BooleanAtomicFormula(in);
+                    }
+
+                    @Override
+                    public BooleanAtomicFormula[] newArray(int size) {
+                        return new BooleanAtomicFormula[size];
+                    }
+                };
+
+        @Override
+        public int getTag() {
+            return IntegrityFormula.BOOLEAN_ATOMIC_FORMULA_TAG;
+        }
+
+        @Override
+        public boolean matches(AppInstallMetadata appInstallMetadata) {
+            if (mValue == null) {
+                return false;
+            }
+            return getBooleanMetadataValue(appInstallMetadata, getKey()) == mValue;
+        }
+
+        @Override
+        public boolean isAppCertificateFormula() {
+            return false;
+        }
+
+        @Override
+        public boolean isInstallerFormula() {
+            return false;
+        }
+
+        @Override
+        public String toString() {
+            if (mValue == null) {
+                return String.format("(%s)", keyToString(getKey()));
+            }
+            return String.format("(%s %s %s)", keyToString(getKey()), operatorToString(EQ), mValue);
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+            BooleanAtomicFormula that = (BooleanAtomicFormula) o;
+            return getKey() == that.getKey() && mValue == that.mValue;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(getKey(), mValue);
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
+            if (mValue == null) {
+                throw new IllegalStateException("Cannot write an empty BooleanAtomicFormula.");
+            }
+            dest.writeInt(getKey());
+            dest.writeByte((byte) (mValue ? 1 : 0));
+        }
+
+        public Boolean getValue() {
+            return mValue;
+        }
+
+        private static boolean getBooleanMetadataValue(
+                AppInstallMetadata appInstallMetadata, int key) {
+            switch (key) {
+                case AtomicFormula.PRE_INSTALLED:
+                    return appInstallMetadata.isPreInstalled();
+                case AtomicFormula.STAMP_TRUSTED:
+                    return appInstallMetadata.isStampTrusted();
+                default:
+                    throw new IllegalStateException(
+                            "Unexpected key in BooleanAtomicFormula: " + key);
+            }
+        }
+    }
+
+    public int getKey() {
+        return mKey;
+    }
+
+    static String keyToString(int key) {
+        switch (key) {
+            case PACKAGE_NAME:
+                return "PACKAGE_NAME";
+            case APP_CERTIFICATE:
+                return "APP_CERTIFICATE";
+            case VERSION_CODE:
+                return "VERSION_CODE";
+            case INSTALLER_NAME:
+                return "INSTALLER_NAME";
+            case INSTALLER_CERTIFICATE:
+                return "INSTALLER_CERTIFICATE";
+            case PRE_INSTALLED:
+                return "PRE_INSTALLED";
+            case STAMP_TRUSTED:
+                return "STAMP_TRUSTED";
+            case STAMP_CERTIFICATE_HASH:
+                return "STAMP_CERTIFICATE_HASH";
+            default:
+                throw new IllegalArgumentException("Unknown key " + key);
+        }
+    }
+
+    static String operatorToString(int op) {
+        switch (op) {
+            case EQ:
+                return "EQ";
+            case GT:
+                return "GT";
+            case GTE:
+                return "GTE";
+            default:
+                throw new IllegalArgumentException("Unknown operator " + op);
+        }
+    }
+
+    private static boolean isValidKey(int key) {
+        return key == PACKAGE_NAME
+                || key == APP_CERTIFICATE
+                || key == VERSION_CODE
+                || key == INSTALLER_NAME
+                || key == INSTALLER_CERTIFICATE
+                || key == PRE_INSTALLED
+                || key == STAMP_TRUSTED
+                || key == STAMP_CERTIFICATE_HASH;
+    }
+}
diff --git a/android/content/integrity/CompoundFormula.java b/android/content/integrity/CompoundFormula.java
new file mode 100644
index 0000000..14b1197
--- /dev/null
+++ b/android/content/integrity/CompoundFormula.java
@@ -0,0 +1,228 @@
+/*
+ * 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.content.integrity;
+
+import static com.android.internal.util.Preconditions.checkArgument;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Represents a compound formula formed by joining other simple and complex formulas with boolean
+ * connectors.
+ *
+ * <p>Instances of this class are immutable.
+ *
+ * @hide
+ */
+@VisibleForTesting
+public final class CompoundFormula extends IntegrityFormula implements Parcelable {
+
+    /** @hide */
+    @IntDef(value = {AND, OR, NOT})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Connector {}
+
+    /** Boolean AND operator. */
+    public static final int AND = 0;
+
+    /** Boolean OR operator. */
+    public static final int OR = 1;
+
+    /** Boolean NOT operator. */
+    public static final int NOT = 2;
+
+    private final @Connector int mConnector;
+    private final @NonNull List<IntegrityFormula> mFormulas;
+
+    @NonNull
+    public static final Creator<CompoundFormula> CREATOR =
+            new Creator<CompoundFormula>() {
+                @Override
+                public CompoundFormula createFromParcel(Parcel in) {
+                    return new CompoundFormula(in);
+                }
+
+                @Override
+                public CompoundFormula[] newArray(int size) {
+                    return new CompoundFormula[size];
+                }
+            };
+
+    /**
+     * Create a new formula from operator and operands.
+     *
+     * @throws IllegalArgumentException if the number of operands is not matching the requirements
+     *                                  for that operator (at least 2 for {@link #AND} and {@link
+     *                                  #OR}, 1 for {@link #NOT}).
+     */
+    public CompoundFormula(@Connector int connector, List<IntegrityFormula> formulas) {
+        checkArgument(
+                isValidConnector(connector), String.format("Unknown connector: %d", connector));
+        validateFormulas(connector, formulas);
+        this.mConnector = connector;
+        this.mFormulas = Collections.unmodifiableList(formulas);
+    }
+
+    CompoundFormula(Parcel in) {
+        mConnector = in.readInt();
+        int length = in.readInt();
+        checkArgument(length >= 0, "Must have non-negative length. Got " + length);
+        mFormulas = new ArrayList<>(length);
+        for (int i = 0; i < length; i++) {
+            mFormulas.add(IntegrityFormula.readFromParcel(in));
+        }
+        validateFormulas(mConnector, mFormulas);
+    }
+
+    public @Connector int getConnector() {
+        return mConnector;
+    }
+
+    @NonNull
+    public List<IntegrityFormula> getFormulas() {
+        return mFormulas;
+    }
+
+    @Override
+    public int getTag() {
+        return IntegrityFormula.COMPOUND_FORMULA_TAG;
+    }
+
+    @Override
+    public boolean matches(AppInstallMetadata appInstallMetadata) {
+        switch (getConnector()) {
+            case NOT:
+                return !getFormulas().get(0).matches(appInstallMetadata);
+            case AND:
+                return getFormulas().stream()
+                        .allMatch(formula -> formula.matches(appInstallMetadata));
+            case OR:
+                return getFormulas().stream()
+                        .anyMatch(formula -> formula.matches(appInstallMetadata));
+            default:
+                throw new IllegalArgumentException("Unknown connector " + getConnector());
+        }
+    }
+
+    @Override
+    public boolean isAppCertificateFormula() {
+        return getFormulas().stream().anyMatch(formula -> formula.isAppCertificateFormula());
+    }
+
+    @Override
+    public boolean isInstallerFormula() {
+        return getFormulas().stream().anyMatch(formula -> formula.isInstallerFormula());
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        if (mFormulas.size() == 1) {
+            sb.append(String.format("%s ", connectorToString(mConnector)));
+            sb.append(mFormulas.get(0).toString());
+        } else {
+            for (int i = 0; i < mFormulas.size(); i++) {
+                if (i > 0) {
+                    sb.append(String.format(" %s ", connectorToString(mConnector)));
+                }
+                sb.append(mFormulas.get(i).toString());
+            }
+        }
+        return sb.toString();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        CompoundFormula that = (CompoundFormula) o;
+        return mConnector == that.mConnector && mFormulas.equals(that.mFormulas);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mConnector, mFormulas);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mConnector);
+        dest.writeInt(mFormulas.size());
+        for (IntegrityFormula formula : mFormulas) {
+            IntegrityFormula.writeToParcel(formula, dest, flags);
+        }
+    }
+
+    private static void validateFormulas(
+            @Connector int connector, List<IntegrityFormula> formulas) {
+        switch (connector) {
+            case AND:
+            case OR:
+                checkArgument(
+                        formulas.size() >= 2,
+                        String.format(
+                                "Connector %s must have at least 2 formulas",
+                                connectorToString(connector)));
+                break;
+            case NOT:
+                checkArgument(
+                        formulas.size() == 1,
+                        String.format(
+                                "Connector %s must have 1 formula only",
+                                connectorToString(connector)));
+                break;
+        }
+    }
+
+    private static String connectorToString(int connector) {
+        switch (connector) {
+            case AND:
+                return "AND";
+            case OR:
+                return "OR";
+            case NOT:
+                return "NOT";
+            default:
+                throw new IllegalArgumentException("Unknown connector " + connector);
+        }
+    }
+
+    private static boolean isValidConnector(int connector) {
+        return connector == AND || connector == OR || connector == NOT;
+    }
+}
diff --git a/android/content/integrity/InstallerAllowedByManifestFormula.java b/android/content/integrity/InstallerAllowedByManifestFormula.java
new file mode 100644
index 0000000..9d37299
--- /dev/null
+++ b/android/content/integrity/InstallerAllowedByManifestFormula.java
@@ -0,0 +1,109 @@
+/*
+ * 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.content.integrity;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Map;
+
+/**
+ * An atomic formula that evaluates to true if the installer of the current install is specified in
+ * the "allowed installer" field in the android manifest. Note that an empty "allowed installer" by
+ * default means containing all possible installers.
+ *
+ * @hide
+ */
+public class InstallerAllowedByManifestFormula extends IntegrityFormula implements Parcelable {
+
+    public static final String INSTALLER_CERTIFICATE_NOT_EVALUATED = "";
+
+    public InstallerAllowedByManifestFormula() {
+    }
+
+    private InstallerAllowedByManifestFormula(Parcel in) {
+    }
+
+    @NonNull
+    public static final Creator<InstallerAllowedByManifestFormula> CREATOR =
+            new Creator<InstallerAllowedByManifestFormula>() {
+                @Override
+                public InstallerAllowedByManifestFormula createFromParcel(Parcel in) {
+                    return new InstallerAllowedByManifestFormula(in);
+                }
+
+                @Override
+                public InstallerAllowedByManifestFormula[] newArray(int size) {
+                    return new InstallerAllowedByManifestFormula[size];
+                }
+            };
+
+    @Override
+    public int getTag() {
+        return IntegrityFormula.INSTALLER_ALLOWED_BY_MANIFEST_FORMULA_TAG;
+    }
+
+    @Override
+    public boolean matches(AppInstallMetadata appInstallMetadata) {
+        Map<String, String> allowedInstallersAndCertificates =
+                appInstallMetadata.getAllowedInstallersAndCertificates();
+        return allowedInstallersAndCertificates.isEmpty()
+                || installerInAllowedInstallersFromManifest(
+                appInstallMetadata, allowedInstallersAndCertificates);
+    }
+
+    @Override
+    public boolean isAppCertificateFormula() {
+        return false;
+    }
+
+    @Override
+    public boolean isInstallerFormula() {
+        return true;
+    }
+
+    private static boolean installerInAllowedInstallersFromManifest(
+            AppInstallMetadata appInstallMetadata,
+            Map<String, String> allowedInstallersAndCertificates) {
+        String installerPackage = appInstallMetadata.getInstallerName();
+
+        if (!allowedInstallersAndCertificates.containsKey(installerPackage)) {
+            return false;
+        }
+
+        // If certificate is not specified in the manifest, we do not check it.
+        if (!allowedInstallersAndCertificates.get(installerPackage)
+                .equals(INSTALLER_CERTIFICATE_NOT_EVALUATED)) {
+            return appInstallMetadata.getInstallerCertificates()
+                    .contains(
+                            allowedInstallersAndCertificates
+                                    .get(appInstallMetadata.getInstallerName()));
+        }
+
+        return true;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+    }
+}
diff --git a/android/content/integrity/IntegrityFormula.java b/android/content/integrity/IntegrityFormula.java
new file mode 100644
index 0000000..fc17772
--- /dev/null
+++ b/android/content/integrity/IntegrityFormula.java
@@ -0,0 +1,272 @@
+/*
+ * 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.content.integrity;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.content.integrity.AtomicFormula.BooleanAtomicFormula;
+import android.content.integrity.AtomicFormula.LongAtomicFormula;
+import android.content.integrity.AtomicFormula.StringAtomicFormula;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+
+/**
+ * Represents a rule logic/content.
+ *
+ * @hide
+ */
+@SystemApi
+@TestApi
+@VisibleForTesting
+public abstract class IntegrityFormula {
+
+    /** Factory class for creating integrity formulas based on the app being installed. */
+    public static final class Application {
+        /** Returns an integrity formula that checks the equality to a package name. */
+        @NonNull
+        public static IntegrityFormula packageNameEquals(@NonNull String packageName) {
+            return new StringAtomicFormula(AtomicFormula.PACKAGE_NAME, packageName);
+        }
+
+        /**
+         * Returns an integrity formula that checks if the app certificates contain {@code
+         * appCertificate}.
+         */
+        @NonNull
+        public static IntegrityFormula certificatesContain(@NonNull String appCertificate) {
+            return new StringAtomicFormula(AtomicFormula.APP_CERTIFICATE, appCertificate);
+        }
+
+        /** Returns an integrity formula that checks the equality to a version code. */
+        @NonNull
+        public static IntegrityFormula versionCodeEquals(@NonNull long versionCode) {
+            return new LongAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.EQ, versionCode);
+        }
+
+        /**
+         * Returns an integrity formula that checks the app's version code is greater than the
+         * provided value.
+         */
+        @NonNull
+        public static IntegrityFormula versionCodeGreaterThan(@NonNull long versionCode) {
+            return new LongAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.GT, versionCode);
+        }
+
+        /**
+         * Returns an integrity formula that checks the app's version code is greater than or equal
+         * to the provided value.
+         */
+        @NonNull
+        public static IntegrityFormula versionCodeGreaterThanOrEqualTo(@NonNull long versionCode) {
+            return new LongAtomicFormula(
+                    AtomicFormula.VERSION_CODE, AtomicFormula.GTE, versionCode);
+        }
+
+        /** Returns an integrity formula that is valid when app is pre-installed. */
+        @NonNull
+        public static IntegrityFormula isPreInstalled() {
+            return new BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true);
+        }
+
+        private Application() {}
+    }
+
+    /** Factory class for creating integrity formulas based on installer. */
+    public static final class Installer {
+        /** Returns an integrity formula that checks the equality to an installer name. */
+        @NonNull
+        public static IntegrityFormula packageNameEquals(@NonNull String installerName) {
+            return new StringAtomicFormula(AtomicFormula.INSTALLER_NAME, installerName);
+        }
+
+        /**
+         * An static formula that evaluates to true if the installer is NOT allowed according to the
+         * "allowed installer" field in the android manifest.
+         */
+        @NonNull
+        public static IntegrityFormula notAllowedByManifest() {
+            return not(new InstallerAllowedByManifestFormula());
+        }
+
+        /**
+         * Returns an integrity formula that checks if the installer certificates contain {@code
+         * installerCertificate}.
+         */
+        @NonNull
+        public static IntegrityFormula certificatesContain(@NonNull String installerCertificate) {
+            return new StringAtomicFormula(
+                    AtomicFormula.INSTALLER_CERTIFICATE, installerCertificate);
+        }
+
+        private Installer() {}
+    }
+
+    /** Factory class for creating integrity formulas based on source stamp. */
+    public static final class SourceStamp {
+        /** Returns an integrity formula that checks the equality to a stamp certificate hash. */
+        @NonNull
+        public static IntegrityFormula stampCertificateHashEquals(
+                @NonNull String stampCertificateHash) {
+            return new StringAtomicFormula(
+                    AtomicFormula.STAMP_CERTIFICATE_HASH, stampCertificateHash);
+        }
+
+        /**
+         * Returns an integrity formula that is valid when stamp embedded in the APK is NOT trusted.
+         */
+        @NonNull
+        public static IntegrityFormula notTrusted() {
+            return new BooleanAtomicFormula(AtomicFormula.STAMP_TRUSTED, /* value= */ false);
+        }
+
+        private SourceStamp() {}
+    }
+
+    /** @hide */
+    @IntDef(
+            value = {
+                COMPOUND_FORMULA_TAG,
+                STRING_ATOMIC_FORMULA_TAG,
+                LONG_ATOMIC_FORMULA_TAG,
+                BOOLEAN_ATOMIC_FORMULA_TAG,
+                INSTALLER_ALLOWED_BY_MANIFEST_FORMULA_TAG
+            })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface Tag {}
+
+    /** @hide */
+    public static final int COMPOUND_FORMULA_TAG = 0;
+    /** @hide */
+    public static final int STRING_ATOMIC_FORMULA_TAG = 1;
+    /** @hide */
+    public static final int LONG_ATOMIC_FORMULA_TAG = 2;
+    /** @hide */
+    public static final int BOOLEAN_ATOMIC_FORMULA_TAG = 3;
+    /** @hide */
+    public static final int INSTALLER_ALLOWED_BY_MANIFEST_FORMULA_TAG = 4;
+
+    /**
+     * Returns the tag that identifies the current class.
+     *
+     * @hide
+     */
+    public abstract @Tag int getTag();
+
+    /**
+     * Returns true when the integrity formula is satisfied by the {@code appInstallMetadata}.
+     *
+     * @hide
+     */
+    public abstract boolean matches(AppInstallMetadata appInstallMetadata);
+
+    /**
+     * Returns true when the formula (or one of its atomic formulas) has app certificate as key.
+     *
+     * @hide
+     */
+    public abstract boolean isAppCertificateFormula();
+
+    /**
+     * Returns true when the formula (or one of its atomic formulas) has installer package name or
+     * installer certificate as key.
+     *
+     * @hide
+     */
+    public abstract boolean isInstallerFormula();
+
+    /**
+     * Write an {@link IntegrityFormula} to {@link android.os.Parcel}.
+     *
+     * <p>This helper method is needed because non-final class/interface are not allowed to be
+     * {@link Parcelable}.
+     *
+     * @throws IllegalArgumentException if {@link IntegrityFormula} is not a recognized subclass
+     * @hide
+     */
+    public static void writeToParcel(
+            @NonNull IntegrityFormula formula, @NonNull Parcel dest, int flags) {
+        dest.writeInt(formula.getTag());
+        ((Parcelable) formula).writeToParcel(dest, flags);
+    }
+
+    /**
+     * Read a {@link IntegrityFormula} from a {@link android.os.Parcel}.
+     *
+     * <p>We need this (hacky) helper method because non-final class/interface cannot be {@link
+     * Parcelable} (api lint error).
+     *
+     * @throws IllegalArgumentException if the parcel cannot be parsed
+     * @hide
+     */
+    @NonNull
+    public static IntegrityFormula readFromParcel(@NonNull Parcel in) {
+        int tag = in.readInt();
+        switch (tag) {
+            case COMPOUND_FORMULA_TAG:
+                return CompoundFormula.CREATOR.createFromParcel(in);
+            case STRING_ATOMIC_FORMULA_TAG:
+                return StringAtomicFormula.CREATOR.createFromParcel(in);
+            case LONG_ATOMIC_FORMULA_TAG:
+                return LongAtomicFormula.CREATOR.createFromParcel(in);
+            case BOOLEAN_ATOMIC_FORMULA_TAG:
+                return BooleanAtomicFormula.CREATOR.createFromParcel(in);
+            case INSTALLER_ALLOWED_BY_MANIFEST_FORMULA_TAG:
+                return InstallerAllowedByManifestFormula.CREATOR.createFromParcel(in);
+            default:
+                throw new IllegalArgumentException("Unknown formula tag " + tag);
+        }
+    }
+
+    /**
+     * Returns a formula that evaluates to true when any formula in {@code formulae} evaluates to
+     * true.
+     *
+     * <p>Throws an {@link IllegalArgumentException} if formulae has less than two elements.
+     */
+    @NonNull
+    public static IntegrityFormula any(@NonNull IntegrityFormula... formulae) {
+        return new CompoundFormula(CompoundFormula.OR, Arrays.asList(formulae));
+    }
+
+    /**
+     * Returns a formula that evaluates to true when all formula in {@code formulae} evaluates to
+     * true.
+     *
+     * <p>Throws an {@link IllegalArgumentException} if formulae has less than two elements.
+     */
+    @NonNull
+    public static IntegrityFormula all(@NonNull IntegrityFormula... formulae) {
+        return new CompoundFormula(CompoundFormula.AND, Arrays.asList(formulae));
+    }
+
+    /** Returns a formula that evaluates to true when {@code formula} evaluates to false. */
+    @NonNull
+    public static IntegrityFormula not(@NonNull IntegrityFormula formula) {
+        return new CompoundFormula(CompoundFormula.NOT, Arrays.asList(formula));
+    }
+
+    // Constructor is package private so it cannot be inherited outside of this package.
+    IntegrityFormula() {}
+}
diff --git a/android/content/integrity/IntegrityUtils.java b/android/content/integrity/IntegrityUtils.java
new file mode 100644
index 0000000..c3f7624
--- /dev/null
+++ b/android/content/integrity/IntegrityUtils.java
@@ -0,0 +1,83 @@
+/*
+ * 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.content.integrity;
+
+import static com.android.internal.util.Preconditions.checkArgument;
+
+/**
+ * Utils class for simple operations used in integrity module.
+ *
+ * @hide
+ */
+public class IntegrityUtils {
+
+    private static final char[] HEX_CHARS = "0123456789ABCDEF".toCharArray();
+
+    /**
+     * Obtain the raw bytes from hex encoded string.
+     *
+     * @throws IllegalArgumentException if {@code hexDigest} is not a valid hex encoding of some
+     *     bytes
+     */
+    public static byte[] getBytesFromHexDigest(String hexDigest) {
+        checkArgument(
+                hexDigest.length() % 2 == 0,
+                "Invalid hex encoding " + hexDigest + ": must have even length");
+
+        byte[] rawBytes = new byte[hexDigest.length() / 2];
+        for (int i = 0; i < rawBytes.length; i++) {
+            int upperNibble = hexDigest.charAt(2 * i);
+            int lowerNibble = hexDigest.charAt(2 * i + 1);
+            rawBytes[i] = (byte) ((hexToDec(upperNibble) << 4) | hexToDec(lowerNibble));
+        }
+        return rawBytes;
+    }
+
+    /** Obtain hex encoded string from raw bytes. */
+    public static String getHexDigest(byte[] rawBytes) {
+        char[] hexChars = new char[rawBytes.length * 2];
+
+        for (int i = 0; i < rawBytes.length; i++) {
+            int upperNibble = (rawBytes[i] >>> 4) & 0xF;
+            int lowerNibble = rawBytes[i] & 0xF;
+            hexChars[i * 2] = decToHex(upperNibble);
+            hexChars[i * 2 + 1] = decToHex(lowerNibble);
+        }
+        return new String(hexChars);
+    }
+
+    private static int hexToDec(int hexChar) {
+        if (hexChar >= '0' && hexChar <= '9') {
+            return hexChar - '0';
+        }
+        if (hexChar >= 'a' && hexChar <= 'f') {
+            return hexChar - 'a' + 10;
+        }
+        if (hexChar >= 'A' && hexChar <= 'F') {
+            return hexChar - 'A' + 10;
+        }
+        throw new IllegalArgumentException("Invalid hex char " + hexChar);
+    }
+
+    private static char decToHex(int dec) {
+        if (dec >= 0 && dec < HEX_CHARS.length) {
+            return HEX_CHARS[dec];
+        }
+
+        throw new IllegalArgumentException("Invalid dec value to be converted to hex digit " + dec);
+    }
+}
diff --git a/android/content/integrity/Rule.java b/android/content/integrity/Rule.java
new file mode 100644
index 0000000..d29e6df
--- /dev/null
+++ b/android/content/integrity/Rule.java
@@ -0,0 +1,148 @@
+/*
+ * 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.content.integrity;
+
+import static com.android.internal.util.Preconditions.checkArgument;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Represent rules to be used in the rule evaluation engine to match against app installs.
+ *
+ * <p>Instances of this class are immutable.
+ *
+ * @hide
+ */
+@TestApi
+@SystemApi
+@VisibleForTesting
+public final class Rule implements Parcelable {
+
+    /** @hide */
+    @IntDef(
+            value = {
+                DENY,
+                FORCE_ALLOW,
+            })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Effect {}
+
+    /** If this rule matches the install, the install should be denied. */
+    public static final int DENY = 0;
+
+    /**
+     * If this rule matches the install, the install will be allowed regardless of other matched
+     * rules.
+     */
+    public static final int FORCE_ALLOW = 1;
+
+    private final @NonNull IntegrityFormula mFormula;
+    private final @Effect int mEffect;
+
+    public Rule(@NonNull IntegrityFormula formula, @Effect int effect) {
+        checkArgument(isValidEffect(effect), String.format("Unknown effect: %d", effect));
+        this.mFormula = Objects.requireNonNull(formula);
+        this.mEffect = effect;
+    }
+
+    Rule(Parcel in) {
+        mFormula = IntegrityFormula.readFromParcel(in);
+        mEffect = in.readInt();
+    }
+
+    @NonNull
+    public static final Creator<Rule> CREATOR =
+            new Creator<Rule>() {
+                @Override
+                public Rule createFromParcel(Parcel in) {
+                    return new Rule(in);
+                }
+
+                @Override
+                public Rule[] newArray(int size) {
+                    return new Rule[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        IntegrityFormula.writeToParcel(mFormula, dest, flags);
+        dest.writeInt(mEffect);
+    }
+
+    @NonNull
+    public IntegrityFormula getFormula() {
+        return mFormula;
+    }
+
+    public @Effect int getEffect() {
+        return mEffect;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("Rule: %s, %s", mFormula, effectToString(mEffect));
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        Rule that = (Rule) o;
+        return mEffect == that.mEffect && Objects.equals(mFormula, that.mFormula);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mFormula, mEffect);
+    }
+
+    private static String effectToString(int effect) {
+        switch (effect) {
+            case DENY:
+                return "DENY";
+            case FORCE_ALLOW:
+                return "FORCE_ALLOW";
+            default:
+                throw new IllegalArgumentException("Unknown effect " + effect);
+        }
+    }
+
+    private static boolean isValidEffect(int effect) {
+        return effect == DENY || effect == FORCE_ALLOW;
+    }
+}
diff --git a/android/content/integrity/RuleSet.java b/android/content/integrity/RuleSet.java
new file mode 100644
index 0000000..e121ff8
--- /dev/null
+++ b/android/content/integrity/RuleSet.java
@@ -0,0 +1,93 @@
+/*
+ * 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.content.integrity;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Immutable data class encapsulating all parameters of a rule set.
+ *
+ * @hide
+ */
+@TestApi
+@SystemApi
+public class RuleSet {
+    private final String mVersion;
+    private final List<Rule> mRules;
+
+    private RuleSet(String version, List<Rule> rules) {
+        mVersion = version;
+        mRules = Collections.unmodifiableList(rules);
+    }
+
+    /** @see Builder#setVersion(String). */
+    @NonNull
+    public String getVersion() {
+        return mVersion;
+    }
+
+    /** @see Builder#addRules(List). */
+    @NonNull
+    public List<Rule> getRules() {
+        return mRules;
+    }
+
+    /** Builder class for RuleSetUpdateRequest. */
+    public static class Builder {
+        private String mVersion;
+        private List<Rule> mRules;
+
+        public Builder() {
+            mRules = new ArrayList<>();
+        }
+
+        /**
+         * Set a version string to identify this rule set. This can be retrieved by {@link
+         * AppIntegrityManager#getCurrentRuleSetVersion()}.
+         */
+        @NonNull
+        public Builder setVersion(@NonNull String version) {
+            mVersion = version;
+            return this;
+        }
+
+        /** Add the rules to include. */
+        @NonNull
+        public Builder addRules(@NonNull List<Rule> rules) {
+            mRules.addAll(rules);
+            return this;
+        }
+
+        /**
+         * Builds a {@link RuleSet}.
+         *
+         * @throws IllegalArgumentException if version is null
+         */
+        @NonNull
+        public RuleSet build() {
+            Objects.requireNonNull(mVersion);
+            return new RuleSet(mVersion, mRules);
+        }
+    }
+}
diff --git a/android/content/om/OverlayInfo.java b/android/content/om/OverlayInfo.java
new file mode 100644
index 0000000..62815dd
--- /dev/null
+++ b/android/content/om/OverlayInfo.java
@@ -0,0 +1,464 @@
+/*
+ * 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.content.om;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.annotation.UserIdInt;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Immutable overlay information about a package. All PackageInfos that
+ * represent an overlay package will have a corresponding OverlayInfo.
+ *
+ * @hide
+ */
+@SystemApi
+public final class OverlayInfo implements Parcelable {
+
+    /** @hide */
+    @IntDef(prefix = "STATE_", value = {
+            STATE_UNKNOWN,
+            STATE_MISSING_TARGET,
+            STATE_NO_IDMAP,
+            STATE_DISABLED,
+            STATE_ENABLED,
+            STATE_ENABLED_IMMUTABLE,
+            // @Deprecated STATE_TARGET_IS_BEING_REPLACED,
+            STATE_OVERLAY_IS_BEING_REPLACED,
+    })
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface State {}
+
+    /**
+     * An internal state used as the initial state of an overlay. OverlayInfo
+     * objects exposed outside the {@link
+     * com.android.server.om.OverlayManagerService} should never have this
+     * state.
+     *
+     * @hide
+     */
+    public static final int STATE_UNKNOWN = -1;
+
+    /**
+     * The target package of the overlay is not installed. The overlay cannot be enabled.
+     *
+     * @hide
+     */
+    public static final int STATE_MISSING_TARGET = 0;
+
+    /**
+     * Creation of idmap file failed (e.g. no matching resources). The overlay
+     * cannot be enabled.
+     *
+     * @hide
+     */
+    public static final int STATE_NO_IDMAP = 1;
+
+    /**
+     * The overlay is currently disabled. It can be enabled.
+     *
+     * @see IOverlayManager#setEnabled
+     * @hide
+     */
+    public static final int STATE_DISABLED = 2;
+
+    /**
+     * The overlay is currently enabled. It can be disabled.
+     *
+     * @see IOverlayManager#setEnabled
+     * @hide
+     */
+    public static final int STATE_ENABLED = 3;
+
+    /**
+     * The target package is currently being upgraded or downgraded; the state
+     * will change once the package installation has finished.
+     * @hide
+     *
+     * @deprecated No longer used. Caused invalid transitions from enabled -> upgrading -> enabled,
+     * where an update is propagated when nothing has changed. Can occur during --dont-kill
+     * installs when code and resources are hot swapped and the Activity should not be relaunched.
+     * In all other cases, the process and therefore Activity is killed, so the state loop is
+     * irrelevant.
+     */
+    @Deprecated
+    public static final int STATE_TARGET_IS_BEING_REPLACED = 4;
+
+    /**
+     * The overlay package is currently being upgraded or downgraded; the state
+     * will change once the package installation has finished.
+     * @hide
+     */
+    public static final int STATE_OVERLAY_IS_BEING_REPLACED = 5;
+
+    /**
+     * The overlay package is currently enabled because it is marked as
+     * 'immutable'. It cannot be disabled but will change state if for instance
+     * its target is uninstalled.
+     * @hide
+     */
+    @Deprecated
+    public static final int STATE_ENABLED_IMMUTABLE = 6;
+
+    /**
+     * Overlay category: theme.
+     * <p>
+     * Change how Android (including the status bar, dialogs, ...) looks.
+     *
+     * @hide
+     */
+    public static final String CATEGORY_THEME = "android.theme";
+
+    /**
+     * Package name of the overlay package
+     *
+     * @hide
+     */
+    @NonNull
+    public final String packageName;
+
+    /**
+     * Package name of the target package
+     *
+     * @hide
+     */
+    @NonNull
+    public final String targetPackageName;
+
+    /**
+     * Name of the target overlayable declaration.
+     *
+     * @hide
+     */
+    public final String targetOverlayableName;
+
+    /**
+     * Category of the overlay package
+     *
+     * @hide
+     */
+    public final String category;
+
+    /**
+     * Full path to the base APK for this overlay package
+     * @hide
+     */
+    @NonNull
+    public final String baseCodePath;
+
+    /**
+     * The state of this OverlayInfo as defined by the STATE_* constants in this class.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public final @State int state;
+
+    /**
+     * User handle for which this overlay applies
+     * @hide
+     */
+    public final int userId;
+
+    /**
+     * Priority as configured by {@link com.android.internal.content.om.OverlayConfig}.
+     * Not intended to be exposed to 3rd party.
+     *
+     * @hide
+     */
+    public final int priority;
+
+    /**
+     * isMutable as configured by {@link com.android.internal.content.om.OverlayConfig}.
+     * If false, the overlay is unconditionally loaded and cannot be unloaded. Not intended to be
+     * exposed to 3rd party.
+     *
+     * @hide
+     */
+    public final boolean isMutable;
+
+    /**
+     * Create a new OverlayInfo based on source with an updated state.
+     *
+     * @param source the source OverlayInfo to base the new instance on
+     * @param state the new state for the source OverlayInfo
+     *
+     * @hide
+     */
+    public OverlayInfo(@NonNull OverlayInfo source, @State int state) {
+        this(source.packageName, source.targetPackageName, source.targetOverlayableName,
+                source.category, source.baseCodePath, state, source.userId, source.priority,
+                source.isMutable);
+    }
+
+    /** @hide */
+    public OverlayInfo(@NonNull String packageName, @NonNull String targetPackageName,
+            @Nullable String targetOverlayableName, @Nullable String category,
+            @NonNull String baseCodePath, int state, int userId,
+            int priority, boolean isMutable) {
+        this.packageName = packageName;
+        this.targetPackageName = targetPackageName;
+        this.targetOverlayableName = targetOverlayableName;
+        this.category = category;
+        this.baseCodePath = baseCodePath;
+        this.state = state;
+        this.userId = userId;
+        this.priority = priority;
+        this.isMutable = isMutable;
+        ensureValidState();
+    }
+
+    /** @hide */
+    public OverlayInfo(Parcel source) {
+        packageName = source.readString();
+        targetPackageName = source.readString();
+        targetOverlayableName = source.readString();
+        category = source.readString();
+        baseCodePath = source.readString();
+        state = source.readInt();
+        userId = source.readInt();
+        priority = source.readInt();
+        isMutable = source.readBoolean();
+        ensureValidState();
+    }
+
+    /**
+     * Returns package name of the current overlay.
+     * @hide
+     */
+    @SystemApi
+    @NonNull
+    public String getPackageName() {
+        return packageName;
+    }
+
+    /**
+     * Returns the target package name of the current overlay.
+     * @hide
+     */
+    @SystemApi
+    @NonNull
+    public String getTargetPackageName() {
+        return targetPackageName;
+    }
+
+    /**
+     * Returns the category of the current overlay.
+     * @hide\
+     */
+    @SystemApi
+    @Nullable
+    public String getCategory() {
+        return category;
+    }
+
+    /**
+     * Returns user handle for which this overlay applies to.
+     * @hide
+     */
+    @SystemApi
+    @UserIdInt
+    public int getUserId() {
+        return userId;
+    }
+
+    /**
+     * Returns name of the target overlayable declaration.
+     * @hide
+     */
+    @SystemApi
+    @Nullable
+    public String getTargetOverlayableName() {
+        return targetOverlayableName;
+    }
+
+    @SuppressWarnings("ConstantConditions")
+    private void ensureValidState() {
+        if (packageName == null) {
+            throw new IllegalArgumentException("packageName must not be null");
+        }
+        if (targetPackageName == null) {
+            throw new IllegalArgumentException("targetPackageName must not be null");
+        }
+        if (baseCodePath == null) {
+            throw new IllegalArgumentException("baseCodePath must not be null");
+        }
+        switch (state) {
+            case STATE_UNKNOWN:
+            case STATE_MISSING_TARGET:
+            case STATE_NO_IDMAP:
+            case STATE_DISABLED:
+            case STATE_ENABLED:
+            case STATE_ENABLED_IMMUTABLE:
+            case STATE_TARGET_IS_BEING_REPLACED:
+            case STATE_OVERLAY_IS_BEING_REPLACED:
+                break;
+            default:
+                throw new IllegalArgumentException("State " + state + " is not a valid state");
+        }
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(packageName);
+        dest.writeString(targetPackageName);
+        dest.writeString(targetOverlayableName);
+        dest.writeString(category);
+        dest.writeString(baseCodePath);
+        dest.writeInt(state);
+        dest.writeInt(userId);
+        dest.writeInt(priority);
+        dest.writeBoolean(isMutable);
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<OverlayInfo> CREATOR =
+            new Parcelable.Creator<OverlayInfo>() {
+        @Override
+        public OverlayInfo createFromParcel(Parcel source) {
+            return new OverlayInfo(source);
+        }
+
+        @Override
+        public OverlayInfo[] newArray(int size) {
+            return new OverlayInfo[size];
+        }
+    };
+
+    /**
+     * Return true if this overlay is enabled, i.e. should be used to overlay
+     * the resources in the target package.
+     *
+     * Disabled overlay packages are installed but are currently not in use.
+     *
+     * @return true if the overlay is enabled, else false.
+     * @hide
+     */
+    @SystemApi
+    public boolean isEnabled() {
+        switch (state) {
+            case STATE_ENABLED:
+            case STATE_ENABLED_IMMUTABLE:
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    /**
+     * Translate a state to a human readable string. Only intended for
+     * debugging purposes.
+     *
+     * @return a human readable String representing the state.
+     * @hide
+     */
+    public static String stateToString(@State int state) {
+        switch (state) {
+            case STATE_UNKNOWN:
+                return "STATE_UNKNOWN";
+            case STATE_MISSING_TARGET:
+                return "STATE_MISSING_TARGET";
+            case STATE_NO_IDMAP:
+                return "STATE_NO_IDMAP";
+            case STATE_DISABLED:
+                return "STATE_DISABLED";
+            case STATE_ENABLED:
+                return "STATE_ENABLED";
+            case STATE_ENABLED_IMMUTABLE:
+                return "STATE_ENABLED_IMMUTABLE";
+            case STATE_TARGET_IS_BEING_REPLACED:
+                return "STATE_TARGET_IS_BEING_REPLACED";
+            case STATE_OVERLAY_IS_BEING_REPLACED:
+                return "STATE_OVERLAY_IS_BEING_REPLACED";
+            default:
+                return "<unknown state>";
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + userId;
+        result = prime * result + state;
+        result = prime * result + ((packageName == null) ? 0 : packageName.hashCode());
+        result = prime * result + ((targetPackageName == null) ? 0 : targetPackageName.hashCode());
+        result = prime * result + ((targetOverlayableName == null) ? 0
+                : targetOverlayableName.hashCode());
+        result = prime * result + ((category == null) ? 0 : category.hashCode());
+        result = prime * result + ((baseCodePath == null) ? 0 : baseCodePath.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(@Nullable Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        OverlayInfo other = (OverlayInfo) obj;
+        if (userId != other.userId) {
+            return false;
+        }
+        if (state != other.state) {
+            return false;
+        }
+        if (!packageName.equals(other.packageName)) {
+            return false;
+        }
+        if (!targetPackageName.equals(other.targetPackageName)) {
+            return false;
+        }
+        if (!Objects.equals(targetOverlayableName, other.targetOverlayableName)) {
+            return false;
+        }
+        if (!Objects.equals(category, other.category)) {
+            return false;
+        }
+        if (!baseCodePath.equals(other.baseCodePath)) {
+            return false;
+        }
+        return true;
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        return "OverlayInfo { overlay=" + packageName + ", targetPackage=" + targetPackageName
+                + ((targetOverlayableName == null) ? ""
+                : ", targetOverlayable=" + targetOverlayableName)
+                + ", state=" + state + " (" + stateToString(state) + "), userId=" + userId + " }";
+    }
+}
diff --git a/android/content/om/OverlayManager.java b/android/content/om/OverlayManager.java
new file mode 100644
index 0000000..2bdca7d
--- /dev/null
+++ b/android/content/om/OverlayManager.java
@@ -0,0 +1,282 @@
+/*
+ * 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.content.om;
+
+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.compat.Compatibility;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
+import android.content.Context;
+import android.os.Build;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+
+import com.android.server.SystemConfig;
+
+import java.util.List;
+
+/**
+ * Updates OverlayManager state; gets information about installed overlay packages.
+ *
+ * <p>Users of this API must be actors of any overlays they desire to change the state of.</p>
+ *
+ * <p>An actor is a package responsible for managing the state of overlays targeting overlayables
+ * that specify the actor. For example, an actor may enable or disable an overlay or otherwise
+ * change its state.</p>
+ *
+ * <p>Actors are specified as part of the overlayable definition.
+ *
+ * <pre>{@code
+ * <overlayable name="OverlayableResourcesName" actor="overlay://namespace/actorName">
+ * }</pre></p>
+ *
+ * <p>Actors are defined through {@link SystemConfig}. Only system packages can be used.
+ * The namespace "android" is reserved for use by AOSP and any "android" definitions must
+ * have an implementation on device that fulfill their intended functionality.</p>
+ *
+ * <pre>{@code
+ * <named-actor
+ *     namespace="namespace"
+ *     name="actorName"
+ *     package="com.example.pkg"
+ *     />
+ * }</pre></p>
+ *
+ * <p>An actor can manipulate a particular overlay if any of the following is true:
+ * <ul>
+ * <li>its UID is {@link Process#ROOT_UID}, {@link Process#SYSTEM_UID}</li>
+ * <li>it is the target of the overlay package</li>
+ * <li>it has the CHANGE_OVERLAY_PACKAGES permission and the target does not specify an actor</li>
+ * <li>it is the actor specified by the overlayable</li>
+ * </ul></p>
+ *
+ * @hide
+ */
+@SystemApi
+@SystemService(Context.OVERLAY_SERVICE)
+public class OverlayManager {
+
+    private final IOverlayManager mService;
+    private final Context mContext;
+
+    /**
+     * Pre R a {@link java.lang.SecurityException} would only be thrown by setEnabled APIs (e
+     * .g. {@link #setEnabled(String, boolean, UserHandle)}) for a permission error.
+     * Since R this no longer holds true, and {@link java.lang.SecurityException} can be
+     * thrown for any number of reasons, none of which are exposed to the caller.
+     *
+     * <p>To maintain existing API behavior, if a legacy permission failure or actor enforcement
+     * failure occurs for an app not yet targeting R, coerce it into an {@link
+     * java.lang.IllegalStateException}, which existed in the source prior to R.
+     */
+    @ChangeId
+    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
+    private static final long THROW_SECURITY_EXCEPTIONS = 147340954;
+
+    /**
+     * Creates a new instance.
+     *
+     * @param context The current context in which to operate.
+     * @param service The backing system service.
+     *
+     * @hide
+     */
+    public OverlayManager(Context context, IOverlayManager service) {
+        mContext = context;
+        mService = service;
+    }
+
+    /** @hide */
+    public OverlayManager(Context context) {
+        this(context, IOverlayManager.Stub.asInterface(
+            ServiceManager.getService(Context.OVERLAY_SERVICE)));
+    }
+
+    /**
+     * Request that an overlay package is enabled and any other overlay packages with the same
+     * target package and category are disabled.
+     *
+     * If a set of overlay packages share the same category, single call to this method is
+     * equivalent to multiple calls to {@link #setEnabled(String, boolean, UserHandle)}.
+     *
+     * The caller must pass the actor requirements specified in the class comment.
+     *
+     * @param packageName the name of the overlay package to enable.
+     * @param user The user for which to change the overlay.
+     *
+     * @throws SecurityException when caller is not allowed to enable {@param packageName}
+     * @throws IllegalStateException when enabling fails otherwise
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            "android.permission.INTERACT_ACROSS_USERS",
+            "android.permission.INTERACT_ACROSS_USERS_FULL"
+    })
+    public void setEnabledExclusiveInCategory(@NonNull final String packageName,
+            @NonNull UserHandle user) throws SecurityException, IllegalStateException {
+        try {
+            if (!mService.setEnabledExclusiveInCategory(packageName, user.getIdentifier())) {
+                throw new IllegalStateException("setEnabledExclusiveInCategory failed");
+            }
+        } catch (SecurityException e) {
+            rethrowSecurityException(e);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Request that an overlay package is enabled or disabled.
+     *
+     * While {@link #setEnabledExclusiveInCategory(String, UserHandle)} doesn't support disabling
+     * every overlay in a category, this method allows you to disable everything.
+     *
+     * The caller must pass the actor requirements specified in the class comment.
+     *
+     * @param packageName the name of the overlay package to enable.
+     * @param enable {@code false} if the overlay should be turned off.
+     * @param user The user for which to change the overlay.
+     *
+     * @throws SecurityException when caller is not allowed to enable/disable {@param packageName}
+     * @throws IllegalStateException when enabling/disabling fails otherwise
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            "android.permission.INTERACT_ACROSS_USERS",
+            "android.permission.INTERACT_ACROSS_USERS_FULL"
+    })
+    public void setEnabled(@NonNull final String packageName, final boolean enable,
+            @NonNull UserHandle user) throws SecurityException, IllegalStateException {
+        try {
+            if (!mService.setEnabled(packageName, enable, user.getIdentifier())) {
+                throw new IllegalStateException("setEnabled failed");
+            }
+        } catch (SecurityException e) {
+            rethrowSecurityException(e);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns information about the overlay with the given package name for
+     * the specified user.
+     *
+     * @param packageName The name of the package.
+     * @param userHandle The user to get the OverlayInfos for.
+     * @return An OverlayInfo object; if no overlays exist with the
+     *         requested package name, null is returned.
+     *
+     * @hide
+     */
+    @SystemApi
+    @Nullable
+    public OverlayInfo getOverlayInfo(@NonNull final String packageName,
+            @NonNull final UserHandle userHandle) {
+        try {
+            return mService.getOverlayInfo(packageName, userHandle.getIdentifier());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns information about all overlays for the given target package for
+     * the specified user. The returned list is ordered according to the
+     * overlay priority with the highest priority at the end of the list.
+     *
+     * @param targetPackageName The name of the target package.
+     * @param user The user to get the OverlayInfos for.
+     * @return A list of OverlayInfo objects; if no overlays exist for the
+     *         requested package, an empty list is returned.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            "android.permission.INTERACT_ACROSS_USERS",
+            "android.permission.INTERACT_ACROSS_USERS_FULL"
+    })
+    @NonNull
+    public List<OverlayInfo> getOverlayInfosForTarget(@NonNull final String targetPackageName,
+            @NonNull UserHandle user) {
+        try {
+            return mService.getOverlayInfosForTarget(targetPackageName, user.getIdentifier());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Clear part of the overlay manager's internal cache of PackageInfo
+     * objects. Only intended for testing.
+     *
+     * @param targetPackageName The name of the target package.
+     * @param user The user to get the OverlayInfos for.
+     *
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(anyOf = {
+            "android.permission.INTERACT_ACROSS_USERS",
+    })
+    @NonNull
+    public void invalidateCachesForOverlay(@NonNull final String targetPackageName,
+            @NonNull UserHandle user) {
+        try {
+            mService.invalidateCachesForOverlay(targetPackageName, user.getIdentifier());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Starting on R, actor enforcement and app visibility changes introduce additional failure
+     * cases, but the SecurityException thrown with these checks is unexpected for existing
+     * consumers of the API.
+     *
+     * The only prior case it would be thrown is with a permission failure, but the calling
+     * application would be able to verify that themselves, and so they may choose to ignore
+     * catching SecurityException when calling these APIs.
+     *
+     * For R, this no longer holds true, and SecurityExceptions can be thrown for any number of
+     * reasons, none of which are exposed to the caller. So for consumers targeting below R,
+     * transform these SecurityExceptions into IllegalStateExceptions, which are a little more
+     * expected to be thrown by the setEnabled APIs.
+     *
+     * This will mask the prior permission exception if it applies, but it's assumed that apps
+     * wouldn't call the APIs without the permission on prior versions, and so it's safe to ignore.
+     */
+    private void rethrowSecurityException(SecurityException e) {
+        if (!Compatibility.isChangeEnabled(THROW_SECURITY_EXCEPTIONS)) {
+            throw new IllegalStateException(e);
+        } else {
+            throw e;
+        }
+    }
+}
diff --git a/android/content/om/OverlayableInfo.java b/android/content/om/OverlayableInfo.java
new file mode 100644
index 0000000..5923907
--- /dev/null
+++ b/android/content/om/OverlayableInfo.java
@@ -0,0 +1,120 @@
+/*
+ * 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.content.om;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.internal.util.DataClass;
+
+import java.util.Objects;
+
+/**
+ * Immutable info on an overlayable defined inside a target package.
+ *
+ * @hide
+ */
+@DataClass(genSetters = false, genEqualsHashCode = true, genHiddenConstructor = true)
+public final class OverlayableInfo {
+
+    /**
+     * The "name" attribute of the overlayable tag. Used to identify the set of resources overlaid.
+     */
+    @NonNull
+    public final String name;
+
+    /**
+     * The "actor" attribute of the overlayable tag. Used to signal which apps are allowed to
+     * modify overlay state for this overlayable.
+     */
+    @Nullable
+    public final String actor;
+
+    // CHECKSTYLE:OFF Generated code
+    //
+
+
+
+    // Code below generated by codegen v1.0.3.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/om/OverlayableInfo.java
+
+
+    /**
+     * Creates a new OverlayableInfo.
+     *
+     * @param name
+     *   The "name" attribute of the overlayable tag. Used to identify the set of resources overlaid.
+     * @param actor
+     *   The "actor" attribute of the overlayable tag. Used to signal which apps are allowed to
+     *   modify overlay state for this overlayable.
+     * @hide
+     */
+    @DataClass.Generated.Member
+    public OverlayableInfo(
+            @NonNull String name,
+            @Nullable String actor) {
+        this.name = name;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, name);
+        this.actor = actor;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public boolean equals(Object o) {
+        // You can override field equality logic by defining either of the methods like:
+        // boolean fieldNameEquals(OverlayableInfo other) { ... }
+        // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        @SuppressWarnings("unchecked")
+        OverlayableInfo that = (OverlayableInfo) o;
+        //noinspection PointlessBooleanExpression
+        return true
+                && Objects.equals(name, that.name)
+                && Objects.equals(actor, that.actor);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int hashCode() {
+        // You can override field hashCode logic by defining methods like:
+        // int fieldNameHashCode() { ... }
+
+        int _hash = 1;
+        _hash = 31 * _hash + Objects.hashCode(name);
+        _hash = 31 * _hash + Objects.hashCode(actor);
+        return _hash;
+    }
+
+    @DataClass.Generated(
+            time = 1570059850579L,
+            codegenVersion = "1.0.3",
+            sourceFile = "frameworks/base/core/java/android/content/om/OverlayableInfo.java",
+            inputSignatures = "public final @android.annotation.NonNull java.lang.String name\npublic final @android.annotation.Nullable java.lang.String actor\nclass OverlayableInfo extends java.lang.Object implements []\[email protected](genSetters=false, genEqualsHashCode=true, genHiddenConstructor=true)")
+    @Deprecated
+    private void __metadata() {}
+
+}
diff --git a/android/content/pm/ActivityInfo.java b/android/content/pm/ActivityInfo.java
new file mode 100644
index 0000000..b1f8869
--- /dev/null
+++ b/android/content/pm/ActivityInfo.java
@@ -0,0 +1,1482 @@
+/*
+ * 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.content.pm;
+
+import android.annotation.IntDef;
+import android.annotation.TestApi;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.content.res.Configuration.NativeConfig;
+import android.content.res.TypedArray;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Printer;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Information you can retrieve about a particular application
+ * activity or receiver. This corresponds to information collected
+ * from the AndroidManifest.xml's &lt;activity&gt; and
+ * &lt;receiver&gt; tags.
+ */
+public class ActivityInfo extends ComponentInfo implements Parcelable {
+
+     // NOTE: When adding new data members be sure to update the copy-constructor, Parcel
+     // constructor, and writeToParcel.
+
+    /**
+     * A style resource identifier (in the package's resources) of this
+     * activity's theme.  From the "theme" attribute or, if not set, 0.
+     */
+    public int theme;
+
+    /**
+     * Constant corresponding to <code>standard</code> in
+     * the {@link android.R.attr#launchMode} attribute.
+     */
+    public static final int LAUNCH_MULTIPLE = 0;
+    /**
+     * Constant corresponding to <code>singleTop</code> in
+     * the {@link android.R.attr#launchMode} attribute.
+     */
+    public static final int LAUNCH_SINGLE_TOP = 1;
+    /**
+     * Constant corresponding to <code>singleTask</code> in
+     * the {@link android.R.attr#launchMode} attribute.
+     */
+    public static final int LAUNCH_SINGLE_TASK = 2;
+    /**
+     * Constant corresponding to <code>singleInstance</code> in
+     * the {@link android.R.attr#launchMode} attribute.
+     */
+    public static final int LAUNCH_SINGLE_INSTANCE = 3;
+    /**
+     * The launch mode style requested by the activity.  From the
+     * {@link android.R.attr#launchMode} attribute, one of
+     * {@link #LAUNCH_MULTIPLE},
+     * {@link #LAUNCH_SINGLE_TOP}, {@link #LAUNCH_SINGLE_TASK}, or
+     * {@link #LAUNCH_SINGLE_INSTANCE}.
+     */
+    public int launchMode;
+
+    /**
+     * Constant corresponding to <code>none</code> in
+     * the {@link android.R.attr#documentLaunchMode} attribute.
+     */
+    public static final int DOCUMENT_LAUNCH_NONE = 0;
+    /**
+     * Constant corresponding to <code>intoExisting</code> in
+     * the {@link android.R.attr#documentLaunchMode} attribute.
+     */
+    public static final int DOCUMENT_LAUNCH_INTO_EXISTING = 1;
+    /**
+     * Constant corresponding to <code>always</code> in
+     * the {@link android.R.attr#documentLaunchMode} attribute.
+     */
+    public static final int DOCUMENT_LAUNCH_ALWAYS = 2;
+    /**
+     * Constant corresponding to <code>never</code> in
+     * the {@link android.R.attr#documentLaunchMode} attribute.
+     */
+    public static final int DOCUMENT_LAUNCH_NEVER = 3;
+    /**
+     * The document launch mode style requested by the activity. From the
+     * {@link android.R.attr#documentLaunchMode} attribute, one of
+     * {@link #DOCUMENT_LAUNCH_NONE}, {@link #DOCUMENT_LAUNCH_INTO_EXISTING},
+     * {@link #DOCUMENT_LAUNCH_ALWAYS}.
+     *
+     * <p>Modes DOCUMENT_LAUNCH_ALWAYS
+     * and DOCUMENT_LAUNCH_INTO_EXISTING are equivalent to {@link
+     * android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT
+     * Intent.FLAG_ACTIVITY_NEW_DOCUMENT} with and without {@link
+     * android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK
+     * Intent.FLAG_ACTIVITY_MULTIPLE_TASK} respectively.
+     */
+    public int documentLaunchMode;
+
+    /**
+     * Constant corresponding to <code>persistRootOnly</code> in
+     * the {@link android.R.attr#persistableMode} attribute.
+     */
+    public static final int PERSIST_ROOT_ONLY = 0;
+    /**
+     * Constant corresponding to <code>doNotPersist</code> in
+     * the {@link android.R.attr#persistableMode} attribute.
+     */
+    public static final int PERSIST_NEVER = 1;
+    /**
+     * Constant corresponding to <code>persistAcrossReboots</code> in
+     * the {@link android.R.attr#persistableMode} attribute.
+     */
+    public static final int PERSIST_ACROSS_REBOOTS = 2;
+    /**
+     * Value indicating how this activity is to be persisted across
+     * reboots for restoring in the Recents list.
+     * {@link android.R.attr#persistableMode}
+     */
+    public int persistableMode;
+
+    /**
+     * The maximum number of tasks rooted at this activity that can be in the recent task list.
+     * Refer to {@link android.R.attr#maxRecents}.
+     */
+    public int maxRecents;
+
+    /**
+     * Optional name of a permission required to be able to access this
+     * Activity.  From the "permission" attribute.
+     */
+    public String permission;
+
+    /**
+     * The affinity this activity has for another task in the system.  The
+     * string here is the name of the task, often the package name of the
+     * overall package.  If null, the activity has no affinity.  Set from the
+     * {@link android.R.attr#taskAffinity} attribute.
+     */
+    public String taskAffinity;
+
+    /**
+     * If this is an activity alias, this is the real activity class to run
+     * for it.  Otherwise, this is null.
+     */
+    public String targetActivity;
+
+    /**
+     * Token used to string together multiple events within a single launch action.
+     * @hide
+     */
+    public String launchToken;
+
+    /**
+     * Activity can not be resized and always occupies the fullscreen area with all windows fully
+     * visible.
+     * @hide
+     */
+    public static final int RESIZE_MODE_UNRESIZEABLE = 0;
+    /**
+     * Activity didn't explicitly request to be resizeable, but we are making it resizeable because
+     * of the SDK version it targets. Only affects apps with target SDK >= N where the app is
+     * implied to be resizeable if it doesn't explicitly set the attribute to any value.
+     * @hide
+     */
+    public static final int RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION = 1;
+    /**
+     * Activity explicitly requested to be resizeable.
+     * @hide
+     */
+    @TestApi
+    public static final int RESIZE_MODE_RESIZEABLE = 2;
+    /**
+     * Activity is resizeable and supported picture-in-picture mode.  This flag is now deprecated
+     * since activities do not need to be resizeable to support picture-in-picture.
+     * See {@link #FLAG_SUPPORTS_PICTURE_IN_PICTURE}.
+     *
+     * @hide
+     * @deprecated
+     */
+    public static final int RESIZE_MODE_RESIZEABLE_AND_PIPABLE_DEPRECATED = 3;
+    /**
+     * Activity does not support resizing, but we are forcing it to be resizeable. Only affects
+     * certain pre-N apps where we force them to be resizeable.
+     * @hide
+     */
+    public static final int RESIZE_MODE_FORCE_RESIZEABLE = 4;
+    /**
+     * Activity does not support resizing, but we are forcing it to be resizeable as long
+     * as the size remains landscape.
+     * @hide
+     */
+    public static final int RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY = 5;
+    /**
+     * Activity does not support resizing, but we are forcing it to be resizeable as long
+     * as the size remains portrait.
+     * @hide
+     */
+    public static final int RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY = 6;
+    /**
+     * Activity does not support resizing, but we are forcing it to be resizeable as long
+     * as the bounds remain in the same orientation as they are.
+     * @hide
+     */
+    public static final int RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION = 7;
+    /**
+     * Value indicating if the resizing mode the activity supports.
+     * See {@link android.R.attr#resizeableActivity}.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public int resizeMode = RESIZE_MODE_RESIZEABLE;
+
+    /**
+     * Value indicating the maximum aspect ratio the activity supports.
+     * <p>
+     * 0 means unset.
+     * @See {@link android.R.attr#maxAspectRatio}.
+     * @hide
+     */
+    public float maxAspectRatio;
+
+    /**
+     * Value indicating the minimum aspect ratio the activity supports.
+     * <p>
+     * 0 means unset.
+     * @See {@link android.R.attr#minAspectRatio}.
+     * @hide
+     */
+    public float minAspectRatio;
+
+    /**
+     * Name of the VrListenerService component to run for this activity.
+     * @see android.R.attr#enableVrMode
+     * @hide
+     */
+    public String requestedVrComponent;
+
+    /**
+     * Value for {@link #colorMode} indicating that the activity should use the
+     * default color mode (sRGB, low dynamic range).
+     *
+     * @see android.R.attr#colorMode
+     */
+    public static final int COLOR_MODE_DEFAULT = 0;
+    /**
+     * Value of {@link #colorMode} indicating that the activity should use a
+     * wide color gamut if the presentation display supports it.
+     *
+     * @see android.R.attr#colorMode
+     */
+    public static final int COLOR_MODE_WIDE_COLOR_GAMUT = 1;
+    /**
+     * Value of {@link #colorMode} indicating that the activity should use a
+     * high dynamic range if the presentation display supports it.
+     *
+     * @see android.R.attr#colorMode
+     */
+    public static final int COLOR_MODE_HDR = 2;
+
+    /** @hide */
+    @IntDef(prefix = { "COLOR_MODE_" }, value = {
+            COLOR_MODE_DEFAULT,
+            COLOR_MODE_WIDE_COLOR_GAMUT,
+            COLOR_MODE_HDR,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ColorMode {}
+
+    /**
+     * The color mode requested by this activity. The target display may not be
+     * able to honor the request.
+     */
+    @ColorMode
+    public int colorMode = COLOR_MODE_DEFAULT;
+
+    /**
+     * Bit in {@link #flags} indicating whether this activity is able to
+     * run in multiple processes.  If
+     * true, the system may instantiate it in the some process as the
+     * process starting it in order to conserve resources.  If false, the
+     * default, it always runs in {@link #processName}.  Set from the
+     * {@link android.R.attr#multiprocess} attribute.
+     */
+    public static final int FLAG_MULTIPROCESS = 0x0001;
+    /**
+     * Bit in {@link #flags} indicating that, when the activity's task is
+     * relaunched from home, this activity should be finished.
+     * Set from the
+     * {@link android.R.attr#finishOnTaskLaunch} attribute.
+     */
+    public static final int FLAG_FINISH_ON_TASK_LAUNCH = 0x0002;
+    /**
+     * Bit in {@link #flags} indicating that, when the activity is the root
+     * of a task, that task's stack should be cleared each time the user
+     * re-launches it from home.  As a result, the user will always
+     * return to the original activity at the top of the task.
+     * This flag only applies to activities that
+     * are used to start the root of a new task.  Set from the
+     * {@link android.R.attr#clearTaskOnLaunch} attribute.
+     */
+    public static final int FLAG_CLEAR_TASK_ON_LAUNCH = 0x0004;
+    /**
+     * Bit in {@link #flags} indicating that, when the activity is the root
+     * of a task, that task's stack should never be cleared when it is
+     * relaunched from home.  Set from the
+     * {@link android.R.attr#alwaysRetainTaskState} attribute.
+     */
+    public static final int FLAG_ALWAYS_RETAIN_TASK_STATE = 0x0008;
+    /**
+     * Bit in {@link #flags} indicating that the activity's state
+     * is not required to be saved, so that if there is a failure the
+     * activity will not be removed from the activity stack.  Set from the
+     * {@link android.R.attr#stateNotNeeded} attribute.
+     */
+    public static final int FLAG_STATE_NOT_NEEDED = 0x0010;
+    /**
+     * Bit in {@link #flags} that indicates that the activity should not
+     * appear in the list of recently launched activities.  Set from the
+     * {@link android.R.attr#excludeFromRecents} attribute.
+     */
+    public static final int FLAG_EXCLUDE_FROM_RECENTS = 0x0020;
+    /**
+     * Bit in {@link #flags} that indicates that the activity can be moved
+     * between tasks based on its task affinity.  Set from the
+     * {@link android.R.attr#allowTaskReparenting} attribute.
+     */
+    public static final int FLAG_ALLOW_TASK_REPARENTING = 0x0040;
+    /**
+     * Bit in {@link #flags} indicating that, when the user navigates away
+     * from an activity, it should be finished.
+     * Set from the
+     * {@link android.R.attr#noHistory} attribute.
+     */
+    public static final int FLAG_NO_HISTORY = 0x0080;
+    /**
+     * Bit in {@link #flags} indicating that, when a request to close system
+     * windows happens, this activity is finished.
+     * Set from the
+     * {@link android.R.attr#finishOnCloseSystemDialogs} attribute.
+     */
+    public static final int FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS = 0x0100;
+    /**
+     * Value for {@link #flags}: true when the application's rendering should
+     * be hardware accelerated.
+     */
+    public static final int FLAG_HARDWARE_ACCELERATED = 0x0200;
+    /**
+     * Value for {@link #flags}: true when the application can be displayed for all users
+     * regardless of if the user of the application is the current user. Set from the
+     * {@link android.R.attr#showForAllUsers} attribute.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static final int FLAG_SHOW_FOR_ALL_USERS = 0x0400;
+    /**
+     * Bit in {@link #flags} corresponding to an immersive activity
+     * that wishes not to be interrupted by notifications.
+     * Applications that hide the system notification bar with
+     * {@link android.view.WindowManager.LayoutParams#FLAG_FULLSCREEN}
+     * may still be interrupted by high-priority notifications; for example, an
+     * incoming phone call may use
+     * {@link android.app.Notification#fullScreenIntent fullScreenIntent}
+     * to present a full-screen in-call activity to the user, pausing the
+     * current activity as a side-effect. An activity with
+     * {@link #FLAG_IMMERSIVE} set, however, will not be interrupted; the
+     * notification may be shown in some other way (such as a small floating
+     * "toast" window).
+     *
+     * Note that this flag will always reflect the Activity's
+     * <code>android:immersive</code> manifest definition, even if the Activity's
+     * immersive state is changed at runtime via
+     * {@link android.app.Activity#setImmersive(boolean)}.
+     *
+     * @see android.app.Notification#FLAG_HIGH_PRIORITY
+     * @see android.app.Activity#setImmersive(boolean)
+     */
+    public static final int FLAG_IMMERSIVE = 0x0800;
+    /**
+     * Bit in {@link #flags}: If set, a task rooted at this activity will have its
+     * baseIntent replaced by the activity immediately above this. Each activity may further
+     * relinquish its identity to the activity above it using this flag. Set from the
+     * {@link android.R.attr#relinquishTaskIdentity} attribute.
+     */
+    public static final int FLAG_RELINQUISH_TASK_IDENTITY = 0x1000;
+    /**
+     * Bit in {@link #flags} indicating that tasks started with this activity are to be
+     * removed from the recent list of tasks when the last activity in the task is finished.
+     * Corresponds to {@link android.R.attr#autoRemoveFromRecents}
+     */
+    public static final int FLAG_AUTO_REMOVE_FROM_RECENTS = 0x2000;
+    /**
+     * Bit in {@link #flags} indicating that this activity can start is creation/resume
+     * while the previous activity is still pausing.  Corresponds to
+     * {@link android.R.attr#resumeWhilePausing}
+     */
+    public static final int FLAG_RESUME_WHILE_PAUSING = 0x4000;
+    /**
+     * Bit in {@link #flags} indicating that this activity should be run with VR mode enabled.
+     *
+     * @see android.app.Activity#setVrModeEnabled(boolean, ComponentName)
+     */
+    public static final int FLAG_ENABLE_VR_MODE = 0x8000;
+
+    /**
+     * Bit in {@link #flags} indicating if the activity is always focusable regardless of if it is
+     * in a task/stack whose activities are normally not focusable.
+     * See android.R.attr#alwaysFocusable.
+     * @hide
+     */
+    public static final int FLAG_ALWAYS_FOCUSABLE = 0x40000;
+
+    /**
+     * Bit in {@link #flags} indicating if the activity is visible to instant
+     * applications. The activity is visible if it's either implicitly or
+     * explicitly exposed.
+     * @hide
+     */
+    public static final int FLAG_VISIBLE_TO_INSTANT_APP = 0x100000;
+
+    /**
+     * Bit in {@link #flags} indicating if the activity is implicitly visible
+     * to instant applications. Implicitly visible activities are those that
+     * implement certain intent-filters:
+     * <ul>
+     * <li>action {@link Intent#CATEGORY_BROWSABLE}</li>
+     * <li>action {@link Intent#ACTION_SEND}</li>
+     * <li>action {@link Intent#ACTION_SENDTO}</li>
+     * <li>action {@link Intent#ACTION_SEND_MULTIPLE}</li>
+     * </ul>
+     * @hide
+     */
+    public static final int FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP = 0x200000;
+
+    /**
+     * Bit in {@link #flags} indicating if the activity supports picture-in-picture mode.
+     * See {@link android.R.attr#supportsPictureInPicture}.
+     * @hide
+     */
+    public static final int FLAG_SUPPORTS_PICTURE_IN_PICTURE = 0x400000;
+
+    /**
+     * Bit in {@link #flags} indicating if the activity should be shown when locked.
+     * See {@link android.R.attr#showWhenLocked}
+     * @hide
+     */
+    public static final int FLAG_SHOW_WHEN_LOCKED = 0x800000;
+
+    /**
+     * Bit in {@link #flags} indicating if the screen should turn on when starting the activity.
+     * See {@link android.R.attr#turnScreenOn}
+     * @hide
+     */
+    public static final int FLAG_TURN_SCREEN_ON = 0x1000000;
+
+    /**
+     * Bit in {@link #flags} indicating whether the display should preferably be switched to a
+     * minimal post processing mode.
+     * See {@link android.R.attr#preferMinimalPostProcessing}
+     */
+    public static final int FLAG_PREFER_MINIMAL_POST_PROCESSING = 0x2000000;
+
+    /**
+     * @hide Bit in {@link #flags}: If set, this component will only be seen
+     * by the system user.  Only works with broadcast receivers.  Set from the
+     * android.R.attr#systemUserOnly attribute.
+     */
+    public static final int FLAG_SYSTEM_USER_ONLY = 0x20000000;
+    /**
+     * Bit in {@link #flags}: If set, a single instance of the receiver will
+     * run for all users on the device.  Set from the
+     * {@link android.R.attr#singleUser} attribute.  Note that this flag is
+     * only relevant for ActivityInfo structures that are describing receiver
+     * components; it is not applied to activities.
+     */
+    public static final int FLAG_SINGLE_USER = 0x40000000;
+    /**
+     * @hide Bit in {@link #flags}: If set, this activity may be launched into an
+     * owned ActivityContainer such as that within an ActivityView. If not set and
+     * this activity is launched into such a container a SecurityException will be
+     * thrown. Set from the {@link android.R.attr#allowEmbedded} attribute.
+     */
+    @UnsupportedAppUsage
+    public static final int FLAG_ALLOW_EMBEDDED = 0x80000000;
+
+    /**
+     * Options that have been set in the activity declaration in the
+     * manifest.
+     * These include:
+     * {@link #FLAG_MULTIPROCESS},
+     * {@link #FLAG_FINISH_ON_TASK_LAUNCH}, {@link #FLAG_CLEAR_TASK_ON_LAUNCH},
+     * {@link #FLAG_ALWAYS_RETAIN_TASK_STATE},
+     * {@link #FLAG_STATE_NOT_NEEDED}, {@link #FLAG_EXCLUDE_FROM_RECENTS},
+     * {@link #FLAG_ALLOW_TASK_REPARENTING}, {@link #FLAG_NO_HISTORY},
+     * {@link #FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS},
+     * {@link #FLAG_HARDWARE_ACCELERATED}, {@link #FLAG_SINGLE_USER}.
+     */
+    public int flags;
+
+    /**
+     * Bit in {@link #privateFlags} indicating if the activity should be shown when locked in case
+     * an activity behind this can also be shown when locked.
+     * See {@link android.R.attr#inheritShowWhenLocked}.
+     * @hide
+     */
+    public static final int FLAG_INHERIT_SHOW_WHEN_LOCKED = 0x1;
+
+    /**
+     * Options that have been set in the activity declaration in the manifest.
+     * These include:
+     * {@link #FLAG_INHERIT_SHOW_WHEN_LOCKED}.
+     * @hide
+     */
+    public int privateFlags;
+
+    /** @hide */
+    @IntDef(prefix = { "SCREEN_ORIENTATION_" }, value = {
+            SCREEN_ORIENTATION_UNSET,
+            SCREEN_ORIENTATION_UNSPECIFIED,
+            SCREEN_ORIENTATION_LANDSCAPE,
+            SCREEN_ORIENTATION_PORTRAIT,
+            SCREEN_ORIENTATION_USER,
+            SCREEN_ORIENTATION_BEHIND,
+            SCREEN_ORIENTATION_SENSOR,
+            SCREEN_ORIENTATION_NOSENSOR,
+            SCREEN_ORIENTATION_SENSOR_LANDSCAPE,
+            SCREEN_ORIENTATION_SENSOR_PORTRAIT,
+            SCREEN_ORIENTATION_REVERSE_LANDSCAPE,
+            SCREEN_ORIENTATION_REVERSE_PORTRAIT,
+            SCREEN_ORIENTATION_FULL_SENSOR,
+            SCREEN_ORIENTATION_USER_LANDSCAPE,
+            SCREEN_ORIENTATION_USER_PORTRAIT,
+            SCREEN_ORIENTATION_FULL_USER,
+            SCREEN_ORIENTATION_LOCKED
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ScreenOrientation {}
+
+    /**
+     * Internal constant used to indicate that the app didn't set a specific orientation value.
+     * Different from {@link #SCREEN_ORIENTATION_UNSPECIFIED} below as the app can set its
+     * orientation to {@link #SCREEN_ORIENTATION_UNSPECIFIED} while this means that the app didn't
+     * set anything. The system will mostly treat this similar to
+     * {@link #SCREEN_ORIENTATION_UNSPECIFIED}.
+     * @hide
+     */
+    public static final int SCREEN_ORIENTATION_UNSET = -2;
+    /**
+     * Constant corresponding to <code>unspecified</code> in
+     * the {@link android.R.attr#screenOrientation} attribute.
+     */
+    public static final int SCREEN_ORIENTATION_UNSPECIFIED = -1;
+    /**
+     * Constant corresponding to <code>landscape</code> in
+     * the {@link android.R.attr#screenOrientation} attribute.
+     */
+    public static final int SCREEN_ORIENTATION_LANDSCAPE = 0;
+    /**
+     * Constant corresponding to <code>portrait</code> in
+     * the {@link android.R.attr#screenOrientation} attribute.
+     */
+    public static final int SCREEN_ORIENTATION_PORTRAIT = 1;
+    /**
+     * Constant corresponding to <code>user</code> in
+     * the {@link android.R.attr#screenOrientation} attribute.
+     */
+    public static final int SCREEN_ORIENTATION_USER = 2;
+    /**
+     * Constant corresponding to <code>behind</code> in
+     * the {@link android.R.attr#screenOrientation} attribute.
+     */
+    public static final int SCREEN_ORIENTATION_BEHIND = 3;
+    /**
+     * Constant corresponding to <code>sensor</code> in
+     * the {@link android.R.attr#screenOrientation} attribute.
+     */
+    public static final int SCREEN_ORIENTATION_SENSOR = 4;
+
+    /**
+     * Constant corresponding to <code>nosensor</code> in
+     * the {@link android.R.attr#screenOrientation} attribute.
+     */
+    public static final int SCREEN_ORIENTATION_NOSENSOR = 5;
+
+    /**
+     * Constant corresponding to <code>sensorLandscape</code> in
+     * the {@link android.R.attr#screenOrientation} attribute.
+     */
+    public static final int SCREEN_ORIENTATION_SENSOR_LANDSCAPE = 6;
+
+    /**
+     * Constant corresponding to <code>sensorPortrait</code> in
+     * the {@link android.R.attr#screenOrientation} attribute.
+     */
+    public static final int SCREEN_ORIENTATION_SENSOR_PORTRAIT = 7;
+
+    /**
+     * Constant corresponding to <code>reverseLandscape</code> in
+     * the {@link android.R.attr#screenOrientation} attribute.
+     */
+    public static final int SCREEN_ORIENTATION_REVERSE_LANDSCAPE = 8;
+
+    /**
+     * Constant corresponding to <code>reversePortrait</code> in
+     * the {@link android.R.attr#screenOrientation} attribute.
+     */
+    public static final int SCREEN_ORIENTATION_REVERSE_PORTRAIT = 9;
+
+    /**
+     * Constant corresponding to <code>fullSensor</code> in
+     * the {@link android.R.attr#screenOrientation} attribute.
+     */
+    public static final int SCREEN_ORIENTATION_FULL_SENSOR = 10;
+
+    /**
+     * Constant corresponding to <code>userLandscape</code> in
+     * the {@link android.R.attr#screenOrientation} attribute.
+     */
+    public static final int SCREEN_ORIENTATION_USER_LANDSCAPE = 11;
+
+    /**
+     * Constant corresponding to <code>userPortrait</code> in
+     * the {@link android.R.attr#screenOrientation} attribute.
+     */
+    public static final int SCREEN_ORIENTATION_USER_PORTRAIT = 12;
+
+    /**
+     * Constant corresponding to <code>fullUser</code> in
+     * the {@link android.R.attr#screenOrientation} attribute.
+     */
+    public static final int SCREEN_ORIENTATION_FULL_USER = 13;
+
+    /**
+     * Constant corresponding to <code>locked</code> in
+     * the {@link android.R.attr#screenOrientation} attribute.
+     */
+    public static final int SCREEN_ORIENTATION_LOCKED = 14;
+
+    /**
+     * The preferred screen orientation this activity would like to run in.
+     * From the {@link android.R.attr#screenOrientation} attribute, one of
+     * {@link #SCREEN_ORIENTATION_UNSPECIFIED},
+     * {@link #SCREEN_ORIENTATION_LANDSCAPE},
+     * {@link #SCREEN_ORIENTATION_PORTRAIT},
+     * {@link #SCREEN_ORIENTATION_USER},
+     * {@link #SCREEN_ORIENTATION_BEHIND},
+     * {@link #SCREEN_ORIENTATION_SENSOR},
+     * {@link #SCREEN_ORIENTATION_NOSENSOR},
+     * {@link #SCREEN_ORIENTATION_SENSOR_LANDSCAPE},
+     * {@link #SCREEN_ORIENTATION_SENSOR_PORTRAIT},
+     * {@link #SCREEN_ORIENTATION_REVERSE_LANDSCAPE},
+     * {@link #SCREEN_ORIENTATION_REVERSE_PORTRAIT},
+     * {@link #SCREEN_ORIENTATION_FULL_SENSOR},
+     * {@link #SCREEN_ORIENTATION_USER_LANDSCAPE},
+     * {@link #SCREEN_ORIENTATION_USER_PORTRAIT},
+     * {@link #SCREEN_ORIENTATION_FULL_USER},
+     * {@link #SCREEN_ORIENTATION_LOCKED},
+     */
+    @ScreenOrientation
+    public int screenOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "CONFIG_" }, value = {
+            CONFIG_MCC,
+            CONFIG_MNC,
+            CONFIG_LOCALE,
+            CONFIG_TOUCHSCREEN,
+            CONFIG_KEYBOARD,
+            CONFIG_KEYBOARD_HIDDEN,
+            CONFIG_NAVIGATION,
+            CONFIG_ORIENTATION,
+            CONFIG_SCREEN_LAYOUT,
+            CONFIG_UI_MODE,
+            CONFIG_SCREEN_SIZE,
+            CONFIG_SMALLEST_SCREEN_SIZE,
+            CONFIG_DENSITY,
+            CONFIG_LAYOUT_DIRECTION,
+            CONFIG_COLOR_MODE,
+            CONFIG_FONT_SCALE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Config {}
+
+    /**
+     * Bit in {@link #configChanges} that indicates that the activity
+     * can itself handle changes to the IMSI MCC.  Set from the
+     * {@link android.R.attr#configChanges} attribute.
+     */
+    public static final int CONFIG_MCC = 0x0001;
+    /**
+     * Bit in {@link #configChanges} that indicates that the activity
+     * can itself handle changes to the IMSI MNC.  Set from the
+     * {@link android.R.attr#configChanges} attribute.
+     */
+    public static final int CONFIG_MNC = 0x0002;
+    /**
+     * Bit in {@link #configChanges} that indicates that the activity
+     * can itself handle changes to the locale.  Set from the
+     * {@link android.R.attr#configChanges} attribute.
+     */
+    public static final int CONFIG_LOCALE = 0x0004;
+    /**
+     * Bit in {@link #configChanges} that indicates that the activity
+     * can itself handle changes to the touchscreen type.  Set from the
+     * {@link android.R.attr#configChanges} attribute.
+     */
+    public static final int CONFIG_TOUCHSCREEN = 0x0008;
+    /**
+     * Bit in {@link #configChanges} that indicates that the activity
+     * can itself handle changes to the keyboard type.  Set from the
+     * {@link android.R.attr#configChanges} attribute.
+     */
+    public static final int CONFIG_KEYBOARD = 0x0010;
+    /**
+     * Bit in {@link #configChanges} that indicates that the activity
+     * can itself handle changes to the keyboard or navigation being hidden/exposed.
+     * Note that inspite of the name, this applies to the changes to any
+     * hidden states: keyboard or navigation.
+     * Set from the {@link android.R.attr#configChanges} attribute.
+     */
+    public static final int CONFIG_KEYBOARD_HIDDEN = 0x0020;
+    /**
+     * Bit in {@link #configChanges} that indicates that the activity
+     * can itself handle changes to the navigation type.  Set from the
+     * {@link android.R.attr#configChanges} attribute.
+     */
+    public static final int CONFIG_NAVIGATION = 0x0040;
+    /**
+     * Bit in {@link #configChanges} that indicates that the activity
+     * can itself handle changes to the screen orientation.  Set from the
+     * {@link android.R.attr#configChanges} attribute.
+     */
+    public static final int CONFIG_ORIENTATION = 0x0080;
+    /**
+     * Bit in {@link #configChanges} that indicates that the activity
+     * can itself handle changes to the screen layout.  Set from the
+     * {@link android.R.attr#configChanges} attribute.
+     */
+    public static final int CONFIG_SCREEN_LAYOUT = 0x0100;
+    /**
+     * Bit in {@link #configChanges} that indicates that the activity
+     * can itself handle the ui mode. Set from the
+     * {@link android.R.attr#configChanges} attribute.
+     */
+    public static final int CONFIG_UI_MODE = 0x0200;
+    /**
+     * Bit in {@link #configChanges} that indicates that the activity
+     * can itself handle the screen size. Set from the
+     * {@link android.R.attr#configChanges} attribute.  This will be
+     * set by default for applications that target an earlier version
+     * than {@link android.os.Build.VERSION_CODES#HONEYCOMB_MR2}...
+     * <b>however</b>, you will not see the bit set here becomes some
+     * applications incorrectly compare {@link #configChanges} against
+     * an absolute value rather than correctly masking out the bits
+     * they are interested in.  Please don't do that, thanks.
+     */
+    public static final int CONFIG_SCREEN_SIZE = 0x0400;
+    /**
+     * Bit in {@link #configChanges} that indicates that the activity
+     * can itself handle the smallest screen size. Set from the
+     * {@link android.R.attr#configChanges} attribute.  This will be
+     * set by default for applications that target an earlier version
+     * than {@link android.os.Build.VERSION_CODES#HONEYCOMB_MR2}...
+     * <b>however</b>, you will not see the bit set here becomes some
+     * applications incorrectly compare {@link #configChanges} against
+     * an absolute value rather than correctly masking out the bits
+     * they are interested in.  Please don't do that, thanks.
+     */
+    public static final int CONFIG_SMALLEST_SCREEN_SIZE = 0x0800;
+    /**
+     * Bit in {@link #configChanges} that indicates that the activity
+     * can itself handle density changes. Set from the
+     * {@link android.R.attr#configChanges} attribute.
+     */
+    public static final int CONFIG_DENSITY = 0x1000;
+    /**
+     * Bit in {@link #configChanges} that indicates that the activity
+     * can itself handle the change to layout direction. Set from the
+     * {@link android.R.attr#configChanges} attribute.
+     */
+    public static final int CONFIG_LAYOUT_DIRECTION = 0x2000;
+    /**
+     * Bit in {@link #configChanges} that indicates that the activity
+     * can itself handle the change to the display color gamut or dynamic
+     * range. Set from the {@link android.R.attr#configChanges} attribute.
+     */
+    public static final int CONFIG_COLOR_MODE = 0x4000;
+    /**
+     * Bit in {@link #configChanges} that indicates that the activity
+     * can itself handle asset path changes.  Set from the {@link android.R.attr#configChanges}
+     * attribute. This is not a core resource configuration, but a higher-level value, so its
+     * constant starts at the high bits.
+     * @hide We do not want apps handling this yet, but we do need some kind of bit for diffs.
+     */
+    public static final int CONFIG_ASSETS_PATHS = 0x80000000;
+    /**
+     * Bit in {@link #configChanges} that indicates that the activity
+     * can itself handle changes to the font scaling factor.  Set from the
+     * {@link android.R.attr#configChanges} attribute.  This is
+     * not a core resource configuration, but a higher-level value, so its
+     * constant starts at the high bits.
+     */
+    public static final int CONFIG_FONT_SCALE = 0x40000000;
+    /**
+     * Bit indicating changes to window configuration that isn't exposed to apps.
+     * This is for internal use only and apps don't handle it.
+     * @hide
+     * {@link Configuration}.
+     */
+    public static final int CONFIG_WINDOW_CONFIGURATION = 0x20000000;
+
+    /** @hide
+     * Unfortunately the constants for config changes in native code are
+     * different from ActivityInfo. :(  Here are the values we should use for the
+     * native side given the bit we have assigned in ActivityInfo.
+     */
+    public static int[] CONFIG_NATIVE_BITS = new int[] {
+        Configuration.NATIVE_CONFIG_MNC,                    // MNC
+        Configuration.NATIVE_CONFIG_MCC,                    // MCC
+        Configuration.NATIVE_CONFIG_LOCALE,                 // LOCALE
+        Configuration.NATIVE_CONFIG_TOUCHSCREEN,            // TOUCH SCREEN
+        Configuration.NATIVE_CONFIG_KEYBOARD,               // KEYBOARD
+        Configuration.NATIVE_CONFIG_KEYBOARD_HIDDEN,        // KEYBOARD HIDDEN
+        Configuration.NATIVE_CONFIG_NAVIGATION,             // NAVIGATION
+        Configuration.NATIVE_CONFIG_ORIENTATION,            // ORIENTATION
+        Configuration.NATIVE_CONFIG_SCREEN_LAYOUT,          // SCREEN LAYOUT
+        Configuration.NATIVE_CONFIG_UI_MODE,                // UI MODE
+        Configuration.NATIVE_CONFIG_SCREEN_SIZE,            // SCREEN SIZE
+        Configuration.NATIVE_CONFIG_SMALLEST_SCREEN_SIZE,   // SMALLEST SCREEN SIZE
+        Configuration.NATIVE_CONFIG_DENSITY,                // DENSITY
+        Configuration.NATIVE_CONFIG_LAYOUTDIR,              // LAYOUT DIRECTION
+        Configuration.NATIVE_CONFIG_COLOR_MODE,             // COLOR_MODE
+    };
+
+    /**
+     * Convert Java change bits to native.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static @NativeConfig int activityInfoConfigJavaToNative(@Config int input) {
+        int output = 0;
+        for (int i = 0; i < CONFIG_NATIVE_BITS.length; i++) {
+            if ((input & (1 << i)) != 0) {
+                output |= CONFIG_NATIVE_BITS[i];
+            }
+        }
+        return output;
+    }
+
+    /**
+     * Convert native change bits to Java.
+     *
+     * @hide
+     */
+    public static @Config int activityInfoConfigNativeToJava(@NativeConfig int input) {
+        int output = 0;
+        for (int i = 0; i < CONFIG_NATIVE_BITS.length; i++) {
+            if ((input & CONFIG_NATIVE_BITS[i]) != 0) {
+                output |= (1 << i);
+            }
+        }
+        return output;
+    }
+
+    /**
+     * @hide
+     * Unfortunately some developers (OpenFeint I am looking at you) have
+     * compared the configChanges bit field against absolute values, so if we
+     * introduce a new bit they break.  To deal with that, we will make sure
+     * the public field will not have a value that breaks them, and let the
+     * framework call here to get the real value.
+     */
+    public int getRealConfigChanged() {
+        return applicationInfo.targetSdkVersion < android.os.Build.VERSION_CODES.HONEYCOMB_MR2
+                ? (configChanges | ActivityInfo.CONFIG_SCREEN_SIZE
+                        | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE)
+                : configChanges;
+    }
+
+    /**
+     * Bit mask of kinds of configuration changes that this activity
+     * can handle itself (without being restarted by the system).
+     * Contains any combination of {@link #CONFIG_FONT_SCALE},
+     * {@link #CONFIG_MCC}, {@link #CONFIG_MNC},
+     * {@link #CONFIG_LOCALE}, {@link #CONFIG_TOUCHSCREEN},
+     * {@link #CONFIG_KEYBOARD}, {@link #CONFIG_NAVIGATION},
+     * {@link #CONFIG_ORIENTATION}, {@link #CONFIG_SCREEN_LAYOUT},
+     * {@link #CONFIG_DENSITY}, {@link #CONFIG_LAYOUT_DIRECTION} and
+     * {@link #CONFIG_COLOR_MODE}.
+     * Set from the {@link android.R.attr#configChanges} attribute.
+     */
+    public int configChanges;
+
+    /**
+     * The desired soft input mode for this activity's main window.
+     * Set from the {@link android.R.attr#windowSoftInputMode} attribute
+     * in the activity's manifest.  May be any of the same values allowed
+     * for {@link android.view.WindowManager.LayoutParams#softInputMode
+     * WindowManager.LayoutParams.softInputMode}.  If 0 (unspecified),
+     * the mode from the theme will be used.
+     */
+    @android.view.WindowManager.LayoutParams.SoftInputModeFlags
+    public int softInputMode;
+
+    /**
+     * The desired extra UI options for this activity and its main window.
+     * Set from the {@link android.R.attr#uiOptions} attribute in the
+     * activity's manifest.
+     */
+    public int uiOptions = 0;
+
+    /**
+     * Flag for use with {@link #uiOptions}.
+     * Indicates that the action bar should put all action items in a separate bar when
+     * the screen is narrow.
+     * <p>This value corresponds to "splitActionBarWhenNarrow" for the {@link #uiOptions} XML
+     * attribute.
+     */
+    public static final int UIOPTION_SPLIT_ACTION_BAR_WHEN_NARROW = 1;
+
+    /**
+     * If defined, the activity named here is the logical parent of this activity.
+     */
+    public String parentActivityName;
+
+    /**
+     * Screen rotation animation desired by the activity, with values as defined
+     * for {@link android.view.WindowManager.LayoutParams#rotationAnimation}.
+     *
+     * -1 means to use the system default.
+     *
+     * @hide
+     */
+    public int rotationAnimation = -1;
+
+    /** @hide */
+    public static final int LOCK_TASK_LAUNCH_MODE_DEFAULT = 0;
+    /** @hide */
+    public static final int LOCK_TASK_LAUNCH_MODE_NEVER = 1;
+    /** @hide */
+    public static final int LOCK_TASK_LAUNCH_MODE_ALWAYS = 2;
+    /** @hide */
+    public static final int LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED = 3;
+
+    /** @hide */
+    public static final String lockTaskLaunchModeToString(int lockTaskLaunchMode) {
+        switch (lockTaskLaunchMode) {
+            case LOCK_TASK_LAUNCH_MODE_DEFAULT:
+                return "LOCK_TASK_LAUNCH_MODE_DEFAULT";
+            case LOCK_TASK_LAUNCH_MODE_NEVER:
+                return "LOCK_TASK_LAUNCH_MODE_NEVER";
+            case LOCK_TASK_LAUNCH_MODE_ALWAYS:
+                return "LOCK_TASK_LAUNCH_MODE_ALWAYS";
+            case LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED:
+                return "LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED";
+            default:
+                return "unknown=" + lockTaskLaunchMode;
+        }
+    }
+    /**
+     * Value indicating if the activity is to be locked at startup. Takes on the values from
+     * {@link android.R.attr#lockTaskMode}.
+     * @hide
+     */
+    public int lockTaskLaunchMode;
+
+    /**
+     * Information about desired position and size of activity on the display when
+     * it is first started.
+     */
+    public WindowLayout windowLayout;
+
+    public ActivityInfo() {
+    }
+
+    public ActivityInfo(ActivityInfo orig) {
+        super(orig);
+        theme = orig.theme;
+        launchMode = orig.launchMode;
+        documentLaunchMode = orig.documentLaunchMode;
+        permission = orig.permission;
+        taskAffinity = orig.taskAffinity;
+        targetActivity = orig.targetActivity;
+        flags = orig.flags;
+        privateFlags = orig.privateFlags;
+        screenOrientation = orig.screenOrientation;
+        configChanges = orig.configChanges;
+        softInputMode = orig.softInputMode;
+        uiOptions = orig.uiOptions;
+        parentActivityName = orig.parentActivityName;
+        maxRecents = orig.maxRecents;
+        lockTaskLaunchMode = orig.lockTaskLaunchMode;
+        windowLayout = orig.windowLayout;
+        resizeMode = orig.resizeMode;
+        requestedVrComponent = orig.requestedVrComponent;
+        rotationAnimation = orig.rotationAnimation;
+        colorMode = orig.colorMode;
+        maxAspectRatio = orig.maxAspectRatio;
+        minAspectRatio = orig.minAspectRatio;
+    }
+
+    /**
+     * Return the theme resource identifier to use for this activity.  If
+     * the activity defines a theme, that is used; else, the application
+     * theme is used.
+     *
+     * @return The theme associated with this activity.
+     */
+    public final int getThemeResource() {
+        return theme != 0 ? theme : applicationInfo.theme;
+    }
+
+    private String persistableModeToString() {
+        switch(persistableMode) {
+            case PERSIST_ROOT_ONLY: return "PERSIST_ROOT_ONLY";
+            case PERSIST_NEVER: return "PERSIST_NEVER";
+            case PERSIST_ACROSS_REBOOTS: return "PERSIST_ACROSS_REBOOTS";
+            default: return "UNKNOWN=" + persistableMode;
+        }
+    }
+
+    /**
+     * Returns true if the activity has maximum or minimum aspect ratio.
+     * @hide
+     */
+    public boolean hasFixedAspectRatio() {
+        return maxAspectRatio != 0 || minAspectRatio != 0;
+    }
+
+    /**
+     * Returns true if the activity's orientation is fixed.
+     * @hide
+     */
+    public boolean isFixedOrientation() {
+        return isFixedOrientationLandscape() || isFixedOrientationPortrait()
+                || screenOrientation == SCREEN_ORIENTATION_LOCKED;
+    }
+
+    /**
+     * Returns true if the activity's orientation is fixed to landscape.
+     * @hide
+     */
+    boolean isFixedOrientationLandscape() {
+        return isFixedOrientationLandscape(screenOrientation);
+    }
+
+    /**
+     * Returns true if the activity's orientation is fixed to landscape.
+     * @hide
+     */
+    public static boolean isFixedOrientationLandscape(@ScreenOrientation int orientation) {
+        return orientation == SCREEN_ORIENTATION_LANDSCAPE
+                || orientation == SCREEN_ORIENTATION_SENSOR_LANDSCAPE
+                || orientation == SCREEN_ORIENTATION_REVERSE_LANDSCAPE
+                || orientation == SCREEN_ORIENTATION_USER_LANDSCAPE;
+    }
+
+    /**
+     * Returns true if the activity's orientation is fixed to portrait.
+     * @hide
+     */
+    boolean isFixedOrientationPortrait() {
+        return isFixedOrientationPortrait(screenOrientation);
+    }
+
+    /**
+     * Returns true if the activity's orientation is fixed to portrait.
+     * @hide
+     */
+    public static boolean isFixedOrientationPortrait(@ScreenOrientation int orientation) {
+        return orientation == SCREEN_ORIENTATION_PORTRAIT
+                || orientation == SCREEN_ORIENTATION_SENSOR_PORTRAIT
+                || orientation == SCREEN_ORIENTATION_REVERSE_PORTRAIT
+                || orientation == SCREEN_ORIENTATION_USER_PORTRAIT;
+    }
+
+    /**
+     * Returns true if the activity supports picture-in-picture.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public boolean supportsPictureInPicture() {
+        return (flags & FLAG_SUPPORTS_PICTURE_IN_PICTURE) != 0;
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage
+    public static boolean isResizeableMode(int mode) {
+        return mode == RESIZE_MODE_RESIZEABLE
+                || mode == RESIZE_MODE_FORCE_RESIZEABLE
+                || mode == RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY
+                || mode == RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY
+                || mode == RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION
+                || mode == RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
+    }
+
+    /** @hide */
+    public static boolean isPreserveOrientationMode(int mode) {
+        return mode == RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY
+                || mode == RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY
+                || mode == RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION;
+    }
+
+    /** @hide */
+    public static String resizeModeToString(int mode) {
+        switch (mode) {
+            case RESIZE_MODE_UNRESIZEABLE:
+                return "RESIZE_MODE_UNRESIZEABLE";
+            case RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION:
+                return "RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION";
+            case RESIZE_MODE_RESIZEABLE:
+                return "RESIZE_MODE_RESIZEABLE";
+            case RESIZE_MODE_FORCE_RESIZEABLE:
+                return "RESIZE_MODE_FORCE_RESIZEABLE";
+            case RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY:
+                return "RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY";
+            case RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY:
+                return "RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY";
+            case RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION:
+                return "RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION";
+            default:
+                return "unknown=" + mode;
+        }
+    }
+
+    public void dump(Printer pw, String prefix) {
+        dump(pw, prefix, DUMP_FLAG_ALL);
+    }
+
+    /** @hide */
+    public void dump(Printer pw, String prefix, int dumpFlags) {
+        super.dumpFront(pw, prefix);
+        if (permission != null) {
+            pw.println(prefix + "permission=" + permission);
+        }
+        if ((dumpFlags & DUMP_FLAG_DETAILS) != 0) {
+            pw.println(prefix + "taskAffinity=" + taskAffinity
+                    + " targetActivity=" + targetActivity
+                    + " persistableMode=" + persistableModeToString());
+        }
+        if (launchMode != 0 || flags != 0 || privateFlags != 0 || theme != 0) {
+            pw.println(prefix + "launchMode=" + launchMode
+                    + " flags=0x" + Integer.toHexString(flags)
+                    + " privateFlags=0x" + Integer.toHexString(privateFlags)
+                    + " theme=0x" + Integer.toHexString(theme));
+        }
+        if (screenOrientation != SCREEN_ORIENTATION_UNSPECIFIED
+                || configChanges != 0 || softInputMode != 0) {
+            pw.println(prefix + "screenOrientation=" + screenOrientation
+                    + " configChanges=0x" + Integer.toHexString(configChanges)
+                    + " softInputMode=0x" + Integer.toHexString(softInputMode));
+        }
+        if (uiOptions != 0) {
+            pw.println(prefix + " uiOptions=0x" + Integer.toHexString(uiOptions));
+        }
+        if ((dumpFlags & DUMP_FLAG_DETAILS) != 0) {
+            pw.println(prefix + "lockTaskLaunchMode="
+                    + lockTaskLaunchModeToString(lockTaskLaunchMode));
+        }
+        if (windowLayout != null) {
+            pw.println(prefix + "windowLayout=" + windowLayout.width + "|"
+                    + windowLayout.widthFraction + ", " + windowLayout.height + "|"
+                    + windowLayout.heightFraction + ", " + windowLayout.gravity);
+        }
+        pw.println(prefix + "resizeMode=" + resizeModeToString(resizeMode));
+        if (requestedVrComponent != null) {
+            pw.println(prefix + "requestedVrComponent=" + requestedVrComponent);
+        }
+        if (maxAspectRatio != 0) {
+            pw.println(prefix + "maxAspectRatio=" + maxAspectRatio);
+        }
+        if (minAspectRatio != 0) {
+            pw.println(prefix + "minAspectRatio=" + minAspectRatio);
+        }
+        super.dumpBack(pw, prefix, dumpFlags);
+    }
+
+    public String toString() {
+        return "ActivityInfo{"
+            + Integer.toHexString(System.identityHashCode(this))
+            + " " + name + "}";
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel dest, int parcelableFlags) {
+        super.writeToParcel(dest, parcelableFlags);
+        dest.writeInt(theme);
+        dest.writeInt(launchMode);
+        dest.writeInt(documentLaunchMode);
+        dest.writeString8(permission);
+        dest.writeString8(taskAffinity);
+        dest.writeString8(targetActivity);
+        dest.writeString8(launchToken);
+        dest.writeInt(flags);
+        dest.writeInt(privateFlags);
+        dest.writeInt(screenOrientation);
+        dest.writeInt(configChanges);
+        dest.writeInt(softInputMode);
+        dest.writeInt(uiOptions);
+        dest.writeString8(parentActivityName);
+        dest.writeInt(persistableMode);
+        dest.writeInt(maxRecents);
+        dest.writeInt(lockTaskLaunchMode);
+        if (windowLayout != null) {
+            dest.writeInt(1);
+            windowLayout.writeToParcel(dest);
+        } else {
+            dest.writeInt(0);
+        }
+        dest.writeInt(resizeMode);
+        dest.writeString8(requestedVrComponent);
+        dest.writeInt(rotationAnimation);
+        dest.writeInt(colorMode);
+        dest.writeFloat(maxAspectRatio);
+        dest.writeFloat(minAspectRatio);
+    }
+
+    /**
+     * Determines whether the {@link Activity} is considered translucent or floating.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    @TestApi
+    public static boolean isTranslucentOrFloating(TypedArray attributes) {
+        final boolean isTranslucent =
+                attributes.getBoolean(com.android.internal.R.styleable.Window_windowIsTranslucent,
+                        false);
+        final boolean isFloating =
+                attributes.getBoolean(com.android.internal.R.styleable.Window_windowIsFloating,
+                        false);
+
+        return isFloating || isTranslucent;
+    }
+
+    /**
+     * Convert the screen orientation constant to a human readable format.
+     * @hide
+     */
+    public static String screenOrientationToString(int orientation) {
+        switch (orientation) {
+            case SCREEN_ORIENTATION_UNSET:
+                return "SCREEN_ORIENTATION_UNSET";
+            case SCREEN_ORIENTATION_UNSPECIFIED:
+                return "SCREEN_ORIENTATION_UNSPECIFIED";
+            case SCREEN_ORIENTATION_LANDSCAPE:
+                return "SCREEN_ORIENTATION_LANDSCAPE";
+            case SCREEN_ORIENTATION_PORTRAIT:
+                return "SCREEN_ORIENTATION_PORTRAIT";
+            case SCREEN_ORIENTATION_USER:
+                return "SCREEN_ORIENTATION_USER";
+            case SCREEN_ORIENTATION_BEHIND:
+                return "SCREEN_ORIENTATION_BEHIND";
+            case SCREEN_ORIENTATION_SENSOR:
+                return "SCREEN_ORIENTATION_SENSOR";
+            case SCREEN_ORIENTATION_NOSENSOR:
+                return "SCREEN_ORIENTATION_NOSENSOR";
+            case SCREEN_ORIENTATION_SENSOR_LANDSCAPE:
+                return "SCREEN_ORIENTATION_SENSOR_LANDSCAPE";
+            case SCREEN_ORIENTATION_SENSOR_PORTRAIT:
+                return "SCREEN_ORIENTATION_SENSOR_PORTRAIT";
+            case SCREEN_ORIENTATION_REVERSE_LANDSCAPE:
+                return "SCREEN_ORIENTATION_REVERSE_LANDSCAPE";
+            case SCREEN_ORIENTATION_REVERSE_PORTRAIT:
+                return "SCREEN_ORIENTATION_REVERSE_PORTRAIT";
+            case SCREEN_ORIENTATION_FULL_SENSOR:
+                return "SCREEN_ORIENTATION_FULL_SENSOR";
+            case SCREEN_ORIENTATION_USER_LANDSCAPE:
+                return "SCREEN_ORIENTATION_USER_LANDSCAPE";
+            case SCREEN_ORIENTATION_USER_PORTRAIT:
+                return "SCREEN_ORIENTATION_USER_PORTRAIT";
+            case SCREEN_ORIENTATION_FULL_USER:
+                return "SCREEN_ORIENTATION_FULL_USER";
+            case SCREEN_ORIENTATION_LOCKED:
+                return "SCREEN_ORIENTATION_LOCKED";
+            default:
+                return Integer.toString(orientation);
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public static String colorModeToString(@ColorMode int colorMode) {
+        switch (colorMode) {
+            case COLOR_MODE_DEFAULT:
+                return "COLOR_MODE_DEFAULT";
+            case COLOR_MODE_WIDE_COLOR_GAMUT:
+                return "COLOR_MODE_WIDE_COLOR_GAMUT";
+            case COLOR_MODE_HDR:
+                return "COLOR_MODE_HDR";
+            default:
+                return Integer.toString(colorMode);
+        }
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<ActivityInfo> CREATOR
+            = new Parcelable.Creator<ActivityInfo>() {
+        public ActivityInfo createFromParcel(Parcel source) {
+            return new ActivityInfo(source);
+        }
+        public ActivityInfo[] newArray(int size) {
+            return new ActivityInfo[size];
+        }
+    };
+
+    private ActivityInfo(Parcel source) {
+        super(source);
+        theme = source.readInt();
+        launchMode = source.readInt();
+        documentLaunchMode = source.readInt();
+        permission = source.readString8();
+        taskAffinity = source.readString8();
+        targetActivity = source.readString8();
+        launchToken = source.readString8();
+        flags = source.readInt();
+        privateFlags = source.readInt();
+        screenOrientation = source.readInt();
+        configChanges = source.readInt();
+        softInputMode = source.readInt();
+        uiOptions = source.readInt();
+        parentActivityName = source.readString8();
+        persistableMode = source.readInt();
+        maxRecents = source.readInt();
+        lockTaskLaunchMode = source.readInt();
+        if (source.readInt() == 1) {
+            windowLayout = new WindowLayout(source);
+        }
+        resizeMode = source.readInt();
+        requestedVrComponent = source.readString8();
+        rotationAnimation = source.readInt();
+        colorMode = source.readInt();
+        maxAspectRatio = source.readFloat();
+        minAspectRatio = source.readFloat();
+    }
+
+    /**
+     * Contains information about position and size of the activity on the display.
+     *
+     * Used in freeform mode to set desired position when activity is first launched.
+     * It describes how big the activity wants to be in both width and height,
+     * the minimal allowed size, and the gravity to be applied.
+     *
+     * @attr ref android.R.styleable#AndroidManifestLayout_defaultWidth
+     * @attr ref android.R.styleable#AndroidManifestLayout_defaultHeight
+     * @attr ref android.R.styleable#AndroidManifestLayout_gravity
+     * @attr ref android.R.styleable#AndroidManifestLayout_minWidth
+     * @attr ref android.R.styleable#AndroidManifestLayout_minHeight
+     */
+    public static final class WindowLayout {
+        public WindowLayout(int width, float widthFraction, int height, float heightFraction,
+                int gravity, int minWidth, int minHeight) {
+            this.width = width;
+            this.widthFraction = widthFraction;
+            this.height = height;
+            this.heightFraction = heightFraction;
+            this.gravity = gravity;
+            this.minWidth = minWidth;
+            this.minHeight = minHeight;
+        }
+
+        /** @hide */
+        public WindowLayout(Parcel source) {
+            width = source.readInt();
+            widthFraction = source.readFloat();
+            height = source.readInt();
+            heightFraction = source.readFloat();
+            gravity = source.readInt();
+            minWidth = source.readInt();
+            minHeight = source.readInt();
+            windowLayoutAffinity = source.readString8();
+        }
+
+        /**
+         * Width of activity in pixels.
+         *
+         * @attr ref android.R.styleable#AndroidManifestLayout_defaultWidth
+         */
+        public final int width;
+
+        /**
+         * Width of activity as a fraction of available display width.
+         * If both {@link #width} and this value are set this one will be preferred.
+         *
+         * @attr ref android.R.styleable#AndroidManifestLayout_defaultWidth
+         */
+        public final float widthFraction;
+
+        /**
+         * Height of activity in pixels.
+         *
+         * @attr ref android.R.styleable#AndroidManifestLayout_defaultHeight
+         */
+        public final int height;
+
+        /**
+         * Height of activity as a fraction of available display height.
+         * If both {@link #height} and this value are set this one will be preferred.
+         *
+         * @attr ref android.R.styleable#AndroidManifestLayout_defaultHeight
+         */
+        public final float heightFraction;
+
+        /**
+         * Gravity of activity.
+         * Currently {@link android.view.Gravity#TOP}, {@link android.view.Gravity#BOTTOM},
+         * {@link android.view.Gravity#LEFT} and {@link android.view.Gravity#RIGHT} are supported.
+         *
+         * @attr ref android.R.styleable#AndroidManifestLayout_gravity
+         */
+        public final int gravity;
+
+        /**
+         * Minimal width of activity in pixels to be able to display its content.
+         *
+         * <p><strong>NOTE:</strong> A task's root activity value is applied to all additional
+         * activities launched in the task. That is if the root activity of a task set minimal
+         * width, then the system will set the same minimal width on all other activities in the
+         * task. It will also ignore any other minimal width attributes of non-root activities.
+         *
+         * @attr ref android.R.styleable#AndroidManifestLayout_minWidth
+         */
+        public final int minWidth;
+
+        /**
+         * Minimal height of activity in pixels to be able to display its content.
+         *
+         * <p><strong>NOTE:</strong> A task's root activity value is applied to all additional
+         * activities launched in the task. That is if the root activity of a task set minimal
+         * height, then the system will set the same minimal height on all other activities in the
+         * task. It will also ignore any other minimal height attributes of non-root activities.
+         *
+         * @attr ref android.R.styleable#AndroidManifestLayout_minHeight
+         */
+        public final int minHeight;
+
+        /**
+         * Affinity of window layout parameters. Activities with the same UID and window layout
+         * affinity will share the same window dimension record.
+         * @hide
+         */
+        public String windowLayoutAffinity;
+
+        /**
+         * Returns if this {@link WindowLayout} has specified bounds.
+         * @hide
+         */
+        public boolean hasSpecifiedSize() {
+            return width >= 0 || height >= 0 || widthFraction >= 0 || heightFraction >= 0;
+        }
+
+        /** @hide */
+        public void writeToParcel(Parcel dest) {
+            dest.writeInt(width);
+            dest.writeFloat(widthFraction);
+            dest.writeInt(height);
+            dest.writeFloat(heightFraction);
+            dest.writeInt(gravity);
+            dest.writeInt(minWidth);
+            dest.writeInt(minHeight);
+            dest.writeString8(windowLayoutAffinity);
+        }
+    }
+}
diff --git a/android/content/pm/ActivityPresentationInfo.java b/android/content/pm/ActivityPresentationInfo.java
new file mode 100644
index 0000000..ccc61dc
--- /dev/null
+++ b/android/content/pm/ActivityPresentationInfo.java
@@ -0,0 +1,39 @@
+/*
+ * 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.content.pm;
+
+import android.annotation.NonNull;
+import android.content.ComponentName;
+
+/**
+ * Holds basic information about an activity.
+ *
+ * @hide
+ */
+public final class ActivityPresentationInfo {
+    public final int taskId;
+    public final int displayId;
+
+    @NonNull
+    public final ComponentName componentName;
+
+    public ActivityPresentationInfo(int taskId, int displayId,
+            @NonNull ComponentName componentName) {
+        this.taskId = taskId;
+        this.displayId = displayId;
+        this.componentName = componentName;
+    }
+}
diff --git a/android/content/pm/AndroidTestBaseUpdater.java b/android/content/pm/AndroidTestBaseUpdater.java
new file mode 100644
index 0000000..1cbbdca
--- /dev/null
+++ b/android/content/pm/AndroidTestBaseUpdater.java
@@ -0,0 +1,26 @@
+/*
+ * 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.content.pm;
+
+/**
+ * Dummy class to maintain legacy behavior of including a class in core source to toggle
+ * whether or not a shared library is stripped at build time.
+ *
+ * @hide
+ */
+public class AndroidTestBaseUpdater {
+}
diff --git a/android/content/pm/ApplicationInfo.java b/android/content/pm/ApplicationInfo.java
new file mode 100644
index 0000000..043953d
--- /dev/null
+++ b/android/content/pm/ApplicationInfo.java
@@ -0,0 +1,2243 @@
+/*
+ * 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.content.pm;
+
+import static android.os.Build.VERSION_CODES.DONUT;
+
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Context;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.os.Environment;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.UserHandle;
+import android.os.storage.StorageManager;
+import android.util.Printer;
+import android.util.SparseArray;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.util.ArrayUtils;
+import com.android.server.SystemConfig;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.text.Collator;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Objects;
+import java.util.UUID;
+
+/**
+ * Information you can retrieve about a particular application.  This
+ * corresponds to information collected from the AndroidManifest.xml's
+ * &lt;application&gt; tag.
+ */
+public class ApplicationInfo extends PackageItemInfo implements Parcelable {
+    /**
+     * Default task affinity of all activities in this application. See 
+     * {@link ActivityInfo#taskAffinity} for more information.  This comes 
+     * from the "taskAffinity" attribute. 
+     */
+    public String taskAffinity;
+    
+    /**
+     * Optional name of a permission required to be able to access this
+     * application's components.  From the "permission" attribute.
+     */
+    public String permission;
+    
+    /**
+     * The name of the process this application should run in.  From the
+     * "process" attribute or, if not set, the same as
+     * <var>packageName</var>.
+     */
+    public String processName;
+    
+    /**
+     * Class implementing the Application object.  From the "class"
+     * attribute.
+     */
+    public String className;
+    
+    /**
+     * A style resource identifier (in the package's resources) of the
+     * description of an application.  From the "description" attribute
+     * or, if not set, 0.
+     */
+    public int descriptionRes;    
+    
+    /**
+     * A style resource identifier (in the package's resources) of the
+     * default visual theme of the application.  From the "theme" attribute
+     * or, if not set, 0.
+     */
+    public int theme;
+    
+    /**
+     * Class implementing the Application's manage space
+     * functionality.  From the "manageSpaceActivity"
+     * attribute. This is an optional attribute and will be null if
+     * applications don't specify it in their manifest
+     */
+    public String manageSpaceActivityName;    
+    
+    /**
+     * Class implementing the Application's backup functionality.  From
+     * the "backupAgent" attribute.  This is an optional attribute and
+     * will be null if the application does not specify it in its manifest.
+     * 
+     * <p>If android:allowBackup is set to false, this attribute is ignored.
+     */
+    public String backupAgentName;
+
+    /**
+     * An optional attribute that indicates the app supports automatic backup of app data.
+     * <p>0 is the default and means the app's entire data folder + managed external storage will
+     * be backed up;
+     * Any negative value indicates the app does not support full-data backup, though it may still
+     * want to participate via the traditional key/value backup API;
+     * A positive number specifies an xml resource in which the application has defined its backup
+     * include/exclude criteria.
+     * <p>If android:allowBackup is set to false, this attribute is ignored.
+     *
+     * @see android.content.Context#getNoBackupFilesDir()
+     * @see #FLAG_ALLOW_BACKUP
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public int fullBackupContent = 0;
+
+    /**
+     * <code>true</code> if the package is capable of presenting a unified interface representing
+     * multiple profiles.
+     * @hide
+     */
+    public boolean crossProfile;
+
+    /**
+     * The default extra UI options for activities in this application.
+     * Set from the {@link android.R.attr#uiOptions} attribute in the
+     * activity's manifest.
+     */
+    public int uiOptions = 0;
+
+    /**
+     * Value for {@link #flags}: if set, this application is installed in the
+     * device's system image.
+     */
+    public static final int FLAG_SYSTEM = 1<<0;
+    
+    /**
+     * Value for {@link #flags}: set to true if this application would like to
+     * allow debugging of its
+     * code, even when installed on a non-development system.  Comes
+     * from {@link android.R.styleable#AndroidManifestApplication_debuggable
+     * android:debuggable} of the &lt;application&gt; tag.
+     */
+    public static final int FLAG_DEBUGGABLE = 1<<1;
+    
+    /**
+     * Value for {@link #flags}: set to true if this application has code
+     * associated with it.  Comes
+     * from {@link android.R.styleable#AndroidManifestApplication_hasCode
+     * android:hasCode} of the &lt;application&gt; tag.
+     */
+    public static final int FLAG_HAS_CODE = 1<<2;
+    
+    /**
+     * Value for {@link #flags}: set to true if this application is persistent.
+     * Comes from {@link android.R.styleable#AndroidManifestApplication_persistent
+     * android:persistent} of the &lt;application&gt; tag.
+     */
+    public static final int FLAG_PERSISTENT = 1<<3;
+
+    /**
+     * Value for {@link #flags}: set to true if this application holds the
+     * {@link android.Manifest.permission#FACTORY_TEST} permission and the
+     * device is running in factory test mode.
+     */
+    public static final int FLAG_FACTORY_TEST = 1<<4;
+
+    /**
+     * Value for {@link #flags}: default value for the corresponding ActivityInfo flag.
+     * Comes from {@link android.R.styleable#AndroidManifestApplication_allowTaskReparenting
+     * android:allowTaskReparenting} of the &lt;application&gt; tag.
+     */
+    public static final int FLAG_ALLOW_TASK_REPARENTING = 1<<5;
+    
+    /**
+     * Value for {@link #flags}: default value for the corresponding ActivityInfo flag.
+     * Comes from {@link android.R.styleable#AndroidManifestApplication_allowClearUserData
+     * android:allowClearUserData} of the &lt;application&gt; tag.
+     */
+    public static final int FLAG_ALLOW_CLEAR_USER_DATA = 1<<6;
+    
+    /**
+     * Value for {@link #flags}: this is set if this application has been
+     * installed as an update to a built-in system application.
+     */
+    public static final int FLAG_UPDATED_SYSTEM_APP = 1<<7;
+    
+    /**
+     * Value for {@link #flags}: this is set if the application has specified
+     * {@link android.R.styleable#AndroidManifestApplication_testOnly
+     * android:testOnly} to be true.
+     */
+    public static final int FLAG_TEST_ONLY = 1<<8;
+
+    /**
+     * Value for {@link #flags}: true when the application's window can be
+     * reduced in size for smaller screens.  Corresponds to
+     * {@link android.R.styleable#AndroidManifestSupportsScreens_smallScreens
+     * android:smallScreens}.
+     */
+    public static final int FLAG_SUPPORTS_SMALL_SCREENS = 1<<9;
+    
+    /**
+     * Value for {@link #flags}: true when the application's window can be
+     * displayed on normal screens.  Corresponds to
+     * {@link android.R.styleable#AndroidManifestSupportsScreens_normalScreens
+     * android:normalScreens}.
+     */
+    public static final int FLAG_SUPPORTS_NORMAL_SCREENS = 1<<10; 
+    
+    /**
+     * Value for {@link #flags}: true when the application's window can be
+     * increased in size for larger screens.  Corresponds to
+     * {@link android.R.styleable#AndroidManifestSupportsScreens_largeScreens
+     * android:largeScreens}.
+     */
+    public static final int FLAG_SUPPORTS_LARGE_SCREENS = 1<<11;
+    
+    /**
+     * Value for {@link #flags}: true when the application knows how to adjust
+     * its UI for different screen sizes.  Corresponds to
+     * {@link android.R.styleable#AndroidManifestSupportsScreens_resizeable
+     * android:resizeable}.
+     */
+    public static final int FLAG_RESIZEABLE_FOR_SCREENS = 1<<12;
+    
+    /**
+     * Value for {@link #flags}: true when the application knows how to
+     * accommodate different screen densities.  Corresponds to
+     * {@link android.R.styleable#AndroidManifestSupportsScreens_anyDensity
+     * android:anyDensity}.
+     *
+     * @deprecated Set by default when targeting API 4 or higher and apps
+     *             should not set this to false.
+     */
+    @Deprecated
+    public static final int FLAG_SUPPORTS_SCREEN_DENSITIES = 1<<13;
+    
+    /**
+     * Value for {@link #flags}: set to true if this application would like to
+     * request the VM to operate under the safe mode. Comes from
+     * {@link android.R.styleable#AndroidManifestApplication_vmSafeMode
+     * android:vmSafeMode} of the &lt;application&gt; tag.
+     */
+    public static final int FLAG_VM_SAFE_MODE = 1<<14;
+
+    /**
+     * Value for {@link #flags}: set to <code>false</code> if the application does not wish
+     * to permit any OS-driven backups of its data; <code>true</code> otherwise.
+     * 
+     * <p>Comes from the
+     * {@link android.R.styleable#AndroidManifestApplication_allowBackup android:allowBackup}
+     * attribute of the &lt;application&gt; tag.
+     */
+    public static final int FLAG_ALLOW_BACKUP = 1<<15;
+
+    /**
+     * Value for {@link #flags}: set to <code>false</code> if the application must be kept
+     * in memory following a full-system restore operation; <code>true</code> otherwise.
+     * Ordinarily, during a full system restore operation each application is shut down
+     * following execution of its agent's onRestore() method.  Setting this attribute to
+     * <code>false</code> prevents this.  Most applications will not need to set this attribute.
+     *
+     * <p>If
+     * {@link android.R.styleable#AndroidManifestApplication_allowBackup android:allowBackup}
+     * is set to <code>false</code> or no
+     * {@link android.R.styleable#AndroidManifestApplication_backupAgent android:backupAgent}
+     * is specified, this flag will be ignored.
+     *
+     * <p>Comes from the
+     * {@link android.R.styleable#AndroidManifestApplication_killAfterRestore android:killAfterRestore}
+     * attribute of the &lt;application&gt; tag.
+     */
+    public static final int FLAG_KILL_AFTER_RESTORE = 1<<16;
+
+    /**
+     * Value for {@link #flags}: Set to <code>true</code> if the application's backup
+     * agent claims to be able to handle restore data even "from the future,"
+     * i.e. from versions of the application with a versionCode greater than
+     * the one currently installed on the device.  <i>Use with caution!</i>  By default
+     * this attribute is <code>false</code> and the Backup Manager will ensure that data
+     * from "future" versions of the application are never supplied during a restore operation.
+     *
+     * <p>If
+     * {@link android.R.styleable#AndroidManifestApplication_allowBackup android:allowBackup}
+     * is set to <code>false</code> or no
+     * {@link android.R.styleable#AndroidManifestApplication_backupAgent android:backupAgent}
+     * is specified, this flag will be ignored.
+     *
+     * <p>Comes from the
+     * {@link android.R.styleable#AndroidManifestApplication_restoreAnyVersion android:restoreAnyVersion}
+     * attribute of the &lt;application&gt; tag.
+     */
+    public static final int FLAG_RESTORE_ANY_VERSION = 1<<17;
+
+    /**
+     * Value for {@link #flags}: Set to true if the application is
+     * currently installed on external/removable/unprotected storage.  Such
+     * applications may not be available if their storage is not currently
+     * mounted.  When the storage it is on is not available, it will look like
+     * the application has been uninstalled (its .apk is no longer available)
+     * but its persistent data is not removed.
+     */
+    public static final int FLAG_EXTERNAL_STORAGE = 1<<18;
+
+    /**
+     * Value for {@link #flags}: true when the application's window can be
+     * increased in size for extra large screens.  Corresponds to
+     * {@link android.R.styleable#AndroidManifestSupportsScreens_xlargeScreens
+     * android:xlargeScreens}.
+     */
+    public static final int FLAG_SUPPORTS_XLARGE_SCREENS = 1<<19;
+    
+    /**
+     * Value for {@link #flags}: true when the application has requested a
+     * large heap for its processes.  Corresponds to
+     * {@link android.R.styleable#AndroidManifestApplication_largeHeap
+     * android:largeHeap}.
+     */
+    public static final int FLAG_LARGE_HEAP = 1<<20;
+
+    /**
+     * Value for {@link #flags}: true if this application's package is in
+     * the stopped state.
+     */
+    public static final int FLAG_STOPPED = 1<<21;
+
+    /**
+     * Value for {@link #flags}: true  when the application is willing to support
+     * RTL (right to left). All activities will inherit this value.
+     *
+     * Set from the {@link android.R.attr#supportsRtl} attribute in the
+     * activity's manifest.
+     *
+     * Default value is false (no support for RTL).
+     */
+    public static final int FLAG_SUPPORTS_RTL = 1<<22;
+
+    /**
+     * Value for {@link #flags}: true if the application is currently
+     * installed for the calling user.
+     */
+    public static final int FLAG_INSTALLED = 1<<23;
+
+    /**
+     * Value for {@link #flags}: true if the application only has its
+     * data installed; the application package itself does not currently
+     * exist on the device.
+     */
+    public static final int FLAG_IS_DATA_ONLY = 1<<24;
+
+    /**
+     * Value for {@link #flags}: true if the application was declared to be a
+     * game, or false if it is a non-game application.
+     *
+     * @deprecated use {@link #CATEGORY_GAME} instead.
+     */
+    @Deprecated
+    public static final int FLAG_IS_GAME = 1<<25;
+
+    /**
+     * Value for {@link #flags}: {@code true} if the application asks that only
+     * full-data streaming backups of its data be performed even though it defines
+     * a {@link android.app.backup.BackupAgent BackupAgent}, which normally
+     * indicates that the app will manage its backed-up data via incremental
+     * key/value updates.
+     */
+    public static final int FLAG_FULL_BACKUP_ONLY = 1<<26;
+
+    /**
+     * Value for {@link #flags}: {@code true} if the application may use cleartext network traffic
+     * (e.g., HTTP rather than HTTPS; WebSockets rather than WebSockets Secure; XMPP, IMAP, STMP
+     * without STARTTLS or TLS). If {@code false}, the app declares that it does not intend to use
+     * cleartext network traffic, in which case platform components (e.g., HTTP stacks,
+     * {@code DownloadManager}, {@code MediaPlayer}) will refuse app's requests to use cleartext
+     * traffic. Third-party libraries are encouraged to honor this flag as well.
+     *
+     * <p>NOTE: {@code WebView} honors this flag for applications targeting API level 26 and up.
+     *
+     * <p>This flag is ignored on Android N and above if an Android Network Security Config is
+     * present.
+     *
+     * <p>This flag comes from
+     * {@link android.R.styleable#AndroidManifestApplication_usesCleartextTraffic
+     * android:usesCleartextTraffic} of the &lt;application&gt; tag.
+     */
+    public static final int FLAG_USES_CLEARTEXT_TRAFFIC = 1<<27;
+
+    /**
+     * When set installer extracts native libs from .apk files.
+     */
+    public static final int FLAG_EXTRACT_NATIVE_LIBS = 1<<28;
+
+    /**
+     * Value for {@link #flags}: {@code true} when the application's rendering
+     * should be hardware accelerated.
+     */
+    public static final int FLAG_HARDWARE_ACCELERATED = 1<<29;
+
+    /**
+     * Value for {@link #flags}: true if this application's package is in
+     * the suspended state.
+     */
+    public static final int FLAG_SUSPENDED = 1<<30;
+
+    /**
+     * Value for {@link #flags}: true if code from this application will need to be
+     * loaded into other applications' processes. On devices that support multiple
+     * instruction sets, this implies the code might be loaded into a process that's
+     * using any of the devices supported instruction sets.
+     *
+     * <p> The system might treat such applications specially, for eg., by
+     * extracting the application's native libraries for all supported instruction
+     * sets or by compiling the application's dex code for all supported instruction
+     * sets.
+     */
+    public static final int FLAG_MULTIARCH  = 1 << 31;
+
+    /**
+     * Flags associated with the application.  Any combination of
+     * {@link #FLAG_SYSTEM}, {@link #FLAG_DEBUGGABLE}, {@link #FLAG_HAS_CODE},
+     * {@link #FLAG_PERSISTENT}, {@link #FLAG_FACTORY_TEST}, and
+     * {@link #FLAG_ALLOW_TASK_REPARENTING}
+     * {@link #FLAG_ALLOW_CLEAR_USER_DATA}, {@link #FLAG_UPDATED_SYSTEM_APP},
+     * {@link #FLAG_TEST_ONLY}, {@link #FLAG_SUPPORTS_SMALL_SCREENS},
+     * {@link #FLAG_SUPPORTS_NORMAL_SCREENS},
+     * {@link #FLAG_SUPPORTS_LARGE_SCREENS}, {@link #FLAG_SUPPORTS_XLARGE_SCREENS},
+     * {@link #FLAG_RESIZEABLE_FOR_SCREENS},
+     * {@link #FLAG_SUPPORTS_SCREEN_DENSITIES}, {@link #FLAG_VM_SAFE_MODE},
+     * {@link #FLAG_ALLOW_BACKUP}, {@link #FLAG_KILL_AFTER_RESTORE},
+     * {@link #FLAG_RESTORE_ANY_VERSION}, {@link #FLAG_EXTERNAL_STORAGE},
+     * {@link #FLAG_LARGE_HEAP}, {@link #FLAG_STOPPED},
+     * {@link #FLAG_SUPPORTS_RTL}, {@link #FLAG_INSTALLED},
+     * {@link #FLAG_IS_DATA_ONLY}, {@link #FLAG_IS_GAME},
+     * {@link #FLAG_FULL_BACKUP_ONLY}, {@link #FLAG_USES_CLEARTEXT_TRAFFIC},
+     * {@link #FLAG_MULTIARCH}.
+     */
+    public int flags = 0;
+
+    /**
+     * Value for {@link #privateFlags}: true if the application is hidden via restrictions and for
+     * most purposes is considered as not installed.
+     * {@hide}
+     */
+    public static final int PRIVATE_FLAG_HIDDEN = 1<<0;
+
+    /**
+     * Value for {@link #privateFlags}: set to <code>true</code> if the application
+     * has reported that it is heavy-weight, and thus can not participate in
+     * the normal application lifecycle.
+     *
+     * <p>Comes from the
+     * android.R.styleable#AndroidManifestApplication_cantSaveState
+     * attribute of the &lt;application&gt; tag.
+     *
+     * {@hide}
+     */
+    public static final int PRIVATE_FLAG_CANT_SAVE_STATE = 1<<1;
+
+    /**
+     * Value for {@link #privateFlags}: set to {@code true} if the application
+     * is permitted to hold privileged permissions.
+     *
+     * {@hide}
+     */
+    @UnsupportedAppUsage
+    @TestApi
+    public static final int PRIVATE_FLAG_PRIVILEGED = 1<<3;
+
+    /**
+     * Value for {@link #privateFlags}: {@code true} if the application has any IntentFiler
+     * with some data URI using HTTP or HTTPS with an associated VIEW action.
+     *
+     * {@hide}
+     */
+    public static final int PRIVATE_FLAG_HAS_DOMAIN_URLS = 1<<4;
+
+    /**
+     * When set, the default data storage directory for this app is pointed at
+     * the device-protected location.
+     *
+     * @hide
+     */
+    public static final int PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE = 1 << 5;
+
+    /**
+     * When set, assume that all components under the given app are direct boot
+     * aware, unless otherwise specified.
+     *
+     * @hide
+     */
+    public static final int PRIVATE_FLAG_DIRECT_BOOT_AWARE = 1 << 6;
+
+    /**
+     * Value for {@link #privateFlags}: {@code true} if the application is installed
+     * as instant app.
+     *
+     * @hide
+     */
+    public static final int PRIVATE_FLAG_INSTANT = 1 << 7;
+
+    /**
+     * When set, at least one component inside this application is direct boot
+     * aware.
+     *
+     * @hide
+     */
+    public static final int PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE = 1 << 8;
+
+
+    /**
+     * When set, signals that the application is required for the system user and should not be
+     * uninstalled.
+     *
+     * @hide
+     */
+    public static final int PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER = 1 << 9;
+
+    /**
+     * When set, the application explicitly requested that its activities be resizeable by default.
+     * @see android.R.styleable#AndroidManifestActivity_resizeableActivity
+     *
+     * @hide
+     */
+    public static final int PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE = 1 << 10;
+
+    /**
+     * When set, the application explicitly requested that its activities *not* be resizeable by
+     * default.
+     * @see android.R.styleable#AndroidManifestActivity_resizeableActivity
+     *
+     * @hide
+     */
+    public static final int PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE = 1 << 11;
+
+    /**
+     * The application isn't requesting explicitly requesting for its activities to be resizeable or
+     * non-resizeable by default. So, we are making it activities resizeable by default based on the
+     * target SDK version of the app.
+     * @see android.R.styleable#AndroidManifestActivity_resizeableActivity
+     *
+     * NOTE: This only affects apps with target SDK >= N where the resizeableActivity attribute was
+     * introduced. It shouldn't be confused with {@link ActivityInfo#RESIZE_MODE_FORCE_RESIZEABLE}
+     * where certain pre-N apps are forced to the resizeable.
+     *
+     * @hide
+     */
+    public static final int PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION =
+            1 << 12;
+
+    /**
+     * Value for {@link #privateFlags}: {@code true} means the OS should go ahead and
+     * run full-data backup operations for the app even when it is in a
+     * foreground-equivalent run state.  Defaults to {@code false} if unspecified.
+     * @hide
+     */
+    public static final int PRIVATE_FLAG_BACKUP_IN_FOREGROUND = 1 << 13;
+
+    /**
+     * Value for {@link #privateFlags}: {@code true} means this application
+     * contains a static shared library. Defaults to {@code false} if unspecified.
+     * @hide
+     */
+    public static final int PRIVATE_FLAG_STATIC_SHARED_LIBRARY = 1 << 14;
+
+    /**
+     * Value for {@link #privateFlags}: When set, the application will only have its splits loaded
+     * if they are required to load a component. Splits can be loaded on demand using the
+     * {@link Context#createContextForSplit(String)} API.
+     * @hide
+     */
+    public static final int PRIVATE_FLAG_ISOLATED_SPLIT_LOADING = 1 << 15;
+
+    /**
+     * Value for {@link #privateFlags}: When set, the application was installed as
+     * a virtual preload.
+     * @hide
+     */
+    public static final int PRIVATE_FLAG_VIRTUAL_PRELOAD = 1 << 16;
+
+    /**
+     * Value for {@link #privateFlags}: whether this app is pre-installed on the
+     * OEM partition of the system image.
+     * @hide
+     */
+    public static final int PRIVATE_FLAG_OEM = 1 << 17;
+
+    /**
+     * Value for {@link #privateFlags}: whether this app is pre-installed on the
+     * vendor partition of the system image.
+     * @hide
+     */
+    public static final int PRIVATE_FLAG_VENDOR = 1 << 18;
+
+    /**
+     * Value for {@link #privateFlags}: whether this app is pre-installed on the
+     * product partition of the system image.
+     * @hide
+     */
+    public static final int PRIVATE_FLAG_PRODUCT = 1 << 19;
+
+    /**
+     * Value for {@link #privateFlags}: whether this app is signed with the
+     * platform key.
+     * @hide
+     */
+    public static final int PRIVATE_FLAG_SIGNED_WITH_PLATFORM_KEY = 1 << 20;
+
+    /**
+     * Value for {@link #privateFlags}: whether this app is pre-installed on the
+     * system_ext partition of the system image.
+     * @hide
+     */
+    public static final int PRIVATE_FLAG_SYSTEM_EXT = 1 << 21;
+
+    /**
+     * Indicates whether this package requires access to non-SDK APIs.
+     * Only system apps and tests are allowed to use this property.
+     * @hide
+     */
+    public static final int PRIVATE_FLAG_USES_NON_SDK_API = 1 << 22;
+
+    /**
+     * Indicates whether this application can be profiled by the shell user,
+     * even when running on a device that is running in user mode.
+     * @hide
+     */
+    public static final int PRIVATE_FLAG_PROFILEABLE_BY_SHELL = 1 << 23;
+
+    /**
+     * Indicates whether this package requires access to non-SDK APIs.
+     * Only system apps and tests are allowed to use this property.
+     * @hide
+     */
+    public static final int PRIVATE_FLAG_HAS_FRAGILE_USER_DATA = 1 << 24;
+
+    /**
+     * Indicates whether this application wants to use the embedded dex in the APK, rather than
+     * extracted or locally compiled variants. This keeps the dex code protected by the APK
+     * signature. Such apps will always run in JIT mode (same when they are first installed), and
+     * the system will never generate ahead-of-time compiled code for them. Depending on the app's
+     * workload, there may be some run time performance change, noteably the cold start time.
+     *
+     * @hide
+     */
+    public static final int PRIVATE_FLAG_USE_EMBEDDED_DEX = 1 << 25;
+
+    /**
+     * Value for {@link #privateFlags}: indicates whether this application's data will be cleared
+     * on a failed restore.
+     *
+     * <p>Comes from the
+     * android.R.styleable#AndroidManifestApplication_allowClearUserDataOnFailedRestore attribute
+     * of the &lt;application&gt; tag.
+     *
+     * @hide
+     */
+    public static final int PRIVATE_FLAG_ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE = 1 << 26;
+
+    /**
+     * Value for {@link #privateFlags}: true if the application allows its audio playback
+     * to be captured by other apps.
+     *
+     * @hide
+     */
+    public static final int PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE  = 1 << 27;
+
+    /**
+     * Indicates whether this package is in fact a runtime resource overlay.
+     *
+     * @hide
+     */
+    public static final int PRIVATE_FLAG_IS_RESOURCE_OVERLAY = 1 << 28;
+
+    /**
+     * Value for {@link #privateFlags}: If {@code true} this app requests
+     * full external storage access. The request may not be honored due to
+     * policy or other reasons.
+     *
+     * @hide
+     */
+    public static final int PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE = 1 << 29;
+
+    /**
+     * Value for {@link #privateFlags}: whether this app is pre-installed on the
+     * ODM partition of the system image.
+     * @hide
+     */
+    public static final int PRIVATE_FLAG_ODM = 1 << 30;
+
+    /**
+     * Value for {@link #privateFlags}: If {@code true} this app allows heap tagging.
+     * {@link com.android.server.am.ProcessList#NATIVE_HEAP_POINTER_TAGGING}
+     * @hide
+     */
+    public static final int PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING = 1 << 31;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "PRIVATE_FLAG_" }, value = {
+            PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE,
+            PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION,
+            PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE,
+            PRIVATE_FLAG_BACKUP_IN_FOREGROUND,
+            PRIVATE_FLAG_CANT_SAVE_STATE,
+            PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE,
+            PRIVATE_FLAG_DIRECT_BOOT_AWARE,
+            PRIVATE_FLAG_HAS_DOMAIN_URLS,
+            PRIVATE_FLAG_HIDDEN,
+            PRIVATE_FLAG_INSTANT,
+            PRIVATE_FLAG_IS_RESOURCE_OVERLAY,
+            PRIVATE_FLAG_ISOLATED_SPLIT_LOADING,
+            PRIVATE_FLAG_OEM,
+            PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE,
+            PRIVATE_FLAG_USE_EMBEDDED_DEX,
+            PRIVATE_FLAG_PRIVILEGED,
+            PRIVATE_FLAG_PRODUCT,
+            PRIVATE_FLAG_SYSTEM_EXT,
+            PRIVATE_FLAG_PROFILEABLE_BY_SHELL,
+            PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER,
+            PRIVATE_FLAG_SIGNED_WITH_PLATFORM_KEY,
+            PRIVATE_FLAG_STATIC_SHARED_LIBRARY,
+            PRIVATE_FLAG_VENDOR,
+            PRIVATE_FLAG_VIRTUAL_PRELOAD,
+            PRIVATE_FLAG_HAS_FRAGILE_USER_DATA,
+            PRIVATE_FLAG_ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE,
+            PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE,
+            PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE,
+            PRIVATE_FLAG_ODM,
+            PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ApplicationInfoPrivateFlags {}
+
+    /**
+     * Constant corresponding to <code>allowed</code> in the
+     * {@link android.R.attr#autoRevokePermissions} attribute.
+     *
+     * @hide
+     */
+    public static final int AUTO_REVOKE_ALLOWED = 0;
+
+    /**
+     * Constant corresponding to <code>discouraged</code> in the
+     * {@link android.R.attr#autoRevokePermissions} attribute.
+     *
+     * @hide
+     */
+    public static final int AUTO_REVOKE_DISCOURAGED = 1;
+
+    /**
+     * Constant corresponding to <code>disallowed</code> in the
+     * {@link android.R.attr#autoRevokePermissions} attribute.
+     *
+     * @hide
+     */
+    public static final int AUTO_REVOKE_DISALLOWED = 2;
+
+    /**
+     * Private/hidden flags. See {@code PRIVATE_FLAG_...} constants.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    @TestApi
+    public @ApplicationInfoPrivateFlags int privateFlags;
+
+    /**
+     * @hide
+     */
+    public static final String METADATA_PRELOADED_FONTS = "preloaded_fonts";
+
+    /**
+     * The required smallest screen width the application can run on.  If 0,
+     * nothing has been specified.  Comes from
+     * {@link android.R.styleable#AndroidManifestSupportsScreens_requiresSmallestWidthDp
+     * android:requiresSmallestWidthDp} attribute of the &lt;supports-screens&gt; tag.
+     */
+    public int requiresSmallestWidthDp = 0;
+
+    /**
+     * The maximum smallest screen width the application is designed for.  If 0,
+     * nothing has been specified.  Comes from
+     * {@link android.R.styleable#AndroidManifestSupportsScreens_compatibleWidthLimitDp
+     * android:compatibleWidthLimitDp} attribute of the &lt;supports-screens&gt; tag.
+     */
+    public int compatibleWidthLimitDp = 0;
+
+    /**
+     * The maximum smallest screen width the application will work on.  If 0,
+     * nothing has been specified.  Comes from
+     * {@link android.R.styleable#AndroidManifestSupportsScreens_largestWidthLimitDp
+     * android:largestWidthLimitDp} attribute of the &lt;supports-screens&gt; tag.
+     */
+    public int largestWidthLimitDp = 0;
+
+    /**
+     * Value indicating the maximum aspect ratio the application supports.
+     * <p>
+     * 0 means unset.
+     * @See {@link android.R.attr#maxAspectRatio}.
+     * @hide
+     */
+    public float maxAspectRatio;
+
+    /**
+     * Value indicating the minimum aspect ratio the application supports.
+     * <p>
+     * 0 means unset.
+     * @see {@link android.R.attr#minAspectRatio}.
+     * @hide
+     */
+    public float minAspectRatio;
+
+    /** @hide */
+    public String volumeUuid;
+
+    /**
+     * UUID of the storage volume on which this application is being hosted. For
+     * apps hosted on the default internal storage at
+     * {@link Environment#getDataDirectory()}, the UUID value is
+     * {@link StorageManager#UUID_DEFAULT}.
+     */
+    public UUID storageUuid;
+
+    /** {@hide} */
+    @UnsupportedAppUsage
+    public String scanSourceDir;
+    /** {@hide} */
+    @UnsupportedAppUsage
+    public String scanPublicSourceDir;
+
+    /**
+     * Full path to the base APK for this application.
+     */
+    public String sourceDir;
+
+    /**
+     * Full path to the publicly available parts of {@link #sourceDir},
+     * including resources and manifest. This may be different from
+     * {@link #sourceDir} if an application is forward locked.
+     */
+    public String publicSourceDir;
+
+    /**
+     * The names of all installed split APKs, ordered lexicographically.
+     */
+    public String[] splitNames;
+
+    /**
+     * Full paths to zero or more split APKs, indexed by the same order as {@link #splitNames}.
+     */
+    public String[] splitSourceDirs;
+
+    /**
+     * Full path to the publicly available parts of {@link #splitSourceDirs},
+     * including resources and manifest. This may be different from
+     * {@link #splitSourceDirs} if an application is forward locked.
+     *
+     * @see #splitSourceDirs
+     */
+    public String[] splitPublicSourceDirs;
+
+    /**
+     * Maps the dependencies between split APKs. All splits implicitly depend on the base APK.
+     *
+     * Available since platform version O.
+     *
+     * Only populated if the application opts in to isolated split loading via the
+     * {@link android.R.attr.isolatedSplits} attribute in the &lt;manifest&gt; tag of the app's
+     * AndroidManifest.xml.
+     *
+     * The keys and values are all indices into the {@link #splitNames}, {@link #splitSourceDirs},
+     * and {@link #splitPublicSourceDirs} arrays.
+     * Each key represents a split and its value is an array of splits. The first element of this
+     * array is the parent split, and the rest are configuration splits. These configuration splits
+     * have no dependencies themselves.
+     * Cycles do not exist because they are illegal and screened for during installation.
+     *
+     * May be null if no splits are installed, or if no dependencies exist between them.
+     *
+     * NOTE: Any change to the way split dependencies are stored must update the logic that
+     *       creates the class loader context for dexopt (DexoptUtils#getClassLoaderContexts).
+     *
+     * @hide
+     */
+    public SparseArray<int[]> splitDependencies;
+
+    /**
+     * Full paths to the locations of extra resource packages (runtime overlays)
+     * this application uses. This field is only used if there are extra resource
+     * packages, otherwise it is null.
+     *
+     * {@hide}
+     */
+    @UnsupportedAppUsage
+    public String[] resourceDirs;
+
+    /**
+     * String retrieved from the seinfo tag found in selinux policy. This value can be set through
+     * the mac_permissions.xml policy construct. This value is used for setting an SELinux security
+     * context on the process as well as its data directory.
+     *
+     * {@hide}
+     */
+    public String seInfo;
+
+    /**
+     * The seinfo tag generated per-user. This value may change based upon the
+     * user's configuration. For example, when an instant app is installed for
+     * a user. It is an error if this field is ever {@code null} when trying to
+     * start a new process.
+     * <p>NOTE: We need to separate this out because we modify per-user values
+     * multiple times. This needs to be refactored since we're performing more
+     * work than necessary and these values should only be set once. When that
+     * happens, we can merge the per-user value with the seInfo state above.
+     *
+     * {@hide}
+     */
+    public String seInfoUser;
+
+    /**
+     * Paths to all shared libraries this application is linked against.  This
+     * field is only set if the {@link PackageManager#GET_SHARED_LIBRARY_FILES
+     * PackageManager.GET_SHARED_LIBRARY_FILES} flag was used when retrieving
+     * the structure.
+     */
+    public String[] sharedLibraryFiles;
+
+    /**
+     * List of all shared libraries this application is linked against.  This
+     * field is only set if the {@link PackageManager#GET_SHARED_LIBRARY_FILES
+     * PackageManager.GET_SHARED_LIBRARY_FILES} flag was used when retrieving
+     * the structure.
+     *
+     * {@hide}
+     */
+    public List<SharedLibraryInfo> sharedLibraryInfos;
+
+    /**
+     * Full path to the default directory assigned to the package for its
+     * persistent data.
+     */
+    public String dataDir;
+
+    /**
+     * Full path to the device-protected directory assigned to the package for
+     * its persistent data.
+     *
+     * @see Context#createDeviceProtectedStorageContext()
+     */
+    public String deviceProtectedDataDir;
+
+    /**
+     * Full path to the credential-protected directory assigned to the package
+     * for its persistent data.
+     *
+     * @hide
+     */
+    @SystemApi
+    public String credentialProtectedDataDir;
+
+    /**
+     * Full path to the directory where native JNI libraries are stored.
+     */
+    public String nativeLibraryDir;
+
+    /**
+     * Full path where unpacked native libraries for {@link #secondaryCpuAbi}
+     * are stored, if present.
+     *
+     * The main reason this exists is for bundled multi-arch apps, where
+     * it's not trivial to calculate the location of libs for the secondary abi
+     * given the location of the primary.
+     *
+     * TODO: Change the layout of bundled installs so that we can use
+     * nativeLibraryRootDir & nativeLibraryRootRequiresIsa there as well.
+     * (e.g {@code [ "/system/app-lib/Foo/arm", "/system/app-lib/Foo/arm64" ]}
+     * instead of {@code [ "/system/lib/Foo", "/system/lib64/Foo" ]}.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public String secondaryNativeLibraryDir;
+
+    /**
+     * The root path where unpacked native libraries are stored.
+     * <p>
+     * When {@link #nativeLibraryRootRequiresIsa} is set, the libraries are
+     * placed in ISA-specific subdirectories under this path, otherwise the
+     * libraries are placed directly at this path.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public String nativeLibraryRootDir;
+
+    /**
+     * Flag indicating that ISA must be appended to
+     * {@link #nativeLibraryRootDir} to be useful.
+     *
+     * @hide
+     */
+    public boolean nativeLibraryRootRequiresIsa;
+
+    /**
+     * The primary ABI that this application requires, This is inferred from the ABIs
+     * of the native JNI libraries the application bundles. Will be {@code null}
+     * if this application does not require any particular ABI.
+     *
+     * If non-null, the application will always be launched with this ABI.
+     *
+     * {@hide}
+     */
+    @UnsupportedAppUsage
+    public String primaryCpuAbi;
+
+    /**
+     * The secondary ABI for this application. Might be non-null for multi-arch
+     * installs. The application itself never uses this ABI, but other applications that
+     * use its code might.
+     *
+     * {@hide}
+     */
+    @UnsupportedAppUsage
+    public String secondaryCpuAbi;
+
+    /**
+     * The kernel user-ID that has been assigned to this application;
+     * currently this is not a unique ID (multiple applications can have
+     * the same uid).
+     */
+    public int uid;
+    
+    /**
+     * The minimum SDK version this application can run on. It will not run
+     * on earlier versions.
+     */
+    public int minSdkVersion;
+
+    /**
+     * The minimum SDK version this application targets.  It may run on earlier
+     * versions, but it knows how to work with any new behavior added at this
+     * version.  Will be {@link android.os.Build.VERSION_CODES#CUR_DEVELOPMENT}
+     * if this is a development build and the app is targeting that.  You should
+     * compare that this number is >= the SDK version number at which your
+     * behavior was introduced.
+     */
+    public int targetSdkVersion;
+
+    /**
+     * The app's declared version code.
+     * @hide
+     */
+    public long longVersionCode;
+
+    /**
+     * An integer representation of the app's declared version code. This is being left in place as
+     * some apps were using reflection to access it before the move to long in
+     * {@link android.os.Build.VERSION_CODES#P}
+     * @deprecated Use {@link #longVersionCode} instead.
+     * @hide
+     */
+    @Deprecated
+    @UnsupportedAppUsage
+    public int versionCode;
+
+    /**
+     * The user-visible SDK version (ex. 26) of the framework against which the application claims
+     * to have been compiled, or {@code 0} if not specified.
+     * <p>
+     * This property is the compile-time equivalent of
+     * {@link android.os.Build.VERSION#CODENAME Build.VERSION.SDK_INT}.
+     *
+     * @hide For platform use only; we don't expect developers to need to read this value.
+     */
+    public int compileSdkVersion;
+
+    /**
+     * The development codename (ex. "O", "REL") of the framework against which the application
+     * claims to have been compiled, or {@code null} if not specified.
+     * <p>
+     * This property is the compile-time equivalent of
+     * {@link android.os.Build.VERSION#CODENAME Build.VERSION.CODENAME}.
+     *
+     * @hide For platform use only; we don't expect developers to need to read this value.
+     */
+    @Nullable
+    public String compileSdkVersionCodename;
+
+    /**
+     * When false, indicates that all components within this application are
+     * considered disabled, regardless of their individually set enabled status.
+     */
+    public boolean enabled = true;
+
+    /**
+     * For convenient access to the current enabled setting of this app.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public int enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
+
+    /**
+     * For convenient access to package's install location.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public int installLocation = PackageInfo.INSTALL_LOCATION_UNSPECIFIED;
+
+    /**
+     * Resource file providing the application's Network Security Config.
+     * @hide
+     */
+    public int networkSecurityConfigRes;
+
+    /**
+     * Version of the sandbox the application wants to run in.
+     * @hide
+     */
+    @SystemApi
+    public int targetSandboxVersion;
+
+    /**
+     * The factory of this package, as specified by the &lt;manifest&gt;
+     * tag's {@link android.R.styleable#AndroidManifestApplication_appComponentFactory}
+     * attribute.
+     */
+    public String appComponentFactory;
+
+    /**
+     * Resource id of {@link com.android.internal.R.styleable.AndroidManifestProvider_icon}
+     * @hide
+     */
+    public int iconRes;
+
+    /**
+     * Resource id of {@link com.android.internal.R.styleable.AndroidManifestProvider_roundIcon}
+     * @hide
+     */
+    public int roundIconRes;
+
+    /**
+     * The category of this app. Categories are used to cluster multiple apps
+     * together into meaningful groups, such as when summarizing battery,
+     * network, or disk usage. Apps should only define this value when they fit
+     * well into one of the specific categories.
+     * <p>
+     * Set from the {@link android.R.attr#appCategory} attribute in the
+     * manifest. If the manifest doesn't define a category, this value may have
+     * been provided by the installer via
+     * {@link PackageManager#setApplicationCategoryHint(String, int)}.
+     */
+    public @Category int category = CATEGORY_UNDEFINED;
+
+    /** {@hide} */
+    @IntDef(prefix = { "CATEGORY_" }, value = {
+            CATEGORY_UNDEFINED,
+            CATEGORY_GAME,
+            CATEGORY_AUDIO,
+            CATEGORY_VIDEO,
+            CATEGORY_IMAGE,
+            CATEGORY_SOCIAL,
+            CATEGORY_NEWS,
+            CATEGORY_MAPS,
+            CATEGORY_PRODUCTIVITY
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Category {
+    }
+
+    /**
+     * Value when category is undefined.
+     *
+     * @see #category
+     */
+    public static final int CATEGORY_UNDEFINED = -1;
+
+    /**
+     * Category for apps which are primarily games.
+     *
+     * @see #category
+     */
+    public static final int CATEGORY_GAME = 0;
+
+    /**
+     * Category for apps which primarily work with audio or music, such as music
+     * players.
+     *
+     * @see #category
+     */
+    public static final int CATEGORY_AUDIO = 1;
+
+    /**
+     * Category for apps which primarily work with video or movies, such as
+     * streaming video apps.
+     *
+     * @see #category
+     */
+    public static final int CATEGORY_VIDEO = 2;
+
+    /**
+     * Category for apps which primarily work with images or photos, such as
+     * camera or gallery apps.
+     *
+     * @see #category
+     */
+    public static final int CATEGORY_IMAGE = 3;
+
+    /**
+     * Category for apps which are primarily social apps, such as messaging,
+     * communication, email, or social network apps.
+     *
+     * @see #category
+     */
+    public static final int CATEGORY_SOCIAL = 4;
+
+    /**
+     * Category for apps which are primarily news apps, such as newspapers,
+     * magazines, or sports apps.
+     *
+     * @see #category
+     */
+    public static final int CATEGORY_NEWS = 5;
+
+    /**
+     * Category for apps which are primarily maps apps, such as navigation apps.
+     *
+     * @see #category
+     */
+    public static final int CATEGORY_MAPS = 6;
+
+    /**
+     * Category for apps which are primarily productivity apps, such as cloud
+     * storage or workplace apps.
+     *
+     * @see #category
+     */
+    public static final int CATEGORY_PRODUCTIVITY = 7;
+
+    /**
+     * Return a concise, localized title for the given
+     * {@link ApplicationInfo#category} value, or {@code null} for unknown
+     * values such as {@link #CATEGORY_UNDEFINED}.
+     *
+     * @see #category
+     */
+    public static CharSequence getCategoryTitle(Context context, @Category int category) {
+        switch (category) {
+            case ApplicationInfo.CATEGORY_GAME:
+                return context.getText(com.android.internal.R.string.app_category_game);
+            case ApplicationInfo.CATEGORY_AUDIO:
+                return context.getText(com.android.internal.R.string.app_category_audio);
+            case ApplicationInfo.CATEGORY_VIDEO:
+                return context.getText(com.android.internal.R.string.app_category_video);
+            case ApplicationInfo.CATEGORY_IMAGE:
+                return context.getText(com.android.internal.R.string.app_category_image);
+            case ApplicationInfo.CATEGORY_SOCIAL:
+                return context.getText(com.android.internal.R.string.app_category_social);
+            case ApplicationInfo.CATEGORY_NEWS:
+                return context.getText(com.android.internal.R.string.app_category_news);
+            case ApplicationInfo.CATEGORY_MAPS:
+                return context.getText(com.android.internal.R.string.app_category_maps);
+            case ApplicationInfo.CATEGORY_PRODUCTIVITY:
+                return context.getText(com.android.internal.R.string.app_category_productivity);
+            default:
+                return null;
+        }
+    }
+
+    /** @hide */
+    public String classLoaderName;
+
+    /** @hide */
+    public String[] splitClassLoaderNames;
+
+    /** @hide */
+    public boolean hiddenUntilInstalled;
+
+    /** @hide */
+    public String zygotePreloadName;
+
+    /**
+     * Default (unspecified) setting of GWP-ASan.
+     */
+    public static final int GWP_ASAN_DEFAULT = -1;
+
+    /**
+     * Never enable GWP-ASan in this application or process.
+     */
+    public static final int GWP_ASAN_NEVER = 0;
+
+    /**
+     * Always enable GWP-ASan in this application or process.
+     */
+    public static final int GWP_ASAN_ALWAYS = 1;
+
+    /**
+     * These constants need to match the values of gwpAsanMode in application manifest.
+     * @hide
+     */
+    @IntDef(prefix = {"GWP_ASAN_"}, value = {
+            GWP_ASAN_DEFAULT,
+            GWP_ASAN_NEVER,
+            GWP_ASAN_ALWAYS,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface GwpAsanMode {}
+
+    /**
+     * Indicates if the application has requested GWP-ASan to be enabled, disabled, or left
+     * unspecified. Processes can override this setting.
+     */
+    private @GwpAsanMode int gwpAsanMode;
+
+    /**
+     * Represents the default policy. The actual policy used will depend on other properties of
+     * the application, e.g. the target SDK version.
+     * @hide
+     */
+    public static final int HIDDEN_API_ENFORCEMENT_DEFAULT = -1;
+    /**
+     * No API enforcement; the app can access the entire internal private API. Only for use by
+     * system apps.
+     * @hide
+     */
+    public static final int HIDDEN_API_ENFORCEMENT_DISABLED = 0;
+    /**
+     * No API enforcement, but enable the detection logic and warnings. Observed behaviour is the
+     * same as {@link #HIDDEN_API_ENFORCEMENT_DISABLED} but you may see warnings in the log when
+     * APIs are accessed.
+     * @hide
+     * */
+    public static final int HIDDEN_API_ENFORCEMENT_JUST_WARN = 1;
+    /**
+     * Dark grey list enforcement. Enforces the dark grey and black lists
+     * @hide
+     */
+    public static final int HIDDEN_API_ENFORCEMENT_ENABLED = 2;
+
+    private static final int HIDDEN_API_ENFORCEMENT_MIN = HIDDEN_API_ENFORCEMENT_DEFAULT;
+    private static final int HIDDEN_API_ENFORCEMENT_MAX = HIDDEN_API_ENFORCEMENT_ENABLED;
+
+    /**
+     * Values in this IntDef MUST be kept in sync with enum hiddenapi::EnforcementPolicy in
+     * art/runtime/hidden_api.h
+     * @hide
+     */
+    @IntDef(prefix = { "HIDDEN_API_ENFORCEMENT_" }, value = {
+            HIDDEN_API_ENFORCEMENT_DEFAULT,
+            HIDDEN_API_ENFORCEMENT_DISABLED,
+            HIDDEN_API_ENFORCEMENT_JUST_WARN,
+            HIDDEN_API_ENFORCEMENT_ENABLED,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface HiddenApiEnforcementPolicy {}
+
+    /** @hide */
+    public static boolean isValidHiddenApiEnforcementPolicy(int policy) {
+        return policy >= HIDDEN_API_ENFORCEMENT_MIN && policy <= HIDDEN_API_ENFORCEMENT_MAX;
+    }
+
+    private int mHiddenApiPolicy = HIDDEN_API_ENFORCEMENT_DEFAULT;
+
+    public void dump(Printer pw, String prefix) {
+        dump(pw, prefix, DUMP_FLAG_ALL);
+    }
+
+    /** @hide */
+    public void dump(Printer pw, String prefix, int dumpFlags) {
+        super.dumpFront(pw, prefix);
+        if ((dumpFlags & DUMP_FLAG_DETAILS) != 0 && className != null) {
+            pw.println(prefix + "className=" + className);
+        }
+        if (permission != null) {
+            pw.println(prefix + "permission=" + permission);
+        }
+        pw.println(prefix + "processName=" + processName);
+        if ((dumpFlags & DUMP_FLAG_DETAILS) != 0) {
+            pw.println(prefix + "taskAffinity=" + taskAffinity);
+        }
+        pw.println(prefix + "uid=" + uid + " flags=0x" + Integer.toHexString(flags)
+                + " privateFlags=0x" + Integer.toHexString(privateFlags)
+                + " theme=0x" + Integer.toHexString(theme));
+        if ((dumpFlags & DUMP_FLAG_DETAILS) != 0) {
+            pw.println(prefix + "requiresSmallestWidthDp=" + requiresSmallestWidthDp
+                    + " compatibleWidthLimitDp=" + compatibleWidthLimitDp
+                    + " largestWidthLimitDp=" + largestWidthLimitDp);
+        }
+        pw.println(prefix + "sourceDir=" + sourceDir);
+        if (!Objects.equals(sourceDir, publicSourceDir)) {
+            pw.println(prefix + "publicSourceDir=" + publicSourceDir);
+        }
+        if (!ArrayUtils.isEmpty(splitSourceDirs)) {
+            pw.println(prefix + "splitSourceDirs=" + Arrays.toString(splitSourceDirs));
+        }
+        if (!ArrayUtils.isEmpty(splitPublicSourceDirs)
+                && !Arrays.equals(splitSourceDirs, splitPublicSourceDirs)) {
+            pw.println(prefix + "splitPublicSourceDirs=" + Arrays.toString(splitPublicSourceDirs));
+        }
+        if (resourceDirs != null) {
+            pw.println(prefix + "resourceDirs=" + Arrays.toString(resourceDirs));
+        }
+        if ((dumpFlags & DUMP_FLAG_DETAILS) != 0 && seInfo != null) {
+            pw.println(prefix + "seinfo=" + seInfo);
+            pw.println(prefix + "seinfoUser=" + seInfoUser);
+        }
+        pw.println(prefix + "dataDir=" + dataDir);
+        if ((dumpFlags & DUMP_FLAG_DETAILS) != 0) {
+            pw.println(prefix + "deviceProtectedDataDir=" + deviceProtectedDataDir);
+            pw.println(prefix + "credentialProtectedDataDir=" + credentialProtectedDataDir);
+            if (sharedLibraryFiles != null) {
+                pw.println(prefix + "sharedLibraryFiles=" + Arrays.toString(sharedLibraryFiles));
+            }
+        }
+        if (classLoaderName != null) {
+            pw.println(prefix + "classLoaderName=" + classLoaderName);
+        }
+        if (!ArrayUtils.isEmpty(splitClassLoaderNames)) {
+            pw.println(prefix + "splitClassLoaderNames=" + Arrays.toString(splitClassLoaderNames));
+        }
+
+        pw.println(prefix + "enabled=" + enabled
+                + " minSdkVersion=" + minSdkVersion
+                + " targetSdkVersion=" + targetSdkVersion
+                + " versionCode=" + longVersionCode
+                + " targetSandboxVersion=" + targetSandboxVersion);
+        if ((dumpFlags & DUMP_FLAG_DETAILS) != 0) {
+            if (manageSpaceActivityName != null) {
+                pw.println(prefix + "manageSpaceActivityName=" + manageSpaceActivityName);
+            }
+            if (descriptionRes != 0) {
+                pw.println(prefix + "description=0x" + Integer.toHexString(descriptionRes));
+            }
+            if (uiOptions != 0) {
+                pw.println(prefix + "uiOptions=0x" + Integer.toHexString(uiOptions));
+            }
+            pw.println(prefix + "supportsRtl=" + (hasRtlSupport() ? "true" : "false"));
+            if (fullBackupContent > 0) {
+                pw.println(prefix + "fullBackupContent=@xml/" + fullBackupContent);
+            } else {
+                pw.println(prefix + "fullBackupContent="
+                        + (fullBackupContent < 0 ? "false" : "true"));
+            }
+            pw.println(prefix + "crossProfile=" + (crossProfile ? "true" : "false"));
+            if (networkSecurityConfigRes != 0) {
+                pw.println(prefix + "networkSecurityConfigRes=0x"
+                        + Integer.toHexString(networkSecurityConfigRes));
+            }
+            if (category != CATEGORY_UNDEFINED) {
+                pw.println(prefix + "category=" + category);
+            }
+            pw.println(prefix + "HiddenApiEnforcementPolicy=" + getHiddenApiEnforcementPolicy());
+            pw.println(prefix + "usesNonSdkApi=" + usesNonSdkApi());
+            pw.println(prefix + "allowsPlaybackCapture="
+                        + (isAudioPlaybackCaptureAllowed() ? "true" : "false"));
+            if (gwpAsanMode != GWP_ASAN_DEFAULT) {
+                pw.println(prefix + "gwpAsanMode=" + gwpAsanMode);
+            }
+        }
+        super.dumpBack(pw, prefix);
+    }
+
+    /** {@hide} */
+    public void dumpDebug(ProtoOutputStream proto, long fieldId, int dumpFlags) {
+        long token = proto.start(fieldId);
+        super.dumpDebug(proto, ApplicationInfoProto.PACKAGE, dumpFlags);
+        proto.write(ApplicationInfoProto.PERMISSION, permission);
+        proto.write(ApplicationInfoProto.PROCESS_NAME, processName);
+        proto.write(ApplicationInfoProto.UID, uid);
+        proto.write(ApplicationInfoProto.FLAGS, flags);
+        proto.write(ApplicationInfoProto.PRIVATE_FLAGS, privateFlags);
+        proto.write(ApplicationInfoProto.THEME, theme);
+        proto.write(ApplicationInfoProto.SOURCE_DIR, sourceDir);
+        if (!Objects.equals(sourceDir, publicSourceDir)) {
+            proto.write(ApplicationInfoProto.PUBLIC_SOURCE_DIR, publicSourceDir);
+        }
+        if (!ArrayUtils.isEmpty(splitSourceDirs)) {
+            for (String dir : splitSourceDirs) {
+                proto.write(ApplicationInfoProto.SPLIT_SOURCE_DIRS, dir);
+            }
+        }
+        if (!ArrayUtils.isEmpty(splitPublicSourceDirs)
+                && !Arrays.equals(splitSourceDirs, splitPublicSourceDirs)) {
+            for (String dir : splitPublicSourceDirs) {
+                proto.write(ApplicationInfoProto.SPLIT_PUBLIC_SOURCE_DIRS, dir);
+            }
+        }
+        if (resourceDirs != null) {
+            for (String dir : resourceDirs) {
+                proto.write(ApplicationInfoProto.RESOURCE_DIRS, dir);
+            }
+        }
+        proto.write(ApplicationInfoProto.DATA_DIR, dataDir);
+        proto.write(ApplicationInfoProto.CLASS_LOADER_NAME, classLoaderName);
+        if (!ArrayUtils.isEmpty(splitClassLoaderNames)) {
+            for (String name : splitClassLoaderNames) {
+                proto.write(ApplicationInfoProto.SPLIT_CLASS_LOADER_NAMES, name);
+            }
+        }
+
+        long versionToken = proto.start(ApplicationInfoProto.VERSION);
+        proto.write(ApplicationInfoProto.Version.ENABLED, enabled);
+        proto.write(ApplicationInfoProto.Version.MIN_SDK_VERSION, minSdkVersion);
+        proto.write(ApplicationInfoProto.Version.TARGET_SDK_VERSION, targetSdkVersion);
+        proto.write(ApplicationInfoProto.Version.VERSION_CODE, longVersionCode);
+        proto.write(ApplicationInfoProto.Version.TARGET_SANDBOX_VERSION, targetSandboxVersion);
+        proto.end(versionToken);
+
+        if ((dumpFlags & DUMP_FLAG_DETAILS) != 0) {
+            long detailToken = proto.start(ApplicationInfoProto.DETAIL);
+            if (className != null) {
+                proto.write(ApplicationInfoProto.Detail.CLASS_NAME, className);
+            }
+            proto.write(ApplicationInfoProto.Detail.TASK_AFFINITY, taskAffinity);
+            proto.write(ApplicationInfoProto.Detail.REQUIRES_SMALLEST_WIDTH_DP,
+                    requiresSmallestWidthDp);
+            proto.write(ApplicationInfoProto.Detail.COMPATIBLE_WIDTH_LIMIT_DP,
+                    compatibleWidthLimitDp);
+            proto.write(ApplicationInfoProto.Detail.LARGEST_WIDTH_LIMIT_DP,
+                    largestWidthLimitDp);
+            if (seInfo != null) {
+                proto.write(ApplicationInfoProto.Detail.SEINFO, seInfo);
+                proto.write(ApplicationInfoProto.Detail.SEINFO_USER, seInfoUser);
+            }
+            proto.write(ApplicationInfoProto.Detail.DEVICE_PROTECTED_DATA_DIR,
+                    deviceProtectedDataDir);
+            proto.write(ApplicationInfoProto.Detail.CREDENTIAL_PROTECTED_DATA_DIR,
+                    credentialProtectedDataDir);
+            if (sharedLibraryFiles != null) {
+                for (String f : sharedLibraryFiles) {
+                    proto.write(ApplicationInfoProto.Detail.SHARED_LIBRARY_FILES, f);
+                }
+            }
+            if (manageSpaceActivityName != null) {
+                proto.write(ApplicationInfoProto.Detail.MANAGE_SPACE_ACTIVITY_NAME,
+                        manageSpaceActivityName);
+            }
+            if (descriptionRes != 0) {
+                proto.write(ApplicationInfoProto.Detail.DESCRIPTION_RES, descriptionRes);
+            }
+            if (uiOptions != 0) {
+                proto.write(ApplicationInfoProto.Detail.UI_OPTIONS, uiOptions);
+            }
+            proto.write(ApplicationInfoProto.Detail.SUPPORTS_RTL, hasRtlSupport());
+            if (fullBackupContent > 0) {
+                proto.write(ApplicationInfoProto.Detail.CONTENT, "@xml/" + fullBackupContent);
+            } else {
+                proto.write(ApplicationInfoProto.Detail.IS_FULL_BACKUP, fullBackupContent == 0);
+            }
+            if (networkSecurityConfigRes != 0) {
+                proto.write(ApplicationInfoProto.Detail.NETWORK_SECURITY_CONFIG_RES,
+                        networkSecurityConfigRes);
+            }
+            if (category != CATEGORY_UNDEFINED) {
+                proto.write(ApplicationInfoProto.Detail.CATEGORY, category);
+            }
+            if (gwpAsanMode != GWP_ASAN_DEFAULT) {
+                proto.write(ApplicationInfoProto.Detail.ENABLE_GWP_ASAN, gwpAsanMode);
+            }
+            proto.end(detailToken);
+        }
+        proto.end(token);
+    }
+
+    /**
+     * @return true if "supportsRtl" has been set to true in the AndroidManifest
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public boolean hasRtlSupport() {
+        return (flags & FLAG_SUPPORTS_RTL) == FLAG_SUPPORTS_RTL;
+    }
+
+    /** {@hide} */
+    public boolean hasCode() {
+        return (flags & FLAG_HAS_CODE) != 0;
+    }
+
+    public static class DisplayNameComparator
+            implements Comparator<ApplicationInfo> {
+        public DisplayNameComparator(PackageManager pm) {
+            mPM = pm;
+        }
+
+        public final int compare(ApplicationInfo aa, ApplicationInfo ab) {
+            CharSequence  sa = mPM.getApplicationLabel(aa);
+            if (sa == null) {
+                sa = aa.packageName;
+            }
+            CharSequence  sb = mPM.getApplicationLabel(ab);
+            if (sb == null) {
+                sb = ab.packageName;
+            }
+            
+            return sCollator.compare(sa.toString(), sb.toString());
+        }
+
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+        private final Collator   sCollator = Collator.getInstance();
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+        private PackageManager   mPM;
+    }
+
+    public ApplicationInfo() {
+    }
+    
+    public ApplicationInfo(ApplicationInfo orig) {
+        super(orig);
+        taskAffinity = orig.taskAffinity;
+        permission = orig.permission;
+        processName = orig.processName;
+        className = orig.className;
+        theme = orig.theme;
+        flags = orig.flags;
+        privateFlags = orig.privateFlags;
+        requiresSmallestWidthDp = orig.requiresSmallestWidthDp;
+        compatibleWidthLimitDp = orig.compatibleWidthLimitDp;
+        largestWidthLimitDp = orig.largestWidthLimitDp;
+        volumeUuid = orig.volumeUuid;
+        storageUuid = orig.storageUuid;
+        scanSourceDir = orig.scanSourceDir;
+        scanPublicSourceDir = orig.scanPublicSourceDir;
+        sourceDir = orig.sourceDir;
+        publicSourceDir = orig.publicSourceDir;
+        splitNames = orig.splitNames;
+        splitSourceDirs = orig.splitSourceDirs;
+        splitPublicSourceDirs = orig.splitPublicSourceDirs;
+        splitDependencies = orig.splitDependencies;
+        nativeLibraryDir = orig.nativeLibraryDir;
+        secondaryNativeLibraryDir = orig.secondaryNativeLibraryDir;
+        nativeLibraryRootDir = orig.nativeLibraryRootDir;
+        nativeLibraryRootRequiresIsa = orig.nativeLibraryRootRequiresIsa;
+        primaryCpuAbi = orig.primaryCpuAbi;
+        secondaryCpuAbi = orig.secondaryCpuAbi;
+        resourceDirs = orig.resourceDirs;
+        seInfo = orig.seInfo;
+        seInfoUser = orig.seInfoUser;
+        sharedLibraryFiles = orig.sharedLibraryFiles;
+        sharedLibraryInfos = orig.sharedLibraryInfos;
+        dataDir = orig.dataDir;
+        deviceProtectedDataDir = orig.deviceProtectedDataDir;
+        credentialProtectedDataDir = orig.credentialProtectedDataDir;
+        uid = orig.uid;
+        minSdkVersion = orig.minSdkVersion;
+        targetSdkVersion = orig.targetSdkVersion;
+        setVersionCode(orig.longVersionCode);
+        enabled = orig.enabled;
+        enabledSetting = orig.enabledSetting;
+        installLocation = orig.installLocation;
+        manageSpaceActivityName = orig.manageSpaceActivityName;
+        descriptionRes = orig.descriptionRes;
+        uiOptions = orig.uiOptions;
+        backupAgentName = orig.backupAgentName;
+        fullBackupContent = orig.fullBackupContent;
+        crossProfile = orig.crossProfile;
+        networkSecurityConfigRes = orig.networkSecurityConfigRes;
+        category = orig.category;
+        targetSandboxVersion = orig.targetSandboxVersion;
+        classLoaderName = orig.classLoaderName;
+        splitClassLoaderNames = orig.splitClassLoaderNames;
+        appComponentFactory = orig.appComponentFactory;
+        iconRes = orig.iconRes;
+        roundIconRes = orig.roundIconRes;
+        compileSdkVersion = orig.compileSdkVersion;
+        compileSdkVersionCodename = orig.compileSdkVersionCodename;
+        mHiddenApiPolicy = orig.mHiddenApiPolicy;
+        hiddenUntilInstalled = orig.hiddenUntilInstalled;
+        zygotePreloadName = orig.zygotePreloadName;
+        gwpAsanMode = orig.gwpAsanMode;
+    }
+
+    public String toString() {
+        return "ApplicationInfo{"
+            + Integer.toHexString(System.identityHashCode(this))
+            + " " + packageName + "}";
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    @SuppressWarnings("unchecked")
+    public void writeToParcel(Parcel dest, int parcelableFlags) {
+        if (dest.maybeWriteSquashed(this)) {
+            return;
+        }
+        super.writeToParcel(dest, parcelableFlags);
+        dest.writeString8(taskAffinity);
+        dest.writeString8(permission);
+        dest.writeString8(processName);
+        dest.writeString8(className);
+        dest.writeInt(theme);
+        dest.writeInt(flags);
+        dest.writeInt(privateFlags);
+        dest.writeInt(requiresSmallestWidthDp);
+        dest.writeInt(compatibleWidthLimitDp);
+        dest.writeInt(largestWidthLimitDp);
+        if (storageUuid != null) {
+            dest.writeInt(1);
+            dest.writeLong(storageUuid.getMostSignificantBits());
+            dest.writeLong(storageUuid.getLeastSignificantBits());
+        } else {
+            dest.writeInt(0);
+        }
+        dest.writeString8(scanSourceDir);
+        dest.writeString8(scanPublicSourceDir);
+        dest.writeString8(sourceDir);
+        dest.writeString8(publicSourceDir);
+        dest.writeString8Array(splitNames);
+        dest.writeString8Array(splitSourceDirs);
+        dest.writeString8Array(splitPublicSourceDirs);
+        dest.writeSparseArray((SparseArray) splitDependencies);
+        dest.writeString8(nativeLibraryDir);
+        dest.writeString8(secondaryNativeLibraryDir);
+        dest.writeString8(nativeLibraryRootDir);
+        dest.writeInt(nativeLibraryRootRequiresIsa ? 1 : 0);
+        dest.writeString8(primaryCpuAbi);
+        dest.writeString8(secondaryCpuAbi);
+        dest.writeString8Array(resourceDirs);
+        dest.writeString8(seInfo);
+        dest.writeString8(seInfoUser);
+        dest.writeString8Array(sharedLibraryFiles);
+        dest.writeTypedList(sharedLibraryInfos);
+        dest.writeString8(dataDir);
+        dest.writeString8(deviceProtectedDataDir);
+        dest.writeString8(credentialProtectedDataDir);
+        dest.writeInt(uid);
+        dest.writeInt(minSdkVersion);
+        dest.writeInt(targetSdkVersion);
+        dest.writeLong(longVersionCode);
+        dest.writeInt(enabled ? 1 : 0);
+        dest.writeInt(enabledSetting);
+        dest.writeInt(installLocation);
+        dest.writeString8(manageSpaceActivityName);
+        dest.writeString8(backupAgentName);
+        dest.writeInt(descriptionRes);
+        dest.writeInt(uiOptions);
+        dest.writeInt(fullBackupContent);
+        dest.writeBoolean(crossProfile);
+        dest.writeInt(networkSecurityConfigRes);
+        dest.writeInt(category);
+        dest.writeInt(targetSandboxVersion);
+        dest.writeString8(classLoaderName);
+        dest.writeString8Array(splitClassLoaderNames);
+        dest.writeInt(compileSdkVersion);
+        dest.writeString8(compileSdkVersionCodename);
+        dest.writeString8(appComponentFactory);
+        dest.writeInt(iconRes);
+        dest.writeInt(roundIconRes);
+        dest.writeInt(mHiddenApiPolicy);
+        dest.writeInt(hiddenUntilInstalled ? 1 : 0);
+        dest.writeString8(zygotePreloadName);
+        dest.writeInt(gwpAsanMode);
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<ApplicationInfo> CREATOR
+            = new Parcelable.Creator<ApplicationInfo>() {
+        @Override
+        public ApplicationInfo createFromParcel(Parcel source) {
+            return source.readSquashed(ApplicationInfo::new);
+        }
+
+        @Override
+        public ApplicationInfo[] newArray(int size) {
+            return new ApplicationInfo[size];
+        }
+    };
+
+    @SuppressWarnings("unchecked")
+    private ApplicationInfo(Parcel source) {
+        super(source);
+        taskAffinity = source.readString8();
+        permission = source.readString8();
+        processName = source.readString8();
+        className = source.readString8();
+        theme = source.readInt();
+        flags = source.readInt();
+        privateFlags = source.readInt();
+        requiresSmallestWidthDp = source.readInt();
+        compatibleWidthLimitDp = source.readInt();
+        largestWidthLimitDp = source.readInt();
+        if (source.readInt() != 0) {
+            storageUuid = new UUID(source.readLong(), source.readLong());
+            volumeUuid = StorageManager.convert(storageUuid);
+        }
+        scanSourceDir = source.readString8();
+        scanPublicSourceDir = source.readString8();
+        sourceDir = source.readString8();
+        publicSourceDir = source.readString8();
+        splitNames = source.createString8Array();
+        splitSourceDirs = source.createString8Array();
+        splitPublicSourceDirs = source.createString8Array();
+        splitDependencies = source.readSparseArray(null);
+        nativeLibraryDir = source.readString8();
+        secondaryNativeLibraryDir = source.readString8();
+        nativeLibraryRootDir = source.readString8();
+        nativeLibraryRootRequiresIsa = source.readInt() != 0;
+        primaryCpuAbi = source.readString8();
+        secondaryCpuAbi = source.readString8();
+        resourceDirs = source.createString8Array();
+        seInfo = source.readString8();
+        seInfoUser = source.readString8();
+        sharedLibraryFiles = source.createString8Array();
+        sharedLibraryInfos = source.createTypedArrayList(SharedLibraryInfo.CREATOR);
+        dataDir = source.readString8();
+        deviceProtectedDataDir = source.readString8();
+        credentialProtectedDataDir = source.readString8();
+        uid = source.readInt();
+        minSdkVersion = source.readInt();
+        targetSdkVersion = source.readInt();
+        setVersionCode(source.readLong());
+        enabled = source.readInt() != 0;
+        enabledSetting = source.readInt();
+        installLocation = source.readInt();
+        manageSpaceActivityName = source.readString8();
+        backupAgentName = source.readString8();
+        descriptionRes = source.readInt();
+        uiOptions = source.readInt();
+        fullBackupContent = source.readInt();
+        crossProfile = source.readBoolean();
+        networkSecurityConfigRes = source.readInt();
+        category = source.readInt();
+        targetSandboxVersion = source.readInt();
+        classLoaderName = source.readString8();
+        splitClassLoaderNames = source.createString8Array();
+        compileSdkVersion = source.readInt();
+        compileSdkVersionCodename = source.readString8();
+        appComponentFactory = source.readString8();
+        iconRes = source.readInt();
+        roundIconRes = source.readInt();
+        mHiddenApiPolicy = source.readInt();
+        hiddenUntilInstalled = source.readInt() != 0;
+        zygotePreloadName = source.readString8();
+        gwpAsanMode = source.readInt();
+    }
+
+    /**
+     * Retrieve the textual description of the application.  This
+     * will call back on the given PackageManager to load the description from
+     * the application.
+     *
+     * @param pm A PackageManager from which the label can be loaded; usually
+     * the PackageManager from which you originally retrieved this item.
+     *
+     * @return Returns a CharSequence containing the application's description.
+     * If there is no description, null is returned.
+     */
+    public CharSequence loadDescription(PackageManager pm) {
+        if (descriptionRes != 0) {
+            CharSequence label = pm.getText(packageName, descriptionRes, this);
+            if (label != null) {
+                return label;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Disable compatibility mode
+     * 
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    public void disableCompatibilityMode() {
+        flags |= (FLAG_SUPPORTS_LARGE_SCREENS | FLAG_SUPPORTS_NORMAL_SCREENS |
+                FLAG_SUPPORTS_SMALL_SCREENS | FLAG_RESIZEABLE_FOR_SCREENS |
+                FLAG_SUPPORTS_SCREEN_DENSITIES | FLAG_SUPPORTS_XLARGE_SCREENS);
+    }
+
+    /**
+     * Is using compatibility mode for non densty aware legacy applications.
+     *
+     * @hide
+     */
+    public boolean usesCompatibilityMode() {
+        return targetSdkVersion < DONUT ||
+                (flags & (FLAG_SUPPORTS_LARGE_SCREENS | FLAG_SUPPORTS_NORMAL_SCREENS |
+                 FLAG_SUPPORTS_SMALL_SCREENS | FLAG_RESIZEABLE_FOR_SCREENS |
+                 FLAG_SUPPORTS_SCREEN_DENSITIES | FLAG_SUPPORTS_XLARGE_SCREENS)) == 0;
+    }
+
+    /** {@hide} */
+    public void initForUser(int userId) {
+        uid = UserHandle.getUid(userId, UserHandle.getAppId(uid));
+
+        if ("android".equals(packageName)) {
+            dataDir = Environment.getDataSystemDirectory().getAbsolutePath();
+            return;
+        }
+
+        deviceProtectedDataDir = Environment
+                .getDataUserDePackageDirectory(volumeUuid, userId, packageName)
+                .getAbsolutePath();
+        credentialProtectedDataDir = Environment
+                .getDataUserCePackageDirectory(volumeUuid, userId, packageName)
+                .getAbsolutePath();
+
+        if ((privateFlags & PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE) != 0
+                && PackageManager.APPLY_DEFAULT_TO_DEVICE_PROTECTED_STORAGE) {
+            dataDir = deviceProtectedDataDir;
+        } else {
+            dataDir = credentialProtectedDataDir;
+        }
+    }
+
+    private boolean isPackageWhitelistedForHiddenApis() {
+        return SystemConfig.getInstance().getHiddenApiWhitelistedApps().contains(packageName);
+    }
+
+    /**
+     * @hide
+     */
+    public boolean usesNonSdkApi() {
+        return (privateFlags & PRIVATE_FLAG_USES_NON_SDK_API) != 0;
+    }
+
+    /**
+     * Whether an app needs to keep the app data on uninstall.
+     *
+     * @return {@code true} if the app indicates that it needs to keep the app data
+     *
+     * @hide
+     */
+    public boolean hasFragileUserData() {
+        return (privateFlags & PRIVATE_FLAG_HAS_FRAGILE_USER_DATA) != 0;
+    }
+
+    /**
+     * Whether an app allows its playback audio to be captured by other apps.
+     *
+     * @return {@code true} if the app indicates that its audio can be captured by other apps.
+     *
+     * @hide
+     */
+    public boolean isAudioPlaybackCaptureAllowed() {
+        return (privateFlags & PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE) != 0;
+    }
+
+    /**
+     * If {@code true} this app requested to run in the legacy storage mode.
+     *
+     * @hide
+     */
+    public boolean hasRequestedLegacyExternalStorage() {
+        return (privateFlags & PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE) != 0;
+    }
+
+    /**
+     * If {@code true} this app allows heap pointer tagging.
+     *
+     * @hide
+     */
+    public boolean allowsNativeHeapPointerTagging() {
+        return (privateFlags & PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING) != 0;
+    }
+
+    private boolean isAllowedToUseHiddenApis() {
+        if (isSignedWithPlatformKey()) {
+            return true;
+        } else if (isSystemApp() || isUpdatedSystemApp()) {
+            return usesNonSdkApi() || isPackageWhitelistedForHiddenApis();
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public @HiddenApiEnforcementPolicy int getHiddenApiEnforcementPolicy() {
+        if (isAllowedToUseHiddenApis()) {
+            return HIDDEN_API_ENFORCEMENT_DISABLED;
+        }
+        if (mHiddenApiPolicy != HIDDEN_API_ENFORCEMENT_DEFAULT) {
+            return mHiddenApiPolicy;
+        }
+        return HIDDEN_API_ENFORCEMENT_ENABLED;
+    }
+
+    /**
+     * @hide
+     */
+    public void setHiddenApiEnforcementPolicy(@HiddenApiEnforcementPolicy int policy) {
+        if (!isValidHiddenApiEnforcementPolicy(policy)) {
+            throw new IllegalArgumentException("Invalid API enforcement policy: " + policy);
+        }
+        mHiddenApiPolicy = policy;
+    }
+
+    /**
+     * Updates the hidden API enforcement policy for this app from the given values, if appropriate.
+     *
+     * This will have no effect if this app is not subject to hidden API enforcement, i.e. if it
+     * is on the package whitelist.
+     *
+     * @param policy configured policy for this app, or {@link #HIDDEN_API_ENFORCEMENT_DEFAULT}
+     *        if nothing configured.
+     * @hide
+     */
+    public void maybeUpdateHiddenApiEnforcementPolicy(@HiddenApiEnforcementPolicy int policy) {
+        if (isPackageWhitelistedForHiddenApis()) {
+            return;
+        }
+        setHiddenApiEnforcementPolicy(policy);
+    }
+
+    /**
+     * @hide
+     */
+    public void setVersionCode(long newVersionCode) {
+        longVersionCode = newVersionCode;
+        versionCode = (int) newVersionCode;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public Drawable loadDefaultIcon(PackageManager pm) {
+        if ((flags & FLAG_EXTERNAL_STORAGE) != 0
+                && isPackageUnavailable(pm)) {
+            return Resources.getSystem().getDrawable(
+                    com.android.internal.R.drawable.sym_app_on_sd_unavailable_icon);
+        }
+        return pm.getDefaultActivityIcon();
+    }
+    
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    private boolean isPackageUnavailable(PackageManager pm) {
+        try {
+            return pm.getPackageInfo(packageName, 0) == null;
+        } catch (NameNotFoundException ex) {
+            return true;
+        }
+    }
+
+    /** @hide */
+    public boolean isDefaultToDeviceProtectedStorage() {
+        return (privateFlags
+                & ApplicationInfo.PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE) != 0;
+    }
+
+    /** @hide */
+    public boolean isDirectBootAware() {
+        return (privateFlags & ApplicationInfo.PRIVATE_FLAG_DIRECT_BOOT_AWARE) != 0;
+    }
+
+    /**
+     * Check whether the application is encryption aware.
+     *
+     * @see #isDirectBootAware()
+     * @see #isPartiallyDirectBootAware()
+     *
+     * @hide
+     */
+    @SystemApi
+    public boolean isEncryptionAware() {
+        return isDirectBootAware() || isPartiallyDirectBootAware();
+    }
+
+    /** @hide */
+    public boolean isExternal() {
+        return (flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
+    }
+
+    /**
+     * True if the application is installed as an instant app.
+     * @hide
+     */
+    @SystemApi
+    public boolean isInstantApp() {
+        return (privateFlags & ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0;
+    }
+
+    /** @hide */
+    public boolean isInternal() {
+        return (flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) == 0;
+    }
+
+    /** @hide */
+    public boolean isOem() {
+        return (privateFlags & ApplicationInfo.PRIVATE_FLAG_OEM) != 0;
+    }
+
+    /** @hide */
+    public boolean isOdm() {
+        return (privateFlags & ApplicationInfo.PRIVATE_FLAG_ODM) != 0;
+    }
+
+    /** @hide */
+    public boolean isPartiallyDirectBootAware() {
+        return (privateFlags & ApplicationInfo.PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE) != 0;
+    }
+
+    /** @hide */
+    public boolean isSignedWithPlatformKey() {
+        return (privateFlags & ApplicationInfo.PRIVATE_FLAG_SIGNED_WITH_PLATFORM_KEY) != 0;
+    }
+
+    /** @hide */
+    @TestApi
+    public boolean isPrivilegedApp() {
+        return (privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
+    }
+
+    /** @hide */
+    public boolean isRequiredForSystemUser() {
+        return (privateFlags & ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER) != 0;
+    }
+
+    /** @hide */
+    public boolean isStaticSharedLibrary() {
+        return (privateFlags & ApplicationInfo.PRIVATE_FLAG_STATIC_SHARED_LIBRARY) != 0;
+    }
+
+    /** @hide */
+    @TestApi
+    public boolean isSystemApp() {
+        return (flags & ApplicationInfo.FLAG_SYSTEM) != 0;
+    }
+
+    /** @hide */
+    public boolean isUpdatedSystemApp() {
+        return (flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
+    }
+
+    /** @hide */
+    public boolean isVendor() {
+        return (privateFlags & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0;
+    }
+
+    /** @hide */
+    public boolean isProduct() {
+        return (privateFlags & ApplicationInfo.PRIVATE_FLAG_PRODUCT) != 0;
+    }
+
+    /** @hide */
+    public boolean isSystemExt() {
+        return (privateFlags & ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT) != 0;
+    }
+
+    /** @hide */
+    public boolean isEmbeddedDexUsed() {
+        return (privateFlags & PRIVATE_FLAG_USE_EMBEDDED_DEX) != 0;
+    }
+
+    /**
+     * Returns whether or not this application was installed as a virtual preload.
+     */
+    public boolean isVirtualPreload() {
+        return (privateFlags & PRIVATE_FLAG_VIRTUAL_PRELOAD) != 0;
+    }
+
+    /**
+     * Returns whether or not this application can be profiled by the shell user,
+     * even when running on a device that is running in user mode.
+     */
+    public boolean isProfileableByShell() {
+        return (privateFlags & PRIVATE_FLAG_PROFILEABLE_BY_SHELL) != 0;
+    }
+
+    /**
+     * Returns true if the app has declared in its manifest that it wants its split APKs to be
+     * loaded into isolated Contexts, with their own ClassLoaders and Resources objects.
+     * @hide
+     */
+    public boolean requestsIsolatedSplitLoading() {
+        return (privateFlags & ApplicationInfo.PRIVATE_FLAG_ISOLATED_SPLIT_LOADING) != 0;
+    }
+
+    /**
+     * Returns true if the package has declared in its manifest that it is a
+     * runtime resource overlay.
+     */
+    public boolean isResourceOverlay() {
+        return (privateFlags & ApplicationInfo.PRIVATE_FLAG_IS_RESOURCE_OVERLAY) != 0;
+    }
+
+    /**
+     * @hide
+     */
+    @Override protected ApplicationInfo getApplicationInfo() {
+        return this;
+    }
+
+    /**
+     * Return all the APK paths that may be required to load this application, including all
+     * splits, shared libraries, and resource overlays.
+     * @hide
+     */
+    public String[] getAllApkPaths() {
+        final String[][] inputLists = { splitSourceDirs, sharedLibraryFiles, resourceDirs };
+        final List<String> output = new ArrayList<>(10);
+        if (sourceDir != null) {
+            output.add(sourceDir);
+        }
+        for (String[] inputList : inputLists) {
+            if (inputList != null) {
+                for (String input : inputList) {
+                    output.add(input);
+                }
+            }
+        }
+        return output.toArray(new String[output.size()]);
+    }
+
+    /** {@hide} */ public void setCodePath(String codePath) { scanSourceDir = codePath; }
+    /** {@hide} */ public void setBaseCodePath(String baseCodePath) { sourceDir = baseCodePath; }
+    /** {@hide} */ public void setSplitCodePaths(String[] splitCodePaths) { splitSourceDirs = splitCodePaths; }
+    /** {@hide} */ public void setResourcePath(String resourcePath) { scanPublicSourceDir = resourcePath; }
+    /** {@hide} */ public void setBaseResourcePath(String baseResourcePath) { publicSourceDir = baseResourcePath; }
+    /** {@hide} */ public void setSplitResourcePaths(String[] splitResourcePaths) { splitPublicSourceDirs = splitResourcePaths; }
+    /** {@hide} */ public void setGwpAsanMode(@GwpAsanMode int value) { gwpAsanMode = value; }
+
+    /** {@hide} */
+    @UnsupportedAppUsage
+    public String getCodePath() { return scanSourceDir; }
+    /** {@hide} */ public String getBaseCodePath() { return sourceDir; }
+    /** {@hide} */ public String[] getSplitCodePaths() { return splitSourceDirs; }
+    /** {@hide} */ public String getResourcePath() { return scanPublicSourceDir; }
+    /** {@hide} */
+    @UnsupportedAppUsage
+    public String getBaseResourcePath() { return publicSourceDir; }
+    /** {@hide} */ public String[] getSplitResourcePaths() { return splitPublicSourceDirs; }
+    @GwpAsanMode
+    public int getGwpAsanMode() { return gwpAsanMode; }
+}
diff --git a/android/content/pm/AppsQueryHelper.java b/android/content/pm/AppsQueryHelper.java
new file mode 100644
index 0000000..6cb7f77
--- /dev/null
+++ b/android/content/pm/AppsQueryHelper.java
@@ -0,0 +1,217 @@
+/*
+ * 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.content.pm;
+
+import android.Manifest;
+import android.app.AppGlobals;
+import android.content.Intent;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.ArraySet;
+import android.view.inputmethod.InputMethod;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Helper class for querying installed applications using multiple criteria.
+ *
+ * @hide
+ */
+public class AppsQueryHelper {
+
+    /**
+     * Return apps without launcher icon
+     */
+    public static int GET_NON_LAUNCHABLE_APPS = 1;
+
+    /**
+     * Return apps with {@link Manifest.permission#INTERACT_ACROSS_USERS} permission
+     */
+    public static int GET_APPS_WITH_INTERACT_ACROSS_USERS_PERM = 1 << 1;
+
+    /**
+     * Return all input methods available for the current user.
+     */
+    public static int GET_IMES = 1 << 2;
+
+    /**
+     * Return all apps that are flagged as required for the system user.
+     */
+    public static int GET_REQUIRED_FOR_SYSTEM_USER = 1 << 3;
+
+    private final IPackageManager mPackageManager;
+    private List<ApplicationInfo> mAllApps;
+
+    public AppsQueryHelper(IPackageManager packageManager) {
+        mPackageManager = packageManager;
+    }
+
+    public AppsQueryHelper() {
+        this(AppGlobals.getPackageManager());
+    }
+
+    /**
+     * Return a List of all packages that satisfy a specified criteria.
+     * @param flags search flags. Use any combination of {@link #GET_NON_LAUNCHABLE_APPS},
+     * {@link #GET_APPS_WITH_INTERACT_ACROSS_USERS_PERM} or {@link #GET_IMES}.
+     * @param systemAppsOnly if true, only system apps will be returned
+     * @param user user, whose apps are queried
+     */
+    public List<String> queryApps(int flags, boolean systemAppsOnly, UserHandle user) {
+        boolean nonLaunchableApps = (flags & GET_NON_LAUNCHABLE_APPS) > 0;
+        boolean interactAcrossUsers = (flags & GET_APPS_WITH_INTERACT_ACROSS_USERS_PERM) > 0;
+        boolean imes = (flags & GET_IMES) > 0;
+        boolean requiredForSystemUser = (flags & GET_REQUIRED_FOR_SYSTEM_USER) > 0;
+        if (mAllApps == null) {
+            mAllApps = getAllApps(user.getIdentifier());
+        }
+
+        List<String> result = new ArrayList<>();
+        if (flags == 0) {
+            final int allAppsSize = mAllApps.size();
+            for (int i = 0; i < allAppsSize; i++) {
+                final ApplicationInfo appInfo = mAllApps.get(i);
+                if (systemAppsOnly && !appInfo.isSystemApp()) {
+                    continue;
+                }
+                result.add(appInfo.packageName);
+            }
+            return result;
+        }
+
+        if (nonLaunchableApps) {
+            Intent intent = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_LAUNCHER);
+            final List<ResolveInfo> resolveInfos = queryIntentActivitiesAsUser(intent,
+                    user.getIdentifier());
+
+            ArraySet<String> appsWithLaunchers = new ArraySet<>();
+            final int resolveInfosSize = resolveInfos.size();
+            for (int i = 0; i < resolveInfosSize; i++) {
+                appsWithLaunchers.add(resolveInfos.get(i).activityInfo.packageName);
+            }
+            final int allAppsSize = mAllApps.size();
+            for (int i = 0; i < allAppsSize; i++) {
+                final ApplicationInfo appInfo = mAllApps.get(i);
+                if (systemAppsOnly && !appInfo.isSystemApp()) {
+                    continue;
+                }
+                final String packageName = appInfo.packageName;
+                if (!appsWithLaunchers.contains(packageName)) {
+                    result.add(packageName);
+                }
+            }
+        }
+        if (interactAcrossUsers) {
+            final List<PackageInfo> packagesHoldingPermissions = getPackagesHoldingPermission(
+                    Manifest.permission.INTERACT_ACROSS_USERS, user.getIdentifier());
+            final int packagesHoldingPermissionsSize = packagesHoldingPermissions.size();
+            for (int i = 0; i < packagesHoldingPermissionsSize; i++) {
+                PackageInfo packageInfo = packagesHoldingPermissions.get(i);
+                if (systemAppsOnly && !packageInfo.applicationInfo.isSystemApp()) {
+                    continue;
+                }
+                if (!result.contains(packageInfo.packageName)) {
+                    result.add(packageInfo.packageName);
+                }
+            }
+        }
+
+        if (imes) {
+            final List<ResolveInfo> resolveInfos = queryIntentServicesAsUser(
+                    new Intent(InputMethod.SERVICE_INTERFACE), user.getIdentifier());
+            final int resolveInfosSize = resolveInfos.size();
+
+            for (int i = 0; i < resolveInfosSize; i++) {
+                ServiceInfo serviceInfo = resolveInfos.get(i).serviceInfo;
+                if (systemAppsOnly && !serviceInfo.applicationInfo.isSystemApp()) {
+                    continue;
+                }
+                if (!result.contains(serviceInfo.packageName)) {
+                    result.add(serviceInfo.packageName);
+                }
+            }
+        }
+
+        if (requiredForSystemUser) {
+            final int allAppsSize = mAllApps.size();
+            for (int i = 0; i < allAppsSize; i++) {
+                final ApplicationInfo appInfo = mAllApps.get(i);
+                if (systemAppsOnly && !appInfo.isSystemApp()) {
+                    continue;
+                }
+                if (appInfo.isRequiredForSystemUser()) {
+                    result.add(appInfo.packageName);
+                }
+            }
+        }
+        return result;
+    }
+
+    @VisibleForTesting
+    @SuppressWarnings("unchecked")
+    protected List<ApplicationInfo> getAllApps(int userId) {
+        try {
+            return mPackageManager.getInstalledApplications(
+                    PackageManager.MATCH_UNINSTALLED_PACKAGES
+                            | PackageManager.MATCH_DISABLED_COMPONENTS, userId).getList();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    @VisibleForTesting
+    protected List<ResolveInfo> queryIntentActivitiesAsUser(Intent intent, int userId) {
+        try {
+            return mPackageManager.queryIntentActivities(intent, null,
+                    PackageManager.MATCH_DISABLED_COMPONENTS
+                            | PackageManager.MATCH_UNINSTALLED_PACKAGES
+                            | PackageManager.MATCH_DIRECT_BOOT_AWARE
+                            | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
+                    userId).getList();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    @VisibleForTesting
+    protected List<ResolveInfo> queryIntentServicesAsUser(Intent intent, int userId) {
+        try {
+            return mPackageManager.queryIntentServices(intent, null,
+                    PackageManager.GET_META_DATA
+                            | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
+                            | PackageManager.MATCH_DIRECT_BOOT_AWARE
+                            | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId)
+                    .getList();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    @VisibleForTesting
+    @SuppressWarnings("unchecked")
+    protected List<PackageInfo> getPackagesHoldingPermission(String perm, int userId) {
+        try {
+            return mPackageManager.getPackagesHoldingPermissions(new String[]{perm}, 0,
+                    userId).getList();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+}
diff --git a/android/content/pm/AuxiliaryResolveInfo.java b/android/content/pm/AuxiliaryResolveInfo.java
new file mode 100644
index 0000000..7d07e1d
--- /dev/null
+++ b/android/content/pm/AuxiliaryResolveInfo.java
@@ -0,0 +1,134 @@
+/*
+ * 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.content.pm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Auxiliary application resolution response.
+ * <p>
+ * Used when resolution occurs, but, the target is not actually on the device.
+ * This happens resolving instant apps that haven't been installed yet or if
+ * the application consists of multiple feature splits and the needed split
+ * hasn't been installed.
+ * @hide
+ */
+public final class AuxiliaryResolveInfo {
+    /** The activity to launch if there's an installation failure. */
+    public final ComponentName installFailureActivity;
+    /** Whether or not instant resolution needs the second phase */
+    public final boolean needsPhaseTwo;
+    /** Opaque token to track the instant application resolution */
+    public final String token;
+    /** An intent to start upon failure to install */
+    public final Intent failureIntent;
+    /** The matching filters for this resolve info. */
+    public final List<AuxiliaryFilter> filters;
+    /** Stored {@link InstantAppRequest#hostDigestPrefixSecure} to prevent re-generation */
+    public final int[] hostDigestPrefixSecure;
+
+    /** Create a response for installing an instant application. */
+    public AuxiliaryResolveInfo(@NonNull String token,
+            boolean needsPhase2,
+            @Nullable Intent failureIntent,
+            @Nullable List<AuxiliaryFilter> filters,
+            @Nullable int[] hostDigestPrefix) {
+        this.token = token;
+        this.needsPhaseTwo = needsPhase2;
+        this.failureIntent = failureIntent;
+        this.filters = filters;
+        this.installFailureActivity = null;
+        this.hostDigestPrefixSecure = hostDigestPrefix;
+    }
+
+    /** Create a response for installing a split on demand. */
+    public AuxiliaryResolveInfo(@Nullable ComponentName failureActivity,
+            @Nullable Intent failureIntent,
+            @Nullable List<AuxiliaryFilter> filters) {
+        super();
+        this.installFailureActivity = failureActivity;
+        this.filters = filters;
+        this.token = null;
+        this.needsPhaseTwo = false;
+        this.failureIntent = failureIntent;
+        this.hostDigestPrefixSecure = null;
+    }
+
+    /** Create a response for installing a split on demand. */
+    public AuxiliaryResolveInfo(@Nullable ComponentName failureActivity,
+            String packageName, long versionCode, String splitName) {
+        this(failureActivity, null, Collections.singletonList(
+                new AuxiliaryResolveInfo.AuxiliaryFilter(packageName, versionCode, splitName)));
+    }
+
+    /** @hide */
+    public static final class AuxiliaryFilter extends IntentFilter {
+        /** Resolved information returned from the external instant resolver */
+        public final InstantAppResolveInfo resolveInfo;
+        /** The resolved package. Copied from {@link #resolveInfo}. */
+        public final String packageName;
+        /** The version code of the package */
+        public final long versionCode;
+        /** The resolve split. Copied from the matched filter in {@link #resolveInfo}. */
+        public final String splitName;
+        /** The extras to pass on to the installer for this filter. */
+        public final Bundle extras;
+
+        public AuxiliaryFilter(IntentFilter orig, InstantAppResolveInfo resolveInfo,
+                String splitName, Bundle extras) {
+            super(orig);
+            this.resolveInfo = resolveInfo;
+            this.packageName = resolveInfo.getPackageName();
+            this.versionCode = resolveInfo.getLongVersionCode();
+            this.splitName = splitName;
+            this.extras = extras;
+        }
+
+        public AuxiliaryFilter(InstantAppResolveInfo resolveInfo,
+                String splitName, Bundle extras) {
+            this.resolveInfo = resolveInfo;
+            this.packageName = resolveInfo.getPackageName();
+            this.versionCode = resolveInfo.getLongVersionCode();
+            this.splitName = splitName;
+            this.extras = extras;
+        }
+
+        public AuxiliaryFilter(String packageName, long versionCode, String splitName) {
+            this.resolveInfo = null;
+            this.packageName = packageName;
+            this.versionCode = versionCode;
+            this.splitName = splitName;
+            this.extras = null;
+        }
+
+        @Override
+        public String toString() {
+            return "AuxiliaryFilter{"
+                    + "packageName='" + packageName + '\''
+                    + ", versionCode=" + versionCode
+                    + ", splitName='" + splitName + '\'' + '}';
+        }
+    }
+}
diff --git a/android/content/pm/BaseParceledListSlice.java b/android/content/pm/BaseParceledListSlice.java
new file mode 100644
index 0000000..bd847bf
--- /dev/null
+++ b/android/content/pm/BaseParceledListSlice.java
@@ -0,0 +1,214 @@
+/*
+ * 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.content.pm;
+
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Transfer a large list of Parcelable objects across an IPC.  Splits into
+ * multiple transactions if needed.
+ *
+ * Caveat: for efficiency and security, all elements must be the same concrete type.
+ * In order to avoid writing the class name of each object, we must ensure that
+ * each object is the same type, or else unparceling then reparceling the data may yield
+ * a different result if the class name encoded in the Parcelable is a Base type.
+ * See b/17671747.
+ *
+ * @hide
+ */
+abstract class BaseParceledListSlice<T> implements Parcelable {
+    private static String TAG = "ParceledListSlice";
+    private static boolean DEBUG = false;
+
+    /*
+     * TODO get this number from somewhere else. For now set it to a quarter of
+     * the 1MB limit.
+     */
+    private static final int MAX_IPC_SIZE = IBinder.getSuggestedMaxIpcSizeBytes();
+
+    private final List<T> mList;
+
+    private int mInlineCountLimit = Integer.MAX_VALUE;
+
+    public BaseParceledListSlice(List<T> list) {
+        mList = list;
+    }
+
+    @SuppressWarnings("unchecked")
+    BaseParceledListSlice(Parcel p, ClassLoader loader) {
+        final int N = p.readInt();
+        mList = new ArrayList<T>(N);
+        if (DEBUG) Log.d(TAG, "Retrieving " + N + " items");
+        if (N <= 0) {
+            return;
+        }
+
+        Parcelable.Creator<?> creator = readParcelableCreator(p, loader);
+        Class<?> listElementClass = null;
+
+        int i = 0;
+        while (i < N) {
+            if (p.readInt() == 0) {
+                break;
+            }
+
+            final T parcelable = readCreator(creator, p, loader);
+            if (listElementClass == null) {
+                listElementClass = parcelable.getClass();
+            } else {
+                verifySameType(listElementClass, parcelable.getClass());
+            }
+
+            mList.add(parcelable);
+
+            if (DEBUG) Log.d(TAG, "Read inline #" + i + ": " + mList.get(mList.size()-1));
+            i++;
+        }
+        if (i >= N) {
+            return;
+        }
+        final IBinder retriever = p.readStrongBinder();
+        while (i < N) {
+            if (DEBUG) Log.d(TAG, "Reading more @" + i + " of " + N + ": retriever=" + retriever);
+            Parcel data = Parcel.obtain();
+            Parcel reply = Parcel.obtain();
+            data.writeInt(i);
+            try {
+                retriever.transact(IBinder.FIRST_CALL_TRANSACTION, data, reply, 0);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failure retrieving array; only received " + i + " of " + N, e);
+                return;
+            }
+            while (i < N && reply.readInt() != 0) {
+                final T parcelable = readCreator(creator, reply, loader);
+                verifySameType(listElementClass, parcelable.getClass());
+
+                mList.add(parcelable);
+
+                if (DEBUG) Log.d(TAG, "Read extra #" + i + ": " + mList.get(mList.size()-1));
+                i++;
+            }
+            reply.recycle();
+            data.recycle();
+        }
+    }
+
+    private T readCreator(Parcelable.Creator<?> creator, Parcel p, ClassLoader loader) {
+        if (creator instanceof Parcelable.ClassLoaderCreator<?>) {
+            Parcelable.ClassLoaderCreator<?> classLoaderCreator =
+                    (Parcelable.ClassLoaderCreator<?>) creator;
+            return (T) classLoaderCreator.createFromParcel(p, loader);
+        }
+        return (T) creator.createFromParcel(p);
+    }
+
+    private static void verifySameType(final Class<?> expected, final Class<?> actual) {
+        if (!actual.equals(expected)) {
+            throw new IllegalArgumentException("Can't unparcel type "
+                    + (actual == null ? null : actual.getName()) + " in list of type "
+                    + (expected == null ? null : expected.getName()));
+        }
+    }
+
+    @UnsupportedAppUsage
+    public List<T> getList() {
+        return mList;
+    }
+
+    /**
+     * Set a limit on the maximum number of entries in the array that will be included
+     * inline in the initial parcelling of this object.
+     */
+    public void setInlineCountLimit(int maxCount) {
+        mInlineCountLimit = maxCount;
+    }
+
+    /**
+     * Write this to another Parcel. Note that this discards the internal Parcel
+     * and should not be used anymore. This is so we can pass this to a Binder
+     * where we won't have a chance to call recycle on this.
+     */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        final int N = mList.size();
+        final int callFlags = flags;
+        dest.writeInt(N);
+        if (DEBUG) Log.d(TAG, "Writing " + N + " items");
+        if (N > 0) {
+            final Class<?> listElementClass = mList.get(0).getClass();
+            writeParcelableCreator(mList.get(0), dest);
+            int i = 0;
+            while (i < N && i < mInlineCountLimit && dest.dataSize() < MAX_IPC_SIZE) {
+                dest.writeInt(1);
+
+                final T parcelable = mList.get(i);
+                verifySameType(listElementClass, parcelable.getClass());
+                writeElement(parcelable, dest, callFlags);
+
+                if (DEBUG) Log.d(TAG, "Wrote inline #" + i + ": " + mList.get(i));
+                i++;
+            }
+            if (i < N) {
+                dest.writeInt(0);
+                Binder retriever = new Binder() {
+                    @Override
+                    protected boolean onTransact(int code, Parcel data, Parcel reply, int flags)
+                            throws RemoteException {
+                        if (code != FIRST_CALL_TRANSACTION) {
+                            return super.onTransact(code, data, reply, flags);
+                        }
+                        int i = data.readInt();
+                        if (DEBUG) Log.d(TAG, "Writing more @" + i + " of " + N);
+                        while (i < N && reply.dataSize() < MAX_IPC_SIZE) {
+                            reply.writeInt(1);
+
+                            final T parcelable = mList.get(i);
+                            verifySameType(listElementClass, parcelable.getClass());
+                            writeElement(parcelable, reply, callFlags);
+
+                            if (DEBUG) Log.d(TAG, "Wrote extra #" + i + ": " + mList.get(i));
+                            i++;
+                        }
+                        if (i < N) {
+                            if (DEBUG) Log.d(TAG, "Breaking @" + i + " of " + N);
+                            reply.writeInt(0);
+                        }
+                        return true;
+                    }
+                };
+                if (DEBUG) Log.d(TAG, "Breaking @" + i + " of " + N + ": retriever=" + retriever);
+                dest.writeStrongBinder(retriever);
+            }
+        }
+    }
+
+    protected abstract void writeElement(T parcelable, Parcel reply, int callFlags);
+
+    @UnsupportedAppUsage
+    protected abstract void writeParcelableCreator(T parcelable, Parcel dest);
+
+    protected abstract Parcelable.Creator<?> readParcelableCreator(Parcel from, ClassLoader loader);
+}
diff --git a/android/content/pm/ChangedPackages.java b/android/content/pm/ChangedPackages.java
new file mode 100644
index 0000000..950a29a
--- /dev/null
+++ b/android/content/pm/ChangedPackages.java
@@ -0,0 +1,84 @@
+/**
+ * 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.content.pm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.TestApi;
+import android.content.Intent;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.List;
+
+/**
+ * Packages that have been changed since the last time they
+ * were requested.
+ * @see PackageManager#getChangedPackages(int)
+ */
+public final class ChangedPackages implements Parcelable {
+    /** The last known sequence number for these changes */
+    private final int mSequenceNumber;
+    /** The names of the packages that have changed */
+    private final List<String> mPackageNames;
+
+    public ChangedPackages(int sequenceNumber, @NonNull List<String> packageNames) {
+        this.mSequenceNumber = sequenceNumber;
+        this.mPackageNames = packageNames;
+    }
+
+    /** @hide */
+    protected ChangedPackages(Parcel in) {
+        mSequenceNumber = in.readInt();
+        mPackageNames = in.createStringArrayList();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mSequenceNumber);
+        dest.writeStringList(mPackageNames);
+    }
+
+    /**
+     * Returns the last known sequence number for these changes.
+     */
+    public int getSequenceNumber() {
+        return mSequenceNumber;
+    }
+
+    /**
+     * Returns the names of the packages that have changed.
+     */
+    public @NonNull List<String> getPackageNames() {
+        return mPackageNames;
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<ChangedPackages> CREATOR =
+            new Parcelable.Creator<ChangedPackages>() {
+        public ChangedPackages createFromParcel(Parcel in) {
+            return new ChangedPackages(in);
+        }
+
+        public ChangedPackages[] newArray(int size) {
+            return new ChangedPackages[size];
+        }
+    };
+}
diff --git a/android/content/pm/ComponentInfo.java b/android/content/pm/ComponentInfo.java
new file mode 100644
index 0000000..628bcd7
--- /dev/null
+++ b/android/content/pm/ComponentInfo.java
@@ -0,0 +1,248 @@
+/*
+ * 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.content.pm;
+
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.ComponentName;
+import android.graphics.drawable.Drawable;
+import android.os.Parcel;
+import android.util.Printer;
+
+/**
+ * Base class containing information common to all application components
+ * ({@link ActivityInfo}, {@link ServiceInfo}).  This class is not intended
+ * to be used by itself; it is simply here to share common definitions
+ * between all application components.  As such, it does not itself
+ * implement Parcelable, but does provide convenience methods to assist
+ * in the implementation of Parcelable in subclasses.
+ */
+public class ComponentInfo extends PackageItemInfo {
+    /**
+     * Global information about the application/package this component is a
+     * part of.
+     */
+    public ApplicationInfo applicationInfo;
+    
+    /**
+     * The name of the process this component should run in.
+     * From the "android:process" attribute or, if not set, the same
+     * as <var>applicationInfo.processName</var>.
+     */
+    public String processName;
+
+    /**
+     * The name of the split in which this component is declared.
+     * Null if the component was declared in the base APK.
+     */
+    public String splitName;
+
+    /**
+     * A string resource identifier (in the package's resources) containing
+     * a user-readable description of the component.  From the "description"
+     * attribute or, if not set, 0.
+     */
+    public int descriptionRes;
+    
+    /**
+     * Indicates whether or not this component may be instantiated.  Note that this value can be
+     * overridden by the one in its parent {@link ApplicationInfo}.
+     */
+    public boolean enabled = true;
+
+    /**
+     * Set to true if this component is available for use by other applications.
+     * Comes from {@link android.R.attr#exported android:exported} of the
+     * &lt;activity&gt;, &lt;receiver&gt;, &lt;service&gt;, or
+     * &lt;provider&gt; tag.
+     */
+    public boolean exported = false;
+
+    /**
+     * Indicates if this component is aware of direct boot lifecycle, and can be
+     * safely run before the user has entered their credentials (such as a lock
+     * pattern or PIN).
+     */
+    public boolean directBootAware = false;
+
+    public ComponentInfo() {
+    }
+
+    public ComponentInfo(ComponentInfo orig) {
+        super(orig);
+        applicationInfo = orig.applicationInfo;
+        processName = orig.processName;
+        splitName = orig.splitName;
+        descriptionRes = orig.descriptionRes;
+        enabled = orig.enabled;
+        exported = orig.exported;
+        directBootAware = orig.directBootAware;
+    }
+
+    /** @hide */
+    @Override public CharSequence loadUnsafeLabel(PackageManager pm) {
+        if (nonLocalizedLabel != null) {
+            return nonLocalizedLabel;
+        }
+        ApplicationInfo ai = applicationInfo;
+        CharSequence label;
+        if (labelRes != 0) {
+            label = pm.getText(packageName, labelRes, ai);
+            if (label != null) {
+                return label;
+            }
+        }
+        if (ai.nonLocalizedLabel != null) {
+            return ai.nonLocalizedLabel;
+        }
+        if (ai.labelRes != 0) {
+            label = pm.getText(packageName, ai.labelRes, ai);
+            if (label != null) {
+                return label;
+            }
+        }
+        return name;
+    }
+
+    /**
+     * Return whether this component and its enclosing application are enabled.
+     */
+    public boolean isEnabled() {
+        return enabled && applicationInfo.enabled;
+    }
+    
+    /**
+     * Return the icon resource identifier to use for this component.  If
+     * the component defines an icon, that is used; else, the application
+     * icon is used.
+     * 
+     * @return The icon associated with this component.
+     */
+    public final int getIconResource() {
+        return icon != 0 ? icon : applicationInfo.icon;
+    }
+
+    /**
+     * Return the logo resource identifier to use for this component.  If
+     * the component defines a logo, that is used; else, the application
+     * logo is used.
+     *
+     * @return The logo associated with this component.
+     */
+    public final int getLogoResource() {
+        return logo != 0 ? logo : applicationInfo.logo;
+    }
+    
+    /**
+     * Return the banner resource identifier to use for this component. If the
+     * component defines a banner, that is used; else, the application banner is
+     * used.
+     *
+     * @return The banner associated with this component.
+     */
+    public final int getBannerResource() {
+        return banner != 0 ? banner : applicationInfo.banner;
+    }
+
+    /** {@hide} */
+    @UnsupportedAppUsage
+    public ComponentName getComponentName() {
+        return new ComponentName(packageName, name);
+    }
+
+    protected void dumpFront(Printer pw, String prefix) {
+        super.dumpFront(pw, prefix);
+        if (processName != null && !packageName.equals(processName)) {
+            pw.println(prefix + "processName=" + processName);
+        }
+        if (splitName != null) {
+            pw.println(prefix + "splitName=" + splitName);
+        }
+        pw.println(prefix + "enabled=" + enabled + " exported=" + exported
+                + " directBootAware=" + directBootAware);
+        if (descriptionRes != 0) {
+            pw.println(prefix + "description=" + descriptionRes);
+        }
+    }
+
+    protected void dumpBack(Printer pw, String prefix) {
+        dumpBack(pw, prefix, DUMP_FLAG_ALL);
+    }
+
+    void dumpBack(Printer pw, String prefix, int dumpFlags) {
+        if ((dumpFlags & DUMP_FLAG_APPLICATION) != 0) {
+            if (applicationInfo != null) {
+                pw.println(prefix + "ApplicationInfo:");
+                applicationInfo.dump(pw, prefix + "  ", dumpFlags);
+            } else {
+                pw.println(prefix + "ApplicationInfo: null");
+            }
+        }
+        super.dumpBack(pw, prefix);
+    }
+
+    public void writeToParcel(Parcel dest, int parcelableFlags) {
+        super.writeToParcel(dest, parcelableFlags);
+        applicationInfo.writeToParcel(dest, parcelableFlags);
+        dest.writeString8(processName);
+        dest.writeString8(splitName);
+        dest.writeInt(descriptionRes);
+        dest.writeInt(enabled ? 1 : 0);
+        dest.writeInt(exported ? 1 : 0);
+        dest.writeInt(directBootAware ? 1 : 0);
+    }
+    
+    protected ComponentInfo(Parcel source) {
+        super(source);
+        applicationInfo = ApplicationInfo.CREATOR.createFromParcel(source);
+        processName = source.readString8();
+        splitName = source.readString8();
+        descriptionRes = source.readInt();
+        enabled = (source.readInt() != 0);
+        exported = (source.readInt() != 0);
+        directBootAware = (source.readInt() != 0);
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public Drawable loadDefaultIcon(PackageManager pm) {
+        return applicationInfo.loadIcon(pm);
+    }
+    
+    /**
+     * @hide
+     */
+    @Override protected Drawable loadDefaultBanner(PackageManager pm) {
+        return applicationInfo.loadBanner(pm);
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    protected Drawable loadDefaultLogo(PackageManager pm) {
+        return applicationInfo.loadLogo(pm);
+    }
+    
+    /**
+     * @hide
+     */
+    @Override protected ApplicationInfo getApplicationInfo() {
+        return applicationInfo;
+    }
+}
diff --git a/android/content/pm/ConfigurationInfo.java b/android/content/pm/ConfigurationInfo.java
new file mode 100644
index 0000000..20494e9
--- /dev/null
+++ b/android/content/pm/ConfigurationInfo.java
@@ -0,0 +1,145 @@
+/*
+ * 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.content.pm;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Information you can retrieve about hardware configuration preferences
+ * declared by an application. This corresponds to information collected from the
+ * AndroidManifest.xml's &lt;uses-configuration&gt; and &lt;uses-feature&gt; tags.
+ */
+public class ConfigurationInfo implements Parcelable {
+    /**
+     * The kind of touch screen attached to the device.
+     * One of: {@link android.content.res.Configuration#TOUCHSCREEN_NOTOUCH},
+     * {@link android.content.res.Configuration#TOUCHSCREEN_STYLUS}, 
+     * {@link android.content.res.Configuration#TOUCHSCREEN_FINGER}. 
+     */
+    public int reqTouchScreen;
+    
+    /**
+     * Application's input method preference.
+     * One of: {@link android.content.res.Configuration#KEYBOARD_UNDEFINED},
+     * {@link android.content.res.Configuration#KEYBOARD_NOKEYS},
+     * {@link android.content.res.Configuration#KEYBOARD_QWERTY},
+     * {@link android.content.res.Configuration#KEYBOARD_12KEY}
+     */
+    public int reqKeyboardType;
+    
+    /**
+     * A flag indicating whether any keyboard is available.
+     * one of: {@link android.content.res.Configuration#NAVIGATION_UNDEFINED},
+     * {@link android.content.res.Configuration#NAVIGATION_DPAD}, 
+     * {@link android.content.res.Configuration#NAVIGATION_TRACKBALL},
+     * {@link android.content.res.Configuration#NAVIGATION_WHEEL}
+     */
+    public int reqNavigation;
+    
+    /**
+     * Value for {@link #reqInputFeatures}: if set, indicates that the application
+     * requires a hard keyboard
+     */
+    public static final int INPUT_FEATURE_HARD_KEYBOARD = 0x00000001;
+    
+    /**
+     * Value for {@link #reqInputFeatures}: if set, indicates that the application
+     * requires a five way navigation device
+     */
+    public static final int INPUT_FEATURE_FIVE_WAY_NAV = 0x00000002;
+    
+    /**
+     * Flags associated with the input features.  Any combination of
+     * {@link #INPUT_FEATURE_HARD_KEYBOARD},
+     * {@link #INPUT_FEATURE_FIVE_WAY_NAV}
+     */
+    public int reqInputFeatures = 0;
+
+    /**
+     * Default value for {@link #reqGlEsVersion};
+     */
+    public static final int GL_ES_VERSION_UNDEFINED = 0;
+    /**
+     * The GLES version used by an application. The upper order 16 bits represent the
+     * major version and the lower order 16 bits the minor version.
+     */
+    public int reqGlEsVersion;
+
+    public ConfigurationInfo() {
+    }
+
+    public ConfigurationInfo(ConfigurationInfo orig) {
+        reqTouchScreen = orig.reqTouchScreen;
+        reqKeyboardType = orig.reqKeyboardType;
+        reqNavigation = orig.reqNavigation;
+        reqInputFeatures = orig.reqInputFeatures;
+        reqGlEsVersion = orig.reqGlEsVersion;
+    }
+
+    public String toString() {
+        return "ConfigurationInfo{"
+            + Integer.toHexString(System.identityHashCode(this))
+            + " touchscreen = " + reqTouchScreen
+            + " inputMethod = " + reqKeyboardType
+            + " navigation = " + reqNavigation
+            + " reqInputFeatures = " + reqInputFeatures
+            + " reqGlEsVersion = " + reqGlEsVersion + "}";
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel dest, int parcelableFlags) {
+        dest.writeInt(reqTouchScreen);
+        dest.writeInt(reqKeyboardType);
+        dest.writeInt(reqNavigation);
+        dest.writeInt(reqInputFeatures);
+        dest.writeInt(reqGlEsVersion);
+    }
+
+    public static final @android.annotation.NonNull Creator<ConfigurationInfo> CREATOR =
+        new Creator<ConfigurationInfo>() {
+        public ConfigurationInfo createFromParcel(Parcel source) {
+            return new ConfigurationInfo(source);
+        }
+        public ConfigurationInfo[] newArray(int size) {
+            return new ConfigurationInfo[size];
+        }
+    };
+
+    private ConfigurationInfo(Parcel source) {
+        reqTouchScreen = source.readInt();
+        reqKeyboardType = source.readInt();
+        reqNavigation = source.readInt();
+        reqInputFeatures = source.readInt();
+        reqGlEsVersion = source.readInt();
+    }
+
+    /**
+     * This method extracts the major and minor version of reqGLEsVersion attribute
+     * and returns it as a string. Say reqGlEsVersion value of 0x00010002 is returned
+     * as 1.2
+     * @return String representation of the reqGlEsVersion attribute
+     */
+    public String getGlEsVersion() {
+        int major = ((reqGlEsVersion & 0xffff0000) >> 16);
+        int minor = reqGlEsVersion & 0x0000ffff;
+        return String.valueOf(major)+"."+String.valueOf(minor);
+    }
+}
diff --git a/android/content/pm/CrossProfileApps.java b/android/content/pm/CrossProfileApps.java
new file mode 100644
index 0000000..99e6d91
--- /dev/null
+++ b/android/content/pm/CrossProfileApps.java
@@ -0,0 +1,522 @@
+/*
+ * 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.content.pm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.app.Activity;
+import android.app.AppOpsManager.Mode;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.Settings;
+
+import com.android.internal.R;
+import com.android.internal.util.UserIcons;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * Class for handling cross profile operations. Apps can use this class to interact with its
+ * instance in any profile that is in {@link #getTargetUserProfiles()}. For example, app can
+ * use this class to start its main activity in managed profile.
+ */
+public class CrossProfileApps {
+
+    /**
+     * Broadcast signalling that the receiving app's permission to interact across profiles has
+     * changed. This includes the user, admin, or OEM changing their consent such that the
+     * permission for the app to interact across profiles has changed.
+     *
+     * <p>This broadcast is not sent when other circumstances result in a change to being able to
+     * interact across profiles in practice, such as the profile being turned off or removed, apps
+     * being uninstalled, etc. The methods {@link #canInteractAcrossProfiles()} and {@link
+     * #canRequestInteractAcrossProfiles()} can be used by apps prior to attempting to interact
+     * across profiles or attempting to request user consent to interact across profiles.
+     *
+     * <p>Apps that have set the {@code android:crossProfile} manifest attribute to {@code true}
+     * can receive this broadcast in manifest broadcast receivers. Otherwise, it can only be
+     * received by dynamically-registered broadcast receivers.
+     */
+    public static final String ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED =
+            "android.content.pm.action.CAN_INTERACT_ACROSS_PROFILES_CHANGED";
+
+    private final Context mContext;
+    private final ICrossProfileApps mService;
+    private final UserManager mUserManager;
+    private final Resources mResources;
+
+    /** @hide */
+    public CrossProfileApps(Context context, ICrossProfileApps service) {
+        mContext = context;
+        mService = service;
+        mUserManager = context.getSystemService(UserManager.class);
+        mResources = context.getResources();
+    }
+
+    /**
+     * Starts the specified main activity of the caller package in the specified profile.
+     *
+     * @param component The ComponentName of the activity to launch, it must be exported and has
+     *        action {@link android.content.Intent#ACTION_MAIN}, category
+     *        {@link android.content.Intent#CATEGORY_LAUNCHER}. Otherwise, SecurityException will
+     *        be thrown.
+     * @param targetUser The UserHandle of the profile, must be one of the users returned by
+     *        {@link #getTargetUserProfiles()}, otherwise a {@link SecurityException} will
+     *        be thrown.
+     */
+    public void startMainActivity(@NonNull ComponentName component,
+            @NonNull UserHandle targetUser) {
+        try {
+            mService.startActivityAsUser(
+                    mContext.getIApplicationThread(),
+                    mContext.getPackageName(),
+                    mContext.getAttributionTag(),
+                    component,
+                    targetUser.getIdentifier(),
+                    true);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Starts the specified activity of the caller package in the specified profile.
+     *
+     * <p>The caller must have the {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES},
+     * {@code android.Manifest.permission#INTERACT_ACROSS_USERS}, or {@code
+     * android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permission. Both the caller and
+     * target user profiles must be in the same profile group. The target user must be a valid user
+     * returned from {@link #getTargetUserProfiles()}.
+     *
+     * @param intent The intent to launch. A component in the caller package must be specified.
+     * @param targetUser The {@link UserHandle} of the profile; must be one of the users returned by
+     *        {@link #getTargetUserProfiles()} if different to the calling user, otherwise a
+     *        {@link SecurityException} will be thrown.
+     * @param callingActivity The activity to start the new activity from for the purposes of
+     *        deciding which task the new activity should belong to. If {@code null}, the activity
+     *        will always be started in a new task.
+     */
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.INTERACT_ACROSS_PROFILES,
+            android.Manifest.permission.INTERACT_ACROSS_USERS})
+    public void startActivity(
+            @NonNull Intent intent,
+            @NonNull UserHandle targetUser,
+            @Nullable Activity callingActivity) {
+        startActivity(intent, targetUser, callingActivity, /* options= */ null);
+    }
+
+    /**
+     * Starts the specified activity of the caller package in the specified profile.
+     *
+     * <p>The caller must have the {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES},
+     * {@code android.Manifest.permission#INTERACT_ACROSS_USERS}, or {@code
+     * android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permission. Both the caller and
+     * target user profiles must be in the same profile group. The target user must be a valid user
+     * returned from {@link #getTargetUserProfiles()}.
+     *
+     * @param intent The intent to launch. A component in the caller package must be specified.
+     * @param targetUser The {@link UserHandle} of the profile; must be one of the users returned by
+     *        {@link #getTargetUserProfiles()} if different to the calling user, otherwise a
+     *        {@link SecurityException} will be thrown.
+     * @param callingActivity The activity to start the new activity from for the purposes of
+     *        deciding which task the new activity should belong to. If {@code null}, the activity
+     *        will always be started in a new task.
+     * @param options The activity options or {@code null}. See {@link android.app.ActivityOptions}.
+     */
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.INTERACT_ACROSS_PROFILES,
+            android.Manifest.permission.INTERACT_ACROSS_USERS})
+    public void startActivity(
+            @NonNull Intent intent,
+            @NonNull UserHandle targetUser,
+            @Nullable Activity callingActivity,
+            @Nullable Bundle options) {
+        try {
+            mService.startActivityAsUserByIntent(
+                    mContext.getIApplicationThread(),
+                    mContext.getPackageName(),
+                    mContext.getAttributionTag(),
+                    intent,
+                    targetUser.getIdentifier(),
+                    callingActivity != null ? callingActivity.getActivityToken() : null,
+                    options);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Starts the specified activity of the caller package in the specified profile. Unlike
+     * {@link #startMainActivity}, this can start any activity of the caller package, not just
+     * the main activity.
+     * The caller must have the {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES}
+     * permission and both the caller and target user profiles must be in the same profile group.
+     *
+     * @param component The ComponentName of the activity to launch. It must be exported.
+     * @param targetUser The UserHandle of the profile, must be one of the users returned by
+     *        {@link #getTargetUserProfiles()}, otherwise a {@link SecurityException} will
+     *        be thrown.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_PROFILES)
+    public void startActivity(@NonNull ComponentName component, @NonNull UserHandle targetUser) {
+        try {
+            mService.startActivityAsUser(mContext.getIApplicationThread(),
+                    mContext.getPackageName(), mContext.getAttributionTag(), component,
+                    targetUser.getIdentifier(), false);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Return a list of user profiles that that the caller can use when calling other APIs in this
+     * class.
+     * <p>
+     * A user profile would be considered as a valid target user profile, provided that:
+     * <ul>
+     * <li>It gets caller app installed</li>
+     * <li>It is not equal to the calling user</li>
+     * <li>It is in the same profile group of calling user profile</li>
+     * <li>It is enabled</li>
+     * </ul>
+     *
+     * @see UserManager#getUserProfiles()
+     */
+    public @NonNull List<UserHandle> getTargetUserProfiles() {
+        try {
+            return mService.getTargetUserProfiles(mContext.getPackageName());
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Return a label that calling app can show to user for the semantic of profile switching --
+     * launching its own activity in specified user profile. For example, it may return
+     * "Switch to work" if the given user handle is the managed profile one.
+     *
+     * @param userHandle The UserHandle of the target profile, must be one of the users returned by
+     *        {@link #getTargetUserProfiles()}, otherwise a {@link SecurityException} will
+     *        be thrown.
+     * @return a label that calling app can show user for the semantic of launching its own
+     *         activity in the specified user profile.
+     *
+     * @see #startMainActivity(ComponentName, UserHandle)
+     */
+    public @NonNull CharSequence getProfileSwitchingLabel(@NonNull UserHandle userHandle) {
+        verifyCanAccessUser(userHandle);
+
+        final int stringRes = mUserManager.isManagedProfile(userHandle.getIdentifier())
+                ? R.string.managed_profile_label
+                : R.string.user_owner_label;
+        return mResources.getString(stringRes);
+    }
+
+    /**
+     * Return a drawable that calling app can show to user for the semantic of profile switching --
+     * launching its own activity in specified user profile. For example, it may return a briefcase
+     * icon if the given user handle is the managed profile one.
+     *
+     * @param userHandle The UserHandle of the target profile, must be one of the users returned by
+     *        {@link #getTargetUserProfiles()}, otherwise a {@link SecurityException} will
+     *        be thrown.
+     * @return an icon that calling app can show user for the semantic of launching its own
+     *         activity in specified user profile.
+     *
+     * @see #startMainActivity(ComponentName, UserHandle)
+     */
+    public @NonNull Drawable getProfileSwitchingIconDrawable(@NonNull UserHandle userHandle) {
+        verifyCanAccessUser(userHandle);
+
+        final boolean isManagedProfile =
+                mUserManager.isManagedProfile(userHandle.getIdentifier());
+        if (isManagedProfile) {
+            return mResources.getDrawable(R.drawable.ic_corp_badge, null);
+        } else {
+            return UserIcons.getDefaultUserIcon(
+                    mResources, UserHandle.USER_SYSTEM, true /* light */);
+        }
+    }
+
+    /**
+     * Returns whether the calling package can request to navigate the user to
+     * the relevant settings page to request user consent to interact across profiles.
+     *
+     * <p>If {@code true}, the navigation intent can be obtained via {@link
+     * #createRequestInteractAcrossProfilesIntent()}. The package can then listen to {@link
+     * #ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED} broadcasts.
+     *
+     * <p>Specifically, returns whether the following are all true:
+     * <ul>
+     * <li>{@code UserManager#getEnabledProfileIds(int)} ()} returns at least one other profile for
+     * the calling user.</li>
+     * <li>The calling app has requested
+     * {@code android.Manifest.permission.INTERACT_ACROSS_PROFILES} in its manifest.</li>
+     * </ul>
+     *
+     * <p>Note that in order for the user to be able to grant the consent, the requesting package
+     * must be whitelisted by the admin or the OEM and installed in the other profile. If this is
+     * not the case the user will be shown a message explaining why they can't grant the consent.
+     *
+     * <p>Note that user consent could already be granted if given a return value of {@code true}.
+     * The package's current ability to interact across profiles can be checked with {@link
+     * #canInteractAcrossProfiles()}.
+     *
+     * @return true if the calling package can request to interact across profiles.
+     */
+    public boolean canRequestInteractAcrossProfiles() {
+        try {
+            return mService.canRequestInteractAcrossProfiles(mContext.getPackageName());
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns whether the calling package can interact across profiles.
+
+     * <p>Specifically, returns whether the following are all true:
+     * <ul>
+     * <li>{@link #getTargetUserProfiles()} returns a non-empty list for the calling user.</li>
+     * <li>The user has previously consented to cross-profile communication for the calling
+     * package.</li>
+     * <li>The calling package has either been whitelisted by default by the OEM or has been
+     * explicitly whitelisted by the admin via
+     * {@link android.app.admin.DevicePolicyManager#setCrossProfilePackages(ComponentName, Set)}.
+     * </li>
+     * </ul>
+     *
+     * <p>If {@code false}, the package's current ability to request user consent to interact across
+     * profiles can be checked with {@link #canRequestInteractAcrossProfiles()}. If {@code true},
+     * user consent can be obtained via {@link #createRequestInteractAcrossProfilesIntent()}. The
+     * package can then listen to {@link #ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED} broadcasts.
+     *
+     * @return true if the calling package can interact across profiles.
+     * @throws SecurityException if {@code mContext.getPackageName()} does not belong to the
+     * calling UID.
+     */
+    public boolean canInteractAcrossProfiles() {
+        try {
+            return mService.canInteractAcrossProfiles(mContext.getPackageName());
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns an {@link Intent} to open the settings page that allows the user to decide whether
+     * the calling app can interact across profiles.
+     *
+     * <p>The method {@link #canRequestInteractAcrossProfiles()} must be returning {@code true}.
+     *
+     * <p>Note that the user may already have given consent and the app may already be able to
+     * interact across profiles, even if {@link #canRequestInteractAcrossProfiles()} is {@code
+     * true}. The current ability to interact across profiles is given by {@link
+     * #canInteractAcrossProfiles()}.
+     *
+     * @return an {@link Intent} to open the settings page that allows the user to decide whether
+     * the app can interact across profiles
+     *
+     * @throws SecurityException if {@code mContext.getPackageName()} does not belong to the
+     * calling UID, or {@link #canRequestInteractAcrossProfiles()} is {@code false}.
+     */
+    public @NonNull Intent createRequestInteractAcrossProfilesIntent() {
+        if (!canRequestInteractAcrossProfiles()) {
+            throw new SecurityException(
+                    "The calling package can not request to interact across profiles.");
+        }
+        final Intent settingsIntent = new Intent();
+        settingsIntent.setAction(Settings.ACTION_MANAGE_CROSS_PROFILE_ACCESS);
+        final Uri packageUri = Uri.parse("package:" + mContext.getPackageName());
+        settingsIntent.setData(packageUri);
+        return settingsIntent;
+    }
+
+    /**
+     * Sets the app-op for {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES} that is
+     * configurable by users in Settings. This configures it for the profile group of the calling
+     * package.
+     *
+     * <p>Before calling, check {@link #canConfigureInteractAcrossProfiles(String)} and do not call
+     * if it is {@code false}. If presenting a user interface, do not allow the user to configure
+     * the app-op in that case.
+     *
+     * <p>The underlying app-op {@link android.app.AppOpsManager#OP_INTERACT_ACROSS_PROFILES} should
+     * never be set directly. This method ensures that the app-op is kept in sync for the app across
+     * each user in the profile group and that those apps are sent a broadcast when their ability to
+     * interact across profiles changes.
+     *
+     * <p>This method should be used directly whenever a user's action results in a change in an
+     * app's ability to interact across profiles, as defined by the return value of {@link
+     * #canInteractAcrossProfiles()}. This includes user consent changes in Settings or during
+     * provisioning.
+     *
+     * <p>If other changes could have affected the app's ability to interact across profiles, as
+     * defined by the return value of {@link #canInteractAcrossProfiles()}, such as changes to the
+     * admin or OEM consent whitelists, then {@link #resetInteractAcrossProfilesAppOps(Collection,
+     * Set)} should be used.
+     *
+     * <p>If the caller does not have the {@link android.Manifest.permission
+     * #CONFIGURE_INTERACT_ACROSS_PROFILES} permission, then they must have the permissions that
+     * would have been required to call {@link android.app.AppOpsManager#setMode(int, int, String,
+     * int)}, which includes {@link android.Manifest.permission#MANAGE_APP_OPS_MODES}.
+     *
+     * <p>Also requires either {@link android.Manifest.permission#INTERACT_ACROSS_USERS} or {@link
+     * android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}.
+     *
+     * @hide
+     */
+    @RequiresPermission(
+            allOf={android.Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES,
+                    android.Manifest.permission.INTERACT_ACROSS_USERS})
+    public void setInteractAcrossProfilesAppOp(@NonNull String packageName, @Mode int newMode) {
+        try {
+            mService.setInteractAcrossProfilesAppOp(packageName, newMode);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns whether the given package can have its ability to interact across profiles configured
+     * by the user. This means that every other condition to interact across profiles has been set.
+     *
+     * <p>This differs from {@link #canRequestInteractAcrossProfiles()} since it will not return
+     * {@code false} simply when the target profile is disabled.
+     *
+     * @hide
+     */
+    public boolean canConfigureInteractAcrossProfiles(@NonNull String packageName) {
+        try {
+            return mService.canConfigureInteractAcrossProfiles(packageName);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns {@code true} if the given package has requested
+     * {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES} and the user has at least one
+     * other profile in the same profile group.
+     *
+     * <p>This differs from {@link #canConfigureInteractAcrossProfiles(String)} since it will
+     * not return {@code false} if the app is not whitelisted or not installed in the other profile.
+     *
+     * <p>Note that platform-signed apps that are automatically granted the permission and are not
+     * whitelisted by the OEM will not be included in this list.
+     *
+     * @hide
+     */
+    public boolean canUserAttemptToConfigureInteractAcrossProfiles(String packageName) {
+        try {
+            return mService.canUserAttemptToConfigureInteractAcrossProfiles(packageName);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+    /**
+     * For each of the packages defined in {@code previousCrossProfilePackages} but not included in
+     * {@code newCrossProfilePackages}, resets the app-op for {@link android.Manifest.permission
+     * #INTERACT_ACROSS_PROFILES} back to its default value if it can no longer be configured by
+     * users in Settings, as defined by {@link #canConfigureInteractAcrossProfiles(String)}.
+     *
+     * <p>This method should be used whenever an app's ability to interact across profiles could
+     * have changed as a result of non-user actions, such as changes to admin or OEM consent
+     * whitelists.
+     *
+     * <p>If the caller does not have the {@link android.Manifest.permission
+     * #CONFIGURE_INTERACT_ACROSS_PROFILES} permission, then they must have the permissions that
+     * would have been required to call {@link android.app.AppOpsManager#setMode(int, int, String,
+     * int)}, which includes {@link android.Manifest.permission#MANAGE_APP_OPS_MODES}.
+     *
+     * <p>Also requires either {@link android.Manifest.permission#INTERACT_ACROSS_USERS} or {@link
+     * android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}.
+     *
+     * @hide
+     */
+    @RequiresPermission(
+            allOf={android.Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES,
+                    android.Manifest.permission.INTERACT_ACROSS_USERS})
+    public void resetInteractAcrossProfilesAppOps(
+            @NonNull Collection<String> previousCrossProfilePackages,
+            @NonNull Set<String> newCrossProfilePackages) {
+        if (previousCrossProfilePackages.isEmpty()) {
+            return;
+        }
+        final List<String> unsetCrossProfilePackages =
+                previousCrossProfilePackages.stream()
+                        .filter(packageName -> !newCrossProfilePackages.contains(packageName))
+                        .collect(Collectors.toList());
+        if (unsetCrossProfilePackages.isEmpty()) {
+            return;
+        }
+        try {
+            mService.resetInteractAcrossProfilesAppOps(unsetCrossProfilePackages);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Clears the app-op for {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES} back to
+     * its default value for every package on the device.
+     *
+     * <p>This method can be used to ensure that app-op state is not left around on existing users
+     * for previously-configured profiles.
+     *
+     * <p>If the caller does not have the {@link android.Manifest.permission
+     * #CONFIGURE_INTERACT_ACROSS_PROFILES} permission, then they must have the permissions that
+     * would have been required to call {@link android.app.AppOpsManager#setMode(int, int, String,
+     * int)}, which includes {@link android.Manifest.permission#MANAGE_APP_OPS_MODES}.
+     *
+     * <p>Also requires either {@link android.Manifest.permission#INTERACT_ACROSS_USERS} or {@link
+     * android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}.
+     *
+     * @hide
+     */
+    @RequiresPermission(
+            allOf={android.Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES,
+                    android.Manifest.permission.INTERACT_ACROSS_USERS})
+    public void clearInteractAcrossProfilesAppOps() {
+        try {
+            mService.clearInteractAcrossProfilesAppOps();
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    private void verifyCanAccessUser(UserHandle userHandle) {
+        if (!getTargetUserProfiles().contains(userHandle)) {
+            throw new SecurityException("Not allowed to access " + userHandle);
+        }
+    }
+}
diff --git a/android/content/pm/CrossProfileAppsInternal.java b/android/content/pm/CrossProfileAppsInternal.java
new file mode 100644
index 0000000..16a749f
--- /dev/null
+++ b/android/content/pm/CrossProfileAppsInternal.java
@@ -0,0 +1,65 @@
+/*
+ * 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.content.pm;
+
+import android.annotation.UserIdInt;
+import android.os.UserHandle;
+
+import java.util.List;
+
+/**
+ * Exposes internal methods from {@link com.android.server.pm.CrossProfileAppsServiceImpl} to other
+ * system server classes.
+ *
+ * @hide Only for use within the system server.
+ */
+public abstract class CrossProfileAppsInternal {
+    /**
+     * Returns whether the package has the necessary permissions to communicate cross-profile.
+     *
+     * <p>This means having at least one of these conditions:
+     * <ul>
+     *     <li>{@code Manifest.permission.INTERACT_ACROSS_USERS_FULL} granted.
+     *     <li>{@code Manifest.permission.INTERACT_ACROSS_USERS} granted.
+     *     <li>{@code Manifest.permission.INTERACT_ACROSS_PROFILES} granted, or the corresponding
+     *     AppOps {@code android:interact_across_profiles} is set to "allow".
+     * </ul>
+     */
+    public abstract boolean verifyPackageHasInteractAcrossProfilePermission(String packageName,
+            @UserIdInt int userId) throws PackageManager.NameNotFoundException;
+
+    /**
+     * Returns whether the package has the necessary permissions to communicate cross-profile.
+     *
+     * <p>This means having at least one of these conditions:
+     * <ul>
+     *     <li>{@code Manifest.permission.INTERACT_ACROSS_USERS_FULL} granted.
+     *     <li>{@code Manifest.permission.INTERACT_ACROSS_USERS} granted.
+     *     <li>{@code Manifest.permission.INTERACT_ACROSS_PROFILES} granted, or the corresponding
+     *     AppOps {@code android:interact_across_profiles} is set to "allow".
+     * </ul>
+     */
+    public abstract boolean verifyUidHasInteractAcrossProfilePermission(String packageName,
+            int uid);
+
+    /**
+     * Returns the list of target user profiles for the given package on the given user. See {@link
+     * CrossProfileApps#getTargetUserProfiles()}.
+     */
+    public abstract List<UserHandle> getTargetUserProfiles(
+            String packageName, @UserIdInt int userId);
+}
diff --git a/android/content/pm/DataLoaderManager.java b/android/content/pm/DataLoaderManager.java
new file mode 100644
index 0000000..e8fb241
--- /dev/null
+++ b/android/content/pm/DataLoaderManager.java
@@ -0,0 +1,83 @@
+/*
+ * 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.content.pm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.RemoteException;
+
+/**
+ * Data loader manager takes care of data loaders of different packages. It provides methods to
+ * initialize a data loader binder service (binding and creating it), to return a binder of the data
+ * loader binder service and to destroy a data loader binder service.
+ * @see com.android.server.pm.DataLoaderManagerService
+ * @hide
+ */
+public class DataLoaderManager {
+    private static final String TAG = "DataLoaderManager";
+    private final IDataLoaderManager mService;
+
+    public DataLoaderManager(IDataLoaderManager service) {
+        mService = service;
+    }
+
+    /**
+     * Finds a data loader binder service and binds to it. This requires PackageManager.
+     *
+     * @param dataLoaderId ID for the new data loader binder service.
+     * @param params       DataLoaderParamsParcel object that contains data loader params, including
+     *                     its package name, class name, and additional parameters.
+     * @param listener     Callback for the data loader service to report status back to the
+     *                     caller.
+     * @return false if 1) target ID collides with a data loader that is already bound to data
+     * loader manager; 2) package name is not specified; 3) fails to find data loader package;
+     * or 4) fails to bind to the specified data loader service, otherwise return true.
+     */
+    public boolean bindToDataLoader(int dataLoaderId, @NonNull DataLoaderParamsParcel params,
+            @NonNull IDataLoaderStatusListener listener) {
+        try {
+            return mService.bindToDataLoader(dataLoaderId, params, listener);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns a binder interface of the data loader binder service, given its ID.
+     */
+    @Nullable
+    public IDataLoader getDataLoader(int dataLoaderId) {
+        try {
+            return mService.getDataLoader(dataLoaderId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Unbinds from a data loader binder service, specified by its ID.
+     * DataLoader will receive destroy notification.
+     */
+    @Nullable
+    public void unbindFromDataLoader(int dataLoaderId) {
+        try {
+            mService.unbindFromDataLoader(dataLoaderId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+}
diff --git a/android/content/pm/DataLoaderParams.java b/android/content/pm/DataLoaderParams.java
new file mode 100644
index 0000000..a791026
--- /dev/null
+++ b/android/content/pm/DataLoaderParams.java
@@ -0,0 +1,98 @@
+/*
+ * 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.content.pm;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.content.ComponentName;
+
+/**
+ * This class represents the parameters used to configure a Data Loader.
+ *
+ * WARNING: This is a system API to aid internal development.
+ * Use at your own risk. It will change or be removed without warning.
+ * @hide
+ */
+@SystemApi
+public class DataLoaderParams {
+    @NonNull
+    private final DataLoaderParamsParcel mData;
+
+    /**
+     * Creates and populates set of Data Loader parameters for Streaming installation.
+     *
+     * @param componentName Data Loader component supporting Streaming installation.
+     * @param arguments free form installation arguments
+     */
+    public static final @NonNull DataLoaderParams forStreaming(@NonNull ComponentName componentName,
+            @NonNull String arguments) {
+        return new DataLoaderParams(DataLoaderType.STREAMING, componentName, arguments);
+    }
+
+    /**
+     * Creates and populates set of Data Loader parameters for Incremental installation.
+     *
+     * @param componentName Data Loader component supporting Incremental installation.
+     * @param arguments free form installation arguments
+     */
+    public static final @NonNull DataLoaderParams forIncremental(
+            @NonNull ComponentName componentName, @NonNull String arguments) {
+        return new DataLoaderParams(DataLoaderType.INCREMENTAL, componentName, arguments);
+    }
+
+    /** @hide */
+    public DataLoaderParams(@NonNull @DataLoaderType int type, @NonNull ComponentName componentName,
+            @NonNull String arguments) {
+        DataLoaderParamsParcel data = new DataLoaderParamsParcel();
+        data.type = type;
+        data.packageName = componentName.getPackageName();
+        data.className = componentName.getClassName();
+        data.arguments = arguments;
+        mData = data;
+    }
+
+    /** @hide */
+    DataLoaderParams(@NonNull DataLoaderParamsParcel data) {
+        mData = data;
+    }
+
+    /** @hide */
+    public final @NonNull DataLoaderParamsParcel getData() {
+        return mData;
+    }
+
+    /**
+     * @return data loader type
+     */
+    public final @NonNull @DataLoaderType int getType() {
+        return mData.type;
+    }
+
+    /**
+     * @return data loader's component name
+     */
+    public final @NonNull ComponentName getComponentName() {
+        return new ComponentName(mData.packageName, mData.className);
+    }
+
+    /**
+     * @return data loader's arguments
+     */
+    public final @NonNull String getArguments() {
+        return mData.arguments;
+    }
+}
diff --git a/android/content/pm/FallbackCategoryProvider.java b/android/content/pm/FallbackCategoryProvider.java
new file mode 100644
index 0000000..a0a11aa
--- /dev/null
+++ b/android/content/pm/FallbackCategoryProvider.java
@@ -0,0 +1,69 @@
+/*
+ * 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.content.pm;
+
+import android.content.res.AssetManager;
+import android.content.res.Resources;
+import android.os.SystemProperties;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+
+/**
+ * Class that provides fallback values for {@link ApplicationInfo#category}.
+ *
+ * @hide
+ */
+public class FallbackCategoryProvider {
+    private static final String TAG = "FallbackCategoryProvider";
+
+    private static final ArrayMap<String, Integer> sFallbacks = new ArrayMap<>();
+
+    public static void loadFallbacks() {
+        sFallbacks.clear();
+        if (SystemProperties.getBoolean("fw.ignore_fb_categories", false)) {
+            Log.d(TAG, "Ignoring fallback categories");
+            return;
+        }
+
+        final AssetManager assets = new AssetManager();
+        assets.addAssetPath("/system/framework/framework-res.apk");
+        final Resources res = new Resources(assets, null, null);
+
+        try (BufferedReader reader = new BufferedReader(new InputStreamReader(
+                res.openRawResource(com.android.internal.R.raw.fallback_categories)))) {
+            String line;
+            while ((line = reader.readLine()) != null) {
+                if (line.charAt(0) == '#') continue;
+                final String[] split = line.split(",");
+                if (split.length == 2) {
+                    sFallbacks.put(split[0], Integer.parseInt(split[1]));
+                }
+            }
+            Log.d(TAG, "Found " + sFallbacks.size() + " fallback categories");
+        } catch (IOException | NumberFormatException e) {
+            Log.w(TAG, "Failed to read fallback categories", e);
+        }
+    }
+
+    public static int getFallbackCategory(String packageName) {
+        return sFallbacks.getOrDefault(packageName, ApplicationInfo.CATEGORY_UNDEFINED);
+    }
+}
diff --git a/android/content/pm/FeatureGroupInfo.java b/android/content/pm/FeatureGroupInfo.java
new file mode 100644
index 0000000..38c8f74
--- /dev/null
+++ b/android/content/pm/FeatureGroupInfo.java
@@ -0,0 +1,65 @@
+/**
+ * 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.content.pm;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A set of features that can be requested by an application. This corresponds
+ * to information collected from the
+ * AndroidManifest.xml's {@code <feature-group>} tag.
+ */
+public final class FeatureGroupInfo implements Parcelable {
+
+    /**
+     * The list of features that are required by this group.
+     *
+     * @see FeatureInfo#FLAG_REQUIRED
+     */
+    public FeatureInfo[] features;
+
+    public FeatureGroupInfo() {
+    }
+
+    public FeatureGroupInfo(FeatureGroupInfo other) {
+        features = other.features;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeTypedArray(features, flags);
+    }
+
+    public static final @android.annotation.NonNull Creator<FeatureGroupInfo> CREATOR = new Creator<FeatureGroupInfo>() {
+        @Override
+        public FeatureGroupInfo createFromParcel(Parcel source) {
+            FeatureGroupInfo group = new FeatureGroupInfo();
+            group.features = source.createTypedArray(FeatureInfo.CREATOR);
+            return group;
+        }
+
+        @Override
+        public FeatureGroupInfo[] newArray(int size) {
+            return new FeatureGroupInfo[size];
+        }
+    };
+}
diff --git a/android/content/pm/FeatureInfo.java b/android/content/pm/FeatureInfo.java
new file mode 100644
index 0000000..89269e0
--- /dev/null
+++ b/android/content/pm/FeatureInfo.java
@@ -0,0 +1,158 @@
+/*
+ * 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.content.pm;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.proto.ProtoOutputStream;
+
+/**
+ * Definition of a single optional hardware or software feature of an Android
+ * device.
+ * <p>
+ * This object is used to represent both features supported by a device and
+ * features requested by an app. Apps can request that certain features be
+ * available as a prerequisite to being installed through the
+ * {@code uses-feature} tag in their manifests.
+ * <p>
+ * Starting in {@link android.os.Build.VERSION_CODES#N}, features can have a
+ * version, which must always be backwards compatible. That is, a device
+ * claiming to support version 3 of a specific feature must support apps
+ * requesting version 1 of that feature.
+ */
+public class FeatureInfo implements Parcelable {
+    /**
+     * The name of this feature, for example "android.hardware.camera".  If
+     * this is null, then this is an OpenGL ES version feature as described
+     * in {@link #reqGlEsVersion}.
+     */
+    public String name;
+
+    /**
+     * If this object represents a feature supported by a device, this is the
+     * maximum version of this feature supported by the device. The device
+     * implicitly supports all older versions of this feature.
+     * <p>
+     * If this object represents a feature requested by an app, this is the
+     * minimum version of the feature required by the app.
+     * <p>
+     * When a feature version is undefined by a device, it's assumed to be
+     * version 0.
+     */
+    public int version;
+
+    /**
+     * Default value for {@link #reqGlEsVersion};
+     */
+    public static final int GL_ES_VERSION_UNDEFINED = 0;
+    
+    /**
+     * The GLES version used by an application. The upper order 16 bits represent the
+     * major version and the lower order 16 bits the minor version.  Only valid
+     * if {@link #name} is null.
+     */
+    public int reqGlEsVersion;
+
+    /**
+     * Set on {@link #flags} if this feature has been required by the application.
+     */
+    public static final int FLAG_REQUIRED = 0x0001;
+    
+    /**
+     * Additional flags.  May be zero or more of {@link #FLAG_REQUIRED}.
+     */
+    public int flags;
+    
+    public FeatureInfo() {
+    }
+
+    public FeatureInfo(FeatureInfo orig) {
+        name = orig.name;
+        version = orig.version;
+        reqGlEsVersion = orig.reqGlEsVersion;
+        flags = orig.flags;
+    }
+
+    @Override
+    public String toString() {
+        if (name != null) {
+            return "FeatureInfo{"
+                    + Integer.toHexString(System.identityHashCode(this))
+                    + " " + name + " v=" + version + " fl=0x" + Integer.toHexString(flags) + "}";
+        } else {
+            return "FeatureInfo{"
+                    + Integer.toHexString(System.identityHashCode(this))
+                    + " glEsVers=" + getGlEsVersion()
+                    + " fl=0x" + Integer.toHexString(flags) + "}";
+        }
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int parcelableFlags) {
+        dest.writeString8(name);
+        dest.writeInt(version);
+        dest.writeInt(reqGlEsVersion);
+        dest.writeInt(flags);
+    }
+
+    /** @hide */
+    public void dumpDebug(ProtoOutputStream proto, long fieldId) {
+        long token = proto.start(fieldId);
+        if (name != null) {
+            proto.write(FeatureInfoProto.NAME, name);
+        }
+        proto.write(FeatureInfoProto.VERSION, version);
+        proto.write(FeatureInfoProto.GLES_VERSION, getGlEsVersion());
+        proto.write(FeatureInfoProto.FLAGS, flags);
+        proto.end(token);
+    }
+
+    public static final @android.annotation.NonNull Creator<FeatureInfo> CREATOR = new Creator<FeatureInfo>() {
+        @Override
+        public FeatureInfo createFromParcel(Parcel source) {
+            return new FeatureInfo(source);
+        }
+        @Override
+        public FeatureInfo[] newArray(int size) {
+            return new FeatureInfo[size];
+        }
+    };
+
+    private FeatureInfo(Parcel source) {
+        name = source.readString8();
+        version = source.readInt();
+        reqGlEsVersion = source.readInt();
+        flags = source.readInt();
+    }
+
+    /**
+     * This method extracts the major and minor version of reqGLEsVersion attribute
+     * and returns it as a string. Say reqGlEsVersion value of 0x00010002 is returned
+     * as 1.2
+     * @return String representation of the reqGlEsVersion attribute
+     */
+    public String getGlEsVersion() {
+        int major = ((reqGlEsVersion & 0xffff0000) >> 16);
+        int minor = reqGlEsVersion & 0x0000ffff;
+        return String.valueOf(major)+"."+String.valueOf(minor);
+    }
+}
diff --git a/android/content/pm/InstallSourceInfo.java b/android/content/pm/InstallSourceInfo.java
new file mode 100644
index 0000000..a45bf79
--- /dev/null
+++ b/android/content/pm/InstallSourceInfo.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Information about how an app was installed.
+ * @see PackageManager#getInstallSourceInfo(String)
+ */
+public final class InstallSourceInfo implements Parcelable {
+
+    @Nullable private final String mInitiatingPackageName;
+
+    @Nullable private final SigningInfo mInitiatingPackageSigningInfo;
+
+    @Nullable private final String mOriginatingPackageName;
+
+    @Nullable private final String mInstallingPackageName;
+
+    /** @hide */
+    public InstallSourceInfo(@Nullable String initiatingPackageName,
+            @Nullable SigningInfo initiatingPackageSigningInfo,
+            @Nullable String originatingPackageName, @Nullable String installingPackageName) {
+        mInitiatingPackageName = initiatingPackageName;
+        mInitiatingPackageSigningInfo = initiatingPackageSigningInfo;
+        mOriginatingPackageName = originatingPackageName;
+        mInstallingPackageName = installingPackageName;
+    }
+
+    @Override
+    public int describeContents() {
+        return mInitiatingPackageSigningInfo == null
+                ? 0 : mInitiatingPackageSigningInfo.describeContents();
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeString(mInitiatingPackageName);
+        dest.writeParcelable(mInitiatingPackageSigningInfo, flags);
+        dest.writeString(mOriginatingPackageName);
+        dest.writeString(mInstallingPackageName);
+    }
+
+    private InstallSourceInfo(Parcel source) {
+        mInitiatingPackageName = source.readString();
+        mInitiatingPackageSigningInfo = source.readParcelable(SigningInfo.class.getClassLoader());
+        mOriginatingPackageName = source.readString();
+        mInstallingPackageName = source.readString();
+    }
+
+    /**
+     * The name of the package that requested the installation, or null if not available.
+     *
+     * This is normally the same as the installing package name. If the installing package name
+     * is changed, for example by calling
+     * {@link PackageManager#setInstallerPackageName(String, String)}, the initiating package name
+     * remains unchanged. It continues to identify the actual package that performed the install
+     * or update.
+     * <p>
+     * Null may be returned if the app was not installed by a package (e.g. a system app or an app
+     * installed via adb) or if the initiating package has itself been uninstalled.
+     */
+    @Nullable
+    public String getInitiatingPackageName() {
+        return mInitiatingPackageName;
+    }
+
+    /**
+     * Information about the signing certificates used to sign the initiating package, if available.
+     */
+    @Nullable
+    public SigningInfo getInitiatingPackageSigningInfo() {
+        return mInitiatingPackageSigningInfo;
+    }
+
+    /**
+     * The name of the package on behalf of which the initiating package requested the installation,
+     * or null if not available.
+     * <p>
+     * For example if a downloaded APK is installed via the Package Installer this could be the
+     * app that performed the download. This value is provided by the initiating package and not
+     * verified by the framework.
+     * <p>
+     * Note that the {@code InstallSourceInfo} returned by
+     * {@link PackageManager#getInstallSourceInfo(String)} will not have this information
+     * available unless the calling application holds the INSTALL_PACKAGES permission.
+     */
+    @Nullable
+    public String getOriginatingPackageName() {
+        return mOriginatingPackageName;
+    }
+
+    /**
+     * The name of the package responsible for the installation (the installer of record), or null
+     * if not available.
+     * Note that this may differ from the initiating package name and can be modified via
+     * {@link PackageManager#setInstallerPackageName(String, String)}.
+     * <p>
+     * Null may be returned if the app was not installed by a package (e.g. a system app or an app
+     * installed via adb) or if the installing package has itself been uninstalled.
+     */
+    @Nullable
+    public String getInstallingPackageName() {
+        return mInstallingPackageName;
+    }
+
+    @NonNull
+    public static final Parcelable.Creator<InstallSourceInfo> CREATOR =
+            new Creator<InstallSourceInfo>() {
+                @Override
+                public InstallSourceInfo createFromParcel(Parcel source) {
+                    return new InstallSourceInfo(source);
+                }
+
+                @Override
+                public InstallSourceInfo[] newArray(int size) {
+                    return new InstallSourceInfo[size];
+                }
+            };
+}
diff --git a/android/content/pm/InstallationFile.java b/android/content/pm/InstallationFile.java
new file mode 100644
index 0000000..de761ad
--- /dev/null
+++ b/android/content/pm/InstallationFile.java
@@ -0,0 +1,99 @@
+/*
+ * 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.content.pm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+
+/**
+ * Definition of a file in a streaming installation session.
+ * You can use this class to retrieve the information of such a file, such as its name, size and
+ * metadata. These file attributes will be consistent with those used in:
+ * {@code PackageInstaller.Session#addFile}, when the file was first added into the session.
+ *
+ * WARNING: This is a system API to aid internal development.
+ * Use at your own risk. It will change or be removed without warning.
+ *
+ * @see android.content.pm.PackageInstaller.Session#addFile
+ * @hide
+ */
+@SystemApi
+public final class InstallationFile {
+    private final @NonNull InstallationFileParcel mParcel;
+
+    /**
+     * Constructor, internal use only
+     * @hide
+     */
+    public InstallationFile(@PackageInstaller.FileLocation int location, @NonNull String name,
+            long lengthBytes, @Nullable byte[] metadata, @Nullable byte[] signature) {
+        mParcel = new InstallationFileParcel();
+        mParcel.location = location;
+        mParcel.name = name;
+        mParcel.size = lengthBytes;
+        mParcel.metadata = metadata;
+        mParcel.signature = signature;
+    }
+
+    /**
+     * Installation Location of this file. Can be one of the following three locations:
+     * <ul>
+     *     <li>(1) {@code PackageInstaller.LOCATION_DATA_APP}</li>
+     *     <li>(2) {@code PackageInstaller.LOCATION_MEDIA_OBB}</li>
+     *     <li>(3) {@code PackageInstaller.LOCATION_MEDIA_DATA}</li>
+     * </ul>
+     * @see android.content.pm.PackageInstaller
+     * @return Integer that denotes the installation location of the file.
+     */
+    public @PackageInstaller.FileLocation int getLocation() {
+        return mParcel.location;
+    }
+
+    /**
+     * @return Name of the file.
+     */
+    public @NonNull String getName() {
+        return mParcel.name;
+    }
+
+    /**
+     * @return File size in bytes.
+     */
+    public long getLengthBytes() {
+        return mParcel.size;
+    }
+
+    /**
+     * @return File metadata as a byte array
+     */
+    public @Nullable byte[] getMetadata() {
+        return mParcel.metadata;
+    }
+
+    /**
+     * @return File signature info as a byte array
+     */
+    public @Nullable byte[] getSignature() {
+        return mParcel.signature;
+    }
+
+    /** @hide */
+    public @NonNull InstallationFileParcel getData() {
+        return mParcel;
+    }
+}
diff --git a/android/content/pm/InstantAppInfo.java b/android/content/pm/InstantAppInfo.java
new file mode 100644
index 0000000..24d6a07
--- /dev/null
+++ b/android/content/pm/InstantAppInfo.java
@@ -0,0 +1,151 @@
+/*
+ * 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.content.pm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.graphics.drawable.Drawable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * This class represents the state of an instant app. Instant apps can
+ * be installed or uninstalled. If the app is installed you can call
+ * {@link #getApplicationInfo()} to get the app info, otherwise this
+ * class provides APIs to get basic app info for showing it in the UI,
+ * such as permissions, label, package name.
+ *
+ * @hide
+ */
+@SystemApi
+public final class InstantAppInfo implements Parcelable {
+    private final ApplicationInfo mApplicationInfo;
+
+    private final String mPackageName;
+    private final CharSequence mLabelText;
+
+    private final String[] mRequestedPermissions;
+    private final String[] mGrantedPermissions;
+
+    public InstantAppInfo(ApplicationInfo appInfo,
+            String[] requestedPermissions, String[] grantedPermissions) {
+        mApplicationInfo = appInfo;
+        mPackageName = null;
+        mLabelText = null;
+        mRequestedPermissions = requestedPermissions;
+        mGrantedPermissions = grantedPermissions;
+    }
+
+    public InstantAppInfo(String packageName, CharSequence label,
+            String[] requestedPermissions, String[] grantedPermissions) {
+        mApplicationInfo = null;
+        mPackageName = packageName;
+        mLabelText = label;
+        mRequestedPermissions = requestedPermissions;
+        mGrantedPermissions = grantedPermissions;
+    }
+
+    private InstantAppInfo(Parcel parcel) {
+        mPackageName = parcel.readString();
+        mLabelText = parcel.readCharSequence();
+        mRequestedPermissions = parcel.readStringArray();
+        mGrantedPermissions = parcel.createStringArray();
+        mApplicationInfo = parcel.readParcelable(null);
+    }
+
+    /**
+     * @return The application info if the app is installed,
+     *     <code>null</code> otherwise,
+     */
+    public @Nullable ApplicationInfo getApplicationInfo() {
+        return mApplicationInfo;
+    }
+
+    /**
+     * @return The package name.
+     */
+    public @NonNull String getPackageName() {
+        if (mApplicationInfo != null) {
+            return mApplicationInfo.packageName;
+        }
+        return mPackageName;
+    }
+
+    /**
+     * @param packageManager Package manager for loading resources.
+     * @return Loads the label if the app is installed or returns the cached one otherwise.
+     */
+    public @NonNull CharSequence loadLabel(@NonNull PackageManager packageManager) {
+        if (mApplicationInfo != null) {
+            return mApplicationInfo.loadLabel(packageManager);
+        }
+        return mLabelText;
+    }
+
+    /**
+     * @param packageManager Package manager for loading resources.
+     * @return Loads the icon if the app is installed or returns the cached one otherwise.
+     */
+    public @NonNull Drawable loadIcon(@NonNull PackageManager packageManager) {
+        if (mApplicationInfo != null) {
+            return mApplicationInfo.loadIcon(packageManager);
+        }
+        return packageManager.getInstantAppIcon(mPackageName);
+    }
+
+    /**
+     * @return The requested permissions.
+     */
+    public @Nullable String[] getRequestedPermissions() {
+        return mRequestedPermissions;
+    }
+
+    /**
+     * @return The granted permissions.
+     */
+    public @Nullable String[] getGrantedPermissions() {
+        return mGrantedPermissions;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeString(mPackageName);
+        parcel.writeCharSequence(mLabelText);
+        parcel.writeStringArray(mRequestedPermissions);
+        parcel.writeStringArray(mGrantedPermissions);
+        parcel.writeParcelable(mApplicationInfo, flags);
+    }
+
+    public static final @android.annotation.NonNull Creator<InstantAppInfo> CREATOR =
+            new Creator<InstantAppInfo>() {
+        @Override
+        public InstantAppInfo createFromParcel(Parcel parcel) {
+            return new InstantAppInfo(parcel);
+        }
+
+        @Override
+        public InstantAppInfo[] newArray(int size) {
+            return new InstantAppInfo[0];
+        }
+    };
+}
diff --git a/android/content/pm/InstantAppIntentFilter.java b/android/content/pm/InstantAppIntentFilter.java
new file mode 100644
index 0000000..7c63406
--- /dev/null
+++ b/android/content/pm/InstantAppIntentFilter.java
@@ -0,0 +1,82 @@
+/*
+ * 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.content.pm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.content.IntentFilter;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Information about an instant application intent filter.
+ * @hide
+ */
+@SystemApi
+public final class InstantAppIntentFilter implements Parcelable {
+    private final String mSplitName;
+    /** The filters used to match domain */
+    private final List<IntentFilter> mFilters = new ArrayList<IntentFilter>();
+
+    public InstantAppIntentFilter(@Nullable String splitName, @NonNull List<IntentFilter> filters) {
+        if (filters == null || filters.size() == 0) {
+            throw new IllegalArgumentException();
+        }
+        mSplitName = splitName;
+        mFilters.addAll(filters);
+    }
+
+    InstantAppIntentFilter(Parcel in) {
+        mSplitName = in.readString();
+        in.readList(mFilters, null /*loader*/);
+    }
+
+    public String getSplitName() {
+        return mSplitName;
+    }
+
+    public List<IntentFilter> getFilters() {
+        return mFilters;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeString(mSplitName);
+        out.writeList(mFilters);
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<InstantAppIntentFilter> CREATOR
+            = new Parcelable.Creator<InstantAppIntentFilter>() {
+        @Override
+        public InstantAppIntentFilter createFromParcel(Parcel in) {
+            return new InstantAppIntentFilter(in);
+        }
+        @Override
+        public InstantAppIntentFilter[] newArray(int size) {
+            return new InstantAppIntentFilter[size];
+        }
+    };
+}
diff --git a/android/content/pm/InstantAppRequest.java b/android/content/pm/InstantAppRequest.java
new file mode 100644
index 0000000..84f5021
--- /dev/null
+++ b/android/content/pm/InstantAppRequest.java
@@ -0,0 +1,78 @@
+/*
+ * 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.content.pm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Intent;
+import android.os.Bundle;
+
+/**
+ * Information needed to make an instant application resolution request.
+ * @hide
+ */
+public final class InstantAppRequest {
+
+    /** Response from the first phase of instant application resolution */
+    public final AuxiliaryResolveInfo responseObj;
+    /** The original intent that triggered instant application resolution */
+    public final Intent origIntent;
+    /** Resolved type of the intent */
+    public final String resolvedType;
+    /** The name of the package requesting the instant application */
+    public final String callingPackage;
+    /** The feature in the package requesting the instant application */
+    public final String callingFeatureId;
+    /** Whether or not the requesting package was an instant app */
+    public final boolean isRequesterInstantApp;
+    /** ID of the user requesting the instant application */
+    public final int userId;
+    /**
+     * Optional extra bundle provided by the source application to the installer for additional
+     * verification.
+     */
+    public final Bundle verificationBundle;
+    /** Whether resolution occurs because an application is starting */
+    public final boolean resolveForStart;
+    /**
+     * The hash prefix of an instant app's domain or null if no host is defined.
+     * Secure version that should be carried through for external use.
+     */
+    @Nullable
+    public final int[] hostDigestPrefixSecure;
+    /** A unique identifier */
+    @NonNull
+    public final String token;
+
+    public InstantAppRequest(AuxiliaryResolveInfo responseObj, Intent origIntent,
+            String resolvedType, String callingPackage, @Nullable String callingFeatureId,
+            boolean isRequesterInstantApp, int userId, Bundle verificationBundle,
+            boolean resolveForStart, @Nullable int[] hostDigestPrefixSecure,
+            @NonNull String token) {
+        this.responseObj = responseObj;
+        this.origIntent = origIntent;
+        this.resolvedType = resolvedType;
+        this.callingPackage = callingPackage;
+        this.callingFeatureId = callingFeatureId;
+        this.isRequesterInstantApp = isRequesterInstantApp;
+        this.userId = userId;
+        this.verificationBundle = verificationBundle;
+        this.resolveForStart = resolveForStart;
+        this.hostDigestPrefixSecure = hostDigestPrefixSecure;
+        this.token = token;
+    }
+}
diff --git a/android/content/pm/InstantAppRequestInfo.java b/android/content/pm/InstantAppRequestInfo.java
new file mode 100644
index 0000000..c531632
--- /dev/null
+++ b/android/content/pm/InstantAppRequestInfo.java
@@ -0,0 +1,230 @@
+/*
+ * 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.content.pm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.content.Intent;
+import android.os.Parcelable;
+import android.os.UserHandle;
+
+import com.android.internal.util.DataClass;
+
+/**
+ * Information exposed to {@link android.app.InstantAppResolverService} to complete
+ * an instant application resolution request.
+ * @hide
+ */
+@SystemApi
+@DataClass(genParcelable = true, genConstructor = true, genAidl = true, genGetters = true)
+public final class InstantAppRequestInfo implements Parcelable {
+
+    /**
+     * The sanitized {@link Intent} used for resolution. A sanitized Intent is an intent with
+     * potential PII removed from the original intent. Fields removed include extras and the
+     * host + path of the data, if defined.
+     */
+    @NonNull
+    private final Intent mIntent;
+
+    /** The hash prefix of the instant app's domain or null if no host is defined. */
+    @Nullable
+    private final int[] mHostDigestPrefix;
+
+    /** The user requesting the instant application */
+    @NonNull
+    private final UserHandle mUserHandle;
+
+    /** Whether or not the requesting package was an instant app itself */
+    private final boolean mRequesterInstantApp;
+
+    /** A unique identifier */
+    @NonNull
+    private final String mToken;
+
+
+
+    // Code below generated by codegen v1.0.15.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/InstantAppRequestInfo.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    /**
+     * Creates a new InstantAppRequestInfo.
+     *
+     * @param intent
+     *   The sanitized {@link Intent} used for resolution. A sanitized Intent is an intent with
+     *   potential PII removed from the original intent. Fields removed include extras and the
+     *   host + path of the data, if defined.
+     * @param hostDigestPrefix
+     *   The hash prefix of the instant app's domain or null if no host is defined.
+     * @param userHandle
+     *   The user requesting the instant application
+     * @param requesterInstantApp
+     *   Whether or not the requesting package was an instant app itself
+     * @param token
+     *   A unique identifier
+     */
+    @DataClass.Generated.Member
+    public InstantAppRequestInfo(
+            @NonNull Intent intent,
+            @Nullable int[] hostDigestPrefix,
+            @NonNull UserHandle userHandle,
+            boolean requesterInstantApp,
+            @NonNull String token) {
+        this.mIntent = intent;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mIntent);
+        this.mHostDigestPrefix = hostDigestPrefix;
+        this.mUserHandle = userHandle;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mUserHandle);
+        this.mRequesterInstantApp = requesterInstantApp;
+        this.mToken = token;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mToken);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * The sanitized {@link Intent} used for resolution. A sanitized Intent is an intent with
+     * potential PII removed from the original intent. Fields removed include extras and the
+     * host + path of the data, if defined.
+     */
+    @DataClass.Generated.Member
+    public @NonNull Intent getIntent() {
+        return mIntent;
+    }
+
+    /**
+     * The hash prefix of the instant app's domain or null if no host is defined.
+     */
+    @DataClass.Generated.Member
+    public @Nullable int[] getHostDigestPrefix() {
+        return mHostDigestPrefix;
+    }
+
+    /**
+     * The user requesting the instant application
+     */
+    @DataClass.Generated.Member
+    public @NonNull UserHandle getUserHandle() {
+        return mUserHandle;
+    }
+
+    /**
+     * Whether or not the requesting package was an instant app itself
+     */
+    @DataClass.Generated.Member
+    public boolean isRequesterInstantApp() {
+        return mRequesterInstantApp;
+    }
+
+    /**
+     * A unique identifier
+     */
+    @DataClass.Generated.Member
+    public @NonNull String getToken() {
+        return mToken;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        byte flg = 0;
+        if (mRequesterInstantApp) flg |= 0x8;
+        if (mHostDigestPrefix != null) flg |= 0x2;
+        dest.writeByte(flg);
+        dest.writeTypedObject(mIntent, flags);
+        if (mHostDigestPrefix != null) dest.writeIntArray(mHostDigestPrefix);
+        dest.writeTypedObject(mUserHandle, flags);
+        dest.writeString(mToken);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    /* package-private */ InstantAppRequestInfo(@NonNull android.os.Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        byte flg = in.readByte();
+        boolean requesterInstantApp = (flg & 0x8) != 0;
+        Intent intent = (Intent) in.readTypedObject(Intent.CREATOR);
+        int[] hostDigestPrefix = (flg & 0x2) == 0 ? null : in.createIntArray();
+        UserHandle userHandle = (UserHandle) in.readTypedObject(UserHandle.CREATOR);
+        String token = in.readString();
+
+        this.mIntent = intent;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mIntent);
+        this.mHostDigestPrefix = hostDigestPrefix;
+        this.mUserHandle = userHandle;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mUserHandle);
+        this.mRequesterInstantApp = requesterInstantApp;
+        this.mToken = token;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mToken);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<InstantAppRequestInfo> CREATOR
+            = new Parcelable.Creator<InstantAppRequestInfo>() {
+        @Override
+        public InstantAppRequestInfo[] newArray(int size) {
+            return new InstantAppRequestInfo[size];
+        }
+
+        @Override
+        public InstantAppRequestInfo createFromParcel(@NonNull android.os.Parcel in) {
+            return new InstantAppRequestInfo(in);
+        }
+    };
+
+    @DataClass.Generated(
+            time = 1583964236162L,
+            codegenVersion = "1.0.15",
+            sourceFile = "frameworks/base/core/java/android/content/pm/InstantAppRequestInfo.java",
+            inputSignatures = "private final @android.annotation.NonNull android.content.Intent mIntent\nprivate final @android.annotation.Nullable int[] mHostDigestPrefix\nprivate final @android.annotation.NonNull android.os.UserHandle mUserHandle\nprivate final  boolean mRequesterInstantApp\nprivate final @android.annotation.NonNull java.lang.String mToken\nclass InstantAppRequestInfo extends java.lang.Object implements [android.os.Parcelable]\[email protected](genParcelable=true, genConstructor=true, genAidl=true, genGetters=true)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/android/content/pm/InstantAppResolveInfo.java b/android/content/pm/InstantAppResolveInfo.java
new file mode 100644
index 0000000..4c963a6
--- /dev/null
+++ b/android/content/pm/InstantAppResolveInfo.java
@@ -0,0 +1,401 @@
+/*
+ * 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.content.pm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+import java.util.Random;
+
+/**
+ * Describes an externally resolvable instant application. There are three states that this class
+ * can represent: <p/>
+ * <ul>
+ *     <li>
+ *         The first, usable only for non http/s intents, implies that the resolver cannot
+ *         immediately resolve this intent and would prefer that resolution be deferred to the
+ *         instant app installer. Represent this state with {@link #InstantAppResolveInfo(Bundle)}.
+ *         If the {@link android.content.Intent} has the scheme set to http/s and a set of digest
+ *         prefixes were passed into one of the resolve methods in
+ *         {@link android.app.InstantAppResolverService}, this state cannot be used.
+ *     </li>
+ *     <li>
+ *         The second represents a partial match and is constructed with any of the other
+ *         constructors. By setting one or more of the {@link Nullable}arguments to null, you
+ *         communicate to the resolver in response to
+ *         {@link android.app.InstantAppResolverService#onGetInstantAppResolveInfo(Intent, int[],
+ *                String, InstantAppResolverService.InstantAppResolutionCallback)}
+ *         that you need a 2nd round of resolution to complete the request.
+ *     </li>
+ *     <li>
+ *         The third represents a complete match and is constructed with all @Nullable parameters
+ *         populated.
+ *     </li>
+ * </ul>
+ * @hide
+ */
+@SystemApi
+public final class InstantAppResolveInfo implements Parcelable {
+    /** Algorithm that will be used to generate the domain digest */
+    private static final String SHA_ALGORITHM = "SHA-256";
+
+    private static final byte[] EMPTY_DIGEST = new byte[0];
+
+    private final InstantAppDigest mDigest;
+    private final String mPackageName;
+    /** The filters used to match domain */
+    private final List<InstantAppIntentFilter> mFilters;
+    /** The version code of the app that this class resolves to */
+    private final long mVersionCode;
+    /** Data about the app that should be passed along to the Instant App installer on resolve */
+    private final Bundle mExtras;
+    /**
+     * A flag that indicates that the resolver is aware that an app may match, but would prefer
+     * that the installer get the sanitized intent to decide.
+     */
+    private final boolean mShouldLetInstallerDecide;
+
+    /** Constructor for intent-based InstantApp resolution results. */
+    public InstantAppResolveInfo(@NonNull InstantAppDigest digest, @Nullable String packageName,
+            @Nullable List<InstantAppIntentFilter> filters, int versionCode) {
+        this(digest, packageName, filters, (long) versionCode, null /* extras */);
+    }
+
+    /** Constructor for intent-based InstantApp resolution results with extras. */
+    public InstantAppResolveInfo(@NonNull InstantAppDigest digest, @Nullable String packageName,
+            @Nullable List<InstantAppIntentFilter> filters, long versionCode,
+            @Nullable Bundle extras) {
+        this(digest, packageName, filters, versionCode, extras, false);
+    }
+
+    /** Constructor for intent-based InstantApp resolution results by hostname. */
+    public InstantAppResolveInfo(@NonNull String hostName, @Nullable String packageName,
+            @Nullable List<InstantAppIntentFilter> filters) {
+        this(new InstantAppDigest(hostName), packageName, filters, -1 /*versionCode*/,
+                null /* extras */);
+    }
+
+    /**
+     * Constructor that indicates that resolution could be delegated to the installer when the
+     * sanitized intent contains enough information to resolve completely.
+     */
+    public InstantAppResolveInfo(@Nullable Bundle extras) {
+        this(InstantAppDigest.UNDEFINED, null, null, -1, extras, true);
+    }
+
+    private InstantAppResolveInfo(@NonNull InstantAppDigest digest, @Nullable String packageName,
+            @Nullable List<InstantAppIntentFilter> filters, long versionCode,
+            @Nullable Bundle extras, boolean shouldLetInstallerDecide) {
+        // validate arguments
+        if ((packageName == null && (filters != null && filters.size() != 0))
+                || (packageName != null && (filters == null || filters.size() == 0))) {
+            throw new IllegalArgumentException();
+        }
+        mDigest = digest;
+        if (filters != null) {
+            mFilters = new ArrayList<>(filters.size());
+            mFilters.addAll(filters);
+        } else {
+            mFilters = null;
+        }
+        mPackageName = packageName;
+        mVersionCode = versionCode;
+        mExtras = extras;
+        mShouldLetInstallerDecide = shouldLetInstallerDecide;
+    }
+
+    InstantAppResolveInfo(Parcel in) {
+        mShouldLetInstallerDecide = in.readBoolean();
+        mExtras = in.readBundle();
+        if (mShouldLetInstallerDecide) {
+            mDigest = InstantAppDigest.UNDEFINED;
+            mPackageName = null;
+            mFilters = Collections.emptyList();
+            mVersionCode = -1;
+        } else {
+            mDigest = in.readParcelable(null /*loader*/);
+            mPackageName = in.readString();
+            mFilters = new ArrayList<>();
+            in.readList(mFilters, null /*loader*/);
+            mVersionCode = in.readLong();
+        }
+    }
+
+    /**
+     * Returns true if the resolver is aware that an app may match, but would prefer
+     * that the installer get the sanitized intent to decide. This should not be true for
+     * resolutions that include a host and will be ignored in such cases.
+     */
+    public boolean shouldLetInstallerDecide() {
+        return mShouldLetInstallerDecide;
+    }
+
+    public byte[] getDigestBytes() {
+        return mDigest.mDigestBytes.length > 0 ? mDigest.getDigestBytes()[0] : EMPTY_DIGEST;
+    }
+
+    public int getDigestPrefix() {
+        return mDigest.getDigestPrefix()[0];
+    }
+
+    public String getPackageName() {
+        return mPackageName;
+    }
+
+    public List<InstantAppIntentFilter> getIntentFilters() {
+        return mFilters;
+    }
+
+    /**
+     * @deprecated Use {@link #getLongVersionCode} instead.
+     */
+    @Deprecated
+    public int getVersionCode() {
+        return (int) (mVersionCode & 0xffffffff);
+    }
+
+    public long getLongVersionCode() {
+        return mVersionCode;
+    }
+
+    @Nullable
+    public Bundle getExtras() {
+        return mExtras;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeBoolean(mShouldLetInstallerDecide);
+        out.writeBundle(mExtras);
+        if (mShouldLetInstallerDecide) {
+            return;
+        }
+        out.writeParcelable(mDigest, flags);
+        out.writeString(mPackageName);
+        out.writeList(mFilters);
+        out.writeLong(mVersionCode);
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<InstantAppResolveInfo> CREATOR
+            = new Parcelable.Creator<InstantAppResolveInfo>() {
+        public InstantAppResolveInfo createFromParcel(Parcel in) {
+            return new InstantAppResolveInfo(in);
+        }
+
+        public InstantAppResolveInfo[] newArray(int size) {
+            return new InstantAppResolveInfo[size];
+        }
+    };
+
+    /**
+     * Helper class to generate and store each of the digests and prefixes
+     * sent to the Instant App Resolver.
+     * <p>
+     * Since intent filters may want to handle multiple hosts within a
+     * domain [eg “*.google.com”], the resolver is presented with multiple
+     * hash prefixes. For example, "a.b.c.d.e" generates digests for
+     * "d.e", "c.d.e", "b.c.d.e" and "a.b.c.d.e".
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final class InstantAppDigest implements Parcelable {
+        static final int DIGEST_MASK = 0xfffff000;
+
+        /**
+         * A special instance that represents and undefined digest used for cases that a host was
+         * not provided or is irrelevant to the response.
+         */
+        public static final InstantAppDigest UNDEFINED =
+                new InstantAppDigest(new byte[][]{}, new int[]{});
+
+        private static Random sRandom = null;
+        static {
+            try {
+                sRandom = SecureRandom.getInstance("SHA1PRNG");
+            } catch (NoSuchAlgorithmException e) {
+                // oh well
+                sRandom = new Random();
+            }
+        }
+        /** Full digest of the domain hashes */
+        private final byte[][] mDigestBytes;
+        /** The first 5 bytes of the domain hashes */
+        private final int[] mDigestPrefix;
+        /** The first 5 bytes of the domain hashes interspersed with random data */
+        private int[] mDigestPrefixSecure;
+
+        public InstantAppDigest(@NonNull String hostName) {
+            this(hostName, -1 /*maxDigests*/);
+        }
+
+        /** @hide */
+        public InstantAppDigest(@NonNull String hostName, int maxDigests) {
+            if (hostName == null) {
+                throw new IllegalArgumentException();
+            }
+            mDigestBytes = generateDigest(hostName.toLowerCase(Locale.ENGLISH), maxDigests);
+            mDigestPrefix = new int[mDigestBytes.length];
+            for (int i = 0; i < mDigestBytes.length; i++) {
+                mDigestPrefix[i] =
+                        ((mDigestBytes[i][0] & 0xFF) << 24
+                                | (mDigestBytes[i][1] & 0xFF) << 16
+                                | (mDigestBytes[i][2] & 0xFF) << 8
+                                | (mDigestBytes[i][3] & 0xFF) << 0)
+                        & DIGEST_MASK;
+            }
+        }
+
+        private InstantAppDigest(byte[][] digestBytes, int[] prefix) {
+            this.mDigestPrefix = prefix;
+            this.mDigestBytes = digestBytes;
+        }
+
+        private static byte[][] generateDigest(String hostName, int maxDigests) {
+            ArrayList<byte[]> digests = new ArrayList<>();
+            try {
+                final MessageDigest digest = MessageDigest.getInstance(SHA_ALGORITHM);
+                if (maxDigests <= 0) {
+                    final byte[] hostBytes = hostName.getBytes();
+                    digests.add(digest.digest(hostBytes));
+                } else {
+                    int prevDot = hostName.lastIndexOf('.');
+                    prevDot = hostName.lastIndexOf('.', prevDot - 1);
+                    // shortcut for short URLs
+                    if (prevDot < 0) {
+                        digests.add(digest.digest(hostName.getBytes()));
+                    } else {
+                        byte[] hostBytes =
+                                hostName.substring(prevDot + 1, hostName.length()).getBytes();
+                        digests.add(digest.digest(hostBytes));
+                        int digestCount = 1;
+                        while (prevDot >= 0 && digestCount < maxDigests) {
+                            prevDot = hostName.lastIndexOf('.', prevDot - 1);
+                            hostBytes =
+                                    hostName.substring(prevDot + 1, hostName.length()).getBytes();
+                            digests.add(digest.digest(hostBytes));
+                            digestCount++;
+                        }
+                    }
+                }
+            } catch (NoSuchAlgorithmException e) {
+                throw new IllegalStateException("could not find digest algorithm");
+            }
+            return digests.toArray(new byte[digests.size()][]);
+        }
+
+        InstantAppDigest(Parcel in) {
+            final int digestCount = in.readInt();
+            if (digestCount == -1) {
+                mDigestBytes = null;
+            } else {
+                mDigestBytes = new byte[digestCount][];
+                for (int i = 0; i < digestCount; i++) {
+                    mDigestBytes[i] = in.createByteArray();
+                }
+            }
+            mDigestPrefix = in.createIntArray();
+            mDigestPrefixSecure = in.createIntArray();
+        }
+
+        public byte[][] getDigestBytes() {
+            return mDigestBytes;
+        }
+
+        public int[] getDigestPrefix() {
+            return mDigestPrefix;
+        }
+
+        /**
+         * Returns a digest prefix with additional random prefixes interspersed.
+         * @hide
+         */
+        public int[] getDigestPrefixSecure() {
+            if (this == InstantAppResolveInfo.InstantAppDigest.UNDEFINED) {
+                return getDigestPrefix();
+            } else if (mDigestPrefixSecure == null) {
+                // let's generate some random data to intersperse throughout the set of prefixes
+                final int realSize = getDigestPrefix().length;
+                final int manufacturedSize = realSize + 10 + sRandom.nextInt(10);
+                mDigestPrefixSecure = Arrays.copyOf(getDigestPrefix(), manufacturedSize);
+                for (int i = realSize; i < manufacturedSize; i++) {
+                    mDigestPrefixSecure[i] = sRandom.nextInt() & DIGEST_MASK;
+                }
+                Arrays.sort(mDigestPrefixSecure);
+            }
+            return mDigestPrefixSecure;
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel out, int flags) {
+            final boolean isUndefined = this == UNDEFINED;
+            out.writeBoolean(isUndefined);
+            if (isUndefined) {
+                return;
+            }
+            if (mDigestBytes == null) {
+                out.writeInt(-1);
+            } else {
+                out.writeInt(mDigestBytes.length);
+                for (int i = 0; i < mDigestBytes.length; i++) {
+                    out.writeByteArray(mDigestBytes[i]);
+                }
+            }
+            out.writeIntArray(mDigestPrefix);
+            out.writeIntArray(mDigestPrefixSecure);
+        }
+
+        @SuppressWarnings("hiding")
+        public static final @android.annotation.NonNull Parcelable.Creator<InstantAppDigest> CREATOR =
+                new Parcelable.Creator<InstantAppDigest>() {
+            @Override
+            public InstantAppDigest createFromParcel(Parcel in) {
+                if (in.readBoolean() /* is undefined */) {
+                    return UNDEFINED;
+                }
+                return new InstantAppDigest(in);
+            }
+            @Override
+            public InstantAppDigest[] newArray(int size) {
+                return new InstantAppDigest[size];
+            }
+        };
+    }
+}
diff --git a/android/content/pm/InstrumentationInfo.java b/android/content/pm/InstrumentationInfo.java
new file mode 100644
index 0000000..bfbd4c6
--- /dev/null
+++ b/android/content/pm/InstrumentationInfo.java
@@ -0,0 +1,228 @@
+/*
+ * 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.content.pm;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.SparseArray;
+
+/**
+ * Information you can retrieve about a particular piece of test
+ * instrumentation.  This corresponds to information collected
+ * from the AndroidManifest.xml's &lt;instrumentation&gt; tag.
+ */
+public class InstrumentationInfo extends PackageItemInfo implements Parcelable {
+    /**
+     * The name of the application package being instrumented.  From the
+     * "package" attribute.
+     */
+    public String targetPackage;
+
+    /**
+     * Names of the process(es) this instrumentation will run in.  If not specified, only
+     * runs in the main process of the targetPackage.  Can either be a comma-separated list
+     * of process names or '*' for any process that launches to run targetPackage code.
+     */
+    public String targetProcesses;
+
+    /**
+     * Full path to the base APK for this application.
+     */
+    public String sourceDir;
+
+    /**
+     * Full path to the publicly available parts of {@link #sourceDir},
+     * including resources and manifest. This may be different from
+     * {@link #sourceDir} if an application is forward locked.
+     */
+    public String publicSourceDir;
+
+    /**
+     * The names of all installed split APKs, ordered lexicographically.
+     */
+    public String[] splitNames;
+
+    /**
+     * Full paths to zero or more split APKs, indexed by the same order as {@link #splitNames}.
+     */
+    public String[] splitSourceDirs;
+
+    /**
+     * Full path to the publicly available parts of {@link #splitSourceDirs},
+     * including resources and manifest. This may be different from
+     * {@link #splitSourceDirs} if an application is forward locked.
+     *
+     * @see #splitSourceDirs
+     */
+    public String[] splitPublicSourceDirs;
+
+    /**
+     * Maps the dependencies between split APKs. All splits implicitly depend on the base APK.
+     *
+     * Available since platform version O.
+     *
+     * Only populated if the application opts in to isolated split loading via the
+     * {@link android.R.attr.isolatedSplits} attribute in the &lt;manifest&gt; tag of the app's
+     * AndroidManifest.xml.
+     *
+     * The keys and values are all indices into the {@link #splitNames}, {@link #splitSourceDirs},
+     * and {@link #splitPublicSourceDirs} arrays.
+     * Each key represents a split and its value is an array of splits. The first element of this
+     * array is the parent split, and the rest are configuration splits. These configuration splits
+     * have no dependencies themselves.
+     * Cycles do not exist because they are illegal and screened for during installation.
+     *
+     * May be null if no splits are installed, or if no dependencies exist between them.
+     * @hide
+     */
+    public SparseArray<int[]> splitDependencies;
+
+    /**
+     * Full path to a directory assigned to the package for its persistent data.
+     */
+    public String dataDir;
+
+    /** {@hide} */
+    public String deviceProtectedDataDir;
+    /** {@hide} */
+    public String credentialProtectedDataDir;
+
+    /** {@hide} */
+    public String primaryCpuAbi;
+
+    /** {@hide} */
+    public String secondaryCpuAbi;
+
+    /** {@hide} Full path to the directory containing primary ABI native libraries. */
+    public String nativeLibraryDir;
+
+    /** {@hide} Full path to the directory containing secondary ABI native libraries. */
+    public String secondaryNativeLibraryDir;
+
+    /**
+     * Specifies whether or not this instrumentation will handle profiling.
+     */
+    public boolean handleProfiling;
+    
+    /** Specifies whether or not to run this instrumentation as a functional test */
+    public boolean functionalTest;
+
+    public InstrumentationInfo() {
+    }
+
+    public InstrumentationInfo(InstrumentationInfo orig) {
+        super(orig);
+        targetPackage = orig.targetPackage;
+        targetProcesses = orig.targetProcesses;
+        sourceDir = orig.sourceDir;
+        publicSourceDir = orig.publicSourceDir;
+        splitNames = orig.splitNames;
+        splitSourceDirs = orig.splitSourceDirs;
+        splitPublicSourceDirs = orig.splitPublicSourceDirs;
+        splitDependencies = orig.splitDependencies;
+        dataDir = orig.dataDir;
+        deviceProtectedDataDir = orig.deviceProtectedDataDir;
+        credentialProtectedDataDir = orig.credentialProtectedDataDir;
+        primaryCpuAbi = orig.primaryCpuAbi;
+        secondaryCpuAbi = orig.secondaryCpuAbi;
+        nativeLibraryDir = orig.nativeLibraryDir;
+        secondaryNativeLibraryDir = orig.secondaryNativeLibraryDir;
+        handleProfiling = orig.handleProfiling;
+        functionalTest = orig.functionalTest;
+    }
+
+    public String toString() {
+        return "InstrumentationInfo{"
+            + Integer.toHexString(System.identityHashCode(this))
+            + " " + packageName + "}";
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel dest, int parcelableFlags) {
+        super.writeToParcel(dest, parcelableFlags);
+        dest.writeString8(targetPackage);
+        dest.writeString8(targetProcesses);
+        dest.writeString8(sourceDir);
+        dest.writeString8(publicSourceDir);
+        dest.writeString8Array(splitNames);
+        dest.writeString8Array(splitSourceDirs);
+        dest.writeString8Array(splitPublicSourceDirs);
+        dest.writeSparseArray((SparseArray) splitDependencies);
+        dest.writeString8(dataDir);
+        dest.writeString8(deviceProtectedDataDir);
+        dest.writeString8(credentialProtectedDataDir);
+        dest.writeString8(primaryCpuAbi);
+        dest.writeString8(secondaryCpuAbi);
+        dest.writeString8(nativeLibraryDir);
+        dest.writeString8(secondaryNativeLibraryDir);
+        dest.writeInt((handleProfiling == false) ? 0 : 1);
+        dest.writeInt((functionalTest == false) ? 0 : 1);
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<InstrumentationInfo> CREATOR
+            = new Parcelable.Creator<InstrumentationInfo>() {
+        public InstrumentationInfo createFromParcel(Parcel source) {
+            return new InstrumentationInfo(source);
+        }
+        public InstrumentationInfo[] newArray(int size) {
+            return new InstrumentationInfo[size];
+        }
+    };
+
+    @SuppressWarnings("unchecked")
+    private InstrumentationInfo(Parcel source) {
+        super(source);
+        targetPackage = source.readString8();
+        targetProcesses = source.readString8();
+        sourceDir = source.readString8();
+        publicSourceDir = source.readString8();
+        splitNames = source.createString8Array();
+        splitSourceDirs = source.createString8Array();
+        splitPublicSourceDirs = source.createString8Array();
+        splitDependencies = source.readSparseArray(null);
+        dataDir = source.readString8();
+        deviceProtectedDataDir = source.readString8();
+        credentialProtectedDataDir = source.readString8();
+        primaryCpuAbi = source.readString8();
+        secondaryCpuAbi = source.readString8();
+        nativeLibraryDir = source.readString8();
+        secondaryNativeLibraryDir = source.readString8();
+        handleProfiling = source.readInt() != 0;
+        functionalTest = source.readInt() != 0;
+    }
+
+    /** {@hide} */
+    public void copyTo(ApplicationInfo ai) {
+        ai.packageName = packageName;
+        ai.sourceDir = sourceDir;
+        ai.publicSourceDir = publicSourceDir;
+        ai.splitNames = splitNames;
+        ai.splitSourceDirs = splitSourceDirs;
+        ai.splitPublicSourceDirs = splitPublicSourceDirs;
+        ai.splitDependencies = splitDependencies;
+        ai.dataDir = dataDir;
+        ai.deviceProtectedDataDir = deviceProtectedDataDir;
+        ai.credentialProtectedDataDir = credentialProtectedDataDir;
+        ai.primaryCpuAbi = primaryCpuAbi;
+        ai.secondaryCpuAbi = secondaryCpuAbi;
+        ai.nativeLibraryDir = nativeLibraryDir;
+        ai.secondaryNativeLibraryDir = secondaryNativeLibraryDir;
+    }
+}
diff --git a/android/content/pm/IntentFilterVerificationInfo.java b/android/content/pm/IntentFilterVerificationInfo.java
new file mode 100644
index 0000000..67bda2c
--- /dev/null
+++ b/android/content/pm/IntentFilterVerificationInfo.java
@@ -0,0 +1,258 @@
+/*
+ * 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.content.pm;
+
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
+
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+import android.util.ArraySet;
+import android.util.Log;
+
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Set;
+
+/**
+ * The {@link com.android.server.pm.PackageManagerService} maintains some
+ * {@link IntentFilterVerificationInfo}s for each domain / package name.
+ *
+ * @hide
+ */
+@SystemApi
+public final class IntentFilterVerificationInfo implements Parcelable {
+    private static final String TAG = IntentFilterVerificationInfo.class.getName();
+
+    private static final String TAG_DOMAIN = "domain";
+    private static final String ATTR_DOMAIN_NAME = "name";
+    private static final String ATTR_PACKAGE_NAME = "packageName";
+    private static final String ATTR_STATUS = "status";
+
+    private ArraySet<String> mDomains = new ArraySet<>();
+    private String mPackageName;
+    private int mMainStatus;
+
+    /** @hide */
+    public IntentFilterVerificationInfo() {
+        mPackageName = null;
+        mMainStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
+    }
+
+    /** @hide */
+    public IntentFilterVerificationInfo(String packageName, ArraySet<String> domains) {
+        mPackageName = packageName;
+        mDomains = domains;
+        mMainStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
+    }
+
+    /** @hide */
+    public IntentFilterVerificationInfo(XmlPullParser parser)
+            throws IOException, XmlPullParserException {
+        readFromXml(parser);
+    }
+
+    /** @hide */
+    public IntentFilterVerificationInfo(Parcel source) {
+        readFromParcel(source);
+    }
+
+    public String getPackageName() {
+        return mPackageName;
+    }
+
+    public int getStatus() {
+        return mMainStatus;
+    }
+
+    /** @hide */
+    public void setStatus(int s) {
+        if (s >= INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED &&
+                s <= INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) {
+            mMainStatus = s;
+        } else {
+            Log.w(TAG, "Trying to set a non supported status: " + s);
+        }
+    }
+
+    public Set<String> getDomains() {
+        return mDomains;
+    }
+
+    /** @hide */
+    public void setDomains(ArraySet<String> list) {
+        mDomains = list;
+    }
+
+    /** @hide */
+    public String getDomainsString() {
+        StringBuilder sb = new StringBuilder();
+        for (String str : mDomains) {
+            if (sb.length() > 0) {
+                sb.append(" ");
+            }
+            sb.append(str);
+        }
+        return sb.toString();
+    }
+
+    String getStringFromXml(XmlPullParser parser, String attribute, String defaultValue) {
+        String value = parser.getAttributeValue(null, attribute);
+        if (value == null) {
+            String msg = "Missing element under " + TAG +": " + attribute + " at " +
+                    parser.getPositionDescription();
+            Log.w(TAG, msg);
+            return defaultValue;
+        } else {
+            return value;
+        }
+    }
+
+    int getIntFromXml(XmlPullParser parser, String attribute, int defaultValue) {
+        String value = parser.getAttributeValue(null, attribute);
+        if (TextUtils.isEmpty(value)) {
+            String msg = "Missing element under " + TAG +": " + attribute + " at " +
+                    parser.getPositionDescription();
+            Log.w(TAG, msg);
+            return defaultValue;
+        } else {
+            return Integer.parseInt(value);
+        }
+    }
+
+    /** @hide */
+    public void readFromXml(XmlPullParser parser) throws XmlPullParserException,
+            IOException {
+        mPackageName = getStringFromXml(parser, ATTR_PACKAGE_NAME, null);
+        if (mPackageName == null) {
+            Log.e(TAG, "Package name cannot be null!");
+        }
+        int status = getIntFromXml(parser, ATTR_STATUS, -1);
+        if (status == -1) {
+            Log.e(TAG, "Unknown status value: " + status);
+        }
+        mMainStatus = status;
+
+        int outerDepth = parser.getDepth();
+        int type;
+        while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG
+                || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG
+                    || type == XmlPullParser.TEXT) {
+                continue;
+            }
+
+            String tagName = parser.getName();
+            if (tagName.equals(TAG_DOMAIN)) {
+                String name = getStringFromXml(parser, ATTR_DOMAIN_NAME, null);
+                if (!TextUtils.isEmpty(name)) {
+                    mDomains.add(name);
+                }
+            } else {
+                Log.w(TAG, "Unknown tag parsing IntentFilter: " + tagName);
+            }
+            XmlUtils.skipCurrentTag(parser);
+        }
+    }
+
+    /** @hide */
+    public void writeToXml(XmlSerializer serializer) throws IOException {
+        serializer.attribute(null, ATTR_PACKAGE_NAME, mPackageName);
+        serializer.attribute(null, ATTR_STATUS, String.valueOf(mMainStatus));
+        for (String str : mDomains) {
+            serializer.startTag(null, TAG_DOMAIN);
+            serializer.attribute(null, ATTR_DOMAIN_NAME, str);
+            serializer.endTag(null, TAG_DOMAIN);
+        }
+    }
+
+    /** @hide */
+    public String getStatusString() {
+        return getStatusStringFromValue(((long)mMainStatus) << 32);
+    }
+
+    /** @hide */
+    public static String getStatusStringFromValue(long val) {
+        StringBuilder sb = new StringBuilder();
+        switch ((int)(val >> 32)) {
+            case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS:
+                sb.append("always : ");
+                sb.append(Long.toHexString(val & 0x00000000FFFFFFFF));
+                break;
+
+            case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK:
+                sb.append("ask");
+                break;
+
+            case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER:
+                sb.append("never");
+                break;
+
+            case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK:
+                sb.append("always-ask");
+                break;
+
+            case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED:
+            default:
+                sb.append("undefined");
+                break;
+        }
+        return sb.toString();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    private void readFromParcel(Parcel source) {
+        mPackageName = source.readString();
+        mMainStatus = source.readInt();
+        ArrayList<String> list = new ArrayList<>();
+        source.readStringList(list);
+        mDomains.addAll(list);
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mPackageName);
+        dest.writeInt(mMainStatus);
+        dest.writeStringList(new ArrayList<>(mDomains));
+    }
+
+    public static final @android.annotation.NonNull Creator<IntentFilterVerificationInfo> CREATOR =
+            new Creator<IntentFilterVerificationInfo>() {
+                public IntentFilterVerificationInfo createFromParcel(Parcel source) {
+                    return new IntentFilterVerificationInfo(source);
+                }
+                public IntentFilterVerificationInfo[] newArray(int size) {
+                    return new IntentFilterVerificationInfo[size];
+                }
+            };
+}
diff --git a/android/content/pm/KeySet.java b/android/content/pm/KeySet.java
new file mode 100644
index 0000000..5c1d35e
--- /dev/null
+++ b/android/content/pm/KeySet.java
@@ -0,0 +1,109 @@
+/*
+ * 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.content.pm;
+
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Represents a {@code KeySet} that has been declared in the AndroidManifest.xml
+ * file for the application.  A {@code KeySet} can be used explicitly to
+ * represent a trust relationship with other applications on the device.
+ * @hide
+ */
+public class KeySet implements Parcelable {
+
+    private IBinder token;
+
+    /** @hide */
+    public KeySet(IBinder token) {
+        if (token == null) {
+            throw new NullPointerException("null value for KeySet IBinder token");
+        }
+        this.token = token;
+    }
+
+    /** @hide */
+    public IBinder getToken() {
+        return token;
+    }
+
+    /** @hide */
+    @Override
+    public boolean equals(Object o) {
+        if (o instanceof KeySet) {
+            KeySet ks = (KeySet) o;
+            return token == ks.token;
+        }
+        return false;
+    }
+
+    /** @hide */
+    @Override
+    public int hashCode() {
+        return token.hashCode();
+    }
+
+    /**
+     * Implement Parcelable
+     * @hide
+     */
+    public static final @android.annotation.NonNull Parcelable.Creator<KeySet> CREATOR
+            = new Parcelable.Creator<KeySet>() {
+
+        /**
+         * Create a KeySet from a Parcel
+         *
+         * @param in The parcel containing the KeySet
+         */
+        public KeySet createFromParcel(Parcel source) {
+            return readFromParcel(source);
+        }
+
+        /**
+         * Create an array of null KeySets
+         */
+        public KeySet[] newArray(int size) {
+            return new KeySet[size];
+        }
+    };
+
+    /**
+     * @hide
+     */
+    private static KeySet readFromParcel(Parcel in) {
+        IBinder token = in.readStrongBinder();
+        return new KeySet(token);
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeStrongBinder(token);
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+}
\ No newline at end of file
diff --git a/android/content/pm/LabeledIntent.java b/android/content/pm/LabeledIntent.java
new file mode 100644
index 0000000..ae41252
--- /dev/null
+++ b/android/content/pm/LabeledIntent.java
@@ -0,0 +1,193 @@
+/*
+ * 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.content.pm;
+
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.os.Parcel;
+import android.text.TextUtils;
+
+/**
+ * A special subclass of Intent that can have a custom label/icon
+ * associated with it.  Primarily for use with {@link Intent#ACTION_CHOOSER}.
+ */
+public class LabeledIntent extends Intent {
+    private String mSourcePackage;
+    private int mLabelRes;
+    private CharSequence mNonLocalizedLabel;
+    private int mIcon;
+    
+    /**
+     * Create a labeled intent from the given intent, supplying the label
+     * and icon resources for it.
+     * 
+     * @param origIntent The original Intent to copy.
+     * @param sourcePackage The package in which the label and icon live.
+     * @param labelRes Resource containing the label, or 0 if none.
+     * @param icon Resource containing the icon, or 0 if none.
+     */
+    public LabeledIntent(Intent origIntent, String sourcePackage,
+            int labelRes, int icon) {
+        super(origIntent);
+        mSourcePackage = sourcePackage;
+        mLabelRes = labelRes;
+        mNonLocalizedLabel = null;
+        mIcon = icon;
+    }
+    
+    /**
+     * Create a labeled intent from the given intent, supplying a textual
+     * label and icon resource for it.
+     * 
+     * @param origIntent The original Intent to copy.
+     * @param sourcePackage The package in which the label and icon live.
+     * @param nonLocalizedLabel Concrete text to use for the label.
+     * @param icon Resource containing the icon, or 0 if none.
+     */
+    public LabeledIntent(Intent origIntent, String sourcePackage,
+            CharSequence nonLocalizedLabel, int icon) {
+        super(origIntent);
+        mSourcePackage = sourcePackage;
+        mLabelRes = 0;
+        mNonLocalizedLabel = nonLocalizedLabel;
+        mIcon = icon;
+    }
+    
+    /**
+     * Create a labeled intent with no intent data but supplying the label
+     * and icon resources for it.
+     * 
+     * @param sourcePackage The package in which the label and icon live.
+     * @param labelRes Resource containing the label, or 0 if none.
+     * @param icon Resource containing the icon, or 0 if none.
+     */
+    public LabeledIntent(String sourcePackage, int labelRes, int icon) {
+        mSourcePackage = sourcePackage;
+        mLabelRes = labelRes;
+        mNonLocalizedLabel = null;
+        mIcon = icon;
+    }
+    
+    /**
+     * Create a labeled intent with no intent data but supplying a textual
+     * label and icon resource for it.
+     * 
+     * @param sourcePackage The package in which the label and icon live.
+     * @param nonLocalizedLabel Concrete text to use for the label.
+     * @param icon Resource containing the icon, or 0 if none.
+     */
+    public LabeledIntent(String sourcePackage,
+            CharSequence nonLocalizedLabel, int icon) {
+        mSourcePackage = sourcePackage;
+        mLabelRes = 0;
+        mNonLocalizedLabel = nonLocalizedLabel;
+        mIcon = icon;
+    }
+    
+    /**
+     * Return the name of the package holding label and icon resources.
+     */
+    public String getSourcePackage() {
+        return mSourcePackage;
+    }
+    
+    /**
+     * Return any resource identifier that has been given for the label text.
+     */
+    public int getLabelResource() {
+        return mLabelRes;
+    }
+    
+    /**
+     * Return any concrete text that has been given for the label text.
+     */
+    public CharSequence getNonLocalizedLabel() {
+        return mNonLocalizedLabel;
+    }
+    
+    /**
+     * Return any resource identifier that has been given for the label icon.
+     */
+    public int getIconResource() {
+        return mIcon;
+    }
+    
+    /**
+     * Retrieve the label associated with this object.  If the object does
+     * not have a label, null will be returned, in which case you will probably
+     * want to load the label from the underlying resolved info for the Intent.
+     */
+    public CharSequence loadLabel(PackageManager pm) {
+        if (mNonLocalizedLabel != null) {
+            return mNonLocalizedLabel;
+        }
+        if (mLabelRes != 0 && mSourcePackage != null) {
+            CharSequence label = pm.getText(mSourcePackage, mLabelRes, null);
+            if (label != null) {
+                return label;
+            }
+        }
+        return null;
+    }
+    
+    /**
+     * Retrieve the icon associated with this object.  If the object does
+     * not have a icon, null will be returned, in which case you will probably
+     * want to load the icon from the underlying resolved info for the Intent.
+     */
+    public Drawable loadIcon(PackageManager pm) {
+        if (mIcon != 0 && mSourcePackage != null) {
+            Drawable icon = pm.getDrawable(mSourcePackage, mIcon, null);
+            if (icon != null) {
+                return icon;
+            }
+        }
+        return null;
+    }
+    
+    public void writeToParcel(Parcel dest, int parcelableFlags) {
+        super.writeToParcel(dest, parcelableFlags);
+        dest.writeString(mSourcePackage);
+        dest.writeInt(mLabelRes);
+        TextUtils.writeToParcel(mNonLocalizedLabel, dest, parcelableFlags);
+        dest.writeInt(mIcon);
+    }
+
+    /** @hide */
+    protected LabeledIntent(Parcel in) {
+        readFromParcel(in);
+    }
+    
+    public void readFromParcel(Parcel in) {
+        super.readFromParcel(in);
+        mSourcePackage = in.readString();
+        mLabelRes = in.readInt();
+        mNonLocalizedLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+        mIcon = in.readInt();
+    }
+    
+    public static final @android.annotation.NonNull Creator<LabeledIntent> CREATOR
+            = new Creator<LabeledIntent>() {
+        public LabeledIntent createFromParcel(Parcel source) {
+            return new LabeledIntent(source);
+        }
+        public LabeledIntent[] newArray(int size) {
+            return new LabeledIntent[size];
+        }
+    };
+
+}
diff --git a/android/content/pm/LauncherActivityInfo.java b/android/content/pm/LauncherActivityInfo.java
new file mode 100644
index 0000000..67deb82
--- /dev/null
+++ b/android/content/pm/LauncherActivityInfo.java
@@ -0,0 +1,178 @@
+/*
+ * 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.content.pm;
+
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.DisplayMetrics;
+
+/**
+ * A representation of an activity that can belong to this user or a managed
+ * profile associated with this user. It can be used to query the label, icon
+ * and badged icon for the activity.
+ */
+public class LauncherActivityInfo {
+    private static final String TAG = "LauncherActivityInfo";
+
+    private final PackageManager mPm;
+
+    @UnsupportedAppUsage
+    private ActivityInfo mActivityInfo;
+    private ComponentName mComponentName;
+    private UserHandle mUser;
+
+    /**
+     * Create a launchable activity object for a given ResolveInfo and user.
+     *
+     * @param context The context for fetching resources.
+     * @param info ResolveInfo from which to create the LauncherActivityInfo.
+     * @param user The UserHandle of the profile to which this activity belongs.
+     */
+    LauncherActivityInfo(Context context, ActivityInfo info, UserHandle user) {
+        this(context);
+        mActivityInfo = info;
+        mComponentName =  new ComponentName(info.packageName, info.name);
+        mUser = user;
+    }
+
+    LauncherActivityInfo(Context context) {
+        mPm = context.getPackageManager();
+    }
+
+    /**
+     * Returns the component name of this activity.
+     *
+     * @return ComponentName of the activity
+     */
+    public ComponentName getComponentName() {
+        return mComponentName;
+    }
+
+    /**
+     * Returns the user handle of the user profile that this activity belongs to. In order to
+     * persist the identity of the profile, do not store the UserHandle. Instead retrieve its
+     * serial number from UserManager. You can convert the serial number back to a UserHandle
+     * for later use.
+     *
+     * @see UserManager#getSerialNumberForUser(UserHandle)
+     * @see UserManager#getUserForSerialNumber(long)
+     *
+     * @return The UserHandle of the profile.
+     */
+    public UserHandle getUser() {
+        return mUser;
+    }
+
+    /**
+     * Retrieves the label for the activity.
+     *
+     * @return The label for the activity.
+     */
+    public CharSequence getLabel() {
+        // TODO: Go through LauncherAppsService
+        return mActivityInfo.loadLabel(mPm);
+    }
+
+    /**
+     * Returns the icon for this activity, without any badging for the profile.
+     * @param density The preferred density of the icon, zero for default density. Use
+     * density DPI values from {@link DisplayMetrics}.
+     * @see #getBadgedIcon(int)
+     * @see DisplayMetrics
+     * @return The drawable associated with the activity.
+     */
+    public Drawable getIcon(int density) {
+        // TODO: Go through LauncherAppsService
+        final int iconRes = mActivityInfo.getIconResource();
+        Drawable icon = null;
+        // Get the preferred density icon from the app's resources
+        if (density != 0 && iconRes != 0) {
+            try {
+                final Resources resources
+                        = mPm.getResourcesForApplication(mActivityInfo.applicationInfo);
+                icon = resources.getDrawableForDensity(iconRes, density);
+            } catch (NameNotFoundException | Resources.NotFoundException exc) {
+            }
+        }
+        // Get the default density icon
+        if (icon == null) {
+            icon = mActivityInfo.loadIcon(mPm);
+        }
+        return icon;
+    }
+
+    /**
+     * Returns the application flags from the ApplicationInfo of the activity.
+     *
+     * @return Application flags
+     * @hide remove before shipping
+     */
+    public int getApplicationFlags() {
+        return mActivityInfo.applicationInfo.flags;
+    }
+
+    /**
+     * Returns the application info for the appliction this activity belongs to.
+     * @return
+     */
+    public ApplicationInfo getApplicationInfo() {
+        return mActivityInfo.applicationInfo;
+    }
+
+    /**
+     * Returns the time at which the package was first installed.
+     *
+     * @return The time of installation of the package, in milliseconds.
+     */
+    public long getFirstInstallTime() {
+        try {
+            // TODO: Go through LauncherAppsService
+            return mPm.getPackageInfo(mActivityInfo.packageName,
+                    PackageManager.MATCH_UNINSTALLED_PACKAGES).firstInstallTime;
+        } catch (NameNotFoundException nnfe) {
+            // Sorry, can't find package
+            return 0;
+        }
+    }
+
+    /**
+     * Returns the name for the acitivty from  android:name in the manifest.
+     * @return the name from android:name for the acitivity.
+     */
+    public String getName() {
+        return mActivityInfo.name;
+    }
+
+    /**
+     * Returns the activity icon with badging appropriate for the profile.
+     * @param density Optional density for the icon, or 0 to use the default density. Use
+     * {@link DisplayMetrics} for DPI values.
+     * @see DisplayMetrics
+     * @return A badged icon for the activity.
+     */
+    public Drawable getBadgedIcon(int density) {
+        Drawable originalIcon = getIcon(density);
+
+        return mPm.getUserBadgedIcon(originalIcon, mUser);
+    }
+}
diff --git a/android/content/pm/LauncherApps.java b/android/content/pm/LauncherApps.java
new file mode 100644
index 0000000..bbcac56
--- /dev/null
+++ b/android/content/pm/LauncherApps.java
@@ -0,0 +1,2141 @@
+/*
+ * 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.content.pm;
+
+import static android.Manifest.permission;
+
+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.SdkConstant.SdkConstantType;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.annotation.TestApi;
+import android.app.PendingIntent;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProviderInfo;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.content.LocusId;
+import android.content.pm.PackageInstaller.SessionCallback;
+import android.content.pm.PackageInstaller.SessionCallbackDelegate;
+import android.content.pm.PackageInstaller.SessionInfo;
+import android.content.pm.PackageManager.ApplicationInfoFlags;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Rect;
+import android.graphics.drawable.AdaptiveIconDrawable;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.util.Pair;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.function.pooled.PooledLambda;
+
+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.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/**
+ * Class for retrieving a list of launchable activities for the current user and any associated
+ * managed profiles that are visible to the current user, which can be retrieved with
+ * {@link #getProfiles}. This is mainly for use by launchers.
+ *
+ * Apps can be queried for each user profile.
+ * Since the PackageManager will not deliver package broadcasts for other profiles, you can register
+ * for package changes here.
+ * <p>
+ * To watch for managed profiles being added or removed, register for the following broadcasts:
+ * {@link Intent#ACTION_MANAGED_PROFILE_ADDED} and {@link Intent#ACTION_MANAGED_PROFILE_REMOVED}.
+ * <p>
+ * Note as of Android O, apps on a managed profile are no longer allowed to access apps on the
+ * main profile.  Apps can only access profiles returned by {@link #getProfiles()}.
+ */
+@SystemService(Context.LAUNCHER_APPS_SERVICE)
+public class LauncherApps {
+
+    static final String TAG = "LauncherApps";
+    static final boolean DEBUG = false;
+
+    /**
+     * Activity Action: For the default launcher to show the confirmation dialog to create
+     * a pinned shortcut.
+     *
+     * <p>See the {@link ShortcutManager} javadoc for details.
+     *
+     * <p>
+     * Use {@link #getPinItemRequest(Intent)} to get a {@link PinItemRequest} object,
+     * and call {@link PinItemRequest#accept(Bundle)}
+     * if the user accepts.  If the user doesn't accept, no further action is required.
+     *
+     * @see #EXTRA_PIN_ITEM_REQUEST
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_CONFIRM_PIN_SHORTCUT =
+            "android.content.pm.action.CONFIRM_PIN_SHORTCUT";
+
+    /**
+     * Activity Action: For the default launcher to show the confirmation dialog to create
+     * a pinned app widget.
+     *
+     * <p>See the {@link android.appwidget.AppWidgetManager#requestPinAppWidget} javadoc for
+     * details.
+     *
+     * <p>
+     * Use {@link #getPinItemRequest(Intent)} to get a {@link PinItemRequest} object,
+     * and call {@link PinItemRequest#accept(Bundle)}
+     * if the user accepts.  If the user doesn't accept, no further action is required.
+     *
+     * @see #EXTRA_PIN_ITEM_REQUEST
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_CONFIRM_PIN_APPWIDGET =
+            "android.content.pm.action.CONFIRM_PIN_APPWIDGET";
+
+    /**
+     * An extra for {@link #ACTION_CONFIRM_PIN_SHORTCUT} &amp; {@link #ACTION_CONFIRM_PIN_APPWIDGET}
+     * containing a {@link PinItemRequest} of appropriate type asked to pin.
+     *
+     * <p>A helper function {@link #getPinItemRequest(Intent)} can be used
+     * instead of using this constant directly.
+     *
+     * @see #ACTION_CONFIRM_PIN_SHORTCUT
+     * @see #ACTION_CONFIRM_PIN_APPWIDGET
+     */
+    public static final String EXTRA_PIN_ITEM_REQUEST =
+            "android.content.pm.extra.PIN_ITEM_REQUEST";
+
+    private final Context mContext;
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    private final ILauncherApps mService;
+    @UnsupportedAppUsage
+    private final PackageManager mPm;
+    private final UserManager mUserManager;
+
+    private final List<CallbackMessageHandler> mCallbacks = new ArrayList<>();
+    private final List<SessionCallbackDelegate> mDelegates = new ArrayList<>();
+
+    private final Map<ShortcutChangeCallback, Pair<Executor, IShortcutChangeCallback>>
+            mShortcutChangeCallbacks = new HashMap<>();
+
+    /**
+     * Callbacks for package changes to this and related managed profiles.
+     */
+    public static abstract class Callback {
+        /**
+         * Indicates that a package was removed from the specified profile.
+         *
+         * If a package is removed while being updated onPackageChanged will be
+         * called instead.
+         *
+         * @param packageName The name of the package that was removed.
+         * @param user The UserHandle of the profile that generated the change.
+         */
+        abstract public void onPackageRemoved(String packageName, UserHandle user);
+
+        /**
+         * Indicates that a package was added to the specified profile.
+         *
+         * If a package is added while being updated then onPackageChanged will be
+         * called instead.
+         *
+         * @param packageName The name of the package that was added.
+         * @param user The UserHandle of the profile that generated the change.
+         */
+        abstract public void onPackageAdded(String packageName, UserHandle user);
+
+        /**
+         * Indicates that a package was modified in the specified profile.
+         * This can happen, for example, when the package is updated or when
+         * one or more components are enabled or disabled.
+         *
+         * @param packageName The name of the package that has changed.
+         * @param user The UserHandle of the profile that generated the change.
+         */
+        abstract public void onPackageChanged(String packageName, UserHandle user);
+
+        /**
+         * Indicates that one or more packages have become available. For
+         * example, this can happen when a removable storage card has
+         * reappeared.
+         *
+         * @param packageNames The names of the packages that have become
+         *            available.
+         * @param user The UserHandle of the profile that generated the change.
+         * @param replacing Indicates whether these packages are replacing
+         *            existing ones.
+         */
+        abstract public void onPackagesAvailable(String[] packageNames, UserHandle user,
+                boolean replacing);
+
+        /**
+         * Indicates that one or more packages have become unavailable. For
+         * example, this can happen when a removable storage card has been
+         * removed.
+         *
+         * @param packageNames The names of the packages that have become
+         *            unavailable.
+         * @param user The UserHandle of the profile that generated the change.
+         * @param replacing Indicates whether the packages are about to be
+         *            replaced with new versions.
+         */
+        abstract public void onPackagesUnavailable(String[] packageNames, UserHandle user,
+                boolean replacing);
+
+        /**
+         * Indicates that one or more packages have been suspended. For
+         * example, this can happen when a Device Administrator suspends
+         * an applicaton.
+         *
+         * <p>Note: On devices running {@link android.os.Build.VERSION_CODES#P Android P} or higher,
+         * any apps that override {@link #onPackagesSuspended(String[], UserHandle, Bundle)} will
+         * not receive this callback.
+         *
+         * @param packageNames The names of the packages that have just been
+         *            suspended.
+         * @param user The UserHandle of the profile that generated the change.
+         */
+        public void onPackagesSuspended(String[] packageNames, UserHandle user) {
+        }
+
+        /**
+         * Indicates that one or more packages have been suspended. A device administrator or an app
+         * with {@code android.permission.SUSPEND_APPS} can do this.
+         *
+         * <p>A suspending app with the permission {@code android.permission.SUSPEND_APPS} can
+         * optionally provide a {@link Bundle} of extra information that it deems helpful for the
+         * launcher to handle the suspended state of these packages. The contents of this
+         * {@link Bundle} are supposed to be a contract between the suspending app and the launcher.
+         *
+         * @param packageNames The names of the packages that have just been suspended.
+         * @param user the user for which the given packages were suspended.
+         * @param launcherExtras A {@link Bundle} of extras for the launcher, if provided to the
+         *                      system, {@code null} otherwise.
+         * @see PackageManager#isPackageSuspended()
+         * @see #getSuspendedPackageLauncherExtras(String, UserHandle)
+         * @deprecated {@code launcherExtras} should be obtained by using
+         * {@link #getSuspendedPackageLauncherExtras(String, UserHandle)}. For all other cases,
+         * {@link #onPackagesSuspended(String[], UserHandle)} should be used.
+         */
+        @Deprecated
+        public void onPackagesSuspended(String[] packageNames, UserHandle user,
+                @Nullable Bundle launcherExtras) {
+            onPackagesSuspended(packageNames, user);
+        }
+
+        /**
+         * Indicates that one or more packages have been unsuspended. For
+         * example, this can happen when a Device Administrator unsuspends
+         * an applicaton.
+         *
+         * @param packageNames The names of the packages that have just been
+         *            unsuspended.
+         * @param user The UserHandle of the profile that generated the change.
+         */
+        public void onPackagesUnsuspended(String[] packageNames, UserHandle user) {
+        }
+
+        /**
+         * Indicates that one or more shortcuts of any kind (dynamic, pinned, or manifest)
+         * have been added, updated or removed.
+         *
+         * <p>Only the applications that are allowed to access the shortcut information,
+         * as defined in {@link #hasShortcutHostPermission()}, will receive it.
+         *
+         * @param packageName The name of the package that has the shortcuts.
+         * @param shortcuts All shortcuts from the package (dynamic, manifest and/or pinned).
+         *    Only "key" information will be provided, as defined in
+         *    {@link ShortcutInfo#hasKeyFieldsOnly()}.
+         * @param user The UserHandle of the profile that generated the change.
+         *
+         * @see ShortcutManager
+         */
+        public void onShortcutsChanged(@NonNull String packageName,
+                @NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user) {
+        }
+    }
+
+    /**
+     * Represents a query passed to {@link #getShortcuts(ShortcutQuery, UserHandle)}.
+     */
+    public static class ShortcutQuery {
+        /**
+         * Include dynamic shortcuts in the result.
+         */
+        public static final int FLAG_MATCH_DYNAMIC = 1 << 0;
+
+        /** @hide kept for unit tests */
+        @Deprecated
+        public static final int FLAG_GET_DYNAMIC = FLAG_MATCH_DYNAMIC;
+
+        /**
+         * Include pinned shortcuts in the result.
+         *
+         * <p>If you are the selected assistant app, and wishes to fetch all shortcuts that the
+         * user owns on the launcher (or by other launchers, in case the user has multiple), use
+         * {@link #FLAG_MATCH_PINNED_BY_ANY_LAUNCHER} instead.
+         *
+         * <p>If you're a regular launcher app, there's no way to get shortcuts pinned by other
+         * launchers, and {@link #FLAG_MATCH_PINNED_BY_ANY_LAUNCHER} will be ignored. So use this
+         * flag to get own pinned shortcuts.
+         */
+        public static final int FLAG_MATCH_PINNED = 1 << 1;
+
+        /** @hide kept for unit tests */
+        @Deprecated
+        public static final int FLAG_GET_PINNED = FLAG_MATCH_PINNED;
+
+        /**
+         * Include manifest shortcuts in the result.
+         */
+        public static final int FLAG_MATCH_MANIFEST = 1 << 3;
+
+        /**
+         * Include cached shortcuts in the result.
+         */
+        public static final int FLAG_MATCH_CACHED = 1 << 4;
+
+        /** @hide kept for unit tests */
+        @Deprecated
+        public static final int FLAG_GET_MANIFEST = FLAG_MATCH_MANIFEST;
+
+        /**
+         * Include all pinned shortcuts by any launchers, not just by the caller,
+         * in the result.
+         *
+         * <p>The caller must be the selected assistant app to use this flag, or have the system
+         * {@code ACCESS_SHORTCUTS} permission.
+         *
+         * <p>If you are the selected assistant app, and wishes to fetch all shortcuts that the
+         * user owns on the launcher (or by other launchers, in case the user has multiple), use
+         * {@link #FLAG_MATCH_PINNED_BY_ANY_LAUNCHER} instead.
+         *
+         * <p>If you're a regular launcher app (or any app that's not the selected assistant app)
+         * then this flag will be ignored.
+         */
+        public static final int FLAG_MATCH_PINNED_BY_ANY_LAUNCHER = 1 << 10;
+
+        /**
+         * FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED | FLAG_MATCH_MANIFEST | FLAG_MATCH_CACHED
+         * @hide
+         */
+        public static final int FLAG_MATCH_ALL_KINDS =
+                FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED | FLAG_MATCH_MANIFEST | FLAG_MATCH_CACHED;
+
+        /**
+         * FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED | FLAG_MATCH_MANIFEST | FLAG_MATCH_ALL_PINNED
+         * @hide
+         */
+        public static final int FLAG_MATCH_ALL_KINDS_WITH_ALL_PINNED =
+                FLAG_MATCH_ALL_KINDS | FLAG_MATCH_PINNED_BY_ANY_LAUNCHER;
+
+        /** @hide kept for unit tests */
+        @Deprecated
+        public static final int FLAG_GET_ALL_KINDS = FLAG_MATCH_ALL_KINDS;
+
+        /**
+         * Requests "key" fields only.  See {@link ShortcutInfo#hasKeyFieldsOnly()}'s javadoc to
+         * see which fields fields "key".
+         * This allows quicker access to shortcut information in order to
+         * determine whether the caller's in-memory cache needs to be updated.
+         *
+         * <p>Typically, launcher applications cache all or most shortcut information
+         * in memory in order to show shortcuts without a delay.
+         *
+         * When a given launcher application wants to update its cache, such as when its process
+         * restarts, it can fetch shortcut information with this flag.
+         * The application can then check {@link ShortcutInfo#getLastChangedTimestamp()} for each
+         * shortcut, fetching a shortcut's non-key information only if that shortcut has been
+         * updated.
+         *
+         * @see ShortcutManager
+         */
+        public static final int FLAG_GET_KEY_FIELDS_ONLY = 1 << 2;
+
+        /** @hide */
+        @IntDef(flag = true, prefix = { "FLAG_" }, value = {
+                FLAG_MATCH_DYNAMIC,
+                FLAG_MATCH_PINNED,
+                FLAG_MATCH_MANIFEST,
+                FLAG_MATCH_CACHED,
+                FLAG_GET_KEY_FIELDS_ONLY,
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface QueryFlags {}
+
+        long mChangedSince;
+
+        @Nullable
+        String mPackage;
+
+        @Nullable
+        List<String> mShortcutIds;
+
+        @Nullable
+        List<LocusId> mLocusIds;
+
+        @Nullable
+        ComponentName mActivity;
+
+        @QueryFlags
+        int mQueryFlags;
+
+        public ShortcutQuery() {
+        }
+
+        /**
+         * If non-zero, returns only shortcuts that have been added or updated
+         * since the given timestamp, expressed in milliseconds since the Epoch&mdash;see
+         * {@link System#currentTimeMillis()}.
+         */
+        public ShortcutQuery setChangedSince(long changedSince) {
+            mChangedSince = changedSince;
+            return this;
+        }
+
+        /**
+         * If non-null, returns only shortcuts from the package.
+         */
+        public ShortcutQuery setPackage(@Nullable String packageName) {
+            mPackage = packageName;
+            return this;
+        }
+
+        /**
+         * If non-null, return only the specified shortcuts by ID.  When setting this field,
+         * a package name must also be set with {@link #setPackage}.
+         */
+        public ShortcutQuery setShortcutIds(@Nullable List<String> shortcutIds) {
+            mShortcutIds = shortcutIds;
+            return this;
+        }
+
+        /**
+         * If non-null, return only the specified shortcuts by locus ID.  When setting this field,
+         * a package name must also be set with {@link #setPackage}.
+         */
+        @NonNull
+        public ShortcutQuery setLocusIds(@Nullable List<LocusId> locusIds) {
+            mLocusIds = locusIds;
+            return this;
+        }
+
+        /**
+         * If non-null, returns only shortcuts associated with the activity; i.e.
+         * {@link ShortcutInfo}s whose {@link ShortcutInfo#getActivity()} are equal
+         * to {@code activity}.
+         */
+        public ShortcutQuery setActivity(@Nullable ComponentName activity) {
+            mActivity = activity;
+            return this;
+        }
+
+        /**
+         * Set query options.  At least one of the {@code MATCH} flags should be set.  Otherwise,
+         * no shortcuts will be returned.
+         *
+         * <ul>
+         *     <li>{@link #FLAG_MATCH_DYNAMIC}
+         *     <li>{@link #FLAG_MATCH_PINNED}
+         *     <li>{@link #FLAG_MATCH_MANIFEST}
+         *     <li>{@link #FLAG_MATCH_CACHED}
+         *     <li>{@link #FLAG_GET_KEY_FIELDS_ONLY}
+         * </ul>
+         */
+        public ShortcutQuery setQueryFlags(@QueryFlags int queryFlags) {
+            mQueryFlags = queryFlags;
+            return this;
+        }
+    }
+
+    /**
+     * Callbacks for shortcut changes to this and related managed profiles.
+     *
+     * @hide
+     */
+    public interface ShortcutChangeCallback {
+        /**
+         * Indicates that one or more shortcuts, that match the {@link ShortcutQuery} used to
+         * register this callback, have been added or updated.
+         * @see LauncherApps#registerShortcutChangeCallback(ShortcutChangeCallback, ShortcutQuery,
+         * Executor)
+         *
+         * <p>Only the applications that are allowed to access the shortcut information,
+         * as defined in {@link #hasShortcutHostPermission()}, will receive it.
+         *
+         * @param packageName The name of the package that has the shortcuts.
+         * @param shortcuts Shortcuts from the package that have updated or added. Only "key"
+         *    information will be provided, as defined in {@link ShortcutInfo#hasKeyFieldsOnly()}.
+         * @param user The UserHandle of the profile that generated the change.
+         *
+         * @see ShortcutManager
+         */
+        default void onShortcutsAddedOrUpdated(@NonNull String packageName,
+                @NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user) {}
+
+        /**
+         * Indicates that one or more shortcuts, that match the {@link ShortcutQuery} used to
+         * register this callback, have been removed.
+         * @see LauncherApps#registerShortcutChangeCallback(ShortcutChangeCallback, ShortcutQuery,
+         * Executor)
+         *
+         * <p>Only the applications that are allowed to access the shortcut information,
+         * as defined in {@link #hasShortcutHostPermission()}, will receive it.
+         *
+         * @param packageName The name of the package that has the shortcuts.
+         * @param shortcuts Shortcuts from the package that have been removed. Only "key"
+         *    information will be provided, as defined in {@link ShortcutInfo#hasKeyFieldsOnly()}.
+         * @param user The UserHandle of the profile that generated the change.
+         *
+         * @see ShortcutManager
+         */
+        default void onShortcutsRemoved(@NonNull String packageName,
+                @NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user) {}
+    }
+
+    /**
+     * Callback proxy class for {@link ShortcutChangeCallback}
+     *
+     * @hide
+     */
+    private static class ShortcutChangeCallbackProxy extends
+            android.content.pm.IShortcutChangeCallback.Stub {
+        private final WeakReference<Pair<Executor, ShortcutChangeCallback>> mRemoteReferences;
+
+        ShortcutChangeCallbackProxy(Executor executor, ShortcutChangeCallback callback) {
+            mRemoteReferences = new WeakReference<>(new Pair<>(executor, callback));
+        }
+
+        @Override
+        public void onShortcutsAddedOrUpdated(@NonNull String packageName,
+                @NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user) {
+            Pair<Executor, ShortcutChangeCallback> remoteReferences = mRemoteReferences.get();
+            if (remoteReferences == null) {
+                // Binder is dead.
+                return;
+            }
+
+            final Executor executor = remoteReferences.first;
+            final ShortcutChangeCallback callback = remoteReferences.second;
+            executor.execute(
+                    PooledLambda.obtainRunnable(ShortcutChangeCallback::onShortcutsAddedOrUpdated,
+                            callback, packageName, shortcuts, user).recycleOnUse());
+        }
+
+        @Override
+        public void onShortcutsRemoved(@NonNull String packageName,
+                @NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user) {
+            Pair<Executor, ShortcutChangeCallback> remoteReferences = mRemoteReferences.get();
+            if (remoteReferences == null) {
+                // Binder is dead.
+                return;
+            }
+
+            final Executor executor = remoteReferences.first;
+            final ShortcutChangeCallback callback = remoteReferences.second;
+            executor.execute(
+                    PooledLambda.obtainRunnable(ShortcutChangeCallback::onShortcutsRemoved,
+                            callback, packageName, shortcuts, user).recycleOnUse());
+        }
+    }
+
+    /** @hide */
+    public LauncherApps(Context context, ILauncherApps service) {
+        mContext = context;
+        mService = service;
+        mPm = context.getPackageManager();
+        mUserManager = context.getSystemService(UserManager.class);
+    }
+
+    /** @hide */
+    @TestApi
+    public LauncherApps(Context context) {
+        this(context, ILauncherApps.Stub.asInterface(
+                ServiceManager.getService(Context.LAUNCHER_APPS_SERVICE)));
+    }
+
+    /**
+     * Show an error log on logcat, when the calling user is a managed profile, the target
+     * user is different from the calling user, and it is not called from a package that has the
+     * {@link permission.INTERACT_ACROSS_USERS_FULL} permission, in order to help
+     * developers to detect it.
+     */
+    private void logErrorForInvalidProfileAccess(@NonNull UserHandle target) {
+        if (UserHandle.myUserId() != target.getIdentifier() && mUserManager.isManagedProfile()
+                    && mContext.checkSelfPermission(permission.INTERACT_ACROSS_USERS_FULL)
+                            != PackageManager.PERMISSION_GRANTED) {
+            Log.w(TAG, "Accessing other profiles/users from managed profile is no longer allowed.");
+        }
+    }
+
+    /**
+     * Return a list of profiles that the caller can access via the {@link LauncherApps} APIs.
+     *
+     * <p>If the caller is running on a managed profile, it'll return only the current profile.
+     * Otherwise it'll return the same list as {@link UserManager#getUserProfiles()} would.
+     */
+    public List<UserHandle> getProfiles() {
+        if (mUserManager.isManagedProfile()) {
+            // If it's a managed profile, only return the current profile.
+            final List result =  new ArrayList(1);
+            result.add(android.os.Process.myUserHandle());
+            return result;
+        } else {
+            return mUserManager.getUserProfiles();
+        }
+    }
+
+    /**
+     * Retrieves a list of activities that specify {@link Intent#ACTION_MAIN} and
+     * {@link Intent#CATEGORY_LAUNCHER}, across all apps, for a specified user. If an app doesn't
+     * have any activities that specify <code>ACTION_MAIN</code> or <code>CATEGORY_LAUNCHER</code>,
+     * the system adds a synthesized activity to the list. This synthesized activity represents the
+     * app's details page within system settings.
+     *
+     * <p class="note"><b>Note: </b>It's possible for system apps, such as app stores, to prevent
+     * the system from adding synthesized activities to the returned list.</p>
+     *
+     * <p>As of <a href="/reference/android/os/Build.VERSION_CODES.html#Q">Android Q</a>, at least
+     * one of the app's activities or synthesized activities appears in the returned list unless the
+     * app satisfies at least one of the following conditions:</p>
+     * <ul>
+     * <li>The app is a system app.</li>
+     * <li>The app doesn't request any <a href="/guide/topics/permissions/overview">permissions</a>.
+     * </li>
+     * <li>The app doesn't have a <em>launcher activity</em> that is enabled by default. A launcher
+     * activity has an intent containing the <code>ACTION_MAIN</code> action and the
+     * <code>CATEGORY_LAUNCHER</code> category.</li>
+     * </ul>
+     *
+     * <p>Additionally, the system hides synthesized activities for some or all apps in the
+     * following enterprise-related cases:</p>
+     * <ul>
+     * <li>If the device is a
+     * <a href="https://developers.google.com/android/work/overview#company-owned-devices-for-knowledge-workers">fully
+     * managed device</a>, no synthesized activities for any app appear in the returned list.</li>
+     * <li>If the current user has a
+     * <a href="https://developers.google.com/android/work/overview#employee-owned-devices-byod">work
+     * profile</a>, no synthesized activities for the user's work apps appear in the returned
+     * list.</li>
+     * </ul>
+     *
+     * @param packageName The specific package to query. If null, it checks all installed packages
+     *            in the profile.
+     * @param user The UserHandle of the profile.
+     * @return List of launchable activities. Can be an empty list but will not be null.
+     */
+    public List<LauncherActivityInfo> getActivityList(String packageName, UserHandle user) {
+        logErrorForInvalidProfileAccess(user);
+        try {
+            return convertToActivityList(mService.getLauncherActivities(mContext.getPackageName(),
+                    packageName, user), user);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the activity info for a given intent and user handle, if it resolves. Otherwise it
+     * returns null.
+     *
+     * @param intent The intent to find a match for.
+     * @param user The profile to look in for a match.
+     * @return An activity info object if there is a match.
+     */
+    public LauncherActivityInfo resolveActivity(Intent intent, UserHandle user) {
+        logErrorForInvalidProfileAccess(user);
+        try {
+            ActivityInfo ai = mService.resolveActivity(mContext.getPackageName(),
+                    intent.getComponent(), user);
+            if (ai != null) {
+                LauncherActivityInfo info = new LauncherActivityInfo(mContext, ai, user);
+                return info;
+            }
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+        return null;
+    }
+
+    /**
+     * Starts a Main activity in the specified profile.
+     *
+     * @param component The ComponentName of the activity to launch
+     * @param user The UserHandle of the profile
+     * @param sourceBounds The Rect containing the source bounds of the clicked icon
+     * @param opts Options to pass to startActivity
+     */
+    public void startMainActivity(ComponentName component, UserHandle user, Rect sourceBounds,
+            Bundle opts) {
+        logErrorForInvalidProfileAccess(user);
+        if (DEBUG) {
+            Log.i(TAG, "StartMainActivity " + component + " " + user.getIdentifier());
+        }
+        try {
+            mService.startActivityAsUser(mContext.getIApplicationThread(),
+                    mContext.getPackageName(), mContext.getAttributionTag(),
+                    component, sourceBounds, opts, user);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Starts an activity to show the details of the specified session.
+     *
+     * @param sessionInfo The SessionInfo of the session
+     * @param sourceBounds The Rect containing the source bounds of the clicked icon
+     * @param opts Options to pass to startActivity
+     */
+    public void startPackageInstallerSessionDetailsActivity(@NonNull SessionInfo sessionInfo,
+            @Nullable Rect sourceBounds, @Nullable Bundle opts) {
+        try {
+            mService.startSessionDetailsActivityAsUser(mContext.getIApplicationThread(),
+                    mContext.getPackageName(), mContext.getAttributionTag(), sessionInfo,
+                    sourceBounds, opts, sessionInfo.getUser());
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Starts the settings activity to show the application details for a
+     * package in the specified profile.
+     *
+     * @param component The ComponentName of the package to launch settings for.
+     * @param user The UserHandle of the profile
+     * @param sourceBounds The Rect containing the source bounds of the clicked icon
+     * @param opts Options to pass to startActivity
+     */
+    public void startAppDetailsActivity(ComponentName component, UserHandle user,
+            Rect sourceBounds, Bundle opts) {
+        logErrorForInvalidProfileAccess(user);
+        try {
+            mService.showAppDetailsAsUser(mContext.getIApplicationThread(),
+                    mContext.getPackageName(), mContext.getAttributionTag(),
+                    component, sourceBounds, opts, user);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Retrieves a list of config activities for creating {@link ShortcutInfo}.
+     *
+     * @param packageName The specific package to query. If null, it checks all installed packages
+     *            in the profile.
+     * @param user The UserHandle of the profile.
+     * @return List of config activities. Can be an empty list but will not be null.
+     *
+     * @see Intent#ACTION_CREATE_SHORTCUT
+     * @see #getShortcutConfigActivityIntent(LauncherActivityInfo)
+     */
+    public List<LauncherActivityInfo> getShortcutConfigActivityList(@Nullable String packageName,
+            @NonNull UserHandle user) {
+        logErrorForInvalidProfileAccess(user);
+        try {
+            return convertToActivityList(mService.getShortcutConfigActivities(
+                    mContext.getPackageName(), packageName, user),
+                    user);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    private List<LauncherActivityInfo> convertToActivityList(
+            @Nullable ParceledListSlice<ResolveInfo> activities, UserHandle user) {
+        if (activities == null) {
+            return Collections.EMPTY_LIST;
+        }
+        ArrayList<LauncherActivityInfo> lais = new ArrayList<>();
+        for (ResolveInfo ri : activities.getList()) {
+            LauncherActivityInfo lai = new LauncherActivityInfo(mContext, ri.activityInfo, user);
+            if (DEBUG) {
+                Log.v(TAG, "Returning activity for profile " + user + " : "
+                        + lai.getComponentName());
+            }
+            lais.add(lai);
+        }
+        return lais;
+    }
+
+    /**
+     * Returns an intent sender which can be used to start the configure activity for creating
+     * custom shortcuts. Use this method if the provider is in another profile as you are not
+     * allowed to start an activity in another profile.
+     *
+     * <p>The caller should receive {@link PinItemRequest} in onActivityResult on
+     * {@link android.app.Activity#RESULT_OK}.
+     *
+     * <p>Callers must be allowed to access the shortcut information, as defined in {@link
+     * #hasShortcutHostPermission()}.
+     *
+     * @param info a configuration activity returned by {@link #getShortcutConfigActivityList}
+     *
+     * @throws IllegalStateException when the user is locked or not running.
+     * @throws SecurityException if {@link #hasShortcutHostPermission()} is false.
+     *
+     * @see #getPinItemRequest(Intent)
+     * @see Intent#ACTION_CREATE_SHORTCUT
+     * @see android.app.Activity#startIntentSenderForResult
+     */
+    @Nullable
+    public IntentSender getShortcutConfigActivityIntent(@NonNull LauncherActivityInfo info) {
+        try {
+            return mService.getShortcutConfigActivityIntent(
+                    mContext.getPackageName(), info.getComponentName(), info.getUser());
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Checks if the package is installed and enabled for a profile.
+     *
+     * @param packageName The package to check.
+     * @param user The UserHandle of the profile.
+     *
+     * @return true if the package exists and is enabled.
+     */
+    public boolean isPackageEnabled(String packageName, UserHandle user) {
+        logErrorForInvalidProfileAccess(user);
+        try {
+            return mService.isPackageEnabled(mContext.getPackageName(), packageName, user);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Gets the launcher extras supplied to the system when the given package was suspended via
+     * {@code PackageManager#setPackagesSuspended(String[], boolean, PersistableBundle,
+     * PersistableBundle, String)}.
+     *
+     * <p>The contents of this {@link Bundle} are supposed to be a contract between the suspending
+     * app and the launcher.
+     *
+     * <p>Note: This just returns whatever extras were provided to the system, <em>which might
+     * even be {@code null}.</em>
+     *
+     * @param packageName The package for which to fetch the launcher extras.
+     * @param user The {@link UserHandle} of the profile.
+     * @return A {@link Bundle} of launcher extras. Or {@code null} if the package is not currently
+     *         suspended.
+     *
+     * @see Callback#onPackagesSuspended(String[], UserHandle, Bundle)
+     * @see PackageManager#isPackageSuspended()
+     */
+    public @Nullable Bundle getSuspendedPackageLauncherExtras(String packageName, UserHandle user) {
+        logErrorForInvalidProfileAccess(user);
+        try {
+            return mService.getSuspendedPackageLauncherExtras(packageName, user);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns whether a package should be hidden from suggestions to the user. Currently, this
+     * could be done because the package was marked as distracting to the user via
+     * {@code PackageManager.setDistractingPackageRestrictions(String[], int)}.
+     *
+     * @param packageName The package for which to check.
+     * @param user the {@link UserHandle} of the profile.
+     * @return
+     */
+    public boolean shouldHideFromSuggestions(@NonNull String packageName,
+            @NonNull UserHandle user) {
+        Objects.requireNonNull(packageName, "packageName");
+        Objects.requireNonNull(user, "user");
+        try {
+            return mService.shouldHideFromSuggestions(packageName, user);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns {@link ApplicationInfo} about an application installed for a specific user profile.
+     *
+     * @param packageName The package name of the application
+     * @param flags Additional option flags {@link PackageManager#getApplicationInfo}
+     * @param user The UserHandle of the profile.
+     *
+     * @return {@link ApplicationInfo} containing information about the package. Returns
+     *         {@code null} if the package isn't installed for the given profile, or the profile
+     *         isn't enabled.
+     */
+    public ApplicationInfo getApplicationInfo(@NonNull String packageName,
+            @ApplicationInfoFlags int flags, @NonNull UserHandle user)
+            throws PackageManager.NameNotFoundException {
+        Objects.requireNonNull(packageName, "packageName");
+        Objects.requireNonNull(user, "user");
+        logErrorForInvalidProfileAccess(user);
+        try {
+            final ApplicationInfo ai = mService
+                    .getApplicationInfo(mContext.getPackageName(), packageName, flags, user);
+            if (ai == null) {
+                throw new NameNotFoundException("Package " + packageName + " not found for user "
+                        + user.getIdentifier());
+            }
+            return ai;
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns an object describing the app usage limit for the given package.
+     * If there are multiple limits that apply to the package, the one with the smallest
+     * time remaining will be returned.
+     *
+     * @param packageName name of the package whose app usage limit will be returned
+     * @param user the user of the package
+     *
+     * @return an {@link AppUsageLimit} object describing the app time limit containing
+     * the given package with the smallest time remaining, or {@code null} if none exist.
+     * @throws SecurityException when the caller is not the recents app.
+     * @hide
+     */
+    @Nullable
+    @SystemApi
+    public LauncherApps.AppUsageLimit getAppUsageLimit(@NonNull String packageName,
+            @NonNull UserHandle user) {
+        try {
+            return mService.getAppUsageLimit(mContext.getPackageName(), packageName, user);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Checks if the activity exists and it enabled for a profile.
+     *
+     * <p>The activity may still not be exported, in which case {@link #startMainActivity} will
+     * throw a {@link SecurityException} unless the caller has the same UID as the target app's.
+     *
+     * @param component The activity to check.
+     * @param user The UserHandle of the profile.
+     *
+     * @return true if the activity exists and is enabled.
+     */
+    public boolean isActivityEnabled(ComponentName component, UserHandle user) {
+        logErrorForInvalidProfileAccess(user);
+        try {
+            return mService.isActivityEnabled(mContext.getPackageName(), component, user);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns whether the caller can access the shortcut information.  Access is currently
+     * available to:
+     *
+     * <ul>
+     *     <li>The current launcher (or default launcher if there is no set current launcher).</li>
+     *     <li>The currently active voice interaction service.</li>
+     * </ul>
+     *
+     * <p>Note when this method returns {@code false}, it may be a temporary situation because
+     * the user is trying a new launcher application.  The user may decide to change the default
+     * launcher back to the calling application again, so even if a launcher application loses
+     * this permission, it does <b>not</b> have to purge pinned shortcut information.
+     * If the calling launcher application contains pinned shortcuts, they will still work,
+     * even though the caller no longer has the shortcut host permission.
+     *
+     * @throws IllegalStateException when the user is locked.
+     *
+     * @see ShortcutManager
+     */
+    public boolean hasShortcutHostPermission() {
+        try {
+            return mService.hasShortcutHostPermission(mContext.getPackageName());
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    private List<ShortcutInfo> maybeUpdateDisabledMessage(List<ShortcutInfo> shortcuts) {
+        if (shortcuts == null) {
+            return null;
+        }
+        for (int i = shortcuts.size() - 1; i >= 0; i--) {
+            final ShortcutInfo si = shortcuts.get(i);
+            final String message = ShortcutInfo.getDisabledReasonForRestoreIssue(mContext,
+                    si.getDisabledReason());
+            if (message != null) {
+                si.setDisabledMessage(message);
+            }
+        }
+        return shortcuts;
+    }
+
+    /**
+     * Returns {@link ShortcutInfo}s that match {@code query}.
+     *
+     * <p>Callers must be allowed to access the shortcut information, as defined in {@link
+     * #hasShortcutHostPermission()}.
+     *
+     * @param query result includes shortcuts matching this query.
+     * @param user The UserHandle of the profile.
+     *
+     * @return the IDs of {@link ShortcutInfo}s that match the query.
+     * @throws IllegalStateException when the user is locked, or when the {@code user} user
+     * is locked or not running.
+     *
+     * @see ShortcutManager
+     */
+    @Nullable
+    public List<ShortcutInfo> getShortcuts(@NonNull ShortcutQuery query,
+            @NonNull UserHandle user) {
+        logErrorForInvalidProfileAccess(user);
+        try {
+            // Note this is the only case we need to update the disabled message for shortcuts
+            // that weren't restored.
+            // The restore problem messages are only shown by the user, and publishers will never
+            // see them. The only other API that the launcher gets shortcuts is the shortcut
+            // changed callback, but that only returns shortcuts with the "key" information, so
+            // that won't return disabled message.
+            return maybeUpdateDisabledMessage(mService.getShortcuts(mContext.getPackageName(),
+                    new ShortcutQueryWrapper(query), user)
+                    .getList());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @hide // No longer used.  Use getShortcuts() instead.  Kept for unit tests.
+     */
+    @Nullable
+    @Deprecated
+    public List<ShortcutInfo> getShortcutInfo(@NonNull String packageName,
+            @NonNull List<String> ids, @NonNull UserHandle user) {
+        final ShortcutQuery q = new ShortcutQuery();
+        q.setPackage(packageName);
+        q.setShortcutIds(ids);
+        q.setQueryFlags(ShortcutQuery.FLAG_GET_ALL_KINDS);
+        return getShortcuts(q, user);
+    }
+
+    /**
+     * Pin shortcuts on a package.
+     *
+     * <p>This API is <b>NOT</b> cumulative; this will replace all pinned shortcuts for the package.
+     * However, different launchers may have different set of pinned shortcuts.
+     *
+     * <p>The calling launcher application must be allowed to access the shortcut information,
+     * as defined in {@link #hasShortcutHostPermission()}.
+     *
+     * @param packageName The target package name.
+     * @param shortcutIds The IDs of the shortcut to be pinned.
+     * @param user The UserHandle of the profile.
+     * @throws IllegalStateException when the user is locked, or when the {@code user} user
+     * is locked or not running.
+     *
+     * @see ShortcutManager
+     */
+    public void pinShortcuts(@NonNull String packageName, @NonNull List<String> shortcutIds,
+            @NonNull UserHandle user) {
+        logErrorForInvalidProfileAccess(user);
+        try {
+            mService.pinShortcuts(mContext.getPackageName(), packageName, shortcutIds, user);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Mark shortcuts as cached for a package.
+     *
+     * <p>Only dynamic long lived shortcuts can be cached. None dynamic or non long lived shortcuts
+     * in the list will be ignored.
+     *
+     * <p>Unlike pinned shortcuts, where different callers can have different sets of pinned
+     * shortcuts, cached state is per shortcut only, and even if multiple callers cache the same
+     * shortcut, it can be uncached by any valid caller.
+     *
+     * @param packageName The target package name.
+     * @param shortcutIds The IDs of the shortcut to be cached.
+     * @param user The UserHandle of the profile.
+     * @throws IllegalStateException when the user is locked, or when the {@code user} user
+     * is locked or not running.
+     *
+     * @see ShortcutManager
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.ACCESS_SHORTCUTS)
+    public void cacheShortcuts(@NonNull String packageName, @NonNull List<String> shortcutIds,
+            @NonNull UserHandle user) {
+        logErrorForInvalidProfileAccess(user);
+        try {
+            mService.cacheShortcuts(mContext.getPackageName(), packageName, shortcutIds, user);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Remove cached flag from shortcuts for a package.
+     *
+     * @param packageName The target package name.
+     * @param shortcutIds The IDs of the shortcut to be uncached.
+     * @param user The UserHandle of the profile.
+     * @throws IllegalStateException when the user is locked, or when the {@code user} user
+     * is locked or not running.
+     *
+     * @see ShortcutManager
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.ACCESS_SHORTCUTS)
+    public void uncacheShortcuts(@NonNull String packageName, @NonNull List<String> shortcutIds,
+            @NonNull UserHandle user) {
+        logErrorForInvalidProfileAccess(user);
+        try {
+            mService.uncacheShortcuts(mContext.getPackageName(), packageName, shortcutIds, user);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @hide kept for testing.
+     */
+    @Deprecated
+    public int getShortcutIconResId(@NonNull ShortcutInfo shortcut) {
+        return shortcut.getIconResourceId();
+    }
+
+    /**
+     * @hide kept for testing.
+     */
+    @Deprecated
+    public int getShortcutIconResId(@NonNull String packageName, @NonNull String shortcutId,
+            @NonNull UserHandle user) {
+        final ShortcutQuery q = new ShortcutQuery();
+        q.setPackage(packageName);
+        q.setShortcutIds(Arrays.asList(shortcutId));
+        q.setQueryFlags(ShortcutQuery.FLAG_GET_ALL_KINDS);
+        final List<ShortcutInfo> shortcuts = getShortcuts(q, user);
+
+        return shortcuts.size() > 0 ? shortcuts.get(0).getIconResourceId() : 0;
+    }
+
+    /**
+     * @hide internal/unit tests only
+     */
+    public ParcelFileDescriptor getShortcutIconFd(
+            @NonNull ShortcutInfo shortcut) {
+        return getShortcutIconFd(shortcut.getPackage(), shortcut.getId(),
+                shortcut.getUserId());
+    }
+
+    /**
+     * @hide internal/unit tests only
+     */
+    public ParcelFileDescriptor getShortcutIconFd(
+            @NonNull String packageName, @NonNull String shortcutId, @NonNull UserHandle user) {
+        return getShortcutIconFd(packageName, shortcutId, user.getIdentifier());
+    }
+
+    private ParcelFileDescriptor getShortcutIconFd(
+            @NonNull String packageName, @NonNull String shortcutId, int userId) {
+        try {
+            return mService.getShortcutIconFd(mContext.getPackageName(),
+                    packageName, shortcutId, userId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @hide internal/unit tests only
+     */
+    @VisibleForTesting
+    public ParcelFileDescriptor getUriShortcutIconFd(@NonNull ShortcutInfo shortcut) {
+        return getUriShortcutIconFd(shortcut.getPackage(), shortcut.getId(), shortcut.getUserId());
+    }
+
+    private ParcelFileDescriptor getUriShortcutIconFd(@NonNull String packageName,
+            @NonNull String shortcutId, int userId) {
+        String uri = null;
+        try {
+            uri = mService.getShortcutIconUri(mContext.getPackageName(), packageName, shortcutId,
+                    userId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+
+        if (uri == null) {
+            return null;
+        }
+        try {
+            return mContext.getContentResolver().openFileDescriptor(Uri.parse(uri), "r");
+        } catch (FileNotFoundException e) {
+            Log.e(TAG, "Icon file not found: " + uri);
+            return null;
+        }
+    }
+
+    /**
+     * Returns the icon for this shortcut, without any badging for the profile.
+     *
+     * <p>The calling launcher application must be allowed to access the shortcut information,
+     * as defined in {@link #hasShortcutHostPermission()}.
+     *
+     * @param density The preferred density of the icon, zero for default density. Use
+     * density DPI values from {@link DisplayMetrics}.
+     *
+     * @return The drawable associated with the shortcut.
+     * @throws IllegalStateException when the user is locked, or when the {@code user} user
+     * is locked or not running.
+     *
+     * @see ShortcutManager
+     * @see #getShortcutBadgedIconDrawable(ShortcutInfo, int)
+     * @see DisplayMetrics
+     */
+    public Drawable getShortcutIconDrawable(@NonNull ShortcutInfo shortcut, int density) {
+        if (shortcut.hasIconFile()) {
+            final ParcelFileDescriptor pfd = getShortcutIconFd(shortcut);
+            return loadDrawableFromFileDescriptor(pfd, shortcut.hasAdaptiveBitmap());
+        } else if (shortcut.hasIconUri()) {
+            final ParcelFileDescriptor pfd = getUriShortcutIconFd(shortcut);
+            return loadDrawableFromFileDescriptor(pfd, shortcut.hasAdaptiveBitmap());
+        } else if (shortcut.hasIconResource()) {
+            return loadDrawableResourceFromPackage(shortcut.getPackage(),
+                    shortcut.getIconResourceId(), shortcut.getUserHandle(), density);
+        } else if (shortcut.getIcon() != null) {
+            // This happens if a shortcut is pending-approval.
+            final Icon icon = shortcut.getIcon();
+            switch (icon.getType()) {
+                case Icon.TYPE_RESOURCE: {
+                    return loadDrawableResourceFromPackage(shortcut.getPackage(),
+                            icon.getResId(), shortcut.getUserHandle(), density);
+                }
+                case Icon.TYPE_BITMAP:
+                case Icon.TYPE_ADAPTIVE_BITMAP: {
+                    return icon.loadDrawable(mContext);
+                }
+                default:
+                    return null; // Shouldn't happen though.
+            }
+        } else {
+            return null; // Has no icon.
+        }
+    }
+
+    private Drawable loadDrawableFromFileDescriptor(ParcelFileDescriptor pfd, boolean adaptive) {
+        if (pfd == null) {
+            return null;
+        }
+        try {
+            final Bitmap bmp = BitmapFactory.decodeFileDescriptor(pfd.getFileDescriptor());
+            if (bmp != null) {
+                BitmapDrawable dr = new BitmapDrawable(mContext.getResources(), bmp);
+                if (adaptive) {
+                    return new AdaptiveIconDrawable(null, dr);
+                } else {
+                    return dr;
+                }
+            }
+            return null;
+        } finally {
+            try {
+                pfd.close();
+            } catch (IOException ignore) {
+            }
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public Icon getShortcutIcon(@NonNull ShortcutInfo shortcut) {
+        if (shortcut.hasIconFile()) {
+            final ParcelFileDescriptor pfd = getShortcutIconFd(shortcut);
+            if (pfd == null) {
+                return null;
+            }
+            try {
+                final Bitmap bmp = BitmapFactory.decodeFileDescriptor(pfd.getFileDescriptor());
+                if (bmp != null) {
+                    if (shortcut.hasAdaptiveBitmap()) {
+                        return Icon.createWithAdaptiveBitmap(bmp);
+                    } else {
+                        return Icon.createWithBitmap(bmp);
+                    }
+                }
+                return null;
+            } finally {
+                try {
+                    pfd.close();
+                } catch (IOException ignore) {
+                }
+            }
+        } else if (shortcut.hasIconResource()) {
+            return Icon.createWithResource(shortcut.getPackage(), shortcut.getIconResourceId());
+        } else {
+            return shortcut.getIcon();
+        }
+    }
+
+    private Drawable loadDrawableResourceFromPackage(String packageName, int resId,
+            UserHandle user, int density) {
+        try {
+            if (resId == 0) {
+                return null; // Shouldn't happen but just in case.
+            }
+            final ApplicationInfo ai = getApplicationInfo(packageName, /* flags =*/ 0, user);
+            final Resources res = mContext.getPackageManager().getResourcesForApplication(ai);
+            return res.getDrawableForDensity(resId, density);
+        } catch (NameNotFoundException | Resources.NotFoundException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Returns the shortcut icon with badging appropriate for the profile.
+     *
+     * <p>The calling launcher application must be allowed to access the shortcut information,
+     * as defined in {@link #hasShortcutHostPermission()}.
+     *
+     * @param density Optional density for the icon, or 0 to use the default density. Use
+     * @return A badged icon for the shortcut.
+     * @throws IllegalStateException when the user is locked, or when the {@code user} user
+     * is locked or not running.
+     *
+     * @see ShortcutManager
+     * @see #getShortcutIconDrawable(ShortcutInfo, int)
+     * @see DisplayMetrics
+     */
+    public Drawable getShortcutBadgedIconDrawable(ShortcutInfo shortcut, int density) {
+        final Drawable originalIcon = getShortcutIconDrawable(shortcut, density);
+
+        return (originalIcon == null) ? null : mContext.getPackageManager().getUserBadgedIcon(
+                originalIcon, shortcut.getUserHandle());
+    }
+
+    /**
+     * Starts a shortcut.
+     *
+     * <p>The calling launcher application must be allowed to access the shortcut information,
+     * as defined in {@link #hasShortcutHostPermission()}.
+     *
+     * @param packageName The target shortcut package name.
+     * @param shortcutId The target shortcut ID.
+     * @param sourceBounds The Rect containing the source bounds of the clicked icon.
+     * @param startActivityOptions Options to pass to startActivity.
+     * @param user The UserHandle of the profile.
+     * @throws IllegalStateException when the user is locked, or when the {@code user} user
+     * is locked or not running.
+     *
+     * @throws android.content.ActivityNotFoundException failed to start shortcut. (e.g.
+     * the shortcut no longer exists, is disabled, the intent receiver activity doesn't exist, etc)
+     */
+    public void startShortcut(@NonNull String packageName, @NonNull String shortcutId,
+            @Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions,
+            @NonNull UserHandle user) {
+        logErrorForInvalidProfileAccess(user);
+
+        startShortcut(packageName, shortcutId, sourceBounds, startActivityOptions,
+                user.getIdentifier());
+    }
+
+    /**
+     * Launches a shortcut.
+     *
+     * <p>The calling launcher application must be allowed to access the shortcut information,
+     * as defined in {@link #hasShortcutHostPermission()}.
+     *
+     * @param shortcut The target shortcut.
+     * @param sourceBounds The Rect containing the source bounds of the clicked icon.
+     * @param startActivityOptions Options to pass to startActivity.
+     * @throws IllegalStateException when the user is locked, or when the {@code user} user
+     * is locked or not running.
+     *
+     * @throws android.content.ActivityNotFoundException failed to start shortcut. (e.g.
+     * the shortcut no longer exists, is disabled, the intent receiver activity doesn't exist, etc)
+     */
+    public void startShortcut(@NonNull ShortcutInfo shortcut,
+            @Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions) {
+        startShortcut(shortcut.getPackage(), shortcut.getId(),
+                sourceBounds, startActivityOptions,
+                shortcut.getUserId());
+    }
+
+    @UnsupportedAppUsage
+    private void startShortcut(@NonNull String packageName, @NonNull String shortcutId,
+            @Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions,
+            int userId) {
+        try {
+            final boolean success = mService.startShortcut(mContext.getPackageName(), packageName,
+                    null /* default featureId */, shortcutId, sourceBounds, startActivityOptions,
+                    userId);
+            if (!success) {
+                throw new ActivityNotFoundException("Shortcut could not be started");
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Registers a callback for changes to packages in this user and managed profiles.
+     *
+     * @param callback The callback to register.
+     */
+    public void registerCallback(Callback callback) {
+        registerCallback(callback, null);
+    }
+
+    /**
+     * Registers a callback for changes to packages in this user and managed profiles.
+     *
+     * @param callback The callback to register.
+     * @param handler that should be used to post callbacks on, may be null.
+     */
+    public void registerCallback(Callback callback, Handler handler) {
+        synchronized (this) {
+            if (callback != null && findCallbackLocked(callback) < 0) {
+                boolean addedFirstCallback = mCallbacks.size() == 0;
+                addCallbackLocked(callback, handler);
+                if (addedFirstCallback) {
+                    try {
+                        mService.addOnAppsChangedListener(mContext.getPackageName(),
+                                mAppsChangedListener);
+                    } catch (RemoteException re) {
+                        throw re.rethrowFromSystemServer();
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Unregisters a callback that was previously registered.
+     *
+     * @param callback The callback to unregister.
+     * @see #registerCallback(Callback)
+     */
+    public void unregisterCallback(Callback callback) {
+        synchronized (this) {
+            removeCallbackLocked(callback);
+            if (mCallbacks.size() == 0) {
+                try {
+                    mService.removeOnAppsChangedListener(mAppsChangedListener);
+                } catch (RemoteException re) {
+                    throw re.rethrowFromSystemServer();
+                }
+            }
+        }
+    }
+
+    /** @return position in mCallbacks for callback or -1 if not present. */
+    private int findCallbackLocked(Callback callback) {
+        if (callback == null) {
+            throw new IllegalArgumentException("Callback cannot be null");
+        }
+        final int size = mCallbacks.size();
+        for (int i = 0; i < size; ++i) {
+            if (mCallbacks.get(i).mCallback == callback) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    private void removeCallbackLocked(Callback callback) {
+        int pos = findCallbackLocked(callback);
+        if (pos >= 0) {
+            mCallbacks.remove(pos);
+        }
+    }
+
+    private void addCallbackLocked(Callback callback, Handler handler) {
+        // Remove if already present.
+        removeCallbackLocked(callback);
+        if (handler == null) {
+            handler = new Handler();
+        }
+        CallbackMessageHandler toAdd = new CallbackMessageHandler(handler.getLooper(), callback);
+        mCallbacks.add(toAdd);
+    }
+
+    private IOnAppsChangedListener.Stub mAppsChangedListener = new IOnAppsChangedListener.Stub() {
+
+        @Override
+        public void onPackageRemoved(UserHandle user, String packageName)
+                throws RemoteException {
+            if (DEBUG) {
+                Log.d(TAG, "onPackageRemoved " + user.getIdentifier() + "," + packageName);
+            }
+            synchronized (LauncherApps.this) {
+                for (CallbackMessageHandler callback : mCallbacks) {
+                    callback.postOnPackageRemoved(packageName, user);
+                }
+            }
+        }
+
+        @Override
+        public void onPackageChanged(UserHandle user, String packageName) throws RemoteException {
+            if (DEBUG) {
+                Log.d(TAG, "onPackageChanged " + user.getIdentifier() + "," + packageName);
+            }
+            synchronized (LauncherApps.this) {
+                for (CallbackMessageHandler callback : mCallbacks) {
+                    callback.postOnPackageChanged(packageName, user);
+                }
+            }
+        }
+
+        @Override
+        public void onPackageAdded(UserHandle user, String packageName) throws RemoteException {
+            if (DEBUG) {
+                Log.d(TAG, "onPackageAdded " + user.getIdentifier() + "," + packageName);
+            }
+            synchronized (LauncherApps.this) {
+                for (CallbackMessageHandler callback : mCallbacks) {
+                    callback.postOnPackageAdded(packageName, user);
+                }
+            }
+        }
+
+        @Override
+        public void onPackagesAvailable(UserHandle user, String[] packageNames, boolean replacing)
+                throws RemoteException {
+            if (DEBUG) {
+                Log.d(TAG, "onPackagesAvailable " + user.getIdentifier() + "," + packageNames);
+            }
+            synchronized (LauncherApps.this) {
+                for (CallbackMessageHandler callback : mCallbacks) {
+                    callback.postOnPackagesAvailable(packageNames, user, replacing);
+                }
+            }
+        }
+
+        @Override
+        public void onPackagesUnavailable(UserHandle user, String[] packageNames, boolean replacing)
+                throws RemoteException {
+            if (DEBUG) {
+                Log.d(TAG, "onPackagesUnavailable " + user.getIdentifier() + "," + packageNames);
+            }
+            synchronized (LauncherApps.this) {
+                for (CallbackMessageHandler callback : mCallbacks) {
+                    callback.postOnPackagesUnavailable(packageNames, user, replacing);
+                }
+            }
+        }
+
+        @Override
+        public void onPackagesSuspended(UserHandle user, String[] packageNames,
+                Bundle launcherExtras)
+                throws RemoteException {
+            if (DEBUG) {
+                Log.d(TAG, "onPackagesSuspended " + user.getIdentifier() + "," + packageNames);
+            }
+            synchronized (LauncherApps.this) {
+                for (CallbackMessageHandler callback : mCallbacks) {
+                    callback.postOnPackagesSuspended(packageNames, launcherExtras, user);
+                }
+            }
+        }
+
+        @Override
+        public void onPackagesUnsuspended(UserHandle user, String[] packageNames)
+                throws RemoteException {
+            if (DEBUG) {
+                Log.d(TAG, "onPackagesUnsuspended " + user.getIdentifier() + "," + packageNames);
+            }
+            synchronized (LauncherApps.this) {
+                for (CallbackMessageHandler callback : mCallbacks) {
+                    callback.postOnPackagesUnsuspended(packageNames, user);
+                }
+            }
+        }
+
+        @Override
+        public void onShortcutChanged(UserHandle user, String packageName,
+                ParceledListSlice shortcuts) {
+            if (DEBUG) {
+                Log.d(TAG, "onShortcutChanged " + user.getIdentifier() + "," + packageName);
+            }
+            final List<ShortcutInfo> list = shortcuts.getList();
+            synchronized (LauncherApps.this) {
+                for (CallbackMessageHandler callback : mCallbacks) {
+                    callback.postOnShortcutChanged(packageName, user, list);
+                }
+            }
+        }
+    };
+
+    private static class CallbackMessageHandler extends Handler {
+        private static final int MSG_ADDED = 1;
+        private static final int MSG_REMOVED = 2;
+        private static final int MSG_CHANGED = 3;
+        private static final int MSG_AVAILABLE = 4;
+        private static final int MSG_UNAVAILABLE = 5;
+        private static final int MSG_SUSPENDED = 6;
+        private static final int MSG_UNSUSPENDED = 7;
+        private static final int MSG_SHORTCUT_CHANGED = 8;
+
+        private LauncherApps.Callback mCallback;
+
+        private static class CallbackInfo {
+            String[] packageNames;
+            String packageName;
+            Bundle launcherExtras;
+            boolean replacing;
+            UserHandle user;
+            List<ShortcutInfo> shortcuts;
+        }
+
+        public CallbackMessageHandler(Looper looper, LauncherApps.Callback callback) {
+            super(looper, null, true);
+            mCallback = callback;
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            if (mCallback == null || !(msg.obj instanceof CallbackInfo)) {
+                return;
+            }
+            CallbackInfo info = (CallbackInfo) msg.obj;
+            switch (msg.what) {
+                case MSG_ADDED:
+                    mCallback.onPackageAdded(info.packageName, info.user);
+                    break;
+                case MSG_REMOVED:
+                    mCallback.onPackageRemoved(info.packageName, info.user);
+                    break;
+                case MSG_CHANGED:
+                    mCallback.onPackageChanged(info.packageName, info.user);
+                    break;
+                case MSG_AVAILABLE:
+                    mCallback.onPackagesAvailable(info.packageNames, info.user, info.replacing);
+                    break;
+                case MSG_UNAVAILABLE:
+                    mCallback.onPackagesUnavailable(info.packageNames, info.user, info.replacing);
+                    break;
+                case MSG_SUSPENDED:
+                    mCallback.onPackagesSuspended(info.packageNames, info.user, info.launcherExtras
+                    );
+                    break;
+                case MSG_UNSUSPENDED:
+                    mCallback.onPackagesUnsuspended(info.packageNames, info.user);
+                    break;
+                case MSG_SHORTCUT_CHANGED:
+                    mCallback.onShortcutsChanged(info.packageName, info.shortcuts, info.user);
+                    break;
+            }
+        }
+
+        public void postOnPackageAdded(String packageName, UserHandle user) {
+            CallbackInfo info = new CallbackInfo();
+            info.packageName = packageName;
+            info.user = user;
+            obtainMessage(MSG_ADDED, info).sendToTarget();
+        }
+
+        public void postOnPackageRemoved(String packageName, UserHandle user) {
+            CallbackInfo info = new CallbackInfo();
+            info.packageName = packageName;
+            info.user = user;
+            obtainMessage(MSG_REMOVED, info).sendToTarget();
+        }
+
+        public void postOnPackageChanged(String packageName, UserHandle user) {
+            CallbackInfo info = new CallbackInfo();
+            info.packageName = packageName;
+            info.user = user;
+            obtainMessage(MSG_CHANGED, info).sendToTarget();
+        }
+
+        public void postOnPackagesAvailable(String[] packageNames, UserHandle user,
+                boolean replacing) {
+            CallbackInfo info = new CallbackInfo();
+            info.packageNames = packageNames;
+            info.replacing = replacing;
+            info.user = user;
+            obtainMessage(MSG_AVAILABLE, info).sendToTarget();
+        }
+
+        public void postOnPackagesUnavailable(String[] packageNames, UserHandle user,
+                boolean replacing) {
+            CallbackInfo info = new CallbackInfo();
+            info.packageNames = packageNames;
+            info.replacing = replacing;
+            info.user = user;
+            obtainMessage(MSG_UNAVAILABLE, info).sendToTarget();
+        }
+
+        public void postOnPackagesSuspended(String[] packageNames, Bundle launcherExtras,
+                UserHandle user) {
+            CallbackInfo info = new CallbackInfo();
+            info.packageNames = packageNames;
+            info.user = user;
+            info.launcherExtras = launcherExtras;
+            obtainMessage(MSG_SUSPENDED, info).sendToTarget();
+        }
+
+        public void postOnPackagesUnsuspended(String[] packageNames, UserHandle user) {
+            CallbackInfo info = new CallbackInfo();
+            info.packageNames = packageNames;
+            info.user = user;
+            obtainMessage(MSG_UNSUSPENDED, info).sendToTarget();
+        }
+
+        public void postOnShortcutChanged(String packageName, UserHandle user,
+                List<ShortcutInfo> shortcuts) {
+            CallbackInfo info = new CallbackInfo();
+            info.packageName = packageName;
+            info.user = user;
+            info.shortcuts = shortcuts;
+            obtainMessage(MSG_SHORTCUT_CHANGED, info).sendToTarget();
+        }
+    }
+
+    /**
+     * Register a callback to watch for session lifecycle events in this user and managed profiles.
+     * @param callback The callback to register.
+     * @param executor {@link Executor} to handle the callbacks, cannot be null.
+     *
+     * @see PackageInstaller#registerSessionCallback(SessionCallback)
+     */
+    public void registerPackageInstallerSessionCallback(
+            @NonNull @CallbackExecutor Executor executor, @NonNull SessionCallback callback) {
+        if (executor == null) {
+            throw new NullPointerException("Executor must not be null");
+        }
+
+        synchronized (mDelegates) {
+            final SessionCallbackDelegate delegate = new SessionCallbackDelegate(callback,
+                    executor);
+            try {
+                mService.registerPackageInstallerCallback(mContext.getPackageName(),
+                        delegate);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+            mDelegates.add(delegate);
+        }
+    }
+
+    /**
+     * Unregisters a callback that was previously registered.
+     *
+     * @param callback The callback to unregister.
+     * @see #registerPackageInstallerSessionCallback(Executor, SessionCallback)
+     */
+    public void unregisterPackageInstallerSessionCallback(@NonNull SessionCallback callback) {
+        synchronized (mDelegates) {
+            for (Iterator<SessionCallbackDelegate> i = mDelegates.iterator(); i.hasNext();) {
+                final SessionCallbackDelegate delegate = i.next();
+                if (delegate.mCallback == callback) {
+                    mPm.getPackageInstaller().unregisterSessionCallback(delegate.mCallback);
+                    i.remove();
+                }
+            }
+        }
+    }
+
+    /**
+     * Return list of all known install sessions in this user and managed profiles, regardless
+     * of the installer.
+     *
+     * @see PackageInstaller#getAllSessions()
+     */
+    public @NonNull List<SessionInfo> getAllPackageInstallerSessions() {
+        try {
+            return mService.getAllSessions(mContext.getPackageName()).getList();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Register a callback to watch for shortcut change events in this user and managed profiles.
+     *
+     * @param callback The callback to register.
+     * @param query {@link ShortcutQuery} to match and filter the shortcut events. Only matching
+     * shortcuts will be returned by the callback.
+     * @param executor {@link Executor} to handle the callbacks. To dispatch callbacks to the main
+     * thread of your application, you can use {@link android.content.Context#getMainExecutor()}.
+     *
+     * @hide
+     */
+    public void registerShortcutChangeCallback(@NonNull ShortcutChangeCallback callback,
+            @NonNull ShortcutQuery query, @NonNull @CallbackExecutor Executor executor) {
+        Objects.requireNonNull(callback, "Callback cannot be null");
+        Objects.requireNonNull(query, "Query cannot be null");
+        Objects.requireNonNull(executor, "Executor cannot be null");
+
+        synchronized (mShortcutChangeCallbacks) {
+            IShortcutChangeCallback proxy = new ShortcutChangeCallbackProxy(executor, callback);
+            mShortcutChangeCallbacks.put(callback, new Pair<>(executor, proxy));
+            try {
+                mService.registerShortcutChangeCallback(mContext.getPackageName(),
+                        new ShortcutQueryWrapper(query), proxy);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * Unregisters a callback that was previously registered.
+     * @see #registerShortcutChangeCallback(ShortcutChangeCallback, ShortcutQuery, Executor)
+     *
+     * @param callback Callback to be unregistered.
+     *
+     * @hide
+     */
+    public void unregisterShortcutChangeCallback(@NonNull ShortcutChangeCallback callback) {
+        Objects.requireNonNull(callback, "Callback cannot be null");
+
+        synchronized (mShortcutChangeCallbacks) {
+            if (mShortcutChangeCallbacks.containsKey(callback)) {
+                IShortcutChangeCallback proxy = mShortcutChangeCallbacks.remove(callback).second;
+                try {
+                    mService.unregisterShortcutChangeCallback(mContext.getPackageName(), proxy);
+                } catch (RemoteException e) {
+                    throw e.rethrowFromSystemServer();
+                }
+            }
+        }
+    }
+
+    /**
+     * A helper method to extract a {@link PinItemRequest} set to
+     * the {@link #EXTRA_PIN_ITEM_REQUEST} extra.
+     */
+    public PinItemRequest getPinItemRequest(Intent intent) {
+        return intent.getParcelableExtra(EXTRA_PIN_ITEM_REQUEST);
+    }
+
+    /**
+     * Represents a "pin shortcut" or a "pin appwidget" request made by an app, which is sent with
+     * an {@link #ACTION_CONFIRM_PIN_SHORTCUT} or {@link #ACTION_CONFIRM_PIN_APPWIDGET} intent
+     * respectively to the default launcher app.
+     *
+     * <h3>Request of the {@link #REQUEST_TYPE_SHORTCUT} type.
+     *
+     * <p>A {@link #REQUEST_TYPE_SHORTCUT} request represents a request to pin a
+     * {@link ShortcutInfo}.  If the launcher accepts a request, call {@link #accept()},
+     * or {@link #accept(Bundle)} with a null or empty Bundle.  No options are defined for
+     * pin-shortcuts requests.
+     *
+     * <p>{@link #getShortcutInfo()} always returns a non-null {@link ShortcutInfo} for this type.
+     *
+     * <p>The launcher may receive a request with a {@link ShortcutInfo} that is already pinned, in
+     * which case {@link ShortcutInfo#isPinned()} returns true.  This means the user wants to create
+     * another pinned shortcut for a shortcut that's already pinned.  If the launcher accepts it,
+     * {@link #accept()} must still be called even though the shortcut is already pinned, and
+     * create a new pinned shortcut icon for it.
+     *
+     * <p>See also {@link ShortcutManager} for more details.
+     *
+     * <h3>Request of the {@link #REQUEST_TYPE_APPWIDGET} type.
+     *
+     * <p>A {@link #REQUEST_TYPE_SHORTCUT} request represents a request to pin a
+     * an AppWidget.  If the launcher accepts a request, call {@link #accept(Bundle)} with
+     * the appwidget integer ID set to the
+     * {@link android.appwidget.AppWidgetManager#EXTRA_APPWIDGET_ID} extra.
+     *
+     * <p>{@link #getAppWidgetProviderInfo(Context)} always returns a non-null
+     * {@link AppWidgetProviderInfo} for this type.
+     *
+     * <p>See also {@link AppWidgetManager} for more details.
+     *
+     * @see #EXTRA_PIN_ITEM_REQUEST
+     * @see #getPinItemRequest(Intent)
+     */
+    public static final class PinItemRequest implements Parcelable {
+
+        /** This is a request to pin shortcut. */
+        public static final int REQUEST_TYPE_SHORTCUT = 1;
+
+        /** This is a request to pin app widget. */
+        public static final int REQUEST_TYPE_APPWIDGET = 2;
+
+        /** @hide */
+        @IntDef(prefix = { "REQUEST_TYPE_" }, value = {
+                REQUEST_TYPE_SHORTCUT,
+                REQUEST_TYPE_APPWIDGET
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface RequestType {}
+
+        private final int mRequestType;
+        private final IPinItemRequest mInner;
+
+        /**
+         * @hide
+         */
+        public PinItemRequest(IPinItemRequest inner, int type) {
+            mInner = inner;
+            mRequestType = type;
+        }
+
+        /**
+         * Represents the type of a request, which is one of the {@code REQUEST_TYPE_} constants.
+         *
+         * @return one of the {@code REQUEST_TYPE_} constants.
+         */
+        @RequestType
+        public int getRequestType() {
+            return mRequestType;
+        }
+
+        /**
+         * {@link ShortcutInfo} sent by the requesting app.
+         * Always non-null for a {@link #REQUEST_TYPE_SHORTCUT} request, and always null for a
+         * different request type.
+         *
+         * @return requested {@link ShortcutInfo} when a request is of the
+         * {@link #REQUEST_TYPE_SHORTCUT} type.  Null otherwise.
+         */
+        @Nullable
+        public ShortcutInfo getShortcutInfo() {
+            try {
+                return mInner.getShortcutInfo();
+            } catch (RemoteException e) {
+                throw e.rethrowAsRuntimeException();
+            }
+        }
+
+        /**
+         * {@link AppWidgetProviderInfo} sent by the requesting app.
+         * Always non-null for a {@link #REQUEST_TYPE_APPWIDGET} request, and always null for a
+         * different request type.
+         *
+         * <p>Launcher should not show any configuration activity associated with the provider, and
+         * assume that the widget is already fully configured. Upon accepting the widget, it should
+         * pass the widgetId in {@link #accept(Bundle)}.
+         *
+         * @return requested {@link AppWidgetProviderInfo} when a request is of the
+         * {@link #REQUEST_TYPE_APPWIDGET} type.  Null otherwise.
+         */
+        @Nullable
+        public AppWidgetProviderInfo getAppWidgetProviderInfo(Context context) {
+            try {
+                final AppWidgetProviderInfo info = mInner.getAppWidgetProviderInfo();
+                if (info == null) {
+                    return null;
+                }
+                info.updateDimensions(context.getResources().getDisplayMetrics());
+                return info;
+            } catch (RemoteException e) {
+                throw e.rethrowAsRuntimeException();
+            }
+        }
+
+        /**
+         * Any extras sent by the requesting app.
+         *
+         * @return For a shortcut request, this method always return null.  For an AppWidget
+         * request, this method returns the extras passed to the
+         * {@link android.appwidget.AppWidgetManager#requestPinAppWidget(
+         * ComponentName, Bundle, PendingIntent)} API.  See {@link AppWidgetManager} for details.
+         */
+        @Nullable
+        public Bundle getExtras() {
+            try {
+                return mInner.getExtras();
+            } catch (RemoteException e) {
+                throw e.rethrowAsRuntimeException();
+            }
+        }
+
+        /**
+         * Return whether a request is still valid.
+         *
+         * @return {@code TRUE} if a request is valid and {@link #accept(Bundle)} may be called.
+         */
+        public boolean isValid() {
+            try {
+                return mInner.isValid();
+            } catch (RemoteException e) {
+                return false;
+            }
+        }
+
+        /**
+         * Called by the receiving launcher app when the user accepts the request.
+         *
+         * @param options must be set for a {@link #REQUEST_TYPE_APPWIDGET} request.
+         *
+         * @return {@code TRUE} if the shortcut or the AppWidget has actually been pinned.
+         * {@code FALSE} if the item hasn't been pinned, for example, because the request had
+         * already been canceled, in which case the launcher must not pin the requested item.
+         */
+        public boolean accept(@Nullable Bundle options) {
+            try {
+                return mInner.accept(options);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        /**
+         * Called by the receiving launcher app when the user accepts the request, with no options.
+         *
+         * @return {@code TRUE} if the shortcut or the AppWidget has actually been pinned.
+         * {@code FALSE} if the item hasn't been pinned, for example, because the request had
+         * already been canceled, in which case the launcher must not pin the requested item.
+         */
+        public boolean accept() {
+            return accept(/* options= */ null);
+        }
+
+        private PinItemRequest(Parcel source) {
+            final ClassLoader cl = getClass().getClassLoader();
+
+            mRequestType = source.readInt();
+            mInner = IPinItemRequest.Stub.asInterface(source.readStrongBinder());
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(mRequestType);
+            dest.writeStrongBinder(mInner.asBinder());
+        }
+
+        public static final @android.annotation.NonNull Creator<PinItemRequest> CREATOR =
+                new Creator<PinItemRequest>() {
+                    public PinItemRequest createFromParcel(Parcel source) {
+                        return new PinItemRequest(source);
+                    }
+                    public PinItemRequest[] newArray(int size) {
+                        return new PinItemRequest[size];
+                    }
+                };
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+    }
+
+    /**
+     * A class that encapsulates information about the usage limit set for an app or
+     * a group of apps.
+     *
+     * <p>The launcher can query specifics about the usage limit such as how much usage time
+     * the limit has and how much of the total usage time is remaining via the APIs available
+     * in this class.
+     *
+     * @see #getAppUsageLimit(String, UserHandle)
+     * @hide
+     */
+    @SystemApi
+    public static final class AppUsageLimit implements Parcelable {
+        private final long mTotalUsageLimit;
+        private final long mUsageRemaining;
+
+        /** @hide */
+        public AppUsageLimit(long totalUsageLimit, long usageRemaining) {
+            this.mTotalUsageLimit = totalUsageLimit;
+            this.mUsageRemaining = usageRemaining;
+        }
+
+        /**
+         * Returns the total usage limit in milliseconds set for an app or a group of apps.
+         *
+         * @return the total usage limit in milliseconds
+         */
+        public long getTotalUsageLimit() {
+            return mTotalUsageLimit;
+        }
+
+        /**
+         * Returns the usage remaining in milliseconds for an app or the group of apps
+         * this limit refers to.
+         *
+         * @return the usage remaining in milliseconds
+         */
+        public long getUsageRemaining() {
+            return mUsageRemaining;
+        }
+
+        private AppUsageLimit(Parcel source) {
+            mTotalUsageLimit = source.readLong();
+            mUsageRemaining = source.readLong();
+        }
+
+        public static final @android.annotation.NonNull Creator<AppUsageLimit> CREATOR = new Creator<AppUsageLimit>() {
+            @Override
+            public AppUsageLimit createFromParcel(Parcel source) {
+                return new AppUsageLimit(source);
+            }
+
+            @Override
+            public AppUsageLimit[] newArray(int size) {
+                return new AppUsageLimit[size];
+            }
+        };
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeLong(mTotalUsageLimit);
+            dest.writeLong(mUsageRemaining);
+        }
+    }
+}
diff --git a/android/content/pm/LimitedLengthInputStream.java b/android/content/pm/LimitedLengthInputStream.java
new file mode 100644
index 0000000..19b681e
--- /dev/null
+++ b/android/content/pm/LimitedLengthInputStream.java
@@ -0,0 +1,95 @@
+package android.content.pm;
+
+import libcore.util.ArrayUtils;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * A class that limits the amount of data that is read from an InputStream. When
+ * the specified length is reached, the stream returns an EOF even if the
+ * underlying stream still has more data.
+ *
+ * @hide
+ */
+public class LimitedLengthInputStream extends FilterInputStream {
+    /**
+     * The end of the stream where we don't want to allow more data to be read.
+     */
+    private final long mEnd;
+
+    /**
+     * Current offset in the stream.
+     */
+    private long mOffset;
+
+    /**
+     * @param in underlying stream to wrap
+     * @param offset offset into stream where data starts
+     * @param length length of data at offset
+     * @throws IOException if an error occurred with the underlying stream
+     */
+    public LimitedLengthInputStream(InputStream in, long offset, long length) throws IOException {
+        super(in);
+
+        if (in == null) {
+            throw new IOException("in == null");
+        }
+
+        if (offset < 0) {
+            throw new IOException("offset < 0");
+        }
+
+        if (length < 0) {
+            throw new IOException("length < 0");
+        }
+
+        if (length > Long.MAX_VALUE - offset) {
+            throw new IOException("offset + length > Long.MAX_VALUE");
+        }
+
+        mEnd = offset + length;
+
+        skip(offset);
+        mOffset = offset;
+    }
+
+    @Override
+    public synchronized int read() throws IOException {
+        if (mOffset >= mEnd) {
+            return -1;
+        }
+
+        mOffset++;
+        return super.read();
+    }
+
+    @Override
+    public int read(byte[] buffer, int offset, int byteCount) throws IOException {
+        if (mOffset >= mEnd) {
+            return -1;
+        }
+
+        final int arrayLength = buffer.length;
+        ArrayUtils.throwsIfOutOfBounds(arrayLength, offset, byteCount);
+
+        if (mOffset > Long.MAX_VALUE - byteCount) {
+            throw new IOException("offset out of bounds: " + mOffset + " + " + byteCount);
+        }
+
+        if (mOffset + byteCount > mEnd) {
+            byteCount = (int) (mEnd - mOffset);
+        }
+
+        final int numRead = super.read(buffer, offset, byteCount);
+        mOffset += numRead;
+
+        return numRead;
+    }
+
+    @Override
+    public int read(byte[] buffer) throws IOException {
+        return read(buffer, 0, buffer.length);
+    }
+}
diff --git a/android/content/pm/MacAuthenticatedInputStream.java b/android/content/pm/MacAuthenticatedInputStream.java
new file mode 100644
index 0000000..11f4b94
--- /dev/null
+++ b/android/content/pm/MacAuthenticatedInputStream.java
@@ -0,0 +1,78 @@
+/*
+ * 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.content.pm;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.crypto.Mac;
+
+/**
+ * An input stream filter that applies a MAC to the data passing through it. At
+ * the end of the data that should be authenticated, the tag can be calculated.
+ * After that, the stream should not be used.
+ *
+ * @hide
+ */
+public class MacAuthenticatedInputStream extends FilterInputStream {
+    private final Mac mMac;
+
+    public MacAuthenticatedInputStream(InputStream in, Mac mac) {
+        super(in);
+
+        mMac = mac;
+    }
+
+    public boolean isTagEqual(byte[] tag) {
+        final byte[] actualTag = mMac.doFinal();
+
+        if (tag == null || actualTag == null || tag.length != actualTag.length) {
+            return false;
+        }
+
+        /*
+         * Attempt to prevent timing attacks by doing the same amount of work
+         * whether the first byte matches or not. Do not change this to a loop
+         * that exits early when a byte does not match.
+         */
+        int value = 0;
+        for (int i = 0; i < tag.length; i++) {
+            value |= tag[i] ^ actualTag[i];
+        }
+
+        return value == 0;
+    }
+
+    @Override
+    public int read() throws IOException {
+        final int b = super.read();
+        if (b >= 0) {
+            mMac.update((byte) b);
+        }
+        return b;
+    }
+
+    @Override
+    public int read(byte[] buffer, int offset, int count) throws IOException {
+        int numRead = super.read(buffer, offset, count);
+        if (numRead > 0) {
+            mMac.update(buffer, offset, numRead);
+        }
+        return numRead;
+    }
+}
diff --git a/android/content/pm/ModuleInfo.java b/android/content/pm/ModuleInfo.java
new file mode 100644
index 0000000..a6db662
--- /dev/null
+++ b/android/content/pm/ModuleInfo.java
@@ -0,0 +1,168 @@
+/*
+ * 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.content.pm;
+
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Information you can retrieve about a particular system
+ * module.
+ */
+public final class ModuleInfo implements Parcelable {
+
+     // NOTE: When adding new data members be sure to update the copy-constructor, Parcel
+     // constructor, and writeToParcel.
+
+    /** Public name of this module. */
+    private CharSequence mName;
+
+    /** The package name of this module. */
+    private String mPackageName;
+
+    /**
+     * The name of the APEX this module is distributed as, or null if it is not distributed via
+     * APEX.
+     */
+    @Nullable private String mApexModuleName;
+
+    /** Whether or not this module is hidden from the user. */
+    private boolean mHidden;
+
+    // TODO: Decide whether we need an additional metadata bundle to support out of band
+    // updates to ModuleInfo.
+    //
+    // private Bundle mMetadata;
+
+    /** @hide */
+    public ModuleInfo() {
+    }
+
+    /** @hide */
+    public ModuleInfo(ModuleInfo orig) {
+        mName = orig.mName;
+        mPackageName = orig.mPackageName;
+        mHidden = orig.mHidden;
+        mApexModuleName = orig.mApexModuleName;
+    }
+
+    /** @hide Sets the public name of this module. */
+    public ModuleInfo setName(CharSequence name) {
+        mName = name;
+        return this;
+    }
+
+    /** Gets the public name of this module. */
+    public @Nullable CharSequence getName() {
+        return mName;
+    }
+
+    /** @hide Sets the package name of this module. */
+    public ModuleInfo setPackageName(String packageName) {
+        mPackageName = packageName;
+        return this;
+    }
+
+    /** Gets the package name of this module. */
+    public @Nullable String getPackageName() {
+        return mPackageName;
+    }
+
+    /** @hide Sets whether or not this package is hidden. */
+    public ModuleInfo setHidden(boolean hidden) {
+        mHidden = hidden;
+        return this;
+    }
+
+    /** Gets whether or not this package is hidden. */
+    public boolean isHidden() {
+        return mHidden;
+    }
+
+    /** @hide Sets the apex module name. */
+    public ModuleInfo setApexModuleName(@Nullable String apexModuleName) {
+        mApexModuleName = apexModuleName;
+        return this;
+    }
+
+    /** @hide Gets the apex module name. */
+    public @Nullable String getApexModuleName() {
+        return mApexModuleName;
+    }
+
+    /** Returns a string representation of this object. */
+    public String toString() {
+        return "ModuleInfo{"
+            + Integer.toHexString(System.identityHashCode(this))
+            + " " + mName + "}";
+    }
+
+    /** Describes the kinds of special objects contained in this object. */
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public int hashCode() {
+        int hashCode = 0;
+        hashCode = 31 * hashCode + Objects.hashCode(mName);
+        hashCode = 31 * hashCode + Objects.hashCode(mPackageName);
+        hashCode = 31 * hashCode + Objects.hashCode(mApexModuleName);
+        hashCode = 31 * hashCode + Boolean.hashCode(mHidden);
+        return hashCode;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof ModuleInfo)) {
+            return false;
+        }
+        final ModuleInfo other = (ModuleInfo) obj;
+        return Objects.equals(mName, other.mName)
+                && Objects.equals(mPackageName, other.mPackageName)
+                && Objects.equals(mApexModuleName, other.mApexModuleName)
+                && mHidden == other.mHidden;
+    }
+
+    /** Flattens this object into the given {@link Parcel}. */
+    public void writeToParcel(Parcel dest, int parcelableFlags) {
+        dest.writeCharSequence(mName);
+        dest.writeString(mPackageName);
+        dest.writeBoolean(mHidden);
+        dest.writeString(mApexModuleName);
+    }
+
+    private ModuleInfo(Parcel source) {
+        mName = source.readCharSequence();
+        mPackageName = source.readString();
+        mHidden = source.readBoolean();
+        mApexModuleName = source.readString();
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<ModuleInfo> CREATOR =
+            new Parcelable.Creator<ModuleInfo>() {
+        public ModuleInfo createFromParcel(Parcel source) {
+            return new ModuleInfo(source);
+        }
+        public ModuleInfo[] newArray(int size) {
+            return new ModuleInfo[size];
+        }
+    };
+}
diff --git a/android/content/pm/PackageInfo.java b/android/content/pm/PackageInfo.java
new file mode 100644
index 0000000..d7abb68
--- /dev/null
+++ b/android/content/pm/PackageInfo.java
@@ -0,0 +1,557 @@
+/*
+ * 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.content.pm;
+
+import android.annotation.Nullable;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Overall information about the contents of a package.  This corresponds
+ * to all of the information collected from AndroidManifest.xml.
+ */
+public class PackageInfo implements Parcelable {
+    /**
+     * The name of this package.  From the &lt;manifest&gt; tag's "name"
+     * attribute.
+     */
+    public String packageName;
+
+    /**
+     * The names of any installed split APKs for this package.
+     */
+    public String[] splitNames;
+
+    /**
+     * @deprecated Use {@link #getLongVersionCode()} instead, which includes both
+     * this and the additional
+     * {@link android.R.styleable#AndroidManifest_versionCodeMajor versionCodeMajor} attribute.
+     * The version number of this package, as specified by the &lt;manifest&gt;
+     * tag's {@link android.R.styleable#AndroidManifest_versionCode versionCode}
+     * attribute.
+     * @see #getLongVersionCode()
+     */
+    @Deprecated
+    public int versionCode;
+
+    /**
+     * @hide
+     * The major version number of this package, as specified by the &lt;manifest&gt;
+     * tag's {@link android.R.styleable#AndroidManifest_versionCode versionCodeMajor}
+     * attribute.
+     * @see #getLongVersionCode()
+     */
+    public int versionCodeMajor;
+
+    /**
+     * Return {@link android.R.styleable#AndroidManifest_versionCode versionCode} and
+     * {@link android.R.styleable#AndroidManifest_versionCodeMajor versionCodeMajor} combined
+     * together as a single long value.  The
+     * {@link android.R.styleable#AndroidManifest_versionCodeMajor versionCodeMajor} is placed in
+     * the upper 32 bits.
+     */
+    public long getLongVersionCode() {
+        return composeLongVersionCode(versionCodeMajor, versionCode);
+    }
+
+    /**
+     * Set the full version code in this PackageInfo, updating {@link #versionCode}
+     * with the lower bits.
+     * @see #getLongVersionCode()
+     */
+    public void setLongVersionCode(long longVersionCode) {
+        versionCodeMajor = (int) (longVersionCode>>32);
+        versionCode = (int) longVersionCode;
+    }
+
+    /**
+     * @hide Internal implementation for composing a minor and major version code in to
+     * a single long version code.
+     */
+    public static long composeLongVersionCode(int major, int minor) {
+        return (((long) major) << 32) | (((long) minor) & 0xffffffffL);
+    }
+
+    /**
+     * The version name of this package, as specified by the &lt;manifest&gt;
+     * tag's {@link android.R.styleable#AndroidManifest_versionName versionName}
+     * attribute.
+     */
+    public String versionName;
+
+    /**
+     * The revision number of the base APK for this package, as specified by the
+     * &lt;manifest&gt; tag's
+     * {@link android.R.styleable#AndroidManifest_revisionCode revisionCode}
+     * attribute.
+     */
+    public int baseRevisionCode;
+
+    /**
+     * The revision number of any split APKs for this package, as specified by
+     * the &lt;manifest&gt; tag's
+     * {@link android.R.styleable#AndroidManifest_revisionCode revisionCode}
+     * attribute. Indexes are a 1:1 mapping against {@link #splitNames}.
+     */
+    public int[] splitRevisionCodes;
+
+    /**
+     * The shared user ID name of this package, as specified by the &lt;manifest&gt;
+     * tag's {@link android.R.styleable#AndroidManifest_sharedUserId sharedUserId}
+     * attribute.
+     */
+    public String sharedUserId;
+    
+    /**
+     * The shared user ID label of this package, as specified by the &lt;manifest&gt;
+     * tag's {@link android.R.styleable#AndroidManifest_sharedUserLabel sharedUserLabel}
+     * attribute.
+     */
+    public int sharedUserLabel;
+    
+    /**
+     * Information collected from the &lt;application&gt; tag, or null if
+     * there was none.
+     */
+    public ApplicationInfo applicationInfo;
+    
+    /**
+     * The time at which the app was first installed.  Units are as
+     * per {@link System#currentTimeMillis()}.
+     */
+    public long firstInstallTime;
+
+    /**
+     * The time at which the app was last updated.  Units are as
+     * per {@link System#currentTimeMillis()}.
+     */
+    public long lastUpdateTime;
+
+    /**
+     * All kernel group-IDs that have been assigned to this package.
+     * This is only filled in if the flag {@link PackageManager#GET_GIDS} was set.
+     */
+    public int[] gids;
+
+    /**
+     * Array of all {@link android.R.styleable#AndroidManifestActivity
+     * &lt;activity&gt;} tags included under &lt;application&gt;,
+     * or null if there were none.  This is only filled in if the flag
+     * {@link PackageManager#GET_ACTIVITIES} was set.
+     */
+    public ActivityInfo[] activities;
+
+    /**
+     * Array of all {@link android.R.styleable#AndroidManifestReceiver
+     * &lt;receiver&gt;} tags included under &lt;application&gt;,
+     * or null if there were none.  This is only filled in if the flag
+     * {@link PackageManager#GET_RECEIVERS} was set.
+     */
+    public ActivityInfo[] receivers;
+
+    /**
+     * Array of all {@link android.R.styleable#AndroidManifestService
+     * &lt;service&gt;} tags included under &lt;application&gt;,
+     * or null if there were none.  This is only filled in if the flag
+     * {@link PackageManager#GET_SERVICES} was set.
+     */
+    public ServiceInfo[] services;
+
+    /**
+     * Array of all {@link android.R.styleable#AndroidManifestProvider
+     * &lt;provider&gt;} tags included under &lt;application&gt;,
+     * or null if there were none.  This is only filled in if the flag
+     * {@link PackageManager#GET_PROVIDERS} was set.
+     */
+    public ProviderInfo[] providers;
+
+    /**
+     * Array of all {@link android.R.styleable#AndroidManifestInstrumentation
+     * &lt;instrumentation&gt;} tags included under &lt;manifest&gt;,
+     * or null if there were none.  This is only filled in if the flag
+     * {@link PackageManager#GET_INSTRUMENTATION} was set.
+     */
+    public InstrumentationInfo[] instrumentation;
+
+    /**
+     * Array of all {@link android.R.styleable#AndroidManifestPermission
+     * &lt;permission&gt;} tags included under &lt;manifest&gt;,
+     * or null if there were none.  This is only filled in if the flag
+     * {@link PackageManager#GET_PERMISSIONS} was set.
+     */
+    public PermissionInfo[] permissions;
+
+    /**
+     * Array of all {@link android.R.styleable#AndroidManifestUsesPermission
+     * &lt;uses-permission&gt;} tags included under &lt;manifest&gt;,
+     * or null if there were none.  This is only filled in if the flag
+     * {@link PackageManager#GET_PERMISSIONS} was set.  This list includes
+     * all permissions requested, even those that were not granted or known
+     * by the system at install time.
+     */
+    public String[] requestedPermissions;
+
+    /**
+     * Array of flags of all {@link android.R.styleable#AndroidManifestUsesPermission
+     * &lt;uses-permission&gt;} tags included under &lt;manifest&gt;,
+     * or null if there were none.  This is only filled in if the flag
+     * {@link PackageManager#GET_PERMISSIONS} was set.  Each value matches
+     * the corresponding entry in {@link #requestedPermissions}, and will have
+     * the flag {@link #REQUESTED_PERMISSION_GRANTED} set as appropriate.
+     */
+    public int[] requestedPermissionsFlags;
+
+    /**
+     * Flag for {@link #requestedPermissionsFlags}: the requested permission
+     * is required for the application to run; the user can not optionally
+     * disable it.  Currently all permissions are required.
+     *
+     * @removed We do not support required permissions.
+     */
+    public static final int REQUESTED_PERMISSION_REQUIRED = 1<<0;
+
+    /**
+     * Flag for {@link #requestedPermissionsFlags}: the requested permission
+     * is currently granted to the application.
+     */
+    public static final int REQUESTED_PERMISSION_GRANTED = 1<<1;
+
+    /**
+     * Array of all signatures read from the package file. This is only filled
+     * in if the flag {@link PackageManager#GET_SIGNATURES} was set. A package
+     * must be signed with at least one certificate which is at position zero.
+     * The package can be signed with additional certificates which appear as
+     * subsequent entries.
+     *
+     * <strong>Note:</strong> Signature ordering is not guaranteed to be
+     * stable which means that a package signed with certificates A and B is
+     * equivalent to being signed with certificates B and A. This means that
+     * in case multiple signatures are reported you cannot assume the one at
+     * the first position to be the same across updates.
+     *
+     * <strong>Deprecated</strong> This has been replaced by the
+     * {@link PackageInfo#signingInfo} field, which takes into
+     * account signing certificate rotation.  For backwards compatibility in
+     * the event of signing certificate rotation, this will return the oldest
+     * reported signing certificate, so that an application will appear to
+     * callers as though no rotation occurred.
+     *
+     * @deprecated use {@code signingInfo} instead
+     */
+    @Deprecated
+    public Signature[] signatures;
+
+    /**
+     * Signing information read from the package file, potentially
+     * including past signing certificates no longer used after signing
+     * certificate rotation.  This is only filled in if
+     * the flag {@link PackageManager#GET_SIGNING_CERTIFICATES} was set.
+     *
+     * Use this field instead of the deprecated {@code signatures} field.
+     * See {@link SigningInfo} for more information on its contents.
+     */
+    public SigningInfo signingInfo;
+
+    /**
+     * Application specified preferred configuration
+     * {@link android.R.styleable#AndroidManifestUsesConfiguration
+     * &lt;uses-configuration&gt;} tags included under &lt;manifest&gt;,
+     * or null if there were none. This is only filled in if the flag
+     * {@link PackageManager#GET_CONFIGURATIONS} was set.
+     */
+    public ConfigurationInfo[] configPreferences;
+
+    /**
+     * Features that this application has requested.
+     *
+     * @see FeatureInfo#FLAG_REQUIRED
+     */
+    public FeatureInfo[] reqFeatures;
+
+    /**
+     * Groups of features that this application has requested.
+     * Each group contains a set of features that are required.
+     * A device must match the features listed in {@link #reqFeatures} and one
+     * or more FeatureGroups in order to have satisfied the feature requirement.
+     *
+     * @see FeatureInfo#FLAG_REQUIRED
+     */
+    public FeatureGroupInfo[] featureGroups;
+
+    /**
+     * Constant corresponding to <code>auto</code> in
+     * the {@link android.R.attr#installLocation} attribute.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static final int INSTALL_LOCATION_UNSPECIFIED = -1;
+
+    /**
+     * Constant corresponding to <code>auto</code> in the
+     * {@link android.R.attr#installLocation} attribute.
+     */
+    public static final int INSTALL_LOCATION_AUTO = 0;
+
+    /**
+     * Constant corresponding to <code>internalOnly</code> in the
+     * {@link android.R.attr#installLocation} attribute.
+     */
+    public static final int INSTALL_LOCATION_INTERNAL_ONLY = 1;
+
+    /**
+     * Constant corresponding to <code>preferExternal</code> in the
+     * {@link android.R.attr#installLocation} attribute.
+     */
+    public static final int INSTALL_LOCATION_PREFER_EXTERNAL = 2;
+
+    /**
+     * The install location requested by the package. From the
+     * {@link android.R.attr#installLocation} attribute, one of
+     * {@link #INSTALL_LOCATION_AUTO}, {@link #INSTALL_LOCATION_INTERNAL_ONLY},
+     * {@link #INSTALL_LOCATION_PREFER_EXTERNAL}
+     */
+    public int installLocation = INSTALL_LOCATION_INTERNAL_ONLY;
+
+    /** @hide */
+    public boolean isStub;
+
+    /** @hide */
+    @UnsupportedAppUsage
+    public boolean coreApp;
+
+    /** @hide */
+    public boolean requiredForAllUsers;
+
+    /** @hide */
+    public String restrictedAccountType;
+
+    /** @hide */
+    public String requiredAccountType;
+
+    /**
+     * What package, if any, this package will overlay.
+     *
+     * Package name of target package, or null.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public String overlayTarget;
+
+    /**
+     * The name of the overlayable set of elements package, if any, this package will overlay.
+     *
+     * Overlayable name defined within the target package, or null.
+     * @hide
+     */
+    public String targetOverlayableName;
+
+    /**
+     * The overlay category, if any, of this package
+     *
+     * @hide
+     */
+    public String overlayCategory;
+
+    /** @hide */
+    public int overlayPriority;
+
+    /**
+     * Whether the overlay is static, meaning it cannot be enabled/disabled at runtime.
+     * @hide
+     */
+    public boolean mOverlayIsStatic;
+
+    /**
+     * The user-visible SDK version (ex. 26) of the framework against which the application claims
+     * to have been compiled, or {@code 0} if not specified.
+     * <p>
+     * This property is the compile-time equivalent of
+     * {@link android.os.Build.VERSION#SDK_INT Build.VERSION.SDK_INT}.
+     *
+     * @hide For platform use only; we don't expect developers to need to read this value.
+     */
+    public int compileSdkVersion;
+
+    /**
+     * The development codename (ex. "O", "REL") of the framework against which the application
+     * claims to have been compiled, or {@code null} if not specified.
+     * <p>
+     * This property is the compile-time equivalent of
+     * {@link android.os.Build.VERSION#CODENAME Build.VERSION.CODENAME}.
+     *
+     * @hide For platform use only; we don't expect developers to need to read this value.
+     */
+    @Nullable
+    public String compileSdkVersionCodename;
+
+    /**
+     * Whether the package is an APEX package.
+     */
+    public boolean isApex;
+
+    public PackageInfo() {
+    }
+
+    /**
+     * Returns true if the package is a valid Runtime Overlay package.
+     * @hide
+     */
+    public boolean isOverlayPackage() {
+        return overlayTarget != null;
+    }
+
+    /**
+     * Returns true if the package is a valid static Runtime Overlay package. Static overlays
+     * are not updatable outside of a system update and are safe to load in the system process.
+     * @hide
+     */
+    public boolean isStaticOverlayPackage() {
+        return overlayTarget != null && mOverlayIsStatic;
+    }
+
+    @Override
+    public String toString() {
+        return "PackageInfo{"
+            + Integer.toHexString(System.identityHashCode(this))
+            + " " + packageName + "}";
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int parcelableFlags) {
+        // Allow ApplicationInfo to be squashed.
+        final boolean prevAllowSquashing = dest.allowSquashing();
+        dest.writeString8(packageName);
+        dest.writeString8Array(splitNames);
+        dest.writeInt(versionCode);
+        dest.writeInt(versionCodeMajor);
+        dest.writeString8(versionName);
+        dest.writeInt(baseRevisionCode);
+        dest.writeIntArray(splitRevisionCodes);
+        dest.writeString8(sharedUserId);
+        dest.writeInt(sharedUserLabel);
+        if (applicationInfo != null) {
+            dest.writeInt(1);
+            applicationInfo.writeToParcel(dest, parcelableFlags);
+        } else {
+            dest.writeInt(0);
+        }
+        dest.writeLong(firstInstallTime);
+        dest.writeLong(lastUpdateTime);
+        dest.writeIntArray(gids);
+        dest.writeTypedArray(activities, parcelableFlags);
+        dest.writeTypedArray(receivers, parcelableFlags);
+        dest.writeTypedArray(services, parcelableFlags);
+        dest.writeTypedArray(providers, parcelableFlags);
+        dest.writeTypedArray(instrumentation, parcelableFlags);
+        dest.writeTypedArray(permissions, parcelableFlags);
+        dest.writeString8Array(requestedPermissions);
+        dest.writeIntArray(requestedPermissionsFlags);
+        dest.writeTypedArray(signatures, parcelableFlags);
+        dest.writeTypedArray(configPreferences, parcelableFlags);
+        dest.writeTypedArray(reqFeatures, parcelableFlags);
+        dest.writeTypedArray(featureGroups, parcelableFlags);
+        dest.writeInt(installLocation);
+        dest.writeInt(isStub ? 1 : 0);
+        dest.writeInt(coreApp ? 1 : 0);
+        dest.writeInt(requiredForAllUsers ? 1 : 0);
+        dest.writeString8(restrictedAccountType);
+        dest.writeString8(requiredAccountType);
+        dest.writeString8(overlayTarget);
+        dest.writeString8(overlayCategory);
+        dest.writeInt(overlayPriority);
+        dest.writeBoolean(mOverlayIsStatic);
+        dest.writeInt(compileSdkVersion);
+        dest.writeString8(compileSdkVersionCodename);
+        if (signingInfo != null) {
+            dest.writeInt(1);
+            signingInfo.writeToParcel(dest, parcelableFlags);
+        } else {
+            dest.writeInt(0);
+        }
+        dest.writeBoolean(isApex);
+        dest.restoreAllowSquashing(prevAllowSquashing);
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<PackageInfo> CREATOR
+            = new Parcelable.Creator<PackageInfo>() {
+        @Override
+        public PackageInfo createFromParcel(Parcel source) {
+            return new PackageInfo(source);
+        }
+
+        @Override
+        public PackageInfo[] newArray(int size) {
+            return new PackageInfo[size];
+        }
+    };
+
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    private PackageInfo(Parcel source) {
+        packageName = source.readString8();
+        splitNames = source.createString8Array();
+        versionCode = source.readInt();
+        versionCodeMajor = source.readInt();
+        versionName = source.readString8();
+        baseRevisionCode = source.readInt();
+        splitRevisionCodes = source.createIntArray();
+        sharedUserId = source.readString8();
+        sharedUserLabel = source.readInt();
+        int hasApp = source.readInt();
+        if (hasApp != 0) {
+            applicationInfo = ApplicationInfo.CREATOR.createFromParcel(source);
+        }
+        firstInstallTime = source.readLong();
+        lastUpdateTime = source.readLong();
+        gids = source.createIntArray();
+        activities = source.createTypedArray(ActivityInfo.CREATOR);
+        receivers = source.createTypedArray(ActivityInfo.CREATOR);
+        services = source.createTypedArray(ServiceInfo.CREATOR);
+        providers = source.createTypedArray(ProviderInfo.CREATOR);
+        instrumentation = source.createTypedArray(InstrumentationInfo.CREATOR);
+        permissions = source.createTypedArray(PermissionInfo.CREATOR);
+        requestedPermissions = source.createString8Array();
+        requestedPermissionsFlags = source.createIntArray();
+        signatures = source.createTypedArray(Signature.CREATOR);
+        configPreferences = source.createTypedArray(ConfigurationInfo.CREATOR);
+        reqFeatures = source.createTypedArray(FeatureInfo.CREATOR);
+        featureGroups = source.createTypedArray(FeatureGroupInfo.CREATOR);
+        installLocation = source.readInt();
+        isStub = source.readInt() != 0;
+        coreApp = source.readInt() != 0;
+        requiredForAllUsers = source.readInt() != 0;
+        restrictedAccountType = source.readString8();
+        requiredAccountType = source.readString8();
+        overlayTarget = source.readString8();
+        overlayCategory = source.readString8();
+        overlayPriority = source.readInt();
+        mOverlayIsStatic = source.readBoolean();
+        compileSdkVersion = source.readInt();
+        compileSdkVersionCodename = source.readString8();
+        int hasSigningInfo = source.readInt();
+        if (hasSigningInfo != 0) {
+            signingInfo = SigningInfo.CREATOR.createFromParcel(source);
+        }
+        isApex = source.readBoolean();
+    }
+}
diff --git a/android/content/pm/PackageInfoLite.java b/android/content/pm/PackageInfoLite.java
new file mode 100644
index 0000000..9735f81
--- /dev/null
+++ b/android/content/pm/PackageInfoLite.java
@@ -0,0 +1,158 @@
+/*
+ * 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.content.pm;
+
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.content.PackageHelper;
+
+/**
+ * Basic information about a package as specified in its manifest.
+ * Utility class used in PackageManager methods
+ * @hide
+ */
+public class PackageInfoLite implements Parcelable {
+    /**
+     * The name of this package.  From the &lt;manifest&gt; tag's "name"
+     * attribute.
+     */
+    public String packageName;
+
+    /** Names of any split APKs, ordered by parsed splitName */
+    public String[] splitNames;
+
+    /**
+     * The android:versionCode of the package.
+     * @deprecated Use {@link #getLongVersionCode()} instead, which includes both
+     * this and the additional
+     * {@link android.R.styleable#AndroidManifest_versionCode versionCodeMajor} attribute.
+     */
+    @Deprecated
+    public int versionCode;
+
+    /**
+     * @hide
+     * The android:versionCodeMajor of the package.
+     */
+    public int versionCodeMajor;
+
+    /**
+     * Return {@link #versionCode} and {@link #versionCodeMajor} combined together as a
+     * single long value.  The {@link #versionCodeMajor} is placed in the upper 32 bits.
+     */
+    public long getLongVersionCode() {
+        return PackageInfo.composeLongVersionCode(versionCodeMajor, versionCode);
+    }
+
+    /** Revision code of base APK */
+    public int baseRevisionCode;
+    /** Revision codes of any split APKs, ordered by parsed splitName */
+    public int[] splitRevisionCodes;
+
+    /**
+     * The android:multiArch flag from the package manifest. If set,
+     * we will extract all native libraries for the given app, not just those
+     * from the preferred ABI.
+     */
+    public boolean multiArch;
+
+    /**
+     * The android:debuggable flag from the package manifest.
+     */
+    public boolean debuggable;
+
+    /**
+     * Specifies the recommended install location. Can be one of
+     * {@link PackageHelper#RECOMMEND_INSTALL_INTERNAL} to install on internal storage,
+     * {@link PackageHelper#RECOMMEND_INSTALL_EXTERNAL} to install on external media,
+     * {@link PackageHelper#RECOMMEND_FAILED_INSUFFICIENT_STORAGE} for storage errors,
+     * or {@link PackageHelper#RECOMMEND_FAILED_INVALID_APK} for parse errors.
+     */
+    public int recommendedInstallLocation;
+    public int installLocation;
+
+    public VerifierInfo[] verifiers;
+
+    public PackageInfoLite() {
+    }
+
+    public String toString() {
+        return "PackageInfoLite{"
+            + Integer.toHexString(System.identityHashCode(this))
+            + " " + packageName + "}";
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel dest, int parcelableFlags) {
+        dest.writeString(packageName);
+        dest.writeStringArray(splitNames);
+        dest.writeInt(versionCode);
+        dest.writeInt(versionCodeMajor);
+        dest.writeInt(baseRevisionCode);
+        dest.writeIntArray(splitRevisionCodes);
+        dest.writeInt(recommendedInstallLocation);
+        dest.writeInt(installLocation);
+        dest.writeInt(multiArch ? 1 : 0);
+        dest.writeInt(debuggable ? 1 : 0);
+
+        if (verifiers == null || verifiers.length == 0) {
+            dest.writeInt(0);
+        } else {
+            dest.writeInt(verifiers.length);
+            dest.writeTypedArray(verifiers, parcelableFlags);
+        }
+    }
+
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    public static final @android.annotation.NonNull Parcelable.Creator<PackageInfoLite> CREATOR
+            = new Parcelable.Creator<PackageInfoLite>() {
+        public PackageInfoLite createFromParcel(Parcel source) {
+            return new PackageInfoLite(source);
+        }
+
+        public PackageInfoLite[] newArray(int size) {
+            return new PackageInfoLite[size];
+        }
+    };
+
+    private PackageInfoLite(Parcel source) {
+        packageName = source.readString();
+        splitNames = source.createStringArray();
+        versionCode = source.readInt();
+        versionCodeMajor = source.readInt();
+        baseRevisionCode = source.readInt();
+        splitRevisionCodes = source.createIntArray();
+        recommendedInstallLocation = source.readInt();
+        installLocation = source.readInt();
+        multiArch = (source.readInt() != 0);
+        debuggable = (source.readInt() != 0);
+
+        final int verifiersLength = source.readInt();
+        if (verifiersLength == 0) {
+            verifiers = new VerifierInfo[0];
+        } else {
+            verifiers = new VerifierInfo[verifiersLength];
+            source.readTypedArray(verifiers, VerifierInfo.CREATOR);
+        }
+    }
+}
diff --git a/android/content/pm/PackageInstaller.java b/android/content/pm/PackageInstaller.java
new file mode 100644
index 0000000..85a3986
--- /dev/null
+++ b/android/content/pm/PackageInstaller.java
@@ -0,0 +1,2750 @@
+/*
+ * 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.content.pm;
+
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.MODE_DEFAULT;
+import static android.app.AppOpsManager.MODE_IGNORED;
+
+import android.Manifest;
+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.SystemApi;
+import android.annotation.TestApi;
+import android.app.ActivityManager;
+import android.app.AppGlobals;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.content.pm.PackageManager.DeleteFlags;
+import android.content.pm.PackageManager.InstallReason;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.os.Build;
+import android.os.FileBridge;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
+import android.os.Parcelable;
+import android.os.ParcelableException;
+import android.os.RemoteException;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.util.ArraySet;
+import android.util.ExceptionUtils;
+
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.function.pooled.PooledLambda;
+
+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.security.MessageDigest;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.Executor;
+
+/**
+ * Offers the ability to install, upgrade, and remove applications on the
+ * device. This includes support for apps packaged either as a single
+ * "monolithic" APK, or apps packaged as multiple "split" APKs.
+ * <p>
+ * An app is delivered for installation through a
+ * {@link PackageInstaller.Session}, which any app can create. Once the session
+ * is created, the installer can stream one or more APKs into place until it
+ * decides to either commit or destroy the session. Committing may require user
+ * intervention to complete the installation, unless the caller falls into one of the
+ * following categories, in which case the installation will complete automatically.
+ * <ul>
+ * <li>the device owner
+ * <li>the affiliated profile owner
+ * </ul>
+ * <p>
+ * Sessions can install brand new apps, upgrade existing apps, or add new splits
+ * into an existing app.
+ * <p>
+ * Apps packaged as multiple split APKs always consist of a single "base" APK
+ * (with a {@code null} split name) and zero or more "split" APKs (with unique
+ * split names). Any subset of these APKs can be installed together, as long as
+ * the following constraints are met:
+ * <ul>
+ * <li>All APKs must have the exact same package name, version code, and signing
+ * certificates.
+ * <li>All APKs must have unique split names.
+ * <li>All installations must contain a single base APK.
+ * </ul>
+ * <p>
+ * The ApiDemos project contains examples of using this API:
+ * <code>ApiDemos/src/com/example/android/apis/content/InstallApk*.java</code>.
+ */
+public class PackageInstaller {
+    private static final String TAG = "PackageInstaller";
+
+    /** {@hide} */
+    public static final boolean ENABLE_REVOCABLE_FD =
+            SystemProperties.getBoolean("fw.revocable_fd", false);
+
+    /**
+     * Activity Action: Show details about a particular install session. This
+     * may surface actions such as pause, resume, or cancel.
+     * <p>
+     * This should always be scoped to the installer package that owns the
+     * session. Clients should use {@link SessionInfo#createDetailsIntent()} to
+     * build this intent correctly.
+     * <p>
+     * In some cases, a matching Activity may not exist, so ensure you safeguard
+     * against this.
+     * <p>
+     * The session to show details for is defined in {@link #EXTRA_SESSION_ID}.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_SESSION_DETAILS = "android.content.pm.action.SESSION_DETAILS";
+
+    /**
+     * Broadcast Action: Explicit broadcast sent to the last known default launcher when a session
+     * for a new install is committed. For managed profile, this is sent to the default launcher
+     * of the primary profile.
+     * <p>
+     * The associated session is defined in {@link #EXTRA_SESSION} and the user for which this
+     * session was created in {@link Intent#EXTRA_USER}.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_SESSION_COMMITTED =
+            "android.content.pm.action.SESSION_COMMITTED";
+
+    /**
+     * Broadcast Action: Send information about a staged install session when its state is updated.
+     * <p>
+     * The associated session information is defined in {@link #EXTRA_SESSION}.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_SESSION_UPDATED =
+            "android.content.pm.action.SESSION_UPDATED";
+
+    /** {@hide} */
+    public static final String ACTION_CONFIRM_INSTALL = "android.content.pm.action.CONFIRM_INSTALL";
+
+    /**
+     * An integer session ID that an operation is working with.
+     *
+     * @see Intent#getIntExtra(String, int)
+     */
+    public static final String EXTRA_SESSION_ID = "android.content.pm.extra.SESSION_ID";
+
+    /**
+     * {@link SessionInfo} that an operation is working with.
+     *
+     * @see Intent#getParcelableExtra(String)
+     */
+    public static final String EXTRA_SESSION = "android.content.pm.extra.SESSION";
+
+    /**
+     * Package name that an operation is working with.
+     *
+     * @see Intent#getStringExtra(String)
+     */
+    public static final String EXTRA_PACKAGE_NAME = "android.content.pm.extra.PACKAGE_NAME";
+
+    /**
+     * Current status of an operation. Will be one of
+     * {@link #STATUS_PENDING_USER_ACTION}, {@link #STATUS_SUCCESS},
+     * {@link #STATUS_FAILURE}, {@link #STATUS_FAILURE_ABORTED},
+     * {@link #STATUS_FAILURE_BLOCKED}, {@link #STATUS_FAILURE_CONFLICT},
+     * {@link #STATUS_FAILURE_INCOMPATIBLE}, {@link #STATUS_FAILURE_INVALID}, or
+     * {@link #STATUS_FAILURE_STORAGE}.
+     * <p>
+     * More information about a status may be available through additional
+     * extras; see the individual status documentation for details.
+     *
+     * @see Intent#getIntExtra(String, int)
+     */
+    public static final String EXTRA_STATUS = "android.content.pm.extra.STATUS";
+
+    /**
+     * Detailed string representation of the status, including raw details that
+     * are useful for debugging.
+     *
+     * @see Intent#getStringExtra(String)
+     */
+    public static final String EXTRA_STATUS_MESSAGE = "android.content.pm.extra.STATUS_MESSAGE";
+
+    /**
+     * Another package name relevant to a status. This is typically the package
+     * responsible for causing an operation failure.
+     *
+     * @see Intent#getStringExtra(String)
+     */
+    public static final String
+            EXTRA_OTHER_PACKAGE_NAME = "android.content.pm.extra.OTHER_PACKAGE_NAME";
+
+    /**
+     * Storage path relevant to a status.
+     *
+     * @see Intent#getStringExtra(String)
+     */
+    public static final String EXTRA_STORAGE_PATH = "android.content.pm.extra.STORAGE_PATH";
+
+    /** {@hide} */
+    @Deprecated
+    public static final String EXTRA_PACKAGE_NAMES = "android.content.pm.extra.PACKAGE_NAMES";
+
+    /** {@hide} */
+    public static final String EXTRA_LEGACY_STATUS = "android.content.pm.extra.LEGACY_STATUS";
+    /** {@hide} */
+    public static final String EXTRA_LEGACY_BUNDLE = "android.content.pm.extra.LEGACY_BUNDLE";
+    /** {@hide} */
+    public static final String EXTRA_CALLBACK = "android.content.pm.extra.CALLBACK";
+
+    /**
+     * Type of DataLoader for this session. Will be one of
+     * {@link #DATA_LOADER_TYPE_NONE}, {@link #DATA_LOADER_TYPE_STREAMING},
+     * {@link #DATA_LOADER_TYPE_INCREMENTAL}.
+     * <p>
+     * See the individual types documentation for details.
+     *
+     * @see Intent#getIntExtra(String, int)
+     * {@hide}
+     */
+    @SystemApi
+    public static final String EXTRA_DATA_LOADER_TYPE = "android.content.pm.extra.DATA_LOADER_TYPE";
+
+    /**
+     * Streaming installation pending.
+     * Caller should make sure DataLoader is able to prepare image and reinitiate the operation.
+     *
+     * @see #EXTRA_SESSION_ID
+     * {@hide}
+     */
+    public static final int STATUS_PENDING_STREAMING = -2;
+
+    /**
+     * User action is currently required to proceed. You can launch the intent
+     * activity described by {@link Intent#EXTRA_INTENT} to involve the user and
+     * continue.
+     * <p>
+     * You may choose to immediately launch the intent if the user is actively
+     * using your app. Otherwise, you should use a notification to guide the
+     * user back into your app before launching.
+     *
+     * @see Intent#getParcelableExtra(String)
+     */
+    public static final int STATUS_PENDING_USER_ACTION = -1;
+
+    /**
+     * The operation succeeded.
+     */
+    public static final int STATUS_SUCCESS = 0;
+
+    /**
+     * The operation failed in a generic way. The system will always try to
+     * provide a more specific failure reason, but in some rare cases this may
+     * be delivered.
+     *
+     * @see #EXTRA_STATUS_MESSAGE
+     */
+    public static final int STATUS_FAILURE = 1;
+
+    /**
+     * The operation failed because it was blocked. For example, a device policy
+     * may be blocking the operation, a package verifier may have blocked the
+     * operation, or the app may be required for core system operation.
+     * <p>
+     * The result may also contain {@link #EXTRA_OTHER_PACKAGE_NAME} with the
+     * specific package blocking the install.
+     *
+     * @see #EXTRA_STATUS_MESSAGE
+     * @see #EXTRA_OTHER_PACKAGE_NAME
+     */
+    public static final int STATUS_FAILURE_BLOCKED = 2;
+
+    /**
+     * The operation failed because it was actively aborted. For example, the
+     * user actively declined requested permissions, or the session was
+     * abandoned.
+     *
+     * @see #EXTRA_STATUS_MESSAGE
+     */
+    public static final int STATUS_FAILURE_ABORTED = 3;
+
+    /**
+     * The operation failed because one or more of the APKs was invalid. For
+     * example, they might be malformed, corrupt, incorrectly signed,
+     * mismatched, etc.
+     *
+     * @see #EXTRA_STATUS_MESSAGE
+     */
+    public static final int STATUS_FAILURE_INVALID = 4;
+
+    /**
+     * The operation failed because it conflicts (or is inconsistent with) with
+     * another package already installed on the device. For example, an existing
+     * permission, incompatible certificates, etc. The user may be able to
+     * uninstall another app to fix the issue.
+     * <p>
+     * The result may also contain {@link #EXTRA_OTHER_PACKAGE_NAME} with the
+     * specific package identified as the cause of the conflict.
+     *
+     * @see #EXTRA_STATUS_MESSAGE
+     * @see #EXTRA_OTHER_PACKAGE_NAME
+     */
+    public static final int STATUS_FAILURE_CONFLICT = 5;
+
+    /**
+     * The operation failed because of storage issues. For example, the device
+     * may be running low on space, or external media may be unavailable. The
+     * user may be able to help free space or insert different external media.
+     * <p>
+     * The result may also contain {@link #EXTRA_STORAGE_PATH} with the path to
+     * the storage device that caused the failure.
+     *
+     * @see #EXTRA_STATUS_MESSAGE
+     * @see #EXTRA_STORAGE_PATH
+     */
+    public static final int STATUS_FAILURE_STORAGE = 6;
+
+    /**
+     * The operation failed because it is fundamentally incompatible with this
+     * device. For example, the app may require a hardware feature that doesn't
+     * exist, it may be missing native code for the ABIs supported by the
+     * device, or it requires a newer SDK version, etc.
+     *
+     * @see #EXTRA_STATUS_MESSAGE
+     */
+    public static final int STATUS_FAILURE_INCOMPATIBLE = 7;
+
+    /**
+     * Default value, non-streaming installation session.
+     *
+     * @see #EXTRA_DATA_LOADER_TYPE
+     * {@hide}
+     */
+    @SystemApi
+    public static final int DATA_LOADER_TYPE_NONE = DataLoaderType.NONE;
+
+    /**
+     * Streaming installation using data loader.
+     *
+     * @see #EXTRA_DATA_LOADER_TYPE
+     * {@hide}
+     */
+    @SystemApi
+    public static final int DATA_LOADER_TYPE_STREAMING = DataLoaderType.STREAMING;
+
+    /**
+     * Streaming installation using Incremental FileSystem.
+     *
+     * @see #EXTRA_DATA_LOADER_TYPE
+     * {@hide}
+     */
+    @SystemApi
+    public static final int DATA_LOADER_TYPE_INCREMENTAL = DataLoaderType.INCREMENTAL;
+
+    /**
+     * Target location for the file in installation session is /data/app/<packageName>-<id>.
+     * This is the intended location for APKs.
+     * Requires permission to install packages.
+     * {@hide}
+     */
+    @SystemApi
+    public static final int LOCATION_DATA_APP = InstallationFileLocation.DATA_APP;
+
+    /**
+     * Target location for the file in installation session is
+     * /data/media/<userid>/Android/obb/<packageName>. This is the intended location for OBBs.
+     * {@hide}
+     */
+    @SystemApi
+    public static final int LOCATION_MEDIA_OBB = InstallationFileLocation.MEDIA_OBB;
+
+    /**
+     * Target location for the file in installation session is
+     * /data/media/<userid>/Android/data/<packageName>.
+     * This is the intended location for application data.
+     * Can only be used by an app itself running under specific user.
+     * {@hide}
+     */
+    @SystemApi
+    public static final int LOCATION_MEDIA_DATA = InstallationFileLocation.MEDIA_DATA;
+
+    /** @hide */
+    @IntDef(prefix = { "LOCATION_" }, value = {
+            LOCATION_DATA_APP,
+            LOCATION_MEDIA_OBB,
+            LOCATION_MEDIA_DATA})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FileLocation{}
+
+    private final IPackageInstaller mInstaller;
+    private final int mUserId;
+    private final String mInstallerPackageName;
+
+    private final ArrayList<SessionCallbackDelegate> mDelegates = new ArrayList<>();
+
+    /** {@hide} */
+    public PackageInstaller(IPackageInstaller installer,
+            String installerPackageName, int userId) {
+        mInstaller = installer;
+        mInstallerPackageName = installerPackageName;
+        mUserId = userId;
+    }
+
+    /**
+     * Create a new session using the given parameters, returning a unique ID
+     * that represents the session. Once created, the session can be opened
+     * multiple times across multiple device boots.
+     * <p>
+     * The system may automatically destroy sessions that have not been
+     * finalized (either committed or abandoned) within a reasonable period of
+     * time, typically on the order of a day.
+     *
+     * @throws IOException if parameters were unsatisfiable, such as lack of
+     *             disk space or unavailable media.
+     * @throws SecurityException when installation services are unavailable,
+     *             such as when called from a restricted user.
+     * @throws IllegalArgumentException when {@link SessionParams} is invalid.
+     * @return positive, non-zero unique ID that represents the created session.
+     *         This ID remains consistent across device reboots until the
+     *         session is finalized. IDs are not reused during a given boot.
+     */
+    public int createSession(@NonNull SessionParams params) throws IOException {
+        try {
+            return mInstaller.createSession(params, mInstallerPackageName, mUserId);
+        } catch (RuntimeException e) {
+            ExceptionUtils.maybeUnwrapIOException(e);
+            throw e;
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Open an existing session to actively perform work. To succeed, the caller
+     * must be the owner of the install session.
+     *
+     * @throws IOException if parameters were unsatisfiable, such as lack of
+     *             disk space or unavailable media.
+     * @throws SecurityException when the caller does not own the session, or
+     *             the session is invalid.
+     */
+    public @NonNull Session openSession(int sessionId) throws IOException {
+        try {
+            try {
+                return new Session(mInstaller.openSession(sessionId));
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        } catch (RuntimeException e) {
+            ExceptionUtils.maybeUnwrapIOException(e);
+            throw e;
+        }
+    }
+
+    /**
+     * Update the icon representing the app being installed in a specific
+     * session. This should be roughly
+     * {@link ActivityManager#getLauncherLargeIconSize()} in both dimensions.
+     *
+     * @throws SecurityException when the caller does not own the session, or
+     *             the session is invalid.
+     */
+    public void updateSessionAppIcon(int sessionId, @Nullable Bitmap appIcon) {
+        try {
+            mInstaller.updateSessionAppIcon(sessionId, appIcon);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Update the label representing the app being installed in a specific
+     * session.
+     *
+     * @throws SecurityException when the caller does not own the session, or
+     *             the session is invalid.
+     */
+    public void updateSessionAppLabel(int sessionId, @Nullable CharSequence appLabel) {
+        try {
+            final String val = (appLabel != null) ? appLabel.toString() : null;
+            mInstaller.updateSessionAppLabel(sessionId, val);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Completely abandon the given session, destroying all staged data and
+     * rendering it invalid. Abandoned sessions will be reported to
+     * {@link SessionCallback} listeners as failures. This is equivalent to
+     * opening the session and calling {@link Session#abandon()}.
+     *
+     * @throws SecurityException when the caller does not own the session, or
+     *             the session is invalid.
+     */
+    public void abandonSession(int sessionId) {
+        try {
+            mInstaller.abandonSession(sessionId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Return details for a specific session. No special permissions are
+     * required to retrieve these details.
+     *
+     * @return details for the requested session, or {@code null} if the session
+     *         does not exist.
+     */
+    public @Nullable SessionInfo getSessionInfo(int sessionId) {
+        try {
+            return mInstaller.getSessionInfo(sessionId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Return list of all known install sessions, regardless of the installer.
+     */
+    public @NonNull List<SessionInfo> getAllSessions() {
+        try {
+            return mInstaller.getAllSessions(mUserId).getList();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Return list of all known install sessions owned by the calling app.
+     */
+    public @NonNull List<SessionInfo> getMySessions() {
+        try {
+            return mInstaller.getMySessions(mInstallerPackageName, mUserId).getList();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Return list of all staged install sessions.
+     */
+    public @NonNull List<SessionInfo> getStagedSessions() {
+        try {
+            // TODO: limit this to the mUserId?
+            return mInstaller.getStagedSessions().getList();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns first active staged session, or {@code null} if there is none.
+     *
+     * <p>For more information on what sessions are considered active see
+     * {@link SessionInfo#isStagedSessionActive()}.
+     *
+     * @deprecated Use {@link #getActiveStagedSessions} as there can be more than one active staged
+     * session
+     */
+    @Deprecated
+    public @Nullable SessionInfo getActiveStagedSession() {
+        List<SessionInfo> activeSessions = getActiveStagedSessions();
+        return activeSessions.isEmpty() ? null : activeSessions.get(0);
+    }
+
+    /**
+     * Returns list of active staged sessions. Returns empty list if there is none.
+     *
+     * <p>For more information on what sessions are considered active see
+     *      * {@link SessionInfo#isStagedSessionActive()}.
+     */
+    public @NonNull List<SessionInfo> getActiveStagedSessions() {
+        final List<SessionInfo> activeStagedSessions = new ArrayList<>();
+        final List<SessionInfo> stagedSessions = getStagedSessions();
+        for (int i = 0; i < stagedSessions.size(); i++) {
+            final SessionInfo sessionInfo = stagedSessions.get(i);
+            if (sessionInfo.isStagedSessionActive()) {
+                activeStagedSessions.add(sessionInfo);
+            }
+        }
+        return activeStagedSessions;
+    }
+
+    /**
+     * Uninstall the given package, removing it completely from the device. This
+     * method is available to:
+     * <ul>
+     * <li>the current "installer of record" for the package
+     * <li>the device owner
+     * <li>the affiliated profile owner
+     * </ul>
+     *
+     * @param packageName The package to uninstall.
+     * @param statusReceiver Where to deliver the result.
+     *
+     * @see android.app.admin.DevicePolicyManager
+     */
+    @RequiresPermission(anyOf = {
+            Manifest.permission.DELETE_PACKAGES,
+            Manifest.permission.REQUEST_DELETE_PACKAGES})
+    public void uninstall(@NonNull String packageName, @NonNull IntentSender statusReceiver) {
+        uninstall(packageName, 0 /*flags*/, statusReceiver);
+    }
+
+    /**
+     * Uninstall the given package, removing it completely from the device. This
+     * method is only available to the current "installer of record" for the
+     * package.
+     *
+     * @param packageName The package to uninstall.
+     * @param flags Flags for uninstall.
+     * @param statusReceiver Where to deliver the result.
+     *
+     * @hide
+     */
+    public void uninstall(@NonNull String packageName, @DeleteFlags int flags,
+            @NonNull IntentSender statusReceiver) {
+        uninstall(new VersionedPackage(packageName, PackageManager.VERSION_CODE_HIGHEST),
+                flags, statusReceiver);
+    }
+
+    /**
+     * Uninstall the given package with a specific version code, removing it
+     * completely from the device. If the version code of the package
+     * does not match the one passed in the versioned package argument this
+     * method is a no-op. Use {@link PackageManager#VERSION_CODE_HIGHEST} to
+     * uninstall the latest version of the package.
+     * <p>
+     * This method is available to:
+     * <ul>
+     * <li>the current "installer of record" for the package
+     * <li>the device owner
+     * <li>the affiliated profile owner
+     * </ul>
+     *
+     * @param versionedPackage The versioned package to uninstall.
+     * @param statusReceiver Where to deliver the result.
+     *
+     * @see android.app.admin.DevicePolicyManager
+     */
+    @RequiresPermission(anyOf = {
+            Manifest.permission.DELETE_PACKAGES,
+            Manifest.permission.REQUEST_DELETE_PACKAGES})
+    public void uninstall(@NonNull VersionedPackage versionedPackage,
+            @NonNull IntentSender statusReceiver) {
+        uninstall(versionedPackage, 0 /*flags*/, statusReceiver);
+    }
+
+    /**
+     * Uninstall the given package with a specific version code, removing it
+     * completely from the device. This method is only available to the current
+     * "installer of record" for the package. If the version code of the package
+     * does not match the one passed in the versioned package argument this
+     * method is a no-op. Use {@link PackageManager#VERSION_CODE_HIGHEST} to
+     * uninstall the latest version of the package.
+     *
+     * @param versionedPackage The versioned package to uninstall.
+     * @param flags Flags for uninstall.
+     * @param statusReceiver Where to deliver the result.
+     *
+     * @hide
+     */
+    @RequiresPermission(anyOf = {
+            Manifest.permission.DELETE_PACKAGES,
+            Manifest.permission.REQUEST_DELETE_PACKAGES})
+    public void uninstall(@NonNull VersionedPackage versionedPackage, @DeleteFlags int flags,
+            @NonNull IntentSender statusReceiver) {
+        Objects.requireNonNull(versionedPackage, "versionedPackage cannot be null");
+        try {
+            mInstaller.uninstall(versionedPackage, mInstallerPackageName,
+                    flags, statusReceiver, mUserId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Install the given package, which already exists on the device, for the user for which this
+     * installer was created.
+     *
+     * <p>This will
+     * {@link PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set) whitelist
+     * all restricted permissions}.
+     *
+     * @param packageName The package to install.
+     * @param installReason Reason for install.
+     * @param statusReceiver Where to deliver the result.
+     */
+    @RequiresPermission(allOf = {
+            Manifest.permission.INSTALL_PACKAGES,
+            Manifest.permission.INSTALL_EXISTING_PACKAGES})
+    public void installExistingPackage(@NonNull String packageName,
+            @InstallReason int installReason,
+            @Nullable IntentSender statusReceiver) {
+        Objects.requireNonNull(packageName, "packageName cannot be null");
+        try {
+            mInstaller.installExistingPackage(packageName,
+                    PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS, installReason,
+                    statusReceiver, mUserId, null);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+
+    /** {@hide} */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES)
+    public void setPermissionsResult(int sessionId, boolean accepted) {
+        try {
+            mInstaller.setPermissionsResult(sessionId, accepted);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Events for observing session lifecycle.
+     * <p>
+     * A typical session lifecycle looks like this:
+     * <ul>
+     * <li>An installer creates a session to indicate pending app delivery. All
+     * install details are available at this point.
+     * <li>The installer opens the session to deliver APK data. Note that a
+     * session may be opened and closed multiple times as network connectivity
+     * changes. The installer may deliver periodic progress updates.
+     * <li>The installer commits or abandons the session, resulting in the
+     * session being finished.
+     * </ul>
+     */
+    public static abstract class SessionCallback {
+        /**
+         * New session has been created. Details about the session can be
+         * obtained from {@link PackageInstaller#getSessionInfo(int)}.
+         */
+        public abstract void onCreated(int sessionId);
+
+        /**
+         * Badging details for an existing session has changed. For example, the
+         * app icon or label has been updated.
+         */
+        public abstract void onBadgingChanged(int sessionId);
+
+        /**
+         * Active state for session has been changed.
+         * <p>
+         * A session is considered active whenever there is ongoing forward
+         * progress being made, such as the installer holding an open
+         * {@link Session} instance while streaming data into place, or the
+         * system optimizing code as the result of
+         * {@link Session#commit(IntentSender)}.
+         * <p>
+         * If the installer closes the {@link Session} without committing, the
+         * session is considered inactive until the installer opens the session
+         * again.
+         */
+        public abstract void onActiveChanged(int sessionId, boolean active);
+
+        /**
+         * Progress for given session has been updated.
+         * <p>
+         * Note that this progress may not directly correspond to the value
+         * reported by
+         * {@link PackageInstaller.Session#setStagingProgress(float)}, as the
+         * system may carve out a portion of the overall progress to represent
+         * its own internal installation work.
+         */
+        public abstract void onProgressChanged(int sessionId, float progress);
+
+        /**
+         * Session has completely finished, either with success or failure.
+         */
+        public abstract void onFinished(int sessionId, boolean success);
+    }
+
+    /** {@hide} */
+    static class SessionCallbackDelegate extends IPackageInstallerCallback.Stub {
+        private static final int MSG_SESSION_CREATED = 1;
+        private static final int MSG_SESSION_BADGING_CHANGED = 2;
+        private static final int MSG_SESSION_ACTIVE_CHANGED = 3;
+        private static final int MSG_SESSION_PROGRESS_CHANGED = 4;
+        private static final int MSG_SESSION_FINISHED = 5;
+
+        final SessionCallback mCallback;
+        final Executor mExecutor;
+
+        SessionCallbackDelegate(SessionCallback callback, Executor executor) {
+            mCallback = callback;
+            mExecutor = executor;
+        }
+
+        @Override
+        public void onSessionCreated(int sessionId) {
+            mExecutor.execute(PooledLambda.obtainRunnable(SessionCallback::onCreated, mCallback,
+                    sessionId).recycleOnUse());
+        }
+
+        @Override
+        public void onSessionBadgingChanged(int sessionId) {
+            mExecutor.execute(PooledLambda.obtainRunnable(SessionCallback::onBadgingChanged,
+                    mCallback, sessionId).recycleOnUse());
+        }
+
+        @Override
+        public void onSessionActiveChanged(int sessionId, boolean active) {
+            mExecutor.execute(PooledLambda.obtainRunnable(SessionCallback::onActiveChanged,
+                    mCallback, sessionId, active).recycleOnUse());
+        }
+
+        @Override
+        public void onSessionProgressChanged(int sessionId, float progress) {
+            mExecutor.execute(PooledLambda.obtainRunnable(SessionCallback::onProgressChanged,
+                    mCallback, sessionId, progress).recycleOnUse());
+        }
+
+        @Override
+        public void onSessionFinished(int sessionId, boolean success) {
+            mExecutor.execute(PooledLambda.obtainRunnable(SessionCallback::onFinished,
+                    mCallback, sessionId, success).recycleOnUse());
+        }
+    }
+
+    /** {@hide} */
+    @Deprecated
+    public void addSessionCallback(@NonNull SessionCallback callback) {
+        registerSessionCallback(callback);
+    }
+
+    /**
+     * Register to watch for session lifecycle events. No special permissions
+     * are required to watch for these events.
+     */
+    public void registerSessionCallback(@NonNull SessionCallback callback) {
+        registerSessionCallback(callback, new Handler());
+    }
+
+    /** {@hide} */
+    @Deprecated
+    public void addSessionCallback(@NonNull SessionCallback callback, @NonNull Handler handler) {
+        registerSessionCallback(callback, handler);
+    }
+
+    /**
+     * Register to watch for session lifecycle events. No special permissions
+     * are required to watch for these events.
+     *
+     * @param handler to dispatch callback events through, otherwise uses
+     *            calling thread.
+     */
+    public void registerSessionCallback(@NonNull SessionCallback callback, @NonNull Handler handler) {
+        synchronized (mDelegates) {
+            final SessionCallbackDelegate delegate = new SessionCallbackDelegate(callback,
+                    new HandlerExecutor(handler));
+            try {
+                mInstaller.registerCallback(delegate, mUserId);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+            mDelegates.add(delegate);
+        }
+    }
+
+    /** {@hide} */
+    @Deprecated
+    public void removeSessionCallback(@NonNull SessionCallback callback) {
+        unregisterSessionCallback(callback);
+    }
+
+    /**
+     * Unregister a previously registered callback.
+     */
+    public void unregisterSessionCallback(@NonNull SessionCallback callback) {
+        synchronized (mDelegates) {
+            for (Iterator<SessionCallbackDelegate> i = mDelegates.iterator(); i.hasNext();) {
+                final SessionCallbackDelegate delegate = i.next();
+                if (delegate.mCallback == callback) {
+                    try {
+                        mInstaller.unregisterCallback(delegate);
+                    } catch (RemoteException e) {
+                        throw e.rethrowFromSystemServer();
+                    }
+                    i.remove();
+                }
+            }
+        }
+    }
+
+    /**
+     * An installation that is being actively staged. For an install to succeed,
+     * all existing and new packages must have identical package names, version
+     * codes, and signing certificates.
+     * <p>
+     * A session may contain any number of split packages. If the application
+     * does not yet exist, this session must include a base package.
+     * <p>
+     * If an APK included in this session is already defined by the existing
+     * installation (for example, the same split name), the APK in this session
+     * will replace the existing APK.
+     * <p>
+     * In such a case that multiple packages need to be committed simultaneously,
+     * multiple sessions can be referenced by a single multi-package session.
+     * This session is created with no package name and calling
+     * {@link SessionParams#setMultiPackage()}. The individual session IDs can be
+     * added with {@link #addChildSessionId(int)} and commit of the multi-package
+     * session will result in all child sessions being committed atomically.
+     */
+    public static class Session implements Closeable {
+        /** {@hide} */
+        protected final IPackageInstallerSession mSession;
+
+        /** {@hide} */
+        public Session(IPackageInstallerSession session) {
+            mSession = session;
+        }
+
+        /** {@hide} */
+        @Deprecated
+        public void setProgress(float progress) {
+            setStagingProgress(progress);
+        }
+
+        /**
+         * Set current progress of staging this session. Valid values are
+         * anywhere between 0 and 1.
+         * <p>
+         * Note that this progress may not directly correspond to the value
+         * reported by {@link SessionCallback#onProgressChanged(int, float)}, as
+         * the system may carve out a portion of the overall progress to
+         * represent its own internal installation work.
+         */
+        public void setStagingProgress(float progress) {
+            try {
+                mSession.setClientProgress(progress);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        /** {@hide} */
+        @UnsupportedAppUsage
+        public void addProgress(float progress) {
+            try {
+                mSession.addClientProgress(progress);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        /**
+         * Open a stream to write an APK file into the session.
+         * <p>
+         * The returned stream will start writing data at the requested offset
+         * in the underlying file, which can be used to resume a partially
+         * written file. If a valid file length is specified, the system will
+         * preallocate the underlying disk space to optimize placement on disk.
+         * It's strongly recommended to provide a valid file length when known.
+         * <p>
+         * You can write data into the returned stream, optionally call
+         * {@link #fsync(OutputStream)} as needed to ensure bytes have been
+         * persisted to disk, and then close when finished. All streams must be
+         * closed before calling {@link #commit(IntentSender)}.
+         *
+         * @param name arbitrary, unique name of your choosing to identify the
+         *            APK being written. You can open a file again for
+         *            additional writes (such as after a reboot) by using the
+         *            same name. This name is only meaningful within the context
+         *            of a single install session.
+         * @param offsetBytes offset into the file to begin writing at, or 0 to
+         *            start at the beginning of the file.
+         * @param lengthBytes total size of the file being written, used to
+         *            preallocate the underlying disk space, or -1 if unknown.
+         *            The system may clear various caches as needed to allocate
+         *            this space.
+         * @throws IOException if trouble opening the file for writing, such as
+         *             lack of disk space or unavailable media.
+         * @throws SecurityException if called after the session has been
+         *             sealed or abandoned
+         */
+        public @NonNull OutputStream openWrite(@NonNull String name, long offsetBytes,
+                long lengthBytes) throws IOException {
+            try {
+                if (ENABLE_REVOCABLE_FD) {
+                    return new ParcelFileDescriptor.AutoCloseOutputStream(
+                            mSession.openWrite(name, offsetBytes, lengthBytes));
+                } else {
+                    final ParcelFileDescriptor clientSocket = mSession.openWrite(name,
+                            offsetBytes, lengthBytes);
+                    return new FileBridge.FileBridgeOutputStream(clientSocket);
+                }
+            } catch (RuntimeException e) {
+                ExceptionUtils.maybeUnwrapIOException(e);
+                throw e;
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        /** {@hide} */
+        public void write(@NonNull String name, long offsetBytes, long lengthBytes,
+                @NonNull ParcelFileDescriptor fd) throws IOException {
+            try {
+                mSession.write(name, offsetBytes, lengthBytes, fd);
+            } catch (RuntimeException e) {
+                ExceptionUtils.maybeUnwrapIOException(e);
+                throw e;
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        /**
+         * Ensure that any outstanding data for given stream has been committed
+         * to disk. This is only valid for streams returned from
+         * {@link #openWrite(String, long, long)}.
+         */
+        public void fsync(@NonNull OutputStream out) throws IOException {
+            if (ENABLE_REVOCABLE_FD) {
+                if (out instanceof ParcelFileDescriptor.AutoCloseOutputStream) {
+                    try {
+                        Os.fsync(((ParcelFileDescriptor.AutoCloseOutputStream) out).getFD());
+                    } catch (ErrnoException e) {
+                        throw e.rethrowAsIOException();
+                    }
+                } else {
+                    throw new IllegalArgumentException("Unrecognized stream");
+                }
+            } else {
+                if (out instanceof FileBridge.FileBridgeOutputStream) {
+                    ((FileBridge.FileBridgeOutputStream) out).fsync();
+                } else {
+                    throw new IllegalArgumentException("Unrecognized stream");
+                }
+            }
+        }
+
+        /**
+         * Return all APK names contained in this session.
+         * <p>
+         * This returns all names which have been previously written through
+         * {@link #openWrite(String, long, long)} as part of this session.
+         *
+         * @throws SecurityException if called after the session has been
+         *             committed or abandoned.
+         */
+        public @NonNull String[] getNames() throws IOException {
+            try {
+                return mSession.getNames();
+            } catch (RuntimeException e) {
+                ExceptionUtils.maybeUnwrapIOException(e);
+                throw e;
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        /**
+         * Open a stream to read an APK file from the session.
+         * <p>
+         * This is only valid for names which have been previously written
+         * through {@link #openWrite(String, long, long)} as part of this
+         * session. For example, this stream may be used to calculate a
+         * {@link MessageDigest} of a written APK before committing.
+         *
+         * @throws SecurityException if called after the session has been
+         *             committed or abandoned.
+         */
+        public @NonNull InputStream openRead(@NonNull String name) throws IOException {
+            try {
+                final ParcelFileDescriptor pfd = mSession.openRead(name);
+                return new ParcelFileDescriptor.AutoCloseInputStream(pfd);
+            } catch (RuntimeException e) {
+                ExceptionUtils.maybeUnwrapIOException(e);
+                throw e;
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        /**
+         * Removes a split.
+         * <p>
+         * Split removals occur prior to adding new APKs. If upgrading a feature
+         * split, it is not expected nor desirable to remove the split prior to
+         * upgrading.
+         * <p>
+         * When split removal is bundled with new APKs, the packageName must be
+         * identical.
+         */
+        public void removeSplit(@NonNull String splitName) throws IOException {
+            try {
+                mSession.removeSplit(splitName);
+            } catch (RuntimeException e) {
+                ExceptionUtils.maybeUnwrapIOException(e);
+                throw e;
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        /**
+         * @return data loader params or null if the session is not using one.
+         *
+         * WARNING: This is a system API to aid internal development.
+         * Use at your own risk. It will change or be removed without warning.
+         * {@hide}
+         */
+        @SystemApi
+        @RequiresPermission(android.Manifest.permission.USE_INSTALLER_V2)
+        public @Nullable DataLoaderParams getDataLoaderParams() {
+            try {
+                DataLoaderParamsParcel data = mSession.getDataLoaderParams();
+                if (data == null) {
+                    return null;
+                }
+                return new DataLoaderParams(data);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        /**
+         * Adds a file to session. On commit this file will be pulled from dataLoader.
+         *
+         * @param location target location for the file. Possible values:
+         *            {@link #LOCATION_DATA_APP},
+         *            {@link #LOCATION_MEDIA_OBB},
+         *            {@link #LOCATION_MEDIA_DATA}.
+         * @param name arbitrary, unique name of your choosing to identify the
+         *            APK being written. You can open a file again for
+         *            additional writes (such as after a reboot) by using the
+         *            same name. This name is only meaningful within the context
+         *            of a single install session.
+         * @param lengthBytes total size of the file being written.
+         *            The system may clear various caches as needed to allocate
+         *            this space.
+         * @param metadata additional info use by dataLoader to pull data for the file.
+         * @param signature additional file signature, e.g.
+         *                  <a href="https://source.android.com/security/apksigning/v4.html">APK Signature Scheme v4</a>
+         * @throws SecurityException if called after the session has been
+         *             sealed or abandoned
+         * @throws IllegalStateException if called for non-callback session
+         *
+         * WARNING: This is a system API to aid internal development.
+         * Use at your own risk. It will change or be removed without warning.
+         * {@hide}
+         */
+        @SystemApi
+        @RequiresPermission(android.Manifest.permission.USE_INSTALLER_V2)
+        public void addFile(@FileLocation int location, @NonNull String name, long lengthBytes,
+                @NonNull byte[] metadata, @Nullable byte[] signature) {
+            try {
+                mSession.addFile(location, name, lengthBytes, metadata, signature);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        /**
+         * Removes a file.
+         *
+         * @param location target location for the file. Possible values:
+         *            {@link #LOCATION_DATA_APP},
+         *            {@link #LOCATION_MEDIA_OBB},
+         *            {@link #LOCATION_MEDIA_DATA}.
+         * @param name name of a file, e.g. split.
+         * @throws SecurityException if called after the session has been
+         *             sealed or abandoned
+         * @throws IllegalStateException if called for non-callback session
+         * {@hide}
+         */
+        @SystemApi
+        @RequiresPermission(android.Manifest.permission.USE_INSTALLER_V2)
+        public void removeFile(@FileLocation int location, @NonNull String name) {
+            try {
+                mSession.removeFile(location, name);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        /**
+         * Attempt to commit everything staged in this session. This may require
+         * user intervention, and so it may not happen immediately. The final
+         * result of the commit will be reported through the given callback.
+         * <p>
+         * Once this method is called, the session is sealed and no additional mutations may be
+         * performed on the session. In case of device reboot or data loader transient failure
+         * before the session has been finalized, you may commit the session again.
+         * <p>
+         * If the installer is the device owner or the affiliated profile owner, there will be no
+         * user intervention.
+         *
+         * @param statusReceiver Called when the state of the session changes. Intents
+         *                       sent to this receiver contain {@link #EXTRA_STATUS}. Refer to the
+         *                       individual status codes on how to handle them.
+         *
+         * @throws SecurityException if streams opened through
+         *             {@link #openWrite(String, long, long)} are still open.
+         *
+         * @see android.app.admin.DevicePolicyManager
+         */
+        public void commit(@NonNull IntentSender statusReceiver) {
+            try {
+                mSession.commit(statusReceiver, false);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        /**
+         * Attempt to commit a session that has been {@link #transfer(String) transferred}.
+         *
+         * <p>If the device reboots before the session has been finalized, you may commit the
+         * session again.
+         *
+         * <p>The caller of this method is responsible to ensure the safety of the session. As the
+         * session was created by another - usually less trusted - app, it is paramount that before
+         * committing <u>all</u> public and system {@link SessionInfo properties of the session}
+         * and <u>all</u> {@link #openRead(String) APKs} are verified by the caller. It might happen
+         * that new properties are added to the session with a new API revision. In this case the
+         * callers need to be updated.
+         *
+         * @param statusReceiver Called when the state of the session changes. Intents
+         *                       sent to this receiver contain {@link #EXTRA_STATUS}. Refer to the
+         *                       individual status codes on how to handle them.
+         *
+         * @hide
+         */
+        @SystemApi
+        @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES)
+        public void commitTransferred(@NonNull IntentSender statusReceiver) {
+            try {
+                mSession.commit(statusReceiver, true);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        /**
+         * Transfer the session to a new owner.
+         * <p>
+         * Only sessions that update the installing app can be transferred.
+         * <p>
+         * After the transfer to a package with a different uid all method calls on the session
+         * will cause {@link SecurityException}s.
+         * <p>
+         * Once this method is called, the session is sealed and no additional mutations beside
+         * committing it may be performed on the session.
+         *
+         * @param packageName The package of the new owner. Needs to hold the INSTALL_PACKAGES
+         *                    permission.
+         *
+         * @throws PackageManager.NameNotFoundException if the new owner could not be found.
+         * @throws SecurityException if called after the session has been committed or abandoned.
+         * @throws SecurityException if the session does not update the original installer
+         * @throws SecurityException if streams opened through
+         *                           {@link #openWrite(String, long, long) are still open.
+         */
+        public void transfer(@NonNull String packageName)
+                throws PackageManager.NameNotFoundException {
+            Objects.requireNonNull(packageName);
+
+            try {
+                mSession.transfer(packageName);
+            } catch (ParcelableException e) {
+                e.maybeRethrow(PackageManager.NameNotFoundException.class);
+                throw new RuntimeException(e);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        /**
+         * Release this session object. You can open the session again if it
+         * hasn't been finalized.
+         */
+        @Override
+        public void close() {
+            try {
+                mSession.close();
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        /**
+         * Completely abandon this session, destroying all staged data and
+         * rendering it invalid. Abandoned sessions will be reported to
+         * {@link SessionCallback} listeners as failures. This is equivalent to
+         * opening the session and calling {@link Session#abandon()}.
+         */
+        public void abandon() {
+            try {
+                mSession.abandon();
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        /**
+         * @return {@code true} if this session will commit more than one package when it is
+         * committed.
+         */
+        public boolean isMultiPackage() {
+            try {
+                return mSession.isMultiPackage();
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        /**
+         * @return {@code true} if this session will be staged and applied at next reboot.
+         */
+        public boolean isStaged() {
+            try {
+                return mSession.isStaged();
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        /**
+         * @return the session ID of the multi-package session that this belongs to or
+         * {@link SessionInfo#INVALID_ID} if it does not belong to a multi-package session.
+         */
+        public int getParentSessionId() {
+            try {
+                return mSession.getParentSessionId();
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        /**
+         * @return the set of session IDs that will be committed atomically when this session is
+         * committed if this is a multi-package session or null if none exist.
+         */
+        @NonNull
+        public int[] getChildSessionIds() {
+            try {
+                return mSession.getChildSessionIds();
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        /**
+         * Adds a session ID to the set of sessions that will be committed atomically
+         * when this session is committed.
+         *
+         * <p>If the parent is staged or has rollback enabled, all children must have
+         * the same properties.
+         *
+         * @param sessionId the session ID to add to this multi-package session.
+         */
+        public void addChildSessionId(int sessionId) {
+            try {
+                mSession.addChildSessionId(sessionId);
+            } catch (RemoteException e) {
+                e.rethrowFromSystemServer();
+            }
+        }
+
+        /**
+         * Removes a session ID from the set of sessions that will be committed
+         * atomically when this session is committed.
+         *
+         * @param sessionId the session ID to remove from this multi-package session.
+         */
+        public void removeChildSessionId(int sessionId) {
+            try {
+                mSession.removeChildSessionId(sessionId);
+            } catch (RemoteException e) {
+                e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * Parameters for creating a new {@link PackageInstaller.Session}.
+     */
+    public static class SessionParams implements Parcelable {
+
+        /** {@hide} */
+        public static final int MODE_INVALID = -1;
+
+        /**
+         * Mode for an install session whose staged APKs should fully replace any
+         * existing APKs for the target app.
+         */
+        public static final int MODE_FULL_INSTALL = 1;
+
+        /**
+         * Mode for an install session that should inherit any existing APKs for the
+         * target app, unless they have been explicitly overridden (based on split
+         * name) by the session. For example, this can be used to add one or more
+         * split APKs to an existing installation.
+         * <p>
+         * If there are no existing APKs for the target app, this behaves like
+         * {@link #MODE_FULL_INSTALL}.
+         */
+        public static final int MODE_INHERIT_EXISTING = 2;
+
+        /**
+         * Special constant to refer to all restricted permissions.
+         */
+        public static final @NonNull Set<String> RESTRICTED_PERMISSIONS_ALL = new ArraySet<>();
+
+        /** {@hide} */
+        public static final int UID_UNKNOWN = -1;
+
+        /** {@hide} */
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+        public int mode = MODE_INVALID;
+        /** {@hide} */
+        @UnsupportedAppUsage
+        public int installFlags = PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS;
+        /** {@hide} */
+        public int installLocation = PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY;
+        /** {@hide} */
+        public @InstallReason int installReason = PackageManager.INSTALL_REASON_UNKNOWN;
+        /** {@hide} */
+        @UnsupportedAppUsage
+        public long sizeBytes = -1;
+        /** {@hide} */
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+        public String appPackageName;
+        /** {@hide} */
+        @UnsupportedAppUsage
+        public Bitmap appIcon;
+        /** {@hide} */
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+        public String appLabel;
+        /** {@hide} */
+        public long appIconLastModified = -1;
+        /** {@hide} */
+        public Uri originatingUri;
+        /** {@hide} */
+        @UnsupportedAppUsage
+        public int originatingUid = UID_UNKNOWN;
+        /** {@hide} */
+        public Uri referrerUri;
+        /** {@hide} */
+        public String abiOverride;
+        /** {@hide} */
+        public String volumeUuid;
+        /** {@hide} */
+        public String[] grantedRuntimePermissions;
+        /** {@hide} */
+        public List<String> whitelistedRestrictedPermissions;
+        /** {@hide} */
+        public int autoRevokePermissionsMode = MODE_DEFAULT;
+        /** {@hide} */
+        public String installerPackageName;
+        /** {@hide} */
+        public boolean isMultiPackage;
+        /** {@hide} */
+        public boolean isStaged;
+        /** {@hide} */
+        public long requiredInstalledVersionCode = PackageManager.VERSION_CODE_HIGHEST;
+        /** {@hide} */
+        public DataLoaderParams dataLoaderParams;
+        /** {@hide} */
+        public int rollbackDataPolicy = PackageManager.RollbackDataPolicy.RESTORE;
+        /** {@hide} */
+        public boolean forceQueryableOverride;
+
+        /**
+         * Construct parameters for a new package install session.
+         *
+         * @param mode one of {@link #MODE_FULL_INSTALL} or
+         *            {@link #MODE_INHERIT_EXISTING} describing how the session
+         *            should interact with an existing app.
+         */
+        public SessionParams(int mode) {
+            this.mode = mode;
+        }
+
+        /** {@hide} */
+        public SessionParams(Parcel source) {
+            mode = source.readInt();
+            installFlags = source.readInt();
+            installLocation = source.readInt();
+            installReason = source.readInt();
+            sizeBytes = source.readLong();
+            appPackageName = source.readString();
+            appIcon = source.readParcelable(null);
+            appLabel = source.readString();
+            originatingUri = source.readParcelable(null);
+            originatingUid = source.readInt();
+            referrerUri = source.readParcelable(null);
+            abiOverride = source.readString();
+            volumeUuid = source.readString();
+            grantedRuntimePermissions = source.readStringArray();
+            whitelistedRestrictedPermissions = source.createStringArrayList();
+            autoRevokePermissionsMode = source.readInt();
+            installerPackageName = source.readString();
+            isMultiPackage = source.readBoolean();
+            isStaged = source.readBoolean();
+            forceQueryableOverride = source.readBoolean();
+            requiredInstalledVersionCode = source.readLong();
+            DataLoaderParamsParcel dataLoaderParamsParcel = source.readParcelable(
+                    DataLoaderParamsParcel.class.getClassLoader());
+            if (dataLoaderParamsParcel != null) {
+                dataLoaderParams = new DataLoaderParams(dataLoaderParamsParcel);
+            }
+            rollbackDataPolicy = source.readInt();
+        }
+
+        /** {@hide} */
+        public SessionParams copy() {
+            SessionParams ret = new SessionParams(mode);
+            ret.installFlags = installFlags;
+            ret.installLocation = installLocation;
+            ret.installReason = installReason;
+            ret.sizeBytes = sizeBytes;
+            ret.appPackageName = appPackageName;
+            ret.appIcon = appIcon;  // not a copy.
+            ret.appLabel = appLabel;
+            ret.originatingUri = originatingUri;  // not a copy, but immutable.
+            ret.originatingUid = originatingUid;
+            ret.referrerUri = referrerUri;  // not a copy, but immutable.
+            ret.abiOverride = abiOverride;
+            ret.volumeUuid = volumeUuid;
+            ret.grantedRuntimePermissions = grantedRuntimePermissions;
+            ret.whitelistedRestrictedPermissions = whitelistedRestrictedPermissions;
+            ret.autoRevokePermissionsMode = autoRevokePermissionsMode;
+            ret.installerPackageName = installerPackageName;
+            ret.isMultiPackage = isMultiPackage;
+            ret.isStaged = isStaged;
+            ret.forceQueryableOverride = forceQueryableOverride;
+            ret.requiredInstalledVersionCode = requiredInstalledVersionCode;
+            ret.dataLoaderParams = dataLoaderParams;
+            ret.rollbackDataPolicy = rollbackDataPolicy;
+            return ret;
+        }
+
+        /**
+         * Check if there are hidden options set.
+         *
+         * <p>Hidden options are those options that cannot be verified via public or system-api
+         * methods on {@link SessionInfo}.
+         *
+         * @return {@code true} if any hidden option is set.
+         *
+         * @hide
+         */
+        public boolean areHiddenOptionsSet() {
+            return (installFlags & (PackageManager.INSTALL_REQUEST_DOWNGRADE
+                    | PackageManager.INSTALL_ALLOW_DOWNGRADE
+                    | PackageManager.INSTALL_DONT_KILL_APP
+                    | PackageManager.INSTALL_INSTANT_APP
+                    | PackageManager.INSTALL_FULL_APP
+                    | PackageManager.INSTALL_VIRTUAL_PRELOAD
+                    | PackageManager.INSTALL_ALLOCATE_AGGRESSIVE)) != installFlags
+                    || abiOverride != null || volumeUuid != null;
+        }
+
+        /**
+         * Provide value of {@link PackageInfo#installLocation}, which may be used
+         * to determine where the app will be staged. Defaults to
+         * {@link PackageInfo#INSTALL_LOCATION_INTERNAL_ONLY}.
+         */
+        public void setInstallLocation(int installLocation) {
+            this.installLocation = installLocation;
+        }
+
+        /**
+         * Optionally indicate the total size (in bytes) of all APKs that will be
+         * delivered in this session. The system may use this to ensure enough disk
+         * space exists before proceeding, or to estimate container size for
+         * installations living on external storage.
+         *
+         * @see PackageInfo#INSTALL_LOCATION_AUTO
+         * @see PackageInfo#INSTALL_LOCATION_PREFER_EXTERNAL
+         */
+        public void setSize(long sizeBytes) {
+            this.sizeBytes = sizeBytes;
+        }
+
+        /**
+         * Optionally set the package name of the app being installed. It's strongly
+         * recommended that you provide this value when known, so that observers can
+         * communicate installing apps to users.
+         * <p>
+         * If the APKs staged in the session aren't consistent with this package
+         * name, the install will fail. Regardless of this value, all APKs in the
+         * app must have the same package name.
+         */
+        public void setAppPackageName(@Nullable String appPackageName) {
+            this.appPackageName = appPackageName;
+        }
+
+        /**
+         * Optionally set an icon representing the app being installed. This should
+         * be roughly {@link ActivityManager#getLauncherLargeIconSize()} in both
+         * dimensions.
+         */
+        public void setAppIcon(@Nullable Bitmap appIcon) {
+            this.appIcon = appIcon;
+        }
+
+        /**
+         * Optionally set a label representing the app being installed.
+         */
+        public void setAppLabel(@Nullable CharSequence appLabel) {
+            this.appLabel = (appLabel != null) ? appLabel.toString() : null;
+        }
+
+        /**
+         * Optionally set the URI where this package was downloaded from. This is
+         * informational and may be used as a signal for anti-malware purposes.
+         *
+         * @see Intent#EXTRA_ORIGINATING_URI
+         */
+        public void setOriginatingUri(@Nullable Uri originatingUri) {
+            this.originatingUri = originatingUri;
+        }
+
+        /**
+         * Sets the UID that initiated the package installation. This is informational
+         * and may be used as a signal for anti-malware purposes.
+         */
+        public void setOriginatingUid(int originatingUid) {
+            this.originatingUid = originatingUid;
+        }
+
+        /**
+         * Optionally set the URI that referred you to install this package. This is
+         * informational and may be used as a signal for anti-malware purposes.
+         *
+         * @see Intent#EXTRA_REFERRER
+         */
+        public void setReferrerUri(@Nullable Uri referrerUri) {
+            this.referrerUri = referrerUri;
+        }
+
+        /**
+         * Sets which runtime permissions to be granted to the package at installation.
+         *
+         * @param permissions The permissions to grant or null to grant all runtime
+         *     permissions.
+         *
+         * @hide
+         */
+        @TestApi
+        @SystemApi
+        @RequiresPermission(android.Manifest.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS)
+        public void setGrantedRuntimePermissions(String[] permissions) {
+            installFlags |= PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS;
+            this.grantedRuntimePermissions = permissions;
+        }
+
+        /**
+         * Sets which restricted permissions to be whitelisted for the app. Whitelisting
+         * is not granting the permissions, rather it allows the app to hold permissions
+         * which are otherwise restricted. Whitelisting a non restricted permission has
+         * no effect.
+         *
+         * <p> Permissions can be hard restricted which means that the app cannot hold
+         * them or soft restricted where the app can hold the permission but in a weaker
+         * form. Whether a permission is {@link PermissionInfo#FLAG_HARD_RESTRICTED hard
+         * restricted} or {@link PermissionInfo#FLAG_SOFT_RESTRICTED soft restricted}
+         * depends on the permission declaration. Whitelisting a hard restricted permission
+         * allows the app to hold that permission and whitelisting a soft restricted
+         * permission allows the app to hold the permission in its full, unrestricted form.
+         *
+         * <p> Permissions can also be immutably restricted which means that the whitelist
+         * state of the permission can be determined only at install time and cannot be
+         * changed on updated or at a later point via the package manager APIs.
+         *
+         * <p>Initially, all restricted permissions are whitelisted but you can change
+         * which ones are whitelisted by calling this method or the corresponding ones
+         * on the {@link PackageManager}.
+         *
+         * @see PackageManager#addWhitelistedRestrictedPermission(String, String, int)
+         * @see PackageManager#removeWhitelistedRestrictedPermission(String, String, int)
+         */
+        public void setWhitelistedRestrictedPermissions(@Nullable Set<String> permissions) {
+            if (permissions == RESTRICTED_PERMISSIONS_ALL) {
+                installFlags |= PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS;
+                whitelistedRestrictedPermissions = null;
+            } else {
+                installFlags &= ~PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS;
+                whitelistedRestrictedPermissions = (permissions != null)
+                        ? new ArrayList<>(permissions) : null;
+            }
+        }
+
+        /**
+         * Sets whether permissions should be auto-revoked if this package is unused for an
+         * extended periodd of time.
+         *
+         * It's disabled by default but generally the installer should enable it for most packages,
+         * excluding only those where doing so might cause breakage that cannot be easily addressed
+         * by simply re-requesting the permission(s).
+         *
+         * If user explicitly enabled or disabled it via settings, this call is ignored.
+         *
+         * @param shouldAutoRevoke whether permissions should be auto-revoked.
+         */
+        public void setAutoRevokePermissionsMode(boolean shouldAutoRevoke) {
+            autoRevokePermissionsMode = shouldAutoRevoke ? MODE_ALLOWED : MODE_IGNORED;
+        }
+
+        /**
+         * Request that rollbacks be enabled or disabled for the given upgrade with rollback data
+         * policy set to RESTORE.
+         *
+         * <p>If the parent session is staged or has rollback enabled, all children sessions
+         * must have the same properties.
+         *
+         * @param enable set to {@code true} to enable, {@code false} to disable
+         * @see SessionParams#setEnableRollback(boolean, int)
+         * @hide
+         */
+        @SystemApi @TestApi
+        public void setEnableRollback(boolean enable) {
+            if (enable) {
+                installFlags |= PackageManager.INSTALL_ENABLE_ROLLBACK;
+            } else {
+                installFlags &= ~PackageManager.INSTALL_ENABLE_ROLLBACK;
+            }
+            rollbackDataPolicy = PackageManager.RollbackDataPolicy.RESTORE;
+        }
+
+        /**
+         * Request that rollbacks be enabled or disabled for the given upgrade.
+         *
+         * <p>If the parent session is staged or has rollback enabled, all children sessions
+         * must have the same properties.
+         *
+         * <p> For a multi-package install, this method must be called on each child session to
+         * specify rollback data policies explicitly. Note each child session is allowed to have
+         * different policies.
+         *
+         * @param enable set to {@code true} to enable, {@code false} to disable
+         * @param dataPolicy the rollback data policy for this session
+         * @hide
+         */
+        @SystemApi @TestApi
+        public void setEnableRollback(boolean enable,
+                @PackageManager.RollbackDataPolicy int dataPolicy) {
+            if (enable) {
+                installFlags |= PackageManager.INSTALL_ENABLE_ROLLBACK;
+            } else {
+                installFlags &= ~PackageManager.INSTALL_ENABLE_ROLLBACK;
+            }
+            rollbackDataPolicy = dataPolicy;
+        }
+
+
+        /**
+         * @deprecated use {@link #setRequestDowngrade(boolean)}.
+         * {@hide}
+         */
+        @SystemApi
+        @Deprecated
+        public void setAllowDowngrade(boolean allowDowngrade) {
+            setRequestDowngrade(allowDowngrade);
+        }
+
+        /** {@hide} */
+        @SystemApi @TestApi
+        public void setRequestDowngrade(boolean requestDowngrade) {
+            if (requestDowngrade) {
+                installFlags |= PackageManager.INSTALL_REQUEST_DOWNGRADE;
+            } else {
+                installFlags &= ~PackageManager.INSTALL_REQUEST_DOWNGRADE;
+            }
+        }
+
+        /**
+         * Require the given version of the package be installed.
+         * The install will only be allowed if the existing version code of
+         * the package installed on the device matches the given version code.
+         * Use {@link * PackageManager#VERSION_CODE_HIGHEST} to allow
+         * installation regardless of the currently installed package version.
+         *
+         * @hide
+         */
+        public void setRequiredInstalledVersionCode(long versionCode) {
+            requiredInstalledVersionCode = versionCode;
+        }
+
+        /** {@hide} */
+        public void setInstallFlagsForcePermissionPrompt() {
+            installFlags |= PackageManager.INSTALL_FORCE_PERMISSION_PROMPT;
+        }
+
+        /** {@hide} */
+        @SystemApi
+        public void setDontKillApp(boolean dontKillApp) {
+            if (dontKillApp) {
+                installFlags |= PackageManager.INSTALL_DONT_KILL_APP;
+            } else {
+                installFlags &= ~PackageManager.INSTALL_DONT_KILL_APP;
+            }
+        }
+
+        /** {@hide} */
+        @SystemApi
+        public void setInstallAsInstantApp(boolean isInstantApp) {
+            if (isInstantApp) {
+                installFlags |= PackageManager.INSTALL_INSTANT_APP;
+                installFlags &= ~PackageManager.INSTALL_FULL_APP;
+            } else {
+                installFlags &= ~PackageManager.INSTALL_INSTANT_APP;
+                installFlags |= PackageManager.INSTALL_FULL_APP;
+            }
+        }
+
+        /**
+         * Sets the install as a virtual preload. Will only have effect when called
+         * by the verifier.
+         * {@hide}
+         */
+        @SystemApi
+        public void setInstallAsVirtualPreload() {
+            installFlags |= PackageManager.INSTALL_VIRTUAL_PRELOAD;
+        }
+
+        /**
+         * Set the reason for installing this package.
+         * <p>
+         * The install reason should be a pre-defined integer. The behavior is
+         * undefined if other values are used.
+         *
+         * @see PackageManager#INSTALL_REASON_UNKNOWN
+         * @see PackageManager#INSTALL_REASON_POLICY
+         * @see PackageManager#INSTALL_REASON_DEVICE_RESTORE
+         * @see PackageManager#INSTALL_REASON_DEVICE_SETUP
+         * @see PackageManager#INSTALL_REASON_USER
+         */
+        public void setInstallReason(@InstallReason int installReason) {
+            this.installReason = installReason;
+        }
+
+        /** {@hide} */
+        @SystemApi
+        @RequiresPermission(android.Manifest.permission.ALLOCATE_AGGRESSIVE)
+        public void setAllocateAggressive(boolean allocateAggressive) {
+            if (allocateAggressive) {
+                installFlags |= PackageManager.INSTALL_ALLOCATE_AGGRESSIVE;
+            } else {
+                installFlags &= ~PackageManager.INSTALL_ALLOCATE_AGGRESSIVE;
+            }
+        }
+
+        /**
+         * Set the installer package for the app.
+         *
+         * By default this is the app that created the {@link PackageInstaller} object.
+         *
+         * @param installerPackageName name of the installer package
+         * {@hide}
+         */
+        @TestApi
+        public void setInstallerPackageName(@Nullable String installerPackageName) {
+            this.installerPackageName = installerPackageName;
+        }
+
+        /**
+         * Set this session to be the parent of a multi-package install.
+         *
+         * A multi-package install session contains no APKs and only references other install
+         * sessions via ID. When a multi-package session is committed, all of its children
+         * are committed to the system in an atomic manner. If any children fail to install,
+         * all of them do, including the multi-package session.
+         */
+        public void setMultiPackage() {
+            this.isMultiPackage = true;
+        }
+
+        /**
+         * Set this session to be staged to be installed at reboot.
+         *
+         * Staged sessions are scheduled to be installed at next reboot. Staged sessions can also be
+         * multi-package. In that case, if any of the children sessions fail to install at reboot,
+         * all the other children sessions are aborted as well.
+         *
+         * <p>If the parent session is staged or has rollback enabled, all children sessions
+         * must have the same properties.
+         *
+         * {@hide}
+         */
+        @SystemApi @TestApi
+        @RequiresPermission(Manifest.permission.INSTALL_PACKAGES)
+        public void setStaged() {
+            this.isStaged = true;
+        }
+
+        /**
+         * Set this session to be installing an APEX package.
+         *
+         * {@hide}
+         */
+        @SystemApi @TestApi
+        @RequiresPermission(Manifest.permission.INSTALL_PACKAGES)
+        public void setInstallAsApex() {
+            installFlags |= PackageManager.INSTALL_APEX;
+        }
+
+        /** @hide */
+        public boolean getEnableRollback() {
+            return (installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0;
+        }
+
+        /**
+         * Set the data loader params for the session.
+         * This also switches installation into data provider mode and disallow direct writes into
+         * staging folder.
+         *
+         * WARNING: This is a system API to aid internal development.
+         * Use at your own risk. It will change or be removed without warning.
+         * {@hide}
+         */
+        @SystemApi
+        @RequiresPermission(allOf = {
+                Manifest.permission.INSTALL_PACKAGES,
+                Manifest.permission.USE_INSTALLER_V2})
+        public void setDataLoaderParams(@NonNull DataLoaderParams dataLoaderParams) {
+            this.dataLoaderParams = dataLoaderParams;
+        }
+
+        /**
+         *
+         * {@hide}
+         */
+        public void setForceQueryable() {
+            this.forceQueryableOverride = true;
+        }
+
+        /** {@hide} */
+        public void dump(IndentingPrintWriter pw) {
+            pw.printPair("mode", mode);
+            pw.printHexPair("installFlags", installFlags);
+            pw.printPair("installLocation", installLocation);
+            pw.printPair("sizeBytes", sizeBytes);
+            pw.printPair("appPackageName", appPackageName);
+            pw.printPair("appIcon", (appIcon != null));
+            pw.printPair("appLabel", appLabel);
+            pw.printPair("originatingUri", originatingUri);
+            pw.printPair("originatingUid", originatingUid);
+            pw.printPair("referrerUri", referrerUri);
+            pw.printPair("abiOverride", abiOverride);
+            pw.printPair("volumeUuid", volumeUuid);
+            pw.printPair("grantedRuntimePermissions", grantedRuntimePermissions);
+            pw.printPair("whitelistedRestrictedPermissions", whitelistedRestrictedPermissions);
+            pw.printPair("autoRevokePermissions", autoRevokePermissionsMode);
+            pw.printPair("installerPackageName", installerPackageName);
+            pw.printPair("isMultiPackage", isMultiPackage);
+            pw.printPair("isStaged", isStaged);
+            pw.printPair("forceQueryable", forceQueryableOverride);
+            pw.printPair("requiredInstalledVersionCode", requiredInstalledVersionCode);
+            pw.printPair("dataLoaderParams", dataLoaderParams);
+            pw.printPair("rollbackDataPolicy", rollbackDataPolicy);
+            pw.println();
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(mode);
+            dest.writeInt(installFlags);
+            dest.writeInt(installLocation);
+            dest.writeInt(installReason);
+            dest.writeLong(sizeBytes);
+            dest.writeString(appPackageName);
+            dest.writeParcelable(appIcon, flags);
+            dest.writeString(appLabel);
+            dest.writeParcelable(originatingUri, flags);
+            dest.writeInt(originatingUid);
+            dest.writeParcelable(referrerUri, flags);
+            dest.writeString(abiOverride);
+            dest.writeString(volumeUuid);
+            dest.writeStringArray(grantedRuntimePermissions);
+            dest.writeStringList(whitelistedRestrictedPermissions);
+            dest.writeInt(autoRevokePermissionsMode);
+            dest.writeString(installerPackageName);
+            dest.writeBoolean(isMultiPackage);
+            dest.writeBoolean(isStaged);
+            dest.writeBoolean(forceQueryableOverride);
+            dest.writeLong(requiredInstalledVersionCode);
+            if (dataLoaderParams != null) {
+                dest.writeParcelable(dataLoaderParams.getData(), flags);
+            } else {
+                dest.writeParcelable(null, flags);
+            }
+            dest.writeInt(rollbackDataPolicy);
+        }
+
+        public static final Parcelable.Creator<SessionParams>
+                CREATOR = new Parcelable.Creator<SessionParams>() {
+                    @Override
+                    public SessionParams createFromParcel(Parcel p) {
+                        return new SessionParams(p);
+                    }
+
+                    @Override
+                    public SessionParams[] newArray(int size) {
+                        return new SessionParams[size];
+                    }
+                };
+    }
+
+    /**
+     * Details for an active install session.
+     */
+    public static class SessionInfo implements Parcelable {
+
+        /**
+         * A session ID that does not exist or is invalid.
+         */
+        public static final int INVALID_ID = -1;
+        /** {@hide} */
+        private static final int[] NO_SESSIONS = {};
+
+        /** @hide */
+        @IntDef(prefix = { "STAGED_SESSION_" }, value = {
+                STAGED_SESSION_NO_ERROR,
+                STAGED_SESSION_VERIFICATION_FAILED,
+                STAGED_SESSION_ACTIVATION_FAILED,
+                STAGED_SESSION_UNKNOWN})
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface StagedSessionErrorCode{}
+        /**
+         * Constant indicating that no error occurred during the preparation or the activation of
+         * this staged session.
+         */
+        public static final int STAGED_SESSION_NO_ERROR = 0;
+
+        /**
+         * Constant indicating that an error occurred during the verification phase (pre-reboot) of
+         * this staged session.
+         */
+        public static final int STAGED_SESSION_VERIFICATION_FAILED = 1;
+
+        /**
+         * Constant indicating that an error occurred during the activation phase (post-reboot) of
+         * this staged session.
+         */
+        public static final int STAGED_SESSION_ACTIVATION_FAILED = 2;
+
+        /**
+         * Constant indicating that an unknown error occurred while processing this staged session.
+         */
+        public static final int STAGED_SESSION_UNKNOWN = 3;
+
+        /** {@hide} */
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+        public int sessionId;
+        /** {@hide} */
+        public int userId;
+        /** {@hide} */
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+        public String installerPackageName;
+        /** {@hide} */
+        @UnsupportedAppUsage
+        public String resolvedBaseCodePath;
+        /** {@hide} */
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+        public float progress;
+        /** {@hide} */
+        @UnsupportedAppUsage
+        public boolean sealed;
+        /** {@hide} */
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+        public boolean active;
+
+        /** {@hide} */
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+        public int mode;
+        /** {@hide} */
+        public @InstallReason int installReason;
+        /** {@hide} */
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+        public long sizeBytes;
+        /** {@hide} */
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+        public String appPackageName;
+        /** {@hide} */
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+        public Bitmap appIcon;
+        /** {@hide} */
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+        public CharSequence appLabel;
+
+        /** {@hide} */
+        public int installLocation;
+        /** {@hide} */
+        public Uri originatingUri;
+        /** {@hide} */
+        public int originatingUid;
+        /** {@hide} */
+        public Uri referrerUri;
+        /** {@hide} */
+        public String[] grantedRuntimePermissions;
+        /** {@hide}*/
+        public List<String> whitelistedRestrictedPermissions;
+        /** {@hide}*/
+        public int autoRevokePermissionsMode = MODE_DEFAULT;
+        /** {@hide} */
+        public int installFlags;
+        /** {@hide} */
+        public boolean isMultiPackage;
+        /** {@hide} */
+        public boolean isStaged;
+        /** {@hide} */
+        public boolean forceQueryable;
+        /** {@hide} */
+        public int parentSessionId = INVALID_ID;
+        /** {@hide} */
+        public int[] childSessionIds = NO_SESSIONS;
+
+        /** {@hide} */
+        public boolean isStagedSessionApplied;
+        /** {@hide} */
+        public boolean isStagedSessionReady;
+        /** {@hide} */
+        public boolean isStagedSessionFailed;
+        private int mStagedSessionErrorCode;
+        private String mStagedSessionErrorMessage;
+
+        /** {@hide} */
+        public boolean isCommitted;
+
+        /** {@hide} */
+        public long createdMillis;
+
+        /** {@hide} */
+        public long updatedMillis;
+
+        /** {@hide} */
+        public int rollbackDataPolicy;
+
+        /** {@hide} */
+        @UnsupportedAppUsage
+        public SessionInfo() {
+        }
+
+        /** {@hide} */
+        public SessionInfo(Parcel source) {
+            sessionId = source.readInt();
+            userId = source.readInt();
+            installerPackageName = source.readString();
+            resolvedBaseCodePath = source.readString();
+            progress = source.readFloat();
+            sealed = source.readInt() != 0;
+            active = source.readInt() != 0;
+
+            mode = source.readInt();
+            installReason = source.readInt();
+            sizeBytes = source.readLong();
+            appPackageName = source.readString();
+            appIcon = source.readParcelable(null);
+            appLabel = source.readString();
+
+            installLocation = source.readInt();
+            originatingUri = source.readParcelable(null);
+            originatingUid = source.readInt();
+            referrerUri = source.readParcelable(null);
+            grantedRuntimePermissions = source.readStringArray();
+            whitelistedRestrictedPermissions = source.createStringArrayList();
+            autoRevokePermissionsMode = source.readInt();
+
+            installFlags = source.readInt();
+            isMultiPackage = source.readBoolean();
+            isStaged = source.readBoolean();
+            forceQueryable = source.readBoolean();
+            parentSessionId = source.readInt();
+            childSessionIds = source.createIntArray();
+            if (childSessionIds == null) {
+                childSessionIds = NO_SESSIONS;
+            }
+            isStagedSessionApplied = source.readBoolean();
+            isStagedSessionReady = source.readBoolean();
+            isStagedSessionFailed = source.readBoolean();
+            mStagedSessionErrorCode = source.readInt();
+            mStagedSessionErrorMessage = source.readString();
+            isCommitted = source.readBoolean();
+            rollbackDataPolicy = source.readInt();
+            createdMillis = source.readLong();
+        }
+
+        /**
+         * Return the ID for this session.
+         */
+        public int getSessionId() {
+            return sessionId;
+        }
+
+        /**
+         * Return the user associated with this session.
+         */
+        public @NonNull UserHandle getUser() {
+            return new UserHandle(userId);
+        }
+
+        /**
+         * Return the package name of the app that owns this session.
+         */
+        public @Nullable String getInstallerPackageName() {
+            return installerPackageName;
+        }
+
+        /**
+         * Return current overall progress of this session, between 0 and 1.
+         * <p>
+         * Note that this progress may not directly correspond to the value
+         * reported by
+         * {@link PackageInstaller.Session#setStagingProgress(float)}, as the
+         * system may carve out a portion of the overall progress to represent
+         * its own internal installation work.
+         */
+        public float getProgress() {
+            return progress;
+        }
+
+        /**
+         * Return if this session is currently active.
+         * <p>
+         * A session is considered active whenever there is ongoing forward
+         * progress being made, such as the installer holding an open
+         * {@link Session} instance while streaming data into place, or the
+         * system optimizing code as the result of
+         * {@link Session#commit(IntentSender)}.
+         * <p>
+         * If the installer closes the {@link Session} without committing, the
+         * session is considered inactive until the installer opens the session
+         * again.
+         */
+        public boolean isActive() {
+            return active;
+        }
+
+        /**
+         * Return if this session is sealed.
+         * <p>
+         * Once sealed, no further changes may be made to the session. A session
+         * is sealed the moment {@link Session#commit(IntentSender)} is called.
+         */
+        public boolean isSealed() {
+            return sealed;
+        }
+
+        /**
+         * Return the reason for installing this package.
+         *
+         * @return The install reason.
+         */
+        public @InstallReason int getInstallReason() {
+            return installReason;
+        }
+
+        /** {@hide} */
+        @Deprecated
+        public boolean isOpen() {
+            return isActive();
+        }
+
+        /**
+         * Return the package name this session is working with. May be {@code null}
+         * if unknown.
+         */
+        public @Nullable String getAppPackageName() {
+            return appPackageName;
+        }
+
+        /**
+         * Return an icon representing the app being installed. May be {@code null}
+         * if unavailable.
+         */
+        public @Nullable Bitmap getAppIcon() {
+            if (appIcon == null) {
+                // Icon may have been omitted for calls that return bulk session
+                // lists, so try fetching the specific icon.
+                try {
+                    final SessionInfo info = AppGlobals.getPackageManager().getPackageInstaller()
+                            .getSessionInfo(sessionId);
+                    appIcon = (info != null) ? info.appIcon : null;
+                } catch (RemoteException e) {
+                    throw e.rethrowFromSystemServer();
+                }
+            }
+            return appIcon;
+        }
+
+        /**
+         * Return a label representing the app being installed. May be {@code null}
+         * if unavailable.
+         */
+        public @Nullable CharSequence getAppLabel() {
+            return appLabel;
+        }
+
+        /**
+         * Return an Intent that can be started to view details about this install
+         * session. This may surface actions such as pause, resume, or cancel.
+         * <p>
+         * In some cases, a matching Activity may not exist, so ensure you safeguard
+         * against this.
+         *
+         * @see PackageInstaller#ACTION_SESSION_DETAILS
+         */
+        public @Nullable Intent createDetailsIntent() {
+            final Intent intent = new Intent(PackageInstaller.ACTION_SESSION_DETAILS);
+            intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
+            intent.setPackage(installerPackageName);
+            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            return intent;
+        }
+
+        /**
+         * Get the mode of the session as set in the constructor of the {@link SessionParams}.
+         *
+         * @return One of {@link SessionParams#MODE_FULL_INSTALL}
+         *         or {@link SessionParams#MODE_INHERIT_EXISTING}
+         */
+        public int getMode() {
+            return mode;
+        }
+
+        /**
+         * Get the value set in {@link SessionParams#setInstallLocation(int)}.
+         */
+        public int getInstallLocation() {
+            return installLocation;
+        }
+
+        /**
+         * Get the value as set in {@link SessionParams#setSize(long)}.
+         *
+         * <p>The value is a hint and does not have to match the actual size.
+         */
+        public long getSize() {
+            return sizeBytes;
+        }
+
+        /**
+         * Get the value set in {@link SessionParams#setOriginatingUri(Uri)}.
+         * Note: This value will only be non-null for the owner of the session.
+         */
+        public @Nullable Uri getOriginatingUri() {
+            return originatingUri;
+        }
+
+        /**
+         * Get the value set in {@link SessionParams#setOriginatingUid(int)}.
+         */
+        public int getOriginatingUid() {
+            return originatingUid;
+        }
+
+        /**
+         * Get the value set in {@link SessionParams#setReferrerUri(Uri)}
+         * Note: This value will only be non-null for the owner of the session.
+         */
+        public @Nullable Uri getReferrerUri() {
+            return referrerUri;
+        }
+
+        /**
+         * Get the value set in {@link SessionParams#setGrantedRuntimePermissions(String[])}.
+         *
+         * @hide
+         */
+        @SystemApi
+        public @Nullable String[] getGrantedRuntimePermissions() {
+            return grantedRuntimePermissions;
+        }
+
+        /**
+         * Get the value set in {@link SessionParams#setWhitelistedRestrictedPermissions(Set)}.
+         * Note that if all permissions are whitelisted this method returns {@link
+         * SessionParams#RESTRICTED_PERMISSIONS_ALL}.
+         *
+         * @hide
+         */
+        @TestApi
+        @SystemApi
+        public @NonNull Set<String> getWhitelistedRestrictedPermissions() {
+            if ((installFlags & PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS) != 0) {
+                return SessionParams.RESTRICTED_PERMISSIONS_ALL;
+            }
+            if (whitelistedRestrictedPermissions != null) {
+                return new ArraySet<>(whitelistedRestrictedPermissions);
+            }
+            return Collections.emptySet();
+        }
+
+        /**
+         * Get the status of whether permission auto-revocation should be allowed, ignored, or
+         * deferred to manifest data.
+         *
+         * @see android.app.AppOpsManager#MODE_ALLOWED
+         * @see android.app.AppOpsManager#MODE_IGNORED
+         * @see android.app.AppOpsManager#MODE_DEFAULT
+         *
+         * @return the status of auto-revoke for this package
+         *
+         * @hide
+         */
+        @TestApi
+        @SystemApi
+        public int getAutoRevokePermissionsMode() {
+            return autoRevokePermissionsMode;
+        }
+
+        /**
+         * Get the value set in {@link SessionParams#setAllowDowngrade(boolean)}.
+         *
+         * @deprecated use {@link #getRequestDowngrade()}.
+         * @hide
+         */
+        @SystemApi
+        @Deprecated
+        public boolean getAllowDowngrade() {
+            return getRequestDowngrade();
+        }
+
+        /**
+         * Get the value set in {@link SessionParams#setRequestDowngrade(boolean)}.
+         *
+         * @hide
+         */
+        @SystemApi
+        public boolean getRequestDowngrade() {
+            return (installFlags & PackageManager.INSTALL_REQUEST_DOWNGRADE) != 0;
+        }
+
+        /**
+         * Get the value set in {@link SessionParams#setDontKillApp(boolean)}.
+         *
+         * @hide
+         */
+        @SystemApi
+        public boolean getDontKillApp() {
+            return (installFlags & PackageManager.INSTALL_DONT_KILL_APP) != 0;
+        }
+
+        /**
+         * If {@link SessionParams#setInstallAsInstantApp(boolean)} was called with {@code true},
+         * return true. If it was called with {@code false} or if it was not called return false.
+         *
+         * @hide
+         *
+         * @see #getInstallAsFullApp
+         */
+        @SystemApi
+        public boolean getInstallAsInstantApp(boolean isInstantApp) {
+            return (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
+        }
+
+        /**
+         * If {@link SessionParams#setInstallAsInstantApp(boolean)} was called with {@code false},
+         * return true. If it was called with {@code true} or if it was not called return false.
+         *
+         * @hide
+         *
+         * @see #getInstallAsInstantApp
+         */
+        @SystemApi
+        public boolean getInstallAsFullApp(boolean isInstantApp) {
+            return (installFlags & PackageManager.INSTALL_FULL_APP) != 0;
+        }
+
+        /**
+         * Get if {@link SessionParams#setInstallAsVirtualPreload()} was called.
+         *
+         * @hide
+         */
+        @SystemApi
+        public boolean getInstallAsVirtualPreload() {
+            return (installFlags & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0;
+        }
+
+        /**
+         * Return whether rollback is enabled or disabled for the given upgrade.
+         *
+         * @hide
+         */
+        @SystemApi
+        public boolean getEnableRollback() {
+            return (installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0;
+        }
+
+        /**
+         * Get the value set in {@link SessionParams#setAllocateAggressive(boolean)}.
+         *
+         * @hide
+         */
+        @SystemApi
+        public boolean getAllocateAggressive() {
+            return (installFlags & PackageManager.INSTALL_ALLOCATE_AGGRESSIVE) != 0;
+        }
+
+
+        /** {@hide} */
+        @Deprecated
+        public @Nullable Intent getDetailsIntent() {
+            return createDetailsIntent();
+        }
+
+        /**
+         * Returns true if this session is a multi-package session containing references to other
+         * sessions.
+         */
+        public boolean isMultiPackage() {
+            return isMultiPackage;
+        }
+
+        /**
+         * Returns true if this session is a staged session.
+         */
+        public boolean isStaged() {
+            return isStaged;
+        }
+
+        /**
+         * Return the data policy associated with the rollback for the given upgrade.
+         *
+         * @hide
+         */
+        @SystemApi @TestApi
+        @PackageManager.RollbackDataPolicy
+        public int getRollbackDataPolicy() {
+            return rollbackDataPolicy;
+        }
+
+        /**
+         * Returns true if this session is marked as forceQueryable
+         * {@hide}
+         */
+        public boolean isForceQueryable() {
+            return forceQueryable;
+        }
+
+        /**
+         * Returns {@code true} if this session is an active staged session.
+         *
+         * We consider a session active if it has been committed and it is either pending
+         * verification, or will be applied at next reboot.
+         *
+         * <p>Staged session is active iff:
+         * <ul>
+         *     <li>It is committed, i.e. {@link SessionInfo#isCommitted()} is {@code true}, and
+         *     <li>it is not applied, i.e. {@link SessionInfo#isStagedSessionApplied()} is {@code
+         *     false}, and
+         *     <li>it is not failed, i.e. {@link SessionInfo#isStagedSessionFailed()} is
+         *     {@code false}.
+         * </ul>
+         *
+         * <p>In case of a multi-package session, reasoning above is applied to the parent session,
+         * since that is the one that should have been {@link Session#commit committed}.
+         */
+        public boolean isStagedSessionActive() {
+            return isStaged && isCommitted && !isStagedSessionApplied && !isStagedSessionFailed
+                    && !hasParentSessionId();
+        }
+
+        /**
+         * Returns the parent multi-package session ID if this session belongs to one,
+         * {@link #INVALID_ID} otherwise.
+         */
+        public int getParentSessionId() {
+            return parentSessionId;
+        }
+
+        /**
+         * Returns true if session has a valid parent session, otherwise false.
+         */
+        public boolean hasParentSessionId() {
+            return parentSessionId != INVALID_ID;
+        }
+
+        /**
+         * Returns the set of session IDs that will be committed when this session is commited if
+         * this session is a multi-package session.
+         */
+        @NonNull
+        public int[] getChildSessionIds() {
+            return childSessionIds;
+        }
+
+        private void checkSessionIsStaged() {
+            if (!isStaged) {
+                throw new IllegalStateException("Session is not marked as staged.");
+            }
+        }
+
+        /**
+         * Whether the staged session has been applied successfully, meaning that all of its
+         * packages have been activated and no further action is required.
+         * Only meaningful if {@code isStaged} is true.
+         */
+        public boolean isStagedSessionApplied() {
+            checkSessionIsStaged();
+            return isStagedSessionApplied;
+        }
+
+        /**
+         * Whether the staged session is ready to be applied at next reboot. Only meaningful if
+         * {@code isStaged} is true.
+         */
+        public boolean isStagedSessionReady() {
+            checkSessionIsStaged();
+            return isStagedSessionReady;
+        }
+
+        /**
+         * Whether something went wrong and the staged session is declared as failed, meaning that
+         * it will be ignored at next reboot. Only meaningful if {@code isStaged} is true.
+         */
+        public boolean isStagedSessionFailed() {
+            checkSessionIsStaged();
+            return isStagedSessionFailed;
+        }
+
+        /**
+         * If something went wrong with a staged session, clients can check this error code to
+         * understand which kind of failure happened. Only meaningful if {@code isStaged} is true.
+         */
+        public @StagedSessionErrorCode int getStagedSessionErrorCode() {
+            checkSessionIsStaged();
+            return mStagedSessionErrorCode;
+        }
+
+        /**
+         * Text description of the error code returned by {@code getStagedSessionErrorCode}, or
+         * empty string if no error was encountered.
+         */
+        public @NonNull String getStagedSessionErrorMessage() {
+            checkSessionIsStaged();
+            return mStagedSessionErrorMessage;
+        }
+
+        /** {@hide} */
+        public void setStagedSessionErrorCode(@StagedSessionErrorCode int errorCode,
+                                              String errorMessage) {
+            mStagedSessionErrorCode = errorCode;
+            mStagedSessionErrorMessage = errorMessage;
+        }
+
+        /**
+         * Returns {@code true} if {@link Session#commit(IntentSender)}} was called for this
+         * session.
+         */
+        public boolean isCommitted() {
+            return isCommitted;
+        }
+
+        /**
+         * The timestamp of the initial creation of the session.
+         */
+        public long getCreatedMillis() {
+            return createdMillis;
+        }
+
+        /**
+         * The timestamp of the last update that occurred to the session, including changing of
+         * states in case of staged sessions.
+         */
+        @CurrentTimeMillisLong
+        public long getUpdatedMillis() {
+            return updatedMillis;
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(sessionId);
+            dest.writeInt(userId);
+            dest.writeString(installerPackageName);
+            dest.writeString(resolvedBaseCodePath);
+            dest.writeFloat(progress);
+            dest.writeInt(sealed ? 1 : 0);
+            dest.writeInt(active ? 1 : 0);
+
+            dest.writeInt(mode);
+            dest.writeInt(installReason);
+            dest.writeLong(sizeBytes);
+            dest.writeString(appPackageName);
+            dest.writeParcelable(appIcon, flags);
+            dest.writeString(appLabel != null ? appLabel.toString() : null);
+
+            dest.writeInt(installLocation);
+            dest.writeParcelable(originatingUri, flags);
+            dest.writeInt(originatingUid);
+            dest.writeParcelable(referrerUri, flags);
+            dest.writeStringArray(grantedRuntimePermissions);
+            dest.writeStringList(whitelistedRestrictedPermissions);
+            dest.writeInt(autoRevokePermissionsMode);
+            dest.writeInt(installFlags);
+            dest.writeBoolean(isMultiPackage);
+            dest.writeBoolean(isStaged);
+            dest.writeBoolean(forceQueryable);
+            dest.writeInt(parentSessionId);
+            dest.writeIntArray(childSessionIds);
+            dest.writeBoolean(isStagedSessionApplied);
+            dest.writeBoolean(isStagedSessionReady);
+            dest.writeBoolean(isStagedSessionFailed);
+            dest.writeInt(mStagedSessionErrorCode);
+            dest.writeString(mStagedSessionErrorMessage);
+            dest.writeBoolean(isCommitted);
+            dest.writeInt(rollbackDataPolicy);
+            dest.writeLong(createdMillis);
+        }
+
+        public static final Parcelable.Creator<SessionInfo>
+                CREATOR = new Parcelable.Creator<SessionInfo>() {
+                    @Override
+                    public SessionInfo createFromParcel(Parcel p) {
+                        return new SessionInfo(p);
+                    }
+
+                    @Override
+                    public SessionInfo[] newArray(int size) {
+                        return new SessionInfo[size];
+                    }
+                };
+    }
+}
diff --git a/android/content/pm/PackageItemInfo.java b/android/content/pm/PackageItemInfo.java
new file mode 100644
index 0000000..d41ace5
--- /dev/null
+++ b/android/content/pm/PackageItemInfo.java
@@ -0,0 +1,496 @@
+/*
+ * 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.content.pm;
+
+import static android.text.TextUtils.SAFE_STRING_FLAG_FIRST_LINE;
+import static android.text.TextUtils.SAFE_STRING_FLAG_SINGLE_LINE;
+import static android.text.TextUtils.SAFE_STRING_FLAG_TRIM;
+import static android.text.TextUtils.makeSafeForPresentation;
+
+import android.annotation.FloatRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.content.res.XmlResourceParser;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.Printer;
+import android.util.proto.ProtoOutputStream;
+
+
+import java.text.Collator;
+import java.util.Comparator;
+import java.util.Objects;
+
+/**
+ * Base class containing information common to all package items held by
+ * the package manager.  This provides a very common basic set of attributes:
+ * a label, icon, and meta-data.  This class is not intended
+ * to be used by itself; it is simply here to share common definitions
+ * between all items returned by the package manager.  As such, it does not
+ * itself implement Parcelable, but does provide convenience methods to assist
+ * in the implementation of Parcelable in subclasses.
+ */
+public class PackageItemInfo {
+    /** The maximum length of a safe label, in characters */
+    private static final int MAX_SAFE_LABEL_LENGTH = 50000;
+
+    /** @hide */
+    public static final float DEFAULT_MAX_LABEL_SIZE_PX = 500f;
+
+    /**
+     * Remove {@link Character#isWhitespace(int) whitespace} and non-breaking spaces from the edges
+     * of the label.
+     *
+     * @see #loadSafeLabel(PackageManager, float, int)
+     *
+     * @deprecated Use {@link TextUtils#SAFE_STRING_FLAG_TRIM} instead
+     * @hide
+     * @removed
+     */
+    @Deprecated
+    @SystemApi
+    public static final int SAFE_LABEL_FLAG_TRIM = SAFE_STRING_FLAG_TRIM;
+
+    /**
+     * Force entire string into single line of text (no newlines). Cannot be set at the same time as
+     * {@link #SAFE_LABEL_FLAG_FIRST_LINE}.
+     *
+     * @see #loadSafeLabel(PackageManager, float, int)
+     *
+     * @deprecated Use {@link TextUtils#SAFE_STRING_FLAG_SINGLE_LINE} instead
+     * @hide
+     * @removed
+     */
+    @Deprecated
+    @SystemApi
+    public static final int SAFE_LABEL_FLAG_SINGLE_LINE = SAFE_STRING_FLAG_SINGLE_LINE;
+
+    /**
+     * Return only first line of text (truncate at first newline). Cannot be set at the same time as
+     * {@link #SAFE_LABEL_FLAG_SINGLE_LINE}.
+     *
+     * @see #loadSafeLabel(PackageManager, float, int)
+     *
+     * @deprecated Use {@link TextUtils#SAFE_STRING_FLAG_FIRST_LINE} instead
+     * @hide
+     * @removed
+     */
+    @Deprecated
+    @SystemApi
+    public static final int SAFE_LABEL_FLAG_FIRST_LINE = SAFE_STRING_FLAG_FIRST_LINE;
+
+    private static volatile boolean sForceSafeLabels = false;
+
+    /**
+     * Always use {@link #loadSafeLabel safe labels} when calling {@link #loadLabel}.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static void forceSafeLabels() {
+        sForceSafeLabels = true;
+    }
+
+    /**
+     * Public name of this item. From the "android:name" attribute.
+     */
+    public String name;
+
+    /**
+     * Name of the package that this item is in.
+     */
+    public String packageName;
+
+    /**
+     * A string resource identifier (in the package's resources) of this
+     * component's label.  From the "label" attribute or, if not set, 0.
+     */
+    public int labelRes;
+
+    /**
+     * The string provided in the AndroidManifest file, if any.  You
+     * probably don't want to use this.  You probably want
+     * {@link PackageManager#getApplicationLabel}
+     */
+    public CharSequence nonLocalizedLabel;
+
+    /**
+     * A drawable resource identifier (in the package's resources) of this
+     * component's icon.  From the "icon" attribute or, if not set, 0.
+     */
+    public int icon;
+
+    /**
+     * A drawable resource identifier (in the package's resources) of this
+     * component's banner.  From the "banner" attribute or, if not set, 0.
+     */
+    public int banner;
+
+    /**
+     * A drawable resource identifier (in the package's resources) of this
+     * component's logo. Logos may be larger/wider than icons and are
+     * displayed by certain UI elements in place of a name or name/icon
+     * combination. From the "logo" attribute or, if not set, 0.
+     */
+    public int logo;
+
+    /**
+     * Additional meta-data associated with this component.  This field
+     * will only be filled in if you set the
+     * {@link PackageManager#GET_META_DATA} flag when requesting the info.
+     */
+    public Bundle metaData;
+
+    /**
+     * If different of UserHandle.USER_NULL, The icon of this item will represent that user.
+     * @hide
+     */
+    public int showUserIcon;
+
+    public PackageItemInfo() {
+        showUserIcon = UserHandle.USER_NULL;
+    }
+
+    public PackageItemInfo(PackageItemInfo orig) {
+        name = orig.name;
+        if (name != null) name = name.trim();
+        packageName = orig.packageName;
+        labelRes = orig.labelRes;
+        nonLocalizedLabel = orig.nonLocalizedLabel;
+        if (nonLocalizedLabel != null) nonLocalizedLabel = nonLocalizedLabel.toString().trim();
+        icon = orig.icon;
+        banner = orig.banner;
+        logo = orig.logo;
+        metaData = orig.metaData;
+        showUserIcon = orig.showUserIcon;
+    }
+
+    /**
+     * Retrieve the current textual label associated with this item.  This
+     * will call back on the given PackageManager to load the label from
+     * the application.
+     *
+     * @param pm A PackageManager from which the label can be loaded; usually
+     * the PackageManager from which you originally retrieved this item.
+     *
+     * @return Returns a CharSequence containing the item's label.  If the
+     * item does not have a label, its name is returned.
+     */
+    public @NonNull CharSequence loadLabel(@NonNull PackageManager pm) {
+        if (sForceSafeLabels) {
+            return loadSafeLabel(pm, DEFAULT_MAX_LABEL_SIZE_PX, SAFE_STRING_FLAG_TRIM
+                    | SAFE_STRING_FLAG_FIRST_LINE);
+        } else {
+            return loadUnsafeLabel(pm);
+        }
+    }
+
+    /** {@hide} */
+    public CharSequence loadUnsafeLabel(PackageManager pm) {
+        if (nonLocalizedLabel != null) {
+            return nonLocalizedLabel;
+        }
+        if (labelRes != 0) {
+            CharSequence label = pm.getText(packageName, labelRes, getApplicationInfo());
+            if (label != null) {
+                return label.toString().trim();
+            }
+        }
+        if (name != null) {
+            return name;
+        }
+        return packageName;
+    }
+
+    /**
+     * @hide
+     * @deprecated use loadSafeLabel(PackageManager, float, int) instead
+     */
+    @SystemApi
+    @Deprecated
+    public @NonNull CharSequence loadSafeLabel(@NonNull PackageManager pm) {
+        return loadSafeLabel(pm, DEFAULT_MAX_LABEL_SIZE_PX, SAFE_STRING_FLAG_TRIM
+                | SAFE_STRING_FLAG_FIRST_LINE);
+    }
+
+    /**
+     * Calls {@link TextUtils#makeSafeForPresentation} for the label of this item.
+     *
+     * <p>For parameters see {@link TextUtils#makeSafeForPresentation}.
+     *
+     * @hide
+    */
+    @SystemApi
+    public @NonNull CharSequence loadSafeLabel(@NonNull PackageManager pm,
+            @FloatRange(from = 0) float ellipsizeDip, @TextUtils.SafeStringFlags int flags) {
+        Objects.requireNonNull(pm);
+
+        return makeSafeForPresentation(loadUnsafeLabel(pm).toString(), MAX_SAFE_LABEL_LENGTH,
+                ellipsizeDip, flags);
+    }
+
+    /**
+     * Retrieve the current graphical icon associated with this item.  This
+     * will call back on the given PackageManager to load the icon from
+     * the application.
+     *
+     * @param pm A PackageManager from which the icon can be loaded; usually
+     * the PackageManager from which you originally retrieved this item.
+     *
+     * @return Returns a Drawable containing the item's icon.  If the
+     * item does not have an icon, the item's default icon is returned
+     * such as the default activity icon.
+     */
+    public Drawable loadIcon(PackageManager pm) {
+        return pm.loadItemIcon(this, getApplicationInfo());
+    }
+
+    /**
+     * Retrieve the current graphical icon associated with this item without
+     * the addition of a work badge if applicable.
+     * This will call back on the given PackageManager to load the icon from
+     * the application.
+     *
+     * @param pm A PackageManager from which the icon can be loaded; usually
+     * the PackageManager from which you originally retrieved this item.
+     *
+     * @return Returns a Drawable containing the item's icon.  If the
+     * item does not have an icon, the item's default icon is returned
+     * such as the default activity icon.
+     */
+    public Drawable loadUnbadgedIcon(PackageManager pm) {
+        return pm.loadUnbadgedItemIcon(this, getApplicationInfo());
+    }
+
+    /**
+     * Retrieve the current graphical banner associated with this item.  This
+     * will call back on the given PackageManager to load the banner from
+     * the application.
+     *
+     * @param pm A PackageManager from which the banner can be loaded; usually
+     * the PackageManager from which you originally retrieved this item.
+     *
+     * @return Returns a Drawable containing the item's banner.  If the item
+     * does not have a banner, this method will return null.
+     */
+    public Drawable loadBanner(PackageManager pm) {
+        if (banner != 0) {
+            Drawable dr = pm.getDrawable(packageName, banner, getApplicationInfo());
+            if (dr != null) {
+                return dr;
+            }
+        }
+        return loadDefaultBanner(pm);
+    }
+
+    /**
+     * Retrieve the default graphical icon associated with this item.
+     *
+     * @param pm A PackageManager from which the icon can be loaded; usually
+     * the PackageManager from which you originally retrieved this item.
+     *
+     * @return Returns a Drawable containing the item's default icon
+     * such as the default activity icon.
+     *
+     * @hide
+     */
+    public Drawable loadDefaultIcon(PackageManager pm) {
+        return pm.getDefaultActivityIcon();
+    }
+
+    /**
+     * Retrieve the default graphical banner associated with this item.
+     *
+     * @param pm A PackageManager from which the banner can be loaded; usually
+     * the PackageManager from which you originally retrieved this item.
+     *
+     * @return Returns a Drawable containing the item's default banner
+     * or null if no default logo is available.
+     *
+     * @hide
+     */
+    protected Drawable loadDefaultBanner(PackageManager pm) {
+        return null;
+    }
+
+    /**
+     * Retrieve the current graphical logo associated with this item. This
+     * will call back on the given PackageManager to load the logo from
+     * the application.
+     *
+     * @param pm A PackageManager from which the logo can be loaded; usually
+     * the PackageManager from which you originally retrieved this item.
+     *
+     * @return Returns a Drawable containing the item's logo. If the item
+     * does not have a logo, this method will return null.
+     */
+    public Drawable loadLogo(PackageManager pm) {
+        if (logo != 0) {
+            Drawable d = pm.getDrawable(packageName, logo, getApplicationInfo());
+            if (d != null) {
+                return d;
+            }
+        }
+        return loadDefaultLogo(pm);
+    }
+
+    /**
+     * Retrieve the default graphical logo associated with this item.
+     *
+     * @param pm A PackageManager from which the logo can be loaded; usually
+     * the PackageManager from which you originally retrieved this item.
+     *
+     * @return Returns a Drawable containing the item's default logo
+     * or null if no default logo is available.
+     *
+     * @hide
+     */
+    protected Drawable loadDefaultLogo(PackageManager pm) {
+        return null;
+    }
+
+    /**
+     * Load an XML resource attached to the meta-data of this item.  This will
+     * retrieved the name meta-data entry, and if defined call back on the
+     * given PackageManager to load its XML file from the application.
+     *
+     * @param pm A PackageManager from which the XML can be loaded; usually
+     * the PackageManager from which you originally retrieved this item.
+     * @param name Name of the meta-date you would like to load.
+     *
+     * @return Returns an XmlPullParser you can use to parse the XML file
+     * assigned as the given meta-data.  If the meta-data name is not defined
+     * or the XML resource could not be found, null is returned.
+     */
+    public XmlResourceParser loadXmlMetaData(PackageManager pm, String name) {
+        if (metaData != null) {
+            int resid = metaData.getInt(name);
+            if (resid != 0) {
+                return pm.getXml(packageName, resid, getApplicationInfo());
+            }
+        }
+        return null;
+    }
+
+    /**
+     * @hide Flag for dumping: include all details.
+     */
+    public static final int DUMP_FLAG_DETAILS = 1<<0;
+
+    /**
+     * @hide Flag for dumping: include nested ApplicationInfo.
+     */
+    public static final int DUMP_FLAG_APPLICATION = 1<<1;
+
+    /**
+     * @hide Flag for dumping: all flags to dump everything.
+     */
+    public static final int DUMP_FLAG_ALL = DUMP_FLAG_DETAILS | DUMP_FLAG_APPLICATION;
+
+    protected void dumpFront(Printer pw, String prefix) {
+        if (name != null) {
+            pw.println(prefix + "name=" + name);
+        }
+        pw.println(prefix + "packageName=" + packageName);
+        if (labelRes != 0 || nonLocalizedLabel != null || icon != 0 || banner != 0) {
+            pw.println(prefix + "labelRes=0x" + Integer.toHexString(labelRes)
+                    + " nonLocalizedLabel=" + nonLocalizedLabel
+                    + " icon=0x" + Integer.toHexString(icon)
+                    + " banner=0x" + Integer.toHexString(banner));
+        }
+    }
+
+    protected void dumpBack(Printer pw, String prefix) {
+        // no back here
+    }
+
+    public void writeToParcel(Parcel dest, int parcelableFlags) {
+        dest.writeString8(name);
+        dest.writeString8(packageName);
+        dest.writeInt(labelRes);
+        TextUtils.writeToParcel(nonLocalizedLabel, dest, parcelableFlags);
+        dest.writeInt(icon);
+        dest.writeInt(logo);
+        dest.writeBundle(metaData);
+        dest.writeInt(banner);
+        dest.writeInt(showUserIcon);
+    }
+
+    /**
+     * @hide
+     */
+    public void dumpDebug(ProtoOutputStream proto, long fieldId, int dumpFlags) {
+        long token = proto.start(fieldId);
+        if (name != null) {
+            proto.write(PackageItemInfoProto.NAME, name);
+        }
+        proto.write(PackageItemInfoProto.PACKAGE_NAME, packageName);
+        proto.write(PackageItemInfoProto.LABEL_RES, labelRes);
+        if (nonLocalizedLabel != null) {
+            proto.write(PackageItemInfoProto.NON_LOCALIZED_LABEL, nonLocalizedLabel.toString());
+        }
+        proto.write(PackageItemInfoProto.ICON, icon);
+        proto.write(PackageItemInfoProto.BANNER, banner);
+        proto.end(token);
+    }
+
+    protected PackageItemInfo(Parcel source) {
+        name = source.readString8();
+        packageName = source.readString8();
+        labelRes = source.readInt();
+        nonLocalizedLabel
+                = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+        icon = source.readInt();
+        logo = source.readInt();
+        metaData = source.readBundle();
+        banner = source.readInt();
+        showUserIcon = source.readInt();
+    }
+
+    /**
+     * Get the ApplicationInfo for the application to which this item belongs,
+     * if available, otherwise returns null.
+     *
+     * @return Returns the ApplicationInfo of this item, or null if not known.
+     *
+     * @hide
+     */
+    protected ApplicationInfo getApplicationInfo() {
+        return null;
+    }
+
+    public static class DisplayNameComparator
+            implements Comparator<PackageItemInfo> {
+        public DisplayNameComparator(PackageManager pm) {
+            mPM = pm;
+        }
+
+        public final int compare(PackageItemInfo aa, PackageItemInfo ab) {
+            CharSequence  sa = aa.loadLabel(mPM);
+            if (sa == null) sa = aa.name;
+            CharSequence  sb = ab.loadLabel(mPM);
+            if (sb == null) sb = ab.name;
+            return sCollator.compare(sa.toString(), sb.toString());
+        }
+
+        private final Collator   sCollator = Collator.getInstance();
+        private PackageManager   mPM;
+    }
+}
diff --git a/android/content/pm/PackageManager.java b/android/content/pm/PackageManager.java
new file mode 100644
index 0000000..f92d095
--- /dev/null
+++ b/android/content/pm/PackageManager.java
@@ -0,0 +1,8206 @@
+/*
+ * 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.content.pm;
+
+import android.Manifest;
+import android.annotation.CheckResult;
+import android.annotation.DrawableRes;
+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.SdkConstant.SdkConstantType;
+import android.annotation.StringRes;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.annotation.UserIdInt;
+import android.annotation.XmlRes;
+import android.app.ActivityManager;
+import android.app.ActivityThread;
+import android.app.AppDetailsActivity;
+import android.app.PackageDeleteObserver;
+import android.app.PackageInstallObserver;
+import android.app.PropertyInvalidatedCache;
+import android.app.admin.DevicePolicyManager;
+import android.app.usage.StorageStatsManager;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
+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.PackageParser.PackageParserException;
+import android.content.pm.dex.ArtManager;
+import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
+import android.graphics.Rect;
+import android.graphics.drawable.AdaptiveIconDrawable;
+import android.graphics.drawable.Drawable;
+import android.net.wifi.WifiManager;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.PersistableBundle;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.os.incremental.IncrementalManager;
+import android.os.storage.StorageManager;
+import android.os.storage.VolumeInfo;
+import android.permission.PermissionManager;
+import android.util.AndroidException;
+import android.util.Log;
+
+import com.android.internal.util.ArrayUtils;
+
+import dalvik.system.VMRuntime;
+
+import java.io.File;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Class for retrieving various kinds of information related to the application
+ * packages that are currently installed on the device.
+ *
+ * You can find this class through {@link Context#getPackageManager}.
+ */
+public abstract class PackageManager {
+    private static final String TAG = "PackageManager";
+
+    /** {@hide} */
+    public static final boolean APPLY_DEFAULT_TO_DEVICE_PROTECTED_STORAGE = true;
+
+    /**
+     * This exception is thrown when a given package, application, or component
+     * name cannot be found.
+     */
+    public static class NameNotFoundException extends AndroidException {
+        public NameNotFoundException() {
+        }
+
+        public NameNotFoundException(String name) {
+            super(name);
+        }
+    }
+
+    /**
+     * Listener for changes in permissions granted to a UID.
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    public interface OnPermissionsChangedListener {
+
+        /**
+         * Called when the permissions for a UID change.
+         * @param uid The UID with a change.
+         */
+        public void onPermissionsChanged(int uid);
+    }
+
+    /**
+     * As a guiding principle:
+     * <p>
+     * {@code GET_} flags are used to request additional data that may have been
+     * elided to save wire space.
+     * <p>
+     * {@code MATCH_} flags are used to include components or packages that
+     * would have otherwise been omitted from a result set by current system
+     * state.
+     */
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "GET_", "MATCH_" }, value = {
+            GET_ACTIVITIES,
+            GET_CONFIGURATIONS,
+            GET_GIDS,
+            GET_INSTRUMENTATION,
+            GET_INTENT_FILTERS,
+            GET_META_DATA,
+            GET_PERMISSIONS,
+            GET_PROVIDERS,
+            GET_RECEIVERS,
+            GET_SERVICES,
+            GET_SHARED_LIBRARY_FILES,
+            GET_SIGNATURES,
+            GET_SIGNING_CERTIFICATES,
+            GET_URI_PERMISSION_PATTERNS,
+            MATCH_UNINSTALLED_PACKAGES,
+            MATCH_DISABLED_COMPONENTS,
+            MATCH_DISABLED_UNTIL_USED_COMPONENTS,
+            MATCH_SYSTEM_ONLY,
+            MATCH_FACTORY_ONLY,
+            MATCH_DEBUG_TRIAGED_MISSING,
+            MATCH_INSTANT,
+            MATCH_APEX,
+            GET_DISABLED_COMPONENTS,
+            GET_DISABLED_UNTIL_USED_COMPONENTS,
+            GET_UNINSTALLED_PACKAGES,
+            MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface PackageInfoFlags {}
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "GET_", "MATCH_" }, value = {
+            GET_META_DATA,
+            GET_SHARED_LIBRARY_FILES,
+            MATCH_UNINSTALLED_PACKAGES,
+            MATCH_SYSTEM_ONLY,
+            MATCH_DEBUG_TRIAGED_MISSING,
+            MATCH_DISABLED_COMPONENTS,
+            MATCH_DISABLED_UNTIL_USED_COMPONENTS,
+            MATCH_INSTANT,
+            MATCH_STATIC_SHARED_LIBRARIES,
+            GET_DISABLED_UNTIL_USED_COMPONENTS,
+            GET_UNINSTALLED_PACKAGES,
+            MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS,
+            MATCH_APEX,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ApplicationInfoFlags {}
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "GET_", "MATCH_" }, value = {
+            GET_META_DATA,
+            GET_SHARED_LIBRARY_FILES,
+            MATCH_ALL,
+            MATCH_DEBUG_TRIAGED_MISSING,
+            MATCH_DEFAULT_ONLY,
+            MATCH_DISABLED_COMPONENTS,
+            MATCH_DISABLED_UNTIL_USED_COMPONENTS,
+            MATCH_DIRECT_BOOT_AUTO,
+            MATCH_DIRECT_BOOT_AWARE,
+            MATCH_DIRECT_BOOT_UNAWARE,
+            MATCH_SYSTEM_ONLY,
+            MATCH_UNINSTALLED_PACKAGES,
+            MATCH_INSTANT,
+            MATCH_STATIC_SHARED_LIBRARIES,
+            GET_DISABLED_COMPONENTS,
+            GET_DISABLED_UNTIL_USED_COMPONENTS,
+            GET_UNINSTALLED_PACKAGES,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ComponentInfoFlags {}
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "GET_", "MATCH_" }, value = {
+            GET_META_DATA,
+            GET_RESOLVED_FILTER,
+            GET_SHARED_LIBRARY_FILES,
+            MATCH_ALL,
+            MATCH_DEBUG_TRIAGED_MISSING,
+            MATCH_DISABLED_COMPONENTS,
+            MATCH_DISABLED_UNTIL_USED_COMPONENTS,
+            MATCH_DEFAULT_ONLY,
+            MATCH_DIRECT_BOOT_AUTO,
+            MATCH_DIRECT_BOOT_AWARE,
+            MATCH_DIRECT_BOOT_UNAWARE,
+            MATCH_SYSTEM_ONLY,
+            MATCH_UNINSTALLED_PACKAGES,
+            MATCH_INSTANT,
+            GET_DISABLED_COMPONENTS,
+            GET_DISABLED_UNTIL_USED_COMPONENTS,
+            GET_UNINSTALLED_PACKAGES,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ResolveInfoFlags {}
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "GET_", "MATCH_" }, value = {
+            MATCH_ALL,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface InstalledModulesFlags {}
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "GET_", "MATCH_" }, value = {
+            GET_META_DATA,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface PermissionInfoFlags {}
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "GET_", "MATCH_" }, value = {
+            GET_META_DATA,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface PermissionGroupInfoFlags {}
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "GET_", "MATCH_" }, value = {
+            GET_META_DATA,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface InstrumentationInfoFlags {}
+
+    /**
+     * {@link PackageInfo} flag: return information about
+     * activities in the package in {@link PackageInfo#activities}.
+     */
+    public static final int GET_ACTIVITIES              = 0x00000001;
+
+    /**
+     * {@link PackageInfo} flag: return information about
+     * intent receivers in the package in
+     * {@link PackageInfo#receivers}.
+     */
+    public static final int GET_RECEIVERS               = 0x00000002;
+
+    /**
+     * {@link PackageInfo} flag: return information about
+     * services in the package in {@link PackageInfo#services}.
+     */
+    public static final int GET_SERVICES                = 0x00000004;
+
+    /**
+     * {@link PackageInfo} flag: return information about
+     * content providers in the package in
+     * {@link PackageInfo#providers}.
+     */
+    public static final int GET_PROVIDERS               = 0x00000008;
+
+    /**
+     * {@link PackageInfo} flag: return information about
+     * instrumentation in the package in
+     * {@link PackageInfo#instrumentation}.
+     */
+    public static final int GET_INSTRUMENTATION         = 0x00000010;
+
+    /**
+     * {@link PackageInfo} flag: return information about the
+     * intent filters supported by the activity.
+     */
+    public static final int GET_INTENT_FILTERS          = 0x00000020;
+
+    /**
+     * {@link PackageInfo} flag: return information about the
+     * signatures included in the package.
+     *
+     * @deprecated use {@code GET_SIGNING_CERTIFICATES} instead
+     */
+    @Deprecated
+    public static final int GET_SIGNATURES          = 0x00000040;
+
+    /**
+     * {@link ResolveInfo} flag: return the IntentFilter that
+     * was matched for a particular ResolveInfo in
+     * {@link ResolveInfo#filter}.
+     */
+    public static final int GET_RESOLVED_FILTER         = 0x00000040;
+
+    /**
+     * {@link ComponentInfo} flag: return the {@link ComponentInfo#metaData}
+     * data {@link android.os.Bundle}s that are associated with a component.
+     * This applies for any API returning a ComponentInfo subclass.
+     */
+    public static final int GET_META_DATA               = 0x00000080;
+
+    /**
+     * {@link PackageInfo} flag: return the
+     * {@link PackageInfo#gids group ids} that are associated with an
+     * application.
+     * This applies for any API returning a PackageInfo class, either
+     * directly or nested inside of another.
+     */
+    public static final int GET_GIDS                    = 0x00000100;
+
+    /**
+     * @deprecated replaced with {@link #MATCH_DISABLED_COMPONENTS}
+     */
+    @Deprecated
+    public static final int GET_DISABLED_COMPONENTS = 0x00000200;
+
+    /**
+     * {@link PackageInfo} flag: include disabled components in the returned info.
+     */
+    public static final int MATCH_DISABLED_COMPONENTS = 0x00000200;
+
+    /**
+     * {@link ApplicationInfo} flag: return the
+     * {@link ApplicationInfo#sharedLibraryFiles paths to the shared libraries}
+     * that are associated with an application.
+     * This applies for any API returning an ApplicationInfo class, either
+     * directly or nested inside of another.
+     */
+    public static final int GET_SHARED_LIBRARY_FILES    = 0x00000400;
+
+    /**
+     * {@link ProviderInfo} flag: return the
+     * {@link ProviderInfo#uriPermissionPatterns URI permission patterns}
+     * that are associated with a content provider.
+     * This applies for any API returning a ProviderInfo class, either
+     * directly or nested inside of another.
+     */
+    public static final int GET_URI_PERMISSION_PATTERNS  = 0x00000800;
+    /**
+     * {@link PackageInfo} flag: return information about
+     * permissions in the package in
+     * {@link PackageInfo#permissions}.
+     */
+    public static final int GET_PERMISSIONS               = 0x00001000;
+
+    /**
+     * @deprecated replaced with {@link #MATCH_UNINSTALLED_PACKAGES}
+     */
+    @Deprecated
+    public static final int GET_UNINSTALLED_PACKAGES = 0x00002000;
+
+    /**
+     * Flag parameter to retrieve some information about all applications (even
+     * uninstalled ones) which have data directories. This state could have
+     * resulted if applications have been deleted with flag
+     * {@code DELETE_KEEP_DATA} with a possibility of being replaced or
+     * reinstalled in future.
+     * <p>
+     * Note: this flag may cause less information about currently installed
+     * applications to be returned.
+     */
+    public static final int MATCH_UNINSTALLED_PACKAGES = 0x00002000;
+
+    /**
+     * {@link PackageInfo} flag: return information about
+     * hardware preferences in
+     * {@link PackageInfo#configPreferences PackageInfo.configPreferences},
+     * and requested features in {@link PackageInfo#reqFeatures} and
+     * {@link PackageInfo#featureGroups}.
+     */
+    public static final int GET_CONFIGURATIONS = 0x00004000;
+
+    /**
+     * @deprecated replaced with {@link #MATCH_DISABLED_UNTIL_USED_COMPONENTS}.
+     */
+    @Deprecated
+    public static final int GET_DISABLED_UNTIL_USED_COMPONENTS = 0x00008000;
+
+    /**
+     * {@link PackageInfo} flag: include disabled components which are in
+     * that state only because of {@link #COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED}
+     * in the returned info.  Note that if you set this flag, applications
+     * that are in this disabled state will be reported as enabled.
+     */
+    public static final int MATCH_DISABLED_UNTIL_USED_COMPONENTS = 0x00008000;
+
+    /**
+     * Resolution and querying flag: if set, only filters that support the
+     * {@link android.content.Intent#CATEGORY_DEFAULT} will be considered for
+     * matching.  This is a synonym for including the CATEGORY_DEFAULT in your
+     * supplied Intent.
+     */
+    public static final int MATCH_DEFAULT_ONLY  = 0x00010000;
+
+    /**
+     * Querying flag: if set and if the platform is doing any filtering of the
+     * results, then the filtering will not happen. This is a synonym for saying
+     * that all results should be returned.
+     * <p>
+     * <em>This flag should be used with extreme care.</em>
+     */
+    public static final int MATCH_ALL = 0x00020000;
+
+    /**
+     * Querying flag: match components which are direct boot <em>unaware</em> in
+     * the returned info, regardless of the current user state.
+     * <p>
+     * When neither {@link #MATCH_DIRECT_BOOT_AWARE} nor
+     * {@link #MATCH_DIRECT_BOOT_UNAWARE} are specified, the default behavior is
+     * to match only runnable components based on the user state. For example,
+     * when a user is started but credentials have not been presented yet, the
+     * user is running "locked" and only {@link #MATCH_DIRECT_BOOT_AWARE}
+     * components are returned. Once the user credentials have been presented,
+     * the user is running "unlocked" and both {@link #MATCH_DIRECT_BOOT_AWARE}
+     * and {@link #MATCH_DIRECT_BOOT_UNAWARE} components are returned.
+     *
+     * @see UserManager#isUserUnlocked()
+     */
+    public static final int MATCH_DIRECT_BOOT_UNAWARE = 0x00040000;
+
+    /**
+     * Querying flag: match components which are direct boot <em>aware</em> in
+     * the returned info, regardless of the current user state.
+     * <p>
+     * When neither {@link #MATCH_DIRECT_BOOT_AWARE} nor
+     * {@link #MATCH_DIRECT_BOOT_UNAWARE} are specified, the default behavior is
+     * to match only runnable components based on the user state. For example,
+     * when a user is started but credentials have not been presented yet, the
+     * user is running "locked" and only {@link #MATCH_DIRECT_BOOT_AWARE}
+     * components are returned. Once the user credentials have been presented,
+     * the user is running "unlocked" and both {@link #MATCH_DIRECT_BOOT_AWARE}
+     * and {@link #MATCH_DIRECT_BOOT_UNAWARE} components are returned.
+     *
+     * @see UserManager#isUserUnlocked()
+     */
+    public static final int MATCH_DIRECT_BOOT_AWARE = 0x00080000;
+
+    /**
+     * Querying flag: include only components from applications that are marked
+     * with {@link ApplicationInfo#FLAG_SYSTEM}.
+     */
+    public static final int MATCH_SYSTEM_ONLY = 0x00100000;
+
+    /**
+     * Internal {@link PackageInfo} flag: include only components on the system image.
+     * This will not return information on any unbundled update to system components.
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    public static final int MATCH_FACTORY_ONLY = 0x00200000;
+
+    /**
+     * Allows querying of packages installed for any user, not just the specific one. This flag
+     * is only meant for use by apps that have INTERACT_ACROSS_USERS permission.
+     * @hide
+     */
+    @SystemApi
+    public static final int MATCH_ANY_USER = 0x00400000;
+
+    /**
+     * Combination of MATCH_ANY_USER and MATCH_UNINSTALLED_PACKAGES to mean any known
+     * package.
+     * @hide
+     */
+    @TestApi
+    public static final int MATCH_KNOWN_PACKAGES = MATCH_UNINSTALLED_PACKAGES | MATCH_ANY_USER;
+
+    /**
+     * Internal {@link PackageInfo} flag: include components that are part of an
+     * instant app. By default, instant app components are not matched.
+     * @hide
+     */
+    @SystemApi
+    public static final int MATCH_INSTANT = 0x00800000;
+
+    /**
+     * Internal {@link PackageInfo} flag: include only components that are exposed to
+     * instant apps. Matched components may have been either explicitly or implicitly
+     * exposed.
+     * @hide
+     */
+    public static final int MATCH_VISIBLE_TO_INSTANT_APP_ONLY = 0x01000000;
+
+    /**
+     * Internal {@link PackageInfo} flag: include only components that have been
+     * explicitly exposed to instant apps.
+     * @hide
+     */
+    public static final int MATCH_EXPLICITLY_VISIBLE_ONLY = 0x02000000;
+
+    /**
+     * Internal {@link PackageInfo} flag: include static shared libraries.
+     * Apps that depend on static shared libs can always access the version
+     * of the lib they depend on. System/shell/root can access all shared
+     * libs regardless of dependency but need to explicitly ask for them
+     * via this flag.
+     * @hide
+     */
+    public static final int MATCH_STATIC_SHARED_LIBRARIES = 0x04000000;
+
+    /**
+     * {@link PackageInfo} flag: return the signing certificates associated with
+     * this package.  Each entry is a signing certificate that the package
+     * has proven it is authorized to use, usually a past signing certificate from
+     * which it has rotated.
+     */
+    public static final int GET_SIGNING_CERTIFICATES = 0x08000000;
+
+    /**
+     * Querying flag: automatically match components based on their Direct Boot
+     * awareness and the current user state.
+     * <p>
+     * Since the default behavior is to automatically apply the current user
+     * state, this is effectively a sentinel value that doesn't change the
+     * output of any queries based on its presence or absence.
+     * <p>
+     * Instead, this value can be useful in conjunction with
+     * {@link android.os.StrictMode.VmPolicy.Builder#detectImplicitDirectBoot()}
+     * to detect when a caller is relying on implicit automatic matching,
+     * instead of confirming the explicit behavior they want, using a
+     * combination of these flags:
+     * <ul>
+     * <li>{@link #MATCH_DIRECT_BOOT_AWARE}
+     * <li>{@link #MATCH_DIRECT_BOOT_UNAWARE}
+     * <li>{@link #MATCH_DIRECT_BOOT_AUTO}
+     * </ul>
+     */
+    public static final int MATCH_DIRECT_BOOT_AUTO = 0x10000000;
+
+    /** @hide */
+    @Deprecated
+    public static final int MATCH_DEBUG_TRIAGED_MISSING = MATCH_DIRECT_BOOT_AUTO;
+
+    /**
+     * Internal {@link PackageInfo} flag used to indicate that a package is a hidden system app.
+     * @hide
+     */
+    public static final int MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS =  0x20000000;
+
+    /**
+     * {@link PackageInfo} flag: include APEX packages that are currently
+     * installed. In APEX terminology, this corresponds to packages that are
+     * currently active, i.e. mounted and available to other processes of the OS.
+     * In particular, this flag alone will not match APEX files that are staged
+     * for activation at next reboot.
+     */
+    public static final int MATCH_APEX = 0x40000000;
+
+    /**
+     * Flag for {@link #addCrossProfileIntentFilter}: if this flag is set: when
+     * resolving an intent that matches the {@code CrossProfileIntentFilter},
+     * the current profile will be skipped. Only activities in the target user
+     * can respond to the intent.
+     *
+     * @hide
+     */
+    public static final int SKIP_CURRENT_PROFILE = 0x00000002;
+
+    /**
+     * Flag for {@link #addCrossProfileIntentFilter}: if this flag is set:
+     * activities in the other profiles can respond to the intent only if no activity with
+     * non-negative priority in current profile can respond to the intent.
+     * @hide
+     */
+    public static final int ONLY_IF_NO_MATCH_FOUND = 0x00000004;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "MODULE_" }, value = {
+            MODULE_APEX_NAME,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ModuleInfoFlags {}
+
+    /**
+     * Flag for {@link #getModuleInfo}: allow ModuleInfo to be retrieved using the apex module
+     * name, rather than the package name.
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    public static final int MODULE_APEX_NAME = 0x00000001;
+
+    /** @hide */
+    @IntDef(prefix = { "PERMISSION_" }, value = {
+            PERMISSION_GRANTED,
+            PERMISSION_DENIED
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface PermissionResult {}
+
+    /**
+     * Permission check result: this is returned by {@link #checkPermission}
+     * if the permission has been granted to the given package.
+     */
+    public static final int PERMISSION_GRANTED = 0;
+
+    /**
+     * Permission check result: this is returned by {@link #checkPermission}
+     * if the permission has not been granted to the given package.
+     */
+    public static final int PERMISSION_DENIED = -1;
+
+    /** @hide */
+    @IntDef(prefix = { "SIGNATURE_" }, value = {
+            SIGNATURE_MATCH,
+            SIGNATURE_NEITHER_SIGNED,
+            SIGNATURE_FIRST_NOT_SIGNED,
+            SIGNATURE_SECOND_NOT_SIGNED,
+            SIGNATURE_NO_MATCH,
+            SIGNATURE_UNKNOWN_PACKAGE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SignatureResult {}
+
+    /**
+     * Signature check result: this is returned by {@link #checkSignatures}
+     * if all signatures on the two packages match.
+     */
+    public static final int SIGNATURE_MATCH = 0;
+
+    /**
+     * Signature check result: this is returned by {@link #checkSignatures}
+     * if neither of the two packages is signed.
+     */
+    public static final int SIGNATURE_NEITHER_SIGNED = 1;
+
+    /**
+     * Signature check result: this is returned by {@link #checkSignatures}
+     * if the first package is not signed but the second is.
+     */
+    public static final int SIGNATURE_FIRST_NOT_SIGNED = -1;
+
+    /**
+     * Signature check result: this is returned by {@link #checkSignatures}
+     * if the second package is not signed but the first is.
+     */
+    public static final int SIGNATURE_SECOND_NOT_SIGNED = -2;
+
+    /**
+     * Signature check result: this is returned by {@link #checkSignatures}
+     * if not all signatures on both packages match.
+     */
+    public static final int SIGNATURE_NO_MATCH = -3;
+
+    /**
+     * Signature check result: this is returned by {@link #checkSignatures}
+     * if either of the packages are not valid.
+     */
+    public static final int SIGNATURE_UNKNOWN_PACKAGE = -4;
+
+    /** @hide */
+    @IntDef(prefix = { "COMPONENT_ENABLED_STATE_" }, value = {
+            COMPONENT_ENABLED_STATE_DEFAULT,
+            COMPONENT_ENABLED_STATE_ENABLED,
+            COMPONENT_ENABLED_STATE_DISABLED,
+            COMPONENT_ENABLED_STATE_DISABLED_USER,
+            COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface EnabledState {}
+
+    /**
+     * Flag for {@link #setApplicationEnabledSetting(String, int, int)} and
+     * {@link #setComponentEnabledSetting(ComponentName, int, int)}: This
+     * component or application is in its default enabled state (as specified in
+     * its manifest).
+     * <p>
+     * Explicitly setting the component state to this value restores it's
+     * enabled state to whatever is set in the manifest.
+     */
+    public static final int COMPONENT_ENABLED_STATE_DEFAULT = 0;
+
+    /**
+     * Flag for {@link #setApplicationEnabledSetting(String, int, int)}
+     * and {@link #setComponentEnabledSetting(ComponentName, int, int)}: This
+     * component or application has been explictily enabled, regardless of
+     * what it has specified in its manifest.
+     */
+    public static final int COMPONENT_ENABLED_STATE_ENABLED = 1;
+
+    /**
+     * Flag for {@link #setApplicationEnabledSetting(String, int, int)}
+     * and {@link #setComponentEnabledSetting(ComponentName, int, int)}: This
+     * component or application has been explicitly disabled, regardless of
+     * what it has specified in its manifest.
+     */
+    public static final int COMPONENT_ENABLED_STATE_DISABLED = 2;
+
+    /**
+     * Flag for {@link #setApplicationEnabledSetting(String, int, int)} only: The
+     * user has explicitly disabled the application, regardless of what it has
+     * specified in its manifest.  Because this is due to the user's request,
+     * they may re-enable it if desired through the appropriate system UI.  This
+     * option currently <strong>cannot</strong> be used with
+     * {@link #setComponentEnabledSetting(ComponentName, int, int)}.
+     */
+    public static final int COMPONENT_ENABLED_STATE_DISABLED_USER = 3;
+
+    /**
+     * Flag for {@link #setApplicationEnabledSetting(String, int, int)} only: This
+     * application should be considered, until the point where the user actually
+     * wants to use it.  This means that it will not normally show up to the user
+     * (such as in the launcher), but various parts of the user interface can
+     * use {@link #GET_DISABLED_UNTIL_USED_COMPONENTS} to still see it and allow
+     * the user to select it (as for example an IME, device admin, etc).  Such code,
+     * once the user has selected the app, should at that point also make it enabled.
+     * This option currently <strong>can not</strong> be used with
+     * {@link #setComponentEnabledSetting(ComponentName, int, int)}.
+     */
+    public static final int COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED = 4;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {
+            RollbackDataPolicy.RESTORE,
+            RollbackDataPolicy.WIPE,
+            RollbackDataPolicy.RETAIN
+    })
+    public @interface RollbackDataPolicy {
+        /**
+         * User data will be backed up during install and restored during rollback.
+         */
+        int RESTORE = 0;
+        /**
+         * User data won't be backed up during install but will be wiped out during rollback.
+         */
+        int WIPE = 1;
+        /**
+         * User data won't be backed up during install and won't be restored during rollback.
+         * TODO: Not implemented yet.
+         */
+        int RETAIN = 2;
+    }
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "INSTALL_" }, value = {
+            INSTALL_REPLACE_EXISTING,
+            INSTALL_ALLOW_TEST,
+            INSTALL_INTERNAL,
+            INSTALL_FROM_ADB,
+            INSTALL_ALL_USERS,
+            INSTALL_REQUEST_DOWNGRADE,
+            INSTALL_GRANT_RUNTIME_PERMISSIONS,
+            INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS,
+            INSTALL_FORCE_VOLUME_UUID,
+            INSTALL_FORCE_PERMISSION_PROMPT,
+            INSTALL_INSTANT_APP,
+            INSTALL_DONT_KILL_APP,
+            INSTALL_FULL_APP,
+            INSTALL_ALLOCATE_AGGRESSIVE,
+            INSTALL_VIRTUAL_PRELOAD,
+            INSTALL_APEX,
+            INSTALL_ENABLE_ROLLBACK,
+            INSTALL_ALLOW_DOWNGRADE,
+            INSTALL_STAGED,
+            INSTALL_DRY_RUN,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface InstallFlags {}
+
+    /**
+     * Flag parameter for {@link #installPackage} to indicate that you want to
+     * replace an already installed package, if one exists.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static final int INSTALL_REPLACE_EXISTING = 0x00000002;
+
+    /**
+     * Flag parameter for {@link #installPackage} to indicate that you want to
+     * allow test packages (those that have set android:testOnly in their
+     * manifest) to be installed.
+     * @hide
+     */
+    public static final int INSTALL_ALLOW_TEST = 0x00000004;
+
+    /**
+     * Flag parameter for {@link #installPackage} to indicate that this package
+     * must be installed to internal storage.
+     *
+     * @hide
+     */
+    public static final int INSTALL_INTERNAL = 0x00000010;
+
+    /**
+     * Flag parameter for {@link #installPackage} to indicate that this install
+     * was initiated via ADB.
+     *
+     * @hide
+     */
+    public static final int INSTALL_FROM_ADB = 0x00000020;
+
+    /**
+     * Flag parameter for {@link #installPackage} to indicate that this install
+     * should immediately be visible to all users.
+     *
+     * @hide
+     */
+    public static final int INSTALL_ALL_USERS = 0x00000040;
+
+    /**
+     * Flag parameter for {@link #installPackage} to indicate that an upgrade to a lower version
+     * of a package than currently installed has been requested.
+     *
+     * <p>Note that this flag doesn't guarantee that downgrade will be performed. That decision
+     * depends
+     * on whenever:
+     * <ul>
+     * <li>An app is debuggable.
+     * <li>Or a build is debuggable.
+     * <li>Or {@link #INSTALL_ALLOW_DOWNGRADE} is set.
+     * </ul>
+     *
+     * @hide
+     */
+    public static final int INSTALL_REQUEST_DOWNGRADE = 0x00000080;
+
+    /**
+     * Flag parameter for {@link #installPackage} to indicate that all runtime
+     * permissions should be granted to the package. If {@link #INSTALL_ALL_USERS}
+     * is set the runtime permissions will be granted to all users, otherwise
+     * only to the owner.
+     *
+     * @hide
+     */
+    public static final int INSTALL_GRANT_RUNTIME_PERMISSIONS = 0x00000100;
+
+    /**
+     * Flag parameter for {@link #installPackage} to indicate that all restricted
+     * permissions should be whitelisted. If {@link #INSTALL_ALL_USERS}
+     * is set the restricted permissions will be whitelisted for all users, otherwise
+     * only to the owner.
+     *
+     * @hide
+     */
+    public static final int INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS = 0x00400000;
+
+    /** {@hide} */
+    public static final int INSTALL_FORCE_VOLUME_UUID = 0x00000200;
+
+    /**
+     * Flag parameter for {@link #installPackage} to indicate that we always want to force
+     * the prompt for permission approval. This overrides any special behaviour for internal
+     * components.
+     *
+     * @hide
+     */
+    public static final int INSTALL_FORCE_PERMISSION_PROMPT = 0x00000400;
+
+    /**
+     * Flag parameter for {@link #installPackage} to indicate that this package is
+     * to be installed as a lightweight "ephemeral" app.
+     *
+     * @hide
+     */
+    public static final int INSTALL_INSTANT_APP = 0x00000800;
+
+    /**
+     * Flag parameter for {@link #installPackage} to indicate that this package contains
+     * a feature split to an existing application and the existing application should not
+     * be killed during the installation process.
+     *
+     * @hide
+     */
+    public static final int INSTALL_DONT_KILL_APP = 0x00001000;
+
+    /**
+     * Flag parameter for {@link #installPackage} to indicate that this package is
+     * to be installed as a heavy weight app. This is fundamentally the opposite of
+     * {@link #INSTALL_INSTANT_APP}.
+     *
+     * @hide
+     */
+    public static final int INSTALL_FULL_APP = 0x00004000;
+
+    /**
+     * Flag parameter for {@link #installPackage} to indicate that this package
+     * is critical to system health or security, meaning the system should use
+     * {@link StorageManager#FLAG_ALLOCATE_AGGRESSIVE} internally.
+     *
+     * @hide
+     */
+    public static final int INSTALL_ALLOCATE_AGGRESSIVE = 0x00008000;
+
+    /**
+     * Flag parameter for {@link #installPackage} to indicate that this package
+     * is a virtual preload.
+     *
+     * @hide
+     */
+    public static final int INSTALL_VIRTUAL_PRELOAD = 0x00010000;
+
+    /**
+     * Flag parameter for {@link #installPackage} to indicate that this package
+     * is an APEX package
+     *
+     * @hide
+     */
+    public static final int INSTALL_APEX = 0x00020000;
+
+    /**
+     * Flag parameter for {@link #installPackage} to indicate that rollback
+     * should be enabled for this install.
+     *
+     * @hide
+     */
+    public static final int INSTALL_ENABLE_ROLLBACK = 0x00040000;
+
+    /**
+     * Flag parameter for {@link #installPackage} to indicate that package verification should be
+     * disabled for this package.
+     *
+     * @hide
+     */
+    public static final int INSTALL_DISABLE_VERIFICATION = 0x00080000;
+
+    /**
+     * Flag parameter for {@link #installPackage} to indicate that
+     * {@link #INSTALL_REQUEST_DOWNGRADE} should be allowed.
+     *
+     * @hide
+     */
+    public static final int INSTALL_ALLOW_DOWNGRADE = 0x00100000;
+
+    /**
+     * Flag parameter for {@link #installPackage} to indicate that this package
+     * is being installed as part of a staged install.
+     *
+     * @hide
+     */
+    public static final int INSTALL_STAGED = 0x00200000;
+
+    /**
+     * Flag parameter for {@link #installPackage} to indicate that package should only be verified
+     * but not installed.
+     *
+     * @hide
+     */
+    public static final int INSTALL_DRY_RUN = 0x00800000;
+
+    /** @hide */
+    @IntDef(flag = true, value = {
+            DONT_KILL_APP,
+            SYNCHRONOUS
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface EnabledFlags {}
+
+    /**
+     * Flag parameter for
+     * {@link #setComponentEnabledSetting(android.content.ComponentName, int, int)} to indicate
+     * that you don't want to kill the app containing the component.  Be careful when you set this
+     * since changing component states can make the containing application's behavior unpredictable.
+     */
+    public static final int DONT_KILL_APP = 0x00000001;
+
+    /**
+     * Flag parameter for
+     * {@link #setComponentEnabledSetting(android.content.ComponentName, int, int)} to indicate
+     * that the given user's package restrictions state will be serialised to disk after the
+     * component state has been updated. Note that this is synchronous disk access, so calls using
+     * this flag should be run on a background thread.
+     */
+    public static final int SYNCHRONOUS = 0x00000002;
+
+    /** @hide */
+    @IntDef(prefix = { "INSTALL_REASON_" }, value = {
+            INSTALL_REASON_UNKNOWN,
+            INSTALL_REASON_POLICY,
+            INSTALL_REASON_DEVICE_RESTORE,
+            INSTALL_REASON_DEVICE_SETUP,
+            INSTALL_REASON_USER,
+            INSTALL_REASON_ROLLBACK
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface InstallReason {}
+
+    /**
+     * Code indicating that the reason for installing this package is unknown.
+     */
+    public static final int INSTALL_REASON_UNKNOWN = 0;
+
+    /**
+     * Code indicating that this package was installed due to enterprise policy.
+     */
+    public static final int INSTALL_REASON_POLICY = 1;
+
+    /**
+     * Code indicating that this package was installed as part of restoring from another device.
+     */
+    public static final int INSTALL_REASON_DEVICE_RESTORE = 2;
+
+    /**
+     * Code indicating that this package was installed as part of device setup.
+     */
+    public static final int INSTALL_REASON_DEVICE_SETUP = 3;
+
+    /**
+     * Code indicating that the package installation was initiated by the user.
+     */
+    public static final int INSTALL_REASON_USER = 4;
+
+    /**
+     * Code indicating that the package installation was a rollback initiated by RollbackManager.
+     *
+     * @hide
+     */
+    public static final int INSTALL_REASON_ROLLBACK = 5;
+
+    /** @hide */
+    @IntDef(prefix = { "UNINSTALL_REASON_" }, value = {
+            UNINSTALL_REASON_UNKNOWN,
+            UNINSTALL_REASON_USER_TYPE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface UninstallReason {}
+
+    /**
+     * Code indicating that the reason for uninstalling this package is unknown.
+     * @hide
+     */
+    public static final int UNINSTALL_REASON_UNKNOWN = 0;
+
+    /**
+     * Code indicating that this package was uninstalled due to the type of user.
+     * See UserSystemPackageInstaller
+     * @hide
+     */
+    public static final int UNINSTALL_REASON_USER_TYPE = 1;
+
+    /**
+     * @hide
+     */
+    public static final int INSTALL_UNKNOWN = 0;
+
+    /**
+     * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+     * on success.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int INSTALL_SUCCEEDED = 1;
+
+    /**
+     * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+     * if the package is already installed.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int INSTALL_FAILED_ALREADY_EXISTS = -1;
+
+    /**
+     * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+     * if the package archive file is invalid.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int INSTALL_FAILED_INVALID_APK = -2;
+
+    /**
+     * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+     * if the URI passed in is invalid.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int INSTALL_FAILED_INVALID_URI = -3;
+
+    /**
+     * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+     * if the package manager service found that the device didn't have enough storage space to
+     * install the app.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int INSTALL_FAILED_INSUFFICIENT_STORAGE = -4;
+
+    /**
+     * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+     * if a package is already installed with the same name.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int INSTALL_FAILED_DUPLICATE_PACKAGE = -5;
+
+    /**
+     * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+     * if the requested shared user does not exist.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int INSTALL_FAILED_NO_SHARED_USER = -6;
+
+    /**
+     * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+     * if a previously installed package of the same name has a different signature than the new
+     * package (and the old package's data was not removed).
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int INSTALL_FAILED_UPDATE_INCOMPATIBLE = -7;
+
+    /**
+     * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+     * if the new package is requested a shared user which is already installed on the device and
+     * does not have matching signature.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int INSTALL_FAILED_SHARED_USER_INCOMPATIBLE = -8;
+
+    /**
+     * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+     * if the new package uses a shared library that is not available.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int INSTALL_FAILED_MISSING_SHARED_LIBRARY = -9;
+
+    /**
+     * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+     * if the new package uses a shared library that is not available.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int INSTALL_FAILED_REPLACE_COULDNT_DELETE = -10;
+
+    /**
+     * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+     * if the new package failed while optimizing and validating its dex files, either because there
+     * was not enough storage or the validation failed.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int INSTALL_FAILED_DEXOPT = -11;
+
+    /**
+     * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+     * if the new package failed because the current SDK version is older than that required by the
+     * package.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int INSTALL_FAILED_OLDER_SDK = -12;
+
+    /**
+     * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+     * if the new package failed because it contains a content provider with the same authority as a
+     * provider already installed in the system.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int INSTALL_FAILED_CONFLICTING_PROVIDER = -13;
+
+    /**
+     * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+     * if the new package failed because the current SDK version is newer than that required by the
+     * package.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int INSTALL_FAILED_NEWER_SDK = -14;
+
+    /**
+     * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+     * if the new package failed because it has specified that it is a test-only package and the
+     * caller has not supplied the {@link #INSTALL_ALLOW_TEST} flag.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int INSTALL_FAILED_TEST_ONLY = -15;
+
+    /**
+     * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+     * if the package being installed contains native code, but none that is compatible with the
+     * device's CPU_ABI.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int INSTALL_FAILED_CPU_ABI_INCOMPATIBLE = -16;
+
+    /**
+     * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+     * if the new package uses a feature that is not available.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int INSTALL_FAILED_MISSING_FEATURE = -17;
+
+    // ------ Errors related to sdcard
+    /**
+     * Installation return code: this is passed in the
+     * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if a secure container mount point couldn't be
+     * accessed on external media.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int INSTALL_FAILED_CONTAINER_ERROR = -18;
+
+    /**
+     * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+     * if the new package couldn't be installed in the specified install location.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int INSTALL_FAILED_INVALID_INSTALL_LOCATION = -19;
+
+    /**
+     * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+     * if the new package couldn't be installed in the specified install location because the media
+     * is not available.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int INSTALL_FAILED_MEDIA_UNAVAILABLE = -20;
+
+    /**
+     * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+     * if the new package couldn't be installed because the verification timed out.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int INSTALL_FAILED_VERIFICATION_TIMEOUT = -21;
+
+    /**
+     * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+     * if the new package couldn't be installed because the verification did not succeed.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int INSTALL_FAILED_VERIFICATION_FAILURE = -22;
+
+    /**
+     * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+     * if the package changed from what the calling program expected.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int INSTALL_FAILED_PACKAGE_CHANGED = -23;
+
+    /**
+     * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+     * if the new package is assigned a different UID than it previously held.
+     *
+     * @hide
+     */
+    public static final int INSTALL_FAILED_UID_CHANGED = -24;
+
+    /**
+     * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+     * if the new package has an older version code than the currently installed package.
+     *
+     * @hide
+     */
+    public static final int INSTALL_FAILED_VERSION_DOWNGRADE = -25;
+
+    /**
+     * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+     * if the old package has target SDK high enough to support runtime permission and the new
+     * package has target SDK low enough to not support runtime permissions.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int INSTALL_FAILED_PERMISSION_MODEL_DOWNGRADE = -26;
+
+    /**
+     * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+     * if the new package attempts to downgrade the target sandbox version of the app.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int INSTALL_FAILED_SANDBOX_VERSION_DOWNGRADE = -27;
+
+    /**
+     * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+     * if the new package requires at least one split and it was not provided.
+     *
+     * @hide
+     */
+    public static final int INSTALL_FAILED_MISSING_SPLIT = -28;
+
+    /**
+     * Installation parse return code: this is passed in the
+     * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the parser was given a path that is not a
+     * file, or does not end with the expected '.apk' extension.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int INSTALL_PARSE_FAILED_NOT_APK = -100;
+
+    /**
+     * Installation parse return code: this is passed in the
+     * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the parser was unable to retrieve the
+     * AndroidManifest.xml file.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int INSTALL_PARSE_FAILED_BAD_MANIFEST = -101;
+
+    /**
+     * Installation parse return code: this is passed in the
+     * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the parser encountered an unexpected
+     * exception.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION = -102;
+
+    /**
+     * Installation parse return code: this is passed in the
+     * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the parser did not find any certificates in
+     * the .apk.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int INSTALL_PARSE_FAILED_NO_CERTIFICATES = -103;
+
+    /**
+     * Installation parse return code: this is passed in the
+     * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the parser found inconsistent certificates on
+     * the files in the .apk.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES = -104;
+
+    /**
+     * Installation parse return code: this is passed in the
+     * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the parser encountered a
+     * CertificateEncodingException in one of the files in the .apk.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING = -105;
+
+    /**
+     * Installation parse return code: this is passed in the
+     * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the parser encountered a bad or missing
+     * package name in the manifest.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME = -106;
+
+    /**
+     * Installation parse return code: tthis is passed in the
+     * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the parser encountered a bad shared user id
+     * name in the manifest.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID = -107;
+
+    /**
+     * Installation parse return code: this is passed in the
+     * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the parser encountered some structural
+     * problem in the manifest.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int INSTALL_PARSE_FAILED_MANIFEST_MALFORMED = -108;
+
+    /**
+     * Installation parse return code: this is passed in the
+     * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the parser did not find any actionable tags
+     * (instrumentation or application) in the manifest.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int INSTALL_PARSE_FAILED_MANIFEST_EMPTY = -109;
+
+    /**
+     * Installation failed return code: this is passed in the
+     * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the system failed to install the package
+     * because of system issues.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int INSTALL_FAILED_INTERNAL_ERROR = -110;
+
+    /**
+     * Installation failed return code: this is passed in the
+     * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the system failed to install the package
+     * because the user is restricted from installing apps.
+     *
+     * @hide
+     */
+    public static final int INSTALL_FAILED_USER_RESTRICTED = -111;
+
+    /**
+     * Installation failed return code: this is passed in the
+     * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the system failed to install the package
+     * because it is attempting to define a permission that is already defined by some existing
+     * package.
+     * <p>
+     * The package name of the app which has already defined the permission is passed to a
+     * {@link PackageInstallObserver}, if any, as the {@link #EXTRA_FAILURE_EXISTING_PACKAGE} string
+     * extra; and the name of the permission being redefined is passed in the
+     * {@link #EXTRA_FAILURE_EXISTING_PERMISSION} string extra.
+     *
+     * @hide
+     */
+    public static final int INSTALL_FAILED_DUPLICATE_PERMISSION = -112;
+
+    /**
+     * Installation failed return code: this is passed in the
+     * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the system failed to install the package
+     * because its packaged native code did not match any of the ABIs supported by the system.
+     *
+     * @hide
+     */
+    public static final int INSTALL_FAILED_NO_MATCHING_ABIS = -113;
+
+    /**
+     * Internal return code for NativeLibraryHelper methods to indicate that the package
+     * being processed did not contain any native code. This is placed here only so that
+     * it can belong to the same value space as the other install failure codes.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static final int NO_NATIVE_LIBRARIES = -114;
+
+    /** {@hide} */
+    public static final int INSTALL_FAILED_ABORTED = -115;
+
+    /**
+     * Installation failed return code: instant app installs are incompatible with some
+     * other installation flags supplied for the operation; or other circumstances such
+     * as trying to upgrade a system app via an instant app install.
+     * @hide
+     */
+    public static final int INSTALL_FAILED_INSTANT_APP_INVALID = -116;
+
+    /**
+     * Installation parse return code: this is passed in the
+     * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the dex metadata file is invalid or
+     * if there was no matching apk file for a dex metadata file.
+     *
+     * @hide
+     */
+    public static final int INSTALL_FAILED_BAD_DEX_METADATA = -117;
+
+    /**
+     * Installation parse return code: this is passed in the
+     * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if there is any signature problem.
+     *
+     * @hide
+     */
+    public static final int INSTALL_FAILED_BAD_SIGNATURE = -118;
+
+    /**
+     * Installation failed return code: a new staged session was attempted to be committed while
+     * there is already one in-progress or new session has package that is already staged.
+     *
+     * @hide
+     */
+    public static final int INSTALL_FAILED_OTHER_STAGED_SESSION_IN_PROGRESS = -119;
+
+    /**
+     * Installation failed return code: one of the child sessions does not match the parent session
+     * in respect to staged or rollback enabled parameters.
+     *
+     * @hide
+     */
+    public static final int INSTALL_FAILED_MULTIPACKAGE_INCONSISTENCY = -120;
+
+    /**
+     * Installation failed return code: the required installed version code
+     * does not match the currently installed package version code.
+     *
+     * @hide
+     */
+    public static final int INSTALL_FAILED_WRONG_INSTALLED_VERSION = -121;
+
+    /**
+     * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+     * if the new package failed because it contains a request to use a process that was not
+     * explicitly defined as part of its &lt;processes&gt; tag.
+     *
+     * @hide
+     */
+    public static final int INSTALL_FAILED_PROCESS_NOT_DEFINED = -122;
+
+    /**
+     * Installation parse return code: system is in a minimal boot state, and the parser only
+     * allows the package with {@code coreApp} manifest attribute to be a valid application.
+     *
+     * @hide
+     */
+    public static final int INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED = -123;
+
+    /**
+     * Installation failed return code: the {@code resources.arsc} of one of the APKs being
+     * installed is compressed or not aligned on a 4-byte boundary. Resource tables that cannot be
+     * memory mapped exert excess memory pressure on the system and drastically slow down
+     * construction of {@link Resources} objects.
+     *
+     * @hide
+     */
+    public static final int INSTALL_PARSE_FAILED_RESOURCES_ARSC_COMPRESSED = -124;
+
+    /**
+     * Installation failed return code: the package was skipped and should be ignored.
+     *
+     * The reason for the skip is undefined.
+     * @hide
+     */
+    public static final int INSTALL_PARSE_FAILED_SKIPPED = -125;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "DELETE_" }, value = {
+            DELETE_KEEP_DATA,
+            DELETE_ALL_USERS,
+            DELETE_SYSTEM_APP,
+            DELETE_DONT_KILL_APP,
+            DELETE_CHATTY,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface DeleteFlags {}
+
+    /**
+     * Flag parameter for {@link #deletePackage} to indicate that you don't want to delete the
+     * package's data directory.
+     *
+     * @hide
+     */
+    public static final int DELETE_KEEP_DATA = 0x00000001;
+
+    /**
+     * Flag parameter for {@link #deletePackage} to indicate that you want the
+     * package deleted for all users.
+     *
+     * @hide
+     */
+    public static final int DELETE_ALL_USERS = 0x00000002;
+
+    /**
+     * Flag parameter for {@link #deletePackage} to indicate that, if you are calling
+     * uninstall on a system that has been updated, then don't do the normal process
+     * of uninstalling the update and rolling back to the older system version (which
+     * needs to happen for all users); instead, just mark the app as uninstalled for
+     * the current user.
+     *
+     * @hide
+     */
+    public static final int DELETE_SYSTEM_APP = 0x00000004;
+
+    /**
+     * Flag parameter for {@link #deletePackage} to indicate that, if you are calling
+     * uninstall on a package that is replaced to provide new feature splits, the
+     * existing application should not be killed during the removal process.
+     *
+     * @hide
+     */
+    public static final int DELETE_DONT_KILL_APP = 0x00000008;
+
+    /**
+     * Flag parameter for {@link #deletePackage} to indicate that package deletion
+     * should be chatty.
+     *
+     * @hide
+     */
+    public static final int DELETE_CHATTY = 0x80000000;
+
+    /**
+     * Return code for when package deletion succeeds. This is passed to the
+     * {@link IPackageDeleteObserver} if the system succeeded in deleting the
+     * package.
+     *
+     * @hide
+     */
+    public static final int DELETE_SUCCEEDED = 1;
+
+    /**
+     * Deletion failed return code: this is passed to the
+     * {@link IPackageDeleteObserver} if the system failed to delete the package
+     * for an unspecified reason.
+     *
+     * @hide
+     */
+    public static final int DELETE_FAILED_INTERNAL_ERROR = -1;
+
+    /**
+     * Deletion failed return code: this is passed to the
+     * {@link IPackageDeleteObserver} if the system failed to delete the package
+     * because it is the active DevicePolicy manager.
+     *
+     * @hide
+     */
+    public static final int DELETE_FAILED_DEVICE_POLICY_MANAGER = -2;
+
+    /**
+     * Deletion failed return code: this is passed to the
+     * {@link IPackageDeleteObserver} if the system failed to delete the package
+     * since the user is restricted.
+     *
+     * @hide
+     */
+    public static final int DELETE_FAILED_USER_RESTRICTED = -3;
+
+    /**
+     * Deletion failed return code: this is passed to the
+     * {@link IPackageDeleteObserver} if the system failed to delete the package
+     * because a profile or device owner has marked the package as
+     * uninstallable.
+     *
+     * @hide
+     */
+    public static final int DELETE_FAILED_OWNER_BLOCKED = -4;
+
+    /** {@hide} */
+    public static final int DELETE_FAILED_ABORTED = -5;
+
+    /**
+     * Deletion failed return code: this is passed to the
+     * {@link IPackageDeleteObserver} if the system failed to delete the package
+     * because the packge is a shared library used by other installed packages.
+     * {@hide} */
+    public static final int DELETE_FAILED_USED_SHARED_LIBRARY = -6;
+
+    /**
+     * Return code that is passed to the {@link IPackageMoveObserver} when the
+     * package has been successfully moved by the system.
+     *
+     * @hide
+     */
+    public static final int MOVE_SUCCEEDED = -100;
+
+    /**
+     * Error code that is passed to the {@link IPackageMoveObserver} when the
+     * package hasn't been successfully moved by the system because of
+     * insufficient memory on specified media.
+     *
+     * @hide
+     */
+    public static final int MOVE_FAILED_INSUFFICIENT_STORAGE = -1;
+
+    /**
+     * Error code that is passed to the {@link IPackageMoveObserver} if the
+     * specified package doesn't exist.
+     *
+     * @hide
+     */
+    public static final int MOVE_FAILED_DOESNT_EXIST = -2;
+
+    /**
+     * Error code that is passed to the {@link IPackageMoveObserver} if the
+     * specified package cannot be moved since its a system package.
+     *
+     * @hide
+     */
+    public static final int MOVE_FAILED_SYSTEM_PACKAGE = -3;
+
+    /**
+     * Error code that is passed to the {@link IPackageMoveObserver} if the
+     * specified package cannot be moved to the specified location.
+     *
+     * @hide
+     */
+    public static final int MOVE_FAILED_INVALID_LOCATION = -5;
+
+    /**
+     * Error code that is passed to the {@link IPackageMoveObserver} if the
+     * specified package cannot be moved to the specified location.
+     *
+     * @hide
+     */
+    public static final int MOVE_FAILED_INTERNAL_ERROR = -6;
+
+    /**
+     * Error code that is passed to the {@link IPackageMoveObserver} if the
+     * specified package already has an operation pending in the queue.
+     *
+     * @hide
+     */
+    public static final int MOVE_FAILED_OPERATION_PENDING = -7;
+
+    /**
+     * Error code that is passed to the {@link IPackageMoveObserver} if the
+     * specified package cannot be moved since it contains a device admin.
+     *
+     * @hide
+     */
+    public static final int MOVE_FAILED_DEVICE_ADMIN = -8;
+
+    /**
+     * Error code that is passed to the {@link IPackageMoveObserver} if system does not allow
+     * non-system apps to be moved to internal storage.
+     *
+     * @hide
+     */
+    public static final int MOVE_FAILED_3RD_PARTY_NOT_ALLOWED_ON_INTERNAL = -9;
+
+    /** @hide */
+    public static final int MOVE_FAILED_LOCKED_USER = -10;
+
+    /**
+     * Flag parameter for {@link #movePackage} to indicate that
+     * the package should be moved to internal storage if its
+     * been installed on external media.
+     * @hide
+     */
+    @Deprecated
+    @UnsupportedAppUsage
+    public static final int MOVE_INTERNAL = 0x00000001;
+
+    /**
+     * Flag parameter for {@link #movePackage} to indicate that
+     * the package should be moved to external media.
+     * @hide
+     */
+    @Deprecated
+    @UnsupportedAppUsage
+    public static final int MOVE_EXTERNAL_MEDIA = 0x00000002;
+
+    /** {@hide} */
+    public static final String EXTRA_MOVE_ID = "android.content.pm.extra.MOVE_ID";
+
+    /**
+     * Usable by the required verifier as the {@code verificationCode} argument
+     * for {@link PackageManager#verifyPendingInstall} to indicate that it will
+     * allow the installation to proceed without any of the optional verifiers
+     * needing to vote.
+     *
+     * @hide
+     */
+    public static final int VERIFICATION_ALLOW_WITHOUT_SUFFICIENT = 2;
+
+    /**
+     * Used as the {@code verificationCode} argument for
+     * {@link PackageManager#verifyPendingInstall} to indicate that the calling
+     * package verifier allows the installation to proceed.
+     */
+    public static final int VERIFICATION_ALLOW = 1;
+
+    /**
+     * Used as the {@code verificationCode} argument for
+     * {@link PackageManager#verifyPendingInstall} to indicate the calling
+     * package verifier does not vote to allow the installation to proceed.
+     */
+    public static final int VERIFICATION_REJECT = -1;
+
+    /**
+     * Used as the {@code verificationCode} argument for
+     * {@link PackageManager#verifyIntentFilter} to indicate that the calling
+     * IntentFilter Verifier confirms that the IntentFilter is verified.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int INTENT_FILTER_VERIFICATION_SUCCESS = 1;
+
+    /**
+     * Used as the {@code verificationCode} argument for
+     * {@link PackageManager#verifyIntentFilter} to indicate that the calling
+     * IntentFilter Verifier confirms that the IntentFilter is NOT verified.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int INTENT_FILTER_VERIFICATION_FAILURE = -1;
+
+    /**
+     * Internal status code to indicate that an IntentFilter verification result is not specified.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED = 0;
+
+    /**
+     * Used as the {@code status} argument for
+     * {@link #updateIntentVerificationStatusAsUser} to indicate that the User
+     * will always be prompted the Intent Disambiguation Dialog if there are two
+     * or more Intent resolved for the IntentFilter's domain(s).
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK = 1;
+
+    /**
+     * Used as the {@code status} argument for
+     * {@link #updateIntentVerificationStatusAsUser} to indicate that the User
+     * will never be prompted the Intent Disambiguation Dialog if there are two
+     * or more resolution of the Intent. The default App for the domain(s)
+     * specified in the IntentFilter will also ALWAYS be used.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS = 2;
+
+    /**
+     * Used as the {@code status} argument for
+     * {@link #updateIntentVerificationStatusAsUser} to indicate that the User
+     * may be prompted the Intent Disambiguation Dialog if there are two or more
+     * Intent resolved. The default App for the domain(s) specified in the
+     * IntentFilter will also NEVER be presented to the User.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER = 3;
+
+    /**
+     * Used as the {@code status} argument for
+     * {@link #updateIntentVerificationStatusAsUser} to indicate that this app
+     * should always be considered as an ambiguous candidate for handling the
+     * matching Intent even if there are other candidate apps in the "always"
+     * state. Put another way: if there are any 'always ask' apps in a set of
+     * more than one candidate app, then a disambiguation is *always* presented
+     * even if there is another candidate app with the 'always' state.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK = 4;
+
+    /**
+     * Can be used as the {@code millisecondsToDelay} argument for
+     * {@link PackageManager#extendVerificationTimeout}. This is the
+     * maximum time {@code PackageManager} waits for the verification
+     * agent to return (in milliseconds).
+     */
+    public static final long MAXIMUM_VERIFICATION_TIMEOUT = 60*60*1000;
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device's
+     * audio pipeline is low-latency, more suitable for audio applications sensitive to delays or
+     * lag in sound input or output.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_AUDIO_LOW_LATENCY = "android.hardware.audio.low_latency";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device includes at least one form of audio
+     * output, as defined in the Android Compatibility Definition Document (CDD)
+     * <a href="https://source.android.com/compatibility/android-cdd#7_8_audio">section 7.8 Audio</a>.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_AUDIO_OUTPUT = "android.hardware.audio.output";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
+     * The device has professional audio level of functionality and performance.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_AUDIO_PRO = "android.hardware.audio.pro";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device is capable of communicating with
+     * other devices via Bluetooth.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_BLUETOOTH = "android.hardware.bluetooth";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device is capable of communicating with
+     * other devices via Bluetooth Low Energy radio.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_BLUETOOTH_LE = "android.hardware.bluetooth_le";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device has a camera facing away
+     * from the screen.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_CAMERA = "android.hardware.camera";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device's camera supports auto-focus.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_CAMERA_AUTOFOCUS = "android.hardware.camera.autofocus";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device has at least one camera pointing in
+     * some direction, or can support an external camera being connected to it.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_CAMERA_ANY = "android.hardware.camera.any";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device can support having an external camera connected to it.
+     * The external camera may not always be connected or available to applications to use.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_CAMERA_EXTERNAL = "android.hardware.camera.external";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device's camera supports flash.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_CAMERA_FLASH = "android.hardware.camera.flash";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device has a front facing camera.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_CAMERA_FRONT = "android.hardware.camera.front";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: At least one
+     * of the cameras on the device supports the
+     * {@link android.hardware.camera2.CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL full hardware}
+     * capability level.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_CAMERA_LEVEL_FULL = "android.hardware.camera.level.full";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: At least one
+     * of the cameras on the device supports the
+     * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR manual sensor}
+     * capability level.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_CAMERA_CAPABILITY_MANUAL_SENSOR =
+            "android.hardware.camera.capability.manual_sensor";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: At least one
+     * of the cameras on the device supports the
+     * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING manual post-processing}
+     * capability level.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_CAMERA_CAPABILITY_MANUAL_POST_PROCESSING =
+            "android.hardware.camera.capability.manual_post_processing";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: At least one
+     * of the cameras on the device supports the
+     * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_RAW RAW}
+     * capability level.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_CAMERA_CAPABILITY_RAW =
+            "android.hardware.camera.capability.raw";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: At least one
+     * of the cameras on the device supports the
+     * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING
+     * MOTION_TRACKING} capability level.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_CAMERA_AR =
+            "android.hardware.camera.ar";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device's main front and back cameras can stream
+     * concurrently as described in  {@link
+     * android.hardware.camera2.CameraManager#getConcurrentCameraIds()}
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_CAMERA_CONCURRENT = "android.hardware.camera.concurrent";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device is capable of communicating with
+     * consumer IR devices.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_CONSUMER_IR = "android.hardware.consumerir";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device supports a Context Hub, used to expose the
+     * functionalities in {@link android.hardware.location.ContextHubManager}.
+     *
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_CONTEXT_HUB = "android.hardware.context_hub";
+
+    /** {@hide} */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_CTS = "android.software.cts";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device supports one or more methods of
+     * reporting current location.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_LOCATION = "android.hardware.location";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device has a Global Positioning System
+     * receiver and can report precise location.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_LOCATION_GPS = "android.hardware.location.gps";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device can report location with coarse
+     * accuracy using a network-based geolocation system.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_LOCATION_NETWORK = "android.hardware.location.network";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device's
+     * {@link ActivityManager#isLowRamDevice() ActivityManager.isLowRamDevice()} method returns
+     * true.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_RAM_LOW = "android.hardware.ram.low";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device's
+     * {@link ActivityManager#isLowRamDevice() ActivityManager.isLowRamDevice()} method returns
+     * false.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_RAM_NORMAL = "android.hardware.ram.normal";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device can record audio via a
+     * microphone.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_MICROPHONE = "android.hardware.microphone";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device can communicate using Near-Field
+     * Communications (NFC).
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_NFC = "android.hardware.nfc";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device supports host-
+     * based NFC card emulation.
+     *
+     * TODO remove when depending apps have moved to new constant.
+     * @hide
+     * @deprecated
+     */
+    @Deprecated
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_NFC_HCE = "android.hardware.nfc.hce";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device supports host-
+     * based NFC card emulation.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_NFC_HOST_CARD_EMULATION = "android.hardware.nfc.hce";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device supports host-
+     * based NFC-F card emulation.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_NFC_HOST_CARD_EMULATION_NFCF = "android.hardware.nfc.hcef";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device supports uicc-
+     * based NFC card emulation.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC =
+                                                                       "android.hardware.nfc.uicc";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device supports eSE-
+     * based NFC card emulation.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE = "android.hardware.nfc.ese";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The Beam API is enabled on the device.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_NFC_BEAM = "android.sofware.nfc.beam";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device supports any
+     * one of the {@link #FEATURE_NFC}, {@link #FEATURE_NFC_HOST_CARD_EMULATION},
+     * or {@link #FEATURE_NFC_HOST_CARD_EMULATION_NFCF} features.
+     *
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_NFC_ANY = "android.hardware.nfc.any";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device supports Open Mobile API capable UICC-based secure
+     * elements.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_SE_OMAPI_UICC = "android.hardware.se.omapi.uicc";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device supports Open Mobile API capable eSE-based secure
+     * elements.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_SE_OMAPI_ESE = "android.hardware.se.omapi.ese";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device supports Open Mobile API capable SD-based secure
+     * elements.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_SE_OMAPI_SD = "android.hardware.se.omapi.sd";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device supports the OpenGL ES
+     * <a href="http://www.khronos.org/registry/gles/extensions/ANDROID/ANDROID_extension_pack_es31a.txt">
+     * Android Extension Pack</a>.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_OPENGLES_EXTENSION_PACK = "android.hardware.opengles.aep";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature(String, int)}: If this feature is supported, the Vulkan
+     * implementation on this device is hardware accelerated, and the Vulkan native API will
+     * enumerate at least one {@code VkPhysicalDevice}, and the feature version will indicate what
+     * level of optional hardware features limits it supports.
+     * <p>
+     * Level 0 includes the base Vulkan requirements as well as:
+     * <ul><li>{@code VkPhysicalDeviceFeatures::textureCompressionETC2}</li></ul>
+     * <p>
+     * Level 1 additionally includes:
+     * <ul>
+     * <li>{@code VkPhysicalDeviceFeatures::fullDrawIndexUint32}</li>
+     * <li>{@code VkPhysicalDeviceFeatures::imageCubeArray}</li>
+     * <li>{@code VkPhysicalDeviceFeatures::independentBlend}</li>
+     * <li>{@code VkPhysicalDeviceFeatures::geometryShader}</li>
+     * <li>{@code VkPhysicalDeviceFeatures::tessellationShader}</li>
+     * <li>{@code VkPhysicalDeviceFeatures::sampleRateShading}</li>
+     * <li>{@code VkPhysicalDeviceFeatures::textureCompressionASTC_LDR}</li>
+     * <li>{@code VkPhysicalDeviceFeatures::fragmentStoresAndAtomics}</li>
+     * <li>{@code VkPhysicalDeviceFeatures::shaderImageGatherExtended}</li>
+     * <li>{@code VkPhysicalDeviceFeatures::shaderUniformBufferArrayDynamicIndexing}</li>
+     * <li>{@code VkPhysicalDeviceFeatures::shaderSampledImageArrayDynamicIndexing}</li>
+     * </ul>
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_VULKAN_HARDWARE_LEVEL = "android.hardware.vulkan.level";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature(String, int)}: If this feature is supported, the Vulkan
+     * implementation on this device is hardware accelerated, and the Vulkan native API will
+     * enumerate at least one {@code VkPhysicalDevice}, and the feature version will indicate what
+     * level of optional compute features that device supports beyond the Vulkan 1.0 requirements.
+     * <p>
+     * Compute level 0 indicates:
+     * <ul>
+     * <li>The {@code VK_KHR_variable_pointers} extension and
+     *     {@code VkPhysicalDeviceVariablePointerFeaturesKHR::variablePointers} feature are
+           supported.</li>
+     * <li>{@code VkPhysicalDeviceLimits::maxPerStageDescriptorStorageBuffers} is at least 16.</li>
+     * </ul>
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_VULKAN_HARDWARE_COMPUTE = "android.hardware.vulkan.compute";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature(String, int)}: If this feature is supported, the Vulkan
+     * implementation on this device is hardware accelerated, and the feature version will indicate
+     * the highest {@code VkPhysicalDeviceProperties::apiVersion} supported by the physical devices
+     * that support the hardware level indicated by {@link #FEATURE_VULKAN_HARDWARE_LEVEL}. The
+     * feature version uses the same encoding as Vulkan version numbers:
+     * <ul>
+     * <li>Major version number in bits 31-22</li>
+     * <li>Minor version number in bits 21-12</li>
+     * <li>Patch version number in bits 11-0</li>
+     * </ul>
+     * A version of 1.1.0 or higher also indicates:
+     * <ul>
+     * <li>The {@code VK_ANDROID_external_memory_android_hardware_buffer} extension is
+     *     supported.</li>
+     * <li>{@code SYNC_FD} external semaphore and fence handles are supported.</li>
+     * <li>{@code VkPhysicalDeviceSamplerYcbcrConversionFeatures::samplerYcbcrConversion} is
+     *     supported.</li>
+     * </ul>
+     * A subset of devices that support Vulkan 1.1 do so via software emulation. For more
+     * information, see
+     * <a href="{@docRoot}ndk/guides/graphics/design-notes">Vulkan Design Guidelines</a>.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_VULKAN_HARDWARE_VERSION = "android.hardware.vulkan.version";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature(String, int)}: If this feature is supported, the feature version
+     * specifies a date such that the device is known to pass the Vulkan dEQP test suite associated
+     * with that date.  The date is encoded as follows:
+     * <ul>
+     * <li>Year in bits 31-16</li>
+     * <li>Month in bits 15-8</li>
+     * <li>Day in bits 7-0</li>
+     * </ul>
+     * <p>
+     * Example: 2019-03-01 is encoded as 0x07E30301, and would indicate that the device passes the
+     * Vulkan dEQP test suite version that was current on 2019-03-01.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_VULKAN_DEQP_LEVEL = "android.software.vulkan.deqp.level";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device includes broadcast radio tuner.
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_BROADCAST_RADIO = "android.hardware.broadcastradio";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device has a secure implementation of keyguard, meaning the
+     * device supports PIN, pattern and password as defined in Android CDD
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_SECURE_LOCK_SCREEN = "android.software.secure_lock_screen";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device includes an accelerometer.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_SENSOR_ACCELEROMETER = "android.hardware.sensor.accelerometer";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device includes a barometer (air
+     * pressure sensor.)
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_SENSOR_BAROMETER = "android.hardware.sensor.barometer";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device includes a magnetometer (compass).
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_SENSOR_COMPASS = "android.hardware.sensor.compass";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device includes a gyroscope.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_SENSOR_GYROSCOPE = "android.hardware.sensor.gyroscope";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device includes a light sensor.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_SENSOR_LIGHT = "android.hardware.sensor.light";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device includes a proximity sensor.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_SENSOR_PROXIMITY = "android.hardware.sensor.proximity";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device includes a hardware step counter.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_SENSOR_STEP_COUNTER = "android.hardware.sensor.stepcounter";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device includes a hardware step detector.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_SENSOR_STEP_DETECTOR = "android.hardware.sensor.stepdetector";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device includes a heart rate monitor.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_SENSOR_HEART_RATE = "android.hardware.sensor.heartrate";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The heart rate sensor on this device is an Electrocardiogram.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_SENSOR_HEART_RATE_ECG =
+            "android.hardware.sensor.heartrate.ecg";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device includes a relative humidity sensor.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_SENSOR_RELATIVE_HUMIDITY =
+            "android.hardware.sensor.relative_humidity";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device includes an ambient temperature sensor.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_SENSOR_AMBIENT_TEMPERATURE =
+            "android.hardware.sensor.ambient_temperature";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device includes a hinge angle sensor.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_SENSOR_HINGE_ANGLE = "android.hardware.sensor.hinge_angle";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device supports high fidelity sensor processing
+     * capabilities.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_HIFI_SENSORS =
+            "android.hardware.sensor.hifi_sensors";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
+     * The device supports a hardware mechanism for invoking an assist gesture.
+     * @see android.provider.Settings.Secure#ASSIST_GESTURE_ENABLED
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_ASSIST_GESTURE = "android.hardware.sensor.assist";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device has a telephony radio with data
+     * communication support.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_TELEPHONY = "android.hardware.telephony";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device has a CDMA telephony stack.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_TELEPHONY_CDMA = "android.hardware.telephony.cdma";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device has a GSM telephony stack.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_TELEPHONY_GSM = "android.hardware.telephony.gsm";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
+     * The device supports telephony carrier restriction mechanism.
+     *
+     * <p>Devices declaring this feature must have an implementation of the
+     * {@link android.telephony.TelephonyManager#getAllowedCarriers} and
+     * {@link android.telephony.TelephonyManager#setAllowedCarriers}.
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_TELEPHONY_CARRIERLOCK =
+            "android.hardware.telephony.carrierlock";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device
+     * supports embedded subscriptions on eUICCs.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_TELEPHONY_EUICC = "android.hardware.telephony.euicc";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device
+     * supports cell-broadcast reception using the MBMS APIs.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_TELEPHONY_MBMS = "android.hardware.telephony.mbms";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device
+     * supports attaching to IMS implementations using the ImsService API in telephony.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_TELEPHONY_IMS = "android.hardware.telephony.ims";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device supports connecting to USB devices
+     * as the USB host.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_USB_HOST = "android.hardware.usb.host";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device supports connecting to USB accessories.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_USB_ACCESSORY = "android.hardware.usb.accessory";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The SIP API is enabled on the device.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_SIP = "android.software.sip";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device supports SIP-based VOIP.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_SIP_VOIP = "android.software.sip.voip";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The Connection Service API is enabled on the device.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_CONNECTION_SERVICE = "android.software.connectionservice";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device's display has a touch screen.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_TOUCHSCREEN = "android.hardware.touchscreen";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device's touch screen supports
+     * multitouch sufficient for basic two-finger gesture detection.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_TOUCHSCREEN_MULTITOUCH = "android.hardware.touchscreen.multitouch";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device's touch screen is capable of
+     * tracking two or more fingers fully independently.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT = "android.hardware.touchscreen.multitouch.distinct";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device's touch screen is capable of
+     * tracking a full hand of fingers fully independently -- that is, 5 or
+     * more simultaneous independent pointers.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_TOUCHSCREEN_MULTITOUCH_JAZZHAND = "android.hardware.touchscreen.multitouch.jazzhand";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device does not have a touch screen, but
+     * does support touch emulation for basic events. For instance, the
+     * device might use a mouse or remote control to drive a cursor, and
+     * emulate basic touch pointer events like down, up, drag, etc. All
+     * devices that support android.hardware.touchscreen or a sub-feature are
+     * presumed to also support faketouch.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_FAKETOUCH = "android.hardware.faketouch";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device does not have a touch screen, but
+     * does support touch emulation for basic events that supports distinct
+     * tracking of two or more fingers.  This is an extension of
+     * {@link #FEATURE_FAKETOUCH} for input devices with this capability.  Note
+     * that unlike a distinct multitouch screen as defined by
+     * {@link #FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT}, these kinds of input
+     * devices will not actually provide full two-finger gestures since the
+     * input is being transformed to cursor movement on the screen.  That is,
+     * single finger gestures will move a cursor; two-finger swipes will
+     * result in single-finger touch events; other two-finger gestures will
+     * result in the corresponding two-finger touch event.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_FAKETOUCH_MULTITOUCH_DISTINCT = "android.hardware.faketouch.multitouch.distinct";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device does not have a touch screen, but
+     * does support touch emulation for basic events that supports tracking
+     * a hand of fingers (5 or more fingers) fully independently.
+     * This is an extension of
+     * {@link #FEATURE_FAKETOUCH} for input devices with this capability.  Note
+     * that unlike a multitouch screen as defined by
+     * {@link #FEATURE_TOUCHSCREEN_MULTITOUCH_JAZZHAND}, not all two finger
+     * gestures can be detected due to the limitations described for
+     * {@link #FEATURE_FAKETOUCH_MULTITOUCH_DISTINCT}.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_FAKETOUCH_MULTITOUCH_JAZZHAND = "android.hardware.faketouch.multitouch.jazzhand";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device has biometric hardware to detect a fingerprint.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_FINGERPRINT = "android.hardware.fingerprint";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device has biometric hardware to perform face authentication.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_FACE = "android.hardware.biometrics.face";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device has biometric hardware to perform iris authentication.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_IRIS = "android.hardware.biometrics.iris";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device supports portrait orientation
+     * screens.  For backwards compatibility, you can assume that if neither
+     * this nor {@link #FEATURE_SCREEN_LANDSCAPE} is set then the device supports
+     * both portrait and landscape.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_SCREEN_PORTRAIT = "android.hardware.screen.portrait";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device supports landscape orientation
+     * screens.  For backwards compatibility, you can assume that if neither
+     * this nor {@link #FEATURE_SCREEN_PORTRAIT} is set then the device supports
+     * both portrait and landscape.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_SCREEN_LANDSCAPE = "android.hardware.screen.landscape";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device supports live wallpapers.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_LIVE_WALLPAPER = "android.software.live_wallpaper";
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device supports app widgets.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_APP_WIDGETS = "android.software.app_widgets";
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device supports the
+     * {@link android.R.attr#cantSaveState} API.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_CANT_SAVE_STATE = "android.software.cant_save_state";
+
+    /**
+     * @hide
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device supports
+     * {@link android.service.voice.VoiceInteractionService} and
+     * {@link android.app.VoiceInteractor}.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_VOICE_RECOGNIZERS = "android.software.voice_recognizers";
+
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device supports a home screen that is replaceable
+     * by third party applications.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_HOME_SCREEN = "android.software.home_screen";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device supports adding new input methods implemented
+     * with the {@link android.inputmethodservice.InputMethodService} API.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_INPUT_METHODS = "android.software.input_methods";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device supports device policy enforcement via device admins.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_DEVICE_ADMIN = "android.software.device_admin";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device supports leanback UI. This is
+     * typically used in a living room television experience, but is a software
+     * feature unlike {@link #FEATURE_TELEVISION}. Devices running with this
+     * feature will use resources associated with the "television" UI mode.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_LEANBACK = "android.software.leanback";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device supports only leanback UI. Only
+     * applications designed for this experience should be run, though this is
+     * not enforced by the system.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_LEANBACK_ONLY = "android.software.leanback_only";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device supports live TV and can display
+     * contents from TV inputs implemented with the
+     * {@link android.media.tv.TvInputService} API.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_LIVE_TV = "android.software.live_tv";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device supports WiFi (802.11) networking.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_WIFI = "android.hardware.wifi";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device supports Wi-Fi Direct networking.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_WIFI_DIRECT = "android.hardware.wifi.direct";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device supports Wi-Fi Aware.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_WIFI_AWARE = "android.hardware.wifi.aware";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device supports Wi-Fi Passpoint and all
+     * Passpoint related APIs in {@link WifiManager} are supported. Refer to
+     * {@link WifiManager#addOrUpdatePasspointConfiguration} for more info.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_WIFI_PASSPOINT = "android.hardware.wifi.passpoint";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device supports Wi-Fi RTT (IEEE 802.11mc).
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_WIFI_RTT = "android.hardware.wifi.rtt";
+
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device supports LoWPAN networking.
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_LOWPAN = "android.hardware.lowpan";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: This is a device dedicated to showing UI
+     * on a vehicle headunit. A headunit here is defined to be inside a
+     * vehicle that may or may not be moving. A headunit uses either a
+     * primary display in the center console and/or additional displays in
+     * the instrument cluster or elsewhere in the vehicle. Headunit display(s)
+     * have limited size and resolution. The user will likely be focused on
+     * driving so limiting driver distraction is a primary concern. User input
+     * can be a variety of hard buttons, touch, rotary controllers and even mouse-
+     * like interfaces.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_AUTOMOTIVE = "android.hardware.type.automotive";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: This is a device dedicated to showing UI
+     * on a television.  Television here is defined to be a typical living
+     * room television experience: displayed on a big screen, where the user
+     * is sitting far away from it, and the dominant form of input will be
+     * something like a DPAD, not through touch or mouse.
+     * @deprecated use {@link #FEATURE_LEANBACK} instead.
+     */
+    @Deprecated
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_TELEVISION = "android.hardware.type.television";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: This is a device dedicated to showing UI
+     * on a watch. A watch here is defined to be a device worn on the body, perhaps on
+     * the wrist. The user is very close when interacting with the device.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_WATCH = "android.hardware.type.watch";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: This is a device for IoT and may not have an UI. An embedded
+     * device is defined as a full stack Android device with or without a display and no
+     * user-installable apps.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_EMBEDDED = "android.hardware.type.embedded";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: This is a device dedicated to be primarily used
+     * with keyboard, mouse or touchpad. This includes traditional desktop
+     * computers, laptops and variants such as convertibles or detachables.
+     * Due to the larger screen, the device will most likely use the
+     * {@link #FEATURE_FREEFORM_WINDOW_MANAGEMENT} feature as well.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_PC = "android.hardware.type.pc";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
+     * The device supports printing.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_PRINTING = "android.software.print";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
+     * The device supports {@link android.companion.CompanionDeviceManager#associate associating}
+     * with devices via {@link android.companion.CompanionDeviceManager}.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_COMPANION_DEVICE_SETUP
+            = "android.software.companion_device_setup";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
+     * The device can perform backup and restore operations on installed applications.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_BACKUP = "android.software.backup";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device supports freeform window management.
+     * Windows have title bars and can be moved and resized.
+     */
+    // If this feature is present, you also need to set
+    // com.android.internal.R.config_freeformWindowManagement to true in your configuration overlay.
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_FREEFORM_WINDOW_MANAGEMENT
+            = "android.software.freeform_window_management";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
+     * The device supports picture-in-picture multi-window mode.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_PICTURE_IN_PICTURE = "android.software.picture_in_picture";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
+     * The device supports running activities on secondary displays.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS
+            = "android.software.activities_on_secondary_displays";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
+     * The device supports creating secondary users and managed profiles via
+     * {@link DevicePolicyManager}.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_MANAGED_USERS = "android.software.managed_users";
+
+    /**
+     * @hide
+     * TODO: Remove after dependencies updated b/17392243
+     */
+    public static final String FEATURE_MANAGED_PROFILES = "android.software.managed_users";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
+     * The device supports verified boot.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_VERIFIED_BOOT = "android.software.verified_boot";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
+     * The device supports secure removal of users. When a user is deleted the data associated
+     * with that user is securely deleted and no longer available.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_SECURELY_REMOVES_USERS
+            = "android.software.securely_removes_users";
+
+    /** {@hide} */
+    @TestApi
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_FILE_BASED_ENCRYPTION
+            = "android.software.file_based_encryption";
+
+    /** {@hide} */
+    @TestApi
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_ADOPTABLE_STORAGE
+            = "android.software.adoptable_storage";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
+     * The device has a full implementation of the android.webkit.* APIs. Devices
+     * lacking this feature will not have a functioning WebView implementation.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_WEBVIEW = "android.software.webview";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: This device supports ethernet.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_ETHERNET = "android.hardware.ethernet";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: This device supports HDMI-CEC.
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_HDMI_CEC = "android.hardware.hdmi.cec";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
+     * The device has all of the inputs necessary to be considered a compatible game controller, or
+     * includes a compatible game controller in the box.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_GAMEPAD = "android.hardware.gamepad";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
+     * The device has a full implementation of the android.media.midi.* APIs.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_MIDI = "android.software.midi";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
+     * The device implements an optimized mode for virtual reality (VR) applications that handles
+     * stereoscopic rendering of notifications, and disables most monocular system UI components
+     * while a VR application has user focus.
+     * Devices declaring this feature must include an application implementing a
+     * {@link android.service.vr.VrListenerService} that can be targeted by VR applications via
+     * {@link android.app.Activity#setVrModeEnabled}.
+     * @deprecated use {@link #FEATURE_VR_MODE_HIGH_PERFORMANCE} instead.
+     */
+    @Deprecated
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_VR_MODE = "android.software.vr.mode";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
+     * The device implements an optimized mode for virtual reality (VR) applications that handles
+     * stereoscopic rendering of notifications, disables most monocular system UI components
+     * while a VR application has user focus and meets extra CDD requirements to provide a
+     * high-quality VR experience.
+     * Devices declaring this feature must include an application implementing a
+     * {@link android.service.vr.VrListenerService} that can be targeted by VR applications via
+     * {@link android.app.Activity#setVrModeEnabled}.
+     * and must meet CDD requirements to provide a high-quality VR experience.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_VR_MODE_HIGH_PERFORMANCE
+            = "android.hardware.vr.high_performance";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
+     * The device supports autofill of user credentials, addresses, credit cards, etc
+     * via integration with {@link android.service.autofill.AutofillService autofill
+     * providers}.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_AUTOFILL = "android.software.autofill";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
+     * The device implements headtracking suitable for a VR device.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_VR_HEADTRACKING = "android.hardware.vr.headtracking";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
+     * The device has a StrongBox hardware-backed Keystore.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_STRONGBOX_KEYSTORE =
+            "android.hardware.strongbox_keystore";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
+     * The device does not have slices implementation.
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_SLICES_DISABLED = "android.software.slices_disabled";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
+     * The device supports device-unique Keystore attestations.  Only available on devices that
+     * also support {@link #FEATURE_STRONGBOX_KEYSTORE}, and can only be used by device owner
+     * apps (see {@link android.app.admin.DevicePolicyManager#generateKeyPair}).
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_DEVICE_UNIQUE_ATTESTATION =
+            "android.hardware.device_unique_attestation";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
+     * The device has a Keymaster implementation that supports Device ID attestation.
+     *
+     * @see DevicePolicyManager#isDeviceIdAttestationSupported
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_DEVICE_ID_ATTESTATION =
+            "android.software.device_id_attestation";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device has
+     * the requisite kernel support for multinetworking-capable IPsec tunnels.
+     *
+     * <p>This feature implies that the device supports XFRM Interfaces (CONFIG_XFRM_INTERFACE), or
+     * VTIs with kernel patches allowing updates of output/set mark via UPDSA.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_IPSEC_TUNNELS = "android.software.ipsec_tunnels";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device supports a system interface for the user to select
+     * and bind device control services provided by applications.
+     *
+     * @see android.service.controls.ControlsProviderService
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_CONTROLS = "android.software.controls";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device has
+     * the requisite hardware support to support reboot escrow of synthetic password for updates.
+     *
+     * <p>This feature implies that the device has the RebootEscrow HAL implementation.
+     *
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_REBOOT_ESCROW = "android.hardware.reboot_escrow";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device has
+     * the requisite kernel support to support incremental delivery aka Incremental FileSystem.
+     *
+     * @see IncrementalManager#isEnabled
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_INCREMENTAL_DELIVERY =
+            "android.software.incremental_delivery";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
+     * The device has tuner hardware to support tuner operations.
+     *
+     * <p>This feature implies that the device has the tuner HAL implementation.
+     *
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_TUNER = "android.hardware.tv.tuner";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device has
+     * the necessary changes to support app enumeration.
+     *
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_APP_ENUMERATION = "android.software.app_enumeration";
+
+    /** @hide */
+    public static final boolean APP_ENUMERATION_ENABLED_BY_DEFAULT = true;
+
+    /**
+     * Extra field name for the URI to a verification file. Passed to a package
+     * verifier.
+     *
+     * @hide
+     */
+    public static final String EXTRA_VERIFICATION_URI = "android.content.pm.extra.VERIFICATION_URI";
+
+    /**
+     * Extra field name for the ID of a package pending verification. Passed to
+     * a package verifier and is used to call back to
+     * {@link PackageManager#verifyPendingInstall(int, int)}
+     */
+    public static final String EXTRA_VERIFICATION_ID = "android.content.pm.extra.VERIFICATION_ID";
+
+    /**
+     * Extra field name for the package identifier which is trying to install
+     * the package.
+     *
+     * @hide
+     */
+    public static final String EXTRA_VERIFICATION_INSTALLER_PACKAGE
+            = "android.content.pm.extra.VERIFICATION_INSTALLER_PACKAGE";
+
+    /**
+     * Extra field name for the requested install flags for a package pending
+     * verification. Passed to a package verifier.
+     *
+     * @hide
+     */
+    public static final String EXTRA_VERIFICATION_INSTALL_FLAGS
+            = "android.content.pm.extra.VERIFICATION_INSTALL_FLAGS";
+
+    /**
+     * Extra field name for the uid of who is requesting to install
+     * the package.
+     *
+     * @hide
+     */
+    public static final String EXTRA_VERIFICATION_INSTALLER_UID
+            = "android.content.pm.extra.VERIFICATION_INSTALLER_UID";
+
+    /**
+     * Extra field name for the package name of a package pending verification.
+     *
+     * @hide
+     */
+    public static final String EXTRA_VERIFICATION_PACKAGE_NAME
+            = "android.content.pm.extra.VERIFICATION_PACKAGE_NAME";
+    /**
+     * Extra field name for the result of a verification, either
+     * {@link #VERIFICATION_ALLOW}, or {@link #VERIFICATION_REJECT}.
+     * Passed to package verifiers after a package is verified.
+     */
+    public static final String EXTRA_VERIFICATION_RESULT
+            = "android.content.pm.extra.VERIFICATION_RESULT";
+
+    /**
+     * Extra field name for the version code of a package pending verification.
+     * @deprecated Use {@link #EXTRA_VERIFICATION_LONG_VERSION_CODE} instead.
+     * @hide
+     */
+    @Deprecated
+    public static final String EXTRA_VERIFICATION_VERSION_CODE
+            = "android.content.pm.extra.VERIFICATION_VERSION_CODE";
+
+    /**
+     * Extra field name for the long version code of a package pending verification.
+     *
+     * @hide
+     */
+    public static final String EXTRA_VERIFICATION_LONG_VERSION_CODE =
+            "android.content.pm.extra.VERIFICATION_LONG_VERSION_CODE";
+
+    /**
+     * Extra field name for the Merkle tree root hash of a package.
+     * <p>Passed to a package verifier both prior to verification and as a result
+     * of verification.
+     * <p>The value of the extra is a specially formatted list:
+     * {@code filename1:HASH_1;filename2:HASH_2;...;filenameN:HASH_N}
+     * <p>The extra must include an entry for every APK within an installation. If
+     * a hash is not physically present, a hash value of {@code 0} will be used.
+     * <p>The root hash is generated using SHA-256, no salt with a 4096 byte block
+     * size. See the description of the
+     * <a href="https://www.kernel.org/doc/html/latest/filesystems/fsverity.html#merkle-tree">fs-verity merkle-tree</a>
+     * for more details.
+     * @hide
+     */
+    public static final String EXTRA_VERIFICATION_ROOT_HASH =
+            "android.content.pm.extra.EXTRA_VERIFICATION_ROOT_HASH";
+
+    /**
+     * Extra field name for the ID of a intent filter pending verification.
+     * Passed to an intent filter verifier and is used to call back to
+     * {@link #verifyIntentFilter}
+     *
+     * @hide
+     */
+    public static final String EXTRA_INTENT_FILTER_VERIFICATION_ID
+            = "android.content.pm.extra.INTENT_FILTER_VERIFICATION_ID";
+
+    /**
+     * Extra field name for the scheme used for an intent filter pending verification. Passed to
+     * an intent filter verifier and is used to construct the URI to verify against.
+     *
+     * Usually this is "https"
+     *
+     * @hide
+     */
+    public static final String EXTRA_INTENT_FILTER_VERIFICATION_URI_SCHEME
+            = "android.content.pm.extra.INTENT_FILTER_VERIFICATION_URI_SCHEME";
+
+    /**
+     * Extra field name for the host names to be used for an intent filter pending verification.
+     * Passed to an intent filter verifier and is used to construct the URI to verify the
+     * intent filter.
+     *
+     * This is a space delimited list of hosts.
+     *
+     * @hide
+     */
+    public static final String EXTRA_INTENT_FILTER_VERIFICATION_HOSTS
+            = "android.content.pm.extra.INTENT_FILTER_VERIFICATION_HOSTS";
+
+    /**
+     * Extra field name for the package name to be used for an intent filter pending verification.
+     * Passed to an intent filter verifier and is used to check the verification responses coming
+     * from the hosts. Each host response will need to include the package name of APK containing
+     * the intent filter.
+     *
+     * @hide
+     */
+    public static final String EXTRA_INTENT_FILTER_VERIFICATION_PACKAGE_NAME
+            = "android.content.pm.extra.INTENT_FILTER_VERIFICATION_PACKAGE_NAME";
+
+    /**
+     * The action used to request that the user approve a permission request
+     * from the application.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String ACTION_REQUEST_PERMISSIONS =
+            "android.content.pm.action.REQUEST_PERMISSIONS";
+
+    /**
+     * The names of the requested permissions.
+     * <p>
+     * <strong>Type:</strong> String[]
+     * </p>
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_REQUEST_PERMISSIONS_NAMES =
+            "android.content.pm.extra.REQUEST_PERMISSIONS_NAMES";
+
+    /**
+     * The results from the permissions request.
+     * <p>
+     * <strong>Type:</strong> int[] of #PermissionResult
+     * </p>
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_REQUEST_PERMISSIONS_RESULTS
+            = "android.content.pm.extra.REQUEST_PERMISSIONS_RESULTS";
+
+    /**
+     * String extra for {@link PackageInstallObserver} in the 'extras' Bundle in case of
+     * {@link #INSTALL_FAILED_DUPLICATE_PERMISSION}.  This extra names the package which provides
+     * the existing definition for the permission.
+     * @hide
+     */
+    public static final String EXTRA_FAILURE_EXISTING_PACKAGE
+            = "android.content.pm.extra.FAILURE_EXISTING_PACKAGE";
+
+    /**
+     * String extra for {@link PackageInstallObserver} in the 'extras' Bundle in case of
+     * {@link #INSTALL_FAILED_DUPLICATE_PERMISSION}.  This extra names the permission that is
+     * being redundantly defined by the package being installed.
+     * @hide
+     */
+    public static final String EXTRA_FAILURE_EXISTING_PERMISSION
+            = "android.content.pm.extra.FAILURE_EXISTING_PERMISSION";
+
+   /**
+    * Permission flag: The permission is set in its current state
+    * by the user and apps can still request it at runtime.
+    *
+    * @hide
+    */
+    @SystemApi
+    @TestApi
+    public static final int FLAG_PERMISSION_USER_SET = 1 << 0;
+
+    /**
+     * Permission flag: The permission is set in its current state
+     * by the user and it is fixed, i.e. apps can no longer request
+     * this permission.
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    public static final int FLAG_PERMISSION_USER_FIXED =  1 << 1;
+
+    /**
+     * Permission flag: The permission is set in its current state
+     * by device policy and neither apps nor the user can change
+     * its state.
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    public static final int FLAG_PERMISSION_POLICY_FIXED =  1 << 2;
+
+    /**
+     * Permission flag: The permission is set in a granted state but
+     * access to resources it guards is restricted by other means to
+     * enable revoking a permission on legacy apps that do not support
+     * runtime permissions. If this permission is upgraded to runtime
+     * because the app was updated to support runtime permissions, the
+     * the permission will be revoked in the upgrade process.
+     *
+     * @deprecated Renamed to {@link #FLAG_PERMISSION_REVOKED_COMPAT}.
+     *
+     * @hide
+     */
+    @Deprecated
+    @SystemApi
+    @TestApi
+    public static final int FLAG_PERMISSION_REVOKE_ON_UPGRADE =  1 << 3;
+
+    /**
+     * Permission flag: The permission is set in its current state
+     * because the app is a component that is a part of the system.
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    public static final int FLAG_PERMISSION_SYSTEM_FIXED =  1 << 4;
+
+    /**
+     * Permission flag: The permission is granted by default because it
+     * enables app functionality that is expected to work out-of-the-box
+     * for providing a smooth user experience. For example, the phone app
+     * is expected to have the phone permission.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int FLAG_PERMISSION_GRANTED_BY_DEFAULT =  1 << 5;
+
+    /**
+     * Permission flag: The permission has to be reviewed before any of
+     * the app components can run.
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    public static final int FLAG_PERMISSION_REVIEW_REQUIRED =  1 << 6;
+
+    /**
+     * Permission flag: The permission has not been explicitly requested by
+     * the app but has been added automatically by the system. Revoke once
+     * the app does explicitly request it.
+     *
+     * @hide
+     */
+    @TestApi
+    public static final int FLAG_PERMISSION_REVOKE_WHEN_REQUESTED =  1 << 7;
+
+    /**
+     * Permission flag: The permission's usage should be made highly visible to the user
+     * when granted.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED =  1 << 8;
+
+    /**
+     * Permission flag: The permission's usage should be made highly visible to the user
+     * when denied.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED =  1 << 9;
+
+    /**
+     * Permission flag: The permission is restricted but the app is exempt
+     * from the restriction and is allowed to hold this permission in its
+     * full form and the exemption is provided by the installer on record.
+     *
+     * @hide
+     */
+    @TestApi
+    @SystemApi
+    public static final int FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT =  1 << 11;
+
+    /**
+     * Permission flag: The permission is restricted but the app is exempt
+     * from the restriction and is allowed to hold this permission in its
+     * full form and the exemption is provided by the system due to its
+     * permission policy.
+     *
+     * @hide
+     */
+    @TestApi
+    @SystemApi
+    public static final int FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT =  1 << 12;
+
+    /**
+     * Permission flag: The permission is restricted but the app is exempt
+     * from the restriction and is allowed to hold this permission and the
+     * exemption is provided by the system when upgrading from an OS version
+     * where the permission was not restricted to an OS version where the
+     * permission is restricted.
+     *
+     * @hide
+     */
+    @TestApi
+    @SystemApi
+    public static final int FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT =  1 << 13;
+
+
+    /**
+     * Permission flag: The permission is disabled but may be granted. If
+     * disabled the data protected by the permission should be protected
+     * by a no-op (empty list, default error, etc) instead of crashing the
+     * client.
+     *
+     * @hide
+     */
+    @TestApi
+    @SystemApi
+    public static final int FLAG_PERMISSION_APPLY_RESTRICTION =  1 << 14;
+
+    /**
+     * Permission flag: The permission is granted because the application holds a role.
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    public static final int FLAG_PERMISSION_GRANTED_BY_ROLE =  1 << 15;
+
+    /**
+     * Permission flag: The permission should have been revoked but is kept granted for
+     * compatibility. The data protected by the permission should be protected by a no-op (empty
+     * list, default error, etc) instead of crashing the client. The permission will be revoked if
+     * the app is upgraded to supports it.
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    public static final int FLAG_PERMISSION_REVOKED_COMPAT =  FLAG_PERMISSION_REVOKE_ON_UPGRADE;
+
+    /**
+     * Permission flag: The permission is one-time and should be revoked automatically on app
+     * inactivity
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    public static final int FLAG_PERMISSION_ONE_TIME = 1 << 16;
+
+    /**
+     * Permission flag: Whether permission was revoked by auto-revoke.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int FLAG_PERMISSION_AUTO_REVOKED = 1 << 17;
+
+    /**
+     * Permission flags: Reserved for use by the permission controller. The platform and any
+     * packages besides the permission controller should not assume any definition about these
+     * flags.
+     * @hide
+     */
+    @SystemApi
+    public static final int FLAGS_PERMISSION_RESERVED_PERMISSION_CONTROLLER = 1 << 28 | 1 << 29
+            | 1 << 30 | 1 << 31;
+
+    /**
+     * Permission flags: Bitwise or of all permission flags allowing an
+     * exemption for a restricted permission.
+     * @hide
+     */
+    public static final int FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT =
+            FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT
+                    | FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT
+                    | FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT;
+
+    /**
+     * Mask for all permission flags.
+     *
+     * @hide
+     *
+     * @deprecated Don't use - does not capture all flags.
+     */
+    @Deprecated
+    @SystemApi
+    public static final int MASK_PERMISSION_FLAGS = 0xFF;
+
+    /**
+     * Mask for all permission flags.
+     *
+     * @hide
+     */
+    public static final int MASK_PERMISSION_FLAGS_ALL = FLAG_PERMISSION_USER_SET
+            | FLAG_PERMISSION_USER_FIXED
+            | FLAG_PERMISSION_POLICY_FIXED
+            | FLAG_PERMISSION_REVOKE_ON_UPGRADE
+            | FLAG_PERMISSION_SYSTEM_FIXED
+            | FLAG_PERMISSION_GRANTED_BY_DEFAULT
+            | FLAG_PERMISSION_REVIEW_REQUIRED
+            | FLAG_PERMISSION_REVOKE_WHEN_REQUESTED
+            | FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED
+            | FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED
+            | FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT
+            | FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT
+            | FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT
+            | FLAG_PERMISSION_APPLY_RESTRICTION
+            | FLAG_PERMISSION_GRANTED_BY_ROLE
+            | FLAG_PERMISSION_REVOKED_COMPAT
+            | FLAG_PERMISSION_ONE_TIME
+            | FLAG_PERMISSION_AUTO_REVOKED;
+
+    /**
+     * Injected activity in app that forwards user to setting activity of that app.
+     *
+     * @hide
+     */
+    public static final String APP_DETAILS_ACTIVITY_CLASS_NAME = AppDetailsActivity.class.getName();
+
+    /**
+     * Permission whitelist flag: permissions whitelisted by the system.
+     * Permissions can also be whitelisted by the installer or on upgrade.
+     */
+    public static final int FLAG_PERMISSION_WHITELIST_SYSTEM = 1 << 0;
+
+    /**
+     * Permission whitelist flag: permissions whitelisted by the installer.
+     * Permissions can also be whitelisted by the system or on upgrade.
+     */
+    public static final int FLAG_PERMISSION_WHITELIST_INSTALLER = 1 << 1;
+
+    /**
+     * Permission whitelist flag: permissions whitelisted by the system
+     * when upgrading from an OS version where the permission was not
+     * restricted to an OS version where the permission is restricted.
+     * Permissions can also be whitelisted by the installer or the system.
+     */
+    public static final int FLAG_PERMISSION_WHITELIST_UPGRADE = 1 << 2;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = {"FLAG_PERMISSION_WHITELIST_"}, value = {
+            FLAG_PERMISSION_WHITELIST_SYSTEM,
+            FLAG_PERMISSION_WHITELIST_INSTALLER,
+            FLAG_PERMISSION_WHITELIST_UPGRADE
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface PermissionWhitelistFlags {}
+
+    /**
+     * This is a library that contains components apps can invoke. For
+     * example, a services for apps to bind to, or standard chooser UI,
+     * etc. This library is versioned and backwards compatible. Clients
+     * should check its version via {@link android.ext.services.Version
+     * #getVersionCode()} and avoid calling APIs added in later versions.
+     * <p>
+     * This shared library no longer exists since Android R.
+     *
+     * @see #getServicesSystemSharedLibraryPackageName()
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    @TestApi
+    public static final String SYSTEM_SHARED_LIBRARY_SERVICES = "android.ext.services";
+
+    /**
+     * This is a library that contains components apps can dynamically
+     * load. For example, new widgets, helper classes, etc. This library
+     * is versioned and backwards compatible. Clients should check its
+     * version via {@link android.ext.shared.Version#getVersionCode()}
+     * and avoid calling APIs added in later versions.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    @TestApi
+    public static final String SYSTEM_SHARED_LIBRARY_SHARED = "android.ext.shared";
+
+    /**
+     * Used when starting a process for an Activity.
+     *
+     * @hide
+     */
+    public static final int NOTIFY_PACKAGE_USE_ACTIVITY = 0;
+
+    /**
+     * Used when starting a process for a Service.
+     *
+     * @hide
+     */
+    public static final int NOTIFY_PACKAGE_USE_SERVICE = 1;
+
+    /**
+     * Used when moving a Service to the foreground.
+     *
+     * @hide
+     */
+    public static final int NOTIFY_PACKAGE_USE_FOREGROUND_SERVICE = 2;
+
+    /**
+     * Used when starting a process for a BroadcastReceiver.
+     *
+     * @hide
+     */
+    public static final int NOTIFY_PACKAGE_USE_BROADCAST_RECEIVER = 3;
+
+    /**
+     * Used when starting a process for a ContentProvider.
+     *
+     * @hide
+     */
+    public static final int NOTIFY_PACKAGE_USE_CONTENT_PROVIDER = 4;
+
+    /**
+     * Used when starting a process for a BroadcastReceiver.
+     *
+     * @hide
+     */
+    public static final int NOTIFY_PACKAGE_USE_BACKUP = 5;
+
+    /**
+     * Used with Context.getClassLoader() across Android packages.
+     *
+     * @hide
+     */
+    public static final int NOTIFY_PACKAGE_USE_CROSS_PACKAGE = 6;
+
+    /**
+     * Used when starting a package within a process for Instrumentation.
+     *
+     * @hide
+     */
+    public static final int NOTIFY_PACKAGE_USE_INSTRUMENTATION = 7;
+
+    /**
+     * Total number of usage reasons.
+     *
+     * @hide
+     */
+    public static final int NOTIFY_PACKAGE_USE_REASONS_COUNT = 8;
+
+    /**
+     * Constant for specifying the highest installed package version code.
+     */
+    public static final int VERSION_CODE_HIGHEST = -1;
+
+    /**
+     * Apps targeting Android R and above will need to declare the packages and intents they intend
+     * to use to get details about other apps on a device. Such declarations must be made via the
+     * {@code <queries>} tag in the manifest.
+     *
+     * @hide
+     */
+    @ChangeId
+    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
+    public static final long FILTER_APPLICATION_QUERY = 135549675L;
+
+    /** {@hide} */
+    @IntDef(prefix = {"SYSTEM_APP_STATE_"}, value = {
+            SYSTEM_APP_STATE_HIDDEN_UNTIL_INSTALLED_HIDDEN,
+            SYSTEM_APP_STATE_HIDDEN_UNTIL_INSTALLED_VISIBLE,
+            SYSTEM_APP_STATE_INSTALLED,
+            SYSTEM_APP_STATE_UNINSTALLED
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SystemAppState {}
+
+    /**
+     * Constant for noting system app state as hidden before installation
+     * @hide
+     */
+    public static final int SYSTEM_APP_STATE_HIDDEN_UNTIL_INSTALLED_HIDDEN = 0;
+
+    /**
+     * Constant for noting system app state as visible before installation
+     * @hide
+     */
+    public static final int SYSTEM_APP_STATE_HIDDEN_UNTIL_INSTALLED_VISIBLE = 1;
+
+    /**
+     * Constant for noting system app state as installed
+     * @hide
+     */
+    public static final int SYSTEM_APP_STATE_INSTALLED = 2;
+
+    /**
+     * Constant for noting system app state as not installed
+     * @hide
+     */
+    public static final int SYSTEM_APP_STATE_UNINSTALLED = 3;
+
+    /** {@hide} */
+    public int getUserId() {
+        return UserHandle.myUserId();
+    }
+
+    /**
+     * Retrieve overall information about an application package that is
+     * installed on the system.
+     *
+     * @param packageName The full name (i.e. com.google.apps.contacts) of the
+     *            desired package.
+     * @param flags Additional option flags to modify the data returned.
+     * @return A PackageInfo object containing information about the package. If
+     *         flag {@code MATCH_UNINSTALLED_PACKAGES} is set and if the package
+     *         is not found in the list of installed applications, the package
+     *         information is retrieved from the list of uninstalled
+     *         applications (which includes installed applications as well as
+     *         applications with data directory i.e. applications which had been
+     *         deleted with {@code DELETE_KEEP_DATA} flag set).
+     * @throws NameNotFoundException if a package with the given name cannot be
+     *             found on the system.
+     */
+    public abstract PackageInfo getPackageInfo(@NonNull String packageName,
+            @PackageInfoFlags int flags)
+            throws NameNotFoundException;
+
+    /**
+     * Retrieve overall information about an application package that is
+     * installed on the system. This method can be used for retrieving
+     * information about packages for which multiple versions can be installed
+     * at the time. Currently only packages hosting static shared libraries can
+     * have multiple installed versions. The method can also be used to get info
+     * for a package that has a single version installed by passing
+     * {@link #VERSION_CODE_HIGHEST} in the {@link VersionedPackage}
+     * constructor.
+     *
+     * @param versionedPackage The versioned package for which to query.
+     * @param flags Additional option flags to modify the data returned.
+     * @return A PackageInfo object containing information about the package. If
+     *         flag {@code MATCH_UNINSTALLED_PACKAGES} is set and if the package
+     *         is not found in the list of installed applications, the package
+     *         information is retrieved from the list of uninstalled
+     *         applications (which includes installed applications as well as
+     *         applications with data directory i.e. applications which had been
+     *         deleted with {@code DELETE_KEEP_DATA} flag set).
+     * @throws NameNotFoundException if a package with the given name cannot be
+     *             found on the system.
+     */
+    public abstract PackageInfo getPackageInfo(@NonNull VersionedPackage versionedPackage,
+            @PackageInfoFlags int flags) throws NameNotFoundException;
+
+    /**
+     * Retrieve overall information about an application package that is
+     * installed on the system.
+     *
+     * @param packageName The full name (i.e. com.google.apps.contacts) of the
+     *            desired package.
+     * @param flags Additional option flags to modify the data returned.
+     * @param userId The user id.
+     * @return A PackageInfo object containing information about the package. If
+     *         flag {@code MATCH_UNINSTALLED_PACKAGES} is set and if the package
+     *         is not found in the list of installed applications, the package
+     *         information is retrieved from the list of uninstalled
+     *         applications (which includes installed applications as well as
+     *         applications with data directory i.e. applications which had been
+     *         deleted with {@code DELETE_KEEP_DATA} flag set).
+     * @throws NameNotFoundException if a package with the given name cannot be
+     *             found on the system.
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
+    @UnsupportedAppUsage
+    public abstract PackageInfo getPackageInfoAsUser(@NonNull String packageName,
+            @PackageInfoFlags int flags, @UserIdInt int userId) throws NameNotFoundException;
+
+    /**
+     * Map from the current package names in use on the device to whatever
+     * the current canonical name of that package is.
+     * @param packageNames Array of current names to be mapped.
+     * @return Returns an array of the same size as the original, containing
+     * the canonical name for each package.
+     */
+    public abstract String[] currentToCanonicalPackageNames(@NonNull String[] packageNames);
+
+    /**
+     * Map from a packages canonical name to the current name in use on the device.
+     * @param packageNames Array of new names to be mapped.
+     * @return Returns an array of the same size as the original, containing
+     * the current name for each package.
+     */
+    public abstract String[] canonicalToCurrentPackageNames(@NonNull String[] packageNames);
+
+    /**
+     * Returns a "good" intent to launch a front-door activity in a package.
+     * This is used, for example, to implement an "open" button when browsing
+     * through packages.  The current implementation looks first for a main
+     * activity in the category {@link Intent#CATEGORY_INFO}, and next for a
+     * main activity in the category {@link Intent#CATEGORY_LAUNCHER}. Returns
+     * <code>null</code> if neither are found.
+     *
+     * @param packageName The name of the package to inspect.
+     *
+     * @return A fully-qualified {@link Intent} that can be used to launch the
+     * main activity in the package. Returns <code>null</code> if the package
+     * does not contain such an activity, or if <em>packageName</em> is not
+     * recognized.
+     */
+    public abstract @Nullable Intent getLaunchIntentForPackage(@NonNull String packageName);
+
+    /**
+     * Return a "good" intent to launch a front-door Leanback activity in a
+     * package, for use for example to implement an "open" button when browsing
+     * through packages. The current implementation will look for a main
+     * activity in the category {@link Intent#CATEGORY_LEANBACK_LAUNCHER}, or
+     * return null if no main leanback activities are found.
+     *
+     * @param packageName The name of the package to inspect.
+     * @return Returns either a fully-qualified Intent that can be used to launch
+     *         the main Leanback activity in the package, or null if the package
+     *         does not contain such an activity.
+     */
+    public abstract @Nullable Intent getLeanbackLaunchIntentForPackage(@NonNull String packageName);
+
+    /**
+     * Return a "good" intent to launch a front-door Car activity in a
+     * package, for use for example to implement an "open" button when browsing
+     * through packages. The current implementation will look for a main
+     * activity in the category {@link Intent#CATEGORY_CAR_LAUNCHER}, or
+     * return null if no main car activities are found.
+     *
+     * @param packageName The name of the package to inspect.
+     * @return Returns either a fully-qualified Intent that can be used to launch
+     *         the main Car activity in the package, or null if the package
+     *         does not contain such an activity.
+     * @hide
+     */
+    public abstract @Nullable Intent getCarLaunchIntentForPackage(@NonNull String packageName);
+
+    /**
+     * Return an array of all of the POSIX secondary group IDs that have been
+     * assigned to the given package.
+     * <p>
+     * Note that the same package may have different GIDs under different
+     * {@link UserHandle} on the same device.
+     *
+     * @param packageName The full name (i.e. com.google.apps.contacts) of the
+     *            desired package.
+     * @return Returns an int array of the assigned GIDs, or null if there are
+     *         none.
+     * @throws NameNotFoundException if a package with the given name cannot be
+     *             found on the system.
+     */
+    public abstract int[] getPackageGids(@NonNull String packageName)
+            throws NameNotFoundException;
+
+    /**
+     * Return an array of all of the POSIX secondary group IDs that have been
+     * assigned to the given package.
+     * <p>
+     * Note that the same package may have different GIDs under different
+     * {@link UserHandle} on the same device.
+     *
+     * @param packageName The full name (i.e. com.google.apps.contacts) of the
+     *            desired package.
+     * @return Returns an int array of the assigned gids, or null if there are
+     *         none.
+     * @throws NameNotFoundException if a package with the given name cannot be
+     *             found on the system.
+     */
+    public abstract int[] getPackageGids(@NonNull String packageName, @PackageInfoFlags int flags)
+            throws NameNotFoundException;
+
+    /**
+     * Return the UID associated with the given package name.
+     * <p>
+     * Note that the same package will have different UIDs under different
+     * {@link UserHandle} on the same device.
+     *
+     * @param packageName The full name (i.e. com.google.apps.contacts) of the
+     *            desired package.
+     * @return Returns an integer UID who owns the given package name.
+     * @throws NameNotFoundException if a package with the given name can not be
+     *             found on the system.
+     */
+    public abstract int getPackageUid(@NonNull String packageName, @PackageInfoFlags int flags)
+            throws NameNotFoundException;
+
+    /**
+     * Return the UID associated with the given package name.
+     * <p>
+     * Note that the same package will have different UIDs under different
+     * {@link UserHandle} on the same device.
+     *
+     * @param packageName The full name (i.e. com.google.apps.contacts) of the
+     *            desired package.
+     * @param userId The user handle identifier to look up the package under.
+     * @return Returns an integer UID who owns the given package name.
+     * @throws NameNotFoundException if a package with the given name can not be
+     *             found on the system.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public abstract int getPackageUidAsUser(@NonNull String packageName, @UserIdInt int userId)
+            throws NameNotFoundException;
+
+    /**
+     * Return the UID associated with the given package name.
+     * <p>
+     * Note that the same package will have different UIDs under different
+     * {@link UserHandle} on the same device.
+     *
+     * @param packageName The full name (i.e. com.google.apps.contacts) of the
+     *            desired package.
+     * @param userId The user handle identifier to look up the package under.
+     * @return Returns an integer UID who owns the given package name.
+     * @throws NameNotFoundException if a package with the given name can not be
+     *             found on the system.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public abstract int getPackageUidAsUser(@NonNull String packageName,
+            @PackageInfoFlags int flags, @UserIdInt int userId) throws NameNotFoundException;
+
+    /**
+     * Retrieve all of the information we know about a particular permission.
+     *
+     * @param permName The fully qualified name (i.e. com.google.permission.LOGIN)
+     *            of the permission you are interested in.
+     * @param flags Additional option flags to modify the data returned.
+     * @return Returns a {@link PermissionInfo} containing information about the
+     *         permission.
+     * @throws NameNotFoundException if a package with the given name cannot be
+     *             found on the system.
+     */
+    public abstract PermissionInfo getPermissionInfo(@NonNull String permName,
+            @PermissionInfoFlags int flags) throws NameNotFoundException;
+
+    /**
+     * Query for all of the permissions associated with a particular group.
+     *
+     * @param permissionGroup The fully qualified name (i.e. com.google.permission.LOGIN)
+     *            of the permission group you are interested in. Use null to
+     *            find all of the permissions not associated with a group.
+     * @param flags Additional option flags to modify the data returned.
+     * @return Returns a list of {@link PermissionInfo} containing information
+     *         about all of the permissions in the given group.
+     * @throws NameNotFoundException if a package with the given name cannot be
+     *             found on the system.
+     */
+    @NonNull
+    public abstract List<PermissionInfo> queryPermissionsByGroup(@NonNull String permissionGroup,
+            @PermissionInfoFlags int flags) throws NameNotFoundException;
+
+    /**
+     * Returns true if some permissions are individually controlled.
+     *
+     * <p>The user usually grants and revokes permission-groups. If this option is set some
+     * dangerous system permissions can be revoked/granted by the user separately from their group.
+     *
+     * @hide
+     */
+    @TestApi @SystemApi
+    public abstract boolean arePermissionsIndividuallyControlled();
+
+    /**
+     * Returns true if wireless consent mode is enabled
+     *
+     * @hide
+     */
+    public abstract boolean isWirelessConsentModeEnabled();
+
+    /**
+     * Retrieve all of the information we know about a particular group of
+     * permissions.
+     *
+     * @param permName The fully qualified name (i.e.
+     *            com.google.permission_group.APPS) of the permission you are
+     *            interested in.
+     * @param flags Additional option flags to modify the data returned.
+     * @return Returns a {@link PermissionGroupInfo} containing information
+     *         about the permission.
+     * @throws NameNotFoundException if a package with the given name cannot be
+     *             found on the system.
+     */
+    @NonNull
+    public abstract PermissionGroupInfo getPermissionGroupInfo(@NonNull String permName,
+            @PermissionGroupInfoFlags int flags) throws NameNotFoundException;
+
+    /**
+     * Retrieve all of the known permission groups in the system.
+     *
+     * @param flags Additional option flags to modify the data returned.
+     * @return Returns a list of {@link PermissionGroupInfo} containing
+     *         information about all of the known permission groups.
+     */
+    @NonNull
+    public abstract List<PermissionGroupInfo> getAllPermissionGroups(
+            @PermissionGroupInfoFlags int flags);
+
+    /**
+     * Retrieve all of the information we know about a particular
+     * package/application.
+     *
+     * @param packageName The full name (i.e. com.google.apps.contacts) of an
+     *            application.
+     * @param flags Additional option flags to modify the data returned.
+     * @return An {@link ApplicationInfo} containing information about the
+     *         package. If flag {@code MATCH_UNINSTALLED_PACKAGES} is set and if
+     *         the package is not found in the list of installed applications,
+     *         the application information is retrieved from the list of
+     *         uninstalled applications (which includes installed applications
+     *         as well as applications with data directory i.e. applications
+     *         which had been deleted with {@code DELETE_KEEP_DATA} flag set).
+     * @throws NameNotFoundException if a package with the given name cannot be
+     *             found on the system.
+     */
+    @NonNull
+    public abstract ApplicationInfo getApplicationInfo(@NonNull String packageName,
+            @ApplicationInfoFlags int flags) throws NameNotFoundException;
+
+    /** {@hide} */
+    @NonNull
+    @UnsupportedAppUsage
+    public abstract ApplicationInfo getApplicationInfoAsUser(@NonNull String packageName,
+            @ApplicationInfoFlags int flags, @UserIdInt int userId) throws NameNotFoundException;
+
+    /**
+     * Retrieve all of the information we know about a particular
+     * package/application, for a specific user.
+     *
+     * @param packageName The full name (i.e. com.google.apps.contacts) of an
+     *            application.
+     * @param flags Additional option flags to modify the data returned.
+     * @return An {@link ApplicationInfo} containing information about the
+     *         package. If flag {@code MATCH_UNINSTALLED_PACKAGES} is set and if
+     *         the package is not found in the list of installed applications,
+     *         the application information is retrieved from the list of
+     *         uninstalled applications (which includes installed applications
+     *         as well as applications with data directory i.e. applications
+     *         which had been deleted with {@code DELETE_KEEP_DATA} flag set).
+     * @throws NameNotFoundException if a package with the given name cannot be
+     *             found on the system.
+     * @hide
+     */
+    @NonNull
+    @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
+    @SystemApi
+    public ApplicationInfo getApplicationInfoAsUser(@NonNull String packageName,
+            @ApplicationInfoFlags int flags, @NonNull UserHandle user)
+            throws NameNotFoundException {
+        return getApplicationInfoAsUser(packageName, flags, user.getIdentifier());
+    }
+
+    /**
+     * Retrieve all of the information we know about a particular activity
+     * class.
+     *
+     * @param component The full component name (i.e.
+     *            com.google.apps.contacts/com.google.apps.contacts.
+     *            ContactsList) of an Activity class.
+     * @param flags Additional option flags to modify the data returned.
+     * @return An {@link ActivityInfo} containing information about the
+     *         activity.
+     * @throws NameNotFoundException if a package with the given name cannot be
+     *             found on the system.
+     */
+    @NonNull
+    public abstract ActivityInfo getActivityInfo(@NonNull ComponentName component,
+            @ComponentInfoFlags int flags) throws NameNotFoundException;
+
+    /**
+     * Retrieve all of the information we know about a particular receiver
+     * class.
+     *
+     * @param component The full component name (i.e.
+     *            com.google.apps.calendar/com.google.apps.calendar.
+     *            CalendarAlarm) of a Receiver class.
+     * @param flags Additional option flags to modify the data returned.
+     * @return An {@link ActivityInfo} containing information about the
+     *         receiver.
+     * @throws NameNotFoundException if a package with the given name cannot be
+     *             found on the system.
+     */
+    @NonNull
+    public abstract ActivityInfo getReceiverInfo(@NonNull ComponentName component,
+            @ComponentInfoFlags int flags) throws NameNotFoundException;
+
+    /**
+     * Retrieve all of the information we know about a particular service class.
+     *
+     * @param component The full component name (i.e.
+     *            com.google.apps.media/com.google.apps.media.
+     *            BackgroundPlayback) of a Service class.
+     * @param flags Additional option flags to modify the data returned.
+     * @return A {@link ServiceInfo} object containing information about the
+     *         service.
+     * @throws NameNotFoundException if a package with the given name cannot be
+     *             found on the system.
+     */
+    @NonNull
+    public abstract ServiceInfo getServiceInfo(@NonNull ComponentName component,
+            @ComponentInfoFlags int flags) throws NameNotFoundException;
+
+    /**
+     * Retrieve all of the information we know about a particular content
+     * provider class.
+     *
+     * @param component The full component name (i.e.
+     *            com.google.providers.media/com.google.providers.media.
+     *            MediaProvider) of a ContentProvider class.
+     * @param flags Additional option flags to modify the data returned.
+     * @return A {@link ProviderInfo} object containing information about the
+     *         provider.
+     * @throws NameNotFoundException if a package with the given name cannot be
+     *             found on the system.
+     */
+    @NonNull
+    public abstract ProviderInfo getProviderInfo(@NonNull ComponentName component,
+            @ComponentInfoFlags int flags) throws NameNotFoundException;
+
+    /**
+     * Retrieve information for a particular module.
+     *
+     * @param packageName The name of the module.
+     * @param flags Additional option flags to modify the data returned.
+     * @return A {@link ModuleInfo} object containing information about the
+     *         module.
+     * @throws NameNotFoundException if a module with the given name cannot be
+     *             found on the system.
+     */
+    @NonNull
+    public ModuleInfo getModuleInfo(@NonNull String packageName, @ModuleInfoFlags int flags)
+            throws NameNotFoundException {
+        throw new UnsupportedOperationException(
+                "getModuleInfo not implemented in subclass");
+    }
+
+    /**
+     * Return a List of all modules that are installed.
+     *
+     * @param flags Additional option flags to modify the data returned.
+     * @return A {@link List} of {@link ModuleInfo} objects, one for each installed
+     *         module, containing information about the module. In the unlikely case
+     *         there are no installed modules, an empty list is returned.
+     */
+    @NonNull
+    public List<ModuleInfo> getInstalledModules(@InstalledModulesFlags int flags) {
+        throw new UnsupportedOperationException(
+                "getInstalledModules not implemented in subclass");
+    }
+
+    /**
+     * Return a List of all packages that are installed for the current user.
+     *
+     * @param flags Additional option flags to modify the data returned.
+     * @return A List of PackageInfo objects, one for each installed package,
+     *         containing information about the package. In the unlikely case
+     *         there are no installed packages, an empty list is returned. If
+     *         flag {@code MATCH_UNINSTALLED_PACKAGES} is set, the package
+     *         information is retrieved from the list of uninstalled
+     *         applications (which includes installed applications as well as
+     *         applications with data directory i.e. applications which had been
+     *         deleted with {@code DELETE_KEEP_DATA} flag set).
+     */
+    @NonNull
+    public abstract List<PackageInfo> getInstalledPackages(@PackageInfoFlags int flags);
+
+    /**
+     * Return a List of all installed packages that are currently holding any of
+     * the given permissions.
+     *
+     * @param flags Additional option flags to modify the data returned.
+     * @return A List of PackageInfo objects, one for each installed package
+     *         that holds any of the permissions that were provided, containing
+     *         information about the package. If no installed packages hold any
+     *         of the permissions, an empty list is returned. If flag
+     *         {@code MATCH_UNINSTALLED_PACKAGES} is set, the package
+     *         information is retrieved from the list of uninstalled
+     *         applications (which includes installed applications as well as
+     *         applications with data directory i.e. applications which had been
+     *         deleted with {@code DELETE_KEEP_DATA} flag set).
+     */
+    @NonNull
+    public abstract List<PackageInfo> getPackagesHoldingPermissions(
+            @NonNull String[] permissions, @PackageInfoFlags int flags);
+
+    /**
+     * Return a List of all packages that are installed on the device, for a
+     * specific user.
+     *
+     * @param flags Additional option flags to modify the data returned.
+     * @param userId The user for whom the installed packages are to be listed
+     * @return A List of PackageInfo objects, one for each installed package,
+     *         containing information about the package. In the unlikely case
+     *         there are no installed packages, an empty list is returned. If
+     *         flag {@code MATCH_UNINSTALLED_PACKAGES} is set, the package
+     *         information is retrieved from the list of uninstalled
+     *         applications (which includes installed applications as well as
+     *         applications with data directory i.e. applications which had been
+     *         deleted with {@code DELETE_KEEP_DATA} flag set).
+     * @hide
+     */
+    @NonNull
+    @TestApi
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+    public abstract List<PackageInfo> getInstalledPackagesAsUser(@PackageInfoFlags int flags,
+            @UserIdInt int userId);
+
+    /**
+     * Check whether a particular package has been granted a particular
+     * permission.
+     *
+     * @param permName The name of the permission you are checking for.
+     * @param packageName The name of the package you are checking against.
+     *
+     * @return If the package has the permission, PERMISSION_GRANTED is
+     * returned.  If it does not have the permission, PERMISSION_DENIED
+     * is returned.
+     *
+     * @see #PERMISSION_GRANTED
+     * @see #PERMISSION_DENIED
+     */
+    @CheckResult
+    @PermissionResult
+    public abstract int checkPermission(@NonNull String permName,
+            @NonNull String packageName);
+
+    /**
+     * Checks whether a particular permissions has been revoked for a
+     * package by policy. Typically the device owner or the profile owner
+     * may apply such a policy. The user cannot grant policy revoked
+     * permissions, hence the only way for an app to get such a permission
+     * is by a policy change.
+     *
+     * @param permName The name of the permission you are checking for.
+     * @param packageName The name of the package you are checking against.
+     *
+     * @return Whether the permission is restricted by policy.
+     */
+    @CheckResult
+    public abstract boolean isPermissionRevokedByPolicy(@NonNull String permName,
+            @NonNull String packageName);
+
+    /**
+     * Gets the package name of the component controlling runtime permissions.
+     *
+     * @return The package name.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    @NonNull
+    @TestApi
+    public abstract String getPermissionControllerPackageName();
+
+    /**
+     * Add a new dynamic permission to the system.  For this to work, your
+     * package must have defined a permission tree through the
+     * {@link android.R.styleable#AndroidManifestPermissionTree
+     * &lt;permission-tree&gt;} tag in its manifest.  A package can only add
+     * permissions to trees that were defined by either its own package or
+     * another with the same user id; a permission is in a tree if it
+     * matches the name of the permission tree + ".": for example,
+     * "com.foo.bar" is a member of the permission tree "com.foo".
+     *
+     * <p>It is good to make your permission tree name descriptive, because you
+     * are taking possession of that entire set of permission names.  Thus, it
+     * must be under a domain you control, with a suffix that will not match
+     * any normal permissions that may be declared in any applications that
+     * are part of that domain.
+     *
+     * <p>New permissions must be added before
+     * any .apks are installed that use those permissions.  Permissions you
+     * add through this method are remembered across reboots of the device.
+     * If the given permission already exists, the info you supply here
+     * will be used to update it.
+     *
+     * @param info Description of the permission to be added.
+     *
+     * @return Returns true if a new permission was created, false if an
+     * existing one was updated.
+     *
+     * @throws SecurityException if you are not allowed to add the
+     * given permission name.
+     *
+     * @see #removePermission(String)
+     */
+    public abstract boolean addPermission(@NonNull PermissionInfo info);
+
+    /**
+     * Like {@link #addPermission(PermissionInfo)} but asynchronously
+     * persists the package manager state after returning from the call,
+     * allowing it to return quicker and batch a series of adds at the
+     * expense of no guarantee the added permission will be retained if
+     * the device is rebooted before it is written.
+     */
+    public abstract boolean addPermissionAsync(@NonNull PermissionInfo info);
+
+    /**
+     * Removes a permission that was previously added with
+     * {@link #addPermission(PermissionInfo)}.  The same ownership rules apply
+     * -- you are only allowed to remove permissions that you are allowed
+     * to add.
+     *
+     * @param permName The name of the permission to remove.
+     *
+     * @throws SecurityException if you are not allowed to remove the
+     * given permission name.
+     *
+     * @see #addPermission(PermissionInfo)
+     */
+    public abstract void removePermission(@NonNull String permName);
+
+    /**
+     * Permission flags set when granting or revoking a permission.
+     *
+     * @hide
+     */
+    @SystemApi
+    @IntDef(prefix = { "FLAG_PERMISSION_" }, value = {
+            FLAG_PERMISSION_USER_SET,
+            FLAG_PERMISSION_USER_FIXED,
+            FLAG_PERMISSION_POLICY_FIXED,
+            FLAG_PERMISSION_REVOKE_ON_UPGRADE,
+            FLAG_PERMISSION_SYSTEM_FIXED,
+            FLAG_PERMISSION_GRANTED_BY_DEFAULT,
+            FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED,
+            FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED,
+            /*
+            FLAG_PERMISSION_REVOKE_WHEN_REQUESED
+            */
+            FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT,
+            FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT,
+            FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT,
+            FLAG_PERMISSION_APPLY_RESTRICTION,
+            FLAG_PERMISSION_GRANTED_BY_ROLE,
+            FLAG_PERMISSION_REVOKED_COMPAT,
+            FLAG_PERMISSION_ONE_TIME,
+            FLAG_PERMISSION_AUTO_REVOKED
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface PermissionFlags {}
+
+    /**
+     * Grant a runtime permission to an application which the application does not
+     * already have. The permission must have been requested by the application.
+     * If the application is not allowed to hold the permission, a {@link
+     * java.lang.SecurityException} is thrown. If the package or permission is
+     * invalid, a {@link java.lang.IllegalArgumentException} is thrown.
+     * <p>
+     * <strong>Note: </strong>Using this API requires holding
+     * android.permission.GRANT_RUNTIME_PERMISSIONS and if the user id is
+     * not the current user android.permission.INTERACT_ACROSS_USERS_FULL.
+     * </p>
+     *
+     * @param packageName The package to which to grant the permission.
+     * @param permName The permission name to grant.
+     * @param user The user for which to grant the permission.
+     *
+     * @see #revokeRuntimePermission(String, String, android.os.UserHandle)
+     *
+     * @hide
+     */
+    @TestApi
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS)
+    public abstract void grantRuntimePermission(@NonNull String packageName,
+            @NonNull String permName, @NonNull UserHandle user);
+
+    /**
+     * Revoke a runtime permission that was previously granted by {@link
+     * #grantRuntimePermission(String, String, android.os.UserHandle)}. The
+     * permission must have been requested by and granted to the application.
+     * If the application is not allowed to hold the permission, a {@link
+     * java.lang.SecurityException} is thrown. If the package or permission is
+     * invalid, a {@link java.lang.IllegalArgumentException} is thrown.
+     * <p>
+     * <strong>Note: </strong>Using this API requires holding
+     * android.permission.REVOKE_RUNTIME_PERMISSIONS and if the user id is
+     * not the current user android.permission.INTERACT_ACROSS_USERS_FULL.
+     * </p>
+     *
+     * @param packageName The package from which to revoke the permission.
+     * @param permName The permission name to revoke.
+     * @param user The user for which to revoke the permission.
+     *
+     * @see #grantRuntimePermission(String, String, android.os.UserHandle)
+     *
+     * @hide
+     */
+    @TestApi
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS)
+    public abstract void revokeRuntimePermission(@NonNull String packageName,
+            @NonNull String permName, @NonNull UserHandle user);
+
+    /**
+     * Revoke a runtime permission that was previously granted by {@link
+     * #grantRuntimePermission(String, String, android.os.UserHandle)}. The
+     * permission must have been requested by and granted to the application.
+     * If the application is not allowed to hold the permission, a {@link
+     * java.lang.SecurityException} is thrown. If the package or permission is
+     * invalid, a {@link java.lang.IllegalArgumentException} is thrown.
+     * <p>
+     * <strong>Note: </strong>Using this API requires holding
+     * android.permission.REVOKE_RUNTIME_PERMISSIONS and if the user id is
+     * not the current user android.permission.INTERACT_ACROSS_USERS_FULL.
+     * </p>
+     *
+     * @param packageName The package from which to revoke the permission.
+     * @param permName The permission name to revoke.
+     * @param user The user for which to revoke the permission.
+     * @param reason The reason for the revoke.
+     *
+     * @see #grantRuntimePermission(String, String, android.os.UserHandle)
+     *
+     * @hide
+     */
+    @TestApi
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS)
+    public void revokeRuntimePermission(@NonNull String packageName,
+            @NonNull String permName, @NonNull UserHandle user, @NonNull String reason) {
+        revokeRuntimePermission(packageName, permName, user);
+    }
+
+    /**
+     * Gets the state flags associated with a permission.
+     *
+     * @param permName The permission for which to get the flags.
+     * @param packageName The package name for which to get the flags.
+     * @param user The user for which to get permission flags.
+     * @return The permission flags.
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
+            android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS,
+            android.Manifest.permission.GET_RUNTIME_PERMISSIONS
+    })
+    @PermissionFlags
+    public abstract int getPermissionFlags(@NonNull String permName,
+            @NonNull String packageName, @NonNull UserHandle user);
+
+    /**
+     * Updates the flags associated with a permission by replacing the flags in
+     * the specified mask with the provided flag values.
+     *
+     * @param permName The permission for which to update the flags.
+     * @param packageName The package name for which to update the flags.
+     * @param flagMask The flags which to replace.
+     * @param flagValues The flags with which to replace.
+     * @param user The user for which to update the permission flags.
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
+            android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS
+    })
+    public abstract void updatePermissionFlags(@NonNull String permName,
+            @NonNull String packageName, @PermissionFlags int flagMask,
+            @PermissionFlags int flagValues, @NonNull UserHandle user);
+
+    /**
+     * Gets the restricted permissions that have been whitelisted and the app
+     * is allowed to have them granted in their full form.
+     *
+     * <p> Permissions can be hard restricted which means that the app cannot hold
+     * them or soft restricted where the app can hold the permission but in a weaker
+     * form. Whether a permission is {@link PermissionInfo#FLAG_HARD_RESTRICTED hard
+     * restricted} or {@link PermissionInfo#FLAG_SOFT_RESTRICTED soft restricted}
+     * depends on the permission declaration. Whitelisting a hard restricted permission
+     * allows for the to hold that permission and whitelisting a soft restricted
+     * permission allows the app to hold the permission in its full, unrestricted form.
+     *
+     * <p><ol>There are three whitelists:
+     *
+     * <li>one for cases where the system permission policy whitelists a permission
+     * This list corresponds to the{@link #FLAG_PERMISSION_WHITELIST_SYSTEM} flag.
+     * Can only be accessed by pre-installed holders of a dedicated permission.
+     *
+     * <li>one for cases where the system whitelists the permission when upgrading
+     * from an OS version in which the permission was not restricted to an OS version
+     * in which the permission is restricted. This list corresponds to the {@link
+     * #FLAG_PERMISSION_WHITELIST_UPGRADE} flag. Can be accessed by pre-installed
+     * holders of a dedicated permission or the installer on record.
+     *
+     * <li>one for cases where the installer of the package whitelists a permission.
+     * This list corresponds to the {@link #FLAG_PERMISSION_WHITELIST_INSTALLER} flag.
+     * Can be accessed by pre-installed holders of a dedicated permission or the
+     * installer on record.
+     *
+     * @param packageName The app for which to get whitelisted permissions.
+     * @param whitelistFlag The flag to determine which whitelist to query. Only one flag
+     * can be passed.s
+     * @return The whitelisted permissions that are on any of the whitelists you query for.
+     *
+     * @see #addWhitelistedRestrictedPermission(String, String, int)
+     * @see #removeWhitelistedRestrictedPermission(String, String, int)
+     * @see #FLAG_PERMISSION_WHITELIST_SYSTEM
+     * @see #FLAG_PERMISSION_WHITELIST_UPGRADE
+     * @see #FLAG_PERMISSION_WHITELIST_INSTALLER
+     *
+     * @throws SecurityException if you try to access a whitelist that you have no access to.
+     */
+    @RequiresPermission(value = Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS,
+            conditional = true)
+    public @NonNull Set<String> getWhitelistedRestrictedPermissions(
+            @NonNull String packageName, @PermissionWhitelistFlags int whitelistFlag) {
+        return Collections.emptySet();
+    }
+
+    /**
+     * Adds a whitelisted restricted permission for an app.
+     *
+     * <p> Permissions can be hard restricted which means that the app cannot hold
+     * them or soft restricted where the app can hold the permission but in a weaker
+     * form. Whether a permission is {@link PermissionInfo#FLAG_HARD_RESTRICTED hard
+     * restricted} or {@link PermissionInfo#FLAG_SOFT_RESTRICTED soft restricted}
+     * depends on the permission declaration. Whitelisting a hard restricted permission
+     * allows for the to hold that permission and whitelisting a soft restricted
+     * permission allows the app to hold the permission in its full, unrestricted form.
+     *
+     * <p><ol>There are three whitelists:
+     *
+     * <li>one for cases where the system permission policy whitelists a permission
+     * This list corresponds to the {@link #FLAG_PERMISSION_WHITELIST_SYSTEM} flag.
+     * Can only be modified by pre-installed holders of a dedicated permission.
+     *
+     * <li>one for cases where the system whitelists the permission when upgrading
+     * from an OS version in which the permission was not restricted to an OS version
+     * in which the permission is restricted. This list corresponds to the {@link
+     * #FLAG_PERMISSION_WHITELIST_UPGRADE} flag. Can be modified by pre-installed
+     * holders of a dedicated permission. The installer on record can only remove
+     * permissions from this whitelist.
+     *
+     * <li>one for cases where the installer of the package whitelists a permission.
+     * This list corresponds to the {@link #FLAG_PERMISSION_WHITELIST_INSTALLER} flag.
+     * Can be modified by pre-installed holders of a dedicated permission or the installer
+     * on record.
+     *
+     * <p>You need to specify the whitelists for which to set the whitelisted permissions
+     * which will clear the previous whitelisted permissions and replace them with the
+     * provided ones.
+     *
+     * @param packageName The app for which to get whitelisted permissions.
+     * @param permName The whitelisted permission to add.
+     * @param whitelistFlags The whitelists to which to add. Passing multiple flags
+     * updates all specified whitelists.
+     * @return Whether the permission was added to the whitelist.
+     *
+     * @see #getWhitelistedRestrictedPermissions(String, int)
+     * @see #removeWhitelistedRestrictedPermission(String, String, int)
+     * @see #FLAG_PERMISSION_WHITELIST_SYSTEM
+     * @see #FLAG_PERMISSION_WHITELIST_UPGRADE
+     * @see #FLAG_PERMISSION_WHITELIST_INSTALLER
+     *
+     * @throws SecurityException if you try to modify a whitelist that you have no access to.
+     */
+    @RequiresPermission(value = Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS,
+            conditional = true)
+    public boolean addWhitelistedRestrictedPermission(@NonNull String packageName,
+            @NonNull String permName, @PermissionWhitelistFlags int whitelistFlags) {
+        return false;
+    }
+
+    /**
+     * Removes a whitelisted restricted permission for an app.
+     *
+     * <p> Permissions can be hard restricted which means that the app cannot hold
+     * them or soft restricted where the app can hold the permission but in a weaker
+     * form. Whether a permission is {@link PermissionInfo#FLAG_HARD_RESTRICTED hard
+     * restricted} or {@link PermissionInfo#FLAG_SOFT_RESTRICTED soft restricted}
+     * depends on the permission declaration. Whitelisting a hard restricted permission
+     * allows for the to hold that permission and whitelisting a soft restricted
+     * permission allows the app to hold the permission in its full, unrestricted form.
+     *
+     * <p><ol>There are three whitelists:
+     *
+     * <li>one for cases where the system permission policy whitelists a permission
+     * This list corresponds to the {@link #FLAG_PERMISSION_WHITELIST_SYSTEM} flag.
+     * Can only be modified by pre-installed holders of a dedicated permission.
+     *
+     * <li>one for cases where the system whitelists the permission when upgrading
+     * from an OS version in which the permission was not restricted to an OS version
+     * in which the permission is restricted. This list corresponds to the {@link
+     * #FLAG_PERMISSION_WHITELIST_UPGRADE} flag. Can be modified by pre-installed
+     * holders of a dedicated permission. The installer on record can only remove
+     * permissions from this whitelist.
+     *
+     * <li>one for cases where the installer of the package whitelists a permission.
+     * This list corresponds to the {@link #FLAG_PERMISSION_WHITELIST_INSTALLER} flag.
+     * Can be modified by pre-installed holders of a dedicated permission or the installer
+     * on record.
+     *
+     * <p>You need to specify the whitelists for which to set the whitelisted permissions
+     * which will clear the previous whitelisted permissions and replace them with the
+     * provided ones.
+     *
+     * @param packageName The app for which to get whitelisted permissions.
+     * @param permName The whitelisted permission to remove.
+     * @param whitelistFlags The whitelists from which to remove. Passing multiple flags
+     * updates all specified whitelists.
+     * @return Whether the permission was removed from the whitelist.
+     *
+     * @see #getWhitelistedRestrictedPermissions(String, int)
+     * @see #addWhitelistedRestrictedPermission(String, String, int)
+     * @see #FLAG_PERMISSION_WHITELIST_SYSTEM
+     * @see #FLAG_PERMISSION_WHITELIST_UPGRADE
+     * @see #FLAG_PERMISSION_WHITELIST_INSTALLER
+     *
+     * @throws SecurityException if you try to modify a whitelist that you have no access to.
+     */
+    @RequiresPermission(value = Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS,
+        conditional = true)
+    public boolean removeWhitelistedRestrictedPermission(@NonNull String packageName,
+            @NonNull String permName, @PermissionWhitelistFlags int whitelistFlags) {
+        return false;
+    }
+
+    /**
+     * Marks an application exempt from having its permissions be automatically revoked when
+     * the app is unused for an extended period of time.
+     *
+     * Only the installer on record that installed the given package, or a holder of
+     * {@code WHITELIST_AUTO_REVOKE_PERMISSIONS} is allowed to call this.
+     *
+     * Packages start in whitelisted state, and it is the installer's responsibility to
+     * un-whitelist the packages it installs, unless auto-revoking permissions from that package
+     * would cause breakages beyond having to re-request the permission(s).
+     *
+     * @param packageName The app for which to set exemption.
+     * @param whitelisted Whether the app should be whitelisted.
+     *
+     * @return whether any change took effect.
+     *
+     * @see #isAutoRevokeWhitelisted
+     *
+     * @throws SecurityException if you you have no access to modify this.
+     */
+    @RequiresPermission(value = Manifest.permission.WHITELIST_AUTO_REVOKE_PERMISSIONS,
+            conditional = true)
+    public boolean setAutoRevokeWhitelisted(@NonNull String packageName, boolean whitelisted) {
+        return false;
+    }
+
+    /**
+     * Checks whether an application is exempt from having its permissions be automatically revoked
+     * when the app is unused for an extended period of time.
+     *
+     * Only the installer on record that installed the given package, or a holder of
+     * {@code WHITELIST_AUTO_REVOKE_PERMISSIONS} is allowed to call this.
+     * @param packageName The app for which to set exemption.
+     *
+     * @return Whether the app is whitelisted.
+     *
+     * @see #setAutoRevokeWhitelisted
+     *
+     * @throws SecurityException if you you have no access to this.
+     */
+    @RequiresPermission(value = Manifest.permission.WHITELIST_AUTO_REVOKE_PERMISSIONS,
+            conditional = true)
+    public boolean isAutoRevokeWhitelisted(@NonNull String packageName) {
+        return false;
+    }
+
+
+    /**
+     * Gets whether you should show UI with rationale for requesting a permission.
+     * You should do this only if you do not have the permission and the context in
+     * which the permission is requested does not clearly communicate to the user
+     * what would be the benefit from grating this permission.
+     *
+     * @param permName A permission your app wants to request.
+     * @return Whether you can show permission rationale UI.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public abstract boolean shouldShowRequestPermissionRationale(@NonNull String permName);
+
+    /**
+     * Gets the localized label that corresponds to the option in settings for granting
+     * background access.
+     *
+     * <p>The intended use is for apps to reference this label in its instruction for users to grant
+     * a background permission.
+     *
+     * @return the localized label that corresponds to the settings option for granting
+     * background access
+     */
+    @NonNull
+    public CharSequence getBackgroundPermissionOptionLabel() {
+        return "";
+    }
+
+    /**
+     * Returns an {@link android.content.Intent} suitable for passing to
+     * {@link android.app.Activity#startActivityForResult(android.content.Intent, int)}
+     * which prompts the user to grant permissions to this application.
+     *
+     * @throws NullPointerException if {@code permissions} is {@code null} or empty.
+     *
+     * @hide
+     */
+    @NonNull
+    @UnsupportedAppUsage
+    public Intent buildRequestPermissionsIntent(@NonNull String[] permissions) {
+        if (ArrayUtils.isEmpty(permissions)) {
+           throw new IllegalArgumentException("permission cannot be null or empty");
+        }
+        Intent intent = new Intent(ACTION_REQUEST_PERMISSIONS);
+        intent.putExtra(EXTRA_REQUEST_PERMISSIONS_NAMES, permissions);
+        intent.setPackage(getPermissionControllerPackageName());
+        return intent;
+    }
+
+    /**
+     * Compare the signatures of two packages to determine if the same
+     * signature appears in both of them.  If they do contain the same
+     * signature, then they are allowed special privileges when working
+     * with each other: they can share the same user-id, run instrumentation
+     * against each other, etc.
+     *
+     * @param packageName1 First package name whose signature will be compared.
+     * @param packageName2 Second package name whose signature will be compared.
+     *
+     * @return Returns an integer indicating whether all signatures on the
+     * two packages match. The value is >= 0 ({@link #SIGNATURE_MATCH}) if
+     * all signatures match or < 0 if there is not a match ({@link
+     * #SIGNATURE_NO_MATCH} or {@link #SIGNATURE_UNKNOWN_PACKAGE}).
+     *
+     * @see #checkSignatures(int, int)
+     */
+    @CheckResult
+    @SignatureResult
+    public abstract int checkSignatures(@NonNull String packageName1,
+            @NonNull String packageName2);
+
+    /**
+     * Like {@link #checkSignatures(String, String)}, but takes UIDs of
+     * the two packages to be checked.  This can be useful, for example,
+     * when doing the check in an IPC, where the UID is the only identity
+     * available.  It is functionally identical to determining the package
+     * associated with the UIDs and checking their signatures.
+     *
+     * @param uid1 First UID whose signature will be compared.
+     * @param uid2 Second UID whose signature will be compared.
+     *
+     * @return Returns an integer indicating whether all signatures on the
+     * two packages match. The value is >= 0 ({@link #SIGNATURE_MATCH}) if
+     * all signatures match or < 0 if there is not a match ({@link
+     * #SIGNATURE_NO_MATCH} or {@link #SIGNATURE_UNKNOWN_PACKAGE}).
+     *
+     * @see #checkSignatures(String, String)
+     */
+    @CheckResult
+    public abstract @SignatureResult int checkSignatures(int uid1, int uid2);
+
+    /**
+     * Retrieve the names of all packages that are associated with a particular
+     * user id.  In most cases, this will be a single package name, the package
+     * that has been assigned that user id.  Where there are multiple packages
+     * sharing the same user id through the "sharedUserId" mechanism, all
+     * packages with that id will be returned.
+     *
+     * @param uid The user id for which you would like to retrieve the
+     * associated packages.
+     *
+     * @return Returns an array of one or more packages assigned to the user
+     * id, or null if there are no known packages with the given id.
+     */
+    public abstract @Nullable String[] getPackagesForUid(int uid);
+
+    /**
+     * Retrieve the official name associated with a uid. This name is
+     * guaranteed to never change, though it is possible for the underlying
+     * uid to be changed.  That is, if you are storing information about
+     * uids in persistent storage, you should use the string returned
+     * by this function instead of the raw uid.
+     *
+     * @param uid The uid for which you would like to retrieve a name.
+     * @return Returns a unique name for the given uid, or null if the
+     * uid is not currently assigned.
+     */
+    public abstract @Nullable String getNameForUid(int uid);
+
+    /**
+     * Retrieves the official names associated with each given uid.
+     * @see #getNameForUid(int)
+     *
+     * @hide
+     */
+    @TestApi
+    public abstract @Nullable String[] getNamesForUids(int[] uids);
+
+    /**
+     * Return the user id associated with a shared user name. Multiple
+     * applications can specify a shared user name in their manifest and thus
+     * end up using a common uid. This might be used for new applications
+     * that use an existing shared user name and need to know the uid of the
+     * shared user.
+     *
+     * @param sharedUserName The shared user name whose uid is to be retrieved.
+     * @return Returns the UID associated with the shared user.
+     * @throws NameNotFoundException if a package with the given name cannot be
+     *             found on the system.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public abstract int getUidForSharedUser(@NonNull String sharedUserName)
+            throws NameNotFoundException;
+
+    /**
+     * Return a List of all application packages that are installed for the
+     * current user. If flag GET_UNINSTALLED_PACKAGES has been set, a list of all
+     * applications including those deleted with {@code DELETE_KEEP_DATA}
+     * (partially installed apps with data directory) will be returned.
+     *
+     * @param flags Additional option flags to modify the data returned.
+     * @return A List of ApplicationInfo objects, one for each installed
+     *         application. In the unlikely case there are no installed
+     *         packages, an empty list is returned. If flag
+     *         {@code MATCH_UNINSTALLED_PACKAGES} is set, the application
+     *         information is retrieved from the list of uninstalled
+     *         applications (which includes installed applications as well as
+     *         applications with data directory i.e. applications which had been
+     *         deleted with {@code DELETE_KEEP_DATA} flag set).
+     */
+    @NonNull
+    public abstract List<ApplicationInfo> getInstalledApplications(@ApplicationInfoFlags int flags);
+
+    /**
+     * Return a List of all application packages that are installed on the
+     * device, for a specific user. If flag GET_UNINSTALLED_PACKAGES has been
+     * set, a list of all applications including those deleted with
+     * {@code DELETE_KEEP_DATA} (partially installed apps with data directory)
+     * will be returned.
+     *
+     * @param flags Additional option flags to modify the data returned.
+     * @param userId The user for whom the installed applications are to be
+     *            listed
+     * @return A List of ApplicationInfo objects, one for each installed
+     *         application. In the unlikely case there are no installed
+     *         packages, an empty list is returned. If flag
+     *         {@code MATCH_UNINSTALLED_PACKAGES} is set, the application
+     *         information is retrieved from the list of uninstalled
+     *         applications (which includes installed applications as well as
+     *         applications with data directory i.e. applications which had been
+     *         deleted with {@code DELETE_KEEP_DATA} flag set).
+     * @hide
+     */
+    @NonNull
+    @TestApi
+    public abstract List<ApplicationInfo> getInstalledApplicationsAsUser(
+            @ApplicationInfoFlags int flags, @UserIdInt int userId);
+
+    /**
+     * Gets the instant applications the user recently used.
+     *
+     * @return The instant app list.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.ACCESS_INSTANT_APPS)
+    public abstract @NonNull List<InstantAppInfo> getInstantApps();
+
+    /**
+     * Gets the icon for an instant application.
+     *
+     * @param packageName The app package name.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.ACCESS_INSTANT_APPS)
+    public abstract @Nullable Drawable getInstantAppIcon(String packageName);
+
+    /**
+     * Gets whether this application is an instant app.
+     *
+     * @return Whether caller is an instant app.
+     *
+     * @see #isInstantApp(String)
+     * @see #updateInstantAppCookie(byte[])
+     * @see #getInstantAppCookie()
+     * @see #getInstantAppCookieMaxBytes()
+     */
+    public abstract boolean isInstantApp();
+
+    /**
+     * Gets whether the given package is an instant app.
+     *
+     * @param packageName The package to check
+     * @return Whether the given package is an instant app.
+     *
+     * @see #isInstantApp()
+     * @see #updateInstantAppCookie(byte[])
+     * @see #getInstantAppCookie()
+     * @see #getInstantAppCookieMaxBytes()
+     * @see #clearInstantAppCookie()
+     */
+    public abstract boolean isInstantApp(@NonNull String packageName);
+
+    /**
+     * Gets the maximum size in bytes of the cookie data an instant app
+     * can store on the device.
+     *
+     * @return The max cookie size in bytes.
+     *
+     * @see #isInstantApp()
+     * @see #isInstantApp(String)
+     * @see #updateInstantAppCookie(byte[])
+     * @see #getInstantAppCookie()
+     * @see #clearInstantAppCookie()
+     */
+    public abstract int getInstantAppCookieMaxBytes();
+
+    /**
+     * deprecated
+     * @hide
+     */
+    public abstract int getInstantAppCookieMaxSize();
+
+    /**
+     * Gets the instant application cookie for this app. Non
+     * instant apps and apps that were instant but were upgraded
+     * to normal apps can still access this API. For instant apps
+     * this cookie is cached for some time after uninstall while for
+     * normal apps the cookie is deleted after the app is uninstalled.
+     * The cookie is always present while the app is installed.
+     *
+     * @return The cookie.
+     *
+     * @see #isInstantApp()
+     * @see #isInstantApp(String)
+     * @see #updateInstantAppCookie(byte[])
+     * @see #getInstantAppCookieMaxBytes()
+     * @see #clearInstantAppCookie()
+     */
+    public abstract @NonNull byte[] getInstantAppCookie();
+
+    /**
+     * Clears the instant application cookie for the calling app.
+     *
+     * @see #isInstantApp()
+     * @see #isInstantApp(String)
+     * @see #getInstantAppCookieMaxBytes()
+     * @see #getInstantAppCookie()
+     * @see #clearInstantAppCookie()
+     */
+    public abstract void clearInstantAppCookie();
+
+    /**
+     * Updates the instant application cookie for the calling app. Non
+     * instant apps and apps that were instant but were upgraded
+     * to normal apps can still access this API. For instant apps
+     * this cookie is cached for some time after uninstall while for
+     * normal apps the cookie is deleted after the app is uninstalled.
+     * The cookie is always present while the app is installed. The
+     * cookie size is limited by {@link #getInstantAppCookieMaxBytes()}.
+     * Passing <code>null</code> or an empty array clears the cookie.
+     * </p>
+     *
+     * @param cookie The cookie data.
+     *
+     * @see #isInstantApp()
+     * @see #isInstantApp(String)
+     * @see #getInstantAppCookieMaxBytes()
+     * @see #getInstantAppCookie()
+     * @see #clearInstantAppCookie()
+     *
+     * @throws IllegalArgumentException if the array exceeds max cookie size.
+     */
+    public abstract void updateInstantAppCookie(@Nullable byte[] cookie);
+
+    /**
+     * @removed
+     */
+    public abstract boolean setInstantAppCookie(@Nullable byte[] cookie);
+
+    /**
+     * Get a list of shared libraries that are available on the
+     * system.
+     *
+     * @return An array of shared library names that are
+     * available on the system, or null if none are installed.
+     *
+     */
+    @Nullable
+    public abstract String[] getSystemSharedLibraryNames();
+
+    /**
+     * Get a list of shared libraries on the device.
+     *
+     * @param flags To filter the libraries to return.
+     * @return The shared library list.
+     *
+     * @see #MATCH_UNINSTALLED_PACKAGES
+     */
+    public abstract @NonNull List<SharedLibraryInfo> getSharedLibraries(
+            @InstallFlags int flags);
+
+    /**
+     * Get a list of shared libraries on the device.
+     *
+     * @param flags To filter the libraries to return.
+     * @param userId The user to query for.
+     * @return The shared library list.
+     *
+     * @see #MATCH_FACTORY_ONLY
+     * @see #MATCH_KNOWN_PACKAGES
+     * @see #MATCH_ANY_USER
+     * @see #MATCH_UNINSTALLED_PACKAGES
+     *
+     * @hide
+     */
+    public abstract @NonNull List<SharedLibraryInfo> getSharedLibrariesAsUser(
+            @InstallFlags int flags, @UserIdInt int userId);
+
+    /**
+     * Get the list of shared libraries declared by a package.
+     *
+     * @param packageName the package name to query
+     * @param flags the flags to filter packages
+     * @return the shared library list
+     *
+     * @hide
+     */
+    @NonNull
+    @RequiresPermission(Manifest.permission.ACCESS_SHARED_LIBRARIES)
+    @SystemApi
+    public List<SharedLibraryInfo> getDeclaredSharedLibraries(@NonNull String packageName,
+            @InstallFlags int flags) {
+        throw new UnsupportedOperationException(
+                "getDeclaredSharedLibraries() not implemented in subclass");
+    }
+
+    /**
+     * Get the name of the package hosting the services shared library.
+     * <p>
+     * Note that this package is no longer a shared library since Android R. It is now a package
+     * that hosts for a bunch of updatable services that the system binds to.
+     *
+     * @return The library host package.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    @TestApi
+    public abstract @NonNull String getServicesSystemSharedLibraryPackageName();
+
+    /**
+     * Get the name of the package hosting the shared components shared library.
+     *
+     * @return The library host package.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    @TestApi
+    public abstract @NonNull String getSharedSystemSharedLibraryPackageName();
+
+    /**
+     * Returns the names of the packages that have been changed
+     * [eg. added, removed or updated] since the given sequence
+     * number.
+     * <p>If no packages have been changed, returns <code>null</code>.
+     * <p>The sequence number starts at <code>0</code> and is
+     * reset every boot.
+     * @param sequenceNumber The first sequence number for which to retrieve package changes.
+     * @see android.provider.Settings.Global#BOOT_COUNT
+     */
+    public abstract @Nullable ChangedPackages getChangedPackages(
+            @IntRange(from=0) int sequenceNumber);
+
+    /**
+     * Get a list of features that are available on the
+     * system.
+     *
+     * @return An array of FeatureInfo classes describing the features
+     * that are available on the system, or null if there are none(!!).
+     */
+    @NonNull
+    public abstract FeatureInfo[] getSystemAvailableFeatures();
+
+    /**
+     * Check whether the given feature name is one of the available features as
+     * returned by {@link #getSystemAvailableFeatures()}. This tests for the
+     * presence of <em>any</em> version of the given feature name; use
+     * {@link #hasSystemFeature(String, int)} to check for a minimum version.
+     *
+     * @return Returns true if the devices supports the feature, else false.
+     */
+    public abstract boolean hasSystemFeature(@NonNull String featureName);
+
+    /**
+     * Check whether the given feature name and version is one of the available
+     * features as returned by {@link #getSystemAvailableFeatures()}. Since
+     * features are defined to always be backwards compatible, this returns true
+     * if the available feature version is greater than or equal to the
+     * requested version.
+     *
+     * @return Returns true if the devices supports the feature, else false.
+     */
+    public abstract boolean hasSystemFeature(@NonNull String featureName, int version);
+
+    /**
+     * Determine the best action to perform for a given Intent. This is how
+     * {@link Intent#resolveActivity} finds an activity if a class has not been
+     * explicitly specified.
+     * <p>
+     * <em>Note:</em> if using an implicit Intent (without an explicit
+     * ComponentName specified), be sure to consider whether to set the
+     * {@link #MATCH_DEFAULT_ONLY} only flag. You need to do so to resolve the
+     * activity in the same way that
+     * {@link android.content.Context#startActivity(Intent)} and
+     * {@link android.content.Intent#resolveActivity(PackageManager)
+     * Intent.resolveActivity(PackageManager)} do.
+     * </p>
+     *
+     * @param intent An intent containing all of the desired specification
+     *            (action, data, type, category, and/or component).
+     * @param flags Additional option flags to modify the data returned. The
+     *            most important is {@link #MATCH_DEFAULT_ONLY}, to limit the
+     *            resolution to only those activities that support the
+     *            {@link android.content.Intent#CATEGORY_DEFAULT}.
+     * @return Returns a ResolveInfo object containing the final activity intent
+     *         that was determined to be the best action. Returns null if no
+     *         matching activity was found. If multiple matching activities are
+     *         found and there is no default set, returns a ResolveInfo object
+     *         containing something else, such as the activity resolver.
+     */
+    @Nullable
+    public abstract ResolveInfo resolveActivity(@NonNull Intent intent,
+            @ResolveInfoFlags int flags);
+
+    /**
+     * Determine the best action to perform for a given Intent for a given user.
+     * This is how {@link Intent#resolveActivity} finds an activity if a class
+     * has not been explicitly specified.
+     * <p>
+     * <em>Note:</em> if using an implicit Intent (without an explicit
+     * ComponentName specified), be sure to consider whether to set the
+     * {@link #MATCH_DEFAULT_ONLY} only flag. You need to do so to resolve the
+     * activity in the same way that
+     * {@link android.content.Context#startActivity(Intent)} and
+     * {@link android.content.Intent#resolveActivity(PackageManager)
+     * Intent.resolveActivity(PackageManager)} do.
+     * </p>
+     *
+     * @param intent An intent containing all of the desired specification
+     *            (action, data, type, category, and/or component).
+     * @param flags Additional option flags to modify the data returned. The
+     *            most important is {@link #MATCH_DEFAULT_ONLY}, to limit the
+     *            resolution to only those activities that support the
+     *            {@link android.content.Intent#CATEGORY_DEFAULT}.
+     * @param userId The user id.
+     * @return Returns a ResolveInfo object containing the final activity intent
+     *         that was determined to be the best action. Returns null if no
+     *         matching activity was found. If multiple matching activities are
+     *         found and there is no default set, returns a ResolveInfo object
+     *         containing something else, such as the activity resolver.
+     * @hide
+     */
+    @Nullable
+    @UnsupportedAppUsage
+    public abstract ResolveInfo resolveActivityAsUser(@NonNull Intent intent,
+            @ResolveInfoFlags int flags, @UserIdInt int userId);
+
+    /**
+     * Retrieve all activities that can be performed for the given intent.
+     *
+     * @param intent The desired intent as per resolveActivity().
+     * @param flags Additional option flags to modify the data returned. The
+     *            most important is {@link #MATCH_DEFAULT_ONLY}, to limit the
+     *            resolution to only those activities that support the
+     *            {@link android.content.Intent#CATEGORY_DEFAULT}. Or, set
+     *            {@link #MATCH_ALL} to prevent any filtering of the results.
+     * @return Returns a List of ResolveInfo objects containing one entry for
+     *         each matching activity, ordered from best to worst. In other
+     *         words, the first item is what would be returned by
+     *         {@link #resolveActivity}. If there are no matching activities, an
+     *         empty list is returned.
+     */
+    @NonNull
+    public abstract List<ResolveInfo> queryIntentActivities(@NonNull Intent intent,
+            @ResolveInfoFlags int flags);
+
+    /**
+     * Retrieve all activities that can be performed for the given intent, for a
+     * specific user.
+     *
+     * @param intent The desired intent as per resolveActivity().
+     * @param flags Additional option flags to modify the data returned. The
+     *            most important is {@link #MATCH_DEFAULT_ONLY}, to limit the
+     *            resolution to only those activities that support the
+     *            {@link android.content.Intent#CATEGORY_DEFAULT}. Or, set
+     *            {@link #MATCH_ALL} to prevent any filtering of the results.
+     * @return Returns a List of ResolveInfo objects containing one entry for
+     *         each matching activity, ordered from best to worst. In other
+     *         words, the first item is what would be returned by
+     *         {@link #resolveActivity}. If there are no matching activities, an
+     *         empty list is returned.
+     * @hide
+     */
+    @NonNull
+    @UnsupportedAppUsage
+    public abstract List<ResolveInfo> queryIntentActivitiesAsUser(@NonNull Intent intent,
+            @ResolveInfoFlags int flags, @UserIdInt int userId);
+
+    /**
+     * Retrieve all activities that can be performed for the given intent, for a
+     * specific user.
+     *
+     * @param intent The desired intent as per resolveActivity().
+     * @param flags Additional option flags to modify the data returned. The
+     *            most important is {@link #MATCH_DEFAULT_ONLY}, to limit the
+     *            resolution to only those activities that support the
+     *            {@link android.content.Intent#CATEGORY_DEFAULT}. Or, set
+     *            {@link #MATCH_ALL} to prevent any filtering of the results.
+     * @param user The user being queried.
+     * @return Returns a List of ResolveInfo objects containing one entry for
+     *         each matching activity, ordered from best to worst. In other
+     *         words, the first item is what would be returned by
+     *         {@link #resolveActivity}. If there are no matching activities, an
+     *         empty list is returned.
+     * @hide
+     */
+    @NonNull
+    @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
+    @SystemApi
+    public List<ResolveInfo> queryIntentActivitiesAsUser(@NonNull Intent intent,
+            @ResolveInfoFlags int flags, @NonNull UserHandle user) {
+        return queryIntentActivitiesAsUser(intent, flags, user.getIdentifier());
+    }
+
+    /**
+     * Retrieve a set of activities that should be presented to the user as
+     * similar options. This is like {@link #queryIntentActivities}, except it
+     * also allows you to supply a list of more explicit Intents that you would
+     * like to resolve to particular options, and takes care of returning the
+     * final ResolveInfo list in a reasonable order, with no duplicates, based
+     * on those inputs.
+     *
+     * @param caller The class name of the activity that is making the request.
+     *            This activity will never appear in the output list. Can be
+     *            null.
+     * @param specifics An array of Intents that should be resolved to the first
+     *            specific results. Can be null.
+     * @param intent The desired intent as per resolveActivity().
+     * @param flags Additional option flags to modify the data returned. The
+     *            most important is {@link #MATCH_DEFAULT_ONLY}, to limit the
+     *            resolution to only those activities that support the
+     *            {@link android.content.Intent#CATEGORY_DEFAULT}.
+     * @return Returns a List of ResolveInfo objects containing one entry for
+     *         each matching activity. The list is ordered first by all of the
+     *         intents resolved in <var>specifics</var> and then any additional
+     *         activities that can handle <var>intent</var> but did not get
+     *         included by one of the <var>specifics</var> intents. If there are
+     *         no matching activities, an empty list is returned.
+     */
+    @NonNull
+    public abstract List<ResolveInfo> queryIntentActivityOptions(@Nullable ComponentName caller,
+            @Nullable Intent[] specifics, @NonNull Intent intent, @ResolveInfoFlags int flags);
+
+    /**
+     * Retrieve all receivers that can handle a broadcast of the given intent.
+     *
+     * @param intent The desired intent as per resolveActivity().
+     * @param flags Additional option flags to modify the data returned.
+     * @return Returns a List of ResolveInfo objects containing one entry for
+     *         each matching receiver, ordered from best to worst. If there are
+     *         no matching receivers, an empty list or null is returned.
+     */
+    @NonNull
+    public abstract List<ResolveInfo> queryBroadcastReceivers(@NonNull Intent intent,
+            @ResolveInfoFlags int flags);
+
+    /**
+     * Retrieve all receivers that can handle a broadcast of the given intent,
+     * for a specific user.
+     *
+     * @param intent The desired intent as per resolveActivity().
+     * @param flags Additional option flags to modify the data returned.
+     * @param userHandle UserHandle of the user being queried.
+     * @return Returns a List of ResolveInfo objects containing one entry for
+     *         each matching receiver, ordered from best to worst. If there are
+     *         no matching receivers, an empty list or null is returned.
+     * @hide
+     */
+    @NonNull
+    @SystemApi
+    @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
+    public List<ResolveInfo> queryBroadcastReceiversAsUser(@NonNull Intent intent,
+            @ResolveInfoFlags int flags, UserHandle userHandle) {
+        return queryBroadcastReceiversAsUser(intent, flags, userHandle.getIdentifier());
+    }
+
+    /**
+     * @hide
+     */
+    @NonNull
+    @UnsupportedAppUsage
+    public abstract List<ResolveInfo> queryBroadcastReceiversAsUser(@NonNull Intent intent,
+            @ResolveInfoFlags int flags, @UserIdInt int userId);
+
+
+    /** @deprecated @hide */
+    @NonNull
+    @Deprecated
+    @UnsupportedAppUsage
+    public List<ResolveInfo> queryBroadcastReceivers(@NonNull Intent intent,
+            @ResolveInfoFlags int flags, @UserIdInt int userId) {
+        final String msg = "Shame on you for calling the hidden API "
+                + "queryBroadcastReceivers(). Shame!";
+        if (VMRuntime.getRuntime().getTargetSdkVersion() >= Build.VERSION_CODES.O) {
+            throw new UnsupportedOperationException(msg);
+        } else {
+            Log.d(TAG, msg);
+            return queryBroadcastReceiversAsUser(intent, flags, userId);
+        }
+    }
+
+    /**
+     * Determine the best service to handle for a given Intent.
+     *
+     * @param intent An intent containing all of the desired specification
+     *            (action, data, type, category, and/or component).
+     * @param flags Additional option flags to modify the data returned.
+     * @return Returns a ResolveInfo object containing the final service intent
+     *         that was determined to be the best action. Returns null if no
+     *         matching service was found.
+     */
+    @Nullable
+    public abstract ResolveInfo resolveService(@NonNull Intent intent, @ResolveInfoFlags int flags);
+
+    /**
+     * @hide
+     */
+    @Nullable
+    public abstract ResolveInfo resolveServiceAsUser(@NonNull Intent intent,
+            @ResolveInfoFlags int flags, @UserIdInt int userId);
+
+    /**
+     * Retrieve all services that can match the given intent.
+     *
+     * @param intent The desired intent as per resolveService().
+     * @param flags Additional option flags to modify the data returned.
+     * @return Returns a List of ResolveInfo objects containing one entry for
+     *         each matching service, ordered from best to worst. In other
+     *         words, the first item is what would be returned by
+     *         {@link #resolveService}. If there are no matching services, an
+     *         empty list or null is returned.
+     */
+    @NonNull
+    public abstract List<ResolveInfo> queryIntentServices(@NonNull Intent intent,
+            @ResolveInfoFlags int flags);
+
+    /**
+     * Retrieve all services that can match the given intent for a given user.
+     *
+     * @param intent The desired intent as per resolveService().
+     * @param flags Additional option flags to modify the data returned.
+     * @param userId The user id.
+     * @return Returns a List of ResolveInfo objects containing one entry for
+     *         each matching service, ordered from best to worst. In other
+     *         words, the first item is what would be returned by
+     *         {@link #resolveService}. If there are no matching services, an
+     *         empty list or null is returned.
+     * @hide
+     */
+    @NonNull
+    @UnsupportedAppUsage
+    public abstract List<ResolveInfo> queryIntentServicesAsUser(@NonNull Intent intent,
+            @ResolveInfoFlags int flags, @UserIdInt int userId);
+
+    /**
+     * Retrieve all services that can match the given intent for a given user.
+     *
+     * @param intent The desired intent as per resolveService().
+     * @param flags Additional option flags to modify the data returned.
+     * @param user The user being queried.
+     * @return Returns a List of ResolveInfo objects containing one entry for
+     *         each matching service, ordered from best to worst. In other
+     *         words, the first item is what would be returned by
+     *         {@link #resolveService}. If there are no matching services, an
+     *         empty list or null is returned.
+     * @hide
+     */
+    @NonNull
+    @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
+    @SystemApi
+    public List<ResolveInfo> queryIntentServicesAsUser(@NonNull Intent intent,
+            @ResolveInfoFlags int flags, @NonNull UserHandle user) {
+        return queryIntentServicesAsUser(intent, flags, user.getIdentifier());
+    }
+
+    /**
+     * Retrieve all providers that can match the given intent.
+     *
+     * @param intent An intent containing all of the desired specification
+     *            (action, data, type, category, and/or component).
+     * @param flags Additional option flags to modify the data returned.
+     * @param userId The user id.
+     * @return Returns a List of ResolveInfo objects containing one entry for
+     *         each matching provider, ordered from best to worst. If there are
+     *         no matching services, an empty list or null is returned.
+     * @hide
+     */
+    @NonNull
+    @UnsupportedAppUsage
+    public abstract List<ResolveInfo> queryIntentContentProvidersAsUser(
+            @NonNull Intent intent, @ResolveInfoFlags int flags, @UserIdInt int userId);
+
+    /**
+     * Retrieve all providers that can match the given intent.
+     *
+     * @param intent An intent containing all of the desired specification
+     *            (action, data, type, category, and/or component).
+     * @param flags Additional option flags to modify the data returned.
+     * @param user The user being queried.
+     * @return Returns a List of ResolveInfo objects containing one entry for
+     *         each matching provider, ordered from best to worst. If there are
+     *         no matching services, an empty list or null is returned.
+     * @hide
+     */
+    @NonNull
+    @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
+    @SystemApi
+    public List<ResolveInfo> queryIntentContentProvidersAsUser(@NonNull Intent intent,
+            @ResolveInfoFlags int flags, @NonNull UserHandle user) {
+        return queryIntentContentProvidersAsUser(intent, flags, user.getIdentifier());
+    }
+
+    /**
+     * Retrieve all providers that can match the given intent.
+     *
+     * @param intent An intent containing all of the desired specification
+     *            (action, data, type, category, and/or component).
+     * @param flags Additional option flags to modify the data returned.
+     * @return Returns a List of ResolveInfo objects containing one entry for
+     *         each matching provider, ordered from best to worst. If there are
+     *         no matching services, an empty list or null is returned.
+     */
+    @NonNull
+    public abstract List<ResolveInfo> queryIntentContentProviders(@NonNull Intent intent,
+            @ResolveInfoFlags int flags);
+
+    /**
+     * Find a single content provider by its authority.
+     * <p>
+     * Example:<p>
+     * <pre>
+     * Uri uri = Uri.parse("content://com.example.app.provider/table1");
+     * ProviderInfo info = packageManager.resolveContentProvider(uri.getAuthority(), flags);
+     * </pre>
+     *
+     * @param authority The authority of the provider to find.
+     * @param flags Additional option flags to modify the data returned.
+     * @return A {@link ProviderInfo} object containing information about the
+     *         provider. If a provider was not found, returns null.
+     */
+    @Nullable
+    public abstract ProviderInfo resolveContentProvider(@NonNull String authority,
+            @ComponentInfoFlags int flags);
+
+    /**
+     * Find a single content provider by its base path name.
+     *
+     * @param providerName The name of the provider to find.
+     * @param flags Additional option flags to modify the data returned.
+     * @param userId The user id.
+     * @return A {@link ProviderInfo} object containing information about the
+     *         provider. If a provider was not found, returns null.
+     * @hide
+     */
+    @Nullable
+    @UnsupportedAppUsage
+    public abstract ProviderInfo resolveContentProviderAsUser(@NonNull String providerName,
+            @ComponentInfoFlags int flags, @UserIdInt int userId);
+
+    /**
+     * Retrieve content provider information.
+     * <p>
+     * <em>Note: unlike most other methods, an empty result set is indicated
+     * by a null return instead of an empty list.</em>
+     *
+     * @param processName If non-null, limits the returned providers to only
+     *            those that are hosted by the given process. If null, all
+     *            content providers are returned.
+     * @param uid If <var>processName</var> is non-null, this is the required
+     *            uid owning the requested content providers.
+     * @param flags Additional option flags to modify the data returned.
+     * @return A list of {@link ProviderInfo} objects containing one entry for
+     *         each provider either matching <var>processName</var> or, if
+     *         <var>processName</var> is null, all known content providers.
+     *         <em>If there are no matching providers, null is returned.</em>
+     */
+    @NonNull
+    public abstract List<ProviderInfo> queryContentProviders(
+            @Nullable String processName, int uid, @ComponentInfoFlags int flags);
+
+    /**
+     * Same as {@link #queryContentProviders}, except when {@code metaDataKey} is not null,
+     * it only returns providers which have metadata with the {@code metaDataKey} key.
+     *
+     * <p>DO NOT USE the {@code metaDataKey} parameter, unless you're the contacts provider.
+     * You really shouldn't need it.  Other apps should use {@link #queryIntentContentProviders}
+     * instead.
+     *
+     * <p>The {@code metaDataKey} parameter was added to allow the contacts provider to quickly
+     * scan the GAL providers on the device.  Unfortunately the discovery protocol used metadata
+     * to mark GAL providers, rather than intent filters, so we can't use
+     * {@link #queryIntentContentProviders} for that.
+     *
+     * @hide
+     */
+    @NonNull
+    public List<ProviderInfo> queryContentProviders(@Nullable String processName,
+            int uid, @ComponentInfoFlags int flags, String metaDataKey) {
+        // Provide the default implementation for mocks.
+        return queryContentProviders(processName, uid, flags);
+    }
+
+    /**
+     * Retrieve all of the information we know about a particular
+     * instrumentation class.
+     *
+     * @param className The full name (i.e.
+     *            com.google.apps.contacts.InstrumentList) of an Instrumentation
+     *            class.
+     * @param flags Additional option flags to modify the data returned.
+     * @return An {@link InstrumentationInfo} object containing information
+     *         about the instrumentation.
+     * @throws NameNotFoundException if a package with the given name cannot be
+     *             found on the system.
+     */
+    @NonNull
+    public abstract InstrumentationInfo getInstrumentationInfo(@NonNull ComponentName className,
+            @InstrumentationInfoFlags int flags) throws NameNotFoundException;
+
+    /**
+     * Retrieve information about available instrumentation code. May be used to
+     * retrieve either all instrumentation code, or only the code targeting a
+     * particular package.
+     *
+     * @param targetPackage If null, all instrumentation is returned; only the
+     *            instrumentation targeting this package name is returned.
+     * @param flags Additional option flags to modify the data returned.
+     * @return A list of {@link InstrumentationInfo} objects containing one
+     *         entry for each matching instrumentation. If there are no
+     *         instrumentation available, returns an empty list.
+     */
+    @NonNull
+    public abstract List<InstrumentationInfo> queryInstrumentation(@NonNull String targetPackage,
+            @InstrumentationInfoFlags int flags);
+
+    /**
+     * Retrieve an image from a package.  This is a low-level API used by
+     * the various package manager info structures (such as
+     * {@link ComponentInfo} to implement retrieval of their associated
+     * icon.
+     *
+     * @param packageName The name of the package that this icon is coming from.
+     * Cannot be null.
+     * @param resid The resource identifier of the desired image.  Cannot be 0.
+     * @param appInfo Overall information about <var>packageName</var>.  This
+     * may be null, in which case the application information will be retrieved
+     * for you if needed; if you already have this information around, it can
+     * be much more efficient to supply it here.
+     *
+     * @return Returns a Drawable holding the requested image.  Returns null if
+     * an image could not be found for any reason.
+     */
+    @Nullable
+    public abstract Drawable getDrawable(@NonNull String packageName, @DrawableRes int resid,
+            @Nullable ApplicationInfo appInfo);
+
+    /**
+     * Retrieve the icon associated with an activity.  Given the full name of
+     * an activity, retrieves the information about it and calls
+     * {@link ComponentInfo#loadIcon ComponentInfo.loadIcon()} to return its icon.
+     * If the activity cannot be found, NameNotFoundException is thrown.
+     *
+     * @param activityName Name of the activity whose icon is to be retrieved.
+     *
+     * @return Returns the image of the icon, or the default activity icon if
+     * it could not be found.  Does not return null.
+     * @throws NameNotFoundException Thrown if the resources for the given
+     * activity could not be loaded.
+     *
+     * @see #getActivityIcon(Intent)
+     */
+    @NonNull
+    public abstract Drawable getActivityIcon(@NonNull ComponentName activityName)
+            throws NameNotFoundException;
+
+    /**
+     * Retrieve the icon associated with an Intent.  If intent.getClassName() is
+     * set, this simply returns the result of
+     * getActivityIcon(intent.getClassName()).  Otherwise it resolves the intent's
+     * component and returns the icon associated with the resolved component.
+     * If intent.getClassName() cannot be found or the Intent cannot be resolved
+     * to a component, NameNotFoundException is thrown.
+     *
+     * @param intent The intent for which you would like to retrieve an icon.
+     *
+     * @return Returns the image of the icon, or the default activity icon if
+     * it could not be found.  Does not return null.
+     * @throws NameNotFoundException Thrown if the resources for application
+     * matching the given intent could not be loaded.
+     *
+     * @see #getActivityIcon(ComponentName)
+     */
+    @NonNull
+    public abstract Drawable getActivityIcon(@NonNull Intent intent)
+            throws NameNotFoundException;
+
+    /**
+     * Retrieve the banner associated with an activity. Given the full name of
+     * an activity, retrieves the information about it and calls
+     * {@link ComponentInfo#loadIcon ComponentInfo.loadIcon()} to return its
+     * banner. If the activity cannot be found, NameNotFoundException is thrown.
+     *
+     * @param activityName Name of the activity whose banner is to be retrieved.
+     * @return Returns the image of the banner, or null if the activity has no
+     *         banner specified.
+     * @throws NameNotFoundException Thrown if the resources for the given
+     *             activity could not be loaded.
+     * @see #getActivityBanner(Intent)
+     */
+    @Nullable
+    public abstract Drawable getActivityBanner(@NonNull ComponentName activityName)
+            throws NameNotFoundException;
+
+    /**
+     * Retrieve the banner associated with an Intent. If intent.getClassName()
+     * is set, this simply returns the result of
+     * getActivityBanner(intent.getClassName()). Otherwise it resolves the
+     * intent's component and returns the banner associated with the resolved
+     * component. If intent.getClassName() cannot be found or the Intent cannot
+     * be resolved to a component, NameNotFoundException is thrown.
+     *
+     * @param intent The intent for which you would like to retrieve a banner.
+     * @return Returns the image of the banner, or null if the activity has no
+     *         banner specified.
+     * @throws NameNotFoundException Thrown if the resources for application
+     *             matching the given intent could not be loaded.
+     * @see #getActivityBanner(ComponentName)
+     */
+    @Nullable
+    public abstract Drawable getActivityBanner(@NonNull Intent intent)
+            throws NameNotFoundException;
+
+    /**
+     * Return the generic icon for an activity that is used when no specific
+     * icon is defined.
+     *
+     * @return Drawable Image of the icon.
+     */
+    @NonNull
+    public abstract Drawable getDefaultActivityIcon();
+
+    /**
+     * Retrieve the icon associated with an application.  If it has not defined
+     * an icon, the default app icon is returned.  Does not return null.
+     *
+     * @param info Information about application being queried.
+     *
+     * @return Returns the image of the icon, or the default application icon
+     * if it could not be found.
+     *
+     * @see #getApplicationIcon(String)
+     */
+    @NonNull
+    public abstract Drawable getApplicationIcon(@NonNull ApplicationInfo info);
+
+    /**
+     * Retrieve the icon associated with an application.  Given the name of the
+     * application's package, retrieves the information about it and calls
+     * getApplicationIcon() to return its icon. If the application cannot be
+     * found, NameNotFoundException is thrown.
+     *
+     * @param packageName Name of the package whose application icon is to be
+     *                    retrieved.
+     *
+     * @return Returns the image of the icon, or the default application icon
+     * if it could not be found.  Does not return null.
+     * @throws NameNotFoundException Thrown if the resources for the given
+     * application could not be loaded.
+     *
+     * @see #getApplicationIcon(ApplicationInfo)
+     */
+    @NonNull
+    public abstract Drawable getApplicationIcon(@NonNull String packageName)
+            throws NameNotFoundException;
+
+    /**
+     * Retrieve the banner associated with an application.
+     *
+     * @param info Information about application being queried.
+     * @return Returns the image of the banner or null if the application has no
+     *         banner specified.
+     * @see #getApplicationBanner(String)
+     */
+    @Nullable
+    public abstract Drawable getApplicationBanner(@NonNull ApplicationInfo info);
+
+    /**
+     * Retrieve the banner associated with an application. Given the name of the
+     * application's package, retrieves the information about it and calls
+     * getApplicationIcon() to return its banner. If the application cannot be
+     * found, NameNotFoundException is thrown.
+     *
+     * @param packageName Name of the package whose application banner is to be
+     *            retrieved.
+     * @return Returns the image of the banner or null if the application has no
+     *         banner specified.
+     * @throws NameNotFoundException Thrown if the resources for the given
+     *             application could not be loaded.
+     * @see #getApplicationBanner(ApplicationInfo)
+     */
+    @Nullable
+    public abstract Drawable getApplicationBanner(@NonNull String packageName)
+            throws NameNotFoundException;
+
+    /**
+     * Retrieve the logo associated with an activity. Given the full name of an
+     * activity, retrieves the information about it and calls
+     * {@link ComponentInfo#loadLogo ComponentInfo.loadLogo()} to return its
+     * logo. If the activity cannot be found, NameNotFoundException is thrown.
+     *
+     * @param activityName Name of the activity whose logo is to be retrieved.
+     * @return Returns the image of the logo or null if the activity has no logo
+     *         specified.
+     * @throws NameNotFoundException Thrown if the resources for the given
+     *             activity could not be loaded.
+     * @see #getActivityLogo(Intent)
+     */
+    @Nullable
+    public abstract Drawable getActivityLogo(@NonNull ComponentName activityName)
+            throws NameNotFoundException;
+
+    /**
+     * Retrieve the logo associated with an Intent.  If intent.getClassName() is
+     * set, this simply returns the result of
+     * getActivityLogo(intent.getClassName()).  Otherwise it resolves the intent's
+     * component and returns the logo associated with the resolved component.
+     * If intent.getClassName() cannot be found or the Intent cannot be resolved
+     * to a component, NameNotFoundException is thrown.
+     *
+     * @param intent The intent for which you would like to retrieve a logo.
+     *
+     * @return Returns the image of the logo, or null if the activity has no
+     * logo specified.
+     *
+     * @throws NameNotFoundException Thrown if the resources for application
+     * matching the given intent could not be loaded.
+     *
+     * @see #getActivityLogo(ComponentName)
+     */
+    @Nullable
+    public abstract Drawable getActivityLogo(@NonNull Intent intent)
+            throws NameNotFoundException;
+
+    /**
+     * Retrieve the logo associated with an application.  If it has not specified
+     * a logo, this method returns null.
+     *
+     * @param info Information about application being queried.
+     *
+     * @return Returns the image of the logo, or null if no logo is specified
+     * by the application.
+     *
+     * @see #getApplicationLogo(String)
+     */
+    @Nullable
+    public abstract Drawable getApplicationLogo(@NonNull ApplicationInfo info);
+
+    /**
+     * Retrieve the logo associated with an application.  Given the name of the
+     * application's package, retrieves the information about it and calls
+     * getApplicationLogo() to return its logo. If the application cannot be
+     * found, NameNotFoundException is thrown.
+     *
+     * @param packageName Name of the package whose application logo is to be
+     *                    retrieved.
+     *
+     * @return Returns the image of the logo, or null if no application logo
+     * has been specified.
+     *
+     * @throws NameNotFoundException Thrown if the resources for the given
+     * application could not be loaded.
+     *
+     * @see #getApplicationLogo(ApplicationInfo)
+     */
+    @Nullable
+    public abstract Drawable getApplicationLogo(@NonNull String packageName)
+            throws NameNotFoundException;
+
+    /**
+     * If the target user is a managed 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 #getUserBadgedDrawableForDensity(
+     * 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 drawable The drawable to badge.
+     * @param user The target user.
+     * @return A drawable that combines the original icon and a badge as
+     *         determined by the system.
+     */
+    @NonNull
+    public abstract Drawable getUserBadgedIcon(@NonNull Drawable drawable,
+            @NonNull UserHandle user);
+
+    /**
+     * If the target user is a managed profile of the calling user or the caller
+     * is itself a managed 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 drawable 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.
+     */
+    @NonNull
+    public abstract Drawable getUserBadgedDrawableForDensity(@NonNull Drawable drawable,
+            @NonNull UserHandle user, @Nullable Rect badgeLocation, int badgeDensity);
+
+    /**
+     * If the target user is a managed profile of the calling user or the caller
+     * is itself a managed profile, then this returns a drawable to use as a small
+     * icon to include in a view to distinguish it from the original icon.
+     *
+     * @param user The target user.
+     * @param density The optional desired density for the badge as per
+     *         {@link android.util.DisplayMetrics#densityDpi}. If not provided
+     *         the density of the current display is used.
+     * @return the drawable or null if no drawable is required.
+     * @hide
+     */
+    @Nullable
+    @UnsupportedAppUsage
+    public abstract Drawable getUserBadgeForDensity(@NonNull UserHandle user, int density);
+
+    /**
+     * If the target user is a managed profile of the calling user or the caller
+     * is itself a managed profile, then this returns a drawable to use as a small
+     * icon to include in a view to distinguish it from the original icon. This version
+     * doesn't have background protection and should be used over a light background instead of
+     * a badge.
+     *
+     * @param user The target user.
+     * @param density The optional desired density for the badge as per
+     *         {@link android.util.DisplayMetrics#densityDpi}. If not provided
+     *         the density of the current display is used.
+     * @return the drawable or null if no drawable is required.
+     * @hide
+     */
+    @Nullable
+    @UnsupportedAppUsage
+    public abstract Drawable getUserBadgeForDensityNoBackground(@NonNull UserHandle user,
+            int density);
+
+    /**
+     * If the target user is a managed profile of the calling user or the caller
+     * is itself a managed 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.
+     *
+     * @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.
+     */
+    @NonNull
+    public abstract CharSequence getUserBadgedLabel(@NonNull CharSequence label,
+            @NonNull UserHandle user);
+
+    /**
+     * Retrieve text from a package.  This is a low-level API used by
+     * the various package manager info structures (such as
+     * {@link ComponentInfo} to implement retrieval of their associated
+     * labels and other text.
+     *
+     * @param packageName The name of the package that this text is coming from.
+     * Cannot be null.
+     * @param resid The resource identifier of the desired text.  Cannot be 0.
+     * @param appInfo Overall information about <var>packageName</var>.  This
+     * may be null, in which case the application information will be retrieved
+     * for you if needed; if you already have this information around, it can
+     * be much more efficient to supply it here.
+     *
+     * @return Returns a CharSequence holding the requested text.  Returns null
+     * if the text could not be found for any reason.
+     */
+    @Nullable
+    public abstract CharSequence getText(@NonNull String packageName, @StringRes int resid,
+            @Nullable ApplicationInfo appInfo);
+
+    /**
+     * Retrieve an XML file from a package.  This is a low-level API used to
+     * retrieve XML meta data.
+     *
+     * @param packageName The name of the package that this xml is coming from.
+     * Cannot be null.
+     * @param resid The resource identifier of the desired xml.  Cannot be 0.
+     * @param appInfo Overall information about <var>packageName</var>.  This
+     * may be null, in which case the application information will be retrieved
+     * for you if needed; if you already have this information around, it can
+     * be much more efficient to supply it here.
+     *
+     * @return Returns an XmlPullParser allowing you to parse out the XML
+     * data.  Returns null if the xml resource could not be found for any
+     * reason.
+     */
+    @Nullable
+    public abstract XmlResourceParser getXml(@NonNull String packageName, @XmlRes int resid,
+            @Nullable ApplicationInfo appInfo);
+
+    /**
+     * Return the label to use for this application.
+     *
+     * @return Returns a {@link CharSequence} containing the label associated with
+     * this application, or its name the  item does not have a label.
+     * @param info The {@link ApplicationInfo} of the application to get the label of.
+     */
+    @NonNull
+    public abstract CharSequence getApplicationLabel(@NonNull ApplicationInfo info);
+
+    /**
+     * Retrieve the resources associated with an activity.  Given the full
+     * name of an activity, retrieves the information about it and calls
+     * getResources() to return its application's resources.  If the activity
+     * cannot be found, NameNotFoundException is thrown.
+     *
+     * @param activityName Name of the activity whose resources are to be
+     *                     retrieved.
+     *
+     * @return Returns the application's Resources.
+     * @throws NameNotFoundException Thrown if the resources for the given
+     * application could not be loaded.
+     *
+     * @see #getResourcesForApplication(ApplicationInfo)
+     */
+    @NonNull
+    public abstract Resources getResourcesForActivity(@NonNull ComponentName activityName)
+            throws NameNotFoundException;
+
+    /**
+     * Retrieve the resources for an application.  Throws NameNotFoundException
+     * if the package is no longer installed.
+     *
+     * @param app Information about the desired application.
+     *
+     * @return Returns the application's Resources.
+     * @throws NameNotFoundException Thrown if the resources for the given
+     * application could not be loaded (most likely because it was uninstalled).
+     */
+    @NonNull
+    public abstract Resources getResourcesForApplication(@NonNull ApplicationInfo app)
+            throws NameNotFoundException;
+
+    /**
+     * Retrieve the resources associated with an application.  Given the full
+     * package name of an application, retrieves the information about it and
+     * calls getResources() to return its application's resources.  If the
+     * appPackageName cannot be found, NameNotFoundException is thrown.
+     *
+     * @param packageName Package name of the application whose resources
+     *                       are to be retrieved.
+     *
+     * @return Returns the application's Resources.
+     * @throws NameNotFoundException Thrown if the resources for the given
+     * application could not be loaded.
+     *
+     * @see #getResourcesForApplication(ApplicationInfo)
+     */
+    @NonNull
+    public abstract Resources getResourcesForApplication(@NonNull String packageName)
+            throws NameNotFoundException;
+
+    /** @hide */
+    @NonNull
+    @UnsupportedAppUsage
+    public abstract Resources getResourcesForApplicationAsUser(@NonNull String packageName,
+            @UserIdInt int userId) throws NameNotFoundException;
+
+    /**
+     * Retrieve overall information about an application package defined in a
+     * package archive file
+     *
+     * @param archiveFilePath The path to the archive file
+     * @param flags Additional option flags to modify the data returned.
+     * @return A PackageInfo object containing information about the package
+     *         archive. If the package could not be parsed, returns null.
+     */
+    @Nullable
+    public PackageInfo getPackageArchiveInfo(@NonNull String archiveFilePath,
+            @PackageInfoFlags int flags) {
+        final PackageParser parser = new PackageParser();
+        parser.setCallback(new PackageParser.CallbackImpl(this));
+        final File apkFile = new File(archiveFilePath);
+        try {
+            if ((flags & (MATCH_DIRECT_BOOT_UNAWARE | MATCH_DIRECT_BOOT_AWARE)) != 0) {
+                // Caller expressed an explicit opinion about what encryption
+                // aware/unaware components they want to see, so fall through and
+                // give them what they want
+            } else {
+                // Caller expressed no opinion, so match everything
+                flags |= MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE;
+            }
+
+            PackageParser.Package pkg = parser.parseMonolithicPackage(apkFile, 0);
+            if ((flags & GET_SIGNATURES) != 0) {
+                PackageParser.collectCertificates(pkg, false /* skipVerify */);
+            }
+            PackageUserState state = new PackageUserState();
+            return PackageParser.generatePackageInfo(pkg, null, flags, 0, 0, null, state);
+        } catch (PackageParserException e) {
+            return null;
+        }
+    }
+
+    /**
+     * If there is already an application with the given package name installed
+     * on the system for other users, also install it for the calling user.
+     * @hide
+     *
+     * @deprecated use {@link PackageInstaller#installExistingPackage()} instead.
+     */
+    @Deprecated
+    @SystemApi
+    public abstract int installExistingPackage(@NonNull String packageName)
+            throws NameNotFoundException;
+
+    /**
+     * If there is already an application with the given package name installed
+     * on the system for other users, also install it for the calling user.
+     * @hide
+     *
+     * @deprecated use {@link PackageInstaller#installExistingPackage()} instead.
+     */
+    @Deprecated
+    @SystemApi
+    public abstract int installExistingPackage(@NonNull String packageName,
+            @InstallReason int installReason) throws NameNotFoundException;
+
+    /**
+     * If there is already an application with the given package name installed
+     * on the system for other users, also install it for the specified user.
+     * @hide
+     *
+     * @deprecated use {@link PackageInstaller#installExistingPackage()} instead.
+     */
+    @Deprecated
+    @RequiresPermission(anyOf = {
+            Manifest.permission.INSTALL_EXISTING_PACKAGES,
+            Manifest.permission.INSTALL_PACKAGES,
+            Manifest.permission.INTERACT_ACROSS_USERS_FULL})
+    @UnsupportedAppUsage
+    public abstract int installExistingPackageAsUser(@NonNull String packageName,
+            @UserIdInt int userId) throws NameNotFoundException;
+
+    /**
+     * Allows a package listening to the
+     * {@link Intent#ACTION_PACKAGE_NEEDS_VERIFICATION package verification
+     * broadcast} to respond to the package manager. The response must include
+     * the {@code verificationCode} which is one of
+     * {@link PackageManager#VERIFICATION_ALLOW} or
+     * {@link PackageManager#VERIFICATION_REJECT}.
+     *
+     * @param id pending package identifier as passed via the
+     *            {@link PackageManager#EXTRA_VERIFICATION_ID} Intent extra.
+     * @param verificationCode either {@link PackageManager#VERIFICATION_ALLOW}
+     *            or {@link PackageManager#VERIFICATION_REJECT}.
+     * @throws SecurityException if the caller does not have the
+     *            PACKAGE_VERIFICATION_AGENT permission.
+     */
+    public abstract void verifyPendingInstall(int id, int verificationCode);
+
+    /**
+     * Allows a package listening to the
+     * {@link Intent#ACTION_PACKAGE_NEEDS_VERIFICATION package verification
+     * broadcast} to extend the default timeout for a response and declare what
+     * action to perform after the timeout occurs. The response must include
+     * the {@code verificationCodeAtTimeout} which is one of
+     * {@link PackageManager#VERIFICATION_ALLOW} or
+     * {@link PackageManager#VERIFICATION_REJECT}.
+     *
+     * This method may only be called once per package id. Additional calls
+     * will have no effect.
+     *
+     * @param id pending package identifier as passed via the
+     *            {@link PackageManager#EXTRA_VERIFICATION_ID} Intent extra.
+     * @param verificationCodeAtTimeout either
+     *            {@link PackageManager#VERIFICATION_ALLOW} or
+     *            {@link PackageManager#VERIFICATION_REJECT}. If
+     *            {@code verificationCodeAtTimeout} is neither
+     *            {@link PackageManager#VERIFICATION_ALLOW} or
+     *            {@link PackageManager#VERIFICATION_REJECT}, then
+     *            {@code verificationCodeAtTimeout} will default to
+     *            {@link PackageManager#VERIFICATION_REJECT}.
+     * @param millisecondsToDelay the amount of time requested for the timeout.
+     *            Must be positive and less than
+     *            {@link PackageManager#MAXIMUM_VERIFICATION_TIMEOUT}. If
+     *            {@code millisecondsToDelay} is out of bounds,
+     *            {@code millisecondsToDelay} will be set to the closest in
+     *            bounds value; namely, 0 or
+     *            {@link PackageManager#MAXIMUM_VERIFICATION_TIMEOUT}.
+     * @throws SecurityException if the caller does not have the
+     *            PACKAGE_VERIFICATION_AGENT permission.
+     */
+    public abstract void extendVerificationTimeout(int id,
+            int verificationCodeAtTimeout, long millisecondsToDelay);
+
+    /**
+     * Allows a package listening to the
+     * {@link Intent#ACTION_INTENT_FILTER_NEEDS_VERIFICATION} intent filter verification
+     * broadcast to respond to the package manager. The response must include
+     * the {@code verificationCode} which is one of
+     * {@link PackageManager#INTENT_FILTER_VERIFICATION_SUCCESS} or
+     * {@link PackageManager#INTENT_FILTER_VERIFICATION_FAILURE}.
+     *
+     * @param verificationId pending package identifier as passed via the
+     *            {@link PackageManager#EXTRA_VERIFICATION_ID} Intent extra.
+     * @param verificationCode either {@link PackageManager#INTENT_FILTER_VERIFICATION_SUCCESS}
+     *            or {@link PackageManager#INTENT_FILTER_VERIFICATION_FAILURE}.
+     * @param failedDomains a list of failed domains if the verificationCode is
+     *            {@link PackageManager#INTENT_FILTER_VERIFICATION_FAILURE}, otherwise null;
+     * @throws SecurityException if the caller does not have the
+     *            INTENT_FILTER_VERIFICATION_AGENT permission.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT)
+    public abstract void verifyIntentFilter(int verificationId, int verificationCode,
+            @NonNull List<String> failedDomains);
+
+    /**
+     * Get the status of a Domain Verification Result for an IntentFilter. This is
+     * related to the {@link android.content.IntentFilter#setAutoVerify(boolean)} and
+     * {@link android.content.IntentFilter#getAutoVerify()}
+     *
+     * This is used by the ResolverActivity to change the status depending on what the User select
+     * in the Disambiguation Dialog and also used by the Settings App for changing the default App
+     * for a domain.
+     *
+     * @param packageName The package name of the Activity associated with the IntentFilter.
+     * @param userId The user id.
+     *
+     * @return The status to set to. This can be
+     *              {@link #INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK} or
+     *              {@link #INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS} or
+     *              {@link #INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER} or
+     *              {@link #INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED}
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+    public abstract int getIntentVerificationStatusAsUser(@NonNull String packageName,
+            @UserIdInt int userId);
+
+    /**
+     * Allow to change the status of a Intent Verification status for all IntentFilter of an App.
+     * This is related to the {@link android.content.IntentFilter#setAutoVerify(boolean)} and
+     * {@link android.content.IntentFilter#getAutoVerify()}
+     *
+     * This is used by the ResolverActivity to change the status depending on what the User select
+     * in the Disambiguation Dialog and also used by the Settings App for changing the default App
+     * for a domain.
+     *
+     * @param packageName The package name of the Activity associated with the IntentFilter.
+     * @param status The status to set to. This can be
+     *              {@link #INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK} or
+     *              {@link #INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS} or
+     *              {@link #INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER}
+     * @param userId The user id.
+     *
+     * @return true if the status has been set. False otherwise.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.SET_PREFERRED_APPLICATIONS)
+    public abstract boolean updateIntentVerificationStatusAsUser(@NonNull String packageName,
+            int status, @UserIdInt int userId);
+
+    /**
+     * Get the list of IntentFilterVerificationInfo for a specific package and User.
+     *
+     * @param packageName the package name. When this parameter is set to a non null value,
+     *                    the results will be filtered by the package name provided.
+     *                    Otherwise, there will be no filtering and it will return a list
+     *                    corresponding for all packages
+     *
+     * @return a list of IntentFilterVerificationInfo for a specific package.
+     *
+     * @hide
+     */
+    @NonNull
+    @SystemApi
+    public abstract List<IntentFilterVerificationInfo> getIntentFilterVerifications(
+            @NonNull String packageName);
+
+    /**
+     * Get the list of IntentFilter for a specific package.
+     *
+     * @param packageName the package name. This parameter is set to a non null value,
+     *                    the list will contain all the IntentFilter for that package.
+     *                    Otherwise, the list will be empty.
+     *
+     * @return a list of IntentFilter for a specific package.
+     *
+     * @hide
+     */
+    @NonNull
+    @SystemApi
+    public abstract List<IntentFilter> getAllIntentFilters(@NonNull String packageName);
+
+    /**
+     * Get the default Browser package name for a specific user.
+     *
+     * @param userId The user id.
+     *
+     * @return the package name of the default Browser for the specified user. If the user id passed
+     *         is -1 (all users) it will return a null value.
+     *
+     * @hide
+     */
+    @Nullable
+    @TestApi
+    @SystemApi
+    @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+    public abstract String getDefaultBrowserPackageNameAsUser(@UserIdInt int userId);
+
+    /**
+     * Set the default Browser package name for a specific user.
+     *
+     * @param packageName The package name of the default Browser.
+     * @param userId The user id.
+     *
+     * @return true if the default Browser for the specified user has been set,
+     *         otherwise return false. If the user id passed is -1 (all users) this call will not
+     *         do anything and just return false.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(allOf = {
+            Manifest.permission.SET_PREFERRED_APPLICATIONS,
+            Manifest.permission.INTERACT_ACROSS_USERS_FULL})
+    public abstract boolean setDefaultBrowserPackageNameAsUser(@Nullable String packageName,
+            @UserIdInt int userId);
+
+    /**
+     * Change the installer associated with a given package.  There are limitations
+     * on how the installer package can be changed; in particular:
+     * <ul>
+     * <li> A SecurityException will be thrown if <var>installerPackageName</var>
+     * is not signed with the same certificate as the calling application.
+     * <li> A SecurityException will be thrown if <var>targetPackage</var> already
+     * has an installer package, and that installer package is not signed with
+     * the same certificate as the calling application.
+     * </ul>
+     *
+     * @param targetPackage The installed package whose installer will be changed.
+     * @param installerPackageName The package name of the new installer.  May be
+     * null to clear the association.
+     */
+    public abstract void setInstallerPackageName(@NonNull String targetPackage,
+            @Nullable String installerPackageName);
+
+    /** @hide */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.INSTALL_PACKAGES)
+    public abstract void setUpdateAvailable(@NonNull String packageName, boolean updateAvaialble);
+
+    /**
+     * Attempts to delete a package. Since this may take a little while, the
+     * result will be posted back to the given observer. A deletion will fail if
+     * the calling context lacks the
+     * {@link android.Manifest.permission#DELETE_PACKAGES} permission, if the
+     * named package cannot be found, or if the named package is a system
+     * package.
+     *
+     * @param packageName The name of the package to delete
+     * @param observer An observer callback to get notified when the package
+     *            deletion is complete.
+     *            {@link android.content.pm.IPackageDeleteObserver#packageDeleted}
+     *            will be called when that happens. observer may be null to
+     *            indicate that no callback is desired.
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.DELETE_PACKAGES)
+    @UnsupportedAppUsage
+    public abstract void deletePackage(@NonNull String packageName,
+            @Nullable IPackageDeleteObserver observer, @DeleteFlags int flags);
+
+    /**
+     * Attempts to delete a package. Since this may take a little while, the
+     * result will be posted back to the given observer. A deletion will fail if
+     * the named package cannot be found, or if the named package is a system
+     * package.
+     *
+     * @param packageName The name of the package to delete
+     * @param observer An observer callback to get notified when the package
+     *            deletion is complete.
+     *            {@link android.content.pm.IPackageDeleteObserver#packageDeleted}
+     *            will be called when that happens. observer may be null to
+     *            indicate that no callback is desired.
+     * @param userId The user Id
+     * @hide
+     */
+    @RequiresPermission(anyOf = {
+            Manifest.permission.DELETE_PACKAGES,
+            Manifest.permission.INTERACT_ACROSS_USERS_FULL})
+    @UnsupportedAppUsage
+    public abstract void deletePackageAsUser(@NonNull String packageName,
+            @Nullable IPackageDeleteObserver observer, @DeleteFlags int flags,
+            @UserIdInt int userId);
+
+    /**
+     * Retrieve the package name of the application that installed a package. This identifies
+     * which market the package came from.
+     *
+     * @param packageName The name of the package to query
+     * @throws IllegalArgumentException if the given package name is not installed
+     *
+     * @deprecated use {@link #getInstallSourceInfo(String)} instead
+     */
+    @Deprecated
+    @Nullable
+    public abstract String getInstallerPackageName(@NonNull String packageName);
+
+    /**
+     * Retrieves information about how a package was installed or updated.
+     * <p>
+     * If the calling application does not hold the INSTALL_PACKAGES permission then
+     * the result will always return {@code null} from
+     * {@link InstallSourceInfo#getOriginatingPackageName()}.
+     * <p>
+     * If the package that requested the install has been uninstalled, then information about it
+     * will only be returned from {@link InstallSourceInfo#getInitiatingPackageName()} and
+     * {@link InstallSourceInfo#getInitiatingPackageSigningInfo()} if the calling package is
+     * requesting its own install information and is not an instant app.
+     *
+     * @param packageName The name of the package to query
+     * @throws NameNotFoundException if the given package name is not installed
+     */
+    @NonNull
+    public InstallSourceInfo getInstallSourceInfo(@NonNull String packageName)
+            throws NameNotFoundException {
+        throw new UnsupportedOperationException("getInstallSourceInfo not implemented");
+    }
+
+    /**
+     * Attempts to clear the user data directory of an application.
+     * Since this may take a little while, the result will
+     * be posted back to the given observer.  A deletion will fail if the
+     * named package cannot be found, or if the named package is a "system package".
+     *
+     * @param packageName The name of the package
+     * @param observer An observer callback to get notified when the operation is finished
+     * {@link android.content.pm.IPackageDataObserver#onRemoveCompleted(String, boolean)}
+     * will be called when that happens.  observer may be null to indicate that
+     * no callback is desired.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public abstract void clearApplicationUserData(@NonNull String packageName,
+            @Nullable IPackageDataObserver observer);
+    /**
+     * Attempts to delete the cache files associated with an application.
+     * Since this may take a little while, the result will
+     * be posted back to the given observer.  A deletion will fail if the calling context
+     * lacks the {@link android.Manifest.permission#DELETE_CACHE_FILES} permission, if the
+     * named package cannot be found, or if the named package is a "system package".
+     *
+     * @param packageName The name of the package to delete
+     * @param observer An observer callback to get notified when the cache file deletion
+     * is complete.
+     * {@link android.content.pm.IPackageDataObserver#onRemoveCompleted(String, boolean)}
+     * will be called when that happens.  observer may be null to indicate that
+     * no callback is desired.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public abstract void deleteApplicationCacheFiles(@NonNull String packageName,
+            @Nullable IPackageDataObserver observer);
+
+    /**
+     * Attempts to delete the cache files associated with an application for a given user. Since
+     * this may take a little while, the result will be posted back to the given observer. A
+     * deletion will fail if the calling context lacks the
+     * {@link android.Manifest.permission#DELETE_CACHE_FILES} permission, if the named package
+     * cannot be found, or if the named package is a "system package". If {@code userId} does not
+     * belong to the calling user, the caller must have
+     * {@link android.Manifest.permission#INTERACT_ACROSS_USERS} permission.
+     *
+     * @param packageName The name of the package to delete
+     * @param userId the user for which the cache files needs to be deleted
+     * @param observer An observer callback to get notified when the cache file deletion is
+     *            complete.
+     *            {@link android.content.pm.IPackageDataObserver#onRemoveCompleted(String, boolean)}
+     *            will be called when that happens. observer may be null to indicate that no
+     *            callback is desired.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public abstract void deleteApplicationCacheFilesAsUser(@NonNull String packageName,
+            @UserIdInt int userId, @Nullable IPackageDataObserver observer);
+
+    /**
+     * Free storage by deleting LRU sorted list of cache files across
+     * all applications. If the currently available free storage
+     * on the device is greater than or equal to the requested
+     * free storage, no cache files are cleared. If the currently
+     * available storage on the device is less than the requested
+     * free storage, some or all of the cache files across
+     * all applications are deleted (based on last accessed time)
+     * to increase the free storage space on the device to
+     * the requested value. There is no guarantee that clearing all
+     * the cache files from all applications will clear up
+     * enough storage to achieve the desired value.
+     * @param freeStorageSize The number of bytes of storage to be
+     * freed by the system. Say if freeStorageSize is XX,
+     * and the current free storage is YY,
+     * if XX is less than YY, just return. if not free XX-YY number
+     * of bytes if possible.
+     * @param observer call back used to notify when
+     * the operation is completed
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public void freeStorageAndNotify(long freeStorageSize,
+            @Nullable IPackageDataObserver observer) {
+        freeStorageAndNotify(null, freeStorageSize, observer);
+    }
+
+    /** {@hide} */
+    @UnsupportedAppUsage
+    public abstract void freeStorageAndNotify(@Nullable String volumeUuid, long freeStorageSize,
+            @Nullable IPackageDataObserver observer);
+
+    /**
+     * Free storage by deleting LRU sorted list of cache files across
+     * all applications. If the currently available free storage
+     * on the device is greater than or equal to the requested
+     * free storage, no cache files are cleared. If the currently
+     * available storage on the device is less than the requested
+     * free storage, some or all of the cache files across
+     * all applications are deleted (based on last accessed time)
+     * to increase the free storage space on the device to
+     * the requested value. There is no guarantee that clearing all
+     * the cache files from all applications will clear up
+     * enough storage to achieve the desired value.
+     * @param freeStorageSize The number of bytes of storage to be
+     * freed by the system. Say if freeStorageSize is XX,
+     * and the current free storage is YY,
+     * if XX is less than YY, just return. if not free XX-YY number
+     * of bytes if possible.
+     * @param pi IntentSender call back used to
+     * notify when the operation is completed.May be null
+     * to indicate that no call back is desired.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public void freeStorage(long freeStorageSize, @Nullable IntentSender pi) {
+        freeStorage(null, freeStorageSize, pi);
+    }
+
+    /** {@hide} */
+    @UnsupportedAppUsage
+    public abstract void freeStorage(@Nullable String volumeUuid, long freeStorageSize,
+            @Nullable IntentSender pi);
+
+    /**
+     * Retrieve the size information for a package.
+     * Since this may take a little while, the result will
+     * be posted back to the given observer.  The calling context
+     * should have the {@link android.Manifest.permission#GET_PACKAGE_SIZE} permission.
+     *
+     * @param packageName The name of the package whose size information is to be retrieved
+     * @param userId The user whose size information should be retrieved.
+     * @param observer An observer callback to get notified when the operation
+     * is complete.
+     * {@link android.content.pm.IPackageStatsObserver#onGetStatsCompleted(PackageStats, boolean)}
+     * The observer's callback is invoked with a PackageStats object(containing the
+     * code, data and cache sizes of the package) and a boolean value representing
+     * the status of the operation. observer may be null to indicate that
+     * no callback is desired.
+     *
+     * @deprecated use {@link StorageStatsManager} instead.
+     * @hide
+     */
+    @Deprecated
+    @UnsupportedAppUsage
+    public abstract void getPackageSizeInfoAsUser(@NonNull String packageName,
+            @UserIdInt int userId, @Nullable IPackageStatsObserver observer);
+
+    /**
+     * Like {@link #getPackageSizeInfoAsUser(String, int, IPackageStatsObserver)}, but
+     * returns the size for the calling user.
+     *
+     * @deprecated use {@link StorageStatsManager} instead.
+     * @hide
+     */
+    @Deprecated
+    @UnsupportedAppUsage
+    public void getPackageSizeInfo(@NonNull String packageName, IPackageStatsObserver observer) {
+        getPackageSizeInfoAsUser(packageName, getUserId(), observer);
+    }
+
+    /**
+     * @deprecated This function no longer does anything. It is the platform's
+     * responsibility to assign preferred activities and this cannot be modified
+     * directly. To determine the activities resolved by the platform, use
+     * {@link #resolveActivity} or {@link #queryIntentActivities}. To configure
+     * an app to be responsible for a particular role and to check current role
+     * holders, see {@link android.app.role.RoleManager}.
+     */
+    @Deprecated
+    public abstract void addPackageToPreferred(@NonNull String packageName);
+
+    /**
+     * @deprecated This function no longer does anything. It is the platform's
+     * responsibility to assign preferred activities and this cannot be modified
+     * directly. To determine the activities resolved by the platform, use
+     * {@link #resolveActivity} or {@link #queryIntentActivities}. To configure
+     * an app to be responsible for a particular role and to check current role
+     * holders, see {@link android.app.role.RoleManager}.
+     */
+    @Deprecated
+    public abstract void removePackageFromPreferred(@NonNull String packageName);
+
+    /**
+     * Retrieve the list of all currently configured preferred packages. The
+     * first package on the list is the most preferred, the last is the least
+     * preferred.
+     *
+     * @param flags Additional option flags to modify the data returned.
+     * @return A List of PackageInfo objects, one for each preferred
+     *         application, in order of preference.
+     *
+     * @deprecated This function no longer does anything. It is the platform's
+     * responsibility to assign preferred activities and this cannot be modified
+     * directly. To determine the activities resolved by the platform, use
+     * {@link #resolveActivity} or {@link #queryIntentActivities}. To configure
+     * an app to be responsible for a particular role and to check current role
+     * holders, see {@link android.app.role.RoleManager}.
+     */
+    @NonNull
+    @Deprecated
+    public abstract List<PackageInfo> getPreferredPackages(@PackageInfoFlags int flags);
+
+    /**
+     * Add a new preferred activity mapping to the system.  This will be used
+     * to automatically select the given activity component when
+     * {@link Context#startActivity(Intent) Context.startActivity()} finds
+     * multiple matching activities and also matches the given filter.
+     *
+     * @param filter The set of intents under which this activity will be
+     * made preferred.
+     * @param match The IntentFilter match category that this preference
+     * applies to.
+     * @param set The set of activities that the user was picking from when
+     * this preference was made.
+     * @param activity The component name of the activity that is to be
+     * preferred.
+     *
+     * @deprecated This function no longer does anything. It is the platform's
+     * responsibility to assign preferred activities and this cannot be modified
+     * directly. To determine the activities resolved by the platform, use
+     * {@link #resolveActivity} or {@link #queryIntentActivities}. To configure
+     * an app to be responsible for a particular role and to check current role
+     * holders, see {@link android.app.role.RoleManager}.
+     */
+    @Deprecated
+    public abstract void addPreferredActivity(@NonNull IntentFilter filter, int match,
+            @Nullable ComponentName[] set, @NonNull ComponentName activity);
+
+    /**
+     * Same as {@link #addPreferredActivity(IntentFilter, int,
+            ComponentName[], ComponentName)}, but with a specific userId to apply the preference
+            to.
+     * @hide
+     *
+     * @deprecated This function no longer does anything. It is the platform's
+     * responsibility to assign preferred activities and this cannot be modified
+     * directly. To determine the activities resolved by the platform, use
+     * {@link #resolveActivity} or {@link #queryIntentActivities}. To configure
+     * an app to be responsible for a particular role and to check current role
+     * holders, see {@link android.app.role.RoleManager}.
+     */
+    @Deprecated
+    @UnsupportedAppUsage
+    public void addPreferredActivityAsUser(@NonNull IntentFilter filter, int match,
+            @Nullable ComponentName[] set, @NonNull ComponentName activity, @UserIdInt int userId) {
+        throw new RuntimeException("Not implemented. Must override in a subclass.");
+    }
+
+    /**
+     * Replaces an existing preferred activity mapping to the system, and if that were not present
+     * adds a new preferred activity.  This will be used
+     * to automatically select the given activity component when
+     * {@link Context#startActivity(Intent) Context.startActivity()} finds
+     * multiple matching activities and also matches the given filter.
+     *
+     * @param filter The set of intents under which this activity will be
+     * made preferred.
+     * @param match The IntentFilter match category that this preference
+     * applies to.
+     * @param set The set of activities that the user was picking from when
+     * this preference was made.
+     * @param activity The component name of the activity that is to be
+     * preferred.
+     *
+     * @hide
+     *
+     * @deprecated This function no longer does anything. It is the platform's
+     * responsibility to assign preferred activities and this cannot be modified
+     * directly. To determine the activities resolved by the platform, use
+     * {@link #resolveActivity} or {@link #queryIntentActivities}. To configure
+     * an app to be responsible for a particular role and to check current role
+     * holders, see {@link android.app.role.RoleManager}.
+     */
+    @Deprecated
+    @UnsupportedAppUsage
+    public abstract void replacePreferredActivity(@NonNull IntentFilter filter, int match,
+            @Nullable ComponentName[] set, @NonNull ComponentName activity);
+
+    /**
+     * Replaces an existing preferred activity mapping to the system, and if that were not present
+     * adds a new preferred activity.  This will be used to automatically select the given activity
+     * component when {@link Context#startActivity(Intent) Context.startActivity()} finds multiple
+     * matching activities and also matches the given filter.
+     *
+     * @param filter The set of intents under which this activity will be made preferred.
+     * @param match The IntentFilter match category that this preference applies to. Should be a
+     *              combination of {@link IntentFilter#MATCH_CATEGORY_MASK} and
+     *              {@link IntentFilter#MATCH_ADJUSTMENT_MASK}).
+     * @param set The set of activities that the user was picking from when this preference was
+     *            made.
+     * @param activity The component name of the activity that is to be preferred.
+     *
+     * @hide
+     */
+    @SystemApi
+    public void replacePreferredActivity(@NonNull IntentFilter filter, int match,
+            @NonNull List<ComponentName> set, @NonNull ComponentName activity) {
+        replacePreferredActivity(filter, match, set.toArray(new ComponentName[0]), activity);
+    }
+
+    /**
+     * @hide
+     *
+     * @deprecated This function no longer does anything. It is the platform's
+     * responsibility to assign preferred activities and this cannot be modified
+     * directly. To determine the activities resolved by the platform, use
+     * {@link #resolveActivity} or {@link #queryIntentActivities}. To configure
+     * an app to be responsible for a particular role and to check current role
+     * holders, see {@link android.app.role.RoleManager}.
+     */
+    @Deprecated
+    @UnsupportedAppUsage
+    public void replacePreferredActivityAsUser(@NonNull IntentFilter filter, int match,
+            @Nullable ComponentName[] set, @NonNull ComponentName activity, @UserIdInt int userId) {
+        throw new RuntimeException("Not implemented. Must override in a subclass.");
+    }
+
+    /**
+     * Remove all preferred activity mappings, previously added with
+     * {@link #addPreferredActivity}, from the
+     * system whose activities are implemented in the given package name.
+     * An application can only clear its own package(s).
+     *
+     * @param packageName The name of the package whose preferred activity
+     * mappings are to be removed.
+     *
+     * @deprecated This function no longer does anything. It is the platform's
+     * responsibility to assign preferred activities and this cannot be modified
+     * directly. To determine the activities resolved by the platform, use
+     * {@link #resolveActivity} or {@link #queryIntentActivities}. To configure
+     * an app to be responsible for a particular role and to check current role
+     * holders, see {@link android.app.role.RoleManager}.
+     */
+    @Deprecated
+    public abstract void clearPackagePreferredActivities(@NonNull String packageName);
+
+    /**
+     * Retrieve all preferred activities, previously added with
+     * {@link #addPreferredActivity}, that are
+     * currently registered with the system.
+     *
+     * @param outFilters A required list in which to place the filters of all of the
+     * preferred activities.
+     * @param outActivities A required list in which to place the component names of
+     * all of the preferred activities.
+     * @param packageName An optional package in which you would like to limit
+     * the list.  If null, all activities will be returned; if non-null, only
+     * those activities in the given package are returned.
+     *
+     * @return Returns the total number of registered preferred activities
+     * (the number of distinct IntentFilter records, not the number of unique
+     * activity components) that were found.
+     *
+     * @deprecated This function no longer does anything. It is the platform's
+     * responsibility to assign preferred activities and this cannot be modified
+     * directly. To determine the activities resolved by the platform, use
+     * {@link #resolveActivity} or {@link #queryIntentActivities}. To configure
+     * an app to be responsible for a particular role and to check current role
+     * holders, see {@link android.app.role.RoleManager}.
+     */
+    @Deprecated
+    public abstract int getPreferredActivities(@NonNull List<IntentFilter> outFilters,
+            @NonNull List<ComponentName> outActivities, @Nullable String packageName);
+
+    /**
+     * Ask for the set of available 'home' activities and the current explicit
+     * default, if any.
+     * @hide
+     */
+    @Nullable
+    @UnsupportedAppUsage
+    public abstract ComponentName getHomeActivities(@NonNull List<ResolveInfo> outActivities);
+
+    /**
+     * Set the enabled setting for a package component (activity, receiver, service, provider).
+     * This setting will override any enabled state which may have been set by the component in its
+     * manifest.
+     *
+     * @param componentName The component to enable
+     * @param newState The new enabled state for the component.
+     * @param flags Optional behavior flags.
+     */
+    @RequiresPermission(value = android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE,
+            conditional = true)
+    public abstract void setComponentEnabledSetting(@NonNull ComponentName componentName,
+            @EnabledState int newState, @EnabledFlags int flags);
+
+    /**
+     * Return the enabled setting for a package component (activity,
+     * receiver, service, provider).  This returns the last value set by
+     * {@link #setComponentEnabledSetting(ComponentName, int, int)}; in most
+     * cases this value will be {@link #COMPONENT_ENABLED_STATE_DEFAULT} since
+     * the value originally specified in the manifest has not been modified.
+     *
+     * @param componentName The component to retrieve.
+     * @return Returns the current enabled state for the component.
+     */
+    public abstract @EnabledState int getComponentEnabledSetting(
+            @NonNull ComponentName componentName);
+
+    /**
+     * Set whether a synthetic app details activity will be generated if the app has no enabled
+     * launcher activity. Disabling this allows the app to have no launcher icon.
+     *
+     * @param packageName The package name of the app
+     * @param enabled The new enabled state for the synthetic app details activity.
+     *
+     * @hide
+     */
+    @RequiresPermission(value = android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE,
+            conditional = true)
+    @SystemApi
+    public void setSyntheticAppDetailsActivityEnabled(@NonNull String packageName,
+            boolean enabled) {
+        throw new UnsupportedOperationException(
+                "setSyntheticAppDetailsActivityEnabled not implemented");
+    }
+
+
+    /**
+     * Return whether a synthetic app details activity will be generated if the app has no enabled
+     * launcher activity.
+     *
+     * @param packageName The package name of the app
+     * @return Returns the enabled state for the synthetic app details activity.
+     *
+     *
+     */
+    public boolean getSyntheticAppDetailsActivityEnabled(@NonNull String packageName) {
+        throw new UnsupportedOperationException(
+                "getSyntheticAppDetailsActivityEnabled not implemented");
+    }
+
+    /**
+     * Set the enabled setting for an application
+     * This setting will override any enabled state which may have been set by the application in
+     * its manifest.  It also overrides the enabled state set in the manifest for any of the
+     * application's components.  It does not override any enabled state set by
+     * {@link #setComponentEnabledSetting} for any of the application's components.
+     *
+     * @param packageName The package name of the application to enable
+     * @param newState The new enabled state for the application.
+     * @param flags Optional behavior flags.
+     */
+    @RequiresPermission(value = android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE,
+            conditional = true)
+    public abstract void setApplicationEnabledSetting(@NonNull String packageName,
+            @EnabledState int newState, @EnabledFlags int flags);
+
+    /**
+     * Return the enabled setting for an application. This returns
+     * the last value set by
+     * {@link #setApplicationEnabledSetting(String, int, int)}; in most
+     * cases this value will be {@link #COMPONENT_ENABLED_STATE_DEFAULT} since
+     * the value originally specified in the manifest has not been modified.
+     *
+     * @param packageName The package name of the application to retrieve.
+     * @return Returns the current enabled state for the application.
+     * @throws IllegalArgumentException if the named package does not exist.
+     */
+    public abstract @EnabledState int getApplicationEnabledSetting(@NonNull String packageName);
+
+    /**
+     * Flush the package restrictions for a given user to disk. This forces the package restrictions
+     * like component and package enabled settings to be written to disk and avoids the delay that
+     * is otherwise present when changing those settings.
+     *
+     * @param userId Ther userId of the user whose restrictions are to be flushed.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public abstract void flushPackageRestrictionsAsUser(@UserIdInt int userId);
+
+    /**
+     * Puts the package in a hidden state, which is almost like an uninstalled state,
+     * making the package unavailable, but it doesn't remove the data or the actual
+     * package file. Application can be unhidden by either resetting the hidden state
+     * or by installing it, such as with {@link #installExistingPackage(String)}
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public abstract boolean setApplicationHiddenSettingAsUser(@NonNull String packageName,
+            boolean hidden, @NonNull UserHandle userHandle);
+
+    /**
+     * Returns the hidden state of a package.
+     * @see #setApplicationHiddenSettingAsUser(String, boolean, UserHandle)
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public abstract boolean getApplicationHiddenSettingAsUser(@NonNull String packageName,
+            @NonNull UserHandle userHandle);
+
+    /**
+     * Sets system app state
+     * @param packageName Package name of the app.
+     * @param state State of the app.
+     * @hide
+     */
+    public void setSystemAppState(@NonNull String packageName, @SystemAppState int state) {
+        throw new RuntimeException("Not implemented. Must override in a subclass");
+    }
+
+    /**
+     * Return whether the device has been booted into safe mode.
+     */
+    public abstract boolean isSafeMode();
+
+    /**
+     * Adds a listener for permission changes for installed packages.
+     *
+     * @param listener The listener to add.
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    @RequiresPermission(Manifest.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS)
+    public abstract void addOnPermissionsChangeListener(
+            @NonNull OnPermissionsChangedListener listener);
+
+    /**
+     * Remvoes a listener for permission changes for installed packages.
+     *
+     * @param listener The listener to remove.
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    @RequiresPermission(Manifest.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS)
+    public abstract void removeOnPermissionsChangeListener(
+            @NonNull OnPermissionsChangedListener listener);
+
+    /**
+     * Return the {@link KeySet} associated with the String alias for this
+     * application.
+     *
+     * @param alias The alias for a given {@link KeySet} as defined in the
+     *        application's AndroidManifest.xml.
+     * @hide
+     */
+    @NonNull
+    @UnsupportedAppUsage
+    public abstract KeySet getKeySetByAlias(@NonNull String packageName, @NonNull String alias);
+
+    /** Return the signing {@link KeySet} for this application.
+     * @hide
+     */
+    @NonNull
+    @UnsupportedAppUsage
+    public abstract KeySet getSigningKeySet(@NonNull String packageName);
+
+    /**
+     * Return whether the package denoted by packageName has been signed by all
+     * of the keys specified by the {@link KeySet} ks.  This will return true if
+     * the package has been signed by additional keys (a superset) as well.
+     * Compare to {@link #isSignedByExactly(String packageName, KeySet ks)}.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public abstract boolean isSignedBy(@NonNull String packageName, @NonNull KeySet ks);
+
+    /**
+     * Return whether the package denoted by packageName has been signed by all
+     * of, and only, the keys specified by the {@link KeySet} ks. Compare to
+     * {@link #isSignedBy(String packageName, KeySet ks)}.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public abstract boolean isSignedByExactly(@NonNull String packageName, @NonNull KeySet ks);
+
+    /**
+     * Flag to denote no restrictions. This should be used to clear any restrictions that may have
+     * been previously set for the package.
+     * @hide
+     * @see #setDistractingPackageRestrictions(String[], int)
+     */
+    @SystemApi
+    public static final int RESTRICTION_NONE = 0x0;
+
+    /**
+     * Flag to denote that a package should be hidden from any suggestions to the user.
+     * @hide
+     * @see #setDistractingPackageRestrictions(String[], int)
+     */
+    @SystemApi
+    public static final int RESTRICTION_HIDE_FROM_SUGGESTIONS = 0x00000001;
+
+    /**
+     * Flag to denote that a package's notifications should be hidden.
+     * @hide
+     * @see #setDistractingPackageRestrictions(String[], int)
+     */
+    @SystemApi
+    public static final int RESTRICTION_HIDE_NOTIFICATIONS = 0x00000002;
+
+    /**
+     * Restriction flags to set on a package that is considered as distracting to the user.
+     * These should help the user to restrict their usage of these apps.
+     *
+     * @see #setDistractingPackageRestrictions(String[], int)
+     * @hide
+     */
+    @IntDef(flag = true, prefix = {"RESTRICTION_"}, value = {
+            RESTRICTION_NONE,
+            RESTRICTION_HIDE_FROM_SUGGESTIONS,
+            RESTRICTION_HIDE_NOTIFICATIONS
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface DistractionRestriction {}
+
+    /**
+     * Mark or unmark the given packages as distracting to the user.
+     * These packages can have certain restrictions set that should discourage the user to launch
+     * them often. For example, notifications from such an app can be hidden, or the app can be
+     * removed from launcher suggestions, so the user is able to restrict their use of these apps.
+     *
+     * <p>The caller must hold {@link android.Manifest.permission#SUSPEND_APPS} to use this API.
+     *
+     * @param packages Packages to mark as distracting.
+     * @param restrictionFlags Any combination of restrictions to impose on the given packages.
+     *                         {@link #RESTRICTION_NONE} can be used to clear any existing
+     *                         restrictions.
+     * @return A list of packages that could not have the {@code restrictionFlags} set. The system
+     * may prevent restricting critical packages to preserve normal device function.
+     *
+     * @hide
+     * @see #RESTRICTION_NONE
+     * @see #RESTRICTION_HIDE_FROM_SUGGESTIONS
+     * @see #RESTRICTION_HIDE_NOTIFICATIONS
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.SUSPEND_APPS)
+    @NonNull
+    public String[] setDistractingPackageRestrictions(@NonNull String[] packages,
+            @DistractionRestriction int restrictionFlags) {
+        throw new UnsupportedOperationException(
+                "setDistractingPackageRestrictions not implemented");
+    }
+
+    /**
+     * Puts the package in a suspended state, where attempts at starting activities are denied.
+     *
+     * <p>It doesn't remove the data or the actual package file. The application's notifications
+     * will be hidden, any of its started activities will be stopped and it will not be able to
+     * show toasts or system alert windows or ring the device.
+     *
+     * <p>When the user tries to launch a suspended app, a system dialog with the given
+     * {@code dialogMessage} will be shown instead. Since the message is supplied to the system as
+     * a {@link String}, the caller needs to take care of localization as needed.
+     * The dialog message can optionally contain a placeholder for the name of the suspended app.
+     * The system uses {@link String#format(Locale, String, Object...) String.format} to insert the
+     * app name into the message, so an example format string could be {@code "The app %1$s is
+     * currently suspended"}. This makes it easier for callers to provide a single message which
+     * works for all the packages being suspended in a single call.
+     *
+     * <p>The package must already be installed. If the package is uninstalled while suspended
+     * the package will no longer be suspended. </p>
+     *
+     * <p>Optionally, the suspending app can provide extra information in the form of
+     * {@link PersistableBundle} objects to be shared with the apps being suspended and the
+     * launcher to support customization that they might need to handle the suspended state.
+     *
+     * <p>The caller must hold {@link Manifest.permission#SUSPEND_APPS} to use this API.
+     *
+     * @param packageNames The names of the packages to set the suspended status.
+     * @param suspended If set to {@code true}, the packages will be suspended, if set to
+     * {@code false}, the packages will be unsuspended.
+     * @param appExtras An optional {@link PersistableBundle} that the suspending app can provide
+     *                  which will be shared with the apps being suspended. Ignored if
+     *                  {@code suspended} is false.
+     * @param launcherExtras An optional {@link PersistableBundle} that the suspending app can
+     *                       provide which will be shared with the launcher. Ignored if
+     *                       {@code suspended} is false.
+     * @param dialogMessage The message to be displayed to the user, when they try to launch a
+     *                      suspended app.
+     *
+     * @return an array of package names for which the suspended status could not be set as
+     * requested in this method. Returns {@code null} if {@code packageNames} was {@code null}.
+     *
+     * @deprecated use {@link #setPackagesSuspended(String[], boolean, PersistableBundle,
+     * PersistableBundle, android.content.pm.SuspendDialogInfo)} instead.
+     *
+     * @hide
+     */
+    @SystemApi
+    @Deprecated
+    @RequiresPermission(Manifest.permission.SUSPEND_APPS)
+    @Nullable
+    public String[] setPackagesSuspended(@Nullable String[] packageNames, boolean suspended,
+            @Nullable PersistableBundle appExtras, @Nullable PersistableBundle launcherExtras,
+            @Nullable String dialogMessage) {
+        throw new UnsupportedOperationException("setPackagesSuspended not implemented");
+    }
+
+    /**
+     * Puts the given packages in a suspended state, where attempts at starting activities are
+     * denied.
+     *
+     * <p>The suspended application's notifications and all of its windows will be hidden, any
+     * of its started activities will be stopped and it won't be able to ring the device.
+     * It doesn't remove the data or the actual package file.
+     *
+     * <p>When the user tries to launch a suspended app, a system dialog alerting them that the app
+     * is suspended will be shown instead.
+     * The caller can optionally customize the dialog by passing a {@link SuspendDialogInfo} object
+     * to this API. This dialog will have a button that starts the
+     * {@link Intent#ACTION_SHOW_SUSPENDED_APP_DETAILS} intent if the suspending app declares an
+     * activity which handles this action.
+     *
+     * <p>The packages being suspended must already be installed. If a package is uninstalled, it
+     * will no longer be suspended.
+     *
+     * <p>Optionally, the suspending app can provide extra information in the form of
+     * {@link PersistableBundle} objects to be shared with the apps being suspended and the
+     * launcher to support customization that they might need to handle the suspended state.
+     *
+     * <p>The caller must hold {@link Manifest.permission#SUSPEND_APPS} to use this API.
+     *
+     * @param packageNames The names of the packages to set the suspended status.
+     * @param suspended If set to {@code true}, the packages will be suspended, if set to
+     * {@code false}, the packages will be unsuspended.
+     * @param appExtras An optional {@link PersistableBundle} that the suspending app can provide
+     *                  which will be shared with the apps being suspended. Ignored if
+     *                  {@code suspended} is false.
+     * @param launcherExtras An optional {@link PersistableBundle} that the suspending app can
+     *                       provide which will be shared with the launcher. Ignored if
+     *                       {@code suspended} is false.
+     * @param dialogInfo An optional {@link SuspendDialogInfo} object describing the dialog that
+     *                   should be shown to the user when they try to launch a suspended app.
+     *                   Ignored if {@code suspended} is false.
+     *
+     * @return an array of package names for which the suspended status could not be set as
+     * requested in this method. Returns {@code null} if {@code packageNames} was {@code null}.
+     *
+     * @see #isPackageSuspended
+     * @see SuspendDialogInfo
+     * @see SuspendDialogInfo.Builder
+     * @see Intent#ACTION_SHOW_SUSPENDED_APP_DETAILS
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.SUSPEND_APPS)
+    @Nullable
+    public String[] setPackagesSuspended(@Nullable String[] packageNames, boolean suspended,
+            @Nullable PersistableBundle appExtras, @Nullable PersistableBundle launcherExtras,
+            @Nullable SuspendDialogInfo dialogInfo) {
+        throw new UnsupportedOperationException("setPackagesSuspended not implemented");
+    }
+
+    /**
+     * Returns any packages in a given set of packages that cannot be suspended via a call to {@link
+     * #setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle,
+     * SuspendDialogInfo) setPackagesSuspended}. The platform prevents suspending certain critical
+     * packages to keep the device in a functioning state, e.g. the default dialer and launcher.
+     * Apps need to hold {@link Manifest.permission#SUSPEND_APPS SUSPEND_APPS} to call this API.
+     *
+     * <p>
+     * Note that this set of critical packages can change with time, so even though a package name
+     * was not returned by this call, it does not guarantee that a subsequent call to
+     * {@link #setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle,
+     * SuspendDialogInfo) setPackagesSuspended} for that package will succeed, especially if
+     * significant time elapsed between the two calls.
+     *
+     * @param packageNames The packages to check.
+     * @return A list of packages that can not be currently suspended by the system.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.SUSPEND_APPS)
+    @NonNull
+    public String[] getUnsuspendablePackages(@NonNull String[] packageNames) {
+        throw new UnsupportedOperationException("getUnsuspendablePackages not implemented");
+    }
+
+    /**
+     * @see #setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle, String)
+     * @param packageName The name of the package to get the suspended status of.
+     * @param userId The user id.
+     * @return {@code true} if the package is suspended or {@code false} if the package is not
+     * suspended.
+     * @throws IllegalArgumentException if the package was not found.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public abstract boolean isPackageSuspendedForUser(@NonNull String packageName, int userId);
+
+    /**
+     * Query if an app is currently suspended.
+     *
+     * @return {@code true} if the given package is suspended, {@code false} otherwise
+     * @throws NameNotFoundException if the package could not be found.
+     *
+     * @see #isPackageSuspended()
+     */
+    public boolean isPackageSuspended(@NonNull String packageName) throws NameNotFoundException {
+        throw new UnsupportedOperationException("isPackageSuspended not implemented");
+    }
+
+    /**
+     * Apps can query this to know if they have been suspended. A system app with the permission
+     * {@code android.permission.SUSPEND_APPS} can put any app on the device into a suspended state.
+     *
+     * <p>While in this state, the application's notifications will be hidden, any of its started
+     * activities will be stopped and it will not be able to show toasts or dialogs or play audio.
+     * When the user tries to launch a suspended app, the system will, instead, show a
+     * dialog to the user informing them that they cannot use this app while it is suspended.
+     *
+     * <p>When an app is put into this state, the broadcast action
+     * {@link Intent#ACTION_MY_PACKAGE_SUSPENDED} will be delivered to any of its broadcast
+     * receivers that included this action in their intent-filters, <em>including manifest
+     * receivers.</em> Similarly, a broadcast action {@link Intent#ACTION_MY_PACKAGE_UNSUSPENDED}
+     * is delivered when a previously suspended app is taken out of this state. Apps are expected to
+     * use these to gracefully deal with transitions to and from this state.
+     *
+     * @return {@code true} if the calling package has been suspended, {@code false} otherwise.
+     *
+     * @see #getSuspendedPackageAppExtras()
+     * @see Intent#ACTION_MY_PACKAGE_SUSPENDED
+     * @see Intent#ACTION_MY_PACKAGE_UNSUSPENDED
+     */
+    public boolean isPackageSuspended() {
+        throw new UnsupportedOperationException("isPackageSuspended not implemented");
+    }
+
+    /**
+     * Returns a {@link Bundle} of extras that was meant to be sent to the calling app when it was
+     * suspended. An app with the permission {@code android.permission.SUSPEND_APPS} can supply this
+     * to the system at the time of suspending an app.
+     *
+     * <p>This is the same {@link Bundle} that is sent along with the broadcast
+     * {@link Intent#ACTION_MY_PACKAGE_SUSPENDED}, whenever the app is suspended. The contents of
+     * this {@link Bundle} are a contract between the suspended app and the suspending app.
+     *
+     * <p>Note: These extras are optional, so if no extras were supplied to the system, this method
+     * will return {@code null}, even when the calling app has been suspended.
+     *
+     * @return A {@link Bundle} containing the extras for the app, or {@code null} if the
+     * package is not currently suspended.
+     *
+     * @see #isPackageSuspended()
+     * @see Intent#ACTION_MY_PACKAGE_UNSUSPENDED
+     * @see Intent#ACTION_MY_PACKAGE_SUSPENDED
+     * @see Intent#EXTRA_SUSPENDED_PACKAGE_EXTRAS
+     */
+    public @Nullable Bundle getSuspendedPackageAppExtras() {
+        throw new UnsupportedOperationException("getSuspendedPackageAppExtras not implemented");
+    }
+
+    /**
+     * Provide a hint of what the {@link ApplicationInfo#category} value should
+     * be for the given package.
+     * <p>
+     * This hint can only be set by the app which installed this package, as
+     * determined by {@link #getInstallerPackageName(String)}.
+     *
+     * @param packageName the package to change the category hint for.
+     * @param categoryHint the category hint to set.
+     */
+    public abstract void setApplicationCategoryHint(@NonNull String packageName,
+            @ApplicationInfo.Category int categoryHint);
+
+    /** {@hide} */
+    public static boolean isMoveStatusFinished(int status) {
+        return (status < 0 || status > 100);
+    }
+
+    /** {@hide} */
+    public static abstract class MoveCallback {
+        public void onCreated(int moveId, Bundle extras) {}
+        public abstract void onStatusChanged(int moveId, int status, long estMillis);
+    }
+
+    /** {@hide} */
+    @UnsupportedAppUsage
+    public abstract int getMoveStatus(int moveId);
+
+    /** {@hide} */
+    @UnsupportedAppUsage
+    public abstract void registerMoveCallback(@NonNull MoveCallback callback,
+            @NonNull Handler handler);
+    /** {@hide} */
+    @UnsupportedAppUsage
+    public abstract void unregisterMoveCallback(@NonNull MoveCallback callback);
+
+    /** {@hide} */
+    @UnsupportedAppUsage
+    public abstract int movePackage(@NonNull String packageName, @NonNull VolumeInfo vol);
+    /** {@hide} */
+    @UnsupportedAppUsage
+    public abstract @Nullable VolumeInfo getPackageCurrentVolume(@NonNull ApplicationInfo app);
+    /** {@hide} */
+    @NonNull
+    @UnsupportedAppUsage
+    public abstract List<VolumeInfo> getPackageCandidateVolumes(
+            @NonNull ApplicationInfo app);
+
+    /** {@hide} */
+    public abstract int movePrimaryStorage(@NonNull VolumeInfo vol);
+    /** {@hide} */
+    public abstract @Nullable VolumeInfo getPrimaryStorageCurrentVolume();
+    /** {@hide} */
+    public abstract @NonNull List<VolumeInfo> getPrimaryStorageCandidateVolumes();
+
+    /**
+     * Returns the device identity that verifiers can use to associate their scheme to a particular
+     * device. This should not be used by anything other than a package verifier.
+     *
+     * @return identity that uniquely identifies current device
+     * @hide
+     */
+    @NonNull
+    public abstract VerifierDeviceIdentity getVerifierDeviceIdentity();
+
+    /**
+     * Returns true if the device is upgrading, such as first boot after OTA.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public abstract boolean isUpgrade();
+
+    /**
+     * Returns true if the device is upgrading, such as first boot after OTA.
+     */
+    public boolean isDeviceUpgrading() {
+        return false;
+    }
+
+    /**
+     * Return interface that offers the ability to install, upgrade, and remove
+     * applications on the device.
+     */
+    public abstract @NonNull PackageInstaller getPackageInstaller();
+
+    /**
+     * Adds a {@code CrossProfileIntentFilter}. After calling this method all
+     * intents sent from the user with id sourceUserId can also be be resolved
+     * by activities in the user with id targetUserId if they match the
+     * specified intent filter.
+     *
+     * @param filter The {@link IntentFilter} the intent has to match
+     * @param sourceUserId The source user id.
+     * @param targetUserId The target user id.
+     * @param flags The possible values are {@link #SKIP_CURRENT_PROFILE} and
+     *            {@link #ONLY_IF_NO_MATCH_FOUND}.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public abstract void addCrossProfileIntentFilter(@NonNull IntentFilter filter,
+            @UserIdInt int sourceUserId, @UserIdInt int targetUserId, int flags);
+
+    /**
+     * Clearing {@code CrossProfileIntentFilter}s which have the specified user
+     * as their source, and have been set by the app calling this method.
+     *
+     * @param sourceUserId The source user id.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public abstract void clearCrossProfileIntentFilters(@UserIdInt int sourceUserId);
+
+    /**
+     * @hide
+     */
+    @NonNull
+    @UnsupportedAppUsage
+    public abstract Drawable loadItemIcon(@NonNull PackageItemInfo itemInfo,
+            @Nullable ApplicationInfo appInfo);
+
+    /**
+     * @hide
+     */
+    @NonNull
+    @UnsupportedAppUsage
+    public abstract Drawable loadUnbadgedItemIcon(@NonNull PackageItemInfo itemInfo,
+            @Nullable ApplicationInfo appInfo);
+
+    /** {@hide} */
+    @UnsupportedAppUsage
+    public abstract boolean isPackageAvailable(@NonNull String packageName);
+
+    /** {@hide} */
+    @NonNull
+    @UnsupportedAppUsage
+    public static String installStatusToString(int status, @Nullable String msg) {
+        final String str = installStatusToString(status);
+        if (msg != null) {
+            return str + ": " + msg;
+        } else {
+            return str;
+        }
+    }
+
+    /** {@hide} */
+    @NonNull
+    @UnsupportedAppUsage
+    public static String installStatusToString(int status) {
+        switch (status) {
+            case INSTALL_SUCCEEDED: return "INSTALL_SUCCEEDED";
+            case INSTALL_FAILED_ALREADY_EXISTS: return "INSTALL_FAILED_ALREADY_EXISTS";
+            case INSTALL_FAILED_INVALID_APK: return "INSTALL_FAILED_INVALID_APK";
+            case INSTALL_FAILED_INVALID_URI: return "INSTALL_FAILED_INVALID_URI";
+            case INSTALL_FAILED_INSUFFICIENT_STORAGE: return "INSTALL_FAILED_INSUFFICIENT_STORAGE";
+            case INSTALL_FAILED_DUPLICATE_PACKAGE: return "INSTALL_FAILED_DUPLICATE_PACKAGE";
+            case INSTALL_FAILED_NO_SHARED_USER: return "INSTALL_FAILED_NO_SHARED_USER";
+            case INSTALL_FAILED_UPDATE_INCOMPATIBLE: return "INSTALL_FAILED_UPDATE_INCOMPATIBLE";
+            case INSTALL_FAILED_SHARED_USER_INCOMPATIBLE: return "INSTALL_FAILED_SHARED_USER_INCOMPATIBLE";
+            case INSTALL_FAILED_MISSING_SHARED_LIBRARY: return "INSTALL_FAILED_MISSING_SHARED_LIBRARY";
+            case INSTALL_FAILED_REPLACE_COULDNT_DELETE: return "INSTALL_FAILED_REPLACE_COULDNT_DELETE";
+            case INSTALL_FAILED_DEXOPT: return "INSTALL_FAILED_DEXOPT";
+            case INSTALL_FAILED_OLDER_SDK: return "INSTALL_FAILED_OLDER_SDK";
+            case INSTALL_FAILED_CONFLICTING_PROVIDER: return "INSTALL_FAILED_CONFLICTING_PROVIDER";
+            case INSTALL_FAILED_NEWER_SDK: return "INSTALL_FAILED_NEWER_SDK";
+            case INSTALL_FAILED_TEST_ONLY: return "INSTALL_FAILED_TEST_ONLY";
+            case INSTALL_FAILED_CPU_ABI_INCOMPATIBLE: return "INSTALL_FAILED_CPU_ABI_INCOMPATIBLE";
+            case INSTALL_FAILED_MISSING_FEATURE: return "INSTALL_FAILED_MISSING_FEATURE";
+            case INSTALL_FAILED_CONTAINER_ERROR: return "INSTALL_FAILED_CONTAINER_ERROR";
+            case INSTALL_FAILED_INVALID_INSTALL_LOCATION: return "INSTALL_FAILED_INVALID_INSTALL_LOCATION";
+            case INSTALL_FAILED_MEDIA_UNAVAILABLE: return "INSTALL_FAILED_MEDIA_UNAVAILABLE";
+            case INSTALL_FAILED_VERIFICATION_TIMEOUT: return "INSTALL_FAILED_VERIFICATION_TIMEOUT";
+            case INSTALL_FAILED_VERIFICATION_FAILURE: return "INSTALL_FAILED_VERIFICATION_FAILURE";
+            case INSTALL_FAILED_PACKAGE_CHANGED: return "INSTALL_FAILED_PACKAGE_CHANGED";
+            case INSTALL_FAILED_UID_CHANGED: return "INSTALL_FAILED_UID_CHANGED";
+            case INSTALL_FAILED_VERSION_DOWNGRADE: return "INSTALL_FAILED_VERSION_DOWNGRADE";
+            case INSTALL_PARSE_FAILED_NOT_APK: return "INSTALL_PARSE_FAILED_NOT_APK";
+            case INSTALL_PARSE_FAILED_BAD_MANIFEST: return "INSTALL_PARSE_FAILED_BAD_MANIFEST";
+            case INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION: return "INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION";
+            case INSTALL_PARSE_FAILED_NO_CERTIFICATES: return "INSTALL_PARSE_FAILED_NO_CERTIFICATES";
+            case INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES: return "INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES";
+            case INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING: return "INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING";
+            case INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME: return "INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME";
+            case INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID: return "INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID";
+            case INSTALL_PARSE_FAILED_MANIFEST_MALFORMED: return "INSTALL_PARSE_FAILED_MANIFEST_MALFORMED";
+            case INSTALL_PARSE_FAILED_MANIFEST_EMPTY: return "INSTALL_PARSE_FAILED_MANIFEST_EMPTY";
+            case INSTALL_FAILED_INTERNAL_ERROR: return "INSTALL_FAILED_INTERNAL_ERROR";
+            case INSTALL_FAILED_USER_RESTRICTED: return "INSTALL_FAILED_USER_RESTRICTED";
+            case INSTALL_FAILED_DUPLICATE_PERMISSION: return "INSTALL_FAILED_DUPLICATE_PERMISSION";
+            case INSTALL_FAILED_NO_MATCHING_ABIS: return "INSTALL_FAILED_NO_MATCHING_ABIS";
+            case INSTALL_FAILED_ABORTED: return "INSTALL_FAILED_ABORTED";
+            case INSTALL_FAILED_BAD_DEX_METADATA: return "INSTALL_FAILED_BAD_DEX_METADATA";
+            case INSTALL_FAILED_MISSING_SPLIT: return "INSTALL_FAILED_MISSING_SPLIT";
+            case INSTALL_FAILED_BAD_SIGNATURE: return "INSTALL_FAILED_BAD_SIGNATURE";
+            case INSTALL_FAILED_WRONG_INSTALLED_VERSION: return "INSTALL_FAILED_WRONG_INSTALLED_VERSION";
+            case INSTALL_FAILED_PROCESS_NOT_DEFINED: return "INSTALL_FAILED_PROCESS_NOT_DEFINED";
+            default: return Integer.toString(status);
+        }
+    }
+
+    /** {@hide} */
+    public static int installStatusToPublicStatus(int status) {
+        switch (status) {
+            case INSTALL_SUCCEEDED: return PackageInstaller.STATUS_SUCCESS;
+            case INSTALL_FAILED_ALREADY_EXISTS: return PackageInstaller.STATUS_FAILURE_CONFLICT;
+            case INSTALL_FAILED_INVALID_APK: return PackageInstaller.STATUS_FAILURE_INVALID;
+            case INSTALL_FAILED_INVALID_URI: return PackageInstaller.STATUS_FAILURE_INVALID;
+            case INSTALL_FAILED_INSUFFICIENT_STORAGE: return PackageInstaller.STATUS_FAILURE_STORAGE;
+            case INSTALL_FAILED_DUPLICATE_PACKAGE: return PackageInstaller.STATUS_FAILURE_CONFLICT;
+            case INSTALL_FAILED_NO_SHARED_USER: return PackageInstaller.STATUS_FAILURE_CONFLICT;
+            case INSTALL_FAILED_UPDATE_INCOMPATIBLE: return PackageInstaller.STATUS_FAILURE_CONFLICT;
+            case INSTALL_FAILED_SHARED_USER_INCOMPATIBLE: return PackageInstaller.STATUS_FAILURE_CONFLICT;
+            case INSTALL_FAILED_MISSING_SHARED_LIBRARY: return PackageInstaller.STATUS_FAILURE_INCOMPATIBLE;
+            case INSTALL_FAILED_REPLACE_COULDNT_DELETE: return PackageInstaller.STATUS_FAILURE_CONFLICT;
+            case INSTALL_FAILED_DEXOPT: return PackageInstaller.STATUS_FAILURE_INVALID;
+            case INSTALL_FAILED_OLDER_SDK: return PackageInstaller.STATUS_FAILURE_INCOMPATIBLE;
+            case INSTALL_FAILED_CONFLICTING_PROVIDER: return PackageInstaller.STATUS_FAILURE_CONFLICT;
+            case INSTALL_FAILED_NEWER_SDK: return PackageInstaller.STATUS_FAILURE_INCOMPATIBLE;
+            case INSTALL_FAILED_TEST_ONLY: return PackageInstaller.STATUS_FAILURE_INVALID;
+            case INSTALL_FAILED_CPU_ABI_INCOMPATIBLE: return PackageInstaller.STATUS_FAILURE_INCOMPATIBLE;
+            case INSTALL_FAILED_MISSING_FEATURE: return PackageInstaller.STATUS_FAILURE_INCOMPATIBLE;
+            case INSTALL_FAILED_CONTAINER_ERROR: return PackageInstaller.STATUS_FAILURE_STORAGE;
+            case INSTALL_FAILED_INVALID_INSTALL_LOCATION: return PackageInstaller.STATUS_FAILURE_STORAGE;
+            case INSTALL_FAILED_MEDIA_UNAVAILABLE: return PackageInstaller.STATUS_FAILURE_STORAGE;
+            case INSTALL_FAILED_VERIFICATION_TIMEOUT: return PackageInstaller.STATUS_FAILURE_ABORTED;
+            case INSTALL_FAILED_VERIFICATION_FAILURE: return PackageInstaller.STATUS_FAILURE_ABORTED;
+            case INSTALL_FAILED_PACKAGE_CHANGED: return PackageInstaller.STATUS_FAILURE_INVALID;
+            case INSTALL_FAILED_UID_CHANGED: return PackageInstaller.STATUS_FAILURE_INVALID;
+            case INSTALL_FAILED_VERSION_DOWNGRADE: return PackageInstaller.STATUS_FAILURE_INVALID;
+            case INSTALL_FAILED_PERMISSION_MODEL_DOWNGRADE: return PackageInstaller.STATUS_FAILURE_INVALID;
+            case INSTALL_PARSE_FAILED_NOT_APK: return PackageInstaller.STATUS_FAILURE_INVALID;
+            case INSTALL_PARSE_FAILED_BAD_MANIFEST: return PackageInstaller.STATUS_FAILURE_INVALID;
+            case INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION: return PackageInstaller.STATUS_FAILURE_INVALID;
+            case INSTALL_PARSE_FAILED_NO_CERTIFICATES: return PackageInstaller.STATUS_FAILURE_INVALID;
+            case INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES: return PackageInstaller.STATUS_FAILURE_INVALID;
+            case INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING: return PackageInstaller.STATUS_FAILURE_INVALID;
+            case INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME: return PackageInstaller.STATUS_FAILURE_INVALID;
+            case INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID: return PackageInstaller.STATUS_FAILURE_INVALID;
+            case INSTALL_PARSE_FAILED_MANIFEST_MALFORMED: return PackageInstaller.STATUS_FAILURE_INVALID;
+            case INSTALL_PARSE_FAILED_MANIFEST_EMPTY: return PackageInstaller.STATUS_FAILURE_INVALID;
+            case INSTALL_FAILED_BAD_DEX_METADATA: return PackageInstaller.STATUS_FAILURE_INVALID;
+            case INSTALL_FAILED_BAD_SIGNATURE: return PackageInstaller.STATUS_FAILURE_INVALID;
+            case INSTALL_FAILED_INTERNAL_ERROR: return PackageInstaller.STATUS_FAILURE;
+            case INSTALL_FAILED_USER_RESTRICTED: return PackageInstaller.STATUS_FAILURE_INCOMPATIBLE;
+            case INSTALL_FAILED_DUPLICATE_PERMISSION: return PackageInstaller.STATUS_FAILURE_CONFLICT;
+            case INSTALL_FAILED_NO_MATCHING_ABIS: return PackageInstaller.STATUS_FAILURE_INCOMPATIBLE;
+            case INSTALL_FAILED_ABORTED: return PackageInstaller.STATUS_FAILURE_ABORTED;
+            case INSTALL_FAILED_MISSING_SPLIT: return PackageInstaller.STATUS_FAILURE_INCOMPATIBLE;
+            default: return PackageInstaller.STATUS_FAILURE;
+        }
+    }
+
+    /** {@hide} */
+    @NonNull
+    public static String deleteStatusToString(int status, @Nullable String msg) {
+        final String str = deleteStatusToString(status);
+        if (msg != null) {
+            return str + ": " + msg;
+        } else {
+            return str;
+        }
+    }
+
+    /** {@hide} */
+    @NonNull
+    @UnsupportedAppUsage
+    public static String deleteStatusToString(int status) {
+        switch (status) {
+            case DELETE_SUCCEEDED: return "DELETE_SUCCEEDED";
+            case DELETE_FAILED_INTERNAL_ERROR: return "DELETE_FAILED_INTERNAL_ERROR";
+            case DELETE_FAILED_DEVICE_POLICY_MANAGER: return "DELETE_FAILED_DEVICE_POLICY_MANAGER";
+            case DELETE_FAILED_USER_RESTRICTED: return "DELETE_FAILED_USER_RESTRICTED";
+            case DELETE_FAILED_OWNER_BLOCKED: return "DELETE_FAILED_OWNER_BLOCKED";
+            case DELETE_FAILED_ABORTED: return "DELETE_FAILED_ABORTED";
+            case DELETE_FAILED_USED_SHARED_LIBRARY: return "DELETE_FAILED_USED_SHARED_LIBRARY";
+            default: return Integer.toString(status);
+        }
+    }
+
+    /** {@hide} */
+    public static int deleteStatusToPublicStatus(int status) {
+        switch (status) {
+            case DELETE_SUCCEEDED: return PackageInstaller.STATUS_SUCCESS;
+            case DELETE_FAILED_INTERNAL_ERROR: return PackageInstaller.STATUS_FAILURE;
+            case DELETE_FAILED_DEVICE_POLICY_MANAGER: return PackageInstaller.STATUS_FAILURE_BLOCKED;
+            case DELETE_FAILED_USER_RESTRICTED: return PackageInstaller.STATUS_FAILURE_BLOCKED;
+            case DELETE_FAILED_OWNER_BLOCKED: return PackageInstaller.STATUS_FAILURE_BLOCKED;
+            case DELETE_FAILED_ABORTED: return PackageInstaller.STATUS_FAILURE_ABORTED;
+            case DELETE_FAILED_USED_SHARED_LIBRARY: return PackageInstaller.STATUS_FAILURE_CONFLICT;
+            default: return PackageInstaller.STATUS_FAILURE;
+        }
+    }
+
+    /** {@hide} */
+    @NonNull
+    public static String permissionFlagToString(int flag) {
+        switch (flag) {
+            case FLAG_PERMISSION_GRANTED_BY_DEFAULT: return "GRANTED_BY_DEFAULT";
+            case FLAG_PERMISSION_POLICY_FIXED: return "POLICY_FIXED";
+            case FLAG_PERMISSION_SYSTEM_FIXED: return "SYSTEM_FIXED";
+            case FLAG_PERMISSION_USER_SET: return "USER_SET";
+            case FLAG_PERMISSION_USER_FIXED: return "USER_FIXED";
+            case FLAG_PERMISSION_REVIEW_REQUIRED: return "REVIEW_REQUIRED";
+            case FLAG_PERMISSION_REVOKE_WHEN_REQUESTED: return "REVOKE_WHEN_REQUESTED";
+            case FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED: return "USER_SENSITIVE_WHEN_GRANTED";
+            case FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED: return "USER_SENSITIVE_WHEN_DENIED";
+            case FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT: return "RESTRICTION_INSTALLER_EXEMPT";
+            case FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT: return "RESTRICTION_SYSTEM_EXEMPT";
+            case FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT: return "RESTRICTION_UPGRADE_EXEMPT";
+            case FLAG_PERMISSION_APPLY_RESTRICTION: return "APPLY_RESTRICTION";
+            case FLAG_PERMISSION_GRANTED_BY_ROLE: return "GRANTED_BY_ROLE";
+            case FLAG_PERMISSION_REVOKED_COMPAT: return "REVOKED_COMPAT";
+            case FLAG_PERMISSION_ONE_TIME: return "ONE_TIME";
+            case FLAG_PERMISSION_AUTO_REVOKED: return "AUTO_REVOKED";
+            default: return Integer.toString(flag);
+        }
+    }
+
+    /** {@hide} */
+    public static class LegacyPackageDeleteObserver extends PackageDeleteObserver {
+        private final IPackageDeleteObserver mLegacy;
+
+        public LegacyPackageDeleteObserver(IPackageDeleteObserver legacy) {
+            mLegacy = legacy;
+        }
+
+        @Override
+        public void onPackageDeleted(String basePackageName, int returnCode, String msg) {
+            if (mLegacy == null) return;
+            try {
+                mLegacy.packageDeleted(basePackageName, returnCode);
+            } catch (RemoteException ignored) {
+            }
+        }
+    }
+
+    /**
+     * Return the install reason that was recorded when a package was first
+     * installed for a specific user. Requesting the install reason for another
+     * user will require the permission INTERACT_ACROSS_USERS_FULL.
+     *
+     * @param packageName The package for which to retrieve the install reason
+     * @param user The user for whom to retrieve the install reason
+     * @return The install reason. If the package is not installed for the given
+     *         user, {@code INSTALL_REASON_UNKNOWN} is returned.
+     * @hide
+     */
+    @TestApi
+    @InstallReason
+    public abstract int getInstallReason(@NonNull String packageName, @NonNull UserHandle user);
+
+    /**
+     * Checks whether the calling package is allowed to request package installs through package
+     * installer. Apps are encouraged to call this API before launching the package installer via
+     * intent {@link android.content.Intent#ACTION_INSTALL_PACKAGE}. Starting from Android O, the
+     * user can explicitly choose what external sources they trust to install apps on the device.
+     * If this API returns false, the install request will be blocked by the package installer and
+     * a dialog will be shown to the user with an option to launch settings to change their
+     * preference. An application must target Android O or higher and declare permission
+     * {@link android.Manifest.permission#REQUEST_INSTALL_PACKAGES} in order to use this API.
+     *
+     * @return true if the calling package is trusted by the user to request install packages on
+     * the device, false otherwise.
+     * @see android.content.Intent#ACTION_INSTALL_PACKAGE
+     * @see android.provider.Settings#ACTION_MANAGE_UNKNOWN_APP_SOURCES
+     */
+    public abstract boolean canRequestPackageInstalls();
+
+    /**
+     * Return the {@link ComponentName} of the activity providing Settings for the Instant App
+     * resolver.
+     *
+     * @see {@link android.content.Intent#ACTION_INSTANT_APP_RESOLVER_SETTINGS}
+     * @hide
+     */
+    @Nullable
+    @SystemApi
+    public abstract ComponentName getInstantAppResolverSettingsComponent();
+
+    /**
+     * Return the {@link ComponentName} of the activity responsible for installing instant
+     * applications.
+     *
+     * @see {@link android.content.Intent#ACTION_INSTALL_INSTANT_APP_PACKAGE}
+     * @hide
+     */
+    @Nullable
+    @SystemApi
+    public abstract ComponentName getInstantAppInstallerComponent();
+
+    /**
+     * Return the Android Id for a given Instant App.
+     *
+     * @see {@link android.provider.Settings.Secure#ANDROID_ID}
+     * @hide
+     */
+    @Nullable
+    public abstract String getInstantAppAndroidId(@NonNull String packageName,
+            @NonNull UserHandle user);
+
+    /**
+     * Callback use to notify the callers of module registration that the operation
+     * has finished.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static abstract class DexModuleRegisterCallback {
+        public abstract void onDexModuleRegistered(String dexModulePath, boolean success,
+                String message);
+    }
+
+    /**
+     * Register an application dex module with the package manager.
+     * The package manager will keep track of the given module for future optimizations.
+     *
+     * Dex module optimizations will disable the classpath checking at runtime. The client bares
+     * the responsibility to ensure that the static assumptions on classes in the optimized code
+     * hold at runtime (e.g. there's no duplicate classes in the classpath).
+     *
+     * Note that the package manager already keeps track of dex modules loaded with
+     * {@link dalvik.system.DexClassLoader} and {@link dalvik.system.PathClassLoader}.
+     * This can be called for an eager registration.
+     *
+     * The call might take a while and the results will be posted on the main thread, using
+     * the given callback.
+     *
+     * If the module is intended to be shared with other apps, make sure that the file
+     * permissions allow for it.
+     * If at registration time the permissions allow for others to read it, the module would
+     * be marked as a shared module which might undergo a different optimization strategy.
+     * (usually shared modules will generated larger optimizations artifacts,
+     * taking more disk space).
+     *
+     * @param dexModulePath the absolute path of the dex module.
+     * @param callback if not null, {@link DexModuleRegisterCallback#onDexModuleRegistered} will
+     *                 be called once the registration finishes.
+     *
+     * @hide
+     */
+    @SystemApi
+    public abstract void registerDexModule(@NonNull String dexModulePath,
+            @Nullable DexModuleRegisterCallback callback);
+
+    /**
+     * Returns the {@link ArtManager} associated with this package manager.
+     *
+     * @hide
+     */
+    @SystemApi
+    public @NonNull ArtManager getArtManager() {
+        throw new UnsupportedOperationException("getArtManager not implemented in subclass");
+    }
+
+    /**
+     * Sets or clears the harmful app warning details for the given app.
+     *
+     * When set, any attempt to launch an activity in this package will be intercepted and a
+     * warning dialog will be shown to the user instead, with the given warning. The user
+     * will have the option to proceed with the activity launch, or to uninstall the application.
+     *
+     * @param packageName The full name of the package to warn on.
+     * @param warning A warning string to display to the user describing the threat posed by the
+     *                application, or null to clear the warning.
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.SET_HARMFUL_APP_WARNINGS)
+    @SystemApi
+    public void setHarmfulAppWarning(@NonNull String packageName, @Nullable CharSequence warning) {
+        throw new UnsupportedOperationException("setHarmfulAppWarning not implemented in subclass");
+    }
+
+    /**
+     * Returns the harmful app warning string for the given app, or null if there is none set.
+     *
+     * @param packageName The full name of the desired package.
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.SET_HARMFUL_APP_WARNINGS)
+    @Nullable
+    @SystemApi
+    public CharSequence getHarmfulAppWarning(@NonNull String packageName) {
+        throw new UnsupportedOperationException("getHarmfulAppWarning not implemented in subclass");
+    }
+
+    /** @hide */
+    @IntDef(prefix = { "CERT_INPUT_" }, value = {
+            CERT_INPUT_RAW_X509,
+            CERT_INPUT_SHA256
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface CertificateInputType {}
+
+    /**
+     * Certificate input bytes: the input bytes represent an encoded X.509 Certificate which could
+     * be generated using an {@code CertificateFactory}
+     */
+    public static final int CERT_INPUT_RAW_X509 = 0;
+
+    /**
+     * Certificate input bytes: the input bytes represent the SHA256 output of an encoded X.509
+     * Certificate.
+     */
+    public static final int CERT_INPUT_SHA256 = 1;
+
+    /**
+     * Searches the set of signing certificates by which the given package has proven to have been
+     * signed.  This should be used instead of {@code getPackageInfo} with {@code GET_SIGNATURES}
+     * since it takes into account the possibility of signing certificate rotation, except in the
+     * case of packages that are signed by multiple certificates, for which signing certificate
+     * rotation is not supported.  This method is analogous to using {@code getPackageInfo} with
+     * {@code GET_SIGNING_CERTIFICATES} and then searching through the resulting {@code
+     * signingInfo} field to see if the desired certificate is present.
+     *
+     * @param packageName package whose signing certificates to check
+     * @param certificate signing certificate for which to search
+     * @param type representation of the {@code certificate}
+     * @return true if this package was or is signed by exactly the certificate {@code certificate}
+     */
+    public boolean hasSigningCertificate(@NonNull String packageName, @NonNull byte[] certificate,
+            @CertificateInputType int type) {
+        throw new UnsupportedOperationException(
+                "hasSigningCertificate not implemented in subclass");
+    }
+
+    /**
+     * Searches the set of signing certificates by which the package(s) for the given uid has proven
+     * to have been signed.  For multiple packages sharing the same uid, this will return the
+     * signing certificates found in the signing history of the "newest" package, where "newest"
+     * indicates the package with the newest signing certificate in the shared uid group.  This
+     * method should be used instead of {@code getPackageInfo} with {@code GET_SIGNATURES}
+     * since it takes into account the possibility of signing certificate rotation, except in the
+     * case of packages that are signed by multiple certificates, for which signing certificate
+     * rotation is not supported. This method is analogous to using {@code getPackagesForUid}
+     * followed by {@code getPackageInfo} with {@code GET_SIGNING_CERTIFICATES}, selecting the
+     * {@code PackageInfo} of the newest-signed bpackage , and finally searching through the
+     * resulting {@code signingInfo} field to see if the desired certificate is there.
+     *
+     * @param uid uid whose signing certificates to check
+     * @param certificate signing certificate for which to search
+     * @param type representation of the {@code certificate}
+     * @return true if this package was or is signed by exactly the certificate {@code certificate}
+     */
+    public boolean hasSigningCertificate(
+            int uid, @NonNull byte[] certificate, @CertificateInputType int type) {
+        throw new UnsupportedOperationException(
+                "hasSigningCertificate not implemented in subclass");
+    }
+
+    /**
+     * @return the default text classifier package name, or null if there's none.
+     *
+     * @hide
+     */
+    @Nullable
+    @TestApi
+    public String getDefaultTextClassifierPackageName() {
+        throw new UnsupportedOperationException(
+                "getDefaultTextClassifierPackageName not implemented in subclass");
+    }
+
+    /**
+     * @return the system defined text classifier package names, or null if there's none.
+     *
+     * @hide
+     */
+    @Nullable
+    @TestApi
+    public String getSystemTextClassifierPackageName() {
+        throw new UnsupportedOperationException(
+                "getSystemTextClassifierPackageName not implemented in subclass");
+    }
+
+    /**
+     * @return  attention service package name, or null if there's none.
+     *
+     * @hide
+     */
+    public String getAttentionServicePackageName() {
+        throw new UnsupportedOperationException(
+                "getAttentionServicePackageName not implemented in subclass");
+    }
+
+    /**
+     * @return the wellbeing app package name, or null if it's not defined by the OEM.
+     *
+     * @hide
+     */
+    @Nullable
+    @TestApi
+    public String getWellbeingPackageName() {
+        throw new UnsupportedOperationException(
+                "getWellbeingPackageName not implemented in subclass");
+    }
+
+    /**
+     * @return the system defined app predictor package name, or null if there's none.
+     *
+     * @hide
+     */
+    @Nullable
+    public String getAppPredictionServicePackageName() {
+        throw new UnsupportedOperationException(
+            "getAppPredictionServicePackageName not implemented in subclass");
+    }
+
+    /**
+     * @return the system defined content capture service package name, or null if there's none.
+     *
+     * @hide
+     */
+    @Nullable
+    public String getSystemCaptionsServicePackageName() {
+        throw new UnsupportedOperationException(
+                "getSystemCaptionsServicePackageName not implemented in subclass");
+    }
+
+    /**
+     * @return the system defined setup wizard package name, or null if there's none.
+     *
+     * @hide
+     */
+    @Nullable
+    public String getSetupWizardPackageName() {
+        throw new UnsupportedOperationException(
+                "getSetupWizardPackageName not implemented in subclass");
+    }
+
+    /**
+     * @return the system defined content capture package name, or null if there's none.
+     *
+     * @hide
+     */
+    @TestApi
+    @Nullable
+    public String getContentCaptureServicePackageName() {
+        throw new UnsupportedOperationException(
+                "getContentCaptureServicePackageName not implemented in subclass");
+    }
+
+    /**
+     * @return the incident report approver app package name, or null if it's not defined
+     * by the OEM.
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    @Nullable
+    public String getIncidentReportApproverPackageName() {
+        throw new UnsupportedOperationException(
+                "getIncidentReportApproverPackageName not implemented in subclass");
+    }
+
+    /**
+     * @return whether a given package's state is protected, e.g. package cannot be disabled,
+     *         suspended, hidden or force stopped.
+     *
+     * @hide
+     */
+    public boolean isPackageStateProtected(@NonNull String packageName, @UserIdInt int userId) {
+        throw new UnsupportedOperationException(
+            "isPackageStateProtected not implemented in subclass");
+    }
+
+    /**
+     * Notify to the rest of the system that a new device configuration has
+     * been prepared and that it is time to refresh caches.
+     *
+     * @see android.content.Intent#ACTION_DEVICE_CUSTOMIZATION_READY
+     *
+     * @hide
+     */
+    @SystemApi
+    public void sendDeviceCustomizationReadyBroadcast() {
+        throw new UnsupportedOperationException(
+            "sendDeviceCustomizationReadyBroadcast not implemented in subclass");
+    }
+
+    /**
+     * @return whether this package is whitelisted from having its runtime permission be
+     *         auto-revoked if unused for an extended period of time.
+     */
+    public boolean isAutoRevokeWhitelisted() {
+        throw new UnsupportedOperationException(
+                "isAutoRevokeWhitelisted not implemented in subclass");
+    }
+
+    /**
+     * Returns if the provided drawable represents the default activity icon provided by the system.
+     *
+     * PackageManager silently returns a default application icon for any package/activity if the
+     * app itself does not define one or if the system encountered any error when loading the icon.
+     *
+     * Developers can use this to check implement app specific logic around retrying or caching.
+     *
+     * @return true if the drawable represents the default activity icon, false otherwise
+     * @see #getDefaultActivityIcon()
+     * @see #getActivityIcon
+     * @see LauncherActivityInfo#getIcon(int)
+     */
+    public boolean isDefaultApplicationIcon(@NonNull Drawable drawable) {
+        int resId = drawable instanceof AdaptiveIconDrawable
+                ? ((AdaptiveIconDrawable) drawable).getSourceDrawableResId() : Resources.ID_NULL;
+        return resId == com.android.internal.R.drawable.sym_def_app_icon
+                || resId == com.android.internal.R.drawable.sym_app_on_sd_unavailable_icon;
+    }
+
+    /**
+     * Sets MIME group's MIME types.
+     *
+     * Libraries should use a reverse-DNS prefix followed by a ':' character and library-specific
+     * group name to avoid namespace collisions, e.g. "com.example:myFeature".
+     *
+     * @param mimeGroup MIME group to modify.
+     * @param mimeTypes new MIME types contained by MIME group.
+     * @throws IllegalArgumentException if the MIME group was not declared in the manifest.
+     */
+    public void setMimeGroup(@NonNull String mimeGroup, @NonNull Set<String> mimeTypes) {
+        throw new UnsupportedOperationException(
+                "setMimeGroup not implemented in subclass");
+    }
+
+    /**
+     * Gets all MIME types contained by MIME group.
+     *
+     * Libraries should use a reverse-DNS prefix followed by a ':' character and library-specific
+     * group name to avoid namespace collisions, e.g. "com.example:myFeature".
+     *
+     * @param mimeGroup MIME group to retrieve.
+     * @return MIME types contained by the MIME group.
+     * @throws IllegalArgumentException if the MIME group was not declared in the manifest.
+     */
+    @NonNull
+    public Set<String> getMimeGroup(@NonNull String mimeGroup) {
+        throw new UnsupportedOperationException(
+                "getMimeGroup not implemented in subclass");
+    }
+
+    // Some of the flags don't affect the query result, but let's be conservative and cache
+    // each combination of flags separately.
+
+    private static final class ApplicationInfoQuery {
+        final String packageName;
+        final int flags;
+        final int userId;
+
+        ApplicationInfoQuery(@Nullable String packageName, int flags, int userId) {
+            this.packageName = packageName;
+            this.flags = flags;
+            this.userId = userId;
+        }
+
+        @Override
+        public String toString() {
+            return String.format(
+                    "ApplicationInfoQuery(packageName=\"%s\", flags=%s, userId=%s)",
+                    packageName, flags, userId);
+        }
+
+        @Override
+        public int hashCode() {
+            int hash = Objects.hashCode(packageName);
+            hash = hash * 13 + Objects.hashCode(flags);
+            hash = hash * 13 + Objects.hashCode(userId);
+            return hash;
+        }
+
+        @Override
+        public boolean equals(Object rval) {
+            if (rval == null) {
+                return false;
+            }
+            ApplicationInfoQuery other;
+            try {
+                other = (ApplicationInfoQuery) rval;
+            } catch (ClassCastException ex) {
+                return false;
+            }
+            return Objects.equals(packageName, other.packageName)
+                    && flags == other.flags
+                    && userId == other.userId;
+        }
+    }
+
+    private static ApplicationInfo getApplicationInfoAsUserUncached(
+            String packageName, int flags, int userId) {
+        try {
+            return ActivityThread.getPackageManager()
+                    .getApplicationInfo(packageName, flags, userId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    private static final PropertyInvalidatedCache<ApplicationInfoQuery, ApplicationInfo>
+            sApplicationInfoCache =
+            new PropertyInvalidatedCache<ApplicationInfoQuery, ApplicationInfo>(
+                    16, PermissionManager.CACHE_KEY_PACKAGE_INFO) {
+                @Override
+                protected ApplicationInfo recompute(ApplicationInfoQuery query) {
+                    return getApplicationInfoAsUserUncached(
+                            query.packageName, query.flags, query.userId);
+                }
+                @Override
+                protected ApplicationInfo maybeCheckConsistency(
+                        ApplicationInfoQuery query, ApplicationInfo proposedResult) {
+                    // Implementing this debug check for ApplicationInfo would require a
+                    // complicated deep comparison, so just bypass it for now.
+                    return proposedResult;
+                }
+            };
+
+    /** @hide */
+    public static ApplicationInfo getApplicationInfoAsUserCached(
+            String packageName, int flags, int userId) {
+        return sApplicationInfoCache.query(
+                new ApplicationInfoQuery(packageName, flags, userId));
+    }
+
+    /**
+     * Make getApplicationInfoAsUser() bypass the cache in this process.
+     *
+     * @hide
+     */
+    public static void disableApplicationInfoCache() {
+        sApplicationInfoCache.disableLocal();
+    }
+
+    private static final PropertyInvalidatedCache.AutoCorker sCacheAutoCorker =
+            new PropertyInvalidatedCache.AutoCorker(PermissionManager.CACHE_KEY_PACKAGE_INFO);
+
+    /**
+     * Invalidate caches of package and permission information system-wide.
+     *
+     * @hide
+     */
+    public static void invalidatePackageInfoCache() {
+        sCacheAutoCorker.autoCork();
+    }
+
+    // Some of the flags don't affect the query result, but let's be conservative and cache
+    // each combination of flags separately.
+
+    private static final class PackageInfoQuery {
+        final String packageName;
+        final int flags;
+        final int userId;
+
+        PackageInfoQuery(@Nullable String packageName, int flags, int userId) {
+            this.packageName = packageName;
+            this.flags = flags;
+            this.userId = userId;
+        }
+
+        @Override
+        public String toString() {
+            return String.format(
+                    "PackageInfoQuery(packageName=\"%s\", flags=%s, userId=%s)",
+                    packageName, flags, userId);
+        }
+
+        @Override
+        public int hashCode() {
+            int hash = Objects.hashCode(packageName);
+            hash = hash * 13 + Objects.hashCode(flags);
+            hash = hash * 13 + Objects.hashCode(userId);
+            return hash;
+        }
+
+        @Override
+        public boolean equals(Object rval) {
+            if (rval == null) {
+                return false;
+            }
+            PackageInfoQuery other;
+            try {
+                other = (PackageInfoQuery) rval;
+            } catch (ClassCastException ex) {
+                return false;
+            }
+            return Objects.equals(packageName, other.packageName)
+                    && flags == other.flags
+                    && userId == other.userId;
+        }
+    }
+
+    private static PackageInfo getPackageInfoAsUserUncached(
+            String packageName, int flags, int userId) {
+        try {
+            return ActivityThread.getPackageManager().getPackageInfo(packageName, flags, userId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    private static final PropertyInvalidatedCache<PackageInfoQuery, PackageInfo>
+            sPackageInfoCache =
+            new PropertyInvalidatedCache<PackageInfoQuery, PackageInfo>(
+                    16, PermissionManager.CACHE_KEY_PACKAGE_INFO) {
+                @Override
+                protected PackageInfo recompute(PackageInfoQuery query) {
+                    return getPackageInfoAsUserUncached(
+                            query.packageName, query.flags, query.userId);
+                }
+                @Override
+                protected PackageInfo maybeCheckConsistency(
+                        PackageInfoQuery query, PackageInfo proposedResult) {
+                    // Implementing this debug check for PackageInfo would require a
+                    // complicated deep comparison, so just bypass it for now.
+                    return proposedResult;
+                }
+            };
+
+    /** @hide */
+    public static PackageInfo getPackageInfoAsUserCached(
+            String packageName, int flags, int userId) {
+        return sPackageInfoCache.query(new PackageInfoQuery(packageName, flags, userId));
+    }
+
+    /**
+     * Make getPackageInfoAsUser() bypass the cache in this process.
+     * @hide
+     */
+    public static void disablePackageInfoCache() {
+        sPackageInfoCache.disableLocal();
+    }
+
+    /**
+     * Inhibit package info cache invalidations when correct.
+     *
+     * @hide */
+    public static void corkPackageInfoCache() {
+        PropertyInvalidatedCache.corkInvalidations(PermissionManager.CACHE_KEY_PACKAGE_INFO);
+    }
+
+    /**
+     * Enable package info cache invalidations.
+     *
+     * @hide */
+    public static void uncorkPackageInfoCache() {
+        PropertyInvalidatedCache.uncorkInvalidations(PermissionManager.CACHE_KEY_PACKAGE_INFO);
+    }
+}
diff --git a/android/content/pm/PackageManagerInternal.java b/android/content/pm/PackageManagerInternal.java
new file mode 100644
index 0000000..81de29c
--- /dev/null
+++ b/android/content/pm/PackageManagerInternal.java
@@ -0,0 +1,991 @@
+/*
+ * 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.content.pm;
+
+import android.annotation.AppIdInt;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.annotation.WorkerThread;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.content.pm.PackageManager.ApplicationInfoFlags;
+import android.content.pm.PackageManager.ComponentInfoFlags;
+import android.content.pm.PackageManager.PackageInfoFlags;
+import android.content.pm.PackageManager.ResolveInfoFlags;
+import android.content.pm.parsing.component.ParsedMainComponent;
+import android.os.Bundle;
+import android.os.PersistableBundle;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.SparseArray;
+
+import com.android.server.pm.PackageList;
+import com.android.server.pm.PackageSetting;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Collection;
+import java.util.List;
+import java.util.function.Consumer;
+
+/**
+ * Package manager local system service interface.
+ *
+ * @hide Only for use within the system server.
+ */
+public abstract class PackageManagerInternal {
+    public static final int PACKAGE_SYSTEM = 0;
+    public static final int PACKAGE_SETUP_WIZARD = 1;
+    public static final int PACKAGE_INSTALLER = 2;
+    public static final int PACKAGE_VERIFIER = 3;
+    public static final int PACKAGE_BROWSER = 4;
+    public static final int PACKAGE_SYSTEM_TEXT_CLASSIFIER = 5;
+    public static final int PACKAGE_PERMISSION_CONTROLLER = 6;
+    public static final int PACKAGE_WELLBEING = 7;
+    public static final int PACKAGE_DOCUMENTER = 8;
+    public static final int PACKAGE_CONFIGURATOR = 9;
+    public static final int PACKAGE_INCIDENT_REPORT_APPROVER = 10;
+    public static final int PACKAGE_APP_PREDICTOR = 11;
+    public static final int PACKAGE_WIFI = 13;
+    public static final int PACKAGE_COMPANION = 14;
+    public static final int PACKAGE_RETAIL_DEMO = 15;
+
+    @IntDef(flag = true, prefix = "RESOLVE_", value = {
+            RESOLVE_NON_BROWSER_ONLY,
+            RESOLVE_NON_RESOLVER_ONLY
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface PrivateResolveFlags {}
+
+    /**
+     * Internal {@link #resolveIntent(Intent, String, int, int, int, boolean, int)} flag:
+     * only match components that contain a generic web intent filter.
+     */
+    public static final int RESOLVE_NON_BROWSER_ONLY = 0x00000001;
+
+    /**
+     * Internal {@link #resolveIntent(Intent, String, int, int, int, boolean, int)} flag: do not
+     * match to the resolver.
+     */
+    public static final int RESOLVE_NON_RESOLVER_ONLY = 0x00000002;
+
+    @IntDef(value = {
+            INTEGRITY_VERIFICATION_ALLOW,
+            INTEGRITY_VERIFICATION_REJECT,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface IntegrityVerificationResult {}
+
+    /**
+     * Used as the {@code verificationCode} argument for
+     * {@link PackageManagerInternal#setIntegrityVerificationResult(int, int)} to indicate that the
+     * integrity component allows the install to proceed.
+     */
+    public static final int INTEGRITY_VERIFICATION_ALLOW = 1;
+
+    /**
+     * Used as the {@code verificationCode} argument for
+     * {@link PackageManagerInternal#setIntegrityVerificationResult(int, int)} to indicate that the
+     * integrity component does not allow install to proceed.
+     */
+    public static final int INTEGRITY_VERIFICATION_REJECT = 0;
+
+    @IntDef(value = {
+        PACKAGE_SYSTEM,
+        PACKAGE_SETUP_WIZARD,
+        PACKAGE_INSTALLER,
+        PACKAGE_VERIFIER,
+        PACKAGE_BROWSER,
+        PACKAGE_SYSTEM_TEXT_CLASSIFIER,
+        PACKAGE_PERMISSION_CONTROLLER,
+        PACKAGE_WELLBEING,
+        PACKAGE_DOCUMENTER,
+        PACKAGE_CONFIGURATOR,
+        PACKAGE_INCIDENT_REPORT_APPROVER,
+        PACKAGE_APP_PREDICTOR,
+        PACKAGE_WIFI,
+        PACKAGE_COMPANION,
+        PACKAGE_RETAIL_DEMO,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface KnownPackage {}
+
+    /** Observer called whenever the list of packages changes */
+    public interface PackageListObserver {
+        /** A package was added to the system. */
+        void onPackageAdded(@NonNull String packageName, int uid);
+        /** A package was changed - either installed for a specific user or updated. */
+        default void onPackageChanged(@NonNull String packageName, int uid) {}
+        /** A package was removed from the system. */
+        void onPackageRemoved(@NonNull String packageName, int uid);
+    }
+
+    /**
+     * Called when the package for the default SMS handler changed
+     *
+     * @param packageName the new sms package
+     * @param userId user for which the change was made
+     */
+    public void onDefaultSmsAppChanged(String packageName, int userId) {}
+
+    /**
+     * Called when the package for the default sim call manager changed
+     *
+     * @param packageName the new sms package
+     * @param userId user for which the change was made
+     */
+    public void onDefaultSimCallManagerAppChanged(String packageName, int userId) {}
+
+    /**
+     * Sets a list of apps to keep in PM's internal data structures and as APKs even if no user has
+     * currently installed it. The apps are not preloaded.
+     * @param packageList List of package names to keep cached.
+     */
+    public abstract void setKeepUninstalledPackages(List<String> packageList);
+
+    /**
+     * Gets whether some of the permissions used by this package require a user
+     * review before any of the app components can run.
+     * @param packageName The package name for which to check.
+     * @param userId The user under which to check.
+     * @return True a permissions review is required.
+     */
+    public abstract boolean isPermissionsReviewRequired(String packageName, int userId);
+
+    /**
+     * Retrieve all of the information we know about a particular package/application.
+     * @param filterCallingUid The results will be filtered in the context of this UID instead
+     * of the calling UID.
+     * @see PackageManager#getPackageInfo(String, int)
+     */
+    public abstract PackageInfo getPackageInfo(String packageName,
+            @PackageInfoFlags int flags, int filterCallingUid, int userId);
+
+    /**
+     * Retrieve CE data directory inode number of an application.
+     * Return 0 if there's error.
+     */
+    public abstract long getCeDataInode(String packageName, int userId);
+
+    /**
+     * Return a List of all application packages that are installed on the
+     * device, for a specific user. If flag GET_UNINSTALLED_PACKAGES has been
+     * set, a list of all applications including those deleted with
+     * {@code DELETE_KEEP_DATA} (partially installed apps with data directory)
+     * will be returned.
+     *
+     * @param flags Additional option flags to modify the data returned.
+     * @param userId The user for whom the installed applications are to be
+     *            listed
+     * @param callingUid The uid of the original caller app
+     * @return A List of ApplicationInfo objects, one for each installed
+     *         application. In the unlikely case there are no installed
+     *         packages, an empty list is returned. If flag
+     *         {@code MATCH_UNINSTALLED_PACKAGES} is set, the application
+     *         information is retrieved from the list of uninstalled
+     *         applications (which includes installed applications as well as
+     *         applications with data directory i.e. applications which had been
+     *         deleted with {@code DELETE_KEEP_DATA} flag set).
+     */
+    public abstract List<ApplicationInfo> getInstalledApplications(
+            @ApplicationInfoFlags int flags, @UserIdInt int userId, int callingUid);
+
+    /**
+     * Retrieve launcher extras for a suspended package provided to the system in
+     * {@link PackageManager#setPackagesSuspended(String[], boolean, PersistableBundle,
+     * PersistableBundle, String)}.
+     *
+     * @param packageName The package for which to return launcher extras.
+     * @param userId The user for which to check.
+     * @return The launcher extras.
+     *
+     * @see PackageManager#setPackagesSuspended(String[], boolean, PersistableBundle,
+     * PersistableBundle, String)
+     * @see PackageManager#isPackageSuspended()
+     */
+    public abstract Bundle getSuspendedPackageLauncherExtras(String packageName,
+            int userId);
+
+    /**
+     * Internal api to query the suspended state of a package.
+     * @param packageName The package to check.
+     * @param userId The user id to check for.
+     * @return {@code true} if the package is suspended, {@code false} otherwise.
+     * @see PackageManager#isPackageSuspended(String)
+     */
+    public abstract boolean isPackageSuspended(String packageName, int userId);
+
+    /**
+     * Removes all package suspensions imposed by any non-system packages.
+     */
+    public abstract void removeAllNonSystemPackageSuspensions(int userId);
+
+    /**
+     * Removes all suspensions imposed on the given package by non-system packages.
+     */
+    public abstract void removeNonSystemPackageSuspensions(String packageName, int userId);
+
+    /**
+     * Removes all {@link PackageManager.DistractionRestriction restrictions} set on the given
+     * package
+     */
+    public abstract void removeDistractingPackageRestrictions(String packageName, int userId);
+
+    /**
+     * Removes all {@link PackageManager.DistractionRestriction restrictions} set on all the
+     * packages.
+     */
+    public abstract void removeAllDistractingPackageRestrictions(int userId);
+
+    /**
+     * Flushes package restrictions for the given user immediately to disk.
+     */
+    @WorkerThread
+    public abstract void flushPackageRestrictions(int userId);
+
+    /**
+     * Get the name of the package that suspended the given package. Packages can be suspended by
+     * device administrators or apps holding {@link android.Manifest.permission#MANAGE_USERS} or
+     * {@link android.Manifest.permission#SUSPEND_APPS}.
+     *
+     * @param suspendedPackage The package that has been suspended.
+     * @param userId The user for which to check.
+     * @return Name of the package that suspended the given package. Returns {@code null} if the
+     * given package is not currently suspended and the platform package name - i.e.
+     * {@code "android"} - if the package was suspended by a device admin.
+     */
+    public abstract String getSuspendingPackage(String suspendedPackage, int userId);
+
+    /**
+     * Get the information describing the dialog to be shown to the user when they try to launch a
+     * suspended application.
+     *
+     * @param suspendedPackage The package that has been suspended.
+     * @param suspendingPackage
+     * @param userId The user for which to check.
+     * @return A {@link SuspendDialogInfo} object describing the dialog to be shown.
+     */
+    @Nullable
+    public abstract SuspendDialogInfo getSuspendedDialogInfo(String suspendedPackage,
+            String suspendingPackage, int userId);
+
+    /**
+     * Gets any distraction flags set via
+     * {@link PackageManager#setDistractingPackageRestrictions(String[], int)}
+     *
+     * @param packageName
+     * @param userId
+     * @return A bitwise OR of any of the {@link PackageManager.DistractionRestriction}
+     */
+    public abstract @PackageManager.DistractionRestriction int getDistractingPackageRestrictions(
+            String packageName, int userId);
+
+    /**
+     * Do a straight uid lookup for the given package/application in the given user. This enforces
+     * app visibility rules and permissions. Call {@link #getPackageUidInternal} for the internal
+     * implementation.
+     * @deprecated Use {@link PackageManager#getPackageUid(String, int)}
+     * @return The app's uid, or < 0 if the package was not found in that user
+     */
+    @Deprecated
+    public abstract int getPackageUid(String packageName,
+            @PackageInfoFlags int flags, int userId);
+
+    /**
+     * Do a straight uid lookup for the given package/application in the given user.
+     * @see PackageManager#getPackageUidAsUser(String, int, int)
+     * @return The app's uid, or < 0 if the package was not found in that user
+     * TODO(b/148235092): rename this to getPackageUid
+     */
+    public abstract int getPackageUidInternal(String packageName,
+            @PackageInfoFlags int flags, int userId);
+
+    /**
+     * Retrieve all of the information we know about a particular package/application.
+     * @param filterCallingUid The results will be filtered in the context of this UID instead
+     * of the calling UID.
+     * @see PackageManager#getApplicationInfo(String, int)
+     */
+    public abstract ApplicationInfo getApplicationInfo(String packageName,
+            @ApplicationInfoFlags int flags, int filterCallingUid, int userId);
+
+    /**
+     * Retrieve all of the information we know about a particular activity class.
+     * @param filterCallingUid The results will be filtered in the context of this UID instead
+     * of the calling UID.
+     * @see PackageManager#getActivityInfo(ComponentName, int)
+     */
+    public abstract ActivityInfo getActivityInfo(ComponentName component,
+            @ComponentInfoFlags int flags, int filterCallingUid, int userId);
+
+    /**
+     * Retrieve all activities that can be performed for the given intent.
+     * @param resolvedType the resolved type of the intent, which should be resolved via
+     * {@link Intent#resolveTypeIfNeeded(ContentResolver)} before passing to {@link PackageManager}
+     * @param filterCallingUid The results will be filtered in the context of this UID instead
+     * of the calling UID.
+     * @see PackageManager#queryIntentActivities(Intent, int)
+     */
+    public abstract List<ResolveInfo> queryIntentActivities(
+            Intent intent, @Nullable String resolvedType, @ResolveInfoFlags int flags,
+            int filterCallingUid, int userId);
+
+
+    /**
+     * Retrieve all services that can be performed for the given intent.
+     * @see PackageManager#queryIntentServices(Intent, int)
+     */
+    public abstract List<ResolveInfo> queryIntentServices(
+            Intent intent, int flags, int callingUid, int userId);
+
+    /**
+     * Interface to {@link com.android.server.pm.PackageManagerService#getHomeActivitiesAsUser}.
+     */
+    public abstract ComponentName getHomeActivitiesAsUser(List<ResolveInfo> allHomeCandidates,
+            int userId);
+
+    /**
+     * @return The default home activity component name.
+     */
+    public abstract ComponentName getDefaultHomeActivity(int userId);
+
+    /**
+     * @return The SystemUI service component name.
+     */
+    public abstract ComponentName getSystemUiServiceComponent();
+
+    /**
+     * Called by DeviceOwnerManagerService to set the package names of device owner and profile
+     * owners.
+     */
+    public abstract void setDeviceAndProfileOwnerPackages(
+            int deviceOwnerUserId, String deviceOwner, SparseArray<String> profileOwners);
+
+    /**
+     * Called by DevicePolicyManagerService to set the package names protected by the device
+     * owner.
+     */
+    public abstract void setDeviceOwnerProtectedPackages(List<String> packageNames);
+
+    /**
+     * Returns {@code true} if a given package can't be wiped. Otherwise, returns {@code false}.
+     */
+    public abstract boolean isPackageDataProtected(int userId, String packageName);
+
+    /**
+     * Returns {@code true} if a given package's state is protected, e.g. it cannot be force
+     * stopped, suspended, disabled or hidden. Otherwise, returns {@code false}.
+     */
+    public abstract boolean isPackageStateProtected(String packageName, int userId);
+
+    /**
+     * Returns {@code true} if a given package is installed as ephemeral. Otherwise, returns
+     * {@code false}.
+     */
+    public abstract boolean isPackageEphemeral(int userId, String packageName);
+
+    /**
+     * Gets whether the package was ever launched.
+     * @param packageName The package name.
+     * @param userId The user for which to check.
+     * @return Whether was launched.
+     * @throws IllegalArgumentException if the package is not found
+     */
+    public abstract boolean wasPackageEverLaunched(String packageName, int userId);
+
+    /**
+     * Retrieve the official name associated with a uid. This name is
+     * guaranteed to never change, though it is possible for the underlying
+     * uid to be changed. That is, if you are storing information about
+     * uids in persistent storage, you should use the string returned
+     * by this function instead of the raw uid.
+     *
+     * @param uid The uid for which you would like to retrieve a name.
+     * @return Returns a unique name for the given uid, or null if the
+     * uid is not currently assigned.
+     */
+    public abstract String getNameForUid(int uid);
+
+    /**
+     * Request to perform the second phase of ephemeral resolution.
+     * @param responseObj The response of the first phase of ephemeral resolution
+     * @param origIntent The original intent that triggered ephemeral resolution
+     * @param resolvedType The resolved type of the intent
+     * @param callingPkg The app requesting the ephemeral application
+     * @param callingFeatureId The feature in the package
+     * @param isRequesterInstantApp Whether or not the app requesting the ephemeral application
+     *                              is an instant app
+     * @param verificationBundle Optional bundle to pass to the installer for additional
+     * verification
+     * @param userId The ID of the user that triggered ephemeral resolution
+     */
+    public abstract void requestInstantAppResolutionPhaseTwo(AuxiliaryResolveInfo responseObj,
+            Intent origIntent, String resolvedType, String callingPkg,
+            @Nullable String callingFeatureId, boolean isRequesterInstantApp,
+            Bundle verificationBundle, int userId);
+
+    /**
+     * Grants implicit access based on an interaction between two apps. This grants access to the
+     * from one application to the other's package metadata.
+     * <p>
+     * When an application explicitly tries to interact with another application [via an
+     * activity, service or provider that is either declared in the caller's
+     * manifest via the {@code <queries>} tag or has been exposed via the target apps manifest using
+     * the {@code visibleToInstantApp} attribute], the target application must be able to see
+     * metadata about the calling app. If the calling application uses an implicit intent [ie
+     * action VIEW, category BROWSABLE], it remains hidden from the launched app.
+     * <p>
+     * If an interaction is not explicit, the {@code direct} argument should be set to false as
+     * visibility should not be granted in some cases. This method handles that logic.
+     * <p>
+     * @param userId the user
+     * @param intent the intent that triggered the grant
+     * @param recipientAppId The app ID of the application that is being given access to {@code
+     *                       visibleUid}
+     * @param visibleUid The uid of the application that is becoming accessible to {@code
+     *                   recipientAppId}
+     * @param direct true if the access is being made due to direct interaction between visibleUid
+     *               and recipientAppId.
+     */
+    public abstract void grantImplicitAccess(
+            @UserIdInt int userId, Intent intent,
+            @AppIdInt int recipientAppId, int visibleUid,
+            boolean direct);
+
+    public abstract boolean isInstantAppInstallerComponent(ComponentName component);
+    /**
+     * Prunes instant apps and state associated with uninstalled
+     * instant apps according to the current platform policy.
+     */
+    public abstract void pruneInstantApps();
+
+    /**
+     * Prunes the cache of the APKs in the given APEXes.
+     * @param apexPackages The list of APEX packages that may contain APK-in-APEX.
+     */
+    public abstract void pruneCachedApksInApex(@NonNull List<PackageInfo> apexPackages);
+
+    /**
+     * @return The SetupWizard package name.
+     */
+    public abstract String getSetupWizardPackageName();
+
+    public interface ExternalSourcesPolicy {
+
+        int USER_TRUSTED = 0;   // User has trusted the package to install apps
+        int USER_BLOCKED = 1;   // User has blocked the package to install apps
+        int USER_DEFAULT = 2;   // Default code to use when user response is unavailable
+
+        /**
+         * Checks the user preference for whether a package is trusted to request installs through
+         * package installer
+         *
+         * @param packageName The package to check for
+         * @param uid the uid in which the package is running
+         * @return {@link #USER_TRUSTED} if the user has trusted the package, {@link #USER_BLOCKED}
+         * if user has blocked requests from the package, {@link #USER_DEFAULT} if the user response
+         * is not yet available
+         */
+        int getPackageTrustedToInstallApps(String packageName, int uid);
+    }
+
+    public abstract void setExternalSourcesPolicy(ExternalSourcesPolicy policy);
+
+    /**
+     * Return true if the given package is a persistent app process.
+     */
+    public abstract boolean isPackagePersistent(String packageName);
+
+    /**
+     * Get all overlay packages for a user.
+     * @param userId The user for which to get the overlays.
+     * @return A list of overlay packages. An empty list is returned if the
+     *         user has no installed overlay packages.
+     */
+    public abstract List<PackageInfo> getOverlayPackages(int userId);
+
+    /**
+     * Get the names of all target packages for a user.
+     * @param userId The user for which to get the package names.
+     * @return A list of target package names. This list includes the "android" package.
+     */
+    public abstract List<String> getTargetPackageNames(int userId);
+
+    /**
+     * Set which overlay to use for a package.
+     * @param userId The user for which to update the overlays.
+     * @param targetPackageName The package name of the package for which to update the overlays.
+     * @param overlayPackageNames The complete list of overlay packages that should be enabled for
+     *                            the target. Previously enabled overlays not specified in the list
+     *                            will be disabled. Pass in null or an empty list to disable
+     *                            all overlays. The order of the items is significant if several
+     *                            overlays modify the same resource.
+     * @param outUpdatedPackageNames An output list that contains the package names of packages
+     *                               affected by the update of enabled overlays.
+     * @return true if all packages names were known by the package manager, false otherwise
+     */
+    public abstract boolean setEnabledOverlayPackages(int userId, String targetPackageName,
+            List<String> overlayPackageNames, Collection<String> outUpdatedPackageNames);
+
+    /**
+     * Resolves an activity intent, allowing instant apps to be resolved.
+     */
+    public abstract ResolveInfo resolveIntent(Intent intent, String resolvedType,
+            int flags, @PrivateResolveFlags int privateResolveFlags, int userId,
+            boolean resolveForStart, int filterCallingUid);
+
+    /**
+    * Resolves a service intent, allowing instant apps to be resolved.
+    */
+    public abstract ResolveInfo resolveService(Intent intent, String resolvedType,
+           int flags, int userId, int callingUid);
+
+   /**
+    * Resolves a content provider intent.
+    */
+    public abstract ProviderInfo resolveContentProvider(String name, int flags, int userId);
+
+    /**
+     * Track the creator of a new isolated uid.
+     * @param isolatedUid The newly created isolated uid.
+     * @param ownerUid The uid of the app that created the isolated process.
+     */
+    public abstract void addIsolatedUid(int isolatedUid, int ownerUid);
+
+    /**
+     * Track removal of an isolated uid.
+     * @param isolatedUid isolated uid that is no longer being used.
+     */
+    public abstract void removeIsolatedUid(int isolatedUid);
+
+    /**
+     * Return the taget SDK version for the app with the given UID.
+     */
+    public abstract int getUidTargetSdkVersion(int uid);
+
+    /**
+     * Return the taget SDK version for the app with the given package name.
+     */
+    public abstract int getPackageTargetSdkVersion(String packageName);
+
+    /** Whether the binder caller can access instant apps. */
+    public abstract boolean canAccessInstantApps(int callingUid, int userId);
+
+    /** Whether the binder caller can access the given component. */
+    public abstract boolean canAccessComponent(int callingUid, ComponentName component, int userId);
+
+    /**
+     * Returns {@code true} if a given package has instant application meta-data.
+     * Otherwise, returns {@code false}. Meta-data is state (eg. cookie, app icon, etc)
+     * associated with an instant app. It may be kept after the instant app has been uninstalled.
+     */
+    public abstract boolean hasInstantApplicationMetadata(String packageName, int userId);
+
+    /**
+     * Updates a package last used time.
+     */
+    public abstract void notifyPackageUse(String packageName, int reason);
+
+    /**
+     * Returns a package object for the given package name.
+     */
+    public abstract @Nullable AndroidPackage getPackage(@NonNull String packageName);
+
+    public abstract @Nullable PackageSetting getPackageSetting(String packageName);
+
+    /**
+     * Returns a package for the given UID. If the UID is part of a shared user ID, one
+     * of the packages will be chosen to be returned.
+     */
+    public abstract @Nullable AndroidPackage getPackage(int uid);
+
+    /**
+     * Returns a list without a change observer.
+     *
+     * @see #getPackageList(PackageListObserver)
+     */
+    public @NonNull PackageList getPackageList() {
+        return getPackageList(null);
+    }
+
+    /**
+     * Returns the list of packages installed at the time of the method call.
+     * <p>The given observer is notified when the list of installed packages
+     * changes [eg. a package was installed or uninstalled]. It will not be
+     * notified if a package is updated.
+     * <p>The package list will not be updated automatically as packages are
+     * installed / uninstalled. Any changes must be handled within the observer.
+     */
+    public abstract @NonNull PackageList getPackageList(@Nullable PackageListObserver observer);
+
+    /**
+     * Removes the observer.
+     * <p>Generally not needed. {@link #getPackageList(PackageListObserver)} will automatically
+     * remove the observer.
+     * <p>Does nothing if the observer isn't currently registered.
+     * <p>Observers are notified asynchronously and it's possible for an observer to be
+     * invoked after its been removed.
+     */
+    public abstract void removePackageListObserver(@NonNull PackageListObserver observer);
+
+    /**
+     * Returns a package object for the disabled system package name.
+     */
+    public abstract @Nullable PackageSetting getDisabledSystemPackage(@NonNull String packageName);
+
+    /**
+     * Returns the package name for the disabled system package.
+     *
+     * This is equivalent to
+     * {@link #getDisabledSystemPackage(String)}
+     *     .{@link PackageSetting#pkg}
+     *     .{@link AndroidPackage#getPackageName()}
+     */
+    public abstract @Nullable String getDisabledSystemPackageName(@NonNull String packageName);
+
+    /**
+     * Returns whether or not the component is the resolver activity.
+     */
+    public abstract boolean isResolveActivityComponent(@NonNull ComponentInfo component);
+
+
+    /**
+     * Returns a list of package names for a known package
+     */
+    public abstract @NonNull String[] getKnownPackageNames(
+            @KnownPackage int knownPackage, int userId);
+
+    /**
+     * Returns whether the package is an instant app.
+     */
+    public abstract boolean isInstantApp(String packageName, int userId);
+
+    /**
+     * Returns whether the package is an instant app.
+     */
+    public abstract @Nullable String getInstantAppPackageName(int uid);
+
+    /**
+     * Returns whether or not access to the application should be filtered.
+     * <p>
+     * Access may be limited based upon whether the calling or target applications
+     * are instant applications.
+     *
+     * @see #canAccessInstantApps
+     */
+    public abstract boolean filterAppAccess(
+            @NonNull AndroidPackage pkg, int callingUid, int userId);
+
+    /**
+     * Returns whether or not access to the application should be filtered.
+     *
+     * @see #filterAppAccess(AndroidPackage, int, int)
+     */
+    public abstract boolean filterAppAccess(
+            @NonNull String packageName, int callingUid, int userId);
+
+    /** Returns whether the given package was signed by the platform */
+    public abstract boolean isPlatformSigned(String pkg);
+
+    /**
+     * Returns true if it's still safe to restore data backed up from this app's version
+     * that was signed with restoringFromSigHash.
+     */
+    public abstract boolean isDataRestoreSafe(@NonNull byte[] restoringFromSigHash,
+            @NonNull String packageName);
+
+    /**
+     * Returns true if it's still safe to restore data backed up from this app's version
+     * that was signed with restoringFromSig.
+     */
+    public abstract boolean isDataRestoreSafe(@NonNull Signature restoringFromSig,
+            @NonNull String packageName);
+
+    /**
+     * Returns {@code true} if the the signing information for {@code clientUid} is sufficient
+     * to gain access gated by {@code capability}.  This can happen if the two UIDs have the
+     * same signing information, if the signing information {@code clientUid} indicates that
+     * it has the signing certificate for {@code serverUid} in its signing history (if it was
+     * previously signed by it), or if the signing certificate for {@code clientUid} is in the
+     * signing history for {@code serverUid} and with the {@code capability} specified.
+     */
+    public abstract boolean hasSignatureCapability(int serverUid, int clientUid,
+            @PackageParser.SigningDetails.CertCapabilities int capability);
+
+    /**
+     * Get appIds of all available apps which specified android:sharedUserId in the manifest.
+     *
+     * @return a SparseArray mapping from appId to it's sharedUserId.
+     */
+    public abstract SparseArray<String> getAppsWithSharedUserIds();
+
+    /**
+     * Get all packages which share the same userId as the specified package, or an empty array
+     * if the package does not have a shared userId.
+     */
+    @NonNull
+    public abstract String[] getSharedUserPackagesForPackage(@NonNull String packageName,
+            int userId);
+
+    /**
+     * Return the processes that have been declared for a uid.
+     *
+     * @param uid The uid to query.
+     *
+     * @return Returns null if there are no declared processes for the uid; otherwise,
+     * returns the set of processes it declared.
+     */
+    public abstract ArrayMap<String, ProcessInfo> getProcessesForUid(int uid);
+
+    /**
+     * Return the gids associated with a particular permission.
+     *
+     * @param permissionName The name of the permission to query.
+     * @param userId The user id the gids will be associated with.
+     *
+     * @return Returns null if there are no gids associated with the permission, otherwise an
+     * array if the gid ints.
+     */
+    public abstract int[] getPermissionGids(String permissionName, int userId);
+
+    /**
+     * Return if device is currently in a "core" boot environment, typically
+     * used to support full-disk encryption. Only apps marked with
+     * {@code coreApp} attribute are available.
+     */
+    public abstract boolean isOnlyCoreApps();
+
+    /**
+     * Make a best-effort attempt to provide the requested free disk space by
+     * deleting cached files.
+     *
+     * @throws IOException if the request was unable to be fulfilled.
+     */
+    public abstract void freeStorage(String volumeUuid, long bytes, int storageFlags)
+            throws IOException;
+
+    /** Returns {@code true} if the specified component is enabled and matches the given flags. */
+    public abstract boolean isEnabledAndMatches(@NonNull ParsedMainComponent component, int flags,
+            int userId);
+
+    /** Returns {@code true} if the given user requires extra badging for icons. */
+    public abstract boolean userNeedsBadging(int userId);
+
+    /**
+     * Perform the given action for each package.
+     * Note that packages lock will be held while performing the actions.
+     *
+     * @param actionLocked action to be performed
+     */
+    public abstract void forEachPackage(Consumer<AndroidPackage> actionLocked);
+
+    /**
+     * Perform the given action for each {@link PackageSetting}.
+     * Note that packages lock will be held while performing the actions.
+     *
+     * @param actionLocked action to be performed
+     */
+    public abstract void forEachPackageSetting(Consumer<PackageSetting> actionLocked);
+
+    /**
+     * Perform the given action for each installed package for a user.
+     * Note that packages lock will be held while performin the actions.
+     */
+    public abstract void forEachInstalledPackage(
+            @NonNull Consumer<AndroidPackage> actionLocked, @UserIdInt int userId);
+
+    /** Returns the list of enabled components */
+    public abstract ArraySet<String> getEnabledComponents(String packageName, int userId);
+
+    /** Returns the list of disabled components */
+    public abstract ArraySet<String> getDisabledComponents(String packageName, int userId);
+
+    /** Returns whether the given package is enabled for the given user */
+    public abstract @PackageManager.EnabledState int getApplicationEnabledState(
+            String packageName, int userId);
+
+    /**
+     * Extra field name for the token of a request to enable rollback for a
+     * package.
+     */
+    public static final String EXTRA_ENABLE_ROLLBACK_TOKEN =
+            "android.content.pm.extra.ENABLE_ROLLBACK_TOKEN";
+
+    /**
+     * Extra field name for the session id of a request to enable rollback
+     * for a package.
+     */
+    public static final String EXTRA_ENABLE_ROLLBACK_SESSION_ID =
+            "android.content.pm.extra.ENABLE_ROLLBACK_SESSION_ID";
+
+    /**
+     * Used as the {@code enableRollbackCode} argument for
+     * {@link PackageManagerInternal#setEnableRollbackCode} to indicate that
+     * enabling rollback succeeded.
+     */
+    public static final int ENABLE_ROLLBACK_SUCCEEDED = 1;
+
+    /**
+     * Used as the {@code enableRollbackCode} argument for
+     * {@link PackageManagerInternal#setEnableRollbackCode} to indicate that
+     * enabling rollback failed.
+     */
+    public static final int ENABLE_ROLLBACK_FAILED = -1;
+
+    /**
+     * Allows the rollback manager listening to the
+     * {@link Intent#ACTION_PACKAGE_ENABLE_ROLLBACK enable rollback broadcast}
+     * to respond to the package manager. The response must include the
+     * {@code enableRollbackCode} which is one of
+     * {@link PackageManager#ENABLE_ROLLBACK_SUCCEEDED} or
+     * {@link PackageManager#ENABLE_ROLLBACK_FAILED}.
+     *
+     * @param token pending package identifier as passed via the
+     *            {@link PackageManager#EXTRA_ENABLE_ROLLBACK_TOKEN} Intent extra.
+     * @param enableRollbackCode the status code result of enabling rollback
+     * @throws SecurityException if the caller does not have the
+     *            PACKAGE_ROLLBACK_AGENT permission.
+     */
+    public abstract void setEnableRollbackCode(int token, int enableRollbackCode);
+
+    /**
+     * Ask the package manager to compile layouts in the given package.
+     */
+    public abstract boolean compileLayouts(String packageName);
+
+    /*
+     * Inform the package manager that the pending package install identified by
+     * {@code token} can be completed.
+     */
+    public abstract void finishPackageInstall(int token, boolean didLaunch);
+
+    /**
+     * Remove the default browser stored in the legacy package settings.
+     *
+     * @param userId the user id
+     *
+     * @return the package name of the default browser, or {@code null} if none
+     */
+    @Nullable
+    public abstract String removeLegacyDefaultBrowserPackageName(int userId);
+
+    /**
+     * Returns {@code true} if given {@code packageName} is an apex package.
+     */
+    public abstract boolean isApexPackage(String packageName);
+
+    /**
+     * Returns list of {@code packageName} of apks inside the given apex.
+     * @param apexPackageName Package name of the apk container of apex
+     */
+    public abstract List<String> getApksInApex(String apexPackageName);
+
+    /**
+     * Uninstalls given {@code packageName}.
+     *
+     * @param packageName apex package to uninstall.
+     * @param versionCode version of a package to uninstall.
+     * @param userId user to uninstall apex package for. Must be
+     *               {@link android.os.UserHandle#USER_ALL}, otherwise failure will be reported.
+     * @param intentSender a {@link IntentSender} to send result of an uninstall to.
+     * @param flags flags about the uninstall.
+     */
+    public abstract void uninstallApex(String packageName, long versionCode, int userId,
+            IntentSender intentSender, int flags);
+
+    /**
+     * Update fingerprint of build that updated the runtime permissions for a user.
+     *
+     * @param userId The user to update
+     */
+    public abstract void updateRuntimePermissionsFingerprint(@UserIdInt int userId);
+
+    /**
+     * Migrates legacy obb data to its new location.
+     */
+    public abstract void migrateLegacyObbData();
+
+    /**
+     * Writes all package manager settings to disk. If {@code async} is {@code true}, the
+     * settings are written at some point in the future. Otherwise, the call blocks until
+     * the settings have been written.
+     */
+    public abstract void writeSettings(boolean async);
+
+    /**
+     * Writes all permission settings for the given set of users to disk. If {@code async}
+     * is {@code true}, the settings are written at some point in the future. Otherwise,
+     * the call blocks until the settings have been written.
+     */
+    public abstract void writePermissionSettings(@NonNull @UserIdInt int[] userIds, boolean async);
+
+    /**
+     * Returns {@code true} if the caller is the installer of record for the given package.
+     * Otherwise, {@code false}.
+     */
+    public abstract boolean isCallerInstallerOfRecord(
+            @NonNull AndroidPackage pkg, int callingUid);
+
+    /** Returns whether or not permissions need to be upgraded for the given user */
+    public abstract boolean isPermissionUpgradeNeeded(@UserIdInt int userId);
+
+    /** Sets the enforcement of reading external storage */
+    public abstract void setReadExternalStorageEnforced(boolean enforced);
+
+    /**
+     * Allows the integrity component to respond to the
+     * {@link Intent#ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION package verification
+     * broadcast} to respond to the package manager. The response must include
+     * the {@code verificationCode} which is one of
+     * {@link #INTEGRITY_VERIFICATION_ALLOW} and {@link #INTEGRITY_VERIFICATION_REJECT}.
+     *
+     * @param verificationId pending package identifier as passed via the
+     *            {@link PackageManager#EXTRA_VERIFICATION_ID} Intent extra.
+     * @param verificationResult either {@link #INTEGRITY_VERIFICATION_ALLOW}
+     *            or {@link #INTEGRITY_VERIFICATION_REJECT}.
+     */
+    public abstract void setIntegrityVerificationResult(int verificationId,
+            @IntegrityVerificationResult int verificationResult);
+
+    /**
+     * Returns MIME types contained in {@code mimeGroup} from {@code packageName} package
+     */
+    public abstract List<String> getMimeGroup(String packageName, String mimeGroup);
+
+    /**
+     * Toggles visibility logging to help in debugging the app enumeration feature.
+     * @param packageName the package name that should begin logging
+     * @param enabled true if visibility blocks should be logged
+     */
+    public abstract void setVisibilityLogging(String packageName, boolean enabled);
+
+    /**
+     * Returns if a package name is a valid system package.
+     */
+    public abstract boolean isSystemPackage(@NonNull String packageName);
+
+    /**
+     * Unblocks uninstall for all packages for the user.
+     */
+    public abstract void clearBlockUninstallForUser(@UserIdInt int userId);
+}
diff --git a/android/content/pm/PackageParser.java b/android/content/pm/PackageParser.java
new file mode 100644
index 0000000..3b3521f
--- /dev/null
+++ b/android/content/pm/PackageParser.java
@@ -0,0 +1,8332 @@
+/*
+ * 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.content.pm;
+
+import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
+import static android.content.pm.ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.content.pm.ApplicationInfo.FLAG_SUSPENDED;
+import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE;
+import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
+import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE;
+import static android.content.pm.PackageManager.FEATURE_WATCH;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
+import static android.os.Build.VERSION_CODES.O;
+import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
+import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED;
+
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StringRes;
+import android.annotation.TestApi;
+import android.apex.ApexInfo;
+import android.app.ActivityTaskManager;
+import android.app.ActivityThread;
+import android.app.ResourcesManager;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.permission.SplitPermissionInfoParcelable;
+import android.content.pm.split.DefaultSplitAssetLoader;
+import android.content.pm.split.SplitAssetDependencyLoader;
+import android.content.pm.split.SplitAssetLoader;
+import android.content.res.ApkAssets;
+import android.content.res.AssetManager;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.FileUtils;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.PatternMatcher;
+import android.os.RemoteException;
+import android.os.SystemProperties;
+import android.os.Trace;
+import android.os.UserHandle;
+import android.os.storage.StorageManager;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.AttributeSet;
+import android.util.Base64;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.util.PackageUtils;
+import android.util.Pair;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.TypedValue;
+import android.util.apk.ApkSignatureVerifier;
+import android.view.Display;
+import android.view.Gravity;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.ClassLoaderFactory;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.XmlUtils;
+
+import libcore.io.IoUtils;
+import libcore.util.EmptyArray;
+import libcore.util.HexEncoding;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.reflect.Constructor;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.security.cert.CertificateException;
+import java.security.spec.EncodedKeySpec;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Parser for package files (APKs) on disk. This supports apps packaged either
+ * as a single "monolithic" APK, or apps packaged as a "cluster" of multiple
+ * APKs in a single directory.
+ * <p>
+ * Apps packaged as multiple APKs always consist of a single "base" APK (with a
+ * {@code null} split name) and zero or more "split" APKs (with unique split
+ * names). Any subset of those split APKs are a valid install, as long as the
+ * following constraints are met:
+ * <ul>
+ * <li>All APKs must have the exact same package name, version code, and signing
+ * certificates.
+ * <li>All APKs must have unique split names.
+ * <li>All installations must contain a single base APK.
+ * </ul>
+ *
+ * @hide
+ */
+public class PackageParser {
+
+    public static final boolean DEBUG_JAR = false;
+    public static final boolean DEBUG_PARSER = false;
+    public static final boolean DEBUG_BACKUP = false;
+    public static final boolean LOG_PARSE_TIMINGS = Build.IS_DEBUGGABLE;
+    public static final int LOG_PARSE_TIMINGS_THRESHOLD_MS = 100;
+
+    private static final String PROPERTY_CHILD_PACKAGES_ENABLED =
+            "persist.sys.child_packages_enabled";
+
+    public static final boolean MULTI_PACKAGE_APK_ENABLED = Build.IS_DEBUGGABLE &&
+            SystemProperties.getBoolean(PROPERTY_CHILD_PACKAGES_ENABLED, false);
+
+    public static final float DEFAULT_PRE_O_MAX_ASPECT_RATIO = 1.86f;
+    public static final float DEFAULT_PRE_Q_MIN_ASPECT_RATIO = 1.333f;
+    public static final float DEFAULT_PRE_Q_MIN_ASPECT_RATIO_WATCH = 1f;
+
+    private static final int DEFAULT_MIN_SDK_VERSION = 1;
+    private static final int DEFAULT_TARGET_SDK_VERSION = 0;
+
+    // TODO: switch outError users to PackageParserException
+    // TODO: refactor "codePath" to "apkPath"
+
+    /** File name in an APK for the Android manifest. */
+    public static final String ANDROID_MANIFEST_FILENAME = "AndroidManifest.xml";
+
+    /** Path prefix for apps on expanded storage */
+    public static final String MNT_EXPAND = "/mnt/expand/";
+
+    public static final String TAG_ADOPT_PERMISSIONS = "adopt-permissions";
+    public static final String TAG_APPLICATION = "application";
+    public static final String TAG_COMPATIBLE_SCREENS = "compatible-screens";
+    public static final String TAG_EAT_COMMENT = "eat-comment";
+    public static final String TAG_FEATURE_GROUP = "feature-group";
+    public static final String TAG_INSTRUMENTATION = "instrumentation";
+    public static final String TAG_KEY_SETS = "key-sets";
+    public static final String TAG_MANIFEST = "manifest";
+    public static final String TAG_ORIGINAL_PACKAGE = "original-package";
+    public static final String TAG_OVERLAY = "overlay";
+    public static final String TAG_PACKAGE = "package";
+    public static final String TAG_PACKAGE_VERIFIER = "package-verifier";
+    public static final String TAG_ATTRIBUTION = "attribution";
+    public static final String TAG_PERMISSION = "permission";
+    public static final String TAG_PERMISSION_GROUP = "permission-group";
+    public static final String TAG_PERMISSION_TREE = "permission-tree";
+    public static final String TAG_PROTECTED_BROADCAST = "protected-broadcast";
+    public static final String TAG_QUERIES = "queries";
+    public static final String TAG_RESTRICT_UPDATE = "restrict-update";
+    public static final String TAG_SUPPORT_SCREENS = "supports-screens";
+    public static final String TAG_SUPPORTS_INPUT = "supports-input";
+    public static final String TAG_USES_CONFIGURATION = "uses-configuration";
+    public static final String TAG_USES_FEATURE = "uses-feature";
+    public static final String TAG_USES_GL_TEXTURE = "uses-gl-texture";
+    public static final String TAG_USES_PERMISSION = "uses-permission";
+    public static final String TAG_USES_PERMISSION_SDK_23 = "uses-permission-sdk-23";
+    public static final String TAG_USES_PERMISSION_SDK_M = "uses-permission-sdk-m";
+    public static final String TAG_USES_SDK = "uses-sdk";
+    public static final String TAG_USES_SPLIT = "uses-split";
+
+    public static final String METADATA_MAX_ASPECT_RATIO = "android.max_aspect";
+    public static final String METADATA_ACTIVITY_WINDOW_LAYOUT_AFFINITY =
+            "android.activity_window_layout_affinity";
+
+    /**
+     * Bit mask of all the valid bits that can be set in recreateOnConfigChanges.
+     * @hide
+     */
+    private static final int RECREATE_ON_CONFIG_CHANGES_MASK =
+            ActivityInfo.CONFIG_MCC | ActivityInfo.CONFIG_MNC;
+
+    // These are the tags supported by child packages
+    public static final Set<String> CHILD_PACKAGE_TAGS = new ArraySet<>();
+    static {
+        CHILD_PACKAGE_TAGS.add(TAG_APPLICATION);
+        CHILD_PACKAGE_TAGS.add(TAG_COMPATIBLE_SCREENS);
+        CHILD_PACKAGE_TAGS.add(TAG_EAT_COMMENT);
+        CHILD_PACKAGE_TAGS.add(TAG_FEATURE_GROUP);
+        CHILD_PACKAGE_TAGS.add(TAG_INSTRUMENTATION);
+        CHILD_PACKAGE_TAGS.add(TAG_SUPPORT_SCREENS);
+        CHILD_PACKAGE_TAGS.add(TAG_SUPPORTS_INPUT);
+        CHILD_PACKAGE_TAGS.add(TAG_USES_CONFIGURATION);
+        CHILD_PACKAGE_TAGS.add(TAG_USES_FEATURE);
+        CHILD_PACKAGE_TAGS.add(TAG_USES_GL_TEXTURE);
+        CHILD_PACKAGE_TAGS.add(TAG_USES_PERMISSION);
+        CHILD_PACKAGE_TAGS.add(TAG_USES_PERMISSION_SDK_23);
+        CHILD_PACKAGE_TAGS.add(TAG_USES_PERMISSION_SDK_M);
+        CHILD_PACKAGE_TAGS.add(TAG_USES_SDK);
+    }
+
+    public static final boolean LOG_UNSAFE_BROADCASTS = false;
+
+    /**
+     * Total number of packages that were read from the cache.  We use it only for logging.
+     */
+    public static final AtomicInteger sCachedPackageReadCount = new AtomicInteger();
+
+    // Set of broadcast actions that are safe for manifest receivers
+    public static final Set<String> SAFE_BROADCASTS = new ArraySet<>();
+    static {
+        SAFE_BROADCASTS.add(Intent.ACTION_BOOT_COMPLETED);
+    }
+
+    /** @hide */
+    public static final String APK_FILE_EXTENSION = ".apk";
+    /** @hide */
+    public static final String APEX_FILE_EXTENSION = ".apex";
+
+    /** @hide */
+    public static class NewPermissionInfo {
+        @UnsupportedAppUsage
+        public final String name;
+        @UnsupportedAppUsage
+        public final int sdkVersion;
+        public final int fileVersion;
+
+        public NewPermissionInfo(String name, int sdkVersion, int fileVersion) {
+            this.name = name;
+            this.sdkVersion = sdkVersion;
+            this.fileVersion = fileVersion;
+        }
+    }
+
+    /**
+     * List of new permissions that have been added since 1.0.
+     * NOTE: These must be declared in SDK version order, with permissions
+     * added to older SDKs appearing before those added to newer SDKs.
+     * If sdkVersion is 0, then this is not a permission that we want to
+     * automatically add to older apps, but we do want to allow it to be
+     * granted during a platform update.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static final PackageParser.NewPermissionInfo NEW_PERMISSIONS[] =
+        new PackageParser.NewPermissionInfo[] {
+            new PackageParser.NewPermissionInfo(android.Manifest.permission.WRITE_EXTERNAL_STORAGE,
+                    android.os.Build.VERSION_CODES.DONUT, 0),
+            new PackageParser.NewPermissionInfo(android.Manifest.permission.READ_PHONE_STATE,
+                    android.os.Build.VERSION_CODES.DONUT, 0)
+    };
+
+    /**
+     * @deprecated callers should move to explicitly passing around source path.
+     */
+    @Deprecated
+    public String mArchiveSourcePath;
+
+    public String[] mSeparateProcesses;
+    private boolean mOnlyCoreApps;
+    private DisplayMetrics mMetrics;
+    @UnsupportedAppUsage
+    public Callback mCallback;
+    private File mCacheDir;
+
+    public static final int SDK_VERSION = Build.VERSION.SDK_INT;
+    public static final String[] SDK_CODENAMES = Build.VERSION.ACTIVE_CODENAMES;
+
+    public int mParseError = PackageManager.INSTALL_SUCCEEDED;
+
+    public static boolean sCompatibilityModeEnabled = true;
+    public static boolean sUseRoundIcon = false;
+
+    public static final int PARSE_DEFAULT_INSTALL_LOCATION =
+            PackageInfo.INSTALL_LOCATION_UNSPECIFIED;
+    public static final int PARSE_DEFAULT_TARGET_SANDBOX = 1;
+
+    static class ParsePackageItemArgs {
+        final Package owner;
+        final String[] outError;
+        final int nameRes;
+        final int labelRes;
+        final int iconRes;
+        final int roundIconRes;
+        final int logoRes;
+        final int bannerRes;
+
+        String tag;
+        TypedArray sa;
+
+        ParsePackageItemArgs(Package _owner, String[] _outError,
+                int _nameRes, int _labelRes, int _iconRes, int _roundIconRes, int _logoRes,
+                int _bannerRes) {
+            owner = _owner;
+            outError = _outError;
+            nameRes = _nameRes;
+            labelRes = _labelRes;
+            iconRes = _iconRes;
+            logoRes = _logoRes;
+            bannerRes = _bannerRes;
+            roundIconRes = _roundIconRes;
+        }
+    }
+
+    /** @hide */
+    @VisibleForTesting
+    public static class ParseComponentArgs extends ParsePackageItemArgs {
+        final String[] sepProcesses;
+        final int processRes;
+        final int descriptionRes;
+        final int enabledRes;
+        int flags;
+
+        public ParseComponentArgs(Package _owner, String[] _outError,
+                int _nameRes, int _labelRes, int _iconRes, int _roundIconRes, int _logoRes,
+                int _bannerRes,
+                String[] _sepProcesses, int _processRes,
+                int _descriptionRes, int _enabledRes) {
+            super(_owner, _outError, _nameRes, _labelRes, _iconRes, _roundIconRes, _logoRes,
+                    _bannerRes);
+            sepProcesses = _sepProcesses;
+            processRes = _processRes;
+            descriptionRes = _descriptionRes;
+            enabledRes = _enabledRes;
+        }
+    }
+
+    /**
+     * Lightweight parsed details about a single package.
+     */
+    public static class PackageLite {
+        @UnsupportedAppUsage
+        public final String packageName;
+        public final int versionCode;
+        public final int versionCodeMajor;
+        @UnsupportedAppUsage
+        public final int installLocation;
+        public final VerifierInfo[] verifiers;
+
+        /** Names of any split APKs, ordered by parsed splitName */
+        public final String[] splitNames;
+
+        /** Names of any split APKs that are features. Ordered by splitName */
+        public final boolean[] isFeatureSplits;
+
+        /** Dependencies of any split APKs, ordered by parsed splitName */
+        public final String[] usesSplitNames;
+        public final String[] configForSplit;
+
+        /**
+         * Path where this package was found on disk. For monolithic packages
+         * this is path to single base APK file; for cluster packages this is
+         * path to the cluster directory.
+         */
+        public final String codePath;
+
+        /** Path of base APK */
+        public final String baseCodePath;
+        /** Paths of any split APKs, ordered by parsed splitName */
+        public final String[] splitCodePaths;
+
+        /** Revision code of base APK */
+        public final int baseRevisionCode;
+        /** Revision codes of any split APKs, ordered by parsed splitName */
+        public final int[] splitRevisionCodes;
+
+        public final boolean coreApp;
+        public final boolean debuggable;
+        public final boolean multiArch;
+        public final boolean use32bitAbi;
+        public final boolean extractNativeLibs;
+        public final boolean isolatedSplits;
+
+        public PackageLite(String codePath, ApkLite baseApk, String[] splitNames,
+                boolean[] isFeatureSplits, String[] usesSplitNames, String[] configForSplit,
+                String[] splitCodePaths, int[] splitRevisionCodes) {
+            this.packageName = baseApk.packageName;
+            this.versionCode = baseApk.versionCode;
+            this.versionCodeMajor = baseApk.versionCodeMajor;
+            this.installLocation = baseApk.installLocation;
+            this.verifiers = baseApk.verifiers;
+            this.splitNames = splitNames;
+            this.isFeatureSplits = isFeatureSplits;
+            this.usesSplitNames = usesSplitNames;
+            this.configForSplit = configForSplit;
+            this.codePath = codePath;
+            this.baseCodePath = baseApk.codePath;
+            this.splitCodePaths = splitCodePaths;
+            this.baseRevisionCode = baseApk.revisionCode;
+            this.splitRevisionCodes = splitRevisionCodes;
+            this.coreApp = baseApk.coreApp;
+            this.debuggable = baseApk.debuggable;
+            this.multiArch = baseApk.multiArch;
+            this.use32bitAbi = baseApk.use32bitAbi;
+            this.extractNativeLibs = baseApk.extractNativeLibs;
+            this.isolatedSplits = baseApk.isolatedSplits;
+        }
+
+        public List<String> getAllCodePaths() {
+            ArrayList<String> paths = new ArrayList<>();
+            paths.add(baseCodePath);
+            if (!ArrayUtils.isEmpty(splitCodePaths)) {
+                Collections.addAll(paths, splitCodePaths);
+            }
+            return paths;
+        }
+    }
+
+    /**
+     * Lightweight parsed details about a single APK file.
+     */
+    public static class ApkLite {
+        public final String codePath;
+        public final String packageName;
+        public final String splitName;
+        public boolean isFeatureSplit;
+        public final String configForSplit;
+        public final String usesSplitName;
+        public final int versionCode;
+        public final int versionCodeMajor;
+        public final int revisionCode;
+        public final int installLocation;
+        public final int minSdkVersion;
+        public final int targetSdkVersion;
+        public final VerifierInfo[] verifiers;
+        public final SigningDetails signingDetails;
+        public final boolean coreApp;
+        public final boolean debuggable;
+        public final boolean multiArch;
+        public final boolean use32bitAbi;
+        public final boolean extractNativeLibs;
+        public final boolean isolatedSplits;
+        public final boolean isSplitRequired;
+        public final boolean useEmbeddedDex;
+        public final String targetPackageName;
+        public final boolean overlayIsStatic;
+        public final int overlayPriority;
+
+        public ApkLite(String codePath, String packageName, String splitName,
+                boolean isFeatureSplit,
+                String configForSplit, String usesSplitName, boolean isSplitRequired,
+                int versionCode, int versionCodeMajor,
+                int revisionCode, int installLocation, List<VerifierInfo> verifiers,
+                SigningDetails signingDetails, boolean coreApp,
+                boolean debuggable, boolean multiArch, boolean use32bitAbi,
+                boolean useEmbeddedDex, boolean extractNativeLibs, boolean isolatedSplits,
+                String targetPackageName, boolean overlayIsStatic, int overlayPriority,
+                int minSdkVersion, int targetSdkVersion) {
+            this.codePath = codePath;
+            this.packageName = packageName;
+            this.splitName = splitName;
+            this.isFeatureSplit = isFeatureSplit;
+            this.configForSplit = configForSplit;
+            this.usesSplitName = usesSplitName;
+            this.versionCode = versionCode;
+            this.versionCodeMajor = versionCodeMajor;
+            this.revisionCode = revisionCode;
+            this.installLocation = installLocation;
+            this.signingDetails = signingDetails;
+            this.verifiers = verifiers.toArray(new VerifierInfo[verifiers.size()]);
+            this.coreApp = coreApp;
+            this.debuggable = debuggable;
+            this.multiArch = multiArch;
+            this.use32bitAbi = use32bitAbi;
+            this.useEmbeddedDex = useEmbeddedDex;
+            this.extractNativeLibs = extractNativeLibs;
+            this.isolatedSplits = isolatedSplits;
+            this.isSplitRequired = isSplitRequired;
+            this.targetPackageName = targetPackageName;
+            this.overlayIsStatic = overlayIsStatic;
+            this.overlayPriority = overlayPriority;
+            this.minSdkVersion = minSdkVersion;
+            this.targetSdkVersion = targetSdkVersion;
+        }
+
+        public long getLongVersionCode() {
+            return PackageInfo.composeLongVersionCode(versionCodeMajor, versionCode);
+        }
+    }
+
+    /**
+     * Cached parse state for new components.
+     *
+     * Allows reuse of the same parse argument records to avoid GC pressure.  Lifetime is carefully
+     * scoped to the parsing of a single application element.
+     */
+    private static class CachedComponentArgs {
+        ParseComponentArgs mActivityArgs;
+        ParseComponentArgs mActivityAliasArgs;
+        ParseComponentArgs mServiceArgs;
+        ParseComponentArgs mProviderArgs;
+    }
+
+    /**
+     * Cached state for parsing instrumentation to avoid GC pressure.
+     *
+     * Must be manually reset to null for each new manifest.
+     */
+    private ParsePackageItemArgs mParseInstrumentationArgs;
+
+    /** If set to true, we will only allow package files that exactly match
+     *  the DTD.  Otherwise, we try to get as much from the package as we
+     *  can without failing.  This should normally be set to false, to
+     *  support extensions to the DTD in future versions. */
+    public static final boolean RIGID_PARSER = false;
+
+    private static final String TAG = "PackageParser";
+
+    @UnsupportedAppUsage
+    public PackageParser() {
+        mMetrics = new DisplayMetrics();
+        mMetrics.setToDefaults();
+    }
+
+    @UnsupportedAppUsage
+    public void setSeparateProcesses(String[] procs) {
+        mSeparateProcesses = procs;
+    }
+
+    /**
+     * Flag indicating this parser should only consider apps with
+     * {@code coreApp} manifest attribute to be valid apps. This is useful when
+     * creating a minimalist boot environment.
+     */
+    public void setOnlyCoreApps(boolean onlyCoreApps) {
+        mOnlyCoreApps = onlyCoreApps;
+    }
+
+    public void setDisplayMetrics(DisplayMetrics metrics) {
+        mMetrics = metrics;
+    }
+
+    /**
+     * Sets the cache directory for this package parser.
+     */
+    public void setCacheDir(File cacheDir) {
+        mCacheDir = cacheDir;
+    }
+
+    /**
+     * Callback interface for retrieving information that may be needed while parsing
+     * a package.
+     */
+    public interface Callback {
+        boolean hasFeature(String feature);
+    }
+
+    /**
+     * Standard implementation of {@link Callback} on top of the public {@link PackageManager}
+     * class.
+     */
+    public static final class CallbackImpl implements Callback {
+        private final PackageManager mPm;
+
+        public CallbackImpl(PackageManager pm) {
+            mPm = pm;
+        }
+
+        @Override public boolean hasFeature(String feature) {
+            return mPm.hasSystemFeature(feature);
+        }
+    }
+
+    /**
+     * Set the {@link Callback} that can be used while parsing.
+     */
+    public void setCallback(Callback cb) {
+        mCallback = cb;
+    }
+
+    public static final boolean isApkFile(File file) {
+        return isApkPath(file.getName());
+    }
+
+    public static boolean isApkPath(String path) {
+        return path.endsWith(APK_FILE_EXTENSION);
+    }
+
+    /**
+     * Returns true if the package is installed and not hidden, or if the caller
+     * explicitly wanted all uninstalled and hidden packages as well.
+     * @param appInfo The applicationInfo of the app being checked.
+     */
+    private static boolean checkUseInstalledOrHidden(int flags, PackageUserState state,
+            ApplicationInfo appInfo) {
+        // Returns false if the package is hidden system app until installed.
+        if ((flags & PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS) == 0
+                && !state.installed
+                && appInfo != null && appInfo.hiddenUntilInstalled) {
+            return false;
+        }
+
+        // If available for the target user, or trying to match uninstalled packages and it's
+        // a system app.
+        return state.isAvailable(flags)
+                || (appInfo != null && appInfo.isSystemApp()
+                        && ((flags & PackageManager.MATCH_KNOWN_PACKAGES) != 0
+                        || (flags & PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS) != 0));
+    }
+
+    public static boolean isAvailable(PackageUserState state) {
+        return checkUseInstalledOrHidden(0, state, null);
+    }
+
+    /**
+     * Generate and return the {@link PackageInfo} for a parsed package.
+     *
+     * @param p the parsed package.
+     * @param flags indicating which optional information is included.
+     */
+    @UnsupportedAppUsage
+    public static PackageInfo generatePackageInfo(PackageParser.Package p,
+            int[] gids, int flags, long firstInstallTime, long lastUpdateTime,
+            Set<String> grantedPermissions, PackageUserState state) {
+
+        return generatePackageInfo(p, gids, flags, firstInstallTime, lastUpdateTime,
+                grantedPermissions, state, UserHandle.getCallingUserId());
+    }
+
+    @UnsupportedAppUsage
+    public static PackageInfo generatePackageInfo(PackageParser.Package p,
+            int[] gids, int flags, long firstInstallTime, long lastUpdateTime,
+            Set<String> grantedPermissions, PackageUserState state, int userId) {
+
+        return generatePackageInfo(p, null, gids, flags, firstInstallTime, lastUpdateTime,
+                grantedPermissions, state, userId);
+    }
+
+    /**
+     * PackageInfo generator specifically for apex files.
+     *
+     * @param pkg Package to generate info from. Should be derived from an apex.
+     * @param apexInfo Apex info relating to the package.
+     * @return PackageInfo
+     * @throws PackageParserException
+     */
+    public static PackageInfo generatePackageInfo(
+            PackageParser.Package pkg, ApexInfo apexInfo, int flags) {
+        return generatePackageInfo(pkg, apexInfo, EmptyArray.INT, flags, 0, 0,
+                Collections.emptySet(), new PackageUserState(), UserHandle.getCallingUserId());
+    }
+
+    private static PackageInfo generatePackageInfo(PackageParser.Package p, ApexInfo apexInfo,
+            int gids[], int flags, long firstInstallTime, long lastUpdateTime,
+            Set<String> grantedPermissions, PackageUserState state, int userId) {
+        if (!checkUseInstalledOrHidden(flags, state, p.applicationInfo) || !p.isMatch(flags)) {
+            return null;
+        }
+        PackageInfo pi = new PackageInfo();
+        pi.packageName = p.packageName;
+        pi.splitNames = p.splitNames;
+        pi.versionCode = p.mVersionCode;
+        pi.versionCodeMajor = p.mVersionCodeMajor;
+        pi.baseRevisionCode = p.baseRevisionCode;
+        pi.splitRevisionCodes = p.splitRevisionCodes;
+        pi.versionName = p.mVersionName;
+        pi.sharedUserId = p.mSharedUserId;
+        pi.sharedUserLabel = p.mSharedUserLabel;
+        pi.applicationInfo = generateApplicationInfo(p, flags, state, userId);
+        pi.installLocation = p.installLocation;
+        pi.isStub = p.isStub;
+        pi.coreApp = p.coreApp;
+        if ((pi.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0
+                || (pi.applicationInfo.flags&ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
+            pi.requiredForAllUsers = p.mRequiredForAllUsers;
+        }
+        pi.restrictedAccountType = p.mRestrictedAccountType;
+        pi.requiredAccountType = p.mRequiredAccountType;
+        pi.overlayTarget = p.mOverlayTarget;
+        pi.targetOverlayableName = p.mOverlayTargetName;
+        pi.overlayCategory = p.mOverlayCategory;
+        pi.overlayPriority = p.mOverlayPriority;
+        pi.mOverlayIsStatic = p.mOverlayIsStatic;
+        pi.compileSdkVersion = p.mCompileSdkVersion;
+        pi.compileSdkVersionCodename = p.mCompileSdkVersionCodename;
+        pi.firstInstallTime = firstInstallTime;
+        pi.lastUpdateTime = lastUpdateTime;
+        if ((flags&PackageManager.GET_GIDS) != 0) {
+            pi.gids = gids;
+        }
+        if ((flags&PackageManager.GET_CONFIGURATIONS) != 0) {
+            int N = p.configPreferences != null ? p.configPreferences.size() : 0;
+            if (N > 0) {
+                pi.configPreferences = new ConfigurationInfo[N];
+                p.configPreferences.toArray(pi.configPreferences);
+            }
+            N = p.reqFeatures != null ? p.reqFeatures.size() : 0;
+            if (N > 0) {
+                pi.reqFeatures = new FeatureInfo[N];
+                p.reqFeatures.toArray(pi.reqFeatures);
+            }
+            N = p.featureGroups != null ? p.featureGroups.size() : 0;
+            if (N > 0) {
+                pi.featureGroups = new FeatureGroupInfo[N];
+                p.featureGroups.toArray(pi.featureGroups);
+            }
+        }
+        if ((flags & PackageManager.GET_ACTIVITIES) != 0) {
+            final int N = p.activities.size();
+            if (N > 0) {
+                int num = 0;
+                final ActivityInfo[] res = new ActivityInfo[N];
+                for (int i = 0; i < N; i++) {
+                    final Activity a = p.activities.get(i);
+                    if (state.isMatch(a.info, flags)) {
+                        if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(a.className)) {
+                            continue;
+                        }
+                        res[num++] = generateActivityInfo(a, flags, state, userId);
+                    }
+                }
+                pi.activities = ArrayUtils.trimToSize(res, num);
+            }
+        }
+        if ((flags & PackageManager.GET_RECEIVERS) != 0) {
+            final int N = p.receivers.size();
+            if (N > 0) {
+                int num = 0;
+                final ActivityInfo[] res = new ActivityInfo[N];
+                for (int i = 0; i < N; i++) {
+                    final Activity a = p.receivers.get(i);
+                    if (state.isMatch(a.info, flags)) {
+                        res[num++] = generateActivityInfo(a, flags, state, userId);
+                    }
+                }
+                pi.receivers = ArrayUtils.trimToSize(res, num);
+            }
+        }
+        if ((flags & PackageManager.GET_SERVICES) != 0) {
+            final int N = p.services.size();
+            if (N > 0) {
+                int num = 0;
+                final ServiceInfo[] res = new ServiceInfo[N];
+                for (int i = 0; i < N; i++) {
+                    final Service s = p.services.get(i);
+                    if (state.isMatch(s.info, flags)) {
+                        res[num++] = generateServiceInfo(s, flags, state, userId);
+                    }
+                }
+                pi.services = ArrayUtils.trimToSize(res, num);
+            }
+        }
+        if ((flags & PackageManager.GET_PROVIDERS) != 0) {
+            final int N = p.providers.size();
+            if (N > 0) {
+                int num = 0;
+                final ProviderInfo[] res = new ProviderInfo[N];
+                for (int i = 0; i < N; i++) {
+                    final Provider pr = p.providers.get(i);
+                    if (state.isMatch(pr.info, flags)) {
+                        res[num++] = generateProviderInfo(pr, flags, state, userId);
+                    }
+                }
+                pi.providers = ArrayUtils.trimToSize(res, num);
+            }
+        }
+        if ((flags&PackageManager.GET_INSTRUMENTATION) != 0) {
+            int N = p.instrumentation.size();
+            if (N > 0) {
+                pi.instrumentation = new InstrumentationInfo[N];
+                for (int i=0; i<N; i++) {
+                    pi.instrumentation[i] = generateInstrumentationInfo(
+                            p.instrumentation.get(i), flags);
+                }
+            }
+        }
+        if ((flags&PackageManager.GET_PERMISSIONS) != 0) {
+            int N = p.permissions.size();
+            if (N > 0) {
+                pi.permissions = new PermissionInfo[N];
+                for (int i=0; i<N; i++) {
+                    pi.permissions[i] = generatePermissionInfo(p.permissions.get(i), flags);
+                }
+            }
+            N = p.requestedPermissions.size();
+            if (N > 0) {
+                pi.requestedPermissions = new String[N];
+                pi.requestedPermissionsFlags = new int[N];
+                for (int i=0; i<N; i++) {
+                    final String perm = p.requestedPermissions.get(i);
+                    pi.requestedPermissions[i] = perm;
+                    // The notion of required permissions is deprecated but for compatibility.
+                    pi.requestedPermissionsFlags[i] |= PackageInfo.REQUESTED_PERMISSION_REQUIRED;
+                    if (grantedPermissions != null && grantedPermissions.contains(perm)) {
+                        pi.requestedPermissionsFlags[i] |= PackageInfo.REQUESTED_PERMISSION_GRANTED;
+                    }
+                }
+            }
+        }
+
+        if (apexInfo != null) {
+            File apexFile = new File(apexInfo.modulePath);
+
+            pi.applicationInfo.sourceDir = apexFile.getPath();
+            pi.applicationInfo.publicSourceDir = apexFile.getPath();
+            if (apexInfo.isFactory) {
+                pi.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+            } else {
+                pi.applicationInfo.flags &= ~ApplicationInfo.FLAG_SYSTEM;
+            }
+            if (apexInfo.isActive) {
+                pi.applicationInfo.flags |= ApplicationInfo.FLAG_INSTALLED;
+            } else {
+                pi.applicationInfo.flags &= ~ApplicationInfo.FLAG_INSTALLED;
+            }
+            pi.isApex = true;
+        }
+
+        // deprecated method of getting signing certificates
+        if ((flags & PackageManager.GET_SIGNATURES) != 0) {
+            if (p.mSigningDetails.hasPastSigningCertificates()) {
+                // Package has included signing certificate rotation information.  Return the oldest
+                // cert so that programmatic checks keep working even if unaware of key rotation.
+                pi.signatures = new Signature[1];
+                pi.signatures[0] = p.mSigningDetails.pastSigningCertificates[0];
+            } else if (p.mSigningDetails.hasSignatures()) {
+                // otherwise keep old behavior
+                int numberOfSigs = p.mSigningDetails.signatures.length;
+                pi.signatures = new Signature[numberOfSigs];
+                System.arraycopy(p.mSigningDetails.signatures, 0, pi.signatures, 0, numberOfSigs);
+            }
+        }
+
+        // replacement for GET_SIGNATURES
+        if ((flags & PackageManager.GET_SIGNING_CERTIFICATES) != 0) {
+            if (p.mSigningDetails != SigningDetails.UNKNOWN) {
+                // only return a valid SigningInfo if there is signing information to report
+                pi.signingInfo = new SigningInfo(p.mSigningDetails);
+            } else {
+                pi.signingInfo = null;
+            }
+        }
+        return pi;
+    }
+
+    public static final int PARSE_MUST_BE_APK = 1 << 0;
+    public static final int PARSE_IGNORE_PROCESSES = 1 << 1;
+    public static final int PARSE_EXTERNAL_STORAGE = 1 << 3;
+    public static final int PARSE_IS_SYSTEM_DIR = 1 << 4;
+    public static final int PARSE_COLLECT_CERTIFICATES = 1 << 5;
+    public static final int PARSE_ENFORCE_CODE = 1 << 6;
+    public static final int PARSE_CHATTY = 1 << 31;
+
+    @IntDef(flag = true, prefix = { "PARSE_" }, value = {
+            PARSE_CHATTY,
+            PARSE_COLLECT_CERTIFICATES,
+            PARSE_ENFORCE_CODE,
+            PARSE_EXTERNAL_STORAGE,
+            PARSE_IGNORE_PROCESSES,
+            PARSE_IS_SYSTEM_DIR,
+            PARSE_MUST_BE_APK,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ParseFlags {}
+
+    public static final Comparator<String> sSplitNameComparator = new SplitNameComparator();
+
+    /**
+     * Used to sort a set of APKs based on their split names, always placing the
+     * base APK (with {@code null} split name) first.
+     */
+    private static class SplitNameComparator implements Comparator<String> {
+        @Override
+        public int compare(String lhs, String rhs) {
+            if (lhs == null) {
+                return -1;
+            } else if (rhs == null) {
+                return 1;
+            } else {
+                return lhs.compareTo(rhs);
+            }
+        }
+    }
+
+    /**
+     * Parse only lightweight details about the package at the given location.
+     * Automatically detects if the package is a monolithic style (single APK
+     * file) or cluster style (directory of APKs).
+     * <p>
+     * This performs sanity checking on cluster style packages, such as
+     * requiring identical package name and version codes, a single base APK,
+     * and unique split names.
+     *
+     * @see PackageParser#parsePackage(File, int)
+     */
+    @UnsupportedAppUsage
+    public static PackageLite parsePackageLite(File packageFile, int flags)
+            throws PackageParserException {
+        if (packageFile.isDirectory()) {
+            return parseClusterPackageLite(packageFile, flags);
+        } else {
+            return parseMonolithicPackageLite(packageFile, flags);
+        }
+    }
+
+    private static PackageLite parseMonolithicPackageLite(File packageFile, int flags)
+            throws PackageParserException {
+        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parseApkLite");
+        final ApkLite baseApk = parseApkLite(packageFile, flags);
+        final String packagePath = packageFile.getAbsolutePath();
+        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+        return new PackageLite(packagePath, baseApk, null, null, null, null, null, null);
+    }
+
+    static PackageLite parseClusterPackageLite(File packageDir, int flags)
+            throws PackageParserException {
+        final File[] files = packageDir.listFiles();
+        if (ArrayUtils.isEmpty(files)) {
+            throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
+                    "No packages found in split");
+        }
+        // Apk directory is directly nested under the current directory
+        if (files.length == 1 && files[0].isDirectory()) {
+            return parseClusterPackageLite(files[0], flags);
+        }
+
+        String packageName = null;
+        int versionCode = 0;
+
+        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parseApkLite");
+        final ArrayMap<String, ApkLite> apks = new ArrayMap<>();
+        for (File file : files) {
+            if (isApkFile(file)) {
+                final ApkLite lite = parseApkLite(file, flags);
+
+                // Assert that all package names and version codes are
+                // consistent with the first one we encounter.
+                if (packageName == null) {
+                    packageName = lite.packageName;
+                    versionCode = lite.versionCode;
+                } else {
+                    if (!packageName.equals(lite.packageName)) {
+                        throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
+                                "Inconsistent package " + lite.packageName + " in " + file
+                                + "; expected " + packageName);
+                    }
+                    if (versionCode != lite.versionCode) {
+                        throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
+                                "Inconsistent version " + lite.versionCode + " in " + file
+                                + "; expected " + versionCode);
+                    }
+                }
+
+                // Assert that each split is defined only once
+                if (apks.put(lite.splitName, lite) != null) {
+                    throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
+                            "Split name " + lite.splitName
+                            + " defined more than once; most recent was " + file);
+                }
+            }
+        }
+        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+
+        final ApkLite baseApk = apks.remove(null);
+        if (baseApk == null) {
+            throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
+                    "Missing base APK in " + packageDir);
+        }
+
+        // Always apply deterministic ordering based on splitName
+        final int size = apks.size();
+
+        String[] splitNames = null;
+        boolean[] isFeatureSplits = null;
+        String[] usesSplitNames = null;
+        String[] configForSplits = null;
+        String[] splitCodePaths = null;
+        int[] splitRevisionCodes = null;
+        String[] splitClassLoaderNames = null;
+        if (size > 0) {
+            splitNames = new String[size];
+            isFeatureSplits = new boolean[size];
+            usesSplitNames = new String[size];
+            configForSplits = new String[size];
+            splitCodePaths = new String[size];
+            splitRevisionCodes = new int[size];
+
+            splitNames = apks.keySet().toArray(splitNames);
+            Arrays.sort(splitNames, sSplitNameComparator);
+
+            for (int i = 0; i < size; i++) {
+                final ApkLite apk = apks.get(splitNames[i]);
+                usesSplitNames[i] = apk.usesSplitName;
+                isFeatureSplits[i] = apk.isFeatureSplit;
+                configForSplits[i] = apk.configForSplit;
+                splitCodePaths[i] = apk.codePath;
+                splitRevisionCodes[i] = apk.revisionCode;
+            }
+        }
+
+        final String codePath = packageDir.getAbsolutePath();
+        return new PackageLite(codePath, baseApk, splitNames, isFeatureSplits, usesSplitNames,
+                configForSplits, splitCodePaths, splitRevisionCodes);
+    }
+
+    /**
+     * Parse the package at the given location. Automatically detects if the
+     * package is a monolithic style (single APK file) or cluster style
+     * (directory of APKs).
+     * <p>
+     * This performs sanity checking on cluster style packages, such as
+     * requiring identical package name and version codes, a single base APK,
+     * and unique split names.
+     * <p>
+     * Note that this <em>does not</em> perform signature verification; that
+     * must be done separately in {@link #collectCertificates(Package, boolean)}.
+     *
+     * If {@code useCaches} is true, the package parser might return a cached
+     * result from a previous parse of the same {@code packageFile} with the same
+     * {@code flags}. Note that this method does not check whether {@code packageFile}
+     * has changed since the last parse, it's up to callers to do so.
+     *
+     * @see #parsePackageLite(File, int)
+     */
+    @UnsupportedAppUsage
+    public Package parsePackage(File packageFile, int flags, boolean useCaches)
+            throws PackageParserException {
+        if (packageFile.isDirectory()) {
+            return parseClusterPackage(packageFile, flags);
+        } else {
+            return parseMonolithicPackage(packageFile, flags);
+        }
+    }
+
+    /**
+     * Equivalent to {@link #parsePackage(File, int, boolean)} with {@code useCaches == false}.
+     */
+    @UnsupportedAppUsage
+    public Package parsePackage(File packageFile, int flags) throws PackageParserException {
+        return parsePackage(packageFile, flags, false /* useCaches */);
+    }
+
+    /**
+     * Parse all APKs contained in the given directory, treating them as a
+     * single package. This also performs sanity checking, such as requiring
+     * identical package name and version codes, a single base APK, and unique
+     * split names.
+     * <p>
+     * Note that this <em>does not</em> perform signature verification; that
+     * must be done separately in
+     * {@link #collectCertificates(Package, boolean)} .
+     */
+    private Package parseClusterPackage(File packageDir, int flags) throws PackageParserException {
+        final PackageLite lite = parseClusterPackageLite(packageDir, 0);
+        if (mOnlyCoreApps && !lite.coreApp) {
+            throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                    "Not a coreApp: " + packageDir);
+        }
+
+        // Build the split dependency tree.
+        SparseArray<int[]> splitDependencies = null;
+        final SplitAssetLoader assetLoader;
+        if (lite.isolatedSplits && !ArrayUtils.isEmpty(lite.splitNames)) {
+            try {
+                splitDependencies = SplitAssetDependencyLoader.createDependenciesFromPackage(lite);
+                assetLoader = new SplitAssetDependencyLoader(lite, splitDependencies, flags);
+            } catch (SplitAssetDependencyLoader.IllegalDependencyException e) {
+                throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST, e.getMessage());
+            }
+        } else {
+            assetLoader = new DefaultSplitAssetLoader(lite, flags);
+        }
+
+        try {
+            final AssetManager assets = assetLoader.getBaseAssetManager();
+            final File baseApk = new File(lite.baseCodePath);
+            final Package pkg = parseBaseApk(baseApk, assets, flags);
+            if (pkg == null) {
+                throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
+                        "Failed to parse base APK: " + baseApk);
+            }
+
+            if (!ArrayUtils.isEmpty(lite.splitNames)) {
+                final int num = lite.splitNames.length;
+                pkg.splitNames = lite.splitNames;
+                pkg.splitCodePaths = lite.splitCodePaths;
+                pkg.splitRevisionCodes = lite.splitRevisionCodes;
+                pkg.splitFlags = new int[num];
+                pkg.splitPrivateFlags = new int[num];
+                pkg.applicationInfo.splitNames = pkg.splitNames;
+                pkg.applicationInfo.splitDependencies = splitDependencies;
+                pkg.applicationInfo.splitClassLoaderNames = new String[num];
+
+                for (int i = 0; i < num; i++) {
+                    final AssetManager splitAssets = assetLoader.getSplitAssetManager(i);
+                    parseSplitApk(pkg, i, splitAssets, flags);
+                }
+            }
+
+            pkg.setCodePath(lite.codePath);
+            pkg.setUse32bitAbi(lite.use32bitAbi);
+            return pkg;
+        } finally {
+            IoUtils.closeQuietly(assetLoader);
+        }
+    }
+
+    /**
+     * Parse the given APK file, treating it as as a single monolithic package.
+     * <p>
+     * Note that this <em>does not</em> perform signature verification; that
+     * must be done separately in
+     * {@link #collectCertificates(Package, boolean)}.
+     */
+    @UnsupportedAppUsage
+    public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException {
+        final PackageLite lite = parseMonolithicPackageLite(apkFile, flags);
+        if (mOnlyCoreApps) {
+            if (!lite.coreApp) {
+                throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                        "Not a coreApp: " + apkFile);
+            }
+        }
+
+        final SplitAssetLoader assetLoader = new DefaultSplitAssetLoader(lite, flags);
+        try {
+            final Package pkg = parseBaseApk(apkFile, assetLoader.getBaseAssetManager(), flags);
+            pkg.setCodePath(apkFile.getCanonicalPath());
+            pkg.setUse32bitAbi(lite.use32bitAbi);
+            return pkg;
+        } catch (IOException e) {
+            throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
+                    "Failed to get path: " + apkFile, e);
+        } finally {
+            IoUtils.closeQuietly(assetLoader);
+        }
+    }
+
+    private Package parseBaseApk(File apkFile, AssetManager assets, int flags)
+            throws PackageParserException {
+        final String apkPath = apkFile.getAbsolutePath();
+
+        String volumeUuid = null;
+        if (apkPath.startsWith(MNT_EXPAND)) {
+            final int end = apkPath.indexOf('/', MNT_EXPAND.length());
+            volumeUuid = apkPath.substring(MNT_EXPAND.length(), end);
+        }
+
+        mParseError = PackageManager.INSTALL_SUCCEEDED;
+        mArchiveSourcePath = apkFile.getAbsolutePath();
+
+        if (DEBUG_JAR) Slog.d(TAG, "Scanning base APK: " + apkPath);
+
+        XmlResourceParser parser = null;
+        try {
+            final int cookie = assets.findCookieForPath(apkPath);
+            if (cookie == 0) {
+                throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
+                        "Failed adding asset path: " + apkPath);
+            }
+            parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
+            final Resources res = new Resources(assets, mMetrics, null);
+
+            final String[] outError = new String[1];
+            final Package pkg = parseBaseApk(apkPath, res, parser, flags, outError);
+            if (pkg == null) {
+                throw new PackageParserException(mParseError,
+                        apkPath + " (at " + parser.getPositionDescription() + "): " + outError[0]);
+            }
+
+            pkg.setVolumeUuid(volumeUuid);
+            pkg.setApplicationVolumeUuid(volumeUuid);
+            pkg.setBaseCodePath(apkPath);
+            pkg.setSigningDetails(SigningDetails.UNKNOWN);
+
+            return pkg;
+
+        } catch (PackageParserException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
+                    "Failed to read manifest from " + apkPath, e);
+        } finally {
+            IoUtils.closeQuietly(parser);
+        }
+    }
+
+    private void parseSplitApk(Package pkg, int splitIndex, AssetManager assets, int flags)
+            throws PackageParserException {
+        final String apkPath = pkg.splitCodePaths[splitIndex];
+
+        mParseError = PackageManager.INSTALL_SUCCEEDED;
+        mArchiveSourcePath = apkPath;
+
+        if (DEBUG_JAR) Slog.d(TAG, "Scanning split APK: " + apkPath);
+
+        final Resources res;
+        XmlResourceParser parser = null;
+        try {
+            // This must always succeed, as the path has been added to the AssetManager before.
+            final int cookie = assets.findCookieForPath(apkPath);
+            if (cookie == 0) {
+                throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
+                        "Failed adding asset path: " + apkPath);
+            }
+
+            parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
+            res = new Resources(assets, mMetrics, null);
+
+            final String[] outError = new String[1];
+            pkg = parseSplitApk(pkg, res, parser, flags, splitIndex, outError);
+            if (pkg == null) {
+                throw new PackageParserException(mParseError,
+                        apkPath + " (at " + parser.getPositionDescription() + "): " + outError[0]);
+            }
+
+        } catch (PackageParserException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
+                    "Failed to read manifest from " + apkPath, e);
+        } finally {
+            IoUtils.closeQuietly(parser);
+        }
+    }
+
+    /**
+     * Parse the manifest of a <em>split APK</em>.
+     * <p>
+     * Note that split APKs have many more restrictions on what they're capable
+     * of doing, so many valid features of a base APK have been carefully
+     * omitted here.
+     */
+    private Package parseSplitApk(Package pkg, Resources res, XmlResourceParser parser, int flags,
+            int splitIndex, String[] outError) throws XmlPullParserException, IOException,
+            PackageParserException {
+        AttributeSet attrs = parser;
+
+        // We parsed manifest tag earlier; just skip past it
+        parsePackageSplitNames(parser, attrs);
+
+        mParseInstrumentationArgs = null;
+
+        int type;
+
+        boolean foundApp = false;
+
+        int outerDepth = parser.getDepth();
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+
+            String tagName = parser.getName();
+            if (tagName.equals(TAG_APPLICATION)) {
+                if (foundApp) {
+                    if (RIGID_PARSER) {
+                        outError[0] = "<manifest> has more than one <application>";
+                        mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+                        return null;
+                    } else {
+                        Slog.w(TAG, "<manifest> has more than one <application>");
+                        XmlUtils.skipCurrentTag(parser);
+                        continue;
+                    }
+                }
+
+                foundApp = true;
+                if (!parseSplitApplication(pkg, res, parser, flags, splitIndex, outError)) {
+                    return null;
+                }
+
+            } else if (RIGID_PARSER) {
+                outError[0] = "Bad element under <manifest>: "
+                    + parser.getName();
+                mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+                return null;
+
+            } else {
+                Slog.w(TAG, "Unknown element under <manifest>: " + parser.getName()
+                        + " at " + mArchiveSourcePath + " "
+                        + parser.getPositionDescription());
+                XmlUtils.skipCurrentTag(parser);
+                continue;
+            }
+        }
+
+        if (!foundApp) {
+            outError[0] = "<manifest> does not contain an <application>";
+            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_EMPTY;
+        }
+
+        return pkg;
+    }
+
+    /** Parses the public keys from the set of signatures. */
+    public static ArraySet<PublicKey> toSigningKeys(Signature[] signatures)
+            throws CertificateException {
+        ArraySet<PublicKey> keys = new ArraySet<>(signatures.length);
+        for (int i = 0; i < signatures.length; i++) {
+            keys.add(signatures[i].getPublicKey());
+        }
+        return keys;
+    }
+
+    /**
+     * Collect certificates from all the APKs described in the given package,
+     * populating {@link Package#mSigningDetails}. Also asserts that all APK
+     * contents are signed correctly and consistently.
+     */
+    @UnsupportedAppUsage
+    public static void collectCertificates(Package pkg, boolean skipVerify)
+            throws PackageParserException {
+        collectCertificatesInternal(pkg, skipVerify);
+        final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
+        for (int i = 0; i < childCount; i++) {
+            Package childPkg = pkg.childPackages.get(i);
+            childPkg.mSigningDetails = pkg.mSigningDetails;
+        }
+    }
+
+    private static void collectCertificatesInternal(Package pkg, boolean skipVerify)
+            throws PackageParserException {
+        pkg.mSigningDetails = SigningDetails.UNKNOWN;
+
+        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
+        try {
+            collectCertificates(pkg, new File(pkg.baseCodePath), skipVerify);
+
+            if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) {
+                for (int i = 0; i < pkg.splitCodePaths.length; i++) {
+                    collectCertificates(pkg, new File(pkg.splitCodePaths[i]), skipVerify);
+                }
+            }
+        } finally {
+            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+        }
+    }
+
+    @UnsupportedAppUsage
+    private static void collectCertificates(Package pkg, File apkFile, boolean skipVerify)
+            throws PackageParserException {
+        final String apkPath = apkFile.getAbsolutePath();
+
+        int minSignatureScheme = ApkSignatureVerifier.getMinimumSignatureSchemeVersionForTargetSdk(
+                pkg.applicationInfo.targetSdkVersion);
+        if (pkg.applicationInfo.isStaticSharedLibrary()) {
+            // must use v2 signing scheme
+            minSignatureScheme = SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2;
+        }
+        SigningDetails verified;
+        if (skipVerify) {
+            // systemDir APKs are already trusted, save time by not verifying
+            verified = ApkSignatureVerifier.unsafeGetCertsWithoutVerification(
+                        apkPath, minSignatureScheme);
+        } else {
+            verified = ApkSignatureVerifier.verify(apkPath, minSignatureScheme);
+        }
+
+        // Verify that entries are signed consistently with the first pkg
+        // we encountered. Note that for splits, certificates may have
+        // already been populated during an earlier parse of a base APK.
+        if (pkg.mSigningDetails == SigningDetails.UNKNOWN) {
+            pkg.mSigningDetails = verified;
+        } else {
+            if (!Signature.areExactMatch(pkg.mSigningDetails.signatures, verified.signatures)) {
+                throw new PackageParserException(
+                        INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
+                        apkPath + " has mismatched certificates");
+            }
+        }
+    }
+
+    private static AssetManager newConfiguredAssetManager() {
+        AssetManager assetManager = new AssetManager();
+        assetManager.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                Build.VERSION.RESOURCES_SDK_INT);
+        return assetManager;
+    }
+
+    /**
+     * Utility method that retrieves lightweight details about a single APK
+     * file, including package name, split name, and install location.
+     *
+     * @param apkFile path to a single APK
+     * @param flags optional parse flags, such as
+     *            {@link #PARSE_COLLECT_CERTIFICATES}
+     */
+    public static ApkLite parseApkLite(File apkFile, int flags)
+            throws PackageParserException {
+        return parseApkLiteInner(apkFile, null, null, flags);
+    }
+
+    /**
+     * Utility method that retrieves lightweight details about a single APK
+     * file, including package name, split name, and install location.
+     *
+     * @param fd already open file descriptor of an apk file
+     * @param debugPathName arbitrary text name for this file, for debug output
+     * @param flags optional parse flags, such as
+     *            {@link #PARSE_COLLECT_CERTIFICATES}
+     */
+    public static ApkLite parseApkLite(FileDescriptor fd, String debugPathName, int flags)
+            throws PackageParserException {
+        return parseApkLiteInner(null, fd, debugPathName, flags);
+    }
+
+    private static ApkLite parseApkLiteInner(File apkFile, FileDescriptor fd, String debugPathName,
+            int flags) throws PackageParserException {
+        final String apkPath = fd != null ? debugPathName : apkFile.getAbsolutePath();
+
+        XmlResourceParser parser = null;
+        ApkAssets apkAssets = null;
+        try {
+            try {
+                apkAssets = fd != null
+                        ? ApkAssets.loadFromFd(fd, debugPathName, 0 /* flags */, null /* assets */)
+                        : ApkAssets.loadFromPath(apkPath);
+            } catch (IOException e) {
+                throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
+                        "Failed to parse " + apkPath);
+            }
+
+            parser = apkAssets.openXml(ANDROID_MANIFEST_FILENAME);
+
+            final SigningDetails signingDetails;
+            if ((flags & PARSE_COLLECT_CERTIFICATES) != 0) {
+                // TODO: factor signature related items out of Package object
+                final Package tempPkg = new Package((String) null);
+                final boolean skipVerify = (flags & PARSE_IS_SYSTEM_DIR) != 0;
+                Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
+                try {
+                    collectCertificates(tempPkg, apkFile, skipVerify);
+                } finally {
+                    Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+                }
+                signingDetails = tempPkg.mSigningDetails;
+            } else {
+                signingDetails = SigningDetails.UNKNOWN;
+            }
+
+            final AttributeSet attrs = parser;
+            return parseApkLite(apkPath, parser, attrs, signingDetails);
+
+        } catch (XmlPullParserException | IOException | RuntimeException e) {
+            Slog.w(TAG, "Failed to parse " + apkPath, e);
+            throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
+                    "Failed to parse " + apkPath, e);
+        } finally {
+            IoUtils.closeQuietly(parser);
+            if (apkAssets != null) {
+                try {
+                    apkAssets.close();
+                } catch (Throwable ignored) {
+                }
+            }
+            // TODO(b/72056911): Implement AutoCloseable on ApkAssets.
+        }
+    }
+
+    public static String validateName(String name, boolean requireSeparator,
+            boolean requireFilename) {
+        final int N = name.length();
+        boolean hasSep = false;
+        boolean front = true;
+        for (int i=0; i<N; i++) {
+            final char c = name.charAt(i);
+            if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
+                front = false;
+                continue;
+            }
+            if (!front) {
+                if ((c >= '0' && c <= '9') || c == '_') {
+                    continue;
+                }
+            }
+            if (c == '.') {
+                hasSep = true;
+                front = true;
+                continue;
+            }
+            return "bad character '" + c + "'";
+        }
+        if (requireFilename && !FileUtils.isValidExtFilename(name)) {
+            return "Invalid filename";
+        }
+        return hasSep || !requireSeparator
+                ? null : "must have at least one '.' separator";
+    }
+
+    public static Pair<String, String> parsePackageSplitNames(XmlPullParser parser,
+            AttributeSet attrs) throws IOException, XmlPullParserException,
+            PackageParserException {
+
+        int type;
+        while ((type = parser.next()) != XmlPullParser.START_TAG
+                && type != XmlPullParser.END_DOCUMENT) {
+        }
+
+        if (type != XmlPullParser.START_TAG) {
+            throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                    "No start tag found");
+        }
+        if (!parser.getName().equals(TAG_MANIFEST)) {
+            throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                    "No <manifest> tag");
+        }
+
+        final String packageName = attrs.getAttributeValue(null, "package");
+        if (!"android".equals(packageName)) {
+            final String error = validateName(packageName, true, true);
+            if (error != null) {
+                throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME,
+                        "Invalid manifest package: " + error);
+            }
+        }
+
+        String splitName = attrs.getAttributeValue(null, "split");
+        if (splitName != null) {
+            if (splitName.length() == 0) {
+                splitName = null;
+            } else {
+                final String error = validateName(splitName, false, false);
+                if (error != null) {
+                    throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME,
+                            "Invalid manifest split: " + error);
+                }
+            }
+        }
+
+        return Pair.create(packageName.intern(),
+                (splitName != null) ? splitName.intern() : splitName);
+    }
+
+    private static ApkLite parseApkLite(String codePath, XmlPullParser parser, AttributeSet attrs,
+            SigningDetails signingDetails)
+            throws IOException, XmlPullParserException, PackageParserException {
+        final Pair<String, String> packageSplit = parsePackageSplitNames(parser, attrs);
+
+        int installLocation = PARSE_DEFAULT_INSTALL_LOCATION;
+        int versionCode = 0;
+        int versionCodeMajor = 0;
+        int targetSdkVersion = DEFAULT_TARGET_SDK_VERSION;
+        int minSdkVersion = DEFAULT_MIN_SDK_VERSION;
+        int revisionCode = 0;
+        boolean coreApp = false;
+        boolean debuggable = false;
+        boolean multiArch = false;
+        boolean use32bitAbi = false;
+        boolean extractNativeLibs = true;
+        boolean isolatedSplits = false;
+        boolean isFeatureSplit = false;
+        boolean isSplitRequired = false;
+        boolean useEmbeddedDex = false;
+        String configForSplit = null;
+        String usesSplitName = null;
+        String targetPackage = null;
+        boolean overlayIsStatic = false;
+        int overlayPriority = 0;
+
+        String requiredSystemPropertyName = null;
+        String requiredSystemPropertyValue = null;
+
+        for (int i = 0; i < attrs.getAttributeCount(); i++) {
+            final String attr = attrs.getAttributeName(i);
+            if (attr.equals("installLocation")) {
+                installLocation = attrs.getAttributeIntValue(i,
+                        PARSE_DEFAULT_INSTALL_LOCATION);
+            } else if (attr.equals("versionCode")) {
+                versionCode = attrs.getAttributeIntValue(i, 0);
+            } else if (attr.equals("versionCodeMajor")) {
+                versionCodeMajor = attrs.getAttributeIntValue(i, 0);
+            } else if (attr.equals("revisionCode")) {
+                revisionCode = attrs.getAttributeIntValue(i, 0);
+            } else if (attr.equals("coreApp")) {
+                coreApp = attrs.getAttributeBooleanValue(i, false);
+            } else if (attr.equals("isolatedSplits")) {
+                isolatedSplits = attrs.getAttributeBooleanValue(i, false);
+            } else if (attr.equals("configForSplit")) {
+                configForSplit = attrs.getAttributeValue(i);
+            } else if (attr.equals("isFeatureSplit")) {
+                isFeatureSplit = attrs.getAttributeBooleanValue(i, false);
+            } else if (attr.equals("isSplitRequired")) {
+                isSplitRequired = attrs.getAttributeBooleanValue(i, false);
+            }
+        }
+
+        // Only search the tree when the tag is the direct child of <manifest> tag
+        int type;
+        final int searchDepth = parser.getDepth() + 1;
+
+        final List<VerifierInfo> verifiers = new ArrayList<VerifierInfo>();
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() >= searchDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+
+            if (parser.getDepth() != searchDepth) {
+                continue;
+            }
+
+            if (TAG_PACKAGE_VERIFIER.equals(parser.getName())) {
+                final VerifierInfo verifier = parseVerifier(attrs);
+                if (verifier != null) {
+                    verifiers.add(verifier);
+                }
+            } else if (TAG_APPLICATION.equals(parser.getName())) {
+                for (int i = 0; i < attrs.getAttributeCount(); ++i) {
+                    final String attr = attrs.getAttributeName(i);
+                    if ("debuggable".equals(attr)) {
+                        debuggable = attrs.getAttributeBooleanValue(i, false);
+                    }
+                    if ("multiArch".equals(attr)) {
+                        multiArch = attrs.getAttributeBooleanValue(i, false);
+                    }
+                    if ("use32bitAbi".equals(attr)) {
+                        use32bitAbi = attrs.getAttributeBooleanValue(i, false);
+                    }
+                    if ("extractNativeLibs".equals(attr)) {
+                        extractNativeLibs = attrs.getAttributeBooleanValue(i, true);
+                    }
+                    if ("useEmbeddedDex".equals(attr)) {
+                        useEmbeddedDex = attrs.getAttributeBooleanValue(i, false);
+                    }
+                }
+            } else if (PackageParser.TAG_OVERLAY.equals(parser.getName())) {
+                for (int i = 0; i < attrs.getAttributeCount(); ++i) {
+                    final String attr = attrs.getAttributeName(i);
+                    if ("requiredSystemPropertyName".equals(attr)) {
+                        requiredSystemPropertyName = attrs.getAttributeValue(i);
+                    } else if ("requiredSystemPropertyValue".equals(attr)) {
+                        requiredSystemPropertyValue = attrs.getAttributeValue(i);
+                    } else if ("targetPackage".equals(attr)) {
+                        targetPackage = attrs.getAttributeValue(i);;
+                    } else if ("isStatic".equals(attr)) {
+                        overlayIsStatic = attrs.getAttributeBooleanValue(i, false);
+                    } else if ("priority".equals(attr)) {
+                        overlayPriority = attrs.getAttributeIntValue(i, 0);
+                    }
+                }
+            } else if (TAG_USES_SPLIT.equals(parser.getName())) {
+                if (usesSplitName != null) {
+                    Slog.w(TAG, "Only one <uses-split> permitted. Ignoring others.");
+                    continue;
+                }
+
+                usesSplitName = attrs.getAttributeValue(ANDROID_RESOURCES, "name");
+                if (usesSplitName == null) {
+                    throw new PackageParserException(
+                            PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                            "<uses-split> tag requires 'android:name' attribute");
+                }
+            } else if (TAG_USES_SDK.equals(parser.getName())) {
+                for (int i = 0; i < attrs.getAttributeCount(); ++i) {
+                    final String attr = attrs.getAttributeName(i);
+                    if ("targetSdkVersion".equals(attr)) {
+                        targetSdkVersion = attrs.getAttributeIntValue(i,
+                                DEFAULT_TARGET_SDK_VERSION);
+                    }
+                    if ("minSdkVersion".equals(attr)) {
+                        minSdkVersion = attrs.getAttributeIntValue(i, DEFAULT_MIN_SDK_VERSION);
+                    }
+                }
+            }
+        }
+
+        // Check to see if overlay should be excluded based on system property condition
+        if (!checkRequiredSystemProperties(requiredSystemPropertyName,
+                requiredSystemPropertyValue)) {
+            Slog.i(TAG, "Skipping target and overlay pair " + targetPackage + " and "
+                    + codePath + ": overlay ignored due to required system property: "
+                    + requiredSystemPropertyName + " with value: " + requiredSystemPropertyValue);
+            targetPackage = null;
+            overlayIsStatic = false;
+            overlayPriority = 0;
+        }
+
+        return new ApkLite(codePath, packageSplit.first, packageSplit.second, isFeatureSplit,
+                configForSplit, usesSplitName, isSplitRequired, versionCode, versionCodeMajor,
+                revisionCode, installLocation, verifiers, signingDetails, coreApp, debuggable,
+                multiArch, use32bitAbi, useEmbeddedDex, extractNativeLibs, isolatedSplits,
+                targetPackage, overlayIsStatic, overlayPriority, minSdkVersion, targetSdkVersion);
+    }
+
+    /**
+     * Parses a child package and adds it to the parent if successful. If you add
+     * new tags that need to be supported by child packages make sure to add them
+     * to {@link #CHILD_PACKAGE_TAGS}.
+     *
+     * @param parentPkg The parent that contains the child
+     * @param res Resources against which to resolve values
+     * @param parser Parser of the manifest
+     * @param flags Flags about how to parse
+     * @param outError Human readable error if parsing fails
+     * @return True of parsing succeeded.
+     *
+     * @throws XmlPullParserException
+     * @throws IOException
+     */
+    private boolean parseBaseApkChild(Package parentPkg, Resources res, XmlResourceParser parser,
+            int flags, String[] outError) throws XmlPullParserException, IOException {
+        // Make sure we have a valid child package name
+        String childPackageName = parser.getAttributeValue(null, "package");
+        if (validateName(childPackageName, true, false) != null) {
+            mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
+            return false;
+        }
+
+        // Child packages must be unique
+        if (childPackageName.equals(parentPkg.packageName)) {
+            String message = "Child package name cannot be equal to parent package name: "
+                    + parentPkg.packageName;
+            Slog.w(TAG, message);
+            outError[0] = message;
+            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+            return false;
+        }
+
+        // Child packages must be unique
+        if (parentPkg.hasChildPackage(childPackageName)) {
+            String message = "Duplicate child package:" + childPackageName;
+            Slog.w(TAG, message);
+            outError[0] = message;
+            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+            return false;
+        }
+
+        // Go ahead and parse the child
+        Package childPkg = new Package(childPackageName);
+
+        // Child package inherits parent version code/name/target SDK
+        childPkg.mVersionCode = parentPkg.mVersionCode;
+        childPkg.baseRevisionCode = parentPkg.baseRevisionCode;
+        childPkg.mVersionName = parentPkg.mVersionName;
+        childPkg.applicationInfo.targetSdkVersion = parentPkg.applicationInfo.targetSdkVersion;
+        childPkg.applicationInfo.minSdkVersion = parentPkg.applicationInfo.minSdkVersion;
+
+        childPkg = parseBaseApkCommon(childPkg, CHILD_PACKAGE_TAGS, res, parser, flags, outError);
+        if (childPkg == null) {
+            // If we got null then error was set during child parsing
+            return false;
+        }
+
+        // Set the parent-child relation
+        if (parentPkg.childPackages == null) {
+            parentPkg.childPackages = new ArrayList<>();
+        }
+        parentPkg.childPackages.add(childPkg);
+        childPkg.parentPackage = parentPkg;
+
+        return true;
+    }
+
+    /**
+     * Parse the manifest of a <em>base APK</em>. When adding new features you
+     * need to consider whether they should be supported by split APKs and child
+     * packages.
+     *
+     * @param apkPath The package apk file path
+     * @param res The resources from which to resolve values
+     * @param parser The manifest parser
+     * @param flags Flags how to parse
+     * @param outError Human readable error message
+     * @return Parsed package or null on error.
+     *
+     * @throws XmlPullParserException
+     * @throws IOException
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    private Package parseBaseApk(String apkPath, Resources res, XmlResourceParser parser, int flags,
+            String[] outError) throws XmlPullParserException, IOException {
+        final String splitName;
+        final String pkgName;
+
+        try {
+            Pair<String, String> packageSplit = parsePackageSplitNames(parser, parser);
+            pkgName = packageSplit.first;
+            splitName = packageSplit.second;
+
+            if (!TextUtils.isEmpty(splitName)) {
+                outError[0] = "Expected base APK, but found split " + splitName;
+                mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
+                return null;
+            }
+        } catch (PackageParserException e) {
+            mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
+            return null;
+        }
+
+        final Package pkg = new Package(pkgName);
+
+        TypedArray sa = res.obtainAttributes(parser,
+                com.android.internal.R.styleable.AndroidManifest);
+
+        pkg.mVersionCode = sa.getInteger(
+                com.android.internal.R.styleable.AndroidManifest_versionCode, 0);
+        pkg.mVersionCodeMajor = sa.getInteger(
+                com.android.internal.R.styleable.AndroidManifest_versionCodeMajor, 0);
+        pkg.applicationInfo.setVersionCode(pkg.getLongVersionCode());
+        pkg.baseRevisionCode = sa.getInteger(
+                com.android.internal.R.styleable.AndroidManifest_revisionCode, 0);
+        pkg.mVersionName = sa.getNonConfigurationString(
+                com.android.internal.R.styleable.AndroidManifest_versionName, 0);
+        if (pkg.mVersionName != null) {
+            pkg.mVersionName = pkg.mVersionName.intern();
+        }
+
+        pkg.coreApp = parser.getAttributeBooleanValue(null, "coreApp", false);
+
+        final boolean isolatedSplits = sa.getBoolean(
+                com.android.internal.R.styleable.AndroidManifest_isolatedSplits, false);
+        if (isolatedSplits) {
+            pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_ISOLATED_SPLIT_LOADING;
+        }
+
+        pkg.mCompileSdkVersion = sa.getInteger(
+                com.android.internal.R.styleable.AndroidManifest_compileSdkVersion, 0);
+        pkg.applicationInfo.compileSdkVersion = pkg.mCompileSdkVersion;
+        pkg.mCompileSdkVersionCodename = sa.getNonConfigurationString(
+                com.android.internal.R.styleable.AndroidManifest_compileSdkVersionCodename, 0);
+        if (pkg.mCompileSdkVersionCodename != null) {
+            pkg.mCompileSdkVersionCodename = pkg.mCompileSdkVersionCodename.intern();
+        }
+        pkg.applicationInfo.compileSdkVersionCodename = pkg.mCompileSdkVersionCodename;
+
+        sa.recycle();
+
+        return parseBaseApkCommon(pkg, null, res, parser, flags, outError);
+    }
+
+    /**
+     * This is the common parsing routing for handling parent and child
+     * packages in a base APK. The difference between parent and child
+     * parsing is that some tags are not supported by child packages as
+     * well as some manifest attributes are ignored. The implementation
+     * assumes the calling code has already handled the manifest tag if needed
+     * (this applies to the parent only).
+     *
+     * @param pkg The package which to populate
+     * @param acceptedTags Which tags to handle, null to handle all
+     * @param res Resources against which to resolve values
+     * @param parser Parser of the manifest
+     * @param flags Flags about how to parse
+     * @param outError Human readable error if parsing fails
+     * @return The package if parsing succeeded or null.
+     *
+     * @throws XmlPullParserException
+     * @throws IOException
+     */
+    private Package parseBaseApkCommon(Package pkg, Set<String> acceptedTags, Resources res,
+            XmlResourceParser parser, int flags, String[] outError) throws XmlPullParserException,
+            IOException {
+        mParseInstrumentationArgs = null;
+
+        int type;
+        boolean foundApp = false;
+
+        TypedArray sa = res.obtainAttributes(parser,
+                com.android.internal.R.styleable.AndroidManifest);
+
+        String str = sa.getNonConfigurationString(
+                com.android.internal.R.styleable.AndroidManifest_sharedUserId, 0);
+        if (str != null && str.length() > 0) {
+            String nameError = validateName(str, true, true);
+            if (nameError != null && !"android".equals(pkg.packageName)) {
+                outError[0] = "<manifest> specifies bad sharedUserId name \""
+                    + str + "\": " + nameError;
+                mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID;
+                return null;
+            }
+            pkg.mSharedUserId = str.intern();
+            pkg.mSharedUserLabel = sa.getResourceId(
+                    com.android.internal.R.styleable.AndroidManifest_sharedUserLabel, 0);
+        }
+
+        pkg.installLocation = sa.getInteger(
+                com.android.internal.R.styleable.AndroidManifest_installLocation,
+                PARSE_DEFAULT_INSTALL_LOCATION);
+        pkg.applicationInfo.installLocation = pkg.installLocation;
+
+        final int targetSandboxVersion = sa.getInteger(
+                com.android.internal.R.styleable.AndroidManifest_targetSandboxVersion,
+                PARSE_DEFAULT_TARGET_SANDBOX);
+        pkg.applicationInfo.targetSandboxVersion = targetSandboxVersion;
+
+        /* Set the global "on SD card" flag */
+        if ((flags & PARSE_EXTERNAL_STORAGE) != 0) {
+            pkg.applicationInfo.flags |= ApplicationInfo.FLAG_EXTERNAL_STORAGE;
+        }
+
+        // Resource boolean are -1, so 1 means we don't know the value.
+        int supportsSmallScreens = 1;
+        int supportsNormalScreens = 1;
+        int supportsLargeScreens = 1;
+        int supportsXLargeScreens = 1;
+        int resizeable = 1;
+        int anyDensity = 1;
+
+        int outerDepth = parser.getDepth();
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+
+            String tagName = parser.getName();
+
+            if (acceptedTags != null && !acceptedTags.contains(tagName)) {
+                Slog.w(TAG, "Skipping unsupported element under <manifest>: "
+                        + tagName + " at " + mArchiveSourcePath + " "
+                        + parser.getPositionDescription());
+                XmlUtils.skipCurrentTag(parser);
+                continue;
+            }
+
+            if (tagName.equals(TAG_APPLICATION)) {
+                if (foundApp) {
+                    if (RIGID_PARSER) {
+                        outError[0] = "<manifest> has more than one <application>";
+                        mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+                        return null;
+                    } else {
+                        Slog.w(TAG, "<manifest> has more than one <application>");
+                        XmlUtils.skipCurrentTag(parser);
+                        continue;
+                    }
+                }
+
+                foundApp = true;
+                if (!parseBaseApplication(pkg, res, parser, flags, outError)) {
+                    return null;
+                }
+            } else if (tagName.equals(TAG_OVERLAY)) {
+                sa = res.obtainAttributes(parser,
+                        com.android.internal.R.styleable.AndroidManifestResourceOverlay);
+                pkg.mOverlayTarget = sa.getString(
+                        com.android.internal.R.styleable.AndroidManifestResourceOverlay_targetPackage);
+                pkg.mOverlayTargetName = sa.getString(
+                        com.android.internal.R.styleable.AndroidManifestResourceOverlay_targetName);
+                pkg.mOverlayCategory = sa.getString(
+                        com.android.internal.R.styleable.AndroidManifestResourceOverlay_category);
+                pkg.mOverlayPriority = sa.getInt(
+                        com.android.internal.R.styleable.AndroidManifestResourceOverlay_priority,
+                        0);
+                pkg.mOverlayIsStatic = sa.getBoolean(
+                        com.android.internal.R.styleable.AndroidManifestResourceOverlay_isStatic,
+                        false);
+                final String propName = sa.getString(
+                        com.android.internal.R.styleable
+                        .AndroidManifestResourceOverlay_requiredSystemPropertyName);
+                final String propValue = sa.getString(
+                        com.android.internal.R.styleable
+                        .AndroidManifestResourceOverlay_requiredSystemPropertyValue);
+                sa.recycle();
+
+                if (pkg.mOverlayTarget == null) {
+                    outError[0] = "<overlay> does not specify a target package";
+                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+                    return null;
+                }
+
+                if (pkg.mOverlayPriority < 0 || pkg.mOverlayPriority > 9999) {
+                    outError[0] = "<overlay> priority must be between 0 and 9999";
+                    mParseError =
+                        PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+                    return null;
+                }
+
+                // check to see if overlay should be excluded based on system property condition
+                if (!checkRequiredSystemProperties(propName, propValue)) {
+                    Slog.i(TAG, "Skipping target and overlay pair " + pkg.mOverlayTarget + " and "
+                        + pkg.baseCodePath+ ": overlay ignored due to required system property: "
+                        + propName + " with value: " + propValue);
+                    mParseError = PackageManager.INSTALL_PARSE_FAILED_SKIPPED;
+                    return null;
+                }
+
+                pkg.applicationInfo.privateFlags |=
+                    ApplicationInfo.PRIVATE_FLAG_IS_RESOURCE_OVERLAY;
+
+                XmlUtils.skipCurrentTag(parser);
+
+            } else if (tagName.equals(TAG_KEY_SETS)) {
+                if (!parseKeySets(pkg, res, parser, outError)) {
+                    return null;
+                }
+            } else if (tagName.equals(TAG_PERMISSION_GROUP)) {
+                if (!parsePermissionGroup(pkg, flags, res, parser, outError)) {
+                    return null;
+                }
+            } else if (tagName.equals(TAG_PERMISSION)) {
+                if (!parsePermission(pkg, res, parser, outError)) {
+                    return null;
+                }
+            } else if (tagName.equals(TAG_PERMISSION_TREE)) {
+                if (!parsePermissionTree(pkg, res, parser, outError)) {
+                    return null;
+                }
+            } else if (tagName.equals(TAG_USES_PERMISSION)) {
+                if (!parseUsesPermission(pkg, res, parser)) {
+                    return null;
+                }
+            } else if (tagName.equals(TAG_USES_PERMISSION_SDK_M)
+                    || tagName.equals(TAG_USES_PERMISSION_SDK_23)) {
+                if (!parseUsesPermission(pkg, res, parser)) {
+                    return null;
+                }
+            } else if (tagName.equals(TAG_USES_CONFIGURATION)) {
+                ConfigurationInfo cPref = new ConfigurationInfo();
+                sa = res.obtainAttributes(parser,
+                        com.android.internal.R.styleable.AndroidManifestUsesConfiguration);
+                cPref.reqTouchScreen = sa.getInt(
+                        com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqTouchScreen,
+                        Configuration.TOUCHSCREEN_UNDEFINED);
+                cPref.reqKeyboardType = sa.getInt(
+                        com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqKeyboardType,
+                        Configuration.KEYBOARD_UNDEFINED);
+                if (sa.getBoolean(
+                        com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqHardKeyboard,
+                        false)) {
+                    cPref.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_HARD_KEYBOARD;
+                }
+                cPref.reqNavigation = sa.getInt(
+                        com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqNavigation,
+                        Configuration.NAVIGATION_UNDEFINED);
+                if (sa.getBoolean(
+                        com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqFiveWayNav,
+                        false)) {
+                    cPref.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_FIVE_WAY_NAV;
+                }
+                sa.recycle();
+                pkg.configPreferences = ArrayUtils.add(pkg.configPreferences, cPref);
+
+                XmlUtils.skipCurrentTag(parser);
+
+            } else if (tagName.equals(TAG_USES_FEATURE)) {
+                FeatureInfo fi = parseUsesFeature(res, parser);
+                pkg.reqFeatures = ArrayUtils.add(pkg.reqFeatures, fi);
+
+                if (fi.name == null) {
+                    ConfigurationInfo cPref = new ConfigurationInfo();
+                    cPref.reqGlEsVersion = fi.reqGlEsVersion;
+                    pkg.configPreferences = ArrayUtils.add(pkg.configPreferences, cPref);
+                }
+
+                XmlUtils.skipCurrentTag(parser);
+
+            } else if (tagName.equals(TAG_FEATURE_GROUP)) {
+                FeatureGroupInfo group = new FeatureGroupInfo();
+                ArrayList<FeatureInfo> features = null;
+                final int innerDepth = parser.getDepth();
+                while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                        && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
+                    if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                        continue;
+                    }
+
+                    final String innerTagName = parser.getName();
+                    if (innerTagName.equals("uses-feature")) {
+                        FeatureInfo featureInfo = parseUsesFeature(res, parser);
+                        // FeatureGroups are stricter and mandate that
+                        // any <uses-feature> declared are mandatory.
+                        featureInfo.flags |= FeatureInfo.FLAG_REQUIRED;
+                        features = ArrayUtils.add(features, featureInfo);
+                    } else {
+                        Slog.w(TAG, "Unknown element under <feature-group>: " + innerTagName +
+                                " at " + mArchiveSourcePath + " " +
+                                parser.getPositionDescription());
+                    }
+                    XmlUtils.skipCurrentTag(parser);
+                }
+
+                if (features != null) {
+                    group.features = new FeatureInfo[features.size()];
+                    group.features = features.toArray(group.features);
+                }
+                pkg.featureGroups = ArrayUtils.add(pkg.featureGroups, group);
+
+            } else if (tagName.equals(TAG_USES_SDK)) {
+                if (SDK_VERSION > 0) {
+                    sa = res.obtainAttributes(parser,
+                            com.android.internal.R.styleable.AndroidManifestUsesSdk);
+
+                    int minVers = 1;
+                    String minCode = null;
+                    int targetVers = 0;
+                    String targetCode = null;
+
+                    TypedValue val = sa.peekValue(
+                            com.android.internal.R.styleable.AndroidManifestUsesSdk_minSdkVersion);
+                    if (val != null) {
+                        if (val.type == TypedValue.TYPE_STRING && val.string != null) {
+                            minCode = val.string.toString();
+                        } else {
+                            // If it's not a string, it's an integer.
+                            minVers = val.data;
+                        }
+                    }
+
+                    val = sa.peekValue(
+                            com.android.internal.R.styleable.AndroidManifestUsesSdk_targetSdkVersion);
+                    if (val != null) {
+                        if (val.type == TypedValue.TYPE_STRING && val.string != null) {
+                            targetCode = val.string.toString();
+                            if (minCode == null) {
+                                minCode = targetCode;
+                            }
+                        } else {
+                            // If it's not a string, it's an integer.
+                            targetVers = val.data;
+                        }
+                    } else {
+                        targetVers = minVers;
+                        targetCode = minCode;
+                    }
+
+                    sa.recycle();
+
+                    final int minSdkVersion = PackageParser.computeMinSdkVersion(minVers, minCode,
+                            SDK_VERSION, SDK_CODENAMES, outError);
+                    if (minSdkVersion < 0) {
+                        mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK;
+                        return null;
+                    }
+
+                    final int targetSdkVersion = PackageParser.computeTargetSdkVersion(targetVers,
+                            targetCode, SDK_CODENAMES, outError);
+                    if (targetSdkVersion < 0) {
+                        mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK;
+                        return null;
+                    }
+
+                    pkg.applicationInfo.minSdkVersion = minSdkVersion;
+                    pkg.applicationInfo.targetSdkVersion = targetSdkVersion;
+                }
+
+                XmlUtils.skipCurrentTag(parser);
+
+            } else if (tagName.equals(TAG_SUPPORT_SCREENS)) {
+                sa = res.obtainAttributes(parser,
+                        com.android.internal.R.styleable.AndroidManifestSupportsScreens);
+
+                pkg.applicationInfo.requiresSmallestWidthDp = sa.getInteger(
+                        com.android.internal.R.styleable.AndroidManifestSupportsScreens_requiresSmallestWidthDp,
+                        0);
+                pkg.applicationInfo.compatibleWidthLimitDp = sa.getInteger(
+                        com.android.internal.R.styleable.AndroidManifestSupportsScreens_compatibleWidthLimitDp,
+                        0);
+                pkg.applicationInfo.largestWidthLimitDp = sa.getInteger(
+                        com.android.internal.R.styleable.AndroidManifestSupportsScreens_largestWidthLimitDp,
+                        0);
+
+                // This is a trick to get a boolean and still able to detect
+                // if a value was actually set.
+                supportsSmallScreens = sa.getInteger(
+                        com.android.internal.R.styleable.AndroidManifestSupportsScreens_smallScreens,
+                        supportsSmallScreens);
+                supportsNormalScreens = sa.getInteger(
+                        com.android.internal.R.styleable.AndroidManifestSupportsScreens_normalScreens,
+                        supportsNormalScreens);
+                supportsLargeScreens = sa.getInteger(
+                        com.android.internal.R.styleable.AndroidManifestSupportsScreens_largeScreens,
+                        supportsLargeScreens);
+                supportsXLargeScreens = sa.getInteger(
+                        com.android.internal.R.styleable.AndroidManifestSupportsScreens_xlargeScreens,
+                        supportsXLargeScreens);
+                resizeable = sa.getInteger(
+                        com.android.internal.R.styleable.AndroidManifestSupportsScreens_resizeable,
+                        resizeable);
+                anyDensity = sa.getInteger(
+                        com.android.internal.R.styleable.AndroidManifestSupportsScreens_anyDensity,
+                        anyDensity);
+
+                sa.recycle();
+
+                XmlUtils.skipCurrentTag(parser);
+
+            } else if (tagName.equals(TAG_PROTECTED_BROADCAST)) {
+                sa = res.obtainAttributes(parser,
+                        com.android.internal.R.styleable.AndroidManifestProtectedBroadcast);
+
+                // Note: don't allow this value to be a reference to a resource
+                // that may change.
+                String name = sa.getNonResourceString(
+                        com.android.internal.R.styleable.AndroidManifestProtectedBroadcast_name);
+
+                sa.recycle();
+
+                if (name != null) {
+                    if (pkg.protectedBroadcasts == null) {
+                        pkg.protectedBroadcasts = new ArrayList<String>();
+                    }
+                    if (!pkg.protectedBroadcasts.contains(name)) {
+                        pkg.protectedBroadcasts.add(name.intern());
+                    }
+                }
+
+                XmlUtils.skipCurrentTag(parser);
+
+            } else if (tagName.equals(TAG_INSTRUMENTATION)) {
+                if (parseInstrumentation(pkg, res, parser, outError) == null) {
+                    return null;
+                }
+            } else if (tagName.equals(TAG_ORIGINAL_PACKAGE)) {
+                sa = res.obtainAttributes(parser,
+                        com.android.internal.R.styleable.AndroidManifestOriginalPackage);
+
+                String orig =sa.getNonConfigurationString(
+                        com.android.internal.R.styleable.AndroidManifestOriginalPackage_name, 0);
+                if (!pkg.packageName.equals(orig)) {
+                    if (pkg.mOriginalPackages == null) {
+                        pkg.mOriginalPackages = new ArrayList<String>();
+                        pkg.mRealPackage = pkg.packageName;
+                    }
+                    pkg.mOriginalPackages.add(orig);
+                }
+
+                sa.recycle();
+
+                XmlUtils.skipCurrentTag(parser);
+
+            } else if (tagName.equals(TAG_ADOPT_PERMISSIONS)) {
+                sa = res.obtainAttributes(parser,
+                        com.android.internal.R.styleable.AndroidManifestOriginalPackage);
+
+                String name = sa.getNonConfigurationString(
+                        com.android.internal.R.styleable.AndroidManifestOriginalPackage_name, 0);
+
+                sa.recycle();
+
+                if (name != null) {
+                    if (pkg.mAdoptPermissions == null) {
+                        pkg.mAdoptPermissions = new ArrayList<String>();
+                    }
+                    pkg.mAdoptPermissions.add(name);
+                }
+
+                XmlUtils.skipCurrentTag(parser);
+
+            } else if (tagName.equals(TAG_USES_GL_TEXTURE)) {
+                // Just skip this tag
+                XmlUtils.skipCurrentTag(parser);
+                continue;
+
+            } else if (tagName.equals(TAG_COMPATIBLE_SCREENS)) {
+                // Just skip this tag
+                XmlUtils.skipCurrentTag(parser);
+                continue;
+            } else if (tagName.equals(TAG_SUPPORTS_INPUT)) {//
+                XmlUtils.skipCurrentTag(parser);
+                continue;
+
+            } else if (tagName.equals(TAG_EAT_COMMENT)) {
+                // Just skip this tag
+                XmlUtils.skipCurrentTag(parser);
+                continue;
+
+            } else if (tagName.equals(TAG_PACKAGE)) {
+                if (!MULTI_PACKAGE_APK_ENABLED) {
+                    XmlUtils.skipCurrentTag(parser);
+                    continue;
+                }
+                if (!parseBaseApkChild(pkg, res, parser, flags, outError)) {
+                    // If parsing a child failed the error is already set
+                    return null;
+                }
+
+            } else if (tagName.equals(TAG_RESTRICT_UPDATE)) {
+                if ((flags & PARSE_IS_SYSTEM_DIR) != 0) {
+                    sa = res.obtainAttributes(parser,
+                            com.android.internal.R.styleable.AndroidManifestRestrictUpdate);
+                    final String hash = sa.getNonConfigurationString(
+                            com.android.internal.R.styleable.AndroidManifestRestrictUpdate_hash, 0);
+                    sa.recycle();
+
+                    pkg.restrictUpdateHash = null;
+                    if (hash != null) {
+                        final int hashLength = hash.length();
+                        final byte[] hashBytes = new byte[hashLength / 2];
+                        for (int i = 0; i < hashLength; i += 2){
+                            hashBytes[i/2] = (byte) ((Character.digit(hash.charAt(i), 16) << 4)
+                                    + Character.digit(hash.charAt(i + 1), 16));
+                        }
+                        pkg.restrictUpdateHash = hashBytes;
+                    }
+                }
+
+                XmlUtils.skipCurrentTag(parser);
+
+            } else if (RIGID_PARSER) {
+                outError[0] = "Bad element under <manifest>: "
+                    + parser.getName();
+                mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+                return null;
+
+            } else {
+                Slog.w(TAG, "Unknown element under <manifest>: " + parser.getName()
+                        + " at " + mArchiveSourcePath + " "
+                        + parser.getPositionDescription());
+                XmlUtils.skipCurrentTag(parser);
+                continue;
+            }
+        }
+
+        if (!foundApp && pkg.instrumentation.size() == 0) {
+            outError[0] = "<manifest> does not contain an <application> or <instrumentation>";
+            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_EMPTY;
+        }
+
+        final int NP = PackageParser.NEW_PERMISSIONS.length;
+        StringBuilder newPermsMsg = null;
+        for (int ip=0; ip<NP; ip++) {
+            final PackageParser.NewPermissionInfo npi
+                    = PackageParser.NEW_PERMISSIONS[ip];
+            if (pkg.applicationInfo.targetSdkVersion >= npi.sdkVersion) {
+                break;
+            }
+            if (!pkg.requestedPermissions.contains(npi.name)) {
+                if (newPermsMsg == null) {
+                    newPermsMsg = new StringBuilder(128);
+                    newPermsMsg.append(pkg.packageName);
+                    newPermsMsg.append(": compat added ");
+                } else {
+                    newPermsMsg.append(' ');
+                }
+                newPermsMsg.append(npi.name);
+                pkg.requestedPermissions.add(npi.name);
+                pkg.implicitPermissions.add(npi.name);
+            }
+        }
+        if (newPermsMsg != null) {
+            Slog.i(TAG, newPermsMsg.toString());
+        }
+
+        List<SplitPermissionInfoParcelable> splitPermissions;
+
+        try {
+            splitPermissions = ActivityThread.getPermissionManager().getSplitPermissions();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+
+        final int listSize = splitPermissions.size();
+        for (int is = 0; is < listSize; is++) {
+            final SplitPermissionInfoParcelable spi = splitPermissions.get(is);
+            if (pkg.applicationInfo.targetSdkVersion >= spi.getTargetSdk()
+                    || !pkg.requestedPermissions.contains(spi.getSplitPermission())) {
+                continue;
+            }
+            final List<String> newPerms = spi.getNewPermissions();
+            for (int in = 0; in < newPerms.size(); in++) {
+                final String perm = newPerms.get(in);
+                if (!pkg.requestedPermissions.contains(perm)) {
+                    pkg.requestedPermissions.add(perm);
+                    pkg.implicitPermissions.add(perm);
+                }
+            }
+        }
+
+        if (supportsSmallScreens < 0 || (supportsSmallScreens > 0
+                && pkg.applicationInfo.targetSdkVersion
+                        >= android.os.Build.VERSION_CODES.DONUT)) {
+            pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS;
+        }
+        if (supportsNormalScreens != 0) {
+            pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS;
+        }
+        if (supportsLargeScreens < 0 || (supportsLargeScreens > 0
+                && pkg.applicationInfo.targetSdkVersion
+                        >= android.os.Build.VERSION_CODES.DONUT)) {
+            pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS;
+        }
+        if (supportsXLargeScreens < 0 || (supportsXLargeScreens > 0
+                && pkg.applicationInfo.targetSdkVersion
+                        >= android.os.Build.VERSION_CODES.GINGERBREAD)) {
+            pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS;
+        }
+        if (resizeable < 0 || (resizeable > 0
+                && pkg.applicationInfo.targetSdkVersion
+                        >= android.os.Build.VERSION_CODES.DONUT)) {
+            pkg.applicationInfo.flags |= ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS;
+        }
+        if (anyDensity < 0 || (anyDensity > 0
+                && pkg.applicationInfo.targetSdkVersion
+                        >= android.os.Build.VERSION_CODES.DONUT)) {
+            pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES;
+        }
+
+        // At this point we can check if an application is not supporting densities and hence
+        // cannot be windowed / resized. Note that an SDK version of 0 is common for
+        // pre-Doughnut applications.
+        if (pkg.applicationInfo.usesCompatibilityMode()) {
+            adjustPackageToBeUnresizeableAndUnpipable(pkg);
+        }
+
+        return pkg;
+    }
+
+    /**
+     * Returns {@code true} if both the property name and value are empty or if the given system
+     * property is set to the specified value. Properties can be one or more, and if properties are
+     * more than one, they must be separated by comma, and count of names and values must be equal,
+     * and also every given system property must be set to the corresponding value.
+     * In all other cases, returns {@code false}
+     */
+    public static boolean checkRequiredSystemProperties(@Nullable String rawPropNames,
+            @Nullable String rawPropValues) {
+        if (TextUtils.isEmpty(rawPropNames) || TextUtils.isEmpty(rawPropValues)) {
+            if (!TextUtils.isEmpty(rawPropNames) || !TextUtils.isEmpty(rawPropValues)) {
+                // malformed condition - incomplete
+                Slog.w(TAG, "Disabling overlay - incomplete property :'" + rawPropNames
+                        + "=" + rawPropValues + "' - require both requiredSystemPropertyName"
+                        + " AND requiredSystemPropertyValue to be specified.");
+                return false;
+            }
+            // no valid condition set - so no exclusion criteria, overlay will be included.
+            return true;
+        }
+
+        final String[] propNames = rawPropNames.split(",");
+        final String[] propValues = rawPropValues.split(",");
+
+        if (propNames.length != propValues.length) {
+            Slog.w(TAG, "Disabling overlay - property :'" + rawPropNames
+                    + "=" + rawPropValues + "' - require both requiredSystemPropertyName"
+                    + " AND requiredSystemPropertyValue lists to have the same size.");
+            return false;
+        }
+        for (int i = 0; i < propNames.length; i++) {
+            // Check property value: make sure it is both set and equal to expected value
+            final String currValue = SystemProperties.get(propNames[i]);
+            if (!TextUtils.equals(currValue, propValues[i])) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * This is a pre-density application which will get scaled - instead of being pixel perfect.
+     * This type of application is not resizable.
+     *
+     * @param pkg The package which needs to be marked as unresizable.
+     */
+    private void adjustPackageToBeUnresizeableAndUnpipable(Package pkg) {
+        for (Activity a : pkg.activities) {
+            a.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
+            a.info.flags &= ~FLAG_SUPPORTS_PICTURE_IN_PICTURE;
+        }
+    }
+
+    /**
+
+    /**
+     * Matches a given {@code targetCode} against a set of release codeNames. Target codes can
+     * either be of the form {@code [codename]}" (e.g {@code "Q"}) or of the form
+     * {@code [codename].[fingerprint]} (e.g {@code "Q.cafebc561"}).
+     */
+    private static boolean matchTargetCode(@NonNull String[] codeNames,
+            @NonNull String targetCode) {
+        final String targetCodeName;
+        final int targetCodeIdx = targetCode.indexOf('.');
+        if (targetCodeIdx == -1) {
+            targetCodeName = targetCode;
+        } else {
+            targetCodeName = targetCode.substring(0, targetCodeIdx);
+        }
+        return ArrayUtils.contains(codeNames, targetCodeName);
+    }
+
+    /**
+     * Computes the targetSdkVersion to use at runtime. If the package is not
+     * compatible with this platform, populates {@code outError[0]} with an
+     * error message.
+     * <p>
+     * If {@code targetCode} is not specified, e.g. the value is {@code null},
+     * then the {@code targetVers} will be returned unmodified.
+     * <p>
+     * Otherwise, the behavior varies based on whether the current platform
+     * is a pre-release version, e.g. the {@code platformSdkCodenames} array
+     * has length > 0:
+     * <ul>
+     * <li>If this is a pre-release platform and the value specified by
+     * {@code targetCode} is contained within the array of allowed pre-release
+     * codenames, this method will return {@link Build.VERSION_CODES#CUR_DEVELOPMENT}.
+     * <li>If this is a released platform, this method will return -1 to
+     * indicate that the package is not compatible with this platform.
+     * </ul>
+     *
+     * @param targetVers targetSdkVersion number, if specified in the
+     *                   application manifest, or 0 otherwise
+     * @param targetCode targetSdkVersion code, if specified in the application
+     *                   manifest, or {@code null} otherwise
+     * @param platformSdkCodenames array of allowed pre-release SDK codenames
+     *                             for this platform
+     * @param outError output array to populate with error, if applicable
+     * @return the targetSdkVersion to use at runtime, or -1 if the package is
+     *         not compatible with this platform
+     * @hide Exposed for unit testing only.
+     */
+    @TestApi
+    public static int computeTargetSdkVersion(@IntRange(from = 0) int targetVers,
+            @Nullable String targetCode, @NonNull String[] platformSdkCodenames,
+            @NonNull String[] outError) {
+        // If it's a release SDK, return the version number unmodified.
+        if (targetCode == null) {
+            return targetVers;
+        }
+
+        // If it's a pre-release SDK and the codename matches this platform, it
+        // definitely targets this SDK.
+        if (matchTargetCode(platformSdkCodenames, targetCode)) {
+            return Build.VERSION_CODES.CUR_DEVELOPMENT;
+        }
+
+        // Otherwise, we're looking at an incompatible pre-release SDK.
+        if (platformSdkCodenames.length > 0) {
+            outError[0] = "Requires development platform " + targetCode
+                    + " (current platform is any of "
+                    + Arrays.toString(platformSdkCodenames) + ")";
+        } else {
+            outError[0] = "Requires development platform " + targetCode
+                    + " but this is a release platform.";
+        }
+        return -1;
+    }
+
+    /**
+     * Computes the minSdkVersion to use at runtime. If the package is not
+     * compatible with this platform, populates {@code outError[0]} with an
+     * error message.
+     * <p>
+     * If {@code minCode} is not specified, e.g. the value is {@code null},
+     * then behavior varies based on the {@code platformSdkVersion}:
+     * <ul>
+     * <li>If the platform SDK version is greater than or equal to the
+     * {@code minVers}, returns the {@code mniVers} unmodified.
+     * <li>Otherwise, returns -1 to indicate that the package is not
+     * compatible with this platform.
+     * </ul>
+     * <p>
+     * Otherwise, the behavior varies based on whether the current platform
+     * is a pre-release version, e.g. the {@code platformSdkCodenames} array
+     * has length > 0:
+     * <ul>
+     * <li>If this is a pre-release platform and the value specified by
+     * {@code targetCode} is contained within the array of allowed pre-release
+     * codenames, this method will return {@link Build.VERSION_CODES#CUR_DEVELOPMENT}.
+     * <li>If this is a released platform, this method will return -1 to
+     * indicate that the package is not compatible with this platform.
+     * </ul>
+     *
+     * @param minVers minSdkVersion number, if specified in the application
+     *                manifest, or 1 otherwise
+     * @param minCode minSdkVersion code, if specified in the application
+     *                manifest, or {@code null} otherwise
+     * @param platformSdkVersion platform SDK version number, typically
+     *                           Build.VERSION.SDK_INT
+     * @param platformSdkCodenames array of allowed prerelease SDK codenames
+     *                             for this platform
+     * @param outError output array to populate with error, if applicable
+     * @return the minSdkVersion to use at runtime, or -1 if the package is not
+     *         compatible with this platform
+     * @hide Exposed for unit testing only.
+     */
+    @TestApi
+    public static int computeMinSdkVersion(@IntRange(from = 1) int minVers,
+            @Nullable String minCode, @IntRange(from = 1) int platformSdkVersion,
+            @NonNull String[] platformSdkCodenames, @NonNull String[] outError) {
+        // If it's a release SDK, make sure we meet the minimum SDK requirement.
+        if (minCode == null) {
+            if (minVers <= platformSdkVersion) {
+                return minVers;
+            }
+
+            // We don't meet the minimum SDK requirement.
+            outError[0] = "Requires newer sdk version #" + minVers
+                    + " (current version is #" + platformSdkVersion + ")";
+            return -1;
+        }
+
+        // If it's a pre-release SDK and the codename matches this platform, we
+        // definitely meet the minimum SDK requirement.
+        if (matchTargetCode(platformSdkCodenames, minCode)) {
+            return Build.VERSION_CODES.CUR_DEVELOPMENT;
+        }
+
+        // Otherwise, we're looking at an incompatible pre-release SDK.
+        if (platformSdkCodenames.length > 0) {
+            outError[0] = "Requires development platform " + minCode
+                    + " (current platform is any of "
+                    + Arrays.toString(platformSdkCodenames) + ")";
+        } else {
+            outError[0] = "Requires development platform " + minCode
+                    + " but this is a release platform.";
+        }
+        return -1;
+    }
+
+    private FeatureInfo parseUsesFeature(Resources res, AttributeSet attrs) {
+        FeatureInfo fi = new FeatureInfo();
+        TypedArray sa = res.obtainAttributes(attrs,
+                com.android.internal.R.styleable.AndroidManifestUsesFeature);
+        // Note: don't allow this value to be a reference to a resource
+        // that may change.
+        fi.name = sa.getNonResourceString(
+                com.android.internal.R.styleable.AndroidManifestUsesFeature_name);
+        fi.version = sa.getInt(
+                com.android.internal.R.styleable.AndroidManifestUsesFeature_version, 0);
+        if (fi.name == null) {
+            fi.reqGlEsVersion = sa.getInt(
+                        com.android.internal.R.styleable.AndroidManifestUsesFeature_glEsVersion,
+                        FeatureInfo.GL_ES_VERSION_UNDEFINED);
+        }
+        if (sa.getBoolean(
+                com.android.internal.R.styleable.AndroidManifestUsesFeature_required, true)) {
+            fi.flags |= FeatureInfo.FLAG_REQUIRED;
+        }
+        sa.recycle();
+        return fi;
+    }
+
+    private boolean parseUsesStaticLibrary(Package pkg, Resources res, XmlResourceParser parser,
+            String[] outError) throws XmlPullParserException, IOException {
+        TypedArray sa = res.obtainAttributes(parser,
+                com.android.internal.R.styleable.AndroidManifestUsesStaticLibrary);
+
+        // Note: don't allow this value to be a reference to a resource that may change.
+        String lname = sa.getNonResourceString(
+                com.android.internal.R.styleable.AndroidManifestUsesLibrary_name);
+        final int version = sa.getInt(
+                com.android.internal.R.styleable.AndroidManifestUsesStaticLibrary_version, -1);
+        String certSha256Digest = sa.getNonResourceString(com.android.internal.R.styleable
+                .AndroidManifestUsesStaticLibrary_certDigest);
+        sa.recycle();
+
+        // Since an APK providing a static shared lib can only provide the lib - fail if malformed
+        if (lname == null || version < 0 || certSha256Digest == null) {
+            outError[0] = "Bad uses-static-library declaration name: " + lname + " version: "
+                    + version + " certDigest" + certSha256Digest;
+            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+            XmlUtils.skipCurrentTag(parser);
+            return false;
+        }
+
+        // Can depend only on one version of the same library
+        if (pkg.usesStaticLibraries != null && pkg.usesStaticLibraries.contains(lname)) {
+            outError[0] = "Depending on multiple versions of static library " + lname;
+            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+            XmlUtils.skipCurrentTag(parser);
+            return false;
+        }
+
+        lname = lname.intern();
+        // We allow ":" delimiters in the SHA declaration as this is the format
+        // emitted by the certtool making it easy for developers to copy/paste.
+        certSha256Digest = certSha256Digest.replace(":", "").toLowerCase();
+
+        // Fot apps targeting O-MR1 we require explicit enumeration of all certs.
+        String[] additionalCertSha256Digests = EmptyArray.STRING;
+        if (pkg.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.O_MR1) {
+            additionalCertSha256Digests = parseAdditionalCertificates(res, parser, outError);
+            if (additionalCertSha256Digests == null) {
+                return false;
+            }
+        } else {
+            XmlUtils.skipCurrentTag(parser);
+        }
+
+        final String[] certSha256Digests = new String[additionalCertSha256Digests.length + 1];
+        certSha256Digests[0] = certSha256Digest;
+        System.arraycopy(additionalCertSha256Digests, 0, certSha256Digests,
+                1, additionalCertSha256Digests.length);
+
+        pkg.usesStaticLibraries = ArrayUtils.add(pkg.usesStaticLibraries, lname);
+        pkg.usesStaticLibrariesVersions = ArrayUtils.appendLong(
+                pkg.usesStaticLibrariesVersions, version, true);
+        pkg.usesStaticLibrariesCertDigests = ArrayUtils.appendElement(String[].class,
+                pkg.usesStaticLibrariesCertDigests, certSha256Digests, true);
+
+        return true;
+    }
+
+    private String[] parseAdditionalCertificates(Resources resources, XmlResourceParser parser,
+            String[] outError) throws XmlPullParserException, IOException {
+        String[] certSha256Digests = EmptyArray.STRING;
+
+        int outerDepth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+
+            final String nodeName = parser.getName();
+            if (nodeName.equals("additional-certificate")) {
+                final TypedArray sa = resources.obtainAttributes(parser, com.android.internal.
+                        R.styleable.AndroidManifestAdditionalCertificate);
+                String certSha256Digest = sa.getNonResourceString(com.android.internal.
+                        R.styleable.AndroidManifestAdditionalCertificate_certDigest);
+                sa.recycle();
+
+                if (TextUtils.isEmpty(certSha256Digest)) {
+                    outError[0] = "Bad additional-certificate declaration with empty"
+                            + " certDigest:" + certSha256Digest;
+                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+                    XmlUtils.skipCurrentTag(parser);
+                    sa.recycle();
+                    return null;
+                }
+
+                // We allow ":" delimiters in the SHA declaration as this is the format
+                // emitted by the certtool making it easy for developers to copy/paste.
+                certSha256Digest = certSha256Digest.replace(":", "").toLowerCase();
+                certSha256Digests = ArrayUtils.appendElement(String.class,
+                        certSha256Digests, certSha256Digest);
+            } else {
+                XmlUtils.skipCurrentTag(parser);
+            }
+        }
+
+        return certSha256Digests;
+    }
+
+    private boolean parseUsesPermission(Package pkg, Resources res, XmlResourceParser parser)
+            throws XmlPullParserException, IOException {
+        TypedArray sa = res.obtainAttributes(parser,
+                com.android.internal.R.styleable.AndroidManifestUsesPermission);
+
+        // Note: don't allow this value to be a reference to a resource
+        // that may change.
+        String name = sa.getNonResourceString(
+                com.android.internal.R.styleable.AndroidManifestUsesPermission_name);
+
+        int maxSdkVersion = 0;
+        TypedValue val = sa.peekValue(
+                com.android.internal.R.styleable.AndroidManifestUsesPermission_maxSdkVersion);
+        if (val != null) {
+            if (val.type >= TypedValue.TYPE_FIRST_INT && val.type <= TypedValue.TYPE_LAST_INT) {
+                maxSdkVersion = val.data;
+            }
+        }
+
+        final String requiredFeature = sa.getNonConfigurationString(
+                com.android.internal.R.styleable.AndroidManifestUsesPermission_requiredFeature, 0);
+
+        final String requiredNotfeature = sa.getNonConfigurationString(
+                com.android.internal.R.styleable.AndroidManifestUsesPermission_requiredNotFeature, 0);
+
+        sa.recycle();
+
+        XmlUtils.skipCurrentTag(parser);
+
+        if (name == null) {
+            return true;
+        }
+
+        if ((maxSdkVersion != 0) && (maxSdkVersion < Build.VERSION.RESOURCES_SDK_INT)) {
+            return true;
+        }
+
+        // Only allow requesting this permission if the platform supports the given feature.
+        if (requiredFeature != null && mCallback != null && !mCallback.hasFeature(requiredFeature)) {
+            return true;
+        }
+
+        // Only allow requesting this permission if the platform doesn't support the given feature.
+        if (requiredNotfeature != null && mCallback != null
+                && mCallback.hasFeature(requiredNotfeature)) {
+            return true;
+        }
+
+        int index = pkg.requestedPermissions.indexOf(name);
+        if (index == -1) {
+            pkg.requestedPermissions.add(name.intern());
+        } else {
+            Slog.w(TAG, "Ignoring duplicate uses-permissions/uses-permissions-sdk-m: "
+                    + name + " in package: " + pkg.packageName + " at: "
+                    + parser.getPositionDescription());
+        }
+
+        return true;
+    }
+
+    public static String buildClassName(String pkg, CharSequence clsSeq,
+            String[] outError) {
+        if (clsSeq == null || clsSeq.length() <= 0) {
+            outError[0] = "Empty class name in package " + pkg;
+            return null;
+        }
+        String cls = clsSeq.toString();
+        char c = cls.charAt(0);
+        if (c == '.') {
+            return pkg + cls;
+        }
+        if (cls.indexOf('.') < 0) {
+            StringBuilder b = new StringBuilder(pkg);
+            b.append('.');
+            b.append(cls);
+            return b.toString();
+        }
+        return cls;
+    }
+
+    private static String buildCompoundName(String pkg,
+            CharSequence procSeq, String type, String[] outError) {
+        String proc = procSeq.toString();
+        char c = proc.charAt(0);
+        if (pkg != null && c == ':') {
+            if (proc.length() < 2) {
+                outError[0] = "Bad " + type + " name " + proc + " in package " + pkg
+                        + ": must be at least two characters";
+                return null;
+            }
+            String subName = proc.substring(1);
+            String nameError = validateName(subName, false, false);
+            if (nameError != null) {
+                outError[0] = "Invalid " + type + " name " + proc + " in package "
+                        + pkg + ": " + nameError;
+                return null;
+            }
+            return pkg + proc;
+        }
+        String nameError = validateName(proc, true, false);
+        if (nameError != null && !"system".equals(proc)) {
+            outError[0] = "Invalid " + type + " name " + proc + " in package "
+                    + pkg + ": " + nameError;
+            return null;
+        }
+        return proc;
+    }
+
+    public static String buildProcessName(String pkg, String defProc,
+            CharSequence procSeq, int flags, String[] separateProcesses,
+            String[] outError) {
+        if ((flags&PARSE_IGNORE_PROCESSES) != 0 && !"system".equals(procSeq)) {
+            return defProc != null ? defProc : pkg;
+        }
+        if (separateProcesses != null) {
+            for (int i=separateProcesses.length-1; i>=0; i--) {
+                String sp = separateProcesses[i];
+                if (sp.equals(pkg) || sp.equals(defProc) || sp.equals(procSeq)) {
+                    return pkg;
+                }
+            }
+        }
+        if (procSeq == null || procSeq.length() <= 0) {
+            return defProc;
+        }
+        return TextUtils.safeIntern(buildCompoundName(pkg, procSeq, "process", outError));
+    }
+
+    public static String buildTaskAffinityName(String pkg, String defProc,
+            CharSequence procSeq, String[] outError) {
+        if (procSeq == null) {
+            return defProc;
+        }
+        if (procSeq.length() <= 0) {
+            return null;
+        }
+        return buildCompoundName(pkg, procSeq, "taskAffinity", outError);
+    }
+
+    private boolean parseKeySets(Package owner, Resources res,
+            XmlResourceParser parser, String[] outError)
+            throws XmlPullParserException, IOException {
+        // we've encountered the 'key-sets' tag
+        // all the keys and keysets that we want must be defined here
+        // so we're going to iterate over the parser and pull out the things we want
+        int outerDepth = parser.getDepth();
+        int currentKeySetDepth = -1;
+        int type;
+        String currentKeySet = null;
+        ArrayMap<String, PublicKey> publicKeys = new ArrayMap<String, PublicKey>();
+        ArraySet<String> upgradeKeySets = new ArraySet<String>();
+        ArrayMap<String, ArraySet<String>> definedKeySets = new ArrayMap<String, ArraySet<String>>();
+        ArraySet<String> improperKeySets = new ArraySet<String>();
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG) {
+                if (parser.getDepth() == currentKeySetDepth) {
+                    currentKeySet = null;
+                    currentKeySetDepth = -1;
+                }
+                continue;
+            }
+            String tagName = parser.getName();
+            if (tagName.equals("key-set")) {
+                if (currentKeySet != null) {
+                    outError[0] = "Improperly nested 'key-set' tag at "
+                            + parser.getPositionDescription();
+                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+                    return false;
+                }
+                final TypedArray sa = res.obtainAttributes(parser,
+                        com.android.internal.R.styleable.AndroidManifestKeySet);
+                final String keysetName = sa.getNonResourceString(
+                    com.android.internal.R.styleable.AndroidManifestKeySet_name);
+                definedKeySets.put(keysetName, new ArraySet<String>());
+                currentKeySet = keysetName;
+                currentKeySetDepth = parser.getDepth();
+                sa.recycle();
+            } else if (tagName.equals("public-key")) {
+                if (currentKeySet == null) {
+                    outError[0] = "Improperly nested 'key-set' tag at "
+                            + parser.getPositionDescription();
+                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+                    return false;
+                }
+                final TypedArray sa = res.obtainAttributes(parser,
+                        com.android.internal.R.styleable.AndroidManifestPublicKey);
+                final String publicKeyName = sa.getNonResourceString(
+                        com.android.internal.R.styleable.AndroidManifestPublicKey_name);
+                final String encodedKey = sa.getNonResourceString(
+                            com.android.internal.R.styleable.AndroidManifestPublicKey_value);
+                if (encodedKey == null && publicKeys.get(publicKeyName) == null) {
+                    outError[0] = "'public-key' " + publicKeyName + " must define a public-key value"
+                            + " on first use at " + parser.getPositionDescription();
+                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+                    sa.recycle();
+                    return false;
+                } else if (encodedKey != null) {
+                    PublicKey currentKey = parsePublicKey(encodedKey);
+                    if (currentKey == null) {
+                        Slog.w(TAG, "No recognized valid key in 'public-key' tag at "
+                                + parser.getPositionDescription() + " key-set " + currentKeySet
+                                + " will not be added to the package's defined key-sets.");
+                        sa.recycle();
+                        improperKeySets.add(currentKeySet);
+                        XmlUtils.skipCurrentTag(parser);
+                        continue;
+                    }
+                    if (publicKeys.get(publicKeyName) == null
+                            || publicKeys.get(publicKeyName).equals(currentKey)) {
+
+                        /* public-key first definition, or matches old definition */
+                        publicKeys.put(publicKeyName, currentKey);
+                    } else {
+                        outError[0] = "Value of 'public-key' " + publicKeyName
+                               + " conflicts with previously defined value at "
+                               + parser.getPositionDescription();
+                        mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+                        sa.recycle();
+                        return false;
+                    }
+                }
+                definedKeySets.get(currentKeySet).add(publicKeyName);
+                sa.recycle();
+                XmlUtils.skipCurrentTag(parser);
+            } else if (tagName.equals("upgrade-key-set")) {
+                final TypedArray sa = res.obtainAttributes(parser,
+                        com.android.internal.R.styleable.AndroidManifestUpgradeKeySet);
+                String name = sa.getNonResourceString(
+                        com.android.internal.R.styleable.AndroidManifestUpgradeKeySet_name);
+                upgradeKeySets.add(name);
+                sa.recycle();
+                XmlUtils.skipCurrentTag(parser);
+            } else if (RIGID_PARSER) {
+                outError[0] = "Bad element under <key-sets>: " + parser.getName()
+                        + " at " + mArchiveSourcePath + " "
+                        + parser.getPositionDescription();
+                mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+                return false;
+            } else {
+                Slog.w(TAG, "Unknown element under <key-sets>: " + parser.getName()
+                        + " at " + mArchiveSourcePath + " "
+                        + parser.getPositionDescription());
+                XmlUtils.skipCurrentTag(parser);
+                continue;
+            }
+        }
+        Set<String> publicKeyNames = publicKeys.keySet();
+        if (publicKeyNames.removeAll(definedKeySets.keySet())) {
+            outError[0] = "Package" + owner.packageName + " AndroidManifext.xml "
+                    + "'key-set' and 'public-key' names must be distinct.";
+            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+            return false;
+        }
+        owner.mKeySetMapping = new ArrayMap<String, ArraySet<PublicKey>>();
+        for (ArrayMap.Entry<String, ArraySet<String>> e: definedKeySets.entrySet()) {
+            final String keySetName = e.getKey();
+            if (e.getValue().size() == 0) {
+                Slog.w(TAG, "Package" + owner.packageName + " AndroidManifext.xml "
+                        + "'key-set' " + keySetName + " has no valid associated 'public-key'."
+                        + " Not including in package's defined key-sets.");
+                continue;
+            } else if (improperKeySets.contains(keySetName)) {
+                Slog.w(TAG, "Package" + owner.packageName + " AndroidManifext.xml "
+                        + "'key-set' " + keySetName + " contained improper 'public-key'"
+                        + " tags. Not including in package's defined key-sets.");
+                continue;
+            }
+            owner.mKeySetMapping.put(keySetName, new ArraySet<PublicKey>());
+            for (String s : e.getValue()) {
+                owner.mKeySetMapping.get(keySetName).add(publicKeys.get(s));
+            }
+        }
+        if (owner.mKeySetMapping.keySet().containsAll(upgradeKeySets)) {
+            owner.mUpgradeKeySets = upgradeKeySets;
+        } else {
+            outError[0] ="Package" + owner.packageName + " AndroidManifext.xml "
+                   + "does not define all 'upgrade-key-set's .";
+            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+            return false;
+        }
+        return true;
+    }
+
+    private boolean parsePermissionGroup(Package owner, int flags, Resources res,
+            XmlResourceParser parser, String[] outError)
+            throws XmlPullParserException, IOException {
+        TypedArray sa = res.obtainAttributes(parser,
+                com.android.internal.R.styleable.AndroidManifestPermissionGroup);
+
+        int requestDetailResourceId = sa.getResourceId(
+                com.android.internal.R.styleable.AndroidManifestPermissionGroup_requestDetail, 0);
+        int backgroundRequestResourceId = sa.getResourceId(
+                com.android.internal.R.styleable.AndroidManifestPermissionGroup_backgroundRequest,
+                0);
+        int backgroundRequestDetailResourceId = sa.getResourceId(
+                com.android.internal.R.styleable
+                        .AndroidManifestPermissionGroup_backgroundRequestDetail, 0);
+
+        PermissionGroup perm = new PermissionGroup(owner, requestDetailResourceId,
+                backgroundRequestResourceId, backgroundRequestDetailResourceId);
+
+        if (!parsePackageItemInfo(owner, perm.info, outError,
+                "<permission-group>", sa, true /*nameRequired*/,
+                com.android.internal.R.styleable.AndroidManifestPermissionGroup_name,
+                com.android.internal.R.styleable.AndroidManifestPermissionGroup_label,
+                com.android.internal.R.styleable.AndroidManifestPermissionGroup_icon,
+                com.android.internal.R.styleable.AndroidManifestPermissionGroup_roundIcon,
+                com.android.internal.R.styleable.AndroidManifestPermissionGroup_logo,
+                com.android.internal.R.styleable.AndroidManifestPermissionGroup_banner)) {
+            sa.recycle();
+            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+            return false;
+        }
+
+        perm.info.descriptionRes = sa.getResourceId(
+                com.android.internal.R.styleable.AndroidManifestPermissionGroup_description,
+                0);
+        perm.info.requestRes = sa.getResourceId(
+                com.android.internal.R.styleable.AndroidManifestPermissionGroup_request, 0);
+        perm.info.flags = sa.getInt(
+                com.android.internal.R.styleable.AndroidManifestPermissionGroup_permissionGroupFlags, 0);
+        perm.info.priority = sa.getInt(
+                com.android.internal.R.styleable.AndroidManifestPermissionGroup_priority, 0);
+
+        sa.recycle();
+
+        if (!parseAllMetaData(res, parser, "<permission-group>", perm,
+                outError)) {
+            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+            return false;
+        }
+
+        owner.permissionGroups.add(perm);
+
+        return true;
+    }
+
+    private boolean parsePermission(Package owner, Resources res,
+            XmlResourceParser parser, String[] outError)
+        throws XmlPullParserException, IOException {
+
+        TypedArray sa = res.obtainAttributes(parser,
+                com.android.internal.R.styleable.AndroidManifestPermission);
+
+        String backgroundPermission = null;
+        if (sa.hasValue(
+                com.android.internal.R.styleable.AndroidManifestPermission_backgroundPermission)) {
+            if ("android".equals(owner.packageName)) {
+                backgroundPermission = sa.getNonResourceString(
+                        com.android.internal.R.styleable
+                                .AndroidManifestPermission_backgroundPermission);
+            } else {
+                Slog.w(TAG, owner.packageName + " defines a background permission. Only the "
+                        + "'android' package can do that.");
+            }
+        }
+
+        Permission perm = new Permission(owner, backgroundPermission);
+        if (!parsePackageItemInfo(owner, perm.info, outError,
+                "<permission>", sa, true /*nameRequired*/,
+                com.android.internal.R.styleable.AndroidManifestPermission_name,
+                com.android.internal.R.styleable.AndroidManifestPermission_label,
+                com.android.internal.R.styleable.AndroidManifestPermission_icon,
+                com.android.internal.R.styleable.AndroidManifestPermission_roundIcon,
+                com.android.internal.R.styleable.AndroidManifestPermission_logo,
+                com.android.internal.R.styleable.AndroidManifestPermission_banner)) {
+            sa.recycle();
+            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+            return false;
+        }
+
+        // Note: don't allow this value to be a reference to a resource
+        // that may change.
+        perm.info.group = sa.getNonResourceString(
+                com.android.internal.R.styleable.AndroidManifestPermission_permissionGroup);
+        if (perm.info.group != null) {
+            perm.info.group = perm.info.group.intern();
+        }
+
+        perm.info.descriptionRes = sa.getResourceId(
+                com.android.internal.R.styleable.AndroidManifestPermission_description,
+                0);
+
+        perm.info.requestRes = sa.getResourceId(
+                com.android.internal.R.styleable.AndroidManifestPermission_request, 0);
+
+        perm.info.protectionLevel = sa.getInt(
+                com.android.internal.R.styleable.AndroidManifestPermission_protectionLevel,
+                PermissionInfo.PROTECTION_NORMAL);
+
+        perm.info.flags = sa.getInt(
+                com.android.internal.R.styleable.AndroidManifestPermission_permissionFlags, 0);
+
+        // For now only platform runtime permissions can be restricted
+        if (!perm.info.isRuntime() || !"android".equals(perm.info.packageName)) {
+            perm.info.flags &= ~PermissionInfo.FLAG_HARD_RESTRICTED;
+            perm.info.flags &= ~PermissionInfo.FLAG_SOFT_RESTRICTED;
+        } else {
+            // The platform does not get to specify conflicting permissions
+            if ((perm.info.flags & PermissionInfo.FLAG_HARD_RESTRICTED) != 0
+                    && (perm.info.flags & PermissionInfo.FLAG_SOFT_RESTRICTED) != 0) {
+                throw new IllegalStateException("Permission cannot be both soft and hard"
+                        + " restricted: " + perm.info.name);
+            }
+        }
+
+        sa.recycle();
+
+        if (perm.info.protectionLevel == -1) {
+            outError[0] = "<permission> does not specify protectionLevel";
+            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+            return false;
+        }
+
+        perm.info.protectionLevel = PermissionInfo.fixProtectionLevel(perm.info.protectionLevel);
+
+        if (perm.info.getProtectionFlags() != 0) {
+            if ( (perm.info.protectionLevel&PermissionInfo.PROTECTION_FLAG_INSTANT) == 0
+                    && (perm.info.protectionLevel&PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY) == 0
+                    && (perm.info.protectionLevel&PermissionInfo.PROTECTION_MASK_BASE) !=
+                    PermissionInfo.PROTECTION_SIGNATURE) {
+                outError[0] = "<permission>  protectionLevel specifies a non-instant flag but is "
+                        + "not based on signature type";
+                mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+                return false;
+            }
+        }
+
+        if (!parseAllMetaData(res, parser, "<permission>", perm, outError)) {
+            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+            return false;
+        }
+
+        owner.permissions.add(perm);
+
+        return true;
+    }
+
+    private boolean parsePermissionTree(Package owner, Resources res,
+            XmlResourceParser parser, String[] outError)
+        throws XmlPullParserException, IOException {
+        Permission perm = new Permission(owner, (String) null);
+
+        TypedArray sa = res.obtainAttributes(parser,
+                com.android.internal.R.styleable.AndroidManifestPermissionTree);
+
+        if (!parsePackageItemInfo(owner, perm.info, outError,
+                "<permission-tree>", sa, true /*nameRequired*/,
+                com.android.internal.R.styleable.AndroidManifestPermissionTree_name,
+                com.android.internal.R.styleable.AndroidManifestPermissionTree_label,
+                com.android.internal.R.styleable.AndroidManifestPermissionTree_icon,
+                com.android.internal.R.styleable.AndroidManifestPermissionTree_roundIcon,
+                com.android.internal.R.styleable.AndroidManifestPermissionTree_logo,
+                com.android.internal.R.styleable.AndroidManifestPermissionTree_banner)) {
+            sa.recycle();
+            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+            return false;
+        }
+
+        sa.recycle();
+
+        int index = perm.info.name.indexOf('.');
+        if (index > 0) {
+            index = perm.info.name.indexOf('.', index+1);
+        }
+        if (index < 0) {
+            outError[0] = "<permission-tree> name has less than three segments: "
+                + perm.info.name;
+            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+            return false;
+        }
+
+        perm.info.descriptionRes = 0;
+        perm.info.requestRes = 0;
+        perm.info.protectionLevel = PermissionInfo.PROTECTION_NORMAL;
+        perm.tree = true;
+
+        if (!parseAllMetaData(res, parser, "<permission-tree>", perm,
+                outError)) {
+            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+            return false;
+        }
+
+        owner.permissions.add(perm);
+
+        return true;
+    }
+
+    private Instrumentation parseInstrumentation(Package owner, Resources res,
+            XmlResourceParser parser, String[] outError)
+            throws XmlPullParserException, IOException {
+        TypedArray sa = res.obtainAttributes(parser,
+                com.android.internal.R.styleable.AndroidManifestInstrumentation);
+
+        if (mParseInstrumentationArgs == null) {
+            mParseInstrumentationArgs = new ParsePackageItemArgs(owner, outError,
+                    com.android.internal.R.styleable.AndroidManifestInstrumentation_name,
+                    com.android.internal.R.styleable.AndroidManifestInstrumentation_label,
+                    com.android.internal.R.styleable.AndroidManifestInstrumentation_icon,
+                    com.android.internal.R.styleable.AndroidManifestInstrumentation_roundIcon,
+                    com.android.internal.R.styleable.AndroidManifestInstrumentation_logo,
+                    com.android.internal.R.styleable.AndroidManifestInstrumentation_banner);
+            mParseInstrumentationArgs.tag = "<instrumentation>";
+        }
+
+        mParseInstrumentationArgs.sa = sa;
+
+        Instrumentation a = new Instrumentation(mParseInstrumentationArgs,
+                new InstrumentationInfo());
+        if (outError[0] != null) {
+            sa.recycle();
+            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+            return null;
+        }
+
+        String str;
+        // Note: don't allow this value to be a reference to a resource
+        // that may change.
+        str = sa.getNonResourceString(
+                com.android.internal.R.styleable.AndroidManifestInstrumentation_targetPackage);
+        a.info.targetPackage = str != null ? str.intern() : null;
+
+        str = sa.getNonResourceString(
+                com.android.internal.R.styleable.AndroidManifestInstrumentation_targetProcesses);
+        a.info.targetProcesses = str != null ? str.intern() : null;
+
+        a.info.handleProfiling = sa.getBoolean(
+                com.android.internal.R.styleable.AndroidManifestInstrumentation_handleProfiling,
+                false);
+
+        a.info.functionalTest = sa.getBoolean(
+                com.android.internal.R.styleable.AndroidManifestInstrumentation_functionalTest,
+                false);
+
+        sa.recycle();
+
+        if (a.info.targetPackage == null) {
+            outError[0] = "<instrumentation> does not specify targetPackage";
+            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+            return null;
+        }
+
+        if (!parseAllMetaData(res, parser, "<instrumentation>", a,
+                outError)) {
+            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+            return null;
+        }
+
+        owner.instrumentation.add(a);
+
+        return a;
+    }
+
+    /**
+     * Parse the {@code application} XML tree at the current parse location in a
+     * <em>base APK</em> manifest.
+     * <p>
+     * When adding new features, carefully consider if they should also be
+     * supported by split APKs.
+     */
+    @UnsupportedAppUsage
+    private boolean parseBaseApplication(Package owner, Resources res,
+            XmlResourceParser parser, int flags, String[] outError)
+        throws XmlPullParserException, IOException {
+        final ApplicationInfo ai = owner.applicationInfo;
+        final String pkgName = owner.applicationInfo.packageName;
+
+        TypedArray sa = res.obtainAttributes(parser,
+                com.android.internal.R.styleable.AndroidManifestApplication);
+
+        ai.iconRes = sa.getResourceId(
+            com.android.internal.R.styleable.AndroidManifestApplication_icon, 0);
+        ai.roundIconRes = sa.getResourceId(
+            com.android.internal.R.styleable.AndroidManifestApplication_roundIcon, 0);
+
+        if (!parsePackageItemInfo(owner, ai, outError,
+                "<application>", sa, false /*nameRequired*/,
+                com.android.internal.R.styleable.AndroidManifestApplication_name,
+                com.android.internal.R.styleable.AndroidManifestApplication_label,
+                com.android.internal.R.styleable.AndroidManifestApplication_icon,
+                com.android.internal.R.styleable.AndroidManifestApplication_roundIcon,
+                com.android.internal.R.styleable.AndroidManifestApplication_logo,
+                com.android.internal.R.styleable.AndroidManifestApplication_banner)) {
+            sa.recycle();
+            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+            return false;
+        }
+
+        if (ai.name != null) {
+            ai.className = ai.name;
+        }
+
+        String manageSpaceActivity = sa.getNonConfigurationString(
+                com.android.internal.R.styleable.AndroidManifestApplication_manageSpaceActivity,
+                Configuration.NATIVE_CONFIG_VERSION);
+        if (manageSpaceActivity != null) {
+            ai.manageSpaceActivityName = buildClassName(pkgName, manageSpaceActivity,
+                    outError);
+        }
+
+        boolean allowBackup = sa.getBoolean(
+                com.android.internal.R.styleable.AndroidManifestApplication_allowBackup, true);
+        if (allowBackup) {
+            ai.flags |= ApplicationInfo.FLAG_ALLOW_BACKUP;
+
+            // backupAgent, killAfterRestore, fullBackupContent, backupInForeground,
+            // and restoreAnyVersion are only relevant if backup is possible for the
+            // given application.
+            String backupAgent = sa.getNonConfigurationString(
+                    com.android.internal.R.styleable.AndroidManifestApplication_backupAgent,
+                    Configuration.NATIVE_CONFIG_VERSION);
+            if (backupAgent != null) {
+                ai.backupAgentName = buildClassName(pkgName, backupAgent, outError);
+                if (DEBUG_BACKUP) {
+                    Slog.v(TAG, "android:backupAgent = " + ai.backupAgentName
+                            + " from " + pkgName + "+" + backupAgent);
+                }
+
+                if (sa.getBoolean(
+                        com.android.internal.R.styleable.AndroidManifestApplication_killAfterRestore,
+                        true)) {
+                    ai.flags |= ApplicationInfo.FLAG_KILL_AFTER_RESTORE;
+                }
+                if (sa.getBoolean(
+                        com.android.internal.R.styleable.AndroidManifestApplication_restoreAnyVersion,
+                        false)) {
+                    ai.flags |= ApplicationInfo.FLAG_RESTORE_ANY_VERSION;
+                }
+                if (sa.getBoolean(
+                        com.android.internal.R.styleable.AndroidManifestApplication_fullBackupOnly,
+                        false)) {
+                    ai.flags |= ApplicationInfo.FLAG_FULL_BACKUP_ONLY;
+                }
+                if (sa.getBoolean(
+                        com.android.internal.R.styleable.AndroidManifestApplication_backupInForeground,
+                        false)) {
+                    ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_BACKUP_IN_FOREGROUND;
+                }
+            }
+
+            TypedValue v = sa.peekValue(
+                    com.android.internal.R.styleable.AndroidManifestApplication_fullBackupContent);
+            if (v != null && (ai.fullBackupContent = v.resourceId) == 0) {
+                if (DEBUG_BACKUP) {
+                    Slog.v(TAG, "fullBackupContent specified as boolean=" +
+                            (v.data == 0 ? "false" : "true"));
+                }
+                // "false" => -1, "true" => 0
+                ai.fullBackupContent = (v.data == 0 ? -1 : 0);
+            }
+            if (DEBUG_BACKUP) {
+                Slog.v(TAG, "fullBackupContent=" + ai.fullBackupContent + " for " + pkgName);
+            }
+        }
+
+        ai.theme = sa.getResourceId(
+                com.android.internal.R.styleable.AndroidManifestApplication_theme, 0);
+        ai.descriptionRes = sa.getResourceId(
+                com.android.internal.R.styleable.AndroidManifestApplication_description, 0);
+
+        if (sa.getBoolean(
+                com.android.internal.R.styleable.AndroidManifestApplication_persistent,
+                false)) {
+            // Check if persistence is based on a feature being present
+            final String requiredFeature = sa.getNonResourceString(com.android.internal.R.styleable
+                    .AndroidManifestApplication_persistentWhenFeatureAvailable);
+            if (requiredFeature == null || mCallback.hasFeature(requiredFeature)) {
+                ai.flags |= ApplicationInfo.FLAG_PERSISTENT;
+            }
+        }
+
+        if (sa.getBoolean(
+                com.android.internal.R.styleable.AndroidManifestApplication_requiredForAllUsers,
+                false)) {
+            owner.mRequiredForAllUsers = true;
+        }
+
+        String restrictedAccountType = sa.getString(com.android.internal.R.styleable
+                .AndroidManifestApplication_restrictedAccountType);
+        if (restrictedAccountType != null && restrictedAccountType.length() > 0) {
+            owner.mRestrictedAccountType = restrictedAccountType;
+        }
+
+        String requiredAccountType = sa.getString(com.android.internal.R.styleable
+                .AndroidManifestApplication_requiredAccountType);
+        if (requiredAccountType != null && requiredAccountType.length() > 0) {
+            owner.mRequiredAccountType = requiredAccountType;
+        }
+
+        if (sa.getBoolean(
+                com.android.internal.R.styleable.AndroidManifestApplication_debuggable,
+                false)) {
+            ai.flags |= ApplicationInfo.FLAG_DEBUGGABLE;
+            // Debuggable implies profileable
+            ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PROFILEABLE_BY_SHELL;
+        }
+
+        if (sa.getBoolean(
+                com.android.internal.R.styleable.AndroidManifestApplication_vmSafeMode,
+                false)) {
+            ai.flags |= ApplicationInfo.FLAG_VM_SAFE_MODE;
+        }
+
+        owner.baseHardwareAccelerated = sa.getBoolean(
+                com.android.internal.R.styleable.AndroidManifestApplication_hardwareAccelerated,
+                owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.ICE_CREAM_SANDWICH);
+        if (owner.baseHardwareAccelerated) {
+            ai.flags |= ApplicationInfo.FLAG_HARDWARE_ACCELERATED;
+        }
+
+        if (sa.getBoolean(
+                com.android.internal.R.styleable.AndroidManifestApplication_hasCode,
+                true)) {
+            ai.flags |= ApplicationInfo.FLAG_HAS_CODE;
+        }
+
+        if (sa.getBoolean(
+                com.android.internal.R.styleable.AndroidManifestApplication_allowTaskReparenting,
+                false)) {
+            ai.flags |= ApplicationInfo.FLAG_ALLOW_TASK_REPARENTING;
+        }
+
+        if (sa.getBoolean(
+                com.android.internal.R.styleable.AndroidManifestApplication_allowClearUserData,
+                true)) {
+            ai.flags |= ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA;
+        }
+
+        // The parent package controls installation, hence specify test only installs.
+        if (owner.parentPackage == null) {
+            if (sa.getBoolean(
+                    com.android.internal.R.styleable.AndroidManifestApplication_testOnly,
+                    false)) {
+                ai.flags |= ApplicationInfo.FLAG_TEST_ONLY;
+            }
+        }
+
+        if (sa.getBoolean(
+                com.android.internal.R.styleable.AndroidManifestApplication_largeHeap,
+                false)) {
+            ai.flags |= ApplicationInfo.FLAG_LARGE_HEAP;
+        }
+
+        if (sa.getBoolean(
+                com.android.internal.R.styleable.AndroidManifestApplication_usesCleartextTraffic,
+                owner.applicationInfo.targetSdkVersion < Build.VERSION_CODES.P)) {
+            ai.flags |= ApplicationInfo.FLAG_USES_CLEARTEXT_TRAFFIC;
+        }
+
+        if (sa.getBoolean(
+                com.android.internal.R.styleable.AndroidManifestApplication_supportsRtl,
+                false /* default is no RTL support*/)) {
+            ai.flags |= ApplicationInfo.FLAG_SUPPORTS_RTL;
+        }
+
+        if (sa.getBoolean(
+                com.android.internal.R.styleable.AndroidManifestApplication_multiArch,
+                false)) {
+            ai.flags |= ApplicationInfo.FLAG_MULTIARCH;
+        }
+
+        if (sa.getBoolean(
+                com.android.internal.R.styleable.AndroidManifestApplication_extractNativeLibs,
+                true)) {
+            ai.flags |= ApplicationInfo.FLAG_EXTRACT_NATIVE_LIBS;
+        }
+
+        if (sa.getBoolean(
+                R.styleable.AndroidManifestApplication_useEmbeddedDex,
+                false)) {
+            ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_USE_EMBEDDED_DEX;
+        }
+
+        if (sa.getBoolean(
+                R.styleable.AndroidManifestApplication_defaultToDeviceProtectedStorage,
+                false)) {
+            ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE;
+        }
+        if (sa.getBoolean(
+                R.styleable.AndroidManifestApplication_directBootAware,
+                false)) {
+            ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_DIRECT_BOOT_AWARE;
+        }
+
+        if (sa.hasValueOrEmpty(R.styleable.AndroidManifestApplication_resizeableActivity)) {
+            if (sa.getBoolean(R.styleable.AndroidManifestApplication_resizeableActivity, true)) {
+                ai.privateFlags |= PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE;
+            } else {
+                ai.privateFlags |= PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE;
+            }
+        } else if (owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.N) {
+            ai.privateFlags |= PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
+        }
+
+        if (sa.getBoolean(
+                com.android.internal.R.styleable
+                        .AndroidManifestApplication_allowClearUserDataOnFailedRestore,
+                true)) {
+            ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE;
+        }
+
+        if (sa.getBoolean(
+                R.styleable.AndroidManifestApplication_allowAudioPlaybackCapture,
+                owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.Q)) {
+            ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE;
+        }
+
+        if (sa.getBoolean(
+                R.styleable.AndroidManifestApplication_requestLegacyExternalStorage,
+                owner.applicationInfo.targetSdkVersion < Build.VERSION_CODES.Q)) {
+            ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE;
+        }
+
+        if (sa.getBoolean(
+                R.styleable.AndroidManifestApplication_allowNativeHeapPointerTagging, true)) {
+            ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING;
+        }
+
+        ai.maxAspectRatio = sa.getFloat(R.styleable.AndroidManifestApplication_maxAspectRatio, 0);
+        ai.minAspectRatio = sa.getFloat(R.styleable.AndroidManifestApplication_minAspectRatio, 0);
+
+        ai.networkSecurityConfigRes = sa.getResourceId(
+                com.android.internal.R.styleable.AndroidManifestApplication_networkSecurityConfig,
+                0);
+        ai.category = sa.getInt(
+                com.android.internal.R.styleable.AndroidManifestApplication_appCategory,
+                ApplicationInfo.CATEGORY_UNDEFINED);
+
+        String str;
+        str = sa.getNonConfigurationString(
+                com.android.internal.R.styleable.AndroidManifestApplication_permission, 0);
+        ai.permission = (str != null && str.length() > 0) ? str.intern() : null;
+
+        if (owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.FROYO) {
+            str = sa.getNonConfigurationString(
+                    com.android.internal.R.styleable.AndroidManifestApplication_taskAffinity,
+                    Configuration.NATIVE_CONFIG_VERSION);
+        } else {
+            // Some older apps have been seen to use a resource reference
+            // here that on older builds was ignored (with a warning).  We
+            // need to continue to do this for them so they don't break.
+            str = sa.getNonResourceString(
+                    com.android.internal.R.styleable.AndroidManifestApplication_taskAffinity);
+        }
+        ai.taskAffinity = buildTaskAffinityName(ai.packageName, ai.packageName,
+                str, outError);
+        String factory = sa.getNonResourceString(
+                com.android.internal.R.styleable.AndroidManifestApplication_appComponentFactory);
+        if (factory != null) {
+            ai.appComponentFactory = buildClassName(ai.packageName, factory, outError);
+        }
+
+        if (sa.getBoolean(
+                com.android.internal.R.styleable.AndroidManifestApplication_usesNonSdkApi, false)) {
+            ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_USES_NON_SDK_API;
+        }
+
+        if (sa.getBoolean(
+                com.android.internal.R.styleable.AndroidManifestApplication_hasFragileUserData,
+                false)) {
+            ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_HAS_FRAGILE_USER_DATA;
+        }
+
+        if (outError[0] == null) {
+            CharSequence pname;
+            if (owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.FROYO) {
+                pname = sa.getNonConfigurationString(
+                        com.android.internal.R.styleable.AndroidManifestApplication_process,
+                        Configuration.NATIVE_CONFIG_VERSION);
+            } else {
+                // Some older apps have been seen to use a resource reference
+                // here that on older builds was ignored (with a warning).  We
+                // need to continue to do this for them so they don't break.
+                pname = sa.getNonResourceString(
+                        com.android.internal.R.styleable.AndroidManifestApplication_process);
+            }
+            ai.processName = buildProcessName(ai.packageName, null, pname,
+                    flags, mSeparateProcesses, outError);
+
+            ai.enabled = sa.getBoolean(
+                    com.android.internal.R.styleable.AndroidManifestApplication_enabled, true);
+
+            if (sa.getBoolean(
+                    com.android.internal.R.styleable.AndroidManifestApplication_isGame, false)) {
+                ai.flags |= ApplicationInfo.FLAG_IS_GAME;
+            }
+
+            if (sa.getBoolean(
+                    com.android.internal.R.styleable.AndroidManifestApplication_cantSaveState,
+                    false)) {
+                ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE;
+
+                // A heavy-weight application can not be in a custom process.
+                // We can do direct compare because we intern all strings.
+                if (ai.processName != null && !ai.processName.equals(ai.packageName)) {
+                    outError[0] = "cantSaveState applications can not use custom processes";
+                }
+            }
+        }
+
+        ai.uiOptions = sa.getInt(
+                com.android.internal.R.styleable.AndroidManifestApplication_uiOptions, 0);
+
+        ai.classLoaderName = sa.getString(
+            com.android.internal.R.styleable.AndroidManifestApplication_classLoader);
+        if (ai.classLoaderName != null
+                && !ClassLoaderFactory.isValidClassLoaderName(ai.classLoaderName)) {
+            outError[0] = "Invalid class loader name: " + ai.classLoaderName;
+        }
+
+        ai.zygotePreloadName = sa.getString(
+                com.android.internal.R.styleable.AndroidManifestApplication_zygotePreloadName);
+
+        sa.recycle();
+
+        if (outError[0] != null) {
+            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+            return false;
+        }
+
+        final int innerDepth = parser.getDepth();
+        // IMPORTANT: These must only be cached for a single <application> to avoid components
+        // getting added to the wrong package.
+        final CachedComponentArgs cachedArgs = new CachedComponentArgs();
+        int type;
+        boolean hasActivityOrder = false;
+        boolean hasReceiverOrder = false;
+        boolean hasServiceOrder = false;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+
+            String tagName = parser.getName();
+            if (tagName.equals("activity")) {
+                Activity a = parseActivity(owner, res, parser, flags, outError, cachedArgs, false,
+                        owner.baseHardwareAccelerated);
+                if (a == null) {
+                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+                    return false;
+                }
+
+                hasActivityOrder |= (a.order != 0);
+                owner.activities.add(a);
+
+            } else if (tagName.equals("receiver")) {
+                Activity a = parseActivity(owner, res, parser, flags, outError, cachedArgs,
+                        true, false);
+                if (a == null) {
+                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+                    return false;
+                }
+
+                hasReceiverOrder |= (a.order != 0);
+                owner.receivers.add(a);
+
+            } else if (tagName.equals("service")) {
+                Service s = parseService(owner, res, parser, flags, outError, cachedArgs);
+                if (s == null) {
+                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+                    return false;
+                }
+
+                hasServiceOrder |= (s.order != 0);
+                owner.services.add(s);
+
+            } else if (tagName.equals("provider")) {
+                Provider p = parseProvider(owner, res, parser, flags, outError, cachedArgs);
+                if (p == null) {
+                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+                    return false;
+                }
+
+                owner.providers.add(p);
+
+            } else if (tagName.equals("activity-alias")) {
+                Activity a = parseActivityAlias(owner, res, parser, flags, outError, cachedArgs);
+                if (a == null) {
+                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+                    return false;
+                }
+
+                hasActivityOrder |= (a.order != 0);
+                owner.activities.add(a);
+
+            } else if (parser.getName().equals("meta-data")) {
+                // note: application meta-data is stored off to the side, so it can
+                // remain null in the primary copy (we like to avoid extra copies because
+                // it can be large)
+                if ((owner.mAppMetaData = parseMetaData(res, parser, owner.mAppMetaData,
+                        outError)) == null) {
+                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+                    return false;
+                }
+            } else if (tagName.equals("static-library")) {
+                sa = res.obtainAttributes(parser,
+                        com.android.internal.R.styleable.AndroidManifestStaticLibrary);
+
+                // Note: don't allow this value to be a reference to a resource
+                // that may change.
+                final String lname = sa.getNonResourceString(
+                        com.android.internal.R.styleable.AndroidManifestStaticLibrary_name);
+                final int version = sa.getInt(
+                        com.android.internal.R.styleable.AndroidManifestStaticLibrary_version, -1);
+                final int versionMajor = sa.getInt(
+                        com.android.internal.R.styleable.AndroidManifestStaticLibrary_versionMajor,
+                        0);
+
+                sa.recycle();
+
+                // Since the app canot run without a static lib - fail if malformed
+                if (lname == null || version < 0) {
+                    outError[0] = "Bad static-library declaration name: " + lname
+                            + " version: " + version;
+                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+                    XmlUtils.skipCurrentTag(parser);
+                    return false;
+                }
+
+                if (owner.mSharedUserId != null) {
+                    outError[0] = "sharedUserId not allowed in static shared library";
+                    mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID;
+                    XmlUtils.skipCurrentTag(parser);
+                    return false;
+                }
+
+                if (owner.staticSharedLibName != null) {
+                    outError[0] = "Multiple static-shared libs for package " + pkgName;
+                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+                    XmlUtils.skipCurrentTag(parser);
+                    return false;
+                }
+
+                owner.staticSharedLibName = lname.intern();
+                if (version >= 0) {
+                    owner.staticSharedLibVersion =
+                            PackageInfo.composeLongVersionCode(versionMajor, version);
+                } else {
+                    owner.staticSharedLibVersion = version;
+                }
+                ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_STATIC_SHARED_LIBRARY;
+
+                XmlUtils.skipCurrentTag(parser);
+
+            } else if (tagName.equals("library")) {
+                sa = res.obtainAttributes(parser,
+                        com.android.internal.R.styleable.AndroidManifestLibrary);
+
+                // Note: don't allow this value to be a reference to a resource
+                // that may change.
+                String lname = sa.getNonResourceString(
+                        com.android.internal.R.styleable.AndroidManifestLibrary_name);
+
+                sa.recycle();
+
+                if (lname != null) {
+                    lname = lname.intern();
+                    if (!ArrayUtils.contains(owner.libraryNames, lname)) {
+                        owner.libraryNames = ArrayUtils.add(
+                                owner.libraryNames, lname);
+                    }
+                }
+
+                XmlUtils.skipCurrentTag(parser);
+
+            } else if (tagName.equals("uses-static-library")) {
+                if (!parseUsesStaticLibrary(owner, res, parser, outError)) {
+                    return false;
+                }
+
+            } else if (tagName.equals("uses-library")) {
+                sa = res.obtainAttributes(parser,
+                        com.android.internal.R.styleable.AndroidManifestUsesLibrary);
+
+                // Note: don't allow this value to be a reference to a resource
+                // that may change.
+                String lname = sa.getNonResourceString(
+                        com.android.internal.R.styleable.AndroidManifestUsesLibrary_name);
+                boolean req = sa.getBoolean(
+                        com.android.internal.R.styleable.AndroidManifestUsesLibrary_required,
+                        true);
+
+                sa.recycle();
+
+                if (lname != null) {
+                    lname = lname.intern();
+                    if (req) {
+                        owner.usesLibraries = ArrayUtils.add(owner.usesLibraries, lname);
+                    } else {
+                        owner.usesOptionalLibraries = ArrayUtils.add(
+                                owner.usesOptionalLibraries, lname);
+                    }
+                }
+
+                XmlUtils.skipCurrentTag(parser);
+
+            } else if (tagName.equals("uses-package")) {
+                // Dependencies for app installers; we don't currently try to
+                // enforce this.
+                XmlUtils.skipCurrentTag(parser);
+            } else if (tagName.equals("profileable")) {
+                sa = res.obtainAttributes(parser,
+                        com.android.internal.R.styleable.AndroidManifestProfileable);
+                if (sa.getBoolean(
+                        com.android.internal.R.styleable.AndroidManifestProfileable_shell, false)) {
+                    ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PROFILEABLE_BY_SHELL;
+                }
+                XmlUtils.skipCurrentTag(parser);
+            } else {
+                if (!RIGID_PARSER) {
+                    Slog.w(TAG, "Unknown element under <application>: " + tagName
+                            + " at " + mArchiveSourcePath + " "
+                            + parser.getPositionDescription());
+                    XmlUtils.skipCurrentTag(parser);
+                    continue;
+                } else {
+                    outError[0] = "Bad element under <application>: " + tagName;
+                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+                    return false;
+                }
+            }
+        }
+
+        if (TextUtils.isEmpty(owner.staticSharedLibName)) {
+            // Add a hidden app detail activity to normal apps which forwards user to App Details
+            // page.
+            Activity a = generateAppDetailsHiddenActivity(owner, flags, outError,
+                    owner.baseHardwareAccelerated);
+            owner.activities.add(a);
+        }
+
+        if (hasActivityOrder) {
+            Collections.sort(owner.activities, (a1, a2) -> Integer.compare(a2.order, a1.order));
+        }
+        if (hasReceiverOrder) {
+            Collections.sort(owner.receivers,  (r1, r2) -> Integer.compare(r2.order, r1.order));
+        }
+        if (hasServiceOrder) {
+            Collections.sort(owner.services,  (s1, s2) -> Integer.compare(s2.order, s1.order));
+        }
+        // Must be ran after the entire {@link ApplicationInfo} has been fully processed and after
+        // every activity info has had a chance to set it from its attributes.
+        setMaxAspectRatio(owner);
+        setMinAspectRatio(owner);
+
+        if (hasDomainURLs(owner)) {
+            owner.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS;
+        } else {
+            owner.applicationInfo.privateFlags &= ~ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS;
+        }
+
+        return true;
+    }
+
+    /**
+     * Check if one of the IntentFilter as both actions DEFAULT / VIEW and a HTTP/HTTPS data URI
+     */
+    private static boolean hasDomainURLs(Package pkg) {
+        if (pkg == null || pkg.activities == null) return false;
+        final ArrayList<Activity> activities = pkg.activities;
+        final int countActivities = activities.size();
+        for (int n=0; n<countActivities; n++) {
+            Activity activity = activities.get(n);
+            ArrayList<ActivityIntentInfo> filters = activity.intents;
+            if (filters == null) continue;
+            final int countFilters = filters.size();
+            for (int m=0; m<countFilters; m++) {
+                ActivityIntentInfo aii = filters.get(m);
+                if (!aii.hasAction(Intent.ACTION_VIEW)) continue;
+                if (!aii.hasAction(Intent.ACTION_DEFAULT)) continue;
+                if (aii.hasDataScheme(IntentFilter.SCHEME_HTTP) ||
+                        aii.hasDataScheme(IntentFilter.SCHEME_HTTPS)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Parse the {@code application} XML tree at the current parse location in a
+     * <em>split APK</em> manifest.
+     * <p>
+     * Note that split APKs have many more restrictions on what they're capable
+     * of doing, so many valid features of a base APK have been carefully
+     * omitted here.
+     */
+    private boolean parseSplitApplication(Package owner, Resources res, XmlResourceParser parser,
+            int flags, int splitIndex, String[] outError)
+            throws XmlPullParserException, IOException {
+        TypedArray sa = res.obtainAttributes(parser,
+                com.android.internal.R.styleable.AndroidManifestApplication);
+
+        if (sa.getBoolean(
+                com.android.internal.R.styleable.AndroidManifestApplication_hasCode, true)) {
+            owner.splitFlags[splitIndex] |= ApplicationInfo.FLAG_HAS_CODE;
+        }
+
+        final String classLoaderName = sa.getString(
+                com.android.internal.R.styleable.AndroidManifestApplication_classLoader);
+        if (classLoaderName == null || ClassLoaderFactory.isValidClassLoaderName(classLoaderName)) {
+            owner.applicationInfo.splitClassLoaderNames[splitIndex] = classLoaderName;
+        } else {
+            outError[0] = "Invalid class loader name: " + classLoaderName;
+            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+            return false;
+        }
+
+        final int innerDepth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+
+            ComponentInfo parsedComponent = null;
+
+            // IMPORTANT: These must only be cached for a single <application> to avoid components
+            // getting added to the wrong package.
+            final CachedComponentArgs cachedArgs = new CachedComponentArgs();
+            String tagName = parser.getName();
+            if (tagName.equals("activity")) {
+                Activity a = parseActivity(owner, res, parser, flags, outError, cachedArgs, false,
+                        owner.baseHardwareAccelerated);
+                if (a == null) {
+                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+                    return false;
+                }
+
+                owner.activities.add(a);
+                parsedComponent = a.info;
+
+            } else if (tagName.equals("receiver")) {
+                Activity a = parseActivity(owner, res, parser, flags, outError, cachedArgs,
+                        true, false);
+                if (a == null) {
+                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+                    return false;
+                }
+
+                owner.receivers.add(a);
+                parsedComponent = a.info;
+
+            } else if (tagName.equals("service")) {
+                Service s = parseService(owner, res, parser, flags, outError, cachedArgs);
+                if (s == null) {
+                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+                    return false;
+                }
+
+                owner.services.add(s);
+                parsedComponent = s.info;
+
+            } else if (tagName.equals("provider")) {
+                Provider p = parseProvider(owner, res, parser, flags, outError, cachedArgs);
+                if (p == null) {
+                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+                    return false;
+                }
+
+                owner.providers.add(p);
+                parsedComponent = p.info;
+
+            } else if (tagName.equals("activity-alias")) {
+                Activity a = parseActivityAlias(owner, res, parser, flags, outError, cachedArgs);
+                if (a == null) {
+                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+                    return false;
+                }
+
+                owner.activities.add(a);
+                parsedComponent = a.info;
+
+            } else if (parser.getName().equals("meta-data")) {
+                // note: application meta-data is stored off to the side, so it can
+                // remain null in the primary copy (we like to avoid extra copies because
+                // it can be large)
+                if ((owner.mAppMetaData = parseMetaData(res, parser, owner.mAppMetaData,
+                        outError)) == null) {
+                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+                    return false;
+                }
+
+            } else if (tagName.equals("uses-static-library")) {
+                if (!parseUsesStaticLibrary(owner, res, parser, outError)) {
+                    return false;
+                }
+
+            } else if (tagName.equals("uses-library")) {
+                sa = res.obtainAttributes(parser,
+                        com.android.internal.R.styleable.AndroidManifestUsesLibrary);
+
+                // Note: don't allow this value to be a reference to a resource
+                // that may change.
+                String lname = sa.getNonResourceString(
+                        com.android.internal.R.styleable.AndroidManifestUsesLibrary_name);
+                boolean req = sa.getBoolean(
+                        com.android.internal.R.styleable.AndroidManifestUsesLibrary_required,
+                        true);
+
+                sa.recycle();
+
+                if (lname != null) {
+                    lname = lname.intern();
+                    if (req) {
+                        // Upgrade to treat as stronger constraint
+                        owner.usesLibraries = ArrayUtils.add(owner.usesLibraries, lname);
+                        owner.usesOptionalLibraries = ArrayUtils.remove(
+                                owner.usesOptionalLibraries, lname);
+                    } else {
+                        // Ignore if someone already defined as required
+                        if (!ArrayUtils.contains(owner.usesLibraries, lname)) {
+                            owner.usesOptionalLibraries = ArrayUtils.add(
+                                    owner.usesOptionalLibraries, lname);
+                        }
+                    }
+                }
+
+                XmlUtils.skipCurrentTag(parser);
+
+            } else if (tagName.equals("uses-package")) {
+                // Dependencies for app installers; we don't currently try to
+                // enforce this.
+                XmlUtils.skipCurrentTag(parser);
+
+            } else {
+                if (!RIGID_PARSER) {
+                    Slog.w(TAG, "Unknown element under <application>: " + tagName
+                            + " at " + mArchiveSourcePath + " "
+                            + parser.getPositionDescription());
+                    XmlUtils.skipCurrentTag(parser);
+                    continue;
+                } else {
+                    outError[0] = "Bad element under <application>: " + tagName;
+                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+                    return false;
+                }
+            }
+
+            if (parsedComponent != null && parsedComponent.splitName == null) {
+                // If the loaded component did not specify a split, inherit the split name
+                // based on the split it is defined in.
+                // This is used to later load the correct split when starting this
+                // component.
+                parsedComponent.splitName = owner.splitNames[splitIndex];
+            }
+        }
+
+        return true;
+    }
+
+    private static boolean parsePackageItemInfo(Package owner, PackageItemInfo outInfo,
+            String[] outError, String tag, TypedArray sa, boolean nameRequired,
+            int nameRes, int labelRes, int iconRes, int roundIconRes, int logoRes, int bannerRes) {
+        // This case can only happen in unit tests where we sometimes need to create fakes
+        // of various package parser data structures.
+        if (sa == null) {
+            outError[0] = tag + " does not contain any attributes";
+            return false;
+        }
+
+        String name = sa.getNonConfigurationString(nameRes, 0);
+        if (name == null) {
+            if (nameRequired) {
+                outError[0] = tag + " does not specify android:name";
+                return false;
+            }
+        } else {
+            String outInfoName
+                = buildClassName(owner.applicationInfo.packageName, name, outError);
+            if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(outInfoName)) {
+                outError[0] = tag + " invalid android:name";
+                return false;
+            }
+            outInfo.name = outInfoName;
+            if (outInfoName == null) {
+                return false;
+            }
+        }
+
+        int roundIconVal = sUseRoundIcon ? sa.getResourceId(roundIconRes, 0) : 0;
+        if (roundIconVal != 0) {
+            outInfo.icon = roundIconVal;
+            outInfo.nonLocalizedLabel = null;
+        } else {
+            int iconVal = sa.getResourceId(iconRes, 0);
+            if (iconVal != 0) {
+                outInfo.icon = iconVal;
+                outInfo.nonLocalizedLabel = null;
+            }
+        }
+
+        int logoVal = sa.getResourceId(logoRes, 0);
+        if (logoVal != 0) {
+            outInfo.logo = logoVal;
+        }
+
+        int bannerVal = sa.getResourceId(bannerRes, 0);
+        if (bannerVal != 0) {
+            outInfo.banner = bannerVal;
+        }
+
+        TypedValue v = sa.peekValue(labelRes);
+        if (v != null && (outInfo.labelRes=v.resourceId) == 0) {
+            outInfo.nonLocalizedLabel = v.coerceToString();
+        }
+
+        outInfo.packageName = owner.packageName;
+
+        return true;
+    }
+
+    /**
+     * Generate activity object that forwards user to App Details page automatically.
+     * This activity should be invisible to user and user should not know or see it.
+     */
+    private @NonNull PackageParser.Activity generateAppDetailsHiddenActivity(
+            PackageParser.Package owner, int flags, String[] outError,
+            boolean hardwareAccelerated) {
+
+        // Build custom App Details activity info instead of parsing it from xml
+        Activity a = new Activity(owner, PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME,
+                new ActivityInfo());
+        a.owner = owner;
+        a.setPackageName(owner.packageName);
+
+        a.info.theme = android.R.style.Theme_NoDisplay;
+        a.info.exported = true;
+        a.info.name = PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME;
+        a.info.processName = owner.applicationInfo.processName;
+        a.info.uiOptions = a.info.applicationInfo.uiOptions;
+        a.info.taskAffinity = buildTaskAffinityName(owner.packageName, owner.packageName,
+                ":app_details", outError);
+        a.info.enabled = true;
+        a.info.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
+        a.info.documentLaunchMode = ActivityInfo.DOCUMENT_LAUNCH_NONE;
+        a.info.maxRecents = ActivityTaskManager.getDefaultAppRecentsLimitStatic();
+        a.info.configChanges = getActivityConfigChanges(0, 0);
+        a.info.softInputMode = 0;
+        a.info.persistableMode = ActivityInfo.PERSIST_NEVER;
+        a.info.screenOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
+        a.info.resizeMode = RESIZE_MODE_FORCE_RESIZEABLE;
+        a.info.lockTaskLaunchMode = 0;
+        a.info.directBootAware = false;
+        a.info.rotationAnimation = ROTATION_ANIMATION_UNSPECIFIED;
+        a.info.colorMode = ActivityInfo.COLOR_MODE_DEFAULT;
+        if (hardwareAccelerated) {
+            a.info.flags |= ActivityInfo.FLAG_HARDWARE_ACCELERATED;
+        }
+        return a;
+    }
+
+    private Activity parseActivity(Package owner, Resources res,
+            XmlResourceParser parser, int flags, String[] outError, CachedComponentArgs cachedArgs,
+            boolean receiver, boolean hardwareAccelerated)
+            throws XmlPullParserException, IOException {
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestActivity);
+
+        if (cachedArgs.mActivityArgs == null) {
+            cachedArgs.mActivityArgs = new ParseComponentArgs(owner, outError,
+                    R.styleable.AndroidManifestActivity_name,
+                    R.styleable.AndroidManifestActivity_label,
+                    R.styleable.AndroidManifestActivity_icon,
+                    R.styleable.AndroidManifestActivity_roundIcon,
+                    R.styleable.AndroidManifestActivity_logo,
+                    R.styleable.AndroidManifestActivity_banner,
+                    mSeparateProcesses,
+                    R.styleable.AndroidManifestActivity_process,
+                    R.styleable.AndroidManifestActivity_description,
+                    R.styleable.AndroidManifestActivity_enabled);
+        }
+
+        cachedArgs.mActivityArgs.tag = receiver ? "<receiver>" : "<activity>";
+        cachedArgs.mActivityArgs.sa = sa;
+        cachedArgs.mActivityArgs.flags = flags;
+
+        Activity a = new Activity(cachedArgs.mActivityArgs, new ActivityInfo());
+        if (outError[0] != null) {
+            sa.recycle();
+            return null;
+        }
+
+        boolean setExported = sa.hasValue(R.styleable.AndroidManifestActivity_exported);
+        if (setExported) {
+            a.info.exported = sa.getBoolean(R.styleable.AndroidManifestActivity_exported, false);
+        }
+
+        a.info.theme = sa.getResourceId(R.styleable.AndroidManifestActivity_theme, 0);
+
+        a.info.uiOptions = sa.getInt(R.styleable.AndroidManifestActivity_uiOptions,
+                a.info.applicationInfo.uiOptions);
+
+        String parentName = sa.getNonConfigurationString(
+                R.styleable.AndroidManifestActivity_parentActivityName,
+                Configuration.NATIVE_CONFIG_VERSION);
+        if (parentName != null) {
+            String parentClassName = buildClassName(a.info.packageName, parentName, outError);
+            if (outError[0] == null) {
+                a.info.parentActivityName = parentClassName;
+            } else {
+                Log.e(TAG, "Activity " + a.info.name + " specified invalid parentActivityName " +
+                        parentName);
+                outError[0] = null;
+            }
+        }
+
+        String str;
+        str = sa.getNonConfigurationString(R.styleable.AndroidManifestActivity_permission, 0);
+        if (str == null) {
+            a.info.permission = owner.applicationInfo.permission;
+        } else {
+            a.info.permission = str.length() > 0 ? str.toString().intern() : null;
+        }
+
+        str = sa.getNonConfigurationString(
+                R.styleable.AndroidManifestActivity_taskAffinity,
+                Configuration.NATIVE_CONFIG_VERSION);
+        a.info.taskAffinity = buildTaskAffinityName(owner.applicationInfo.packageName,
+                owner.applicationInfo.taskAffinity, str, outError);
+
+        a.info.splitName =
+                sa.getNonConfigurationString(R.styleable.AndroidManifestActivity_splitName, 0);
+
+        a.info.flags = 0;
+        if (sa.getBoolean(
+                R.styleable.AndroidManifestActivity_multiprocess, false)) {
+            a.info.flags |= ActivityInfo.FLAG_MULTIPROCESS;
+        }
+
+        if (sa.getBoolean(R.styleable.AndroidManifestActivity_finishOnTaskLaunch, false)) {
+            a.info.flags |= ActivityInfo.FLAG_FINISH_ON_TASK_LAUNCH;
+        }
+
+        if (sa.getBoolean(R.styleable.AndroidManifestActivity_clearTaskOnLaunch, false)) {
+            a.info.flags |= ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH;
+        }
+
+        if (sa.getBoolean(R.styleable.AndroidManifestActivity_noHistory, false)) {
+            a.info.flags |= ActivityInfo.FLAG_NO_HISTORY;
+        }
+
+        if (sa.getBoolean(R.styleable.AndroidManifestActivity_alwaysRetainTaskState, false)) {
+            a.info.flags |= ActivityInfo.FLAG_ALWAYS_RETAIN_TASK_STATE;
+        }
+
+        if (sa.getBoolean(R.styleable.AndroidManifestActivity_stateNotNeeded, false)) {
+            a.info.flags |= ActivityInfo.FLAG_STATE_NOT_NEEDED;
+        }
+
+        if (sa.getBoolean(R.styleable.AndroidManifestActivity_excludeFromRecents, false)) {
+            a.info.flags |= ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS;
+        }
+
+        if (sa.getBoolean(R.styleable.AndroidManifestActivity_allowTaskReparenting,
+                (owner.applicationInfo.flags&ApplicationInfo.FLAG_ALLOW_TASK_REPARENTING) != 0)) {
+            a.info.flags |= ActivityInfo.FLAG_ALLOW_TASK_REPARENTING;
+        }
+
+        if (sa.getBoolean(R.styleable.AndroidManifestActivity_finishOnCloseSystemDialogs, false)) {
+            a.info.flags |= ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS;
+        }
+
+        if (sa.getBoolean(R.styleable.AndroidManifestActivity_showOnLockScreen, false)
+                || sa.getBoolean(R.styleable.AndroidManifestActivity_showForAllUsers, false)) {
+            a.info.flags |= ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
+        }
+
+        if (sa.getBoolean(R.styleable.AndroidManifestActivity_immersive, false)) {
+            a.info.flags |= ActivityInfo.FLAG_IMMERSIVE;
+        }
+
+        if (sa.getBoolean(R.styleable.AndroidManifestActivity_systemUserOnly, false)) {
+            a.info.flags |= ActivityInfo.FLAG_SYSTEM_USER_ONLY;
+        }
+
+        if (!receiver) {
+            if (sa.getBoolean(R.styleable.AndroidManifestActivity_hardwareAccelerated,
+                    hardwareAccelerated)) {
+                a.info.flags |= ActivityInfo.FLAG_HARDWARE_ACCELERATED;
+            }
+
+            a.info.launchMode = sa.getInt(
+                    R.styleable.AndroidManifestActivity_launchMode, ActivityInfo.LAUNCH_MULTIPLE);
+            a.info.documentLaunchMode = sa.getInt(
+                    R.styleable.AndroidManifestActivity_documentLaunchMode,
+                    ActivityInfo.DOCUMENT_LAUNCH_NONE);
+            a.info.maxRecents = sa.getInt(
+                    R.styleable.AndroidManifestActivity_maxRecents,
+                    ActivityTaskManager.getDefaultAppRecentsLimitStatic());
+            a.info.configChanges = getActivityConfigChanges(
+                    sa.getInt(R.styleable.AndroidManifestActivity_configChanges, 0),
+                    sa.getInt(R.styleable.AndroidManifestActivity_recreateOnConfigChanges, 0));
+            a.info.softInputMode = sa.getInt(
+                    R.styleable.AndroidManifestActivity_windowSoftInputMode, 0);
+
+            a.info.persistableMode = sa.getInteger(
+                    R.styleable.AndroidManifestActivity_persistableMode,
+                    ActivityInfo.PERSIST_ROOT_ONLY);
+
+            if (sa.getBoolean(R.styleable.AndroidManifestActivity_allowEmbedded, false)) {
+                a.info.flags |= ActivityInfo.FLAG_ALLOW_EMBEDDED;
+            }
+
+            if (sa.getBoolean(R.styleable.AndroidManifestActivity_autoRemoveFromRecents, false)) {
+                a.info.flags |= ActivityInfo.FLAG_AUTO_REMOVE_FROM_RECENTS;
+            }
+
+            if (sa.getBoolean(R.styleable.AndroidManifestActivity_relinquishTaskIdentity, false)) {
+                a.info.flags |= ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY;
+            }
+
+            if (sa.getBoolean(R.styleable.AndroidManifestActivity_resumeWhilePausing, false)) {
+                a.info.flags |= ActivityInfo.FLAG_RESUME_WHILE_PAUSING;
+            }
+
+            a.info.screenOrientation = sa.getInt(
+                    R.styleable.AndroidManifestActivity_screenOrientation,
+                    SCREEN_ORIENTATION_UNSPECIFIED);
+
+            setActivityResizeMode(a.info, sa, owner);
+
+            if (sa.getBoolean(R.styleable.AndroidManifestActivity_supportsPictureInPicture,
+                    false)) {
+                a.info.flags |= FLAG_SUPPORTS_PICTURE_IN_PICTURE;
+            }
+
+            if (sa.getBoolean(R.styleable.AndroidManifestActivity_alwaysFocusable, false)) {
+                a.info.flags |= FLAG_ALWAYS_FOCUSABLE;
+            }
+
+            if (sa.hasValue(R.styleable.AndroidManifestActivity_maxAspectRatio)
+                    && sa.getType(R.styleable.AndroidManifestActivity_maxAspectRatio)
+                    == TypedValue.TYPE_FLOAT) {
+                a.setMaxAspectRatio(sa.getFloat(R.styleable.AndroidManifestActivity_maxAspectRatio,
+                        0 /*default*/));
+            }
+
+            if (sa.hasValue(R.styleable.AndroidManifestActivity_minAspectRatio)
+                    && sa.getType(R.styleable.AndroidManifestActivity_minAspectRatio)
+                    == TypedValue.TYPE_FLOAT) {
+                a.setMinAspectRatio(sa.getFloat(R.styleable.AndroidManifestActivity_minAspectRatio,
+                        0 /*default*/));
+            }
+
+            a.info.lockTaskLaunchMode =
+                    sa.getInt(R.styleable.AndroidManifestActivity_lockTaskMode, 0);
+
+            a.info.directBootAware = sa.getBoolean(
+                    R.styleable.AndroidManifestActivity_directBootAware,
+                    false);
+
+            a.info.requestedVrComponent =
+                sa.getString(R.styleable.AndroidManifestActivity_enableVrMode);
+
+            a.info.rotationAnimation =
+                sa.getInt(R.styleable.AndroidManifestActivity_rotationAnimation, ROTATION_ANIMATION_UNSPECIFIED);
+
+            a.info.colorMode = sa.getInt(R.styleable.AndroidManifestActivity_colorMode,
+                    ActivityInfo.COLOR_MODE_DEFAULT);
+
+            if (sa.getBoolean(
+                        R.styleable.AndroidManifestActivity_preferMinimalPostProcessing, false)) {
+                a.info.flags |= ActivityInfo.FLAG_PREFER_MINIMAL_POST_PROCESSING;
+            }
+
+            if (sa.getBoolean(R.styleable.AndroidManifestActivity_showWhenLocked, false)) {
+                a.info.flags |= ActivityInfo.FLAG_SHOW_WHEN_LOCKED;
+            }
+
+            if (sa.getBoolean(R.styleable.AndroidManifestActivity_turnScreenOn, false)) {
+                a.info.flags |= ActivityInfo.FLAG_TURN_SCREEN_ON;
+            }
+
+            if (sa.getBoolean(R.styleable.AndroidManifestActivity_inheritShowWhenLocked, false)) {
+                a.info.privateFlags |= ActivityInfo.FLAG_INHERIT_SHOW_WHEN_LOCKED;
+            }
+        } else {
+            a.info.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
+            a.info.configChanges = 0;
+
+            if (sa.getBoolean(R.styleable.AndroidManifestActivity_singleUser, false)) {
+                a.info.flags |= ActivityInfo.FLAG_SINGLE_USER;
+            }
+
+            a.info.directBootAware = sa.getBoolean(
+                    R.styleable.AndroidManifestActivity_directBootAware,
+                    false);
+        }
+
+        if (a.info.directBootAware) {
+            owner.applicationInfo.privateFlags |=
+                    ApplicationInfo.PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE;
+        }
+
+        // can't make this final; we may set it later via meta-data
+        boolean visibleToEphemeral =
+                sa.getBoolean(R.styleable.AndroidManifestActivity_visibleToInstantApps, false);
+        if (visibleToEphemeral) {
+            a.info.flags |= ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP;
+            owner.visibleToInstantApps = true;
+        }
+
+        sa.recycle();
+
+        if (receiver && (owner.applicationInfo.privateFlags
+                &ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0) {
+            // A heavy-weight application can not have receives in its main process
+            // We can do direct compare because we intern all strings.
+            if (a.info.processName == owner.packageName) {
+                outError[0] = "Heavy-weight applications can not have receivers in main process";
+            }
+        }
+
+        if (outError[0] != null) {
+            return null;
+        }
+
+        int outerDepth = parser.getDepth();
+        int type;
+        while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
+               && (type != XmlPullParser.END_TAG
+                       || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+
+            if (parser.getName().equals("intent-filter")) {
+                ActivityIntentInfo intent = new ActivityIntentInfo(a);
+                if (!parseIntent(res, parser, true /*allowGlobs*/, true /*allowAutoVerify*/,
+                        intent, outError)) {
+                    return null;
+                }
+                if (intent.countActions() == 0) {
+                    Slog.w(TAG, "No actions in intent filter at "
+                            + mArchiveSourcePath + " "
+                            + parser.getPositionDescription());
+                } else {
+                    a.order = Math.max(intent.getOrder(), a.order);
+                    a.intents.add(intent);
+                }
+                // adjust activity flags when we implicitly expose it via a browsable filter
+                final int visibility = visibleToEphemeral
+                        ? IntentFilter.VISIBILITY_EXPLICIT
+                        : !receiver && isImplicitlyExposedIntent(intent)
+                                ? IntentFilter.VISIBILITY_IMPLICIT
+                                : IntentFilter.VISIBILITY_NONE;
+                intent.setVisibilityToInstantApp(visibility);
+                if (intent.isVisibleToInstantApp()) {
+                    a.info.flags |= ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP;
+                }
+                if (intent.isImplicitlyVisibleToInstantApp()) {
+                    a.info.flags |= ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP;
+                }
+                if (LOG_UNSAFE_BROADCASTS && receiver
+                        && (owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.O)) {
+                    for (int i = 0; i < intent.countActions(); i++) {
+                        final String action = intent.getAction(i);
+                        if (action == null || !action.startsWith("android.")) continue;
+                        if (!SAFE_BROADCASTS.contains(action)) {
+                            Slog.w(TAG, "Broadcast " + action + " may never be delivered to "
+                                    + owner.packageName + " as requested at: "
+                                    + parser.getPositionDescription());
+                        }
+                    }
+                }
+            } else if (!receiver && parser.getName().equals("preferred")) {
+                ActivityIntentInfo intent = new ActivityIntentInfo(a);
+                if (!parseIntent(res, parser, false /*allowGlobs*/, false /*allowAutoVerify*/,
+                        intent, outError)) {
+                    return null;
+                }
+                if (intent.countActions() == 0) {
+                    Slog.w(TAG, "No actions in preferred at "
+                            + mArchiveSourcePath + " "
+                            + parser.getPositionDescription());
+                } else {
+                    if (owner.preferredActivityFilters == null) {
+                        owner.preferredActivityFilters = new ArrayList<ActivityIntentInfo>();
+                    }
+                    owner.preferredActivityFilters.add(intent);
+                }
+                // adjust activity flags when we implicitly expose it via a browsable filter
+                final int visibility = visibleToEphemeral
+                        ? IntentFilter.VISIBILITY_EXPLICIT
+                        : !receiver && isImplicitlyExposedIntent(intent)
+                                ? IntentFilter.VISIBILITY_IMPLICIT
+                                : IntentFilter.VISIBILITY_NONE;
+                intent.setVisibilityToInstantApp(visibility);
+                if (intent.isVisibleToInstantApp()) {
+                    a.info.flags |= ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP;
+                }
+                if (intent.isImplicitlyVisibleToInstantApp()) {
+                    a.info.flags |= ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP;
+                }
+            } else if (parser.getName().equals("meta-data")) {
+                if ((a.metaData = parseMetaData(res, parser, a.metaData,
+                        outError)) == null) {
+                    return null;
+                }
+            } else if (!receiver && parser.getName().equals("layout")) {
+                parseLayout(res, parser, a);
+            } else {
+                if (!RIGID_PARSER) {
+                    Slog.w(TAG, "Problem in package " + mArchiveSourcePath + ":");
+                    if (receiver) {
+                        Slog.w(TAG, "Unknown element under <receiver>: " + parser.getName()
+                                + " at " + mArchiveSourcePath + " "
+                                + parser.getPositionDescription());
+                    } else {
+                        Slog.w(TAG, "Unknown element under <activity>: " + parser.getName()
+                                + " at " + mArchiveSourcePath + " "
+                                + parser.getPositionDescription());
+                    }
+                    XmlUtils.skipCurrentTag(parser);
+                    continue;
+                } else {
+                    if (receiver) {
+                        outError[0] = "Bad element under <receiver>: " + parser.getName();
+                    } else {
+                        outError[0] = "Bad element under <activity>: " + parser.getName();
+                    }
+                    return null;
+                }
+            }
+        }
+
+        resolveWindowLayout(a);
+
+        if (!setExported) {
+            a.info.exported = a.intents.size() > 0;
+        }
+
+        return a;
+    }
+
+    private void setActivityResizeMode(ActivityInfo aInfo, TypedArray sa, Package owner) {
+        final boolean appExplicitDefault = (owner.applicationInfo.privateFlags
+                & (PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE
+                | PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE)) != 0;
+
+        if (sa.hasValue(R.styleable.AndroidManifestActivity_resizeableActivity)
+                || appExplicitDefault) {
+            // Activity or app explicitly set if it is resizeable or not;
+            final boolean appResizeable = (owner.applicationInfo.privateFlags
+                    & PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE) != 0;
+            if (sa.getBoolean(R.styleable.AndroidManifestActivity_resizeableActivity,
+                    appResizeable)) {
+                aInfo.resizeMode = RESIZE_MODE_RESIZEABLE;
+            } else {
+                aInfo.resizeMode = RESIZE_MODE_UNRESIZEABLE;
+            }
+            return;
+        }
+
+        if ((owner.applicationInfo.privateFlags
+                & PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION) != 0) {
+            // The activity or app didn't explicitly set the resizing option, however we want to
+            // make it resize due to the sdk version it is targeting.
+            aInfo.resizeMode = RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
+            return;
+        }
+
+        // resize preference isn't set and target sdk version doesn't support resizing apps by
+        // default. For the app to be resizeable if it isn't fixed orientation or immersive.
+        if (aInfo.isFixedOrientationPortrait()) {
+            aInfo.resizeMode = RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY;
+        } else if (aInfo.isFixedOrientationLandscape()) {
+            aInfo.resizeMode = RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY;
+        } else if (aInfo.isFixedOrientation()) {
+            aInfo.resizeMode = RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION;
+        } else {
+            aInfo.resizeMode = RESIZE_MODE_FORCE_RESIZEABLE;
+        }
+    }
+
+    /**
+     * Sets every the max aspect ratio of every child activity that doesn't already have an aspect
+     * ratio set.
+     */
+    private void setMaxAspectRatio(Package owner) {
+        // Default to (1.86) 16.7:9 aspect ratio for pre-O apps and unset for O and greater.
+        // NOTE: 16.7:9 was the max aspect ratio Android devices can support pre-O per the CDD.
+        float maxAspectRatio = owner.applicationInfo.targetSdkVersion < O
+                ? DEFAULT_PRE_O_MAX_ASPECT_RATIO : 0;
+
+        if (owner.applicationInfo.maxAspectRatio != 0) {
+            // Use the application max aspect ration as default if set.
+            maxAspectRatio = owner.applicationInfo.maxAspectRatio;
+        } else if (owner.mAppMetaData != null
+                && owner.mAppMetaData.containsKey(METADATA_MAX_ASPECT_RATIO)) {
+            maxAspectRatio = owner.mAppMetaData.getFloat(METADATA_MAX_ASPECT_RATIO, maxAspectRatio);
+        }
+
+        for (Activity activity : owner.activities) {
+            // If the max aspect ratio for the activity has already been set, skip.
+            if (activity.hasMaxAspectRatio()) {
+                continue;
+            }
+
+            // By default we prefer to use a values defined on the activity directly than values
+            // defined on the application. We do not check the styled attributes on the activity
+            // as it would have already been set when we processed the activity. We wait to process
+            // the meta data here since this method is called at the end of processing the
+            // application and all meta data is guaranteed.
+            final float activityAspectRatio = activity.metaData != null
+                    ? activity.metaData.getFloat(METADATA_MAX_ASPECT_RATIO, maxAspectRatio)
+                    : maxAspectRatio;
+
+            activity.setMaxAspectRatio(activityAspectRatio);
+        }
+    }
+
+    /**
+     * Sets every the min aspect ratio of every child activity that doesn't already have an aspect
+     * ratio set.
+     */
+    private void setMinAspectRatio(Package owner) {
+        final float minAspectRatio;
+        if (owner.applicationInfo.minAspectRatio != 0) {
+            // Use the application max aspect ration as default if set.
+            minAspectRatio = owner.applicationInfo.minAspectRatio;
+        } else {
+            // Default to (1.33) 4:3 aspect ratio for pre-Q apps and unset for Q and greater.
+            // NOTE: 4:3 was the min aspect ratio Android devices can support pre-Q per the CDD,
+            // except for watches which always supported 1:1.
+            minAspectRatio = owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.Q
+                    ? 0
+                    : (mCallback != null && mCallback.hasFeature(FEATURE_WATCH))
+                            ? DEFAULT_PRE_Q_MIN_ASPECT_RATIO_WATCH
+                            : DEFAULT_PRE_Q_MIN_ASPECT_RATIO;
+        }
+
+        for (Activity activity : owner.activities) {
+            if (activity.hasMinAspectRatio()) {
+                continue;
+            }
+            activity.setMinAspectRatio(minAspectRatio);
+        }
+    }
+
+    /**
+     * @param configChanges The bit mask of configChanges fetched from AndroidManifest.xml.
+     * @param recreateOnConfigChanges The bit mask recreateOnConfigChanges fetched from
+     *                                AndroidManifest.xml.
+     * @hide Exposed for unit testing only.
+     */
+    @TestApi
+    public static int getActivityConfigChanges(int configChanges, int recreateOnConfigChanges) {
+        return configChanges | ((~recreateOnConfigChanges) & RECREATE_ON_CONFIG_CHANGES_MASK);
+    }
+
+    private void parseLayout(Resources res, AttributeSet attrs, Activity a) {
+        TypedArray sw = res.obtainAttributes(attrs,
+                com.android.internal.R.styleable.AndroidManifestLayout);
+        int width = -1;
+        float widthFraction = -1f;
+        int height = -1;
+        float heightFraction = -1f;
+        final int widthType = sw.getType(
+                com.android.internal.R.styleable.AndroidManifestLayout_defaultWidth);
+        if (widthType == TypedValue.TYPE_FRACTION) {
+            widthFraction = sw.getFraction(
+                    com.android.internal.R.styleable.AndroidManifestLayout_defaultWidth,
+                    1, 1, -1);
+        } else if (widthType == TypedValue.TYPE_DIMENSION) {
+            width = sw.getDimensionPixelSize(
+                    com.android.internal.R.styleable.AndroidManifestLayout_defaultWidth,
+                    -1);
+        }
+        final int heightType = sw.getType(
+                com.android.internal.R.styleable.AndroidManifestLayout_defaultHeight);
+        if (heightType == TypedValue.TYPE_FRACTION) {
+            heightFraction = sw.getFraction(
+                    com.android.internal.R.styleable.AndroidManifestLayout_defaultHeight,
+                    1, 1, -1);
+        } else if (heightType == TypedValue.TYPE_DIMENSION) {
+            height = sw.getDimensionPixelSize(
+                    com.android.internal.R.styleable.AndroidManifestLayout_defaultHeight,
+                    -1);
+        }
+        int gravity = sw.getInt(
+                com.android.internal.R.styleable.AndroidManifestLayout_gravity,
+                Gravity.CENTER);
+        int minWidth = sw.getDimensionPixelSize(
+                com.android.internal.R.styleable.AndroidManifestLayout_minWidth,
+                -1);
+        int minHeight = sw.getDimensionPixelSize(
+                com.android.internal.R.styleable.AndroidManifestLayout_minHeight,
+                -1);
+        sw.recycle();
+        a.info.windowLayout = new ActivityInfo.WindowLayout(width, widthFraction,
+                height, heightFraction, gravity, minWidth, minHeight);
+    }
+
+    /**
+     * Resolves values in {@link ActivityInfo.WindowLayout}.
+     *
+     * <p>{@link ActivityInfo.WindowLayout#windowLayoutAffinity} has a fallback metadata used in
+     * Android R and some variants of pre-R.
+     */
+    private void resolveWindowLayout(Activity activity) {
+        // There isn't a metadata for us to fall back. Whatever is in layout is correct.
+        if (activity.metaData == null
+                || !activity.metaData.containsKey(METADATA_ACTIVITY_WINDOW_LAYOUT_AFFINITY)) {
+            return;
+        }
+
+        final ActivityInfo aInfo = activity.info;
+        // Layout already specifies a value. We should just use that one.
+        if (aInfo.windowLayout != null && aInfo.windowLayout.windowLayoutAffinity != null) {
+            return;
+        }
+
+        String windowLayoutAffinity = activity.metaData.getString(
+                METADATA_ACTIVITY_WINDOW_LAYOUT_AFFINITY);
+        if (aInfo.windowLayout == null) {
+            aInfo.windowLayout = new ActivityInfo.WindowLayout(-1 /* width */,
+                    -1 /* widthFraction */, -1 /* height */, -1 /* heightFraction */,
+                    Gravity.NO_GRAVITY, -1 /* minWidth */, -1 /* minHeight */);
+        }
+        aInfo.windowLayout.windowLayoutAffinity = windowLayoutAffinity;
+    }
+
+    private Activity parseActivityAlias(Package owner, Resources res,
+            XmlResourceParser parser, int flags, String[] outError,
+            CachedComponentArgs cachedArgs)
+            throws XmlPullParserException, IOException {
+        TypedArray sa = res.obtainAttributes(parser,
+                com.android.internal.R.styleable.AndroidManifestActivityAlias);
+
+        String targetActivity = sa.getNonConfigurationString(
+                com.android.internal.R.styleable.AndroidManifestActivityAlias_targetActivity,
+                Configuration.NATIVE_CONFIG_VERSION);
+        if (targetActivity == null) {
+            outError[0] = "<activity-alias> does not specify android:targetActivity";
+            sa.recycle();
+            return null;
+        }
+
+        targetActivity = buildClassName(owner.applicationInfo.packageName,
+                targetActivity, outError);
+        if (targetActivity == null) {
+            sa.recycle();
+            return null;
+        }
+
+        if (cachedArgs.mActivityAliasArgs == null) {
+            cachedArgs.mActivityAliasArgs = new ParseComponentArgs(owner, outError,
+                    com.android.internal.R.styleable.AndroidManifestActivityAlias_name,
+                    com.android.internal.R.styleable.AndroidManifestActivityAlias_label,
+                    com.android.internal.R.styleable.AndroidManifestActivityAlias_icon,
+                    com.android.internal.R.styleable.AndroidManifestActivityAlias_roundIcon,
+                    com.android.internal.R.styleable.AndroidManifestActivityAlias_logo,
+                    com.android.internal.R.styleable.AndroidManifestActivityAlias_banner,
+                    mSeparateProcesses,
+                    0,
+                    com.android.internal.R.styleable.AndroidManifestActivityAlias_description,
+                    com.android.internal.R.styleable.AndroidManifestActivityAlias_enabled);
+            cachedArgs.mActivityAliasArgs.tag = "<activity-alias>";
+        }
+
+        cachedArgs.mActivityAliasArgs.sa = sa;
+        cachedArgs.mActivityAliasArgs.flags = flags;
+
+        Activity target = null;
+
+        final int NA = owner.activities.size();
+        for (int i=0; i<NA; i++) {
+            Activity t = owner.activities.get(i);
+            if (targetActivity.equals(t.info.name)) {
+                target = t;
+                break;
+            }
+        }
+
+        if (target == null) {
+            outError[0] = "<activity-alias> target activity " + targetActivity
+                    + " not found in manifest";
+            sa.recycle();
+            return null;
+        }
+
+        ActivityInfo info = new ActivityInfo();
+        info.targetActivity = targetActivity;
+        info.configChanges = target.info.configChanges;
+        info.flags = target.info.flags;
+        info.privateFlags = target.info.privateFlags;
+        info.icon = target.info.icon;
+        info.logo = target.info.logo;
+        info.banner = target.info.banner;
+        info.labelRes = target.info.labelRes;
+        info.nonLocalizedLabel = target.info.nonLocalizedLabel;
+        info.launchMode = target.info.launchMode;
+        info.lockTaskLaunchMode = target.info.lockTaskLaunchMode;
+        info.processName = target.info.processName;
+        if (info.descriptionRes == 0) {
+            info.descriptionRes = target.info.descriptionRes;
+        }
+        info.screenOrientation = target.info.screenOrientation;
+        info.taskAffinity = target.info.taskAffinity;
+        info.theme = target.info.theme;
+        info.softInputMode = target.info.softInputMode;
+        info.uiOptions = target.info.uiOptions;
+        info.parentActivityName = target.info.parentActivityName;
+        info.maxRecents = target.info.maxRecents;
+        info.windowLayout = target.info.windowLayout;
+        info.resizeMode = target.info.resizeMode;
+        info.maxAspectRatio = target.info.maxAspectRatio;
+        info.minAspectRatio = target.info.minAspectRatio;
+        info.requestedVrComponent = target.info.requestedVrComponent;
+
+        info.directBootAware = target.info.directBootAware;
+
+        Activity a = new Activity(cachedArgs.mActivityAliasArgs, info);
+        if (outError[0] != null) {
+            sa.recycle();
+            return null;
+        }
+
+        final boolean setExported = sa.hasValue(
+                com.android.internal.R.styleable.AndroidManifestActivityAlias_exported);
+        if (setExported) {
+            a.info.exported = sa.getBoolean(
+                    com.android.internal.R.styleable.AndroidManifestActivityAlias_exported, false);
+        }
+
+        String str;
+        str = sa.getNonConfigurationString(
+                com.android.internal.R.styleable.AndroidManifestActivityAlias_permission, 0);
+        if (str != null) {
+            a.info.permission = str.length() > 0 ? str.toString().intern() : null;
+        }
+
+        String parentName = sa.getNonConfigurationString(
+                com.android.internal.R.styleable.AndroidManifestActivityAlias_parentActivityName,
+                Configuration.NATIVE_CONFIG_VERSION);
+        if (parentName != null) {
+            String parentClassName = buildClassName(a.info.packageName, parentName, outError);
+            if (outError[0] == null) {
+                a.info.parentActivityName = parentClassName;
+            } else {
+                Log.e(TAG, "Activity alias " + a.info.name +
+                        " specified invalid parentActivityName " + parentName);
+                outError[0] = null;
+            }
+        }
+
+        // TODO add visibleToInstantApps attribute to activity alias
+        final boolean visibleToEphemeral =
+                ((a.info.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0);
+
+        sa.recycle();
+
+        if (outError[0] != null) {
+            return null;
+        }
+
+        int outerDepth = parser.getDepth();
+        int type;
+        while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
+               && (type != XmlPullParser.END_TAG
+                       || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+
+            if (parser.getName().equals("intent-filter")) {
+                ActivityIntentInfo intent = new ActivityIntentInfo(a);
+                if (!parseIntent(res, parser, true /*allowGlobs*/, true /*allowAutoVerify*/,
+                        intent, outError)) {
+                    return null;
+                }
+                if (intent.countActions() == 0) {
+                    Slog.w(TAG, "No actions in intent filter at "
+                            + mArchiveSourcePath + " "
+                            + parser.getPositionDescription());
+                } else {
+                    a.order = Math.max(intent.getOrder(), a.order);
+                    a.intents.add(intent);
+                }
+                // adjust activity flags when we implicitly expose it via a browsable filter
+                final int visibility = visibleToEphemeral
+                        ? IntentFilter.VISIBILITY_EXPLICIT
+                        : isImplicitlyExposedIntent(intent)
+                                ? IntentFilter.VISIBILITY_IMPLICIT
+                                : IntentFilter.VISIBILITY_NONE;
+                intent.setVisibilityToInstantApp(visibility);
+                if (intent.isVisibleToInstantApp()) {
+                    a.info.flags |= ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP;
+                }
+                if (intent.isImplicitlyVisibleToInstantApp()) {
+                    a.info.flags |= ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP;
+                }
+            } else if (parser.getName().equals("meta-data")) {
+                if ((a.metaData=parseMetaData(res, parser, a.metaData,
+                        outError)) == null) {
+                    return null;
+                }
+            } else {
+                if (!RIGID_PARSER) {
+                    Slog.w(TAG, "Unknown element under <activity-alias>: " + parser.getName()
+                            + " at " + mArchiveSourcePath + " "
+                            + parser.getPositionDescription());
+                    XmlUtils.skipCurrentTag(parser);
+                    continue;
+                } else {
+                    outError[0] = "Bad element under <activity-alias>: " + parser.getName();
+                    return null;
+                }
+            }
+        }
+
+        if (!setExported) {
+            a.info.exported = a.intents.size() > 0;
+        }
+
+        return a;
+    }
+
+    private Provider parseProvider(Package owner, Resources res,
+            XmlResourceParser parser, int flags, String[] outError,
+            CachedComponentArgs cachedArgs)
+            throws XmlPullParserException, IOException {
+        TypedArray sa = res.obtainAttributes(parser,
+                com.android.internal.R.styleable.AndroidManifestProvider);
+
+        if (cachedArgs.mProviderArgs == null) {
+            cachedArgs.mProviderArgs = new ParseComponentArgs(owner, outError,
+                    com.android.internal.R.styleable.AndroidManifestProvider_name,
+                    com.android.internal.R.styleable.AndroidManifestProvider_label,
+                    com.android.internal.R.styleable.AndroidManifestProvider_icon,
+                    com.android.internal.R.styleable.AndroidManifestProvider_roundIcon,
+                    com.android.internal.R.styleable.AndroidManifestProvider_logo,
+                    com.android.internal.R.styleable.AndroidManifestProvider_banner,
+                    mSeparateProcesses,
+                    com.android.internal.R.styleable.AndroidManifestProvider_process,
+                    com.android.internal.R.styleable.AndroidManifestProvider_description,
+                    com.android.internal.R.styleable.AndroidManifestProvider_enabled);
+            cachedArgs.mProviderArgs.tag = "<provider>";
+        }
+
+        cachedArgs.mProviderArgs.sa = sa;
+        cachedArgs.mProviderArgs.flags = flags;
+
+        Provider p = new Provider(cachedArgs.mProviderArgs, new ProviderInfo());
+        if (outError[0] != null) {
+            sa.recycle();
+            return null;
+        }
+
+        boolean providerExportedDefault = false;
+
+        if (owner.applicationInfo.targetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR1) {
+            // For compatibility, applications targeting API level 16 or lower
+            // should have their content providers exported by default, unless they
+            // specify otherwise.
+            providerExportedDefault = true;
+        }
+
+        p.info.exported = sa.getBoolean(
+                com.android.internal.R.styleable.AndroidManifestProvider_exported,
+                providerExportedDefault);
+
+        String cpname = sa.getNonConfigurationString(
+                com.android.internal.R.styleable.AndroidManifestProvider_authorities, 0);
+
+        p.info.isSyncable = sa.getBoolean(
+                com.android.internal.R.styleable.AndroidManifestProvider_syncable,
+                false);
+
+        String permission = sa.getNonConfigurationString(
+                com.android.internal.R.styleable.AndroidManifestProvider_permission, 0);
+        String str = sa.getNonConfigurationString(
+                com.android.internal.R.styleable.AndroidManifestProvider_readPermission, 0);
+        if (str == null) {
+            str = permission;
+        }
+        if (str == null) {
+            p.info.readPermission = owner.applicationInfo.permission;
+        } else {
+            p.info.readPermission =
+                str.length() > 0 ? str.toString().intern() : null;
+        }
+        str = sa.getNonConfigurationString(
+                com.android.internal.R.styleable.AndroidManifestProvider_writePermission, 0);
+        if (str == null) {
+            str = permission;
+        }
+        if (str == null) {
+            p.info.writePermission = owner.applicationInfo.permission;
+        } else {
+            p.info.writePermission =
+                str.length() > 0 ? str.toString().intern() : null;
+        }
+
+        p.info.grantUriPermissions = sa.getBoolean(
+                com.android.internal.R.styleable.AndroidManifestProvider_grantUriPermissions,
+                false);
+
+        p.info.forceUriPermissions = sa.getBoolean(
+                com.android.internal.R.styleable.AndroidManifestProvider_forceUriPermissions,
+                false);
+
+        p.info.multiprocess = sa.getBoolean(
+                com.android.internal.R.styleable.AndroidManifestProvider_multiprocess,
+                false);
+
+        p.info.initOrder = sa.getInt(
+                com.android.internal.R.styleable.AndroidManifestProvider_initOrder,
+                0);
+
+        p.info.splitName =
+                sa.getNonConfigurationString(R.styleable.AndroidManifestProvider_splitName, 0);
+
+        p.info.flags = 0;
+
+        if (sa.getBoolean(
+                com.android.internal.R.styleable.AndroidManifestProvider_singleUser,
+                false)) {
+            p.info.flags |= ProviderInfo.FLAG_SINGLE_USER;
+        }
+
+        p.info.directBootAware = sa.getBoolean(
+                R.styleable.AndroidManifestProvider_directBootAware,
+                false);
+        if (p.info.directBootAware) {
+            owner.applicationInfo.privateFlags |=
+                    ApplicationInfo.PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE;
+        }
+
+        final boolean visibleToEphemeral =
+                sa.getBoolean(R.styleable.AndroidManifestProvider_visibleToInstantApps, false);
+        if (visibleToEphemeral) {
+            p.info.flags |= ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP;
+            owner.visibleToInstantApps = true;
+        }
+
+        sa.recycle();
+
+        if ((owner.applicationInfo.privateFlags&ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE)
+                != 0) {
+            // A heavy-weight application can not have providers in its main process
+            // We can do direct compare because we intern all strings.
+            if (p.info.processName == owner.packageName) {
+                outError[0] = "Heavy-weight applications can not have providers in main process";
+                return null;
+            }
+        }
+
+        if (cpname == null) {
+            outError[0] = "<provider> does not include authorities attribute";
+            return null;
+        }
+        if (cpname.length() <= 0) {
+            outError[0] = "<provider> has empty authorities attribute";
+            return null;
+        }
+        p.info.authority = cpname.intern();
+
+        if (!parseProviderTags(
+                res, parser, visibleToEphemeral, p, outError)) {
+            return null;
+        }
+
+        return p;
+    }
+
+    private boolean parseProviderTags(Resources res, XmlResourceParser parser,
+            boolean visibleToEphemeral, Provider outInfo, String[] outError)
+                    throws XmlPullParserException, IOException {
+        int outerDepth = parser.getDepth();
+        int type;
+        while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
+               && (type != XmlPullParser.END_TAG
+                       || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+
+            if (parser.getName().equals("intent-filter")) {
+                ProviderIntentInfo intent = new ProviderIntentInfo(outInfo);
+                if (!parseIntent(res, parser, true /*allowGlobs*/, false /*allowAutoVerify*/,
+                        intent, outError)) {
+                    return false;
+                }
+                if (visibleToEphemeral) {
+                    intent.setVisibilityToInstantApp(IntentFilter.VISIBILITY_EXPLICIT);
+                    outInfo.info.flags |= ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP;
+                }
+                outInfo.order = Math.max(intent.getOrder(), outInfo.order);
+                outInfo.intents.add(intent);
+
+            } else if (parser.getName().equals("meta-data")) {
+                if ((outInfo.metaData=parseMetaData(res, parser,
+                        outInfo.metaData, outError)) == null) {
+                    return false;
+                }
+
+            } else if (parser.getName().equals("grant-uri-permission")) {
+                TypedArray sa = res.obtainAttributes(parser,
+                        com.android.internal.R.styleable.AndroidManifestGrantUriPermission);
+
+                PatternMatcher pa = null;
+
+                String str = sa.getNonConfigurationString(
+                        com.android.internal.R.styleable.AndroidManifestGrantUriPermission_path, 0);
+                if (str != null) {
+                    pa = new PatternMatcher(str, PatternMatcher.PATTERN_LITERAL);
+                }
+
+                str = sa.getNonConfigurationString(
+                        com.android.internal.R.styleable.AndroidManifestGrantUriPermission_pathPrefix, 0);
+                if (str != null) {
+                    pa = new PatternMatcher(str, PatternMatcher.PATTERN_PREFIX);
+                }
+
+                str = sa.getNonConfigurationString(
+                        com.android.internal.R.styleable.AndroidManifestGrantUriPermission_pathPattern, 0);
+                if (str != null) {
+                    pa = new PatternMatcher(str, PatternMatcher.PATTERN_SIMPLE_GLOB);
+                }
+
+                sa.recycle();
+
+                if (pa != null) {
+                    if (outInfo.info.uriPermissionPatterns == null) {
+                        outInfo.info.uriPermissionPatterns = new PatternMatcher[1];
+                        outInfo.info.uriPermissionPatterns[0] = pa;
+                    } else {
+                        final int N = outInfo.info.uriPermissionPatterns.length;
+                        PatternMatcher[] newp = new PatternMatcher[N+1];
+                        System.arraycopy(outInfo.info.uriPermissionPatterns, 0, newp, 0, N);
+                        newp[N] = pa;
+                        outInfo.info.uriPermissionPatterns = newp;
+                    }
+                    outInfo.info.grantUriPermissions = true;
+                } else {
+                    if (!RIGID_PARSER) {
+                        Slog.w(TAG, "Unknown element under <path-permission>: "
+                                + parser.getName() + " at " + mArchiveSourcePath + " "
+                                + parser.getPositionDescription());
+                        XmlUtils.skipCurrentTag(parser);
+                        continue;
+                    } else {
+                        outError[0] = "No path, pathPrefix, or pathPattern for <path-permission>";
+                        return false;
+                    }
+                }
+                XmlUtils.skipCurrentTag(parser);
+
+            } else if (parser.getName().equals("path-permission")) {
+                TypedArray sa = res.obtainAttributes(parser,
+                        com.android.internal.R.styleable.AndroidManifestPathPermission);
+
+                PathPermission pa = null;
+
+                String permission = sa.getNonConfigurationString(
+                        com.android.internal.R.styleable.AndroidManifestPathPermission_permission, 0);
+                String readPermission = sa.getNonConfigurationString(
+                        com.android.internal.R.styleable.AndroidManifestPathPermission_readPermission, 0);
+                if (readPermission == null) {
+                    readPermission = permission;
+                }
+                String writePermission = sa.getNonConfigurationString(
+                        com.android.internal.R.styleable.AndroidManifestPathPermission_writePermission, 0);
+                if (writePermission == null) {
+                    writePermission = permission;
+                }
+
+                boolean havePerm = false;
+                if (readPermission != null) {
+                    readPermission = readPermission.intern();
+                    havePerm = true;
+                }
+                if (writePermission != null) {
+                    writePermission = writePermission.intern();
+                    havePerm = true;
+                }
+
+                if (!havePerm) {
+                    if (!RIGID_PARSER) {
+                        Slog.w(TAG, "No readPermission or writePermssion for <path-permission>: "
+                                + parser.getName() + " at " + mArchiveSourcePath + " "
+                                + parser.getPositionDescription());
+                        XmlUtils.skipCurrentTag(parser);
+                        continue;
+                    } else {
+                        outError[0] = "No readPermission or writePermssion for <path-permission>";
+                        return false;
+                    }
+                }
+
+                String path = sa.getNonConfigurationString(
+                        com.android.internal.R.styleable.AndroidManifestPathPermission_path, 0);
+                if (path != null) {
+                    pa = new PathPermission(path,
+                            PatternMatcher.PATTERN_LITERAL, readPermission, writePermission);
+                }
+
+                path = sa.getNonConfigurationString(
+                        com.android.internal.R.styleable.AndroidManifestPathPermission_pathPrefix, 0);
+                if (path != null) {
+                    pa = new PathPermission(path,
+                            PatternMatcher.PATTERN_PREFIX, readPermission, writePermission);
+                }
+
+                path = sa.getNonConfigurationString(
+                        com.android.internal.R.styleable.AndroidManifestPathPermission_pathPattern, 0);
+                if (path != null) {
+                    pa = new PathPermission(path,
+                            PatternMatcher.PATTERN_SIMPLE_GLOB, readPermission, writePermission);
+                }
+
+                path = sa.getNonConfigurationString(
+                        com.android.internal.R.styleable.AndroidManifestPathPermission_pathAdvancedPattern, 0);
+                if (path != null) {
+                    pa = new PathPermission(path,
+                            PatternMatcher.PATTERN_ADVANCED_GLOB, readPermission, writePermission);
+                }
+
+                sa.recycle();
+
+                if (pa != null) {
+                    if (outInfo.info.pathPermissions == null) {
+                        outInfo.info.pathPermissions = new PathPermission[1];
+                        outInfo.info.pathPermissions[0] = pa;
+                    } else {
+                        final int N = outInfo.info.pathPermissions.length;
+                        PathPermission[] newp = new PathPermission[N+1];
+                        System.arraycopy(outInfo.info.pathPermissions, 0, newp, 0, N);
+                        newp[N] = pa;
+                        outInfo.info.pathPermissions = newp;
+                    }
+                } else {
+                    if (!RIGID_PARSER) {
+                        Slog.w(TAG, "No path, pathPrefix, or pathPattern for <path-permission>: "
+                                + parser.getName() + " at " + mArchiveSourcePath + " "
+                                + parser.getPositionDescription());
+                        XmlUtils.skipCurrentTag(parser);
+                        continue;
+                    }
+                    outError[0] = "No path, pathPrefix, or pathPattern for <path-permission>";
+                    return false;
+                }
+                XmlUtils.skipCurrentTag(parser);
+
+            } else {
+                if (!RIGID_PARSER) {
+                    Slog.w(TAG, "Unknown element under <provider>: "
+                            + parser.getName() + " at " + mArchiveSourcePath + " "
+                            + parser.getPositionDescription());
+                    XmlUtils.skipCurrentTag(parser);
+                    continue;
+                } else {
+                    outError[0] = "Bad element under <provider>: " + parser.getName();
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    private Service parseService(Package owner, Resources res,
+            XmlResourceParser parser, int flags, String[] outError,
+            CachedComponentArgs cachedArgs)
+            throws XmlPullParserException, IOException {
+        TypedArray sa = res.obtainAttributes(parser,
+                com.android.internal.R.styleable.AndroidManifestService);
+
+        if (cachedArgs.mServiceArgs == null) {
+            cachedArgs.mServiceArgs = new ParseComponentArgs(owner, outError,
+                    com.android.internal.R.styleable.AndroidManifestService_name,
+                    com.android.internal.R.styleable.AndroidManifestService_label,
+                    com.android.internal.R.styleable.AndroidManifestService_icon,
+                    com.android.internal.R.styleable.AndroidManifestService_roundIcon,
+                    com.android.internal.R.styleable.AndroidManifestService_logo,
+                    com.android.internal.R.styleable.AndroidManifestService_banner,
+                    mSeparateProcesses,
+                    com.android.internal.R.styleable.AndroidManifestService_process,
+                    com.android.internal.R.styleable.AndroidManifestService_description,
+                    com.android.internal.R.styleable.AndroidManifestService_enabled);
+            cachedArgs.mServiceArgs.tag = "<service>";
+        }
+
+        cachedArgs.mServiceArgs.sa = sa;
+        cachedArgs.mServiceArgs.flags = flags;
+
+        Service s = new Service(cachedArgs.mServiceArgs, new ServiceInfo());
+        if (outError[0] != null) {
+            sa.recycle();
+            return null;
+        }
+
+        boolean setExported = sa.hasValue(
+                com.android.internal.R.styleable.AndroidManifestService_exported);
+        if (setExported) {
+            s.info.exported = sa.getBoolean(
+                    com.android.internal.R.styleable.AndroidManifestService_exported, false);
+        }
+
+        String str = sa.getNonConfigurationString(
+                com.android.internal.R.styleable.AndroidManifestService_permission, 0);
+        if (str == null) {
+            s.info.permission = owner.applicationInfo.permission;
+        } else {
+            s.info.permission = str.length() > 0 ? str.toString().intern() : null;
+        }
+
+        s.info.splitName =
+                sa.getNonConfigurationString(R.styleable.AndroidManifestService_splitName, 0);
+
+        s.info.mForegroundServiceType = sa.getInt(
+                com.android.internal.R.styleable.AndroidManifestService_foregroundServiceType,
+                ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE);
+
+        s.info.flags = 0;
+        if (sa.getBoolean(
+                com.android.internal.R.styleable.AndroidManifestService_stopWithTask,
+                false)) {
+            s.info.flags |= ServiceInfo.FLAG_STOP_WITH_TASK;
+        }
+        if (sa.getBoolean(
+                com.android.internal.R.styleable.AndroidManifestService_isolatedProcess,
+                false)) {
+            s.info.flags |= ServiceInfo.FLAG_ISOLATED_PROCESS;
+        }
+        if (sa.getBoolean(
+                com.android.internal.R.styleable.AndroidManifestService_externalService,
+                false)) {
+            s.info.flags |= ServiceInfo.FLAG_EXTERNAL_SERVICE;
+        }
+        if (sa.getBoolean(
+                com.android.internal.R.styleable.AndroidManifestService_useAppZygote,
+                false)) {
+            s.info.flags |= ServiceInfo.FLAG_USE_APP_ZYGOTE;
+        }
+        if (sa.getBoolean(
+                com.android.internal.R.styleable.AndroidManifestService_singleUser,
+                false)) {
+            s.info.flags |= ServiceInfo.FLAG_SINGLE_USER;
+        }
+
+        s.info.directBootAware = sa.getBoolean(
+                R.styleable.AndroidManifestService_directBootAware,
+                false);
+        if (s.info.directBootAware) {
+            owner.applicationInfo.privateFlags |=
+                    ApplicationInfo.PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE;
+        }
+
+        boolean visibleToEphemeral =
+                sa.getBoolean(R.styleable.AndroidManifestService_visibleToInstantApps, false);
+        if (visibleToEphemeral) {
+            s.info.flags |= ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP;
+            owner.visibleToInstantApps = true;
+        }
+
+        sa.recycle();
+
+        if ((owner.applicationInfo.privateFlags&ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE)
+                != 0) {
+            // A heavy-weight application can not have services in its main process
+            // We can do direct compare because we intern all strings.
+            if (s.info.processName == owner.packageName) {
+                outError[0] = "Heavy-weight applications can not have services in main process";
+                return null;
+            }
+        }
+
+        int outerDepth = parser.getDepth();
+        int type;
+        while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
+               && (type != XmlPullParser.END_TAG
+                       || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+
+            if (parser.getName().equals("intent-filter")) {
+                ServiceIntentInfo intent = new ServiceIntentInfo(s);
+                if (!parseIntent(res, parser, true /*allowGlobs*/, false /*allowAutoVerify*/,
+                        intent, outError)) {
+                    return null;
+                }
+                if (visibleToEphemeral) {
+                    intent.setVisibilityToInstantApp(IntentFilter.VISIBILITY_EXPLICIT);
+                    s.info.flags |= ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP;
+                }
+                s.order = Math.max(intent.getOrder(), s.order);
+                s.intents.add(intent);
+            } else if (parser.getName().equals("meta-data")) {
+                if ((s.metaData=parseMetaData(res, parser, s.metaData,
+                        outError)) == null) {
+                    return null;
+                }
+            } else {
+                if (!RIGID_PARSER) {
+                    Slog.w(TAG, "Unknown element under <service>: "
+                            + parser.getName() + " at " + mArchiveSourcePath + " "
+                            + parser.getPositionDescription());
+                    XmlUtils.skipCurrentTag(parser);
+                    continue;
+                } else {
+                    outError[0] = "Bad element under <service>: " + parser.getName();
+                    return null;
+                }
+            }
+        }
+
+        if (!setExported) {
+            s.info.exported = s.intents.size() > 0;
+        }
+
+        return s;
+    }
+
+    private boolean isImplicitlyExposedIntent(IntentInfo intent) {
+        return intent.hasCategory(Intent.CATEGORY_BROWSABLE)
+                || intent.hasAction(Intent.ACTION_SEND)
+                || intent.hasAction(Intent.ACTION_SENDTO)
+                || intent.hasAction(Intent.ACTION_SEND_MULTIPLE);
+    }
+
+    private boolean parseAllMetaData(Resources res, XmlResourceParser parser, String tag,
+            Component<?> outInfo, String[] outError) throws XmlPullParserException, IOException {
+        int outerDepth = parser.getDepth();
+        int type;
+        while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
+               && (type != XmlPullParser.END_TAG
+                       || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+
+            if (parser.getName().equals("meta-data")) {
+                if ((outInfo.metaData=parseMetaData(res, parser,
+                        outInfo.metaData, outError)) == null) {
+                    return false;
+                }
+            } else {
+                if (!RIGID_PARSER) {
+                    Slog.w(TAG, "Unknown element under " + tag + ": "
+                            + parser.getName() + " at " + mArchiveSourcePath + " "
+                            + parser.getPositionDescription());
+                    XmlUtils.skipCurrentTag(parser);
+                    continue;
+                } else {
+                    outError[0] = "Bad element under " + tag + ": " + parser.getName();
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    private Bundle parseMetaData(Resources res,
+            XmlResourceParser parser, Bundle data, String[] outError)
+            throws XmlPullParserException, IOException {
+
+        TypedArray sa = res.obtainAttributes(parser,
+                com.android.internal.R.styleable.AndroidManifestMetaData);
+
+        if (data == null) {
+            data = new Bundle();
+        }
+
+        String name = sa.getNonConfigurationString(
+                com.android.internal.R.styleable.AndroidManifestMetaData_name, 0);
+        if (name == null) {
+            outError[0] = "<meta-data> requires an android:name attribute";
+            sa.recycle();
+            return null;
+        }
+
+        name = name.intern();
+
+        TypedValue v = sa.peekValue(
+                com.android.internal.R.styleable.AndroidManifestMetaData_resource);
+        if (v != null && v.resourceId != 0) {
+            //Slog.i(TAG, "Meta data ref " + name + ": " + v);
+            data.putInt(name, v.resourceId);
+        } else {
+            v = sa.peekValue(
+                    com.android.internal.R.styleable.AndroidManifestMetaData_value);
+            //Slog.i(TAG, "Meta data " + name + ": " + v);
+            if (v != null) {
+                if (v.type == TypedValue.TYPE_STRING) {
+                    CharSequence cs = v.coerceToString();
+                    data.putString(name, cs != null ? cs.toString() : null);
+                } else if (v.type == TypedValue.TYPE_INT_BOOLEAN) {
+                    data.putBoolean(name, v.data != 0);
+                } else if (v.type >= TypedValue.TYPE_FIRST_INT
+                        && v.type <= TypedValue.TYPE_LAST_INT) {
+                    data.putInt(name, v.data);
+                } else if (v.type == TypedValue.TYPE_FLOAT) {
+                    data.putFloat(name, v.getFloat());
+                } else {
+                    if (!RIGID_PARSER) {
+                        Slog.w(TAG, "<meta-data> only supports string, integer, float, color, boolean, and resource reference types: "
+                                + parser.getName() + " at " + mArchiveSourcePath + " "
+                                + parser.getPositionDescription());
+                    } else {
+                        outError[0] = "<meta-data> only supports string, integer, float, color, boolean, and resource reference types";
+                        data = null;
+                    }
+                }
+            } else {
+                outError[0] = "<meta-data> requires an android:value or android:resource attribute";
+                data = null;
+            }
+        }
+
+        sa.recycle();
+
+        XmlUtils.skipCurrentTag(parser);
+
+        return data;
+    }
+
+    private static VerifierInfo parseVerifier(AttributeSet attrs) {
+        String packageName = null;
+        String encodedPublicKey = null;
+
+        final int attrCount = attrs.getAttributeCount();
+        for (int i = 0; i < attrCount; i++) {
+            final int attrResId = attrs.getAttributeNameResource(i);
+            switch (attrResId) {
+                case com.android.internal.R.attr.name:
+                    packageName = attrs.getAttributeValue(i);
+                    break;
+
+                case com.android.internal.R.attr.publicKey:
+                    encodedPublicKey = attrs.getAttributeValue(i);
+                    break;
+            }
+        }
+
+        if (packageName == null || packageName.length() == 0) {
+            Slog.i(TAG, "verifier package name was null; skipping");
+            return null;
+        }
+
+        final PublicKey publicKey = parsePublicKey(encodedPublicKey);
+        if (publicKey == null) {
+            Slog.i(TAG, "Unable to parse verifier public key for " + packageName);
+            return null;
+        }
+
+        return new VerifierInfo(packageName, publicKey);
+    }
+
+    public static final PublicKey parsePublicKey(final String encodedPublicKey) {
+        if (encodedPublicKey == null) {
+            Slog.w(TAG, "Could not parse null public key");
+            return null;
+        }
+
+        EncodedKeySpec keySpec;
+        try {
+            final byte[] encoded = Base64.decode(encodedPublicKey, Base64.DEFAULT);
+            keySpec = new X509EncodedKeySpec(encoded);
+        } catch (IllegalArgumentException e) {
+            Slog.w(TAG, "Could not parse verifier public key; invalid Base64");
+            return null;
+        }
+
+        /* First try the key as an RSA key. */
+        try {
+            final KeyFactory keyFactory = KeyFactory.getInstance("RSA");
+            return keyFactory.generatePublic(keySpec);
+        } catch (NoSuchAlgorithmException e) {
+            Slog.wtf(TAG, "Could not parse public key: RSA KeyFactory not included in build");
+        } catch (InvalidKeySpecException e) {
+            // Not a RSA public key.
+        }
+
+        /* Now try it as a ECDSA key. */
+        try {
+            final KeyFactory keyFactory = KeyFactory.getInstance("EC");
+            return keyFactory.generatePublic(keySpec);
+        } catch (NoSuchAlgorithmException e) {
+            Slog.wtf(TAG, "Could not parse public key: EC KeyFactory not included in build");
+        } catch (InvalidKeySpecException e) {
+            // Not a ECDSA public key.
+        }
+
+        /* Now try it as a DSA key. */
+        try {
+            final KeyFactory keyFactory = KeyFactory.getInstance("DSA");
+            return keyFactory.generatePublic(keySpec);
+        } catch (NoSuchAlgorithmException e) {
+            Slog.wtf(TAG, "Could not parse public key: DSA KeyFactory not included in build");
+        } catch (InvalidKeySpecException e) {
+            // Not a DSA public key.
+        }
+
+        /* Not a supported key type */
+        return null;
+    }
+
+    public static final String ANDROID_RESOURCES
+            = "http://schemas.android.com/apk/res/android";
+
+    private boolean parseIntent(Resources res, XmlResourceParser parser, boolean allowGlobs,
+            boolean allowAutoVerify, IntentInfo outInfo, String[] outError)
+                    throws XmlPullParserException, IOException {
+
+        TypedArray sa = res.obtainAttributes(parser,
+                com.android.internal.R.styleable.AndroidManifestIntentFilter);
+
+        int priority = sa.getInt(
+                com.android.internal.R.styleable.AndroidManifestIntentFilter_priority, 0);
+        outInfo.setPriority(priority);
+
+        int order = sa.getInt(
+                com.android.internal.R.styleable.AndroidManifestIntentFilter_order, 0);
+        outInfo.setOrder(order);
+
+        TypedValue v = sa.peekValue(
+                com.android.internal.R.styleable.AndroidManifestIntentFilter_label);
+        if (v != null && (outInfo.labelRes=v.resourceId) == 0) {
+            outInfo.nonLocalizedLabel = v.coerceToString();
+        }
+
+        int roundIconVal = sUseRoundIcon ? sa.getResourceId(
+                com.android.internal.R.styleable.AndroidManifestIntentFilter_roundIcon, 0) : 0;
+        if (roundIconVal != 0) {
+            outInfo.icon = roundIconVal;
+        } else {
+            outInfo.icon = sa.getResourceId(
+                    com.android.internal.R.styleable.AndroidManifestIntentFilter_icon, 0);
+        }
+
+        outInfo.logo = sa.getResourceId(
+                com.android.internal.R.styleable.AndroidManifestIntentFilter_logo, 0);
+
+        outInfo.banner = sa.getResourceId(
+                com.android.internal.R.styleable.AndroidManifestIntentFilter_banner, 0);
+
+        if (allowAutoVerify) {
+            outInfo.setAutoVerify(sa.getBoolean(
+                    com.android.internal.R.styleable.AndroidManifestIntentFilter_autoVerify,
+                    false));
+        }
+
+        sa.recycle();
+
+        int outerDepth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+
+            String nodeName = parser.getName();
+            if (nodeName.equals("action")) {
+                String value = parser.getAttributeValue(
+                        ANDROID_RESOURCES, "name");
+                if (value == null || value == "") {
+                    outError[0] = "No value supplied for <android:name>";
+                    return false;
+                }
+                XmlUtils.skipCurrentTag(parser);
+
+                outInfo.addAction(value);
+            } else if (nodeName.equals("category")) {
+                String value = parser.getAttributeValue(
+                        ANDROID_RESOURCES, "name");
+                if (value == null || value == "") {
+                    outError[0] = "No value supplied for <android:name>";
+                    return false;
+                }
+                XmlUtils.skipCurrentTag(parser);
+
+                outInfo.addCategory(value);
+
+            } else if (nodeName.equals("data")) {
+                sa = res.obtainAttributes(parser,
+                        com.android.internal.R.styleable.AndroidManifestData);
+
+                String str = sa.getNonConfigurationString(
+                        com.android.internal.R.styleable.AndroidManifestData_mimeType, 0);
+                if (str != null) {
+                    try {
+                        outInfo.addDataType(str);
+                    } catch (IntentFilter.MalformedMimeTypeException e) {
+                        outError[0] = e.toString();
+                        sa.recycle();
+                        return false;
+                    }
+                }
+
+                str = sa.getNonConfigurationString(
+                        com.android.internal.R.styleable.AndroidManifestData_scheme, 0);
+                if (str != null) {
+                    outInfo.addDataScheme(str);
+                }
+
+                str = sa.getNonConfigurationString(
+                        com.android.internal.R.styleable.AndroidManifestData_ssp, 0);
+                if (str != null) {
+                    outInfo.addDataSchemeSpecificPart(str, PatternMatcher.PATTERN_LITERAL);
+                }
+
+                str = sa.getNonConfigurationString(
+                        com.android.internal.R.styleable.AndroidManifestData_sspPrefix, 0);
+                if (str != null) {
+                    outInfo.addDataSchemeSpecificPart(str, PatternMatcher.PATTERN_PREFIX);
+                }
+
+                str = sa.getNonConfigurationString(
+                        com.android.internal.R.styleable.AndroidManifestData_sspPattern, 0);
+                if (str != null) {
+                    if (!allowGlobs) {
+                        outError[0] = "sspPattern not allowed here; ssp must be literal";
+                        return false;
+                    }
+                    outInfo.addDataSchemeSpecificPart(str, PatternMatcher.PATTERN_SIMPLE_GLOB);
+                }
+
+                String host = sa.getNonConfigurationString(
+                        com.android.internal.R.styleable.AndroidManifestData_host, 0);
+                String port = sa.getNonConfigurationString(
+                        com.android.internal.R.styleable.AndroidManifestData_port, 0);
+                if (host != null) {
+                    outInfo.addDataAuthority(host, port);
+                }
+
+                str = sa.getNonConfigurationString(
+                        com.android.internal.R.styleable.AndroidManifestData_path, 0);
+                if (str != null) {
+                    outInfo.addDataPath(str, PatternMatcher.PATTERN_LITERAL);
+                }
+
+                str = sa.getNonConfigurationString(
+                        com.android.internal.R.styleable.AndroidManifestData_pathPrefix, 0);
+                if (str != null) {
+                    outInfo.addDataPath(str, PatternMatcher.PATTERN_PREFIX);
+                }
+
+                str = sa.getNonConfigurationString(
+                        com.android.internal.R.styleable.AndroidManifestData_pathPattern, 0);
+                if (str != null) {
+                    if (!allowGlobs) {
+                        outError[0] = "pathPattern not allowed here; path must be literal";
+                        return false;
+                    }
+                    outInfo.addDataPath(str, PatternMatcher.PATTERN_SIMPLE_GLOB);
+                }
+
+                str = sa.getNonConfigurationString(
+                        com.android.internal.R.styleable.AndroidManifestData_pathAdvancedPattern, 0);
+                if (str != null) {
+                    if (!allowGlobs) {
+                        outError[0] = "pathAdvancedPattern not allowed here; path must be literal";
+                        return false;
+                    }
+                    outInfo.addDataPath(str, PatternMatcher.PATTERN_ADVANCED_GLOB);
+                }
+
+                sa.recycle();
+                XmlUtils.skipCurrentTag(parser);
+            } else if (!RIGID_PARSER) {
+                Slog.w(TAG, "Unknown element under <intent-filter>: "
+                        + parser.getName() + " at " + mArchiveSourcePath + " "
+                        + parser.getPositionDescription());
+                XmlUtils.skipCurrentTag(parser);
+            } else {
+                outError[0] = "Bad element under <intent-filter>: " + parser.getName();
+                return false;
+            }
+        }
+
+        outInfo.hasDefault = outInfo.hasCategory(Intent.CATEGORY_DEFAULT);
+
+        if (DEBUG_PARSER) {
+            final StringBuilder cats = new StringBuilder("Intent d=");
+            cats.append(outInfo.hasDefault);
+            cats.append(", cat=");
+
+            final Iterator<String> it = outInfo.categoriesIterator();
+            if (it != null) {
+                while (it.hasNext()) {
+                    cats.append(' ');
+                    cats.append(it.next());
+                }
+            }
+            Slog.d(TAG, cats.toString());
+        }
+
+        return true;
+    }
+
+    /**
+     *  A container for signing-related data of an application package.
+     * @hide
+     */
+    public static final class SigningDetails implements Parcelable {
+
+        @IntDef({SigningDetails.SignatureSchemeVersion.UNKNOWN,
+                SigningDetails.SignatureSchemeVersion.JAR,
+                SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2,
+                SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+                SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V4})
+        public @interface SignatureSchemeVersion {
+            int UNKNOWN = 0;
+            int JAR = 1;
+            int SIGNING_BLOCK_V2 = 2;
+            int SIGNING_BLOCK_V3 = 3;
+            int SIGNING_BLOCK_V4 = 4;
+        }
+
+        @Nullable
+        @UnsupportedAppUsage
+        public final Signature[] signatures;
+        @SignatureSchemeVersion
+        public final int signatureSchemeVersion;
+        @Nullable
+        public final ArraySet<PublicKey> publicKeys;
+
+        /**
+         * APK Signature Scheme v3 includes support for adding a proof-of-rotation record that
+         * contains two pieces of information:
+         *   1) the past signing certificates
+         *   2) the flags that APK wants to assign to each of the past signing certificates.
+         *
+         * This collection of {@code Signature} objects, each of which is formed from a former
+         * signing certificate of this APK before it was changed by signing certificate rotation,
+         * represents the first piece of information.  It is the APK saying to the rest of the
+         * world: "hey if you trust the old cert, you can trust me!"  This is useful, if for
+         * instance, the platform would like to determine whether or not to allow this APK to do
+         * something it would've allowed it to do under the old cert (like upgrade).
+         */
+        @Nullable
+        public final Signature[] pastSigningCertificates;
+
+        /** special value used to see if cert is in package - not exposed to callers */
+        private static final int PAST_CERT_EXISTS = 0;
+
+        @IntDef(
+                flag = true,
+                value = {CertCapabilities.INSTALLED_DATA,
+                        CertCapabilities.SHARED_USER_ID,
+                        CertCapabilities.PERMISSION,
+                        CertCapabilities.ROLLBACK})
+        public @interface CertCapabilities {
+
+            /** accept data from already installed pkg with this cert */
+            int INSTALLED_DATA = 1;
+
+            /** accept sharedUserId with pkg with this cert */
+            int SHARED_USER_ID = 2;
+
+            /** grant SIGNATURE permissions to pkgs with this cert */
+            int PERMISSION = 4;
+
+            /** allow pkg to update to one signed by this certificate */
+            int ROLLBACK = 8;
+
+            /** allow pkg to continue to have auth access gated by this cert */
+            int AUTH = 16;
+        }
+
+        /** A representation of unknown signing details. Use instead of null. */
+        public static final SigningDetails UNKNOWN =
+                new SigningDetails(null, SignatureSchemeVersion.UNKNOWN, null, null);
+
+        @VisibleForTesting
+        public SigningDetails(Signature[] signatures,
+                @SignatureSchemeVersion int signatureSchemeVersion,
+                ArraySet<PublicKey> keys, Signature[] pastSigningCertificates) {
+            this.signatures = signatures;
+            this.signatureSchemeVersion = signatureSchemeVersion;
+            this.publicKeys = keys;
+            this.pastSigningCertificates = pastSigningCertificates;
+        }
+
+        public SigningDetails(Signature[] signatures,
+                @SignatureSchemeVersion int signatureSchemeVersion,
+                Signature[] pastSigningCertificates)
+                throws CertificateException {
+            this(signatures, signatureSchemeVersion, toSigningKeys(signatures),
+                    pastSigningCertificates);
+        }
+
+        public SigningDetails(Signature[] signatures,
+                @SignatureSchemeVersion int signatureSchemeVersion)
+                throws CertificateException {
+            this(signatures, signatureSchemeVersion, null);
+        }
+
+        public SigningDetails(SigningDetails orig) {
+            if (orig != null) {
+                if (orig.signatures != null) {
+                    this.signatures = orig.signatures.clone();
+                } else {
+                    this.signatures = null;
+                }
+                this.signatureSchemeVersion = orig.signatureSchemeVersion;
+                this.publicKeys = new ArraySet<>(orig.publicKeys);
+                if (orig.pastSigningCertificates != null) {
+                    this.pastSigningCertificates = orig.pastSigningCertificates.clone();
+                } else {
+                    this.pastSigningCertificates = null;
+                }
+            } else {
+                this.signatures = null;
+                this.signatureSchemeVersion = SignatureSchemeVersion.UNKNOWN;
+                this.publicKeys = null;
+                this.pastSigningCertificates = null;
+            }
+        }
+
+        /** Returns true if the signing details have one or more signatures. */
+        public boolean hasSignatures() {
+            return signatures != null && signatures.length > 0;
+        }
+
+        /** Returns true if the signing details have past signing certificates. */
+        public boolean hasPastSigningCertificates() {
+            return pastSigningCertificates != null && pastSigningCertificates.length > 0;
+        }
+
+        /**
+         * Determines if the provided {@code oldDetails} is an ancestor of or the same as this one.
+         * If the {@code oldDetails} signing certificate appears in our pastSigningCertificates,
+         * then that means it has authorized a signing certificate rotation, which eventually leads
+         * to our certificate, and thus can be trusted. If this method evaluates to true, this
+         * SigningDetails object should be trusted if the previous one is.
+         */
+        public boolean hasAncestorOrSelf(SigningDetails oldDetails) {
+            if (this == UNKNOWN || oldDetails == UNKNOWN) {
+                return false;
+            }
+            if (oldDetails.signatures.length > 1) {
+
+                // multiple-signer packages cannot rotate signing certs, so we just compare current
+                // signers for an exact match
+                return signaturesMatchExactly(oldDetails);
+            } else {
+
+                // we may have signing certificate rotation history, check to see if the oldDetails
+                // was one of our old signing certificates
+                return hasCertificate(oldDetails.signatures[0]);
+            }
+        }
+
+        /**
+         * Similar to {@code hasAncestorOrSelf}.  Returns true only if this {@code SigningDetails}
+         * is a descendant of {@code oldDetails}, not if they're the same.  This is used to
+         * determine if this object is newer than the provided one.
+         */
+        public boolean hasAncestor(SigningDetails oldDetails) {
+            if (this == UNKNOWN || oldDetails == UNKNOWN) {
+                return false;
+            }
+            if (this.hasPastSigningCertificates() && oldDetails.signatures.length == 1) {
+
+                // the last entry in pastSigningCertificates is the current signer, ignore it
+                for (int i = 0; i < pastSigningCertificates.length - 1; i++) {
+                    if (pastSigningCertificates[i].equals(oldDetails.signatures[0])) {
+                        return true;
+                    }
+                }
+            }
+            return false;
+        }
+
+        /**
+         * Determines if the provided {@code oldDetails} is an ancestor of this one, and whether or
+         * not this one grants it the provided capability, represented by the {@code flags}
+         * parameter.  In the event of signing certificate rotation, a package may still interact
+         * with entities signed by its old signing certificate and not want to break previously
+         * functioning behavior.  The {@code flags} value determines which capabilities the app
+         * signed by the newer signing certificate would like to continue to give to its previous
+         * signing certificate(s).
+         */
+        public boolean checkCapability(SigningDetails oldDetails, @CertCapabilities int flags) {
+            if (this == UNKNOWN || oldDetails == UNKNOWN) {
+                return false;
+            }
+            if (oldDetails.signatures.length > 1) {
+
+                // multiple-signer packages cannot rotate signing certs, so we must have an exact
+                // match, which also means all capabilities are granted
+                return signaturesMatchExactly(oldDetails);
+            } else {
+
+                // we may have signing certificate rotation history, check to see if the oldDetails
+                // was one of our old signing certificates, and if we grant it the capability it's
+                // requesting
+                return hasCertificate(oldDetails.signatures[0], flags);
+            }
+        }
+
+        /**
+         * A special case of {@code checkCapability} which re-encodes both sets of signing
+         * certificates to counteract a previous re-encoding.
+         */
+        public boolean checkCapabilityRecover(SigningDetails oldDetails,
+                @CertCapabilities int flags) throws CertificateException {
+            if (oldDetails == UNKNOWN || this == UNKNOWN) {
+                return false;
+            }
+            if (hasPastSigningCertificates() && oldDetails.signatures.length == 1) {
+
+                // signing certificates may have rotated, check entire history for effective match
+                for (int i = 0; i < pastSigningCertificates.length; i++) {
+                    if (Signature.areEffectiveMatch(
+                            oldDetails.signatures[0],
+                            pastSigningCertificates[i])
+                            && pastSigningCertificates[i].getFlags() == flags) {
+                        return true;
+                    }
+                }
+            } else {
+                return Signature.areEffectiveMatch(oldDetails.signatures, signatures);
+            }
+            return false;
+        }
+
+        /**
+         * Determine if {@code signature} is in this SigningDetails' signing certificate history,
+         * including the current signer.  Automatically returns false if this object has multiple
+         * signing certificates, since rotation is only supported for single-signers; this is
+         * enforced by {@code hasCertificateInternal}.
+         */
+        public boolean hasCertificate(Signature signature) {
+            return hasCertificateInternal(signature, PAST_CERT_EXISTS);
+        }
+
+        /**
+         * Determine if {@code signature} is in this SigningDetails' signing certificate history,
+         * including the current signer, and whether or not it has the given permission.
+         * Certificates which match our current signer automatically get all capabilities.
+         * Automatically returns false if this object has multiple signing certificates, since
+         * rotation is only supported for single-signers.
+         */
+        public boolean hasCertificate(Signature signature, @CertCapabilities int flags) {
+            return hasCertificateInternal(signature, flags);
+        }
+
+        /** Convenient wrapper for calling {@code hasCertificate} with certificate's raw bytes. */
+        public boolean hasCertificate(byte[] certificate) {
+            Signature signature = new Signature(certificate);
+            return hasCertificate(signature);
+        }
+
+        private boolean hasCertificateInternal(Signature signature, int flags) {
+            if (this == UNKNOWN) {
+                return false;
+            }
+
+            // only single-signed apps can have pastSigningCertificates
+            if (hasPastSigningCertificates()) {
+
+                // check all past certs, except for the current one, which automatically gets all
+                // capabilities, since it is the same as the current signature
+                for (int i = 0; i < pastSigningCertificates.length - 1; i++) {
+                    if (pastSigningCertificates[i].equals(signature)) {
+                        if (flags == PAST_CERT_EXISTS
+                                || (flags & pastSigningCertificates[i].getFlags()) == flags) {
+                            return true;
+                        }
+                    }
+                }
+            }
+
+            // not in previous certs signing history, just check the current signer and make sure
+            // we are singly-signed
+            return signatures.length == 1 && signatures[0].equals(signature);
+        }
+
+        /**
+         * Determines if the provided {@code sha256String} is an ancestor of this one, and whether
+         * or not this one grants it the provided capability, represented by the {@code flags}
+         * parameter.  In the event of signing certificate rotation, a package may still interact
+         * with entities signed by its old signing certificate and not want to break previously
+         * functioning behavior.  The {@code flags} value determines which capabilities the app
+         * signed by the newer signing certificate would like to continue to give to its previous
+         * signing certificate(s).
+         *
+         * @param sha256String A hex-encoded representation of a sha256 digest.  In the case of an
+         *                     app with multiple signers, this represents the hex-encoded sha256
+         *                     digest of the combined hex-encoded sha256 digests of each individual
+         *                     signing certificate according to {@link
+         *                     PackageUtils#computeSignaturesSha256Digest(Signature[])}
+         */
+        public boolean checkCapability(String sha256String, @CertCapabilities int flags) {
+            if (this == UNKNOWN) {
+                return false;
+            }
+
+            // first see if the hash represents a single-signer in our signing history
+            byte[] sha256Bytes = sha256String == null
+                    ? null : HexEncoding.decode(sha256String, false /* allowSingleChar */);
+            if (hasSha256Certificate(sha256Bytes, flags)) {
+                return true;
+            }
+
+            // Not in signing history, either represents multiple signatures or not a match.
+            // Multiple signers can't rotate, so no need to check flags, just see if the SHAs match.
+            // We already check the single-signer case above as part of hasSha256Certificate, so no
+            // need to verify we have multiple signers, just run the old check
+            // just consider current signing certs
+            final String[] mSignaturesSha256Digests =
+                    PackageUtils.computeSignaturesSha256Digests(signatures);
+            final String mSignaturesSha256Digest =
+                    PackageUtils.computeSignaturesSha256Digest(mSignaturesSha256Digests);
+            return mSignaturesSha256Digest.equals(sha256String);
+        }
+
+        /**
+         * Determine if the {@code sha256Certificate} is in this SigningDetails' signing certificate
+         * history, including the current signer.  Automatically returns false if this object has
+         * multiple signing certificates, since rotation is only supported for single-signers.
+         */
+        public boolean hasSha256Certificate(byte[] sha256Certificate) {
+            return hasSha256CertificateInternal(sha256Certificate, PAST_CERT_EXISTS);
+        }
+
+        /**
+         * Determine if the {@code sha256Certificate} certificate hash corresponds to a signing
+         * certificate in this SigningDetails' signing certificate history, including the current
+         * signer, and whether or not it has the given permission.  Certificates which match our
+         * current signer automatically get all capabilities. Automatically returns false if this
+         * object has multiple signing certificates, since rotation is only supported for
+         * single-signers.
+         */
+        public boolean hasSha256Certificate(byte[] sha256Certificate, @CertCapabilities int flags) {
+            return hasSha256CertificateInternal(sha256Certificate, flags);
+        }
+
+        private boolean hasSha256CertificateInternal(byte[] sha256Certificate, int flags) {
+            if (this == UNKNOWN) {
+                return false;
+            }
+            if (hasPastSigningCertificates()) {
+
+                // check all past certs, except for the last one, which automatically gets all
+                // capabilities, since it is the same as the current signature, and is checked below
+                for (int i = 0; i < pastSigningCertificates.length - 1; i++) {
+                    byte[] digest = PackageUtils.computeSha256DigestBytes(
+                            pastSigningCertificates[i].toByteArray());
+                    if (Arrays.equals(sha256Certificate, digest)) {
+                        if (flags == PAST_CERT_EXISTS
+                                || (flags & pastSigningCertificates[i].getFlags()) == flags) {
+                            return true;
+                        }
+                    }
+                }
+            }
+
+            // not in previous certs signing history, just check the current signer
+            if (signatures.length == 1) {
+                byte[] digest =
+                        PackageUtils.computeSha256DigestBytes(signatures[0].toByteArray());
+                return Arrays.equals(sha256Certificate, digest);
+            }
+            return false;
+        }
+
+        /** Returns true if the signatures in this and other match exactly. */
+        public boolean signaturesMatchExactly(SigningDetails other) {
+            return Signature.areExactMatch(this.signatures, other.signatures);
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            boolean isUnknown = UNKNOWN == this;
+            dest.writeBoolean(isUnknown);
+            if (isUnknown) {
+                return;
+            }
+            dest.writeTypedArray(this.signatures, flags);
+            dest.writeInt(this.signatureSchemeVersion);
+            dest.writeArraySet(this.publicKeys);
+            dest.writeTypedArray(this.pastSigningCertificates, flags);
+        }
+
+        protected SigningDetails(Parcel in) {
+            final ClassLoader boot = Object.class.getClassLoader();
+            this.signatures = in.createTypedArray(Signature.CREATOR);
+            this.signatureSchemeVersion = in.readInt();
+            this.publicKeys = (ArraySet<PublicKey>) in.readArraySet(boot);
+            this.pastSigningCertificates = in.createTypedArray(Signature.CREATOR);
+        }
+
+        public static final @android.annotation.NonNull Creator<SigningDetails> CREATOR = new Creator<SigningDetails>() {
+            @Override
+            public SigningDetails createFromParcel(Parcel source) {
+                if (source.readBoolean()) {
+                    return UNKNOWN;
+                }
+                return new SigningDetails(source);
+            }
+
+            @Override
+            public SigningDetails[] newArray(int size) {
+                return new SigningDetails[size];
+            }
+        };
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (!(o instanceof SigningDetails)) return false;
+
+            SigningDetails that = (SigningDetails) o;
+
+            if (signatureSchemeVersion != that.signatureSchemeVersion) return false;
+            if (!Signature.areExactMatch(signatures, that.signatures)) return false;
+            if (publicKeys != null) {
+                if (!publicKeys.equals((that.publicKeys))) {
+                    return false;
+                }
+            } else if (that.publicKeys != null) {
+                return false;
+            }
+
+            // can't use Signature.areExactMatch() because order matters with the past signing certs
+            if (!Arrays.equals(pastSigningCertificates, that.pastSigningCertificates)) {
+                return false;
+            }
+
+            return true;
+        }
+
+        @Override
+        public int hashCode() {
+            int result = +Arrays.hashCode(signatures);
+            result = 31 * result + signatureSchemeVersion;
+            result = 31 * result + (publicKeys != null ? publicKeys.hashCode() : 0);
+            result = 31 * result + Arrays.hashCode(pastSigningCertificates);
+            return result;
+        }
+
+        /**
+         * Builder of {@code SigningDetails} instances.
+         */
+        public static class Builder {
+            private Signature[] mSignatures;
+            private int mSignatureSchemeVersion = SignatureSchemeVersion.UNKNOWN;
+            private Signature[] mPastSigningCertificates;
+
+            @UnsupportedAppUsage
+            public Builder() {
+            }
+
+            /** get signing certificates used to sign the current APK */
+            @UnsupportedAppUsage
+            public Builder setSignatures(Signature[] signatures) {
+                mSignatures = signatures;
+                return this;
+            }
+
+            /** set the signature scheme version used to sign the APK */
+            @UnsupportedAppUsage
+            public Builder setSignatureSchemeVersion(int signatureSchemeVersion) {
+                mSignatureSchemeVersion = signatureSchemeVersion;
+                return this;
+            }
+
+            /** set the signing certificates by which the APK proved it can be authenticated */
+            @UnsupportedAppUsage
+            public Builder setPastSigningCertificates(Signature[] pastSigningCertificates) {
+                mPastSigningCertificates = pastSigningCertificates;
+                return this;
+            }
+
+            private void checkInvariants() {
+                // must have signatures and scheme version set
+                if (mSignatures == null) {
+                    throw new IllegalStateException("SigningDetails requires the current signing"
+                            + " certificates.");
+                }
+            }
+            /** build a {@code SigningDetails} object */
+            @UnsupportedAppUsage
+            public SigningDetails build()
+                    throws CertificateException {
+                checkInvariants();
+                return new SigningDetails(mSignatures, mSignatureSchemeVersion,
+                        mPastSigningCertificates);
+            }
+        }
+    }
+
+    /**
+     * Representation of a full package parsed from APK files on disk. A package
+     * consists of a single base APK, and zero or more split APKs.
+     *
+     * Deprecated internally. Use AndroidPackage instead.
+     */
+    public final static class Package implements Parcelable {
+
+        @UnsupportedAppUsage
+        public String packageName;
+
+        // The package name declared in the manifest as the package can be
+        // renamed, for example static shared libs use synthetic package names.
+        public String manifestPackageName;
+
+        /** Names of any split APKs, ordered by parsed splitName */
+        public String[] splitNames;
+
+        // TODO: work towards making these paths invariant
+
+        public String volumeUuid;
+
+        /**
+         * Path where this package was found on disk. For monolithic packages
+         * this is path to single base APK file; for cluster packages this is
+         * path to the cluster directory.
+         */
+        public String codePath;
+
+        /** Path of base APK */
+        public String baseCodePath;
+        /** Paths of any split APKs, ordered by parsed splitName */
+        public String[] splitCodePaths;
+
+        /** Revision code of base APK */
+        public int baseRevisionCode;
+        /** Revision codes of any split APKs, ordered by parsed splitName */
+        public int[] splitRevisionCodes;
+
+        /** Flags of any split APKs; ordered by parsed splitName */
+        public int[] splitFlags;
+
+        /**
+         * Private flags of any split APKs; ordered by parsed splitName.
+         *
+         * {@hide}
+         */
+        public int[] splitPrivateFlags;
+
+        public boolean baseHardwareAccelerated;
+
+        // For now we only support one application per package.
+        @UnsupportedAppUsage
+        public ApplicationInfo applicationInfo = new ApplicationInfo();
+
+        @UnsupportedAppUsage
+        public final ArrayList<Permission> permissions = new ArrayList<Permission>(0);
+        @UnsupportedAppUsage
+        public final ArrayList<PermissionGroup> permissionGroups = new ArrayList<PermissionGroup>(0);
+        @UnsupportedAppUsage
+        public final ArrayList<Activity> activities = new ArrayList<Activity>(0);
+        @UnsupportedAppUsage
+        public final ArrayList<Activity> receivers = new ArrayList<Activity>(0);
+        @UnsupportedAppUsage
+        public final ArrayList<Provider> providers = new ArrayList<Provider>(0);
+        @UnsupportedAppUsage
+        public final ArrayList<Service> services = new ArrayList<Service>(0);
+        @UnsupportedAppUsage
+        public final ArrayList<Instrumentation> instrumentation = new ArrayList<Instrumentation>(0);
+
+        @UnsupportedAppUsage
+        public final ArrayList<String> requestedPermissions = new ArrayList<String>();
+
+        /** Permissions requested but not in the manifest. */
+        public final ArrayList<String> implicitPermissions = new ArrayList<>();
+
+        @UnsupportedAppUsage
+        public ArrayList<String> protectedBroadcasts;
+
+        public Package parentPackage;
+        public ArrayList<Package> childPackages;
+
+        public String staticSharedLibName = null;
+        public long staticSharedLibVersion = 0;
+        public ArrayList<String> libraryNames = null;
+        @UnsupportedAppUsage
+        public ArrayList<String> usesLibraries = null;
+        public ArrayList<String> usesStaticLibraries = null;
+        public long[] usesStaticLibrariesVersions = null;
+        public String[][] usesStaticLibrariesCertDigests = null;
+        @UnsupportedAppUsage
+        public ArrayList<String> usesOptionalLibraries = null;
+        @UnsupportedAppUsage
+        public String[] usesLibraryFiles = null;
+        public ArrayList<SharedLibraryInfo> usesLibraryInfos = null;
+
+        public ArrayList<ActivityIntentInfo> preferredActivityFilters = null;
+
+        public ArrayList<String> mOriginalPackages = null;
+        public String mRealPackage = null;
+        public ArrayList<String> mAdoptPermissions = null;
+
+        // We store the application meta-data independently to avoid multiple unwanted references
+        @UnsupportedAppUsage
+        public Bundle mAppMetaData = null;
+
+        // The version code declared for this package.
+        @UnsupportedAppUsage
+        public int mVersionCode;
+
+        // The major version code declared for this package.
+        public int mVersionCodeMajor;
+
+        // Return long containing mVersionCode and mVersionCodeMajor.
+        public long getLongVersionCode() {
+            return PackageInfo.composeLongVersionCode(mVersionCodeMajor, mVersionCode);
+        }
+
+        // The version name declared for this package.
+        @UnsupportedAppUsage
+        public String mVersionName;
+
+        // The shared user id that this package wants to use.
+        @UnsupportedAppUsage
+        public String mSharedUserId;
+
+        // The shared user label that this package wants to use.
+        @UnsupportedAppUsage
+        public int mSharedUserLabel;
+
+        // Signatures that were read from the package.
+        @UnsupportedAppUsage
+        @NonNull public SigningDetails mSigningDetails = SigningDetails.UNKNOWN;
+
+        // For use by package manager service for quick lookup of
+        // preferred up order.
+        @UnsupportedAppUsage
+        public int mPreferredOrder = 0;
+
+        // For use by package manager to keep track of when a package was last used.
+        public long[] mLastPackageUsageTimeInMills =
+                new long[PackageManager.NOTIFY_PACKAGE_USE_REASONS_COUNT];
+
+        // // User set enabled state.
+        // public int mSetEnabled = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
+        //
+        // // Whether the package has been stopped.
+        // public boolean mSetStopped = false;
+
+        // Additional data supplied by callers.
+        @UnsupportedAppUsage
+        public Object mExtras;
+
+        // Applications hardware preferences
+        @UnsupportedAppUsage
+        public ArrayList<ConfigurationInfo> configPreferences = null;
+
+        // Applications requested features
+        @UnsupportedAppUsage
+        public ArrayList<FeatureInfo> reqFeatures = null;
+
+        // Applications requested feature groups
+        public ArrayList<FeatureGroupInfo> featureGroups = null;
+
+        @UnsupportedAppUsage
+        public int installLocation;
+
+        public boolean coreApp;
+
+        /* An app that's required for all users and cannot be uninstalled for a user */
+        public boolean mRequiredForAllUsers;
+
+        /* The restricted account authenticator type that is used by this application */
+        public String mRestrictedAccountType;
+
+        /* The required account type without which this application will not function */
+        public String mRequiredAccountType;
+
+        public String mOverlayTarget;
+        public String mOverlayTargetName;
+        public String mOverlayCategory;
+        public int mOverlayPriority;
+        public boolean mOverlayIsStatic;
+
+        public int mCompileSdkVersion;
+        public String mCompileSdkVersionCodename;
+
+        /**
+         * Data used to feed the KeySetManagerService
+         */
+        @UnsupportedAppUsage
+        public ArraySet<String> mUpgradeKeySets;
+        @UnsupportedAppUsage
+        public ArrayMap<String, ArraySet<PublicKey>> mKeySetMapping;
+
+        /**
+         * The install time abi override for this package, if any.
+         *
+         * TODO: This seems like a horrible place to put the abiOverride because
+         * this isn't something the packageParser parsers. However, this fits in with
+         * the rest of the PackageManager where package scanning randomly pushes
+         * and prods fields out of {@code this.applicationInfo}.
+         */
+        public String cpuAbiOverride;
+        /**
+         * The install time abi override to choose 32bit abi's when multiple abi's
+         * are present. This is only meaningfull for multiarch applications.
+         * The use32bitAbi attribute is ignored if cpuAbiOverride is also set.
+         */
+        public boolean use32bitAbi;
+
+        public byte[] restrictUpdateHash;
+
+        /** Set if the app or any of its components are visible to instant applications. */
+        public boolean visibleToInstantApps;
+        /** Whether or not the package is a stub and must be replaced by the full version. */
+        public boolean isStub;
+
+        @UnsupportedAppUsage
+        public Package(String packageName) {
+            this.packageName = packageName;
+            this.manifestPackageName = packageName;
+            applicationInfo.packageName = packageName;
+            applicationInfo.uid = -1;
+        }
+
+        public void setApplicationVolumeUuid(String volumeUuid) {
+            final UUID storageUuid = StorageManager.convert(volumeUuid);
+            this.applicationInfo.volumeUuid = volumeUuid;
+            this.applicationInfo.storageUuid = storageUuid;
+            if (childPackages != null) {
+                final int packageCount = childPackages.size();
+                for (int i = 0; i < packageCount; i++) {
+                    childPackages.get(i).applicationInfo.volumeUuid = volumeUuid;
+                    childPackages.get(i).applicationInfo.storageUuid = storageUuid;
+                }
+            }
+        }
+
+        public void setApplicationInfoCodePath(String codePath) {
+            this.applicationInfo.setCodePath(codePath);
+            if (childPackages != null) {
+                final int packageCount = childPackages.size();
+                for (int i = 0; i < packageCount; i++) {
+                    childPackages.get(i).applicationInfo.setCodePath(codePath);
+                }
+            }
+        }
+
+        /** @deprecated Forward locked apps no longer supported. Resource path not needed. */
+        @Deprecated
+        public void setApplicationInfoResourcePath(String resourcePath) {
+            this.applicationInfo.setResourcePath(resourcePath);
+            if (childPackages != null) {
+                final int packageCount = childPackages.size();
+                for (int i = 0; i < packageCount; i++) {
+                    childPackages.get(i).applicationInfo.setResourcePath(resourcePath);
+                }
+            }
+        }
+
+        /** @deprecated Forward locked apps no longer supported. Resource path not needed. */
+        @Deprecated
+        public void setApplicationInfoBaseResourcePath(String resourcePath) {
+            this.applicationInfo.setBaseResourcePath(resourcePath);
+            if (childPackages != null) {
+                final int packageCount = childPackages.size();
+                for (int i = 0; i < packageCount; i++) {
+                    childPackages.get(i).applicationInfo.setBaseResourcePath(resourcePath);
+                }
+            }
+        }
+
+        public void setApplicationInfoBaseCodePath(String baseCodePath) {
+            this.applicationInfo.setBaseCodePath(baseCodePath);
+            if (childPackages != null) {
+                final int packageCount = childPackages.size();
+                for (int i = 0; i < packageCount; i++) {
+                    childPackages.get(i).applicationInfo.setBaseCodePath(baseCodePath);
+                }
+            }
+        }
+
+        public List<String> getChildPackageNames() {
+            if (childPackages == null) {
+                return null;
+            }
+            final int childCount = childPackages.size();
+            final List<String> childPackageNames = new ArrayList<>(childCount);
+            for (int i = 0; i < childCount; i++) {
+                String childPackageName = childPackages.get(i).packageName;
+                childPackageNames.add(childPackageName);
+            }
+            return childPackageNames;
+        }
+
+        public boolean hasChildPackage(String packageName) {
+            final int childCount = (childPackages != null) ? childPackages.size() : 0;
+            for (int i = 0; i < childCount; i++) {
+                if (childPackages.get(i).packageName.equals(packageName)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        public void setApplicationInfoSplitCodePaths(String[] splitCodePaths) {
+            this.applicationInfo.setSplitCodePaths(splitCodePaths);
+            // Children have no splits
+        }
+
+        /** @deprecated Forward locked apps no longer supported. Resource path not needed. */
+        @Deprecated
+        public void setApplicationInfoSplitResourcePaths(String[] resroucePaths) {
+            this.applicationInfo.setSplitResourcePaths(resroucePaths);
+            // Children have no splits
+        }
+
+        public void setSplitCodePaths(String[] codePaths) {
+            this.splitCodePaths = codePaths;
+        }
+
+        public void setCodePath(String codePath) {
+            this.codePath = codePath;
+            if (childPackages != null) {
+                final int packageCount = childPackages.size();
+                for (int i = 0; i < packageCount; i++) {
+                    childPackages.get(i).codePath = codePath;
+                }
+            }
+        }
+
+        public void setBaseCodePath(String baseCodePath) {
+            this.baseCodePath = baseCodePath;
+            if (childPackages != null) {
+                final int packageCount = childPackages.size();
+                for (int i = 0; i < packageCount; i++) {
+                    childPackages.get(i).baseCodePath = baseCodePath;
+                }
+            }
+        }
+
+        /** Sets signing details on the package and any of its children. */
+        public void setSigningDetails(@NonNull SigningDetails signingDetails) {
+            mSigningDetails = signingDetails;
+            if (childPackages != null) {
+                final int packageCount = childPackages.size();
+                for (int i = 0; i < packageCount; i++) {
+                    childPackages.get(i).mSigningDetails = signingDetails;
+                }
+            }
+        }
+
+        public void setVolumeUuid(String volumeUuid) {
+            this.volumeUuid = volumeUuid;
+            if (childPackages != null) {
+                final int packageCount = childPackages.size();
+                for (int i = 0; i < packageCount; i++) {
+                    childPackages.get(i).volumeUuid = volumeUuid;
+                }
+            }
+        }
+
+        public void setApplicationInfoFlags(int mask, int flags) {
+            applicationInfo.flags = (applicationInfo.flags & ~mask) | (mask & flags);
+            if (childPackages != null) {
+                final int packageCount = childPackages.size();
+                for (int i = 0; i < packageCount; i++) {
+                    childPackages.get(i).applicationInfo.flags =
+                            (applicationInfo.flags & ~mask) | (mask & flags);
+                }
+            }
+        }
+
+        public void setUse32bitAbi(boolean use32bitAbi) {
+            this.use32bitAbi = use32bitAbi;
+            if (childPackages != null) {
+                final int packageCount = childPackages.size();
+                for (int i = 0; i < packageCount; i++) {
+                    childPackages.get(i).use32bitAbi = use32bitAbi;
+                }
+            }
+        }
+
+        public boolean isLibrary() {
+            return staticSharedLibName != null || !ArrayUtils.isEmpty(libraryNames);
+        }
+
+        public List<String> getAllCodePaths() {
+            ArrayList<String> paths = new ArrayList<>();
+            paths.add(baseCodePath);
+            if (!ArrayUtils.isEmpty(splitCodePaths)) {
+                Collections.addAll(paths, splitCodePaths);
+            }
+            return paths;
+        }
+
+        /**
+         * Filtered set of {@link #getAllCodePaths()} that excludes
+         * resource-only APKs.
+         */
+        public List<String> getAllCodePathsExcludingResourceOnly() {
+            ArrayList<String> paths = new ArrayList<>();
+            if ((applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0) {
+                paths.add(baseCodePath);
+            }
+            if (!ArrayUtils.isEmpty(splitCodePaths)) {
+                for (int i = 0; i < splitCodePaths.length; i++) {
+                    if ((splitFlags[i] & ApplicationInfo.FLAG_HAS_CODE) != 0) {
+                        paths.add(splitCodePaths[i]);
+                    }
+                }
+            }
+            return paths;
+        }
+
+        @UnsupportedAppUsage
+        public void setPackageName(String newName) {
+            packageName = newName;
+            applicationInfo.packageName = newName;
+            for (int i=permissions.size()-1; i>=0; i--) {
+                permissions.get(i).setPackageName(newName);
+            }
+            for (int i=permissionGroups.size()-1; i>=0; i--) {
+                permissionGroups.get(i).setPackageName(newName);
+            }
+            for (int i=activities.size()-1; i>=0; i--) {
+                activities.get(i).setPackageName(newName);
+            }
+            for (int i=receivers.size()-1; i>=0; i--) {
+                receivers.get(i).setPackageName(newName);
+            }
+            for (int i=providers.size()-1; i>=0; i--) {
+                providers.get(i).setPackageName(newName);
+            }
+            for (int i=services.size()-1; i>=0; i--) {
+                services.get(i).setPackageName(newName);
+            }
+            for (int i=instrumentation.size()-1; i>=0; i--) {
+                instrumentation.get(i).setPackageName(newName);
+            }
+        }
+
+        public boolean hasComponentClassName(String name) {
+            for (int i=activities.size()-1; i>=0; i--) {
+                if (name.equals(activities.get(i).className)) {
+                    return true;
+                }
+            }
+            for (int i=receivers.size()-1; i>=0; i--) {
+                if (name.equals(receivers.get(i).className)) {
+                    return true;
+                }
+            }
+            for (int i=providers.size()-1; i>=0; i--) {
+                if (name.equals(providers.get(i).className)) {
+                    return true;
+                }
+            }
+            for (int i=services.size()-1; i>=0; i--) {
+                if (name.equals(services.get(i).className)) {
+                    return true;
+                }
+            }
+            for (int i=instrumentation.size()-1; i>=0; i--) {
+                if (name.equals(instrumentation.get(i).className)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        /** @hide */
+        public boolean isExternal() {
+            return applicationInfo.isExternal();
+        }
+
+        /** @hide */
+        public boolean isForwardLocked() {
+            return false;
+        }
+
+        /** @hide */
+        public boolean isOem() {
+            return applicationInfo.isOem();
+        }
+
+        /** @hide */
+        public boolean isVendor() {
+            return applicationInfo.isVendor();
+        }
+
+        /** @hide */
+        public boolean isProduct() {
+            return applicationInfo.isProduct();
+        }
+
+        /** @hide */
+        public boolean isSystemExt() {
+            return applicationInfo.isSystemExt();
+        }
+
+        /** @hide */
+        public boolean isOdm() {
+            return applicationInfo.isOdm();
+        }
+
+        /** @hide */
+        public boolean isPrivileged() {
+            return applicationInfo.isPrivilegedApp();
+        }
+
+        /** @hide */
+        public boolean isSystem() {
+            return applicationInfo.isSystemApp();
+        }
+
+        /** @hide */
+        public boolean isUpdatedSystemApp() {
+            return applicationInfo.isUpdatedSystemApp();
+        }
+
+        /** @hide */
+        public boolean canHaveOatDir() {
+            // Nobody should be calling this method ever, but we can't rely on this.
+            // Thus no logic here and a reasonable return value.
+            return true;
+        }
+
+        public boolean isMatch(int flags) {
+            if ((flags & PackageManager.MATCH_SYSTEM_ONLY) != 0) {
+                return isSystem();
+            }
+            return true;
+        }
+
+        public long getLatestPackageUseTimeInMills() {
+            long latestUse = 0L;
+            for (long use : mLastPackageUsageTimeInMills) {
+                latestUse = Math.max(latestUse, use);
+            }
+            return latestUse;
+        }
+
+        public long getLatestForegroundPackageUseTimeInMills() {
+            int[] foregroundReasons = {
+                PackageManager.NOTIFY_PACKAGE_USE_ACTIVITY,
+                PackageManager.NOTIFY_PACKAGE_USE_FOREGROUND_SERVICE
+            };
+
+            long latestUse = 0L;
+            for (int reason : foregroundReasons) {
+                latestUse = Math.max(latestUse, mLastPackageUsageTimeInMills[reason]);
+            }
+            return latestUse;
+        }
+
+        public String toString() {
+            return "Package{"
+                + Integer.toHexString(System.identityHashCode(this))
+                + " " + packageName + "}";
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        public Package(Parcel dest) {
+            // We use the boot classloader for all classes that we load.
+            final ClassLoader boot = Object.class.getClassLoader();
+
+            packageName = dest.readString().intern();
+            manifestPackageName = dest.readString();
+            splitNames = dest.readStringArray();
+            volumeUuid = dest.readString();
+            codePath = dest.readString();
+            baseCodePath = dest.readString();
+            splitCodePaths = dest.readStringArray();
+            baseRevisionCode = dest.readInt();
+            splitRevisionCodes = dest.createIntArray();
+            splitFlags = dest.createIntArray();
+            splitPrivateFlags = dest.createIntArray();
+            baseHardwareAccelerated = (dest.readInt() == 1);
+            applicationInfo = dest.readParcelable(boot);
+            if (applicationInfo.permission != null) {
+                applicationInfo.permission = applicationInfo.permission.intern();
+            }
+
+            // We don't serialize the "owner" package and the application info object for each of
+            // these components, in order to save space and to avoid circular dependencies while
+            // serialization. We need to fix them all up here.
+            dest.readParcelableList(permissions, boot);
+            fixupOwner(permissions);
+            dest.readParcelableList(permissionGroups, boot);
+            fixupOwner(permissionGroups);
+            dest.readParcelableList(activities, boot);
+            fixupOwner(activities);
+            dest.readParcelableList(receivers, boot);
+            fixupOwner(receivers);
+            dest.readParcelableList(providers, boot);
+            fixupOwner(providers);
+            dest.readParcelableList(services, boot);
+            fixupOwner(services);
+            dest.readParcelableList(instrumentation, boot);
+            fixupOwner(instrumentation);
+
+            dest.readStringList(requestedPermissions);
+            internStringArrayList(requestedPermissions);
+            dest.readStringList(implicitPermissions);
+            internStringArrayList(implicitPermissions);
+            protectedBroadcasts = dest.createStringArrayList();
+            internStringArrayList(protectedBroadcasts);
+
+            parentPackage = dest.readParcelable(boot);
+
+            childPackages = new ArrayList<>();
+            dest.readParcelableList(childPackages, boot);
+            if (childPackages.size() == 0) {
+                childPackages = null;
+            }
+
+            staticSharedLibName = dest.readString();
+            if (staticSharedLibName != null) {
+                staticSharedLibName = staticSharedLibName.intern();
+            }
+            staticSharedLibVersion = dest.readLong();
+            libraryNames = dest.createStringArrayList();
+            internStringArrayList(libraryNames);
+            usesLibraries = dest.createStringArrayList();
+            internStringArrayList(usesLibraries);
+            usesOptionalLibraries = dest.createStringArrayList();
+            internStringArrayList(usesOptionalLibraries);
+            usesLibraryFiles = dest.readStringArray();
+
+            usesLibraryInfos = dest.createTypedArrayList(SharedLibraryInfo.CREATOR);
+
+            final int libCount = dest.readInt();
+            if (libCount > 0) {
+                usesStaticLibraries = new ArrayList<>(libCount);
+                dest.readStringList(usesStaticLibraries);
+                internStringArrayList(usesStaticLibraries);
+                usesStaticLibrariesVersions = new long[libCount];
+                dest.readLongArray(usesStaticLibrariesVersions);
+                usesStaticLibrariesCertDigests = new String[libCount][];
+                for (int i = 0; i < libCount; i++) {
+                    usesStaticLibrariesCertDigests[i] = dest.createStringArray();
+                }
+            }
+
+            preferredActivityFilters = new ArrayList<>();
+            dest.readParcelableList(preferredActivityFilters, boot);
+            if (preferredActivityFilters.size() == 0) {
+                preferredActivityFilters = null;
+            }
+
+            mOriginalPackages = dest.createStringArrayList();
+            mRealPackage = dest.readString();
+            mAdoptPermissions = dest.createStringArrayList();
+            mAppMetaData = dest.readBundle();
+            mVersionCode = dest.readInt();
+            mVersionCodeMajor = dest.readInt();
+            mVersionName = dest.readString();
+            if (mVersionName != null) {
+                mVersionName = mVersionName.intern();
+            }
+            mSharedUserId = dest.readString();
+            if (mSharedUserId != null) {
+                mSharedUserId = mSharedUserId.intern();
+            }
+            mSharedUserLabel = dest.readInt();
+
+            mSigningDetails = dest.readParcelable(boot);
+
+            mPreferredOrder = dest.readInt();
+
+            // long[] packageUsageTimeMillis is not persisted because it isn't information that
+            // is parsed from the APK.
+
+            // Object mExtras is not persisted because it is not information that is read from
+            // the APK, rather, it is supplied by callers.
+
+
+            configPreferences = new ArrayList<>();
+            dest.readParcelableList(configPreferences, boot);
+            if (configPreferences.size() == 0) {
+                configPreferences = null;
+            }
+
+            reqFeatures = new ArrayList<>();
+            dest.readParcelableList(reqFeatures, boot);
+            if (reqFeatures.size() == 0) {
+                reqFeatures = null;
+            }
+
+            featureGroups = new ArrayList<>();
+            dest.readParcelableList(featureGroups, boot);
+            if (featureGroups.size() == 0) {
+                featureGroups = null;
+            }
+
+            installLocation = dest.readInt();
+            coreApp = (dest.readInt() == 1);
+            mRequiredForAllUsers = (dest.readInt() == 1);
+            mRestrictedAccountType = dest.readString();
+            mRequiredAccountType = dest.readString();
+            mOverlayTarget = dest.readString();
+            mOverlayTargetName = dest.readString();
+            mOverlayCategory = dest.readString();
+            mOverlayPriority = dest.readInt();
+            mOverlayIsStatic = (dest.readInt() == 1);
+            mCompileSdkVersion = dest.readInt();
+            mCompileSdkVersionCodename = dest.readString();
+            mUpgradeKeySets = (ArraySet<String>) dest.readArraySet(boot);
+
+            mKeySetMapping = readKeySetMapping(dest);
+
+            cpuAbiOverride = dest.readString();
+            use32bitAbi = (dest.readInt() == 1);
+            restrictUpdateHash = dest.createByteArray();
+            visibleToInstantApps = dest.readInt() == 1;
+        }
+
+        private static void internStringArrayList(List<String> list) {
+            if (list != null) {
+                final int N = list.size();
+                for (int i = 0; i < N; ++i) {
+                    list.set(i, list.get(i).intern());
+                }
+            }
+        }
+
+        /**
+         * Sets the package owner and the the {@code applicationInfo} for every component
+         * owner by this package.
+         */
+        public void fixupOwner(List<? extends Component<?>> list) {
+            if (list != null) {
+                for (Component<?> c : list) {
+                    c.owner = this;
+                    if (c instanceof Activity) {
+                        ((Activity) c).info.applicationInfo = this.applicationInfo;
+                    } else if (c instanceof Service) {
+                        ((Service) c).info.applicationInfo = this.applicationInfo;
+                    } else if (c instanceof Provider) {
+                        ((Provider) c).info.applicationInfo = this.applicationInfo;
+                    }
+                }
+            }
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeString(packageName);
+            dest.writeString(manifestPackageName);
+            dest.writeStringArray(splitNames);
+            dest.writeString(volumeUuid);
+            dest.writeString(codePath);
+            dest.writeString(baseCodePath);
+            dest.writeStringArray(splitCodePaths);
+            dest.writeInt(baseRevisionCode);
+            dest.writeIntArray(splitRevisionCodes);
+            dest.writeIntArray(splitFlags);
+            dest.writeIntArray(splitPrivateFlags);
+            dest.writeInt(baseHardwareAccelerated ? 1 : 0);
+            dest.writeParcelable(applicationInfo, flags);
+
+            dest.writeParcelableList(permissions, flags);
+            dest.writeParcelableList(permissionGroups, flags);
+            dest.writeParcelableList(activities, flags);
+            dest.writeParcelableList(receivers, flags);
+            dest.writeParcelableList(providers, flags);
+            dest.writeParcelableList(services, flags);
+            dest.writeParcelableList(instrumentation, flags);
+
+            dest.writeStringList(requestedPermissions);
+            dest.writeStringList(implicitPermissions);
+            dest.writeStringList(protectedBroadcasts);
+
+            // TODO: This doesn't work: b/64295061
+            dest.writeParcelable(parentPackage, flags);
+            dest.writeParcelableList(childPackages, flags);
+
+            dest.writeString(staticSharedLibName);
+            dest.writeLong(staticSharedLibVersion);
+            dest.writeStringList(libraryNames);
+            dest.writeStringList(usesLibraries);
+            dest.writeStringList(usesOptionalLibraries);
+            dest.writeStringArray(usesLibraryFiles);
+            dest.writeTypedList(usesLibraryInfos);
+
+            if (ArrayUtils.isEmpty(usesStaticLibraries)) {
+                dest.writeInt(-1);
+            } else {
+                dest.writeInt(usesStaticLibraries.size());
+                dest.writeStringList(usesStaticLibraries);
+                dest.writeLongArray(usesStaticLibrariesVersions);
+                for (String[] usesStaticLibrariesCertDigest : usesStaticLibrariesCertDigests) {
+                    dest.writeStringArray(usesStaticLibrariesCertDigest);
+                }
+            }
+
+            dest.writeParcelableList(preferredActivityFilters, flags);
+
+            dest.writeStringList(mOriginalPackages);
+            dest.writeString(mRealPackage);
+            dest.writeStringList(mAdoptPermissions);
+            dest.writeBundle(mAppMetaData);
+            dest.writeInt(mVersionCode);
+            dest.writeInt(mVersionCodeMajor);
+            dest.writeString(mVersionName);
+            dest.writeString(mSharedUserId);
+            dest.writeInt(mSharedUserLabel);
+
+            dest.writeParcelable(mSigningDetails, flags);
+
+            dest.writeInt(mPreferredOrder);
+
+            // long[] packageUsageTimeMillis is not persisted because it isn't information that
+            // is parsed from the APK.
+
+            // Object mExtras is not persisted because it is not information that is read from
+            // the APK, rather, it is supplied by callers.
+
+            dest.writeParcelableList(configPreferences, flags);
+            dest.writeParcelableList(reqFeatures, flags);
+            dest.writeParcelableList(featureGroups, flags);
+
+            dest.writeInt(installLocation);
+            dest.writeInt(coreApp ? 1 : 0);
+            dest.writeInt(mRequiredForAllUsers ? 1 : 0);
+            dest.writeString(mRestrictedAccountType);
+            dest.writeString(mRequiredAccountType);
+            dest.writeString(mOverlayTarget);
+            dest.writeString(mOverlayTargetName);
+            dest.writeString(mOverlayCategory);
+            dest.writeInt(mOverlayPriority);
+            dest.writeInt(mOverlayIsStatic ? 1 : 0);
+            dest.writeInt(mCompileSdkVersion);
+            dest.writeString(mCompileSdkVersionCodename);
+            dest.writeArraySet(mUpgradeKeySets);
+            writeKeySetMapping(dest, mKeySetMapping);
+            dest.writeString(cpuAbiOverride);
+            dest.writeInt(use32bitAbi ? 1 : 0);
+            dest.writeByteArray(restrictUpdateHash);
+            dest.writeInt(visibleToInstantApps ? 1 : 0);
+        }
+
+        /**
+         * Writes the keyset mapping to the provided package. {@code null} mappings are permitted.
+         */
+        private static void writeKeySetMapping(
+                Parcel dest, ArrayMap<String, ArraySet<PublicKey>> keySetMapping) {
+            if (keySetMapping == null) {
+                dest.writeInt(-1);
+                return;
+            }
+
+            final int N = keySetMapping.size();
+            dest.writeInt(N);
+
+            for (int i = 0; i < N; i++) {
+                dest.writeString(keySetMapping.keyAt(i));
+                ArraySet<PublicKey> keys = keySetMapping.valueAt(i);
+                if (keys == null) {
+                    dest.writeInt(-1);
+                    continue;
+                }
+
+                final int M = keys.size();
+                dest.writeInt(M);
+                for (int j = 0; j < M; j++) {
+                    dest.writeSerializable(keys.valueAt(j));
+                }
+            }
+        }
+
+        /**
+         * Reads a keyset mapping from the given parcel at the given data position. May return
+         * {@code null} if the serialized mapping was {@code null}.
+         */
+        private static ArrayMap<String, ArraySet<PublicKey>> readKeySetMapping(Parcel in) {
+            final int N = in.readInt();
+            if (N == -1) {
+                return null;
+            }
+
+            ArrayMap<String, ArraySet<PublicKey>> keySetMapping = new ArrayMap<>();
+            for (int i = 0; i < N; ++i) {
+                String key = in.readString();
+                final int M = in.readInt();
+                if (M == -1) {
+                    keySetMapping.put(key, null);
+                    continue;
+                }
+
+                ArraySet<PublicKey> keys = new ArraySet<>(M);
+                for (int j = 0; j < M; ++j) {
+                    PublicKey pk = (PublicKey) in.readSerializable();
+                    keys.add(pk);
+                }
+
+                keySetMapping.put(key, keys);
+            }
+
+            return keySetMapping;
+        }
+
+        public static final Parcelable.Creator CREATOR = new Parcelable.Creator<Package>() {
+            public Package createFromParcel(Parcel in) {
+                return new Package(in);
+            }
+
+            public Package[] newArray(int size) {
+                return new Package[size];
+            }
+        };
+    }
+
+    public static abstract class Component<II extends IntentInfo> {
+        @UnsupportedAppUsage
+        public final ArrayList<II> intents;
+        @UnsupportedAppUsage
+        public final String className;
+
+        @UnsupportedAppUsage
+        public Bundle metaData;
+        @UnsupportedAppUsage
+        public Package owner;
+        /** The order of this component in relation to its peers */
+        public int order;
+
+        ComponentName componentName;
+        String componentShortName;
+
+        public Component(Package owner, ArrayList<II> intents, String className) {
+            this.owner = owner;
+            this.intents = intents;
+            this.className = className;
+        }
+
+        public Component(Package owner) {
+            this.owner = owner;
+            this.intents = null;
+            this.className = null;
+        }
+
+        public Component(final ParsePackageItemArgs args, final PackageItemInfo outInfo) {
+            owner = args.owner;
+            intents = new ArrayList<II>(0);
+            if (parsePackageItemInfo(args.owner, outInfo, args.outError, args.tag, args.sa,
+                    true /*nameRequired*/, args.nameRes, args.labelRes, args.iconRes,
+                    args.roundIconRes, args.logoRes, args.bannerRes)) {
+                className = outInfo.name;
+            } else {
+                className = null;
+            }
+        }
+
+        public Component(final ParseComponentArgs args, final ComponentInfo outInfo) {
+            this(args, (PackageItemInfo)outInfo);
+            if (args.outError[0] != null) {
+                return;
+            }
+
+            if (args.processRes != 0) {
+                CharSequence pname;
+                if (owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.FROYO) {
+                    pname = args.sa.getNonConfigurationString(args.processRes,
+                            Configuration.NATIVE_CONFIG_VERSION);
+                } else {
+                    // Some older apps have been seen to use a resource reference
+                    // here that on older builds was ignored (with a warning).  We
+                    // need to continue to do this for them so they don't break.
+                    pname = args.sa.getNonResourceString(args.processRes);
+                }
+                outInfo.processName = buildProcessName(owner.applicationInfo.packageName,
+                        owner.applicationInfo.processName, pname,
+                        args.flags, args.sepProcesses, args.outError);
+            }
+
+            if (args.descriptionRes != 0) {
+                outInfo.descriptionRes = args.sa.getResourceId(args.descriptionRes, 0);
+            }
+
+            outInfo.enabled = args.sa.getBoolean(args.enabledRes, true);
+        }
+
+        public Component(Component<II> clone) {
+            owner = clone.owner;
+            intents = clone.intents;
+            className = clone.className;
+            componentName = clone.componentName;
+            componentShortName = clone.componentShortName;
+        }
+
+        @UnsupportedAppUsage
+        public ComponentName getComponentName() {
+            if (componentName != null) {
+                return componentName;
+            }
+            if (className != null) {
+                componentName = new ComponentName(owner.applicationInfo.packageName,
+                        className);
+            }
+            return componentName;
+        }
+
+        protected Component(Parcel in) {
+            className = in.readString();
+            metaData = in.readBundle();
+            intents = createIntentsList(in);
+
+            owner = null;
+        }
+
+        protected void writeToParcel(Parcel dest, int flags) {
+            dest.writeString(className);
+            dest.writeBundle(metaData);
+
+            writeIntentsList(intents, dest, flags);
+        }
+
+        /**
+         * <p>
+         * Implementation note: The serialized form for the intent list also contains the name
+         * of the concrete class that's stored in the list, and assumes that every element of the
+         * list is of the same type. This is very similar to the original parcelable mechanism.
+         * We cannot use that directly because IntentInfo extends IntentFilter, which is parcelable
+         * and is public API. It also declares Parcelable related methods as final which means
+         * we can't extend them. The approach of using composition instead of inheritance leads to
+         * a large set of cascading changes in the PackageManagerService, which seem undesirable.
+         *
+         * <p>
+         * <b>WARNING: </b> The list of objects returned by this function might need to be fixed up
+         * to make sure their owner fields are consistent. See {@code fixupOwner}.
+         */
+        private static void writeIntentsList(ArrayList<? extends IntentInfo> list, Parcel out,
+                                             int flags) {
+            if (list == null) {
+                out.writeInt(-1);
+                return;
+            }
+
+            final int N = list.size();
+            out.writeInt(N);
+
+            // Don't bother writing the component name if the list is empty.
+            if (N > 0) {
+                IntentInfo info = list.get(0);
+                out.writeString(info.getClass().getName());
+
+                for (int i = 0; i < N;i++) {
+                    list.get(i).writeIntentInfoToParcel(out, flags);
+                }
+            }
+        }
+
+        private static <T extends IntentInfo> ArrayList<T> createIntentsList(Parcel in) {
+            int N = in.readInt();
+            if (N == -1) {
+                return null;
+            }
+
+            if (N == 0) {
+                return new ArrayList<>(0);
+            }
+
+            String componentName = in.readString();
+            final ArrayList<T> intentsList;
+            try {
+                final Class<T> cls = (Class<T>) Class.forName(componentName);
+                final Constructor<T> cons = cls.getConstructor(Parcel.class);
+
+                intentsList = new ArrayList<>(N);
+                for (int i = 0; i < N; ++i) {
+                    intentsList.add(cons.newInstance(in));
+                }
+            } catch (ReflectiveOperationException ree) {
+                throw new AssertionError("Unable to construct intent list for: " + componentName);
+            }
+
+            return intentsList;
+        }
+
+        public void appendComponentShortName(StringBuilder sb) {
+            ComponentName.appendShortString(sb, owner.applicationInfo.packageName, className);
+        }
+
+        public void printComponentShortName(PrintWriter pw) {
+            ComponentName.printShortString(pw, owner.applicationInfo.packageName, className);
+        }
+
+        public void setPackageName(String packageName) {
+            componentName = null;
+            componentShortName = null;
+        }
+    }
+
+    public final static class Permission extends Component<IntentInfo> implements Parcelable {
+        @UnsupportedAppUsage
+        public final PermissionInfo info;
+        @UnsupportedAppUsage
+        public boolean tree;
+        @UnsupportedAppUsage
+        public PermissionGroup group;
+
+        /**
+         * @hide
+         */
+        public Permission(Package owner, @Nullable String backgroundPermission) {
+            super(owner);
+            info = new PermissionInfo(backgroundPermission);
+        }
+
+        @UnsupportedAppUsage
+        public Permission(Package _owner, PermissionInfo _info) {
+            super(_owner);
+            info = _info;
+        }
+
+        public void setPackageName(String packageName) {
+            super.setPackageName(packageName);
+            info.packageName = packageName;
+        }
+
+        public String toString() {
+            return "Permission{"
+                + Integer.toHexString(System.identityHashCode(this))
+                + " " + info.name + "}";
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            super.writeToParcel(dest, flags);
+            dest.writeParcelable(info, flags);
+            dest.writeInt(tree ? 1 : 0);
+            dest.writeParcelable(group, flags);
+        }
+
+        /** @hide */
+        public boolean isAppOp() {
+            return info.isAppOp();
+        }
+
+        private Permission(Parcel in) {
+            super(in);
+            final ClassLoader boot = Object.class.getClassLoader();
+            info = in.readParcelable(boot);
+            if (info.group != null) {
+                info.group = info.group.intern();
+            }
+
+            tree = (in.readInt() == 1);
+            group = in.readParcelable(boot);
+        }
+
+        public static final Parcelable.Creator CREATOR = new Parcelable.Creator<Permission>() {
+            public Permission createFromParcel(Parcel in) {
+                return new Permission(in);
+            }
+
+            public Permission[] newArray(int size) {
+                return new Permission[size];
+            }
+        };
+    }
+
+    public final static class PermissionGroup extends Component<IntentInfo> implements Parcelable {
+        @UnsupportedAppUsage
+        public final PermissionGroupInfo info;
+
+        public PermissionGroup(Package owner, @StringRes int requestDetailResourceId,
+                @StringRes int backgroundRequestResourceId,
+                @StringRes int backgroundRequestDetailResourceId) {
+            super(owner);
+            info = new PermissionGroupInfo(requestDetailResourceId, backgroundRequestResourceId,
+                    backgroundRequestDetailResourceId);
+        }
+
+        public PermissionGroup(Package _owner, PermissionGroupInfo _info) {
+            super(_owner);
+            info = _info;
+        }
+
+        public void setPackageName(String packageName) {
+            super.setPackageName(packageName);
+            info.packageName = packageName;
+        }
+
+        public String toString() {
+            return "PermissionGroup{"
+                + Integer.toHexString(System.identityHashCode(this))
+                + " " + info.name + "}";
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            super.writeToParcel(dest, flags);
+            dest.writeParcelable(info, flags);
+        }
+
+        private PermissionGroup(Parcel in) {
+            super(in);
+            info = in.readParcelable(Object.class.getClassLoader());
+        }
+
+        public static final Parcelable.Creator CREATOR = new Parcelable.Creator<PermissionGroup>() {
+            public PermissionGroup createFromParcel(Parcel in) {
+                return new PermissionGroup(in);
+            }
+
+            public PermissionGroup[] newArray(int size) {
+                return new PermissionGroup[size];
+            }
+        };
+    }
+
+    private static boolean copyNeeded(int flags, Package p,
+            PackageUserState state, Bundle metaData, int userId) {
+        if (userId != UserHandle.USER_SYSTEM) {
+            // We always need to copy for other users, since we need
+            // to fix up the uid.
+            return true;
+        }
+        if (state.enabled != PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
+            boolean enabled = state.enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+            if (p.applicationInfo.enabled != enabled) {
+                return true;
+            }
+        }
+        boolean suspended = (p.applicationInfo.flags & FLAG_SUSPENDED) != 0;
+        if (state.suspended != suspended) {
+            return true;
+        }
+        if (!state.installed || state.hidden) {
+            return true;
+        }
+        if (state.stopped) {
+            return true;
+        }
+        if (state.instantApp != p.applicationInfo.isInstantApp()) {
+            return true;
+        }
+        if ((flags & PackageManager.GET_META_DATA) != 0
+                && (metaData != null || p.mAppMetaData != null)) {
+            return true;
+        }
+        if ((flags & PackageManager.GET_SHARED_LIBRARY_FILES) != 0
+                && p.usesLibraryFiles != null) {
+            return true;
+        }
+        if ((flags & PackageManager.GET_SHARED_LIBRARY_FILES) != 0
+                && p.usesLibraryInfos != null) {
+            return true;
+        }
+        if (p.staticSharedLibName != null) {
+            return true;
+        }
+        return false;
+    }
+
+    @UnsupportedAppUsage
+    public static ApplicationInfo generateApplicationInfo(Package p, int flags,
+            PackageUserState state) {
+        return generateApplicationInfo(p, flags, state, UserHandle.getCallingUserId());
+    }
+
+    private static void updateApplicationInfo(ApplicationInfo ai, int flags,
+            PackageUserState state) {
+        // CompatibilityMode is global state.
+        if (!sCompatibilityModeEnabled) {
+            ai.disableCompatibilityMode();
+        }
+        if (state.installed) {
+            ai.flags |= ApplicationInfo.FLAG_INSTALLED;
+        } else {
+            ai.flags &= ~ApplicationInfo.FLAG_INSTALLED;
+        }
+        if (state.suspended) {
+            ai.flags |= ApplicationInfo.FLAG_SUSPENDED;
+        } else {
+            ai.flags &= ~ApplicationInfo.FLAG_SUSPENDED;
+        }
+        if (state.instantApp) {
+            ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_INSTANT;
+        } else {
+            ai.privateFlags &= ~ApplicationInfo.PRIVATE_FLAG_INSTANT;
+        }
+        if (state.virtualPreload) {
+            ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_VIRTUAL_PRELOAD;
+        } else {
+            ai.privateFlags &= ~ApplicationInfo.PRIVATE_FLAG_VIRTUAL_PRELOAD;
+        }
+        if (state.hidden) {
+            ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_HIDDEN;
+        } else {
+            ai.privateFlags &= ~ApplicationInfo.PRIVATE_FLAG_HIDDEN;
+        }
+        if (state.enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
+            ai.enabled = true;
+        } else if (state.enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
+            ai.enabled = (flags&PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS) != 0;
+        } else if (state.enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED
+                || state.enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
+            ai.enabled = false;
+        }
+        ai.enabledSetting = state.enabled;
+        if (ai.category == ApplicationInfo.CATEGORY_UNDEFINED) {
+            ai.category = state.categoryHint;
+        }
+        if (ai.category == ApplicationInfo.CATEGORY_UNDEFINED) {
+            ai.category = FallbackCategoryProvider.getFallbackCategory(ai.packageName);
+        }
+        ai.seInfoUser = SELinuxUtil.assignSeinfoUser(state);
+        ai.resourceDirs = state.getAllOverlayPaths();
+        ai.icon = (sUseRoundIcon && ai.roundIconRes != 0) ? ai.roundIconRes : ai.iconRes;
+    }
+
+    @UnsupportedAppUsage
+    public static ApplicationInfo generateApplicationInfo(Package p, int flags,
+            PackageUserState state, int userId) {
+        if (p == null) return null;
+        if (!checkUseInstalledOrHidden(flags, state, p.applicationInfo) || !p.isMatch(flags)) {
+            return null;
+        }
+        if (!copyNeeded(flags, p, state, null, userId)
+                && ((flags&PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS) == 0
+                        || state.enabled != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED)) {
+            // In this case it is safe to directly modify the internal ApplicationInfo state:
+            // - CompatibilityMode is global state, so will be the same for every call.
+            // - We only come in to here if the app should reported as installed; this is the
+            // default state, and we will do a copy otherwise.
+            // - The enable state will always be reported the same for the application across
+            // calls; the only exception is for the UNTIL_USED mode, and in that case we will
+            // be doing a copy.
+            updateApplicationInfo(p.applicationInfo, flags, state);
+            return p.applicationInfo;
+        }
+
+        // Make shallow copy so we can store the metadata/libraries safely
+        ApplicationInfo ai = new ApplicationInfo(p.applicationInfo);
+        ai.initForUser(userId);
+        if ((flags & PackageManager.GET_META_DATA) != 0) {
+            ai.metaData = p.mAppMetaData;
+        }
+        if ((flags & PackageManager.GET_SHARED_LIBRARY_FILES) != 0) {
+            ai.sharedLibraryFiles = p.usesLibraryFiles;
+            ai.sharedLibraryInfos = p.usesLibraryInfos;
+        }
+        if (state.stopped) {
+            ai.flags |= ApplicationInfo.FLAG_STOPPED;
+        } else {
+            ai.flags &= ~ApplicationInfo.FLAG_STOPPED;
+        }
+        updateApplicationInfo(ai, flags, state);
+        return ai;
+    }
+
+    public static ApplicationInfo generateApplicationInfo(ApplicationInfo ai, int flags,
+            PackageUserState state, int userId) {
+        if (ai == null) return null;
+        if (!checkUseInstalledOrHidden(flags, state, ai)) {
+            return null;
+        }
+        // This is only used to return the ResolverActivity; we will just always
+        // make a copy.
+        ai = new ApplicationInfo(ai);
+        ai.initForUser(userId);
+        if (state.stopped) {
+            ai.flags |= ApplicationInfo.FLAG_STOPPED;
+        } else {
+            ai.flags &= ~ApplicationInfo.FLAG_STOPPED;
+        }
+        updateApplicationInfo(ai, flags, state);
+        return ai;
+    }
+
+    @UnsupportedAppUsage
+    public static final PermissionInfo generatePermissionInfo(
+            Permission p, int flags) {
+        if (p == null) return null;
+        if ((flags&PackageManager.GET_META_DATA) == 0) {
+            return p.info;
+        }
+        PermissionInfo pi = new PermissionInfo(p.info);
+        pi.metaData = p.metaData;
+        return pi;
+    }
+
+    @UnsupportedAppUsage
+    public static final PermissionGroupInfo generatePermissionGroupInfo(
+            PermissionGroup pg, int flags) {
+        if (pg == null) return null;
+        if ((flags&PackageManager.GET_META_DATA) == 0) {
+            return pg.info;
+        }
+        PermissionGroupInfo pgi = new PermissionGroupInfo(pg.info);
+        pgi.metaData = pg.metaData;
+        return pgi;
+    }
+
+    public final static class Activity extends Component<ActivityIntentInfo> implements Parcelable {
+        @UnsupportedAppUsage
+        public final ActivityInfo info;
+        private boolean mHasMaxAspectRatio;
+        private boolean mHasMinAspectRatio;
+
+        private boolean hasMaxAspectRatio() {
+            return mHasMaxAspectRatio;
+        }
+
+        private boolean hasMinAspectRatio() {
+            return mHasMinAspectRatio;
+        }
+
+        // To construct custom activity which does not exist in manifest
+        Activity(final Package owner, final String className, final ActivityInfo info) {
+            super(owner, new ArrayList<>(0), className);
+            this.info = info;
+            this.info.applicationInfo = owner.applicationInfo;
+        }
+
+        public Activity(final ParseComponentArgs args, final ActivityInfo _info) {
+            super(args, _info);
+            info = _info;
+            info.applicationInfo = args.owner.applicationInfo;
+        }
+
+        public void setPackageName(String packageName) {
+            super.setPackageName(packageName);
+            info.packageName = packageName;
+        }
+
+
+        private void setMaxAspectRatio(float maxAspectRatio) {
+            if (info.resizeMode == RESIZE_MODE_RESIZEABLE
+                    || info.resizeMode == RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION) {
+                // Resizeable activities can be put in any aspect ratio.
+                return;
+            }
+
+            if (maxAspectRatio < 1.0f && maxAspectRatio != 0) {
+                // Ignore any value lesser than 1.0.
+                return;
+            }
+
+            info.maxAspectRatio = maxAspectRatio;
+            mHasMaxAspectRatio = true;
+        }
+
+        private void setMinAspectRatio(float minAspectRatio) {
+            if (info.resizeMode == RESIZE_MODE_RESIZEABLE
+                    || info.resizeMode == RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION) {
+                // Resizeable activities can be put in any aspect ratio.
+                return;
+            }
+
+            if (minAspectRatio < 1.0f && minAspectRatio != 0) {
+                // Ignore any value lesser than 1.0.
+                return;
+            }
+
+            info.minAspectRatio = minAspectRatio;
+            mHasMinAspectRatio = true;
+        }
+
+        public String toString() {
+            StringBuilder sb = new StringBuilder(128);
+            sb.append("Activity{");
+            sb.append(Integer.toHexString(System.identityHashCode(this)));
+            sb.append(' ');
+            appendComponentShortName(sb);
+            sb.append('}');
+            return sb.toString();
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            super.writeToParcel(dest, flags);
+            dest.writeParcelable(info, flags | Parcelable.PARCELABLE_ELIDE_DUPLICATES);
+            dest.writeBoolean(mHasMaxAspectRatio);
+            dest.writeBoolean(mHasMinAspectRatio);
+        }
+
+        private Activity(Parcel in) {
+            super(in);
+            info = in.readParcelable(Object.class.getClassLoader());
+            mHasMaxAspectRatio = in.readBoolean();
+            mHasMinAspectRatio = in.readBoolean();
+
+            for (ActivityIntentInfo aii : intents) {
+                aii.activity = this;
+                order = Math.max(aii.getOrder(), order);
+            }
+
+            if (info.permission != null) {
+                info.permission = info.permission.intern();
+            }
+        }
+
+        public static final Parcelable.Creator CREATOR = new Parcelable.Creator<Activity>() {
+            public Activity createFromParcel(Parcel in) {
+                return new Activity(in);
+            }
+
+            public Activity[] newArray(int size) {
+                return new Activity[size];
+            }
+        };
+    }
+
+    @UnsupportedAppUsage
+    public static final ActivityInfo generateActivityInfo(Activity a, int flags,
+            PackageUserState state, int userId) {
+        if (a == null) return null;
+        if (!checkUseInstalledOrHidden(flags, state, a.owner.applicationInfo)) {
+            return null;
+        }
+        if (!copyNeeded(flags, a.owner, state, a.metaData, userId)) {
+            updateApplicationInfo(a.info.applicationInfo, flags, state);
+            return a.info;
+        }
+        // Make shallow copies so we can store the metadata safely
+        ActivityInfo ai = new ActivityInfo(a.info);
+        ai.metaData = a.metaData;
+        ai.applicationInfo = generateApplicationInfo(a.owner, flags, state, userId);
+        return ai;
+    }
+
+    public static final ActivityInfo generateActivityInfo(ActivityInfo ai, int flags,
+            PackageUserState state, int userId) {
+        if (ai == null) return null;
+        if (!checkUseInstalledOrHidden(flags, state, ai.applicationInfo)) {
+            return null;
+        }
+        // This is only used to return the ResolverActivity; we will just always
+        // make a copy.
+        ai = new ActivityInfo(ai);
+        ai.applicationInfo = generateApplicationInfo(ai.applicationInfo, flags, state, userId);
+        return ai;
+    }
+
+    public final static class Service extends Component<ServiceIntentInfo> implements Parcelable {
+        @UnsupportedAppUsage
+        public final ServiceInfo info;
+
+        public Service(final ParseComponentArgs args, final ServiceInfo _info) {
+            super(args, _info);
+            info = _info;
+            info.applicationInfo = args.owner.applicationInfo;
+        }
+
+        public void setPackageName(String packageName) {
+            super.setPackageName(packageName);
+            info.packageName = packageName;
+        }
+
+        public String toString() {
+            StringBuilder sb = new StringBuilder(128);
+            sb.append("Service{");
+            sb.append(Integer.toHexString(System.identityHashCode(this)));
+            sb.append(' ');
+            appendComponentShortName(sb);
+            sb.append('}');
+            return sb.toString();
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            super.writeToParcel(dest, flags);
+            dest.writeParcelable(info, flags | Parcelable.PARCELABLE_ELIDE_DUPLICATES);
+        }
+
+        private Service(Parcel in) {
+            super(in);
+            info = in.readParcelable(Object.class.getClassLoader());
+
+            for (ServiceIntentInfo aii : intents) {
+                aii.service = this;
+                order = Math.max(aii.getOrder(), order);
+            }
+
+            if (info.permission != null) {
+                info.permission = info.permission.intern();
+            }
+        }
+
+        public static final Parcelable.Creator CREATOR = new Parcelable.Creator<Service>() {
+            public Service createFromParcel(Parcel in) {
+                return new Service(in);
+            }
+
+            public Service[] newArray(int size) {
+                return new Service[size];
+            }
+        };
+    }
+
+    @UnsupportedAppUsage
+    public static final ServiceInfo generateServiceInfo(Service s, int flags,
+            PackageUserState state, int userId) {
+        if (s == null) return null;
+        if (!checkUseInstalledOrHidden(flags, state, s.owner.applicationInfo)) {
+            return null;
+        }
+        if (!copyNeeded(flags, s.owner, state, s.metaData, userId)) {
+            updateApplicationInfo(s.info.applicationInfo, flags, state);
+            return s.info;
+        }
+        // Make shallow copies so we can store the metadata safely
+        ServiceInfo si = new ServiceInfo(s.info);
+        si.metaData = s.metaData;
+        si.applicationInfo = generateApplicationInfo(s.owner, flags, state, userId);
+        return si;
+    }
+
+    public final static class Provider extends Component<ProviderIntentInfo> implements Parcelable {
+        @UnsupportedAppUsage
+        public final ProviderInfo info;
+        @UnsupportedAppUsage
+        public boolean syncable;
+
+        public Provider(final ParseComponentArgs args, final ProviderInfo _info) {
+            super(args, _info);
+            info = _info;
+            info.applicationInfo = args.owner.applicationInfo;
+            syncable = false;
+        }
+
+        @UnsupportedAppUsage
+        public Provider(Provider existingProvider) {
+            super(existingProvider);
+            this.info = existingProvider.info;
+            this.syncable = existingProvider.syncable;
+        }
+
+        public void setPackageName(String packageName) {
+            super.setPackageName(packageName);
+            info.packageName = packageName;
+        }
+
+        public String toString() {
+            StringBuilder sb = new StringBuilder(128);
+            sb.append("Provider{");
+            sb.append(Integer.toHexString(System.identityHashCode(this)));
+            sb.append(' ');
+            appendComponentShortName(sb);
+            sb.append('}');
+            return sb.toString();
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            super.writeToParcel(dest, flags);
+            dest.writeParcelable(info, flags | Parcelable.PARCELABLE_ELIDE_DUPLICATES);
+            dest.writeInt((syncable) ? 1 : 0);
+        }
+
+        private Provider(Parcel in) {
+            super(in);
+            info = in.readParcelable(Object.class.getClassLoader());
+            syncable = (in.readInt() == 1);
+
+            for (ProviderIntentInfo aii : intents) {
+                aii.provider = this;
+            }
+
+            if (info.readPermission != null) {
+                info.readPermission = info.readPermission.intern();
+            }
+
+            if (info.writePermission != null) {
+                info.writePermission = info.writePermission.intern();
+            }
+
+            if (info.authority != null) {
+                info.authority = info.authority.intern();
+            }
+        }
+
+        public static final Parcelable.Creator CREATOR = new Parcelable.Creator<Provider>() {
+            public Provider createFromParcel(Parcel in) {
+                return new Provider(in);
+            }
+
+            public Provider[] newArray(int size) {
+                return new Provider[size];
+            }
+        };
+    }
+
+    @UnsupportedAppUsage
+    public static final ProviderInfo generateProviderInfo(Provider p, int flags,
+            PackageUserState state, int userId) {
+        if (p == null) return null;
+        if (!checkUseInstalledOrHidden(flags, state, p.owner.applicationInfo)) {
+            return null;
+        }
+        if (!copyNeeded(flags, p.owner, state, p.metaData, userId)
+                && ((flags & PackageManager.GET_URI_PERMISSION_PATTERNS) != 0
+                        || p.info.uriPermissionPatterns == null)) {
+            updateApplicationInfo(p.info.applicationInfo, flags, state);
+            return p.info;
+        }
+        // Make shallow copies so we can store the metadata safely
+        ProviderInfo pi = new ProviderInfo(p.info);
+        pi.metaData = p.metaData;
+        if ((flags & PackageManager.GET_URI_PERMISSION_PATTERNS) == 0) {
+            pi.uriPermissionPatterns = null;
+        }
+        pi.applicationInfo = generateApplicationInfo(p.owner, flags, state, userId);
+        return pi;
+    }
+
+    public final static class Instrumentation extends Component<IntentInfo> implements
+            Parcelable {
+        @UnsupportedAppUsage
+        public final InstrumentationInfo info;
+
+        public Instrumentation(final ParsePackageItemArgs args, final InstrumentationInfo _info) {
+            super(args, _info);
+            info = _info;
+        }
+
+        public void setPackageName(String packageName) {
+            super.setPackageName(packageName);
+            info.packageName = packageName;
+        }
+
+        public String toString() {
+            StringBuilder sb = new StringBuilder(128);
+            sb.append("Instrumentation{");
+            sb.append(Integer.toHexString(System.identityHashCode(this)));
+            sb.append(' ');
+            appendComponentShortName(sb);
+            sb.append('}');
+            return sb.toString();
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            super.writeToParcel(dest, flags);
+            dest.writeParcelable(info, flags);
+        }
+
+        private Instrumentation(Parcel in) {
+            super(in);
+            info = in.readParcelable(Object.class.getClassLoader());
+
+            if (info.targetPackage != null) {
+                info.targetPackage = info.targetPackage.intern();
+            }
+
+            if (info.targetProcesses != null) {
+                info.targetProcesses = info.targetProcesses.intern();
+            }
+        }
+
+        public static final Parcelable.Creator CREATOR = new Parcelable.Creator<Instrumentation>() {
+            public Instrumentation createFromParcel(Parcel in) {
+                return new Instrumentation(in);
+            }
+
+            public Instrumentation[] newArray(int size) {
+                return new Instrumentation[size];
+            }
+        };
+    }
+
+    @UnsupportedAppUsage
+    public static final InstrumentationInfo generateInstrumentationInfo(
+            Instrumentation i, int flags) {
+        if (i == null) return null;
+        if ((flags&PackageManager.GET_META_DATA) == 0) {
+            return i.info;
+        }
+        InstrumentationInfo ii = new InstrumentationInfo(i.info);
+        ii.metaData = i.metaData;
+        return ii;
+    }
+
+    public static abstract class IntentInfo extends IntentFilter {
+        @UnsupportedAppUsage
+        public boolean hasDefault;
+        @UnsupportedAppUsage
+        public int labelRes;
+        @UnsupportedAppUsage
+        public CharSequence nonLocalizedLabel;
+        @UnsupportedAppUsage
+        public int icon;
+        @UnsupportedAppUsage
+        public int logo;
+        @UnsupportedAppUsage
+        public int banner;
+        public int preferred;
+
+        @UnsupportedAppUsage
+        protected IntentInfo() {
+        }
+
+        protected IntentInfo(Parcel dest) {
+            super(dest);
+            hasDefault = (dest.readInt() == 1);
+            labelRes = dest.readInt();
+            nonLocalizedLabel = dest.readCharSequence();
+            icon = dest.readInt();
+            logo = dest.readInt();
+            banner = dest.readInt();
+            preferred = dest.readInt();
+        }
+
+
+        public void writeIntentInfoToParcel(Parcel dest, int flags) {
+            super.writeToParcel(dest, flags);
+            dest.writeInt(hasDefault ? 1 : 0);
+            dest.writeInt(labelRes);
+            dest.writeCharSequence(nonLocalizedLabel);
+            dest.writeInt(icon);
+            dest.writeInt(logo);
+            dest.writeInt(banner);
+            dest.writeInt(preferred);
+        }
+    }
+
+    public final static class ActivityIntentInfo extends IntentInfo {
+        @UnsupportedAppUsage
+        public Activity activity;
+
+        public ActivityIntentInfo(Activity _activity) {
+            activity = _activity;
+        }
+
+        public String toString() {
+            StringBuilder sb = new StringBuilder(128);
+            sb.append("ActivityIntentInfo{");
+            sb.append(Integer.toHexString(System.identityHashCode(this)));
+            sb.append(' ');
+            activity.appendComponentShortName(sb);
+            sb.append('}');
+            return sb.toString();
+        }
+
+        public ActivityIntentInfo(Parcel in) {
+            super(in);
+        }
+    }
+
+    public final static class ServiceIntentInfo extends IntentInfo {
+        @UnsupportedAppUsage
+        public Service service;
+
+        public ServiceIntentInfo(Service _service) {
+            service = _service;
+        }
+
+        public String toString() {
+            StringBuilder sb = new StringBuilder(128);
+            sb.append("ServiceIntentInfo{");
+            sb.append(Integer.toHexString(System.identityHashCode(this)));
+            sb.append(' ');
+            service.appendComponentShortName(sb);
+            sb.append('}');
+            return sb.toString();
+        }
+
+        public ServiceIntentInfo(Parcel in) {
+            super(in);
+        }
+    }
+
+    public static final class ProviderIntentInfo extends IntentInfo {
+        @UnsupportedAppUsage
+        public Provider provider;
+
+        public ProviderIntentInfo(Provider provider) {
+            this.provider = provider;
+        }
+
+        public String toString() {
+            StringBuilder sb = new StringBuilder(128);
+            sb.append("ProviderIntentInfo{");
+            sb.append(Integer.toHexString(System.identityHashCode(this)));
+            sb.append(' ');
+            provider.appendComponentShortName(sb);
+            sb.append('}');
+            return sb.toString();
+        }
+
+        public ProviderIntentInfo(Parcel in) {
+            super(in);
+        }
+    }
+
+    /**
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static void setCompatibilityModeEnabled(boolean compatibilityModeEnabled) {
+        sCompatibilityModeEnabled = compatibilityModeEnabled;
+    }
+
+    /**
+     * @hide
+     */
+    public static void readConfigUseRoundIcon(Resources r) {
+        if (r != null) {
+            sUseRoundIcon = r.getBoolean(com.android.internal.R.bool.config_useRoundIcon);
+            return;
+        }
+
+        ApplicationInfo androidAppInfo;
+        try {
+            androidAppInfo = ActivityThread.getPackageManager().getApplicationInfo(
+                    "android", 0 /* flags */,
+                UserHandle.myUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+        Resources systemResources = Resources.getSystem();
+
+        // Create in-flight as this overlayable resource is only used when config changes
+        Resources overlayableRes = ResourcesManager.getInstance().getResources(null,
+                null,
+                null,
+                androidAppInfo.resourceDirs,
+                androidAppInfo.sharedLibraryFiles,
+                Display.DEFAULT_DISPLAY,
+                null,
+                systemResources.getCompatibilityInfo(),
+                systemResources.getClassLoader(),
+                null);
+
+        sUseRoundIcon = overlayableRes.getBoolean(com.android.internal.R.bool.config_useRoundIcon);
+    }
+
+    public static class PackageParserException extends Exception {
+        public final int error;
+
+        public PackageParserException(int error, String detailMessage) {
+            super(detailMessage);
+            this.error = error;
+        }
+
+        public PackageParserException(int error, String detailMessage, Throwable throwable) {
+            super(detailMessage, throwable);
+            this.error = error;
+        }
+    }
+}
diff --git a/android/content/pm/PackageParserCacheHelper.java b/android/content/pm/PackageParserCacheHelper.java
new file mode 100644
index 0000000..8212224
--- /dev/null
+++ b/android/content/pm/PackageParserCacheHelper.java
@@ -0,0 +1,175 @@
+/*
+ * 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.content.pm;
+
+import android.os.Parcel;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * Helper classes to read from and write to Parcel with pooled strings.
+ *
+ * @hide
+ */
+public class PackageParserCacheHelper {
+    private PackageParserCacheHelper() {
+    }
+
+    private static final String TAG = "PackageParserCacheHelper";
+    private static final boolean DEBUG = false;
+
+    /**
+     * Parcel read helper with a string pool.
+     */
+    public static class ReadHelper extends Parcel.ReadWriteHelper {
+        private final ArrayList<String> mStrings = new ArrayList<>();
+
+        private final Parcel mParcel;
+
+        public ReadHelper(Parcel p) {
+            mParcel = p;
+        }
+
+        /**
+         * Prepare to read from a parcel, and install itself as a read-write helper.
+         *
+         * (We don't do it in the constructor to avoid calling methods before the constructor
+         * finishes.)
+         */
+        public void startAndInstall() {
+            mStrings.clear();
+
+            final int poolPosition = mParcel.readInt();
+            final int startPosition = mParcel.dataPosition();
+
+            // The pool is at the end of the parcel.
+            mParcel.setDataPosition(poolPosition);
+            mParcel.readStringList(mStrings);
+
+            // Then move back.
+            mParcel.setDataPosition(startPosition);
+
+            if (DEBUG) {
+                Log.i(TAG, "Read " + mStrings.size() + " strings");
+                for (int i = 0; i < mStrings.size(); i++) {
+                    Log.i(TAG, "  " + i + ": \"" + mStrings.get(i) + "\"");
+                }
+            }
+
+            mParcel.setReadWriteHelper(this);
+        }
+
+        /**
+         * Read an string index from a parcel, and returns the corresponding string from the pool.
+         */
+        public String readString(Parcel p) {
+            return mStrings.get(p.readInt());
+        }
+
+        @Override
+        public String readString8(Parcel p) {
+            return readString(p);
+        }
+
+        @Override
+        public String readString16(Parcel p) {
+            return readString(p);
+        }
+    }
+
+    /**
+     * Parcel write helper with a string pool.
+     */
+    public static class WriteHelper extends Parcel.ReadWriteHelper {
+        private final ArrayList<String> mStrings = new ArrayList<>();
+
+        private final HashMap<String, Integer> mIndexes = new HashMap<>();
+
+        private final Parcel mParcel;
+        private final int mStartPos;
+
+        /**
+         * Constructor.  Prepare a parcel, and install it self as a read-write helper.
+         */
+        public WriteHelper(Parcel p) {
+            mParcel = p;
+            mStartPos = p.dataPosition();
+            mParcel.writeInt(0); // We come back later here and write the pool position.
+
+            mParcel.setReadWriteHelper(this);
+        }
+
+        /**
+         * Instead of writing a string directly to a parcel, this method adds it to the pool,
+         * and write the index in the pool to the parcel.
+         */
+        public void writeString(Parcel p, String s) {
+            final Integer cur = mIndexes.get(s);
+            if (cur != null) {
+                // String already in the pool. Just write the index.
+                p.writeInt(cur); // Already in the pool.
+                if (DEBUG) {
+                    Log.i(TAG, "Duplicate '" + s + "' at " + cur);
+                }
+            } else {
+                // Not in the pool. Add to the pool, and write the index.
+                final int index = mStrings.size();
+                mIndexes.put(s, index);
+                mStrings.add(s);
+
+                if (DEBUG) {
+                    Log.i(TAG, "New '" + s + "' at " + index);
+                }
+
+                p.writeInt(index);
+            }
+        }
+
+        @Override
+        public void writeString8(Parcel p, String s) {
+            writeString(p, s);
+        }
+
+        @Override
+        public void writeString16(Parcel p, String s) {
+            writeString(p, s);
+        }
+
+        /**
+         * Closes a parcel by appending the string pool at the end and updating the pool offset,
+         * which it assumes is at the first byte.  It also uninstalls itself as a read-write helper.
+         */
+        public void finishAndUninstall() {
+            // Uninstall first, so that writeStringList() uses the native writeString.
+            mParcel.setReadWriteHelper(null);
+
+            final int poolPosition = mParcel.dataPosition();
+            mParcel.writeStringList(mStrings);
+
+            mParcel.setDataPosition(mStartPos);
+            mParcel.writeInt(poolPosition);
+
+            // Move back to the end.
+            mParcel.setDataPosition(mParcel.dataSize());
+            if (DEBUG) {
+                Log.i(TAG, "Wrote " + mStrings.size() + " strings");
+            }
+        }
+    }
+}
diff --git a/android/content/pm/PackagePartitions.java b/android/content/pm/PackagePartitions.java
new file mode 100644
index 0000000..9b8396e
--- /dev/null
+++ b/android/content/pm/PackagePartitions.java
@@ -0,0 +1,213 @@
+/*
+ * 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.content.pm;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Environment;
+import android.os.FileUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.function.Function;
+
+/**
+ * Exposes {@link #SYSTEM_PARTITIONS} which represents the partitions in which application packages
+ * can be installed. The partitions are ordered from most generic (lowest priority) to most specific
+ * (greatest priority).
+ *
+ * @hide
+ **/
+public class PackagePartitions {
+    public static final int PARTITION_SYSTEM = 0;
+    public static final int PARTITION_VENDOR = 1;
+    public static final int PARTITION_ODM = 2;
+    public static final int PARTITION_OEM = 3;
+    public static final int PARTITION_PRODUCT = 4;
+    public static final int PARTITION_SYSTEM_EXT = 5;
+
+    @IntDef(flag = true, prefix = { "PARTITION_" }, value = {
+        PARTITION_SYSTEM,
+        PARTITION_VENDOR,
+        PARTITION_ODM,
+        PARTITION_OEM,
+        PARTITION_PRODUCT,
+        PARTITION_SYSTEM_EXT
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface PartitionType {}
+
+    /**
+     * The list of all system partitions that may contain packages in ascending order of
+     * specificity (the more generic, the earlier in the list a partition appears).
+     */
+    private static final ArrayList<SystemPartition> SYSTEM_PARTITIONS =
+            new ArrayList<>(Arrays.asList(
+                    new SystemPartition(Environment.getRootDirectory(), PARTITION_SYSTEM,
+                            true /* containsPrivApp */, false /* containsOverlay */),
+                    new SystemPartition(Environment.getVendorDirectory(), PARTITION_VENDOR,
+                            true /* containsPrivApp */, true /* containsOverlay */),
+                    new SystemPartition(Environment.getOdmDirectory(), PARTITION_ODM,
+                            true /* containsPrivApp */, true /* containsOverlay */),
+                    new SystemPartition(Environment.getOemDirectory(), PARTITION_OEM,
+                            false /* containsPrivApp */, true /* containsOverlay */),
+                    new SystemPartition(Environment.getProductDirectory(), PARTITION_PRODUCT,
+                            true /* containsPrivApp */, true /* containsOverlay */),
+                    new SystemPartition(Environment.getSystemExtDirectory(), PARTITION_SYSTEM_EXT,
+                            true /* containsPrivApp */, true /* containsOverlay */)));
+
+    /**
+     * Returns a list in which the elements are products of the specified function applied to the
+     * list of {@link #SYSTEM_PARTITIONS} in increasing specificity order.
+     */
+    public static <T> ArrayList<T> getOrderedPartitions(
+            @NonNull Function<SystemPartition, T> producer) {
+        final ArrayList<T> out = new ArrayList<>();
+        for (int i = 0, n = SYSTEM_PARTITIONS.size(); i < n; i++) {
+            final T v = producer.apply(SYSTEM_PARTITIONS.get(i));
+            if (v != null)  {
+                out.add(v);
+            }
+        }
+        return out;
+    }
+
+    /** Represents a partition that contains application packages. */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    public static class SystemPartition {
+        @NonNull
+        public final File folder;
+
+        @PartitionType
+        public final int type;
+
+        @Nullable
+        private final DeferredCanonicalFile mAppFolder;
+
+        @Nullable
+        private final DeferredCanonicalFile mPrivAppFolder;
+
+        @Nullable
+        private final DeferredCanonicalFile mOverlayFolder;
+
+        private SystemPartition(@NonNull File folder, @PartitionType int type,
+                boolean containsPrivApp, boolean containsOverlay) {
+            this.folder = folder;
+            this.type = type;
+            this.mAppFolder = new DeferredCanonicalFile(folder, "app");
+            this.mPrivAppFolder = containsPrivApp ?
+                    new DeferredCanonicalFile(folder, "priv-app") : null;
+            this.mOverlayFolder = containsOverlay ?
+                    new DeferredCanonicalFile(folder, "overlay") : null;
+        }
+
+        public SystemPartition(@NonNull SystemPartition original) {
+            this.folder = original.folder;
+            this.type = original.type;
+            this.mAppFolder = original.mAppFolder;
+            this.mPrivAppFolder = original.mPrivAppFolder;
+            this.mOverlayFolder = original.mOverlayFolder;
+        }
+
+        /**
+         * Creates a partition containing the same folders as the original partition but with a
+         * different root folder.
+         */
+        public SystemPartition(@NonNull File rootFolder, @NonNull SystemPartition partition) {
+            this(rootFolder, partition.type, partition.mPrivAppFolder != null,
+                    partition.mOverlayFolder != null);
+        }
+
+        /** Returns the canonical app folder of the partition. */
+        @Nullable
+        public File getAppFolder() {
+            return mAppFolder == null ? null : mAppFolder.getFile();
+        }
+
+        /** Returns the canonical priv-app folder of the partition, if one exists. */
+        @Nullable
+        public File getPrivAppFolder() {
+            return mPrivAppFolder == null ? null : mPrivAppFolder.getFile();
+        }
+
+        /** Returns the canonical overlay folder of the partition, if one exists. */
+        @Nullable
+        public File getOverlayFolder() {
+            return mOverlayFolder == null ? null : mOverlayFolder.getFile();
+        }
+
+        /** Returns whether the partition contains the specified file in its priv-app folder. */
+        public boolean containsPrivApp(@NonNull File scanFile) {
+            return FileUtils.contains(mPrivAppFolder.getFile(), scanFile);
+        }
+
+        /** Returns whether the partition contains the specified file in its app folder. */
+        public boolean containsApp(@NonNull File scanFile) {
+            return FileUtils.contains(mAppFolder.getFile(), scanFile);
+        }
+
+        /** Returns whether the partition contains the specified file in its overlay folder. */
+        public boolean containsOverlay(@NonNull File scanFile) {
+            return FileUtils.contains(mOverlayFolder.getFile(), scanFile);
+        }
+
+        /** Returns whether the partition contains the specified file. */
+        public boolean containsPath(@NonNull String path) {
+            return path.startsWith(folder.getPath() + "/");
+        }
+
+        /** Returns whether the partition contains the specified file in its priv-app folder. */
+        public boolean containsPrivPath(@NonNull String path) {
+            return mPrivAppFolder != null
+                    && path.startsWith(mPrivAppFolder.getFile().getPath() + "/");
+        }
+    }
+
+    /**
+     * A class that defers the canonicalization of its underlying file. This must be done so
+     * processes do not attempt to canonicalize files in directories for which the process does not
+     * have the correct selinux policies.
+     */
+    private static class DeferredCanonicalFile {
+        private boolean mIsCanonical;
+        private File mFile;
+        private DeferredCanonicalFile(File dir, String fileName) {
+            mFile = new File(dir, fileName);
+            mIsCanonical = false;
+        }
+
+        private File getFile() {
+            if (mIsCanonical) {
+                return mFile;
+            }
+            mIsCanonical = true;
+            try {
+                mFile = mFile.getCanonicalFile();
+            } catch (IOException ignore) {
+                // failed to look up canonical path, continue with original one
+            }
+            return mFile;
+        }
+    }
+}
diff --git a/android/content/pm/PackageStats.java b/android/content/pm/PackageStats.java
new file mode 100644
index 0000000..7c12527
--- /dev/null
+++ b/android/content/pm/PackageStats.java
@@ -0,0 +1,214 @@
+/*
+ * 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.content.pm;
+
+import android.app.usage.StorageStatsManager;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.UserHandle;
+import android.text.TextUtils;
+
+import java.util.Objects;
+
+/**
+ * implementation of PackageStats associated with a application package.
+ *
+ * @deprecated this class is an orphan that could never be obtained from a valid
+ *             public API. If you need package storage statistics use the new
+ *             {@link StorageStatsManager} APIs.
+ */
+@Deprecated
+public class PackageStats implements Parcelable {
+    /** Name of the package to which this stats applies. */
+    public String packageName;
+
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    public int userHandle;
+
+    /** Size of the code (e.g., APK) */
+    public long codeSize;
+
+    /**
+     * Size of the internal data size for the application. (e.g.,
+     * /data/data/<app>)
+     */
+    public long dataSize;
+
+    /** Size of cache used by the application. (e.g., /data/data/<app>/cache) */
+    public long cacheSize;
+
+    /**
+     * Size of the secure container on external storage holding the
+     * application's code.
+     */
+    public long externalCodeSize;
+
+    /**
+     * Size of the external data used by the application (e.g.,
+     * <sdcard>/Android/data/<app>)
+     */
+    public long externalDataSize;
+
+    /**
+     * Size of the external cache used by the application (i.e., on the SD
+     * card). If this is a subdirectory of the data directory, this size will be
+     * subtracted out of the external data size.
+     */
+    public long externalCacheSize;
+
+    /** Size of the external media size used by the application. */
+    public long externalMediaSize;
+
+    /** Size of the package's OBBs placed on external media. */
+    public long externalObbSize;
+
+    public static final @android.annotation.NonNull Parcelable.Creator<PackageStats> CREATOR
+            = new Parcelable.Creator<PackageStats>() {
+        public PackageStats createFromParcel(Parcel in) {
+            return new PackageStats(in);
+        }
+
+        public PackageStats[] newArray(int size) {
+            return new PackageStats[size];
+        }
+    };
+
+    public String toString() {
+        final StringBuilder sb = new StringBuilder("PackageStats{");
+        sb.append(Integer.toHexString(System.identityHashCode(this)));
+        sb.append(" ");
+        sb.append(packageName);
+        if (codeSize != 0) {
+            sb.append(" code=");
+            sb.append(codeSize);
+        }
+        if (dataSize != 0) {
+            sb.append(" data=");
+            sb.append(dataSize);
+        }
+        if (cacheSize != 0) {
+            sb.append(" cache=");
+            sb.append(cacheSize);
+        }
+        if (externalCodeSize != 0) {
+            sb.append(" extCode=");
+            sb.append(externalCodeSize);
+        }
+        if (externalDataSize != 0) {
+            sb.append(" extData=");
+            sb.append(externalDataSize);
+        }
+        if (externalCacheSize != 0) {
+            sb.append(" extCache=");
+            sb.append(externalCacheSize);
+        }
+        if (externalMediaSize != 0) {
+            sb.append(" media=");
+            sb.append(externalMediaSize);
+        }
+        if (externalObbSize != 0) {
+            sb.append(" obb=");
+            sb.append(externalObbSize);
+        }
+        sb.append("}");
+        return sb.toString();
+    }
+
+    public PackageStats(String pkgName) {
+        packageName = pkgName;
+        userHandle = UserHandle.myUserId();
+    }
+
+    /** @hide */
+    public PackageStats(String pkgName, int userHandle) {
+        this.packageName = pkgName;
+        this.userHandle = userHandle;
+    }
+
+    public PackageStats(Parcel source) {
+        packageName = source.readString();
+        userHandle = source.readInt();
+        codeSize = source.readLong();
+        dataSize = source.readLong();
+        cacheSize = source.readLong();
+        externalCodeSize = source.readLong();
+        externalDataSize = source.readLong();
+        externalCacheSize = source.readLong();
+        externalMediaSize = source.readLong();
+        externalObbSize = source.readLong();
+    }
+
+    public PackageStats(PackageStats pStats) {
+        packageName = pStats.packageName;
+        userHandle = pStats.userHandle;
+        codeSize = pStats.codeSize;
+        dataSize = pStats.dataSize;
+        cacheSize = pStats.cacheSize;
+        externalCodeSize = pStats.externalCodeSize;
+        externalDataSize = pStats.externalDataSize;
+        externalCacheSize = pStats.externalCacheSize;
+        externalMediaSize = pStats.externalMediaSize;
+        externalObbSize = pStats.externalObbSize;
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel dest, int parcelableFlags){
+        dest.writeString(packageName);
+        dest.writeInt(userHandle);
+        dest.writeLong(codeSize);
+        dest.writeLong(dataSize);
+        dest.writeLong(cacheSize);
+        dest.writeLong(externalCodeSize);
+        dest.writeLong(externalDataSize);
+        dest.writeLong(externalCacheSize);
+        dest.writeLong(externalMediaSize);
+        dest.writeLong(externalObbSize);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof PackageStats)) {
+            return false;
+        }
+
+        final PackageStats otherStats = (PackageStats) obj;
+        return ((TextUtils.equals(packageName, otherStats.packageName))
+                && userHandle == otherStats.userHandle
+                && codeSize == otherStats.codeSize
+                && dataSize == otherStats.dataSize
+                && cacheSize == otherStats.cacheSize
+                && externalCodeSize == otherStats.externalCodeSize
+                && externalDataSize == otherStats.externalDataSize
+                && externalCacheSize == otherStats.externalCacheSize
+                && externalMediaSize == otherStats.externalMediaSize
+                && externalObbSize == otherStats.externalObbSize);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(packageName, userHandle, codeSize, dataSize,
+                cacheSize, externalCodeSize, externalDataSize, externalCacheSize, externalMediaSize,
+                externalObbSize);
+    }
+
+}
diff --git a/android/content/pm/PackageUserState.java b/android/content/pm/PackageUserState.java
new file mode 100644
index 0000000..327d1b8
--- /dev/null
+++ b/android/content/pm/PackageUserState.java
@@ -0,0 +1,636 @@
+/*
+ * 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.content.pm;
+
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
+import static android.content.pm.PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS;
+import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.ComponentName;
+import android.content.pm.parsing.component.ParsedMainComponent;
+import android.os.BaseBundle;
+import android.os.Debug;
+import android.os.PersistableBundle;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.DebugUtils;
+import android.util.Pair;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.CollectionUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * Per-user state information about a package.
+ * @hide
+ */
+public class PackageUserState {
+    private static final boolean DEBUG = false;
+    private static final String LOG_TAG = "PackageUserState";
+
+    public long ceDataInode;
+    public boolean installed;
+    public boolean stopped;
+    public boolean notLaunched;
+    public boolean hidden; // Is the app restricted by owner / admin
+    public int distractionFlags;
+    public boolean suspended;
+    public ArrayMap<String, SuspendParams> suspendParams; // Suspending package to suspend params
+    public boolean instantApp;
+    public boolean virtualPreload;
+    public int enabled;
+    public String lastDisableAppCaller;
+    public int domainVerificationStatus;
+    public int appLinkGeneration;
+    public int categoryHint = ApplicationInfo.CATEGORY_UNDEFINED;
+    public int installReason;
+    public @PackageManager.UninstallReason int uninstallReason;
+    public String harmfulAppWarning;
+
+    public ArraySet<String> disabledComponents;
+    public ArraySet<String> enabledComponents;
+
+    private String[] overlayPaths;
+    private ArrayMap<String, String[]> sharedLibraryOverlayPaths; // Lib name to overlay paths
+    private String[] cachedOverlayPaths;
+
+    @Nullable
+    private ArrayMap<ComponentName, Pair<String, Integer>> componentLabelIconOverrideMap;
+
+    @UnsupportedAppUsage
+    public PackageUserState() {
+        installed = true;
+        hidden = false;
+        suspended = false;
+        enabled = COMPONENT_ENABLED_STATE_DEFAULT;
+        domainVerificationStatus =
+                PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
+        installReason = PackageManager.INSTALL_REASON_UNKNOWN;
+        uninstallReason = PackageManager.UNINSTALL_REASON_UNKNOWN;
+    }
+
+    @VisibleForTesting
+    public PackageUserState(PackageUserState o) {
+        ceDataInode = o.ceDataInode;
+        installed = o.installed;
+        stopped = o.stopped;
+        notLaunched = o.notLaunched;
+        hidden = o.hidden;
+        distractionFlags = o.distractionFlags;
+        suspended = o.suspended;
+        suspendParams = new ArrayMap<>(o.suspendParams);
+        instantApp = o.instantApp;
+        virtualPreload = o.virtualPreload;
+        enabled = o.enabled;
+        lastDisableAppCaller = o.lastDisableAppCaller;
+        domainVerificationStatus = o.domainVerificationStatus;
+        appLinkGeneration = o.appLinkGeneration;
+        categoryHint = o.categoryHint;
+        installReason = o.installReason;
+        uninstallReason = o.uninstallReason;
+        disabledComponents = ArrayUtils.cloneOrNull(o.disabledComponents);
+        enabledComponents = ArrayUtils.cloneOrNull(o.enabledComponents);
+        overlayPaths =
+            o.overlayPaths == null ? null : Arrays.copyOf(o.overlayPaths, o.overlayPaths.length);
+        if (o.sharedLibraryOverlayPaths != null) {
+            sharedLibraryOverlayPaths = new ArrayMap<>(o.sharedLibraryOverlayPaths);
+        }
+        harmfulAppWarning = o.harmfulAppWarning;
+        if (o.componentLabelIconOverrideMap != null) {
+            this.componentLabelIconOverrideMap = new ArrayMap<>(o.componentLabelIconOverrideMap);
+        }
+    }
+
+    public String[] getOverlayPaths() {
+        return overlayPaths;
+    }
+
+    public void setOverlayPaths(String[] paths) {
+        overlayPaths = paths;
+        cachedOverlayPaths = null;
+    }
+
+    public Map<String, String[]> getSharedLibraryOverlayPaths() {
+        return sharedLibraryOverlayPaths;
+    }
+
+    public void setSharedLibraryOverlayPaths(String library, String[] paths) {
+        if (sharedLibraryOverlayPaths == null) {
+            sharedLibraryOverlayPaths = new ArrayMap<>();
+        }
+        sharedLibraryOverlayPaths.put(library, paths);
+        cachedOverlayPaths = null;
+    }
+
+    /**
+     * Overrides the non-localized label and icon of a component.
+     *
+     * @return true if the label or icon was changed.
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public boolean overrideLabelAndIcon(@NonNull ComponentName component,
+            @Nullable String nonLocalizedLabel, @Nullable Integer icon) {
+        String existingLabel = null;
+        Integer existingIcon = null;
+
+        if (componentLabelIconOverrideMap != null) {
+            Pair<String, Integer> pair = componentLabelIconOverrideMap.get(component);
+            if (pair != null) {
+                existingLabel = pair.first;
+                existingIcon = pair.second;
+            }
+        }
+
+        boolean changed = !TextUtils.equals(existingLabel, nonLocalizedLabel)
+                || !Objects.equals(existingIcon, icon);
+
+        if (changed) {
+            if (nonLocalizedLabel == null && icon == null) {
+                componentLabelIconOverrideMap.remove(component);
+                if (componentLabelIconOverrideMap.isEmpty()) {
+                    componentLabelIconOverrideMap = null;
+                }
+            } else {
+                if (componentLabelIconOverrideMap == null) {
+                    componentLabelIconOverrideMap = new ArrayMap<>(1);
+                }
+
+                componentLabelIconOverrideMap.put(component, Pair.create(nonLocalizedLabel, icon));
+            }
+        }
+
+        return changed;
+    }
+
+    /**
+     * Clears all values previously set by {@link #overrideLabelAndIcon(ComponentName,
+     * String, Integer)}.
+     *
+     * This is done when the package is updated as the components and resource IDs may have changed.
+     */
+    public void resetOverrideComponentLabelIcon() {
+        componentLabelIconOverrideMap = null;
+    }
+
+    @Nullable
+    public Pair<String, Integer> getOverrideLabelIconForComponent(ComponentName componentName) {
+        if (ArrayUtils.isEmpty(componentLabelIconOverrideMap)) {
+            return null;
+        }
+
+        return componentLabelIconOverrideMap.get(componentName);
+    }
+
+    /**
+     * Test if this package is installed.
+     */
+    public boolean isAvailable(int flags) {
+        // True if it is installed for this user and it is not hidden. If it is hidden,
+        // still return true if the caller requested MATCH_UNINSTALLED_PACKAGES
+        final boolean matchAnyUser = (flags & PackageManager.MATCH_ANY_USER) != 0;
+        final boolean matchUninstalled = (flags & PackageManager.MATCH_UNINSTALLED_PACKAGES) != 0;
+        return matchAnyUser
+                || (this.installed
+                        && (!this.hidden || matchUninstalled));
+    }
+
+    public boolean isMatch(ComponentInfo componentInfo, int flags) {
+        return isMatch(componentInfo.applicationInfo.isSystemApp(),
+                componentInfo.applicationInfo.enabled, componentInfo.enabled,
+                componentInfo.directBootAware, componentInfo.name, flags);
+    }
+
+    public boolean isMatch(boolean isSystem, boolean isPackageEnabled,
+            ParsedMainComponent component, int flags) {
+        return isMatch(isSystem, isPackageEnabled, component.isEnabled(),
+                component.isDirectBootAware(), component.getName(), flags);
+    }
+
+    /**
+     * Test if the given component is considered installed, enabled and a match
+     * for the given flags.
+     *
+     * <p>
+     * Expects at least one of {@link PackageManager#MATCH_DIRECT_BOOT_AWARE} and
+     * {@link PackageManager#MATCH_DIRECT_BOOT_UNAWARE} are specified in {@code flags}.
+     * </p>
+     *
+     */
+    public boolean isMatch(boolean isSystem, boolean isPackageEnabled, boolean isComponentEnabled,
+               boolean isComponentDirectBootAware, String componentName, int flags) {
+        final boolean matchUninstalled = (flags & PackageManager.MATCH_KNOWN_PACKAGES) != 0;
+        if (!isAvailable(flags) && !(isSystem && matchUninstalled)) {
+            return reportIfDebug(false, flags);
+        }
+
+        if (!isEnabled(isPackageEnabled, isComponentEnabled, componentName, flags)) {
+            return reportIfDebug(false, flags);
+        }
+
+        if ((flags & MATCH_SYSTEM_ONLY) != 0) {
+            if (!isSystem) {
+                return reportIfDebug(false, flags);
+            }
+        }
+
+        final boolean matchesUnaware = ((flags & MATCH_DIRECT_BOOT_UNAWARE) != 0)
+                && !isComponentDirectBootAware;
+        final boolean matchesAware = ((flags & MATCH_DIRECT_BOOT_AWARE) != 0)
+                && isComponentDirectBootAware;
+        return reportIfDebug(matchesUnaware || matchesAware, flags);
+    }
+
+    public boolean reportIfDebug(boolean result, int flags) {
+        if (DEBUG && !result) {
+            Slog.i(LOG_TAG, "No match!; flags: "
+                    + DebugUtils.flagsToString(PackageManager.class, "MATCH_", flags) + " "
+                    + Debug.getCaller());
+        }
+        return result;
+    }
+
+    public boolean isEnabled(ComponentInfo componentInfo, int flags) {
+        return isEnabled(componentInfo.applicationInfo.enabled, componentInfo.enabled,
+                componentInfo.name, flags);
+    }
+
+    public boolean isEnabled(boolean isPackageEnabled,
+            ParsedMainComponent parsedComponent, int flags) {
+        return isEnabled(isPackageEnabled, parsedComponent.isEnabled(), parsedComponent.getName(),
+                flags);
+    }
+
+    /**
+     * Test if the given component is considered enabled.
+     */
+    public boolean isEnabled(boolean isPackageEnabled, boolean isComponentEnabled,
+            String componentName, int flags) {
+        if ((flags & MATCH_DISABLED_COMPONENTS) != 0) {
+            return true;
+        }
+
+        // First check if the overall package is disabled; if the package is
+        // enabled then fall through to check specific component
+        switch (this.enabled) {
+            case COMPONENT_ENABLED_STATE_DISABLED:
+            case COMPONENT_ENABLED_STATE_DISABLED_USER:
+                return false;
+            case COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED:
+                if ((flags & MATCH_DISABLED_UNTIL_USED_COMPONENTS) == 0) {
+                    return false;
+                }
+                // fallthrough
+            case COMPONENT_ENABLED_STATE_DEFAULT:
+                if (!isPackageEnabled) {
+                    return false;
+                }
+                // fallthrough
+            case COMPONENT_ENABLED_STATE_ENABLED:
+                break;
+        }
+
+        // Check if component has explicit state before falling through to
+        // the manifest default
+        if (ArrayUtils.contains(this.enabledComponents, componentName)) {
+            return true;
+        }
+        if (ArrayUtils.contains(this.disabledComponents, componentName)) {
+            return false;
+        }
+
+        return isComponentEnabled;
+    }
+
+    public String[] getAllOverlayPaths() {
+        if (overlayPaths == null && sharedLibraryOverlayPaths == null) {
+            return null;
+        }
+
+        if (cachedOverlayPaths != null) {
+            return cachedOverlayPaths;
+        }
+
+        final LinkedHashSet<String> paths = new LinkedHashSet<>();
+        if (overlayPaths != null) {
+            final int N = overlayPaths.length;
+            for (int i = 0; i < N; i++) {
+                paths.add(overlayPaths[i]);
+            }
+        }
+
+        if (sharedLibraryOverlayPaths != null) {
+            for (String[] libOverlayPaths : sharedLibraryOverlayPaths.values()) {
+                if (libOverlayPaths != null) {
+                    final int N = libOverlayPaths.length;
+                    for (int i = 0; i < N; i++) {
+                        paths.add(libOverlayPaths[i]);
+                    }
+                }
+            }
+        }
+
+        cachedOverlayPaths = paths.toArray(new String[0]);
+        return cachedOverlayPaths;
+    }
+
+    @Override
+    final public boolean equals(Object obj) {
+        if (!(obj instanceof PackageUserState)) {
+            return false;
+        }
+        final PackageUserState oldState = (PackageUserState) obj;
+        if (ceDataInode != oldState.ceDataInode) {
+            return false;
+        }
+        if (installed != oldState.installed) {
+            return false;
+        }
+        if (stopped != oldState.stopped) {
+            return false;
+        }
+        if (notLaunched != oldState.notLaunched) {
+            return false;
+        }
+        if (hidden != oldState.hidden) {
+            return false;
+        }
+        if (distractionFlags != oldState.distractionFlags) {
+            return false;
+        }
+        if (suspended != oldState.suspended) {
+            return false;
+        }
+        if (suspended) {
+            if (!Objects.equals(suspendParams, oldState.suspendParams)) {
+                return false;
+            }
+        }
+        if (instantApp != oldState.instantApp) {
+            return false;
+        }
+        if (virtualPreload != oldState.virtualPreload) {
+            return false;
+        }
+        if (enabled != oldState.enabled) {
+            return false;
+        }
+        if ((lastDisableAppCaller == null && oldState.lastDisableAppCaller != null)
+                || (lastDisableAppCaller != null
+                        && !lastDisableAppCaller.equals(oldState.lastDisableAppCaller))) {
+            return false;
+        }
+        if (domainVerificationStatus != oldState.domainVerificationStatus) {
+            return false;
+        }
+        if (appLinkGeneration != oldState.appLinkGeneration) {
+            return false;
+        }
+        if (categoryHint != oldState.categoryHint) {
+            return false;
+        }
+        if (installReason != oldState.installReason) {
+            return false;
+        }
+        if (uninstallReason != oldState.uninstallReason) {
+            return false;
+        }
+        if ((disabledComponents == null && oldState.disabledComponents != null)
+                || (disabledComponents != null && oldState.disabledComponents == null)) {
+            return false;
+        }
+        if (disabledComponents != null) {
+            if (disabledComponents.size() != oldState.disabledComponents.size()) {
+                return false;
+            }
+            for (int i = disabledComponents.size() - 1; i >=0; --i) {
+                if (!oldState.disabledComponents.contains(disabledComponents.valueAt(i))) {
+                    return false;
+                }
+            }
+        }
+        if ((enabledComponents == null && oldState.enabledComponents != null)
+                || (enabledComponents != null && oldState.enabledComponents == null)) {
+            return false;
+        }
+        if (enabledComponents != null) {
+            if (enabledComponents.size() != oldState.enabledComponents.size()) {
+                return false;
+            }
+            for (int i = enabledComponents.size() - 1; i >=0; --i) {
+                if (!oldState.enabledComponents.contains(enabledComponents.valueAt(i))) {
+                    return false;
+                }
+            }
+        }
+        if (harmfulAppWarning == null && oldState.harmfulAppWarning != null
+                || (harmfulAppWarning != null
+                        && !harmfulAppWarning.equals(oldState.harmfulAppWarning))) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int hashCode = Long.hashCode(ceDataInode);
+        hashCode = 31 * hashCode + Boolean.hashCode(installed);
+        hashCode = 31 * hashCode + Boolean.hashCode(stopped);
+        hashCode = 31 * hashCode + Boolean.hashCode(notLaunched);
+        hashCode = 31 * hashCode + Boolean.hashCode(hidden);
+        hashCode = 31 * hashCode + distractionFlags;
+        hashCode = 31 * hashCode + Boolean.hashCode(suspended);
+        hashCode = 31 * hashCode + Objects.hashCode(suspendParams);
+        hashCode = 31 * hashCode + Boolean.hashCode(instantApp);
+        hashCode = 31 * hashCode + Boolean.hashCode(virtualPreload);
+        hashCode = 31 * hashCode + enabled;
+        hashCode = 31 * hashCode + Objects.hashCode(lastDisableAppCaller);
+        hashCode = 31 * hashCode + domainVerificationStatus;
+        hashCode = 31 * hashCode + appLinkGeneration;
+        hashCode = 31 * hashCode + categoryHint;
+        hashCode = 31 * hashCode + installReason;
+        hashCode = 31 * hashCode + uninstallReason;
+        hashCode = 31 * hashCode + Objects.hashCode(disabledComponents);
+        hashCode = 31 * hashCode + Objects.hashCode(enabledComponents);
+        hashCode = 31 * hashCode + Objects.hashCode(harmfulAppWarning);
+        return hashCode;
+    }
+
+    /**
+     * Container to describe suspension parameters.
+     */
+    public static final class SuspendParams {
+        private static final String TAG_DIALOG_INFO = "dialog-info";
+        private static final String TAG_APP_EXTRAS = "app-extras";
+        private static final String TAG_LAUNCHER_EXTRAS = "launcher-extras";
+
+        public SuspendDialogInfo dialogInfo;
+        public PersistableBundle appExtras;
+        public PersistableBundle launcherExtras;
+
+        private SuspendParams() {
+        }
+
+        /**
+         * Returns a {@link SuspendParams} object with the given fields. Returns {@code null} if all
+         * the fields are {@code null}.
+         *
+         * @param dialogInfo
+         * @param appExtras
+         * @param launcherExtras
+         * @return A {@link SuspendParams} object or {@code null}.
+         */
+        public static SuspendParams getInstanceOrNull(SuspendDialogInfo dialogInfo,
+                PersistableBundle appExtras, PersistableBundle launcherExtras) {
+            if (dialogInfo == null && appExtras == null && launcherExtras == null) {
+                return null;
+            }
+            final SuspendParams instance = new SuspendParams();
+            instance.dialogInfo = dialogInfo;
+            instance.appExtras = appExtras;
+            instance.launcherExtras = launcherExtras;
+            return instance;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (!(obj instanceof SuspendParams)) {
+                return false;
+            }
+            final SuspendParams other = (SuspendParams) obj;
+            if (!Objects.equals(dialogInfo, other.dialogInfo)) {
+                return false;
+            }
+            if (!BaseBundle.kindofEquals(appExtras, other.appExtras)) {
+                return false;
+            }
+            if (!BaseBundle.kindofEquals(launcherExtras, other.launcherExtras)) {
+                return false;
+            }
+            return true;
+        }
+
+        @Override
+        public int hashCode() {
+            int hashCode = Objects.hashCode(dialogInfo);
+            hashCode = 31 * hashCode + ((appExtras != null) ? appExtras.size() : 0);
+            hashCode = 31 * hashCode + ((launcherExtras != null) ? launcherExtras.size() : 0);
+            return hashCode;
+        }
+
+        /**
+         * Serializes this object into an xml format
+         * @param out the {@link XmlSerializer} object
+         * @throws IOException
+         */
+        public void saveToXml(XmlSerializer out) throws IOException {
+            if (dialogInfo != null) {
+                out.startTag(null, TAG_DIALOG_INFO);
+                dialogInfo.saveToXml(out);
+                out.endTag(null, TAG_DIALOG_INFO);
+            }
+            if (appExtras != null) {
+                out.startTag(null, TAG_APP_EXTRAS);
+                try {
+                    appExtras.saveToXml(out);
+                } catch (XmlPullParserException e) {
+                    Slog.e(LOG_TAG, "Exception while trying to write appExtras."
+                            + " Will be lost on reboot", e);
+                }
+                out.endTag(null, TAG_APP_EXTRAS);
+            }
+            if (launcherExtras != null) {
+                out.startTag(null, TAG_LAUNCHER_EXTRAS);
+                try {
+                    launcherExtras.saveToXml(out);
+                } catch (XmlPullParserException e) {
+                    Slog.e(LOG_TAG, "Exception while trying to write launcherExtras."
+                            + " Will be lost on reboot", e);
+                }
+                out.endTag(null, TAG_LAUNCHER_EXTRAS);
+            }
+        }
+
+        /**
+         * Parses this object from the xml format. Returns {@code null} if no object related
+         * information could be read.
+         * @param in the reader
+         * @return
+         */
+        public static SuspendParams restoreFromXml(XmlPullParser in) throws IOException {
+            SuspendDialogInfo readDialogInfo = null;
+            PersistableBundle readAppExtras = null;
+            PersistableBundle readLauncherExtras = null;
+
+            final int currentDepth = in.getDepth();
+            int type;
+            try {
+                while ((type = in.next()) != XmlPullParser.END_DOCUMENT
+                        && (type != XmlPullParser.END_TAG
+                        || in.getDepth() > currentDepth)) {
+                    if (type == XmlPullParser.END_TAG
+                            || type == XmlPullParser.TEXT) {
+                        continue;
+                    }
+                    switch (in.getName()) {
+                        case TAG_DIALOG_INFO:
+                            readDialogInfo = SuspendDialogInfo.restoreFromXml(in);
+                            break;
+                        case TAG_APP_EXTRAS:
+                            readAppExtras = PersistableBundle.restoreFromXml(in);
+                            break;
+                        case TAG_LAUNCHER_EXTRAS:
+                            readLauncherExtras = PersistableBundle.restoreFromXml(in);
+                            break;
+                        default:
+                            Slog.w(LOG_TAG, "Unknown tag " + in.getName()
+                                    + " in SuspendParams. Ignoring");
+                            break;
+                    }
+                }
+            } catch (XmlPullParserException e) {
+                Slog.e(LOG_TAG, "Exception while trying to parse SuspendParams,"
+                        + " some fields may default", e);
+            }
+            return getInstanceOrNull(readDialogInfo, readAppExtras, readLauncherExtras);
+        }
+    }
+}
diff --git a/android/content/pm/ParceledListSlice.java b/android/content/pm/ParceledListSlice.java
new file mode 100644
index 0000000..73119e0
--- /dev/null
+++ b/android/content/pm/ParceledListSlice.java
@@ -0,0 +1,93 @@
+/*
+ * 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.content.pm;
+
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Transfer a large list of Parcelable objects across an IPC.  Splits into
+ * multiple transactions if needed.
+ *
+ * @see BaseParceledListSlice
+ *
+ * @hide
+ */
+public class ParceledListSlice<T extends Parcelable> extends BaseParceledListSlice<T> {
+    @UnsupportedAppUsage
+    public ParceledListSlice(List<T> list) {
+        super(list);
+    }
+
+    private ParceledListSlice(Parcel in, ClassLoader loader) {
+        super(in, loader);
+    }
+
+    public static <T extends Parcelable> ParceledListSlice<T> emptyList() {
+        return new ParceledListSlice<T>(Collections.<T> emptyList());
+    }
+
+    @Override
+    public int describeContents() {
+        int contents = 0;
+        final List<T> list = getList();
+        for (int i=0; i<list.size(); i++) {
+            contents |= list.get(i).describeContents();
+        }
+        return contents;
+    }
+
+    @Override
+    protected void writeElement(T parcelable, Parcel dest, int callFlags) {
+        parcelable.writeToParcel(dest, callFlags);
+    }
+
+    @Override
+    @UnsupportedAppUsage
+    protected void writeParcelableCreator(T parcelable, Parcel dest) {
+        dest.writeParcelableCreator((Parcelable) parcelable);
+    }
+
+    @Override
+    protected Parcelable.Creator<?> readParcelableCreator(Parcel from, ClassLoader loader) {
+        return from.readParcelableCreator(loader);
+    }
+
+    @SuppressWarnings("unchecked")
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    public static final Parcelable.ClassLoaderCreator<ParceledListSlice> CREATOR =
+            new Parcelable.ClassLoaderCreator<ParceledListSlice>() {
+        public ParceledListSlice createFromParcel(Parcel in) {
+            return new ParceledListSlice(in, null);
+        }
+
+        @Override
+        public ParceledListSlice createFromParcel(Parcel in, ClassLoader loader) {
+            return new ParceledListSlice(in, loader);
+        }
+
+        @Override
+        public ParceledListSlice[] newArray(int size) {
+            return new ParceledListSlice[size];
+        }
+    };
+}
diff --git a/android/content/pm/PathPermission.java b/android/content/pm/PathPermission.java
new file mode 100644
index 0000000..11c9a7d
--- /dev/null
+++ b/android/content/pm/PathPermission.java
@@ -0,0 +1,68 @@
+/*
+ * 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.content.pm;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.PatternMatcher;
+
+/**
+ * Description of permissions needed to access a particular path
+ * in a {@link ProviderInfo}.
+ */
+public class PathPermission extends PatternMatcher {
+    private final String mReadPermission;
+    private final String mWritePermission;
+    
+    public PathPermission(String pattern, int type, String readPermission,
+            String writePermission) {
+        super(pattern, type);
+        mReadPermission = readPermission;
+        mWritePermission = writePermission;
+    }
+    
+    public String getReadPermission() {
+        return mReadPermission;
+    }
+    
+    public String getWritePermission() {
+        return mWritePermission;
+    }
+    
+    public void writeToParcel(Parcel dest, int flags) {
+        super.writeToParcel(dest, flags);
+        dest.writeString(mReadPermission);
+        dest.writeString(mWritePermission);
+    }
+    
+    public PathPermission(Parcel src) {
+        super(src);
+        mReadPermission = src.readString();
+        mWritePermission = src.readString();
+    }
+    
+    public static final @android.annotation.NonNull Parcelable.Creator<PathPermission> CREATOR
+            = new Parcelable.Creator<PathPermission>() {
+        public PathPermission createFromParcel(Parcel source) {
+            return new PathPermission(source);
+        }
+
+        public PathPermission[] newArray(int size) {
+            return new PathPermission[size];
+        }
+    };
+}
\ No newline at end of file
diff --git a/android/content/pm/PermissionGroupInfo.java b/android/content/pm/PermissionGroupInfo.java
new file mode 100644
index 0000000..e65e742
--- /dev/null
+++ b/android/content/pm/PermissionGroupInfo.java
@@ -0,0 +1,225 @@
+/*
+ * 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.content.pm;
+
+import static android.content.res.Resources.ID_NULL;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StringRes;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Information you can retrieve about a particular security permission
+ * group known to the system.  This corresponds to information collected from the
+ * AndroidManifest.xml's &lt;permission-group&gt; tags.
+ */
+public class PermissionGroupInfo extends PackageItemInfo implements Parcelable {
+    /**
+     * A string resource identifier (in the package's resources) of this
+     * permission's description.  From the "description" attribute or,
+     * if not set, 0.
+     */
+    public @StringRes int descriptionRes;
+
+    /**
+     * A string resource identifier (in the package's resources) used to request the permissions.
+     * From the "request" attribute or, if not set, 0.
+     *
+     * @hide
+     */
+    @SystemApi
+    public @StringRes int requestRes;
+
+    /**
+     * A string resource identifier (in the package's resources) used as subtitle when requesting
+     * only access while in the foreground.
+     *
+     * From the "requestDetail" attribute or, if not set, {@link
+     * android.content.res.Resources#ID_NULL}.
+     *
+     * @hide
+     */
+    @SystemApi
+    public final @StringRes int requestDetailResourceId;
+
+    /**
+     * A string resource identifier (in the package's resources) used when requesting background
+     * access. Also used when requesting both foreground and background access.
+     *
+     * From the "backgroundRequest" attribute or, if not set, {@link
+     * android.content.res.Resources#ID_NULL}.
+     *
+     * @hide
+     */
+    @SystemApi
+    public final @StringRes int backgroundRequestResourceId;
+
+    /**
+     * A string resource identifier (in the package's resources) used as subtitle when requesting
+     * background access.
+     *
+     * From the "backgroundRequestDetail" attribute or, if not set, {@link
+     * android.content.res.Resources#ID_NULL}.
+     *
+     * @hide
+     */
+    @SystemApi
+    public final @StringRes int backgroundRequestDetailResourceId;
+
+    /**
+     * The description string provided in the AndroidManifest file, if any.  You
+     * probably don't want to use this, since it will be null if the description
+     * is in a resource.  You probably want
+     * {@link PermissionInfo#loadDescription} instead.
+     */
+    public @Nullable CharSequence nonLocalizedDescription;
+
+    /**
+     * Flag for {@link #flags}, corresponding to <code>personalInfo</code>
+     * value of {@link android.R.attr#permissionGroupFlags}.
+     */
+    public static final int FLAG_PERSONAL_INFO = 1<<0;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "FLAG_" }, value = {
+            FLAG_PERSONAL_INFO,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Flags {}
+
+    /**
+     * Additional flags about this group as given by
+     * {@link android.R.attr#permissionGroupFlags}.
+     */
+    public @Flags int flags;
+
+    /**
+     * Prioritization of this group, for visually sorting with other groups.
+     */
+    public int priority;
+
+    /**
+     * @hide
+     */
+    public PermissionGroupInfo(@StringRes int requestDetailResourceId,
+            @StringRes int backgroundRequestResourceId,
+            @StringRes int backgroundRequestDetailResourceId) {
+        this.requestDetailResourceId = requestDetailResourceId;
+        this.backgroundRequestResourceId = backgroundRequestResourceId;
+        this.backgroundRequestDetailResourceId = backgroundRequestDetailResourceId;
+    }
+
+    /**
+     * @deprecated Should only be created by the system.
+     */
+    @Deprecated
+    public PermissionGroupInfo() {
+        this(ID_NULL, ID_NULL, ID_NULL);
+    }
+
+    /**
+     * @deprecated Should only be created by the system.
+     */
+    @Deprecated
+    public PermissionGroupInfo(@NonNull PermissionGroupInfo orig) {
+        super(orig);
+        descriptionRes = orig.descriptionRes;
+        requestRes = orig.requestRes;
+        requestDetailResourceId = orig.requestDetailResourceId;
+        backgroundRequestResourceId = orig.backgroundRequestResourceId;
+        backgroundRequestDetailResourceId = orig.backgroundRequestDetailResourceId;
+        nonLocalizedDescription = orig.nonLocalizedDescription;
+        flags = orig.flags;
+        priority = orig.priority;
+    }
+
+    /**
+     * Retrieve the textual description of this permission.  This
+     * will call back on the given PackageManager to load the description from
+     * the application.
+     *
+     * @param pm A PackageManager from which the label can be loaded; usually
+     * the PackageManager from which you originally retrieved this item.
+     *
+     * @return Returns a CharSequence containing the permission's description.
+     * If there is no description, null is returned.
+     */
+    public @Nullable CharSequence loadDescription(@NonNull PackageManager pm) {
+        if (nonLocalizedDescription != null) {
+            return nonLocalizedDescription;
+        }
+        if (descriptionRes != 0) {
+            CharSequence label = pm.getText(packageName, descriptionRes, null);
+            if (label != null) {
+                return label;
+            }
+        }
+        return null;
+    }
+
+    public String toString() {
+        return "PermissionGroupInfo{"
+            + Integer.toHexString(System.identityHashCode(this))
+            + " " + name + " flgs=0x" + Integer.toHexString(flags) + "}";
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel dest, int parcelableFlags) {
+        super.writeToParcel(dest, parcelableFlags);
+        dest.writeInt(descriptionRes);
+        dest.writeInt(requestRes);
+        dest.writeInt(requestDetailResourceId);
+        dest.writeInt(backgroundRequestResourceId);
+        dest.writeInt(backgroundRequestDetailResourceId);
+        TextUtils.writeToParcel(nonLocalizedDescription, dest, parcelableFlags);
+        dest.writeInt(flags);
+        dest.writeInt(priority);
+    }
+
+    public static final @NonNull Creator<PermissionGroupInfo> CREATOR =
+            new Creator<PermissionGroupInfo>() {
+        public PermissionGroupInfo createFromParcel(Parcel source) {
+            return new PermissionGroupInfo(source);
+        }
+        public PermissionGroupInfo[] newArray(int size) {
+            return new PermissionGroupInfo[size];
+        }
+    };
+
+    private PermissionGroupInfo(Parcel source) {
+        super(source);
+        descriptionRes = source.readInt();
+        requestRes = source.readInt();
+        requestDetailResourceId = source.readInt();
+        backgroundRequestResourceId = source.readInt();
+        backgroundRequestDetailResourceId = source.readInt();
+        nonLocalizedDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+        flags = source.readInt();
+        priority = source.readInt();
+    }
+}
diff --git a/android/content/pm/PermissionInfo.java b/android/content/pm/PermissionInfo.java
new file mode 100644
index 0000000..5f6befd
--- /dev/null
+++ b/android/content/pm/PermissionInfo.java
@@ -0,0 +1,687 @@
+/*
+ * 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.content.pm;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StringRes;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Information you can retrieve about a particular security permission
+ * known to the system.  This corresponds to information collected from the
+ * AndroidManifest.xml's &lt;permission&gt; tags.
+ */
+public class PermissionInfo extends PackageItemInfo implements Parcelable {
+    /**
+     * A normal application value for {@link #protectionLevel}, corresponding
+     * to the <code>normal</code> value of
+     * {@link android.R.attr#protectionLevel}.
+     */
+    public static final int PROTECTION_NORMAL = 0;
+
+    /**
+     * Dangerous value for {@link #protectionLevel}, corresponding
+     * to the <code>dangerous</code> value of
+     * {@link android.R.attr#protectionLevel}.
+     */
+    public static final int PROTECTION_DANGEROUS = 1;
+
+    /**
+     * System-level value for {@link #protectionLevel}, corresponding
+     * to the <code>signature</code> value of
+     * {@link android.R.attr#protectionLevel}.
+     */
+    public static final int PROTECTION_SIGNATURE = 2;
+
+    /**
+     * @deprecated Use {@link #PROTECTION_SIGNATURE}|{@link #PROTECTION_FLAG_PRIVILEGED}
+     * instead.
+     */
+    @Deprecated
+    public static final int PROTECTION_SIGNATURE_OR_SYSTEM = 3;
+
+    /** @hide */
+    @IntDef(flag = false, prefix = { "PROTECTION_" }, value = {
+            PROTECTION_NORMAL,
+            PROTECTION_DANGEROUS,
+            PROTECTION_SIGNATURE,
+            PROTECTION_SIGNATURE_OR_SYSTEM,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Protection {}
+
+    /**
+     * Additional flag for {@link #protectionLevel}, corresponding
+     * to the <code>privileged</code> value of
+     * {@link android.R.attr#protectionLevel}.
+     */
+    public static final int PROTECTION_FLAG_PRIVILEGED = 0x10;
+
+    /**
+     * @deprecated Old name for {@link #PROTECTION_FLAG_PRIVILEGED}, which
+     * is now very confusing because it only applies to privileged apps, not all
+     * apps on the system image.
+     */
+    @Deprecated
+    public static final int PROTECTION_FLAG_SYSTEM = 0x10;
+
+    /**
+     * Additional flag for {@link #protectionLevel}, corresponding
+     * to the <code>development</code> value of
+     * {@link android.R.attr#protectionLevel}.
+     */
+    public static final int PROTECTION_FLAG_DEVELOPMENT = 0x20;
+
+    /**
+     * Additional flag for {@link #protectionLevel}, corresponding
+     * to the <code>appop</code> value of
+     * {@link android.R.attr#protectionLevel}.
+     */
+    public static final int PROTECTION_FLAG_APPOP = 0x40;
+
+    /**
+     * Additional flag for {@link #protectionLevel}, corresponding
+     * to the <code>pre23</code> value of
+     * {@link android.R.attr#protectionLevel}.
+     */
+    public static final int PROTECTION_FLAG_PRE23 = 0x80;
+
+    /**
+     * Additional flag for {@link #protectionLevel}, corresponding
+     * to the <code>installer</code> value of
+     * {@link android.R.attr#protectionLevel}.
+     */
+    public static final int PROTECTION_FLAG_INSTALLER = 0x100;
+
+    /**
+     * Additional flag for {@link #protectionLevel}, corresponding
+     * to the <code>verifier</code> value of
+     * {@link android.R.attr#protectionLevel}.
+     */
+    public static final int PROTECTION_FLAG_VERIFIER = 0x200;
+
+    /**
+     * Additional flag for {@link #protectionLevel}, corresponding
+     * to the <code>preinstalled</code> value of
+     * {@link android.R.attr#protectionLevel}.
+     */
+    public static final int PROTECTION_FLAG_PREINSTALLED = 0x400;
+
+    /**
+     * Additional flag for {@link #protectionLevel}, corresponding
+     * to the <code>setup</code> value of
+     * {@link android.R.attr#protectionLevel}.
+     */
+    public static final int PROTECTION_FLAG_SETUP = 0x800;
+
+    /**
+     * Additional flag for {@link #protectionLevel}, corresponding
+     * to the <code>instant</code> value of
+     * {@link android.R.attr#protectionLevel}.
+     */
+    public static final int PROTECTION_FLAG_INSTANT = 0x1000;
+
+    /**
+     * Additional flag for {@link #protectionLevel}, corresponding
+     * to the <code>runtime</code> value of
+     * {@link android.R.attr#protectionLevel}.
+     */
+    public static final int PROTECTION_FLAG_RUNTIME_ONLY = 0x2000;
+
+    /**
+     * Additional flag for {@link #protectionLevel}, corresponding
+     * to the <code>oem</code> value of
+     * {@link android.R.attr#protectionLevel}.
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    public static final int PROTECTION_FLAG_OEM = 0x4000;
+
+    /**
+     * Additional flag for {${link #protectionLevel}, corresponding
+     * to the <code>vendorPrivileged</code> value of
+     * {@link android.R.attr#protectionLevel}.
+     *
+     * @hide
+     */
+    @TestApi
+    public static final int PROTECTION_FLAG_VENDOR_PRIVILEGED = 0x8000;
+
+    /**
+     * Additional flag for {@link #protectionLevel}, corresponding
+     * to the <code>text_classifier</code> value of
+     * {@link android.R.attr#protectionLevel}.
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 0x10000;
+
+    /**
+     * Additional flag for {${link #protectionLevel}, corresponding
+     * to the <code>wellbeing</code> value of
+     * {@link android.R.attr#protectionLevel}.
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    public static final int PROTECTION_FLAG_WELLBEING = 0x20000;
+
+    /**
+     * Additional flag for {@link #protectionLevel}, corresponding to the
+     * {@code documenter} value of {@link android.R.attr#protectionLevel}.
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    public static final int PROTECTION_FLAG_DOCUMENTER = 0x40000;
+
+    /**
+     * Additional flag for {@link #protectionLevel}, corresponding to the
+     * {@code configurator} value of {@link android.R.attr#protectionLevel}.
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    public static final int PROTECTION_FLAG_CONFIGURATOR = 0x80000;
+
+    /**
+     * Additional flag for {${link #protectionLevel}, corresponding
+     * to the <code>incident_report_approver</code> value of
+     * {@link android.R.attr#protectionLevel}.
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    public static final int PROTECTION_FLAG_INCIDENT_REPORT_APPROVER = 0x100000;
+
+    /**
+     * Additional flag for {@link #protectionLevel}, corresponding
+     * to the <code>app_predictor</code> value of
+     * {@link android.R.attr#protectionLevel}.
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    public static final int PROTECTION_FLAG_APP_PREDICTOR = 0x200000;
+
+    /**
+     * Additional flag for {@link #protectionLevel}, corresponding
+     * to the <code>companion</code> value of
+     * {@link android.R.attr#protectionLevel}.
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    public static final int PROTECTION_FLAG_COMPANION = 0x800000;
+
+    /**
+     * Additional flag for {@link #protectionLevel}, corresponding
+     * to the <code>retailDemo</code> value of
+     * {@link android.R.attr#protectionLevel}.
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    public static final int PROTECTION_FLAG_RETAIL_DEMO = 0x1000000;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "PROTECTION_FLAG_" }, value = {
+            PROTECTION_FLAG_PRIVILEGED,
+            PROTECTION_FLAG_SYSTEM,
+            PROTECTION_FLAG_DEVELOPMENT,
+            PROTECTION_FLAG_APPOP,
+            PROTECTION_FLAG_PRE23,
+            PROTECTION_FLAG_INSTALLER,
+            PROTECTION_FLAG_VERIFIER,
+            PROTECTION_FLAG_PREINSTALLED,
+            PROTECTION_FLAG_SETUP,
+            PROTECTION_FLAG_INSTANT,
+            PROTECTION_FLAG_RUNTIME_ONLY,
+            PROTECTION_FLAG_OEM,
+            PROTECTION_FLAG_VENDOR_PRIVILEGED,
+            PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER,
+            PROTECTION_FLAG_WELLBEING,
+            PROTECTION_FLAG_DOCUMENTER,
+            PROTECTION_FLAG_CONFIGURATOR,
+            PROTECTION_FLAG_INCIDENT_REPORT_APPROVER,
+            PROTECTION_FLAG_APP_PREDICTOR,
+            PROTECTION_FLAG_COMPANION,
+            PROTECTION_FLAG_RETAIL_DEMO,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ProtectionFlags {}
+
+    /**
+     * Mask for {@link #protectionLevel}: the basic protection type.
+     *
+     * @deprecated Use #getProtection() instead.
+     */
+    @Deprecated
+    public static final int PROTECTION_MASK_BASE = 0xf;
+
+    /**
+     * Mask for {@link #protectionLevel}: additional flag bits.
+     *
+     * @deprecated Use #getProtectionFlags() instead.
+     */
+    @Deprecated
+    public static final int PROTECTION_MASK_FLAGS = 0xfff0;
+
+    /**
+     * The level of access this permission is protecting, as per
+     * {@link android.R.attr#protectionLevel}. Consists of
+     * a base permission type and zero or more flags. Use the following functions
+     * to extract them.
+     *
+     * <pre>
+     * int basePermissionType = permissionInfo.getProtection();
+     * int permissionFlags = permissionInfo.getProtectionFlags();
+     * </pre>
+     *
+     * <p></p>Base permission types are {@link #PROTECTION_NORMAL},
+     * {@link #PROTECTION_DANGEROUS}, {@link #PROTECTION_SIGNATURE}
+     * and the deprecated {@link #PROTECTION_SIGNATURE_OR_SYSTEM}.
+     * Flags are listed under {@link android.R.attr#protectionLevel}.
+     *
+     * @deprecated Use #getProtection() and #getProtectionFlags() instead.
+     */
+    @Deprecated
+    public int protectionLevel;
+
+    /**
+     * The group this permission is a part of, as per
+     * {@link android.R.attr#permissionGroup}.
+     */
+    public @Nullable String group;
+
+    /**
+     * Flag for {@link #flags}, corresponding to <code>costsMoney</code>
+     * value of {@link android.R.attr#permissionFlags}.
+     */
+    public static final int FLAG_COSTS_MONEY = 1<<0;
+
+    /**
+     * Flag for {@link #flags}, corresponding to <code>removed</code>
+     * value of {@link android.R.attr#permissionFlags}.
+     * @hide
+     */
+    @TestApi
+    @SystemApi
+    public static final int FLAG_REMOVED = 1<<1;
+
+    /**
+     * Flag for {@link #flags}, corresponding to <code>hardRestricted</code>
+     * value of {@link android.R.attr#permissionFlags}.
+     *
+     * <p> This permission is restricted by the platform and it would be
+     * grantable only to apps that meet special criteria per platform
+     * policy.
+     */
+    public static final int FLAG_HARD_RESTRICTED = 1<<2;
+
+    /**
+     * Flag for {@link #flags}, corresponding to <code>softRestricted</code>
+     * value of {@link android.R.attr#permissionFlags}.
+     *
+     * <p>This permission is restricted by the platform and it would be
+     * grantable in its full form to apps that meet special criteria
+     * per platform policy. Otherwise, a weaker form of the permission
+     * would be granted. The weak grant depends on the permission.
+     */
+    public static final int FLAG_SOFT_RESTRICTED = 1<<3;
+
+    /**
+     * Flag for {@link #flags}, corresponding to <code>immutablyRestricted</code>
+     * value of {@link android.R.attr#permissionFlags}.
+     *
+     * <p>This permission is restricted immutably which means that its
+     * restriction state may be specified only on the first install of
+     * the app and will stay in this initial whitelist state until
+     * the app is uninstalled.
+     */
+    public static final int FLAG_IMMUTABLY_RESTRICTED = 1<<4;
+
+    /**
+     * Flag for {@link #flags}, indicating that this permission has been
+     * installed into the system's globally defined permissions.
+     */
+    public static final int FLAG_INSTALLED = 1<<30;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "FLAG_" }, value = {
+            FLAG_COSTS_MONEY,
+            FLAG_REMOVED,
+            FLAG_HARD_RESTRICTED,
+            FLAG_SOFT_RESTRICTED,
+            FLAG_IMMUTABLY_RESTRICTED,
+            FLAG_INSTALLED
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Flags {}
+
+    /**
+     * Additional flags about this permission as given by
+     * {@link android.R.attr#permissionFlags}.
+     */
+    public @Flags int flags;
+
+    /**
+     * A string resource identifier (in the package's resources) of this
+     * permission's description.  From the "description" attribute or,
+     * if not set, 0.
+     */
+    public @StringRes int descriptionRes;
+
+    /**
+     * A string resource identifier (in the package's resources) used to request the permissions.
+     * From the "request" attribute or, if not set, 0.
+     *
+     * @hide
+     */
+    @SystemApi
+    public @StringRes int requestRes;
+
+    /**
+     * Some permissions only grant access while the app is in foreground. Some of these permissions
+     * allow to add background capabilities by adding another permission.
+     *
+     * If this is such a permission, this is the name of the permission adding the background
+     * access.
+     *
+     * From the "backgroundPermission" attribute or, if not set null
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    public final @Nullable String backgroundPermission;
+
+    /**
+     * The description string provided in the AndroidManifest file, if any.  You
+     * probably don't want to use this, since it will be null if the description
+     * is in a resource.  You probably want
+     * {@link PermissionInfo#loadDescription} instead.
+     */
+    public @Nullable CharSequence nonLocalizedDescription;
+
+    /** @hide */
+    public static int fixProtectionLevel(int level) {
+        if (level == PROTECTION_SIGNATURE_OR_SYSTEM) {
+            level = PROTECTION_SIGNATURE | PROTECTION_FLAG_PRIVILEGED;
+        }
+        if ((level & PROTECTION_FLAG_VENDOR_PRIVILEGED) != 0
+                && (level & PROTECTION_FLAG_PRIVILEGED) == 0) {
+            // 'vendorPrivileged' must be 'privileged'. If not,
+            // drop the vendorPrivileged.
+            level = level & ~PROTECTION_FLAG_VENDOR_PRIVILEGED;
+        }
+        return level;
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage
+    public static @NonNull String protectionToString(int level) {
+        String protLevel = "????";
+        switch (level & PROTECTION_MASK_BASE) {
+            case PermissionInfo.PROTECTION_DANGEROUS:
+                protLevel = "dangerous";
+                break;
+            case PermissionInfo.PROTECTION_NORMAL:
+                protLevel = "normal";
+                break;
+            case PermissionInfo.PROTECTION_SIGNATURE:
+                protLevel = "signature";
+                break;
+            case PermissionInfo.PROTECTION_SIGNATURE_OR_SYSTEM:
+                protLevel = "signatureOrSystem";
+                break;
+        }
+        if ((level & PermissionInfo.PROTECTION_FLAG_PRIVILEGED) != 0) {
+            protLevel += "|privileged";
+        }
+        if ((level & PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0) {
+            protLevel += "|development";
+        }
+        if ((level & PermissionInfo.PROTECTION_FLAG_APPOP) != 0) {
+            protLevel += "|appop";
+        }
+        if ((level & PermissionInfo.PROTECTION_FLAG_PRE23) != 0) {
+            protLevel += "|pre23";
+        }
+        if ((level & PermissionInfo.PROTECTION_FLAG_INSTALLER) != 0) {
+            protLevel += "|installer";
+        }
+        if ((level & PermissionInfo.PROTECTION_FLAG_VERIFIER) != 0) {
+            protLevel += "|verifier";
+        }
+        if ((level & PermissionInfo.PROTECTION_FLAG_PREINSTALLED) != 0) {
+            protLevel += "|preinstalled";
+        }
+        if ((level & PermissionInfo.PROTECTION_FLAG_SETUP) != 0) {
+            protLevel += "|setup";
+        }
+        if ((level & PermissionInfo.PROTECTION_FLAG_INSTANT) != 0) {
+            protLevel += "|instant";
+        }
+        if ((level & PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY) != 0) {
+            protLevel += "|runtime";
+        }
+        if ((level & PermissionInfo.PROTECTION_FLAG_OEM) != 0) {
+            protLevel += "|oem";
+        }
+        if ((level & PermissionInfo.PROTECTION_FLAG_VENDOR_PRIVILEGED) != 0) {
+            protLevel += "|vendorPrivileged";
+        }
+        if ((level & PermissionInfo.PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER) != 0) {
+            protLevel += "|textClassifier";
+        }
+        if ((level & PermissionInfo.PROTECTION_FLAG_WELLBEING) != 0) {
+            protLevel += "|wellbeing";
+        }
+        if ((level & PermissionInfo.PROTECTION_FLAG_DOCUMENTER) != 0) {
+            protLevel += "|documenter";
+        }
+        if ((level & PROTECTION_FLAG_CONFIGURATOR) != 0) {
+            protLevel += "|configurator";
+        }
+        if ((level & PermissionInfo.PROTECTION_FLAG_INCIDENT_REPORT_APPROVER) != 0) {
+            protLevel += "|incidentReportApprover";
+        }
+        if ((level & PermissionInfo.PROTECTION_FLAG_APP_PREDICTOR) != 0) {
+            protLevel += "|appPredictor";
+        }
+        if ((level & PermissionInfo.PROTECTION_FLAG_RETAIL_DEMO) != 0) {
+            protLevel += "|retailDemo";
+        }
+        return protLevel;
+    }
+
+    /**
+     * @hide
+     */
+    public PermissionInfo(@Nullable String backgroundPermission) {
+        this.backgroundPermission = backgroundPermission;
+    }
+
+    /**
+     * @deprecated Should only be created by the system.
+     */
+    @Deprecated
+    public PermissionInfo() {
+        this((String) null);
+    }
+
+    /**
+     * @deprecated Should only be created by the system.
+     */
+    @Deprecated
+    public PermissionInfo(@NonNull PermissionInfo orig) {
+        super(orig);
+        protectionLevel = orig.protectionLevel;
+        flags = orig.flags;
+        group = orig.group;
+        backgroundPermission = orig.backgroundPermission;
+        descriptionRes = orig.descriptionRes;
+        requestRes = orig.requestRes;
+        nonLocalizedDescription = orig.nonLocalizedDescription;
+    }
+
+    /**
+     * Retrieve the textual description of this permission.  This
+     * will call back on the given PackageManager to load the description from
+     * the application.
+     *
+     * @param pm A PackageManager from which the label can be loaded; usually
+     * the PackageManager from which you originally retrieved this item.
+     *
+     * @return Returns a CharSequence containing the permission's description.
+     * If there is no description, null is returned.
+     */
+    public @Nullable CharSequence loadDescription(@NonNull PackageManager pm) {
+        if (nonLocalizedDescription != null) {
+            return nonLocalizedDescription;
+        }
+        if (descriptionRes != 0) {
+            CharSequence label = pm.getText(packageName, descriptionRes, null);
+            if (label != null) {
+                return label;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Return the base permission type.
+     */
+    @Protection
+    public int getProtection() {
+        return protectionLevel & PROTECTION_MASK_BASE;
+    }
+
+    /**
+     * Return the additional flags in {@link #protectionLevel}.
+     */
+    @ProtectionFlags
+    public int getProtectionFlags() {
+        return protectionLevel & ~PROTECTION_MASK_BASE;
+    }
+
+    @Override
+    public String toString() {
+        return "PermissionInfo{"
+            + Integer.toHexString(System.identityHashCode(this))
+            + " " + name + "}";
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int parcelableFlags) {
+        super.writeToParcel(dest, parcelableFlags);
+        dest.writeInt(protectionLevel);
+        dest.writeInt(flags);
+        dest.writeString8(group);
+        dest.writeString8(backgroundPermission);
+        dest.writeInt(descriptionRes);
+        dest.writeInt(requestRes);
+        TextUtils.writeToParcel(nonLocalizedDescription, dest, parcelableFlags);
+    }
+
+    /** @hide */
+    public int calculateFootprint() {
+        int size = name.length();
+        if (nonLocalizedLabel != null) {
+            size += nonLocalizedLabel.length();
+        }
+        if (nonLocalizedDescription != null) {
+            size += nonLocalizedDescription.length();
+        }
+        return size;
+    }
+
+    /** @hide */
+    public boolean isHardRestricted() {
+        return (flags & PermissionInfo.FLAG_HARD_RESTRICTED) != 0;
+    }
+
+    /** @hide */
+    public boolean isSoftRestricted() {
+        return (flags & PermissionInfo.FLAG_SOFT_RESTRICTED) != 0;
+    }
+
+    /** @hide */
+    public boolean isRestricted() {
+        return isHardRestricted() || isSoftRestricted();
+    }
+
+    /** @hide */
+    public boolean isAppOp() {
+        return (protectionLevel & PermissionInfo.PROTECTION_FLAG_APPOP) != 0;
+    }
+
+    /** @hide */
+    public boolean isRuntime() {
+        return getProtection() == PROTECTION_DANGEROUS;
+    }
+
+    public static final @NonNull Creator<PermissionInfo> CREATOR =
+        new Creator<PermissionInfo>() {
+        @Override
+        public PermissionInfo createFromParcel(Parcel source) {
+            return new PermissionInfo(source);
+        }
+        @Override
+        public PermissionInfo[] newArray(int size) {
+            return new PermissionInfo[size];
+        }
+    };
+
+    private PermissionInfo(Parcel source) {
+        super(source);
+        protectionLevel = source.readInt();
+        flags = source.readInt();
+        group = source.readString8();
+        backgroundPermission = source.readString8();
+        descriptionRes = source.readInt();
+        requestRes = source.readInt();
+        nonLocalizedDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+    }
+}
diff --git a/android/content/pm/ProcessInfo.java b/android/content/pm/ProcessInfo.java
new file mode 100644
index 0000000..d45ff98
--- /dev/null
+++ b/android/content/pm/ProcessInfo.java
@@ -0,0 +1,182 @@
+/*
+ * 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.content.pm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.ApplicationInfo;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+import android.util.ArraySet;
+
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling;
+
+/**
+ * Information about a process an app may run.  This corresponds to information collected from the
+ * AndroidManifest.xml's &lt;permission-group&gt; tags.
+ * @hide
+ */
+@DataClass(genGetters = true, genSetters = false, genParcelable = true, genAidl = false,
+        genBuilder = false)
+public class ProcessInfo implements Parcelable {
+    /**
+     * The name of the process, fully-qualified based on the app's package name.
+     */
+    @NonNull
+    public String name;
+
+    /**
+     * If non-null, these are permissions that are not allowed in this process.
+     */
+    @Nullable
+    @DataClass.ParcelWith(Parcelling.BuiltIn.ForInternedStringArraySet.class)
+    public ArraySet<String> deniedPermissions;
+
+    /**
+     * Indicates if the process has requested GWP-ASan to be enabled, disabled, or left unspecified.
+     */
+    public @ApplicationInfo.GwpAsanMode int gwpAsanMode;
+
+    @Deprecated
+    public ProcessInfo(@NonNull ProcessInfo orig) {
+        this.name = orig.name;
+        this.deniedPermissions = orig.deniedPermissions;
+        this.gwpAsanMode = orig.gwpAsanMode;
+    }
+
+
+
+    // Code below generated by codegen v1.0.15.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/ProcessInfo.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    /**
+     * Creates a new ProcessInfo.
+     *
+     * @param name
+     *   The name of the process, fully-qualified based on the app's package name.
+     * @param deniedPermissions
+     *   If non-null, these are permissions that are not allowed in this process.
+     * @param gwpAsanMode
+     *   Indicates if the process has requested GWP-ASan to be enabled, disabled, or left unspecified.
+     */
+    @DataClass.Generated.Member
+    public ProcessInfo(
+            @NonNull String name,
+            @Nullable ArraySet<String> deniedPermissions,
+            @ApplicationInfo.GwpAsanMode int gwpAsanMode) {
+        this.name = name;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, name);
+        this.deniedPermissions = deniedPermissions;
+        this.gwpAsanMode = gwpAsanMode;
+        com.android.internal.util.AnnotationValidations.validate(
+                ApplicationInfo.GwpAsanMode.class, null, gwpAsanMode);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    static Parcelling<ArraySet<String>> sParcellingForDeniedPermissions =
+            Parcelling.Cache.get(
+                    Parcelling.BuiltIn.ForInternedStringArraySet.class);
+    static {
+        if (sParcellingForDeniedPermissions == null) {
+            sParcellingForDeniedPermissions = Parcelling.Cache.put(
+                    new Parcelling.BuiltIn.ForInternedStringArraySet());
+        }
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        byte flg = 0;
+        if (deniedPermissions != null) flg |= 0x2;
+        dest.writeByte(flg);
+        dest.writeString(name);
+        sParcellingForDeniedPermissions.parcel(deniedPermissions, dest, flags);
+        dest.writeInt(gwpAsanMode);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    protected ProcessInfo(@NonNull Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        byte flg = in.readByte();
+        String _name = in.readString();
+        ArraySet<String> _deniedPermissions = sParcellingForDeniedPermissions.unparcel(in);
+        int _gwpAsanMode = in.readInt();
+
+        this.name = _name;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, name);
+        this.deniedPermissions = _deniedPermissions;
+        this.gwpAsanMode = _gwpAsanMode;
+        com.android.internal.util.AnnotationValidations.validate(
+                ApplicationInfo.GwpAsanMode.class, null, gwpAsanMode);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<ProcessInfo> CREATOR
+            = new Parcelable.Creator<ProcessInfo>() {
+        @Override
+        public ProcessInfo[] newArray(int size) {
+            return new ProcessInfo[size];
+        }
+
+        @Override
+        public ProcessInfo createFromParcel(@NonNull Parcel in) {
+            return new ProcessInfo(in);
+        }
+    };
+
+    @DataClass.Generated(
+            time = 1584555730519L,
+            codegenVersion = "1.0.15",
+            sourceFile = "frameworks/base/core/java/android/content/pm/ProcessInfo.java",
+            inputSignatures = "public @android.annotation.NonNull java.lang.String name\npublic @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedStringArraySet.class) android.util.ArraySet<java.lang.String> deniedPermissions\npublic @android.content.pm.ApplicationInfo.GwpAsanMode int gwpAsanMode\nclass ProcessInfo extends java.lang.Object implements [android.os.Parcelable]\[email protected](genGetters=true, genSetters=false, genParcelable=true, genAidl=false, genBuilder=false)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/android/content/pm/ProviderInfo.java b/android/content/pm/ProviderInfo.java
new file mode 100644
index 0000000..3984ade
--- /dev/null
+++ b/android/content/pm/ProviderInfo.java
@@ -0,0 +1,190 @@
+/*
+ * 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.content.pm;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.PatternMatcher;
+import android.util.Printer;
+
+/**
+ * Holds information about a specific
+ * {@link android.content.ContentProvider content provider}. This is returned by
+ * {@link android.content.pm.PackageManager#resolveContentProvider(java.lang.String, int)
+ * PackageManager.resolveContentProvider()}.
+ */
+public final class ProviderInfo extends ComponentInfo
+        implements Parcelable {
+    
+    /** The name provider is published under content:// */
+    public String authority = null;
+    
+    /** Optional permission required for read-only access this content
+     * provider. */
+    public String readPermission = null;
+    
+    /** Optional permission required for read/write access this content
+     * provider. */
+    public String writePermission = null;
+    
+    /** If true, additional permissions to specific Uris in this content
+     * provider can be granted, as per the
+     * {@link android.R.styleable#AndroidManifestProvider_grantUriPermissions
+     * grantUriPermissions} attribute.
+     */
+    public boolean grantUriPermissions = false;
+
+    /** If true, always apply URI permission grants, as per the
+     * {@link android.R.styleable#AndroidManifestProvider_forceUriPermissions
+     * forceUriPermissions} attribute.
+     */
+    public boolean forceUriPermissions = false;
+
+    /**
+     * If non-null, these are the patterns that are allowed for granting URI
+     * permissions.  Any URI that does not match one of these patterns will not
+     * allowed to be granted.  If null, all URIs are allowed.  The
+     * {@link PackageManager#GET_URI_PERMISSION_PATTERNS
+     * PackageManager.GET_URI_PERMISSION_PATTERNS} flag must be specified for
+     * this field to be filled in.
+     */
+    public PatternMatcher[] uriPermissionPatterns = null;
+    
+    /**
+     * If non-null, these are path-specific permissions that are allowed for
+     * accessing the provider.  Any permissions listed here will allow a
+     * holding client to access the provider, and the provider will check
+     * the URI it provides when making calls against the patterns here.
+     */
+    public PathPermission[] pathPermissions = null;
+    
+    /** If true, this content provider allows multiple instances of itself
+     *  to run in different process.  If false, a single instances is always
+     *  run in {@link #processName}. */
+    public boolean multiprocess = false;
+    
+    /** Used to control initialization order of single-process providers
+     *  running in the same process.  Higher goes first. */
+    public int initOrder = 0;
+
+    /**
+     * Bit in {@link #flags} indicating if the provider is visible to ephemeral applications.
+     * @hide
+     */
+    public static final int FLAG_VISIBLE_TO_INSTANT_APP = 0x100000;
+
+    /**
+     * Bit in {@link #flags}: If set, a single instance of the provider will
+     * run for all users on the device.  Set from the
+     * {@link android.R.attr#singleUser} attribute.
+     */
+    public static final int FLAG_SINGLE_USER = 0x40000000;
+
+    /**
+     * Options that have been set in the provider declaration in the
+     * manifest.
+     * These include: {@link #FLAG_SINGLE_USER}.
+     */
+    public int flags = 0;
+
+    /**
+     * Whether or not this provider is syncable.
+     * @deprecated This flag is now being ignored. The current way to make a provider
+     * syncable is to provide a SyncAdapter service for a given provider/account type. 
+     */
+    @Deprecated
+    public boolean isSyncable = false;
+
+    public ProviderInfo() {
+    }
+
+    public ProviderInfo(ProviderInfo orig) {
+        super(orig);
+        authority = orig.authority;
+        readPermission = orig.readPermission;
+        writePermission = orig.writePermission;
+        grantUriPermissions = orig.grantUriPermissions;
+        forceUriPermissions = orig.forceUriPermissions;
+        uriPermissionPatterns = orig.uriPermissionPatterns;
+        pathPermissions = orig.pathPermissions;
+        multiprocess = orig.multiprocess;
+        initOrder = orig.initOrder;
+        flags = orig.flags;
+        isSyncable = orig.isSyncable;
+    }
+
+    public void dump(Printer pw, String prefix) {
+        dump(pw, prefix, DUMP_FLAG_ALL);
+    }
+
+    /** @hide */
+    public void dump(Printer pw, String prefix, int dumpFlags) {
+        super.dumpFront(pw, prefix);
+        pw.println(prefix + "authority=" + authority);
+        pw.println(prefix + "flags=0x" + Integer.toHexString(flags));
+        super.dumpBack(pw, prefix, dumpFlags);
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override public void writeToParcel(Parcel out, int parcelableFlags) {
+        super.writeToParcel(out, parcelableFlags);
+        out.writeString8(authority);
+        out.writeString8(readPermission);
+        out.writeString8(writePermission);
+        out.writeInt(grantUriPermissions ? 1 : 0);
+        out.writeInt(forceUriPermissions ? 1 : 0);
+        out.writeTypedArray(uriPermissionPatterns, parcelableFlags);
+        out.writeTypedArray(pathPermissions, parcelableFlags);
+        out.writeInt(multiprocess ? 1 : 0);
+        out.writeInt(initOrder);
+        out.writeInt(flags);
+        out.writeInt(isSyncable ? 1 : 0);
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<ProviderInfo> CREATOR
+            = new Parcelable.Creator<ProviderInfo>() {
+        public ProviderInfo createFromParcel(Parcel in) {
+            return new ProviderInfo(in);
+        }
+
+        public ProviderInfo[] newArray(int size) {
+            return new ProviderInfo[size];
+        }
+    };
+
+    public String toString() {
+        return "ContentProviderInfo{name=" + authority + " className=" + name + "}";
+    }
+
+    private ProviderInfo(Parcel in) {
+        super(in);
+        authority = in.readString8();
+        readPermission = in.readString8();
+        writePermission = in.readString8();
+        grantUriPermissions = in.readInt() != 0;
+        forceUriPermissions = in.readInt() != 0;
+        uriPermissionPatterns = in.createTypedArray(PatternMatcher.CREATOR);
+        pathPermissions = in.createTypedArray(PathPermission.CREATOR);
+        multiprocess = in.readInt() != 0;
+        initOrder = in.readInt();
+        flags = in.readInt();
+        isSyncable = in.readInt() != 0;
+    }
+}
diff --git a/android/content/pm/ProviderInfoList.java b/android/content/pm/ProviderInfoList.java
new file mode 100644
index 0000000..566be2e
--- /dev/null
+++ b/android/content/pm/ProviderInfoList.java
@@ -0,0 +1,87 @@
+/*
+ * 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.content.pm;
+
+import android.annotation.NonNull;
+import android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Equivalent to List<ProviderInfo>, but it "squashes" the ApplicationInfo in the elements.
+ *
+ * @hide
+ */
+@TestApi
+public final class ProviderInfoList implements Parcelable {
+    private final List<ProviderInfo> mList;
+
+    private ProviderInfoList(Parcel source) {
+        final ArrayList<ProviderInfo> list = new ArrayList<>();
+        source.readTypedList(list, ProviderInfo.CREATOR);
+        mList = list;
+    }
+
+    private ProviderInfoList(List<ProviderInfo> list) {
+        mList = list;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        // Allow ApplicationInfo to be squashed.
+        final boolean prevAllowSquashing = dest.allowSquashing();
+        dest.writeTypedList(mList, flags);
+        dest.restoreAllowSquashing(prevAllowSquashing);
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<ProviderInfoList> CREATOR
+            = new Parcelable.Creator<ProviderInfoList>() {
+        @Override
+        public ProviderInfoList createFromParcel(@NonNull Parcel source) {
+            return new ProviderInfoList(source);
+        }
+
+        @Override
+        public ProviderInfoList[] newArray(int size) {
+            return new ProviderInfoList[size];
+        }
+    };
+
+    /**
+     * Return the stored list.
+     */
+    @NonNull
+    public List<ProviderInfo> getList() {
+        return mList;
+    }
+
+    /**
+     * Create a new instance with a {@code list}. The passed list will be shared with the new
+     * instance, so the caller shouldn't modify it.
+     */
+    @NonNull
+    public static ProviderInfoList fromList(@NonNull List<ProviderInfo> list) {
+        return new ProviderInfoList(list);
+    }
+}
diff --git a/android/content/pm/RegisteredServicesCache.java b/android/content/pm/RegisteredServicesCache.java
new file mode 100644
index 0000000..bd909c7
--- /dev/null
+++ b/android/content/pm/RegisteredServicesCache.java
@@ -0,0 +1,827 @@
+/*
+ * 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.content.pm;
+
+import android.Manifest;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.AtomicFile;
+import android.util.AttributeSet;
+import android.util.IntArray;
+import android.util.Log;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.Xml;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.FastXmlSerializer;
+
+import libcore.io.IoUtils;
+
+import com.google.android.collect.Lists;
+import com.google.android.collect.Maps;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Cache of registered services. This cache is lazily built by interrogating
+ * {@link PackageManager} on a per-user basis. It's updated as packages are
+ * added, removed and changed. Users are responsible for calling
+ * {@link #invalidateCache(int)} when a user is started, since
+ * {@link PackageManager} broadcasts aren't sent for stopped users.
+ * <p>
+ * The services are referred to by type V and are made available via the
+ * {@link #getServiceInfo} method.
+ *
+ * @hide
+ */
+public abstract class RegisteredServicesCache<V> {
+    private static final String TAG = "PackageManager";
+    private static final boolean DEBUG = false;
+    protected static final String REGISTERED_SERVICES_DIR = "registered_services";
+
+    public final Context mContext;
+    private final String mInterfaceName;
+    private final String mMetaDataName;
+    private final String mAttributesName;
+    private final XmlSerializerAndParser<V> mSerializerAndParser;
+
+    protected final Object mServicesLock = new Object();
+
+    @GuardedBy("mServicesLock")
+    private final SparseArray<UserServices<V>> mUserServices = new SparseArray<UserServices<V>>(2);
+
+    private static class UserServices<V> {
+        @GuardedBy("mServicesLock")
+        final Map<V, Integer> persistentServices = Maps.newHashMap();
+        @GuardedBy("mServicesLock")
+        Map<V, ServiceInfo<V>> services = null;
+        @GuardedBy("mServicesLock")
+        boolean mPersistentServicesFileDidNotExist = true;
+        @GuardedBy("mServicesLock")
+        boolean mBindInstantServiceAllowed = false;
+    }
+
+    @GuardedBy("mServicesLock")
+    private UserServices<V> findOrCreateUserLocked(int userId) {
+        return findOrCreateUserLocked(userId, true);
+    }
+
+    @GuardedBy("mServicesLock")
+    private UserServices<V> findOrCreateUserLocked(int userId, boolean loadFromFileIfNew) {
+        UserServices<V> services = mUserServices.get(userId);
+        if (services == null) {
+            services = new UserServices<V>();
+            mUserServices.put(userId, services);
+            if (loadFromFileIfNew && mSerializerAndParser != null) {
+                // Check if user exists and try loading data from file
+                // clear existing data if there was an error during migration
+                UserInfo user = getUser(userId);
+                if (user != null) {
+                    AtomicFile file = createFileForUser(user.id);
+                    if (file.getBaseFile().exists()) {
+                        if (DEBUG) {
+                            Slog.i(TAG, String.format("Loading u%s data from %s", user.id, file));
+                        }
+                        InputStream is = null;
+                        try {
+                            is = file.openRead();
+                            readPersistentServicesLocked(is);
+                        } catch (Exception e) {
+                            Log.w(TAG, "Error reading persistent services for user " + user.id, e);
+                        } finally {
+                            IoUtils.closeQuietly(is);
+                        }
+                    }
+                }
+            }
+        }
+        return services;
+    }
+
+    // the listener and handler are synchronized on "this" and must be updated together
+    private RegisteredServicesCacheListener<V> mListener;
+    private Handler mHandler;
+
+    @UnsupportedAppUsage
+    public RegisteredServicesCache(Context context, String interfaceName, String metaDataName,
+            String attributeName, XmlSerializerAndParser<V> serializerAndParser) {
+        mContext = context;
+        mInterfaceName = interfaceName;
+        mMetaDataName = metaDataName;
+        mAttributesName = attributeName;
+        mSerializerAndParser = serializerAndParser;
+
+        migrateIfNecessaryLocked();
+
+        IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+        intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+        intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+        intentFilter.addDataScheme("package");
+        mContext.registerReceiverAsUser(mPackageReceiver, UserHandle.ALL, intentFilter, null, null);
+
+        // Register for events related to sdcard installation.
+        IntentFilter sdFilter = new IntentFilter();
+        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
+        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
+        mContext.registerReceiver(mExternalReceiver, sdFilter);
+
+        // Register for user-related events
+        IntentFilter userFilter = new IntentFilter();
+        sdFilter.addAction(Intent.ACTION_USER_REMOVED);
+        mContext.registerReceiver(mUserRemovedReceiver, userFilter);
+    }
+
+    private void handlePackageEvent(Intent intent, int userId) {
+        // Don't regenerate the services map when the package is removed or its
+        // ASEC container unmounted as a step in replacement.  The subsequent
+        // _ADDED / _AVAILABLE call will regenerate the map in the final state.
+        final String action = intent.getAction();
+        // it's a new-component action if it isn't some sort of removal
+        final boolean isRemoval = Intent.ACTION_PACKAGE_REMOVED.equals(action)
+                || Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action);
+        // if it's a removal, is it part of an update-in-place step?
+        final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
+
+        if (isRemoval && replacing) {
+            // package is going away, but it's the middle of an upgrade: keep the current
+            // state and do nothing here.  This clause is intentionally empty.
+        } else {
+            int[] uids = null;
+            // either we're adding/changing, or it's a removal without replacement, so
+            // we need to update the set of available services
+            if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)
+                    || Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
+                uids = intent.getIntArrayExtra(Intent.EXTRA_CHANGED_UID_LIST);
+            } else {
+                int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+                if (uid > 0) {
+                    uids = new int[] { uid };
+                }
+            }
+            generateServicesMap(uids, userId);
+        }
+    }
+
+    private final BroadcastReceiver mPackageReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+            if (uid != -1) {
+                handlePackageEvent(intent, UserHandle.getUserId(uid));
+            }
+        }
+    };
+
+    private final BroadcastReceiver mExternalReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            // External apps can't coexist with multi-user, so scan owner
+            handlePackageEvent(intent, UserHandle.USER_SYSTEM);
+        }
+    };
+
+    private final BroadcastReceiver mUserRemovedReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
+            if (DEBUG) {
+                Slog.d(TAG, "u" + userId + " removed - cleaning up");
+            }
+            onUserRemoved(userId);
+        }
+    };
+
+    public void invalidateCache(int userId) {
+        synchronized (mServicesLock) {
+            final UserServices<V> user = findOrCreateUserLocked(userId);
+            user.services = null;
+            onServicesChangedLocked(userId);
+        }
+    }
+
+    public void dump(FileDescriptor fd, PrintWriter fout, String[] args, int userId) {
+        synchronized (mServicesLock) {
+            final UserServices<V> user = findOrCreateUserLocked(userId);
+            if (user.services != null) {
+                fout.println("RegisteredServicesCache: " + user.services.size() + " services");
+                for (ServiceInfo<?> info : user.services.values()) {
+                    fout.println("  " + info);
+                }
+            } else {
+                fout.println("RegisteredServicesCache: services not loaded");
+            }
+        }
+    }
+
+    public RegisteredServicesCacheListener<V> getListener() {
+        synchronized (this) {
+            return mListener;
+        }
+    }
+
+    public void setListener(RegisteredServicesCacheListener<V> listener, Handler handler) {
+        if (handler == null) {
+            handler = new Handler(mContext.getMainLooper());
+        }
+        synchronized (this) {
+            mHandler = handler;
+            mListener = listener;
+        }
+    }
+
+    private void notifyListener(final V type, final int userId, final boolean removed) {
+        if (DEBUG) {
+            Log.d(TAG, "notifyListener: " + type + " is " + (removed ? "removed" : "added"));
+        }
+        RegisteredServicesCacheListener<V> listener;
+        Handler handler;
+        synchronized (this) {
+            listener = mListener;
+            handler = mHandler;
+        }
+        if (listener == null) {
+            return;
+        }
+
+        final RegisteredServicesCacheListener<V> listener2 = listener;
+        handler.post(() -> {
+            try {
+                listener2.onServiceChanged(type, userId, removed);
+            } catch (Throwable th) {
+                Slog.wtf(TAG, "Exception from onServiceChanged", th);
+            }
+        });
+    }
+
+    /**
+     * Value type that describes a Service. The information within can be used
+     * to bind to the service.
+     */
+    public static class ServiceInfo<V> {
+        @UnsupportedAppUsage
+        public final V type;
+        public final ComponentInfo componentInfo;
+        @UnsupportedAppUsage
+        public final ComponentName componentName;
+        @UnsupportedAppUsage
+        public final int uid;
+
+        /** @hide */
+        public ServiceInfo(V type, ComponentInfo componentInfo, ComponentName componentName) {
+            this.type = type;
+            this.componentInfo = componentInfo;
+            this.componentName = componentName;
+            this.uid = (componentInfo != null) ? componentInfo.applicationInfo.uid : -1;
+        }
+
+        @Override
+        public String toString() {
+            return "ServiceInfo: " + type + ", " + componentName + ", uid " + uid;
+        }
+    }
+
+    /**
+     * Accessor for the registered authenticators.
+     * @param type the account type of the authenticator
+     * @return the AuthenticatorInfo that matches the account type or null if none is present
+     */
+    public ServiceInfo<V> getServiceInfo(V type, int userId) {
+        synchronized (mServicesLock) {
+            // Find user and lazily populate cache
+            final UserServices<V> user = findOrCreateUserLocked(userId);
+            if (user.services == null) {
+                generateServicesMap(null, userId);
+            }
+            return user.services.get(type);
+        }
+    }
+
+    /**
+     * @return a collection of {@link RegisteredServicesCache.ServiceInfo} objects for all
+     * registered authenticators.
+     */
+    public Collection<ServiceInfo<V>> getAllServices(int userId) {
+        synchronized (mServicesLock) {
+            // Find user and lazily populate cache
+            final UserServices<V> user = findOrCreateUserLocked(userId);
+            if (user.services == null) {
+                generateServicesMap(null, userId);
+            }
+            return Collections.unmodifiableCollection(
+                    new ArrayList<ServiceInfo<V>>(user.services.values()));
+        }
+    }
+
+    public void updateServices(int userId) {
+        if (DEBUG) {
+            Slog.d(TAG, "updateServices u" + userId);
+        }
+        List<ServiceInfo<V>> allServices;
+        synchronized (mServicesLock) {
+            final UserServices<V> user = findOrCreateUserLocked(userId);
+            // If services haven't been initialized yet - no updates required
+            if (user.services == null) {
+                return;
+            }
+            allServices = new ArrayList<>(user.services.values());
+        }
+        IntArray updatedUids = null;
+        for (ServiceInfo<V> service : allServices) {
+            long versionCode = service.componentInfo.applicationInfo.versionCode;
+            String pkg = service.componentInfo.packageName;
+            ApplicationInfo newAppInfo = null;
+            try {
+                newAppInfo = mContext.getPackageManager().getApplicationInfoAsUser(pkg, 0, userId);
+            } catch (NameNotFoundException e) {
+                // Package uninstalled - treat as null app info
+            }
+            // If package updated or removed
+            if ((newAppInfo == null) || (newAppInfo.versionCode != versionCode)) {
+                if (DEBUG) {
+                    Slog.d(TAG, "Package " + pkg + " uid=" + service.uid
+                            + " updated. New appInfo: " + newAppInfo);
+                }
+                if (updatedUids == null) {
+                    updatedUids = new IntArray();
+                }
+                updatedUids.add(service.uid);
+            }
+        }
+        if (updatedUids != null && updatedUids.size() > 0) {
+            int[] updatedUidsArray = updatedUids.toArray();
+            generateServicesMap(updatedUidsArray, userId);
+        }
+    }
+
+    /**
+     * @return whether the binding to service is allowed for instant apps.
+     */
+    public boolean getBindInstantServiceAllowed(int userId) {
+        mContext.enforceCallingOrSelfPermission(
+                Manifest.permission.MANAGE_BIND_INSTANT_SERVICE,
+                "getBindInstantServiceAllowed");
+
+        synchronized (mServicesLock) {
+            final UserServices<V> user = findOrCreateUserLocked(userId);
+            return user.mBindInstantServiceAllowed;
+        }
+    }
+
+    /**
+     * Set whether the binding to service is allowed or not for instant apps.
+     */
+    public void setBindInstantServiceAllowed(int userId, boolean allowed) {
+        mContext.enforceCallingOrSelfPermission(
+                Manifest.permission.MANAGE_BIND_INSTANT_SERVICE,
+                "setBindInstantServiceAllowed");
+
+        synchronized (mServicesLock) {
+            final UserServices<V> user = findOrCreateUserLocked(userId);
+            user.mBindInstantServiceAllowed = allowed;
+        }
+    }
+
+    @VisibleForTesting
+    protected boolean inSystemImage(int callerUid) {
+        String[] packages = mContext.getPackageManager().getPackagesForUid(callerUid);
+        if (packages != null) {
+            for (String name : packages) {
+                try {
+                    PackageInfo packageInfo =
+                            mContext.getPackageManager().getPackageInfo(name, 0 /* flags */);
+                    if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+                        return true;
+                    }
+                } catch (PackageManager.NameNotFoundException e) {
+                    return false;
+                }
+            }
+        }
+        return false;
+    }
+
+    @VisibleForTesting
+    protected List<ResolveInfo> queryIntentServices(int userId) {
+        final PackageManager pm = mContext.getPackageManager();
+        int flags = PackageManager.GET_META_DATA
+                | PackageManager.MATCH_DIRECT_BOOT_AWARE
+                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+        synchronized (mServicesLock) {
+            final UserServices<V> user = findOrCreateUserLocked(userId);
+            if (user.mBindInstantServiceAllowed) {
+                flags |= PackageManager.MATCH_INSTANT;
+            }
+        }
+        return pm.queryIntentServicesAsUser(new Intent(mInterfaceName), flags, userId);
+    }
+
+    /**
+     * Populate {@link UserServices#services} by scanning installed packages for
+     * given {@link UserHandle}.
+     * @param changedUids the array of uids that have been affected, as mentioned in the broadcast
+     *                    or null to assume that everything is affected.
+     * @param userId the user for whom to update the services map.
+     */
+    private void generateServicesMap(int[] changedUids, int userId) {
+        if (DEBUG) {
+            Slog.d(TAG, "generateServicesMap() for " + userId + ", changed UIDs = "
+                    + Arrays.toString(changedUids));
+        }
+
+        final ArrayList<ServiceInfo<V>> serviceInfos = new ArrayList<>();
+        final List<ResolveInfo> resolveInfos = queryIntentServices(userId);
+        for (ResolveInfo resolveInfo : resolveInfos) {
+            try {
+                ServiceInfo<V> info = parseServiceInfo(resolveInfo);
+                if (info == null) {
+                    Log.w(TAG, "Unable to load service info " + resolveInfo.toString());
+                    continue;
+                }
+                serviceInfos.add(info);
+            } catch (XmlPullParserException | IOException e) {
+                Log.w(TAG, "Unable to load service info " + resolveInfo.toString(), e);
+            }
+        }
+
+        synchronized (mServicesLock) {
+            final UserServices<V> user = findOrCreateUserLocked(userId);
+            final boolean firstScan = user.services == null;
+            if (firstScan) {
+                user.services = Maps.newHashMap();
+            }
+
+            StringBuilder changes = new StringBuilder();
+            boolean changed = false;
+            for (ServiceInfo<V> info : serviceInfos) {
+                // four cases:
+                // - doesn't exist yet
+                //   - add, notify user that it was added
+                // - exists and the UID is the same
+                //   - replace, don't notify user
+                // - exists, the UID is different, and the new one is not a system package
+                //   - ignore
+                // - exists, the UID is different, and the new one is a system package
+                //   - add, notify user that it was added
+                Integer previousUid = user.persistentServices.get(info.type);
+                if (previousUid == null) {
+                    if (DEBUG) {
+                        changes.append("  New service added: ").append(info).append("\n");
+                    }
+                    changed = true;
+                    user.services.put(info.type, info);
+                    user.persistentServices.put(info.type, info.uid);
+                    if (!(user.mPersistentServicesFileDidNotExist && firstScan)) {
+                        notifyListener(info.type, userId, false /* removed */);
+                    }
+                } else if (previousUid == info.uid) {
+                    if (DEBUG) {
+                        changes.append("  Existing service (nop): ").append(info).append("\n");
+                    }
+                    user.services.put(info.type, info);
+                } else if (inSystemImage(info.uid)
+                        || !containsTypeAndUid(serviceInfos, info.type, previousUid)) {
+                    if (DEBUG) {
+                        if (inSystemImage(info.uid)) {
+                            changes.append("  System service replacing existing: ").append(info)
+                                    .append("\n");
+                        } else {
+                            changes.append("  Existing service replacing a removed service: ")
+                                    .append(info).append("\n");
+                        }
+                    }
+                    changed = true;
+                    user.services.put(info.type, info);
+                    user.persistentServices.put(info.type, info.uid);
+                    notifyListener(info.type, userId, false /* removed */);
+                } else {
+                    // ignore
+                    if (DEBUG) {
+                        changes.append("  Existing service with new uid ignored: ").append(info)
+                                .append("\n");
+                    }
+                }
+            }
+
+            ArrayList<V> toBeRemoved = Lists.newArrayList();
+            for (V v1 : user.persistentServices.keySet()) {
+                // Remove a persisted service that's not in the currently available services list.
+                // And only if it is in the list of changedUids.
+                if (!containsType(serviceInfos, v1)
+                        && containsUid(changedUids, user.persistentServices.get(v1))) {
+                    toBeRemoved.add(v1);
+                }
+            }
+            for (V v1 : toBeRemoved) {
+                if (DEBUG) {
+                    changes.append("  Service removed: ").append(v1).append("\n");
+                }
+                changed = true;
+                user.persistentServices.remove(v1);
+                user.services.remove(v1);
+                notifyListener(v1, userId, true /* removed */);
+            }
+            if (DEBUG) {
+                Log.d(TAG, "user.services=");
+                for (V v : user.services.keySet()) {
+                    Log.d(TAG, "  " + v + " " + user.services.get(v));
+                }
+                Log.d(TAG, "user.persistentServices=");
+                for (V v : user.persistentServices.keySet()) {
+                    Log.d(TAG, "  " + v + " " + user.persistentServices.get(v));
+                }
+            }
+            if (DEBUG) {
+                if (changes.length() > 0) {
+                    Log.d(TAG, "generateServicesMap(" + mInterfaceName + "): " +
+                            serviceInfos.size() + " services:\n" + changes);
+                } else {
+                    Log.d(TAG, "generateServicesMap(" + mInterfaceName + "): " +
+                            serviceInfos.size() + " services unchanged");
+                }
+            }
+            if (changed) {
+                onServicesChangedLocked(userId);
+                writePersistentServicesLocked(user, userId);
+            }
+        }
+    }
+
+    protected void onServicesChangedLocked(int userId) {
+        // Feel free to override
+    }
+
+    /**
+     * Returns true if the list of changed uids is null (wildcard) or the specified uid
+     * is contained in the list of changed uids.
+     */
+    private boolean containsUid(int[] changedUids, int uid) {
+        return changedUids == null || ArrayUtils.contains(changedUids, uid);
+    }
+
+    private boolean containsType(ArrayList<ServiceInfo<V>> serviceInfos, V type) {
+        for (int i = 0, N = serviceInfos.size(); i < N; i++) {
+            if (serviceInfos.get(i).type.equals(type)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    private boolean containsTypeAndUid(ArrayList<ServiceInfo<V>> serviceInfos, V type, int uid) {
+        for (int i = 0, N = serviceInfos.size(); i < N; i++) {
+            final ServiceInfo<V> serviceInfo = serviceInfos.get(i);
+            if (serviceInfo.type.equals(type) && serviceInfo.uid == uid) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    @VisibleForTesting
+    protected ServiceInfo<V> parseServiceInfo(ResolveInfo service)
+            throws XmlPullParserException, IOException {
+        android.content.pm.ServiceInfo si = service.serviceInfo;
+        ComponentName componentName = new ComponentName(si.packageName, si.name);
+
+        PackageManager pm = mContext.getPackageManager();
+
+        XmlResourceParser parser = null;
+        try {
+            parser = si.loadXmlMetaData(pm, mMetaDataName);
+            if (parser == null) {
+                throw new XmlPullParserException("No " + mMetaDataName + " meta-data");
+            }
+
+            AttributeSet attrs = Xml.asAttributeSet(parser);
+
+            int type;
+            while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
+                    && type != XmlPullParser.START_TAG) {
+            }
+
+            String nodeName = parser.getName();
+            if (!mAttributesName.equals(nodeName)) {
+                throw new XmlPullParserException(
+                        "Meta-data does not start with " + mAttributesName +  " tag");
+            }
+
+            V v = parseServiceAttributes(pm.getResourcesForApplication(si.applicationInfo),
+                    si.packageName, attrs);
+            if (v == null) {
+                return null;
+            }
+            final android.content.pm.ServiceInfo serviceInfo = service.serviceInfo;
+            return new ServiceInfo<V>(v, serviceInfo, componentName);
+        } catch (NameNotFoundException e) {
+            throw new XmlPullParserException(
+                    "Unable to load resources for pacakge " + si.packageName);
+        } finally {
+            if (parser != null) parser.close();
+        }
+    }
+
+    /**
+     * Read all sync status back in to the initial engine state.
+     */
+    private void readPersistentServicesLocked(InputStream is)
+            throws XmlPullParserException, IOException {
+        XmlPullParser parser = Xml.newPullParser();
+        parser.setInput(is, StandardCharsets.UTF_8.name());
+        int eventType = parser.getEventType();
+        while (eventType != XmlPullParser.START_TAG
+                && eventType != XmlPullParser.END_DOCUMENT) {
+            eventType = parser.next();
+        }
+        String tagName = parser.getName();
+        if ("services".equals(tagName)) {
+            eventType = parser.next();
+            do {
+                if (eventType == XmlPullParser.START_TAG && parser.getDepth() == 2) {
+                    tagName = parser.getName();
+                    if ("service".equals(tagName)) {
+                        V service = mSerializerAndParser.createFromXml(parser);
+                        if (service == null) {
+                            break;
+                        }
+                        String uidString = parser.getAttributeValue(null, "uid");
+                        final int uid = Integer.parseInt(uidString);
+                        final int userId = UserHandle.getUserId(uid);
+                        final UserServices<V> user = findOrCreateUserLocked(userId,
+                                false /*loadFromFileIfNew*/) ;
+                        user.persistentServices.put(service, uid);
+                    }
+                }
+                eventType = parser.next();
+            } while (eventType != XmlPullParser.END_DOCUMENT);
+        }
+    }
+
+    private void migrateIfNecessaryLocked() {
+        if (mSerializerAndParser == null) {
+            return;
+        }
+        File systemDir = new File(getDataDirectory(), "system");
+        File syncDir = new File(systemDir, REGISTERED_SERVICES_DIR);
+        AtomicFile oldFile = new AtomicFile(new File(syncDir, mInterfaceName + ".xml"));
+        boolean oldFileExists = oldFile.getBaseFile().exists();
+
+        if (oldFileExists) {
+            File marker = new File(syncDir, mInterfaceName + ".xml.migrated");
+            // if not migrated, perform the migration and add a marker
+            if (!marker.exists()) {
+                if (DEBUG) {
+                    Slog.i(TAG, "Marker file " + marker + " does not exist - running migration");
+                }
+                InputStream is = null;
+                try {
+                    is = oldFile.openRead();
+                    mUserServices.clear();
+                    readPersistentServicesLocked(is);
+                } catch (Exception e) {
+                    Log.w(TAG, "Error reading persistent services, starting from scratch", e);
+                } finally {
+                    IoUtils.closeQuietly(is);
+                }
+                try {
+                    for (UserInfo user : getUsers()) {
+                        UserServices<V> userServices = mUserServices.get(user.id);
+                        if (userServices != null) {
+                            if (DEBUG) {
+                                Slog.i(TAG, "Migrating u" + user.id + " services "
+                                        + userServices.persistentServices);
+                            }
+                            writePersistentServicesLocked(userServices, user.id);
+                        }
+                    }
+                    marker.createNewFile();
+                } catch (Exception e) {
+                    Log.w(TAG, "Migration failed", e);
+                }
+                // Migration is complete and we don't need to keep data for all users anymore,
+                // It will be loaded from a new location when requested
+                mUserServices.clear();
+            }
+        }
+    }
+
+    /**
+     * Writes services of a specified user to the file.
+     */
+    private void writePersistentServicesLocked(UserServices<V> user, int userId) {
+        if (mSerializerAndParser == null) {
+            return;
+        }
+        AtomicFile atomicFile = createFileForUser(userId);
+        FileOutputStream fos = null;
+        try {
+            fos = atomicFile.startWrite();
+            XmlSerializer out = new FastXmlSerializer();
+            out.setOutput(fos, StandardCharsets.UTF_8.name());
+            out.startDocument(null, true);
+            out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
+            out.startTag(null, "services");
+            for (Map.Entry<V, Integer> service : user.persistentServices.entrySet()) {
+                out.startTag(null, "service");
+                out.attribute(null, "uid", Integer.toString(service.getValue()));
+                mSerializerAndParser.writeAsXml(service.getKey(), out);
+                out.endTag(null, "service");
+            }
+            out.endTag(null, "services");
+            out.endDocument();
+            atomicFile.finishWrite(fos);
+        } catch (IOException e1) {
+            Log.w(TAG, "Error writing accounts", e1);
+            if (fos != null) {
+                atomicFile.failWrite(fos);
+            }
+        }
+    }
+
+    @VisibleForTesting
+    protected void onUserRemoved(int userId) {
+        synchronized (mServicesLock) {
+            mUserServices.remove(userId);
+        }
+    }
+
+    @VisibleForTesting
+    protected List<UserInfo> getUsers() {
+        return UserManager.get(mContext).getUsers(true);
+    }
+
+    @VisibleForTesting
+    protected UserInfo getUser(int userId) {
+        return UserManager.get(mContext).getUserInfo(userId);
+    }
+
+    private AtomicFile createFileForUser(int userId) {
+        File userDir = getUserSystemDirectory(userId);
+        File userFile = new File(userDir, REGISTERED_SERVICES_DIR + "/" + mInterfaceName + ".xml");
+        return new AtomicFile(userFile);
+    }
+
+    @VisibleForTesting
+    protected File getUserSystemDirectory(int userId) {
+        return Environment.getUserSystemDirectory(userId);
+    }
+
+    @VisibleForTesting
+    protected File getDataDirectory() {
+        return Environment.getDataDirectory();
+    }
+
+    @VisibleForTesting
+    protected Map<V, Integer> getPersistentServices(int userId) {
+        return findOrCreateUserLocked(userId).persistentServices;
+    }
+
+    public abstract V parseServiceAttributes(Resources res,
+            String packageName, AttributeSet attrs);
+}
diff --git a/android/content/pm/RegisteredServicesCacheListener.java b/android/content/pm/RegisteredServicesCacheListener.java
new file mode 100644
index 0000000..df79544
--- /dev/null
+++ b/android/content/pm/RegisteredServicesCacheListener.java
@@ -0,0 +1,30 @@
+/*
+ * 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.content.pm;
+
+/**
+ * Listener for changes to the set of registered services managed by a RegisteredServicesCache.
+ * @hide
+ */
+public interface RegisteredServicesCacheListener<V> {
+    /**
+     * Invoked when a service is registered or changed.
+     * @param type the type of registered service
+     * @param removed true if the service was removed
+     */
+    void onServiceChanged(V type, int userId, boolean removed);
+}
diff --git a/android/content/pm/ResolveInfo.java b/android/content/pm/ResolveInfo.java
new file mode 100644
index 0000000..4f2bf65
--- /dev/null
+++ b/android/content/pm/ResolveInfo.java
@@ -0,0 +1,529 @@
+/*
+ * 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.content.pm;
+
+import android.annotation.SystemApi;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.ComponentName;
+import android.content.IntentFilter;
+import android.graphics.drawable.Drawable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.Printer;
+import android.util.Slog;
+
+import java.text.Collator;
+import java.util.Comparator;
+
+/**
+ * Information that is returned from resolving an intent
+ * against an IntentFilter. This partially corresponds to
+ * information collected from the AndroidManifest.xml's
+ * &lt;intent&gt; tags.
+ */
+public class ResolveInfo implements Parcelable {
+    private static final String TAG = "ResolveInfo";
+    private static final String INTENT_FORWARDER_ACTIVITY =
+            "com.android.internal.app.IntentForwarderActivity";
+
+    /**
+     * The activity or broadcast receiver that corresponds to this resolution
+     * match, if this resolution is for an activity or broadcast receiver.
+     * Exactly one of {@link #activityInfo}, {@link #serviceInfo}, or
+     * {@link #providerInfo} will be non-null.
+     */
+    public ActivityInfo activityInfo;
+
+    /**
+     * The service that corresponds to this resolution match, if this resolution
+     * is for a service. Exactly one of {@link #activityInfo},
+     * {@link #serviceInfo}, or {@link #providerInfo} will be non-null.
+     */
+    public ServiceInfo serviceInfo;
+
+    /**
+     * The provider that corresponds to this resolution match, if this
+     * resolution is for a provider. Exactly one of {@link #activityInfo},
+     * {@link #serviceInfo}, or {@link #providerInfo} will be non-null.
+     */
+    public ProviderInfo providerInfo;
+
+    /**
+     * An auxiliary response that may modify the resolved information. This is
+     * only set under certain circumstances; such as when resolving instant apps
+     * or components defined in un-installed splits.
+     * @hide
+     */
+    public AuxiliaryResolveInfo auxiliaryInfo;
+
+    /**
+     * Whether or not an instant app is available for the resolved intent.
+     */
+    public boolean isInstantAppAvailable;
+
+    /**
+     * The IntentFilter that was matched for this ResolveInfo.
+     */
+    public IntentFilter filter;
+
+    /**
+     * The declared priority of this match.  Comes from the "priority"
+     * attribute or, if not set, defaults to 0.  Higher values are a higher
+     * priority.
+     */
+    public int priority;
+
+    /**
+     * Order of result according to the user's preference.  If the user
+     * has not set a preference for this result, the value is 0; higher
+     * values are a higher priority.
+     */
+    public int preferredOrder;
+
+    /**
+     * The system's evaluation of how well the activity matches the
+     * IntentFilter.  This is a match constant, a combination of
+     * {@link IntentFilter#MATCH_CATEGORY_MASK IntentFilter.MATCH_CATEGORY_MASK}
+     * and {@link IntentFilter#MATCH_ADJUSTMENT_MASK IntentFiler.MATCH_ADJUSTMENT_MASK}.
+     */
+    public int match;
+
+    /**
+     * Only set when returned by
+     * {@link PackageManager#queryIntentActivityOptions}, this tells you
+     * which of the given specific intents this result came from.  0 is the
+     * first in the list, < 0 means it came from the generic Intent query.
+     */
+    public int specificIndex = -1;
+
+    /**
+     * This filter has specified the Intent.CATEGORY_DEFAULT, meaning it
+     * would like to be considered a default action that the user can
+     * perform on this data.
+     */
+    public boolean isDefault;
+
+    /**
+     * A string resource identifier (in the package's resources) of this
+     * match's label.  From the "label" attribute or, if not set, 0.
+     */
+    public int labelRes;
+
+    /**
+     * The actual string retrieve from <var>labelRes</var> or null if none
+     * was provided.
+     */
+    public CharSequence nonLocalizedLabel;
+
+    /**
+     * A drawable resource identifier (in the package's resources) of this
+     * match's icon.  From the "icon" attribute or, if not set, 0. It is
+     * set only if the icon can be obtained by resource id alone.
+     */
+    public int icon;
+
+    /**
+     * Optional -- if non-null, the {@link #labelRes} and {@link #icon}
+     * resources will be loaded from this package, rather than the one
+     * containing the resolved component.
+     */
+    public String resolvePackageName;
+
+    /**
+     * If not equal to UserHandle.USER_CURRENT, then the intent will be forwarded to this user.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public int targetUserId;
+
+    /**
+     * Set to true if the icon cannot be obtained by resource ids alone.
+     * It is set to true for ResolveInfos from the managed profile: They need to
+     * have their icon badged, so it cannot be obtained by resource ids alone.
+     * @hide
+     */
+    public boolean noResourceId;
+
+    /**
+     * Same as {@link #icon} but it will always correspond to "icon" attribute
+     * regardless of {@link #noResourceId} value.
+     * @hide
+     */
+    public int iconResourceId;
+
+    /**
+     * @hide Target comes from system process?
+     */
+    @UnsupportedAppUsage
+    public boolean system;
+
+    /**
+     * Will be set to {@code true} if the {@link IntentFilter} responsible for intent
+     * resolution is classified as a "browser".
+     *
+     * @hide
+     */
+    @SystemApi
+    public boolean handleAllWebDataURI;
+
+    /** {@hide} */
+    @UnsupportedAppUsage
+    public ComponentInfo getComponentInfo() {
+        if (activityInfo != null) return activityInfo;
+        if (serviceInfo != null) return serviceInfo;
+        if (providerInfo != null) return providerInfo;
+        throw new IllegalStateException("Missing ComponentInfo!");
+    }
+
+    /**
+     * Retrieve the current textual label associated with this resolution.  This
+     * will call back on the given PackageManager to load the label from
+     * the application.
+     *
+     * @param pm A PackageManager from which the label can be loaded; usually
+     * the PackageManager from which you originally retrieved this item.
+     *
+     * @return Returns a CharSequence containing the resolutions's label.  If the
+     * item does not have a label, its name is returned.
+     */
+    public CharSequence loadLabel(PackageManager pm) {
+        if (nonLocalizedLabel != null) {
+            return nonLocalizedLabel;
+        }
+        CharSequence label;
+        if (resolvePackageName != null && labelRes != 0) {
+            label = pm.getText(resolvePackageName, labelRes, null);
+            if (label != null) {
+                return label.toString().trim();
+            }
+        }
+        ComponentInfo ci = getComponentInfo();
+        ApplicationInfo ai = ci.applicationInfo;
+        if (labelRes != 0) {
+            label = pm.getText(ci.packageName, labelRes, ai);
+            if (label != null) {
+                return label.toString().trim();
+            }
+        }
+
+        CharSequence data = ci.loadLabel(pm);
+        // Make the data safe
+        if (data != null) data = data.toString().trim();
+        return data;
+    }
+
+    /**
+     * @return The resource that would be used when loading
+     * the label for this resolve info.
+     *
+     * @hide
+     */
+    public int resolveLabelResId() {
+        if (labelRes != 0) {
+            return labelRes;
+        }
+        final ComponentInfo componentInfo = getComponentInfo();
+        if (componentInfo.labelRes != 0) {
+            return componentInfo.labelRes;
+        }
+        return componentInfo.applicationInfo.labelRes;
+    }
+
+    /**
+     * @return The resource that would be used when loading
+     * the icon for this resolve info.
+     *
+     * @hide
+     */
+    public int resolveIconResId() {
+        if (icon != 0) {
+            return icon;
+        }
+        final ComponentInfo componentInfo = getComponentInfo();
+        if (componentInfo.icon != 0) {
+            return componentInfo.icon;
+        }
+        return componentInfo.applicationInfo.icon;
+    }
+
+    /**
+     * Retrieve the current graphical icon associated with this resolution.  This
+     * will call back on the given PackageManager to load the icon from
+     * the application.
+     *
+     * @param pm A PackageManager from which the icon can be loaded; usually
+     * the PackageManager from which you originally retrieved this item.
+     *
+     * @return Returns a Drawable containing the resolution's icon.  If the
+     * item does not have an icon, the default activity icon is returned.
+     */
+    public Drawable loadIcon(PackageManager pm) {
+        Drawable dr = null;
+        if (resolvePackageName != null && iconResourceId != 0) {
+            dr = pm.getDrawable(resolvePackageName, iconResourceId, null);
+        }
+        ComponentInfo ci = getComponentInfo();
+        if (dr == null && iconResourceId != 0) {
+            ApplicationInfo ai = ci.applicationInfo;
+            dr = pm.getDrawable(ci.packageName, iconResourceId, ai);
+        }
+        if (dr != null) {
+            return pm.getUserBadgedIcon(dr, new UserHandle(pm.getUserId()));
+        }
+        return ci.loadIcon(pm);
+    }
+
+    /**
+     * Return the icon resource identifier to use for this match.  If the
+     * match defines an icon, that is used; else if the activity defines
+     * an icon, that is used; else, the application icon is used.
+     * This function does not check noResourceId flag.
+     *
+     * @return The icon associated with this match.
+     */
+    final int getIconResourceInternal() {
+        if (iconResourceId != 0) return iconResourceId;
+        final ComponentInfo ci = getComponentInfo();
+        if (ci != null) {
+            return ci.getIconResource();
+        }
+        return 0;
+    }
+
+    /**
+     * Return the icon resource identifier to use for this match.  If the
+     * match defines an icon, that is used; else if the activity defines
+     * an icon, that is used; else, the application icon is used.
+     *
+     * @return The icon associated with this match.
+     */
+    public final int getIconResource() {
+        if (noResourceId) return 0;
+        return getIconResourceInternal();
+    }
+
+    public void dump(Printer pw, String prefix) {
+        dump(pw, prefix, PackageItemInfo.DUMP_FLAG_ALL);
+    }
+
+    /** @hide */
+    public void dump(Printer pw, String prefix, int dumpFlags) {
+        if (filter != null) {
+            pw.println(prefix + "Filter:");
+            filter.dump(pw, prefix + "  ");
+        }
+        pw.println(prefix + "priority=" + priority
+                + " preferredOrder=" + preferredOrder
+                + " match=0x" + Integer.toHexString(match)
+                + " specificIndex=" + specificIndex
+                + " isDefault=" + isDefault);
+        if (resolvePackageName != null) {
+            pw.println(prefix + "resolvePackageName=" + resolvePackageName);
+        }
+        if (labelRes != 0 || nonLocalizedLabel != null || icon != 0) {
+            pw.println(prefix + "labelRes=0x" + Integer.toHexString(labelRes)
+                    + " nonLocalizedLabel=" + nonLocalizedLabel
+                    + " icon=0x" + Integer.toHexString(icon));
+        }
+        if (activityInfo != null) {
+            pw.println(prefix + "ActivityInfo:");
+            activityInfo.dump(pw, prefix + "  ", dumpFlags);
+        } else if (serviceInfo != null) {
+            pw.println(prefix + "ServiceInfo:");
+            serviceInfo.dump(pw, prefix + "  ", dumpFlags);
+        } else if (providerInfo != null) {
+            pw.println(prefix + "ProviderInfo:");
+            providerInfo.dump(pw, prefix + "  ", dumpFlags);
+        }
+    }
+
+    /**
+     * Returns whether this resolution represents the intent forwarder activity.
+     *
+     * @return whether this resolution represents the intent forwarder activity
+     */
+    public boolean isCrossProfileIntentForwarderActivity() {
+        return activityInfo != null
+                && INTENT_FORWARDER_ACTIVITY.equals(activityInfo.targetActivity);
+    }
+
+    public ResolveInfo() {
+        targetUserId = UserHandle.USER_CURRENT;
+    }
+
+    public ResolveInfo(ResolveInfo orig) {
+        activityInfo = orig.activityInfo;
+        serviceInfo = orig.serviceInfo;
+        providerInfo = orig.providerInfo;
+        filter = orig.filter;
+        priority = orig.priority;
+        preferredOrder = orig.preferredOrder;
+        match = orig.match;
+        specificIndex = orig.specificIndex;
+        labelRes = orig.labelRes;
+        nonLocalizedLabel = orig.nonLocalizedLabel;
+        icon = orig.icon;
+        resolvePackageName = orig.resolvePackageName;
+        noResourceId = orig.noResourceId;
+        iconResourceId = orig.iconResourceId;
+        system = orig.system;
+        targetUserId = orig.targetUserId;
+        handleAllWebDataURI = orig.handleAllWebDataURI;
+        isInstantAppAvailable = orig.isInstantAppAvailable;
+    }
+
+    public String toString() {
+        final ComponentInfo ci = getComponentInfo();
+        StringBuilder sb = new StringBuilder(128);
+        sb.append("ResolveInfo{");
+        sb.append(Integer.toHexString(System.identityHashCode(this)));
+        sb.append(' ');
+        ComponentName.appendShortString(sb, ci.packageName, ci.name);
+        if (priority != 0) {
+            sb.append(" p=");
+            sb.append(priority);
+        }
+        if (preferredOrder != 0) {
+            sb.append(" o=");
+            sb.append(preferredOrder);
+        }
+        sb.append(" m=0x");
+        sb.append(Integer.toHexString(match));
+        if (targetUserId != UserHandle.USER_CURRENT) {
+            sb.append(" targetUserId=");
+            sb.append(targetUserId);
+        }
+        sb.append('}');
+        return sb.toString();
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel dest, int parcelableFlags) {
+        if (activityInfo != null) {
+            dest.writeInt(1);
+            activityInfo.writeToParcel(dest, parcelableFlags);
+        } else if (serviceInfo != null) {
+            dest.writeInt(2);
+            serviceInfo.writeToParcel(dest, parcelableFlags);
+        } else if (providerInfo != null) {
+            dest.writeInt(3);
+            providerInfo.writeToParcel(dest, parcelableFlags);
+        } else {
+            dest.writeInt(0);
+        }
+        if (filter != null) {
+            dest.writeInt(1);
+            filter.writeToParcel(dest, parcelableFlags);
+        } else {
+            dest.writeInt(0);
+        }
+        dest.writeInt(priority);
+        dest.writeInt(preferredOrder);
+        dest.writeInt(match);
+        dest.writeInt(specificIndex);
+        dest.writeInt(labelRes);
+        TextUtils.writeToParcel(nonLocalizedLabel, dest, parcelableFlags);
+        dest.writeInt(icon);
+        dest.writeString8(resolvePackageName);
+        dest.writeInt(targetUserId);
+        dest.writeInt(system ? 1 : 0);
+        dest.writeInt(noResourceId ? 1 : 0);
+        dest.writeInt(iconResourceId);
+        dest.writeInt(handleAllWebDataURI ? 1 : 0);
+        dest.writeInt(isInstantAppAvailable ? 1 : 0);
+    }
+
+    public static final @android.annotation.NonNull Creator<ResolveInfo> CREATOR
+            = new Creator<ResolveInfo>() {
+        public ResolveInfo createFromParcel(Parcel source) {
+            return new ResolveInfo(source);
+        }
+        public ResolveInfo[] newArray(int size) {
+            return new ResolveInfo[size];
+        }
+    };
+
+    private ResolveInfo(Parcel source) {
+        activityInfo = null;
+        serviceInfo = null;
+        providerInfo = null;
+        switch (source.readInt()) {
+            case 1:
+                activityInfo = ActivityInfo.CREATOR.createFromParcel(source);
+                break;
+            case 2:
+                serviceInfo = ServiceInfo.CREATOR.createFromParcel(source);
+                break;
+            case 3:
+                providerInfo = ProviderInfo.CREATOR.createFromParcel(source);
+                break;
+            default:
+                Slog.w(TAG, "Missing ComponentInfo!");
+                break;
+        }
+        if (source.readInt() != 0) {
+            filter = IntentFilter.CREATOR.createFromParcel(source);
+        }
+        priority = source.readInt();
+        preferredOrder = source.readInt();
+        match = source.readInt();
+        specificIndex = source.readInt();
+        labelRes = source.readInt();
+        nonLocalizedLabel
+                = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+        icon = source.readInt();
+        resolvePackageName = source.readString8();
+        targetUserId = source.readInt();
+        system = source.readInt() != 0;
+        noResourceId = source.readInt() != 0;
+        iconResourceId = source.readInt();
+        handleAllWebDataURI = source.readInt() != 0;
+        isInstantAppAvailable = source.readInt() != 0;
+    }
+
+    public static class DisplayNameComparator
+            implements Comparator<ResolveInfo> {
+        public DisplayNameComparator(PackageManager pm) {
+            mPM = pm;
+            mCollator.setStrength(Collator.PRIMARY);
+        }
+
+        public final int compare(ResolveInfo a, ResolveInfo b) {
+            // We want to put the one targeted to another user at the end of the dialog.
+            if (a.targetUserId != UserHandle.USER_CURRENT) {
+                return 1;
+            }
+            if (b.targetUserId != UserHandle.USER_CURRENT) {
+                return -1;
+            }
+            CharSequence  sa = a.loadLabel(mPM);
+            if (sa == null) sa = a.activityInfo.name;
+            CharSequence  sb = b.loadLabel(mPM);
+            if (sb == null) sb = b.activityInfo.name;
+            
+            return mCollator.compare(sa.toString(), sb.toString());
+        }
+
+        private final Collator   mCollator = Collator.getInstance();
+        private PackageManager   mPM;
+    }
+}
diff --git a/android/content/pm/SELinuxUtil.java b/android/content/pm/SELinuxUtil.java
new file mode 100644
index 0000000..025c0fe
--- /dev/null
+++ b/android/content/pm/SELinuxUtil.java
@@ -0,0 +1,41 @@
+/*
+ * 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.content.pm;
+
+import com.android.internal.util.ArrayUtils;
+
+/**
+ * Utility methods that need to be used in application space.
+ * @hide
+ */
+public final class SELinuxUtil {
+
+    /** Append to existing seinfo label for instant apps @hide */
+    private static final String INSTANT_APP_STR = ":ephemeralapp";
+
+    /** Append to existing seinfo when modifications are complete @hide */
+    public static final String COMPLETE_STR = ":complete";
+
+    /** @hide */
+    public static String assignSeinfoUser(PackageUserState userState) {
+        if (userState.instantApp) {
+           return INSTANT_APP_STR + COMPLETE_STR;
+        }
+        return COMPLETE_STR;
+    }
+
+}
diff --git a/android/content/pm/ServiceInfo.java b/android/content/pm/ServiceInfo.java
new file mode 100644
index 0000000..d3f9e24
--- /dev/null
+++ b/android/content/pm/ServiceInfo.java
@@ -0,0 +1,268 @@
+/*
+ * 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.content.pm;
+
+import android.annotation.IntDef;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Printer;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Information you can retrieve about a particular application
+ * service. This corresponds to information collected from the
+ * AndroidManifest.xml's &lt;service&gt; tags.
+ */
+public class ServiceInfo extends ComponentInfo
+        implements Parcelable {
+    /**
+     * Optional name of a permission required to be able to access this
+     * Service.  From the "permission" attribute.
+     */
+    public String permission;
+
+    /**
+     * Bit in {@link #flags}: If set, the service will automatically be
+     * stopped by the system if the user removes a task that is rooted
+     * in one of the application's activities.  Set from the
+     * {@link android.R.attr#stopWithTask} attribute.
+     */
+    public static final int FLAG_STOP_WITH_TASK = 0x0001;
+
+    /**
+     * Bit in {@link #flags}: If set, the service will run in its own
+     * isolated process.  Set from the
+     * {@link android.R.attr#isolatedProcess} attribute.
+     */
+    public static final int FLAG_ISOLATED_PROCESS = 0x0002;
+
+    /**
+     * Bit in {@link #flags}: If set, the service can be bound and run in the
+     * calling application's package, rather than the package in which it is
+     * declared.  Set from {@link android.R.attr#externalService} attribute.
+     */
+    public static final int FLAG_EXTERNAL_SERVICE = 0x0004;
+
+    /**
+     * Bit in {@link #flags}: If set, the service (which must be isolated)
+     * will be spawned from an Application Zygote, instead of the regular Zygote.
+     * The Application Zygote will pre-initialize the application's class loader,
+     * and call a static callback into the application to allow it to perform
+     * application-specific preloads (such as loading a shared library). Therefore,
+     * spawning from the Application Zygote will typically reduce the service
+     * launch time and reduce its memory usage. The downside of using this flag
+     * is that you will have an additional process (the app zygote itself) that
+     * is taking up memory. Whether actual memory usage is improved therefore
+     * strongly depends on the number of isolated services that an application
+     * starts, and how much memory those services save by preloading. Therefore,
+     * it is recommended to measure memory usage under typical workloads to
+     * determine whether it makes sense to use this flag.
+     */
+    public static final int FLAG_USE_APP_ZYGOTE = 0x0008;
+
+    /**
+     * Bit in {@link #flags} indicating if the service is visible to ephemeral applications.
+     * @hide
+     */
+    public static final int FLAG_VISIBLE_TO_INSTANT_APP = 0x100000;
+
+    /**
+     * Bit in {@link #flags}: If set, a single instance of the service will
+     * run for all users on the device.  Set from the
+     * {@link android.R.attr#singleUser} attribute.
+     */
+    public static final int FLAG_SINGLE_USER = 0x40000000;
+
+    /**
+     * Options that have been set in the service declaration in the
+     * manifest.
+     * These include:
+     * {@link #FLAG_STOP_WITH_TASK}, {@link #FLAG_ISOLATED_PROCESS},
+     * {@link #FLAG_SINGLE_USER}.
+     */
+    public int flags;
+
+    /**
+     * The default foreground service type if not been set in manifest file.
+     */
+    public static final int FOREGROUND_SERVICE_TYPE_NONE = 0;
+
+    /**
+     * Constant corresponding to <code>dataSync</code> in
+     * the {@link android.R.attr#foregroundServiceType} attribute.
+     * Data(photo, file, account) upload/download, backup/restore, import/export, fetch,
+     * transfer over network between device and cloud.
+     */
+    public static final int FOREGROUND_SERVICE_TYPE_DATA_SYNC = 1 << 0;
+
+    /**
+     * Constant corresponding to <code>mediaPlayback</code> in
+     * the {@link android.R.attr#foregroundServiceType} attribute.
+     * Music, video, news or other media playback.
+     */
+    public static final int FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK = 1 << 1;
+
+    /**
+     * Constant corresponding to <code>phoneCall</code> in
+     * the {@link android.R.attr#foregroundServiceType} attribute.
+     * Ongoing phone call or video conference.
+     */
+    public static final int FOREGROUND_SERVICE_TYPE_PHONE_CALL = 1 << 2;
+
+    /**
+     * Constant corresponding to <code>location</code> in
+     * the {@link android.R.attr#foregroundServiceType} attribute.
+     * GPS, map, navigation location update.
+     */
+    public static final int FOREGROUND_SERVICE_TYPE_LOCATION = 1 << 3;
+
+    /**
+     * Constant corresponding to <code>connectedDevice</code> in
+     * the {@link android.R.attr#foregroundServiceType} attribute.
+     * Auto, bluetooth, TV or other devices connection, monitoring and interaction.
+     */
+    public static final int FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE = 1 << 4;
+
+    /**
+     * Constant corresponding to {@code mediaProjection} in
+     * the {@link android.R.attr#foregroundServiceType} attribute.
+     * Managing a media projection session, e.g for screen recording or taking screenshots.
+     */
+    public static final int FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION = 1 << 5;
+
+    /**
+     * Constant corresponding to {@code camera} in
+     * the {@link android.R.attr#foregroundServiceType} attribute.
+     * Use the camera device or record video.
+     * For apps with <code>targetSdkVersion</code> {@link android.os.Build.VERSION_CODES#R} and
+     * above, a foreground service will not be able to access the camera if this type is not
+     * specified in the manifest and in
+     * {@link android.app.Service#startForeground(int, android.app.Notification, int)}.
+     */
+    public static final int FOREGROUND_SERVICE_TYPE_CAMERA = 1 << 6;
+
+    /**
+     * Constant corresponding to {@code microphone} in
+     * the {@link android.R.attr#foregroundServiceType} attribute.
+     * Use the microphone device or record audio.
+     * For apps with <code>targetSdkVersion</code> {@link android.os.Build.VERSION_CODES#R} and
+     * above, a foreground service will not be able to access the microphone if this type is not
+     * specified in the manifest and in
+     * {@link android.app.Service#startForeground(int, android.app.Notification, int)}.
+     */
+    public static final int FOREGROUND_SERVICE_TYPE_MICROPHONE = 1 << 7;
+
+    /**
+     * A special value indicates to use all types set in manifest file.
+     */
+    public static final int FOREGROUND_SERVICE_TYPE_MANIFEST = -1;
+
+    /**
+     * The set of flags for foreground service type.
+     * The foreground service type is set in {@link android.R.attr#foregroundServiceType}
+     * attribute.
+     * @hide
+     */
+    @IntDef(flag = true, prefix = { "FOREGROUND_SERVICE_TYPE_" }, value = {
+            FOREGROUND_SERVICE_TYPE_MANIFEST,
+            FOREGROUND_SERVICE_TYPE_NONE,
+            FOREGROUND_SERVICE_TYPE_DATA_SYNC,
+            FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK,
+            FOREGROUND_SERVICE_TYPE_PHONE_CALL,
+            FOREGROUND_SERVICE_TYPE_LOCATION,
+            FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE,
+            FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION,
+            FOREGROUND_SERVICE_TYPE_CAMERA,
+            FOREGROUND_SERVICE_TYPE_MICROPHONE
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ForegroundServiceType {}
+
+    /**
+     * The type of foreground service, set in
+     * {@link android.R.attr#foregroundServiceType} attribute by ORing flags in
+     * {@link ForegroundServiceType}
+     * @hide
+     */
+    public @ForegroundServiceType int mForegroundServiceType = FOREGROUND_SERVICE_TYPE_NONE;
+
+    public ServiceInfo() {
+    }
+
+    public ServiceInfo(ServiceInfo orig) {
+        super(orig);
+        permission = orig.permission;
+        flags = orig.flags;
+        mForegroundServiceType = orig.mForegroundServiceType;
+    }
+
+    /**
+     * Return foreground service type specified in the manifest..
+     * @return foreground service type specified in the manifest.
+     */
+    public @ForegroundServiceType int getForegroundServiceType() {
+        return mForegroundServiceType;
+    }
+
+    public void dump(Printer pw, String prefix) {
+        dump(pw, prefix, DUMP_FLAG_ALL);
+    }
+
+    /** @hide */
+    void dump(Printer pw, String prefix, int dumpFlags) {
+        super.dumpFront(pw, prefix);
+        pw.println(prefix + "permission=" + permission);
+        pw.println(prefix + "flags=0x" + Integer.toHexString(flags));
+        super.dumpBack(pw, prefix, dumpFlags);
+    }
+
+    public String toString() {
+        return "ServiceInfo{"
+            + Integer.toHexString(System.identityHashCode(this))
+            + " " + name + "}";
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel dest, int parcelableFlags) {
+        super.writeToParcel(dest, parcelableFlags);
+        dest.writeString8(permission);
+        dest.writeInt(flags);
+        dest.writeInt(mForegroundServiceType);
+    }
+
+    public static final @android.annotation.NonNull Creator<ServiceInfo> CREATOR =
+        new Creator<ServiceInfo>() {
+        public ServiceInfo createFromParcel(Parcel source) {
+            return new ServiceInfo(source);
+        }
+        public ServiceInfo[] newArray(int size) {
+            return new ServiceInfo[size];
+        }
+    };
+
+    private ServiceInfo(Parcel source) {
+        super(source);
+        permission = source.readString8();
+        flags = source.readInt();
+        mForegroundServiceType = source.readInt();
+    }
+}
diff --git a/android/content/pm/SharedLibraryInfo.java b/android/content/pm/SharedLibraryInfo.java
new file mode 100644
index 0000000..da2a3d8
--- /dev/null
+++ b/android/content/pm/SharedLibraryInfo.java
@@ -0,0 +1,352 @@
+/*
+ * 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.content.pm;
+
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * This class provides information for a shared library. There are
+ * three types of shared libraries: builtin - non-updatable part of
+ * the OS; dynamic - updatable backwards-compatible dynamically linked;
+ * static - non backwards-compatible emulating static linking.
+ */
+public final class SharedLibraryInfo implements Parcelable {
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "TYPE_" }, value = {
+            TYPE_BUILTIN,
+            TYPE_DYNAMIC,
+            TYPE_STATIC,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface Type{}
+
+    /**
+     * Shared library type: this library is a part of the OS
+     * and cannot be updated or uninstalled.
+     */
+    public static final int TYPE_BUILTIN = 0;
+
+    /**
+     * Shared library type: this library is backwards-compatible, can
+     * be updated, and updates can be uninstalled. Clients link against
+     * the latest version of the library.
+     */
+    public static final int TYPE_DYNAMIC = 1;
+
+    /**
+     * Shared library type: this library is <strong>not</strong> backwards
+     * -compatible, can be updated and updates can be uninstalled. Clients
+     * link against a specific version of the library.
+     */
+    public static final int TYPE_STATIC = 2;
+
+    /**
+     * Constant for referring to an undefined version.
+     */
+    public static final int VERSION_UNDEFINED = -1;
+
+    private final String mPath;
+    private final String mPackageName;
+    private final String mName;
+    private final List<String> mCodePaths;
+
+    private final long mVersion;
+    private final @Type int mType;
+    private final VersionedPackage mDeclaringPackage;
+    private final List<VersionedPackage> mDependentPackages;
+    private List<SharedLibraryInfo> mDependencies;
+
+    /**
+     * Creates a new instance.
+     *
+     * @param codePaths For a non {@link #TYPE_BUILTIN builtin} library, the locations of jars of
+     *                  this shared library. Null for builtin library.
+     * @param name The lib name.
+     * @param version The lib version if not builtin.
+     * @param type The lib type.
+     * @param declaringPackage The package that declares the library.
+     * @param dependentPackages The packages that depend on the library.
+     *
+     * @hide
+     */
+    public SharedLibraryInfo(String path, String packageName, List<String> codePaths,
+            String name, long version, int type,
+            VersionedPackage declaringPackage, List<VersionedPackage> dependentPackages,
+            List<SharedLibraryInfo> dependencies) {
+        mPath = path;
+        mPackageName = packageName;
+        mCodePaths = codePaths;
+        mName = name;
+        mVersion = version;
+        mType = type;
+        mDeclaringPackage = declaringPackage;
+        mDependentPackages = dependentPackages;
+        mDependencies = dependencies;
+    }
+
+    private SharedLibraryInfo(Parcel parcel) {
+        mPath = parcel.readString8();
+        mPackageName = parcel.readString8();
+        if (parcel.readInt() != 0) {
+            mCodePaths = Arrays.asList(parcel.createString8Array());
+        } else {
+            mCodePaths = null;
+        }
+        mName = parcel.readString8();
+        mVersion = parcel.readLong();
+        mType = parcel.readInt();
+        mDeclaringPackage = parcel.readParcelable(null);
+        mDependentPackages = parcel.readArrayList(null);
+        mDependencies = parcel.createTypedArrayList(SharedLibraryInfo.CREATOR);
+    }
+
+    /**
+     * Gets the type of this library.
+     *
+     * @return The library type.
+     */
+    public @Type int getType() {
+        return mType;
+    }
+
+    /**
+     * Gets the library name an app defines in its manifest
+     * to depend on the library.
+     *
+     * @return The name.
+     */
+    public String getName() {
+        return mName;
+    }
+
+    /**
+     * If the shared library is a jar file, returns the path of that jar. Null otherwise.
+     * Only libraries with TYPE_BUILTIN are in jar files.
+     *
+     * @return The path.
+     *
+     * @hide
+     */
+    public @Nullable String getPath() {
+        return mPath;
+    }
+
+    /**
+     * If the shared library is an apk, returns the package name. Null otherwise.
+     * Only libraries with TYPE_DYNAMIC or TYPE_STATIC are in apks.
+     *
+     * @return The package name.
+     *
+     * @hide
+     */
+    public @Nullable String getPackageName() {
+        return mPackageName;
+    }
+
+    /**
+     * Get all code paths for that library.
+     *
+     * @return All code paths.
+     *
+     * @hide
+     */
+    public List<String> getAllCodePaths() {
+        if (getPath() != null) {
+            // Builtin library.
+            ArrayList<String> list = new ArrayList<>();
+            list.add(getPath());
+            return list;
+        } else {
+            // Static or dynamic library.
+            return mCodePaths;
+        }
+    }
+
+    /**
+     * Add a library dependency to that library. Note that this
+     * should be called under the package manager lock.
+     *
+     * @hide
+     */
+    public void addDependency(@Nullable SharedLibraryInfo info) {
+        if (info == null) {
+            // For convenience of the caller, allow null to be passed.
+            // This can happen when we create the dependencies of builtin
+            // libraries.
+            return;
+        }
+        if (mDependencies == null) {
+            mDependencies = new ArrayList<>();
+        }
+        mDependencies.add(info);
+    }
+
+    /**
+     * Clear all dependencies.
+     *
+     * @hide
+     */
+    public void clearDependencies() {
+        mDependencies = null;
+    }
+
+    /**
+     * Gets the libraries this library directly depends on. Note that
+     * the package manager prevents recursive dependencies when installing
+     * a package.
+     *
+     * @return The dependencies.
+     *
+     * @hide
+     */
+    public @Nullable List<SharedLibraryInfo> getDependencies() {
+        return mDependencies;
+    }
+
+    /**
+     * @deprecated Use {@link #getLongVersion()} instead.
+     */
+    @Deprecated
+    public @IntRange(from = -1) int getVersion() {
+        return mVersion < 0 ? (int) mVersion : (int) (mVersion & 0x7fffffff);
+    }
+
+    /**
+     * Gets the version of the library. For {@link #TYPE_STATIC static} libraries
+     * this is the declared version and for {@link #TYPE_DYNAMIC dynamic} and
+     * {@link #TYPE_BUILTIN builtin} it is {@link #VERSION_UNDEFINED} as these
+     * are not versioned.
+     *
+     * @return The version.
+     */
+    public @IntRange(from = -1) long getLongVersion() {
+        return mVersion;
+    }
+
+    /**
+     * @removed
+     */
+    public boolean isBuiltin() {
+        return mType == TYPE_BUILTIN;
+    }
+
+    /**
+     * @removed
+     */
+    public boolean isDynamic() {
+        return mType == TYPE_DYNAMIC;
+    }
+
+    /**
+     * @removed
+     */
+    public boolean isStatic() {
+        return mType == TYPE_STATIC;
+    }
+
+    /**
+     * Gets the package that declares the library.
+     *
+     * @return The package declaring the library.
+     */
+    public @NonNull VersionedPackage getDeclaringPackage() {
+        return mDeclaringPackage;
+    }
+
+    /**
+     * Gets the packages that depend on the library.
+     *
+     * @return The dependent packages.
+     */
+    public @NonNull List<VersionedPackage> getDependentPackages() {
+        if (mDependentPackages == null) {
+            return Collections.emptyList();
+        }
+        return mDependentPackages;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public String toString() {
+        return "SharedLibraryInfo{name:" + mName + ", type:" + typeToString(mType)
+                + ", version:" + mVersion + (!getDependentPackages().isEmpty()
+                ? " has dependents" : "") + "}";
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeString8(mPath);
+        parcel.writeString8(mPackageName);
+        if (mCodePaths != null) {
+            parcel.writeInt(1);
+            parcel.writeString8Array(mCodePaths.toArray(new String[mCodePaths.size()]));
+        } else {
+            parcel.writeInt(0);
+        }
+        parcel.writeString8(mName);
+        parcel.writeLong(mVersion);
+        parcel.writeInt(mType);
+        parcel.writeParcelable(mDeclaringPackage, flags);
+        parcel.writeList(mDependentPackages);
+        parcel.writeTypedList(mDependencies);
+    }
+
+    private static String typeToString(int type) {
+        switch (type) {
+            case TYPE_BUILTIN: {
+                return "builtin";
+            }
+            case TYPE_DYNAMIC: {
+                return "dynamic";
+            }
+            case TYPE_STATIC: {
+                return "static";
+            }
+            default: {
+                return "unknown";
+            }
+        }
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<SharedLibraryInfo> CREATOR =
+            new Parcelable.Creator<SharedLibraryInfo>() {
+        public SharedLibraryInfo createFromParcel(Parcel source) {
+            return new SharedLibraryInfo(source);
+        }
+
+        public SharedLibraryInfo[] newArray(int size) {
+            return new SharedLibraryInfo[size];
+        }
+    };
+}
diff --git a/android/content/pm/ShortcutInfo.java b/android/content/pm/ShortcutInfo.java
new file mode 100644
index 0000000..85bf11c
--- /dev/null
+++ b/android/content/pm/ShortcutInfo.java
@@ -0,0 +1,2427 @@
+/*
+ * 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.content.pm;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.annotation.UserIdInt;
+import android.app.Notification;
+import android.app.Person;
+import android.app.TaskStackBuilder;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.LocusId;
+import android.content.pm.LauncherApps.ShortcutQuery;
+import android.content.res.Resources;
+import android.content.res.Resources.NotFoundException;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Icon;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.PersistableBundle;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.ArraySet;
+import android.util.Log;
+import android.view.contentcapture.ContentCaptureContext;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Represents a shortcut that can be published via {@link ShortcutManager}.
+ *
+ * @see ShortcutManager
+ */
+public final class ShortcutInfo implements Parcelable {
+    static final String TAG = "Shortcut";
+
+    private static final String RES_TYPE_STRING = "string";
+
+    private static final String ANDROID_PACKAGE_NAME = "android";
+
+    private static final int IMPLICIT_RANK_MASK = 0x7fffffff;
+
+    private static final int RANK_CHANGED_BIT = ~IMPLICIT_RANK_MASK;
+
+    /** @hide */
+    public static final int RANK_NOT_SET = Integer.MAX_VALUE;
+
+    /** @hide */
+    public static final int FLAG_DYNAMIC = 1 << 0;
+
+    /** @hide */
+    public static final int FLAG_PINNED = 1 << 1;
+
+    /** @hide */
+    public static final int FLAG_HAS_ICON_RES = 1 << 2;
+
+    /** @hide */
+    public static final int FLAG_HAS_ICON_FILE = 1 << 3;
+
+    /** @hide */
+    public static final int FLAG_KEY_FIELDS_ONLY = 1 << 4;
+
+    /** @hide */
+    public static final int FLAG_MANIFEST = 1 << 5;
+
+    /** @hide */
+    public static final int FLAG_DISABLED = 1 << 6;
+
+    /** @hide */
+    public static final int FLAG_STRINGS_RESOLVED = 1 << 7;
+
+    /** @hide */
+    public static final int FLAG_IMMUTABLE = 1 << 8;
+
+    /** @hide */
+    public static final int FLAG_ADAPTIVE_BITMAP = 1 << 9;
+
+    /** @hide */
+    public static final int FLAG_RETURNED_BY_SERVICE = 1 << 10;
+
+    /** @hide When this is set, the bitmap icon is waiting to be saved. */
+    public static final int FLAG_ICON_FILE_PENDING_SAVE = 1 << 11;
+
+    /**
+     * "Shadow" shortcuts are the ones that are restored, but the owner package hasn't been
+     * installed yet.
+     * @hide
+     */
+    public static final int FLAG_SHADOW = 1 << 12;
+
+    /** @hide */
+    public static final int FLAG_LONG_LIVED = 1 << 13;
+
+    /** @hide */
+    public static final int FLAG_CACHED = 1 << 14;
+
+    /** @hide */
+    public static final int FLAG_HAS_ICON_URI = 1 << 15;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "FLAG_" }, value = {
+            FLAG_DYNAMIC,
+            FLAG_PINNED,
+            FLAG_HAS_ICON_RES,
+            FLAG_HAS_ICON_FILE,
+            FLAG_KEY_FIELDS_ONLY,
+            FLAG_MANIFEST,
+            FLAG_DISABLED,
+            FLAG_STRINGS_RESOLVED,
+            FLAG_IMMUTABLE,
+            FLAG_ADAPTIVE_BITMAP,
+            FLAG_RETURNED_BY_SERVICE,
+            FLAG_ICON_FILE_PENDING_SAVE,
+            FLAG_SHADOW,
+            FLAG_LONG_LIVED,
+            FLAG_CACHED,
+            FLAG_HAS_ICON_URI,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ShortcutFlags {}
+
+    // Cloning options.
+
+    /** @hide */
+    private static final int CLONE_REMOVE_ICON = 1 << 0;
+
+    /** @hide */
+    private static final int CLONE_REMOVE_INTENT = 1 << 1;
+
+    /** @hide */
+    public static final int CLONE_REMOVE_NON_KEY_INFO = 1 << 2;
+
+    /** @hide */
+    public static final int CLONE_REMOVE_RES_NAMES = 1 << 3;
+
+    /** @hide */
+    public static final int CLONE_REMOVE_PERSON = 1 << 4;
+
+    /** @hide */
+    public static final int CLONE_REMOVE_FOR_CREATOR = CLONE_REMOVE_ICON | CLONE_REMOVE_RES_NAMES;
+
+    /** @hide */
+    public static final int CLONE_REMOVE_FOR_LAUNCHER = CLONE_REMOVE_ICON | CLONE_REMOVE_INTENT
+            | CLONE_REMOVE_RES_NAMES | CLONE_REMOVE_PERSON;
+
+    /** @hide */
+    public static final int CLONE_REMOVE_FOR_LAUNCHER_APPROVAL = CLONE_REMOVE_INTENT
+            | CLONE_REMOVE_RES_NAMES | CLONE_REMOVE_PERSON;
+
+    /** @hide */
+    public static final int CLONE_REMOVE_FOR_APP_PREDICTION = CLONE_REMOVE_ICON
+            | CLONE_REMOVE_RES_NAMES;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "CLONE_" }, value = {
+            CLONE_REMOVE_ICON,
+            CLONE_REMOVE_INTENT,
+            CLONE_REMOVE_NON_KEY_INFO,
+            CLONE_REMOVE_RES_NAMES,
+            CLONE_REMOVE_PERSON,
+            CLONE_REMOVE_FOR_CREATOR,
+            CLONE_REMOVE_FOR_LAUNCHER,
+            CLONE_REMOVE_FOR_LAUNCHER_APPROVAL,
+            CLONE_REMOVE_FOR_APP_PREDICTION
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface CloneFlags {}
+
+    /**
+     * Shortcut is not disabled.
+     */
+    public static final int DISABLED_REASON_NOT_DISABLED = 0;
+
+    /**
+     * Shortcut has been disabled by the publisher app with the
+     * {@link ShortcutManager#disableShortcuts(List)} API.
+     */
+    public static final int DISABLED_REASON_BY_APP = 1;
+
+    /**
+     * Shortcut has been disabled due to changes to the publisher app. (e.g. a manifest shortcut
+     * no longer exists.)
+     */
+    public static final int DISABLED_REASON_APP_CHANGED = 2;
+
+    /**
+     * Shortcut is disabled for an unknown reason.
+     */
+    public static final int DISABLED_REASON_UNKNOWN = 3;
+
+    /**
+     * A disabled reason that's equal to or bigger than this is due to backup and restore issue.
+     * A shortcut with such a reason wil be visible to the launcher, but not to the publisher.
+     * ({@link #isVisibleToPublisher()} will be false.)
+     */
+    private static final int DISABLED_REASON_RESTORE_ISSUE_START = 100;
+
+    /**
+     * Shortcut has been restored from the previous device, but the publisher app on the current
+     * device is of a lower version. The shortcut will not be usable until the app is upgraded to
+     * the same version or higher.
+     */
+    public static final int DISABLED_REASON_VERSION_LOWER = 100;
+
+    /**
+     * Shortcut has not been restored because the publisher app does not support backup and restore.
+     */
+    public static final int DISABLED_REASON_BACKUP_NOT_SUPPORTED = 101;
+
+    /**
+     * Shortcut has not been restored because the publisher app's signature has changed.
+     */
+    public static final int DISABLED_REASON_SIGNATURE_MISMATCH = 102;
+
+    /**
+     * Shortcut has not been restored for unknown reason.
+     */
+    public static final int DISABLED_REASON_OTHER_RESTORE_ISSUE = 103;
+
+    /** @hide */
+    @IntDef(prefix = { "DISABLED_REASON_" }, value = {
+            DISABLED_REASON_NOT_DISABLED,
+            DISABLED_REASON_BY_APP,
+            DISABLED_REASON_APP_CHANGED,
+            DISABLED_REASON_UNKNOWN,
+            DISABLED_REASON_VERSION_LOWER,
+            DISABLED_REASON_BACKUP_NOT_SUPPORTED,
+            DISABLED_REASON_SIGNATURE_MISMATCH,
+            DISABLED_REASON_OTHER_RESTORE_ISSUE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface DisabledReason{}
+
+    /**
+     * Return a label for disabled reasons, which are *not* supposed to be shown to the user.
+     * @hide
+     */
+    public static String getDisabledReasonDebugString(@DisabledReason int disabledReason) {
+        switch (disabledReason) {
+            case DISABLED_REASON_NOT_DISABLED:
+                return "[Not disabled]";
+            case DISABLED_REASON_BY_APP:
+                return "[Disabled: by app]";
+            case DISABLED_REASON_APP_CHANGED:
+                return "[Disabled: app changed]";
+            case DISABLED_REASON_VERSION_LOWER:
+                return "[Disabled: lower version]";
+            case DISABLED_REASON_BACKUP_NOT_SUPPORTED:
+                return "[Disabled: backup not supported]";
+            case DISABLED_REASON_SIGNATURE_MISMATCH:
+                return "[Disabled: signature mismatch]";
+            case DISABLED_REASON_OTHER_RESTORE_ISSUE:
+                return "[Disabled: unknown restore issue]";
+        }
+        return "[Disabled: unknown reason:" + disabledReason + "]";
+    }
+
+    /**
+     * Return a label for a disabled reason for shortcuts that are disabled due to a backup and
+     * restore issue. If the reason is not due to backup & restore, then it'll return null.
+     *
+     * This method returns localized, user-facing strings, which will be returned by
+     * {@link #getDisabledMessage()}.
+     *
+     * @hide
+     */
+    public static String getDisabledReasonForRestoreIssue(Context context,
+            @DisabledReason int disabledReason) {
+        final Resources res = context.getResources();
+
+        switch (disabledReason) {
+            case DISABLED_REASON_VERSION_LOWER:
+                return res.getString(
+                        com.android.internal.R.string.shortcut_restored_on_lower_version);
+            case DISABLED_REASON_BACKUP_NOT_SUPPORTED:
+                return res.getString(
+                        com.android.internal.R.string.shortcut_restore_not_supported);
+            case DISABLED_REASON_SIGNATURE_MISMATCH:
+                return res.getString(
+                        com.android.internal.R.string.shortcut_restore_signature_mismatch);
+            case DISABLED_REASON_OTHER_RESTORE_ISSUE:
+                return res.getString(
+                        com.android.internal.R.string.shortcut_restore_unknown_issue);
+            case DISABLED_REASON_UNKNOWN:
+                return res.getString(
+                        com.android.internal.R.string.shortcut_disabled_reason_unknown);
+        }
+        return null;
+    }
+
+    /** @hide */
+    public static boolean isDisabledForRestoreIssue(@DisabledReason int disabledReason) {
+        return disabledReason >= DISABLED_REASON_RESTORE_ISSUE_START;
+    }
+
+    /**
+     * Shortcut category for messaging related actions, such as chat.
+     */
+    public static final String SHORTCUT_CATEGORY_CONVERSATION = "android.shortcut.conversation";
+
+    private final String mId;
+
+    @NonNull
+    private final String mPackageName;
+
+    @Nullable
+    private ComponentName mActivity;
+
+    @Nullable
+    private Icon mIcon;
+
+    private int mTitleResId;
+
+    private String mTitleResName;
+
+    @Nullable
+    private CharSequence mTitle;
+
+    private int mTextResId;
+
+    private String mTextResName;
+
+    @Nullable
+    private CharSequence mText;
+
+    private int mDisabledMessageResId;
+
+    private String mDisabledMessageResName;
+
+    @Nullable
+    private CharSequence mDisabledMessage;
+
+    @Nullable
+    private ArraySet<String> mCategories;
+
+    /**
+     * Intents *with extras removed*.
+     */
+    @Nullable
+    private Intent[] mIntents;
+
+    /**
+     * Extras for the intents.
+     */
+    @Nullable
+    private PersistableBundle[] mIntentPersistableExtrases;
+
+    @Nullable
+    private Person[] mPersons;
+
+    @Nullable
+    private LocusId mLocusId;
+
+    private int mRank;
+
+    /**
+     * Internally used for auto-rank-adjustment.
+     *
+     * RANK_CHANGED_BIT is used to denote that the rank of a shortcut is changing.
+     * The rest of the bits are used to denote the order in which shortcuts are passed to
+     * APIs, which is used to preserve the argument order when ranks are tie.
+     */
+    private int mImplicitRank;
+
+    @Nullable
+    private PersistableBundle mExtras;
+
+    private long mLastChangedTimestamp;
+
+    // Internal use only.
+    @ShortcutFlags
+    private int mFlags;
+
+    // Internal use only.
+    private int mIconResId;
+
+    private String mIconResName;
+
+    // Internal use only.
+    private String mIconUri;
+
+    // Internal use only.
+    @Nullable
+    private String mBitmapPath;
+
+    private final int mUserId;
+
+    /** @hide */
+    public static final int VERSION_CODE_UNKNOWN = -1;
+
+    private int mDisabledReason;
+
+    private ShortcutInfo(Builder b) {
+        mUserId = b.mContext.getUserId();
+
+        mId = Preconditions.checkStringNotEmpty(b.mId, "Shortcut ID must be provided");
+
+        // Note we can't do other null checks here because SM.updateShortcuts() takes partial
+        // information.
+        mPackageName = b.mContext.getPackageName();
+        mActivity = b.mActivity;
+        mIcon = b.mIcon;
+        mTitle = b.mTitle;
+        mTitleResId = b.mTitleResId;
+        mText = b.mText;
+        mTextResId = b.mTextResId;
+        mDisabledMessage = b.mDisabledMessage;
+        mDisabledMessageResId = b.mDisabledMessageResId;
+        mCategories = cloneCategories(b.mCategories);
+        mIntents = cloneIntents(b.mIntents);
+        fixUpIntentExtras();
+        mPersons = clonePersons(b.mPersons);
+        if (b.mIsLongLived) {
+            setLongLived();
+        }
+        mRank = b.mRank;
+        mExtras = b.mExtras;
+        mLocusId = b.mLocusId;
+
+        updateTimestamp();
+    }
+
+    /**
+     * Extract extras from {@link #mIntents} and set them to {@link #mIntentPersistableExtrases}
+     * as {@link PersistableBundle}, and remove extras from the original intents.
+     */
+    private void fixUpIntentExtras() {
+        if (mIntents == null) {
+            mIntentPersistableExtrases = null;
+            return;
+        }
+        mIntentPersistableExtrases = new PersistableBundle[mIntents.length];
+        for (int i = 0; i < mIntents.length; i++) {
+            final Intent intent = mIntents[i];
+            final Bundle extras = intent.getExtras();
+            if (extras == null) {
+                mIntentPersistableExtrases[i] = null;
+            } else {
+                mIntentPersistableExtrases[i] = new PersistableBundle(extras);
+                intent.replaceExtras((Bundle) null);
+            }
+        }
+    }
+
+    private static ArraySet<String> cloneCategories(Set<String> source) {
+        if (source == null) {
+            return null;
+        }
+        final ArraySet<String> ret = new ArraySet<>(source.size());
+        for (CharSequence s : source) {
+            if (!TextUtils.isEmpty(s)) {
+                ret.add(s.toString().intern());
+            }
+        }
+        return ret;
+    }
+
+    private static Intent[] cloneIntents(Intent[] intents) {
+        if (intents == null) {
+            return null;
+        }
+        final Intent[] ret = new Intent[intents.length];
+        for (int i = 0; i < ret.length; i++) {
+            if (intents[i] != null) {
+                ret[i] = new Intent(intents[i]);
+            }
+        }
+        return ret;
+    }
+
+    private static PersistableBundle[] clonePersistableBundle(PersistableBundle[] bundle) {
+        if (bundle == null) {
+            return null;
+        }
+        final PersistableBundle[] ret = new PersistableBundle[bundle.length];
+        for (int i = 0; i < ret.length; i++) {
+            if (bundle[i] != null) {
+                ret[i] = new PersistableBundle(bundle[i]);
+            }
+        }
+        return ret;
+    }
+
+    private static Person[] clonePersons(Person[] persons) {
+        if (persons == null) {
+            return null;
+        }
+        final Person[] ret = new Person[persons.length];
+        for (int i = 0; i < ret.length; i++) {
+            if (persons[i] != null) {
+                // Don't need to keep the icon, remove it to save space
+                ret[i] = persons[i].toBuilder().setIcon(null).build();
+            }
+        }
+        return ret;
+    }
+
+    /**
+     * Throws if any of the mandatory fields is not set.
+     *
+     * @hide
+     */
+    public void enforceMandatoryFields(boolean forPinned) {
+        Preconditions.checkStringNotEmpty(mId, "Shortcut ID must be provided");
+        if (!forPinned) {
+            Objects.requireNonNull(mActivity, "Activity must be provided");
+        }
+        if (mTitle == null && mTitleResId == 0) {
+            throw new IllegalArgumentException("Short label must be provided");
+        }
+        Objects.requireNonNull(mIntents, "Shortcut Intent must be provided");
+        Preconditions.checkArgument(mIntents.length > 0, "Shortcut Intent must be provided");
+    }
+
+    /**
+     * Copy constructor.
+     */
+    private ShortcutInfo(ShortcutInfo source, @CloneFlags int cloneFlags) {
+        mUserId = source.mUserId;
+        mId = source.mId;
+        mPackageName = source.mPackageName;
+        mActivity = source.mActivity;
+        mFlags = source.mFlags;
+        mLastChangedTimestamp = source.mLastChangedTimestamp;
+        mDisabledReason = source.mDisabledReason;
+        mLocusId = source.mLocusId;
+
+        // Just always keep it since it's cheep.
+        mIconResId = source.mIconResId;
+
+        if ((cloneFlags & CLONE_REMOVE_NON_KEY_INFO) == 0) {
+
+            if ((cloneFlags & CLONE_REMOVE_ICON) == 0) {
+                mIcon = source.mIcon;
+                mBitmapPath = source.mBitmapPath;
+                mIconUri = source.mIconUri;
+            }
+
+            mTitle = source.mTitle;
+            mTitleResId = source.mTitleResId;
+            mText = source.mText;
+            mTextResId = source.mTextResId;
+            mDisabledMessage = source.mDisabledMessage;
+            mDisabledMessageResId = source.mDisabledMessageResId;
+            mCategories = cloneCategories(source.mCategories);
+            if ((cloneFlags & CLONE_REMOVE_PERSON) == 0) {
+                mPersons = clonePersons(source.mPersons);
+            }
+            if ((cloneFlags & CLONE_REMOVE_INTENT) == 0) {
+                mIntents = cloneIntents(source.mIntents);
+                mIntentPersistableExtrases =
+                        clonePersistableBundle(source.mIntentPersistableExtrases);
+            }
+            mRank = source.mRank;
+            mExtras = source.mExtras;
+
+            if ((cloneFlags & CLONE_REMOVE_RES_NAMES) == 0) {
+                mTitleResName = source.mTitleResName;
+                mTextResName = source.mTextResName;
+                mDisabledMessageResName = source.mDisabledMessageResName;
+                mIconResName = source.mIconResName;
+            }
+        } else {
+            // Set this bit.
+            mFlags |= FLAG_KEY_FIELDS_ONLY;
+        }
+    }
+
+    /**
+     * Load a string resource from the publisher app.
+     *
+     * @param resId resource ID
+     * @param defValue default value to be returned when the specified resource isn't found.
+     */
+    private CharSequence getResourceString(Resources res, int resId, CharSequence defValue) {
+        try {
+            return res.getString(resId);
+        } catch (NotFoundException e) {
+            Log.e(TAG, "Resource for ID=" + resId + " not found in package " + mPackageName);
+            return defValue;
+        }
+    }
+
+    /**
+     * Load the string resources for the text fields and set them to the actual value fields.
+     * This will set {@link #FLAG_STRINGS_RESOLVED}.
+     *
+     * @param res {@link Resources} for the publisher.  Must have been loaded with
+     * {@link PackageManager#getResourcesForApplicationAsUser}.
+     *
+     * @hide
+     */
+    public void resolveResourceStrings(@NonNull Resources res) {
+        mFlags |= FLAG_STRINGS_RESOLVED;
+
+        if ((mTitleResId == 0) && (mTextResId == 0) && (mDisabledMessageResId == 0)) {
+            return; // Bail early.
+        }
+
+        if (mTitleResId != 0) {
+            mTitle = getResourceString(res, mTitleResId, mTitle);
+        }
+        if (mTextResId != 0) {
+            mText = getResourceString(res, mTextResId, mText);
+        }
+        if (mDisabledMessageResId != 0) {
+            mDisabledMessage = getResourceString(res, mDisabledMessageResId, mDisabledMessage);
+        }
+    }
+
+    /**
+     * Look up resource name for a given resource ID.
+     *
+     * @return a simple resource name (e.g. "text_1") when {@code withType} is false, or with the
+     * type (e.g. "string/text_1").
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    public static String lookUpResourceName(@NonNull Resources res, int resId, boolean withType,
+            @NonNull String packageName) {
+        if (resId == 0) {
+            return null;
+        }
+        try {
+            final String fullName = res.getResourceName(resId);
+
+            if (ANDROID_PACKAGE_NAME.equals(getResourcePackageName(fullName))) {
+                // If it's a framework resource, the value won't change, so just return the ID
+                // value as a string.
+                return String.valueOf(resId);
+            }
+            return withType ? getResourceTypeAndEntryName(fullName)
+                    : getResourceEntryName(fullName);
+        } catch (NotFoundException e) {
+            Log.e(TAG, "Resource name for ID=" + resId + " not found in package " + packageName
+                    + ". Resource IDs may change when the application is upgraded, and the system"
+                    + " may not be able to find the correct resource.");
+            return null;
+        }
+    }
+
+    /**
+     * Extract the package name from a fully-donated resource name.
+     * e.g. "com.android.app1:drawable/icon1" -> "com.android.app1"
+     * @hide
+     */
+    @VisibleForTesting
+    public static String getResourcePackageName(@NonNull String fullResourceName) {
+        final int p1 = fullResourceName.indexOf(':');
+        if (p1 < 0) {
+            return null;
+        }
+        return fullResourceName.substring(0, p1);
+    }
+
+    /**
+     * Extract the type name from a fully-donated resource name.
+     * e.g. "com.android.app1:drawable/icon1" -> "drawable"
+     * @hide
+     */
+    @VisibleForTesting
+    public static String getResourceTypeName(@NonNull String fullResourceName) {
+        final int p1 = fullResourceName.indexOf(':');
+        if (p1 < 0) {
+            return null;
+        }
+        final int p2 = fullResourceName.indexOf('/', p1 + 1);
+        if (p2 < 0) {
+            return null;
+        }
+        return fullResourceName.substring(p1 + 1, p2);
+    }
+
+    /**
+     * Extract the type name + the entry name from a fully-donated resource name.
+     * e.g. "com.android.app1:drawable/icon1" -> "drawable/icon1"
+     * @hide
+     */
+    @VisibleForTesting
+    public static String getResourceTypeAndEntryName(@NonNull String fullResourceName) {
+        final int p1 = fullResourceName.indexOf(':');
+        if (p1 < 0) {
+            return null;
+        }
+        return fullResourceName.substring(p1 + 1);
+    }
+
+    /**
+     * Extract the entry name from a fully-donated resource name.
+     * e.g. "com.android.app1:drawable/icon1" -> "icon1"
+     * @hide
+     */
+    @VisibleForTesting
+    public static String getResourceEntryName(@NonNull String fullResourceName) {
+        final int p1 = fullResourceName.indexOf('/');
+        if (p1 < 0) {
+            return null;
+        }
+        return fullResourceName.substring(p1 + 1);
+    }
+
+    /**
+     * Return the resource ID for a given resource ID.
+     *
+     * Basically its' a wrapper over {@link Resources#getIdentifier(String, String, String)}, except
+     * if {@code resourceName} is an integer then it'll just return its value.  (Which also the
+     * aforementioned method would do internally, but not documented, so doing here explicitly.)
+     *
+     * @param res {@link Resources} for the publisher.  Must have been loaded with
+     * {@link PackageManager#getResourcesForApplicationAsUser}.
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    public static int lookUpResourceId(@NonNull Resources res, @Nullable String resourceName,
+            @Nullable String resourceType, String packageName) {
+        if (resourceName == null) {
+            return 0;
+        }
+        try {
+            try {
+                // It the name can be parsed as an integer, just use it.
+                return Integer.parseInt(resourceName);
+            } catch (NumberFormatException ignore) {
+            }
+
+            return res.getIdentifier(resourceName, resourceType, packageName);
+        } catch (NotFoundException e) {
+            Log.e(TAG, "Resource ID for name=" + resourceName + " not found in package "
+                    + packageName);
+            return 0;
+        }
+    }
+
+    /**
+     * Look up resource names from the resource IDs for the icon res and the text fields, and fill
+     * in the resource name fields.
+     *
+     * @param res {@link Resources} for the publisher.  Must have been loaded with
+     * {@link PackageManager#getResourcesForApplicationAsUser}.
+     *
+     * @hide
+     */
+    public void lookupAndFillInResourceNames(@NonNull Resources res) {
+        if ((mTitleResId == 0) && (mTextResId == 0) && (mDisabledMessageResId == 0)
+                && (mIconResId == 0)) {
+            return; // Bail early.
+        }
+
+        // We don't need types for strings because their types are always "string".
+        mTitleResName = lookUpResourceName(res, mTitleResId, /*withType=*/ false, mPackageName);
+        mTextResName = lookUpResourceName(res, mTextResId, /*withType=*/ false, mPackageName);
+        mDisabledMessageResName = lookUpResourceName(res, mDisabledMessageResId,
+                /*withType=*/ false, mPackageName);
+
+        // But icons have multiple possible types, so include the type.
+        mIconResName = lookUpResourceName(res, mIconResId, /*withType=*/ true, mPackageName);
+    }
+
+    /**
+     * Look up resource IDs from the resource names for the icon res and the text fields, and fill
+     * in the resource ID fields.
+     *
+     * This is called when an app is updated.
+     *
+     * @hide
+     */
+    public void lookupAndFillInResourceIds(@NonNull Resources res) {
+        if ((mTitleResName == null) && (mTextResName == null) && (mDisabledMessageResName == null)
+                && (mIconResName == null)) {
+            return; // Bail early.
+        }
+
+        mTitleResId = lookUpResourceId(res, mTitleResName, RES_TYPE_STRING, mPackageName);
+        mTextResId = lookUpResourceId(res, mTextResName, RES_TYPE_STRING, mPackageName);
+        mDisabledMessageResId = lookUpResourceId(res, mDisabledMessageResName, RES_TYPE_STRING,
+                mPackageName);
+
+        // mIconResName already contains the type, so the third argument is not needed.
+        mIconResId = lookUpResourceId(res, mIconResName, null, mPackageName);
+    }
+
+    /**
+     * Copy a {@link ShortcutInfo}, optionally removing fields.
+     * @hide
+     */
+    public ShortcutInfo clone(@CloneFlags int cloneFlags) {
+        return new ShortcutInfo(this, cloneFlags);
+    }
+
+    /**
+     * @hide
+     *
+     * @isUpdating set true if it's "update", as opposed to "replace".
+     */
+    public void ensureUpdatableWith(ShortcutInfo source, boolean isUpdating) {
+        if (isUpdating) {
+            Preconditions.checkState(isVisibleToPublisher(),
+                    "[Framework BUG] Invisible shortcuts can't be updated");
+        }
+        Preconditions.checkState(mUserId == source.mUserId, "Owner User ID must match");
+        Preconditions.checkState(mId.equals(source.mId), "ID must match");
+        Preconditions.checkState(mPackageName.equals(source.mPackageName),
+                "Package name must match");
+
+        if (isVisibleToPublisher()) {
+            // Don't do this check for restore-blocked shortcuts.
+            Preconditions.checkState(!isImmutable(), "Target ShortcutInfo is immutable");
+        }
+    }
+
+    /**
+     * Copy non-null/zero fields from another {@link ShortcutInfo}.  Only "public" information
+     * will be overwritten.  The timestamp will *not* be updated to be consistent with other
+     * setters (and also the clock is not injectable in this file).
+     *
+     * - Flags will not change
+     * - mBitmapPath will not change
+     * - Current time will be set to timestamp
+     *
+     * @throws IllegalStateException if source is not compatible.
+     *
+     * @hide
+     */
+    public void copyNonNullFieldsFrom(ShortcutInfo source) {
+        ensureUpdatableWith(source, /*isUpdating=*/ true);
+
+        if (source.mActivity != null) {
+            mActivity = source.mActivity;
+        }
+
+        if (source.mIcon != null) {
+            mIcon = source.mIcon;
+
+            mIconResId = 0;
+            mIconResName = null;
+            mBitmapPath = null;
+            mIconUri = null;
+        }
+        if (source.mTitle != null) {
+            mTitle = source.mTitle;
+            mTitleResId = 0;
+            mTitleResName = null;
+        } else if (source.mTitleResId != 0) {
+            mTitle = null;
+            mTitleResId = source.mTitleResId;
+            mTitleResName = null;
+        }
+
+        if (source.mText != null) {
+            mText = source.mText;
+            mTextResId = 0;
+            mTextResName = null;
+        } else if (source.mTextResId != 0) {
+            mText = null;
+            mTextResId = source.mTextResId;
+            mTextResName = null;
+        }
+        if (source.mDisabledMessage != null) {
+            mDisabledMessage = source.mDisabledMessage;
+            mDisabledMessageResId = 0;
+            mDisabledMessageResName = null;
+        } else if (source.mDisabledMessageResId != 0) {
+            mDisabledMessage = null;
+            mDisabledMessageResId = source.mDisabledMessageResId;
+            mDisabledMessageResName = null;
+        }
+        if (source.mCategories != null) {
+            mCategories = cloneCategories(source.mCategories);
+        }
+        if (source.mPersons != null) {
+            mPersons = clonePersons(source.mPersons);
+        }
+        if (source.mIntents != null) {
+            mIntents = cloneIntents(source.mIntents);
+            mIntentPersistableExtrases =
+                    clonePersistableBundle(source.mIntentPersistableExtrases);
+        }
+        if (source.mRank != RANK_NOT_SET) {
+            mRank = source.mRank;
+        }
+        if (source.mExtras != null) {
+            mExtras = source.mExtras;
+        }
+
+        if (source.mLocusId != null) {
+            mLocusId = source.mLocusId;
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public static Icon validateIcon(Icon icon) {
+        switch (icon.getType()) {
+            case Icon.TYPE_RESOURCE:
+            case Icon.TYPE_BITMAP:
+            case Icon.TYPE_ADAPTIVE_BITMAP:
+            case Icon.TYPE_URI:
+            case Icon.TYPE_URI_ADAPTIVE_BITMAP:
+                break; // OK
+            default:
+                throw getInvalidIconException();
+        }
+        if (icon.hasTint()) {
+            throw new IllegalArgumentException("Icons with tints are not supported");
+        }
+
+        return icon;
+    }
+
+    /** @hide */
+    public static IllegalArgumentException getInvalidIconException() {
+        return new IllegalArgumentException("Unsupported icon type:"
+                +" only the bitmap and resource types are supported");
+    }
+
+    /**
+     * Builder class for {@link ShortcutInfo} objects.
+     *
+     * @see ShortcutManager
+     */
+    public static class Builder {
+        private final Context mContext;
+
+        private String mId;
+
+        private ComponentName mActivity;
+
+        private Icon mIcon;
+
+        private int mTitleResId;
+
+        private CharSequence mTitle;
+
+        private int mTextResId;
+
+        private CharSequence mText;
+
+        private int mDisabledMessageResId;
+
+        private CharSequence mDisabledMessage;
+
+        private Set<String> mCategories;
+
+        private Intent[] mIntents;
+
+        private Person[] mPersons;
+
+        private boolean mIsLongLived;
+
+        private int mRank = RANK_NOT_SET;
+
+        private PersistableBundle mExtras;
+
+        private LocusId mLocusId;
+
+        /**
+         * Old style constructor.
+         * @hide
+         */
+        @Deprecated
+        public Builder(Context context) {
+            mContext = context;
+        }
+
+        /**
+         * Used with the old style constructor, kept for unit tests.
+         * @hide
+         */
+        @NonNull
+        @Deprecated
+        public Builder setId(@NonNull String id) {
+            mId = Preconditions.checkStringNotEmpty(id, "id cannot be empty");
+            return this;
+        }
+
+        /**
+         * Constructor.
+         *
+         * @param context Client context.
+         * @param id ID of the shortcut.
+         */
+        public Builder(Context context, String id) {
+            mContext = context;
+            mId = Preconditions.checkStringNotEmpty(id, "id cannot be empty");
+        }
+
+        /**
+         * Sets the {@link LocusId} associated with this shortcut.
+         *
+         * <p>This method should be called when the {@link LocusId} is used in other places (such
+         * as {@link Notification} and {@link ContentCaptureContext}) so the device's intelligence
+         * services can correlate them.
+         */
+        @NonNull
+        public Builder setLocusId(@NonNull LocusId locusId) {
+            mLocusId = Objects.requireNonNull(locusId, "locusId cannot be null");
+            return this;
+        }
+
+        /**
+         * Sets the target activity.  A shortcut will be shown along with this activity's icon
+         * on the launcher.
+         *
+         * When selecting a target activity, keep the following in mind:
+         * <ul>
+         * <li>All dynamic shortcuts must have a target activity.  When a shortcut with no target
+         * activity is published using
+         * {@link ShortcutManager#addDynamicShortcuts(List)} or
+         * {@link ShortcutManager#setDynamicShortcuts(List)},
+         * the first main activity defined in the app's <code>AndroidManifest.xml</code>
+         * file is used.
+         *
+         * <li>Only "main" activities&mdash;ones that define the {@link Intent#ACTION_MAIN}
+         * and {@link Intent#CATEGORY_LAUNCHER} intent filters&mdash;can be target
+         * activities.
+         *
+         * <li>By default, the first main activity defined in the app's manifest is
+         * the target activity.
+         *
+         * <li>A target activity must belong to the publisher app.
+         * </ul>
+         *
+         * @see ShortcutInfo#getActivity()
+         */
+        @NonNull
+        public Builder setActivity(@NonNull ComponentName activity) {
+            mActivity = Objects.requireNonNull(activity, "activity cannot be null");
+            return this;
+        }
+
+        /**
+         * Sets an icon of a shortcut.
+         *
+         * <p>Icons are not available on {@link ShortcutInfo} instances
+         * returned by {@link ShortcutManager} or {@link LauncherApps}.  The default launcher
+         * app can use {@link LauncherApps#getShortcutIconDrawable(ShortcutInfo, int)}
+         * or {@link LauncherApps#getShortcutBadgedIconDrawable(ShortcutInfo, int)} to fetch
+         * shortcut icons.
+         *
+         * <p>Tints set with {@link Icon#setTint} or {@link Icon#setTintList} are not supported
+         * and will be ignored.
+         *
+         * <p>Only icons created with {@link Icon#createWithBitmap(Bitmap)},
+         * {@link Icon#createWithAdaptiveBitmap(Bitmap)}
+         * and {@link Icon#createWithResource} are supported.
+         * Other types, such as URI-based icons, are not supported.
+         *
+         * @see LauncherApps#getShortcutIconDrawable(ShortcutInfo, int)
+         * @see LauncherApps#getShortcutBadgedIconDrawable(ShortcutInfo, int)
+         */
+        @NonNull
+        public Builder setIcon(Icon icon) {
+            mIcon = validateIcon(icon);
+            return this;
+        }
+
+        /**
+         * @hide We don't support resource strings for dynamic shortcuts for now.  (But unit tests
+         * use it.)
+         */
+        @Deprecated
+        public Builder setShortLabelResId(int shortLabelResId) {
+            Preconditions.checkState(mTitle == null, "shortLabel already set");
+            mTitleResId = shortLabelResId;
+            return this;
+        }
+
+        /**
+         * Sets the short title of a shortcut.
+         *
+         * <p>This is a mandatory field when publishing a new shortcut with
+         * {@link ShortcutManager#addDynamicShortcuts(List)} or
+         * {@link ShortcutManager#setDynamicShortcuts(List)}.
+         *
+         * <p>This field is intended to be a concise description of a shortcut.
+         *
+         * <p>The recommended maximum length is 10 characters.
+         *
+         * @see ShortcutInfo#getShortLabel()
+         */
+        @NonNull
+        public Builder setShortLabel(@NonNull CharSequence shortLabel) {
+            Preconditions.checkState(mTitleResId == 0, "shortLabelResId already set");
+            mTitle = Preconditions.checkStringNotEmpty(shortLabel, "shortLabel cannot be empty");
+            return this;
+        }
+
+        /**
+         * @hide We don't support resource strings for dynamic shortcuts for now.  (But unit tests
+         * use it.)
+         */
+        @Deprecated
+        public Builder setLongLabelResId(int longLabelResId) {
+            Preconditions.checkState(mText == null, "longLabel already set");
+            mTextResId = longLabelResId;
+            return this;
+        }
+
+        /**
+         * Sets the text of a shortcut.
+         *
+         * <p>This field is intended to be more descriptive than the shortcut title.  The launcher
+         * shows this instead of the short title when it has enough space.
+         *
+         * <p>The recommend maximum length is 25 characters.
+         *
+         * @see ShortcutInfo#getLongLabel()
+         */
+        @NonNull
+        public Builder setLongLabel(@NonNull CharSequence longLabel) {
+            Preconditions.checkState(mTextResId == 0, "longLabelResId already set");
+            mText = Preconditions.checkStringNotEmpty(longLabel, "longLabel cannot be empty");
+            return this;
+        }
+
+        /** @hide -- old signature, the internal code still uses it. */
+        @Deprecated
+        public Builder setTitle(@NonNull CharSequence value) {
+            return setShortLabel(value);
+        }
+
+        /** @hide -- old signature, the internal code still uses it. */
+        @Deprecated
+        public Builder setTitleResId(int value) {
+            return setShortLabelResId(value);
+        }
+
+        /** @hide -- old signature, the internal code still uses it. */
+        @Deprecated
+        public Builder setText(@NonNull CharSequence value) {
+            return setLongLabel(value);
+        }
+
+        /** @hide -- old signature, the internal code still uses it. */
+        @Deprecated
+        public Builder setTextResId(int value) {
+            return setLongLabelResId(value);
+        }
+
+        /**
+         * @hide We don't support resource strings for dynamic shortcuts for now.  (But unit tests
+         * use it.)
+         */
+        @Deprecated
+        public Builder setDisabledMessageResId(int disabledMessageResId) {
+            Preconditions.checkState(mDisabledMessage == null, "disabledMessage already set");
+            mDisabledMessageResId = disabledMessageResId;
+            return this;
+        }
+
+        /**
+         * Sets the message that should be shown when the user attempts to start a shortcut that
+         * is disabled.
+         *
+         * @see ShortcutInfo#getDisabledMessage()
+         */
+        @NonNull
+        public Builder setDisabledMessage(@NonNull CharSequence disabledMessage) {
+            Preconditions.checkState(
+                    mDisabledMessageResId == 0, "disabledMessageResId already set");
+            mDisabledMessage =
+                    Preconditions.checkStringNotEmpty(disabledMessage,
+                            "disabledMessage cannot be empty");
+            return this;
+        }
+
+        /**
+         * Sets categories for a shortcut.  Launcher apps may use this information to
+         * categorize shortcuts.
+         *
+         * @see #SHORTCUT_CATEGORY_CONVERSATION
+         * @see ShortcutInfo#getCategories()
+         */
+        @NonNull
+        public Builder setCategories(Set<String> categories) {
+            mCategories = categories;
+            return this;
+        }
+
+        /**
+         * Sets the intent of a shortcut.  Alternatively, {@link #setIntents(Intent[])} can be used
+         * to launch an activity with other activities in the back stack.
+         *
+         * <p>This is a mandatory field when publishing a new shortcut with
+         * {@link ShortcutManager#addDynamicShortcuts(List)} or
+         * {@link ShortcutManager#setDynamicShortcuts(List)}.
+         *
+         * <p>A shortcut can launch any intent that the publisher app has permission to
+         * launch.  For example, a shortcut can launch an unexported activity within the publisher
+         * app.  A shortcut intent doesn't have to point at the target activity.
+         *
+         * <p>The given {@code intent} can contain extras, but these extras must contain values
+         * of primitive types in order for the system to persist these values.
+         *
+         * @see ShortcutInfo#getIntent()
+         * @see #setIntents(Intent[])
+         */
+        @NonNull
+        public Builder setIntent(@NonNull Intent intent) {
+            return setIntents(new Intent[]{intent});
+        }
+
+        /**
+         * Sets multiple intents instead of a single intent, in order to launch an activity with
+         * other activities in back stack.  Use {@link TaskStackBuilder} to build intents. The
+         * last element in the list represents the only intent that doesn't place an activity on
+         * the back stack.
+         * See the {@link ShortcutManager} javadoc for details.
+         *
+         * @see Builder#setIntent(Intent)
+         * @see ShortcutInfo#getIntents()
+         * @see Context#startActivities(Intent[])
+         * @see TaskStackBuilder
+         */
+        @NonNull
+        public Builder setIntents(@NonNull Intent[] intents) {
+            Objects.requireNonNull(intents, "intents cannot be null");
+            Objects.requireNonNull(intents.length, "intents cannot be empty");
+            for (Intent intent : intents) {
+                Objects.requireNonNull(intent, "intents cannot contain null");
+                Objects.requireNonNull(intent.getAction(), "intent's action must be set");
+            }
+            // Make sure always clone incoming intents.
+            mIntents = cloneIntents(intents);
+            return this;
+        }
+
+        /**
+         * Add a person that is relevant to this shortcut. Alternatively,
+         * {@link #setPersons(Person[])} can be used to add multiple persons to a shortcut.
+         *
+         * <p> This is an optional field, but the addition of person may cause this shortcut to
+         * appear more prominently in the user interface (e.g. ShareSheet).
+         *
+         * <p> A person should usually contain a uri in order to benefit from the ranking boost.
+         * However, even if no uri is provided, it's beneficial to provide people in the shortcut,
+         * such that listeners and voice only devices can announce and handle them properly.
+         *
+         * @see Person
+         * @see #setPersons(Person[])
+         */
+        @NonNull
+        public Builder setPerson(@NonNull Person person) {
+            return setPersons(new Person[]{person});
+        }
+
+        /**
+         * Sets multiple persons instead of a single person.
+         *
+         * @see Person
+         * @see #setPerson(Person)
+         */
+        @NonNull
+        public Builder setPersons(@NonNull Person[] persons) {
+            Objects.requireNonNull(persons, "persons cannot be null");
+            Objects.requireNonNull(persons.length, "persons cannot be empty");
+            for (Person person : persons) {
+                Objects.requireNonNull(person, "persons cannot contain null");
+            }
+            mPersons = clonePersons(persons);
+            return this;
+        }
+
+        /**
+         * Sets if a shortcut would be valid even if it has been unpublished/invisible by the app
+         * (as a dynamic or pinned shortcut). If it is long lived, it can be cached by various
+         * system services even after it has been unpublished as a dynamic shortcut.
+         */
+        @NonNull
+        public Builder setLongLived(boolean londLived) {
+            mIsLongLived = londLived;
+            return this;
+        }
+
+        /**
+         * "Rank" of a shortcut, which is a non-negative value that's used by the launcher app
+         * to sort shortcuts.
+         *
+         * See {@link ShortcutInfo#getRank()} for details.
+         */
+        @NonNull
+        public Builder setRank(int rank) {
+            Preconditions.checkArgument((0 <= rank),
+                    "Rank cannot be negative or bigger than MAX_RANK");
+            mRank = rank;
+            return this;
+        }
+
+        /**
+         * Extras that the app can set for any purpose.
+         *
+         * <p>Apps can store arbitrary shortcut metadata in extras and retrieve the
+         * metadata later using {@link ShortcutInfo#getExtras()}.
+         */
+        @NonNull
+        public Builder setExtras(@NonNull PersistableBundle extras) {
+            mExtras = extras;
+            return this;
+        }
+
+        /**
+         * Creates a {@link ShortcutInfo} instance.
+         */
+        @NonNull
+        public ShortcutInfo build() {
+            return new ShortcutInfo(this);
+        }
+    }
+
+    /**
+     * Returns the ID of a shortcut.
+     *
+     * <p>Shortcut IDs are unique within each publisher app and must be stable across
+     * devices so that shortcuts will still be valid when restored on a different device.
+     * See {@link ShortcutManager} for details.
+     */
+    @NonNull
+    public String getId() {
+        return mId;
+    }
+
+    /**
+     * Gets the {@link LocusId} associated with this shortcut.
+     *
+     * <p>Used by the device's intelligence services to correlate objects (such as
+     * {@link Notification} and {@link ContentCaptureContext}) that are correlated.
+     */
+    @Nullable
+    public LocusId getLocusId() {
+        return mLocusId;
+    }
+
+    /**
+     * Return the package name of the publisher app.
+     */
+    @NonNull
+    public String getPackage() {
+        return mPackageName;
+    }
+
+    /**
+     * Return the target activity.
+     *
+     * <p>This has nothing to do with the activity that this shortcut will launch.
+     * Launcher apps should show the launcher icon for the returned activity alongside
+     * this shortcut.
+     *
+     * @see Builder#setActivity
+     */
+    @Nullable
+    public ComponentName getActivity() {
+        return mActivity;
+    }
+
+    /** @hide */
+    public void setActivity(ComponentName activity) {
+        mActivity = activity;
+    }
+
+    /**
+     * Returns the shortcut icon.
+     *
+     * @hide
+     */
+    @Nullable
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    public Icon getIcon() {
+        return mIcon;
+    }
+
+    /** @hide -- old signature, the internal code still uses it. */
+    @Nullable
+    @Deprecated
+    public CharSequence getTitle() {
+        return mTitle;
+    }
+
+    /** @hide -- old signature, the internal code still uses it. */
+    @Deprecated
+    public int getTitleResId() {
+        return mTitleResId;
+    }
+
+    /** @hide -- old signature, the internal code still uses it. */
+    @Nullable
+    @Deprecated
+    public CharSequence getText() {
+        return mText;
+    }
+
+    /** @hide -- old signature, the internal code still uses it. */
+    @Deprecated
+    public int getTextResId() {
+        return mTextResId;
+    }
+
+    /**
+     * Return the short description of a shortcut.
+     *
+     * @see Builder#setShortLabel(CharSequence)
+     */
+    @Nullable
+    public CharSequence getShortLabel() {
+        return mTitle;
+    }
+
+    /** @hide */
+    public int getShortLabelResourceId() {
+        return mTitleResId;
+    }
+
+    /**
+     * Return the long description of a shortcut.
+     *
+     * @see Builder#setLongLabel(CharSequence)
+     */
+    @Nullable
+    public CharSequence getLongLabel() {
+        return mText;
+    }
+
+    /** @hide */
+    public int getLongLabelResourceId() {
+        return mTextResId;
+    }
+
+    /**
+     * Return the message that should be shown when the user attempts to start a shortcut
+     * that is disabled.
+     *
+     * @see Builder#setDisabledMessage(CharSequence)
+     */
+    @Nullable
+    public CharSequence getDisabledMessage() {
+        return mDisabledMessage;
+    }
+
+    /** @hide */
+    public int getDisabledMessageResourceId() {
+        return mDisabledMessageResId;
+    }
+
+    /** @hide */
+    public void setDisabledReason(@DisabledReason int reason) {
+        mDisabledReason = reason;
+    }
+
+    /**
+     * Returns why a shortcut has been disabled.
+     */
+    @DisabledReason
+    public int getDisabledReason() {
+        return mDisabledReason;
+    }
+
+    /**
+     * Return the shortcut's categories.
+     *
+     * @see Builder#setCategories(Set)
+     */
+    @Nullable
+    public Set<String> getCategories() {
+        return mCategories;
+    }
+
+    /**
+     * Returns the intent that is executed when the user selects this shortcut.
+     * If setIntents() was used, then return the last intent in the array.
+     *
+     * <p>Launcher apps <b>cannot</b> see the intent.  If a {@link ShortcutInfo} is
+     * obtained via {@link LauncherApps}, then this method will always return null.
+     * Launchers can only start a shortcut intent with {@link LauncherApps#startShortcut}.
+     *
+     * @see Builder#setIntent(Intent)
+     */
+    @Nullable
+    public Intent getIntent() {
+        if (mIntents == null || mIntents.length == 0) {
+            return null;
+        }
+        final int last = mIntents.length - 1;
+        final Intent intent = new Intent(mIntents[last]);
+        return setIntentExtras(intent, mIntentPersistableExtrases[last]);
+    }
+
+    /**
+     * Return the intent set with {@link Builder#setIntents(Intent[])}.
+     *
+     * <p>Launcher apps <b>cannot</b> see the intents.  If a {@link ShortcutInfo} is
+     * obtained via {@link LauncherApps}, then this method will always return null.
+     * Launchers can only start a shortcut intent with {@link LauncherApps#startShortcut}.
+     *
+     * @see Builder#setIntents(Intent[])
+     */
+    @Nullable
+    public Intent[] getIntents() {
+        final Intent[] ret = new Intent[mIntents.length];
+
+        for (int i = 0; i < ret.length; i++) {
+            ret[i] = new Intent(mIntents[i]);
+            setIntentExtras(ret[i], mIntentPersistableExtrases[i]);
+        }
+
+        return ret;
+    }
+
+    /**
+     * Return "raw" intents, which is the original intents without the extras.
+     * @hide
+     */
+    @Nullable
+    public Intent[] getIntentsNoExtras() {
+        return mIntents;
+    }
+
+    /**
+     * Return the Persons set with {@link Builder#setPersons(Person[])}.
+     *
+     * @hide
+     */
+    @Nullable
+    @SystemApi
+    public Person[] getPersons() {
+        return clonePersons(mPersons);
+    }
+
+    /**
+     * The extras in the intents.  We convert extras into {@link PersistableBundle} so we can
+     * persist them.
+     * @hide
+     */
+    @Nullable
+    public PersistableBundle[] getIntentPersistableExtrases() {
+        return mIntentPersistableExtrases;
+    }
+
+    /**
+     * "Rank" of a shortcut, which is a non-negative, sequential value that's unique for each
+     * {@link #getActivity} for each of the two types of shortcuts (static and dynamic).
+     *
+     * <p><em>Floating shortcuts</em>, or shortcuts that are neither static nor dynamic, will all
+     * have rank 0, because they aren't sorted.
+     *
+     * See the {@link ShortcutManager}'s class javadoc for details.
+     *
+     * @see Builder#setRank(int)
+     */
+    public int getRank() {
+        return mRank;
+    }
+
+    /** @hide */
+    public boolean hasRank() {
+        return mRank != RANK_NOT_SET;
+    }
+
+    /** @hide */
+    public void setRank(int rank) {
+        mRank = rank;
+    }
+
+    /** @hide */
+    public void clearImplicitRankAndRankChangedFlag() {
+        mImplicitRank = 0;
+    }
+
+    /** @hide */
+    public void setImplicitRank(int rank) {
+        // Make sure to keep RANK_CHANGED_BIT.
+        mImplicitRank = (mImplicitRank & RANK_CHANGED_BIT) | (rank & IMPLICIT_RANK_MASK);
+    }
+
+    /** @hide */
+    public int getImplicitRank() {
+        return mImplicitRank & IMPLICIT_RANK_MASK;
+    }
+
+    /** @hide */
+    public void setRankChanged() {
+        mImplicitRank |= RANK_CHANGED_BIT;
+    }
+
+    /** @hide */
+    public boolean isRankChanged() {
+        return (mImplicitRank & RANK_CHANGED_BIT) != 0;
+    }
+
+    /**
+     * Extras that the app can set for any purpose.
+     *
+     * @see Builder#setExtras(PersistableBundle)
+     */
+    @Nullable
+    public PersistableBundle getExtras() {
+        return mExtras;
+    }
+
+    /** @hide */
+    public int getUserId() {
+        return mUserId;
+    }
+
+    /**
+     * {@link UserHandle} on which the publisher created this shortcut.
+     */
+    public UserHandle getUserHandle() {
+        return UserHandle.of(mUserId);
+    }
+
+    /**
+     * Last time when any of the fields was updated.
+     */
+    public long getLastChangedTimestamp() {
+        return mLastChangedTimestamp;
+    }
+
+    /** @hide */
+    @ShortcutFlags
+    public int getFlags() {
+        return mFlags;
+    }
+
+    /** @hide*/
+    public void replaceFlags(@ShortcutFlags int flags) {
+        mFlags = flags;
+    }
+
+    /** @hide*/
+    public void addFlags(@ShortcutFlags int flags) {
+        mFlags |= flags;
+    }
+
+    /** @hide*/
+    public void clearFlags(@ShortcutFlags int flags) {
+        mFlags &= ~flags;
+    }
+
+    /** @hide*/
+    public boolean hasFlags(@ShortcutFlags int flags) {
+        return (mFlags & flags) == flags;
+    }
+
+    /** @hide */
+    public boolean isReturnedByServer() {
+        return hasFlags(FLAG_RETURNED_BY_SERVICE);
+    }
+
+    /** @hide */
+    public void setReturnedByServer() {
+        addFlags(FLAG_RETURNED_BY_SERVICE);
+    }
+
+    /** @hide */
+    public boolean isLongLived() {
+        return hasFlags(FLAG_LONG_LIVED);
+    }
+
+    /** @hide */
+    public void setLongLived() {
+        addFlags(FLAG_LONG_LIVED);
+    }
+
+    /** @hide */
+    public void setCached() {
+        addFlags(FLAG_CACHED);
+    }
+
+    /** Return whether a shortcut is cached. */
+    public boolean isCached() {
+        return hasFlags(FLAG_CACHED);
+    }
+
+    /** Return whether a shortcut is dynamic. */
+    public boolean isDynamic() {
+        return hasFlags(FLAG_DYNAMIC);
+    }
+
+    /** Return whether a shortcut is pinned. */
+    public boolean isPinned() {
+        return hasFlags(FLAG_PINNED);
+    }
+
+    /**
+     * Return whether a shortcut is static; that is, whether a shortcut is
+     * published from AndroidManifest.xml.  If {@code true}, the shortcut is
+     * also {@link #isImmutable()}.
+     *
+     * <p>When an app is upgraded and a shortcut is no longer published from AndroidManifest.xml,
+     * this will be set to {@code false}.  If the shortcut is not pinned, then it'll disappear.
+     * However, if it's pinned, it will still be visible, {@link #isEnabled()} will be
+     * {@code false} and {@link #isImmutable()} will be {@code true}.
+     */
+    public boolean isDeclaredInManifest() {
+        return hasFlags(FLAG_MANIFEST);
+    }
+
+    /** @hide kept for unit tests */
+    @Deprecated
+    public boolean isManifestShortcut() {
+        return isDeclaredInManifest();
+    }
+
+    /**
+     * @return true if pinned or cached, but neither static nor dynamic.
+     * @hide
+     */
+    public boolean isFloating() {
+        return (isPinned() || isCached()) && !(isDynamic() || isManifestShortcut());
+    }
+
+    /** @hide */
+    public boolean isOriginallyFromManifest() {
+        return hasFlags(FLAG_IMMUTABLE);
+    }
+
+    /** @hide */
+    public boolean isDynamicVisible() {
+        return isDynamic() && isVisibleToPublisher();
+    }
+
+    /** @hide */
+    public boolean isPinnedVisible() {
+        return isPinned() && isVisibleToPublisher();
+    }
+
+    /** @hide */
+    public boolean isManifestVisible() {
+        return isDeclaredInManifest() && isVisibleToPublisher();
+    }
+
+    /** @hide */
+    public boolean isNonManifestVisible() {
+        return !isDeclaredInManifest() && isVisibleToPublisher()
+                && (isPinned() || isCached() || isDynamic());
+    }
+
+    /**
+     * Return if a shortcut is immutable, in which case it cannot be modified with any of
+     * {@link ShortcutManager} APIs.
+     *
+     * <p>All static shortcuts are immutable.  When a static shortcut is pinned and is then
+     * disabled because it doesn't appear in AndroidManifest.xml for a newer version of the
+     * app, {@link #isDeclaredInManifest()} returns {@code false}, but the shortcut
+     * is still immutable.
+     *
+     * <p>All shortcuts originally published via the {@link ShortcutManager} APIs
+     * are all mutable.
+     */
+    public boolean isImmutable() {
+        return hasFlags(FLAG_IMMUTABLE);
+    }
+
+    /**
+     * Returns {@code false} if a shortcut is disabled with
+     * {@link ShortcutManager#disableShortcuts}.
+     */
+    public boolean isEnabled() {
+        return !hasFlags(FLAG_DISABLED);
+    }
+
+    /** @hide */
+    public boolean isAlive() {
+        return hasFlags(FLAG_PINNED) || hasFlags(FLAG_DYNAMIC) || hasFlags(FLAG_MANIFEST)
+                || hasFlags(FLAG_CACHED);
+    }
+
+    /** @hide */
+    public boolean usesQuota() {
+        return hasFlags(FLAG_DYNAMIC) || hasFlags(FLAG_MANIFEST);
+    }
+
+    /**
+     * Return whether a shortcut's icon is a resource in the owning package.
+     *
+     * @hide internal/unit tests only
+     */
+    public boolean hasIconResource() {
+        return hasFlags(FLAG_HAS_ICON_RES);
+    }
+
+    /**
+     * Return whether a shortcut's icon is provided via a URI.
+     *
+     * @hide internal/unit tests only
+     */
+    public boolean hasIconUri() {
+        return hasFlags(FLAG_HAS_ICON_URI);
+    }
+
+    /** @hide */
+    public boolean hasStringResources() {
+        return (mTitleResId != 0) || (mTextResId != 0) || (mDisabledMessageResId != 0);
+    }
+
+    /** @hide */
+    public boolean hasAnyResources() {
+        return hasIconResource() || hasStringResources();
+    }
+
+    /**
+     * Return whether a shortcut's icon is stored as a file.
+     *
+     * @hide internal/unit tests only
+     */
+    public boolean hasIconFile() {
+        return hasFlags(FLAG_HAS_ICON_FILE);
+    }
+
+    /**
+     * Return whether a shortcut's icon is adaptive bitmap following design guideline
+     * defined in {@link android.graphics.drawable.AdaptiveIconDrawable}.
+     *
+     * @hide internal/unit tests only
+     */
+    public boolean hasAdaptiveBitmap() {
+        return hasFlags(FLAG_ADAPTIVE_BITMAP);
+    }
+
+    /** @hide */
+    public boolean isIconPendingSave() {
+        return hasFlags(FLAG_ICON_FILE_PENDING_SAVE);
+    }
+
+    /** @hide */
+    public void setIconPendingSave() {
+        addFlags(FLAG_ICON_FILE_PENDING_SAVE);
+    }
+
+    /** @hide */
+    public void clearIconPendingSave() {
+        clearFlags(FLAG_ICON_FILE_PENDING_SAVE);
+    }
+
+    /**
+     * When the system wasn't able to restore a shortcut, it'll still be registered to the system
+     * but disabled, and such shortcuts will not be visible to the publisher. They're still visible
+     * to launchers though.
+     *
+     * @hide
+     */
+    @TestApi
+    public boolean isVisibleToPublisher() {
+        return !isDisabledForRestoreIssue(mDisabledReason);
+    }
+
+    /**
+     * Return whether a shortcut only contains "key" information only or not.  If true, only the
+     * following fields are available.
+     * <ul>
+     *     <li>{@link #getId()}
+     *     <li>{@link #getPackage()}
+     *     <li>{@link #getActivity()}
+     *     <li>{@link #getLastChangedTimestamp()}
+     *     <li>{@link #isDynamic()}
+     *     <li>{@link #isPinned()}
+     *     <li>{@link #isDeclaredInManifest()}
+     *     <li>{@link #isImmutable()}
+     *     <li>{@link #isEnabled()}
+     *     <li>{@link #getUserHandle()}
+     * </ul>
+     *
+     * <p>For performance reasons, shortcuts passed to
+     * {@link LauncherApps.Callback#onShortcutsChanged(String, List, UserHandle)} as well as those
+     * returned from {@link LauncherApps#getShortcuts(ShortcutQuery, UserHandle)}
+     * while using the {@link ShortcutQuery#FLAG_GET_KEY_FIELDS_ONLY} option contain only key
+     * information.
+     */
+    public boolean hasKeyFieldsOnly() {
+        return hasFlags(FLAG_KEY_FIELDS_ONLY);
+    }
+
+    /** @hide */
+    public boolean hasStringResourcesResolved() {
+        return hasFlags(FLAG_STRINGS_RESOLVED);
+    }
+
+    /** @hide */
+    public void updateTimestamp() {
+        mLastChangedTimestamp = System.currentTimeMillis();
+    }
+
+    /** @hide */
+    // VisibleForTesting
+    public void setTimestamp(long value) {
+        mLastChangedTimestamp = value;
+    }
+
+    /** @hide */
+    public void clearIcon() {
+        mIcon = null;
+    }
+
+    /** @hide */
+    public void setIconResourceId(int iconResourceId) {
+        if (mIconResId != iconResourceId) {
+            mIconResName = null;
+        }
+        mIconResId = iconResourceId;
+    }
+
+    /**
+     * Get the resource ID for the icon, valid only when {@link #hasIconResource()} } is true.
+     * @hide internal / tests only.
+     */
+    public int getIconResourceId() {
+        return mIconResId;
+    }
+
+    /** @hide */
+    public void setIconUri(String iconUri) {
+        mIconUri = iconUri;
+    }
+
+    /**
+     * Get the Uri for the icon, valid only when {@link #hasIconUri()} } is true.
+     * @hide internal / tests only.
+     */
+    public String getIconUri() {
+        return mIconUri;
+    }
+
+    /**
+     * Bitmap path.  Note this will be null even if {@link #hasIconFile()} is set when the save
+     * is pending.  Use {@link #isIconPendingSave()} to check it.
+     *
+     * @hide
+     */
+    public String getBitmapPath() {
+        return mBitmapPath;
+    }
+
+    /** @hide */
+    public void setBitmapPath(String bitmapPath) {
+        mBitmapPath = bitmapPath;
+    }
+
+    /** @hide */
+    public void setDisabledMessageResId(int disabledMessageResId) {
+        if (mDisabledMessageResId != disabledMessageResId) {
+            mDisabledMessageResName = null;
+        }
+        mDisabledMessageResId = disabledMessageResId;
+        mDisabledMessage = null;
+    }
+
+    /** @hide */
+    public void setDisabledMessage(String disabledMessage) {
+        mDisabledMessage = disabledMessage;
+        mDisabledMessageResId = 0;
+        mDisabledMessageResName = null;
+    }
+
+    /** @hide */
+    public String getTitleResName() {
+        return mTitleResName;
+    }
+
+    /** @hide */
+    public void setTitleResName(String titleResName) {
+        mTitleResName = titleResName;
+    }
+
+    /** @hide */
+    public String getTextResName() {
+        return mTextResName;
+    }
+
+    /** @hide */
+    public void setTextResName(String textResName) {
+        mTextResName = textResName;
+    }
+
+    /** @hide */
+    public String getDisabledMessageResName() {
+        return mDisabledMessageResName;
+    }
+
+    /** @hide */
+    public void setDisabledMessageResName(String disabledMessageResName) {
+        mDisabledMessageResName = disabledMessageResName;
+    }
+
+    /** @hide */
+    public String getIconResName() {
+        return mIconResName;
+    }
+
+    /** @hide */
+    public void setIconResName(String iconResName) {
+        mIconResName = iconResName;
+    }
+
+    /**
+     * Replaces the intent.
+     *
+     * @throws IllegalArgumentException when extra is not compatible with {@link PersistableBundle}.
+     *
+     * @hide
+     */
+    public void setIntents(Intent[] intents) throws IllegalArgumentException {
+        Objects.requireNonNull(intents);
+        Preconditions.checkArgument(intents.length > 0);
+
+        mIntents = cloneIntents(intents);
+        fixUpIntentExtras();
+    }
+
+    /** @hide */
+    public static Intent setIntentExtras(Intent intent, PersistableBundle extras) {
+        if (extras == null) {
+            intent.replaceExtras((Bundle) null);
+        } else {
+            intent.replaceExtras(new Bundle(extras));
+        }
+        return intent;
+    }
+
+    /**
+     * Replaces the categories.
+     *
+     * @hide
+     */
+    public void setCategories(Set<String> categories) {
+        mCategories = cloneCategories(categories);
+    }
+
+    private ShortcutInfo(Parcel source) {
+        final ClassLoader cl = getClass().getClassLoader();
+
+        mUserId = source.readInt();
+        mId = source.readString8();
+        mPackageName = source.readString8();
+        mActivity = source.readParcelable(cl);
+        mFlags = source.readInt();
+        mIconResId = source.readInt();
+        mLastChangedTimestamp = source.readLong();
+        mDisabledReason = source.readInt();
+
+        if (source.readInt() == 0) {
+            return; // key information only.
+        }
+
+        mIcon = source.readParcelable(cl);
+        mTitle = source.readCharSequence();
+        mTitleResId = source.readInt();
+        mText = source.readCharSequence();
+        mTextResId = source.readInt();
+        mDisabledMessage = source.readCharSequence();
+        mDisabledMessageResId = source.readInt();
+        mIntents = source.readParcelableArray(cl, Intent.class);
+        mIntentPersistableExtrases = source.readParcelableArray(cl, PersistableBundle.class);
+        mRank = source.readInt();
+        mExtras = source.readParcelable(cl);
+        mBitmapPath = source.readString8();
+
+        mIconResName = source.readString8();
+        mTitleResName = source.readString8();
+        mTextResName = source.readString8();
+        mDisabledMessageResName = source.readString8();
+
+        int N = source.readInt();
+        if (N == 0) {
+            mCategories = null;
+        } else {
+            mCategories = new ArraySet<>(N);
+            for (int i = 0; i < N; i++) {
+                mCategories.add(source.readString8().intern());
+            }
+        }
+
+        mPersons = source.readParcelableArray(cl, Person.class);
+        mLocusId = source.readParcelable(cl);
+        mIconUri = source.readString8();
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mUserId);
+        dest.writeString8(mId);
+        dest.writeString8(mPackageName);
+        dest.writeParcelable(mActivity, flags);
+        dest.writeInt(mFlags);
+        dest.writeInt(mIconResId);
+        dest.writeLong(mLastChangedTimestamp);
+        dest.writeInt(mDisabledReason);
+
+        if (hasKeyFieldsOnly()) {
+            dest.writeInt(0);
+            return;
+        }
+        dest.writeInt(1);
+
+        dest.writeParcelable(mIcon, flags);
+        dest.writeCharSequence(mTitle);
+        dest.writeInt(mTitleResId);
+        dest.writeCharSequence(mText);
+        dest.writeInt(mTextResId);
+        dest.writeCharSequence(mDisabledMessage);
+        dest.writeInt(mDisabledMessageResId);
+
+        dest.writeParcelableArray(mIntents, flags);
+        dest.writeParcelableArray(mIntentPersistableExtrases, flags);
+        dest.writeInt(mRank);
+        dest.writeParcelable(mExtras, flags);
+        dest.writeString8(mBitmapPath);
+
+        dest.writeString8(mIconResName);
+        dest.writeString8(mTitleResName);
+        dest.writeString8(mTextResName);
+        dest.writeString8(mDisabledMessageResName);
+
+        if (mCategories != null) {
+            final int N = mCategories.size();
+            dest.writeInt(N);
+            for (int i = 0; i < N; i++) {
+                dest.writeString8(mCategories.valueAt(i));
+            }
+        } else {
+            dest.writeInt(0);
+        }
+
+        dest.writeParcelableArray(mPersons, flags);
+        dest.writeParcelable(mLocusId, flags);
+        dest.writeString8(mIconUri);
+    }
+
+    public static final @android.annotation.NonNull Creator<ShortcutInfo> CREATOR =
+            new Creator<ShortcutInfo>() {
+                public ShortcutInfo createFromParcel(Parcel source) {
+                    return new ShortcutInfo(source);
+                }
+                public ShortcutInfo[] newArray(int size) {
+                    return new ShortcutInfo[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+
+    /**
+     * Return a string representation, intended for logging.  Some fields will be retracted.
+     */
+    @Override
+    public String toString() {
+        return toStringInner(/* secure =*/ true, /* includeInternalData =*/ false,
+                /*indent=*/ null);
+    }
+
+    /** @hide */
+    public String toInsecureString() {
+        return toStringInner(/* secure =*/ false, /* includeInternalData =*/ true,
+                /*indent=*/ null);
+    }
+
+    /** @hide */
+    public String toDumpString(String indent) {
+        return toStringInner(/* secure =*/ false, /* includeInternalData =*/ true, indent);
+    }
+
+    private void addIndentOrComma(StringBuilder sb, String indent) {
+        if (indent != null) {
+            sb.append("\n  ");
+            sb.append(indent);
+        } else {
+            sb.append(", ");
+        }
+    }
+
+    private String toStringInner(boolean secure, boolean includeInternalData, String indent) {
+        final StringBuilder sb = new StringBuilder();
+
+        if (indent != null) {
+            sb.append(indent);
+        }
+
+        sb.append("ShortcutInfo {");
+
+        sb.append("id=");
+        sb.append(secure ? "***" : mId);
+
+        sb.append(", flags=0x");
+        sb.append(Integer.toHexString(mFlags));
+        sb.append(" [");
+        if ((mFlags & FLAG_SHADOW) != 0) {
+            // Note the shadow flag isn't actually used anywhere and it's just for dumpsys, so
+            // we don't have an isXxx for this.
+            sb.append("Sdw");
+        }
+        if (!isEnabled()) {
+            sb.append("Dis");
+        }
+        if (isImmutable()) {
+            sb.append("Im");
+        }
+        if (isManifestShortcut()) {
+            sb.append("Man");
+        }
+        if (isDynamic()) {
+            sb.append("Dyn");
+        }
+        if (isPinned()) {
+            sb.append("Pin");
+        }
+        if (hasIconFile()) {
+            sb.append("Ic-f");
+        }
+        if (isIconPendingSave()) {
+            sb.append("Pens");
+        }
+        if (hasIconResource()) {
+            sb.append("Ic-r");
+        }
+        if (hasIconUri()) {
+            sb.append("Ic-u");
+        }
+        if (hasAdaptiveBitmap()) {
+            sb.append("Ic-a");
+        }
+        if (hasKeyFieldsOnly()) {
+            sb.append("Key");
+        }
+        if (hasStringResourcesResolved()) {
+            sb.append("Str");
+        }
+        if (isReturnedByServer()) {
+            sb.append("Rets");
+        }
+        if (isLongLived()) {
+            sb.append("Liv");
+        }
+        sb.append("]");
+
+        addIndentOrComma(sb, indent);
+
+        sb.append("packageName=");
+        sb.append(mPackageName);
+
+        addIndentOrComma(sb, indent);
+
+        sb.append("activity=");
+        sb.append(mActivity);
+
+        addIndentOrComma(sb, indent);
+
+        sb.append("shortLabel=");
+        sb.append(secure ? "***" : mTitle);
+        sb.append(", resId=");
+        sb.append(mTitleResId);
+        sb.append("[");
+        sb.append(mTitleResName);
+        sb.append("]");
+
+        addIndentOrComma(sb, indent);
+
+        sb.append("longLabel=");
+        sb.append(secure ? "***" : mText);
+        sb.append(", resId=");
+        sb.append(mTextResId);
+        sb.append("[");
+        sb.append(mTextResName);
+        sb.append("]");
+
+        addIndentOrComma(sb, indent);
+
+        sb.append("disabledMessage=");
+        sb.append(secure ? "***" : mDisabledMessage);
+        sb.append(", resId=");
+        sb.append(mDisabledMessageResId);
+        sb.append("[");
+        sb.append(mDisabledMessageResName);
+        sb.append("]");
+
+        addIndentOrComma(sb, indent);
+
+        sb.append("disabledReason=");
+        sb.append(getDisabledReasonDebugString(mDisabledReason));
+
+        addIndentOrComma(sb, indent);
+
+        sb.append("categories=");
+        sb.append(mCategories);
+
+        addIndentOrComma(sb, indent);
+
+        sb.append("persons=");
+        sb.append(mPersons);
+
+        addIndentOrComma(sb, indent);
+
+        sb.append("icon=");
+        sb.append(mIcon);
+
+        addIndentOrComma(sb, indent);
+
+        sb.append("rank=");
+        sb.append(mRank);
+
+        sb.append(", timestamp=");
+        sb.append(mLastChangedTimestamp);
+
+        addIndentOrComma(sb, indent);
+
+        sb.append("intents=");
+        if (mIntents == null) {
+            sb.append("null");
+        } else {
+            if (secure) {
+                sb.append("size:");
+                sb.append(mIntents.length);
+            } else {
+                final int size = mIntents.length;
+                sb.append("[");
+                String sep = "";
+                for (int i = 0; i < size; i++) {
+                    sb.append(sep);
+                    sep = ", ";
+                    sb.append(mIntents[i]);
+                    sb.append("/");
+                    sb.append(mIntentPersistableExtrases[i]);
+                }
+                sb.append("]");
+            }
+        }
+
+        addIndentOrComma(sb, indent);
+
+        sb.append("extras=");
+        sb.append(mExtras);
+
+        if (includeInternalData) {
+            addIndentOrComma(sb, indent);
+
+            sb.append("iconRes=");
+            sb.append(mIconResId);
+            sb.append("[");
+            sb.append(mIconResName);
+            sb.append("]");
+
+            sb.append(", bitmapPath=");
+            sb.append(mBitmapPath);
+
+            sb.append(", iconUri=");
+            sb.append(mIconUri);
+        }
+
+        if (mLocusId != null) {
+            sb.append("locusId="); sb.append(mLocusId); // LocusId.toString() is PII-safe.
+        }
+
+        sb.append("}");
+        return sb.toString();
+    }
+
+    /** @hide */
+    public ShortcutInfo(
+            @UserIdInt int userId, String id, String packageName, ComponentName activity,
+            Icon icon, CharSequence title, int titleResId, String titleResName,
+            CharSequence text, int textResId, String textResName,
+            CharSequence disabledMessage, int disabledMessageResId, String disabledMessageResName,
+            Set<String> categories, Intent[] intentsWithExtras, int rank, PersistableBundle extras,
+            long lastChangedTimestamp,
+            int flags, int iconResId, String iconResName, String bitmapPath, String iconUri,
+            int disabledReason, Person[] persons, LocusId locusId) {
+        mUserId = userId;
+        mId = id;
+        mPackageName = packageName;
+        mActivity = activity;
+        mIcon = icon;
+        mTitle = title;
+        mTitleResId = titleResId;
+        mTitleResName = titleResName;
+        mText = text;
+        mTextResId = textResId;
+        mTextResName = textResName;
+        mDisabledMessage = disabledMessage;
+        mDisabledMessageResId = disabledMessageResId;
+        mDisabledMessageResName = disabledMessageResName;
+        mCategories = cloneCategories(categories);
+        mIntents = cloneIntents(intentsWithExtras);
+        fixUpIntentExtras();
+        mRank = rank;
+        mExtras = extras;
+        mLastChangedTimestamp = lastChangedTimestamp;
+        mFlags = flags;
+        mIconResId = iconResId;
+        mIconResName = iconResName;
+        mBitmapPath = bitmapPath;
+        mIconUri = iconUri;
+        mDisabledReason = disabledReason;
+        mPersons = persons;
+        mLocusId = locusId;
+    }
+}
diff --git a/android/content/pm/ShortcutManager.java b/android/content/pm/ShortcutManager.java
new file mode 100644
index 0000000..35c99a1
--- /dev/null
+++ b/android/content/pm/ShortcutManager.java
@@ -0,0 +1,774 @@
+/*
+ * 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.content.pm;
+
+import android.Manifest;
+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.annotation.UserIdInt;
+import android.app.Notification;
+import android.app.usage.UsageStatsManager;
+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.graphics.drawable.AdaptiveIconDrawable;
+import android.os.Build;
+import android.os.Build.VERSION_CODES;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+
+/**
+ * <p><code>ShortcutManager</code> executes operations on an app's set of <i>shortcuts</i>, which
+ * represent specific tasks and actions that users can perform within your app. This page lists
+ * components of the <code>ShortcutManager</code> class that you can use to create and manage
+ * sets of shortcuts.
+ *
+ * <p>To learn about methods that retrieve information about a single shortcut&mdash;including
+ * identifiers, type, and status&mdash;read the <code>
+ * <a href="/reference/android/content/pm/ShortcutInfo.html">ShortcutInfo</a></code> reference.
+ *
+ * <p>For guidance about using shortcuts, see
+ * <a href="/guide/topics/ui/shortcuts/index.html">App shortcuts</a>.
+ *
+ * <h3>Retrieving class instances</h3>
+ * <!-- Provides a heading for the content filled in by the @SystemService annotation below -->
+ */
+@SystemService(Context.SHORTCUT_SERVICE)
+public class ShortcutManager {
+    private static final String TAG = "ShortcutManager";
+
+    /**
+     * Include manifest shortcuts in the result.
+     *
+     * @see #getShortcuts(int)
+     */
+    public static final int FLAG_MATCH_MANIFEST = 1 << 0;
+
+    /**
+     * Include dynamic shortcuts in the result.
+     *
+     * @see #getShortcuts(int)
+     */
+    public static final int FLAG_MATCH_DYNAMIC = 1 << 1;
+
+    /**
+     * Include pinned shortcuts in the result.
+     *
+     * @see #getShortcuts(int)
+     */
+    public static final int FLAG_MATCH_PINNED = 1 << 2;
+
+    /**
+     * Include cached shortcuts in the result.
+     *
+     * @see #getShortcuts(int)
+     */
+    public static final int FLAG_MATCH_CACHED = 1 << 3;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "FLAG_MATCH_" }, value = {
+            FLAG_MATCH_MANIFEST,
+            FLAG_MATCH_DYNAMIC,
+            FLAG_MATCH_PINNED,
+            FLAG_MATCH_CACHED,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ShortcutMatchFlags {}
+
+    private final Context mContext;
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    private final IShortcutService mService;
+
+    /**
+     * @hide
+     */
+    public ShortcutManager(Context context, IShortcutService service) {
+        mContext = context;
+        mService = service;
+    }
+
+    /**
+     * @hide
+     */
+    @TestApi
+    public ShortcutManager(Context context) {
+        this(context, IShortcutService.Stub.asInterface(
+                ServiceManager.getService(Context.SHORTCUT_SERVICE)));
+    }
+
+    /**
+     * Publish the list of shortcuts.  All existing dynamic shortcuts from the caller app
+     * will be replaced.  If there are already pinned shortcuts with the same IDs,
+     * the mutable pinned shortcuts are updated.
+     *
+     * <p>This API will be rate-limited.
+     *
+     * @return {@code true} if the call has succeeded. {@code false} if the call is rate-limited.
+     *
+     * @throws IllegalArgumentException if {@link #getMaxShortcutCountPerActivity()} is exceeded,
+     * or when trying to update immutable shortcuts.
+     *
+     * @throws IllegalStateException when the user is locked.
+     */
+    public boolean setDynamicShortcuts(@NonNull List<ShortcutInfo> shortcutInfoList) {
+        try {
+            return mService.setDynamicShortcuts(mContext.getPackageName(),
+                    new ParceledListSlice(shortcutInfoList), injectMyUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Return all dynamic shortcuts from the caller app.
+     *
+     * <p>This API is intended to be used for examining what shortcuts are currently published.
+     * Re-publishing returned {@link ShortcutInfo}s via APIs such as
+     * {@link #setDynamicShortcuts(List)} may cause loss of information such as icons.
+     *
+     * @throws IllegalStateException when the user is locked.
+     */
+    @NonNull
+    public List<ShortcutInfo> getDynamicShortcuts() {
+        try {
+            return mService.getShortcuts(mContext.getPackageName(), FLAG_MATCH_DYNAMIC,
+                    injectMyUserId()).getList();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Return all static (manifest) shortcuts from the caller app.
+     *
+     * <p>This API is intended to be used for examining what shortcuts are currently published.
+     * Re-publishing returned {@link ShortcutInfo}s via APIs such as
+     * {@link #setDynamicShortcuts(List)} may cause loss of information such as icons.
+     *
+     * @throws IllegalStateException when the user is locked.
+     */
+    @NonNull
+    public List<ShortcutInfo> getManifestShortcuts() {
+        try {
+            return mService.getShortcuts(mContext.getPackageName(), FLAG_MATCH_MANIFEST,
+                    injectMyUserId()).getList();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns {@link ShortcutInfo}s that match {@code matchFlags}.
+     *
+     * @param matchFlags result includes shortcuts matching this flags. Any combination of:
+     * <ul>
+     *     <li>{@link #FLAG_MATCH_MANIFEST}
+     *     <li>{@link #FLAG_MATCH_DYNAMIC}
+     *     <li>{@link #FLAG_MATCH_PINNED}
+     *     <li>{@link #FLAG_MATCH_CACHED}
+     * </ul>
+
+     * @return list of {@link ShortcutInfo}s that match the flag.
+     *
+     * <p>At least one of the {@code MATCH} flags should be set. Otherwise no shortcuts will be
+     * returned.
+     *
+     * @throws IllegalStateException when the user is locked.
+     */
+    @NonNull
+    public List<ShortcutInfo> getShortcuts(@ShortcutMatchFlags int matchFlags) {
+        try {
+            return mService.getShortcuts(mContext.getPackageName(), matchFlags, injectMyUserId())
+                    .getList();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Publish the list of dynamic shortcuts.  If there are already dynamic or pinned shortcuts with
+     * the same IDs, each mutable shortcut is updated.
+     *
+     * <p>This API will be rate-limited.
+     *
+     * @return {@code true} if the call has succeeded. {@code false} if the call is rate-limited.
+     *
+     * @throws IllegalArgumentException if {@link #getMaxShortcutCountPerActivity()} is exceeded,
+     * or when trying to update immutable shortcuts.
+     *
+     * @throws IllegalStateException when the user is locked.
+     */
+    public boolean addDynamicShortcuts(@NonNull List<ShortcutInfo> shortcutInfoList) {
+        try {
+            return mService.addDynamicShortcuts(mContext.getPackageName(),
+                    new ParceledListSlice(shortcutInfoList), injectMyUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Delete dynamic shortcuts by ID.
+     *
+     * @throws IllegalStateException when the user is locked.
+     */
+    public void removeDynamicShortcuts(@NonNull List<String> shortcutIds) {
+        try {
+            mService.removeDynamicShortcuts(mContext.getPackageName(), shortcutIds,
+                    injectMyUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Delete all dynamic shortcuts from the caller app.
+     *
+     * @throws IllegalStateException when the user is locked.
+     */
+    public void removeAllDynamicShortcuts() {
+        try {
+            mService.removeAllDynamicShortcuts(mContext.getPackageName(), injectMyUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Delete long lived shortcuts by ID.
+     *
+     * @throws IllegalStateException when the user is locked.
+     */
+    public void removeLongLivedShortcuts(@NonNull List<String> shortcutIds) {
+        try {
+            mService.removeLongLivedShortcuts(mContext.getPackageName(), shortcutIds,
+                    injectMyUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Return all pinned shortcuts from the caller app.
+     *
+     * <p>This API is intended to be used for examining what shortcuts are currently published.
+     * Re-publishing returned {@link ShortcutInfo}s via APIs such as
+     * {@link #setDynamicShortcuts(List)} may cause loss of information such as icons.
+     *
+     * @throws IllegalStateException when the user is locked.
+     */
+    @NonNull
+    public List<ShortcutInfo> getPinnedShortcuts() {
+        try {
+            return mService.getShortcuts(mContext.getPackageName(), FLAG_MATCH_PINNED,
+                    injectMyUserId()).getList();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Update all existing shortcuts with the same IDs.  Target shortcuts may be pinned and/or
+     * dynamic, but they must not be immutable.
+     *
+     * <p>This API will be rate-limited.
+     *
+     * @return {@code true} if the call has succeeded. {@code false} if the call is rate-limited.
+     *
+     * @throws IllegalArgumentException If trying to update immutable shortcuts.
+     *
+     * @throws IllegalStateException when the user is locked.
+     */
+    public boolean updateShortcuts(@NonNull List<ShortcutInfo> shortcutInfoList) {
+        try {
+            return mService.updateShortcuts(mContext.getPackageName(),
+                    new ParceledListSlice(shortcutInfoList), injectMyUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Disable pinned shortcuts.  For more details, read
+     * <a href="/guide/topics/ui/shortcuts/managing-shortcuts.html#disable-shortcuts">
+     * Disable shortcuts</a>.
+     *
+     * @throws IllegalArgumentException If trying to disable immutable shortcuts.
+     *
+     * @throws IllegalStateException when the user is locked.
+     */
+    public void disableShortcuts(@NonNull List<String> shortcutIds) {
+        try {
+            mService.disableShortcuts(mContext.getPackageName(), shortcutIds,
+                    /* disabledMessage =*/ null, /* disabledMessageResId =*/ 0,
+                    injectMyUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @hide old signature, kept for unit testing.
+     */
+    public void disableShortcuts(@NonNull List<String> shortcutIds, int disabledMessageResId) {
+        try {
+            mService.disableShortcuts(mContext.getPackageName(), shortcutIds,
+                    /* disabledMessage =*/ null, disabledMessageResId,
+                    injectMyUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @hide old signature, kept for unit testing.
+     */
+    public void disableShortcuts(@NonNull List<String> shortcutIds, String disabledMessage) {
+        disableShortcuts(shortcutIds, (CharSequence) disabledMessage);
+    }
+
+    /**
+     * Disable pinned shortcuts, showing the user a custom error message when they try to select
+     * the disabled shortcuts.
+     * For more details, read
+     * <a href="/guide/topics/ui/shortcuts/managing-shortcuts.html#disable-shortcuts">
+     * Disable shortcuts</a>.
+     *
+     * @throws IllegalArgumentException If trying to disable immutable shortcuts.
+     *
+     * @throws IllegalStateException when the user is locked.
+     */
+    public void disableShortcuts(@NonNull List<String> shortcutIds, CharSequence disabledMessage) {
+        try {
+            mService.disableShortcuts(mContext.getPackageName(), shortcutIds,
+                    disabledMessage, /* disabledMessageResId =*/ 0,
+                    injectMyUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Re-enable pinned shortcuts that were previously disabled.  If the target shortcuts
+     * are already enabled, this method does nothing.
+     *
+     * @throws IllegalArgumentException If trying to enable immutable shortcuts.
+     *
+     * @throws IllegalStateException when the user is locked.
+     */
+    public void enableShortcuts(@NonNull List<String> shortcutIds) {
+        try {
+            mService.enableShortcuts(mContext.getPackageName(), shortcutIds, injectMyUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+
+    /**
+     * @hide old signature, kept for unit testing.
+     */
+    public int getMaxShortcutCountForActivity() {
+        return getMaxShortcutCountPerActivity();
+    }
+
+    /**
+     * Return the maximum number of static and dynamic shortcuts that each launcher icon
+     * can have at a time.
+     */
+    public int getMaxShortcutCountPerActivity() {
+        try {
+            return mService.getMaxShortcutCountPerActivity(
+                    mContext.getPackageName(), injectMyUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Return the number of times the caller app can call the rate-limited APIs
+     * before the rate limit counter is reset.
+     *
+     * @see #getRateLimitResetTime()
+     *
+     * @hide
+     */
+    public int getRemainingCallCount() {
+        try {
+            return mService.getRemainingCallCount(mContext.getPackageName(), injectMyUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Return when the rate limit count will be reset next time, in milliseconds since the epoch.
+     *
+     * @see #getRemainingCallCount()
+     * @see System#currentTimeMillis()
+     *
+     * @hide
+     */
+    public long getRateLimitResetTime() {
+        try {
+            return mService.getRateLimitResetTime(mContext.getPackageName(), injectMyUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Return {@code true} when rate-limiting is active for the caller app.
+     *
+     * <p>For details, see <a href="/guide/topics/ui/shortcuts/managing-shortcuts#rate-limiting">
+     * Rate limiting</a>.
+     *
+     * @throws IllegalStateException when the user is locked.
+     */
+    public boolean isRateLimitingActive() {
+        try {
+            return mService.getRemainingCallCount(mContext.getPackageName(), injectMyUserId())
+                    == 0;
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Return the max width for icons, in pixels.
+     *
+     * <p> Note that this method returns max width of icon's visible part. Hence, it does not take
+     * into account the inset introduced by {@link AdaptiveIconDrawable}. To calculate bitmap image
+     * to function as {@link AdaptiveIconDrawable}, multiply
+     * 1 + 2 * {@link AdaptiveIconDrawable#getExtraInsetFraction()} to the returned size.
+     */
+    public int getIconMaxWidth() {
+        try {
+            // TODO Implement it properly using xdpi.
+            return mService.getIconMaxDimensions(mContext.getPackageName(), injectMyUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Return the max height for icons, in pixels.
+     */
+    public int getIconMaxHeight() {
+        try {
+            // TODO Implement it properly using ydpi.
+            return mService.getIconMaxDimensions(mContext.getPackageName(), injectMyUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Apps that publish shortcuts should call this method whenever the user
+     * selects the shortcut containing the given ID or when the user completes
+     * an action in the app that is equivalent to selecting the shortcut.
+     * For more details, read about
+     * <a href="/guide/topics/ui/shortcuts/managing-shortcuts.html#track-usage">
+     * tracking shortcut usage</a>.
+     *
+     * <p>The information is accessible via {@link UsageStatsManager#queryEvents}
+     * Typically, launcher apps use this information to build a prediction model
+     * so that they can promote the shortcuts that are likely to be used at the moment.
+     *
+     * @throws IllegalStateException when the user is locked.
+     */
+    public void reportShortcutUsed(String shortcutId) {
+        try {
+            mService.reportShortcutUsed(mContext.getPackageName(), shortcutId,
+                    injectMyUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Return {@code TRUE} if the app is running on a device whose default launcher supports
+     * {@link #requestPinShortcut(ShortcutInfo, IntentSender)}.
+     *
+     * <p>The return value may change in subsequent calls if the user changes the default launcher
+     * app.
+     *
+     * <p><b>Note:</b> See also the support library counterpart
+     * {@link android.support.v4.content.pm.ShortcutManagerCompat#isRequestPinShortcutSupported(
+     * Context)}, which supports Android versions lower than {@link VERSION_CODES#O} using the
+     * legacy private intent {@code com.android.launcher.action.INSTALL_SHORTCUT}.
+     *
+     * @see #requestPinShortcut(ShortcutInfo, IntentSender)
+     */
+    public boolean isRequestPinShortcutSupported() {
+        try {
+            return mService.isRequestPinItemSupported(injectMyUserId(),
+                    LauncherApps.PinItemRequest.REQUEST_TYPE_SHORTCUT);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Request to create a pinned shortcut.  The default launcher will receive this request and
+     * ask the user for approval.  If the user approves it, the shortcut will be created, and
+     * {@code resultIntent} will be sent. If a request is denied by the user, however, no response
+     * will be sent to the caller.
+     *
+     * <p>Only apps with a foreground activity or a foreground service can call this method.
+     * Otherwise, it'll throw {@link IllegalStateException}.
+     *
+     * <p>It's up to the launcher to decide how to handle previous pending requests when the same
+     * package calls this API multiple times in a row. One possible strategy is to ignore any
+     * previous requests.
+     *
+     * <p><b>Note:</b> See also the support library counterpart
+     * {@link android.support.v4.content.pm.ShortcutManagerCompat#requestPinShortcut(
+     * Context, ShortcutInfoCompat, IntentSender)},
+     * which supports Android versions lower than {@link VERSION_CODES#O} using the
+     * legacy private intent {@code com.android.launcher.action.INSTALL_SHORTCUT}.
+     *
+     * @param shortcut Shortcut to pin.  If an app wants to pin an existing (either static
+     *     or dynamic) shortcut, then it only needs to have an ID. Although other fields don't have
+     *     to be set, the target shortcut must be enabled.
+     *
+     *     <p>If it's a new shortcut, all the mandatory fields, such as a short label, must be
+     *     set.
+     * @param resultIntent If not null, this intent will be sent when the shortcut is pinned.
+     *    Use {@link android.app.PendingIntent#getIntentSender()} to create an {@link IntentSender}.
+     *    To avoid background execution limits, use an unexported, manifest-declared receiver.
+     *    For more details, see
+     *    <a href="/guide/topics/ui/shortcuts/creating-shortcuts.html#pinned">
+     *    Creating pinned shortcuts</a>.
+     *
+     * @return {@code TRUE} if the launcher supports this feature.  Note the API will return without
+     *    waiting for the user to respond, so getting {@code TRUE} from this API does *not* mean
+     *    the shortcut was pinned successfully.  {@code FALSE} if the launcher doesn't support this
+     *    feature.
+     *
+     * @see #isRequestPinShortcutSupported()
+     * @see IntentSender
+     * @see android.app.PendingIntent#getIntentSender()
+     *
+     * @throws IllegalArgumentException if a shortcut with the same ID exists and is disabled.
+     * @throws IllegalStateException The caller doesn't have a foreground activity or a foreground
+     * service, or the device is locked.
+     */
+    public boolean requestPinShortcut(@NonNull ShortcutInfo shortcut,
+            @Nullable IntentSender resultIntent) {
+        try {
+            return mService.requestPinShortcut(mContext.getPackageName(), shortcut,
+                    resultIntent, injectMyUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns an Intent which can be used by the default launcher to pin a shortcut containing the
+     * given {@link ShortcutInfo}. This method should be used by an Activity to set a result in
+     * response to {@link Intent#ACTION_CREATE_SHORTCUT}.
+     *
+     * @param shortcut New shortcut to pin.  If an app wants to pin an existing (either dynamic
+     *     or manifest) shortcut, then it only needs to have an ID, and other fields don't have to
+     *     be set, in which case, the target shortcut must be enabled.
+     *     If it's a new shortcut, all the mandatory fields, such as a short label, must be
+     *     set.
+     * @return The intent that should be set as the result for the calling activity, or
+     *     <code>null</code> if the current launcher doesn't support shortcuts.
+     *
+     * @see Intent#ACTION_CREATE_SHORTCUT
+     *
+     * @throws IllegalArgumentException if a shortcut with the same ID exists and is disabled.
+     */
+    public Intent createShortcutResultIntent(@NonNull ShortcutInfo shortcut) {
+        try {
+            return mService.createShortcutResultIntent(mContext.getPackageName(), shortcut,
+                    injectMyUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Called internally when an app is considered to have come to the foreground
+     * even when technically it's not.  This method resets the throttling for this package.
+     * For example, when the user sends an "inline reply" on a notification, the system UI will
+     * call it.
+     *
+     * @hide
+     */
+    public void onApplicationActive(@NonNull String packageName, @UserIdInt int userId) {
+        try {
+            mService.onApplicationActive(packageName, userId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /** @hide injection point */
+    @VisibleForTesting
+    protected int injectMyUserId() {
+        return mContext.getUserId();
+    }
+
+    /**
+     * Used by framework's ShareSheet (ChooserActivity.java) to retrieve all of the direct share
+     * targets that match the given IntentFilter.
+     *
+     * @param filter IntentFilter that will be used to retrieve the matching {@link ShortcutInfo}s.
+     * @return List of {@link ShareShortcutInfo}s that match the given IntentFilter.
+     * @hide
+     */
+    @NonNull
+    @SystemApi
+    @RequiresPermission(Manifest.permission.MANAGE_APP_PREDICTIONS)
+    public List<ShareShortcutInfo> getShareTargets(@NonNull IntentFilter filter) {
+        try {
+            return mService.getShareTargets(mContext.getPackageName(), filter,
+                    injectMyUserId()).getList();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Represents the result of a query return by {@link #getShareTargets(IntentFilter)}.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final class ShareShortcutInfo implements Parcelable {
+        private final ShortcutInfo mShortcutInfo;
+        private final ComponentName mTargetComponent;
+
+        /**
+         * @hide
+         */
+        public ShareShortcutInfo(@NonNull ShortcutInfo shortcutInfo,
+                @NonNull ComponentName targetComponent) {
+            if (shortcutInfo == null) {
+                throw new NullPointerException("shortcut info is null");
+            }
+            if (targetComponent == null) {
+                throw new NullPointerException("target component is null");
+            }
+
+            mShortcutInfo = shortcutInfo;
+            mTargetComponent = targetComponent;
+        }
+
+        private ShareShortcutInfo(@NonNull Parcel in) {
+            mShortcutInfo = in.readParcelable(ShortcutInfo.class.getClassLoader());
+            mTargetComponent = in.readParcelable(ComponentName.class.getClassLoader());
+        }
+
+        @NonNull
+        public ShortcutInfo getShortcutInfo() {
+            return mShortcutInfo;
+        }
+
+        @NonNull
+        public ComponentName getTargetComponent() {
+            return mTargetComponent;
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
+            dest.writeParcelable(mShortcutInfo, flags);
+            dest.writeParcelable(mTargetComponent, flags);
+        }
+
+        public static final @NonNull Parcelable.Creator<ShareShortcutInfo> CREATOR =
+                new Parcelable.Creator<ShareShortcutInfo>() {
+                    public ShareShortcutInfo createFromParcel(Parcel in) {
+                        return new ShareShortcutInfo(in);
+                    }
+
+                    public ShareShortcutInfo[] newArray(int size) {
+                        return new ShareShortcutInfo[size];
+                    }
+                };
+    }
+
+    /**
+     * Used by framework's ShareSheet (ChooserActivity.java) to check if a given package has share
+     * target definitions in it's resources.
+     *
+     * @param packageName Package to check for share targets.
+     * @return True if the package has any share target definitions, False otherwise.
+     * @hide
+     */
+    @SystemApi
+    public boolean hasShareTargets(@NonNull String packageName) {
+        try {
+            return mService.hasShareTargets(mContext.getPackageName(), packageName,
+                    injectMyUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Publish a single dynamic shortcut. If there are already dynamic or pinned shortcuts with the
+     * same ID, each mutable shortcut is updated.
+     *
+     * <p>This method is useful when posting notifications which are tagged with shortcut IDs; In
+     * order to make sure shortcuts exist and are up-to-date, without the need to explicitly handle
+     * the shortcut count limit.
+     * @see android.app.NotificationManager#notify(int, Notification)
+     * @see Notification.Builder#setShortcutId(String)
+     *
+     * <p>If {@link #getMaxShortcutCountPerActivity()} is already reached, an existing shortcut with
+     * the lowest rank will be removed to add space for the new shortcut.
+     *
+     * <p>If the rank of the shortcut is not explicitly set, it will be set to zero, and shortcut
+     * will be added to the top of the list.
+     *
+     * @throws IllegalArgumentException if trying to update an immutable shortcut.
+     *
+     * @throws IllegalStateException when the user is locked.
+     */
+    public void pushDynamicShortcut(@NonNull ShortcutInfo shortcut) {
+        try {
+            mService.pushDynamicShortcut(mContext.getPackageName(), shortcut, injectMyUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+}
diff --git a/android/content/pm/ShortcutQueryWrapper.java b/android/content/pm/ShortcutQueryWrapper.java
new file mode 100644
index 0000000..c613441
--- /dev/null
+++ b/android/content/pm/ShortcutQueryWrapper.java
@@ -0,0 +1,190 @@
+/*
+ * 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.content.pm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.LocusId;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.DataClass;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @hide
+ */
+@DataClass(genParcelable = true, genToString = true)
+public final class ShortcutQueryWrapper extends LauncherApps.ShortcutQuery implements Parcelable {
+
+    public ShortcutQueryWrapper(LauncherApps.ShortcutQuery query) {
+        this();
+        mChangedSince = query.mChangedSince;
+        mPackage = query.mPackage;
+        mLocusIds = query.mLocusIds;
+        mShortcutIds = query.mShortcutIds;
+        mActivity = query.mActivity;
+        mQueryFlags = query.mQueryFlags;
+    }
+
+    public long getChangedSince() {
+        return mChangedSince;
+    }
+
+    @Nullable
+    public String getPackage() {
+        return mPackage;
+    }
+
+    @Nullable
+    public List<LocusId> getLocusIds() {
+        return mLocusIds;
+    }
+
+    @Nullable
+    public List<String> getShortcutIds() {
+        return mShortcutIds;
+    }
+
+    @Nullable
+    public ComponentName getActivity() {
+        return mActivity;
+    }
+
+    public int getQueryFlags() {
+        return mQueryFlags;
+    }
+
+    // Code below generated by codegen v1.0.14.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/ShortcutQueryWrapper.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @DataClass.Generated.Member
+    public ShortcutQueryWrapper() {
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public String toString() {
+        // You can override field toString logic by defining methods like:
+        // String fieldNameToString() { ... }
+
+        return "ShortcutQueryWrapper { " +
+        " }";
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        byte flg = 0;
+        if (mPackage != null) flg |= 0x2;
+        if (mShortcutIds != null) flg |= 0x4;
+        if (mLocusIds != null) flg |= 0x8;
+        if (mActivity != null) flg |= 0x10;
+        dest.writeByte(flg);
+        dest.writeLong(mChangedSince);
+        if (mPackage != null) dest.writeString(mPackage);
+        if (mShortcutIds != null) dest.writeStringList(mShortcutIds);
+        if (mLocusIds != null) dest.writeParcelableList(mLocusIds, flags);
+        if (mActivity != null) dest.writeTypedObject(mActivity, flags);
+        dest.writeInt(mQueryFlags);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    /* package-private */ ShortcutQueryWrapper(@NonNull Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        byte flg = in.readByte();
+        long changedSince = in.readLong();
+        String pkg = (flg & 0x2) == 0 ? null : in.readString();
+        List<String> shortcutIds = null;
+        if ((flg & 0x4) != 0) {
+            shortcutIds = new ArrayList<>();
+            in.readStringList(shortcutIds);
+        }
+        List<LocusId> locusIds = null;
+        if ((flg & 0x8) != 0) {
+            locusIds = new ArrayList<>();
+            in.readParcelableList(locusIds, LocusId.class.getClassLoader());
+        }
+        ComponentName activity = (flg & 0x10) == 0 ? null
+                : (ComponentName) in.readTypedObject(ComponentName.CREATOR);
+        int queryFlags = in.readInt();
+
+        this.mChangedSince = changedSince;
+        this.mPackage = pkg;
+        this.mShortcutIds = shortcutIds;
+        this.mLocusIds = locusIds;
+        this.mActivity = activity;
+        this.mQueryFlags = queryFlags;
+        com.android.internal.util.AnnotationValidations.validate(
+                QueryFlags.class, null, mQueryFlags);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<ShortcutQueryWrapper> CREATOR
+            = new Parcelable.Creator<ShortcutQueryWrapper>() {
+        @Override
+        public ShortcutQueryWrapper[] newArray(int size) {
+            return new ShortcutQueryWrapper[size];
+        }
+
+        @Override
+        public ShortcutQueryWrapper createFromParcel(@NonNull Parcel in) {
+            return new ShortcutQueryWrapper(in);
+        }
+    };
+
+    @DataClass.Generated(
+            time = 1582049937960L,
+            codegenVersion = "1.0.14",
+            sourceFile = "frameworks/base/core/java/android/content/pm/ShortcutQueryWrapper.java",
+            inputSignatures = "public  long getChangedSince()\npublic @android.annotation.Nullable java.lang.String getPackage()\npublic @android.annotation.Nullable java.util.List<android.content.LocusId> getLocusIds()\npublic @android.annotation.Nullable java.util.List<java.lang.String> getShortcutIds()\npublic @android.annotation.Nullable android.content.ComponentName getActivity()\npublic  int getQueryFlags()\nclass ShortcutQueryWrapper extends android.content.pm.LauncherApps.ShortcutQuery implements [android.os.Parcelable]\[email protected](genParcelable=true, genToString=true)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/android/content/pm/ShortcutServiceInternal.java b/android/content/pm/ShortcutServiceInternal.java
new file mode 100644
index 0000000..eee91ce
--- /dev/null
+++ b/android/content/pm/ShortcutServiceInternal.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.content.pm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.IntentSender;
+import android.content.LocusId;
+import android.content.pm.LauncherApps.ShortcutQuery;
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+
+import java.util.List;
+
+/**
+ * Entry points used by {@link LauncherApps}.
+ *
+ * <p>No permission / argument checks will be performed inside.
+ * Callers must check the calling app permission and the calling package name.
+ * @hide
+ */
+public abstract class ShortcutServiceInternal {
+    public interface ShortcutChangeListener {
+        void onShortcutChanged(@NonNull String packageName, @UserIdInt int userId);
+    }
+
+    public abstract List<ShortcutInfo>
+            getShortcuts(int launcherUserId,
+            @NonNull String callingPackage, long changedSince,
+            @Nullable String packageName, @Nullable List<String> shortcutIds,
+            @Nullable List<LocusId> locusIds, @Nullable ComponentName componentName,
+            @ShortcutQuery.QueryFlags int flags, int userId, int callingPid, int callingUid);
+
+    public abstract boolean
+            isPinnedByCaller(int launcherUserId, @NonNull String callingPackage,
+            @NonNull String packageName, @NonNull String id, int userId);
+
+    public abstract void pinShortcuts(int launcherUserId,
+            @NonNull String callingPackage, @NonNull String packageName,
+            @NonNull List<String> shortcutIds, int userId);
+
+    public abstract Intent[] createShortcutIntents(
+            int launcherUserId, @NonNull String callingPackage,
+            @NonNull String packageName, @NonNull String shortcutId, int userId,
+            int callingPid, int callingUid);
+
+    public abstract void addListener(@NonNull ShortcutChangeListener listener);
+
+    public abstract void addShortcutChangeCallback(
+            @NonNull LauncherApps.ShortcutChangeCallback callback);
+
+    public abstract int getShortcutIconResId(int launcherUserId, @NonNull String callingPackage,
+            @NonNull String packageName, @NonNull String shortcutId, int userId);
+
+    public abstract ParcelFileDescriptor getShortcutIconFd(int launcherUserId,
+            @NonNull String callingPackage,
+            @NonNull String packageName, @NonNull String shortcutId, int userId);
+
+    public abstract boolean hasShortcutHostPermission(int launcherUserId,
+            @NonNull String callingPackage, int callingPid, int callingUid);
+
+    public abstract void setShortcutHostPackage(@NonNull String type, @Nullable String packageName,
+            int userId);
+
+    public abstract boolean requestPinAppWidget(@NonNull String callingPackage,
+            @NonNull AppWidgetProviderInfo appWidget, @Nullable Bundle extras,
+            @Nullable IntentSender resultIntent, int userId);
+
+    public abstract boolean isRequestPinItemSupported(int callingUserId, int requestType);
+
+    public abstract boolean isForegroundDefaultLauncher(@NonNull String callingPackage,
+            int callingUid);
+
+    public abstract void cacheShortcuts(int launcherUserId,
+            @NonNull String callingPackage, @NonNull String packageName,
+            @NonNull List<String> shortcutIds, int userId);
+    public abstract void uncacheShortcuts(int launcherUserId,
+            @NonNull String callingPackage, @NonNull String packageName,
+            @NonNull List<String> shortcutIds, int userId);
+
+    /**
+     * Retrieves all of the direct share targets that match the given IntentFilter for the specified
+     * user.
+     */
+    public abstract List<ShortcutManager.ShareShortcutInfo> getShareTargets(
+            @NonNull String callingPackage, @NonNull IntentFilter intentFilter, int userId);
+
+    /**
+     * Returns the icon Uri of the shortcut, and grants Uri read permission to the caller.
+     */
+    public abstract String getShortcutIconUri(int launcherUserId, @NonNull String launcherPackage,
+            @NonNull String packageName, @NonNull String shortcutId, int userId);
+
+    public abstract boolean isSharingShortcut(int callingUserId, @NonNull String callingPackage,
+            @NonNull String packageName, @NonNull String shortcutId, int userId,
+            @NonNull IntentFilter filter);
+}
diff --git a/android/content/pm/Signature.java b/android/content/pm/Signature.java
new file mode 100644
index 0000000..3b84ae7
--- /dev/null
+++ b/android/content/pm/Signature.java
@@ -0,0 +1,361 @@
+/*
+ * 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.content.pm;
+
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.ArrayUtils;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.lang.ref.SoftReference;
+import java.security.PublicKey;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+
+/**
+ * Opaque, immutable representation of a signing certificate associated with an
+ * application package.
+ * <p>
+ * This class name is slightly misleading, since it's not actually a signature.
+ */
+public class Signature implements Parcelable {
+    private final byte[] mSignature;
+    private int mHashCode;
+    private boolean mHaveHashCode;
+    private SoftReference<String> mStringRef;
+    private Certificate[] mCertificateChain;
+    /**
+     * APK Signature Scheme v3 includes support for adding a proof-of-rotation record that
+     * contains two pieces of information:
+     *   1) the past signing certificates
+     *   2) the flags that APK wants to assign to each of the past signing certificates.
+     *
+     * These flags represent the second piece of information and are viewed as capabilities.
+     * They are an APK's way of telling the platform: "this is how I want to trust my old certs,
+     * please enforce that." This is useful for situation where this app itself is using its
+     * signing certificate as an authorization mechanism, like whether or not to allow another
+     * app to have its SIGNATURE permission.  An app could specify whether to allow other apps
+     * signed by its old cert 'X' to still get a signature permission it defines, for example.
+     */
+    private int mFlags;
+
+    /**
+     * Create Signature from an existing raw byte array.
+     */
+    public Signature(byte[] signature) {
+        mSignature = signature.clone();
+        mCertificateChain = null;
+    }
+
+    /**
+     * Create signature from a certificate chain. Used for backward
+     * compatibility.
+     *
+     * @throws CertificateEncodingException
+     * @hide
+     */
+    public Signature(Certificate[] certificateChain) throws CertificateEncodingException {
+        mSignature = certificateChain[0].getEncoded();
+        if (certificateChain.length > 1) {
+            mCertificateChain = Arrays.copyOfRange(certificateChain, 1, certificateChain.length);
+        }
+    }
+
+    private static final int parseHexDigit(int nibble) {
+        if ('0' <= nibble && nibble <= '9') {
+            return nibble - '0';
+        } else if ('a' <= nibble && nibble <= 'f') {
+            return nibble - 'a' + 10;
+        } else if ('A' <= nibble && nibble <= 'F') {
+            return nibble - 'A' + 10;
+        } else {
+            throw new IllegalArgumentException("Invalid character " + nibble + " in hex string");
+        }
+    }
+
+    /**
+     * Create Signature from a text representation previously returned by
+     * {@link #toChars} or {@link #toCharsString()}. Signatures are expected to
+     * be a hex-encoded ASCII string.
+     *
+     * @param text hex-encoded string representing the signature
+     * @throws IllegalArgumentException when signature is odd-length
+     */
+    public Signature(String text) {
+        final byte[] input = text.getBytes();
+        final int N = input.length;
+
+        if (N % 2 != 0) {
+            throw new IllegalArgumentException("text size " + N + " is not even");
+        }
+
+        final byte[] sig = new byte[N / 2];
+        int sigIndex = 0;
+
+        for (int i = 0; i < N;) {
+            final int hi = parseHexDigit(input[i++]);
+            final int lo = parseHexDigit(input[i++]);
+            sig[sigIndex++] = (byte) ((hi << 4) | lo);
+        }
+
+        mSignature = sig;
+    }
+
+    /**
+     * Sets the flags representing the capabilities of the past signing certificate.
+     * @hide
+     */
+    public void setFlags(int flags) {
+        this.mFlags = flags;
+    }
+
+    /**
+     * Returns the flags representing the capabilities of the past signing certificate.
+     * @hide
+     */
+    public int getFlags() {
+        return mFlags;
+    }
+
+    /**
+     * Encode the Signature as ASCII text.
+     */
+    public char[] toChars() {
+        return toChars(null, null);
+    }
+
+    /**
+     * Encode the Signature as ASCII text in to an existing array.
+     *
+     * @param existingArray Existing char array or null.
+     * @param outLen Output parameter for the number of characters written in
+     * to the array.
+     * @return Returns either <var>existingArray</var> if it was large enough
+     * to hold the ASCII representation, or a newly created char[] array if
+     * needed.
+     */
+    public char[] toChars(char[] existingArray, int[] outLen) {
+        byte[] sig = mSignature;
+        final int N = sig.length;
+        final int N2 = N*2;
+        char[] text = existingArray == null || N2 > existingArray.length
+                ? new char[N2] : existingArray;
+        for (int j=0; j<N; j++) {
+            byte v = sig[j];
+            int d = (v>>4)&0xf;
+            text[j*2] = (char)(d >= 10 ? ('a' + d - 10) : ('0' + d));
+            d = v&0xf;
+            text[j*2+1] = (char)(d >= 10 ? ('a' + d - 10) : ('0' + d));
+        }
+        if (outLen != null) outLen[0] = N;
+        return text;
+    }
+
+    /**
+     * Return the result of {@link #toChars()} as a String.
+     */
+    public String toCharsString() {
+        String str = mStringRef == null ? null : mStringRef.get();
+        if (str != null) {
+            return str;
+        }
+        str = new String(toChars());
+        mStringRef = new SoftReference<String>(str);
+        return str;
+    }
+
+    /**
+     * @return the contents of this signature as a byte array.
+     */
+    public byte[] toByteArray() {
+        byte[] bytes = new byte[mSignature.length];
+        System.arraycopy(mSignature, 0, bytes, 0, mSignature.length);
+        return bytes;
+    }
+
+    /**
+     * Returns the public key for this signature.
+     *
+     * @throws CertificateException when Signature isn't a valid X.509
+     *             certificate; shouldn't happen.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public PublicKey getPublicKey() throws CertificateException {
+        final CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
+        final ByteArrayInputStream bais = new ByteArrayInputStream(mSignature);
+        final Certificate cert = certFactory.generateCertificate(bais);
+        return cert.getPublicKey();
+    }
+
+    /**
+     * Used for compatibility code that needs to check the certificate chain
+     * during upgrades.
+     *
+     * @throws CertificateEncodingException
+     * @hide
+     */
+    public Signature[] getChainSignatures() throws CertificateEncodingException {
+        if (mCertificateChain == null) {
+            return new Signature[] { this };
+        }
+
+        Signature[] chain = new Signature[1 + mCertificateChain.length];
+        chain[0] = this;
+
+        int i = 1;
+        for (Certificate c : mCertificateChain) {
+            chain[i++] = new Signature(c.getEncoded());
+        }
+
+        return chain;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        try {
+            if (obj != null) {
+                Signature other = (Signature)obj;
+                return this == other || Arrays.equals(mSignature, other.mSignature);
+            }
+        } catch (ClassCastException e) {
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        if (mHaveHashCode) {
+            return mHashCode;
+        }
+        mHashCode = Arrays.hashCode(mSignature);
+        mHaveHashCode = true;
+        return mHashCode;
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel dest, int parcelableFlags) {
+        dest.writeByteArray(mSignature);
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<Signature> CREATOR
+            = new Parcelable.Creator<Signature>() {
+        public Signature createFromParcel(Parcel source) {
+            return new Signature(source);
+        }
+
+        public Signature[] newArray(int size) {
+            return new Signature[size];
+        }
+    };
+
+    private Signature(Parcel source) {
+        mSignature = source.createByteArray();
+    }
+
+    /**
+     * Test if given {@link Signature} sets are exactly equal.
+     *
+     * @hide
+     */
+    public static boolean areExactMatch(Signature[] a, Signature[] b) {
+        return (a.length == b.length) && ArrayUtils.containsAll(a, b)
+                && ArrayUtils.containsAll(b, a);
+    }
+
+    /**
+     * Test if given {@link Signature} sets are effectively equal. In rare
+     * cases, certificates can have slightly malformed encoding which causes
+     * exact-byte checks to fail.
+     * <p>
+     * To identify effective equality, we bounce the certificates through an
+     * decode/encode pass before doing the exact-byte check. To reduce attack
+     * surface area, we only allow a byte size delta of a few bytes.
+     *
+     * @throws CertificateException if the before/after length differs
+     *             substantially, usually a signal of something fishy going on.
+     * @hide
+     */
+    public static boolean areEffectiveMatch(Signature[] a, Signature[] b)
+            throws CertificateException {
+        final CertificateFactory cf = CertificateFactory.getInstance("X.509");
+
+        final Signature[] aPrime = new Signature[a.length];
+        for (int i = 0; i < a.length; i++) {
+            aPrime[i] = bounce(cf, a[i]);
+        }
+        final Signature[] bPrime = new Signature[b.length];
+        for (int i = 0; i < b.length; i++) {
+            bPrime[i] = bounce(cf, b[i]);
+        }
+
+        return areExactMatch(aPrime, bPrime);
+    }
+
+    /**
+     * Test if given {@link Signature} objects are effectively equal. In rare
+     * cases, certificates can have slightly malformed encoding which causes
+     * exact-byte checks to fail.
+     * <p>
+     * To identify effective equality, we bounce the certificates through an
+     * decode/encode pass before doing the exact-byte check. To reduce attack
+     * surface area, we only allow a byte size delta of a few bytes.
+     *
+     * @throws CertificateException if the before/after length differs
+     *             substantially, usually a signal of something fishy going on.
+     * @hide
+     */
+    public static boolean areEffectiveMatch(Signature a, Signature b)
+            throws CertificateException {
+        final CertificateFactory cf = CertificateFactory.getInstance("X.509");
+
+        final Signature aPrime = bounce(cf, a);
+        final Signature bPrime = bounce(cf, b);
+
+        return aPrime.equals(bPrime);
+    }
+
+    /**
+     * Bounce the given {@link Signature} through a decode/encode cycle.
+     *
+     * @throws CertificateException if the before/after length differs
+     *             substantially, usually a signal of something fishy going on.
+     * @hide
+     */
+    public static Signature bounce(CertificateFactory cf, Signature s) throws CertificateException {
+        final InputStream is = new ByteArrayInputStream(s.mSignature);
+        final X509Certificate cert = (X509Certificate) cf.generateCertificate(is);
+        final Signature sPrime = new Signature(cert.getEncoded());
+
+        if (Math.abs(sPrime.mSignature.length - s.mSignature.length) > 2) {
+            throw new CertificateException("Bounced cert length looks fishy; before "
+                    + s.mSignature.length + ", after " + sPrime.mSignature.length);
+        }
+
+        return sPrime;
+    }
+}
\ No newline at end of file
diff --git a/android/content/pm/SigningInfo.java b/android/content/pm/SigningInfo.java
new file mode 100644
index 0000000..d14be9c
--- /dev/null
+++ b/android/content/pm/SigningInfo.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.content.pm;
+
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Information pertaining to the signing certificates used to sign a package.
+ */
+public final class SigningInfo implements Parcelable {
+
+    @NonNull
+    private final PackageParser.SigningDetails mSigningDetails;
+
+    public SigningInfo() {
+        mSigningDetails = PackageParser.SigningDetails.UNKNOWN;
+    }
+
+    /**
+     * @hide only packagemanager should be populating this
+     */
+    public SigningInfo(PackageParser.SigningDetails signingDetails) {
+        mSigningDetails = new PackageParser.SigningDetails(signingDetails);
+    }
+
+    public SigningInfo(SigningInfo orig) {
+        mSigningDetails = new PackageParser.SigningDetails(orig.mSigningDetails);
+    }
+
+    private SigningInfo(Parcel source) {
+        mSigningDetails = PackageParser.SigningDetails.CREATOR.createFromParcel(source);
+    }
+
+    /**
+     * Although relatively uncommon, packages may be signed by more than one signer, in which case
+     * their identity is viewed as being the set of all signers, not just any one.
+     */
+    public boolean hasMultipleSigners() {
+        return mSigningDetails.signatures != null && mSigningDetails.signatures.length > 1;
+    }
+
+    /**
+     * APK Signature Scheme v3 enables packages to provide a proof-of-rotation record that the
+     * platform verifies, and uses, to allow the use of new signing certificates.  This is only
+     * available to packages that are not signed by multiple signers.  In the event of a change to a
+     * new signing certificate, the package's past signing certificates are presented as well.  Any
+     * check of a package's signing certificate should also include a search through its entire
+     * signing history, since it could change to a new signing certificate at any time.
+     */
+    public boolean hasPastSigningCertificates() {
+        return mSigningDetails.signatures != null
+                && mSigningDetails.pastSigningCertificates != null;
+    }
+
+    /**
+     * Returns the signing certificates this package has proven it is authorized to use. This
+     * includes both the signing certificate associated with the signer of the package and the past
+     * signing certificates it included as its proof of signing certificate rotation.  This method
+     * is the preferred replacement for the {@code GET_SIGNATURES} flag used with {@link
+     * PackageManager#getPackageInfo(String, int)}.  When determining if a package is signed by a
+     * desired certificate, the returned array should be checked to determine if it is one of the
+     * entries.
+     *
+     * <note>
+     *     This method returns null if the package is signed by multiple signing certificates, as
+     *     opposed to being signed by one current signer and also providing the history of past
+     *     signing certificates.  {@link #hasMultipleSigners()} may be used to determine if this
+     *     package is signed by multiple signers.  Packages which are signed by multiple signers
+     *     cannot change their signing certificates and their {@code Signature} array should be
+     *     checked to make sure that every entry matches the looked-for signing certificates.
+     * </note>
+     */
+    public Signature[] getSigningCertificateHistory() {
+        if (hasMultipleSigners()) {
+            return null;
+        } else if (!hasPastSigningCertificates()) {
+
+            // this package is only signed by one signer with no history, return it
+            return mSigningDetails.signatures;
+        } else {
+
+            // this package has provided proof of past signing certificates, include them
+            return mSigningDetails.pastSigningCertificates;
+        }
+    }
+
+    /**
+     * Returns the signing certificates used to sign the APK contents of this application.  Not
+     * including any past signing certificates the package proved it is authorized to use.
+     * <note>
+     *     This method should not be used unless {@link #hasMultipleSigners()} returns true,
+     *     indicating that {@link #getSigningCertificateHistory()} cannot be used, otherwise {@link
+     *     #getSigningCertificateHistory()} should be preferred.
+     * </note>
+     */
+    public Signature[] getApkContentsSigners() {
+        return mSigningDetails.signatures;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int parcelableFlags) {
+        mSigningDetails.writeToParcel(dest, parcelableFlags);
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<SigningInfo> CREATOR =
+            new Parcelable.Creator<SigningInfo>() {
+        @Override
+        public SigningInfo createFromParcel(Parcel source) {
+            return new SigningInfo(source);
+        }
+
+        @Override
+        public SigningInfo[] newArray(int size) {
+            return new SigningInfo[size];
+        }
+    };
+}
diff --git a/android/content/pm/StringParceledListSlice.java b/android/content/pm/StringParceledListSlice.java
new file mode 100644
index 0000000..9540744
--- /dev/null
+++ b/android/content/pm/StringParceledListSlice.java
@@ -0,0 +1,83 @@
+/*
+ * 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.content.pm;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Transfer a large list of Parcelable objects across an IPC.  Splits into
+ * multiple transactions if needed.
+ *
+ * @see BaseParceledListSlice
+ *
+ * @hide
+ */
+public class StringParceledListSlice extends BaseParceledListSlice<String> {
+    public StringParceledListSlice(List<String> list) {
+        super(list);
+    }
+
+    private StringParceledListSlice(Parcel in, ClassLoader loader) {
+        super(in, loader);
+    }
+
+    public static StringParceledListSlice emptyList() {
+        return new StringParceledListSlice(Collections.<String> emptyList());
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    protected void writeElement(String parcelable, Parcel reply, int callFlags) {
+        reply.writeString(parcelable);
+    }
+
+    @Override
+    protected void writeParcelableCreator(String parcelable, Parcel dest) {
+        return;
+    }
+
+    @Override
+    protected Parcelable.Creator<?> readParcelableCreator(Parcel from, ClassLoader loader) {
+        return Parcel.STRING_CREATOR;
+    }
+
+    @SuppressWarnings("unchecked")
+    public static final Parcelable.ClassLoaderCreator<StringParceledListSlice> CREATOR =
+            new Parcelable.ClassLoaderCreator<StringParceledListSlice>() {
+        public StringParceledListSlice createFromParcel(Parcel in) {
+            return new StringParceledListSlice(in, null);
+        }
+
+        @Override
+        public StringParceledListSlice createFromParcel(Parcel in, ClassLoader loader) {
+            return new StringParceledListSlice(in, loader);
+        }
+
+        @Override
+        public StringParceledListSlice[] newArray(int size) {
+            return new StringParceledListSlice[size];
+        }
+    };
+}
diff --git a/android/content/pm/SuspendDialogInfo.java b/android/content/pm/SuspendDialogInfo.java
new file mode 100644
index 0000000..851a081
--- /dev/null
+++ b/android/content/pm/SuspendDialogInfo.java
@@ -0,0 +1,456 @@
+/*
+ * 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.content.pm;
+
+import static android.content.res.Resources.ID_NULL;
+
+import android.annotation.DrawableRes;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StringRes;
+import android.annotation.SystemApi;
+import android.content.res.ResourceId;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.PersistableBundle;
+import android.util.Slog;
+
+import com.android.internal.util.Preconditions;
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Locale;
+import java.util.Objects;
+
+/**
+ * A container to describe the dialog to be shown when the user tries to launch a suspended
+ * application. The suspending app can customize the dialog's following attributes:
+ * <ul>
+ * <li>The dialog icon, by providing a resource id.
+ * <li>The title text, by providing a resource id.
+ * <li>The text of the dialog's body, by providing a resource id or a string.
+ * <li>The text on the neutral button by providing a resource id.
+ * <li>The action performed on tapping the neutral button. Only {@link #BUTTON_ACTION_UNSUSPEND}
+ * and {@link #BUTTON_ACTION_MORE_DETAILS} are currently supported.
+ * </ul>
+ * System defaults are used whenever any of these are not provided, or any of the provided resource
+ * ids cannot be resolved at the time of displaying the dialog.
+ *
+ * @hide
+ * @see PackageManager#setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle,
+ * SuspendDialogInfo)
+ * @see Builder
+ */
+@SystemApi
+public final class SuspendDialogInfo implements Parcelable {
+    private static final String TAG = SuspendDialogInfo.class.getSimpleName();
+    private static final String XML_ATTR_ICON_RES_ID = "iconResId";
+    private static final String XML_ATTR_TITLE_RES_ID = "titleResId";
+    private static final String XML_ATTR_DIALOG_MESSAGE_RES_ID = "dialogMessageResId";
+    private static final String XML_ATTR_DIALOG_MESSAGE = "dialogMessage";
+    private static final String XML_ATTR_BUTTON_TEXT_RES_ID = "buttonTextResId";
+    private static final String XML_ATTR_BUTTON_ACTION = "buttonAction";
+
+    private final int mIconResId;
+    private final int mTitleResId;
+    private final int mDialogMessageResId;
+    private final String mDialogMessage;
+    private final int mNeutralButtonTextResId;
+    private final int mNeutralButtonAction;
+
+    /**
+     * Used with {@link Builder#setNeutralButtonAction(int)} to create a neutral button that
+     * starts the {@link android.content.Intent#ACTION_SHOW_SUSPENDED_APP_DETAILS} activity.
+     * @see Builder#setNeutralButtonAction(int)
+     */
+    public static final int BUTTON_ACTION_MORE_DETAILS = 0;
+
+    /**
+     * Used with {@link Builder#setNeutralButtonAction(int)} to create a neutral button that
+     * unsuspends the app that the user was trying to launch and continues with the launch. The
+     * system also sends the broadcast
+     * {@link android.content.Intent#ACTION_PACKAGE_UNSUSPENDED_MANUALLY} to the suspending app
+     * when this happens.
+     * @see Builder#setNeutralButtonAction(int)
+     * @see android.content.Intent#ACTION_PACKAGE_UNSUSPENDED_MANUALLY
+     */
+    public static final int BUTTON_ACTION_UNSUSPEND = 1;
+
+    /**
+     * Button actions to specify what happens when the user taps on the neutral button.
+     * To be used with {@link Builder#setNeutralButtonAction(int)}.
+     *
+     * @hide
+     * @see Builder#setNeutralButtonAction(int)
+     */
+    @IntDef(flag = true, prefix = {"BUTTON_ACTION_"}, value = {
+            BUTTON_ACTION_MORE_DETAILS,
+            BUTTON_ACTION_UNSUSPEND
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ButtonAction {
+    }
+
+    /**
+     * @return the resource id of the icon to be used with the dialog
+     * @hide
+     */
+    @DrawableRes
+    public int getIconResId() {
+        return mIconResId;
+    }
+
+    /**
+     * @return the resource id of the title to be used with the dialog
+     * @hide
+     */
+    @StringRes
+    public int getTitleResId() {
+        return mTitleResId;
+    }
+
+    /**
+     * @return the resource id of the text to be shown in the dialog's body
+     * @hide
+     */
+    @StringRes
+    public int getDialogMessageResId() {
+        return mDialogMessageResId;
+    }
+
+    /**
+     * @return the text to be shown in the dialog's body. Returns {@code null} if {@link
+     * #getDialogMessageResId()} returns a valid resource id
+     * @hide
+     */
+    @Nullable
+    public String getDialogMessage() {
+        return mDialogMessage;
+    }
+
+    /**
+     * @return the text to be shown
+     * @hide
+     */
+    @StringRes
+    public int getNeutralButtonTextResId() {
+        return mNeutralButtonTextResId;
+    }
+
+    /**
+     * @return The {@link ButtonAction} that happens on tapping this button
+     * @hide
+     */
+    @ButtonAction
+    public int getNeutralButtonAction() {
+        return mNeutralButtonAction;
+    }
+
+    /**
+     * @hide
+     */
+    public void saveToXml(XmlSerializer out) throws IOException {
+        if (mIconResId != ID_NULL) {
+            XmlUtils.writeIntAttribute(out, XML_ATTR_ICON_RES_ID, mIconResId);
+        }
+        if (mTitleResId != ID_NULL) {
+            XmlUtils.writeIntAttribute(out, XML_ATTR_TITLE_RES_ID, mTitleResId);
+        }
+        if (mDialogMessageResId != ID_NULL) {
+            XmlUtils.writeIntAttribute(out, XML_ATTR_DIALOG_MESSAGE_RES_ID, mDialogMessageResId);
+        } else {
+            XmlUtils.writeStringAttribute(out, XML_ATTR_DIALOG_MESSAGE, mDialogMessage);
+        }
+        if (mNeutralButtonTextResId != ID_NULL) {
+            XmlUtils.writeIntAttribute(out, XML_ATTR_BUTTON_TEXT_RES_ID, mNeutralButtonTextResId);
+        }
+        XmlUtils.writeIntAttribute(out, XML_ATTR_BUTTON_ACTION, mNeutralButtonAction);
+    }
+
+    /**
+     * @hide
+     */
+    public static SuspendDialogInfo restoreFromXml(XmlPullParser in) {
+        final SuspendDialogInfo.Builder dialogInfoBuilder = new SuspendDialogInfo.Builder();
+        try {
+            final int iconId = XmlUtils.readIntAttribute(in, XML_ATTR_ICON_RES_ID, ID_NULL);
+            final int titleId = XmlUtils.readIntAttribute(in, XML_ATTR_TITLE_RES_ID, ID_NULL);
+            final int buttonTextId = XmlUtils.readIntAttribute(in, XML_ATTR_BUTTON_TEXT_RES_ID,
+                    ID_NULL);
+            final int buttonAction = XmlUtils.readIntAttribute(in, XML_ATTR_BUTTON_ACTION,
+                    BUTTON_ACTION_MORE_DETAILS);
+            final int dialogMessageResId = XmlUtils.readIntAttribute(
+                    in, XML_ATTR_DIALOG_MESSAGE_RES_ID, ID_NULL);
+            final String dialogMessage = XmlUtils.readStringAttribute(in, XML_ATTR_DIALOG_MESSAGE);
+
+            if (iconId != ID_NULL) {
+                dialogInfoBuilder.setIcon(iconId);
+            }
+            if (titleId != ID_NULL) {
+                dialogInfoBuilder.setTitle(titleId);
+            }
+            if (buttonTextId != ID_NULL) {
+                dialogInfoBuilder.setNeutralButtonText(buttonTextId);
+            }
+            if (dialogMessageResId != ID_NULL) {
+                dialogInfoBuilder.setMessage(dialogMessageResId);
+            } else if (dialogMessage != null) {
+                dialogInfoBuilder.setMessage(dialogMessage);
+            }
+            dialogInfoBuilder.setNeutralButtonAction(buttonAction);
+        } catch (Exception e) {
+            Slog.e(TAG, "Exception while parsing from xml. Some fields may default", e);
+        }
+        return dialogInfoBuilder.build();
+    }
+
+    @Override
+    public int hashCode() {
+        int hashCode = mIconResId;
+        hashCode = 31 * hashCode + mTitleResId;
+        hashCode = 31 * hashCode + mNeutralButtonTextResId;
+        hashCode = 31 * hashCode + mDialogMessageResId;
+        hashCode = 31 * hashCode + Objects.hashCode(mDialogMessage);
+        hashCode = 31 * hashCode + mNeutralButtonAction;
+        return hashCode;
+    }
+
+    @Override
+    public boolean equals(@Nullable Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof SuspendDialogInfo)) {
+            return false;
+        }
+        final SuspendDialogInfo otherDialogInfo = (SuspendDialogInfo) obj;
+        return mIconResId == otherDialogInfo.mIconResId
+                && mTitleResId == otherDialogInfo.mTitleResId
+                && mDialogMessageResId == otherDialogInfo.mDialogMessageResId
+                && mNeutralButtonTextResId == otherDialogInfo.mNeutralButtonTextResId
+                && mNeutralButtonAction == otherDialogInfo.mNeutralButtonAction
+                && Objects.equals(mDialogMessage, otherDialogInfo.mDialogMessage);
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        final StringBuilder builder = new StringBuilder("SuspendDialogInfo: {");
+        if (mIconResId != ID_NULL) {
+            builder.append("mIconId = 0x");
+            builder.append(Integer.toHexString(mIconResId));
+            builder.append(" ");
+        }
+        if (mTitleResId != ID_NULL) {
+            builder.append("mTitleResId = 0x");
+            builder.append(Integer.toHexString(mTitleResId));
+            builder.append(" ");
+        }
+        if (mNeutralButtonTextResId != ID_NULL) {
+            builder.append("mNeutralButtonTextResId = 0x");
+            builder.append(Integer.toHexString(mNeutralButtonTextResId));
+            builder.append(" ");
+        }
+        if (mDialogMessageResId != ID_NULL) {
+            builder.append("mDialogMessageResId = 0x");
+            builder.append(Integer.toHexString(mDialogMessageResId));
+            builder.append(" ");
+        } else if (mDialogMessage != null) {
+            builder.append("mDialogMessage = \"");
+            builder.append(mDialogMessage);
+            builder.append("\" ");
+        }
+        builder.append("mNeutralButtonAction = ");
+        builder.append(mNeutralButtonAction);
+        builder.append("}");
+        return builder.toString();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int parcelableFlags) {
+        dest.writeInt(mIconResId);
+        dest.writeInt(mTitleResId);
+        dest.writeInt(mDialogMessageResId);
+        dest.writeString(mDialogMessage);
+        dest.writeInt(mNeutralButtonTextResId);
+        dest.writeInt(mNeutralButtonAction);
+    }
+
+    private SuspendDialogInfo(Parcel source) {
+        mIconResId = source.readInt();
+        mTitleResId = source.readInt();
+        mDialogMessageResId = source.readInt();
+        mDialogMessage = source.readString();
+        mNeutralButtonTextResId = source.readInt();
+        mNeutralButtonAction = source.readInt();
+    }
+
+    SuspendDialogInfo(Builder b) {
+        mIconResId = b.mIconResId;
+        mTitleResId = b.mTitleResId;
+        mDialogMessageResId = b.mDialogMessageResId;
+        mDialogMessage = (mDialogMessageResId == ID_NULL) ? b.mDialogMessage : null;
+        mNeutralButtonTextResId = b.mNeutralButtonTextResId;
+        mNeutralButtonAction = b.mNeutralButtonAction;
+    }
+
+    public static final @NonNull Creator<SuspendDialogInfo> CREATOR =
+            new Creator<SuspendDialogInfo>() {
+        @Override
+        public SuspendDialogInfo createFromParcel(Parcel source) {
+            return new SuspendDialogInfo(source);
+        }
+
+        @Override
+        public SuspendDialogInfo[] newArray(int size) {
+            return new SuspendDialogInfo[size];
+        }
+    };
+
+    /**
+     * Builder to build a {@link SuspendDialogInfo} object.
+     */
+    public static final class Builder {
+        private int mDialogMessageResId = ID_NULL;
+        private String mDialogMessage;
+        private int mTitleResId = ID_NULL;
+        private int mIconResId = ID_NULL;
+        private int mNeutralButtonTextResId = ID_NULL;
+        private int mNeutralButtonAction = BUTTON_ACTION_MORE_DETAILS;
+
+        /**
+         * Set the resource id of the icon to be used. If not provided, no icon will be shown.
+         *
+         * @param resId The resource id of the icon.
+         * @return this builder object.
+         */
+        @NonNull
+        public Builder setIcon(@DrawableRes int resId) {
+            Preconditions.checkArgument(ResourceId.isValid(resId), "Invalid resource id provided");
+            mIconResId = resId;
+            return this;
+        }
+
+        /**
+         * Set the resource id of the title text to be displayed. If this is not provided, the
+         * system will use a default title.
+         *
+         * @param resId The resource id of the title.
+         * @return this builder object.
+         */
+        @NonNull
+        public Builder setTitle(@StringRes int resId) {
+            Preconditions.checkArgument(ResourceId.isValid(resId), "Invalid resource id provided");
+            mTitleResId = resId;
+            return this;
+        }
+
+        /**
+         * Set the text to show in the body of the dialog. Ignored if a resource id is set via
+         * {@link #setMessage(int)}.
+         * <p>
+         * The system will use {@link String#format(Locale, String, Object...) String.format} to
+         * insert the suspended app name into the message, so an example format string could be
+         * {@code "The app %1$s is currently suspended"}. This is optional - if the string passed in
+         * {@code message} does not accept an argument, it will be used as is.
+         *
+         * @param message The dialog message.
+         * @return this builder object.
+         * @see #setMessage(int)
+         */
+        @NonNull
+        public Builder setMessage(@NonNull String message) {
+            Preconditions.checkStringNotEmpty(message, "Message cannot be null or empty");
+            mDialogMessage = message;
+            return this;
+        }
+
+        /**
+         * Set the resource id of the dialog message to be shown. If no dialog message is provided
+         * via either this method or {@link #setMessage(String)}, the system will use a default
+         * message.
+         * <p>
+         * The system will use {@link android.content.res.Resources#getString(int, Object...)
+         * getString} to insert the suspended app name into the message, so an example format string
+         * could be {@code "The app %1$s is currently suspended"}. This is optional - if the string
+         * referred to by {@code resId} does not accept an argument, it will be used as is.
+         *
+         * @param resId The resource id of the dialog message.
+         * @return this builder object.
+         * @see #setMessage(String)
+         */
+        @NonNull
+        public Builder setMessage(@StringRes int resId) {
+            Preconditions.checkArgument(ResourceId.isValid(resId), "Invalid resource id provided");
+            mDialogMessageResId = resId;
+            return this;
+        }
+
+        /**
+         * Set the resource id of text to be shown on the neutral button. Tapping this button would
+         * perform the {@link ButtonAction action} specified through
+         * {@link #setNeutralButtonAction(int)}. If this is not provided, the system will use a
+         * default text.
+         *
+         * @param resId The resource id of the button text
+         * @return this builder object.
+         */
+        @NonNull
+        public Builder setNeutralButtonText(@StringRes int resId) {
+            Preconditions.checkArgument(ResourceId.isValid(resId), "Invalid resource id provided");
+            mNeutralButtonTextResId = resId;
+            return this;
+        }
+
+        /**
+         * Set the action expected to happen on neutral button tap. Defaults to
+         * {@link #BUTTON_ACTION_MORE_DETAILS} if this is not provided.
+         *
+         * @param buttonAction Either {@link #BUTTON_ACTION_MORE_DETAILS} or
+         *                     {@link #BUTTON_ACTION_UNSUSPEND}.
+         * @return this builder object
+         */
+        @NonNull
+        public Builder setNeutralButtonAction(@ButtonAction int buttonAction) {
+            Preconditions.checkArgument(buttonAction == BUTTON_ACTION_MORE_DETAILS
+                    || buttonAction == BUTTON_ACTION_UNSUSPEND, "Invalid button action");
+            mNeutralButtonAction = buttonAction;
+            return this;
+        }
+
+        /**
+         * Build the final object based on given inputs.
+         *
+         * @return The {@link SuspendDialogInfo} object built using this builder.
+         */
+        @NonNull
+        public SuspendDialogInfo build() {
+            return new SuspendDialogInfo(this);
+        }
+    }
+}
diff --git a/android/content/pm/UserInfo.java b/android/content/pm/UserInfo.java
new file mode 100644
index 0000000..aca5b45
--- /dev/null
+++ b/android/content/pm/UserInfo.java
@@ -0,0 +1,504 @@
+/*
+ * 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.content.pm;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.DebugUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Per-user information.
+ *
+ * <p>There are 3 base properties of users: {@link #FLAG_SYSTEM}, {@link #FLAG_FULL}, and
+ * {@link #FLAG_PROFILE}. Every user must have one of the following combination of these
+ * flags:
+ * <ul>
+ *    <li>FLAG_SYSTEM (user {@link UserHandle#USER_SYSTEM} on a headless-user-0 device)</li>
+ *    <li>FLAG_SYSTEM and FLAG_FULL (user {@link UserHandle#USER_SYSTEM} on a regular device)</li>
+ *    <li>FLAG_FULL (non-profile secondary user)</li>
+ *    <li>FLAG_PROFILE (profile users)</li>
+ * </ul>
+ * Users can have also have additional flags (such as FLAG_GUEST) as appropriate.
+ *
+ * @hide
+ */
+public class UserInfo implements Parcelable {
+
+    /**
+     * *************************** NOTE ***************************
+     * These flag values CAN NOT CHANGE because they are written
+     * directly to storage.
+     */
+
+    /**
+     * Primary user. Only one user can have this flag set. It identifies the first human user
+     * on a device. This flag is not supported in headless system user mode.
+     */
+    @UnsupportedAppUsage
+    public static final int FLAG_PRIMARY = 0x00000001;
+
+    /**
+     * User with administrative privileges. Such a user can create and
+     * delete users.
+     */
+    public static final int FLAG_ADMIN   = 0x00000002;
+
+    /**
+     * Indicates a guest user that may be transient.
+     * @deprecated Use {@link UserManager#USER_TYPE_FULL_GUEST} instead.
+     */
+    @Deprecated
+    public static final int FLAG_GUEST   = 0x00000004;
+
+    /**
+     * Indicates the user has restrictions in privileges, in addition to those for normal users.
+     * Exact meaning TBD. For instance, maybe they can't install apps or administer WiFi access pts.
+     * @deprecated Use {@link UserManager#USER_TYPE_FULL_RESTRICTED} instead.
+     */
+    @Deprecated
+    public static final int FLAG_RESTRICTED = 0x00000008;
+
+    /**
+     * Indicates that this user has gone through its first-time initialization.
+     */
+    public static final int FLAG_INITIALIZED = 0x00000010;
+
+    /**
+     * Indicates that this user is a profile of another user, for example holding a users
+     * corporate data.
+     * @deprecated Use {@link UserManager#USER_TYPE_PROFILE_MANAGED} instead.
+     */
+    @Deprecated
+    public static final int FLAG_MANAGED_PROFILE = 0x00000020;
+
+    /**
+     * Indicates that this user is disabled.
+     *
+     * <p>Note: If an ephemeral user is disabled, it shouldn't be later re-enabled. Ephemeral users
+     * are disabled as their removal is in progress to indicate that they shouldn't be re-entered.
+     */
+    public static final int FLAG_DISABLED = 0x00000040;
+
+    public static final int FLAG_QUIET_MODE = 0x00000080;
+
+    /**
+     * Indicates that this user is ephemeral. I.e. the user will be removed after leaving
+     * the foreground.
+     */
+    public static final int FLAG_EPHEMERAL = 0x00000100;
+
+    /**
+     * User is for demo purposes only and can be removed at any time.
+     * @deprecated Use {@link UserManager#USER_TYPE_FULL_DEMO} instead.
+     */
+    @Deprecated
+    public static final int FLAG_DEMO = 0x00000200;
+
+    /**
+     * Indicates that this user is a non-profile human user.
+     *
+     * <p>When creating a new (non-system) user, this flag will always be forced true unless the
+     * user is a {@link #FLAG_PROFILE}. If user {@link UserHandle#USER_SYSTEM} is also a
+     * human user, it must also be flagged as FULL.
+     */
+    public static final int FLAG_FULL = 0x00000400;
+
+    /**
+     * Indicates that this user is {@link UserHandle#USER_SYSTEM}. Not applicable to created users.
+     */
+    public static final int FLAG_SYSTEM = 0x00000800;
+
+    /**
+     * Indicates that this user is a profile human user, such as a managed profile.
+     * Mutually exclusive with {@link #FLAG_FULL}.
+     */
+    public static final int FLAG_PROFILE = 0x00001000;
+
+    /**
+     * @hide
+     */
+    @IntDef(flag = true, prefix = "FLAG_", value = {
+            FLAG_PRIMARY,
+            FLAG_ADMIN,
+            FLAG_GUEST,
+            FLAG_RESTRICTED,
+            FLAG_INITIALIZED,
+            FLAG_MANAGED_PROFILE,
+            FLAG_DISABLED,
+            FLAG_QUIET_MODE,
+            FLAG_EPHEMERAL,
+            FLAG_DEMO,
+            FLAG_FULL,
+            FLAG_SYSTEM,
+            FLAG_PROFILE
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface UserInfoFlag {
+    }
+
+    public static final int NO_PROFILE_GROUP_ID = UserHandle.USER_NULL;
+
+    @UnsupportedAppUsage
+    public @UserIdInt int id;
+    @UnsupportedAppUsage
+    public int serialNumber;
+    @UnsupportedAppUsage
+    public String name;
+    @UnsupportedAppUsage
+    public String iconPath;
+    @UnsupportedAppUsage
+    public @UserInfoFlag int flags;
+    @UnsupportedAppUsage
+    public long creationTime;
+    @UnsupportedAppUsage
+    public long lastLoggedInTime;
+    public String lastLoggedInFingerprint;
+
+    /**
+     * Type of user, such as {@link UserManager#USER_TYPE_PROFILE_MANAGED}, corresponding to
+     * {@link com.android.server.pm.UserTypeDetails#getName()}.
+     */
+    public String userType;
+
+    /**
+     * If this user is a parent user, it would be its own user id.
+     * If this user is a child user, it would be its parent user id.
+     * Otherwise, it would be {@link #NO_PROFILE_GROUP_ID}.
+     */
+    @UnsupportedAppUsage
+    public int profileGroupId;
+    public int restrictedProfileParentId;
+
+    /**
+     * Index for distinguishing different profiles with the same parent and user type for the
+     * purpose of badging.
+     * It is used for determining which badge color/label to use (if applicable) from
+     * the options available for a particular user type.
+     */
+    public int profileBadge;
+
+    /** User is only partially created. */
+    @UnsupportedAppUsage
+    public boolean partial;
+    @UnsupportedAppUsage
+    public boolean guestToRemove;
+
+    /**
+     * This is used to optimize the creation of an user, i.e. OEMs might choose to pre-create a
+     * number of users at the first boot, so the actual creation later is faster.
+     *
+     * <p>A {@code preCreated} user is not a real user yet, so it should not show up on regular
+     * user operations (other than user creation per se).
+     *
+     * <p>Once the pre-created is used to create a "real" user later on, {@code preCreate} is set to
+     * {@code false}.
+     */
+    public boolean preCreated;
+
+    /**
+     * Creates a UserInfo whose user type is determined automatically by the flags according to
+     * {@link #getDefaultUserType}; can only be used for user types handled there.
+     */
+    @UnsupportedAppUsage
+    public UserInfo(int id, String name, int flags) {
+        this(id, name, null, flags);
+    }
+
+    /**
+     * Creates a UserInfo whose user type is determined automatically by the flags according to
+     * {@link #getDefaultUserType}; can only be used for user types handled there.
+     */
+    @UnsupportedAppUsage
+    public UserInfo(int id, String name, String iconPath, int flags) {
+        this(id, name, iconPath, flags, getDefaultUserType(flags));
+    }
+
+    public UserInfo(int id, String name, String iconPath, int flags, String userType) {
+        this.id = id;
+        this.name = name;
+        this.flags = flags;
+        this.userType = userType;
+        this.iconPath = iconPath;
+        this.profileGroupId = NO_PROFILE_GROUP_ID;
+        this.restrictedProfileParentId = NO_PROFILE_GROUP_ID;
+    }
+
+    /**
+     * Get the user type (such as {@link UserManager#USER_TYPE_PROFILE_MANAGED}) that corresponds to
+     * the given {@link UserInfoFlag}s.
+
+     * <p>The userInfoFlag can contain GUEST, RESTRICTED, MANAGED_PROFILE, DEMO, or else be
+     * interpreted as a regular "secondary" user. It cannot contain more than one of these.
+     * It can contain other UserInfoFlag properties (like EPHEMERAL), which will be ignored here.
+     *
+     * @throws IllegalArgumentException if userInfoFlag is more than one type of user or if it
+     *                                  is a SYSTEM user.
+     *
+     * @hide
+     */
+    public static @NonNull String getDefaultUserType(@UserInfoFlag int userInfoFlag) {
+        if ((userInfoFlag & FLAG_SYSTEM) != 0) {
+            throw new IllegalArgumentException("Cannot getDefaultUserType for flags "
+                    + Integer.toHexString(userInfoFlag) + " because it corresponds to a "
+                    + "SYSTEM user type.");
+        }
+        final int supportedFlagTypes =
+                FLAG_GUEST | FLAG_RESTRICTED | FLAG_MANAGED_PROFILE | FLAG_DEMO;
+        switch (userInfoFlag & supportedFlagTypes) {
+            case 0 :                   return UserManager.USER_TYPE_FULL_SECONDARY;
+            case FLAG_GUEST:           return UserManager.USER_TYPE_FULL_GUEST;
+            case FLAG_RESTRICTED:      return UserManager.USER_TYPE_FULL_RESTRICTED;
+            case FLAG_MANAGED_PROFILE: return UserManager.USER_TYPE_PROFILE_MANAGED;
+            case FLAG_DEMO:            return UserManager.USER_TYPE_FULL_DEMO;
+            default:
+                throw new IllegalArgumentException("Cannot getDefaultUserType for flags "
+                        + Integer.toHexString(userInfoFlag) + " because it doesn't correspond to a "
+                        + "valid user type.");
+        }
+    }
+
+    @UnsupportedAppUsage
+    public boolean isPrimary() {
+        return (flags & FLAG_PRIMARY) == FLAG_PRIMARY;
+    }
+
+    @UnsupportedAppUsage
+    public boolean isAdmin() {
+        return (flags & FLAG_ADMIN) == FLAG_ADMIN;
+    }
+
+    @UnsupportedAppUsage
+    public boolean isGuest() {
+        return UserManager.isUserTypeGuest(userType);
+    }
+
+    @UnsupportedAppUsage
+    public boolean isRestricted() {
+        return UserManager.isUserTypeRestricted(userType);
+    }
+
+    public boolean isProfile() {
+        return (flags & FLAG_PROFILE) != 0;
+    }
+
+    @UnsupportedAppUsage
+    public boolean isManagedProfile() {
+        return UserManager.isUserTypeManagedProfile(userType);
+    }
+
+    @UnsupportedAppUsage
+    public boolean isEnabled() {
+        return (flags & FLAG_DISABLED) != FLAG_DISABLED;
+    }
+
+    public boolean isQuietModeEnabled() {
+        return (flags & FLAG_QUIET_MODE) == FLAG_QUIET_MODE;
+    }
+
+    public boolean isEphemeral() {
+        return (flags & FLAG_EPHEMERAL) == FLAG_EPHEMERAL;
+    }
+
+    public boolean isInitialized() {
+        return (flags & FLAG_INITIALIZED) == FLAG_INITIALIZED;
+    }
+
+    public boolean isDemo() {
+        return UserManager.isUserTypeDemo(userType);
+    }
+
+    public boolean isFull() {
+        return (flags & FLAG_FULL) == FLAG_FULL;
+    }
+
+    /**
+     * Returns true if the user is a split system user.
+     * <p>If {@link UserManager#isSplitSystemUser split system user mode} is not enabled,
+     * the method always returns false.
+     */
+    public boolean isSystemOnly() {
+        return isSystemOnly(id);
+    }
+
+    /**
+     * Returns true if the given user is a split system user.
+     * <p>If {@link UserManager#isSplitSystemUser split system user mode} is not enabled,
+     * the method always returns false.
+     */
+    public static boolean isSystemOnly(int userId) {
+        return userId == UserHandle.USER_SYSTEM && UserManager.isSplitSystemUser();
+    }
+
+    /**
+     * @return true if this user can be switched to.
+     **/
+    public boolean supportsSwitchTo() {
+        if (isEphemeral() && !isEnabled()) {
+            // Don't support switching to an ephemeral user with removal in progress.
+            return false;
+        }
+        if (preCreated) {
+            // Don't support switching to pre-created users until they become "real" users.
+            return false;
+        }
+        return !isProfile();
+    }
+
+    /**
+     * @return true if this user can be switched to by end user through UI.
+     */
+    public boolean supportsSwitchToByUser() {
+        // Hide the system user when it does not represent a human user.
+        boolean hideSystemUser = UserManager.isHeadlessSystemUserMode();
+        return (!hideSystemUser || id != UserHandle.USER_SYSTEM) && supportsSwitchTo();
+    }
+
+    // TODO(b/142482943): Make this logic more specific and customizable. (canHaveProfile(userType))
+    /* @hide */
+    public boolean canHaveProfile() {
+        if (isProfile() || isGuest() || isRestricted()) {
+            return false;
+        }
+        if (UserManager.isSplitSystemUser() || UserManager.isHeadlessSystemUserMode()) {
+            return id != UserHandle.USER_SYSTEM;
+        } else {
+            return id == UserHandle.USER_SYSTEM;
+        }
+    }
+
+    // TODO(b/142482943): Get rid of this (after removing it from all tests) if feasible.
+    /**
+     * @deprecated This is dangerous since it doesn't set the mandatory fields. Use a different
+     * constructor instead.
+     */
+    @Deprecated
+    @VisibleForTesting
+    public UserInfo() {
+    }
+
+    public UserInfo(UserInfo orig) {
+        name = orig.name;
+        iconPath = orig.iconPath;
+        id = orig.id;
+        flags = orig.flags;
+        userType = orig.userType;
+        serialNumber = orig.serialNumber;
+        creationTime = orig.creationTime;
+        lastLoggedInTime = orig.lastLoggedInTime;
+        lastLoggedInFingerprint = orig.lastLoggedInFingerprint;
+        partial = orig.partial;
+        preCreated = orig.preCreated;
+        profileGroupId = orig.profileGroupId;
+        restrictedProfileParentId = orig.restrictedProfileParentId;
+        guestToRemove = orig.guestToRemove;
+        profileBadge = orig.profileBadge;
+    }
+
+    @UnsupportedAppUsage
+    public UserHandle getUserHandle() {
+        return UserHandle.of(id);
+    }
+
+    // TODO(b/142482943): Probably include mUserType here, which means updating TestDevice, etc.
+    @Override
+    public String toString() {
+        // NOTE:  do not change this string, it's used by 'pm list users', which in turn is
+        // used and parsed by TestDevice. In other words, if you change it, you'd have to change
+        // TestDevice, TestDeviceTest, and possibly others....
+        return "UserInfo{" + id + ":" + name + ":" + Integer.toHexString(flags) + "}";
+    }
+
+    /** @hide */
+    public String toFullString() {
+        return "UserInfo[id=" + id
+                + ", name=" + name
+                + ", type=" + userType
+                + ", flags=" + flagsToString(flags)
+                + (preCreated ? " (pre-created)" : "")
+                + (partial ? " (partial)" : "")
+                + "]";
+    }
+
+    /** @hide */
+    public static String flagsToString(int flags) {
+        return DebugUtils.flagsToString(UserInfo.class, "FLAG_", flags);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int parcelableFlags) {
+        dest.writeInt(id);
+        dest.writeString8(name);
+        dest.writeString8(iconPath);
+        dest.writeInt(flags);
+        dest.writeString8(userType);
+        dest.writeInt(serialNumber);
+        dest.writeLong(creationTime);
+        dest.writeLong(lastLoggedInTime);
+        dest.writeString8(lastLoggedInFingerprint);
+        dest.writeBoolean(partial);
+        dest.writeBoolean(preCreated);
+        dest.writeInt(profileGroupId);
+        dest.writeBoolean(guestToRemove);
+        dest.writeInt(restrictedProfileParentId);
+        dest.writeInt(profileBadge);
+    }
+
+    @UnsupportedAppUsage
+    public static final @android.annotation.NonNull Parcelable.Creator<UserInfo> CREATOR
+            = new Parcelable.Creator<UserInfo>() {
+        public UserInfo createFromParcel(Parcel source) {
+            return new UserInfo(source);
+        }
+        public UserInfo[] newArray(int size) {
+            return new UserInfo[size];
+        }
+    };
+
+    private UserInfo(Parcel source) {
+        id = source.readInt();
+        name = source.readString8();
+        iconPath = source.readString8();
+        flags = source.readInt();
+        userType = source.readString8();
+        serialNumber = source.readInt();
+        creationTime = source.readLong();
+        lastLoggedInTime = source.readLong();
+        lastLoggedInFingerprint = source.readString8();
+        partial = source.readBoolean();
+        preCreated = source.readBoolean();
+        profileGroupId = source.readInt();
+        guestToRemove = source.readBoolean();
+        restrictedProfileParentId = source.readInt();
+        profileBadge = source.readInt();
+    }
+}
diff --git a/android/content/pm/VerifierDeviceIdentity.java b/android/content/pm/VerifierDeviceIdentity.java
new file mode 100644
index 0000000..f29aaec
--- /dev/null
+++ b/android/content/pm/VerifierDeviceIdentity.java
@@ -0,0 +1,243 @@
+/*
+ * 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.content.pm;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.UnsupportedEncodingException;
+import java.security.SecureRandom;
+import java.util.Random;
+
+/**
+ * An identity that uniquely identifies a particular device. In this
+ * implementation, the identity is represented as a 64-bit integer encoded to a
+ * 13-character string using RFC 4648's Base32 encoding without the trailing
+ * padding. This makes it easy for users to read and write the code without
+ * confusing 'I' (letter) with '1' (one) or 'O' (letter) with '0' (zero).
+ *
+ * @hide
+ */
+public class VerifierDeviceIdentity implements Parcelable {
+    /**
+     * Encoded size of a long (64-bit) into Base32. This format will end up
+     * looking like XXXX-XXXX-XXXX-X (length ignores hyphens) when applied with
+     * the GROUP_SIZE below.
+     */
+    private static final int LONG_SIZE = 13;
+
+    /**
+     * Size of groupings when outputting as strings. This helps people read it
+     * out and keep track of where they are.
+     */
+    private static final int GROUP_SIZE = 4;
+
+    private final long mIdentity;
+
+    private final String mIdentityString;
+
+    /**
+     * Create a verifier device identity from a long.
+     *
+     * @param identity device identity in a 64-bit integer.
+     * @throws
+     */
+    public VerifierDeviceIdentity(long identity) {
+        mIdentity = identity;
+        mIdentityString = encodeBase32(identity);
+    }
+
+    private VerifierDeviceIdentity(Parcel source) {
+        final long identity = source.readLong();
+
+        mIdentity = identity;
+        mIdentityString = encodeBase32(identity);
+    }
+
+    /**
+     * Generate a new device identity.
+     *
+     * @return random uniformly-distributed device identity
+     */
+    public static VerifierDeviceIdentity generate() {
+        final SecureRandom sr = new SecureRandom();
+        return generate(sr);
+    }
+
+    /**
+     * Generate a new device identity using a provided random number generator
+     * class. This is used for testing.
+     *
+     * @param rng random number generator to retrieve the next long from
+     * @return verifier device identity based on the input from the provided
+     *         random number generator
+     */
+    @VisibleForTesting
+    static VerifierDeviceIdentity generate(Random rng) {
+        long identity = rng.nextLong();
+        return new VerifierDeviceIdentity(identity);
+    }
+
+    private static final char ENCODE[] = {
+        'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
+        'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
+        'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+        'Y', 'Z', '2', '3', '4', '5', '6', '7',
+    };
+
+    private static final char SEPARATOR = '-';
+
+    private static final String encodeBase32(long input) {
+        final char[] alphabet = ENCODE;
+
+        /*
+         * Make a character array with room for the separators between each
+         * group.
+         */
+        final char encoded[] = new char[LONG_SIZE + (LONG_SIZE / GROUP_SIZE)];
+
+        int index = encoded.length;
+        for (int i = 0; i < LONG_SIZE; i++) {
+            /*
+             * Make sure we don't put a separator at the beginning. Since we're
+             * building from the rear of the array, we use (LONG_SIZE %
+             * GROUP_SIZE) to make the odd-size group appear at the end instead
+             * of the beginning.
+             */
+            if (i > 0 && (i % GROUP_SIZE) == (LONG_SIZE % GROUP_SIZE)) {
+                encoded[--index] = SEPARATOR;
+            }
+
+            /*
+             * Extract 5 bits of data, then shift it out.
+             */
+            final int group = (int) (input & 0x1F);
+            input >>>= 5;
+
+            encoded[--index] = alphabet[group];
+        }
+
+        return String.valueOf(encoded);
+    }
+
+    // TODO move this out to its own class (android.util.Base32)
+    private static final long decodeBase32(byte[] input) throws IllegalArgumentException {
+        long output = 0L;
+        int numParsed = 0;
+
+        final int N = input.length;
+        for (int i = 0; i < N; i++) {
+            final int group = input[i];
+
+            /*
+             * This essentially does the reverse of the ENCODED alphabet above
+             * without a table. A..Z are 0..25 and 2..7 are 26..31.
+             */
+            final int value;
+            if ('A' <= group && group <= 'Z') {
+                value = group - 'A';
+            } else if ('2' <= group && group <= '7') {
+                value = group - ('2' - 26);
+            } else if (group == SEPARATOR) {
+                continue;
+            } else if ('a' <= group && group <= 'z') {
+                /* Lowercase letters should be the same as uppercase for Base32 */
+                value = group - 'a';
+            } else if (group == '0') {
+                /* Be nice to users that mistake O (letter) for 0 (zero) */
+                value = 'O' - 'A';
+            } else if (group == '1') {
+                /* Be nice to users that mistake I (letter) for 1 (one) */
+                value = 'I' - 'A';
+            } else {
+                throw new IllegalArgumentException("base base-32 character: " + group);
+            }
+
+            output = (output << 5) | value;
+            numParsed++;
+
+            if (numParsed == 1) {
+                if ((value & 0xF) != value) {
+                    throw new IllegalArgumentException("illegal start character; will overflow");
+                }
+            } else if (numParsed > 13) {
+                throw new IllegalArgumentException("too long; should have 13 characters");
+            }
+        }
+
+        if (numParsed != 13) {
+            throw new IllegalArgumentException("too short; should have 13 characters");
+        }
+
+        return output;
+    }
+
+    @Override
+    public int hashCode() {
+        return (int) mIdentity;
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (!(other instanceof VerifierDeviceIdentity)) {
+            return false;
+        }
+
+        final VerifierDeviceIdentity o = (VerifierDeviceIdentity) other;
+        return mIdentity == o.mIdentity;
+    }
+
+    @Override
+    public String toString() {
+        return mIdentityString;
+    }
+
+    public static VerifierDeviceIdentity parse(String deviceIdentity)
+            throws IllegalArgumentException {
+        final byte[] input;
+        try {
+            input = deviceIdentity.getBytes("US-ASCII");
+        } catch (UnsupportedEncodingException e) {
+            throw new IllegalArgumentException("bad base-32 characters in input");
+        }
+
+        return new VerifierDeviceIdentity(decodeBase32(input));
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeLong(mIdentity);
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<VerifierDeviceIdentity> CREATOR
+            = new Parcelable.Creator<VerifierDeviceIdentity>() {
+        public VerifierDeviceIdentity createFromParcel(Parcel source) {
+            return new VerifierDeviceIdentity(source);
+        }
+
+        public VerifierDeviceIdentity[] newArray(int size) {
+            return new VerifierDeviceIdentity[size];
+        }
+    };
+}
diff --git a/android/content/pm/VerifierInfo.java b/android/content/pm/VerifierInfo.java
new file mode 100644
index 0000000..5f88555
--- /dev/null
+++ b/android/content/pm/VerifierInfo.java
@@ -0,0 +1,85 @@
+/*
+ * 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.content.pm;
+
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.security.PublicKey;
+
+/**
+ * Contains information about a package verifier as used by
+ * {@code PackageManagerService} during package verification.
+ *
+ * @hide
+ */
+public class VerifierInfo implements Parcelable {
+    /** Package name of the verifier. */
+    public final String packageName;
+
+    /** Signatures used to sign the package verifier's package. */
+    public final PublicKey publicKey;
+
+    /**
+     * Creates an object that represents a verifier info object.
+     *
+     * @param packageName the package name in Java-style. Must not be {@code
+     *            null} or empty.
+     * @param publicKey the public key for the signer encoded in Base64. Must
+     *            not be {@code null} or empty.
+     * @throws IllegalArgumentException if either argument is null or empty.
+     */
+    @UnsupportedAppUsage
+    public VerifierInfo(String packageName, PublicKey publicKey) {
+        if (packageName == null || packageName.length() == 0) {
+            throw new IllegalArgumentException("packageName must not be null or empty");
+        } else if (publicKey == null) {
+            throw new IllegalArgumentException("publicKey must not be null");
+        }
+
+        this.packageName = packageName;
+        this.publicKey = publicKey;
+    }
+
+    private VerifierInfo(Parcel source) {
+        packageName = source.readString();
+        publicKey = (PublicKey) source.readSerializable();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(packageName);
+        dest.writeSerializable(publicKey);
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<VerifierInfo> CREATOR
+            = new Parcelable.Creator<VerifierInfo>() {
+        public VerifierInfo createFromParcel(Parcel source) {
+            return new VerifierInfo(source);
+        }
+
+        public VerifierInfo[] newArray(int size) {
+            return new VerifierInfo[size];
+        }
+    };
+}
\ No newline at end of file
diff --git a/android/content/pm/VersionedPackage.java b/android/content/pm/VersionedPackage.java
new file mode 100644
index 0000000..2987557
--- /dev/null
+++ b/android/content/pm/VersionedPackage.java
@@ -0,0 +1,134 @@
+/*
+ * 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.content.pm;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Encapsulates a package and its version code.
+ */
+public final class VersionedPackage implements Parcelable {
+    private final String mPackageName;
+    private final long mVersionCode;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntRange(from = PackageManager.VERSION_CODE_HIGHEST)
+    public @interface VersionCode{}
+
+    /**
+     * Creates a new instance. Use {@link PackageManager#VERSION_CODE_HIGHEST}
+     * to refer to the highest version code of this package.
+     * @param packageName The package name.
+     * @param versionCode The version code.
+     */
+    public VersionedPackage(@NonNull String packageName,
+            @VersionCode int versionCode) {
+        mPackageName = packageName;
+        mVersionCode = versionCode;
+    }
+
+    /**
+     * Creates a new instance. Use {@link PackageManager#VERSION_CODE_HIGHEST}
+     * to refer to the highest version code of this package.
+     * @param packageName The package name.
+     * @param versionCode The version code.
+     */
+    public VersionedPackage(@NonNull String packageName,
+            @VersionCode long versionCode) {
+        mPackageName = packageName;
+        mVersionCode = versionCode;
+    }
+
+    private VersionedPackage(Parcel parcel) {
+        mPackageName = parcel.readString8();
+        mVersionCode = parcel.readLong();
+    }
+
+    /**
+     * Gets the package name.
+     *
+     * @return The package name.
+     */
+    public @NonNull String getPackageName() {
+        return mPackageName;
+    }
+
+    /**
+     * @deprecated use {@link #getLongVersionCode()} instead.
+     */
+    @Deprecated
+    public @VersionCode int getVersionCode() {
+        return (int) (mVersionCode & 0x7fffffff);
+    }
+
+    /**
+     * Gets the version code.
+     *
+     * @return The version code.
+     */
+    public @VersionCode long getLongVersionCode() {
+        return mVersionCode;
+    }
+
+    @Override
+    public String toString() {
+        return "VersionedPackage[" + mPackageName + "/" + mVersionCode + "]";
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        return o instanceof VersionedPackage
+                && ((VersionedPackage) o).mPackageName.equals(mPackageName)
+                && ((VersionedPackage) o).mVersionCode == mVersionCode;
+    }
+
+    @Override
+    public int hashCode() {
+        // Roll our own hash function without using Objects#hash which incurs the overhead
+        // of autoboxing.
+        return 31 * mPackageName.hashCode() + Long.hashCode(mVersionCode);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeString8(mPackageName);
+        parcel.writeLong(mVersionCode);
+    }
+
+    public static final @android.annotation.NonNull Creator<VersionedPackage> CREATOR = new Creator<VersionedPackage>() {
+        @Override
+        public VersionedPackage createFromParcel(Parcel source) {
+            return new VersionedPackage(source);
+        }
+
+        @Override
+        public VersionedPackage[] newArray(int size) {
+            return new VersionedPackage[size];
+        }
+    };
+}
diff --git a/android/content/pm/XmlSerializerAndParser.java b/android/content/pm/XmlSerializerAndParser.java
new file mode 100644
index 0000000..5dce839
--- /dev/null
+++ b/android/content/pm/XmlSerializerAndParser.java
@@ -0,0 +1,33 @@
+/*
+ * 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.content.pm;
+
+import android.compat.annotation.UnsupportedAppUsage;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+
+/** @hide */
+public interface XmlSerializerAndParser<T> {
+    @UnsupportedAppUsage
+    void writeAsXml(T item, XmlSerializer out) throws IOException;
+    @UnsupportedAppUsage
+    T createFromXml(XmlPullParser parser) throws IOException, XmlPullParserException;
+}
diff --git a/android/content/pm/dex/ArtManager.java b/android/content/pm/dex/ArtManager.java
new file mode 100644
index 0000000..b0970f4
--- /dev/null
+++ b/android/content/pm/dex/ArtManager.java
@@ -0,0 +1,215 @@
+/**
+ * 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.content.pm.dex;
+
+import static android.Manifest.permission.PACKAGE_USAGE_STATS;
+import static android.Manifest.permission.READ_RUNTIME_PROFILES;
+
+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.Context;
+import android.os.Environment;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import java.io.File;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.Executor;
+
+/**
+ * Class for retrieving various kinds of information related to the runtime artifacts of
+ * packages that are currently installed on the device.
+ *
+ * @hide
+ */
+@SystemApi
+public class ArtManager {
+    private static final String TAG = "ArtManager";
+
+    /** The snapshot failed because the package was not found. */
+    public static final int SNAPSHOT_FAILED_PACKAGE_NOT_FOUND = 0;
+    /** The snapshot failed because the package code path does not exist. */
+    public static final int SNAPSHOT_FAILED_CODE_PATH_NOT_FOUND = 1;
+    /** The snapshot failed because of an internal error (e.g. error during opening profiles). */
+    public static final int SNAPSHOT_FAILED_INTERNAL_ERROR = 2;
+
+    /** Constant used for applications profiles. */
+    public static final int PROFILE_APPS = 0;
+    /** Constant used for the boot image profile. */
+    public static final int PROFILE_BOOT_IMAGE = 1;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "PROFILE_" }, value = {
+            PROFILE_APPS,
+            PROFILE_BOOT_IMAGE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ProfileType {}
+
+    private final Context mContext;
+    private final IArtManager mArtManager;
+
+    /**
+     * @hide
+     */
+    public ArtManager(@NonNull Context context, @NonNull IArtManager manager) {
+        mContext = context;
+        mArtManager = manager;
+    }
+
+    /**
+     * Snapshots a runtime profile according to the {@code profileType} parameter.
+     *
+     * If {@code profileType} is {@link ArtManager#PROFILE_APPS} the method will snapshot
+     * the profile for for an apk belonging to the package {@code packageName}.
+     * The apk is identified by {@code codePath}.
+     *
+     * If {@code profileType} is {@code ArtManager.PROFILE_BOOT_IMAGE} the method will snapshot
+     * the profile for the boot image. In this case {@code codePath can be null}. The parameters
+     * {@code packageName} and {@code codePath} are ignored.
+     *u
+     * The calling process must have {@code android.permission.READ_RUNTIME_PROFILE} permission.
+     *
+     * The result will be posted on the {@code executor} using the given {@code callback}.
+     * The profile will be available as a read-only {@link android.os.ParcelFileDescriptor}.
+     *
+     * This method will throw {@link IllegalStateException} if
+     * {@link ArtManager#isRuntimeProfilingEnabled(int)} does not return true for the given
+     * {@code profileType}.
+     *
+     * @param profileType the type of profile that should be snapshot (boot image or app)
+     * @param packageName the target package name or null if the target is the boot image
+     * @param codePath the code path for which the profile should be retrieved or null if
+     *                 the target is the boot image
+     * @param callback the callback which should be used for the result
+     * @param executor the executor which should be used to post the result
+     */
+    @RequiresPermission(allOf = { READ_RUNTIME_PROFILES, PACKAGE_USAGE_STATS })
+    public void snapshotRuntimeProfile(@ProfileType int profileType, @Nullable String packageName,
+            @Nullable String codePath, @NonNull @CallbackExecutor Executor executor,
+            @NonNull SnapshotRuntimeProfileCallback callback) {
+        Slog.d(TAG, "Requesting profile snapshot for " + packageName + ":" + codePath);
+
+        SnapshotRuntimeProfileCallbackDelegate delegate =
+                new SnapshotRuntimeProfileCallbackDelegate(callback, executor);
+        try {
+            mArtManager.snapshotRuntimeProfile(profileType, packageName, codePath, delegate,
+                    mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Returns true if runtime profiles are enabled for the given type, false otherwise.
+     *
+     * The calling process must have {@code android.permission.READ_RUNTIME_PROFILE} permission.
+     *
+     * @param profileType can be either {@link ArtManager#PROFILE_APPS}
+     *                    or {@link ArtManager#PROFILE_BOOT_IMAGE}
+     */
+    @RequiresPermission(allOf = { READ_RUNTIME_PROFILES, PACKAGE_USAGE_STATS })
+    public boolean isRuntimeProfilingEnabled(@ProfileType int profileType) {
+        try {
+            return mArtManager.isRuntimeProfilingEnabled(profileType, mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Callback used for retrieving runtime profiles.
+     */
+    public abstract static class SnapshotRuntimeProfileCallback {
+        /**
+         * Called when the profile snapshot finished with success.
+         *
+         * @param profileReadFd the file descriptor that can be used to read the profile. Note that
+         *                      the file might be empty (which is valid profile).
+         */
+        public abstract void onSuccess(ParcelFileDescriptor profileReadFd);
+
+        /**
+         * Called when the profile snapshot finished with an error.
+         *
+         * @param errCode the error code {@see SNAPSHOT_FAILED_PACKAGE_NOT_FOUND,
+         *      SNAPSHOT_FAILED_CODE_PATH_NOT_FOUND, SNAPSHOT_FAILED_INTERNAL_ERROR}.
+         */
+        public abstract void onError(int errCode);
+    }
+
+    private static class SnapshotRuntimeProfileCallbackDelegate
+            extends android.content.pm.dex.ISnapshotRuntimeProfileCallback.Stub {
+        private final ArtManager.SnapshotRuntimeProfileCallback mCallback;
+        private final Executor mExecutor;
+
+        private SnapshotRuntimeProfileCallbackDelegate(
+                ArtManager.SnapshotRuntimeProfileCallback callback, Executor executor) {
+            mCallback = callback;
+            mExecutor = executor;
+        }
+
+        @Override
+        public void onSuccess(final ParcelFileDescriptor profileReadFd) {
+            mExecutor.execute(() -> mCallback.onSuccess(profileReadFd));
+        }
+
+        @Override
+        public void onError(int errCode) {
+            mExecutor.execute(() -> mCallback.onError(errCode));
+        }
+    }
+
+    /**
+     * Return the profile name for the given split. If {@code splitName} is null the
+     * method returns the profile name for the base apk.
+     *
+     * @hide
+     */
+    public static String getProfileName(String splitName) {
+        return splitName == null ? "primary.prof" : splitName + ".split.prof";
+    }
+
+    /**
+     * Return the path to the current profile corresponding to given package and split.
+     *
+     * @hide
+     */
+    public static String getCurrentProfilePath(String packageName, int userId, String splitName) {
+        File profileDir = Environment.getDataProfilesDePackageDirectory(userId, packageName);
+        return new File(profileDir, getProfileName(splitName)).getAbsolutePath();
+    }
+
+    /**
+     * Return the snapshot profile file for the given package and profile name.
+     *
+     * KEEP in sync with installd dexopt.cpp.
+     * TODO(calin): inject the snapshot profile name from PM to avoid the dependency.
+     *
+     * @hide
+     */
+    public static File getProfileSnapshotFileForName(String packageName, String profileName) {
+        File profileDir = Environment.getDataRefProfilesDePackageDirectory(packageName);
+        return new File(profileDir, profileName  + ".snapshot");
+    }
+}
diff --git a/android/content/pm/dex/ArtManagerInternal.java b/android/content/pm/dex/ArtManagerInternal.java
new file mode 100644
index 0000000..62ab9e0
--- /dev/null
+++ b/android/content/pm/dex/ArtManagerInternal.java
@@ -0,0 +1,34 @@
+/**
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.dex;
+
+import android.content.pm.ApplicationInfo;
+
+/**
+ * Art manager local system service interface.
+ *
+ * @hide Only for use within the system server.
+ */
+public abstract class ArtManagerInternal {
+
+    /**
+     * Return optimization information about the application {@code info} when
+     * in executes using the specified {@code abi}.
+     */
+    public abstract PackageOptimizationInfo getPackageOptimizationInfo(
+            ApplicationInfo info, String abi);
+}
diff --git a/android/content/pm/dex/DexMetadataHelper.java b/android/content/pm/dex/DexMetadataHelper.java
new file mode 100644
index 0000000..cdb1d22
--- /dev/null
+++ b/android/content/pm/dex/DexMetadataHelper.java
@@ -0,0 +1,207 @@
+/**
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.dex;
+
+import static android.content.pm.PackageManager.INSTALL_FAILED_BAD_DEX_METADATA;
+import static android.content.pm.PackageParser.APK_FILE_EXTENSION;
+
+import android.content.pm.PackageParser;
+import android.content.pm.PackageParser.PackageLite;
+import android.content.pm.PackageParser.PackageParserException;
+import android.util.ArrayMap;
+import android.util.jar.StrictJarFile;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Helper class used to compute and validate the location of dex metadata files.
+ *
+ * @hide
+ */
+public class DexMetadataHelper {
+    private static final String DEX_METADATA_FILE_EXTENSION = ".dm";
+
+    private DexMetadataHelper() {}
+
+    /** Return true if the given file is a dex metadata file. */
+    public static boolean isDexMetadataFile(File file) {
+        return isDexMetadataPath(file.getName());
+    }
+
+    /** Return true if the given path is a dex metadata path. */
+    private static boolean isDexMetadataPath(String path) {
+        return path.endsWith(DEX_METADATA_FILE_EXTENSION);
+    }
+
+    /**
+     * Return the size (in bytes) of all dex metadata files associated with the given package.
+     */
+    public static long getPackageDexMetadataSize(PackageLite pkg) {
+        long sizeBytes = 0;
+        Collection<String> dexMetadataList = DexMetadataHelper.getPackageDexMetadata(pkg).values();
+        for (String dexMetadata : dexMetadataList) {
+            sizeBytes += new File(dexMetadata).length();
+        }
+        return sizeBytes;
+    }
+
+    /**
+     * Search for the dex metadata file associated with the given target file.
+     * If it exists, the method returns the dex metadata file; otherwise it returns null.
+     *
+     * Note that this performs a loose matching suitable to be used in the InstallerSession logic.
+     * i.e. the method will attempt to match the {@code dmFile} regardless of {@code targetFile}
+     * extension (e.g. 'foo.dm' will match 'foo' or 'foo.apk').
+     */
+    public static File findDexMetadataForFile(File targetFile) {
+        String dexMetadataPath = buildDexMetadataPathForFile(targetFile);
+        File dexMetadataFile = new File(dexMetadataPath);
+        return dexMetadataFile.exists() ? dexMetadataFile : null;
+    }
+
+    /**
+     * Return the dex metadata files for the given package as a map
+     * [code path -> dex metadata path].
+     *
+     * NOTE: involves I/O checks.
+     */
+    private static Map<String, String> getPackageDexMetadata(PackageLite pkg) {
+        return buildPackageApkToDexMetadataMap(pkg.getAllCodePaths());
+    }
+
+    /**
+     * Look up the dex metadata files for the given code paths building the map
+     * [code path -> dex metadata].
+     *
+     * For each code path (.apk) the method checks if a matching dex metadata file (.dm) exists.
+     * If it does it adds the pair to the returned map.
+     *
+     * Note that this method will do a loose
+     * matching based on the extension ('foo.dm' will match 'foo.apk' or 'foo').
+     *
+     * This should only be used for code paths extracted from a package structure after the naming
+     * was enforced in the installer.
+     */
+    public static Map<String, String> buildPackageApkToDexMetadataMap(
+            List<String> codePaths) {
+        ArrayMap<String, String> result = new ArrayMap<>();
+        for (int i = codePaths.size() - 1; i >= 0; i--) {
+            String codePath = codePaths.get(i);
+            String dexMetadataPath = buildDexMetadataPathForFile(new File(codePath));
+
+            if (Files.exists(Paths.get(dexMetadataPath))) {
+                result.put(codePath, dexMetadataPath);
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * Return the dex metadata path associated with the given code path.
+     * (replaces '.apk' extension with '.dm')
+     *
+     * @throws IllegalArgumentException if the code path is not an .apk.
+     */
+    public static String buildDexMetadataPathForApk(String codePath) {
+        if (!PackageParser.isApkPath(codePath)) {
+            throw new IllegalStateException(
+                    "Corrupted package. Code path is not an apk " + codePath);
+        }
+        return codePath.substring(0, codePath.length() - APK_FILE_EXTENSION.length())
+                + DEX_METADATA_FILE_EXTENSION;
+    }
+
+    /**
+     * Return the dex metadata path corresponding to the given {@code targetFile} using a loose
+     * matching.
+     * i.e. the method will attempt to match the {@code dmFile} regardless of {@code targetFile}
+     * extension (e.g. 'foo.dm' will match 'foo' or 'foo.apk').
+     */
+    private static String buildDexMetadataPathForFile(File targetFile) {
+        return PackageParser.isApkFile(targetFile)
+                ? buildDexMetadataPathForApk(targetFile.getPath())
+                : targetFile.getPath() + DEX_METADATA_FILE_EXTENSION;
+    }
+
+    /**
+     * Validate that the given file is a dex metadata archive.
+     * This is just a sanity validation that the file is a zip archive.
+     *
+     * @throws PackageParserException if the file is not a .dm file.
+     */
+    public static void validateDexMetadataFile(String dmaPath) throws PackageParserException {
+        StrictJarFile jarFile = null;
+        try {
+            jarFile = new StrictJarFile(dmaPath, false, false);
+        } catch (IOException e) {
+            throw new PackageParserException(INSTALL_FAILED_BAD_DEX_METADATA,
+                    "Error opening " + dmaPath, e);
+        } finally {
+            if (jarFile != null) {
+                try {
+                    jarFile.close();
+                } catch (IOException ignored) {
+                }
+            }
+        }
+    }
+
+    /**
+     * Validates that all dex metadata paths in the given list have a matching apk.
+     * (for any foo.dm there should be either a 'foo' of a 'foo.apk' file).
+     * If that's not the case it throws {@code IllegalStateException}.
+     *
+     * This is used to perform a basic sanity check during adb install commands.
+     * (The installer does not support stand alone .dm files)
+     */
+    public static void validateDexPaths(String[] paths) {
+        ArrayList<String> apks = new ArrayList<>();
+        for (int i = 0; i < paths.length; i++) {
+            if (PackageParser.isApkPath(paths[i])) {
+                apks.add(paths[i]);
+            }
+        }
+        ArrayList<String> unmatchedDmFiles = new ArrayList<>();
+        for (int i = 0; i < paths.length; i++) {
+            String dmPath = paths[i];
+            if (isDexMetadataPath(dmPath)) {
+                boolean valid = false;
+                for (int j = apks.size() - 1; j >= 0; j--) {
+                    if (dmPath.equals(buildDexMetadataPathForFile(new File(apks.get(j))))) {
+                        valid = true;
+                        break;
+                    }
+                }
+                if (!valid) {
+                    unmatchedDmFiles.add(dmPath);
+                }
+            }
+        }
+        if (!unmatchedDmFiles.isEmpty()) {
+            throw new IllegalStateException("Unmatched .dm files: " + unmatchedDmFiles);
+        }
+    }
+
+}
diff --git a/android/content/pm/dex/PackageOptimizationInfo.java b/android/content/pm/dex/PackageOptimizationInfo.java
new file mode 100644
index 0000000..7e7d29e
--- /dev/null
+++ b/android/content/pm/dex/PackageOptimizationInfo.java
@@ -0,0 +1,47 @@
+/**
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.dex;
+
+/**
+ * Encapsulates information about the optimizations performed on a package.
+ *
+ * @hide
+ */
+public class PackageOptimizationInfo {
+    private final int mCompilationFilter;
+    private final int mCompilationReason;
+
+    public PackageOptimizationInfo(int compilerFilter, int compilationReason) {
+        this.mCompilationReason = compilationReason;
+        this.mCompilationFilter = compilerFilter;
+    }
+
+    public int getCompilationReason() {
+        return mCompilationReason;
+    }
+
+    public int getCompilationFilter() {
+        return mCompilationFilter;
+    }
+
+    /**
+     * Create a default optimization info object for the case when we have no information.
+     */
+    public static PackageOptimizationInfo createWithNoInfo() {
+        return new PackageOptimizationInfo(-1, -1);
+    }
+}
diff --git a/android/content/pm/parsing/ApkLiteParseUtils.java b/android/content/pm/parsing/ApkLiteParseUtils.java
new file mode 100644
index 0000000..2f416a2
--- /dev/null
+++ b/android/content/pm/parsing/ApkLiteParseUtils.java
@@ -0,0 +1,465 @@
+/*
+ * 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.content.pm.parsing;
+
+import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
+
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageParser;
+import android.content.pm.VerifierInfo;
+import android.content.res.ApkAssets;
+import android.content.res.XmlResourceParser;
+import android.os.Trace;
+import android.util.ArrayMap;
+import android.util.AttributeSet;
+import android.util.Pair;
+import android.util.Slog;
+
+import com.android.internal.R;
+import com.android.internal.util.ArrayUtils;
+
+import libcore.io.IoUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.security.PublicKey;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/** @hide */
+public class ApkLiteParseUtils {
+
+    private static final String TAG = ParsingPackageUtils.TAG;
+
+    // TODO(b/135203078): Consolidate constants
+    private static final int DEFAULT_MIN_SDK_VERSION = 1;
+    private static final int DEFAULT_TARGET_SDK_VERSION = 0;
+
+    private static final int PARSE_DEFAULT_INSTALL_LOCATION =
+            PackageInfo.INSTALL_LOCATION_UNSPECIFIED;
+
+    /**
+     * Parse only lightweight details about the package at the given location.
+     * Automatically detects if the package is a monolithic style (single APK
+     * file) or cluster style (directory of APKs).
+     * <p>
+     * This performs sanity checking on cluster style packages, such as
+     * requiring identical package name and version codes, a single base APK,
+     * and unique split names.
+     *
+     * @see PackageParser#parsePackage(File, int)
+     */
+    @UnsupportedAppUsage
+    public static PackageParser.PackageLite parsePackageLite(File packageFile, int flags)
+            throws PackageParser.PackageParserException {
+        if (packageFile.isDirectory()) {
+            return parseClusterPackageLite(packageFile, flags);
+        } else {
+            return parseMonolithicPackageLite(packageFile, flags);
+        }
+    }
+
+    public static PackageParser.PackageLite parseMonolithicPackageLite(File packageFile, int flags)
+            throws PackageParser.PackageParserException {
+        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parseApkLite");
+        final PackageParser.ApkLite baseApk = parseApkLite(packageFile, flags);
+        final String packagePath = packageFile.getAbsolutePath();
+        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+        return new PackageParser.PackageLite(packagePath, baseApk, null, null, null, null,
+                null, null);
+    }
+
+    public static PackageParser.PackageLite parseClusterPackageLite(File packageDir, int flags)
+            throws PackageParser.PackageParserException {
+        final File[] files = packageDir.listFiles();
+        if (ArrayUtils.isEmpty(files)) {
+            throw new PackageParser.PackageParserException(
+                    PackageManager.INSTALL_PARSE_FAILED_NOT_APK, "No packages found in split");
+        }
+        // Apk directory is directly nested under the current directory
+        if (files.length == 1 && files[0].isDirectory()) {
+            return parseClusterPackageLite(files[0], flags);
+        }
+
+        String packageName = null;
+        int versionCode = 0;
+
+        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parseApkLite");
+        final ArrayMap<String, PackageParser.ApkLite> apks = new ArrayMap<>();
+        for (File file : files) {
+            if (PackageParser.isApkFile(file)) {
+                final PackageParser.ApkLite lite = parseApkLite(file, flags);
+
+                // Assert that all package names and version codes are
+                // consistent with the first one we encounter.
+                if (packageName == null) {
+                    packageName = lite.packageName;
+                    versionCode = lite.versionCode;
+                } else {
+                    if (!packageName.equals(lite.packageName)) {
+                        throw new PackageParser.PackageParserException(
+                                PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST,
+                                "Inconsistent package " + lite.packageName + " in " + file
+                                        + "; expected " + packageName);
+                    }
+                    if (versionCode != lite.versionCode) {
+                        throw new PackageParser.PackageParserException(
+                                PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST,
+                                "Inconsistent version " + lite.versionCode + " in " + file
+                                        + "; expected " + versionCode);
+                    }
+                }
+
+                // Assert that each split is defined only oncuses-static-libe
+                if (apks.put(lite.splitName, lite) != null) {
+                    throw new PackageParser.PackageParserException(
+                            PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST,
+                            "Split name " + lite.splitName
+                                    + " defined more than once; most recent was " + file);
+                }
+            }
+        }
+        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+
+        final PackageParser.ApkLite baseApk = apks.remove(null);
+        if (baseApk == null) {
+            throw new PackageParser.PackageParserException(
+                    PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST,
+                    "Missing base APK in " + packageDir);
+        }
+
+        // Always apply deterministic ordering based on splitName
+        final int size = apks.size();
+
+        String[] splitNames = null;
+        boolean[] isFeatureSplits = null;
+        String[] usesSplitNames = null;
+        String[] configForSplits = null;
+        String[] splitCodePaths = null;
+        int[] splitRevisionCodes = null;
+        if (size > 0) {
+            splitNames = new String[size];
+            isFeatureSplits = new boolean[size];
+            usesSplitNames = new String[size];
+            configForSplits = new String[size];
+            splitCodePaths = new String[size];
+            splitRevisionCodes = new int[size];
+
+            splitNames = apks.keySet().toArray(splitNames);
+            Arrays.sort(splitNames, PackageParser.sSplitNameComparator);
+
+            for (int i = 0; i < size; i++) {
+                final PackageParser.ApkLite apk = apks.get(splitNames[i]);
+                usesSplitNames[i] = apk.usesSplitName;
+                isFeatureSplits[i] = apk.isFeatureSplit;
+                configForSplits[i] = apk.configForSplit;
+                splitCodePaths[i] = apk.codePath;
+                splitRevisionCodes[i] = apk.revisionCode;
+            }
+        }
+
+        final String codePath = packageDir.getAbsolutePath();
+        return new PackageParser.PackageLite(codePath, baseApk, splitNames, isFeatureSplits,
+                usesSplitNames, configForSplits, splitCodePaths, splitRevisionCodes);
+    }
+
+    /**
+     * Utility method that retrieves lightweight details about a single APK
+     * file, including package name, split name, and install location.
+     *
+     * @param apkFile path to a single APK
+     * @param flags optional parse flags, such as
+     *            {@link PackageParser#PARSE_COLLECT_CERTIFICATES}
+     */
+    public static PackageParser.ApkLite parseApkLite(File apkFile, int flags)
+            throws PackageParser.PackageParserException {
+        return parseApkLiteInner(apkFile, null, null, flags);
+    }
+
+    /**
+     * Utility method that retrieves lightweight details about a single APK
+     * file, including package name, split name, and install location.
+     *
+     * @param fd already open file descriptor of an apk file
+     * @param debugPathName arbitrary text name for this file, for debug output
+     * @param flags optional parse flags, such as
+     *            {@link PackageParser#PARSE_COLLECT_CERTIFICATES}
+     */
+    public static PackageParser.ApkLite parseApkLite(FileDescriptor fd, String debugPathName,
+            int flags) throws PackageParser.PackageParserException {
+        return parseApkLiteInner(null, fd, debugPathName, flags);
+    }
+
+    private static PackageParser.ApkLite parseApkLiteInner(File apkFile, FileDescriptor fd,
+            String debugPathName, int flags) throws PackageParser.PackageParserException {
+        final String apkPath = fd != null ? debugPathName : apkFile.getAbsolutePath();
+
+        XmlResourceParser parser = null;
+        ApkAssets apkAssets = null;
+        try {
+            try {
+                apkAssets = fd != null
+                        ? ApkAssets.loadFromFd(fd, debugPathName, 0 /* flags */, null /* assets */)
+                        : ApkAssets.loadFromPath(apkPath);
+            } catch (IOException e) {
+                throw new PackageParser.PackageParserException(
+                        PackageManager.INSTALL_PARSE_FAILED_NOT_APK,
+                        "Failed to parse " + apkPath, e);
+            }
+
+            parser = apkAssets.openXml(PackageParser.ANDROID_MANIFEST_FILENAME);
+
+            final PackageParser.SigningDetails signingDetails;
+            if ((flags & PackageParser.PARSE_COLLECT_CERTIFICATES) != 0) {
+                final boolean skipVerify = (flags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0;
+                Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
+                try {
+                    signingDetails = ParsingPackageUtils.collectCertificates(apkFile.getAbsolutePath(),
+                            skipVerify, false, PackageParser.SigningDetails.UNKNOWN,
+                            DEFAULT_TARGET_SDK_VERSION);
+                } finally {
+                    Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+                }
+            } else {
+                signingDetails = PackageParser.SigningDetails.UNKNOWN;
+            }
+
+            final AttributeSet attrs = parser;
+            return parseApkLite(apkPath, parser, attrs, signingDetails);
+
+        } catch (XmlPullParserException | IOException | RuntimeException e) {
+            Slog.w(TAG, "Failed to parse " + apkPath, e);
+            throw new PackageParser.PackageParserException(
+                    PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
+                    "Failed to parse " + apkPath, e);
+        } finally {
+            IoUtils.closeQuietly(parser);
+            if (apkAssets != null) {
+                try {
+                    apkAssets.close();
+                } catch (Throwable ignored) {
+                }
+            }
+            // TODO(b/72056911): Implement AutoCloseable on ApkAssets.
+        }
+    }
+
+    private static PackageParser.ApkLite parseApkLite(
+            String codePath, XmlPullParser parser, AttributeSet attrs,
+            PackageParser.SigningDetails signingDetails)
+            throws IOException, XmlPullParserException, PackageParser.PackageParserException {
+        final Pair<String, String> packageSplit = PackageParser.parsePackageSplitNames(
+                parser, attrs);
+
+        int installLocation = PARSE_DEFAULT_INSTALL_LOCATION;
+        int versionCode = 0;
+        int versionCodeMajor = 0;
+        int targetSdkVersion = DEFAULT_TARGET_SDK_VERSION;
+        int minSdkVersion = DEFAULT_MIN_SDK_VERSION;
+        int revisionCode = 0;
+        boolean coreApp = false;
+        boolean debuggable = false;
+        boolean multiArch = false;
+        boolean use32bitAbi = false;
+        boolean extractNativeLibs = true;
+        boolean isolatedSplits = false;
+        boolean isFeatureSplit = false;
+        boolean isSplitRequired = false;
+        boolean useEmbeddedDex = false;
+        String configForSplit = null;
+        String usesSplitName = null;
+        String targetPackage = null;
+        boolean overlayIsStatic = false;
+        int overlayPriority = 0;
+
+        String requiredSystemPropertyName = null;
+        String requiredSystemPropertyValue = null;
+
+        for (int i = 0; i < attrs.getAttributeCount(); i++) {
+            final String attr = attrs.getAttributeName(i);
+            switch (attr) {
+                case "installLocation":
+                    installLocation = attrs.getAttributeIntValue(i,
+                            PARSE_DEFAULT_INSTALL_LOCATION);
+                    break;
+                case "versionCode":
+                    versionCode = attrs.getAttributeIntValue(i, 0);
+                    break;
+                case "versionCodeMajor":
+                    versionCodeMajor = attrs.getAttributeIntValue(i, 0);
+                    break;
+                case "revisionCode":
+                    revisionCode = attrs.getAttributeIntValue(i, 0);
+                    break;
+                case "coreApp":
+                    coreApp = attrs.getAttributeBooleanValue(i, false);
+                    break;
+                case "isolatedSplits":
+                    isolatedSplits = attrs.getAttributeBooleanValue(i, false);
+                    break;
+                case "configForSplit":
+                    configForSplit = attrs.getAttributeValue(i);
+                    break;
+                case "isFeatureSplit":
+                    isFeatureSplit = attrs.getAttributeBooleanValue(i, false);
+                    break;
+                case "isSplitRequired":
+                    isSplitRequired = attrs.getAttributeBooleanValue(i, false);
+                    break;
+            }
+        }
+
+        // Only search the tree when the tag is the direct child of <manifest> tag
+        int type;
+        final int searchDepth = parser.getDepth() + 1;
+
+        final List<VerifierInfo> verifiers = new ArrayList<>();
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() >= searchDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+
+            if (parser.getDepth() != searchDepth) {
+                continue;
+            }
+
+            if (PackageParser.TAG_PACKAGE_VERIFIER.equals(parser.getName())) {
+                final VerifierInfo verifier = parseVerifier(attrs);
+                if (verifier != null) {
+                    verifiers.add(verifier);
+                }
+            } else if (PackageParser.TAG_APPLICATION.equals(parser.getName())) {
+                for (int i = 0; i < attrs.getAttributeCount(); ++i) {
+                    final String attr = attrs.getAttributeName(i);
+                    switch (attr) {
+                        case "debuggable":
+                            debuggable = attrs.getAttributeBooleanValue(i, false);
+                            break;
+                        case "multiArch":
+                            multiArch = attrs.getAttributeBooleanValue(i, false);
+                            break;
+                        case "use32bitAbi":
+                            use32bitAbi = attrs.getAttributeBooleanValue(i, false);
+                            break;
+                        case "extractNativeLibs":
+                            extractNativeLibs = attrs.getAttributeBooleanValue(i, true);
+                            break;
+                        case "useEmbeddedDex":
+                            useEmbeddedDex = attrs.getAttributeBooleanValue(i, false);
+                            break;
+                    }
+                }
+            } else if (PackageParser.TAG_OVERLAY.equals(parser.getName())) {
+                for (int i = 0; i < attrs.getAttributeCount(); ++i) {
+                    final String attr = attrs.getAttributeName(i);
+                    if ("requiredSystemPropertyName".equals(attr)) {
+                        requiredSystemPropertyName = attrs.getAttributeValue(i);
+                    } else if ("requiredSystemPropertyValue".equals(attr)) {
+                        requiredSystemPropertyValue = attrs.getAttributeValue(i);
+                    } else if ("targetPackage".equals(attr)) {
+                        targetPackage = attrs.getAttributeValue(i);;
+                    } else if ("isStatic".equals(attr)) {
+                        overlayIsStatic = attrs.getAttributeBooleanValue(i, false);
+                    } else if ("priority".equals(attr)) {
+                        overlayPriority = attrs.getAttributeIntValue(i, 0);
+                    }
+                }
+            } else if (PackageParser.TAG_USES_SPLIT.equals(parser.getName())) {
+                if (usesSplitName != null) {
+                    Slog.w(TAG, "Only one <uses-split> permitted. Ignoring others.");
+                    continue;
+                }
+
+                usesSplitName = attrs.getAttributeValue(PackageParser.ANDROID_RESOURCES, "name");
+                if (usesSplitName == null) {
+                    throw new PackageParser.PackageParserException(
+                            PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                            "<uses-split> tag requires 'android:name' attribute");
+                }
+            } else if (PackageParser.TAG_USES_SDK.equals(parser.getName())) {
+                for (int i = 0; i < attrs.getAttributeCount(); ++i) {
+                    final String attr = attrs.getAttributeName(i);
+                    if ("targetSdkVersion".equals(attr)) {
+                        targetSdkVersion = attrs.getAttributeIntValue(i,
+                                DEFAULT_TARGET_SDK_VERSION);
+                    }
+                    if ("minSdkVersion".equals(attr)) {
+                        minSdkVersion = attrs.getAttributeIntValue(i, DEFAULT_MIN_SDK_VERSION);
+                    }
+                }
+            }
+        }
+
+        // Check to see if overlay should be excluded based on system property condition
+        if (!PackageParser.checkRequiredSystemProperties(requiredSystemPropertyName,
+                requiredSystemPropertyValue)) {
+            Slog.i(TAG, "Skipping target and overlay pair " + targetPackage + " and "
+                    + codePath + ": overlay ignored due to required system property: "
+                    + requiredSystemPropertyName + " with value: " + requiredSystemPropertyValue);
+            targetPackage = null;
+            overlayIsStatic = false;
+            overlayPriority = 0;
+        }
+
+        return new PackageParser.ApkLite(codePath, packageSplit.first, packageSplit.second,
+                isFeatureSplit, configForSplit, usesSplitName, isSplitRequired, versionCode,
+                versionCodeMajor, revisionCode, installLocation, verifiers, signingDetails,
+                coreApp, debuggable, multiArch, use32bitAbi, useEmbeddedDex, extractNativeLibs,
+                isolatedSplits, targetPackage, overlayIsStatic, overlayPriority, minSdkVersion,
+                targetSdkVersion);
+    }
+
+    public static VerifierInfo parseVerifier(AttributeSet attrs) {
+        String packageName = null;
+        String encodedPublicKey = null;
+
+        final int attrCount = attrs.getAttributeCount();
+        for (int i = 0; i < attrCount; i++) {
+            final int attrResId = attrs.getAttributeNameResource(i);
+            switch (attrResId) {
+                case R.attr.name:
+                    packageName = attrs.getAttributeValue(i);
+                    break;
+
+                case R.attr.publicKey:
+                    encodedPublicKey = attrs.getAttributeValue(i);
+                    break;
+            }
+        }
+
+        if (packageName == null || packageName.length() == 0) {
+            Slog.i(TAG, "verifier package name was null; skipping");
+            return null;
+        }
+
+        final PublicKey publicKey = PackageParser.parsePublicKey(encodedPublicKey);
+        if (publicKey == null) {
+            Slog.i(TAG, "Unable to parse verifier public key for " + packageName);
+            return null;
+        }
+
+        return new VerifierInfo(packageName, publicKey);
+    }
+}
diff --git a/android/content/pm/parsing/PackageInfoWithoutStateUtils.java b/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
new file mode 100644
index 0000000..216b3bb
--- /dev/null
+++ b/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
@@ -0,0 +1,772 @@
+/*
+ * 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.content.pm.parsing;
+
+import android.annotation.CheckResult;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.apex.ApexInfo;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ComponentInfo;
+import android.content.pm.ConfigurationInfo;
+import android.content.pm.FallbackCategoryProvider;
+import android.content.pm.FeatureGroupInfo;
+import android.content.pm.FeatureInfo;
+import android.content.pm.InstrumentationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageItemInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageParser;
+import android.content.pm.PackageUserState;
+import android.content.pm.PermissionGroupInfo;
+import android.content.pm.PermissionInfo;
+import android.content.pm.ProviderInfo;
+import android.content.pm.SELinuxUtil;
+import android.content.pm.ServiceInfo;
+import android.content.pm.Signature;
+import android.content.pm.SigningInfo;
+import android.content.pm.parsing.component.ComponentParseUtils;
+import android.content.pm.parsing.component.ParsedActivity;
+import android.content.pm.parsing.component.ParsedComponent;
+import android.content.pm.parsing.component.ParsedInstrumentation;
+import android.content.pm.parsing.component.ParsedMainComponent;
+import android.content.pm.parsing.component.ParsedPermission;
+import android.content.pm.parsing.component.ParsedPermissionGroup;
+import android.content.pm.parsing.component.ParsedProvider;
+import android.content.pm.parsing.component.ParsedService;
+import android.os.Environment;
+import android.os.UserHandle;
+
+import com.android.internal.util.ArrayUtils;
+
+import libcore.util.EmptyArray;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.Set;
+
+/** @hide **/
+public class PackageInfoWithoutStateUtils {
+
+    @Nullable
+    public static PackageInfo generate(ParsingPackageRead pkg, int[] gids,
+            @PackageManager.PackageInfoFlags int flags, long firstInstallTime, long lastUpdateTime,
+            Set<String> grantedPermissions, PackageUserState state, int userId) {
+        return generateWithComponents(pkg, gids, flags, firstInstallTime, lastUpdateTime, grantedPermissions,
+                state, userId, null);
+    }
+
+    @Nullable
+    public static PackageInfo generate(ParsingPackageRead pkg, ApexInfo apexInfo, int flags) {
+        return generateWithComponents(pkg, EmptyArray.INT, flags, 0, 0, Collections.emptySet(),
+                new PackageUserState(), UserHandle.getCallingUserId(), apexInfo);
+    }
+
+    @Nullable
+    private static PackageInfo generateWithComponents(ParsingPackageRead pkg, int[] gids,
+            @PackageManager.PackageInfoFlags int flags, long firstInstallTime, long lastUpdateTime,
+            Set<String> grantedPermissions, PackageUserState state, int userId,
+            @Nullable ApexInfo apexInfo) {
+        ApplicationInfo applicationInfo = generateApplicationInfo(pkg, flags, state, userId);
+        if (applicationInfo == null) {
+            return null;
+        }
+        PackageInfo info = generateWithoutComponents(pkg, gids, flags, firstInstallTime,
+                lastUpdateTime, grantedPermissions, state, userId, apexInfo, applicationInfo);
+
+        if (info == null) {
+            return null;
+        }
+
+        if ((flags & PackageManager.GET_ACTIVITIES) != 0) {
+            final int N = pkg.getActivities().size();
+            if (N > 0) {
+                int num = 0;
+                final ActivityInfo[] res = new ActivityInfo[N];
+                for (int i = 0; i < N; i++) {
+                    final ParsedActivity a = pkg.getActivities().get(i);
+                    if (ComponentParseUtils.isMatch(state, false, pkg.isEnabled(), a,
+                            flags)) {
+                        if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(
+                                a.getName())) {
+                            continue;
+                        }
+                        res[num++] = generateActivityInfo(pkg, a, flags, state,
+                                applicationInfo, userId);
+                    }
+                }
+                info.activities = ArrayUtils.trimToSize(res, num);
+            }
+        }
+        if ((flags & PackageManager.GET_RECEIVERS) != 0) {
+            final int size = pkg.getReceivers().size();
+            if (size > 0) {
+                int num = 0;
+                final ActivityInfo[] res = new ActivityInfo[size];
+                for (int i = 0; i < size; i++) {
+                    final ParsedActivity a = pkg.getReceivers().get(i);
+                    if (ComponentParseUtils.isMatch(state, false, pkg.isEnabled(), a,
+                            flags)) {
+                        res[num++] = generateActivityInfo(pkg, a, flags, state,
+                                applicationInfo, userId);
+                    }
+                }
+                info.receivers = ArrayUtils.trimToSize(res, num);
+            }
+        }
+        if ((flags & PackageManager.GET_SERVICES) != 0) {
+            final int size = pkg.getServices().size();
+            if (size > 0) {
+                int num = 0;
+                final ServiceInfo[] res = new ServiceInfo[size];
+                for (int i = 0; i < size; i++) {
+                    final ParsedService s = pkg.getServices().get(i);
+                    if (ComponentParseUtils.isMatch(state, false, pkg.isEnabled(), s,
+                            flags)) {
+                        res[num++] = generateServiceInfo(pkg, s, flags, state,
+                                applicationInfo, userId);
+                    }
+                }
+                info.services = ArrayUtils.trimToSize(res, num);
+            }
+        }
+        if ((flags & PackageManager.GET_PROVIDERS) != 0) {
+            final int size = pkg.getProviders().size();
+            if (size > 0) {
+                int num = 0;
+                final ProviderInfo[] res = new ProviderInfo[size];
+                for (int i = 0; i < size; i++) {
+                    final ParsedProvider pr = pkg.getProviders()
+                            .get(i);
+                    if (ComponentParseUtils.isMatch(state, false, pkg.isEnabled(), pr,
+                            flags)) {
+                        res[num++] = generateProviderInfo(pkg, pr, flags, state,
+                                applicationInfo, userId);
+                    }
+                }
+                info.providers = ArrayUtils.trimToSize(res, num);
+            }
+        }
+        if ((flags & PackageManager.GET_INSTRUMENTATION) != 0) {
+            int N = pkg.getInstrumentations().size();
+            if (N > 0) {
+                info.instrumentation = new InstrumentationInfo[N];
+                for (int i = 0; i < N; i++) {
+                    info.instrumentation[i] = generateInstrumentationInfo(
+                            pkg.getInstrumentations().get(i), pkg, flags, userId);
+                }
+            }
+        }
+
+        return info;
+    }
+
+    @Nullable
+    public static PackageInfo generateWithoutComponents(ParsingPackageRead pkg, int[] gids,
+            @PackageManager.PackageInfoFlags int flags, long firstInstallTime, long lastUpdateTime,
+            Set<String> grantedPermissions, PackageUserState state, int userId,
+            @Nullable ApexInfo apexInfo, @NonNull ApplicationInfo applicationInfo) {
+        if (!checkUseInstalled(pkg, state, flags)) {
+            return null;
+        }
+
+        return generateWithoutComponentsUnchecked(pkg, gids, flags, firstInstallTime,
+                lastUpdateTime, grantedPermissions, state, userId, apexInfo, applicationInfo);
+    }
+
+    /**
+     * This bypasses critical checks that are necessary for usage with data passed outside of
+     * system server.
+     *
+     * Prefer {@link #generateWithoutComponents(ParsingPackageRead, int[], int, long, long, Set,
+     * PackageUserState, int, ApexInfo, ApplicationInfo)}.
+     */
+    @NonNull
+    public static PackageInfo generateWithoutComponentsUnchecked(ParsingPackageRead pkg, int[] gids,
+            @PackageManager.PackageInfoFlags int flags, long firstInstallTime, long lastUpdateTime,
+            Set<String> grantedPermissions, PackageUserState state, int userId,
+            @Nullable ApexInfo apexInfo, @NonNull ApplicationInfo applicationInfo) {
+        PackageInfo pi = new PackageInfo();
+        pi.packageName = pkg.getPackageName();
+        pi.splitNames = pkg.getSplitNames();
+        pi.versionCode = pkg.getVersionCode();
+        pi.versionCodeMajor = pkg.getVersionCodeMajor();
+        pi.baseRevisionCode = pkg.getBaseRevisionCode();
+        pi.splitRevisionCodes = pkg.getSplitRevisionCodes();
+        pi.versionName = pkg.getVersionName();
+        pi.sharedUserId = pkg.getSharedUserId();
+        pi.sharedUserLabel = pkg.getSharedUserLabel();
+        pi.applicationInfo = applicationInfo;
+        pi.installLocation = pkg.getInstallLocation();
+        if ((pi.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0
+                || (pi.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
+            pi.requiredForAllUsers = pkg.isRequiredForAllUsers();
+        }
+        pi.restrictedAccountType = pkg.getRestrictedAccountType();
+        pi.requiredAccountType = pkg.getRequiredAccountType();
+        pi.overlayTarget = pkg.getOverlayTarget();
+        pi.targetOverlayableName = pkg.getOverlayTargetName();
+        pi.overlayCategory = pkg.getOverlayCategory();
+        pi.overlayPriority = pkg.getOverlayPriority();
+        pi.mOverlayIsStatic = pkg.isOverlayIsStatic();
+        pi.compileSdkVersion = pkg.getCompileSdkVersion();
+        pi.compileSdkVersionCodename = pkg.getCompileSdkVersionCodeName();
+        pi.firstInstallTime = firstInstallTime;
+        pi.lastUpdateTime = lastUpdateTime;
+        if ((flags & PackageManager.GET_GIDS) != 0) {
+            pi.gids = gids;
+        }
+        if ((flags & PackageManager.GET_CONFIGURATIONS) != 0) {
+            int size = pkg.getConfigPreferences().size();
+            if (size > 0) {
+                pi.configPreferences = new ConfigurationInfo[size];
+                pkg.getConfigPreferences().toArray(pi.configPreferences);
+            }
+            size = pkg.getReqFeatures().size();
+            if (size > 0) {
+                pi.reqFeatures = new FeatureInfo[size];
+                pkg.getReqFeatures().toArray(pi.reqFeatures);
+            }
+            size = pkg.getFeatureGroups().size();
+            if (size > 0) {
+                pi.featureGroups = new FeatureGroupInfo[size];
+                pkg.getFeatureGroups().toArray(pi.featureGroups);
+            }
+        }
+        if ((flags & PackageManager.GET_PERMISSIONS) != 0) {
+            int size = ArrayUtils.size(pkg.getPermissions());
+            if (size > 0) {
+                pi.permissions = new PermissionInfo[size];
+                for (int i = 0; i < size; i++) {
+                    pi.permissions[i] = generatePermissionInfo(pkg.getPermissions().get(i),
+                            flags);
+                }
+            }
+            size = pkg.getRequestedPermissions().size();
+            if (size > 0) {
+                pi.requestedPermissions = new String[size];
+                pi.requestedPermissionsFlags = new int[size];
+                for (int i = 0; i < size; i++) {
+                    final String perm = pkg.getRequestedPermissions().get(i);
+                    pi.requestedPermissions[i] = perm;
+                    // The notion of required permissions is deprecated but for compatibility.
+                    pi.requestedPermissionsFlags[i] |= PackageInfo.REQUESTED_PERMISSION_REQUIRED;
+                    if (grantedPermissions != null && grantedPermissions.contains(perm)) {
+                        pi.requestedPermissionsFlags[i] |= PackageInfo.REQUESTED_PERMISSION_GRANTED;
+                    }
+                }
+            }
+        }
+
+        if (apexInfo != null) {
+            File apexFile = new File(apexInfo.modulePath);
+
+            pi.applicationInfo.sourceDir = apexFile.getPath();
+            pi.applicationInfo.publicSourceDir = apexFile.getPath();
+            if (apexInfo.isFactory) {
+                pi.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+            } else {
+                pi.applicationInfo.flags &= ~ApplicationInfo.FLAG_SYSTEM;
+            }
+            if (apexInfo.isActive) {
+                pi.applicationInfo.flags |= ApplicationInfo.FLAG_INSTALLED;
+            } else {
+                pi.applicationInfo.flags &= ~ApplicationInfo.FLAG_INSTALLED;
+            }
+            pi.isApex = true;
+        }
+
+        PackageParser.SigningDetails signingDetails = pkg.getSigningDetails();
+        // deprecated method of getting signing certificates
+        if ((flags & PackageManager.GET_SIGNATURES) != 0) {
+            if (signingDetails.hasPastSigningCertificates()) {
+                // Package has included signing certificate rotation information.  Return the oldest
+                // cert so that programmatic checks keep working even if unaware of key rotation.
+                pi.signatures = new Signature[1];
+                pi.signatures[0] = signingDetails.pastSigningCertificates[0];
+            } else if (signingDetails.hasSignatures()) {
+                // otherwise keep old behavior
+                int numberOfSigs = signingDetails.signatures.length;
+                pi.signatures = new Signature[numberOfSigs];
+                System.arraycopy(signingDetails.signatures, 0, pi.signatures, 0,
+                        numberOfSigs);
+            }
+        }
+
+        // replacement for GET_SIGNATURES
+        if ((flags & PackageManager.GET_SIGNING_CERTIFICATES) != 0) {
+            if (signingDetails != PackageParser.SigningDetails.UNKNOWN) {
+                // only return a valid SigningInfo if there is signing information to report
+                pi.signingInfo = new SigningInfo(signingDetails);
+            } else {
+                pi.signingInfo = null;
+            }
+        }
+
+        return pi;
+    }
+
+    @Nullable
+    public static ApplicationInfo generateApplicationInfo(ParsingPackageRead pkg,
+            @PackageManager.ApplicationInfoFlags int flags, PackageUserState state, int userId) {
+        if (pkg == null) {
+            return null;
+        }
+
+        if (!checkUseInstalled(pkg, state, flags)) {
+            return null;
+        }
+
+        return generateApplicationInfoUnchecked(pkg, flags, state, userId);
+    }
+
+    /**
+     * This bypasses critical checks that are necessary for usage with data passed outside of
+     * system server.
+     *
+     * Prefer {@link #generateApplicationInfo(ParsingPackageRead, int, PackageUserState, int)}.
+     */
+    @NonNull
+    public static ApplicationInfo generateApplicationInfoUnchecked(@NonNull ParsingPackageRead pkg,
+            @PackageManager.ApplicationInfoFlags int flags, PackageUserState state, int userId) {
+        // Make shallow copy so we can store the metadata/libraries safely
+        ApplicationInfo ai = pkg.toAppInfoWithoutState();
+        // Init handles data directories
+        // TODO(b/135203078): Consolidate the data directory logic, remove initForUser
+        ai.initForUser(userId);
+
+        if ((flags & PackageManager.GET_META_DATA) == 0) {
+            ai.metaData = null;
+        }
+        if ((flags & PackageManager.GET_SHARED_LIBRARY_FILES) == 0) {
+            ai.sharedLibraryFiles = null;
+            ai.sharedLibraryInfos = null;
+        }
+
+        // CompatibilityMode is global state.
+        if (!PackageParser.sCompatibilityModeEnabled) {
+            ai.disableCompatibilityMode();
+        }
+
+        ai.flags |= flag(state.stopped, ApplicationInfo.FLAG_STOPPED)
+                | flag(state.installed, ApplicationInfo.FLAG_INSTALLED)
+                | flag(state.suspended, ApplicationInfo.FLAG_SUSPENDED);
+        ai.privateFlags |= flag(state.instantApp, ApplicationInfo.PRIVATE_FLAG_INSTANT)
+                | flag(state.virtualPreload, ApplicationInfo.PRIVATE_FLAG_VIRTUAL_PRELOAD)
+                | flag(state.hidden, ApplicationInfo.PRIVATE_FLAG_HIDDEN);
+
+        if (state.enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
+            ai.enabled = true;
+        } else if (state.enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
+            ai.enabled = (flags & PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS) != 0;
+        } else if (state.enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED
+                || state.enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
+            ai.enabled = false;
+        }
+        ai.enabledSetting = state.enabled;
+        if (ai.category == ApplicationInfo.CATEGORY_UNDEFINED) {
+            ai.category = state.categoryHint;
+        }
+        if (ai.category == ApplicationInfo.CATEGORY_UNDEFINED) {
+            ai.category = FallbackCategoryProvider.getFallbackCategory(ai.packageName);
+        }
+        ai.seInfoUser = SELinuxUtil.assignSeinfoUser(state);
+        ai.resourceDirs = state.getAllOverlayPaths();
+
+        return ai;
+    }
+
+    @Nullable
+    public static ActivityInfo generateActivityInfo(ParsingPackageRead pkg, ParsedActivity a,
+            @PackageManager.ComponentInfoFlags int flags, PackageUserState state,
+            @Nullable ApplicationInfo applicationInfo, int userId) {
+        if (a == null) return null;
+        if (!checkUseInstalled(pkg, state, flags)) {
+            return null;
+        }
+        if (applicationInfo == null) {
+            applicationInfo = generateApplicationInfo(pkg, flags, state, userId);
+        }
+        if (applicationInfo == null) {
+            return null;
+        }
+
+        return generateActivityInfoUnchecked(a, applicationInfo);
+    }
+
+    /**
+     * This bypasses critical checks that are necessary for usage with data passed outside of
+     * system server.
+     *
+     * Prefer {@link #generateActivityInfo(ParsingPackageRead, ParsedActivity, int,
+     * PackageUserState, ApplicationInfo, int)}.
+     */
+    @NonNull
+    public static ActivityInfo generateActivityInfoUnchecked(@NonNull ParsedActivity a,
+            @NonNull ApplicationInfo applicationInfo) {
+        // Make shallow copies so we can store the metadata safely
+        ActivityInfo ai = new ActivityInfo();
+        assignSharedFieldsForComponentInfo(ai, a);
+        ai.targetActivity = a.getTargetActivity();
+        ai.processName = a.getProcessName();
+        ai.exported = a.isExported();
+        ai.theme = a.getTheme();
+        ai.uiOptions = a.getUiOptions();
+        ai.parentActivityName = a.getParentActivityName();
+        ai.permission = a.getPermission();
+        ai.taskAffinity = a.getTaskAffinity();
+        ai.flags = a.getFlags();
+        ai.privateFlags = a.getPrivateFlags();
+        ai.launchMode = a.getLaunchMode();
+        ai.documentLaunchMode = a.getDocumentLaunchMode();
+        ai.maxRecents = a.getMaxRecents();
+        ai.configChanges = a.getConfigChanges();
+        ai.softInputMode = a.getSoftInputMode();
+        ai.persistableMode = a.getPersistableMode();
+        ai.lockTaskLaunchMode = a.getLockTaskLaunchMode();
+        ai.screenOrientation = a.getScreenOrientation();
+        ai.resizeMode = a.getResizeMode();
+        Float maxAspectRatio = a.getMaxAspectRatio();
+        ai.maxAspectRatio = maxAspectRatio != null ? maxAspectRatio : 0f;
+        Float minAspectRatio = a.getMinAspectRatio();
+        ai.minAspectRatio = minAspectRatio != null ? minAspectRatio : 0f;
+        ai.requestedVrComponent = a.getRequestedVrComponent();
+        ai.rotationAnimation = a.getRotationAnimation();
+        ai.colorMode = a.getColorMode();
+        ai.windowLayout = a.getWindowLayout();
+        ai.metaData = a.getMetaData();
+        ai.applicationInfo = applicationInfo;
+        return ai;
+    }
+
+    @Nullable
+    public static ActivityInfo generateActivityInfo(ParsingPackageRead pkg, ParsedActivity a,
+            @PackageManager.ComponentInfoFlags int flags, PackageUserState state, int userId) {
+        return generateActivityInfo(pkg, a, flags, state, null, userId);
+    }
+
+    @Nullable
+    public static ServiceInfo generateServiceInfo(ParsingPackageRead pkg, ParsedService s,
+            @PackageManager.ComponentInfoFlags int flags, PackageUserState state,
+            @Nullable ApplicationInfo applicationInfo, int userId) {
+        if (s == null) return null;
+        if (!checkUseInstalled(pkg, state, flags)) {
+            return null;
+        }
+        if (applicationInfo == null) {
+            applicationInfo = generateApplicationInfo(pkg, flags, state, userId);
+        }
+        if (applicationInfo == null) {
+            return null;
+        }
+
+        return generateServiceInfoUnchecked(s, applicationInfo);
+    }
+
+    /**
+     * This bypasses critical checks that are necessary for usage with data passed outside of
+     * system server.
+     *
+     * Prefer {@link #generateServiceInfo(ParsingPackageRead, ParsedService, int, PackageUserState,
+     * ApplicationInfo, int)}.
+     */
+    @NonNull
+    public static ServiceInfo generateServiceInfoUnchecked(@NonNull ParsedService s,
+            @NonNull ApplicationInfo applicationInfo) {
+        // Make shallow copies so we can store the metadata safely
+        ServiceInfo si = new ServiceInfo();
+        assignSharedFieldsForComponentInfo(si, s);
+        si.exported = s.isExported();
+        si.flags = s.getFlags();
+        si.metaData = s.getMetaData();
+        si.permission = s.getPermission();
+        si.processName = s.getProcessName();
+        si.mForegroundServiceType = s.getForegroundServiceType();
+        si.applicationInfo = applicationInfo;
+        return si;
+    }
+
+    @Nullable
+    public static ServiceInfo generateServiceInfo(ParsingPackageRead pkg, ParsedService s,
+            @PackageManager.ComponentInfoFlags int flags, PackageUserState state, int userId) {
+        return generateServiceInfo(pkg, s, flags, state, null, userId);
+    }
+
+    @Nullable
+    public static ProviderInfo generateProviderInfo(ParsingPackageRead pkg, ParsedProvider p,
+            @PackageManager.ComponentInfoFlags int flags, PackageUserState state,
+            @Nullable ApplicationInfo applicationInfo, int userId) {
+        if (p == null) return null;
+        if (!checkUseInstalled(pkg, state, flags)) {
+            return null;
+        }
+        if (applicationInfo == null) {
+            applicationInfo = generateApplicationInfo(pkg, flags, state, userId);
+        }
+        if (applicationInfo == null) {
+            return null;
+        }
+
+        return generateProviderInfoUnchecked(p, flags, applicationInfo);
+    }
+
+    /**
+     * This bypasses critical checks that are necessary for usage with data passed outside of
+     * system server.
+     *
+     * Prefer {@link #generateProviderInfo(ParsingPackageRead, ParsedProvider, int,
+     * PackageUserState, ApplicationInfo, int)}.
+     */
+    @NonNull
+    public static ProviderInfo generateProviderInfoUnchecked(@NonNull ParsedProvider p,
+            @PackageManager.ComponentInfoFlags int flags,
+            @NonNull ApplicationInfo applicationInfo) {
+        // Make shallow copies so we can store the metadata safely
+        ProviderInfo pi = new ProviderInfo();
+        assignSharedFieldsForComponentInfo(pi, p);
+        pi.exported = p.isExported();
+        pi.flags = p.getFlags();
+        pi.processName = p.getProcessName();
+        pi.authority = p.getAuthority();
+        pi.isSyncable = p.isSyncable();
+        pi.readPermission = p.getReadPermission();
+        pi.writePermission = p.getWritePermission();
+        pi.grantUriPermissions = p.isGrantUriPermissions();
+        pi.forceUriPermissions = p.isForceUriPermissions();
+        pi.multiprocess = p.isMultiProcess();
+        pi.initOrder = p.getInitOrder();
+        pi.uriPermissionPatterns = p.getUriPermissionPatterns();
+        pi.pathPermissions = p.getPathPermissions();
+        pi.metaData = p.getMetaData();
+        if ((flags & PackageManager.GET_URI_PERMISSION_PATTERNS) == 0) {
+            pi.uriPermissionPatterns = null;
+        }
+        pi.applicationInfo = applicationInfo;
+        return pi;
+    }
+
+    @Nullable
+    public static ProviderInfo generateProviderInfo(ParsingPackageRead pkg, ParsedProvider p,
+            @PackageManager.ComponentInfoFlags int flags, PackageUserState state, int userId) {
+        return generateProviderInfo(pkg, p, flags, state, null, userId);
+    }
+
+    @Nullable
+    public static InstrumentationInfo generateInstrumentationInfo(ParsedInstrumentation i,
+            ParsingPackageRead pkg, @PackageManager.ComponentInfoFlags int flags, int userId) {
+        if (i == null) return null;
+
+        InstrumentationInfo ii = new InstrumentationInfo();
+        assignSharedFieldsForPackageItemInfo(ii, i);
+        ii.targetPackage = i.getTargetPackage();
+        ii.targetProcesses = i.getTargetProcesses();
+        ii.handleProfiling = i.isHandleProfiling();
+        ii.functionalTest = i.isFunctionalTest();
+
+        ii.sourceDir = pkg.getBaseCodePath();
+        ii.publicSourceDir = pkg.getBaseCodePath();
+        ii.splitNames = pkg.getSplitNames();
+        ii.splitSourceDirs = pkg.getSplitCodePaths();
+        ii.splitPublicSourceDirs = pkg.getSplitCodePaths();
+        ii.splitDependencies = pkg.getSplitDependencies();
+        ii.dataDir = getDataDir(pkg, userId).getAbsolutePath();
+        ii.deviceProtectedDataDir = getDeviceProtectedDataDir(pkg, userId).getAbsolutePath();
+        ii.credentialProtectedDataDir = getCredentialProtectedDataDir(pkg,
+                userId).getAbsolutePath();
+
+        if ((flags & PackageManager.GET_META_DATA) == 0) {
+            return ii;
+        }
+        ii.metaData = i.getMetaData();
+        return ii;
+    }
+
+    @Nullable
+    public static PermissionInfo generatePermissionInfo(ParsedPermission p,
+            @PackageManager.ComponentInfoFlags int flags) {
+        if (p == null) return null;
+
+        PermissionInfo pi = new PermissionInfo(p.getBackgroundPermission());
+
+        assignSharedFieldsForPackageItemInfo(pi, p);
+
+        pi.group = p.getGroup();
+        pi.requestRes = p.getRequestRes();
+        pi.protectionLevel = p.getProtectionLevel();
+        pi.descriptionRes = p.getDescriptionRes();
+        pi.flags = p.getFlags();
+
+        if ((flags & PackageManager.GET_META_DATA) == 0) {
+            return pi;
+        }
+        pi.metaData = p.getMetaData();
+        return pi;
+    }
+
+    @Nullable
+    public static PermissionGroupInfo generatePermissionGroupInfo(ParsedPermissionGroup pg,
+            @PackageManager.ComponentInfoFlags int flags) {
+        if (pg == null) return null;
+
+        PermissionGroupInfo pgi = new PermissionGroupInfo(
+                pg.getRequestDetailResourceId(),
+                pg.getBackgroundRequestResourceId(),
+                pg.getBackgroundRequestDetailResourceId()
+        );
+
+        assignSharedFieldsForPackageItemInfo(pgi, pg);
+        pgi.descriptionRes = pg.getDescriptionRes();
+        pgi.priority = pg.getPriority();
+        pgi.requestRes = pg.getRequestRes();
+        pgi.flags = pg.getFlags();
+
+        if ((flags & PackageManager.GET_META_DATA) == 0) {
+            return pgi;
+        }
+        pgi.metaData = pg.getMetaData();
+        return pgi;
+    }
+
+    private static void assignSharedFieldsForComponentInfo(@NonNull ComponentInfo componentInfo,
+            @NonNull ParsedMainComponent mainComponent) {
+        assignSharedFieldsForPackageItemInfo(componentInfo, mainComponent);
+        componentInfo.descriptionRes = mainComponent.getDescriptionRes();
+        componentInfo.directBootAware = mainComponent.isDirectBootAware();
+        componentInfo.enabled = mainComponent.isEnabled();
+        componentInfo.splitName = mainComponent.getSplitName();
+    }
+
+    private static void assignSharedFieldsForPackageItemInfo(
+            @NonNull PackageItemInfo packageItemInfo, @NonNull ParsedComponent component) {
+        packageItemInfo.nonLocalizedLabel = ComponentParseUtils.getNonLocalizedLabel(component);
+        packageItemInfo.icon = ComponentParseUtils.getIcon(component);
+
+        packageItemInfo.banner = component.getBanner();
+        packageItemInfo.labelRes = component.getLabelRes();
+        packageItemInfo.logo = component.getLogo();
+        packageItemInfo.name = component.getName();
+        packageItemInfo.packageName = component.getPackageName();
+    }
+
+    @CheckResult
+    private static int flag(boolean hasFlag, int flag) {
+        if (hasFlag) {
+            return flag;
+        } else {
+            return 0;
+        }
+    }
+
+    /** @see ApplicationInfo#flags */
+    public static int appInfoFlags(ParsingPackageRead pkg) {
+        // @formatter:off
+        return flag(pkg.isExternalStorage(), ApplicationInfo.FLAG_EXTERNAL_STORAGE)
+                | flag(pkg.isBaseHardwareAccelerated(), ApplicationInfo.FLAG_HARDWARE_ACCELERATED)
+                | flag(pkg.isAllowBackup(), ApplicationInfo.FLAG_ALLOW_BACKUP)
+                | flag(pkg.isKillAfterRestore(), ApplicationInfo.FLAG_KILL_AFTER_RESTORE)
+                | flag(pkg.isRestoreAnyVersion(), ApplicationInfo.FLAG_RESTORE_ANY_VERSION)
+                | flag(pkg.isFullBackupOnly(), ApplicationInfo.FLAG_FULL_BACKUP_ONLY)
+                | flag(pkg.isPersistent(), ApplicationInfo.FLAG_PERSISTENT)
+                | flag(pkg.isDebuggable(), ApplicationInfo.FLAG_DEBUGGABLE)
+                | flag(pkg.isVmSafeMode(), ApplicationInfo.FLAG_VM_SAFE_MODE)
+                | flag(pkg.isHasCode(), ApplicationInfo.FLAG_HAS_CODE)
+                | flag(pkg.isAllowTaskReparenting(), ApplicationInfo.FLAG_ALLOW_TASK_REPARENTING)
+                | flag(pkg.isAllowClearUserData(), ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA)
+                | flag(pkg.isLargeHeap(), ApplicationInfo.FLAG_LARGE_HEAP)
+                | flag(pkg.isUsesCleartextTraffic(), ApplicationInfo.FLAG_USES_CLEARTEXT_TRAFFIC)
+                | flag(pkg.isSupportsRtl(), ApplicationInfo.FLAG_SUPPORTS_RTL)
+                | flag(pkg.isTestOnly(), ApplicationInfo.FLAG_TEST_ONLY)
+                | flag(pkg.isMultiArch(), ApplicationInfo.FLAG_MULTIARCH)
+                | flag(pkg.isExtractNativeLibs(), ApplicationInfo.FLAG_EXTRACT_NATIVE_LIBS)
+                | flag(pkg.isGame(), ApplicationInfo.FLAG_IS_GAME)
+                | flag(pkg.isSupportsSmallScreens(), ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS)
+                | flag(pkg.isSupportsNormalScreens(), ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS)
+                | flag(pkg.isSupportsLargeScreens(), ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS)
+                | flag(pkg.isSupportsExtraLargeScreens(), ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS)
+                | flag(pkg.isResizeable(), ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS)
+                | flag(pkg.isAnyDensity(), ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES);
+        // @formatter:on
+    }
+
+    /** @see ApplicationInfo#privateFlags */
+    public static int appInfoPrivateFlags(ParsingPackageRead pkg) {
+        // @formatter:off
+        int privateFlags = flag(pkg.isStaticSharedLibrary(), ApplicationInfo.PRIVATE_FLAG_STATIC_SHARED_LIBRARY)
+                | flag(pkg.isOverlay(), ApplicationInfo.PRIVATE_FLAG_IS_RESOURCE_OVERLAY)
+                | flag(pkg.isIsolatedSplitLoading(), ApplicationInfo.PRIVATE_FLAG_ISOLATED_SPLIT_LOADING)
+                | flag(pkg.isHasDomainUrls(), ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS)
+                | flag(pkg.isProfileableByShell(), ApplicationInfo.PRIVATE_FLAG_PROFILEABLE_BY_SHELL)
+                | flag(pkg.isBackupInForeground(), ApplicationInfo.PRIVATE_FLAG_BACKUP_IN_FOREGROUND)
+                | flag(pkg.isUseEmbeddedDex(), ApplicationInfo.PRIVATE_FLAG_USE_EMBEDDED_DEX)
+                | flag(pkg.isDefaultToDeviceProtectedStorage(), ApplicationInfo.PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE)
+                | flag(pkg.isDirectBootAware(), ApplicationInfo.PRIVATE_FLAG_DIRECT_BOOT_AWARE)
+                | flag(pkg.isPartiallyDirectBootAware(), ApplicationInfo.PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE)
+                | flag(pkg.isAllowClearUserDataOnFailedRestore(), ApplicationInfo.PRIVATE_FLAG_ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE)
+                | flag(pkg.isAllowAudioPlaybackCapture(), ApplicationInfo.PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE)
+                | flag(pkg.isRequestLegacyExternalStorage(), ApplicationInfo.PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE)
+                | flag(pkg.isUsesNonSdkApi(), ApplicationInfo.PRIVATE_FLAG_USES_NON_SDK_API)
+                | flag(pkg.isHasFragileUserData(), ApplicationInfo.PRIVATE_FLAG_HAS_FRAGILE_USER_DATA)
+                | flag(pkg.isCantSaveState(), ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE)
+                | flag(pkg.isResizeableActivityViaSdkVersion(), ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION)
+                | flag(pkg.isAllowNativeHeapPointerTagging(), ApplicationInfo.PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING);
+        // @formatter:on
+
+        Boolean resizeableActivity = pkg.getResizeableActivity();
+        if (resizeableActivity != null) {
+            if (resizeableActivity) {
+                privateFlags |= ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE;
+            } else {
+                privateFlags |= ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE;
+            }
+        }
+
+        return privateFlags;
+    }
+
+    private static boolean checkUseInstalled(ParsingPackageRead pkg, PackageUserState state,
+            @PackageManager.PackageInfoFlags int flags) {
+        // If available for the target user
+        return state.isAvailable(flags);
+    }
+
+    @NonNull
+    public static File getDataDir(ParsingPackageRead pkg, int userId) {
+        if ("android".equals(pkg.getPackageName())) {
+            return Environment.getDataSystemDirectory();
+        }
+
+        if (pkg.isDefaultToDeviceProtectedStorage()
+                && PackageManager.APPLY_DEFAULT_TO_DEVICE_PROTECTED_STORAGE) {
+            return getDeviceProtectedDataDir(pkg, userId);
+        } else {
+            return getCredentialProtectedDataDir(pkg, userId);
+        }
+    }
+
+    @NonNull
+    public static File getDeviceProtectedDataDir(ParsingPackageRead pkg, int userId) {
+        return Environment.getDataUserDePackageDirectory(pkg.getVolumeUuid(), userId,
+                pkg.getPackageName());
+    }
+
+    @NonNull
+    public static File getCredentialProtectedDataDir(ParsingPackageRead pkg, int userId) {
+        return Environment.getDataUserCePackageDirectory(pkg.getVolumeUuid(), userId,
+                pkg.getPackageName());
+    }
+}
diff --git a/android/content/pm/parsing/ParsingPackage.java b/android/content/pm/parsing/ParsingPackage.java
new file mode 100644
index 0000000..2ee0ad6
--- /dev/null
+++ b/android/content/pm/parsing/ParsingPackage.java
@@ -0,0 +1,342 @@
+/*
+ * 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.content.pm.parsing;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Intent;
+import android.content.pm.ConfigurationInfo;
+import android.content.pm.FeatureGroupInfo;
+import android.content.pm.FeatureInfo;
+import android.content.pm.PackageParser;
+import android.content.pm.parsing.component.ParsedActivity;
+import android.content.pm.parsing.component.ParsedAttribution;
+import android.content.pm.parsing.component.ParsedInstrumentation;
+import android.content.pm.parsing.component.ParsedIntentInfo;
+import android.content.pm.parsing.component.ParsedPermission;
+import android.content.pm.parsing.component.ParsedPermissionGroup;
+import android.content.pm.parsing.component.ParsedProcess;
+import android.content.pm.parsing.component.ParsedProvider;
+import android.content.pm.parsing.component.ParsedService;
+import android.os.Bundle;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+
+import java.security.PublicKey;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Methods used for mutation during direct package parsing.
+ *
+ * @hide
+ */
+@SuppressWarnings("UnusedReturnValue")
+public interface ParsingPackage extends ParsingPackageRead {
+
+    ParsingPackage addActivity(ParsedActivity parsedActivity);
+
+    ParsingPackage addAdoptPermission(String adoptPermission);
+
+    ParsingPackage addConfigPreference(ConfigurationInfo configPreference);
+
+    ParsingPackage addFeatureGroup(FeatureGroupInfo featureGroup);
+
+    ParsingPackage addImplicitPermission(String permission);
+
+    ParsingPackage addInstrumentation(ParsedInstrumentation instrumentation);
+
+    ParsingPackage addKeySet(String keySetName, PublicKey publicKey);
+
+    ParsingPackage addLibraryName(String libraryName);
+
+    ParsingPackage addOriginalPackage(String originalPackage);
+
+    ParsingPackage addOverlayable(String overlayableName, String actorName);
+
+    ParsingPackage addPermission(ParsedPermission permission);
+
+    ParsingPackage addPermissionGroup(ParsedPermissionGroup permissionGroup);
+
+    ParsingPackage addPreferredActivityFilter(String className, ParsedIntentInfo intentInfo);
+
+    ParsingPackage addProtectedBroadcast(String protectedBroadcast);
+
+    ParsingPackage addProvider(ParsedProvider parsedProvider);
+
+    ParsingPackage addAttribution(ParsedAttribution attribution);
+
+    ParsingPackage addReceiver(ParsedActivity parsedReceiver);
+
+    ParsingPackage addReqFeature(FeatureInfo reqFeature);
+
+    ParsingPackage addRequestedPermission(String permission);
+
+    ParsingPackage addService(ParsedService parsedService);
+
+    ParsingPackage addUsesLibrary(String libraryName);
+
+    ParsingPackage addUsesOptionalLibrary(String libraryName);
+
+    ParsingPackage addUsesStaticLibrary(String libraryName);
+
+    ParsingPackage addUsesStaticLibraryCertDigests(String[] certSha256Digests);
+
+    ParsingPackage addUsesStaticLibraryVersion(long version);
+
+    ParsingPackage addQueriesIntent(Intent intent);
+
+    ParsingPackage addQueriesPackage(String packageName);
+
+    ParsingPackage addQueriesProvider(String authority);
+
+    ParsingPackage setProcesses(@NonNull Map<String, ParsedProcess> processes);
+
+    ParsingPackage asSplit(
+            String[] splitNames,
+            String[] splitCodePaths,
+            int[] splitRevisionCodes,
+            @Nullable SparseArray<int[]> splitDependencies
+    );
+
+    ParsingPackage setMetaData(Bundle metaData);
+
+    ParsingPackage setForceQueryable(boolean forceQueryable);
+
+    ParsingPackage setMaxAspectRatio(float maxAspectRatio);
+
+    ParsingPackage setMinAspectRatio(float minAspectRatio);
+
+    ParsingPackage setPermission(String permission);
+
+    ParsingPackage setProcessName(String processName);
+
+    ParsingPackage setSharedUserId(String sharedUserId);
+
+    ParsingPackage setStaticSharedLibName(String staticSharedLibName);
+
+    ParsingPackage setTaskAffinity(String taskAffinity);
+
+    ParsingPackage setTargetSdkVersion(int targetSdkVersion);
+
+    ParsingPackage setUiOptions(int uiOptions);
+
+    ParsingPackage setBaseHardwareAccelerated(boolean baseHardwareAccelerated);
+
+    ParsingPackage setResizeableActivity(Boolean resizeable);
+
+    ParsingPackage setResizeableActivityViaSdkVersion(boolean resizeableViaSdkVersion);
+
+    ParsingPackage setAllowAudioPlaybackCapture(boolean allowAudioPlaybackCapture);
+
+    ParsingPackage setAllowBackup(boolean allowBackup);
+
+    ParsingPackage setAllowClearUserData(boolean allowClearUserData);
+
+    ParsingPackage setAllowClearUserDataOnFailedRestore(boolean allowClearUserDataOnFailedRestore);
+
+    ParsingPackage setAllowTaskReparenting(boolean allowTaskReparenting);
+
+    ParsingPackage setOverlay(boolean isOverlay);
+
+    ParsingPackage setBackupInForeground(boolean backupInForeground);
+
+    ParsingPackage setCantSaveState(boolean cantSaveState);
+
+    ParsingPackage setDebuggable(boolean debuggable);
+
+    ParsingPackage setDefaultToDeviceProtectedStorage(boolean defaultToDeviceProtectedStorage);
+
+    ParsingPackage setDirectBootAware(boolean directBootAware);
+
+    ParsingPackage setExternalStorage(boolean externalStorage);
+
+    ParsingPackage setExtractNativeLibs(boolean extractNativeLibs);
+
+    ParsingPackage setFullBackupOnly(boolean fullBackupOnly);
+
+    ParsingPackage setHasCode(boolean hasCode);
+
+    ParsingPackage setHasFragileUserData(boolean hasFragileUserData);
+
+    ParsingPackage setGame(boolean isGame);
+
+    ParsingPackage setIsolatedSplitLoading(boolean isolatedSplitLoading);
+
+    ParsingPackage setKillAfterRestore(boolean killAfterRestore);
+
+    ParsingPackage setLargeHeap(boolean largeHeap);
+
+    ParsingPackage setMultiArch(boolean multiArch);
+
+    ParsingPackage setPartiallyDirectBootAware(boolean partiallyDirectBootAware);
+
+    ParsingPackage setPersistent(boolean persistent);
+
+    ParsingPackage setProfileableByShell(boolean profileableByShell);
+
+    ParsingPackage setRequestLegacyExternalStorage(boolean requestLegacyExternalStorage);
+
+    ParsingPackage setAllowNativeHeapPointerTagging(boolean allowNativeHeapPointerTagging);
+
+    ParsingPackage setAutoRevokePermissions(int autoRevokePermissions);
+
+    ParsingPackage setPreserveLegacyExternalStorage(boolean preserveLegacyExternalStorage);
+
+    ParsingPackage setRestoreAnyVersion(boolean restoreAnyVersion);
+
+    ParsingPackage setSplitHasCode(int splitIndex, boolean splitHasCode);
+
+    ParsingPackage setStaticSharedLibrary(boolean staticSharedLibrary);
+
+    ParsingPackage setSupportsRtl(boolean supportsRtl);
+
+    ParsingPackage setTestOnly(boolean testOnly);
+
+    ParsingPackage setUseEmbeddedDex(boolean useEmbeddedDex);
+
+    ParsingPackage setUsesCleartextTraffic(boolean usesCleartextTraffic);
+
+    ParsingPackage setUsesNonSdkApi(boolean usesNonSdkApi);
+
+    ParsingPackage setVisibleToInstantApps(boolean visibleToInstantApps);
+
+    ParsingPackage setVmSafeMode(boolean vmSafeMode);
+
+    ParsingPackage removeUsesOptionalLibrary(String libraryName);
+
+    ParsingPackage setAnyDensity(int anyDensity);
+
+    ParsingPackage setAppComponentFactory(String appComponentFactory);
+
+    ParsingPackage setBackupAgentName(String backupAgentName);
+
+    ParsingPackage setBanner(int banner);
+
+    ParsingPackage setCategory(int category);
+
+    ParsingPackage setClassLoaderName(String classLoaderName);
+
+    ParsingPackage setClassName(String className);
+
+    ParsingPackage setCompatibleWidthLimitDp(int compatibleWidthLimitDp);
+
+    ParsingPackage setDescriptionRes(int descriptionRes);
+
+    ParsingPackage setEnabled(boolean enabled);
+
+    ParsingPackage setGwpAsanMode(int gwpAsanMode);
+
+    ParsingPackage setCrossProfile(boolean crossProfile);
+
+    ParsingPackage setFullBackupContent(int fullBackupContent);
+
+    ParsingPackage setHasDomainUrls(boolean hasDomainUrls);
+
+    ParsingPackage setIconRes(int iconRes);
+
+    ParsingPackage setInstallLocation(int installLocation);
+
+    ParsingPackage setLabelRes(int labelRes);
+
+    ParsingPackage setLargestWidthLimitDp(int largestWidthLimitDp);
+
+    ParsingPackage setLogo(int logo);
+
+    ParsingPackage setManageSpaceActivityName(String manageSpaceActivityName);
+
+    ParsingPackage setMinExtensionVersions(@Nullable SparseIntArray minExtensionVersions);
+
+    ParsingPackage setMinSdkVersion(int minSdkVersion);
+
+    ParsingPackage setNetworkSecurityConfigRes(int networkSecurityConfigRes);
+
+    ParsingPackage setNonLocalizedLabel(CharSequence nonLocalizedLabel);
+
+    ParsingPackage setOverlayCategory(String overlayCategory);
+
+    ParsingPackage setOverlayIsStatic(boolean overlayIsStatic);
+
+    ParsingPackage setOverlayPriority(int overlayPriority);
+
+    ParsingPackage setOverlayTarget(String overlayTarget);
+
+    ParsingPackage setOverlayTargetName(String overlayTargetName);
+
+    ParsingPackage setRealPackage(String realPackage);
+
+    ParsingPackage setRequiredAccountType(String requiredAccountType);
+
+    ParsingPackage setRequiredForAllUsers(boolean requiredForAllUsers);
+
+    ParsingPackage setRequiresSmallestWidthDp(int requiresSmallestWidthDp);
+
+    ParsingPackage setResizeable(int resizeable);
+
+    ParsingPackage setRestrictUpdateHash(byte[] restrictUpdateHash);
+
+    ParsingPackage setRestrictedAccountType(String restrictedAccountType);
+
+    ParsingPackage setRoundIconRes(int roundIconRes);
+
+    ParsingPackage setSharedUserLabel(int sharedUserLabel);
+
+    ParsingPackage setSigningDetails(PackageParser.SigningDetails signingDetails);
+
+    ParsingPackage setSplitClassLoaderName(int splitIndex, String classLoaderName);
+
+    ParsingPackage setStaticSharedLibVersion(long staticSharedLibVersion);
+
+    ParsingPackage setSupportsLargeScreens(int supportsLargeScreens);
+
+    ParsingPackage setSupportsNormalScreens(int supportsNormalScreens);
+
+    ParsingPackage setSupportsSmallScreens(int supportsSmallScreens);
+
+    ParsingPackage setSupportsExtraLargeScreens(int supportsExtraLargeScreens);
+
+    ParsingPackage setTargetSandboxVersion(int targetSandboxVersion);
+
+    ParsingPackage setTheme(int theme);
+
+    ParsingPackage setUpgradeKeySets(@NonNull Set<String> upgradeKeySets);
+
+    ParsingPackage setUse32BitAbi(boolean use32BitAbi);
+
+    ParsingPackage setVolumeUuid(@Nullable String volumeUuid);
+
+    ParsingPackage setZygotePreloadName(String zygotePreloadName);
+
+    ParsingPackage sortActivities();
+
+    ParsingPackage sortReceivers();
+
+    ParsingPackage sortServices();
+
+    ParsingPackage setBaseRevisionCode(int baseRevisionCode);
+
+    ParsingPackage setVersionName(String versionName);
+
+    ParsingPackage setCompileSdkVersion(int compileSdkVersion);
+
+    ParsingPackage setCompileSdkVersionCodename(String compileSdkVersionCodename);
+
+    // TODO(b/135203078): This class no longer has access to ParsedPackage, find a replacement
+    //  for moving to the next step
+    @Deprecated
+    Object hideAsParsed();
+}
diff --git a/android/content/pm/parsing/ParsingPackageImpl.java b/android/content/pm/parsing/ParsingPackageImpl.java
new file mode 100644
index 0000000..f932bc2
--- /dev/null
+++ b/android/content/pm/parsing/ParsingPackageImpl.java
@@ -0,0 +1,2632 @@
+/*
+ * 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.content.pm.parsing;
+
+import static java.util.Collections.emptyList;
+import static java.util.Collections.emptyMap;
+import static java.util.Collections.emptySet;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ConfigurationInfo;
+import android.content.pm.FeatureGroupInfo;
+import android.content.pm.FeatureInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageParser;
+import android.content.pm.parsing.component.ParsedActivity;
+import android.content.pm.parsing.component.ParsedAttribution;
+import android.content.pm.parsing.component.ParsedComponent;
+import android.content.pm.parsing.component.ParsedInstrumentation;
+import android.content.pm.parsing.component.ParsedIntentInfo;
+import android.content.pm.parsing.component.ParsedMainComponent;
+import android.content.pm.parsing.component.ParsedPermission;
+import android.content.pm.parsing.component.ParsedPermissionGroup;
+import android.content.pm.parsing.component.ParsedProcess;
+import android.content.pm.parsing.component.ParsedProvider;
+import android.content.pm.parsing.component.ParsedService;
+import android.content.res.TypedArray;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.storage.StorageManager;
+import android.text.TextUtils;
+import android.util.ArraySet;
+import android.util.Pair;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.CollectionUtils;
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling;
+import com.android.internal.util.Parcelling.BuiltIn.ForBoolean;
+import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
+import com.android.internal.util.Parcelling.BuiltIn.ForInternedStringArray;
+import com.android.internal.util.Parcelling.BuiltIn.ForInternedStringList;
+import com.android.internal.util.Parcelling.BuiltIn.ForInternedStringSet;
+import com.android.internal.util.Parcelling.BuiltIn.ForInternedStringValueMap;
+import com.android.internal.util.Parcelling.BuiltIn.ForStringSet;
+
+import java.security.PublicKey;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * The backing data for a package that was parsed from disk.
+ *
+ * The field nullability annotations here are for internal reference. For effective nullability,
+ * see the parent interfaces.
+ *
+ * TODO(b/135203078): Convert Lists used as sets into Sets, to better express intended use case
+ *
+ * @hide
+ */
+public class ParsingPackageImpl implements ParsingPackage, Parcelable {
+
+    private static final String TAG = "PackageImpl";
+
+    public static ForBoolean sForBoolean = Parcelling.Cache.getOrCreate(ForBoolean.class);
+    public static ForInternedString sForInternedString = Parcelling.Cache.getOrCreate(
+            ForInternedString.class);
+    public static ForInternedStringArray sForInternedStringArray = Parcelling.Cache.getOrCreate(
+            ForInternedStringArray.class);
+    public static ForInternedStringList sForInternedStringList = Parcelling.Cache.getOrCreate(
+            ForInternedStringList.class);
+    public static ForInternedStringValueMap sForInternedStringValueMap =
+            Parcelling.Cache.getOrCreate(ForInternedStringValueMap.class);
+    public static ForStringSet sForStringSet = Parcelling.Cache.getOrCreate(ForStringSet.class);
+    protected static ParsedIntentInfo.StringPairListParceler sForIntentInfoPairs =
+            Parcelling.Cache.getOrCreate(ParsedIntentInfo.StringPairListParceler.class);
+
+    private static final Comparator<ParsedMainComponent> ORDER_COMPARATOR =
+            (first, second) -> Integer.compare(second.getOrder(), first.getOrder());
+
+    // These are objects because null represents not explicitly set
+    @Nullable
+    @DataClass.ParcelWith(ForBoolean.class)
+    private Boolean supportsSmallScreens;
+    @Nullable
+    @DataClass.ParcelWith(ForBoolean.class)
+    private Boolean supportsNormalScreens;
+    @Nullable
+    @DataClass.ParcelWith(ForBoolean.class)
+    private Boolean supportsLargeScreens;
+    @Nullable
+    @DataClass.ParcelWith(ForBoolean.class)
+    private Boolean supportsExtraLargeScreens;
+    @Nullable
+    @DataClass.ParcelWith(ForBoolean.class)
+    private Boolean resizeable;
+    @Nullable
+    @DataClass.ParcelWith(ForBoolean.class)
+    private Boolean anyDensity;
+
+    protected int versionCode;
+    protected int versionCodeMajor;
+    private int baseRevisionCode;
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String versionName;
+
+    private int compileSdkVersion;
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String compileSdkVersionCodeName;
+
+    @NonNull
+    @DataClass.ParcelWith(ForInternedString.class)
+    protected String packageName;
+
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String realPackage;
+
+    @NonNull
+    protected String baseCodePath;
+
+    private boolean requiredForAllUsers;
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String restrictedAccountType;
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String requiredAccountType;
+
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String overlayTarget;
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String overlayTargetName;
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String overlayCategory;
+    private int overlayPriority;
+    private boolean overlayIsStatic;
+    @NonNull
+    @DataClass.ParcelWith(ForInternedStringValueMap.class)
+    private Map<String, String> overlayables = emptyMap();
+
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String staticSharedLibName;
+    private long staticSharedLibVersion;
+    @NonNull
+    @DataClass.ParcelWith(ForInternedStringList.class)
+    private List<String> libraryNames = emptyList();
+    @NonNull
+    @DataClass.ParcelWith(ForInternedStringList.class)
+    protected List<String> usesLibraries = emptyList();
+    @NonNull
+    @DataClass.ParcelWith(ForInternedStringList.class)
+    protected List<String> usesOptionalLibraries = emptyList();
+
+    @NonNull
+    @DataClass.ParcelWith(ForInternedStringList.class)
+    private List<String> usesStaticLibraries = emptyList();
+    @Nullable
+    private long[] usesStaticLibrariesVersions;
+
+    @Nullable
+    private String[][] usesStaticLibrariesCertDigests;
+
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String sharedUserId;
+
+    private int sharedUserLabel;
+    @NonNull
+    private List<ConfigurationInfo> configPreferences = emptyList();
+    @NonNull
+    private List<FeatureInfo> reqFeatures = emptyList();
+    @NonNull
+    private List<FeatureGroupInfo> featureGroups = emptyList();
+
+    @Nullable
+    private byte[] restrictUpdateHash;
+
+    @NonNull
+    @DataClass.ParcelWith(ForInternedStringList.class)
+    protected List<String> originalPackages = emptyList();
+    @NonNull
+    @DataClass.ParcelWith(ForInternedStringList.class)
+    protected List<String> adoptPermissions = emptyList();
+
+    @NonNull
+    @DataClass.ParcelWith(ForInternedStringList.class)
+    private List<String> requestedPermissions = emptyList();
+    @NonNull
+    @DataClass.ParcelWith(ForInternedStringList.class)
+    private List<String> implicitPermissions = emptyList();
+
+    @NonNull
+    private Set<String> upgradeKeySets = emptySet();
+    @NonNull
+    private Map<String, ArraySet<PublicKey>> keySetMapping = emptyMap();
+
+    @NonNull
+    @DataClass.ParcelWith(ForInternedStringList.class)
+    protected List<String> protectedBroadcasts = emptyList();
+
+    @NonNull
+    protected List<ParsedActivity> activities = emptyList();
+
+    @NonNull
+    protected List<ParsedActivity> receivers = emptyList();
+
+    @NonNull
+    protected List<ParsedService> services = emptyList();
+
+    @NonNull
+    protected List<ParsedProvider> providers = emptyList();
+
+    @NonNull
+    private List<ParsedAttribution> attributions = emptyList();
+
+    @NonNull
+    protected List<ParsedPermission> permissions = emptyList();
+
+    @NonNull
+    protected List<ParsedPermissionGroup> permissionGroups = emptyList();
+
+    @NonNull
+    protected List<ParsedInstrumentation> instrumentations = emptyList();
+
+    @NonNull
+    @DataClass.ParcelWith(ParsedIntentInfo.ListParceler.class)
+    private List<Pair<String, ParsedIntentInfo>> preferredActivityFilters = emptyList();
+
+    @NonNull
+    private Map<String, ParsedProcess> processes = emptyMap();
+
+    @Nullable
+    private Bundle metaData;
+
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    protected String volumeUuid;
+    @Nullable
+    private PackageParser.SigningDetails signingDetails;
+
+    @NonNull
+    @DataClass.ParcelWith(ForInternedString.class)
+    protected String codePath;
+
+    private boolean use32BitAbi;
+    private boolean visibleToInstantApps;
+
+    private boolean forceQueryable;
+
+    @NonNull
+    @DataClass.ParcelWith(ForInternedStringList.class)
+    private List<Intent> queriesIntents = emptyList();
+
+    @NonNull
+    @DataClass.ParcelWith(ForInternedStringList.class)
+    private List<String> queriesPackages = emptyList();
+
+    @NonNull
+    @DataClass.ParcelWith(ForInternedStringSet.class)
+    private Set<String> queriesProviders = emptySet();
+
+    @Nullable
+    @DataClass.ParcelWith(ForInternedStringArray.class)
+    private String[] splitClassLoaderNames;
+    @Nullable
+    protected String[] splitCodePaths;
+    @Nullable
+    private SparseArray<int[]> splitDependencies;
+    @Nullable
+    private int[] splitFlags;
+    @Nullable
+    @DataClass.ParcelWith(ForInternedStringArray.class)
+    private String[] splitNames;
+    @Nullable
+    private int[] splitRevisionCodes;
+
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String appComponentFactory;
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String backupAgentName;
+    private int banner;
+    private int category;
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String classLoaderName;
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String className;
+    private int compatibleWidthLimitDp;
+    private int descriptionRes;
+
+    // Usually there's code to set this to true during parsing, but it's possible to install an APK
+    // targeting <R that doesn't contain an <application> tag. That code would be skipped and never
+    // assign this, so initialize this to true for those cases.
+    private boolean enabled = true;
+
+    private boolean crossProfile;
+    private int fullBackupContent;
+    private int iconRes;
+    private int installLocation = PackageParser.PARSE_DEFAULT_INSTALL_LOCATION;
+    private int labelRes;
+    private int largestWidthLimitDp;
+    private int logo;
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String manageSpaceActivityName;
+    private float maxAspectRatio;
+    private float minAspectRatio;
+    @Nullable
+    private SparseIntArray minExtensionVersions;
+    private int minSdkVersion;
+    private int networkSecurityConfigRes;
+    @Nullable
+    private CharSequence nonLocalizedLabel;
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String permission;
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String processName;
+    private int requiresSmallestWidthDp;
+    private int roundIconRes;
+    private int targetSandboxVersion;
+    private int targetSdkVersion;
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String taskAffinity;
+    private int theme;
+
+    private int uiOptions;
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String zygotePreloadName;
+
+    private boolean externalStorage;
+    private boolean baseHardwareAccelerated;
+    private boolean allowBackup;
+    private boolean killAfterRestore;
+    private boolean restoreAnyVersion;
+    private boolean fullBackupOnly;
+    private boolean persistent;
+    private boolean debuggable;
+    private boolean vmSafeMode;
+    private boolean hasCode;
+    private boolean allowTaskReparenting;
+    private boolean allowClearUserData;
+    private boolean largeHeap;
+    private boolean usesCleartextTraffic;
+    private boolean supportsRtl;
+    private boolean testOnly;
+    private boolean multiArch;
+    private boolean extractNativeLibs;
+    private boolean game;
+
+    /**
+     * @see ParsingPackageRead#getResizeableActivity()
+     */
+    @Nullable
+    @DataClass.ParcelWith(ForBoolean.class)
+    private Boolean resizeableActivity;
+
+    private boolean staticSharedLibrary;
+    private boolean overlay;
+    private boolean isolatedSplitLoading;
+    private boolean hasDomainUrls;
+    private boolean profileableByShell;
+    private boolean backupInForeground;
+    private boolean useEmbeddedDex;
+    private boolean defaultToDeviceProtectedStorage;
+    private boolean directBootAware;
+    private boolean partiallyDirectBootAware;
+    private boolean resizeableActivityViaSdkVersion;
+    private boolean allowClearUserDataOnFailedRestore;
+    private boolean allowAudioPlaybackCapture;
+    private boolean requestLegacyExternalStorage;
+    private boolean usesNonSdkApi;
+    private boolean hasFragileUserData;
+    private boolean cantSaveState;
+    private boolean allowNativeHeapPointerTagging;
+    private int autoRevokePermissions;
+    private boolean preserveLegacyExternalStorage;
+
+    protected int gwpAsanMode;
+
+    // TODO(chiuwinson): Non-null
+    @Nullable
+    private ArraySet<String> mimeGroups;
+
+    @VisibleForTesting
+    public ParsingPackageImpl(@NonNull String packageName, @NonNull String baseCodePath,
+            @NonNull String codePath, @Nullable TypedArray manifestArray) {
+        this.packageName = TextUtils.safeIntern(packageName);
+        this.baseCodePath = baseCodePath;
+        this.codePath = codePath;
+
+        if (manifestArray != null) {
+            versionCode = manifestArray.getInteger(R.styleable.AndroidManifest_versionCode, 0);
+            versionCodeMajor = manifestArray.getInteger(
+                    R.styleable.AndroidManifest_versionCodeMajor, 0);
+            setBaseRevisionCode(
+                    manifestArray.getInteger(R.styleable.AndroidManifest_revisionCode, 0));
+            setVersionName(manifestArray.getNonConfigurationString(
+                    R.styleable.AndroidManifest_versionName, 0));
+
+            setCompileSdkVersion(manifestArray.getInteger(
+                    R.styleable.AndroidManifest_compileSdkVersion, 0));
+            setCompileSdkVersionCodename(manifestArray.getNonConfigurationString(
+                    R.styleable.AndroidManifest_compileSdkVersionCodename, 0));
+
+            setIsolatedSplitLoading(manifestArray.getBoolean(
+                    R.styleable.AndroidManifest_isolatedSplits, false));
+
+        }
+    }
+
+    public boolean isSupportsSmallScreens() {
+        if (supportsSmallScreens == null) {
+            return targetSdkVersion >= Build.VERSION_CODES.DONUT;
+        }
+
+        return supportsSmallScreens;
+    }
+
+    public boolean isSupportsNormalScreens() {
+        return supportsNormalScreens == null || supportsNormalScreens;
+    }
+
+    public boolean isSupportsLargeScreens() {
+        if (supportsLargeScreens == null) {
+            return targetSdkVersion >= Build.VERSION_CODES.DONUT;
+        }
+
+        return supportsLargeScreens;
+    }
+
+    public boolean isSupportsExtraLargeScreens() {
+        if (supportsExtraLargeScreens == null) {
+            return targetSdkVersion >= Build.VERSION_CODES.GINGERBREAD;
+        }
+
+        return supportsExtraLargeScreens;
+    }
+
+    public boolean isResizeable() {
+        if (resizeable == null) {
+            return targetSdkVersion >= Build.VERSION_CODES.DONUT;
+        }
+
+        return resizeable;
+    }
+
+    public boolean isAnyDensity() {
+        if (anyDensity == null) {
+            return targetSdkVersion >= Build.VERSION_CODES.DONUT;
+        }
+
+        return anyDensity;
+    }
+
+    @Override
+    public ParsingPackageImpl sortActivities() {
+        Collections.sort(this.activities, ORDER_COMPARATOR);
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl sortReceivers() {
+        Collections.sort(this.receivers, ORDER_COMPARATOR);
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl sortServices() {
+        Collections.sort(this.services, ORDER_COMPARATOR);
+        return this;
+    }
+
+    @Override
+    public Object hideAsParsed() {
+        // There is no equivalent for core-only parsing
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public ParsingPackageImpl addConfigPreference(ConfigurationInfo configPreference) {
+        this.configPreferences = CollectionUtils.add(this.configPreferences, configPreference);
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl addReqFeature(FeatureInfo reqFeature) {
+        this.reqFeatures = CollectionUtils.add(this.reqFeatures, reqFeature);
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl addFeatureGroup(FeatureGroupInfo featureGroup) {
+        this.featureGroups = CollectionUtils.add(this.featureGroups, featureGroup);
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl addProtectedBroadcast(String protectedBroadcast) {
+        if (!this.protectedBroadcasts.contains(protectedBroadcast)) {
+            this.protectedBroadcasts = CollectionUtils.add(this.protectedBroadcasts,
+                    TextUtils.safeIntern(protectedBroadcast));
+        }
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl addInstrumentation(ParsedInstrumentation instrumentation) {
+        this.instrumentations = CollectionUtils.add(this.instrumentations, instrumentation);
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl addOriginalPackage(String originalPackage) {
+        this.originalPackages = CollectionUtils.add(this.originalPackages, originalPackage);
+        return this;
+    }
+
+    @Override
+    public ParsingPackage addOverlayable(String overlayableName, String actorName) {
+        this.overlayables = CollectionUtils.add(this.overlayables, overlayableName,
+                TextUtils.safeIntern(actorName));
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl addAdoptPermission(String adoptPermission) {
+        this.adoptPermissions = CollectionUtils.add(this.adoptPermissions,
+                TextUtils.safeIntern(adoptPermission));
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl addPermission(ParsedPermission permission) {
+        this.permissions = CollectionUtils.add(this.permissions, permission);
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl addPermissionGroup(ParsedPermissionGroup permissionGroup) {
+        this.permissionGroups = CollectionUtils.add(this.permissionGroups, permissionGroup);
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl addRequestedPermission(String permission) {
+        this.requestedPermissions = CollectionUtils.add(this.requestedPermissions,
+                TextUtils.safeIntern(permission));
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl addImplicitPermission(String permission) {
+        this.implicitPermissions = CollectionUtils.add(this.implicitPermissions,
+                TextUtils.safeIntern(permission));
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl addKeySet(String keySetName, PublicKey publicKey) {
+        ArraySet<PublicKey> publicKeys = keySetMapping.get(keySetName);
+        if (publicKeys == null) {
+            publicKeys = new ArraySet<>();
+        }
+        publicKeys.add(publicKey);
+        keySetMapping = CollectionUtils.add(this.keySetMapping, keySetName, publicKeys);
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl addActivity(ParsedActivity parsedActivity) {
+        this.activities = CollectionUtils.add(this.activities, parsedActivity);
+        addMimeGroupsFromComponent(parsedActivity);
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl addReceiver(ParsedActivity parsedReceiver) {
+        this.receivers = CollectionUtils.add(this.receivers, parsedReceiver);
+        addMimeGroupsFromComponent(parsedReceiver);
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl addService(ParsedService parsedService) {
+        this.services = CollectionUtils.add(this.services, parsedService);
+        addMimeGroupsFromComponent(parsedService);
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl addProvider(ParsedProvider parsedProvider) {
+        this.providers = CollectionUtils.add(this.providers, parsedProvider);
+        addMimeGroupsFromComponent(parsedProvider);
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl addAttribution(ParsedAttribution attribution) {
+        this.attributions = CollectionUtils.add(this.attributions, attribution);
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl addLibraryName(String libraryName) {
+        this.libraryNames = CollectionUtils.add(this.libraryNames,
+                TextUtils.safeIntern(libraryName));
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl addUsesOptionalLibrary(String libraryName) {
+        this.usesOptionalLibraries = CollectionUtils.add(this.usesOptionalLibraries,
+                TextUtils.safeIntern(libraryName));
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl addUsesLibrary(String libraryName) {
+        this.usesLibraries = CollectionUtils.add(this.usesLibraries,
+                TextUtils.safeIntern(libraryName));
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl removeUsesOptionalLibrary(String libraryName) {
+        this.usesOptionalLibraries = CollectionUtils.remove(this.usesOptionalLibraries,
+                libraryName);
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl addUsesStaticLibrary(String libraryName) {
+        this.usesStaticLibraries = CollectionUtils.add(this.usesStaticLibraries,
+                TextUtils.safeIntern(libraryName));
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl addUsesStaticLibraryVersion(long version) {
+        this.usesStaticLibrariesVersions = ArrayUtils.appendLong(this.usesStaticLibrariesVersions,
+                version, true);
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl addUsesStaticLibraryCertDigests(String[] certSha256Digests) {
+        this.usesStaticLibrariesCertDigests = ArrayUtils.appendElement(String[].class,
+                this.usesStaticLibrariesCertDigests, certSha256Digests, true);
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl addPreferredActivityFilter(String className,
+            ParsedIntentInfo intentInfo) {
+        this.preferredActivityFilters = CollectionUtils.add(this.preferredActivityFilters,
+                Pair.create(className, intentInfo));
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl addQueriesIntent(Intent intent) {
+        this.queriesIntents = CollectionUtils.add(this.queriesIntents, intent);
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl addQueriesPackage(String packageName) {
+        this.queriesPackages = CollectionUtils.add(this.queriesPackages,
+                TextUtils.safeIntern(packageName));
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl addQueriesProvider(String authority) {
+        this.queriesProviders = CollectionUtils.add(this.queriesProviders, authority);
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setSupportsSmallScreens(int supportsSmallScreens) {
+        if (supportsSmallScreens == 1) {
+            return this;
+        }
+
+        this.supportsSmallScreens = supportsSmallScreens < 0;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setSupportsNormalScreens(int supportsNormalScreens) {
+        if (supportsNormalScreens == 1) {
+            return this;
+        }
+
+        this.supportsNormalScreens = supportsNormalScreens < 0;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setSupportsLargeScreens(int supportsLargeScreens) {
+        if (supportsLargeScreens == 1) {
+            return this;
+        }
+
+        this.supportsLargeScreens = supportsLargeScreens < 0;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setSupportsExtraLargeScreens(int supportsExtraLargeScreens) {
+        if (supportsExtraLargeScreens == 1) {
+            return this;
+        }
+
+        this.supportsExtraLargeScreens = supportsExtraLargeScreens < 0;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setResizeable(int resizeable) {
+        if (resizeable == 1) {
+            return this;
+        }
+
+        this.resizeable = resizeable < 0;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setAnyDensity(int anyDensity) {
+        if (anyDensity == 1) {
+            return this;
+        }
+
+        this.anyDensity = anyDensity < 0;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl asSplit(String[] splitNames, String[] splitCodePaths,
+            int[] splitRevisionCodes, SparseArray<int[]> splitDependencies) {
+        this.splitNames = splitNames;
+        this.splitCodePaths = splitCodePaths;
+        this.splitRevisionCodes = splitRevisionCodes;
+        this.splitDependencies = splitDependencies;
+
+        int count = splitNames.length;
+        this.splitFlags = new int[count];
+        this.splitClassLoaderNames = new String[count];
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setSplitHasCode(int splitIndex, boolean splitHasCode) {
+        this.splitFlags[splitIndex] = splitHasCode
+                ? this.splitFlags[splitIndex] | ApplicationInfo.FLAG_HAS_CODE
+                : this.splitFlags[splitIndex] & ~ApplicationInfo.FLAG_HAS_CODE;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setSplitClassLoaderName(int splitIndex, String classLoaderName) {
+        this.splitClassLoaderNames[splitIndex] = classLoaderName;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setRequiredAccountType(@Nullable String requiredAccountType) {
+        this.requiredAccountType = TextUtils.nullIfEmpty(requiredAccountType);
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setOverlayTarget(@Nullable String overlayTarget) {
+        this.overlayTarget = TextUtils.safeIntern(overlayTarget);
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setVolumeUuid(@Nullable String volumeUuid) {
+        this.volumeUuid = TextUtils.safeIntern(volumeUuid);
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setStaticSharedLibName(String staticSharedLibName) {
+        this.staticSharedLibName = TextUtils.safeIntern(staticSharedLibName);
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setSharedUserId(String sharedUserId) {
+        this.sharedUserId = TextUtils.safeIntern(sharedUserId);
+        return this;
+    }
+
+    @NonNull
+    @Override
+    public String getProcessName() {
+        return processName != null ? processName : packageName;
+    }
+
+    @Override
+    public String toString() {
+        return "Package{"
+                + Integer.toHexString(System.identityHashCode(this))
+                + " " + packageName + "}";
+    }
+
+    @Deprecated
+    @Override
+    public ApplicationInfo toAppInfoWithoutState() {
+        ApplicationInfo appInfo = toAppInfoWithoutStateWithoutFlags();
+        appInfo.flags = PackageInfoWithoutStateUtils.appInfoFlags(this);
+        appInfo.privateFlags = PackageInfoWithoutStateUtils.appInfoPrivateFlags(this);
+        return appInfo;
+    }
+
+    @Override
+    public ApplicationInfo toAppInfoWithoutStateWithoutFlags() {
+        ApplicationInfo appInfo = new ApplicationInfo();
+
+        appInfo.appComponentFactory = appComponentFactory;
+        appInfo.backupAgentName = backupAgentName;
+        appInfo.banner = banner;
+        appInfo.category = category;
+        appInfo.classLoaderName = classLoaderName;
+        appInfo.className = className;
+        appInfo.compatibleWidthLimitDp = compatibleWidthLimitDp;
+        appInfo.compileSdkVersion = compileSdkVersion;
+        appInfo.compileSdkVersionCodename = compileSdkVersionCodeName;
+//        appInfo.credentialProtectedDataDir = credentialProtectedDataDir;
+//        appInfo.dataDir = dataDir;
+        appInfo.descriptionRes = descriptionRes;
+//        appInfo.deviceProtectedDataDir = deviceProtectedDataDir;
+        appInfo.enabled = enabled;
+        appInfo.fullBackupContent = fullBackupContent;
+//        appInfo.hiddenUntilInstalled = hiddenUntilInstalled;
+        appInfo.icon = (PackageParser.sUseRoundIcon && roundIconRes != 0) ? roundIconRes : iconRes;
+        appInfo.iconRes = iconRes;
+        appInfo.roundIconRes = roundIconRes;
+        appInfo.installLocation = installLocation;
+        appInfo.labelRes = labelRes;
+        appInfo.largestWidthLimitDp = largestWidthLimitDp;
+        appInfo.logo = logo;
+        appInfo.manageSpaceActivityName = manageSpaceActivityName;
+        appInfo.maxAspectRatio = maxAspectRatio;
+        appInfo.metaData = metaData;
+        appInfo.minAspectRatio = minAspectRatio;
+        appInfo.minSdkVersion = minSdkVersion;
+        appInfo.name = className;
+        if (appInfo.name != null) {
+            appInfo.name = appInfo.name.trim();
+        }
+//        appInfo.nativeLibraryDir = nativeLibraryDir;
+//        appInfo.nativeLibraryRootDir = nativeLibraryRootDir;
+//        appInfo.nativeLibraryRootRequiresIsa = nativeLibraryRootRequiresIsa;
+        appInfo.networkSecurityConfigRes = networkSecurityConfigRes;
+        appInfo.nonLocalizedLabel = nonLocalizedLabel;
+        if (appInfo.nonLocalizedLabel != null) {
+            appInfo.nonLocalizedLabel = appInfo.nonLocalizedLabel.toString().trim();
+        }
+        appInfo.packageName = packageName;
+        appInfo.permission = permission;
+//        appInfo.primaryCpuAbi = primaryCpuAbi;
+        appInfo.processName = getProcessName();
+        appInfo.requiresSmallestWidthDp = requiresSmallestWidthDp;
+//        appInfo.secondaryCpuAbi = secondaryCpuAbi;
+//        appInfo.secondaryNativeLibraryDir = secondaryNativeLibraryDir;
+//        appInfo.seInfo = seInfo;
+//        appInfo.seInfoUser = seInfoUser;
+//        appInfo.sharedLibraryFiles = usesLibraryFiles.isEmpty()
+//                ? null : usesLibraryFiles.toArray(new String[0]);
+//        appInfo.sharedLibraryInfos = usesLibraryInfos.isEmpty() ? null : usesLibraryInfos;
+        appInfo.splitClassLoaderNames = splitClassLoaderNames;
+        appInfo.splitDependencies = splitDependencies;
+        appInfo.splitNames = splitNames;
+        appInfo.storageUuid = StorageManager.convert(volumeUuid);
+        appInfo.targetSandboxVersion = targetSandboxVersion;
+        appInfo.targetSdkVersion = targetSdkVersion;
+        appInfo.taskAffinity = taskAffinity;
+        appInfo.theme = theme;
+//        appInfo.uid = uid;
+        appInfo.uiOptions = uiOptions;
+        appInfo.volumeUuid = volumeUuid;
+        appInfo.zygotePreloadName = zygotePreloadName;
+        appInfo.crossProfile = isCrossProfile();
+        appInfo.setGwpAsanMode(gwpAsanMode);
+        appInfo.setBaseCodePath(baseCodePath);
+        appInfo.setBaseResourcePath(baseCodePath);
+        appInfo.setCodePath(codePath);
+        appInfo.setResourcePath(codePath);
+        appInfo.setSplitCodePaths(splitCodePaths);
+        appInfo.setSplitResourcePaths(splitCodePaths);
+        appInfo.setVersionCode(PackageInfo.composeLongVersionCode(versionCodeMajor, versionCode));
+
+        // TODO(b/135203078): Can this be removed? Looks only used in ActivityInfo.
+//        appInfo.showUserIcon = pkg.getShowUserIcon();
+        // TODO(b/135203078): Unused?
+//        appInfo.resourceDirs = pkg.getResourceDirs();
+        // TODO(b/135203078): Unused?
+//        appInfo.enabledSetting = pkg.getEnabledSetting();
+        // TODO(b/135203078): See ParsingPackageImpl#getHiddenApiEnforcementPolicy
+//        appInfo.mHiddenApiPolicy = pkg.getHiddenApiPolicy();
+
+        return appInfo;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        sForBoolean.parcel(this.supportsSmallScreens, dest, flags);
+        sForBoolean.parcel(this.supportsNormalScreens, dest, flags);
+        sForBoolean.parcel(this.supportsLargeScreens, dest, flags);
+        sForBoolean.parcel(this.supportsExtraLargeScreens, dest, flags);
+        sForBoolean.parcel(this.resizeable, dest, flags);
+        sForBoolean.parcel(this.anyDensity, dest, flags);
+        dest.writeInt(this.versionCode);
+        dest.writeInt(this.versionCodeMajor);
+        dest.writeInt(this.baseRevisionCode);
+        sForInternedString.parcel(this.versionName, dest, flags);
+        dest.writeInt(this.compileSdkVersion);
+        dest.writeString(this.compileSdkVersionCodeName);
+        sForInternedString.parcel(this.packageName, dest, flags);
+        dest.writeString(this.realPackage);
+        dest.writeString(this.baseCodePath);
+        dest.writeBoolean(this.requiredForAllUsers);
+        dest.writeString(this.restrictedAccountType);
+        dest.writeString(this.requiredAccountType);
+        sForInternedString.parcel(this.overlayTarget, dest, flags);
+        dest.writeString(this.overlayTargetName);
+        dest.writeString(this.overlayCategory);
+        dest.writeInt(this.overlayPriority);
+        dest.writeBoolean(this.overlayIsStatic);
+        sForInternedStringValueMap.parcel(this.overlayables, dest, flags);
+        sForInternedString.parcel(this.staticSharedLibName, dest, flags);
+        dest.writeLong(this.staticSharedLibVersion);
+        sForInternedStringList.parcel(this.libraryNames, dest, flags);
+        sForInternedStringList.parcel(this.usesLibraries, dest, flags);
+        sForInternedStringList.parcel(this.usesOptionalLibraries, dest, flags);
+        sForInternedStringList.parcel(this.usesStaticLibraries, dest, flags);
+        dest.writeLongArray(this.usesStaticLibrariesVersions);
+
+        if (this.usesStaticLibrariesCertDigests == null) {
+            dest.writeInt(-1);
+        } else {
+            dest.writeInt(this.usesStaticLibrariesCertDigests.length);
+            for (int index = 0; index < this.usesStaticLibrariesCertDigests.length; index++) {
+                dest.writeStringArray(this.usesStaticLibrariesCertDigests[index]);
+            }
+        }
+
+        sForInternedString.parcel(this.sharedUserId, dest, flags);
+        dest.writeInt(this.sharedUserLabel);
+        dest.writeTypedList(this.configPreferences);
+        dest.writeTypedList(this.reqFeatures);
+        dest.writeTypedList(this.featureGroups);
+        dest.writeByteArray(this.restrictUpdateHash);
+        dest.writeStringList(this.originalPackages);
+        sForInternedStringList.parcel(this.adoptPermissions, dest, flags);
+        sForInternedStringList.parcel(this.requestedPermissions, dest, flags);
+        sForInternedStringList.parcel(this.implicitPermissions, dest, flags);
+        sForStringSet.parcel(this.upgradeKeySets, dest, flags);
+        dest.writeMap(this.keySetMapping);
+        sForInternedStringList.parcel(this.protectedBroadcasts, dest, flags);
+        dest.writeTypedList(this.activities);
+        dest.writeTypedList(this.receivers);
+        dest.writeTypedList(this.services);
+        dest.writeTypedList(this.providers);
+        dest.writeTypedList(this.attributions);
+        dest.writeTypedList(this.permissions);
+        dest.writeTypedList(this.permissionGroups);
+        dest.writeTypedList(this.instrumentations);
+        sForIntentInfoPairs.parcel(this.preferredActivityFilters, dest, flags);
+        dest.writeMap(this.processes);
+        dest.writeBundle(this.metaData);
+        sForInternedString.parcel(this.volumeUuid, dest, flags);
+        dest.writeParcelable(this.signingDetails, flags);
+        dest.writeString(this.codePath);
+        dest.writeBoolean(this.use32BitAbi);
+        dest.writeBoolean(this.visibleToInstantApps);
+        dest.writeBoolean(this.forceQueryable);
+        dest.writeParcelableList(this.queriesIntents, flags);
+        sForInternedStringList.parcel(this.queriesPackages, dest, flags);
+        dest.writeString(this.appComponentFactory);
+        dest.writeString(this.backupAgentName);
+        dest.writeInt(this.banner);
+        dest.writeInt(this.category);
+        dest.writeString(this.classLoaderName);
+        dest.writeString(this.className);
+        dest.writeInt(this.compatibleWidthLimitDp);
+        dest.writeInt(this.descriptionRes);
+        dest.writeBoolean(this.enabled);
+        dest.writeBoolean(this.crossProfile);
+        dest.writeInt(this.fullBackupContent);
+        dest.writeInt(this.iconRes);
+        dest.writeInt(this.installLocation);
+        dest.writeInt(this.labelRes);
+        dest.writeInt(this.largestWidthLimitDp);
+        dest.writeInt(this.logo);
+        dest.writeString(this.manageSpaceActivityName);
+        dest.writeFloat(this.maxAspectRatio);
+        dest.writeFloat(this.minAspectRatio);
+        dest.writeInt(this.minSdkVersion);
+        dest.writeInt(this.networkSecurityConfigRes);
+        dest.writeCharSequence(this.nonLocalizedLabel);
+        dest.writeString(this.permission);
+        dest.writeString(this.processName);
+        dest.writeInt(this.requiresSmallestWidthDp);
+        dest.writeInt(this.roundIconRes);
+        dest.writeInt(this.targetSandboxVersion);
+        dest.writeInt(this.targetSdkVersion);
+        dest.writeString(this.taskAffinity);
+        dest.writeInt(this.theme);
+        dest.writeInt(this.uiOptions);
+        dest.writeString(this.zygotePreloadName);
+        dest.writeStringArray(this.splitClassLoaderNames);
+        dest.writeStringArray(this.splitCodePaths);
+        dest.writeSparseArray(this.splitDependencies);
+        dest.writeIntArray(this.splitFlags);
+        dest.writeStringArray(this.splitNames);
+        dest.writeIntArray(this.splitRevisionCodes);
+
+        dest.writeBoolean(this.externalStorage);
+        dest.writeBoolean(this.baseHardwareAccelerated);
+        dest.writeBoolean(this.allowBackup);
+        dest.writeBoolean(this.killAfterRestore);
+        dest.writeBoolean(this.restoreAnyVersion);
+        dest.writeBoolean(this.fullBackupOnly);
+        dest.writeBoolean(this.persistent);
+        dest.writeBoolean(this.debuggable);
+        dest.writeBoolean(this.vmSafeMode);
+        dest.writeBoolean(this.hasCode);
+        dest.writeBoolean(this.allowTaskReparenting);
+        dest.writeBoolean(this.allowClearUserData);
+        dest.writeBoolean(this.largeHeap);
+        dest.writeBoolean(this.usesCleartextTraffic);
+        dest.writeBoolean(this.supportsRtl);
+        dest.writeBoolean(this.testOnly);
+        dest.writeBoolean(this.multiArch);
+        dest.writeBoolean(this.extractNativeLibs);
+        dest.writeBoolean(this.game);
+
+        sForBoolean.parcel(this.resizeableActivity, dest, flags);
+
+        dest.writeBoolean(this.staticSharedLibrary);
+        dest.writeBoolean(this.overlay);
+        dest.writeBoolean(this.isolatedSplitLoading);
+        dest.writeBoolean(this.hasDomainUrls);
+        dest.writeBoolean(this.profileableByShell);
+        dest.writeBoolean(this.backupInForeground);
+        dest.writeBoolean(this.useEmbeddedDex);
+        dest.writeBoolean(this.defaultToDeviceProtectedStorage);
+        dest.writeBoolean(this.directBootAware);
+        dest.writeBoolean(this.partiallyDirectBootAware);
+        dest.writeBoolean(this.resizeableActivityViaSdkVersion);
+        dest.writeBoolean(this.allowClearUserDataOnFailedRestore);
+        dest.writeBoolean(this.allowAudioPlaybackCapture);
+        dest.writeBoolean(this.requestLegacyExternalStorage);
+        dest.writeBoolean(this.usesNonSdkApi);
+        dest.writeBoolean(this.hasFragileUserData);
+        dest.writeBoolean(this.cantSaveState);
+        dest.writeBoolean(this.allowNativeHeapPointerTagging);
+        dest.writeInt(this.autoRevokePermissions);
+        dest.writeBoolean(this.preserveLegacyExternalStorage);
+        dest.writeArraySet(this.mimeGroups);
+        dest.writeInt(this.gwpAsanMode);
+        dest.writeSparseIntArray(this.minExtensionVersions);
+    }
+
+    public ParsingPackageImpl(Parcel in) {
+        // We use the boot classloader for all classes that we load.
+        final ClassLoader boot = Object.class.getClassLoader();
+        this.supportsSmallScreens = sForBoolean.unparcel(in);
+        this.supportsNormalScreens = sForBoolean.unparcel(in);
+        this.supportsLargeScreens = sForBoolean.unparcel(in);
+        this.supportsExtraLargeScreens = sForBoolean.unparcel(in);
+        this.resizeable = sForBoolean.unparcel(in);
+        this.anyDensity = sForBoolean.unparcel(in);
+        this.versionCode = in.readInt();
+        this.versionCodeMajor = in.readInt();
+        this.baseRevisionCode = in.readInt();
+        this.versionName = sForInternedString.unparcel(in);
+        this.compileSdkVersion = in.readInt();
+        this.compileSdkVersionCodeName = in.readString();
+        this.packageName = sForInternedString.unparcel(in);
+        this.realPackage = in.readString();
+        this.baseCodePath = in.readString();
+        this.requiredForAllUsers = in.readBoolean();
+        this.restrictedAccountType = in.readString();
+        this.requiredAccountType = in.readString();
+        this.overlayTarget = sForInternedString.unparcel(in);
+        this.overlayTargetName = in.readString();
+        this.overlayCategory = in.readString();
+        this.overlayPriority = in.readInt();
+        this.overlayIsStatic = in.readBoolean();
+        this.overlayables = sForInternedStringValueMap.unparcel(in);
+        this.staticSharedLibName = sForInternedString.unparcel(in);
+        this.staticSharedLibVersion = in.readLong();
+        this.libraryNames = sForInternedStringList.unparcel(in);
+        this.usesLibraries = sForInternedStringList.unparcel(in);
+        this.usesOptionalLibraries = sForInternedStringList.unparcel(in);
+        this.usesStaticLibraries = sForInternedStringList.unparcel(in);
+        this.usesStaticLibrariesVersions = in.createLongArray();
+
+        int digestsSize = in.readInt();
+        if (digestsSize >= 0) {
+            this.usesStaticLibrariesCertDigests = new String[digestsSize][];
+            for (int index = 0; index < digestsSize; index++) {
+                this.usesStaticLibrariesCertDigests[index] = sForInternedStringArray.unparcel(in);
+            }
+        }
+
+        this.sharedUserId = sForInternedString.unparcel(in);
+        this.sharedUserLabel = in.readInt();
+        this.configPreferences = in.createTypedArrayList(ConfigurationInfo.CREATOR);
+        this.reqFeatures = in.createTypedArrayList(FeatureInfo.CREATOR);
+        this.featureGroups = in.createTypedArrayList(FeatureGroupInfo.CREATOR);
+        this.restrictUpdateHash = in.createByteArray();
+        this.originalPackages = in.createStringArrayList();
+        this.adoptPermissions = sForInternedStringList.unparcel(in);
+        this.requestedPermissions = sForInternedStringList.unparcel(in);
+        this.implicitPermissions = sForInternedStringList.unparcel(in);
+        this.upgradeKeySets = sForStringSet.unparcel(in);
+        this.keySetMapping = in.readHashMap(boot);
+        this.protectedBroadcasts = sForInternedStringList.unparcel(in);
+
+        this.activities = in.createTypedArrayList(ParsedActivity.CREATOR);
+        this.receivers = in.createTypedArrayList(ParsedActivity.CREATOR);
+        this.services = in.createTypedArrayList(ParsedService.CREATOR);
+        this.providers = in.createTypedArrayList(ParsedProvider.CREATOR);
+        this.attributions = in.createTypedArrayList(ParsedAttribution.CREATOR);
+        this.permissions = in.createTypedArrayList(ParsedPermission.CREATOR);
+        this.permissionGroups = in.createTypedArrayList(ParsedPermissionGroup.CREATOR);
+        this.instrumentations = in.createTypedArrayList(ParsedInstrumentation.CREATOR);
+        this.preferredActivityFilters = sForIntentInfoPairs.unparcel(in);
+        this.processes = in.readHashMap(boot);
+        this.metaData = in.readBundle(boot);
+        this.volumeUuid = sForInternedString.unparcel(in);
+        this.signingDetails = in.readParcelable(boot);
+        this.codePath = in.readString();
+        this.use32BitAbi = in.readBoolean();
+        this.visibleToInstantApps = in.readBoolean();
+        this.forceQueryable = in.readBoolean();
+        this.queriesIntents = in.createTypedArrayList(Intent.CREATOR);
+        this.queriesPackages = sForInternedStringList.unparcel(in);
+        this.appComponentFactory = in.readString();
+        this.backupAgentName = in.readString();
+        this.banner = in.readInt();
+        this.category = in.readInt();
+        this.classLoaderName = in.readString();
+        this.className = in.readString();
+        this.compatibleWidthLimitDp = in.readInt();
+        this.descriptionRes = in.readInt();
+        this.enabled = in.readBoolean();
+        this.crossProfile = in.readBoolean();
+        this.fullBackupContent = in.readInt();
+        this.iconRes = in.readInt();
+        this.installLocation = in.readInt();
+        this.labelRes = in.readInt();
+        this.largestWidthLimitDp = in.readInt();
+        this.logo = in.readInt();
+        this.manageSpaceActivityName = in.readString();
+        this.maxAspectRatio = in.readFloat();
+        this.minAspectRatio = in.readFloat();
+        this.minSdkVersion = in.readInt();
+        this.networkSecurityConfigRes = in.readInt();
+        this.nonLocalizedLabel = in.readCharSequence();
+        this.permission = in.readString();
+        this.processName = in.readString();
+        this.requiresSmallestWidthDp = in.readInt();
+        this.roundIconRes = in.readInt();
+        this.targetSandboxVersion = in.readInt();
+        this.targetSdkVersion = in.readInt();
+        this.taskAffinity = in.readString();
+        this.theme = in.readInt();
+        this.uiOptions = in.readInt();
+        this.zygotePreloadName = in.readString();
+        this.splitClassLoaderNames = in.createStringArray();
+        this.splitCodePaths = in.createStringArray();
+        this.splitDependencies = in.readSparseArray(boot);
+        this.splitFlags = in.createIntArray();
+        this.splitNames = in.createStringArray();
+        this.splitRevisionCodes = in.createIntArray();
+        this.externalStorage = in.readBoolean();
+        this.baseHardwareAccelerated = in.readBoolean();
+        this.allowBackup = in.readBoolean();
+        this.killAfterRestore = in.readBoolean();
+        this.restoreAnyVersion = in.readBoolean();
+        this.fullBackupOnly = in.readBoolean();
+        this.persistent = in.readBoolean();
+        this.debuggable = in.readBoolean();
+        this.vmSafeMode = in.readBoolean();
+        this.hasCode = in.readBoolean();
+        this.allowTaskReparenting = in.readBoolean();
+        this.allowClearUserData = in.readBoolean();
+        this.largeHeap = in.readBoolean();
+        this.usesCleartextTraffic = in.readBoolean();
+        this.supportsRtl = in.readBoolean();
+        this.testOnly = in.readBoolean();
+        this.multiArch = in.readBoolean();
+        this.extractNativeLibs = in.readBoolean();
+        this.game = in.readBoolean();
+        this.resizeableActivity = sForBoolean.unparcel(in);
+
+        this.staticSharedLibrary = in.readBoolean();
+        this.overlay = in.readBoolean();
+        this.isolatedSplitLoading = in.readBoolean();
+        this.hasDomainUrls = in.readBoolean();
+        this.profileableByShell = in.readBoolean();
+        this.backupInForeground = in.readBoolean();
+        this.useEmbeddedDex = in.readBoolean();
+        this.defaultToDeviceProtectedStorage = in.readBoolean();
+        this.directBootAware = in.readBoolean();
+        this.partiallyDirectBootAware = in.readBoolean();
+        this.resizeableActivityViaSdkVersion = in.readBoolean();
+        this.allowClearUserDataOnFailedRestore = in.readBoolean();
+        this.allowAudioPlaybackCapture = in.readBoolean();
+        this.requestLegacyExternalStorage = in.readBoolean();
+        this.usesNonSdkApi = in.readBoolean();
+        this.hasFragileUserData = in.readBoolean();
+        this.cantSaveState = in.readBoolean();
+        this.allowNativeHeapPointerTagging = in.readBoolean();
+        this.autoRevokePermissions = in.readInt();
+        this.preserveLegacyExternalStorage = in.readBoolean();
+        this.mimeGroups = (ArraySet<String>) in.readArraySet(boot);
+        this.gwpAsanMode = in.readInt();
+        this.minExtensionVersions = in.readSparseIntArray();
+    }
+
+    public static final Parcelable.Creator<ParsingPackageImpl> CREATOR =
+            new Parcelable.Creator<ParsingPackageImpl>() {
+                @Override
+                public ParsingPackageImpl createFromParcel(Parcel source) {
+                    return new ParsingPackageImpl(source);
+                }
+
+                @Override
+                public ParsingPackageImpl[] newArray(int size) {
+                    return new ParsingPackageImpl[size];
+                }
+            };
+
+    @Override
+    public int getVersionCode() {
+        return versionCode;
+    }
+
+    @Override
+    public int getVersionCodeMajor() {
+        return versionCodeMajor;
+    }
+
+    @Override
+    public int getBaseRevisionCode() {
+        return baseRevisionCode;
+    }
+
+    @Nullable
+    @Override
+    public String getVersionName() {
+        return versionName;
+    }
+
+    @Override
+    public int getCompileSdkVersion() {
+        return compileSdkVersion;
+    }
+
+    @Nullable
+    @Override
+    public String getCompileSdkVersionCodeName() {
+        return compileSdkVersionCodeName;
+    }
+
+    @NonNull
+    @Override
+    public String getPackageName() {
+        return packageName;
+    }
+
+    @Nullable
+    @Override
+    public String getRealPackage() {
+        return realPackage;
+    }
+
+    @NonNull
+    @Override
+    public String getBaseCodePath() {
+        return baseCodePath;
+    }
+
+    @Override
+    public boolean isRequiredForAllUsers() {
+        return requiredForAllUsers;
+    }
+
+    @Nullable
+    @Override
+    public String getRestrictedAccountType() {
+        return restrictedAccountType;
+    }
+
+    @Nullable
+    @Override
+    public String getRequiredAccountType() {
+        return requiredAccountType;
+    }
+
+    @Nullable
+    @Override
+    public String getOverlayTarget() {
+        return overlayTarget;
+    }
+
+    @Nullable
+    @Override
+    public String getOverlayTargetName() {
+        return overlayTargetName;
+    }
+
+    @Nullable
+    @Override
+    public String getOverlayCategory() {
+        return overlayCategory;
+    }
+
+    @Override
+    public int getOverlayPriority() {
+        return overlayPriority;
+    }
+
+    @Override
+    public boolean isOverlayIsStatic() {
+        return overlayIsStatic;
+    }
+
+    @NonNull
+    @Override
+    public Map<String,String> getOverlayables() {
+        return overlayables;
+    }
+
+    @Nullable
+    @Override
+    public String getStaticSharedLibName() {
+        return staticSharedLibName;
+    }
+
+    @Override
+    public long getStaticSharedLibVersion() {
+        return staticSharedLibVersion;
+    }
+
+    @NonNull
+    @Override
+    public List<String> getLibraryNames() {
+        return libraryNames;
+    }
+
+    @NonNull
+    @Override
+    public List<String> getUsesLibraries() {
+        return usesLibraries;
+    }
+
+    @NonNull
+    @Override
+    public List<String> getUsesOptionalLibraries() {
+        return usesOptionalLibraries;
+    }
+
+    @NonNull
+    @Override
+    public List<String> getUsesStaticLibraries() {
+        return usesStaticLibraries;
+    }
+
+    @Nullable
+    @Override
+    public long[] getUsesStaticLibrariesVersions() {
+        return usesStaticLibrariesVersions;
+    }
+
+    @Nullable
+    @Override
+    public String[][] getUsesStaticLibrariesCertDigests() {
+        return usesStaticLibrariesCertDigests;
+    }
+
+    @Nullable
+    @Override
+    public String getSharedUserId() {
+        return sharedUserId;
+    }
+
+    @Override
+    public int getSharedUserLabel() {
+        return sharedUserLabel;
+    }
+
+    @NonNull
+    @Override
+    public List<ConfigurationInfo> getConfigPreferences() {
+        return configPreferences;
+    }
+
+    @NonNull
+    @Override
+    public List<FeatureInfo> getReqFeatures() {
+        return reqFeatures;
+    }
+
+    @NonNull
+    @Override
+    public List<FeatureGroupInfo> getFeatureGroups() {
+        return featureGroups;
+    }
+
+    @Nullable
+    @Override
+    public byte[] getRestrictUpdateHash() {
+        return restrictUpdateHash;
+    }
+
+    @NonNull
+    @Override
+    public List<String> getOriginalPackages() {
+        return originalPackages;
+    }
+
+    @NonNull
+    @Override
+    public List<String> getAdoptPermissions() {
+        return adoptPermissions;
+    }
+
+    @NonNull
+    @Override
+    public List<String> getRequestedPermissions() {
+        return requestedPermissions;
+    }
+
+    @NonNull
+    @Override
+    public List<String> getImplicitPermissions() {
+        return implicitPermissions;
+    }
+
+    @NonNull
+    @Override
+    public Set<String> getUpgradeKeySets() {
+        return upgradeKeySets;
+    }
+
+    @NonNull
+    @Override
+    public Map<String,ArraySet<PublicKey>> getKeySetMapping() {
+        return keySetMapping;
+    }
+
+    @NonNull
+    @Override
+    public List<String> getProtectedBroadcasts() {
+        return protectedBroadcasts;
+    }
+
+    @NonNull
+    @Override
+    public List<ParsedActivity> getActivities() {
+        return activities;
+    }
+
+    @NonNull
+    @Override
+    public List<ParsedActivity> getReceivers() {
+        return receivers;
+    }
+
+    @NonNull
+    @Override
+    public List<ParsedService> getServices() {
+        return services;
+    }
+
+    @NonNull
+    @Override
+    public List<ParsedProvider> getProviders() {
+        return providers;
+    }
+
+    @NonNull
+    @Override
+    public List<ParsedAttribution> getAttributions() {
+        return attributions;
+    }
+
+    @NonNull
+    @Override
+    public List<ParsedPermission> getPermissions() {
+        return permissions;
+    }
+
+    @NonNull
+    @Override
+    public List<ParsedPermissionGroup> getPermissionGroups() {
+        return permissionGroups;
+    }
+
+    @NonNull
+    @Override
+    public List<ParsedInstrumentation> getInstrumentations() {
+        return instrumentations;
+    }
+
+    @NonNull
+    @Override
+    public List<Pair<String,ParsedIntentInfo>> getPreferredActivityFilters() {
+        return preferredActivityFilters;
+    }
+
+    @NonNull
+    @Override
+    public Map<String,ParsedProcess> getProcesses() {
+        return processes;
+    }
+
+    @Nullable
+    @Override
+    public Bundle getMetaData() {
+        return metaData;
+    }
+
+    private void addMimeGroupsFromComponent(ParsedComponent component) {
+        for (int i = component.getIntents().size() - 1; i >= 0; i--) {
+            IntentFilter filter = component.getIntents().get(i);
+            for (int groupIndex = filter.countMimeGroups() - 1; groupIndex >= 0; groupIndex--) {
+                mimeGroups = ArrayUtils.add(mimeGroups, filter.getMimeGroup(groupIndex));
+            }
+        }
+    }
+
+    @Override
+    @Nullable
+    public Set<String> getMimeGroups() {
+        return mimeGroups;
+    }
+
+    @Nullable
+    @Override
+    public String getVolumeUuid() {
+        return volumeUuid;
+    }
+
+    @Nullable
+    @Override
+    public PackageParser.SigningDetails getSigningDetails() {
+        return signingDetails;
+    }
+
+    @NonNull
+    @Override
+    public String getCodePath() {
+        return codePath;
+    }
+
+    @Override
+    public boolean isUse32BitAbi() {
+        return use32BitAbi;
+    }
+
+    @Override
+    public boolean isVisibleToInstantApps() {
+        return visibleToInstantApps;
+    }
+
+    @Override
+    public boolean isForceQueryable() {
+        return forceQueryable;
+    }
+
+    @NonNull
+    @Override
+    public List<Intent> getQueriesIntents() {
+        return queriesIntents;
+    }
+
+    @NonNull
+    @Override
+    public List<String> getQueriesPackages() {
+        return queriesPackages;
+    }
+
+    @NonNull
+    @Override
+    public Set<String> getQueriesProviders() {
+        return queriesProviders;
+    }
+
+    @Nullable
+    @Override
+    public String[] getSplitClassLoaderNames() {
+        return splitClassLoaderNames;
+    }
+
+    @Nullable
+    @Override
+    public String[] getSplitCodePaths() {
+        return splitCodePaths;
+    }
+
+    @Nullable
+    @Override
+    public SparseArray<int[]> getSplitDependencies() {
+        return splitDependencies;
+    }
+
+    @Nullable
+    @Override
+    public int[] getSplitFlags() {
+        return splitFlags;
+    }
+
+    @Nullable
+    @Override
+    public String[] getSplitNames() {
+        return splitNames;
+    }
+
+    @Nullable
+    @Override
+    public int[] getSplitRevisionCodes() {
+        return splitRevisionCodes;
+    }
+
+    @Nullable
+    @Override
+    public String getAppComponentFactory() {
+        return appComponentFactory;
+    }
+
+    @Nullable
+    @Override
+    public String getBackupAgentName() {
+        return backupAgentName;
+    }
+
+    @Override
+    public int getBanner() {
+        return banner;
+    }
+
+    @Override
+    public int getCategory() {
+        return category;
+    }
+
+    @Nullable
+    @Override
+    public String getClassLoaderName() {
+        return classLoaderName;
+    }
+
+    @Nullable
+    @Override
+    public String getClassName() {
+        return className;
+    }
+
+    @Override
+    public int getCompatibleWidthLimitDp() {
+        return compatibleWidthLimitDp;
+    }
+
+    @Override
+    public int getDescriptionRes() {
+        return descriptionRes;
+    }
+
+    @Override
+    public boolean isEnabled() {
+        return enabled;
+    }
+
+    @Override
+    public boolean isCrossProfile() {
+        return crossProfile;
+    }
+
+    @Override
+    public int getFullBackupContent() {
+        return fullBackupContent;
+    }
+
+    @Override
+    public int getIconRes() {
+        return iconRes;
+    }
+
+    @Override
+    public int getInstallLocation() {
+        return installLocation;
+    }
+
+    @Override
+    public int getLabelRes() {
+        return labelRes;
+    }
+
+    @Override
+    public int getLargestWidthLimitDp() {
+        return largestWidthLimitDp;
+    }
+
+    @Override
+    public int getLogo() {
+        return logo;
+    }
+
+    @Nullable
+    @Override
+    public String getManageSpaceActivityName() {
+        return manageSpaceActivityName;
+    }
+
+    @Override
+    public float getMaxAspectRatio() {
+        return maxAspectRatio;
+    }
+
+    @Override
+    public float getMinAspectRatio() {
+        return minAspectRatio;
+    }
+
+    @Nullable
+    @Override
+    public SparseIntArray getMinExtensionVersions() {
+        return minExtensionVersions;
+    }
+
+    @Override
+    public int getMinSdkVersion() {
+        return minSdkVersion;
+    }
+
+    @Override
+    public int getNetworkSecurityConfigRes() {
+        return networkSecurityConfigRes;
+    }
+
+    @Nullable
+    @Override
+    public CharSequence getNonLocalizedLabel() {
+        return nonLocalizedLabel;
+    }
+
+    @Nullable
+    @Override
+    public String getPermission() {
+        return permission;
+    }
+
+    @Override
+    public int getRequiresSmallestWidthDp() {
+        return requiresSmallestWidthDp;
+    }
+
+    @Override
+    public int getRoundIconRes() {
+        return roundIconRes;
+    }
+
+    @Override
+    public int getTargetSandboxVersion() {
+        return targetSandboxVersion;
+    }
+
+    @Override
+    public int getTargetSdkVersion() {
+        return targetSdkVersion;
+    }
+
+    @Nullable
+    @Override
+    public String getTaskAffinity() {
+        return taskAffinity;
+    }
+
+    @Override
+    public int getTheme() {
+        return theme;
+    }
+
+    @Override
+    public int getUiOptions() {
+        return uiOptions;
+    }
+
+    @Nullable
+    @Override
+    public String getZygotePreloadName() {
+        return zygotePreloadName;
+    }
+
+    @Override
+    public boolean isExternalStorage() {
+        return externalStorage;
+    }
+
+    @Override
+    public boolean isBaseHardwareAccelerated() {
+        return baseHardwareAccelerated;
+    }
+
+    @Override
+    public boolean isAllowBackup() {
+        return allowBackup;
+    }
+
+    @Override
+    public boolean isKillAfterRestore() {
+        return killAfterRestore;
+    }
+
+    @Override
+    public boolean isRestoreAnyVersion() {
+        return restoreAnyVersion;
+    }
+
+    @Override
+    public boolean isFullBackupOnly() {
+        return fullBackupOnly;
+    }
+
+    @Override
+    public boolean isPersistent() {
+        return persistent;
+    }
+
+    @Override
+    public boolean isDebuggable() {
+        return debuggable;
+    }
+
+    @Override
+    public boolean isVmSafeMode() {
+        return vmSafeMode;
+    }
+
+    @Override
+    public boolean isHasCode() {
+        return hasCode;
+    }
+
+    @Override
+    public boolean isAllowTaskReparenting() {
+        return allowTaskReparenting;
+    }
+
+    @Override
+    public boolean isAllowClearUserData() {
+        return allowClearUserData;
+    }
+
+    @Override
+    public boolean isLargeHeap() {
+        return largeHeap;
+    }
+
+    @Override
+    public boolean isUsesCleartextTraffic() {
+        return usesCleartextTraffic;
+    }
+
+    @Override
+    public boolean isSupportsRtl() {
+        return supportsRtl;
+    }
+
+    @Override
+    public boolean isTestOnly() {
+        return testOnly;
+    }
+
+    @Override
+    public boolean isMultiArch() {
+        return multiArch;
+    }
+
+    @Override
+    public boolean isExtractNativeLibs() {
+        return extractNativeLibs;
+    }
+
+    @Override
+    public boolean isGame() {
+        return game;
+    }
+
+    /**
+     * @see ParsingPackageRead#getResizeableActivity()
+     */
+    @Nullable
+    @Override
+    public Boolean getResizeableActivity() {
+        return resizeableActivity;
+    }
+
+    @Override
+    public boolean isStaticSharedLibrary() {
+        return staticSharedLibrary;
+    }
+
+    @Override
+    public boolean isOverlay() {
+        return overlay;
+    }
+
+    @Override
+    public boolean isIsolatedSplitLoading() {
+        return isolatedSplitLoading;
+    }
+
+    @Override
+    public boolean isHasDomainUrls() {
+        return hasDomainUrls;
+    }
+
+    @Override
+    public boolean isProfileableByShell() {
+        return profileableByShell;
+    }
+
+    @Override
+    public boolean isBackupInForeground() {
+        return backupInForeground;
+    }
+
+    @Override
+    public boolean isUseEmbeddedDex() {
+        return useEmbeddedDex;
+    }
+
+    @Override
+    public boolean isDefaultToDeviceProtectedStorage() {
+        return defaultToDeviceProtectedStorage;
+    }
+
+    @Override
+    public boolean isDirectBootAware() {
+        return directBootAware;
+    }
+
+    @Override
+    public int getGwpAsanMode() {
+        return gwpAsanMode;
+    }
+
+    @Override
+    public boolean isPartiallyDirectBootAware() {
+        return partiallyDirectBootAware;
+    }
+
+    @Override
+    public boolean isResizeableActivityViaSdkVersion() {
+        return resizeableActivityViaSdkVersion;
+    }
+
+    @Override
+    public boolean isAllowClearUserDataOnFailedRestore() {
+        return allowClearUserDataOnFailedRestore;
+    }
+
+    @Override
+    public boolean isAllowAudioPlaybackCapture() {
+        return allowAudioPlaybackCapture;
+    }
+
+    @Override
+    public boolean isRequestLegacyExternalStorage() {
+        return requestLegacyExternalStorage;
+    }
+
+    @Override
+    public boolean isUsesNonSdkApi() {
+        return usesNonSdkApi;
+    }
+
+    @Override
+    public boolean isHasFragileUserData() {
+        return hasFragileUserData;
+    }
+
+    @Override
+    public boolean isCantSaveState() {
+        return cantSaveState;
+    }
+
+    @Override
+    public boolean isAllowNativeHeapPointerTagging() {
+        return allowNativeHeapPointerTagging;
+    }
+
+    @Override
+    public int getAutoRevokePermissions() {
+        return autoRevokePermissions;
+    }
+
+    @Override
+    public boolean hasPreserveLegacyExternalStorage() {
+        return preserveLegacyExternalStorage;
+    }
+
+    @Override
+    public ParsingPackageImpl setBaseRevisionCode(int value) {
+        baseRevisionCode = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setCompileSdkVersion(int value) {
+        compileSdkVersion = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setRequiredForAllUsers(boolean value) {
+        requiredForAllUsers = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setOverlayPriority(int value) {
+        overlayPriority = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setOverlayIsStatic(boolean value) {
+        overlayIsStatic = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setStaticSharedLibVersion(long value) {
+        staticSharedLibVersion = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setSharedUserLabel(int value) {
+        sharedUserLabel = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setRestrictUpdateHash(@Nullable byte... value) {
+        restrictUpdateHash = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setUpgradeKeySets(@NonNull Set<String> value) {
+        upgradeKeySets = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setProcesses(@NonNull Map<String,ParsedProcess> value) {
+        processes = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setMetaData(@Nullable Bundle value) {
+        metaData = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setSigningDetails(@Nullable PackageParser.SigningDetails value) {
+        signingDetails = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setUse32BitAbi(boolean value) {
+        use32BitAbi = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setVisibleToInstantApps(boolean value) {
+        visibleToInstantApps = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setForceQueryable(boolean value) {
+        forceQueryable = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setBanner(int value) {
+        banner = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setCategory(int value) {
+        category = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setCompatibleWidthLimitDp(int value) {
+        compatibleWidthLimitDp = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setDescriptionRes(int value) {
+        descriptionRes = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setEnabled(boolean value) {
+        enabled = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setCrossProfile(boolean value) {
+        crossProfile = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setFullBackupContent(int value) {
+        fullBackupContent = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setIconRes(int value) {
+        iconRes = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setInstallLocation(int value) {
+        installLocation = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setLabelRes(int value) {
+        labelRes = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setLargestWidthLimitDp(int value) {
+        largestWidthLimitDp = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setLogo(int value) {
+        logo = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setMaxAspectRatio(float value) {
+        maxAspectRatio = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setMinAspectRatio(float value) {
+        minAspectRatio = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setMinExtensionVersions(@Nullable SparseIntArray value) {
+        minExtensionVersions = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setMinSdkVersion(int value) {
+        minSdkVersion = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setNetworkSecurityConfigRes(int value) {
+        networkSecurityConfigRes = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setNonLocalizedLabel(@Nullable CharSequence value) {
+        nonLocalizedLabel = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setRequiresSmallestWidthDp(int value) {
+        requiresSmallestWidthDp = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setRoundIconRes(int value) {
+        roundIconRes = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setTargetSandboxVersion(int value) {
+        targetSandboxVersion = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setTargetSdkVersion(int value) {
+        targetSdkVersion = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setTheme(int value) {
+        theme = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setUiOptions(int value) {
+        uiOptions = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setExternalStorage(boolean value) {
+        externalStorage = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setBaseHardwareAccelerated(boolean value) {
+        baseHardwareAccelerated = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setAllowBackup(boolean value) {
+        allowBackup = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setKillAfterRestore(boolean value) {
+        killAfterRestore = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setRestoreAnyVersion(boolean value) {
+        restoreAnyVersion = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setFullBackupOnly(boolean value) {
+        fullBackupOnly = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setPersistent(boolean value) {
+        persistent = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setDebuggable(boolean value) {
+        debuggable = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setVmSafeMode(boolean value) {
+        vmSafeMode = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setHasCode(boolean value) {
+        hasCode = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setAllowTaskReparenting(boolean value) {
+        allowTaskReparenting = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setAllowClearUserData(boolean value) {
+        allowClearUserData = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setLargeHeap(boolean value) {
+        largeHeap = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setUsesCleartextTraffic(boolean value) {
+        usesCleartextTraffic = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setSupportsRtl(boolean value) {
+        supportsRtl = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setTestOnly(boolean value) {
+        testOnly = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setMultiArch(boolean value) {
+        multiArch = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setExtractNativeLibs(boolean value) {
+        extractNativeLibs = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setGame(boolean value) {
+        game = value;
+        return this;
+    }
+
+    /**
+     * @see ParsingPackageRead#getResizeableActivity()
+     */
+    @Override
+    public ParsingPackageImpl setResizeableActivity(@Nullable Boolean value) {
+        resizeableActivity = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setStaticSharedLibrary(boolean value) {
+        staticSharedLibrary = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setOverlay(boolean value) {
+        overlay = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setIsolatedSplitLoading(boolean value) {
+        isolatedSplitLoading = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setHasDomainUrls(boolean value) {
+        hasDomainUrls = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setProfileableByShell(boolean value) {
+        profileableByShell = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setBackupInForeground(boolean value) {
+        backupInForeground = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setUseEmbeddedDex(boolean value) {
+        useEmbeddedDex = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setDefaultToDeviceProtectedStorage(boolean value) {
+        defaultToDeviceProtectedStorage = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setDirectBootAware(boolean value) {
+        directBootAware = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setGwpAsanMode(int value) {
+        gwpAsanMode = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setPartiallyDirectBootAware(boolean value) {
+        partiallyDirectBootAware = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setResizeableActivityViaSdkVersion(boolean value) {
+        resizeableActivityViaSdkVersion = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setAllowClearUserDataOnFailedRestore(boolean value) {
+        allowClearUserDataOnFailedRestore = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setAllowAudioPlaybackCapture(boolean value) {
+        allowAudioPlaybackCapture = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setRequestLegacyExternalStorage(boolean value) {
+        requestLegacyExternalStorage = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setUsesNonSdkApi(boolean value) {
+        usesNonSdkApi = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setHasFragileUserData(boolean value) {
+        hasFragileUserData = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setCantSaveState(boolean value) {
+        cantSaveState = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setAllowNativeHeapPointerTagging(boolean value) {
+        allowNativeHeapPointerTagging = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setAutoRevokePermissions(int value) {
+        autoRevokePermissions = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setPreserveLegacyExternalStorage(boolean value) {
+        preserveLegacyExternalStorage = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setVersionName(String versionName) {
+        this.versionName = versionName;
+        return this;
+    }
+
+    @Override
+    public ParsingPackage setCompileSdkVersionCodename(String compileSdkVersionCodename) {
+        this.compileSdkVersionCodeName = compileSdkVersionCodename;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setProcessName(String processName) {
+        this.processName = processName;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setRealPackage(@Nullable String realPackage) {
+        this.realPackage = realPackage;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setRestrictedAccountType(@Nullable String restrictedAccountType) {
+        this.restrictedAccountType = restrictedAccountType;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setOverlayTargetName(@Nullable String overlayTargetName) {
+        this.overlayTargetName = overlayTargetName;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setOverlayCategory(@Nullable String overlayCategory) {
+        this.overlayCategory = overlayCategory;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setAppComponentFactory(@Nullable String appComponentFactory) {
+        this.appComponentFactory = appComponentFactory;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setBackupAgentName(@Nullable String backupAgentName) {
+        this.backupAgentName = backupAgentName;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setClassLoaderName(@Nullable String classLoaderName) {
+        this.classLoaderName = classLoaderName;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setClassName(@Nullable String className) {
+        this.className = className;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setManageSpaceActivityName(@Nullable String manageSpaceActivityName) {
+        this.manageSpaceActivityName = manageSpaceActivityName;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setPermission(@Nullable String permission) {
+        this.permission = permission;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setTaskAffinity(@Nullable String taskAffinity) {
+        this.taskAffinity = taskAffinity;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setZygotePreloadName(@Nullable String zygotePreloadName) {
+        this.zygotePreloadName = zygotePreloadName;
+        return this;
+    }
+}
diff --git a/android/content/pm/parsing/ParsingPackageRead.java b/android/content/pm/parsing/ParsingPackageRead.java
new file mode 100644
index 0000000..5b53c18
--- /dev/null
+++ b/android/content/pm/parsing/ParsingPackageRead.java
@@ -0,0 +1,866 @@
+/*
+ * 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.content.pm.parsing;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ConfigurationInfo;
+import android.content.pm.FeatureGroupInfo;
+import android.content.pm.FeatureInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageParser;
+import android.content.pm.ServiceInfo;
+import android.content.pm.parsing.component.ParsedActivity;
+import android.content.pm.parsing.component.ParsedAttribution;
+import android.content.pm.parsing.component.ParsedInstrumentation;
+import android.content.pm.parsing.component.ParsedIntentInfo;
+import android.content.pm.parsing.component.ParsedPermission;
+import android.content.pm.parsing.component.ParsedPermissionGroup;
+import android.content.pm.parsing.component.ParsedProcess;
+import android.content.pm.parsing.component.ParsedProvider;
+import android.content.pm.parsing.component.ParsedService;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.util.ArraySet;
+import android.util.Pair;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+
+import com.android.internal.R;
+
+import java.security.PublicKey;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Everything written by {@link ParsingPackage} and readable back.
+ *
+ * @hide
+ */
+@SuppressWarnings("UnusedReturnValue")
+public interface ParsingPackageRead extends Parcelable {
+
+    /**
+     * @see ActivityInfo
+     * @see PackageInfo#activities
+     */
+    @NonNull
+    List<ParsedActivity> getActivities();
+
+    /**
+     * The names of packages to adopt ownership of permissions from, parsed under
+     * {@link PackageParser#TAG_ADOPT_PERMISSIONS}.
+     * @see R.styleable#AndroidManifestOriginalPackage_name
+     */
+    @NonNull
+    List<String> getAdoptPermissions();
+
+    /**
+     * @see PackageInfo#configPreferences
+     * @see R.styleable#AndroidManifestUsesConfiguration
+     */
+    @NonNull
+    List<ConfigurationInfo> getConfigPreferences();
+
+    @NonNull
+    List<ParsedAttribution> getAttributions();
+
+    /**
+     * @see PackageInfo#featureGroups
+     * @see R.styleable#AndroidManifestUsesFeature
+     */
+    @NonNull
+    List<FeatureGroupInfo> getFeatureGroups();
+
+    /**
+     * Permissions requested but not in the manifest. These may have been split or migrated from
+     * previous versions/definitions.
+     */
+    @NonNull
+    List<String> getImplicitPermissions();
+
+    /**
+     * @see android.content.pm.InstrumentationInfo
+     * @see PackageInfo#instrumentation
+     */
+    @NonNull
+    List<ParsedInstrumentation> getInstrumentations();
+
+    /**
+     * For use with {@link com.android.server.pm.KeySetManagerService}. Parsed in
+     * {@link PackageParser#TAG_KEY_SETS}.
+     * @see R.styleable#AndroidManifestKeySet
+     * @see R.styleable#AndroidManifestPublicKey
+     */
+    @NonNull
+    Map<String, ArraySet<PublicKey>> getKeySetMapping();
+
+    /**
+     * Library names this package is declared as, for use by other packages with "uses-library".
+     * @see R.styleable#AndroidManifestLibrary
+     */
+    @NonNull
+    List<String> getLibraryNames();
+
+    /**
+     * For system use to migrate from an old package name to a new one, moving over data
+     * if available.
+     * @see R.styleable#AndroidManifestOriginalPackage}
+     */
+    @NonNull
+    List<String> getOriginalPackages();
+
+    /**
+     * Map of overlayable name to actor name.
+     */
+    @NonNull
+    Map<String, String> getOverlayables();
+
+    /**
+     * @see android.content.pm.PermissionInfo
+     * @see PackageInfo#permissions
+     */
+    @NonNull
+    List<ParsedPermission> getPermissions();
+
+    /**
+     * @see android.content.pm.PermissionGroupInfo
+     */
+    @NonNull
+    List<ParsedPermissionGroup> getPermissionGroups();
+
+    /**
+     * Used to determine the default preferred handler of an {@link Intent}.
+     *
+     * Map of component className to intent info inside that component.
+     * TODO(b/135203078): Is this actually used/working?
+     */
+    @NonNull
+    List<Pair<String, ParsedIntentInfo>> getPreferredActivityFilters();
+
+    /**
+     * System protected broadcasts.
+     * @see R.styleable#AndroidManifestProtectedBroadcast
+     */
+    @NonNull
+    List<String> getProtectedBroadcasts();
+
+    /**
+     * @see android.content.pm.ProviderInfo
+     * @see PackageInfo#providers
+     */
+    @NonNull
+    List<ParsedProvider> getProviders();
+
+    /**
+     * @see android.content.pm.ProcessInfo
+     */
+    @NonNull
+    Map<String, ParsedProcess> getProcesses();
+
+    /**
+     * Since they share several attributes, receivers are parsed as {@link ParsedActivity}, even
+     * though they represent different functionality.
+     * TODO(b/135203078): Reconsider this and maybe make ParsedReceiver so it's not so confusing
+     * @see ActivityInfo
+     * @see PackageInfo#receivers
+     */
+    @NonNull
+    List<ParsedActivity> getReceivers();
+
+    /**
+     * @see PackageInfo#reqFeatures
+     * @see R.styleable#AndroidManifestUsesFeature
+     */
+    @NonNull
+    List<FeatureInfo> getReqFeatures();
+
+    /**
+     * All the permissions declared. This is an effective set, and may include permissions
+     * transformed from split/migrated permissions from previous versions, so may not be exactly
+     * what the package declares in its manifest.
+     * @see PackageInfo#requestedPermissions
+     * @see R.styleable#AndroidManifestUsesPermission
+     */
+    @NonNull
+    List<String> getRequestedPermissions();
+
+    /**
+     * Whether or not the app requested explicitly resizeable Activities.
+     * A null value means nothing was explicitly requested.
+     */
+    @Nullable
+    Boolean getResizeableActivity();
+
+    /**
+     * @see ServiceInfo
+     * @see PackageInfo#services
+     */
+    @NonNull
+    List<ParsedService> getServices();
+
+    /** @see R.styleable#AndroidManifestUsesLibrary */
+    @NonNull
+    List<String> getUsesLibraries();
+
+    /**
+     * Like {@link #getUsesLibraries()}, but marked optional by setting
+     * {@link R.styleable#AndroidManifestUsesLibrary_required} to false . Application is expected
+     * to handle absence manually.
+     * @see R.styleable#AndroidManifestUsesLibrary
+     */
+    @NonNull
+    List<String> getUsesOptionalLibraries();
+
+    /**
+     * TODO(b/135203078): Move static library stuff to an inner data class
+     * @see R.styleable#AndroidManifestUsesStaticLibrary
+     */
+    @NonNull
+    List<String> getUsesStaticLibraries();
+
+    /** @see R.styleable#AndroidManifestUsesStaticLibrary_certDigest */
+    @Nullable
+    String[][] getUsesStaticLibrariesCertDigests();
+
+    /** @see R.styleable#AndroidManifestUsesStaticLibrary_version */
+    @Nullable
+    long[] getUsesStaticLibrariesVersions();
+
+    /**
+     * Intents that this package may query or require and thus requires visibility into.
+     * @see R.styleable#AndroidManifestQueriesIntent
+     */
+    @NonNull
+    List<Intent> getQueriesIntents();
+
+    /**
+     * Other packages that this package may query or require and thus requires visibility into.
+     * @see R.styleable#AndroidManifestQueriesPackage
+     */
+    @NonNull
+    List<String> getQueriesPackages();
+
+    /**
+     * Authorities that this package may query or require and thus requires visibility into.
+     * @see R.styleable#AndroidManifestQueriesProvider
+     */
+    @NonNull
+    Set<String> getQueriesProviders();
+
+    /**
+     * We store the application meta-data independently to avoid multiple unwanted references
+     * TODO(b/135203078): What does this comment mean?
+     * TODO(b/135203078): Make all the Bundles immutable (and non-null by shared empty reference?)
+     */
+    @Nullable
+    Bundle getMetaData();
+
+    /** @see R.styleable#AndroidManifestApplication_forceQueryable */
+    boolean isForceQueryable();
+
+    /**
+     * @see ApplicationInfo#maxAspectRatio
+     * @see R.styleable#AndroidManifestApplication_maxAspectRatio
+     */
+    float getMaxAspectRatio();
+
+    /**
+     * @see ApplicationInfo#minAspectRatio
+     * @see R.styleable#AndroidManifestApplication_minAspectRatio
+     */
+    float getMinAspectRatio();
+
+    /**
+     * @see ApplicationInfo#permission
+     * @see R.styleable#AndroidManifestApplication_permission
+     */
+    @Nullable
+    String getPermission();
+
+    /**
+     * @see ApplicationInfo#processName
+     * @see R.styleable#AndroidManifestApplication_process
+     */
+    @NonNull
+    String getProcessName();
+
+    /**
+     * @see PackageInfo#sharedUserId
+     * @see R.styleable#AndroidManifest_sharedUserId
+     */
+    @Deprecated
+    @Nullable
+    String getSharedUserId();
+
+    /** @see R.styleable#AndroidManifestStaticLibrary_name */
+    @Nullable
+    String getStaticSharedLibName();
+
+    /**
+     * @see ApplicationInfo#taskAffinity
+     * @see R.styleable#AndroidManifestApplication_taskAffinity
+     */
+    @Nullable
+    String getTaskAffinity();
+
+    /**
+     * @see ApplicationInfo#targetSdkVersion
+     * @see R.styleable#AndroidManifestUsesSdk_targetSdkVersion
+     */
+    int getTargetSdkVersion();
+
+    /**
+     * @see ApplicationInfo#uiOptions
+     * @see R.styleable#AndroidManifestApplication_uiOptions
+     */
+    int getUiOptions();
+
+    boolean isCrossProfile();
+
+    boolean isResizeableActivityViaSdkVersion();
+
+    /** @see ApplicationInfo#FLAG_HARDWARE_ACCELERATED */
+    boolean isBaseHardwareAccelerated();
+
+    /**
+     * If omitted from manifest, returns true if {@link #getTargetSdkVersion()} >=
+     * {@link android.os.Build.VERSION_CODES#DONUT}.
+     * @see R.styleable#AndroidManifestSupportsScreens_resizeable
+     * @see ApplicationInfo#FLAG_RESIZEABLE_FOR_SCREENS
+     */
+    boolean isResizeable();
+
+    /** @see ApplicationInfo#PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE */
+    boolean isAllowAudioPlaybackCapture();
+
+    /** @see ApplicationInfo#FLAG_ALLOW_BACKUP */
+    boolean isAllowBackup();
+
+    /** @see ApplicationInfo#FLAG_ALLOW_CLEAR_USER_DATA */
+    boolean isAllowClearUserData();
+
+    /** @see ApplicationInfo#PRIVATE_FLAG_ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE */
+    boolean isAllowClearUserDataOnFailedRestore();
+
+    /** @see ApplicationInfo#FLAG_ALLOW_TASK_REPARENTING */
+    boolean isAllowTaskReparenting();
+
+    /**
+     * @see ApplicationInfo#PRIVATE_FLAG_IS_RESOURCE_OVERLAY
+     * @see ApplicationInfo#isResourceOverlay()
+     */
+    boolean isOverlay();
+
+    /** @see ApplicationInfo#PRIVATE_FLAG_BACKUP_IN_FOREGROUND */
+    boolean isBackupInForeground();
+
+    /** @see ApplicationInfo#PRIVATE_FLAG_CANT_SAVE_STATE */
+    boolean isCantSaveState();
+
+    /** @see ApplicationInfo#FLAG_DEBUGGABLE */
+    boolean isDebuggable();
+
+    /** @see ApplicationInfo#PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE */
+    boolean isDefaultToDeviceProtectedStorage();
+
+    /** @see ApplicationInfo#PRIVATE_FLAG_DIRECT_BOOT_AWARE */
+    boolean isDirectBootAware();
+
+    /** @see ApplicationInfo#FLAG_EXTERNAL_STORAGE */
+    boolean isExternalStorage();
+
+    /** @see ApplicationInfo#FLAG_EXTRACT_NATIVE_LIBS */
+    boolean isExtractNativeLibs();
+
+    /** @see ApplicationInfo#FLAG_FULL_BACKUP_ONLY */
+    boolean isFullBackupOnly();
+
+    /** @see ApplicationInfo#FLAG_HAS_CODE */
+    boolean isHasCode();
+
+    /** @see ApplicationInfo#PRIVATE_FLAG_HAS_FRAGILE_USER_DATA */
+    boolean isHasFragileUserData();
+
+    /** @see ApplicationInfo#FLAG_IS_GAME */
+    @Deprecated
+    boolean isGame();
+
+    /** @see ApplicationInfo#PRIVATE_FLAG_ISOLATED_SPLIT_LOADING */
+    boolean isIsolatedSplitLoading();
+
+    /** @see ApplicationInfo#FLAG_KILL_AFTER_RESTORE */
+    boolean isKillAfterRestore();
+
+    /** @see ApplicationInfo#FLAG_LARGE_HEAP */
+    boolean isLargeHeap();
+
+    /** @see ApplicationInfo#FLAG_MULTIARCH */
+    boolean isMultiArch();
+
+    /** @see ApplicationInfo#PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE */
+    boolean isPartiallyDirectBootAware();
+
+    /** @see ApplicationInfo#FLAG_PERSISTENT */
+    boolean isPersistent();
+
+    /** @see ApplicationInfo#PRIVATE_FLAG_PROFILEABLE_BY_SHELL */
+    boolean isProfileableByShell();
+
+    /** @see ApplicationInfo#PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE */
+    boolean isRequestLegacyExternalStorage();
+
+    /** @see ApplicationInfo#FLAG_RESTORE_ANY_VERSION */
+    boolean isRestoreAnyVersion();
+
+    // ParsingPackageRead setSplitHasCode(int splitIndex, boolean splitHasCode);
+
+    /** Flags of any split APKs; ordered by parsed splitName */
+    @Nullable
+    int[] getSplitFlags();
+
+    /** @see ApplicationInfo#splitSourceDirs */
+    @Nullable
+    String[] getSplitCodePaths();
+
+    /** @see ApplicationInfo#splitDependencies */
+    @Nullable
+    SparseArray<int[]> getSplitDependencies();
+
+    /**
+     * @see ApplicationInfo#splitNames
+     * @see PackageInfo#splitNames
+     */
+    @Nullable
+    String[] getSplitNames();
+
+    /** @see PackageInfo#splitRevisionCodes */
+    int[] getSplitRevisionCodes();
+
+    /** @see ApplicationInfo#PRIVATE_FLAG_STATIC_SHARED_LIBRARY */
+    boolean isStaticSharedLibrary();
+
+    /** @see ApplicationInfo#FLAG_SUPPORTS_RTL */
+    boolean isSupportsRtl();
+
+    /** @see ApplicationInfo#FLAG_TEST_ONLY */
+    boolean isTestOnly();
+
+    /** @see ApplicationInfo#PRIVATE_FLAG_USE_EMBEDDED_DEX */
+    boolean isUseEmbeddedDex();
+
+    /** @see ApplicationInfo#FLAG_USES_CLEARTEXT_TRAFFIC */
+    boolean isUsesCleartextTraffic();
+
+    /** @see ApplicationInfo#PRIVATE_FLAG_USES_NON_SDK_API */
+    boolean isUsesNonSdkApi();
+
+    /**
+     * Set if the any of components are visible to instant applications.
+     * @see R.styleable#AndroidManifestActivity_visibleToInstantApps
+     * @see R.styleable#AndroidManifestProvider_visibleToInstantApps
+     * @see R.styleable#AndroidManifestService_visibleToInstantApps
+     */
+    boolean isVisibleToInstantApps();
+
+    /** @see ApplicationInfo#FLAG_VM_SAFE_MODE */
+    boolean isVmSafeMode();
+
+    /**
+     * If omitted from manifest, returns true if {@link #getTargetSdkVersion()} >=
+     * {@link android.os.Build.VERSION_CODES#DONUT}.
+     * @see R.styleable#AndroidManifestSupportsScreens_anyDensity
+     * @see ApplicationInfo#FLAG_SUPPORTS_SCREEN_DENSITIES
+     */
+    boolean isAnyDensity();
+
+    /**
+     * @see ApplicationInfo#appComponentFactory
+     * @see R.styleable#AndroidManifestApplication_appComponentFactory
+     */
+    @Nullable
+    String getAppComponentFactory();
+
+    /**
+     * @see ApplicationInfo#backupAgentName
+     * @see R.styleable#AndroidManifestApplication_backupAgent
+     */
+    @Nullable
+    String getBackupAgentName();
+
+    /**
+     * @see ApplicationInfo#banner
+     * @see R.styleable#AndroidManifestApplication_banner
+     */
+    int getBanner();
+
+    /**
+     * @see ApplicationInfo#category
+     * @see R.styleable#AndroidManifestApplication_appCategory
+     */
+    int getCategory();
+
+    /**
+     * @see ApplicationInfo#classLoaderName
+     * @see R.styleable#AndroidManifestApplication_classLoader
+     */
+    @Nullable
+    String getClassLoaderName();
+
+    /**
+     * @see ApplicationInfo#className
+     * @see R.styleable#AndroidManifestApplication_name
+     */
+    @Nullable
+    String getClassName();
+
+    String getPackageName();
+
+    /** Path of base APK */
+    String getBaseCodePath();
+
+    /**
+     * Path where this package was found on disk. For monolithic packages
+     * this is path to single base APK file; for cluster packages this is
+     * path to the cluster directory.
+     */
+    @NonNull
+    String getCodePath();
+
+    /**
+     * @see ApplicationInfo#compatibleWidthLimitDp
+     * @see R.styleable#AndroidManifestSupportsScreens_compatibleWidthLimitDp
+     */
+    int getCompatibleWidthLimitDp();
+
+    /**
+     * @see ApplicationInfo#descriptionRes
+     * @see R.styleable#AndroidManifestApplication_description
+     */
+    int getDescriptionRes();
+
+    /**
+     * @see ApplicationInfo#enabled
+     * @see R.styleable#AndroidManifestApplication_enabled
+     */
+    boolean isEnabled();
+
+    /**
+     * @see ApplicationInfo#fullBackupContent
+     * @see R.styleable#AndroidManifestApplication_fullBackupContent
+     */
+    int getFullBackupContent();
+
+    /** @see ApplicationInfo#PRIVATE_FLAG_HAS_DOMAIN_URLS */
+    boolean isHasDomainUrls();
+
+    /**
+     * @see ApplicationInfo#iconRes
+     * @see R.styleable#AndroidManifestApplication_icon
+     */
+    int getIconRes();
+
+    /**
+     * @see ApplicationInfo#installLocation
+     * @see R.styleable#AndroidManifest_installLocation
+     */
+    int getInstallLocation();
+
+    /**
+     * @see ApplicationInfo#labelRes
+     * @see R.styleable#AndroidManifestApplication_label
+     */
+    int getLabelRes();
+
+    /**
+     * @see ApplicationInfo#largestWidthLimitDp
+     * @see R.styleable#AndroidManifestSupportsScreens_largestWidthLimitDp
+     */
+    int getLargestWidthLimitDp();
+
+    /**
+     * @see ApplicationInfo#logo
+     * @see R.styleable#AndroidManifestApplication_logo
+     */
+    int getLogo();
+
+    /**
+     * @see ApplicationInfo#manageSpaceActivityName
+     * @see R.styleable#AndroidManifestApplication_manageSpaceActivity
+     */
+    @Nullable
+    String getManageSpaceActivityName();
+
+    /**
+     * @see ApplicationInfo#minExtensionVersions
+     * @see R.styleable#AndroidManifestExtensionSdk
+     */
+    @Nullable
+    SparseIntArray getMinExtensionVersions();
+
+    /**
+     * @see ApplicationInfo#minSdkVersion
+     * @see R.styleable#AndroidManifestUsesSdk_minSdkVersion
+     */
+    int getMinSdkVersion();
+
+    /**
+     * @see ApplicationInfo#networkSecurityConfigRes
+     * @see R.styleable#AndroidManifestApplication_networkSecurityConfig
+     */
+    int getNetworkSecurityConfigRes();
+
+    /**
+     * If {@link R.styleable#AndroidManifestApplication_label} is a string literal, this is it.
+     * Otherwise, it's stored as {@link #getLabelRes()}.
+     * @see ApplicationInfo#nonLocalizedLabel
+     * @see R.styleable#AndroidManifestApplication_label
+     */
+    @Nullable
+    CharSequence getNonLocalizedLabel();
+
+    /**
+     * @see PackageInfo#overlayCategory
+     * @see R.styleable#AndroidManifestResourceOverlay_category
+     */
+    @Nullable
+    String getOverlayCategory();
+
+    /** @see PackageInfo#mOverlayIsStatic */
+    boolean isOverlayIsStatic();
+
+    /**
+     * @see PackageInfo#overlayPriority
+     * @see R.styleable#AndroidManifestResourceOverlay_priority
+     */
+    int getOverlayPriority();
+
+    /**
+     * @see PackageInfo#overlayTarget
+     * @see R.styleable#AndroidManifestResourceOverlay_targetPackage
+     */
+    @Nullable
+    String getOverlayTarget();
+
+    /**
+     * @see PackageInfo#targetOverlayableName
+     * @see R.styleable#AndroidManifestResourceOverlay_targetName
+     */
+    @Nullable
+    String getOverlayTargetName();
+
+    /**
+     * If a system app declares {@link #getOriginalPackages()}, and the app was previously installed
+     * under one of those original package names, the {@link #getPackageName()} system identifier
+     * will be changed to that previously installed name. This will then be non-null, set to the
+     * manifest package name, for tracking the package under its true name.
+     *
+     * TODO(b/135203078): Remove this in favor of checking originalPackages.isEmpty and
+     *  getManifestPackageName
+     */
+    @Nullable
+    String getRealPackage();
+
+    /**
+     * The required account type without which this application will not function.
+     *
+     * @see PackageInfo#requiredAccountType
+     * @see R.styleable#AndroidManifestApplication_requiredAccountType
+     */
+    @Nullable
+    String getRequiredAccountType();
+
+    /**
+     * @see PackageInfo#requiredForAllUsers
+     * @see R.styleable#AndroidManifestApplication_requiredForAllUsers
+     */
+    boolean isRequiredForAllUsers();
+
+    /**
+     * @see ApplicationInfo#requiresSmallestWidthDp
+     * @see R.styleable#AndroidManifestSupportsScreens_requiresSmallestWidthDp
+     */
+    int getRequiresSmallestWidthDp();
+
+    /**
+     * SHA-512 hash of the only APK that can be used to update a system package.
+     * @see R.styleable#AndroidManifestRestrictUpdate
+     */
+    @Nullable
+    byte[] getRestrictUpdateHash();
+
+    /**
+     * The restricted account authenticator type that is used by this application
+     *
+     * @see PackageInfo#restrictedAccountType
+     * @see R.styleable#AndroidManifestApplication_restrictedAccountType
+     */
+    @Nullable
+    String getRestrictedAccountType();
+
+    /**
+     * @see ApplicationInfo#roundIconRes
+     * @see R.styleable#AndroidManifestApplication_roundIcon
+     */
+    int getRoundIconRes();
+
+    /**
+     * @see PackageInfo#sharedUserLabel
+     * @see R.styleable#AndroidManifest_sharedUserLabel
+     */
+    @Deprecated
+    int getSharedUserLabel();
+
+    /**
+     * The signature data of all APKs in this package, which must be exactly the same across the
+     * base and splits.
+     */
+    PackageParser.SigningDetails getSigningDetails();
+
+    /**
+     * @see ApplicationInfo#splitClassLoaderNames
+     * @see R.styleable#AndroidManifestApplication_classLoader
+     */
+    @Nullable
+    String[] getSplitClassLoaderNames();
+
+    /** @see R.styleable#AndroidManifestStaticLibrary_version */
+    long getStaticSharedLibVersion();
+
+    /**
+     * If omitted from manifest, returns true if {@link #getTargetSdkVersion()} >=
+     * {@link android.os.Build.VERSION_CODES#DONUT}.
+     * @see R.styleable#AndroidManifestSupportsScreens_largeScreens
+     * @see ApplicationInfo#FLAG_SUPPORTS_LARGE_SCREENS
+     */
+    boolean isSupportsLargeScreens();
+
+    /**
+     * If omitted from manifest, returns true.
+     * @see R.styleable#AndroidManifestSupportsScreens_normalScreens
+     * @see ApplicationInfo#FLAG_SUPPORTS_NORMAL_SCREENS
+     */
+    boolean isSupportsNormalScreens();
+
+    /**
+     * If omitted from manifest, returns true if {@link #getTargetSdkVersion()} >=
+     * {@link android.os.Build.VERSION_CODES#DONUT}.
+     * @see R.styleable#AndroidManifestSupportsScreens_smallScreens
+     * @see ApplicationInfo#FLAG_SUPPORTS_SMALL_SCREENS
+     */
+    boolean isSupportsSmallScreens();
+
+    /**
+     * If omitted from manifest, returns true if {@link #getTargetSdkVersion()} >=
+     * {@link android.os.Build.VERSION_CODES#GINGERBREAD}.
+     * @see R.styleable#AndroidManifestSupportsScreens_xlargeScreens
+     * @see ApplicationInfo#FLAG_SUPPORTS_XLARGE_SCREENS
+     */
+    boolean isSupportsExtraLargeScreens();
+
+    /** @see ApplicationInfo#PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING */
+    boolean isAllowNativeHeapPointerTagging();
+
+    int getAutoRevokePermissions();
+
+    boolean hasPreserveLegacyExternalStorage();
+
+    /**
+     * @see ApplicationInfo#targetSandboxVersion
+     * @see R.styleable#AndroidManifest_targetSandboxVersion
+     */
+    @Deprecated
+    int getTargetSandboxVersion();
+
+    /**
+     * @see ApplicationInfo#theme
+     * @see R.styleable#AndroidManifestApplication_theme
+     */
+    int getTheme();
+
+    /**
+     * For use with {@link com.android.server.pm.KeySetManagerService}. Parsed in
+     * {@link PackageParser#TAG_KEY_SETS}.
+     * @see R.styleable#AndroidManifestUpgradeKeySet
+     */
+    @NonNull
+    Set<String> getUpgradeKeySets();
+
+    /**
+     * The install time abi override to choose 32bit abi's when multiple abi's
+     * are present. This is only meaningfull for multiarch applications.
+     * The use32bitAbi attribute is ignored if cpuAbiOverride is also set.
+     */
+    boolean isUse32BitAbi();
+
+    /** @see ApplicationInfo#volumeUuid */
+    @Nullable
+    String getVolumeUuid();
+
+    /** @see ApplicationInfo#zygotePreloadName */
+    @Nullable
+    String getZygotePreloadName();
+
+    /** Revision code of base APK */
+    int getBaseRevisionCode();
+
+    /** @see PackageInfo#versionName */
+    @Nullable
+    String getVersionName();
+
+    /** @see PackageInfo#versionCodeMajor */
+    @Nullable
+    int getVersionCode();
+
+    /** @see PackageInfo#versionCodeMajor */
+    @Nullable
+    int getVersionCodeMajor();
+
+    /**
+     * @see ApplicationInfo#compileSdkVersion
+     * @see R.styleable#AndroidManifest_compileSdkVersion
+     */
+    int getCompileSdkVersion();
+
+    /**
+     * @see ApplicationInfo#compileSdkVersionCodename
+     * @see R.styleable#AndroidManifest_compileSdkVersionCodename
+     */
+    @Nullable
+    String getCompileSdkVersionCodeName();
+
+    @Nullable
+    Set<String> getMimeGroups();
+
+    /**
+     * @see ApplicationInfo#gwpAsanMode
+     * @see R.styleable#AndroidManifest_gwpAsanMode
+     */
+    public int getGwpAsanMode();
+
+    // TODO(b/135203078): Hide and enforce going through PackageInfoUtils
+    ApplicationInfo toAppInfoWithoutState();
+
+    /**
+     * same as toAppInfoWithoutState except without flag computation.
+     */
+    ApplicationInfo toAppInfoWithoutStateWithoutFlags();
+}
diff --git a/android/content/pm/parsing/ParsingPackageUtils.java b/android/content/pm/parsing/ParsingPackageUtils.java
new file mode 100644
index 0000000..aeb4db4
--- /dev/null
+++ b/android/content/pm/parsing/ParsingPackageUtils.java
@@ -0,0 +1,2768 @@
+/*
+ * 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.content.pm.parsing;
+
+import static android.content.pm.ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+import static android.content.pm.PackageManager.FEATURE_WATCH;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_RESOURCES_ARSC_COMPRESSED;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
+import static android.os.Build.VERSION_CODES.DONUT;
+import static android.os.Build.VERSION_CODES.O;
+import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
+
+import android.annotation.AnyRes;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StyleableRes;
+import android.app.ActivityThread;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ConfigurationInfo;
+import android.content.pm.FeatureGroupInfo;
+import android.content.pm.FeatureInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageParser;
+import android.content.pm.PackageParser.PackageParserException;
+import android.content.pm.PackageParser.SigningDetails;
+import android.content.pm.Signature;
+import android.content.pm.parsing.component.ComponentParseUtils;
+import android.content.pm.parsing.component.ParsedActivity;
+import android.content.pm.parsing.component.ParsedActivityUtils;
+import android.content.pm.parsing.component.ParsedAttribution;
+import android.content.pm.parsing.component.ParsedAttributionUtils;
+import android.content.pm.parsing.component.ParsedInstrumentation;
+import android.content.pm.parsing.component.ParsedInstrumentationUtils;
+import android.content.pm.parsing.component.ParsedIntentInfo;
+import android.content.pm.parsing.component.ParsedIntentInfoUtils;
+import android.content.pm.parsing.component.ParsedMainComponent;
+import android.content.pm.parsing.component.ParsedPermission;
+import android.content.pm.parsing.component.ParsedPermissionGroup;
+import android.content.pm.parsing.component.ParsedPermissionUtils;
+import android.content.pm.parsing.component.ParsedProcess;
+import android.content.pm.parsing.component.ParsedProcessUtils;
+import android.content.pm.parsing.component.ParsedProvider;
+import android.content.pm.parsing.component.ParsedProviderUtils;
+import android.content.pm.parsing.component.ParsedService;
+import android.content.pm.parsing.component.ParsedServiceUtils;
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseInput.DeferredError;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.pm.parsing.result.ParseTypeImpl;
+import android.content.pm.permission.SplitPermissionInfoParcelable;
+import android.content.pm.split.DefaultSplitAssetLoader;
+import android.content.pm.split.SplitAssetDependencyLoader;
+import android.content.pm.split.SplitAssetLoader;
+import android.content.res.ApkAssets;
+import android.content.res.AssetManager;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.FileUtils;
+import android.os.RemoteException;
+import android.os.Trace;
+import android.os.ext.SdkExtensions;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.Pair;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+import android.util.TypedValue;
+import android.util.apk.ApkSignatureVerifier;
+
+import com.android.internal.R;
+import com.android.internal.os.ClassLoaderFactory;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.XmlUtils;
+
+import libcore.io.IoUtils;
+import libcore.util.EmptyArray;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
+import java.io.IOException;
+import java.security.PublicKey;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringTokenizer;
+
+/**
+ * TODO(b/135203078): Differentiate between parse_ methods and some add_ method for whether it
+ * mutates the passed-in component or not. Or consolidate so all parse_ methods mutate.
+ *
+ * @hide
+ */
+public class ParsingPackageUtils {
+
+    public static final String TAG = ParsingUtils.TAG;
+
+    /**
+     * For cases outside of PackageManagerService when an APK needs to be parsed as a one-off
+     * request, without caching the input object and without querying the internal system state
+     * for feature support.
+     */
+    @NonNull
+    public static ParseResult<ParsingPackage> parseDefaultOneTime(File file, int flags,
+            @NonNull ParseInput.Callback inputCallback, @NonNull Callback callback) {
+        if ((flags & (PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+                | PackageManager.MATCH_DIRECT_BOOT_AWARE)) == 0) {
+            // Caller expressed no opinion about what encryption
+            // aware/unaware components they want to see, so match both
+            flags |= PackageManager.MATCH_DIRECT_BOOT_AWARE
+                    | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+        }
+
+        ParseInput input = new ParseTypeImpl(inputCallback).reset();
+        ParseResult<ParsingPackage> result;
+
+
+        ParsingPackageUtils parser = new ParsingPackageUtils(false, null, null, callback);
+        try {
+            result = parser.parsePackage(input, file, flags);
+            if (result.isError()) {
+                return result;
+            }
+        } catch (PackageParser.PackageParserException e) {
+            return input.error(PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
+                    "Error parsing package", e);
+        }
+
+        try {
+            ParsingPackage pkg = result.getResult();
+            if ((flags & PackageManager.GET_SIGNATURES) != 0
+                    || (flags & PackageManager.GET_SIGNING_CERTIFICATES) != 0) {
+                ParsingPackageUtils.collectCertificates(pkg, false /* skipVerify */);
+            }
+
+            return input.success(pkg);
+        } catch (PackageParser.PackageParserException e) {
+            return input.error(PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
+                    "Error collecting package certificates", e);
+        }
+    }
+
+    private boolean mOnlyCoreApps;
+    private String[] mSeparateProcesses;
+    private DisplayMetrics mDisplayMetrics;
+    private Callback mCallback;
+
+    public ParsingPackageUtils(boolean onlyCoreApps, String[] separateProcesses,
+            DisplayMetrics displayMetrics, @NonNull Callback callback) {
+        mOnlyCoreApps = onlyCoreApps;
+        mSeparateProcesses = separateProcesses;
+        mDisplayMetrics = displayMetrics;
+        mCallback = callback;
+    }
+
+    /**
+     * Parse the package at the given location. Automatically detects if the
+     * package is a monolithic style (single APK file) or cluster style
+     * (directory of APKs).
+     * <p>
+     * This performs sanity checking on cluster style packages, such as
+     * requiring identical package name and version codes, a single base APK,
+     * and unique split names.
+     * <p>
+     * Note that this <em>does not</em> perform signature verification; that
+     * must be done separately in {@link #collectCertificates(ParsingPackageRead, boolean)}.
+     *
+     * If {@code useCaches} is true, the package parser might return a cached
+     * result from a previous parse of the same {@code packageFile} with the same
+     * {@code flags}. Note that this method does not check whether {@code packageFile}
+     * has changed since the last parse, it's up to callers to do so.
+     *
+     * @see PackageParser#parsePackageLite(File, int)
+     */
+    public ParseResult<ParsingPackage> parsePackage(ParseInput input, File packageFile,
+            int flags)
+            throws PackageParserException {
+        if (packageFile.isDirectory()) {
+            return parseClusterPackage(input, packageFile, flags);
+        } else {
+            return parseMonolithicPackage(input, packageFile, flags);
+        }
+    }
+
+    /**
+     * Parse all APKs contained in the given directory, treating them as a
+     * single package. This also performs sanity checking, such as requiring
+     * identical package name and version codes, a single base APK, and unique
+     * split names.
+     * <p>
+     * Note that this <em>does not</em> perform signature verification; that
+     * must be done separately in {@link #collectCertificates(ParsingPackageRead, boolean)}.
+     */
+    private ParseResult<ParsingPackage> parseClusterPackage(ParseInput input, File packageDir,
+            int flags) throws PackageParserException {
+        final PackageParser.PackageLite lite = ApkLiteParseUtils.parseClusterPackageLite(packageDir,
+                0);
+        if (mOnlyCoreApps && !lite.coreApp) {
+            return input.error(INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED,
+                    "Not a coreApp: " + packageDir);
+        }
+
+        // Build the split dependency tree.
+        SparseArray<int[]> splitDependencies = null;
+        final SplitAssetLoader assetLoader;
+        if (lite.isolatedSplits && !ArrayUtils.isEmpty(lite.splitNames)) {
+            try {
+                splitDependencies = SplitAssetDependencyLoader.createDependenciesFromPackage(lite);
+                assetLoader = new SplitAssetDependencyLoader(lite, splitDependencies, flags);
+            } catch (SplitAssetDependencyLoader.IllegalDependencyException e) {
+                return input.error(INSTALL_PARSE_FAILED_BAD_MANIFEST, e.getMessage());
+            }
+        } else {
+            assetLoader = new DefaultSplitAssetLoader(lite, flags);
+        }
+
+        try {
+            final AssetManager assets = assetLoader.getBaseAssetManager();
+            final File baseApk = new File(lite.baseCodePath);
+            ParseResult<ParsingPackage> result = parseBaseApk(input, baseApk,
+                    lite.codePath, assets, flags);
+            if (result.isError()) {
+                return input.error(result);
+            }
+
+            ParsingPackage pkg = result.getResult();
+            if (!ArrayUtils.isEmpty(lite.splitNames)) {
+                pkg.asSplit(
+                        lite.splitNames,
+                        lite.splitCodePaths,
+                        lite.splitRevisionCodes,
+                        splitDependencies
+                );
+                final int num = lite.splitNames.length;
+
+                for (int i = 0; i < num; i++) {
+                    final AssetManager splitAssets = assetLoader.getSplitAssetManager(i);
+                    parseSplitApk(input, pkg, i, splitAssets, flags);
+                }
+            }
+
+            pkg.setUse32BitAbi(lite.use32bitAbi);
+            return input.success(pkg);
+        } finally {
+            IoUtils.closeQuietly(assetLoader);
+        }
+    }
+
+    /**
+     * Parse the given APK file, treating it as as a single monolithic package.
+     * <p>
+     * Note that this <em>does not</em> perform signature verification; that
+     * must be done separately in {@link #collectCertificates(ParsingPackageRead, boolean)}.
+     */
+    private ParseResult<ParsingPackage> parseMonolithicPackage(ParseInput input, File apkFile,
+            int flags) throws PackageParserException {
+        final PackageParser.PackageLite lite = ApkLiteParseUtils.parseMonolithicPackageLite(apkFile,
+                flags);
+        if (mOnlyCoreApps && !lite.coreApp) {
+            return input.error(INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED,
+                    "Not a coreApp: " + apkFile);
+        }
+
+        final SplitAssetLoader assetLoader = new DefaultSplitAssetLoader(lite, flags);
+        try {
+            ParseResult<ParsingPackage> result = parseBaseApk(input,
+                    apkFile,
+                    apkFile.getCanonicalPath(),
+                    assetLoader.getBaseAssetManager(), flags);
+            if (result.isError()) {
+                return input.error(result);
+            }
+
+            return input.success(result.getResult()
+                    .setUse32BitAbi(lite.use32bitAbi));
+        } catch (IOException e) {
+            return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
+                    "Failed to get path: " + apkFile, e);
+        } finally {
+            IoUtils.closeQuietly(assetLoader);
+        }
+    }
+
+    private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, File apkFile,
+            String codePath, AssetManager assets, int flags) {
+        final String apkPath = apkFile.getAbsolutePath();
+
+        String volumeUuid = null;
+        if (apkPath.startsWith(PackageParser.MNT_EXPAND)) {
+            final int end = apkPath.indexOf('/', PackageParser.MNT_EXPAND.length());
+            volumeUuid = apkPath.substring(PackageParser.MNT_EXPAND.length(), end);
+        }
+
+        if (PackageParser.DEBUG_JAR) Slog.d(TAG, "Scanning base APK: " + apkPath);
+
+        final int cookie = assets.findCookieForPath(apkPath);
+        if (cookie == 0) {
+            return input.error(INSTALL_PARSE_FAILED_BAD_MANIFEST,
+                    "Failed adding asset path: " + apkPath);
+        }
+
+        try (XmlResourceParser parser = assets.openXmlResourceParser(cookie,
+                PackageParser.ANDROID_MANIFEST_FILENAME)) {
+            final Resources res = new Resources(assets, mDisplayMetrics, null);
+
+            ParseResult<ParsingPackage> result = parseBaseApk(input, apkPath, codePath, res,
+                    parser, flags);
+            if (result.isError()) {
+                return input.error(result.getErrorCode(),
+                        apkPath + " (at " + parser.getPositionDescription() + "): "
+                                + result.getErrorMessage());
+            }
+
+            final ParsingPackage pkg = result.getResult();
+            if (assets.containsAllocatedTable()) {
+                final ParseResult<?> deferResult = input.deferError(
+                        "Targeting R+ (version " + Build.VERSION_CODES.R + " and above) requires"
+                                + " the resources.arsc of installed APKs to be stored uncompressed"
+                                + " and aligned on a 4-byte boundary",
+                        DeferredError.RESOURCES_ARSC_COMPRESSED);
+                if (deferResult.isError()) {
+                    return input.error(INSTALL_PARSE_FAILED_RESOURCES_ARSC_COMPRESSED,
+                            deferResult.getErrorMessage());
+                }
+            }
+
+            ApkAssets apkAssets = assets.getApkAssets()[0];
+            if (apkAssets.definesOverlayable()) {
+                SparseArray<String> packageNames = assets.getAssignedPackageIdentifiers();
+                int size = packageNames.size();
+                for (int index = 0; index < size; index++) {
+                    String packageName = packageNames.get(index);
+                    Map<String, String> overlayableToActor = assets.getOverlayableMap(packageName);
+                    if (overlayableToActor != null && !overlayableToActor.isEmpty()) {
+                        for (String overlayable : overlayableToActor.keySet()) {
+                            pkg.addOverlayable(overlayable, overlayableToActor.get(overlayable));
+                        }
+                    }
+                }
+            }
+
+            pkg.setVolumeUuid(volumeUuid);
+
+            if ((flags & PackageParser.PARSE_COLLECT_CERTIFICATES) != 0) {
+                pkg.setSigningDetails(ParsingPackageUtils.collectCertificates(pkg, false));
+            } else {
+                pkg.setSigningDetails(SigningDetails.UNKNOWN);
+            }
+
+            return input.success(pkg);
+        } catch (Exception e) {
+            return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
+                    "Failed to read manifest from " + apkPath, e);
+        }
+    }
+
+    private ParseResult<ParsingPackage> parseSplitApk(ParseInput input,
+            ParsingPackage pkg, int splitIndex, AssetManager assets, int flags) {
+        final String apkPath = pkg.getSplitCodePaths()[splitIndex];
+
+        if (PackageParser.DEBUG_JAR) Slog.d(TAG, "Scanning split APK: " + apkPath);
+
+        // This must always succeed, as the path has been added to the AssetManager before.
+        final int cookie = assets.findCookieForPath(apkPath);
+        if (cookie == 0) {
+            return input.error(INSTALL_PARSE_FAILED_BAD_MANIFEST,
+                    "Failed adding asset path: " + apkPath);
+        }
+        try (XmlResourceParser parser = assets.openXmlResourceParser(cookie,
+                PackageParser.ANDROID_MANIFEST_FILENAME)) {
+            Resources res = new Resources(assets, mDisplayMetrics, null);
+            ParseResult<ParsingPackage> parseResult = parseSplitApk(input, pkg, res,
+                    parser, flags, splitIndex);
+            if (parseResult.isError()) {
+                return input.error(parseResult.getErrorCode(),
+                        apkPath + " (at " + parser.getPositionDescription() + "): "
+                                + parseResult.getErrorMessage());
+            }
+
+            return parseResult;
+        } catch (Exception e) {
+            return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
+                    "Failed to read manifest from " + apkPath, e);
+        }
+    }
+
+    /**
+     * Parse the manifest of a <em>base APK</em>. When adding new features you
+     * need to consider whether they should be supported by split APKs and child
+     * packages.
+     *
+     * @param apkPath The package apk file path
+     * @param res     The resources from which to resolve values
+     * @param parser  The manifest parser
+     * @param flags   Flags how to parse
+     * @return Parsed package or null on error.
+     */
+    private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, String apkPath,
+            String codePath, Resources res, XmlResourceParser parser, int flags)
+            throws XmlPullParserException, IOException, PackageParserException {
+        final String splitName;
+        final String pkgName;
+
+        try {
+            Pair<String, String> packageSplit = PackageParser.parsePackageSplitNames(parser,
+                    parser);
+            pkgName = packageSplit.first;
+            splitName = packageSplit.second;
+
+            if (!TextUtils.isEmpty(splitName)) {
+                return input.error(
+                        PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME,
+                        "Expected base APK, but found split " + splitName
+                );
+            }
+        } catch (PackageParserException e) {
+            return input.error(PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME);
+        }
+
+        final TypedArray manifestArray = res.obtainAttributes(parser, R.styleable.AndroidManifest);
+        try {
+            final boolean isCoreApp =
+                    parser.getAttributeBooleanValue(null, "coreApp", false);
+            final ParsingPackage pkg = mCallback.startParsingPackage(
+                    pkgName, apkPath, codePath, manifestArray, isCoreApp);
+            final ParseResult<ParsingPackage> result =
+                    parseBaseApkTags(input, pkg, manifestArray, res, parser, flags);
+            if (result.isError()) {
+                return result;
+            }
+
+            return input.success(pkg);
+        } finally {
+            manifestArray.recycle();
+        }
+    }
+
+    /**
+     * Parse the manifest of a <em>split APK</em>.
+     * <p>
+     * Note that split APKs have many more restrictions on what they're capable
+     * of doing, so many valid features of a base APK have been carefully
+     * omitted here.
+     *
+     * @param pkg builder to fill
+     * @return false on failure
+     */
+    private ParseResult<ParsingPackage> parseSplitApk(ParseInput input, ParsingPackage pkg,
+            Resources res, XmlResourceParser parser, int flags, int splitIndex)
+            throws XmlPullParserException, IOException, PackageParserException {
+        AttributeSet attrs = parser;
+
+        // We parsed manifest tag earlier; just skip past it
+        PackageParser.parsePackageSplitNames(parser, attrs);
+
+        int type;
+
+        boolean foundApp = false;
+
+        int outerDepth = parser.getDepth();
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
+            if (outerDepth + 1 < parser.getDepth() || type != XmlPullParser.START_TAG) {
+                continue;
+            }
+
+            final ParseResult result;
+            String tagName = parser.getName();
+            if (PackageParser.TAG_APPLICATION.equals(tagName)) {
+                if (foundApp) {
+                    if (PackageParser.RIGID_PARSER) {
+                        result = input.error("<manifest> has more than one <application>");
+                    } else {
+                        Slog.w(TAG, "<manifest> has more than one <application>");
+                        result = input.success(null);
+                    }
+                } else {
+                    foundApp = true;
+                    result = parseSplitApplication(input, pkg, res, parser, flags, splitIndex);
+                }
+            } else {
+                result = ParsingUtils.unknownTag("<manifest>", pkg, parser, input);
+            }
+
+            if (result.isError()) {
+                return input.error(result);
+            }
+        }
+
+        if (!foundApp) {
+            ParseResult<?> deferResult = input.deferError(
+                    "<manifest> does not contain an <application>", DeferredError.MISSING_APP_TAG);
+            if (deferResult.isError()) {
+                return input.error(deferResult);
+            }
+        }
+
+        return input.success(pkg);
+    }
+
+    /**
+     * Parse the {@code application} XML tree at the current parse location in a
+     * <em>split APK</em> manifest.
+     * <p>
+     * Note that split APKs have many more restrictions on what they're capable
+     * of doing, so many valid features of a base APK have been carefully
+     * omitted here.
+     */
+    private ParseResult<ParsingPackage> parseSplitApplication(ParseInput input,
+            ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags, int splitIndex)
+            throws XmlPullParserException, IOException {
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestApplication);
+        try {
+            pkg.setSplitHasCode(splitIndex, sa.getBoolean(
+                    R.styleable.AndroidManifestApplication_hasCode, true));
+
+            final String classLoaderName = sa.getString(
+                    R.styleable.AndroidManifestApplication_classLoader);
+            if (classLoaderName == null || ClassLoaderFactory.isValidClassLoaderName(
+                    classLoaderName)) {
+                pkg.setSplitClassLoaderName(splitIndex, classLoaderName);
+            } else {
+                return input.error("Invalid class loader name: " + classLoaderName);
+            }
+        } finally {
+            sa.recycle();
+        }
+        final int depth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG
+                || parser.getDepth() > depth)) {
+            if (type != XmlPullParser.START_TAG) {
+                continue;
+            }
+
+            ParsedMainComponent mainComponent = null;
+
+            final ParseResult result;
+            String tagName = parser.getName();
+            boolean isActivity = false;
+            switch (tagName) {
+                case "activity":
+                    isActivity = true;
+                    // fall-through
+                case "receiver":
+                    ParseResult<ParsedActivity> activityResult =
+                            ParsedActivityUtils.parseActivityOrReceiver(mSeparateProcesses, pkg,
+                                    res,
+                                    parser, flags, PackageParser.sUseRoundIcon, input);
+                    if (activityResult.isSuccess()) {
+                        ParsedActivity activity = activityResult.getResult();
+                        if (isActivity) {
+                            pkg.addActivity(activity);
+                        } else {
+                            pkg.addReceiver(activity);
+                        }
+                        mainComponent = activity;
+                    }
+                    result = activityResult;
+                    break;
+                case "service":
+                    ParseResult<ParsedService> serviceResult = ParsedServiceUtils.parseService(
+                            mSeparateProcesses, pkg, res, parser, flags,
+                            PackageParser.sUseRoundIcon, input);
+                    if (serviceResult.isSuccess()) {
+                        ParsedService service = serviceResult.getResult();
+                        pkg.addService(service);
+                        mainComponent = service;
+                    }
+                    result = serviceResult;
+                    break;
+                case "provider":
+                    ParseResult<ParsedProvider> providerResult =
+                            ParsedProviderUtils.parseProvider(mSeparateProcesses, pkg, res, parser,
+                                    flags, PackageParser.sUseRoundIcon, input);
+                    if (providerResult.isSuccess()) {
+                        ParsedProvider provider = providerResult.getResult();
+                        pkg.addProvider(provider);
+                        mainComponent = provider;
+                    }
+                    result = providerResult;
+                    break;
+                case "activity-alias":
+                    activityResult = ParsedActivityUtils.parseActivityAlias(pkg, res, parser,
+                            PackageParser.sUseRoundIcon, input);
+                    if (activityResult.isSuccess()) {
+                        ParsedActivity activity = activityResult.getResult();
+                        pkg.addActivity(activity);
+                        mainComponent = activity;
+                    }
+
+                    result = activityResult;
+                    break;
+                default:
+                    result = parseSplitBaseAppChildTags(input, tagName, pkg, res, parser);
+                    break;
+            }
+
+            if (result.isError()) {
+                return input.error(result);
+            }
+
+            if (mainComponent != null && mainComponent.getSplitName() == null) {
+                // If the loaded component did not specify a split, inherit the split name
+                // based on the split it is defined in.
+                // This is used to later load the correct split when starting this
+                // component.
+                mainComponent.setSplitName(pkg.getSplitNames()[splitIndex]);
+            }
+        }
+
+        return input.success(pkg);
+    }
+
+    /**
+     * For parsing non-MainComponents. Main ones have an order and some special handling which is
+     * done directly in {@link #parseSplitApplication(ParseInput, ParsingPackage, Resources,
+     * XmlResourceParser, int, int)}.
+     */
+    private ParseResult parseSplitBaseAppChildTags(ParseInput input, String tag, ParsingPackage pkg,
+            Resources res, XmlResourceParser parser) throws IOException, XmlPullParserException {
+        switch (tag) {
+            case "meta-data":
+                // note: application meta-data is stored off to the side, so it can
+                // remain null in the primary copy (we like to avoid extra copies because
+                // it can be large)
+                ParseResult<Bundle> metaDataResult = parseMetaData(pkg, res, parser,
+                        pkg.getMetaData(), input);
+                if (metaDataResult.isSuccess()) {
+                    pkg.setMetaData(metaDataResult.getResult());
+                }
+                return metaDataResult;
+            case "uses-static-library":
+                return parseUsesStaticLibrary(input, pkg, res, parser);
+            case "uses-library":
+                return parseUsesLibrary(input, pkg, res, parser);
+            case "uses-package":
+                // Dependencies for app installers; we don't currently try to
+                // enforce this.
+                return input.success(null);
+            default:
+                return ParsingUtils.unknownTag("<application>", pkg, parser, input);
+        }
+    }
+
+    private ParseResult<ParsingPackage> parseBaseApkTags(ParseInput input, ParsingPackage pkg,
+            TypedArray sa, Resources res, XmlResourceParser parser, int flags)
+            throws XmlPullParserException, IOException {
+        ParseResult<ParsingPackage> sharedUserResult = parseSharedUser(input, pkg, sa);
+        if (sharedUserResult.isError()) {
+            return sharedUserResult;
+        }
+
+        pkg.setInstallLocation(anInteger(PackageParser.PARSE_DEFAULT_INSTALL_LOCATION,
+                R.styleable.AndroidManifest_installLocation, sa))
+                .setTargetSandboxVersion(anInteger(PackageParser.PARSE_DEFAULT_TARGET_SANDBOX,
+                        R.styleable.AndroidManifest_targetSandboxVersion, sa))
+                /* Set the global "on SD card" flag */
+                .setExternalStorage((flags & PackageParser.PARSE_EXTERNAL_STORAGE) != 0);
+
+        boolean foundApp = false;
+        final int depth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG
+                || parser.getDepth() > depth)) {
+            if (type != XmlPullParser.START_TAG) {
+                continue;
+            }
+
+            String tagName = parser.getName();
+            final ParseResult result;
+
+            // TODO(b/135203078): Convert to instance methods to share variables
+            // <application> has special logic, so it's handled outside the general method
+            if (PackageParser.TAG_APPLICATION.equals(tagName)) {
+                if (foundApp) {
+                    if (PackageParser.RIGID_PARSER) {
+                        result = input.error("<manifest> has more than one <application>");
+                    } else {
+                        Slog.w(TAG, "<manifest> has more than one <application>");
+                        result = input.success(null);
+                    }
+                } else {
+                    foundApp = true;
+                    result = parseBaseApplication(input, pkg, res, parser, flags);
+                }
+            } else {
+                result = parseBaseApkTag(tagName, input, pkg, res, parser, flags);
+            }
+
+            if (result.isError()) {
+                return input.error(result);
+            }
+        }
+
+        if (!foundApp && ArrayUtils.size(pkg.getInstrumentations()) == 0) {
+            ParseResult<?> deferResult = input.deferError(
+                    "<manifest> does not contain an <application> or <instrumentation>",
+                    DeferredError.MISSING_APP_TAG);
+            if (deferResult.isError()) {
+                return input.error(deferResult);
+            }
+        }
+
+        if (!ParsedAttribution.isCombinationValid(pkg.getAttributions())) {
+            return input.error(
+                    INSTALL_PARSE_FAILED_BAD_MANIFEST,
+                    "Combination <feature> tags are not valid"
+            );
+        }
+
+        convertNewPermissions(pkg);
+
+        convertSplitPermissions(pkg);
+
+        // At this point we can check if an application is not supporting densities and hence
+        // cannot be windowed / resized. Note that an SDK version of 0 is common for
+        // pre-Doughnut applications.
+        if (pkg.getTargetSdkVersion() < DONUT
+                || (!pkg.isSupportsSmallScreens()
+                && !pkg.isSupportsNormalScreens()
+                && !pkg.isSupportsLargeScreens()
+                && !pkg.isSupportsExtraLargeScreens()
+                && !pkg.isResizeable()
+                && !pkg.isAnyDensity())) {
+            adjustPackageToBeUnresizeableAndUnpipable(pkg);
+        }
+
+        return input.success(pkg);
+    }
+
+    private ParseResult parseBaseApkTag(String tag, ParseInput input,
+            ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags)
+            throws IOException, XmlPullParserException {
+        switch (tag) {
+            case PackageParser.TAG_OVERLAY:
+                return parseOverlay(input, pkg, res, parser);
+            case PackageParser.TAG_KEY_SETS:
+                return parseKeySets(input, pkg, res, parser);
+            case "feature": // TODO moltmann: Remove
+            case PackageParser.TAG_ATTRIBUTION:
+                return parseAttribution(input, pkg, res, parser);
+            case PackageParser.TAG_PERMISSION_GROUP:
+                return parsePermissionGroup(input, pkg, res, parser);
+            case PackageParser.TAG_PERMISSION:
+                return parsePermission(input, pkg, res, parser);
+            case PackageParser.TAG_PERMISSION_TREE:
+                return parsePermissionTree(input, pkg, res, parser);
+            case PackageParser.TAG_USES_PERMISSION:
+            case PackageParser.TAG_USES_PERMISSION_SDK_M:
+            case PackageParser.TAG_USES_PERMISSION_SDK_23:
+                return parseUsesPermission(input, pkg, res, parser);
+            case PackageParser.TAG_USES_CONFIGURATION:
+                return parseUsesConfiguration(input, pkg, res, parser);
+            case PackageParser.TAG_USES_FEATURE:
+                return parseUsesFeature(input, pkg, res, parser);
+            case PackageParser.TAG_FEATURE_GROUP:
+                return parseFeatureGroup(input, pkg, res, parser);
+            case PackageParser.TAG_USES_SDK:
+                return parseUsesSdk(input, pkg, res, parser);
+            case PackageParser.TAG_SUPPORT_SCREENS:
+                return parseSupportScreens(input, pkg, res, parser);
+            case PackageParser.TAG_PROTECTED_BROADCAST:
+                return parseProtectedBroadcast(input, pkg, res, parser);
+            case PackageParser.TAG_INSTRUMENTATION:
+                return parseInstrumentation(input, pkg, res, parser);
+            case PackageParser.TAG_ORIGINAL_PACKAGE:
+                return parseOriginalPackage(input, pkg, res, parser);
+            case PackageParser.TAG_ADOPT_PERMISSIONS:
+                return parseAdoptPermissions(input, pkg, res, parser);
+            case PackageParser.TAG_USES_GL_TEXTURE:
+            case PackageParser.TAG_COMPATIBLE_SCREENS:
+            case PackageParser.TAG_SUPPORTS_INPUT:
+            case PackageParser.TAG_EAT_COMMENT:
+                // Just skip this tag
+                XmlUtils.skipCurrentTag(parser);
+                return input.success(pkg);
+            case PackageParser.TAG_RESTRICT_UPDATE:
+                return parseRestrictUpdateHash(flags, input, pkg, res, parser);
+            case PackageParser.TAG_QUERIES:
+                return parseQueries(input, pkg, res, parser);
+            default:
+                return ParsingUtils.unknownTag("<manifest>", pkg, parser, input);
+        }
+    }
+
+    private static ParseResult<ParsingPackage> parseSharedUser(ParseInput input,
+            ParsingPackage pkg, TypedArray sa) {
+        String str = nonConfigString(0, R.styleable.AndroidManifest_sharedUserId, sa);
+        if (TextUtils.isEmpty(str)) {
+            return input.success(pkg);
+        }
+
+        if (!"android".equals(pkg.getPackageName())) {
+            ParseResult<?> nameResult = validateName(input, str, true, true);
+            if (nameResult.isError()) {
+                return input.error(PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID,
+                        "<manifest> specifies bad sharedUserId name \"" + str + "\": "
+                                + nameResult.getErrorMessage());
+            }
+        }
+
+        return input.success(pkg
+                .setSharedUserId(str.intern())
+                .setSharedUserLabel(resId(R.styleable.AndroidManifest_sharedUserLabel, sa)));
+    }
+
+    private static ParseResult<ParsingPackage> parseKeySets(ParseInput input,
+            ParsingPackage pkg, Resources res, XmlResourceParser parser)
+            throws XmlPullParserException, IOException {
+        // we've encountered the 'key-sets' tag
+        // all the keys and keysets that we want must be defined here
+        // so we're going to iterate over the parser and pull out the things we want
+        int outerDepth = parser.getDepth();
+        int currentKeySetDepth = -1;
+        int type;
+        String currentKeySet = null;
+        ArrayMap<String, PublicKey> publicKeys = new ArrayMap<>();
+        ArraySet<String> upgradeKeySets = new ArraySet<>();
+        ArrayMap<String, ArraySet<String>> definedKeySets = new ArrayMap<>();
+        ArraySet<String> improperKeySets = new ArraySet<>();
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG) {
+                if (parser.getDepth() == currentKeySetDepth) {
+                    currentKeySet = null;
+                    currentKeySetDepth = -1;
+                }
+                continue;
+            }
+            String tagName = parser.getName();
+            switch (tagName) {
+                case "key-set": {
+                    if (currentKeySet != null) {
+                        return input.error("Improperly nested 'key-set' tag at "
+                                + parser.getPositionDescription());
+                    }
+                    TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestKeySet);
+                    try {
+                        final String keysetName = sa.getNonResourceString(
+                                R.styleable.AndroidManifestKeySet_name);
+                        definedKeySets.put(keysetName, new ArraySet<>());
+                        currentKeySet = keysetName;
+                        currentKeySetDepth = parser.getDepth();
+                    } finally {
+                        sa.recycle();
+                    }
+                } break;
+                case "public-key": {
+                    if (currentKeySet == null) {
+                        return input.error("Improperly nested 'key-set' tag at "
+                                + parser.getPositionDescription());
+                    }
+                    TypedArray sa = res.obtainAttributes(parser,
+                            R.styleable.AndroidManifestPublicKey);
+                    try {
+                        final String publicKeyName = nonResString(
+                                R.styleable.AndroidManifestPublicKey_name, sa);
+                        final String encodedKey = nonResString(
+                                R.styleable.AndroidManifestPublicKey_value, sa);
+                        if (encodedKey == null && publicKeys.get(publicKeyName) == null) {
+                            return input.error("'public-key' " + publicKeyName
+                                    + " must define a public-key value on first use at "
+                                    + parser.getPositionDescription());
+                        } else if (encodedKey != null) {
+                            PublicKey currentKey = PackageParser.parsePublicKey(encodedKey);
+                            if (currentKey == null) {
+                                Slog.w(TAG, "No recognized valid key in 'public-key' tag at "
+                                        + parser.getPositionDescription() + " key-set "
+                                        + currentKeySet
+                                        + " will not be added to the package's defined key-sets.");
+                                improperKeySets.add(currentKeySet);
+                                XmlUtils.skipCurrentTag(parser);
+                                continue;
+                            }
+                            if (publicKeys.get(publicKeyName) == null
+                                    || publicKeys.get(publicKeyName).equals(currentKey)) {
+
+                                /* public-key first definition, or matches old definition */
+                                publicKeys.put(publicKeyName, currentKey);
+                            } else {
+                                return input.error("Value of 'public-key' " + publicKeyName
+                                        + " conflicts with previously defined value at "
+                                        + parser.getPositionDescription());
+                            }
+                        }
+                        definedKeySets.get(currentKeySet).add(publicKeyName);
+                        XmlUtils.skipCurrentTag(parser);
+                    } finally {
+                        sa.recycle();
+                    }
+                } break;
+                case "upgrade-key-set": {
+                    TypedArray sa = res.obtainAttributes(parser,
+                            R.styleable.AndroidManifestUpgradeKeySet);
+                    try {
+                        String name = sa.getNonResourceString(
+                                R.styleable.AndroidManifestUpgradeKeySet_name);
+                        upgradeKeySets.add(name);
+                        XmlUtils.skipCurrentTag(parser);
+                    } finally {
+                        sa.recycle();
+                    }
+                } break;
+                default:
+                    ParseResult result = ParsingUtils.unknownTag("<key-sets>", pkg, parser,
+                            input);
+                    if (result.isError()) {
+                        return input.error(result);
+                    }
+                    break;
+            }
+        }
+        String packageName = pkg.getPackageName();
+        Set<String> publicKeyNames = publicKeys.keySet();
+        if (publicKeyNames.removeAll(definedKeySets.keySet())) {
+            return input.error("Package" + packageName
+                    + " AndroidManifest.xml 'key-set' and 'public-key' names must be distinct.");
+        }
+
+        for (ArrayMap.Entry<String, ArraySet<String>> e : definedKeySets.entrySet()) {
+            final String keySetName = e.getKey();
+            if (e.getValue().size() == 0) {
+                Slog.w(TAG, "Package" + packageName + " AndroidManifest.xml "
+                        + "'key-set' " + keySetName + " has no valid associated 'public-key'."
+                        + " Not including in package's defined key-sets.");
+                continue;
+            } else if (improperKeySets.contains(keySetName)) {
+                Slog.w(TAG, "Package" + packageName + " AndroidManifest.xml "
+                        + "'key-set' " + keySetName + " contained improper 'public-key'"
+                        + " tags. Not including in package's defined key-sets.");
+                continue;
+            }
+
+            for (String s : e.getValue()) {
+                pkg.addKeySet(keySetName, publicKeys.get(s));
+            }
+        }
+        if (pkg.getKeySetMapping().keySet().containsAll(upgradeKeySets)) {
+            pkg.setUpgradeKeySets(upgradeKeySets);
+        } else {
+            return input.error("Package" + packageName
+                    + " AndroidManifest.xml does not define all 'upgrade-key-set's .");
+        }
+
+        return input.success(pkg);
+    }
+
+    private static ParseResult<ParsingPackage> parseAttribution(ParseInput input,
+            ParsingPackage pkg, Resources res, XmlResourceParser parser)
+            throws IOException, XmlPullParserException {
+        ParseResult<ParsedAttribution> result = ParsedAttributionUtils.parseAttribution(res,
+                parser, input);
+        if (result.isError()) {
+            return input.error(result);
+        }
+        return input.success(pkg.addAttribution(result.getResult()));
+    }
+
+    private static ParseResult<ParsingPackage> parsePermissionGroup(ParseInput input,
+            ParsingPackage pkg, Resources res, XmlResourceParser parser)
+            throws XmlPullParserException, IOException {
+        ParseResult<ParsedPermissionGroup> result = ParsedPermissionUtils.parsePermissionGroup(
+                pkg, res, parser, PackageParser.sUseRoundIcon, input);
+        if (result.isError()) {
+            return input.error(result);
+        }
+        return input.success(pkg.addPermissionGroup(result.getResult()));
+    }
+
+    private static ParseResult<ParsingPackage> parsePermission(ParseInput input,
+            ParsingPackage pkg, Resources res, XmlResourceParser parser)
+            throws XmlPullParserException, IOException {
+        ParseResult<ParsedPermission> result = ParsedPermissionUtils.parsePermission(
+                pkg, res, parser, PackageParser.sUseRoundIcon, input);
+        if (result.isError()) {
+            return input.error(result);
+        }
+        return input.success(pkg.addPermission(result.getResult()));
+    }
+
+    private static ParseResult<ParsingPackage> parsePermissionTree(ParseInput input,
+            ParsingPackage pkg, Resources res, XmlResourceParser parser)
+            throws XmlPullParserException, IOException {
+        ParseResult<ParsedPermission> result = ParsedPermissionUtils.parsePermissionTree(
+                pkg, res, parser, PackageParser.sUseRoundIcon, input);
+        if (result.isError()) {
+            return input.error(result);
+        }
+        return input.success(pkg.addPermission(result.getResult()));
+    }
+
+    private ParseResult<ParsingPackage> parseUsesPermission(ParseInput input,
+            ParsingPackage pkg, Resources res, XmlResourceParser parser)
+            throws IOException, XmlPullParserException {
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesPermission);
+        try {
+            // Note: don't allow this value to be a reference to a resource
+            // that may change.
+            String name = sa.getNonResourceString(
+                    R.styleable.AndroidManifestUsesPermission_name);
+
+            int maxSdkVersion = 0;
+            TypedValue val = sa.peekValue(
+                    R.styleable.AndroidManifestUsesPermission_maxSdkVersion);
+            if (val != null) {
+                if (val.type >= TypedValue.TYPE_FIRST_INT && val.type <= TypedValue.TYPE_LAST_INT) {
+                    maxSdkVersion = val.data;
+                }
+            }
+
+            final String requiredFeature = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestUsesPermission_requiredFeature, 0);
+
+            final String requiredNotfeature = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestUsesPermission_requiredNotFeature,
+                    0);
+
+            XmlUtils.skipCurrentTag(parser);
+
+            // Can only succeed from here on out
+            ParseResult<ParsingPackage> success = input.success(pkg);
+
+            if (name == null) {
+                return success;
+            }
+
+            if ((maxSdkVersion != 0) && (maxSdkVersion < Build.VERSION.RESOURCES_SDK_INT)) {
+                return success;
+            }
+
+            // Only allow requesting this permission if the platform supports the given feature.
+            if (requiredFeature != null && mCallback != null && !mCallback.hasFeature(
+                    requiredFeature)) {
+                return success;
+            }
+
+            // Only allow requesting this permission if the platform doesn't support the given
+            // feature.
+            if (requiredNotfeature != null && mCallback != null
+                    && mCallback.hasFeature(requiredNotfeature)) {
+                return success;
+            }
+
+            if (!pkg.getRequestedPermissions().contains(name)) {
+                pkg.addRequestedPermission(name.intern());
+            } else {
+                Slog.w(TAG, "Ignoring duplicate uses-permissions/uses-permissions-sdk-m: "
+                        + name + " in package: " + pkg.getPackageName() + " at: "
+                        + parser.getPositionDescription());
+            }
+
+            return success;
+        } finally {
+            sa.recycle();
+        }
+    }
+
+    private static ParseResult<ParsingPackage> parseUsesConfiguration(ParseInput input,
+            ParsingPackage pkg, Resources res, XmlResourceParser parser) {
+        ConfigurationInfo cPref = new ConfigurationInfo();
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesConfiguration);
+        try {
+            cPref.reqTouchScreen = sa.getInt(
+                    R.styleable.AndroidManifestUsesConfiguration_reqTouchScreen,
+                    Configuration.TOUCHSCREEN_UNDEFINED);
+            cPref.reqKeyboardType = sa.getInt(
+                    R.styleable.AndroidManifestUsesConfiguration_reqKeyboardType,
+                    Configuration.KEYBOARD_UNDEFINED);
+            if (sa.getBoolean(
+                    R.styleable.AndroidManifestUsesConfiguration_reqHardKeyboard,
+                    false)) {
+                cPref.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_HARD_KEYBOARD;
+            }
+            cPref.reqNavigation = sa.getInt(
+                    R.styleable.AndroidManifestUsesConfiguration_reqNavigation,
+                    Configuration.NAVIGATION_UNDEFINED);
+            if (sa.getBoolean(
+                    R.styleable.AndroidManifestUsesConfiguration_reqFiveWayNav,
+                    false)) {
+                cPref.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_FIVE_WAY_NAV;
+            }
+            pkg.addConfigPreference(cPref);
+            return input.success(pkg);
+        } finally {
+            sa.recycle();
+        }
+    }
+
+    private static ParseResult<ParsingPackage> parseUsesFeature(ParseInput input,
+            ParsingPackage pkg, Resources res, XmlResourceParser parser) {
+        FeatureInfo fi = parseFeatureInfo(res, parser);
+        pkg.addReqFeature(fi);
+
+        if (fi.name == null) {
+            ConfigurationInfo cPref = new ConfigurationInfo();
+            cPref.reqGlEsVersion = fi.reqGlEsVersion;
+            pkg.addConfigPreference(cPref);
+        }
+
+        return input.success(pkg);
+    }
+
+    private static FeatureInfo parseFeatureInfo(Resources res, AttributeSet attrs) {
+        FeatureInfo fi = new FeatureInfo();
+        TypedArray sa = res.obtainAttributes(attrs, R.styleable.AndroidManifestUsesFeature);
+        try {
+            // Note: don't allow this value to be a reference to a resource
+            // that may change.
+            fi.name = sa.getNonResourceString(R.styleable.AndroidManifestUsesFeature_name);
+            fi.version = sa.getInt(R.styleable.AndroidManifestUsesFeature_version, 0);
+            if (fi.name == null) {
+                fi.reqGlEsVersion = sa.getInt(R.styleable.AndroidManifestUsesFeature_glEsVersion,
+                        FeatureInfo.GL_ES_VERSION_UNDEFINED);
+            }
+            if (sa.getBoolean(R.styleable.AndroidManifestUsesFeature_required, true)) {
+                fi.flags |= FeatureInfo.FLAG_REQUIRED;
+            }
+            return fi;
+        } finally {
+            sa.recycle();
+        }
+    }
+
+    private static ParseResult<ParsingPackage> parseFeatureGroup(ParseInput input,
+            ParsingPackage pkg, Resources res, XmlResourceParser parser)
+            throws IOException, XmlPullParserException {
+        FeatureGroupInfo group = new FeatureGroupInfo();
+        ArrayList<FeatureInfo> features = null;
+        final int depth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG
+                || parser.getDepth() > depth)) {
+            if (type != XmlPullParser.START_TAG) {
+                continue;
+            }
+
+            final String innerTagName = parser.getName();
+            if (innerTagName.equals("uses-feature")) {
+                FeatureInfo featureInfo = parseFeatureInfo(res, parser);
+                // FeatureGroups are stricter and mandate that
+                // any <uses-feature> declared are mandatory.
+                featureInfo.flags |= FeatureInfo.FLAG_REQUIRED;
+                features = ArrayUtils.add(features, featureInfo);
+            } else {
+                Slog.w(TAG,
+                        "Unknown element under <feature-group>: " + innerTagName +
+                                " at " + pkg.getBaseCodePath() + " " +
+                                parser.getPositionDescription());
+            }
+        }
+
+        if (features != null) {
+            group.features = new FeatureInfo[features.size()];
+            group.features = features.toArray(group.features);
+        }
+
+        pkg.addFeatureGroup(group);
+        return input.success(pkg);
+    }
+
+    private static ParseResult<ParsingPackage> parseUsesSdk(ParseInput input,
+            ParsingPackage pkg, Resources res, XmlResourceParser parser)
+            throws IOException, XmlPullParserException {
+        if (PackageParser.SDK_VERSION > 0) {
+            TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesSdk);
+            try {
+                int minVers = 1;
+                String minCode = null;
+                int targetVers = 0;
+                String targetCode = null;
+
+                TypedValue val = sa.peekValue(R.styleable.AndroidManifestUsesSdk_minSdkVersion);
+                if (val != null) {
+                    if (val.type == TypedValue.TYPE_STRING && val.string != null) {
+                        minCode = val.string.toString();
+                    } else {
+                        // If it's not a string, it's an integer.
+                        minVers = val.data;
+                    }
+                }
+
+                val = sa.peekValue(R.styleable.AndroidManifestUsesSdk_targetSdkVersion);
+                if (val != null) {
+                    if (val.type == TypedValue.TYPE_STRING && val.string != null) {
+                        targetCode = val.string.toString();
+                        if (minCode == null) {
+                            minCode = targetCode;
+                        }
+                    } else {
+                        // If it's not a string, it's an integer.
+                        targetVers = val.data;
+                    }
+                } else {
+                    targetVers = minVers;
+                    targetCode = minCode;
+                }
+
+                ParseResult<Integer> targetSdkVersionResult = computeTargetSdkVersion(
+                        targetVers, targetCode, PackageParser.SDK_CODENAMES, input);
+                if (targetSdkVersionResult.isError()) {
+                    return input.error(targetSdkVersionResult);
+                }
+
+                int targetSdkVersion = targetSdkVersionResult.getResult();
+
+                ParseResult<?> deferResult =
+                        input.enableDeferredError(pkg.getPackageName(), targetSdkVersion);
+                if (deferResult.isError()) {
+                    return input.error(deferResult);
+                }
+
+                ParseResult<Integer> minSdkVersionResult = computeMinSdkVersion(minVers, minCode,
+                        PackageParser.SDK_VERSION, PackageParser.SDK_CODENAMES, input);
+                if (minSdkVersionResult.isError()) {
+                    return input.error(minSdkVersionResult);
+                }
+
+                int minSdkVersion = minSdkVersionResult.getResult();
+
+                pkg.setMinSdkVersion(minSdkVersion)
+                        .setTargetSdkVersion(targetSdkVersion);
+
+                int type;
+                final int innerDepth = parser.getDepth();
+                SparseIntArray minExtensionVersions = null;
+                while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                        && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
+                    if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                        continue;
+                    }
+
+                    final ParseResult result;
+                    if (parser.getName().equals("extension-sdk")) {
+                        if (minExtensionVersions == null) {
+                            minExtensionVersions = new SparseIntArray();
+                        }
+                        result = parseExtensionSdk(input, res, parser, minExtensionVersions);
+                        XmlUtils.skipCurrentTag(parser);
+                    } else {
+                        result = ParsingUtils.unknownTag("<uses-sdk>", pkg, parser, input);
+                    }
+
+                    if (result.isError()) {
+                        return input.error(result);
+                    }
+                }
+                pkg.setMinExtensionVersions(exactSizedCopyOfSparseArray(minExtensionVersions));
+            } finally {
+                sa.recycle();
+            }
+        }
+        return input.success(pkg);
+    }
+
+    @Nullable
+    private static SparseIntArray exactSizedCopyOfSparseArray(@Nullable SparseIntArray input) {
+        if (input == null) {
+            return null;
+        }
+        SparseIntArray output = new SparseIntArray(input.size());
+        for (int i = 0; i < input.size(); i++) {
+            output.put(input.keyAt(i), input.valueAt(i));
+        }
+        return output;
+    }
+
+    private static ParseResult<SparseIntArray> parseExtensionSdk(
+            ParseInput input, Resources res, XmlResourceParser parser,
+            SparseIntArray minExtensionVersions) {
+        int sdkVersion;
+        int minVersion;
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestExtensionSdk);
+        try {
+            sdkVersion = sa.getInt(R.styleable.AndroidManifestExtensionSdk_sdkVersion, -1);
+            minVersion = sa.getInt(R.styleable.AndroidManifestExtensionSdk_minExtensionVersion, -1);
+        } finally {
+            sa.recycle();
+        }
+
+        if (sdkVersion < 0) {
+            return input.error(
+                    PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                    "<extension-sdk> must specify an sdkVersion >= 0");
+        }
+        if (minVersion < 0) {
+            return input.error(
+                    PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                    "<extension-sdk> must specify minExtensionVersion >= 0");
+        }
+
+        try {
+            int version = SdkExtensions.getExtensionVersion(sdkVersion);
+            if (version < minVersion) {
+                return input.error(
+                        PackageManager.INSTALL_FAILED_OLDER_SDK,
+                        "Package requires " + sdkVersion + " extension version " + minVersion
+                                + " which exceeds device version " + version);
+            }
+        } catch (RuntimeException e) {
+            return input.error(
+                    PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                    "Specified sdkVersion " + sdkVersion + " is not valid");
+        }
+        minExtensionVersions.put(sdkVersion, minVersion);
+        return input.success(minExtensionVersions);
+    }
+
+    /**
+     * {@link ParseResult} version of
+     * {@link PackageParser#computeMinSdkVersion(int, String, int, String[], String[])}
+     */
+    public static ParseResult<Integer> computeMinSdkVersion(@IntRange(from = 1) int minVers,
+            @Nullable String minCode, @IntRange(from = 1) int platformSdkVersion,
+            @NonNull String[] platformSdkCodenames, @NonNull ParseInput input) {
+        // If it's a release SDK, make sure we meet the minimum SDK requirement.
+        if (minCode == null) {
+            if (minVers <= platformSdkVersion) {
+                return input.success(minVers);
+            }
+
+            // We don't meet the minimum SDK requirement.
+            return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK,
+                    "Requires newer sdk version #" + minVers
+                            + " (current version is #" + platformSdkVersion + ")");
+        }
+
+        // If it's a pre-release SDK and the codename matches this platform, we
+        // definitely meet the minimum SDK requirement.
+        if (matchTargetCode(platformSdkCodenames, minCode)) {
+            return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);
+        }
+
+        // Otherwise, we're looking at an incompatible pre-release SDK.
+        if (platformSdkCodenames.length > 0) {
+            return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK,
+                    "Requires development platform " + minCode
+                            + " (current platform is any of "
+                            + Arrays.toString(platformSdkCodenames) + ")");
+        } else {
+            return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK,
+                    "Requires development platform " + minCode
+                            + " but this is a release platform.");
+        }
+    }
+
+    /**
+     * {@link ParseResult} version of
+     * {@link PackageParser#computeTargetSdkVersion(int, String, String[], String[])}
+     */
+    public static ParseResult<Integer> computeTargetSdkVersion(@IntRange(from = 0) int targetVers,
+            @Nullable String targetCode, @NonNull String[] platformSdkCodenames,
+            @NonNull ParseInput input) {
+        // If it's a release SDK, return the version number unmodified.
+        if (targetCode == null) {
+            return input.success(targetVers);
+        }
+
+        // If it's a pre-release SDK and the codename matches this platform, it
+        // definitely targets this SDK.
+        if (matchTargetCode(platformSdkCodenames, targetCode)) {
+            return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);
+        }
+
+        // Otherwise, we're looking at an incompatible pre-release SDK.
+        if (platformSdkCodenames.length > 0) {
+            return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK,
+                    "Requires development platform " + targetCode
+                            + " (current platform is any of "
+                            + Arrays.toString(platformSdkCodenames) + ")");
+        } else {
+            return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK,
+                    "Requires development platform " + targetCode
+                            + " but this is a release platform.");
+        }
+    }
+
+    /**
+     * Matches a given {@code targetCode} against a set of release codeNames. Target codes can
+     * either be of the form {@code [codename]}" (e.g {@code "Q"}) or of the form
+     * {@code [codename].[fingerprint]} (e.g {@code "Q.cafebc561"}).
+     */
+    private static boolean matchTargetCode(@NonNull String[] codeNames,
+            @NonNull String targetCode) {
+        final String targetCodeName;
+        final int targetCodeIdx = targetCode.indexOf('.');
+        if (targetCodeIdx == -1) {
+            targetCodeName = targetCode;
+        } else {
+            targetCodeName = targetCode.substring(0, targetCodeIdx);
+        }
+        return ArrayUtils.contains(codeNames, targetCodeName);
+    }
+
+    private static ParseResult<ParsingPackage> parseRestrictUpdateHash(int flags, ParseInput input,
+            ParsingPackage pkg, Resources res, XmlResourceParser parser) {
+        if ((flags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0) {
+            TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestRestrictUpdate);
+            try {
+                final String hash = sa.getNonConfigurationString(
+                        R.styleable.AndroidManifestRestrictUpdate_hash,
+                        0);
+
+                if (hash != null) {
+                    final int hashLength = hash.length();
+                    final byte[] hashBytes = new byte[hashLength / 2];
+                    for (int i = 0; i < hashLength; i += 2) {
+                        hashBytes[i / 2] = (byte) ((Character.digit(hash.charAt(i), 16)
+                                << 4)
+                                + Character.digit(hash.charAt(i + 1), 16));
+                    }
+                    pkg.setRestrictUpdateHash(hashBytes);
+                } else {
+                    pkg.setRestrictUpdateHash(null);
+                }
+            } finally {
+                sa.recycle();
+            }
+        }
+        return input.success(pkg);
+    }
+
+    private static ParseResult<ParsingPackage> parseQueries(ParseInput input, ParsingPackage pkg,
+            Resources res, XmlResourceParser parser) throws IOException, XmlPullParserException {
+        final int depth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG
+                || parser.getDepth() > depth)) {
+            if (type != XmlPullParser.START_TAG) {
+                continue;
+            }
+            if (parser.getName().equals("intent")) {
+                ParseResult<ParsedIntentInfo> result = ParsedIntentInfoUtils.parseIntentInfo(null,
+                        pkg, res, parser, true /*allowGlobs*/, true /*allowAutoVerify*/, input);
+                if (result.isError()) {
+                    return input.error(result);
+                }
+
+                ParsedIntentInfo intentInfo = result.getResult();
+
+                Uri data = null;
+                String dataType = null;
+                String host = IntentFilter.WILDCARD;
+                final int numActions = intentInfo.countActions();
+                final int numSchemes = intentInfo.countDataSchemes();
+                final int numTypes = intentInfo.countDataTypes();
+                final int numHosts = intentInfo.getHosts().length;
+                if ((numSchemes == 0 && numTypes == 0 && numActions == 0)) {
+                    return input.error("intent tags must contain either an action or data.");
+                }
+                if (numActions > 1) {
+                    return input.error("intent tag may have at most one action.");
+                }
+                if (numTypes > 1) {
+                    return input.error("intent tag may have at most one data type.");
+                }
+                if (numSchemes > 1) {
+                    return input.error("intent tag may have at most one data scheme.");
+                }
+                if (numHosts > 1) {
+                    return input.error("intent tag may have at most one data host.");
+                }
+                Intent intent = new Intent();
+                for (int i = 0, max = intentInfo.countCategories(); i < max; i++) {
+                    intent.addCategory(intentInfo.getCategory(i));
+                }
+                if (numHosts == 1) {
+                    host = intentInfo.getHosts()[0];
+                }
+                if (numSchemes == 1) {
+                    data = new Uri.Builder()
+                            .scheme(intentInfo.getDataScheme(0))
+                            .authority(host)
+                            .path(IntentFilter.WILDCARD_PATH)
+                            .build();
+                }
+                if (numTypes == 1) {
+                    dataType = intentInfo.getDataType(0);
+                    // The dataType may have had the '/' removed for the dynamic mimeType feature.
+                    // If we detect that case, we add the * back.
+                    if (!dataType.contains("/")) {
+                        dataType = dataType + "/*";
+                    }
+                    if (data == null) {
+                        data = new Uri.Builder()
+                                .scheme("content")
+                                .authority(IntentFilter.WILDCARD)
+                                .path(IntentFilter.WILDCARD_PATH)
+                                .build();
+                    }
+                }
+                intent.setDataAndType(data, dataType);
+                if (numActions == 1) {
+                    intent.setAction(intentInfo.getAction(0));
+                }
+                pkg.addQueriesIntent(intent);
+            } else if (parser.getName().equals("package")) {
+                final TypedArray sa = res.obtainAttributes(parser,
+                        R.styleable.AndroidManifestQueriesPackage);
+                final String packageName = sa.getNonConfigurationString(
+                        R.styleable.AndroidManifestQueriesPackage_name, 0);
+                if (TextUtils.isEmpty(packageName)) {
+                    return input.error("Package name is missing from package tag.");
+                }
+                pkg.addQueriesPackage(packageName.intern());
+            } else if (parser.getName().equals("provider")) {
+                final TypedArray sa = res.obtainAttributes(parser,
+                        R.styleable.AndroidManifestQueriesProvider);
+                try {
+                    final String authorities = sa.getNonConfigurationString(
+                            R.styleable.AndroidManifestQueriesProvider_authorities, 0);
+                    if (TextUtils.isEmpty(authorities)) {
+                        return input.error(
+                                PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                                "Authority missing from provider tag."
+                        );
+                    }
+                    StringTokenizer authoritiesTokenizer = new StringTokenizer(authorities, ";");
+                    while (authoritiesTokenizer.hasMoreElements()) {
+                        pkg.addQueriesProvider(authoritiesTokenizer.nextToken());
+                    }
+                } finally {
+                    sa.recycle();
+                }
+            }
+        }
+        return input.success(pkg);
+    }
+
+    /**
+     * Parse the {@code application} XML tree at the current parse location in a
+     * <em>base APK</em> manifest.
+     * <p>
+     * When adding new features, carefully consider if they should also be
+     * supported by split APKs.
+     *
+     * This method should avoid using a getter for fields set by this method. Prefer assigning
+     * a local variable and using it. Otherwise there's an ordering problem which can be broken
+     * if any code moves around.
+     */
+    private ParseResult<ParsingPackage> parseBaseApplication(ParseInput input,
+            ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags)
+            throws XmlPullParserException, IOException {
+        final String pkgName = pkg.getPackageName();
+        int targetSdk = pkg.getTargetSdkVersion();
+
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestApplication);
+        try {
+            // TODO(b/135203078): Remove this and force unit tests to mock an empty manifest
+            // This case can only happen in unit tests where we sometimes need to create fakes
+            // of various package parser data structures.
+            if (sa == null) {
+                return input.error("<application> does not contain any attributes");
+            }
+
+            String name = sa.getNonConfigurationString(R.styleable.AndroidManifestApplication_name,
+                    0);
+            if (name != null) {
+                String packageName = pkg.getPackageName();
+                String outInfoName = ParsingUtils.buildClassName(packageName, name);
+                if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(outInfoName)) {
+                    return input.error("<application> invalid android:name");
+                } else if (outInfoName == null) {
+                    return input.error("Empty class name in package " + packageName);
+                }
+
+                pkg.setClassName(outInfoName);
+            }
+
+            TypedValue labelValue = sa.peekValue(R.styleable.AndroidManifestApplication_label);
+            if (labelValue != null) {
+                pkg.setLabelRes(labelValue.resourceId);
+                if (labelValue.resourceId == 0) {
+                    pkg.setNonLocalizedLabel(labelValue.coerceToString());
+                }
+            }
+
+            parseBaseAppBasicFlags(pkg, sa);
+
+            String manageSpaceActivity = nonConfigString(Configuration.NATIVE_CONFIG_VERSION,
+                    R.styleable.AndroidManifestApplication_manageSpaceActivity, sa);
+            if (manageSpaceActivity != null) {
+                String manageSpaceActivityName = ParsingUtils.buildClassName(pkgName,
+                        manageSpaceActivity);
+
+                if (manageSpaceActivityName == null) {
+                    return input.error("Empty class name in package " + pkgName);
+                }
+
+                pkg.setManageSpaceActivityName(manageSpaceActivityName);
+            }
+
+            if (pkg.isAllowBackup()) {
+                // backupAgent, killAfterRestore, fullBackupContent, backupInForeground,
+                // and restoreAnyVersion are only relevant if backup is possible for the
+                // given application.
+                String backupAgent = nonConfigString(Configuration.NATIVE_CONFIG_VERSION,
+                        R.styleable.AndroidManifestApplication_backupAgent, sa);
+                if (backupAgent != null) {
+                    String backupAgentName = ParsingUtils.buildClassName(pkgName, backupAgent);
+                    if (backupAgentName == null) {
+                        return input.error("Empty class name in package " + pkgName);
+                    }
+
+                    if (PackageParser.DEBUG_BACKUP) {
+                        Slog.v(TAG, "android:backupAgent = " + backupAgentName
+                                + " from " + pkgName + "+" + backupAgent);
+                    }
+
+                    pkg.setBackupAgentName(backupAgentName)
+                            .setKillAfterRestore(bool(true,
+                                    R.styleable.AndroidManifestApplication_killAfterRestore, sa))
+                            .setRestoreAnyVersion(bool(false,
+                                    R.styleable.AndroidManifestApplication_restoreAnyVersion, sa))
+                            .setFullBackupOnly(bool(false,
+                                    R.styleable.AndroidManifestApplication_fullBackupOnly, sa))
+                            .setBackupInForeground(bool(false,
+                                    R.styleable.AndroidManifestApplication_backupInForeground, sa));
+                }
+
+                TypedValue v = sa.peekValue(
+                        R.styleable.AndroidManifestApplication_fullBackupContent);
+                int fullBackupContent = 0;
+
+                if (v != null) {
+                    fullBackupContent = v.resourceId;
+
+                    if (v.resourceId == 0) {
+                        if (PackageParser.DEBUG_BACKUP) {
+                            Slog.v(TAG, "fullBackupContent specified as boolean=" +
+                                    (v.data == 0 ? "false" : "true"));
+                        }
+                        // "false" => -1, "true" => 0
+                        fullBackupContent = v.data == 0 ? -1 : 0;
+                    }
+
+                    pkg.setFullBackupContent(fullBackupContent);
+                }
+                if (PackageParser.DEBUG_BACKUP) {
+                    Slog.v(TAG, "fullBackupContent=" + fullBackupContent + " for " + pkgName);
+                }
+            }
+
+            if (sa.getBoolean(R.styleable.AndroidManifestApplication_persistent, false)) {
+                // Check if persistence is based on a feature being present
+                final String requiredFeature = sa.getNonResourceString(R.styleable
+                        .AndroidManifestApplication_persistentWhenFeatureAvailable);
+                pkg.setPersistent(requiredFeature == null || mCallback.hasFeature(requiredFeature));
+            }
+
+            // TODO(b/135203078): Should parsing code be responsible for this? Maybe move to a
+            //  util or just have PackageImpl return true if either flag is set
+            // Debuggable implies profileable
+            pkg.setProfileableByShell(pkg.isProfileableByShell() || pkg.isDebuggable());
+
+            if (sa.hasValueOrEmpty(R.styleable.AndroidManifestApplication_resizeableActivity)) {
+                pkg.setResizeableActivity(sa.getBoolean(
+                        R.styleable.AndroidManifestApplication_resizeableActivity, true));
+            } else {
+                pkg.setResizeableActivityViaSdkVersion(
+                        targetSdk >= Build.VERSION_CODES.N);
+            }
+
+            String taskAffinity;
+            if (targetSdk >= Build.VERSION_CODES.FROYO) {
+                taskAffinity = sa.getNonConfigurationString(
+                        R.styleable.AndroidManifestApplication_taskAffinity,
+                        Configuration.NATIVE_CONFIG_VERSION);
+            } else {
+                // Some older apps have been seen to use a resource reference
+                // here that on older builds was ignored (with a warning).  We
+                // need to continue to do this for them so they don't break.
+                taskAffinity = sa.getNonResourceString(
+                        R.styleable.AndroidManifestApplication_taskAffinity);
+            }
+
+            ParseResult<String> taskAffinityResult = ComponentParseUtils.buildTaskAffinityName(
+                    pkgName, pkgName, taskAffinity, input);
+            if (taskAffinityResult.isError()) {
+                return input.error(taskAffinityResult);
+            }
+
+            pkg.setTaskAffinity(taskAffinityResult.getResult());
+            String factory = sa.getNonResourceString(
+                    R.styleable.AndroidManifestApplication_appComponentFactory);
+            if (factory != null) {
+                String appComponentFactory = ParsingUtils.buildClassName(pkgName, factory);
+                if (appComponentFactory == null) {
+                    return input.error("Empty class name in package " + pkgName);
+                }
+
+                pkg.setAppComponentFactory(appComponentFactory);
+            }
+
+            CharSequence pname;
+            if (targetSdk >= Build.VERSION_CODES.FROYO) {
+                pname = sa.getNonConfigurationString(
+                        R.styleable.AndroidManifestApplication_process,
+                        Configuration.NATIVE_CONFIG_VERSION);
+            } else {
+                // Some older apps have been seen to use a resource reference
+                // here that on older builds was ignored (with a warning).  We
+                // need to continue to do this for them so they don't break.
+                pname = sa.getNonResourceString(
+                        R.styleable.AndroidManifestApplication_process);
+            }
+            ParseResult<String> processNameResult = ComponentParseUtils.buildProcessName(
+                    pkgName, null, pname, flags, mSeparateProcesses, input);
+            if (processNameResult.isError()) {
+                return input.error(processNameResult);
+            }
+
+            String processName = processNameResult.getResult();
+            pkg.setProcessName(processName);
+
+            if (pkg.isCantSaveState()) {
+                // A heavy-weight application can not be in a custom process.
+                // We can do direct compare because we intern all strings.
+                if (processName != null && !processName.equals(pkgName)) {
+                    return input.error(
+                            "cantSaveState applications can not use custom processes");
+                }
+            }
+
+            String classLoaderName = pkg.getClassLoaderName();
+            if (classLoaderName != null
+                    && !ClassLoaderFactory.isValidClassLoaderName(classLoaderName)) {
+                return input.error("Invalid class loader name: " + classLoaderName);
+            }
+
+            pkg.setGwpAsanMode(sa.getInt(R.styleable.AndroidManifestApplication_gwpAsanMode, -1));
+        } finally {
+            sa.recycle();
+        }
+
+        boolean hasActivityOrder = false;
+        boolean hasReceiverOrder = false;
+        boolean hasServiceOrder = false;
+        final int depth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG
+                || parser.getDepth() > depth)) {
+            if (type != XmlPullParser.START_TAG) {
+                continue;
+            }
+
+            final ParseResult result;
+            String tagName = parser.getName();
+            boolean isActivity = false;
+            switch (tagName) {
+                case "activity":
+                    isActivity = true;
+                    // fall-through
+                case "receiver":
+                    ParseResult<ParsedActivity> activityResult =
+                            ParsedActivityUtils.parseActivityOrReceiver(mSeparateProcesses, pkg,
+                                    res, parser, flags, PackageParser.sUseRoundIcon, input);
+
+                    if (activityResult.isSuccess()) {
+                        ParsedActivity activity = activityResult.getResult();
+                        if (isActivity) {
+                            hasActivityOrder |= (activity.getOrder() != 0);
+                            pkg.addActivity(activity);
+                        } else {
+                            hasReceiverOrder |= (activity.getOrder() != 0);
+                            pkg.addReceiver(activity);
+                        }
+                    }
+
+                    result = activityResult;
+                    break;
+                case "service":
+                    ParseResult<ParsedService> serviceResult =
+                            ParsedServiceUtils.parseService(mSeparateProcesses, pkg, res, parser,
+                                    flags, PackageParser.sUseRoundIcon, input);
+                    if (serviceResult.isSuccess()) {
+                        ParsedService service = serviceResult.getResult();
+                        hasServiceOrder |= (service.getOrder() != 0);
+                        pkg.addService(service);
+                    }
+
+                    result = serviceResult;
+                    break;
+                case "provider":
+                    ParseResult<ParsedProvider> providerResult =
+                            ParsedProviderUtils.parseProvider(mSeparateProcesses, pkg, res, parser,
+                                    flags, PackageParser.sUseRoundIcon, input);
+                    if (providerResult.isSuccess()) {
+                        pkg.addProvider(providerResult.getResult());
+                    }
+
+                    result = providerResult;
+                    break;
+                case "activity-alias":
+                    activityResult = ParsedActivityUtils.parseActivityAlias(pkg, res,
+                            parser, PackageParser.sUseRoundIcon, input);
+                    if (activityResult.isSuccess()) {
+                        ParsedActivity activity = activityResult.getResult();
+                        hasActivityOrder |= (activity.getOrder() != 0);
+                        pkg.addActivity(activity);
+                    }
+
+                    result = activityResult;
+                    break;
+                default:
+                    result = parseBaseAppChildTag(input, tagName, pkg, res, parser, flags);
+                    break;
+            }
+
+            if (result.isError()) {
+                return input.error(result);
+            }
+        }
+
+        if (TextUtils.isEmpty(pkg.getStaticSharedLibName())) {
+            // Add a hidden app detail activity to normal apps which forwards user to App Details
+            // page.
+            ParseResult<ParsedActivity> a = generateAppDetailsHiddenActivity(input, pkg);
+            if (a.isError()) {
+                // Error should be impossible here, as the only failure case as of SDK R is a
+                // string validation error on a constant ":app_details" string passed in by the
+                // parsing code itself. For this reason, this is just a hard failure instead of
+                // deferred.
+                return input.error(a);
+            }
+
+            pkg.addActivity(a.getResult());
+        }
+
+        if (hasActivityOrder) {
+            pkg.sortActivities();
+        }
+        if (hasReceiverOrder) {
+            pkg.sortReceivers();
+        }
+        if (hasServiceOrder) {
+            pkg.sortServices();
+        }
+
+        // Must be run after the entire {@link ApplicationInfo} has been fully processed and after
+        // every activity info has had a chance to set it from its attributes.
+        setMaxAspectRatio(pkg);
+        setMinAspectRatio(pkg);
+
+        pkg.setHasDomainUrls(hasDomainURLs(pkg));
+
+        return input.success(pkg);
+    }
+
+    /**
+     * Collection of single-line, no (or little) logic assignments. Separated for readability.
+     *
+     * Flags are separated by type and by default value. They are sorted alphabetically within each
+     * section.
+     */
+    private void parseBaseAppBasicFlags(ParsingPackage pkg, TypedArray sa) {
+        int targetSdk = pkg.getTargetSdkVersion();
+        //@formatter:off
+        // CHECKSTYLE:off
+        pkg
+                // Default true
+                .setAllowBackup(bool(true, R.styleable.AndroidManifestApplication_allowBackup, sa))
+                .setAllowClearUserData(bool(true, R.styleable.AndroidManifestApplication_allowClearUserData, sa))
+                .setAllowClearUserDataOnFailedRestore(bool(true, R.styleable.AndroidManifestApplication_allowClearUserDataOnFailedRestore, sa))
+                .setAllowNativeHeapPointerTagging(bool(true, R.styleable.AndroidManifestApplication_allowNativeHeapPointerTagging, sa))
+                .setEnabled(bool(true, R.styleable.AndroidManifestApplication_enabled, sa))
+                .setExtractNativeLibs(bool(true, R.styleable.AndroidManifestApplication_extractNativeLibs, sa))
+                .setHasCode(bool(true, R.styleable.AndroidManifestApplication_hasCode, sa))
+                // Default false
+                .setAllowTaskReparenting(bool(false, R.styleable.AndroidManifestApplication_allowTaskReparenting, sa))
+                .setCantSaveState(bool(false, R.styleable.AndroidManifestApplication_cantSaveState, sa))
+                .setCrossProfile(bool(false, R.styleable.AndroidManifestApplication_crossProfile, sa))
+                .setDebuggable(bool(false, R.styleable.AndroidManifestApplication_debuggable, sa))
+                .setDefaultToDeviceProtectedStorage(bool(false, R.styleable.AndroidManifestApplication_defaultToDeviceProtectedStorage, sa))
+                .setDirectBootAware(bool(false, R.styleable.AndroidManifestApplication_directBootAware, sa))
+                .setForceQueryable(bool(false, R.styleable.AndroidManifestApplication_forceQueryable, sa))
+                .setGame(bool(false, R.styleable.AndroidManifestApplication_isGame, sa))
+                .setHasFragileUserData(bool(false, R.styleable.AndroidManifestApplication_hasFragileUserData, sa))
+                .setLargeHeap(bool(false, R.styleable.AndroidManifestApplication_largeHeap, sa))
+                .setMultiArch(bool(false, R.styleable.AndroidManifestApplication_multiArch, sa))
+                .setPreserveLegacyExternalStorage(bool(false, R.styleable.AndroidManifestApplication_preserveLegacyExternalStorage, sa))
+                .setRequiredForAllUsers(bool(false, R.styleable.AndroidManifestApplication_requiredForAllUsers, sa))
+                .setSupportsRtl(bool(false, R.styleable.AndroidManifestApplication_supportsRtl, sa))
+                .setTestOnly(bool(false, R.styleable.AndroidManifestApplication_testOnly, sa))
+                .setUseEmbeddedDex(bool(false, R.styleable.AndroidManifestApplication_useEmbeddedDex, sa))
+                .setUsesNonSdkApi(bool(false, R.styleable.AndroidManifestApplication_usesNonSdkApi, sa))
+                .setVmSafeMode(bool(false, R.styleable.AndroidManifestApplication_vmSafeMode, sa))
+                .setAutoRevokePermissions(anInt(R.styleable.AndroidManifestApplication_autoRevokePermissions, sa))
+                // targetSdkVersion gated
+                .setAllowAudioPlaybackCapture(bool(targetSdk >= Build.VERSION_CODES.Q, R.styleable.AndroidManifestApplication_allowAudioPlaybackCapture, sa))
+                .setBaseHardwareAccelerated(bool(targetSdk >= Build.VERSION_CODES.ICE_CREAM_SANDWICH, R.styleable.AndroidManifestApplication_hardwareAccelerated, sa))
+                .setRequestLegacyExternalStorage(bool(targetSdk < Build.VERSION_CODES.Q, R.styleable.AndroidManifestApplication_requestLegacyExternalStorage, sa))
+                .setUsesCleartextTraffic(bool(targetSdk < Build.VERSION_CODES.P, R.styleable.AndroidManifestApplication_usesCleartextTraffic, sa))
+                // Ints Default 0
+                .setUiOptions(anInt(R.styleable.AndroidManifestApplication_uiOptions, sa))
+                // Ints
+                .setCategory(anInt(ApplicationInfo.CATEGORY_UNDEFINED, R.styleable.AndroidManifestApplication_appCategory, sa))
+                // Floats Default 0f
+                .setMaxAspectRatio(aFloat(R.styleable.AndroidManifestApplication_maxAspectRatio, sa))
+                .setMinAspectRatio(aFloat(R.styleable.AndroidManifestApplication_minAspectRatio, sa))
+                // Resource ID
+                .setBanner(resId(R.styleable.AndroidManifestApplication_banner, sa))
+                .setDescriptionRes(resId(R.styleable.AndroidManifestApplication_description, sa))
+                .setIconRes(resId(R.styleable.AndroidManifestApplication_icon, sa))
+                .setLogo(resId(R.styleable.AndroidManifestApplication_logo, sa))
+                .setNetworkSecurityConfigRes(resId(R.styleable.AndroidManifestApplication_networkSecurityConfig, sa))
+                .setRoundIconRes(resId(R.styleable.AndroidManifestApplication_roundIcon, sa))
+                .setTheme(resId(R.styleable.AndroidManifestApplication_theme, sa))
+                // Strings
+                .setClassLoaderName(string(R.styleable.AndroidManifestApplication_classLoader, sa))
+                .setRequiredAccountType(string(R.styleable.AndroidManifestApplication_requiredAccountType, sa))
+                .setRestrictedAccountType(string(R.styleable.AndroidManifestApplication_restrictedAccountType, sa))
+                .setZygotePreloadName(string(R.styleable.AndroidManifestApplication_zygotePreloadName, sa))
+                // Non-Config String
+                .setPermission(nonConfigString(0, R.styleable.AndroidManifestApplication_permission, sa));
+        // CHECKSTYLE:on
+        //@formatter:on
+    }
+
+    /**
+     * For parsing non-MainComponents. Main ones have an order and some special handling which is
+     * done directly in {@link #parseBaseApplication(ParseInput, ParsingPackage, Resources,
+     * XmlResourceParser, int)}.
+     */
+    private ParseResult parseBaseAppChildTag(ParseInput input, String tag, ParsingPackage pkg,
+            Resources res, XmlResourceParser parser, int flags)
+            throws IOException, XmlPullParserException {
+        switch (tag) {
+            case "meta-data":
+                // TODO(b/135203078): I have no idea what this comment means
+                // note: application meta-data is stored off to the side, so it can
+                // remain null in the primary copy (we like to avoid extra copies because
+                // it can be large)
+                ParseResult<Bundle> metaDataResult = parseMetaData(pkg, res, parser,
+                        pkg.getMetaData(), input);
+                if (metaDataResult.isSuccess()) {
+                    pkg.setMetaData(metaDataResult.getResult());
+                }
+
+                return metaDataResult;
+            case "static-library":
+                return parseStaticLibrary(pkg, res, parser, input);
+            case "library":
+                return parseLibrary(pkg, res, parser, input);
+            case "uses-static-library":
+                return parseUsesStaticLibrary(input, pkg, res, parser);
+            case "uses-library":
+                return parseUsesLibrary(input, pkg, res, parser);
+            case "processes":
+                return parseProcesses(input, pkg, res, parser, mSeparateProcesses, flags);
+            case "uses-package":
+                // Dependencies for app installers; we don't currently try to
+                // enforce this.
+                return input.success(null);
+            case "profileable":
+                return parseProfileable(input, pkg, res, parser);
+            default:
+                return ParsingUtils.unknownTag("<application>", pkg, parser, input);
+        }
+    }
+
+    @NonNull
+    private static ParseResult<ParsingPackage> parseStaticLibrary(
+            ParsingPackage pkg, Resources res,
+            XmlResourceParser parser, ParseInput input) {
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestStaticLibrary);
+        try {
+            // Note: don't allow this value to be a reference to a resource
+            // that may change.
+            String lname = sa.getNonResourceString(
+                    R.styleable.AndroidManifestStaticLibrary_name);
+            final int version = sa.getInt(
+                    R.styleable.AndroidManifestStaticLibrary_version, -1);
+            final int versionMajor = sa.getInt(
+                    R.styleable.AndroidManifestStaticLibrary_versionMajor,
+                    0);
+
+            // Since the app canot run without a static lib - fail if malformed
+            if (lname == null || version < 0) {
+                return input.error("Bad static-library declaration name: " + lname
+                        + " version: " + version);
+            } else if (pkg.getSharedUserId() != null) {
+                return input.error(
+                        PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID,
+                        "sharedUserId not allowed in static shared library"
+                );
+            } else if (pkg.getStaticSharedLibName() != null) {
+                return input.error("Multiple static-shared libs for package "
+                        + pkg.getPackageName());
+            }
+
+            return input.success(pkg.setStaticSharedLibName(lname.intern())
+                    .setStaticSharedLibVersion(
+                            PackageInfo.composeLongVersionCode(versionMajor, version))
+                    .setStaticSharedLibrary(true));
+        } finally {
+            sa.recycle();
+        }
+    }
+
+    @NonNull
+    private static ParseResult<ParsingPackage> parseLibrary(
+            ParsingPackage pkg, Resources res,
+            XmlResourceParser parser, ParseInput input) {
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestLibrary);
+        try {
+            // Note: don't allow this value to be a reference to a resource
+            // that may change.
+            String lname = sa.getNonResourceString(R.styleable.AndroidManifestLibrary_name);
+
+            if (lname != null) {
+                lname = lname.intern();
+                if (!ArrayUtils.contains(pkg.getLibraryNames(), lname)) {
+                    pkg.addLibraryName(lname);
+                }
+            }
+            return input.success(pkg);
+        } finally {
+            sa.recycle();
+        }
+    }
+
+    @NonNull
+    private static ParseResult<ParsingPackage> parseUsesStaticLibrary(ParseInput input,
+            ParsingPackage pkg, Resources res, XmlResourceParser parser)
+            throws XmlPullParserException, IOException {
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesStaticLibrary);
+        try {
+            // Note: don't allow this value to be a reference to a resource that may change.
+            String lname = sa.getNonResourceString(
+                    R.styleable.AndroidManifestUsesLibrary_name);
+            final int version = sa.getInt(
+                    R.styleable.AndroidManifestUsesStaticLibrary_version, -1);
+            String certSha256Digest = sa.getNonResourceString(R.styleable
+                    .AndroidManifestUsesStaticLibrary_certDigest);
+
+            // Since an APK providing a static shared lib can only provide the lib - fail if
+            // malformed
+            if (lname == null || version < 0 || certSha256Digest == null) {
+                return input.error("Bad uses-static-library declaration name: " + lname
+                        + " version: " + version + " certDigest" + certSha256Digest);
+            }
+
+            // Can depend only on one version of the same library
+            List<String> usesStaticLibraries = pkg.getUsesStaticLibraries();
+            if (usesStaticLibraries.contains(lname)) {
+                return input.error(
+                        "Depending on multiple versions of static library " + lname);
+            }
+
+            lname = lname.intern();
+            // We allow ":" delimiters in the SHA declaration as this is the format
+            // emitted by the certtool making it easy for developers to copy/paste.
+            certSha256Digest = certSha256Digest.replace(":", "").toLowerCase();
+
+            // Fot apps targeting O-MR1 we require explicit enumeration of all certs.
+            String[] additionalCertSha256Digests = EmptyArray.STRING;
+            if (pkg.getTargetSdkVersion() >= Build.VERSION_CODES.O_MR1) {
+                ParseResult<String[]> certResult = parseAdditionalCertificates(input, res, parser);
+                if (certResult.isError()) {
+                    return input.error(certResult);
+                }
+                additionalCertSha256Digests = certResult.getResult();
+            }
+
+            final String[] certSha256Digests = new String[additionalCertSha256Digests.length + 1];
+            certSha256Digests[0] = certSha256Digest;
+            System.arraycopy(additionalCertSha256Digests, 0, certSha256Digests,
+                    1, additionalCertSha256Digests.length);
+
+            return input.success(pkg.addUsesStaticLibrary(lname)
+                    .addUsesStaticLibraryVersion(version)
+                    .addUsesStaticLibraryCertDigests(certSha256Digests));
+        } finally {
+            sa.recycle();
+        }
+    }
+
+    @NonNull
+    private static ParseResult<ParsingPackage> parseUsesLibrary(ParseInput input,
+            ParsingPackage pkg, Resources res, XmlResourceParser parser) {
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesLibrary);
+        try {
+            // Note: don't allow this value to be a reference to a resource
+            // that may change.
+            String lname = sa.getNonResourceString(R.styleable.AndroidManifestUsesLibrary_name);
+            boolean req = sa.getBoolean(R.styleable.AndroidManifestUsesLibrary_required, true);
+
+            if (lname != null) {
+                lname = lname.intern();
+                if (req) {
+                    // Upgrade to treat as stronger constraint
+                    pkg.addUsesLibrary(lname)
+                            .removeUsesOptionalLibrary(lname);
+                } else {
+                    // Ignore if someone already defined as required
+                    if (!ArrayUtils.contains(pkg.getUsesLibraries(), lname)) {
+                        pkg.addUsesOptionalLibrary(lname);
+                    }
+                }
+            }
+
+            return input.success(pkg);
+        } finally {
+            sa.recycle();
+        }
+    }
+
+    @NonNull
+    private static ParseResult<ParsingPackage> parseProcesses(ParseInput input, ParsingPackage pkg,
+            Resources res, XmlResourceParser parser, String[] separateProcesses, int flags)
+            throws IOException, XmlPullParserException {
+        ParseResult<ArrayMap<String, ParsedProcess>> result =
+                ParsedProcessUtils.parseProcesses(separateProcesses, pkg, res, parser, flags,
+                        input);
+        if (result.isError()) {
+            return input.error(result);
+        }
+
+        return input.success(pkg.setProcesses(result.getResult()));
+    }
+
+    @NonNull
+    private static ParseResult<ParsingPackage> parseProfileable(ParseInput input,
+            ParsingPackage pkg, Resources res, XmlResourceParser parser) {
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestProfileable);
+        try {
+            return input.success(pkg.setProfileableByShell(pkg.isProfileableByShell()
+                    || bool(false, R.styleable.AndroidManifestProfileable_shell, sa)));
+        } finally {
+            sa.recycle();
+        }
+    }
+
+    private static ParseResult<String[]> parseAdditionalCertificates(ParseInput input,
+            Resources resources, XmlResourceParser parser)
+            throws XmlPullParserException, IOException {
+        String[] certSha256Digests = EmptyArray.STRING;
+        final int depth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG
+                || parser.getDepth() > depth)) {
+            if (type != XmlPullParser.START_TAG) {
+                continue;
+            }
+
+            final String nodeName = parser.getName();
+            if (nodeName.equals("additional-certificate")) {
+                TypedArray sa = resources.obtainAttributes(parser,
+                        R.styleable.AndroidManifestAdditionalCertificate);
+                try {
+                    String certSha256Digest = sa.getNonResourceString(
+                            R.styleable.AndroidManifestAdditionalCertificate_certDigest);
+
+                    if (TextUtils.isEmpty(certSha256Digest)) {
+                        return input.error("Bad additional-certificate declaration with empty"
+                                + " certDigest:" + certSha256Digest);
+                    }
+
+
+                    // We allow ":" delimiters in the SHA declaration as this is the format
+                    // emitted by the certtool making it easy for developers to copy/paste.
+                    certSha256Digest = certSha256Digest.replace(":", "").toLowerCase();
+                    certSha256Digests = ArrayUtils.appendElement(String.class,
+                            certSha256Digests, certSha256Digest);
+                } finally {
+                    sa.recycle();
+                }
+            }
+        }
+
+        return input.success(certSha256Digests);
+    }
+
+    /**
+     * Generate activity object that forwards user to App Details page automatically.
+     * This activity should be invisible to user and user should not know or see it.
+     */
+    @NonNull
+    private static ParseResult<ParsedActivity> generateAppDetailsHiddenActivity(ParseInput input,
+            ParsingPackage pkg) {
+        String packageName = pkg.getPackageName();
+        ParseResult<String> result = ComponentParseUtils.buildTaskAffinityName(
+                packageName, packageName, ":app_details", input);
+        if (result.isError()) {
+            return input.error(result);
+        }
+
+        String taskAffinity = result.getResult();
+
+        // Build custom App Details activity info instead of parsing it from xml
+        return input.success(ParsedActivity.makeAppDetailsActivity(packageName,
+                pkg.getProcessName(), pkg.getUiOptions(), taskAffinity,
+                pkg.isBaseHardwareAccelerated()));
+    }
+
+    /**
+     * Check if one of the IntentFilter as both actions DEFAULT / VIEW and a HTTP/HTTPS data URI
+     */
+    private static boolean hasDomainURLs(ParsingPackage pkg) {
+        final List<ParsedActivity> activities = pkg.getActivities();
+        final int activitiesSize = activities.size();
+        for (int index = 0; index < activitiesSize; index++) {
+            ParsedActivity activity = activities.get(index);
+            List<ParsedIntentInfo> filters = activity.getIntents();
+            final int filtersSize = filters.size();
+            for (int filtersIndex = 0; filtersIndex < filtersSize; filtersIndex++) {
+                ParsedIntentInfo aii = filters.get(filtersIndex);
+                if (!aii.hasAction(Intent.ACTION_VIEW)) continue;
+                if (!aii.hasAction(Intent.ACTION_DEFAULT)) continue;
+                if (aii.hasDataScheme(IntentFilter.SCHEME_HTTP) ||
+                        aii.hasDataScheme(IntentFilter.SCHEME_HTTPS)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Sets the max aspect ratio of every child activity that doesn't already have an aspect
+     * ratio set.
+     */
+    private static void setMaxAspectRatio(ParsingPackage pkg) {
+        // Default to (1.86) 16.7:9 aspect ratio for pre-O apps and unset for O and greater.
+        // NOTE: 16.7:9 was the max aspect ratio Android devices can support pre-O per the CDD.
+        float maxAspectRatio = pkg.getTargetSdkVersion() < O
+                ? PackageParser.DEFAULT_PRE_O_MAX_ASPECT_RATIO : 0;
+
+        float packageMaxAspectRatio = pkg.getMaxAspectRatio();
+        if (packageMaxAspectRatio != 0) {
+            // Use the application max aspect ration as default if set.
+            maxAspectRatio = packageMaxAspectRatio;
+        } else {
+            Bundle appMetaData = pkg.getMetaData();
+            if (appMetaData != null && appMetaData.containsKey(
+                    PackageParser.METADATA_MAX_ASPECT_RATIO)) {
+                maxAspectRatio = appMetaData.getFloat(PackageParser.METADATA_MAX_ASPECT_RATIO,
+                        maxAspectRatio);
+            }
+        }
+
+        List<ParsedActivity> activities = pkg.getActivities();
+        int activitiesSize = activities.size();
+        for (int index = 0; index < activitiesSize; index++) {
+            ParsedActivity activity = activities.get(index);
+            // If the max aspect ratio for the activity has already been set, skip.
+            if (activity.getMaxAspectRatio() != null) {
+                continue;
+            }
+
+            // By default we prefer to use a values defined on the activity directly than values
+            // defined on the application. We do not check the styled attributes on the activity
+            // as it would have already been set when we processed the activity. We wait to
+            // process the meta data here since this method is called at the end of processing
+            // the application and all meta data is guaranteed.
+            final float activityAspectRatio = activity.getMetaData() != null
+                    ? activity.getMetaData().getFloat(PackageParser.METADATA_MAX_ASPECT_RATIO,
+                    maxAspectRatio)
+                    : maxAspectRatio;
+
+            activity.setMaxAspectRatio(activity.getResizeMode(), activityAspectRatio);
+        }
+    }
+
+    /**
+     * Sets the min aspect ratio of every child activity that doesn't already have an aspect
+     * ratio set.
+     */
+    private void setMinAspectRatio(ParsingPackage pkg) {
+        final float minAspectRatio;
+        float packageMinAspectRatio = pkg.getMinAspectRatio();
+        if (packageMinAspectRatio != 0) {
+            // Use the application max aspect ration as default if set.
+            minAspectRatio = packageMinAspectRatio;
+        } else {
+            // Default to (1.33) 4:3 aspect ratio for pre-Q apps and unset for Q and greater.
+            // NOTE: 4:3 was the min aspect ratio Android devices can support pre-Q per the CDD,
+            // except for watches which always supported 1:1.
+            minAspectRatio = pkg.getTargetSdkVersion() >= Build.VERSION_CODES.Q
+                    ? 0
+                    : (mCallback != null && mCallback.hasFeature(FEATURE_WATCH))
+                            ? PackageParser.DEFAULT_PRE_Q_MIN_ASPECT_RATIO_WATCH
+                            : PackageParser.DEFAULT_PRE_Q_MIN_ASPECT_RATIO;
+        }
+
+        List<ParsedActivity> activities = pkg.getActivities();
+        int activitiesSize = activities.size();
+        for (int index = 0; index < activitiesSize; index++) {
+            ParsedActivity activity = activities.get(index);
+            if (activity.getMinAspectRatio() == null) {
+                activity.setMinAspectRatio(activity.getResizeMode(), minAspectRatio);
+            }
+        }
+    }
+
+    private static ParseResult<ParsingPackage> parseOverlay(ParseInput input, ParsingPackage pkg,
+            Resources res, XmlResourceParser parser) {
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestResourceOverlay);
+        try {
+            String target = sa.getString(R.styleable.AndroidManifestResourceOverlay_targetPackage);
+            int priority = anInt(0, R.styleable.AndroidManifestResourceOverlay_priority, sa);
+
+            if (target == null) {
+                return input.error("<overlay> does not specify a target package");
+            } else if (priority < 0 || priority > 9999) {
+                return input.error("<overlay> priority must be between 0 and 9999");
+            }
+
+            // check to see if overlay should be excluded based on system property condition
+            String propName = sa.getString(
+                    R.styleable.AndroidManifestResourceOverlay_requiredSystemPropertyName);
+            String propValue = sa.getString(
+                    R.styleable.AndroidManifestResourceOverlay_requiredSystemPropertyValue);
+            if (!PackageParser.checkRequiredSystemProperties(propName, propValue)) {
+                String message = "Skipping target and overlay pair " + target + " and "
+                        + pkg.getBaseCodePath()
+                        + ": overlay ignored due to required system property: "
+                        + propName + " with value: " + propValue;
+                Slog.i(TAG, message);
+                return input.skip(message);
+            }
+
+            return input.success(pkg.setOverlay(true)
+                    .setOverlayTarget(target)
+                    .setOverlayPriority(priority)
+                    .setOverlayTargetName(
+                            sa.getString(R.styleable.AndroidManifestResourceOverlay_targetName))
+                    .setOverlayCategory(
+                            sa.getString(R.styleable.AndroidManifestResourceOverlay_category))
+                    .setOverlayIsStatic(
+                            bool(false, R.styleable.AndroidManifestResourceOverlay_isStatic, sa)));
+        } finally {
+            sa.recycle();
+        }
+    }
+
+    private static ParseResult<ParsingPackage> parseProtectedBroadcast(ParseInput input,
+            ParsingPackage pkg, Resources res, XmlResourceParser parser) {
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestProtectedBroadcast);
+        try {
+            // Note: don't allow this value to be a reference to a resource
+            // that may change.
+            String name = nonResString(R.styleable.AndroidManifestProtectedBroadcast_name, sa);
+            if (name != null) {
+                pkg.addProtectedBroadcast(name);
+            }
+            return input.success(pkg);
+        } finally {
+            sa.recycle();
+        }
+    }
+
+    private static ParseResult<ParsingPackage> parseSupportScreens(ParseInput input,
+            ParsingPackage pkg, Resources res, XmlResourceParser parser) {
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestSupportsScreens);
+        try {
+            int requiresSmallestWidthDp = anInt(0,
+                    R.styleable.AndroidManifestSupportsScreens_requiresSmallestWidthDp, sa);
+            int compatibleWidthLimitDp = anInt(0,
+                    R.styleable.AndroidManifestSupportsScreens_compatibleWidthLimitDp, sa);
+            int largestWidthLimitDp = anInt(0,
+                    R.styleable.AndroidManifestSupportsScreens_largestWidthLimitDp, sa);
+
+            // This is a trick to get a boolean and still able to detect
+            // if a value was actually set.
+            return input.success(pkg
+                    .setSupportsSmallScreens(
+                            anInt(1, R.styleable.AndroidManifestSupportsScreens_smallScreens, sa))
+                    .setSupportsNormalScreens(
+                            anInt(1, R.styleable.AndroidManifestSupportsScreens_normalScreens, sa))
+                    .setSupportsLargeScreens(
+                            anInt(1, R.styleable.AndroidManifestSupportsScreens_largeScreens, sa))
+                    .setSupportsExtraLargeScreens(
+                            anInt(1, R.styleable.AndroidManifestSupportsScreens_xlargeScreens, sa))
+                    .setResizeable(
+                            anInt(1, R.styleable.AndroidManifestSupportsScreens_resizeable, sa))
+                    .setAnyDensity(
+                            anInt(1, R.styleable.AndroidManifestSupportsScreens_anyDensity, sa))
+                    .setRequiresSmallestWidthDp(requiresSmallestWidthDp)
+                    .setCompatibleWidthLimitDp(compatibleWidthLimitDp)
+                    .setLargestWidthLimitDp(largestWidthLimitDp));
+        } finally {
+            sa.recycle();
+        }
+    }
+
+    private static ParseResult<ParsingPackage> parseInstrumentation(ParseInput input,
+            ParsingPackage pkg, Resources res, XmlResourceParser parser)
+            throws XmlPullParserException, IOException {
+        ParseResult<ParsedInstrumentation> result = ParsedInstrumentationUtils.parseInstrumentation(
+                pkg, res, parser, PackageParser.sUseRoundIcon, input);
+        if (result.isError()) {
+            return input.error(result);
+        }
+        return input.success(pkg.addInstrumentation(result.getResult()));
+    }
+
+    private static ParseResult<ParsingPackage> parseOriginalPackage(ParseInput input,
+            ParsingPackage pkg, Resources res, XmlResourceParser parser) {
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestOriginalPackage);
+        try {
+            String orig = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestOriginalPackage_name,
+                    0);
+            if (!pkg.getPackageName().equals(orig)) {
+                if (pkg.getOriginalPackages().isEmpty()) {
+                    pkg.setRealPackage(pkg.getPackageName());
+                }
+                pkg.addOriginalPackage(orig);
+            }
+            return input.success(pkg);
+        } finally {
+            sa.recycle();
+        }
+    }
+
+    private static ParseResult<ParsingPackage> parseAdoptPermissions(ParseInput input,
+            ParsingPackage pkg, Resources res, XmlResourceParser parser) {
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestOriginalPackage);
+        try {
+            String name = nonConfigString(0, R.styleable.AndroidManifestOriginalPackage_name, sa);
+            if (name != null) {
+                pkg.addAdoptPermission(name);
+            }
+            return input.success(pkg);
+        } finally {
+            sa.recycle();
+        }
+    }
+
+    private static void convertNewPermissions(ParsingPackage pkg) {
+        final int NP = PackageParser.NEW_PERMISSIONS.length;
+        StringBuilder newPermsMsg = null;
+        for (int ip = 0; ip < NP; ip++) {
+            final PackageParser.NewPermissionInfo npi
+                    = PackageParser.NEW_PERMISSIONS[ip];
+            if (pkg.getTargetSdkVersion() >= npi.sdkVersion) {
+                break;
+            }
+            if (!pkg.getRequestedPermissions().contains(npi.name)) {
+                if (newPermsMsg == null) {
+                    newPermsMsg = new StringBuilder(128);
+                    newPermsMsg.append(pkg.getPackageName());
+                    newPermsMsg.append(": compat added ");
+                } else {
+                    newPermsMsg.append(' ');
+                }
+                newPermsMsg.append(npi.name);
+                pkg.addRequestedPermission(npi.name)
+                        .addImplicitPermission(npi.name);
+            }
+        }
+        if (newPermsMsg != null) {
+            Slog.i(TAG, newPermsMsg.toString());
+        }
+    }
+
+    private static void convertSplitPermissions(ParsingPackage pkg) {
+        List<SplitPermissionInfoParcelable> splitPermissions;
+
+        try {
+            splitPermissions = ActivityThread.getPermissionManager().getSplitPermissions();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+
+        final int listSize = splitPermissions.size();
+        for (int is = 0; is < listSize; is++) {
+            final SplitPermissionInfoParcelable spi = splitPermissions.get(is);
+            List<String> requestedPermissions = pkg.getRequestedPermissions();
+            if (pkg.getTargetSdkVersion() >= spi.getTargetSdk()
+                    || !requestedPermissions.contains(spi.getSplitPermission())) {
+                continue;
+            }
+            final List<String> newPerms = spi.getNewPermissions();
+            for (int in = 0; in < newPerms.size(); in++) {
+                final String perm = newPerms.get(in);
+                if (!requestedPermissions.contains(perm)) {
+                    pkg.addRequestedPermission(perm)
+                            .addImplicitPermission(perm);
+                }
+            }
+        }
+    }
+
+    /**
+     * This is a pre-density application which will get scaled - instead of being pixel perfect.
+     * This type of application is not resizable.
+     *
+     * @param pkg The package which needs to be marked as unresizable.
+     */
+    private static void adjustPackageToBeUnresizeableAndUnpipable(ParsingPackage pkg) {
+        List<ParsedActivity> activities = pkg.getActivities();
+        int activitiesSize = activities.size();
+        for (int index = 0; index < activitiesSize; index++) {
+            ParsedActivity activity = activities.get(index);
+            activity.setResizeMode(RESIZE_MODE_UNRESIZEABLE)
+                    .setFlags(activity.getFlags() & ~FLAG_SUPPORTS_PICTURE_IN_PICTURE);
+        }
+    }
+
+    private static ParseResult validateName(ParseInput input, String name, boolean requireSeparator,
+            boolean requireFilename) {
+        final int N = name.length();
+        boolean hasSep = false;
+        boolean front = true;
+        for (int i = 0; i < N; i++) {
+            final char c = name.charAt(i);
+            if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
+                front = false;
+                continue;
+            }
+            if (!front) {
+                if ((c >= '0' && c <= '9') || c == '_') {
+                    continue;
+                }
+            }
+            if (c == '.') {
+                hasSep = true;
+                front = true;
+                continue;
+            }
+            return input.error("bad character '" + c + "'");
+        }
+        if (requireFilename && !FileUtils.isValidExtFilename(name)) {
+            return input.error("Invalid filename");
+        }
+        return hasSep || !requireSeparator
+                ? input.success(null)
+                : input.error("must have at least one '.' separator");
+    }
+
+    public static ParseResult<Bundle> parseMetaData(ParsingPackage pkg, Resources res,
+            XmlResourceParser parser, Bundle data, ParseInput input) {
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestMetaData);
+        try {
+            if (data == null) {
+                data = new Bundle();
+            }
+
+            String name = TextUtils.safeIntern(
+                    nonConfigString(0, R.styleable.AndroidManifestMetaData_name, sa));
+            if (name == null) {
+                return input.error("<meta-data> requires an android:name attribute");
+            }
+
+            TypedValue v = sa.peekValue(R.styleable.AndroidManifestMetaData_resource);
+            if (v != null && v.resourceId != 0) {
+                //Slog.i(TAG, "Meta data ref " + name + ": " + v);
+                data.putInt(name, v.resourceId);
+            } else {
+                v = sa.peekValue(R.styleable.AndroidManifestMetaData_value);
+                //Slog.i(TAG, "Meta data " + name + ": " + v);
+                if (v != null) {
+                    if (v.type == TypedValue.TYPE_STRING) {
+                        CharSequence cs = v.coerceToString();
+                        data.putString(name, cs != null ? cs.toString() : null);
+                    } else if (v.type == TypedValue.TYPE_INT_BOOLEAN) {
+                        data.putBoolean(name, v.data != 0);
+                    } else if (v.type >= TypedValue.TYPE_FIRST_INT
+                            && v.type <= TypedValue.TYPE_LAST_INT) {
+                        data.putInt(name, v.data);
+                    } else if (v.type == TypedValue.TYPE_FLOAT) {
+                        data.putFloat(name, v.getFloat());
+                    } else {
+                        if (!PackageParser.RIGID_PARSER) {
+                            Slog.w(TAG,
+                                    "<meta-data> only supports string, integer, float, color, "
+                                            + "boolean, and resource reference types: "
+                                            + parser.getName() + " at "
+                                            + pkg.getBaseCodePath() + " "
+                                            + parser.getPositionDescription());
+                        } else {
+                            return input.error("<meta-data> only supports string, integer, float, "
+                                    + "color, boolean, and resource reference types");
+                        }
+                    }
+                } else {
+                    return input.error("<meta-data> requires an android:value "
+                            + "or android:resource attribute");
+                }
+            }
+            return input.success(data);
+        } finally {
+            sa.recycle();
+        }
+    }
+
+    /**
+     * Collect certificates from all the APKs described in the given package. Also asserts that
+     * all APK contents are signed correctly and consistently.
+     */
+    public static SigningDetails collectCertificates(ParsingPackageRead pkg, boolean skipVerify)
+            throws PackageParserException {
+        SigningDetails signingDetails = SigningDetails.UNKNOWN;
+
+        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
+        try {
+            signingDetails = collectCertificates(
+                    pkg.getBaseCodePath(),
+                    skipVerify,
+                    pkg.isStaticSharedLibrary(),
+                    signingDetails,
+                    pkg.getTargetSdkVersion()
+            );
+
+            String[] splitCodePaths = pkg.getSplitCodePaths();
+            if (!ArrayUtils.isEmpty(splitCodePaths)) {
+                for (int i = 0; i < splitCodePaths.length; i++) {
+                    signingDetails = collectCertificates(
+                            splitCodePaths[i],
+                            skipVerify,
+                            pkg.isStaticSharedLibrary(),
+                            signingDetails,
+                            pkg.getTargetSdkVersion()
+                    );
+                }
+            }
+            return signingDetails;
+        } finally {
+            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+        }
+    }
+
+    public static SigningDetails collectCertificates(String baseCodePath, boolean skipVerify,
+            boolean isStaticSharedLibrary, @NonNull SigningDetails existingSigningDetails,
+            int targetSdk) throws PackageParserException {
+        int minSignatureScheme = ApkSignatureVerifier.getMinimumSignatureSchemeVersionForTargetSdk(
+                targetSdk);
+        if (isStaticSharedLibrary) {
+            // must use v2 signing scheme
+            minSignatureScheme = SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2;
+        }
+        SigningDetails verified;
+        if (skipVerify) {
+            // systemDir APKs are already trusted, save time by not verifying
+            verified = ApkSignatureVerifier.unsafeGetCertsWithoutVerification(
+                    baseCodePath, minSignatureScheme);
+        } else {
+            verified = ApkSignatureVerifier.verify(baseCodePath, minSignatureScheme);
+        }
+
+        // Verify that entries are signed consistently with the first pkg
+        // we encountered. Note that for splits, certificates may have
+        // already been populated during an earlier parse of a base APK.
+        if (existingSigningDetails == SigningDetails.UNKNOWN) {
+            return verified;
+        } else {
+            if (!Signature.areExactMatch(existingSigningDetails.signatures, verified.signatures)) {
+                throw new PackageParserException(
+                        INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
+                        baseCodePath + " has mismatched certificates");
+            }
+
+            return existingSigningDetails;
+        }
+    }
+
+    /*
+     The following set of methods makes code easier to read by re-ordering the TypedArray methods.
+
+     The first parameter is the default, which is the most important to understand for someone
+     reading through the parsing code.
+
+     That's followed by the attribute name, which is usually irrelevant during reading because
+     it'll look like setSomeValue(true, R.styleable.ReallyLongParentName_SomeValueAttr... and
+     the "setSomeValue" part is enough to communicate what the line does.
+
+     Last comes the TypedArray, which is by far the least important since each try-with-resources
+     should only have 1.
+    */
+
+    // Note there is no variant of bool without a defaultValue parameter, since explicit true/false
+    // is important to specify when adding an attribute.
+    private static boolean bool(boolean defaultValue, @StyleableRes int attribute, TypedArray sa) {
+        return sa.getBoolean(attribute, defaultValue);
+    }
+
+    private static float aFloat(float defaultValue, @StyleableRes int attribute, TypedArray sa) {
+        return sa.getFloat(attribute, defaultValue);
+    }
+
+    private static float aFloat(@StyleableRes int attribute, TypedArray sa) {
+        return sa.getFloat(attribute, 0f);
+    }
+
+    private static int anInt(int defaultValue, @StyleableRes int attribute, TypedArray sa) {
+        return sa.getInt(attribute, defaultValue);
+    }
+
+    private static int anInteger(int defaultValue, @StyleableRes int attribute, TypedArray sa) {
+        return sa.getInteger(attribute, defaultValue);
+    }
+
+    private static int anInt(@StyleableRes int attribute, TypedArray sa) {
+        return sa.getInt(attribute, 0);
+    }
+
+    @AnyRes
+    private static int resId(@StyleableRes int attribute, TypedArray sa) {
+        return sa.getResourceId(attribute, 0);
+    }
+
+    private static String string(@StyleableRes int attribute, TypedArray sa) {
+        return sa.getString(attribute);
+    }
+
+    private static String nonConfigString(int allowedChangingConfigs, @StyleableRes int attribute,
+            TypedArray sa) {
+        return sa.getNonConfigurationString(attribute, allowedChangingConfigs);
+    }
+
+    private static String nonResString(@StyleableRes int index, TypedArray sa) {
+        return sa.getNonResourceString(index);
+    }
+
+    /**
+     * Callback interface for retrieving information that may be needed while parsing
+     * a package.
+     */
+    public interface Callback {
+        boolean hasFeature(String feature);
+
+        ParsingPackage startParsingPackage(@NonNull String packageName,
+                @NonNull String baseCodePath, @NonNull String codePath,
+                @NonNull TypedArray manifestArray, boolean isCoreApp);
+    }
+}
diff --git a/android/content/pm/parsing/ParsingUtils.java b/android/content/pm/parsing/ParsingUtils.java
new file mode 100644
index 0000000..ba61de1
--- /dev/null
+++ b/android/content/pm/parsing/ParsingUtils.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.content.pm.parsing;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.PackageParser;
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.res.XmlResourceParser;
+import android.util.Slog;
+
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+/** @hide **/
+public class ParsingUtils {
+
+    // TODO(b/135203078): Consolidate log tags
+    public static final String TAG = "PackageParsing";
+
+    @Nullable
+    public static String buildClassName(String pkg, CharSequence clsSeq) {
+        if (clsSeq == null || clsSeq.length() <= 0) {
+            return null;
+        }
+        String cls = clsSeq.toString();
+        char c = cls.charAt(0);
+        if (c == '.') {
+            return pkg + cls;
+        }
+        if (cls.indexOf('.') < 0) {
+            StringBuilder b = new StringBuilder(pkg);
+            b.append('.');
+            b.append(cls);
+            return b.toString();
+        }
+        return cls;
+    }
+
+    @NonNull
+    public static ParseResult unknownTag(String parentTag, ParsingPackage pkg,
+            XmlResourceParser parser, ParseInput input) throws IOException, XmlPullParserException {
+        if (PackageParser.RIGID_PARSER) {
+            return input.error("Bad element under " + parentTag + ": " + parser.getName());
+        }
+        Slog.w(TAG, "Unknown element under " + parentTag + ": "
+                + parser.getName() + " at " + pkg.getBaseCodePath() + " "
+                + parser.getPositionDescription());
+        XmlUtils.skipCurrentTag(parser);
+        return input.success(null); // Type doesn't matter
+    }
+}
diff --git a/android/content/pm/parsing/component/ComponentParseUtils.java b/android/content/pm/parsing/component/ComponentParseUtils.java
new file mode 100644
index 0000000..c4caedc
--- /dev/null
+++ b/android/content/pm/parsing/component/ComponentParseUtils.java
@@ -0,0 +1,178 @@
+/*
+ * 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.content.pm.parsing.component;
+
+import android.annotation.AttrRes;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Intent;
+import android.content.pm.PackageParser;
+import android.content.pm.PackageUserState;
+import android.content.pm.parsing.ParsingPackage;
+import android.content.pm.parsing.ParsingUtils;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.text.TextUtils;
+
+import android.content.pm.parsing.ParsingPackageUtils;
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+/** @hide */
+public class ComponentParseUtils {
+
+    private static final String TAG = ParsingPackageUtils.TAG;
+
+    public static boolean isImplicitlyExposedIntent(ParsedIntentInfo intentInfo) {
+        return intentInfo.hasCategory(Intent.CATEGORY_BROWSABLE)
+                || intentInfo.hasAction(Intent.ACTION_SEND)
+                || intentInfo.hasAction(Intent.ACTION_SENDTO)
+                || intentInfo.hasAction(Intent.ACTION_SEND_MULTIPLE);
+    }
+
+    static <Component extends ParsedComponent> ParseResult<Component> parseAllMetaData(
+            ParsingPackage pkg, Resources res, XmlResourceParser parser, String tag,
+            Component component, ParseInput input) throws XmlPullParserException, IOException {
+        final int depth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() > depth)) {
+            if (type != XmlPullParser.START_TAG) {
+                continue;
+            }
+
+            final ParseResult result;
+            if ("meta-data".equals(parser.getName())) {
+                result = ParsedComponentUtils.addMetaData(component, pkg, res, parser, input);
+            } else {
+                result = ParsingUtils.unknownTag(tag, pkg, parser, input);
+            }
+
+            if (result.isError()) {
+                return input.error(result);
+            }
+        }
+
+        return input.success(component);
+    }
+
+    @NonNull
+    public static ParseResult<String> buildProcessName(@NonNull String pkg, String defProc,
+            CharSequence procSeq, int flags, String[] separateProcesses, ParseInput input) {
+        if ((flags & PackageParser.PARSE_IGNORE_PROCESSES) != 0 && !"system".contentEquals(
+                procSeq)) {
+            return input.success(defProc != null ? defProc : pkg);
+        }
+        if (separateProcesses != null) {
+            for (int i = separateProcesses.length - 1; i >= 0; i--) {
+                String sp = separateProcesses[i];
+                if (sp.equals(pkg) || sp.equals(defProc) || sp.contentEquals(procSeq)) {
+                    return input.success(pkg);
+                }
+            }
+        }
+        if (procSeq == null || procSeq.length() <= 0) {
+            return input.success(defProc);
+        }
+
+        ParseResult<String> nameResult = ComponentParseUtils.buildCompoundName(pkg, procSeq,
+                "process", input);
+        return input.success(TextUtils.safeIntern(nameResult.getResult()));
+    }
+
+    @NonNull
+    public static ParseResult<String> buildTaskAffinityName(String pkg, String defProc,
+            CharSequence procSeq, ParseInput input) {
+        if (procSeq == null) {
+            return input.success(defProc);
+        }
+        if (procSeq.length() <= 0) {
+            return input.success(null);
+        }
+        return buildCompoundName(pkg, procSeq, "taskAffinity", input);
+    }
+
+    public static ParseResult<String> buildCompoundName(String pkg, CharSequence procSeq,
+            String type, ParseInput input) {
+        String proc = procSeq.toString();
+        char c = proc.charAt(0);
+        if (pkg != null && c == ':') {
+            if (proc.length() < 2) {
+                return input.error("Bad " + type + " name " + proc + " in package " + pkg
+                        + ": must be at least two characters");
+            }
+            String subName = proc.substring(1);
+            String nameError = PackageParser.validateName(subName, false, false);
+            if (nameError != null) {
+                return input.error("Invalid " + type + " name " + proc + " in package " + pkg
+                        + ": " + nameError);
+            }
+            return input.success(pkg + proc);
+        }
+        String nameError = PackageParser.validateName(proc, true, false);
+        if (nameError != null && !"system".equals(proc)) {
+            return input.error("Invalid " + type + " name " + proc + " in package " + pkg
+                    + ": " + nameError);
+        }
+        return input.success(proc);
+    }
+
+    public static int flag(int flag, @AttrRes int attribute, TypedArray typedArray) {
+        return typedArray.getBoolean(attribute, false) ? flag : 0;
+    }
+
+    public static int flag(int flag, @AttrRes int attribute, boolean defaultValue,
+            TypedArray typedArray) {
+        return typedArray.getBoolean(attribute, defaultValue) ? flag : 0;
+    }
+
+    /**
+     * This is not state aware. Avoid and access through PackageInfoUtils in the system server.
+     */
+    @Nullable
+    public static CharSequence getNonLocalizedLabel(
+            ParsedComponent component) {
+        return component.nonLocalizedLabel;
+    }
+
+    /**
+     * This is not state aware. Avoid and access through PackageInfoUtils in the system server.
+     *
+     * This is a method of the utility class to discourage use.
+     */
+    public static int getIcon(ParsedComponent component) {
+        return component.icon;
+    }
+
+    public static boolean isMatch(PackageUserState state, boolean isSystem,
+            boolean isPackageEnabled, ParsedMainComponent component, int flags) {
+        return state.isMatch(isSystem, isPackageEnabled, component.isEnabled(),
+                component.isDirectBootAware(), component.getName(), flags);
+    }
+
+    public static boolean isEnabled(PackageUserState state, boolean isPackageEnabled,
+            ParsedMainComponent parsedComponent, int flags) {
+        return state.isEnabled(isPackageEnabled, parsedComponent.isEnabled(),
+                parsedComponent.getName(), flags);
+    }
+}
diff --git a/android/content/pm/parsing/component/ParsedActivity.java b/android/content/pm/parsing/component/ParsedActivity.java
new file mode 100644
index 0000000..4c93d09
--- /dev/null
+++ b/android/content/pm/parsing/component/ParsedActivity.java
@@ -0,0 +1,434 @@
+/*
+ * 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.content.pm.parsing.component;
+
+import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.content.pm.parsing.ParsingPackageImpl.sForInternedString;
+import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED;
+
+import android.annotation.Nullable;
+import android.app.ActivityTaskManager;
+import android.content.ComponentName;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageParser;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
+
+/** @hide **/
+public class ParsedActivity extends ParsedMainComponent {
+
+    int theme;
+    int uiOptions;
+
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String targetActivity;
+
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String parentActivityName;
+    @Nullable
+    String taskAffinity;
+    int privateFlags;
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String permission;
+
+    int launchMode;
+    int documentLaunchMode;
+    int maxRecents;
+    int configChanges;
+    int softInputMode;
+    int persistableMode;
+    int lockTaskLaunchMode;
+
+    int screenOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+    int resizeMode = ActivityInfo.RESIZE_MODE_RESIZEABLE;
+
+    @Nullable
+    private Float maxAspectRatio;
+
+    @Nullable
+    private Float minAspectRatio;
+
+    @Nullable
+    String requestedVrComponent;
+    int rotationAnimation = -1;
+    int colorMode;
+
+    @Nullable
+    ActivityInfo.WindowLayout windowLayout;
+
+    public ParsedActivity(ParsedActivity other) {
+        super(other);
+        this.theme = other.theme;
+        this.uiOptions = other.uiOptions;
+        this.targetActivity = other.targetActivity;
+        this.parentActivityName = other.parentActivityName;
+        this.taskAffinity = other.taskAffinity;
+        this.privateFlags = other.privateFlags;
+        this.permission = other.permission;
+        this.launchMode = other.launchMode;
+        this.documentLaunchMode = other.documentLaunchMode;
+        this.maxRecents = other.maxRecents;
+        this.configChanges = other.configChanges;
+        this.softInputMode = other.softInputMode;
+        this.persistableMode = other.persistableMode;
+        this.lockTaskLaunchMode = other.lockTaskLaunchMode;
+        this.screenOrientation = other.screenOrientation;
+        this.resizeMode = other.resizeMode;
+        this.maxAspectRatio = other.maxAspectRatio;
+        this.minAspectRatio = other.minAspectRatio;
+        this.requestedVrComponent = other.requestedVrComponent;
+        this.rotationAnimation = other.rotationAnimation;
+        this.colorMode = other.colorMode;
+        this.windowLayout = other.windowLayout;
+    }
+
+    /**
+     * Generate activity object that forwards user to App Details page automatically.
+     * This activity should be invisible to user and user should not know or see it.
+     */
+    public static ParsedActivity makeAppDetailsActivity(String packageName, String processName,
+            int uiOptions, String taskAffinity, boolean hardwareAccelerated) {
+        ParsedActivity activity = new ParsedActivity();
+        activity.setPackageName(packageName);
+        activity.theme = android.R.style.Theme_NoDisplay;
+        activity.exported = true;
+        activity.setName(PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME);
+        activity.setProcessName(processName);
+        activity.uiOptions = uiOptions;
+        activity.taskAffinity = taskAffinity;
+        activity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
+        activity.documentLaunchMode = ActivityInfo.DOCUMENT_LAUNCH_NONE;
+        activity.maxRecents = ActivityTaskManager.getDefaultAppRecentsLimitStatic();
+        activity.configChanges = PackageParser.getActivityConfigChanges(0, 0);
+        activity.softInputMode = 0;
+        activity.persistableMode = ActivityInfo.PERSIST_NEVER;
+        activity.screenOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
+        activity.resizeMode = RESIZE_MODE_FORCE_RESIZEABLE;
+        activity.lockTaskLaunchMode = 0;
+        activity.setDirectBootAware(false);
+        activity.rotationAnimation = ROTATION_ANIMATION_UNSPECIFIED;
+        activity.colorMode = ActivityInfo.COLOR_MODE_DEFAULT;
+        if (hardwareAccelerated) {
+            activity.setFlags(activity.getFlags() | ActivityInfo.FLAG_HARDWARE_ACCELERATED);
+        }
+        return activity;
+    }
+
+    static ParsedActivity makeAlias(String targetActivityName, ParsedActivity target) {
+        ParsedActivity alias = new ParsedActivity();
+        alias.setPackageName(target.getPackageName());
+        alias.setTargetActivity(targetActivityName);
+        alias.configChanges = target.configChanges;
+        alias.flags = target.flags;
+        alias.privateFlags = target.privateFlags;
+        alias.icon = target.icon;
+        alias.logo = target.logo;
+        alias.banner = target.banner;
+        alias.labelRes = target.labelRes;
+        alias.nonLocalizedLabel = target.nonLocalizedLabel;
+        alias.launchMode = target.launchMode;
+        alias.lockTaskLaunchMode = target.lockTaskLaunchMode;
+        alias.descriptionRes = target.descriptionRes;
+        alias.screenOrientation = target.screenOrientation;
+        alias.taskAffinity = target.taskAffinity;
+        alias.theme = target.theme;
+        alias.softInputMode = target.softInputMode;
+        alias.uiOptions = target.uiOptions;
+        alias.parentActivityName = target.parentActivityName;
+        alias.maxRecents = target.maxRecents;
+        alias.windowLayout = target.windowLayout;
+        alias.resizeMode = target.resizeMode;
+        alias.maxAspectRatio = target.maxAspectRatio;
+        alias.minAspectRatio = target.minAspectRatio;
+        alias.requestedVrComponent = target.requestedVrComponent;
+        alias.directBootAware = target.directBootAware;
+        alias.setProcessName(target.getProcessName());
+        return alias;
+
+        // Not all attributes from the target ParsedActivity are copied to the alias.
+        // Careful when adding an attribute and determine whether or not it should be copied.
+//        alias.enabled = target.enabled;
+//        alias.exported = target.exported;
+//        alias.permission = target.permission;
+//        alias.splitName = target.splitName;
+//        alias.documentLaunchMode = target.documentLaunchMode;
+//        alias.persistableMode = target.persistableMode;
+//        alias.rotationAnimation = target.rotationAnimation;
+//        alias.colorMode = target.colorMode;
+//        alias.intents.addAll(target.intents);
+//        alias.order = target.order;
+//        alias.metaData = target.metaData;
+    }
+
+    public ParsedActivity setMaxAspectRatio(int resizeMode, float maxAspectRatio) {
+        if (resizeMode == ActivityInfo.RESIZE_MODE_RESIZEABLE
+                || resizeMode == ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION) {
+            // Resizeable activities can be put in any aspect ratio.
+            return this;
+        }
+
+        if (maxAspectRatio < 1.0f && maxAspectRatio != 0) {
+            // Ignore any value lesser than 1.0.
+            return this;
+        }
+
+        this.maxAspectRatio = maxAspectRatio;
+        return this;
+    }
+
+    public ParsedActivity setMinAspectRatio(int resizeMode, float minAspectRatio) {
+        if (resizeMode == RESIZE_MODE_RESIZEABLE
+                || resizeMode == RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION) {
+            // Resizeable activities can be put in any aspect ratio.
+            return this;
+        }
+
+        if (minAspectRatio < 1.0f && minAspectRatio != 0) {
+            // Ignore any value lesser than 1.0.
+            return this;
+        }
+
+        this.minAspectRatio = minAspectRatio;
+        return this;
+    }
+
+    public ParsedActivity setFlags(int flags) {
+        this.flags = flags;
+        return this;
+    }
+
+    public ParsedActivity setResizeMode(int resizeMode) {
+        this.resizeMode = resizeMode;
+        return this;
+    }
+
+    public ParsedActivity setTargetActivity(String targetActivity) {
+        this.targetActivity = TextUtils.safeIntern(targetActivity);
+        return this;
+    }
+
+    public ParsedActivity setParentActivity(String parentActivity) {
+        this.parentActivityName = TextUtils.safeIntern(parentActivity);
+        return this;
+    }
+
+    public ParsedActivity setPermission(String permission) {
+        // Empty string must be converted to null
+        this.permission = TextUtils.isEmpty(permission) ? null : permission.intern();
+        return this;
+    }
+
+    public String toString() {
+        StringBuilder sb = new StringBuilder(128);
+        sb.append("Activity{");
+        sb.append(Integer.toHexString(System.identityHashCode(this)));
+        sb.append(' ');
+        ComponentName.appendShortString(sb, getPackageName(), getName());
+        sb.append('}');
+        return sb.toString();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        super.writeToParcel(dest, flags);
+        dest.writeInt(this.theme);
+        dest.writeInt(this.uiOptions);
+        dest.writeString(this.targetActivity);
+        dest.writeString(this.parentActivityName);
+        dest.writeString(this.taskAffinity);
+        dest.writeInt(this.privateFlags);
+        sForInternedString.parcel(this.permission, dest, flags);
+        dest.writeInt(this.launchMode);
+        dest.writeInt(this.documentLaunchMode);
+        dest.writeInt(this.maxRecents);
+        dest.writeInt(this.configChanges);
+        dest.writeInt(this.softInputMode);
+        dest.writeInt(this.persistableMode);
+        dest.writeInt(this.lockTaskLaunchMode);
+        dest.writeInt(this.screenOrientation);
+        dest.writeInt(this.resizeMode);
+        dest.writeValue(this.maxAspectRatio);
+        dest.writeValue(this.minAspectRatio);
+        dest.writeString(this.requestedVrComponent);
+        dest.writeInt(this.rotationAnimation);
+        dest.writeInt(this.colorMode);
+        dest.writeBundle(this.metaData);
+
+        if (windowLayout != null) {
+            dest.writeInt(1);
+            windowLayout.writeToParcel(dest);
+        } else {
+            dest.writeBoolean(false);
+        }
+    }
+
+    public ParsedActivity() {
+    }
+
+    protected ParsedActivity(Parcel in) {
+        super(in);
+        this.theme = in.readInt();
+        this.uiOptions = in.readInt();
+        this.targetActivity = in.readString();
+        this.parentActivityName = in.readString();
+        this.taskAffinity = in.readString();
+        this.privateFlags = in.readInt();
+        this.permission = sForInternedString.unparcel(in);
+        this.launchMode = in.readInt();
+        this.documentLaunchMode = in.readInt();
+        this.maxRecents = in.readInt();
+        this.configChanges = in.readInt();
+        this.softInputMode = in.readInt();
+        this.persistableMode = in.readInt();
+        this.lockTaskLaunchMode = in.readInt();
+        this.screenOrientation = in.readInt();
+        this.resizeMode = in.readInt();
+        this.maxAspectRatio = (Float) in.readValue(Float.class.getClassLoader());
+        this.minAspectRatio = (Float) in.readValue(Float.class.getClassLoader());
+        this.requestedVrComponent = in.readString();
+        this.rotationAnimation = in.readInt();
+        this.colorMode = in.readInt();
+        this.metaData = in.readBundle();
+        if (in.readBoolean()) {
+            windowLayout = new ActivityInfo.WindowLayout(in);
+        }
+    }
+
+    public static final Parcelable.Creator<ParsedActivity> CREATOR = new Creator<ParsedActivity>() {
+        @Override
+        public ParsedActivity createFromParcel(Parcel source) {
+            return new ParsedActivity(source);
+        }
+
+        @Override
+        public ParsedActivity[] newArray(int size) {
+            return new ParsedActivity[size];
+        }
+    };
+
+    public int getTheme() {
+        return theme;
+    }
+
+    public int getUiOptions() {
+        return uiOptions;
+    }
+
+    @Nullable
+    public String getTargetActivity() {
+        return targetActivity;
+    }
+
+    @Nullable
+    public String getParentActivityName() {
+        return parentActivityName;
+    }
+
+    @Nullable
+    public String getTaskAffinity() {
+        return taskAffinity;
+    }
+
+    public int getPrivateFlags() {
+        return privateFlags;
+    }
+
+    @Nullable
+    public String getPermission() {
+        return permission;
+    }
+
+    public int getLaunchMode() {
+        return launchMode;
+    }
+
+    public int getDocumentLaunchMode() {
+        return documentLaunchMode;
+    }
+
+    public int getMaxRecents() {
+        return maxRecents;
+    }
+
+    public int getConfigChanges() {
+        return configChanges;
+    }
+
+    public int getSoftInputMode() {
+        return softInputMode;
+    }
+
+    public int getPersistableMode() {
+        return persistableMode;
+    }
+
+    public int getLockTaskLaunchMode() {
+        return lockTaskLaunchMode;
+    }
+
+    public int getScreenOrientation() {
+        return screenOrientation;
+    }
+
+    public int getResizeMode() {
+        return resizeMode;
+    }
+
+    @Nullable
+    public Float getMaxAspectRatio() {
+        return maxAspectRatio;
+    }
+
+    @Nullable
+    public Float getMinAspectRatio() {
+        return minAspectRatio;
+    }
+
+    @Nullable
+    public String getRequestedVrComponent() {
+        return requestedVrComponent;
+    }
+
+    public int getRotationAnimation() {
+        return rotationAnimation;
+    }
+
+    public int getColorMode() {
+        return colorMode;
+    }
+
+    @Nullable
+    public ActivityInfo.WindowLayout getWindowLayout() {
+        return windowLayout;
+    }
+}
diff --git a/android/content/pm/parsing/component/ParsedActivityUtils.java b/android/content/pm/parsing/component/ParsedActivityUtils.java
new file mode 100644
index 0000000..f64560a
--- /dev/null
+++ b/android/content/pm/parsing/component/ParsedActivityUtils.java
@@ -0,0 +1,518 @@
+/*
+ * 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.content.pm.parsing.component;
+
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.content.pm.parsing.component.ComponentParseUtils.flag;
+
+import android.annotation.NonNull;
+import android.app.ActivityTaskManager;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageParser;
+import android.content.pm.parsing.ParsingPackage;
+import android.content.pm.parsing.ParsingPackageUtils;
+import android.content.pm.parsing.ParsingUtils;
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.os.Build;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.Slog;
+import android.util.TypedValue;
+import android.view.Gravity;
+import android.view.WindowManager;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Objects;
+
+/** @hide */
+public class ParsedActivityUtils {
+
+    private static final String TAG = ParsingPackageUtils.TAG;
+
+    @NonNull
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public static ParseResult<ParsedActivity> parseActivityOrReceiver(String[] separateProcesses,
+            ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags,
+            boolean useRoundIcon, ParseInput input)
+            throws XmlPullParserException, IOException {
+        final String packageName = pkg.getPackageName();
+        final ParsedActivity
+                activity = new ParsedActivity();
+
+        boolean receiver = "receiver".equals(parser.getName());
+        String tag = "<" + parser.getName() + ">";
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestActivity);
+        try {
+            ParseResult<ParsedActivity> result =
+                    ParsedMainComponentUtils.parseMainComponent(
+                    activity, tag, separateProcesses,
+                    pkg, sa, flags, useRoundIcon, input,
+                    R.styleable.AndroidManifestActivity_banner,
+                    R.styleable.AndroidManifestActivity_description,
+                    R.styleable.AndroidManifestActivity_directBootAware,
+                    R.styleable.AndroidManifestActivity_enabled,
+                    R.styleable.AndroidManifestActivity_icon,
+                    R.styleable.AndroidManifestActivity_label,
+                    R.styleable.AndroidManifestActivity_logo,
+                    R.styleable.AndroidManifestActivity_name,
+                    R.styleable.AndroidManifestActivity_process,
+                    R.styleable.AndroidManifestActivity_roundIcon,
+                    R.styleable.AndroidManifestActivity_splitName);
+            if (result.isError()) {
+                return result;
+            }
+
+            if (receiver && pkg.isCantSaveState()) {
+                // A heavy-weight application can not have receivers in its main process
+                if (Objects.equals(activity.getProcessName(), packageName)) {
+                    return input.error("Heavy-weight applications can not have receivers "
+                            + "in main process");
+                }
+            }
+
+            // The following section has formatting off to make it easier to read the flags.
+            // Multi-lining them to fit within the column restriction makes it hard to tell what
+            // field is assigned where.
+            // @formatter:off
+            activity.theme = sa.getResourceId(R.styleable.AndroidManifestActivity_theme, 0);
+            activity.uiOptions = sa.getInt(R.styleable.AndroidManifestActivity_uiOptions, pkg.getUiOptions());
+
+            activity.flags |= flag(ActivityInfo.FLAG_ALLOW_TASK_REPARENTING, R.styleable.AndroidManifestActivity_allowTaskReparenting, pkg.isAllowTaskReparenting(), sa)
+                    | flag(ActivityInfo.FLAG_ALWAYS_RETAIN_TASK_STATE, R.styleable.AndroidManifestActivity_alwaysRetainTaskState, sa)
+                    | flag(ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH, R.styleable.AndroidManifestActivity_clearTaskOnLaunch, sa)
+                    | flag(ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS, R.styleable.AndroidManifestActivity_excludeFromRecents, sa)
+                    | flag(ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS, R.styleable.AndroidManifestActivity_finishOnCloseSystemDialogs, sa)
+                    | flag(ActivityInfo.FLAG_FINISH_ON_TASK_LAUNCH, R.styleable.AndroidManifestActivity_finishOnTaskLaunch, sa)
+                    | flag(ActivityInfo.FLAG_IMMERSIVE, R.styleable.AndroidManifestActivity_immersive, sa)
+                    | flag(ActivityInfo.FLAG_MULTIPROCESS, R.styleable.AndroidManifestActivity_multiprocess, sa)
+                    | flag(ActivityInfo.FLAG_NO_HISTORY, R.styleable.AndroidManifestActivity_noHistory, sa)
+                    | flag(ActivityInfo.FLAG_SHOW_FOR_ALL_USERS, R.styleable.AndroidManifestActivity_showForAllUsers, sa)
+                    | flag(ActivityInfo.FLAG_SHOW_FOR_ALL_USERS, R.styleable.AndroidManifestActivity_showOnLockScreen, sa)
+                    | flag(ActivityInfo.FLAG_STATE_NOT_NEEDED, R.styleable.AndroidManifestActivity_stateNotNeeded, sa)
+                    | flag(ActivityInfo.FLAG_SYSTEM_USER_ONLY, R.styleable.AndroidManifestActivity_systemUserOnly, sa);
+
+            if (!receiver) {
+                activity.flags |= flag(ActivityInfo.FLAG_HARDWARE_ACCELERATED, R.styleable.AndroidManifestActivity_hardwareAccelerated, pkg.isBaseHardwareAccelerated(), sa)
+                        | flag(ActivityInfo.FLAG_ALLOW_EMBEDDED, R.styleable.AndroidManifestActivity_allowEmbedded, sa)
+                        | flag(ActivityInfo.FLAG_ALWAYS_FOCUSABLE, R.styleable.AndroidManifestActivity_alwaysFocusable, sa)
+                        | flag(ActivityInfo.FLAG_AUTO_REMOVE_FROM_RECENTS, R.styleable.AndroidManifestActivity_autoRemoveFromRecents, sa)
+                        | flag(ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY, R.styleable.AndroidManifestActivity_relinquishTaskIdentity, sa)
+                        | flag(ActivityInfo.FLAG_RESUME_WHILE_PAUSING, R.styleable.AndroidManifestActivity_resumeWhilePausing, sa)
+                        | flag(ActivityInfo.FLAG_SHOW_WHEN_LOCKED, R.styleable.AndroidManifestActivity_showWhenLocked, sa)
+                        | flag(ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE, R.styleable.AndroidManifestActivity_supportsPictureInPicture, sa)
+                        | flag(ActivityInfo.FLAG_TURN_SCREEN_ON, R.styleable.AndroidManifestActivity_turnScreenOn, sa)
+                        | flag(ActivityInfo.FLAG_PREFER_MINIMAL_POST_PROCESSING, R.styleable.AndroidManifestActivity_preferMinimalPostProcessing, sa);
+
+                activity.privateFlags |= flag(ActivityInfo.FLAG_INHERIT_SHOW_WHEN_LOCKED, R.styleable.AndroidManifestActivity_inheritShowWhenLocked, sa);
+
+                activity.colorMode = sa.getInt(R.styleable.AndroidManifestActivity_colorMode, ActivityInfo.COLOR_MODE_DEFAULT);
+                activity.documentLaunchMode = sa.getInt(R.styleable.AndroidManifestActivity_documentLaunchMode, ActivityInfo.DOCUMENT_LAUNCH_NONE);
+                activity.launchMode = sa.getInt(R.styleable.AndroidManifestActivity_launchMode, ActivityInfo.LAUNCH_MULTIPLE);
+                activity.lockTaskLaunchMode = sa.getInt(R.styleable.AndroidManifestActivity_lockTaskMode, 0);
+                activity.maxRecents = sa.getInt(R.styleable.AndroidManifestActivity_maxRecents, ActivityTaskManager.getDefaultAppRecentsLimitStatic());
+                activity.persistableMode = sa.getInteger(R.styleable.AndroidManifestActivity_persistableMode, ActivityInfo.PERSIST_ROOT_ONLY);
+                activity.requestedVrComponent = sa.getString(R.styleable.AndroidManifestActivity_enableVrMode);
+                activity.rotationAnimation = sa.getInt(R.styleable.AndroidManifestActivity_rotationAnimation, WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED);
+                activity.softInputMode = sa.getInt(R.styleable.AndroidManifestActivity_windowSoftInputMode, 0);
+
+                activity.configChanges = PackageParser.getActivityConfigChanges(
+                        sa.getInt(R.styleable.AndroidManifestActivity_configChanges, 0),
+                        sa.getInt(R.styleable.AndroidManifestActivity_recreateOnConfigChanges, 0));
+
+                int screenOrientation = sa.getInt(R.styleable.AndroidManifestActivity_screenOrientation, SCREEN_ORIENTATION_UNSPECIFIED);
+                int resizeMode = getActivityResizeMode(pkg, sa, screenOrientation);
+                activity.screenOrientation = screenOrientation;
+                activity.resizeMode = resizeMode;
+
+                if (sa.hasValue(R.styleable.AndroidManifestActivity_maxAspectRatio)
+                        && sa.getType(R.styleable.AndroidManifestActivity_maxAspectRatio)
+                        == TypedValue.TYPE_FLOAT) {
+                    activity.setMaxAspectRatio(resizeMode,
+                            sa.getFloat(R.styleable.AndroidManifestActivity_maxAspectRatio,
+                                    0 /*default*/));
+                }
+
+                if (sa.hasValue(R.styleable.AndroidManifestActivity_minAspectRatio)
+                        && sa.getType(R.styleable.AndroidManifestActivity_minAspectRatio)
+                        == TypedValue.TYPE_FLOAT) {
+                    activity.setMinAspectRatio(resizeMode,
+                            sa.getFloat(R.styleable.AndroidManifestActivity_minAspectRatio,
+                                    0 /*default*/));
+                }
+            } else {
+                activity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
+                activity.configChanges = 0;
+                activity.flags |= flag(ActivityInfo.FLAG_SINGLE_USER, R.styleable.AndroidManifestActivity_singleUser, sa);
+            }
+            // @formatter:on
+
+            String taskAffinity = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestActivity_taskAffinity,
+                    Configuration.NATIVE_CONFIG_VERSION);
+
+            ParseResult<String> affinityNameResult = ComponentParseUtils.buildTaskAffinityName(
+                    packageName, pkg.getTaskAffinity(), taskAffinity, input);
+            if (affinityNameResult.isError()) {
+                return input.error(affinityNameResult);
+            }
+
+            activity.taskAffinity = affinityNameResult.getResult();
+
+            boolean visibleToEphemeral = sa.getBoolean(R.styleable.AndroidManifestActivity_visibleToInstantApps, false);
+            if (visibleToEphemeral) {
+                activity.flags |= ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP;
+                pkg.setVisibleToInstantApps(true);
+            }
+
+            return parseActivityOrAlias(activity, pkg, tag, parser, res, sa, receiver,
+                    false /*isAlias*/, visibleToEphemeral, input,
+                    R.styleable.AndroidManifestActivity_parentActivityName,
+                    R.styleable.AndroidManifestActivity_permission,
+                    R.styleable.AndroidManifestActivity_exported
+            );
+        } finally {
+            sa.recycle();
+        }
+    }
+
+    @NonNull
+    public static ParseResult<ParsedActivity> parseActivityAlias(ParsingPackage pkg, Resources res,
+            XmlResourceParser parser, boolean useRoundIcon, ParseInput input)
+            throws XmlPullParserException, IOException {
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestActivityAlias);
+        try {
+            String targetActivity = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestActivityAlias_targetActivity,
+                    Configuration.NATIVE_CONFIG_VERSION);
+            if (targetActivity == null) {
+                return input.error("<activity-alias> does not specify android:targetActivity");
+            }
+
+            String packageName = pkg.getPackageName();
+            targetActivity = ParsingUtils.buildClassName(packageName, targetActivity);
+            if (targetActivity == null) {
+                return input.error("Empty class name in package " + packageName);
+            }
+
+            ParsedActivity target = null;
+
+            List<ParsedActivity> activities = pkg.getActivities();
+            final int activitiesSize = ArrayUtils.size(activities);
+            for (int i = 0; i < activitiesSize; i++) {
+                ParsedActivity t = activities.get(i);
+                if (targetActivity.equals(t.getName())) {
+                    target = t;
+                    break;
+                }
+            }
+
+            if (target == null) {
+                return input.error("<activity-alias> target activity " + targetActivity
+                        + " not found in manifest with activities = "
+                        + pkg.getActivities()
+                        + ", parsedActivities = " + activities);
+            }
+
+            ParsedActivity activity = ParsedActivity.makeAlias(targetActivity, target);
+            String tag = "<" + parser.getName() + ">";
+
+            ParseResult<ParsedActivity> result = ParsedMainComponentUtils.parseMainComponent(
+                    activity, tag, null, pkg, sa, 0, useRoundIcon, input,
+                    R.styleable.AndroidManifestActivityAlias_banner,
+                    R.styleable.AndroidManifestActivityAlias_description,
+                    null /*directBootAwareAttr*/,
+                    R.styleable.AndroidManifestActivityAlias_enabled,
+                    R.styleable.AndroidManifestActivityAlias_icon,
+                    R.styleable.AndroidManifestActivityAlias_label,
+                    R.styleable.AndroidManifestActivityAlias_logo,
+                    R.styleable.AndroidManifestActivityAlias_name,
+                    null /*processAttr*/,
+                    R.styleable.AndroidManifestActivityAlias_roundIcon,
+                    null /*splitNameAttr*/);
+            if (result.isError()) {
+                return result;
+            }
+
+            // TODO add visibleToInstantApps attribute to activity alias
+            final boolean visibleToEphemeral =
+                    ((activity.getFlags() & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0);
+
+            return parseActivityOrAlias(activity, pkg, tag, parser, res, sa, false /*isReceiver*/, true /*isAlias*/,
+                    visibleToEphemeral, input,
+                    R.styleable.AndroidManifestActivityAlias_parentActivityName,
+                    R.styleable.AndroidManifestActivityAlias_permission,
+                    R.styleable.AndroidManifestActivityAlias_exported);
+        } finally {
+            sa.recycle();
+        }
+    }
+
+    /**
+     * This method shares parsing logic between Activity/Receiver/alias instances, but requires
+     * passing in booleans for isReceiver/isAlias, since there's no indicator in the other
+     * parameters.
+     *
+     * They're used to filter the parsed tags and their behavior. This makes the method rather
+     * messy, but it's more maintainable than writing 3 separate methods for essentially the same
+     * type of logic.
+     */
+    @NonNull
+    private static ParseResult<ParsedActivity> parseActivityOrAlias(ParsedActivity activity,
+            ParsingPackage pkg, String tag, XmlResourceParser parser, Resources resources,
+            TypedArray array, boolean isReceiver, boolean isAlias, boolean visibleToEphemeral,
+            ParseInput input, int parentActivityNameAttr, int permissionAttr,
+            int exportedAttr) throws IOException, XmlPullParserException {
+        String parentActivityName = array.getNonConfigurationString(parentActivityNameAttr, Configuration.NATIVE_CONFIG_VERSION);
+        if (parentActivityName != null) {
+            String packageName = pkg.getPackageName();
+            String parentClassName = ParsingUtils.buildClassName(packageName, parentActivityName);
+            if (parentClassName == null) {
+                Log.e(TAG, "Activity " + activity.getName()
+                        + " specified invalid parentActivityName " + parentActivityName);
+            } else {
+                activity.setParentActivity(parentClassName);
+            }
+        }
+
+        String permission = array.getNonConfigurationString(permissionAttr, 0);
+        activity.setPermission(permission != null ? permission : pkg.getPermission());
+
+        final boolean setExported = array.hasValue(exportedAttr);
+        if (setExported) {
+            activity.exported = array.getBoolean(exportedAttr, false);
+        }
+
+        final int depth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG
+                || parser.getDepth() > depth)) {
+            if (type != XmlPullParser.START_TAG) {
+                continue;
+            }
+
+            final ParseResult result;
+            if (parser.getName().equals("intent-filter")) {
+                ParseResult<ParsedIntentInfo> intentResult = parseIntentFilter(pkg, activity,
+                        !isReceiver, visibleToEphemeral, resources, parser, input);
+                if (intentResult.isSuccess()) {
+                    ParsedIntentInfo intent = intentResult.getResult();
+                    if (intent != null) {
+                        activity.order = Math.max(intent.getOrder(), activity.order);
+                        activity.addIntent(intent);
+                        if (PackageParser.LOG_UNSAFE_BROADCASTS && isReceiver
+                                && pkg.getTargetSdkVersion() >= Build.VERSION_CODES.O) {
+                            int actionCount = intent.countActions();
+                            for (int i = 0; i < actionCount; i++) {
+                                final String action = intent.getAction(i);
+                                if (action == null || !action.startsWith("android.")) {
+                                    continue;
+                                }
+
+                                if (!PackageParser.SAFE_BROADCASTS.contains(action)) {
+                                    Slog.w(TAG,
+                                            "Broadcast " + action + " may never be delivered to "
+                                                    + pkg.getPackageName() + " as requested at: "
+                                                    + parser.getPositionDescription());
+                                }
+                            }
+                        }
+                    }
+                }
+                result = intentResult;
+            } else if (parser.getName().equals("meta-data")) {
+                result = ParsedComponentUtils.addMetaData(activity, pkg, resources, parser, input);
+            } else if (!isReceiver && !isAlias && parser.getName().equals("preferred")) {
+                ParseResult<ParsedIntentInfo> intentResult = parseIntentFilter(pkg, activity,
+                        true /*allowImplicitEphemeralVisibility*/, visibleToEphemeral,
+                        resources, parser, input);
+                if (intentResult.isSuccess()) {
+                    ParsedIntentInfo intent = intentResult.getResult();
+                    if (intent != null) {
+                        pkg.addPreferredActivityFilter(activity.getClassName(), intent);
+                    }
+                }
+                result = intentResult;
+            } else if (!isReceiver && !isAlias && parser.getName().equals("layout")) {
+                ParseResult<ActivityInfo.WindowLayout> layoutResult = parseLayout(resources, parser,
+                        input);
+                if (layoutResult.isSuccess()) {
+                    activity.windowLayout = layoutResult.getResult();
+                }
+                result = layoutResult;
+            } else {
+                result = ParsingUtils.unknownTag(tag, pkg, parser, input);
+            }
+
+            if (result.isError()) {
+                return input.error(result);
+            }
+        }
+
+        ParseResult<ActivityInfo.WindowLayout> layoutResult = resolveWindowLayout(activity, input);
+        if (layoutResult.isError()) {
+            return input.error(layoutResult);
+        }
+        activity.windowLayout = layoutResult.getResult();
+
+        if (!setExported) {
+            activity.exported = activity.getIntents().size() > 0;
+        }
+
+        return input.success(activity);
+    }
+
+    @NonNull
+    private static ParseResult<ParsedIntentInfo> parseIntentFilter(ParsingPackage pkg,
+            ParsedActivity activity, boolean allowImplicitEphemeralVisibility,
+            boolean visibleToEphemeral, Resources resources, XmlResourceParser parser,
+            ParseInput input) throws IOException, XmlPullParserException {
+        ParseResult<ParsedIntentInfo> result = ParsedMainComponentUtils.parseIntentFilter(activity,
+                pkg, resources, parser, visibleToEphemeral, true /*allowGlobs*/,
+                true /*allowAutoVerify*/, allowImplicitEphemeralVisibility,
+                true /*failOnNoActions*/, input);
+        if (result.isError()) {
+            return input.error(result);
+        }
+
+        ParsedIntentInfo intent = result.getResult();
+        if (intent != null) {
+            if (intent.isVisibleToInstantApp()) {
+                activity.flags |= ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP;
+            }
+            if (intent.isImplicitlyVisibleToInstantApp()) {
+                activity.flags |= ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP;
+            }
+        }
+
+        return input.success(intent);
+    }
+
+    private static int getActivityResizeMode(ParsingPackage pkg, TypedArray sa,
+            int screenOrientation) {
+        Boolean resizeableActivity = pkg.getResizeableActivity();
+
+        if (sa.hasValue(R.styleable.AndroidManifestActivity_resizeableActivity)
+                || resizeableActivity != null) {
+            // Activity or app explicitly set if it is resizeable or not;
+            if (sa.getBoolean(R.styleable.AndroidManifestActivity_resizeableActivity,
+                    resizeableActivity != null && resizeableActivity)) {
+                return ActivityInfo.RESIZE_MODE_RESIZEABLE;
+            } else {
+                return ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+            }
+        }
+
+        if (pkg.isResizeableActivityViaSdkVersion()) {
+            // The activity or app didn't explicitly set the resizing option, however we want to
+            // make it resize due to the sdk version it is targeting.
+            return ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
+        }
+
+        // resize preference isn't set and target sdk version doesn't support resizing apps by
+        // default. For the app to be resizeable if it isn't fixed orientation or immersive.
+        if (ActivityInfo.isFixedOrientationPortrait(screenOrientation)) {
+            return ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY;
+        } else if (ActivityInfo.isFixedOrientationLandscape(screenOrientation)) {
+            return ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY;
+        } else if (screenOrientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED) {
+            return ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION;
+        } else {
+            return ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE;
+        }
+    }
+
+    @NonNull
+    private static ParseResult<ActivityInfo.WindowLayout> parseLayout(Resources res,
+            AttributeSet attrs, ParseInput input) {
+        TypedArray sw = res.obtainAttributes(attrs, R.styleable.AndroidManifestLayout);
+        try {
+            int width = -1;
+            float widthFraction = -1f;
+            int height = -1;
+            float heightFraction = -1f;
+            final int widthType = sw.getType(R.styleable.AndroidManifestLayout_defaultWidth);
+            if (widthType == TypedValue.TYPE_FRACTION) {
+                widthFraction = sw.getFraction(R.styleable.AndroidManifestLayout_defaultWidth, 1, 1,
+                        -1);
+            } else if (widthType == TypedValue.TYPE_DIMENSION) {
+                width = sw.getDimensionPixelSize(R.styleable.AndroidManifestLayout_defaultWidth,
+                        -1);
+            }
+            final int heightType = sw.getType(R.styleable.AndroidManifestLayout_defaultHeight);
+            if (heightType == TypedValue.TYPE_FRACTION) {
+                heightFraction = sw.getFraction(R.styleable.AndroidManifestLayout_defaultHeight, 1,
+                        1, -1);
+            } else if (heightType == TypedValue.TYPE_DIMENSION) {
+                height = sw.getDimensionPixelSize(R.styleable.AndroidManifestLayout_defaultHeight,
+                        -1);
+            }
+            int gravity = sw.getInt(R.styleable.AndroidManifestLayout_gravity, Gravity.CENTER);
+            int minWidth = sw.getDimensionPixelSize(R.styleable.AndroidManifestLayout_minWidth, -1);
+            int minHeight = sw.getDimensionPixelSize(R.styleable.AndroidManifestLayout_minHeight,
+                    -1);
+            return input.success(new ActivityInfo.WindowLayout(width, widthFraction, height,
+                    heightFraction, gravity, minWidth, minHeight));
+        } finally {
+            sw.recycle();
+        }
+    }
+
+    /**
+     * Resolves values in {@link ActivityInfo.WindowLayout}.
+     *
+     * <p>{@link ActivityInfo.WindowLayout#windowLayoutAffinity} has a fallback metadata used in
+     * Android R and some variants of pre-R.
+     */
+    private static ParseResult<ActivityInfo.WindowLayout> resolveWindowLayout(
+            ParsedActivity activity, ParseInput input) {
+        // There isn't a metadata for us to fall back. Whatever is in layout is correct.
+        if (activity.metaData == null || !activity.metaData.containsKey(
+                PackageParser.METADATA_ACTIVITY_WINDOW_LAYOUT_AFFINITY)) {
+            return input.success(activity.windowLayout);
+        }
+
+        // Layout already specifies a value. We should just use that one.
+        if (activity.windowLayout != null && activity.windowLayout.windowLayoutAffinity != null) {
+            return input.success(activity.windowLayout);
+        }
+
+        String windowLayoutAffinity = activity.metaData.getString(
+                PackageParser.METADATA_ACTIVITY_WINDOW_LAYOUT_AFFINITY);
+        ActivityInfo.WindowLayout layout = activity.windowLayout;
+        if (layout == null) {
+            layout = new ActivityInfo.WindowLayout(-1 /* width */, -1 /* widthFraction */,
+                    -1 /* height */, -1 /* heightFraction */, Gravity.NO_GRAVITY,
+                    -1 /* minWidth */, -1 /* minHeight */);
+        }
+        layout.windowLayoutAffinity = windowLayoutAffinity;
+        return input.success(layout);
+    }
+}
diff --git a/android/content/pm/parsing/component/ParsedAttribution.java b/android/content/pm/parsing/component/ParsedAttribution.java
new file mode 100644
index 0000000..02b3c7d
--- /dev/null
+++ b/android/content/pm/parsing/component/ParsedAttribution.java
@@ -0,0 +1,229 @@
+/*
+ * 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.content.pm.parsing.component;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StringRes;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.ArraySet;
+
+import com.android.internal.util.DataClass;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A {@link android.R.styleable#AndroidManifestAttribution &lt;attribution&gt;} tag parsed from the
+ * manifest.
+ *
+ * @hide
+ */
+@DataClass(genAidl = false)
+public class ParsedAttribution implements Parcelable {
+    /** Maximum length of attribution tag */
+    public static final int MAX_ATTRIBUTION_TAG_LEN = 50;
+
+    /** Maximum amount of attributions per package */
+    private static final int MAX_NUM_ATTRIBUTIONS = 1000;
+
+    /** Tag of the attribution */
+    public final @NonNull String tag;
+
+    /** User visible label fo the attribution */
+    public final @StringRes int label;
+
+    /** Ids of previously declared attributions this attribution inherits from */
+    public final @NonNull List<String> inheritFrom;
+
+    /**
+     * @return Is this set of attributions a valid combination for a single package?
+     */
+    public static boolean isCombinationValid(@Nullable List<ParsedAttribution> attributions) {
+        if (attributions == null) {
+            return true;
+        }
+
+        ArraySet<String> attributionTags = new ArraySet<>(attributions.size());
+        ArraySet<String> inheritFromAttributionTags = new ArraySet<>();
+
+        int numAttributions = attributions.size();
+        if (numAttributions > MAX_NUM_ATTRIBUTIONS) {
+            return false;
+        }
+
+        for (int attributionNum = 0; attributionNum < numAttributions; attributionNum++) {
+            boolean wasAdded = attributionTags.add(attributions.get(attributionNum).tag);
+            if (!wasAdded) {
+                // feature id is not unique
+                return false;
+            }
+        }
+
+        for (int attributionNum = 0; attributionNum < numAttributions; attributionNum++) {
+            ParsedAttribution feature = attributions.get(attributionNum);
+
+            int numInheritFrom = feature.inheritFrom.size();
+            for (int inheritFromNum = 0; inheritFromNum < numInheritFrom; inheritFromNum++) {
+                String inheritFrom = feature.inheritFrom.get(inheritFromNum);
+
+                if (attributionTags.contains(inheritFrom)) {
+                    // Cannot inherit from a attribution that is still defined
+                    return false;
+                }
+
+                boolean wasAdded = inheritFromAttributionTags.add(inheritFrom);
+                if (!wasAdded) {
+                    // inheritFrom is not unique
+                    return false;
+                }
+            }
+        }
+
+        return true;
+    }
+
+
+
+    // Code below generated by codegen v1.0.14.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedAttribution.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @android.annotation.IntDef(prefix = "MAX_", value = {
+        MAX_ATTRIBUTION_TAG_LEN,
+        MAX_NUM_ATTRIBUTIONS
+    })
+    @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE)
+    @DataClass.Generated.Member
+    public @interface Max {}
+
+    @DataClass.Generated.Member
+    public static String maxToString(@Max int value) {
+        switch (value) {
+            case MAX_ATTRIBUTION_TAG_LEN:
+                    return "MAX_ATTRIBUTION_TAG_LEN";
+            case MAX_NUM_ATTRIBUTIONS:
+                    return "MAX_NUM_ATTRIBUTIONS";
+            default: return Integer.toHexString(value);
+        }
+    }
+
+    /**
+     * Creates a new ParsedAttribution.
+     *
+     * @param tag
+     *   Tag of the attribution
+     * @param label
+     *   User visible label fo the attribution
+     * @param inheritFrom
+     *   Ids of previously declared attributions this attribution inherits from
+     */
+    @DataClass.Generated.Member
+    public ParsedAttribution(
+            @NonNull String tag,
+            @StringRes int label,
+            @NonNull List<String> inheritFrom) {
+        this.tag = tag;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, tag);
+        this.label = label;
+        com.android.internal.util.AnnotationValidations.validate(
+                StringRes.class, null, label);
+        this.inheritFrom = inheritFrom;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, inheritFrom);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        dest.writeString(tag);
+        dest.writeInt(label);
+        dest.writeStringList(inheritFrom);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    protected ParsedAttribution(@NonNull Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        String _tag = in.readString();
+        int _label = in.readInt();
+        List<String> _inheritFrom = new ArrayList<>();
+        in.readStringList(_inheritFrom);
+
+        this.tag = _tag;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, tag);
+        this.label = _label;
+        com.android.internal.util.AnnotationValidations.validate(
+                StringRes.class, null, label);
+        this.inheritFrom = _inheritFrom;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, inheritFrom);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<ParsedAttribution> CREATOR
+            = new Parcelable.Creator<ParsedAttribution>() {
+        @Override
+        public ParsedAttribution[] newArray(int size) {
+            return new ParsedAttribution[size];
+        }
+
+        @Override
+        public ParsedAttribution createFromParcel(@NonNull Parcel in) {
+            return new ParsedAttribution(in);
+        }
+    };
+
+    @DataClass.Generated(
+            time = 1583436566499L,
+            codegenVersion = "1.0.14",
+            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedAttribution.java",
+            inputSignatures = "public static final  int MAX_ATTRIBUTION_TAG_LEN\nprivate static final  int MAX_NUM_ATTRIBUTIONS\npublic final @android.annotation.NonNull java.lang.String tag\npublic final @android.annotation.StringRes int label\npublic final @android.annotation.NonNull java.util.List<java.lang.String> inheritFrom\npublic static  boolean isCombinationValid(java.util.List<android.content.pm.parsing.component.ParsedAttribution>)\nclass ParsedAttribution extends java.lang.Object implements [android.os.Parcelable]\[email protected](genAidl=false)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/android/content/pm/parsing/component/ParsedAttributionUtils.java b/android/content/pm/parsing/component/ParsedAttributionUtils.java
new file mode 100644
index 0000000..c4b1a0e
--- /dev/null
+++ b/android/content/pm/parsing/component/ParsedAttributionUtils.java
@@ -0,0 +1,116 @@
+/*
+ * 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.content.pm.parsing.component;
+
+import android.annotation.NonNull;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+
+import com.android.internal.R;
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/** @hide */
+public class ParsedAttributionUtils {
+
+    @NonNull
+    public static ParseResult<ParsedAttribution> parseAttribution(Resources res,
+            XmlResourceParser parser, ParseInput input)
+            throws IOException, XmlPullParserException {
+        String attributionTag;
+        int label;
+        List<String> inheritFrom = null;
+
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestAttribution);
+        if (sa == null) {
+            return input.error("<attribution> could not be parsed");
+        }
+
+        try {
+            attributionTag = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestAttribution_tag, 0);
+            if (attributionTag == null) {
+                // TODO moltmann: Remove handling of featureId
+                attributionTag = sa.getNonConfigurationString(
+                        R.styleable.AndroidManifestAttribution_featureId, 0);
+                if (attributionTag == null) {
+                    return input.error("<attribution> does not specify android:tag");
+                }
+            }
+            if (attributionTag.length() > ParsedAttribution.MAX_ATTRIBUTION_TAG_LEN) {
+                return input.error("android:tag is too long. Max length is "
+                        + ParsedAttribution.MAX_ATTRIBUTION_TAG_LEN);
+            }
+
+            label = sa.getResourceId(R.styleable.AndroidManifestAttribution_label, 0);
+            if (label == Resources.ID_NULL) {
+                return input.error("<attribution> does not specify android:label");
+            }
+        } finally {
+            sa.recycle();
+        }
+
+        int type;
+        final int innerDepth = parser.getDepth();
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+
+            String tagName = parser.getName();
+            if (tagName.equals("inherit-from")) {
+                sa = res.obtainAttributes(parser,
+                        R.styleable.AndroidManifestAttributionInheritFrom);
+                if (sa == null) {
+                    return input.error("<inherit-from> could not be parsed");
+                }
+
+                try {
+                    String inheritFromId = sa.getNonConfigurationString(
+                            R.styleable.AndroidManifestAttributionInheritFrom_tag, 0);
+
+                    if (inheritFrom == null) {
+                        inheritFrom = new ArrayList<>();
+                    }
+                    inheritFrom.add(inheritFromId);
+                } finally {
+                    sa.recycle();
+                }
+            } else {
+                return input.error("Bad element under <attribution>: " + tagName);
+            }
+        }
+
+        if (inheritFrom == null) {
+            inheritFrom = Collections.emptyList();
+        } else {
+            ((ArrayList) inheritFrom).trimToSize();
+        }
+
+        return input.success(new ParsedAttribution(attributionTag, label, inheritFrom));
+    }
+}
diff --git a/android/content/pm/parsing/component/ParsedComponent.java b/android/content/pm/parsing/component/ParsedComponent.java
new file mode 100644
index 0000000..6323d69
--- /dev/null
+++ b/android/content/pm/parsing/component/ParsedComponent.java
@@ -0,0 +1,208 @@
+/*
+ * 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.content.pm.parsing.component;
+
+import static android.content.pm.parsing.ParsingPackageImpl.sForInternedString;
+
+import android.annotation.CallSuper;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import com.android.internal.util.CollectionUtils;
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling;
+import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/** @hide */
+public abstract class ParsedComponent implements Parcelable {
+
+    private static ParsedIntentInfo.ListParceler sForIntentInfos = Parcelling.Cache.getOrCreate(
+            ParsedIntentInfo.ListParceler.class);
+
+    @NonNull
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String name;
+    int icon;
+    int labelRes;
+    @Nullable
+    CharSequence nonLocalizedLabel;
+    int logo;
+    int banner;
+    int descriptionRes;
+
+    // TODO(b/135203078): Replace flags with individual booleans, scoped by subclass
+    int flags;
+
+    @NonNull
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String packageName;
+
+    @Nullable
+    @DataClass.PluralOf("intent")
+    @DataClass.ParcelWith(ParsedIntentInfo.ListParceler.class)
+    private List<ParsedIntentInfo> intents;
+
+    private ComponentName componentName;
+
+    @Nullable
+    protected Bundle metaData;
+
+    ParsedComponent() {
+
+    }
+
+    @SuppressWarnings("IncompleteCopyConstructor")
+    public ParsedComponent(ParsedComponent other) {
+        this.metaData = other.metaData;
+        this.name = other.name;
+        this.icon = other.getIcon();
+        this.labelRes = other.getLabelRes();
+        this.nonLocalizedLabel = other.getNonLocalizedLabel();
+        this.logo = other.getLogo();
+        this.banner = other.getBanner();
+
+        this.descriptionRes = other.getDescriptionRes();
+
+        this.flags = other.getFlags();
+
+        this.setPackageName(other.packageName);
+        this.intents = new ArrayList<>(other.getIntents());
+    }
+
+    public void addIntent(ParsedIntentInfo intent) {
+        this.intents = CollectionUtils.add(this.intents, intent);
+    }
+
+    @NonNull
+    public List<ParsedIntentInfo> getIntents() {
+        return intents != null ? intents : Collections.emptyList();
+    }
+
+    public ParsedComponent setName(String name) {
+        this.name = TextUtils.safeIntern(name);
+        return this;
+    }
+
+    @CallSuper
+    public void setPackageName(@NonNull String packageName) {
+        this.packageName = TextUtils.safeIntern(packageName);
+        //noinspection ConstantConditions
+        this.componentName = null;
+
+        // Note: this method does not edit name (which can point to a class), because this package
+        // name change is not changing the package in code, but the identifier used by the system.
+    }
+
+    @NonNull
+    public ComponentName getComponentName() {
+        if (componentName == null) {
+            componentName = new ComponentName(getPackageName(), getName());
+        }
+        return componentName;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(this.name);
+        dest.writeInt(this.getIcon());
+        dest.writeInt(this.getLabelRes());
+        dest.writeCharSequence(this.getNonLocalizedLabel());
+        dest.writeInt(this.getLogo());
+        dest.writeInt(this.getBanner());
+        dest.writeInt(this.getDescriptionRes());
+        dest.writeInt(this.getFlags());
+        sForInternedString.parcel(this.packageName, dest, flags);
+        sForIntentInfos.parcel(this.getIntents(), dest, flags);
+        dest.writeBundle(this.metaData);
+    }
+
+    protected ParsedComponent(Parcel in) {
+        // We use the boot classloader for all classes that we load.
+        final ClassLoader boot = Object.class.getClassLoader();
+        //noinspection ConstantConditions
+        this.name = in.readString();
+        this.icon = in.readInt();
+        this.labelRes = in.readInt();
+        this.nonLocalizedLabel = in.readCharSequence();
+        this.logo = in.readInt();
+        this.banner = in.readInt();
+        this.descriptionRes = in.readInt();
+        this.flags = in.readInt();
+        //noinspection ConstantConditions
+        this.packageName = sForInternedString.unparcel(in);
+        this.intents = sForIntentInfos.unparcel(in);
+        this.metaData = in.readBundle(boot);
+    }
+
+    @NonNull
+    public String getName() {
+        return name;
+    }
+
+    public int getIcon() {
+        return icon;
+    }
+
+    public int getLabelRes() {
+        return labelRes;
+    }
+
+    @Nullable
+    public CharSequence getNonLocalizedLabel() {
+        return nonLocalizedLabel;
+    }
+
+    public int getLogo() {
+        return logo;
+    }
+
+    public int getBanner() {
+        return banner;
+    }
+
+    public int getDescriptionRes() {
+        return descriptionRes;
+    }
+
+    public int getFlags() {
+        return flags;
+    }
+
+    @NonNull
+    public String getPackageName() {
+        return packageName;
+    }
+
+    @Nullable
+    public Bundle getMetaData() {
+        return metaData;
+    }
+}
diff --git a/android/content/pm/parsing/component/ParsedComponentUtils.java b/android/content/pm/parsing/component/ParsedComponentUtils.java
new file mode 100644
index 0000000..b37b617
--- /dev/null
+++ b/android/content/pm/parsing/component/ParsedComponentUtils.java
@@ -0,0 +1,100 @@
+/*
+ * 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.content.pm.parsing.component;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.PackageManager;
+import android.content.pm.parsing.ParsingPackage;
+import android.content.pm.parsing.ParsingUtils;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.util.TypedValue;
+
+import com.android.internal.annotations.VisibleForTesting;
+import android.content.pm.parsing.ParsingPackageUtils;
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
+
+/** @hide */
+class ParsedComponentUtils {
+
+    private static final String TAG = ParsingPackageUtils.TAG;
+
+    @NonNull
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    static <Component extends ParsedComponent> ParseResult<Component> parseComponent(
+            Component component, String tag, ParsingPackage pkg, TypedArray array,
+            boolean useRoundIcon, ParseInput input, int bannerAttr,
+            @Nullable Integer descriptionAttr, int iconAttr, int labelAttr, int logoAttr,
+            int nameAttr, int roundIconAttr) {
+        String name = array.getNonConfigurationString(nameAttr, 0);
+        if (TextUtils.isEmpty(name)) {
+            return input.error(tag + " does not specify android:name");
+        }
+
+        String packageName = pkg.getPackageName();
+        String className = ParsingUtils.buildClassName(packageName, name);
+        if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(className)) {
+            return input.error(tag + " invalid android:name");
+        }
+
+        //noinspection ConstantConditions; null check done above with isEmpty
+        component.setName(className);
+        component.setPackageName(packageName);
+
+        if (useRoundIcon) {
+            component.icon = array.getResourceId(roundIconAttr, 0);
+        }
+
+        if (component.icon == 0) {
+            component.icon = array.getResourceId(iconAttr, 0);
+        }
+
+        component.logo = array.getResourceId(logoAttr, 0);
+        component.banner = array.getResourceId(bannerAttr, 0);
+
+        if (descriptionAttr != null) {
+            component.descriptionRes = array.getResourceId(descriptionAttr, 0);
+        }
+
+        TypedValue v = array.peekValue(labelAttr);
+        if (v != null) {
+            component.labelRes = v.resourceId;
+            if (v.resourceId == 0) {
+                component.nonLocalizedLabel = v.coerceToString();
+            }
+        }
+
+        return input.success(component);
+    }
+
+    static ParseResult<Bundle> addMetaData(ParsedComponent component, ParsingPackage pkg,
+            Resources resources, XmlResourceParser parser, ParseInput input) {
+        ParseResult<Bundle> result = ParsingPackageUtils.parseMetaData(pkg, resources,
+                parser, component.metaData, input);
+        if (result.isError()) {
+            return input.error(result);
+        }
+        Bundle bundle = result.getResult();
+        component.metaData = bundle;
+        return input.success(bundle);
+    }
+}
diff --git a/android/content/pm/parsing/component/ParsedInstrumentation.java b/android/content/pm/parsing/component/ParsedInstrumentation.java
new file mode 100644
index 0000000..aa33e79
--- /dev/null
+++ b/android/content/pm/parsing/component/ParsedInstrumentation.java
@@ -0,0 +1,115 @@
+/*
+ * 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.content.pm.parsing.component;
+
+import static android.content.pm.parsing.ParsingPackageImpl.sForInternedString;
+
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
+
+/** @hide */
+public class ParsedInstrumentation extends ParsedComponent {
+
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String targetPackage;
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String targetProcesses;
+    boolean handleProfiling;
+    boolean functionalTest;
+
+    public ParsedInstrumentation() {
+    }
+
+    public void setTargetPackage(@Nullable String targetPackage) {
+        this.targetPackage = TextUtils.safeIntern(targetPackage);
+    }
+
+    public void setTargetProcesses(@Nullable String targetProcesses) {
+        this.targetProcesses = TextUtils.safeIntern(targetProcesses);
+    }
+
+    public String toString() {
+        StringBuilder sb = new StringBuilder(128);
+        sb.append("Instrumentation{");
+        sb.append(Integer.toHexString(System.identityHashCode(this)));
+        sb.append(' ');
+        ComponentName.appendShortString(sb, getPackageName(), getName());
+        sb.append('}');
+        return sb.toString();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        super.writeToParcel(dest, flags);
+        sForInternedString.parcel(this.targetPackage, dest, flags);
+        sForInternedString.parcel(this.targetProcesses, dest, flags);
+        dest.writeBoolean(this.handleProfiling);
+        dest.writeBoolean(this.functionalTest);
+    }
+
+    protected ParsedInstrumentation(Parcel in) {
+        super(in);
+        this.targetPackage = sForInternedString.unparcel(in);
+        this.targetProcesses = sForInternedString.unparcel(in);
+        this.handleProfiling = in.readByte() != 0;
+        this.functionalTest = in.readByte() != 0;
+    }
+
+    public static final Parcelable.Creator<ParsedInstrumentation> CREATOR =
+            new Parcelable.Creator<ParsedInstrumentation>() {
+                @Override
+                public ParsedInstrumentation createFromParcel(Parcel source) {
+                    return new ParsedInstrumentation(source);
+                }
+
+                @Override
+                public ParsedInstrumentation[] newArray(int size) {
+                    return new ParsedInstrumentation[size];
+                }
+            };
+
+    @Nullable
+    public String getTargetPackage() {
+        return targetPackage;
+    }
+
+    @Nullable
+    public String getTargetProcesses() {
+        return targetProcesses;
+    }
+
+    public boolean isHandleProfiling() {
+        return handleProfiling;
+    }
+
+    public boolean isFunctionalTest() {
+        return functionalTest;
+    }
+}
diff --git a/android/content/pm/parsing/component/ParsedInstrumentationUtils.java b/android/content/pm/parsing/component/ParsedInstrumentationUtils.java
new file mode 100644
index 0000000..89645fc
--- /dev/null
+++ b/android/content/pm/parsing/component/ParsedInstrumentationUtils.java
@@ -0,0 +1,74 @@
+/*
+ * 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.content.pm.parsing.component;
+
+import android.annotation.NonNull;
+import android.content.pm.parsing.ParsingPackage;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+
+import com.android.internal.R;
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+/** @hide */
+public class ParsedInstrumentationUtils {
+
+    @NonNull
+    public static ParseResult<ParsedInstrumentation> parseInstrumentation(ParsingPackage pkg,
+            Resources res, XmlResourceParser parser, boolean useRoundIcon,
+            ParseInput input) throws IOException, XmlPullParserException {
+        ParsedInstrumentation
+                instrumentation = new ParsedInstrumentation();
+        String tag = "<" + parser.getName() + ">";
+
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestInstrumentation);
+        try {
+            ParseResult<ParsedInstrumentation> result = ParsedComponentUtils.parseComponent(
+                    instrumentation, tag, pkg, sa, useRoundIcon, input,
+                    R.styleable.AndroidManifestInstrumentation_banner,
+                    null /*descriptionAttr*/,
+                    R.styleable.AndroidManifestInstrumentation_icon,
+                    R.styleable.AndroidManifestInstrumentation_label,
+                    R.styleable.AndroidManifestInstrumentation_logo,
+                    R.styleable.AndroidManifestInstrumentation_name,
+                    R.styleable.AndroidManifestInstrumentation_roundIcon);
+            if (result.isError()) {
+                return result;
+            }
+
+            // @formatter:off
+            // Note: don't allow this value to be a reference to a resource
+            // that may change.
+            instrumentation.setTargetPackage(sa.getNonResourceString(R.styleable.AndroidManifestInstrumentation_targetPackage));
+            instrumentation.setTargetProcesses(sa.getNonResourceString(R.styleable.AndroidManifestInstrumentation_targetProcesses));
+            instrumentation.handleProfiling = sa.getBoolean(R.styleable.AndroidManifestInstrumentation_handleProfiling, false);
+            instrumentation.functionalTest = sa.getBoolean(R.styleable.AndroidManifestInstrumentation_functionalTest, false);
+            // @formatter:on
+        } finally {
+            sa.recycle();
+        }
+
+        return ComponentParseUtils.parseAllMetaData(pkg, res, parser, tag, instrumentation,
+                input);
+    }
+}
diff --git a/android/content/pm/parsing/component/ParsedIntentInfo.java b/android/content/pm/parsing/component/ParsedIntentInfo.java
new file mode 100644
index 0000000..0ba92cc
--- /dev/null
+++ b/android/content/pm/parsing/component/ParsedIntentInfo.java
@@ -0,0 +1,200 @@
+/*
+ * 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.content.pm.parsing.component;
+
+import android.annotation.Nullable;
+import android.content.IntentFilter;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Pair;
+
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** @hide **/
+public final class ParsedIntentInfo extends IntentFilter {
+
+    public static final Parceler PARCELER = new Parceler();
+
+    public static class Parceler implements Parcelling<ParsedIntentInfo> {
+
+        @Override
+        public void parcel(ParsedIntentInfo item, Parcel dest, int parcelFlags) {
+            item.writeIntentInfoToParcel(dest, parcelFlags);
+        }
+
+        @Override
+        public ParsedIntentInfo unparcel(Parcel source) {
+            return new ParsedIntentInfo(source);
+        }
+    }
+
+    public static class ListParceler implements Parcelling<List<ParsedIntentInfo>> {
+
+        /**
+         * <p>
+         * Implementation note: The serialized form for the intent list also contains the name
+         * of the concrete class that's stored in the list, and assumes that every element of the
+         * list is of the same type. This is very similar to the original parcelable mechanism.
+         * We cannot use that directly because IntentInfo extends IntentFilter, which is parcelable
+         * and is public API. It also declares Parcelable related methods as final which means
+         * we can't extend them. The approach of using composition instead of inheritance leads to
+         * a large set of cascading changes in the PackageManagerService, which seem undesirable.
+         *
+         * <p>
+         * <b>WARNING: </b> The list of objects returned by this function might need to be fixed up
+         * to make sure their owner fields are consistent. See {@code fixupOwner}.
+         */
+        @Override
+        public void parcel(List<ParsedIntentInfo> item, Parcel dest, int parcelFlags) {
+            if (item == null) {
+                dest.writeInt(-1);
+                return;
+            }
+
+            final int size = item.size();
+            dest.writeInt(size);
+
+            for (int index = 0; index < size; index++) {
+                PARCELER.parcel(item.get(index), dest, parcelFlags);
+            }
+        }
+
+        @Override
+        public List<ParsedIntentInfo> unparcel(Parcel source) {
+            int size = source.readInt();
+            if (size == -1) {
+                return null;
+            }
+
+            if (size == 0) {
+                return new ArrayList<>(0);
+            }
+
+            final ArrayList<ParsedIntentInfo> intentsList = new ArrayList<>(size);
+            for (int i = 0; i < size; ++i) {
+                intentsList.add(PARCELER.unparcel(source));
+            }
+
+            return intentsList;
+        }
+    }
+
+    public static class StringPairListParceler implements Parcelling<List<Pair<String, ParsedIntentInfo>>> {
+
+        @Override
+        public void parcel(List<Pair<String, ParsedIntentInfo>> item, Parcel dest,
+                int parcelFlags) {
+            if (item == null) {
+                dest.writeInt(-1);
+                return;
+            }
+
+            final int size = item.size();
+            dest.writeInt(size);
+
+            for (int index = 0; index < size; index++) {
+                Pair<String, ParsedIntentInfo> pair = item.get(index);
+                dest.writeString(pair.first);
+                PARCELER.parcel(pair.second, dest, parcelFlags);
+            }
+        }
+
+        @Override
+        public List<Pair<String, ParsedIntentInfo>> unparcel(Parcel source) {
+            int size = source.readInt();
+            if (size == -1) {
+                return null;
+            }
+
+            if (size == 0) {
+                return new ArrayList<>(0);
+            }
+
+            final List<Pair<String, ParsedIntentInfo>> list = new ArrayList<>(size);
+            for (int i = 0; i < size; ++i) {
+                list.add(Pair.create(source.readString(), PARCELER.unparcel(source)));
+            }
+
+            return list;
+        }
+    }
+
+    boolean hasDefault;
+    int labelRes;
+    @Nullable
+    CharSequence nonLocalizedLabel;
+    int icon;
+
+    public ParsedIntentInfo() {
+    }
+
+    public ParsedIntentInfo(Parcel in) {
+        super(in);
+        hasDefault = in.readBoolean();
+        labelRes = in.readInt();
+        nonLocalizedLabel = in.readCharSequence();
+        icon = in.readInt();
+    }
+
+    public void writeIntentInfoToParcel(Parcel dest, int flags) {
+        super.writeToParcel(dest, flags);
+        dest.writeBoolean(hasDefault);
+        dest.writeInt(labelRes);
+        dest.writeCharSequence(nonLocalizedLabel);
+        dest.writeInt(icon);
+    }
+
+    public String toString() {
+        return "ProviderIntentInfo{"
+                + Integer.toHexString(System.identityHashCode(this))
+                + '}';
+    }
+
+    public static final Parcelable.Creator<ParsedIntentInfo> CREATOR =
+            new Parcelable.Creator<ParsedIntentInfo>() {
+                @Override
+                public ParsedIntentInfo createFromParcel(Parcel source) {
+                    return new ParsedIntentInfo(source);
+                }
+
+                @Override
+                public ParsedIntentInfo[] newArray(int size) {
+                    return new ParsedIntentInfo[size];
+                }
+            };
+
+    public boolean isHasDefault() {
+        return hasDefault;
+    }
+
+    public int getLabelRes() {
+        return labelRes;
+    }
+
+    @Nullable
+    public CharSequence getNonLocalizedLabel() {
+        return nonLocalizedLabel;
+    }
+
+    public int getIcon() {
+        return icon;
+    }
+}
diff --git a/android/content/pm/parsing/component/ParsedIntentInfoUtils.java b/android/content/pm/parsing/component/ParsedIntentInfoUtils.java
new file mode 100644
index 0000000..390f769
--- /dev/null
+++ b/android/content/pm/parsing/component/ParsedIntentInfoUtils.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.content.pm.parsing.component;
+
+import android.annotation.NonNull;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageParser;
+import android.content.pm.parsing.ParsingPackage;
+import android.content.pm.parsing.ParsingPackageUtils;
+import android.content.pm.parsing.ParsingUtils;
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.os.PatternMatcher;
+import android.util.Slog;
+import android.util.TypedValue;
+
+import com.android.internal.R;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.Iterator;
+
+/** @hide */
+public class ParsedIntentInfoUtils {
+
+    private static final String TAG = ParsingPackageUtils.TAG;
+
+    @NonNull
+    public static ParseResult<ParsedIntentInfo> parseIntentInfo(String className,
+            ParsingPackage pkg, Resources res, XmlResourceParser parser, boolean allowGlobs,
+            boolean allowAutoVerify, ParseInput input)
+            throws XmlPullParserException, IOException {
+        ParsedIntentInfo intentInfo = new ParsedIntentInfo();
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestIntentFilter);
+        try {
+            intentInfo.setPriority(sa.getInt(R.styleable.AndroidManifestIntentFilter_priority, 0));
+            intentInfo.setOrder(sa.getInt(R.styleable.AndroidManifestIntentFilter_order, 0));
+
+            TypedValue v = sa.peekValue(R.styleable.AndroidManifestIntentFilter_label);
+            if (v != null) {
+                intentInfo.labelRes = v.resourceId;
+                if (v.resourceId == 0) {
+                    intentInfo.nonLocalizedLabel = v.coerceToString();
+                }
+            }
+
+            if (PackageParser.sUseRoundIcon) {
+                intentInfo.icon = sa.getResourceId(
+                        R.styleable.AndroidManifestIntentFilter_roundIcon, 0);
+            }
+
+            if (intentInfo.icon == 0) {
+                intentInfo.icon = sa.getResourceId(R.styleable.AndroidManifestIntentFilter_icon, 0);
+            }
+
+            if (allowAutoVerify) {
+                intentInfo.setAutoVerify(sa.getBoolean(
+                        R.styleable.AndroidManifestIntentFilter_autoVerify,
+                        false));
+            }
+        } finally {
+            sa.recycle();
+        }
+        final int depth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG
+                || parser.getDepth() > depth)) {
+            if (type != XmlPullParser.START_TAG) {
+                continue;
+            }
+
+            final ParseResult result;
+            String nodeName = parser.getName();
+            switch (nodeName) {
+                case "action": {
+                    String value = parser.getAttributeValue(PackageParser.ANDROID_RESOURCES,
+                            "name");
+                    if (value == null) {
+                        result = input.error("No value supplied for <android:name>");
+                    } else if (value.isEmpty()) {
+                        intentInfo.addAction(value);
+                        // Prior to R, this was not a failure
+                        result = input.deferError("No value supplied for <android:name>",
+                                ParseInput.DeferredError.EMPTY_INTENT_ACTION_CATEGORY);
+                    } else {
+                        intentInfo.addAction(value);
+                        result = input.success(null);
+                    }
+                    break;
+                }
+                case "category": {
+                    String value = parser.getAttributeValue(PackageParser.ANDROID_RESOURCES,
+                            "name");
+                    if (value == null) {
+                        result = input.error("No value supplied for <android:name>");
+                    } else if (value.isEmpty()) {
+                        intentInfo.addCategory(value);
+                        // Prior to R, this was not a failure
+                        result = input.deferError("No value supplied for <android:name>",
+                                ParseInput.DeferredError.EMPTY_INTENT_ACTION_CATEGORY);
+                    } else {
+                        intentInfo.addCategory(value);
+                        result = input.success(null);
+                    }
+                    break;
+                }
+                case "data":
+                    result = parseData(intentInfo, res, parser, allowGlobs, input);
+                    break;
+                default:
+                    result = ParsingUtils.unknownTag("<intent-filter>", pkg, parser, input);
+                    break;
+            }
+
+            if (result.isError()) {
+                return input.error(result);
+            }
+        }
+
+        intentInfo.hasDefault = intentInfo.hasCategory(Intent.CATEGORY_DEFAULT);
+
+        if (PackageParser.DEBUG_PARSER) {
+            final StringBuilder cats = new StringBuilder("Intent d=");
+            cats.append(intentInfo.isHasDefault());
+            cats.append(", cat=");
+
+            final Iterator<String> it = intentInfo.categoriesIterator();
+            if (it != null) {
+                while (it.hasNext()) {
+                    cats.append(' ');
+                    cats.append(it.next());
+                }
+            }
+            Slog.d(TAG, cats.toString());
+        }
+
+        return input.success(intentInfo);
+    }
+
+    @NonNull
+    private static ParseResult<ParsedIntentInfo> parseData(ParsedIntentInfo intentInfo,
+            Resources resources, XmlResourceParser parser, boolean allowGlobs, ParseInput input) {
+        TypedArray sa = resources.obtainAttributes(parser, R.styleable.AndroidManifestData);
+        try {
+            String str = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestData_mimeType, 0);
+            if (str != null) {
+                try {
+                    intentInfo.addDataType(str);
+                } catch (IntentFilter.MalformedMimeTypeException e) {
+                    return input.error(e.toString());
+                }
+            }
+
+            str = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestData_mimeGroup, 0);
+            if (str != null) {
+                intentInfo.addMimeGroup(str);
+            }
+
+            str = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestData_scheme, 0);
+            if (str != null) {
+                intentInfo.addDataScheme(str);
+            }
+
+            str = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestData_ssp, 0);
+            if (str != null) {
+                intentInfo.addDataSchemeSpecificPart(str,
+                        PatternMatcher.PATTERN_LITERAL);
+            }
+
+            str = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestData_sspPrefix, 0);
+            if (str != null) {
+                intentInfo.addDataSchemeSpecificPart(str,
+                        PatternMatcher.PATTERN_PREFIX);
+            }
+
+            str = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestData_sspPattern, 0);
+            if (str != null) {
+                if (!allowGlobs) {
+                    return input.error(
+                            "sspPattern not allowed here; ssp must be literal");
+                }
+                intentInfo.addDataSchemeSpecificPart(str,
+                        PatternMatcher.PATTERN_SIMPLE_GLOB);
+            }
+
+            String host = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestData_host, 0);
+            String port = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestData_port, 0);
+            if (host != null) {
+                intentInfo.addDataAuthority(host, port);
+            }
+
+            str = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestData_path, 0);
+            if (str != null) {
+                intentInfo.addDataPath(str, PatternMatcher.PATTERN_LITERAL);
+            }
+
+            str = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestData_pathPrefix, 0);
+            if (str != null) {
+                intentInfo.addDataPath(str, PatternMatcher.PATTERN_PREFIX);
+            }
+
+            str = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestData_pathPattern, 0);
+            if (str != null) {
+                if (!allowGlobs) {
+                    return input.error(
+                            "pathPattern not allowed here; path must be literal");
+                }
+                intentInfo.addDataPath(str, PatternMatcher.PATTERN_SIMPLE_GLOB);
+            }
+
+            str = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestData_pathAdvancedPattern, 0);
+            if (str != null) {
+                if (!allowGlobs) {
+                    return input.error(
+                            "pathAdvancedPattern not allowed here; path must be literal");
+                }
+                intentInfo.addDataPath(str, PatternMatcher.PATTERN_ADVANCED_GLOB);
+            }
+
+            return input.success(null);
+        } finally {
+            sa.recycle();
+        }
+    }
+}
diff --git a/android/content/pm/parsing/component/ParsedMainComponent.java b/android/content/pm/parsing/component/ParsedMainComponent.java
new file mode 100644
index 0000000..a5e394d
--- /dev/null
+++ b/android/content/pm/parsing/component/ParsedMainComponent.java
@@ -0,0 +1,152 @@
+/*
+ * 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.content.pm.parsing.component;
+
+import static android.content.pm.parsing.ParsingPackageImpl.sForInternedString;
+
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
+
+/** @hide */
+public class ParsedMainComponent extends ParsedComponent {
+
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String processName;
+    boolean directBootAware;
+    boolean enabled = true;
+    boolean exported;
+    int order;
+
+    @Nullable
+    String splitName;
+
+    public ParsedMainComponent() {
+    }
+
+    public ParsedMainComponent(ParsedMainComponent other) {
+        super(other);
+        this.processName = other.processName;
+        this.directBootAware = other.directBootAware;
+        this.enabled = other.enabled;
+        this.exported = other.exported;
+        this.order = other.order;
+        this.splitName = other.splitName;
+    }
+
+    public ParsedMainComponent setProcessName(String processName) {
+        this.processName = TextUtils.safeIntern(processName);
+        return this;
+    }
+
+    public ParsedMainComponent setEnabled(boolean enabled) {
+        this.enabled = enabled;
+        return this;
+    }
+
+    /**
+     * A main component's name is a class name. This makes code slightly more readable.
+     */
+    public String getClassName() {
+        return getName();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        super.writeToParcel(dest, flags);
+        sForInternedString.parcel(this.processName, dest, flags);
+        dest.writeBoolean(this.directBootAware);
+        dest.writeBoolean(this.enabled);
+        dest.writeBoolean(this.exported);
+        dest.writeInt(this.order);
+        dest.writeString(this.splitName);
+    }
+
+    protected ParsedMainComponent(Parcel in) {
+        super(in);
+        this.processName = sForInternedString.unparcel(in);
+        this.directBootAware = in.readBoolean();
+        this.enabled = in.readBoolean();
+        this.exported = in.readBoolean();
+        this.order = in.readInt();
+        this.splitName = in.readString();
+    }
+
+    public static final Parcelable.Creator<ParsedMainComponent> CREATOR =
+            new Parcelable.Creator<ParsedMainComponent>() {
+                @Override
+                public ParsedMainComponent createFromParcel(Parcel source) {
+                    return new ParsedMainComponent(source);
+                }
+
+                @Override
+                public ParsedMainComponent[] newArray(int size) {
+                    return new ParsedMainComponent[size];
+                }
+            };
+
+    @Nullable
+    public String getProcessName() {
+        return processName;
+    }
+
+    public boolean isDirectBootAware() {
+        return directBootAware;
+    }
+
+    public boolean isEnabled() {
+        return enabled;
+    }
+
+    public boolean isExported() {
+        return exported;
+    }
+
+    public int getOrder() {
+        return order;
+    }
+
+    @Nullable
+    public String getSplitName() {
+        return splitName;
+    }
+
+    public ParsedMainComponent setDirectBootAware(boolean value) {
+        directBootAware = value;
+        return this;
+    }
+
+    public ParsedMainComponent setExported(boolean value) {
+        exported = value;
+        return this;
+    }
+
+    public ParsedMainComponent setSplitName(@Nullable String value) {
+        splitName = value;
+        return this;
+    }
+}
diff --git a/android/content/pm/parsing/component/ParsedMainComponentUtils.java b/android/content/pm/parsing/component/ParsedMainComponentUtils.java
new file mode 100644
index 0000000..f4c9914
--- /dev/null
+++ b/android/content/pm/parsing/component/ParsedMainComponentUtils.java
@@ -0,0 +1,136 @@
+/*
+ * 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.content.pm.parsing.component;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.IntentFilter;
+import android.content.pm.parsing.ParsingPackage;
+import android.content.pm.parsing.ParsingPackageUtils;
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.os.Build;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+/** @hide */
+class ParsedMainComponentUtils {
+
+    private static final String TAG = ParsingPackageUtils.TAG;
+
+    @NonNull
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    static <Component extends ParsedMainComponent> ParseResult<Component> parseMainComponent(
+            Component component, String tag, String[] separateProcesses, ParsingPackage pkg,
+            TypedArray array, int flags, boolean useRoundIcon, ParseInput input,
+            int bannerAttr, int descriptionAttr, @Nullable Integer directBootAwareAttr,
+            @Nullable Integer enabledAttr, int iconAttr, int labelAttr, int logoAttr, int nameAttr,
+            @Nullable Integer processAttr, int roundIconAttr, @Nullable Integer splitNameAttr) {
+        ParseResult<Component> result = ParsedComponentUtils.parseComponent(component, tag, pkg,
+                array, useRoundIcon, input, bannerAttr, descriptionAttr, iconAttr, labelAttr,
+                logoAttr, nameAttr, roundIconAttr);
+        if (result.isError()) {
+            return result;
+        }
+
+        if (directBootAwareAttr != null) {
+            component.directBootAware = array.getBoolean(directBootAwareAttr, false);
+            if (component.isDirectBootAware()) {
+                pkg.setPartiallyDirectBootAware(true);
+            }
+        }
+
+        if (enabledAttr != null) {
+            component.enabled = array.getBoolean(enabledAttr, true);
+        }
+
+        if (processAttr != null) {
+            CharSequence processName;
+            if (pkg.getTargetSdkVersion() >= Build.VERSION_CODES.FROYO) {
+                processName = array.getNonConfigurationString(processAttr,
+                        Configuration.NATIVE_CONFIG_VERSION);
+            } else {
+                // Some older apps have been seen to use a resource reference
+                // here that on older builds was ignored (with a warning).  We
+                // need to continue to do this for them so they don't break.
+                processName = array.getNonResourceString(processAttr);
+            }
+
+            // Backwards-compat, ignore error
+            ParseResult<String> processNameResult = ComponentParseUtils.buildProcessName(
+                    pkg.getPackageName(), pkg.getProcessName(), processName, flags,
+                    separateProcesses, input);
+            if (processNameResult.isError()) {
+                return input.error(processNameResult);
+            }
+
+            component.setProcessName(processNameResult.getResult());
+        }
+
+        if (splitNameAttr != null) {
+            component.splitName = array.getNonConfigurationString(splitNameAttr, 0);
+        }
+
+        return input.success(component);
+    }
+
+    static ParseResult<ParsedIntentInfo> parseIntentFilter(
+            ParsedMainComponent mainComponent,
+            ParsingPackage pkg, Resources resources, XmlResourceParser parser,
+            boolean visibleToEphemeral, boolean allowGlobs, boolean allowAutoVerify,
+            boolean allowImplicitEphemeralVisibility, boolean failOnNoActions,
+            ParseInput input) throws IOException, XmlPullParserException {
+        ParseResult<ParsedIntentInfo> intentResult = ParsedIntentInfoUtils.parseIntentInfo(
+                mainComponent.getName(), pkg, resources, parser, allowGlobs,
+                allowAutoVerify, input);
+        if (intentResult.isError()) {
+            return input.error(intentResult);
+        }
+
+        ParsedIntentInfo intent = intentResult.getResult();
+        int actionCount = intent.countActions();
+        if (actionCount == 0 && failOnNoActions) {
+            Slog.w(TAG, "No actions in " + parser.getName() + " at " + pkg.getBaseCodePath() + " "
+                    + parser.getPositionDescription());
+            // Backward-compat, do not actually fail
+            return input.success(null);
+        }
+
+        int intentVisibility;
+        if (visibleToEphemeral) {
+            intentVisibility = IntentFilter.VISIBILITY_EXPLICIT;
+        } else if (allowImplicitEphemeralVisibility
+                && ComponentParseUtils.isImplicitlyExposedIntent(intent)){
+            intentVisibility = IntentFilter.VISIBILITY_IMPLICIT;
+        } else {
+            intentVisibility = IntentFilter.VISIBILITY_NONE;
+        }
+        intent.setVisibilityToInstantApp(intentVisibility);
+
+        return input.success(intentResult.getResult());
+    }
+
+}
diff --git a/android/content/pm/parsing/component/ParsedPermission.java b/android/content/pm/parsing/component/ParsedPermission.java
new file mode 100644
index 0000000..ced3226
--- /dev/null
+++ b/android/content/pm/parsing/component/ParsedPermission.java
@@ -0,0 +1,191 @@
+/*
+ * 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.content.pm.parsing.component;
+
+import android.annotation.Nullable;
+import android.content.pm.PermissionInfo;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
+
+/** @hide */
+public class ParsedPermission extends ParsedComponent {
+
+    @Nullable
+    String backgroundPermission;
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String group;
+    int requestRes;
+    int protectionLevel;
+    boolean tree;
+    @Nullable
+    private ParsedPermissionGroup parsedPermissionGroup;
+
+    @VisibleForTesting
+    public ParsedPermission() {
+    }
+
+    public ParsedPermission(ParsedPermission other) {
+        super(other);
+        this.backgroundPermission = other.backgroundPermission;
+        this.group = other.group;
+        this.requestRes = other.requestRes;
+        this.protectionLevel = other.protectionLevel;
+        this.tree = other.tree;
+        this.parsedPermissionGroup = other.parsedPermissionGroup;
+    }
+
+    public ParsedPermission(ParsedPermission other, PermissionInfo pendingPermissionInfo,
+            String packageName, String name) {
+        this(other);
+
+        this.flags = pendingPermissionInfo.flags;
+        this.descriptionRes = pendingPermissionInfo.descriptionRes;
+
+        this.backgroundPermission = pendingPermissionInfo.backgroundPermission;
+        this.group = pendingPermissionInfo.group;
+        this.requestRes = pendingPermissionInfo.requestRes;
+        this.protectionLevel = pendingPermissionInfo.protectionLevel;
+
+        setName(name);
+        setPackageName(packageName);
+    }
+
+    public ParsedPermission setGroup(String group) {
+        this.group = TextUtils.safeIntern(group);
+        return this;
+    }
+
+    public ParsedPermission setFlags(int flags) {
+        this.flags = flags;
+        return this;
+    }
+
+    public boolean isRuntime() {
+        return getProtection() == PermissionInfo.PROTECTION_DANGEROUS;
+    }
+
+    public boolean isAppOp() {
+        return (protectionLevel & PermissionInfo.PROTECTION_FLAG_APPOP) != 0;
+    }
+
+    @PermissionInfo.Protection
+    public int getProtection() {
+        return protectionLevel & PermissionInfo.PROTECTION_MASK_BASE;
+    }
+
+    public int getProtectionFlags() {
+        return protectionLevel & ~PermissionInfo.PROTECTION_MASK_BASE;
+    }
+
+    public int calculateFootprint() {
+        int size = getName().length();
+        if (getNonLocalizedLabel() != null) {
+            size += getNonLocalizedLabel().length();
+        }
+        return size;
+    }
+
+    public String toString() {
+        return "Permission{"
+                + Integer.toHexString(System.identityHashCode(this))
+                + " " + getName() + "}";
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        super.writeToParcel(dest, flags);
+        dest.writeString(this.backgroundPermission);
+        dest.writeString(this.group);
+        dest.writeInt(this.requestRes);
+        dest.writeInt(this.protectionLevel);
+        dest.writeBoolean(this.tree);
+        dest.writeParcelable(this.parsedPermissionGroup, flags);
+    }
+
+    protected ParsedPermission(Parcel in) {
+        super(in);
+        // We use the boot classloader for all classes that we load.
+        final ClassLoader boot = Object.class.getClassLoader();
+        this.backgroundPermission = in.readString();
+        this.group = in.readString();
+        this.requestRes = in.readInt();
+        this.protectionLevel = in.readInt();
+        this.tree = in.readBoolean();
+        this.parsedPermissionGroup = in.readParcelable(boot);
+    }
+
+    public static final Parcelable.Creator<ParsedPermission> CREATOR =
+            new Parcelable.Creator<ParsedPermission>() {
+                @Override
+                public ParsedPermission createFromParcel(Parcel source) {
+                    return new ParsedPermission(source);
+                }
+
+                @Override
+                public ParsedPermission[] newArray(int size) {
+                    return new ParsedPermission[size];
+                }
+            };
+
+    @Nullable
+    public String getBackgroundPermission() {
+        return backgroundPermission;
+    }
+
+    @Nullable
+    public String getGroup() {
+        return group;
+    }
+
+    public int getRequestRes() {
+        return requestRes;
+    }
+
+    public int getProtectionLevel() {
+        return protectionLevel;
+    }
+
+    public boolean isTree() {
+        return tree;
+    }
+
+    @Nullable
+    public ParsedPermissionGroup getParsedPermissionGroup() {
+        return parsedPermissionGroup;
+    }
+
+    public ParsedPermission setProtectionLevel(int value) {
+        protectionLevel = value;
+        return this;
+    }
+
+    public ParsedPermission setParsedPermissionGroup(@Nullable ParsedPermissionGroup value) {
+        parsedPermissionGroup = value;
+        return this;
+    }
+}
diff --git a/android/content/pm/parsing/component/ParsedPermissionGroup.java b/android/content/pm/parsing/component/ParsedPermissionGroup.java
new file mode 100644
index 0000000..741c00c
--- /dev/null
+++ b/android/content/pm/parsing/component/ParsedPermissionGroup.java
@@ -0,0 +1,102 @@
+/*
+ * 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.content.pm.parsing.component;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.DataClass;
+
+/** @hide */
+public class ParsedPermissionGroup extends ParsedComponent {
+
+    int requestDetailResourceId;
+    int backgroundRequestResourceId;
+    int backgroundRequestDetailResourceId;
+    int requestRes;
+    int priority;
+
+    public void setPriority(int priority) {
+        this.priority = priority;
+    }
+
+    public String toString() {
+        return "PermissionGroup{"
+                + Integer.toHexString(System.identityHashCode(this))
+                + " " + getName() + "}";
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        super.writeToParcel(dest, flags);
+        dest.writeInt(this.requestDetailResourceId);
+        dest.writeInt(this.backgroundRequestResourceId);
+        dest.writeInt(this.backgroundRequestDetailResourceId);
+        dest.writeInt(this.requestRes);
+        dest.writeInt(this.priority);
+    }
+
+    public ParsedPermissionGroup() {
+    }
+
+    protected ParsedPermissionGroup(Parcel in) {
+        super(in);
+        this.requestDetailResourceId = in.readInt();
+        this.backgroundRequestResourceId = in.readInt();
+        this.backgroundRequestDetailResourceId = in.readInt();
+        this.requestRes = in.readInt();
+        this.priority = in.readInt();
+    }
+
+    public static final Parcelable.Creator<ParsedPermissionGroup> CREATOR =
+            new Parcelable.Creator<ParsedPermissionGroup>() {
+                @Override
+                public ParsedPermissionGroup createFromParcel(Parcel source) {
+                    return new ParsedPermissionGroup(source);
+                }
+
+                @Override
+                public ParsedPermissionGroup[] newArray(int size) {
+                    return new ParsedPermissionGroup[size];
+                }
+            };
+
+    public int getRequestDetailResourceId() {
+        return requestDetailResourceId;
+    }
+
+    public int getBackgroundRequestResourceId() {
+        return backgroundRequestResourceId;
+    }
+
+    public int getBackgroundRequestDetailResourceId() {
+        return backgroundRequestDetailResourceId;
+    }
+
+    public int getRequestRes() {
+        return requestRes;
+    }
+
+    public int getPriority() {
+        return priority;
+    }
+}
diff --git a/android/content/pm/parsing/component/ParsedPermissionUtils.java b/android/content/pm/parsing/component/ParsedPermissionUtils.java
new file mode 100644
index 0000000..1884a1e
--- /dev/null
+++ b/android/content/pm/parsing/component/ParsedPermissionUtils.java
@@ -0,0 +1,210 @@
+/*
+ * 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.content.pm.parsing.component;
+
+import android.annotation.NonNull;
+import android.content.pm.PermissionInfo;
+import android.content.pm.parsing.ParsingPackage;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.util.Slog;
+
+import com.android.internal.R;
+import android.content.pm.parsing.ParsingPackageUtils;
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+/** @hide */
+public class ParsedPermissionUtils {
+
+    private static final String TAG = ParsingPackageUtils.TAG;
+
+    @NonNull
+    public static ParseResult<ParsedPermission> parsePermission(ParsingPackage pkg, Resources res,
+            XmlResourceParser parser, boolean useRoundIcon, ParseInput input)
+            throws IOException, XmlPullParserException {
+        String packageName = pkg.getPackageName();
+        ParsedPermission
+                permission = new ParsedPermission();
+        String tag = "<" + parser.getName() + ">";
+        final ParseResult<ParsedPermission> result;
+
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestPermission);
+        try {
+            result = ParsedComponentUtils.parseComponent(
+                    permission, tag, pkg, sa, useRoundIcon, input,
+                    R.styleable.AndroidManifestPermission_banner,
+                    R.styleable.AndroidManifestPermission_description,
+                    R.styleable.AndroidManifestPermission_icon,
+                    R.styleable.AndroidManifestPermission_label,
+                    R.styleable.AndroidManifestPermission_logo,
+                    R.styleable.AndroidManifestPermission_name,
+                    R.styleable.AndroidManifestPermission_roundIcon);
+            if (result.isError()) {
+                return result;
+            }
+
+            if (sa.hasValue(
+                    R.styleable.AndroidManifestPermission_backgroundPermission)) {
+                if ("android".equals(packageName)) {
+                    permission.backgroundPermission = sa.getNonResourceString(
+                            R.styleable
+                                    .AndroidManifestPermission_backgroundPermission);
+                } else {
+                    Slog.w(TAG, packageName + " defines a background permission. Only the "
+                            + "'android' package can do that.");
+                }
+            }
+
+            // Note: don't allow this value to be a reference to a resource
+            // that may change.
+            permission.setGroup(sa.getNonResourceString(
+                    R.styleable.AndroidManifestPermission_permissionGroup));
+
+            permission.requestRes = sa.getResourceId(
+                    R.styleable.AndroidManifestPermission_request, 0);
+
+            permission.protectionLevel = sa.getInt(
+                    R.styleable.AndroidManifestPermission_protectionLevel,
+                    PermissionInfo.PROTECTION_NORMAL);
+
+            permission.flags = sa.getInt(
+                    R.styleable.AndroidManifestPermission_permissionFlags, 0);
+
+            // For now only platform runtime permissions can be restricted
+            if (!permission.isRuntime() || !"android".equals(permission.getPackageName())) {
+                permission.flags &= ~PermissionInfo.FLAG_HARD_RESTRICTED;
+                permission.flags &= ~PermissionInfo.FLAG_SOFT_RESTRICTED;
+            } else {
+                // The platform does not get to specify conflicting permissions
+                if ((permission.flags & PermissionInfo.FLAG_HARD_RESTRICTED) != 0
+                        && (permission.flags & PermissionInfo.FLAG_SOFT_RESTRICTED) != 0) {
+                    throw new IllegalStateException("Permission cannot be both soft and hard"
+                            + " restricted: " + permission.getName());
+                }
+            }
+        } finally {
+            sa.recycle();
+        }
+
+        // TODO(b/135203078): This is impossible because of default value in above getInt
+        if (permission.protectionLevel == -1) {
+            return input.error("<permission> does not specify protectionLevel");
+        }
+
+        permission.protectionLevel = PermissionInfo.fixProtectionLevel(permission.protectionLevel);
+
+        if (permission.getProtectionFlags() != 0) {
+            if ((permission.protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTANT) == 0
+                    && (permission.protectionLevel & PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY)
+                    == 0
+                    && (permission.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE) !=
+                    PermissionInfo.PROTECTION_SIGNATURE) {
+                return input.error("<permission>  protectionLevel specifies a non-instant flag "
+                        + "but is not based on signature type");
+            }
+        }
+
+        return ComponentParseUtils.parseAllMetaData(pkg, res, parser, tag, permission, input);
+    }
+
+    @NonNull
+    public static ParseResult<ParsedPermission> parsePermissionTree(ParsingPackage pkg, Resources res,
+            XmlResourceParser parser, boolean useRoundIcon, ParseInput input)
+            throws IOException, XmlPullParserException {
+        ParsedPermission permission = new ParsedPermission();
+        String tag = "<" + parser.getName() + ">";
+        final ParseResult<ParsedPermission> result;
+
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestPermissionTree);
+        try {
+            result = ParsedComponentUtils.parseComponent(
+                    permission, tag, pkg, sa, useRoundIcon, input,
+                    R.styleable.AndroidManifestPermissionTree_banner,
+                    null /*descriptionAttr*/,
+                    R.styleable.AndroidManifestPermissionTree_icon,
+                    R.styleable.AndroidManifestPermissionTree_label,
+                    R.styleable.AndroidManifestPermissionTree_logo,
+                    R.styleable.AndroidManifestPermissionTree_name,
+                    R.styleable.AndroidManifestPermissionTree_roundIcon);
+            if (result.isError()) {
+                return result;
+            }
+        } finally {
+            sa.recycle();
+        }
+
+        int index = permission.getName().indexOf('.');
+        if (index > 0) {
+            index = permission.getName().indexOf('.', index + 1);
+        }
+        if (index < 0) {
+            return input.error("<permission-tree> name has less than three segments: "
+                    + permission.getName());
+        }
+
+        permission.protectionLevel = PermissionInfo.PROTECTION_NORMAL;
+        permission.tree = true;
+
+        return ComponentParseUtils.parseAllMetaData(pkg, res, parser, tag, permission,
+                input);
+    }
+
+    @NonNull
+    public static ParseResult<ParsedPermissionGroup> parsePermissionGroup(ParsingPackage pkg,
+            Resources res, XmlResourceParser parser, boolean useRoundIcon, ParseInput input)
+            throws IOException, XmlPullParserException {
+        ParsedPermissionGroup
+                permissionGroup = new ParsedPermissionGroup();
+        String tag = "<" + parser.getName() + ">";
+
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestPermissionGroup);
+        try {
+            ParseResult<ParsedPermissionGroup> result = ParsedComponentUtils.parseComponent(
+                    permissionGroup, tag, pkg, sa, useRoundIcon, input,
+                    R.styleable.AndroidManifestPermissionGroup_banner,
+                    R.styleable.AndroidManifestPermissionGroup_description,
+                    R.styleable.AndroidManifestPermissionGroup_icon,
+                    R.styleable.AndroidManifestPermissionGroup_label,
+                    R.styleable.AndroidManifestPermissionGroup_logo,
+                    R.styleable.AndroidManifestPermissionGroup_name,
+                    R.styleable.AndroidManifestPermissionGroup_roundIcon);
+            if (result.isError()) {
+                return result;
+            }
+
+            // @formatter:off
+            permissionGroup.requestDetailResourceId = sa.getResourceId(R.styleable.AndroidManifestPermissionGroup_requestDetail, 0);
+            permissionGroup.backgroundRequestResourceId = sa.getResourceId(R.styleable.AndroidManifestPermissionGroup_backgroundRequest, 0);
+            permissionGroup.backgroundRequestDetailResourceId = sa.getResourceId(R.styleable.AndroidManifestPermissionGroup_backgroundRequestDetail, 0);
+            permissionGroup.requestRes = sa.getResourceId(R.styleable.AndroidManifestPermissionGroup_request, 0);
+            permissionGroup.flags = sa.getInt(R.styleable.AndroidManifestPermissionGroup_permissionGroupFlags,0);
+            permissionGroup.priority = sa.getInt(R.styleable.AndroidManifestPermissionGroup_priority, 0);
+            // @formatter:on
+        } finally {
+            sa.recycle();
+        }
+
+        return ComponentParseUtils.parseAllMetaData(pkg, res, parser, tag, permissionGroup,
+                input);
+    }
+}
diff --git a/android/content/pm/parsing/component/ParsedProcess.java b/android/content/pm/parsing/component/ParsedProcess.java
new file mode 100644
index 0000000..e0ae81b
--- /dev/null
+++ b/android/content/pm/parsing/component/ParsedProcess.java
@@ -0,0 +1,178 @@
+/*
+ * 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.content.pm.parsing.component;
+
+import static java.util.Collections.emptySet;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.ArraySet;
+
+import com.android.internal.util.CollectionUtils;
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling;
+
+import java.util.Set;
+
+/** @hide */
+@DataClass(genGetters = true, genSetters = false, genParcelable = true, genAidl = false,
+        genBuilder = false)
+public class ParsedProcess implements Parcelable {
+
+    @NonNull
+    protected String name;
+    @NonNull
+    @DataClass.ParcelWith(Parcelling.BuiltIn.ForInternedStringSet.class)
+    protected Set<String> deniedPermissions = emptySet();
+
+    protected int gwpAsanMode = -1;
+
+    public ParsedProcess() {
+    }
+
+    public ParsedProcess(@NonNull ParsedProcess other) {
+        name = other.name;
+        deniedPermissions = new ArraySet<>(other.deniedPermissions);
+    }
+
+    public void addStateFrom(@NonNull ParsedProcess other) {
+        deniedPermissions = CollectionUtils.addAll(deniedPermissions, other.deniedPermissions);
+    }
+
+
+
+    // Code below generated by codegen v1.0.15.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedProcess.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @DataClass.Generated.Member
+    public ParsedProcess(
+            @NonNull String name,
+            @NonNull Set<String> deniedPermissions,
+            int gwpAsanMode) {
+        this.name = name;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, name);
+        this.deniedPermissions = deniedPermissions;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, deniedPermissions);
+        this.gwpAsanMode = gwpAsanMode;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull String getName() {
+        return name;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull Set<String> getDeniedPermissions() {
+        return deniedPermissions;
+    }
+
+    @DataClass.Generated.Member
+    public int getGwpAsanMode() {
+        return gwpAsanMode;
+    }
+
+    @DataClass.Generated.Member
+    static Parcelling<Set<String>> sParcellingForDeniedPermissions =
+            Parcelling.Cache.get(
+                    Parcelling.BuiltIn.ForInternedStringSet.class);
+    static {
+        if (sParcellingForDeniedPermissions == null) {
+            sParcellingForDeniedPermissions = Parcelling.Cache.put(
+                    new Parcelling.BuiltIn.ForInternedStringSet());
+        }
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        dest.writeString(name);
+        sParcellingForDeniedPermissions.parcel(deniedPermissions, dest, flags);
+        dest.writeInt(gwpAsanMode);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    protected ParsedProcess(@NonNull Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        String _name = in.readString();
+        Set<String> _deniedPermissions = sParcellingForDeniedPermissions.unparcel(in);
+        int _gwpAsanMode = in.readInt();
+
+        this.name = _name;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, name);
+        this.deniedPermissions = _deniedPermissions;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, deniedPermissions);
+        this.gwpAsanMode = _gwpAsanMode;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<ParsedProcess> CREATOR
+            = new Parcelable.Creator<ParsedProcess>() {
+        @Override
+        public ParsedProcess[] newArray(int size) {
+            return new ParsedProcess[size];
+        }
+
+        @Override
+        public ParsedProcess createFromParcel(@NonNull Parcel in) {
+            return new ParsedProcess(in);
+        }
+    };
+
+    @DataClass.Generated(
+            time = 1584557524776L,
+            codegenVersion = "1.0.15",
+            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedProcess.java",
+            inputSignatures = "protected @android.annotation.NonNull java.lang.String name\nprotected @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedStringSet.class) java.util.Set<java.lang.String> deniedPermissions\nprotected  int gwpAsanMode\npublic  void addStateFrom(android.content.pm.parsing.component.ParsedProcess)\nclass ParsedProcess extends java.lang.Object implements [android.os.Parcelable]\[email protected](genGetters=true, genSetters=false, genParcelable=true, genAidl=false, genBuilder=false)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/android/content/pm/parsing/component/ParsedProcessUtils.java b/android/content/pm/parsing/component/ParsedProcessUtils.java
new file mode 100644
index 0000000..8372707
--- /dev/null
+++ b/android/content/pm/parsing/component/ParsedProcessUtils.java
@@ -0,0 +1,214 @@
+/*
+ * 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.content.pm.parsing.component;
+
+import android.annotation.NonNull;
+import android.content.pm.parsing.ParsingPackage;
+import android.content.pm.parsing.ParsingUtils;
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+
+import com.android.internal.R;
+import com.android.internal.util.CollectionUtils;
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.Set;
+
+/** @hide */
+public class ParsedProcessUtils {
+
+    private static final String TAG = ParsingUtils.TAG;
+
+    @NonNull
+    private static ParseResult<Set<String>> parseDenyPermission(Set<String> perms,
+            Resources res, XmlResourceParser parser, ParseInput input)
+            throws IOException, XmlPullParserException {
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestDenyPermission);
+        try {
+            String perm = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestDenyPermission_name, 0);
+            if (perm != null && perm.equals(android.Manifest.permission.INTERNET)) {
+                perms = CollectionUtils.add(perms, perm);
+            }
+        } finally {
+            sa.recycle();
+        }
+        XmlUtils.skipCurrentTag(parser);
+        return input.success(perms);
+    }
+
+    @NonNull
+    private static ParseResult<Set<String>> parseAllowPermission(Set<String> perms, Resources res,
+            XmlResourceParser parser, ParseInput input)
+            throws IOException, XmlPullParserException {
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestAllowPermission);
+        try {
+            String perm = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestAllowPermission_name, 0);
+            if (perm != null && perm.equals(android.Manifest.permission.INTERNET)) {
+                perms = CollectionUtils.remove(perms, perm);
+            }
+        } finally {
+            sa.recycle();
+        }
+        XmlUtils.skipCurrentTag(parser);
+        return input.success(perms);
+    }
+
+    @NonNull
+    private static ParseResult<ParsedProcess> parseProcess(Set<String> perms, String[] separateProcesses,
+            ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags,
+            ParseInput input) throws IOException, XmlPullParserException {
+        ParsedProcess proc = new ParsedProcess();
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestProcess);
+        try {
+            if (perms != null) {
+                proc.deniedPermissions = new ArraySet<>(perms);
+            }
+
+            proc.name = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestProcess_process, 0);
+            ParseResult<String> processNameResult = ComponentParseUtils.buildProcessName(
+                    pkg.getPackageName(), pkg.getPackageName(), proc.name, flags, separateProcesses,
+                    input);
+            if (processNameResult.isError()) {
+                return input.error(processNameResult);
+            }
+
+            proc.name = processNameResult.getResult();
+
+            if (proc.name == null || proc.name.length() <= 0) {
+                return input.error("<process> does not specify android:process");
+            }
+
+            proc.gwpAsanMode = sa.getInt(R.styleable.AndroidManifestProcess_gwpAsanMode, -1);
+        } finally {
+            sa.recycle();
+        }
+
+        int type;
+        final int innerDepth = parser.getDepth();
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+
+            ParseResult<?> result;
+
+            String tagName = parser.getName();
+            switch (tagName) {
+                case "deny-permission":
+                    ParseResult<Set<String>> denyResult = parseDenyPermission(
+                            proc.deniedPermissions, res, parser, input);
+                    result = denyResult;
+                    if (denyResult.isSuccess()) {
+                        proc.deniedPermissions = denyResult.getResult();
+                    }
+                    break;
+                case "allow-permission":
+                    ParseResult<Set<String>> allowResult = parseAllowPermission(
+                            proc.deniedPermissions, res, parser, input);
+                    result = allowResult;
+                    if (allowResult.isSuccess()) {
+                        proc.deniedPermissions = allowResult.getResult();
+                    }
+                    break;
+                default:
+                    result = ParsingUtils.unknownTag("<process>", pkg, parser, input);
+                    break;
+            }
+
+            if (result.isError()) {
+                return input.error(result);
+            }
+        }
+
+        return input.success(proc);
+    }
+
+    @NonNull
+    public static ParseResult<ArrayMap<String, ParsedProcess>> parseProcesses(
+            String[] separateProcesses, ParsingPackage pkg, Resources res,
+            XmlResourceParser parser, int flags, ParseInput input)
+            throws IOException, XmlPullParserException {
+        Set<String> deniedPerms = null;
+        ArrayMap<String, ParsedProcess> processes = new ArrayMap<>();
+
+        int type;
+        final int innerDepth = parser.getDepth();
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+
+            ParseResult<?> result;
+
+            String tagName = parser.getName();
+            switch (tagName) {
+                case "deny-permission":
+                    ParseResult<Set<String>> denyResult = parseDenyPermission(deniedPerms, res,
+                            parser, input);
+                    result = denyResult;
+                    if (denyResult.isSuccess()) {
+                        deniedPerms = denyResult.getResult();
+                    }
+                    break;
+                case "allow-permission":
+                    ParseResult<Set<String>> allowResult = parseAllowPermission(deniedPerms, res,
+                            parser, input);
+                    result = allowResult;
+                    if (allowResult.isSuccess()) {
+                        deniedPerms = allowResult.getResult();
+                    }
+                    break;
+                case "process":
+                    ParseResult<ParsedProcess> processResult = parseProcess(deniedPerms,
+                            separateProcesses, pkg, res, parser, flags, input);
+                    result = processResult;
+                    if (processResult.isSuccess()) {
+                        ParsedProcess process = processResult.getResult();
+                        if (processes.put(process.name, process) != null) {
+                            result = input.error(
+                                    "<process> specified existing name '" + process.name + "'");
+                        }
+                    }
+                    break;
+                default:
+                    result = ParsingUtils.unknownTag("<processes>", pkg, parser, input);
+                    break;
+            }
+
+            if (result.isError()) {
+                return input.error(result);
+            }
+
+        }
+
+        return input.success(processes);
+    }
+}
diff --git a/android/content/pm/parsing/component/ParsedProvider.java b/android/content/pm/parsing/component/ParsedProvider.java
new file mode 100644
index 0000000..fcf6e87
--- /dev/null
+++ b/android/content/pm/parsing/component/ParsedProvider.java
@@ -0,0 +1,194 @@
+/*
+ * 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.content.pm.parsing.component;
+
+import static android.content.pm.parsing.ParsingPackageImpl.sForInternedString;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.pm.PathPermission;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.PatternMatcher;
+import android.text.TextUtils;
+
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
+
+/** @hide **/
+public class ParsedProvider extends ParsedMainComponent {
+
+    @NonNull
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String authority;
+    boolean syncable;
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String readPermission;
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String writePermission;
+    boolean grantUriPermissions;
+    boolean forceUriPermissions;
+    boolean multiProcess;
+    int initOrder;
+    @Nullable
+    PatternMatcher[] uriPermissionPatterns;
+    @Nullable
+    PathPermission[] pathPermissions;
+
+    public ParsedProvider(ParsedProvider other) {
+        super(other);
+
+        this.authority = other.authority;
+        this.syncable = other.syncable;
+        this.readPermission = other.readPermission;
+        this.writePermission = other.writePermission;
+        this.grantUriPermissions = other.grantUriPermissions;
+        this.forceUriPermissions = other.forceUriPermissions;
+        this.multiProcess = other.multiProcess;
+        this.initOrder = other.initOrder;
+        this.uriPermissionPatterns = other.uriPermissionPatterns;
+        this.pathPermissions = other.pathPermissions;
+    }
+
+    public void setAuthority(String authority) {
+        this.authority = TextUtils.safeIntern(authority);
+    }
+
+    public void setSyncable(boolean syncable) {
+        this.syncable = syncable;
+    }
+
+    public void setReadPermission(String readPermission) {
+        // Empty string must be converted to null
+        this.readPermission = TextUtils.isEmpty(readPermission)
+                ? null : readPermission.intern();
+    }
+
+    public void setWritePermission(String writePermission) {
+        // Empty string must be converted to null
+        this.writePermission = TextUtils.isEmpty(writePermission)
+                ? null : writePermission.intern();
+    }
+
+    public String toString() {
+        StringBuilder sb = new StringBuilder(128);
+        sb.append("Provider{");
+        sb.append(Integer.toHexString(System.identityHashCode(this)));
+        sb.append(' ');
+        ComponentName.appendShortString(sb, getPackageName(), getName());
+        sb.append('}');
+        return sb.toString();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        super.writeToParcel(dest, flags);
+        dest.writeString(this.authority);
+        dest.writeBoolean(this.syncable);
+        sForInternedString.parcel(this.readPermission, dest, flags);
+        sForInternedString.parcel(this.writePermission, dest, flags);
+        dest.writeBoolean(this.grantUriPermissions);
+        dest.writeBoolean(this.forceUriPermissions);
+        dest.writeBoolean(this.multiProcess);
+        dest.writeInt(this.initOrder);
+        dest.writeTypedArray(this.uriPermissionPatterns, flags);
+        dest.writeTypedArray(this.pathPermissions, flags);
+    }
+
+    public ParsedProvider() {
+    }
+
+    protected ParsedProvider(Parcel in) {
+        super(in);
+        //noinspection ConstantConditions
+        this.authority = in.readString();
+        this.syncable = in.readBoolean();
+        this.readPermission = sForInternedString.unparcel(in);
+        this.writePermission = sForInternedString.unparcel(in);
+        this.grantUriPermissions = in.readBoolean();
+        this.forceUriPermissions = in.readBoolean();
+        this.multiProcess = in.readBoolean();
+        this.initOrder = in.readInt();
+        this.uriPermissionPatterns = in.createTypedArray(PatternMatcher.CREATOR);
+        this.pathPermissions = in.createTypedArray(PathPermission.CREATOR);
+    }
+
+    public static final Parcelable.Creator<ParsedProvider> CREATOR = new Creator<ParsedProvider>() {
+        @Override
+        public ParsedProvider createFromParcel(Parcel source) {
+            return new ParsedProvider(source);
+        }
+
+        @Override
+        public ParsedProvider[] newArray(int size) {
+            return new ParsedProvider[size];
+        }
+    };
+
+    @NonNull
+    public String getAuthority() {
+        return authority;
+    }
+
+    public boolean isSyncable() {
+        return syncable;
+    }
+
+    @Nullable
+    public String getReadPermission() {
+        return readPermission;
+    }
+
+    @Nullable
+    public String getWritePermission() {
+        return writePermission;
+    }
+
+    public boolean isGrantUriPermissions() {
+        return grantUriPermissions;
+    }
+
+    public boolean isForceUriPermissions() {
+        return forceUriPermissions;
+    }
+
+    public boolean isMultiProcess() {
+        return multiProcess;
+    }
+
+    public int getInitOrder() {
+        return initOrder;
+    }
+
+    @Nullable
+    public PatternMatcher[] getUriPermissionPatterns() {
+        return uriPermissionPatterns;
+    }
+
+    @Nullable
+    public PathPermission[] getPathPermissions() {
+        return pathPermissions;
+    }
+}
diff --git a/android/content/pm/parsing/component/ParsedProviderUtils.java b/android/content/pm/parsing/component/ParsedProviderUtils.java
new file mode 100644
index 0000000..aa5ea8d
--- /dev/null
+++ b/android/content/pm/parsing/component/ParsedProviderUtils.java
@@ -0,0 +1,355 @@
+/*
+ * 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.content.pm.parsing.component;
+
+import static android.content.pm.parsing.component.ComponentParseUtils.flag;
+
+import android.annotation.NonNull;
+import android.content.pm.PackageParser;
+import android.content.pm.PathPermission;
+import android.content.pm.ProviderInfo;
+import android.content.pm.parsing.ParsingPackage;
+import android.content.pm.parsing.ParsingPackageUtils;
+import android.content.pm.parsing.ParsingUtils;
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.os.Build;
+import android.os.PatternMatcher;
+import android.util.Slog;
+
+import com.android.internal.R;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.Objects;
+
+/** @hide */
+public class ParsedProviderUtils {
+
+    private static final String TAG = ParsingPackageUtils.TAG;
+
+    @NonNull
+    public static ParseResult<ParsedProvider> parseProvider(String[] separateProcesses,
+            ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags,
+            boolean useRoundIcon, ParseInput input)
+            throws IOException, XmlPullParserException {
+        String authority;
+        boolean visibleToEphemeral;
+
+        final int targetSdkVersion = pkg.getTargetSdkVersion();
+        final String packageName = pkg.getPackageName();
+        final ParsedProvider provider = new ParsedProvider();
+        final String tag = parser.getName();
+
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestProvider);
+        try {
+            ParseResult<ParsedProvider> result =
+                    ParsedMainComponentUtils.parseMainComponent(provider, tag, separateProcesses,
+                    pkg, sa, flags, useRoundIcon, input,
+                    R.styleable.AndroidManifestProvider_banner,
+                    R.styleable.AndroidManifestProvider_description,
+                    R.styleable.AndroidManifestProvider_directBootAware,
+                    R.styleable.AndroidManifestProvider_enabled,
+                    R.styleable.AndroidManifestProvider_icon,
+                    R.styleable.AndroidManifestProvider_label,
+                    R.styleable.AndroidManifestProvider_logo,
+                    R.styleable.AndroidManifestProvider_name,
+                    R.styleable.AndroidManifestProvider_process,
+                    R.styleable.AndroidManifestProvider_roundIcon,
+                    R.styleable.AndroidManifestProvider_splitName);
+            if (result.isError()) {
+                return result;
+            }
+
+            authority = sa.getNonConfigurationString(R.styleable.AndroidManifestProvider_authorities, 0);
+
+            // For compatibility, applications targeting API level 16 or lower
+            // should have their content providers exported by default, unless they
+            // specify otherwise.
+            provider.exported = sa.getBoolean(R.styleable.AndroidManifestProvider_exported,
+                    targetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR1);
+
+            provider.syncable = sa.getBoolean(R.styleable.AndroidManifestProvider_syncable, false);
+
+            String permission = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestProvider_permission, 0);
+            String readPermission = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestProvider_readPermission, 0);
+            if (readPermission == null) {
+                readPermission = permission;
+            }
+            if (readPermission == null) {
+                provider.setReadPermission(pkg.getPermission());
+            } else {
+                provider.setReadPermission(readPermission);
+            }
+            String writePermission = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestProvider_writePermission, 0);
+            if (writePermission == null) {
+                writePermission = permission;
+            }
+            if (writePermission == null) {
+                provider.setWritePermission(pkg.getPermission());
+            } else {
+                provider.setWritePermission(writePermission);
+            }
+
+            provider.grantUriPermissions = sa.getBoolean(R.styleable.AndroidManifestProvider_grantUriPermissions, false);
+            provider.forceUriPermissions = sa.getBoolean(R.styleable.AndroidManifestProvider_forceUriPermissions, false);
+            provider.multiProcess = sa.getBoolean(R.styleable.AndroidManifestProvider_multiprocess, false);
+            provider.initOrder = sa.getInt(R.styleable.AndroidManifestProvider_initOrder, 0);
+
+            provider.flags |= flag(ProviderInfo.FLAG_SINGLE_USER, R.styleable.AndroidManifestProvider_singleUser, sa);
+
+            visibleToEphemeral = sa.getBoolean(R.styleable.AndroidManifestProvider_visibleToInstantApps, false);
+            if (visibleToEphemeral) {
+                provider.flags |= ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP;
+                pkg.setVisibleToInstantApps(true);
+            }
+        } finally {
+            sa.recycle();
+        }
+
+        if (pkg.isCantSaveState()) {
+            // A heavy-weight application can not have providers in its main process
+            if (Objects.equals(provider.getProcessName(), packageName)) {
+                return input.error("Heavy-weight applications can not have providers"
+                        + " in main process");
+            }
+        }
+
+        if (authority == null) {
+            return input.error("<provider> does not include authorities attribute");
+        }
+        if (authority.length() <= 0) {
+            return input.error("<provider> has empty authorities attribute");
+        }
+        provider.setAuthority(authority);
+
+        return parseProviderTags(pkg, tag, res, parser, visibleToEphemeral, provider, input);
+    }
+
+    @NonNull
+    private static ParseResult<ParsedProvider> parseProviderTags(ParsingPackage pkg, String tag,
+            Resources res, XmlResourceParser parser, boolean visibleToEphemeral,
+            ParsedProvider provider, ParseInput input)
+            throws XmlPullParserException, IOException {
+        final int depth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG
+                || parser.getDepth() > depth)) {
+            if (type != XmlPullParser.START_TAG) {
+                continue;
+            }
+
+            String name = parser.getName();
+            final ParseResult result;
+            switch (name) {
+                case "intent-filter":
+                    ParseResult<ParsedIntentInfo> intentResult = ParsedMainComponentUtils
+                            .parseIntentFilter(provider, pkg, res, parser, visibleToEphemeral,
+                                    true /*allowGlobs*/, false /*allowAutoVerify*/,
+                                    false /*allowImplicitEphemeralVisibility*/,
+                                    false /*failOnNoActions*/, input);
+                    result = intentResult;
+                    if (intentResult.isSuccess()) {
+                        ParsedIntentInfo intent = intentResult.getResult();
+                        provider.order = Math.max(intent.getOrder(), provider.order);
+                        provider.addIntent(intent);
+                    }
+                    break;
+                case "meta-data":
+                    result = ParsedComponentUtils.addMetaData(provider, pkg, res, parser, input);
+                    break;
+                case "grant-uri-permission": {
+                    result = parseGrantUriPermission(provider, pkg, res, parser, input);
+                    break;
+                }
+                case "path-permission": {
+                    result = parsePathPermission(provider, pkg, res, parser, input);
+                    break;
+                }
+                default:
+                    result = ParsingUtils.unknownTag(tag, pkg, parser, input);
+                    break;
+            }
+
+            if (result.isError()) {
+                return input.error(result);
+            }
+        }
+
+        return input.success(provider);
+    }
+
+    @NonNull
+    private static ParseResult<ParsedProvider> parseGrantUriPermission(ParsedProvider provider,
+            ParsingPackage pkg, Resources resources, XmlResourceParser parser, ParseInput input) {
+        TypedArray sa = resources.obtainAttributes(parser,
+                R.styleable.AndroidManifestGrantUriPermission);
+        try {
+            String name = parser.getName();
+            // Pattern has priority over prefix over literal path
+            PatternMatcher pa = null;
+            String str = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestGrantUriPermission_pathPattern, 0);
+            if (str != null) {
+                pa = new PatternMatcher(str, PatternMatcher.PATTERN_SIMPLE_GLOB);
+            } else {
+                str = sa.getNonConfigurationString(
+                        R.styleable.AndroidManifestGrantUriPermission_pathPrefix, 0);
+                if (str != null) {
+                    pa = new PatternMatcher(str, PatternMatcher.PATTERN_PREFIX);
+                } else {
+                    str = sa.getNonConfigurationString(
+                            R.styleable.AndroidManifestGrantUriPermission_path, 0);
+                    if (str != null) {
+                        pa = new PatternMatcher(str, PatternMatcher.PATTERN_LITERAL);
+                    }
+                }
+            }
+
+            if (pa != null) {
+                if (provider.uriPermissionPatterns == null) {
+                    provider.uriPermissionPatterns = new PatternMatcher[1];
+                    provider.uriPermissionPatterns[0] = pa;
+                } else {
+                    final int N = provider.uriPermissionPatterns.length;
+                    PatternMatcher[] newp = new PatternMatcher[N + 1];
+                    System.arraycopy(provider.uriPermissionPatterns, 0, newp, 0, N);
+                    newp[N] = pa;
+                    provider.uriPermissionPatterns = newp;
+                }
+                provider.grantUriPermissions = true;
+            } else {
+                if (PackageParser.RIGID_PARSER) {
+                    return input.error("No path, pathPrefix, or pathPattern for <path-permission>");
+                }
+
+                Slog.w(TAG, "Unknown element under <path-permission>: " + name + " at "
+                        + pkg.getBaseCodePath() + " " + parser.getPositionDescription());
+            }
+
+            return input.success(provider);
+        } finally {
+            sa.recycle();
+        }
+    }
+
+    @NonNull
+    private static ParseResult<ParsedProvider> parsePathPermission(ParsedProvider provider,
+            ParsingPackage pkg, Resources resources, XmlResourceParser parser, ParseInput input) {
+        TypedArray sa = resources.obtainAttributes(parser,
+                R.styleable.AndroidManifestPathPermission);
+        try {
+            String name = parser.getName();
+
+            String permission = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestPathPermission_permission, 0);
+            String readPermission = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestPathPermission_readPermission, 0);
+            if (readPermission == null) {
+                readPermission = permission;
+            }
+            String writePermission = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestPathPermission_writePermission, 0);
+            if (writePermission == null) {
+                writePermission = permission;
+            }
+
+            boolean havePerm = false;
+            if (readPermission != null) {
+                readPermission = readPermission.intern();
+                havePerm = true;
+            }
+            if (writePermission != null) {
+                writePermission = writePermission.intern();
+                havePerm = true;
+            }
+
+            if (!havePerm) {
+                if (PackageParser.RIGID_PARSER) {
+                    return input.error(
+                            "No readPermission or writePermission for <path-permission>");
+                }
+                Slog.w(TAG, "No readPermission or writePermission for <path-permission>: "
+                        + name + " at " + pkg.getBaseCodePath() + " " + parser.getPositionDescription());
+                return input.success(provider);
+            }
+
+            // Advanced has priority over simply over prefix over literal
+            PathPermission pa = null;
+            String path = sa.getNonConfigurationString(R.styleable.AndroidManifestPathPermission_pathAdvancedPattern, 0);
+            if (path != null) {
+                pa = new PathPermission(path, PatternMatcher.PATTERN_ADVANCED_GLOB, readPermission,
+                        writePermission);
+            } else {
+                path = sa.getNonConfigurationString(R.styleable.AndroidManifestPathPermission_pathPattern, 0);
+                if (path != null) {
+                    pa = new PathPermission(path, PatternMatcher.PATTERN_SIMPLE_GLOB,
+                            readPermission, writePermission);
+                } else {
+                    path = sa.getNonConfigurationString(
+                            R.styleable.AndroidManifestPathPermission_pathPrefix, 0);
+                    if (path != null) {
+                        pa = new PathPermission(path, PatternMatcher.PATTERN_PREFIX, readPermission,
+                                writePermission);
+                    } else {
+                        path = sa.getNonConfigurationString(R.styleable.AndroidManifestPathPermission_path, 0);
+                        if (path != null) {
+                            pa = new PathPermission(path, PatternMatcher.PATTERN_LITERAL,
+                                    readPermission, writePermission);
+                        }
+                    }
+                }
+            }
+
+            if (pa != null) {
+                if (provider.pathPermissions == null) {
+                    provider.pathPermissions = new PathPermission[1];
+                    provider.pathPermissions[0] = pa;
+                } else {
+                    final int N = provider.pathPermissions.length;
+                    PathPermission[] newp = new PathPermission[N + 1];
+                    System.arraycopy(provider.pathPermissions, 0, newp, 0, N);
+                    newp[N] = pa;
+                    provider.pathPermissions = newp;
+                }
+            } else {
+                if (PackageParser.RIGID_PARSER) {
+                    return input.error(
+                            "No path, pathPrefix, or pathPattern for <path-permission>");
+                }
+
+                Slog.w(TAG, "No path, pathPrefix, or pathPattern for <path-permission>: "
+                        + name + " at " + pkg.getBaseCodePath()
+                        + " "
+                        + parser.getPositionDescription());
+            }
+
+            return input.success(provider);
+        } finally {
+            sa.recycle();
+        }
+    }
+}
diff --git a/android/content/pm/parsing/component/ParsedService.java b/android/content/pm/parsing/component/ParsedService.java
new file mode 100644
index 0000000..7adb262
--- /dev/null
+++ b/android/content/pm/parsing/component/ParsedService.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.content.pm.parsing.component;
+
+import static android.content.pm.parsing.ParsingPackageImpl.sForInternedString;
+
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
+
+/** @hide **/
+public class ParsedService extends ParsedMainComponent {
+
+    int foregroundServiceType;
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String permission;
+
+    public ParsedService(ParsedService other) {
+        super(other);
+        this.foregroundServiceType = other.foregroundServiceType;
+        this.permission = other.permission;
+    }
+
+    public ParsedMainComponent setPermission(String permission) {
+        // Empty string must be converted to null
+        this.permission = TextUtils.isEmpty(permission) ? null : permission.intern();
+        return this;
+    }
+
+    public String toString() {
+        StringBuilder sb = new StringBuilder(128);
+        sb.append("Service{");
+        sb.append(Integer.toHexString(System.identityHashCode(this)));
+        sb.append(' ');
+        ComponentName.appendShortString(sb, getPackageName(), getName());
+        sb.append('}');
+        return sb.toString();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        super.writeToParcel(dest, flags);
+        dest.writeInt(this.foregroundServiceType);
+        sForInternedString.parcel(this.permission, dest, flags);
+    }
+
+    public ParsedService() {
+    }
+
+    protected ParsedService(Parcel in) {
+        super(in);
+        this.foregroundServiceType = in.readInt();
+        this.permission = sForInternedString.unparcel(in);
+    }
+
+    public static final Parcelable.Creator<ParsedService> CREATOR = new Creator<ParsedService>() {
+        @Override
+        public ParsedService createFromParcel(Parcel source) {
+            return new ParsedService(source);
+        }
+
+        @Override
+        public ParsedService[] newArray(int size) {
+            return new ParsedService[size];
+        }
+    };
+
+    public int getForegroundServiceType() {
+        return foregroundServiceType;
+    }
+
+    @Nullable
+    public String getPermission() {
+        return permission;
+    }
+}
diff --git a/android/content/pm/parsing/component/ParsedServiceUtils.java b/android/content/pm/parsing/component/ParsedServiceUtils.java
new file mode 100644
index 0000000..8a8a066
--- /dev/null
+++ b/android/content/pm/parsing/component/ParsedServiceUtils.java
@@ -0,0 +1,165 @@
+/*
+ * 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.content.pm.parsing.component;
+
+import static android.content.pm.parsing.component.ComponentParseUtils.flag;
+
+import android.annotation.NonNull;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ServiceInfo;
+import android.content.pm.parsing.ParsingPackage;
+import android.content.pm.parsing.ParsingPackageUtils;
+import android.content.pm.parsing.ParsingUtils;
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+
+import com.android.internal.R;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.Objects;
+
+/** @hide */
+public class ParsedServiceUtils {
+
+    private static final String TAG = ParsingPackageUtils.TAG;
+
+    @NonNull
+    public static ParseResult<ParsedService> parseService(String[] separateProcesses,
+            ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags,
+            boolean useRoundIcon, ParseInput input)
+            throws XmlPullParserException, IOException {
+        boolean visibleToEphemeral;
+        boolean setExported;
+
+        final String packageName = pkg.getPackageName();
+        final ParsedService service = new ParsedService();
+        String tag = parser.getName();
+
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestService);
+        try {
+            ParseResult<ParsedService> result = ParsedMainComponentUtils.parseMainComponent(
+                    service, tag, separateProcesses, pkg, sa, flags, useRoundIcon, input,
+                    R.styleable.AndroidManifestService_banner,
+                    R.styleable.AndroidManifestService_description,
+                    R.styleable.AndroidManifestService_directBootAware,
+                    R.styleable.AndroidManifestService_enabled,
+                    R.styleable.AndroidManifestService_icon,
+                    R.styleable.AndroidManifestService_label,
+                    R.styleable.AndroidManifestService_logo,
+                    R.styleable.AndroidManifestService_name,
+                    R.styleable.AndroidManifestService_process,
+                    R.styleable.AndroidManifestService_roundIcon,
+                    R.styleable.AndroidManifestService_splitName
+            );
+
+            if (result.isError()) {
+                return result;
+            }
+
+            setExported = sa.hasValue(R.styleable.AndroidManifestService_exported);
+            if (setExported) {
+                service.exported = sa.getBoolean(R.styleable.AndroidManifestService_exported,
+                        false);
+            }
+
+            String permission = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestService_permission, 0);
+            service.setPermission(permission != null ? permission : pkg.getPermission());
+
+            service.foregroundServiceType = sa.getInt(
+                    R.styleable.AndroidManifestService_foregroundServiceType,
+                    ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE);
+
+            service.flags |= flag(ServiceInfo.FLAG_STOP_WITH_TASK,
+                    R.styleable.AndroidManifestService_stopWithTask, sa)
+                    | flag(ServiceInfo.FLAG_ISOLATED_PROCESS,
+                    R.styleable.AndroidManifestService_isolatedProcess, sa)
+                    | flag(ServiceInfo.FLAG_EXTERNAL_SERVICE,
+                    R.styleable.AndroidManifestService_externalService, sa)
+                    | flag(ServiceInfo.FLAG_USE_APP_ZYGOTE,
+                    R.styleable.AndroidManifestService_useAppZygote, sa)
+                    | flag(ServiceInfo.FLAG_SINGLE_USER,
+                    R.styleable.AndroidManifestService_singleUser, sa);
+
+            visibleToEphemeral = sa.getBoolean(
+                    R.styleable.AndroidManifestService_visibleToInstantApps, false);
+            if (visibleToEphemeral) {
+                service.flags |= ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP;
+                pkg.setVisibleToInstantApps(true);
+            }
+        } finally {
+            sa.recycle();
+        }
+
+        if (pkg.isCantSaveState()) {
+            // A heavy-weight application can not have services in its main process
+            // We can do direct compare because we intern all strings.
+            if (Objects.equals(service.getProcessName(), packageName)) {
+                return input.error("Heavy-weight applications can not have services "
+                        + "in main process");
+            }
+        }
+        final int depth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG
+                || parser.getDepth() > depth)) {
+            if (type != XmlPullParser.START_TAG) {
+                continue;
+            }
+
+            final ParseResult parseResult;
+            switch (parser.getName()) {
+                case "intent-filter":
+                    ParseResult<ParsedIntentInfo> intentResult = ParsedMainComponentUtils
+                            .parseIntentFilter(service, pkg, res, parser, visibleToEphemeral,
+                                    true /*allowGlobs*/, false /*allowAutoVerify*/,
+                                    false /*allowImplicitEphemeralVisibility*/,
+                                    false /*failOnNoActions*/, input);
+                    parseResult = intentResult;
+                    if (intentResult.isSuccess()) {
+                        ParsedIntentInfo intent = intentResult.getResult();
+                        service.order = Math.max(intent.getOrder(), service.order);
+                        service.addIntent(intent);
+                    }
+                    break;
+                case "meta-data":
+                    parseResult = ParsedComponentUtils.addMetaData(service, pkg, res, parser, input);
+                    break;
+                default:
+                    parseResult = ParsingUtils.unknownTag(tag, pkg, parser, input);
+                    break;
+            }
+
+            if (parseResult.isError()) {
+                return input.error(parseResult);
+            }
+        }
+
+        if (!setExported) {
+            service.exported = service.getIntents().size() > 0;
+        }
+
+        return input.success(service);
+    }
+}
diff --git a/android/content/pm/parsing/result/ParseInput.java b/android/content/pm/parsing/result/ParseInput.java
new file mode 100644
index 0000000..d5898b7
--- /dev/null
+++ b/android/content/pm/parsing/result/ParseInput.java
@@ -0,0 +1,143 @@
+/*
+ * 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.content.pm.parsing.result;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
+import android.content.pm.PackageManager;
+import android.os.Build;
+
+/**
+ * Used as a method parameter which is then transformed into a {@link ParseResult}. This is
+ * generalized as it doesn't matter what type this input is for. It's simply to hide the
+ * methods of {@link ParseResult}.
+ *
+ * @hide
+ */
+public interface ParseInput {
+
+    /**
+     * Errors encountered during parsing may rely on the targetSDK version of the application to
+     * determine whether or not to fail. These are passed into {@link #deferError(String, long)}
+     * when encountered, and the implementation will handle how to defer the errors until the
+     * targetSdkVersion is known and sent to {@link #enableDeferredError(String, int)}.
+     *
+     * All of these must be marked {@link ChangeId}, as that is the mechanism used to check if the
+     * error must be propagated. This framework also allows developers to pre-disable specific
+     * checks if they wish to target a newer SDK version in a development environment without
+     * having to migrate their entire app to validate on a newer platform.
+     */
+    final class DeferredError {
+        /**
+         * Missing an "application" or "instrumentation" tag.
+         */
+        @ChangeId
+        @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
+        public static final long MISSING_APP_TAG = 150776642;
+
+        /**
+         * An intent filter's actor or category is an empty string. A bug in the platform before R
+         * allowed this to pass through without an error. This does not include cases when the
+         * attribute is null/missing, as that has always been a failure.
+         */
+        @ChangeId
+        @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
+        public static final long EMPTY_INTENT_ACTION_CATEGORY = 151163173;
+
+        /**
+         * The {@code resources.arsc} of one of the APKs being installed is compressed or not
+         * aligned on a 4-byte boundary. Resource tables that cannot be memory mapped exert excess
+         * memory pressure on the system and drastically slow down construction of
+         * {@link android.content.res.Resources} objects.
+         */
+        @ChangeId
+        @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
+        public static final long RESOURCES_ARSC_COMPRESSED = 132742131;
+    }
+
+    <ResultType> ParseResult<ResultType> success(ResultType result);
+
+    /**
+     * Used for errors gated by {@link DeferredError}. Will return an error result if the
+     * targetSdkVersion is already known and this must be returned as a real error. The result
+     * contains null and should not be unwrapped.
+     *
+     * @see #error(String)
+     */
+    ParseResult<?> deferError(@NonNull String parseError, long deferredError);
+
+    /**
+     * Called after targetSdkVersion is known. Returns an error result if a previously deferred
+     * error was registered. The result contains null and should not be unwrapped.
+     */
+    ParseResult<?> enableDeferredError(String packageName, int targetSdkVersion);
+
+    /**
+     * This will assign errorCode to {@link PackageManager#INSTALL_PARSE_FAILED_SKIPPED, used for
+     * packages which should be ignored by the caller.
+     *
+     * @see #error(int, String, Exception)
+     */
+    <ResultType> ParseResult<ResultType> skip(@NonNull String parseError);
+
+    /** @see #error(int, String, Exception) */
+    <ResultType> ParseResult<ResultType> error(int parseError);
+
+    /**
+     * This will assign errorCode to {@link PackageManager#INSTALL_PARSE_FAILED_MANIFEST_MALFORMED}.
+     * @see #error(int, String, Exception)
+     */
+    <ResultType> ParseResult<ResultType> error(@NonNull String parseError);
+
+    /** @see #error(int, String, Exception) */
+    <ResultType> ParseResult<ResultType> error(int parseError, @Nullable String errorMessage);
+
+    /**
+     * Marks this as an error result. When this method is called, the return value <b>must</b>
+     * be returned to the exit of the parent method that took in this {@link ParseInput} as a
+     * parameter.
+     *
+     * The calling site of that method is then expected to check the result for error, and
+     * continue to bubble up if it is an error.
+     *
+     * If the result {@link ParseResult#isSuccess()}, then it can be used as-is, as
+     * overlapping/consecutive successes are allowed.
+     */
+    <ResultType> ParseResult<ResultType> error(int parseError, @Nullable String errorMessage,
+            @Nullable Exception exception);
+
+    /**
+     * Moves the error in {@param result} to this input's type. In practice this does nothing
+     * but cast the type of the {@link ParseResult} for type safety, since the parameter
+     * and the receiver should be the same object.
+     */
+    <ResultType> ParseResult<ResultType> error(ParseResult<?> result);
+
+    /**
+     * Implemented instead of a direct reference to
+     * {@link com.android.internal.compat.IPlatformCompat}, allowing caching and testing logic to
+     * be separated out.
+     */
+    interface Callback {
+        /**
+         * @return true if the changeId should be enabled
+         */
+        boolean isChangeEnabled(long changeId, @NonNull String packageName, int targetSdkVersion);
+    }
+}
diff --git a/android/content/pm/parsing/result/ParseResult.java b/android/content/pm/parsing/result/ParseResult.java
new file mode 100644
index 0000000..518395d
--- /dev/null
+++ b/android/content/pm/parsing/result/ParseResult.java
@@ -0,0 +1,63 @@
+/*
+ * 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.content.pm.parsing.result;
+
+import android.annotation.Nullable;
+
+/**
+ * The output side of {@link ParseInput}, which must result from a method call on
+ * {@link ParseInput}.
+ *
+ * When using this class, keep in mind that all {@link ParseInput}s and {@link ParseResult}s
+ * are the exact same object, scoped per thread, thrown around and casted for type safety.
+ *
+ * @hide
+ */
+public interface ParseResult<ResultType> {
+
+    /**
+     * Returns true if the result is not an error and thus contains a valid object.
+     *
+     * For backwards-compat reasons, it's possible to have a successful result with a null
+     * result object, depending on the behavior of the parsing method.
+     *
+     * It is expected that every method calls this to check for an error state to bubble up
+     * the error to its parent method after every parse method call.
+     *
+     * It is not always necessary to check this, as it is valid to return any ParseResult from
+     * a method so long as the type matches <b>without casting it</b>.
+     *
+     * The infrastructure is set up such that as long as a result is the proper type and
+     * the right side of success vs. error, it can be bubble up through all its parent methods.
+     */
+    boolean isSuccess();
+
+    /**
+     * Opposite of {@link #isSuccess()} for readability.
+     */
+    boolean isError();
+
+    ResultType getResult();
+
+    int getErrorCode();
+
+    @Nullable
+    String getErrorMessage();
+
+    @Nullable
+    Exception getException();
+}
diff --git a/android/content/pm/parsing/result/ParseTypeImpl.java b/android/content/pm/parsing/result/ParseTypeImpl.java
new file mode 100644
index 0000000..6115206
--- /dev/null
+++ b/android/content/pm/parsing/result/ParseTypeImpl.java
@@ -0,0 +1,230 @@
+/*
+ * 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.content.pm.parsing.result;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.PackageManager;
+import android.content.pm.parsing.ParsingUtils;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.internal.util.CollectionUtils;
+
+/** @hide */
+public class ParseTypeImpl implements ParseInput, ParseResult<Object> {
+
+    private static final String TAG = ParsingUtils.TAG;
+
+    public static final boolean DEBUG_FILL_STACK_TRACE = false;
+
+    public static final boolean DEBUG_LOG_ON_ERROR = false;
+
+    public static final boolean DEBUG_THROW_ALL_ERRORS = false;
+
+    @NonNull
+    private Callback mCallback;
+
+    private Object mResult;
+
+    private int mErrorCode = PackageManager.INSTALL_SUCCEEDED;
+
+    @Nullable
+    private String mErrorMessage;
+
+    @Nullable
+    private Exception mException;
+
+    /**
+     * Errors encountered before targetSdkVersion is known.
+     * The size upper bound is the number of longs in {@link DeferredError}
+     */
+    @Nullable
+    private ArrayMap<Long, String> mDeferredErrors = null;
+
+    private String mPackageName;
+    private Integer mTargetSdkVersion;
+
+    /**
+     * @param callback if nullable, fallback to manual targetSdk > Q check
+     */
+    public ParseTypeImpl(@NonNull Callback callback) {
+        mCallback = callback;
+    }
+
+    public ParseInput reset() {
+        mResult = null;
+        mErrorCode = PackageManager.INSTALL_SUCCEEDED;
+        mErrorMessage = null;
+        mException = null;
+        if (mDeferredErrors != null) {
+            // If the memory was already allocated, don't bother freeing and re-allocating,
+            // as this could occur hundreds of times depending on what the caller is doing and
+            // how many APKs they're going through.
+            mDeferredErrors.erase();
+        }
+        return this;
+    }
+
+    @Override
+    public <ResultType> ParseResult<ResultType> success(ResultType result) {
+        if (mErrorCode != PackageManager.INSTALL_SUCCEEDED) {
+            Slog.wtf(ParsingUtils.TAG, "Cannot set to success after set to error, was "
+                    + mErrorMessage, mException);
+        }
+        mResult = result;
+        //noinspection unchecked
+        return (ParseResult<ResultType>) this;
+    }
+
+    @Override
+    public ParseResult<?> deferError(@NonNull String parseError, long deferredError) {
+        if (DEBUG_THROW_ALL_ERRORS) {
+            return error(parseError);
+        }
+        if (mTargetSdkVersion != null) {
+            if (mDeferredErrors != null && mDeferredErrors.containsKey(deferredError)) {
+                // If the map already contains the key, that means it's already been checked and
+                // found to be disabled. Otherwise it would've failed when mTargetSdkVersion was
+                // set to non-null.
+                return success(null);
+            }
+
+            if (mCallback.isChangeEnabled(deferredError, mPackageName, mTargetSdkVersion)) {
+                return error(parseError);
+            } else {
+                if (mDeferredErrors == null) {
+                    mDeferredErrors = new ArrayMap<>();
+                }
+                mDeferredErrors.put(deferredError, null);
+                return success(null);
+            }
+        }
+
+        if (mDeferredErrors == null) {
+            mDeferredErrors = new ArrayMap<>();
+        }
+
+        // Only save the first occurrence of any particular error
+        mDeferredErrors.putIfAbsent(deferredError, parseError);
+        return success(null);
+    }
+
+    @Override
+    public ParseResult<?> enableDeferredError(String packageName, int targetSdkVersion) {
+        mPackageName = packageName;
+        mTargetSdkVersion = targetSdkVersion;
+
+        int size = CollectionUtils.size(mDeferredErrors);
+        for (int index = size - 1; index >= 0; index--) {
+            long changeId = mDeferredErrors.keyAt(index);
+            String errorMessage = mDeferredErrors.valueAt(index);
+            if (mCallback.isChangeEnabled(changeId, mPackageName, mTargetSdkVersion)) {
+                return error(errorMessage);
+            } else {
+                // No point holding onto the string, but need to maintain the key to signal
+                // that the error was checked with isChangeEnabled and found to be disabled.
+                mDeferredErrors.setValueAt(index, null);
+            }
+        }
+
+        return success(null);
+    }
+
+    @Override
+    public <ResultType> ParseResult<ResultType> skip(@NonNull String parseError) {
+        return error(PackageManager.INSTALL_PARSE_FAILED_SKIPPED, parseError);
+    }
+
+    @Override
+    public <ResultType> ParseResult<ResultType> error(int parseError) {
+        return error(parseError, null);
+    }
+
+    @Override
+    public <ResultType> ParseResult<ResultType> error(@NonNull String parseError) {
+        return error(PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, parseError);
+    }
+
+    @Override
+    public <ResultType> ParseResult<ResultType> error(int errorCode,
+            @Nullable String errorMessage) {
+        return error(errorCode, errorMessage, null);
+    }
+
+    @Override
+    public <ResultType> ParseResult<ResultType> error(ParseResult<?> intentResult) {
+        return error(intentResult.getErrorCode(), intentResult.getErrorMessage(),
+                intentResult.getException());
+    }
+
+    @Override
+    public <ResultType> ParseResult<ResultType> error(int errorCode, @Nullable String errorMessage,
+            Exception exception) {
+        mErrorCode = errorCode;
+        mErrorMessage = errorMessage;
+        mException = exception;
+
+        if (DEBUG_FILL_STACK_TRACE) {
+            if (exception == null) {
+                mException = new Exception();
+            }
+        }
+
+        if (DEBUG_LOG_ON_ERROR) {
+            Exception exceptionToLog = mException != null ? mException : new Exception();
+            Log.w(TAG, "ParseInput set to error " + errorCode + ", " + errorMessage,
+                    exceptionToLog);
+        }
+
+        //noinspection unchecked
+        return (ParseResult<ResultType>) this;
+    }
+
+    @Override
+    public Object getResult() {
+        return mResult;
+    }
+
+    @Override
+    public boolean isSuccess() {
+        return mErrorCode == PackageManager.INSTALL_SUCCEEDED;
+    }
+
+    @Override
+    public boolean isError() {
+        return !isSuccess();
+    }
+
+    @Override
+    public int getErrorCode() {
+        return mErrorCode;
+    }
+
+    @Nullable
+    @Override
+    public String getErrorMessage() {
+        return mErrorMessage;
+    }
+
+    @Nullable
+    @Override
+    public Exception getException() {
+        return mException;
+    }
+}
diff --git a/android/content/pm/permission/RuntimePermissionPresentationInfo.java b/android/content/pm/permission/RuntimePermissionPresentationInfo.java
new file mode 100644
index 0000000..97312c4
--- /dev/null
+++ b/android/content/pm/permission/RuntimePermissionPresentationInfo.java
@@ -0,0 +1,114 @@
+/*
+ * 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.content.pm.permission;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * This class contains information about how a runtime permission
+ * is to be presented in the UI. A single runtime permission
+ * presented to the user may correspond to multiple platform defined
+ * permissions, e.g. the location permission may control both the
+ * coarse and fine platform permissions.
+ *
+ * @hide
+ *
+ * @deprecated Not used anymore. Use {@link android.permission.RuntimePermissionPresentationInfo}
+ * instead
+ */
+@Deprecated
+@SystemApi
+public final class RuntimePermissionPresentationInfo implements Parcelable {
+    private static final int FLAG_GRANTED = 1 << 0;
+    private static final int FLAG_STANDARD = 1 << 1;
+
+    private final CharSequence mLabel;
+    private final int mFlags;
+
+    /**
+     * Creates a new instance.
+     *
+     * @param label The permission label.
+     * @param granted Whether the permission is granted.
+     * @param standard Whether this is a platform-defined permission.
+     */
+    public RuntimePermissionPresentationInfo(CharSequence label,
+            boolean granted, boolean standard) {
+        mLabel = label;
+        int flags = 0;
+        if (granted) {
+            flags |= FLAG_GRANTED;
+        }
+        if (standard) {
+            flags |= FLAG_STANDARD;
+        }
+        mFlags = flags;
+    }
+
+    private RuntimePermissionPresentationInfo(Parcel parcel) {
+        mLabel = parcel.readCharSequence();
+        mFlags = parcel.readInt();
+    }
+
+    /**
+     * @return Whether the permission is granted.
+     */
+    public boolean isGranted() {
+        return (mFlags & FLAG_GRANTED) != 0;
+    }
+
+    /**
+     * @return Whether the permission is platform-defined.
+     */
+    public boolean isStandard() {
+        return (mFlags & FLAG_STANDARD) != 0;
+    }
+
+    /**
+     * Gets the permission label.
+     *
+     * @return The label.
+     */
+    public @NonNull CharSequence getLabel() {
+        return mLabel;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeCharSequence(mLabel);
+        parcel.writeInt(mFlags);
+    }
+
+    public static final @android.annotation.NonNull Creator<RuntimePermissionPresentationInfo> CREATOR =
+            new Creator<RuntimePermissionPresentationInfo>() {
+        public RuntimePermissionPresentationInfo createFromParcel(Parcel source) {
+            return new RuntimePermissionPresentationInfo(source);
+        }
+
+        public RuntimePermissionPresentationInfo[] newArray(int size) {
+            return new RuntimePermissionPresentationInfo[size];
+        }
+    };
+}
diff --git a/android/content/pm/permission/SplitPermissionInfoParcelable.java b/android/content/pm/permission/SplitPermissionInfoParcelable.java
new file mode 100644
index 0000000..421ae49
--- /dev/null
+++ b/android/content/pm/permission/SplitPermissionInfoParcelable.java
@@ -0,0 +1,202 @@
+/*
+ * 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.content.pm.permission;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Preconditions;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Parcelable version of {@link android.permission.PermissionManager.SplitPermissionInfo}
+ * @hide
+ */
+@DataClass(genEqualsHashCode = true)
+public class SplitPermissionInfoParcelable implements Parcelable {
+
+    /**
+     * The permission that is split.
+     */
+    @NonNull
+    private final String mSplitPermission;
+
+    /**
+     * The permissions that are added.
+     */
+    @NonNull
+    private final List<String> mNewPermissions;
+
+    /**
+     * The target API level when the permission was split.
+     */
+    @IntRange(from = 0)
+    private final int mTargetSdk;
+
+    private void onConstructed() {
+        Preconditions.checkCollectionElementsNotNull(mNewPermissions, "newPermissions");
+    }
+
+
+
+    // Code below generated by codegen v1.0.0.
+    //
+    // DO NOT MODIFY!
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/permission/SplitPermissionInfoParcelable.java
+    //
+    // CHECKSTYLE:OFF Generated code
+
+    /**
+     * Creates a new SplitPermissionInfoParcelable.
+     *
+     * @param splitPermission
+     *   The permission that is split.
+     * @param newPermissions
+     *   The permissions that are added.
+     * @param targetSdk
+     *   The target API level when the permission was split.
+     */
+    @DataClass.Generated.Member
+    public SplitPermissionInfoParcelable(
+            @NonNull String splitPermission,
+            @NonNull List<String> newPermissions,
+            @IntRange(from = 0) int targetSdk) {
+        this.mSplitPermission = splitPermission;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mSplitPermission);
+        this.mNewPermissions = newPermissions;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mNewPermissions);
+        this.mTargetSdk = targetSdk;
+        com.android.internal.util.AnnotationValidations.validate(
+                IntRange.class, null, mTargetSdk,
+                "from", 0);
+
+        onConstructed();
+    }
+
+    /**
+     * The permission that is split.
+     */
+    @DataClass.Generated.Member
+    public @NonNull String getSplitPermission() {
+        return mSplitPermission;
+    }
+
+    /**
+     * The permissions that are added.
+     */
+    @DataClass.Generated.Member
+    public @NonNull List<String> getNewPermissions() {
+        return mNewPermissions;
+    }
+
+    /**
+     * The target API level when the permission was split.
+     */
+    @DataClass.Generated.Member
+    public @IntRange(from = 0) int getTargetSdk() {
+        return mTargetSdk;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public boolean equals(Object o) {
+        // You can override field equality logic by defining either of the methods like:
+        // boolean fieldNameEquals(SplitPermissionInfoParcelable other) { ... }
+        // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        @SuppressWarnings("unchecked")
+        SplitPermissionInfoParcelable that = (SplitPermissionInfoParcelable) o;
+        //noinspection PointlessBooleanExpression
+        return true
+                && Objects.equals(mSplitPermission, that.mSplitPermission)
+                && Objects.equals(mNewPermissions, that.mNewPermissions)
+                && mTargetSdk == that.mTargetSdk;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int hashCode() {
+        // You can override field hashCode logic by defining methods like:
+        // int fieldNameHashCode() { ... }
+
+        int _hash = 1;
+        _hash = 31 * _hash + Objects.hashCode(mSplitPermission);
+        _hash = 31 * _hash + Objects.hashCode(mNewPermissions);
+        _hash = 31 * _hash + mTargetSdk;
+        return _hash;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        dest.writeString(mSplitPermission);
+        dest.writeStringList(mNewPermissions);
+        dest.writeInt(mTargetSdk);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<SplitPermissionInfoParcelable> CREATOR
+            = new Parcelable.Creator<SplitPermissionInfoParcelable>() {
+        @Override
+        public SplitPermissionInfoParcelable[] newArray(int size) {
+            return new SplitPermissionInfoParcelable[size];
+        }
+
+        @Override
+        @SuppressWarnings({"unchecked", "RedundantCast"})
+        public SplitPermissionInfoParcelable createFromParcel(Parcel in) {
+            // You can override field unparcelling by defining methods like:
+            // static FieldType unparcelFieldName(Parcel in) { ... }
+
+            String splitPermission = in.readString();
+            List<String> newPermissions = new java.util.ArrayList<>();
+            in.readStringList(newPermissions);
+            int targetSdk = in.readInt();
+            return new SplitPermissionInfoParcelable(
+                    splitPermission,
+                    newPermissions,
+                    targetSdk);
+        }
+    };
+
+    @DataClass.Generated(
+            time = 1567634390477L,
+            codegenVersion = "1.0.0",
+            sourceFile = "frameworks/base/core/java/android/content/pm/permission/SplitPermissionInfoParcelable.java",
+            inputSignatures = "private final @android.annotation.NonNull java.lang.String mSplitPermission\nprivate final @android.annotation.NonNull java.util.List<java.lang.String> mNewPermissions\nprivate final @android.annotation.IntRange(from=0L) int mTargetSdk\nprivate  void onConstructed()\nclass SplitPermissionInfoParcelable extends java.lang.Object implements [android.os.Parcelable]\[email protected](genEqualsHashCode=true)")
+    @Deprecated
+    private void __metadata() {}
+
+}
diff --git a/android/content/pm/split/DefaultSplitAssetLoader.java b/android/content/pm/split/DefaultSplitAssetLoader.java
new file mode 100644
index 0000000..9e3a8f4
--- /dev/null
+++ b/android/content/pm/split/DefaultSplitAssetLoader.java
@@ -0,0 +1,103 @@
+/*
+ * 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.content.pm.split;
+
+import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
+
+import android.content.pm.PackageParser;
+import android.content.pm.PackageParser.PackageParserException;
+import android.content.pm.PackageParser.ParseFlags;
+import android.content.res.ApkAssets;
+import android.content.res.AssetManager;
+import android.os.Build;
+
+import com.android.internal.util.ArrayUtils;
+
+import libcore.io.IoUtils;
+
+import java.io.IOException;
+
+/**
+ * Loads the base and split APKs into a single AssetManager.
+ * @hide
+ */
+public class DefaultSplitAssetLoader implements SplitAssetLoader {
+    private final String mBaseCodePath;
+    private final String[] mSplitCodePaths;
+    private final @ParseFlags int mFlags;
+    private AssetManager mCachedAssetManager;
+
+    public DefaultSplitAssetLoader(PackageParser.PackageLite pkg, @ParseFlags int flags) {
+        mBaseCodePath = pkg.baseCodePath;
+        mSplitCodePaths = pkg.splitCodePaths;
+        mFlags = flags;
+    }
+
+    private static ApkAssets loadApkAssets(String path, @ParseFlags int flags)
+            throws PackageParserException {
+        if ((flags & PackageParser.PARSE_MUST_BE_APK) != 0 && !PackageParser.isApkPath(path)) {
+            throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
+                    "Invalid package file: " + path);
+        }
+
+        try {
+            return ApkAssets.loadFromPath(path);
+        } catch (IOException e) {
+            throw new PackageParserException(INSTALL_FAILED_INVALID_APK,
+                    "Failed to load APK at path " + path, e);
+        }
+    }
+
+    @Override
+    public AssetManager getBaseAssetManager() throws PackageParserException {
+        if (mCachedAssetManager != null) {
+            return mCachedAssetManager;
+        }
+
+        ApkAssets[] apkAssets = new ApkAssets[(mSplitCodePaths != null
+                ? mSplitCodePaths.length : 0) + 1];
+
+        // Load the base.
+        int splitIdx = 0;
+        apkAssets[splitIdx++] = loadApkAssets(mBaseCodePath, mFlags);
+
+        // Load any splits.
+        if (!ArrayUtils.isEmpty(mSplitCodePaths)) {
+            for (String apkPath : mSplitCodePaths) {
+                apkAssets[splitIdx++] = loadApkAssets(apkPath, mFlags);
+            }
+        }
+
+        AssetManager assets = new AssetManager();
+        assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                Build.VERSION.RESOURCES_SDK_INT);
+        assets.setApkAssets(apkAssets, false /*invalidateCaches*/);
+
+        mCachedAssetManager = assets;
+        return mCachedAssetManager;
+    }
+
+    @Override
+    public AssetManager getSplitAssetManager(int splitIdx) throws PackageParserException {
+        return getBaseAssetManager();
+    }
+
+    @Override
+    public void close() throws Exception {
+        IoUtils.closeQuietly(mCachedAssetManager);
+    }
+}
diff --git a/android/content/pm/split/SplitAssetDependencyLoader.java b/android/content/pm/split/SplitAssetDependencyLoader.java
new file mode 100644
index 0000000..58eaabf
--- /dev/null
+++ b/android/content/pm/split/SplitAssetDependencyLoader.java
@@ -0,0 +1,133 @@
+/*
+ * 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.content.pm.split;
+
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
+
+import android.annotation.NonNull;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageParser;
+import android.content.pm.PackageParser.PackageParserException;
+import android.content.pm.PackageParser.ParseFlags;
+import android.content.res.ApkAssets;
+import android.content.res.AssetManager;
+import android.os.Build;
+import android.util.SparseArray;
+
+import libcore.io.IoUtils;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+
+/**
+ * Loads AssetManagers for splits and their dependencies. This SplitAssetLoader implementation
+ * is to be used when an application opts-in to isolated split loading.
+ * @hide
+ */
+public class SplitAssetDependencyLoader extends SplitDependencyLoader<PackageParserException>
+        implements SplitAssetLoader {
+    private final String[] mSplitPaths;
+    private final @ParseFlags int mFlags;
+    private final ApkAssets[][] mCachedSplitApks;
+    private final AssetManager[] mCachedAssetManagers;
+
+    public SplitAssetDependencyLoader(PackageParser.PackageLite pkg,
+            SparseArray<int[]> dependencies, @ParseFlags int flags) {
+        super(dependencies);
+
+        // The base is inserted into index 0, so we need to shift all the splits by 1.
+        mSplitPaths = new String[pkg.splitCodePaths.length + 1];
+        mSplitPaths[0] = pkg.baseCodePath;
+        System.arraycopy(pkg.splitCodePaths, 0, mSplitPaths, 1, pkg.splitCodePaths.length);
+
+        mFlags = flags;
+        mCachedSplitApks = new ApkAssets[mSplitPaths.length][];
+        mCachedAssetManagers = new AssetManager[mSplitPaths.length];
+    }
+
+    @Override
+    protected boolean isSplitCached(int splitIdx) {
+        return mCachedAssetManagers[splitIdx] != null;
+    }
+
+    private static ApkAssets loadApkAssets(String path, @ParseFlags int flags)
+            throws PackageParserException {
+        if ((flags & PackageParser.PARSE_MUST_BE_APK) != 0 && !PackageParser.isApkPath(path)) {
+            throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
+                    "Invalid package file: " + path);
+        }
+
+        try {
+            return ApkAssets.loadFromPath(path);
+        } catch (IOException e) {
+            throw new PackageParserException(PackageManager.INSTALL_FAILED_INVALID_APK,
+                    "Failed to load APK at path " + path, e);
+        }
+    }
+
+    private static AssetManager createAssetManagerWithAssets(ApkAssets[] apkAssets) {
+        final AssetManager assets = new AssetManager();
+        assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                Build.VERSION.RESOURCES_SDK_INT);
+        assets.setApkAssets(apkAssets, false /*invalidateCaches*/);
+        return assets;
+    }
+
+    @Override
+    protected void constructSplit(int splitIdx, @NonNull int[] configSplitIndices,
+            int parentSplitIdx) throws PackageParserException {
+        final ArrayList<ApkAssets> assets = new ArrayList<>();
+
+        // Include parent ApkAssets.
+        if (parentSplitIdx >= 0) {
+            Collections.addAll(assets, mCachedSplitApks[parentSplitIdx]);
+        }
+
+        // Include this ApkAssets.
+        assets.add(loadApkAssets(mSplitPaths[splitIdx], mFlags));
+
+        // Load and include all config splits for this feature.
+        for (int configSplitIdx : configSplitIndices) {
+            assets.add(loadApkAssets(mSplitPaths[configSplitIdx], mFlags));
+        }
+
+        // Cache the results.
+        mCachedSplitApks[splitIdx] = assets.toArray(new ApkAssets[assets.size()]);
+        mCachedAssetManagers[splitIdx] = createAssetManagerWithAssets(mCachedSplitApks[splitIdx]);
+    }
+
+    @Override
+    public AssetManager getBaseAssetManager() throws PackageParserException {
+        loadDependenciesForSplit(0);
+        return mCachedAssetManagers[0];
+    }
+
+    @Override
+    public AssetManager getSplitAssetManager(int idx) throws PackageParserException {
+        // Since we insert the base at position 0, and PackageParser keeps splits separate from
+        // the base, we need to adjust the index.
+        loadDependenciesForSplit(idx + 1);
+        return mCachedAssetManagers[idx + 1];
+    }
+
+    @Override
+    public void close() throws Exception {
+        for (AssetManager assets : mCachedAssetManagers) {
+            IoUtils.closeQuietly(assets);
+        }
+    }
+}
diff --git a/android/content/pm/split/SplitAssetLoader.java b/android/content/pm/split/SplitAssetLoader.java
new file mode 100644
index 0000000..108fb95
--- /dev/null
+++ b/android/content/pm/split/SplitAssetLoader.java
@@ -0,0 +1,30 @@
+/*
+ * 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.content.pm.split;
+
+import android.content.pm.PackageParser;
+import android.content.res.AssetManager;
+
+/**
+ * Simple interface for loading base Assets and Splits. Used by PackageParser when parsing
+ * split APKs.
+ *
+ * @hide
+ */
+public interface SplitAssetLoader extends AutoCloseable {
+    AssetManager getBaseAssetManager() throws PackageParser.PackageParserException;
+    AssetManager getSplitAssetManager(int splitIdx) throws PackageParser.PackageParserException;
+}
diff --git a/android/content/pm/split/SplitDependencyLoader.java b/android/content/pm/split/SplitDependencyLoader.java
new file mode 100644
index 0000000..3586546
--- /dev/null
+++ b/android/content/pm/split/SplitDependencyLoader.java
@@ -0,0 +1,242 @@
+/*
+ * 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.content.pm.split;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.content.pm.PackageParser;
+import android.util.IntArray;
+import android.util.SparseArray;
+
+import libcore.util.EmptyArray;
+
+import java.util.Arrays;
+import java.util.BitSet;
+
+/**
+ * A helper class that implements the dependency tree traversal for splits. Callbacks
+ * are implemented by subclasses to notify whether a split has already been constructed
+ * and is cached, and to actually create the split requested.
+ *
+ * This helper is meant to be subclassed so as to reduce the number of allocations
+ * needed to make use of it.
+ *
+ * All inputs and outputs are assumed to be indices into an array of splits.
+ *
+ * @hide
+ */
+public abstract class SplitDependencyLoader<E extends Exception> {
+    private final @NonNull SparseArray<int[]> mDependencies;
+
+    /**
+     * Construct a new SplitDependencyLoader. Meant to be called from the
+     * subclass constructor.
+     * @param dependencies The dependency tree of splits.
+     */
+    protected SplitDependencyLoader(@NonNull SparseArray<int[]> dependencies) {
+        mDependencies = dependencies;
+    }
+
+    /**
+     * Traverses the dependency tree and constructs any splits that are not already
+     * cached. This routine short-circuits and skips the creation of splits closer to the
+     * root if they are cached, as reported by the subclass implementation of
+     * {@link #isSplitCached(int)}. The construction of splits is delegated to the subclass
+     * implementation of {@link #constructSplit(int, int[], int)}.
+     * @param splitIdx The index of the split to load. 0 represents the base Application.
+     */
+    protected void loadDependenciesForSplit(@IntRange(from = 0) int splitIdx) throws E {
+        // Quick check before any allocations are done.
+        if (isSplitCached(splitIdx)) {
+            return;
+        }
+
+        // Special case the base, since it has no dependencies.
+        if (splitIdx == 0) {
+            final int[] configSplitIndices = collectConfigSplitIndices(0);
+            constructSplit(0, configSplitIndices, -1);
+            return;
+        }
+
+        // Build up the dependency hierarchy.
+        final IntArray linearDependencies = new IntArray();
+        linearDependencies.add(splitIdx);
+
+        // Collect all the dependencies that need to be constructed.
+        // They will be listed from leaf to root.
+        while (true) {
+            // Only follow the first index into the array. The others are config splits and
+            // get loaded with the split.
+            final int[] deps = mDependencies.get(splitIdx);
+            if (deps != null && deps.length > 0) {
+                splitIdx = deps[0];
+            } else {
+                splitIdx = -1;
+            }
+
+            if (splitIdx < 0 || isSplitCached(splitIdx)) {
+                break;
+            }
+
+            linearDependencies.add(splitIdx);
+        }
+
+        // Visit each index, from right to left (root to leaf).
+        int parentIdx = splitIdx;
+        for (int i = linearDependencies.size() - 1; i >= 0; i--) {
+            final int idx = linearDependencies.get(i);
+            final int[] configSplitIndices = collectConfigSplitIndices(idx);
+            constructSplit(idx, configSplitIndices, parentIdx);
+            parentIdx = idx;
+        }
+    }
+
+    private @NonNull int[] collectConfigSplitIndices(int splitIdx) {
+        // The config splits appear after the first element.
+        final int[] deps = mDependencies.get(splitIdx);
+        if (deps == null || deps.length <= 1) {
+            return EmptyArray.INT;
+        }
+        return Arrays.copyOfRange(deps, 1, deps.length);
+    }
+
+    /**
+     * Subclass to report whether the split at `splitIdx` is cached and need not be constructed.
+     * It is assumed that if `splitIdx` is cached, any parent of `splitIdx` is also cached.
+     * @param splitIdx The index of the split to check for in the cache.
+     * @return true if the split is cached and does not need to be constructed.
+     */
+    protected abstract boolean isSplitCached(@IntRange(from = 0) int splitIdx);
+
+    /**
+     * Subclass to construct a split at index `splitIdx` with parent split `parentSplitIdx`.
+     * The result is expected to be cached by the subclass in its own structures.
+     * @param splitIdx The index of the split to construct. 0 represents the base Application.
+     * @param configSplitIndices The array of configuration splits to load along with this split.
+     *                           May be empty (length == 0) but never null.
+     * @param parentSplitIdx The index of the parent split. -1 if there is no parent.
+     * @throws E Subclass defined exception representing failure to construct a split.
+     */
+    protected abstract void constructSplit(@IntRange(from = 0) int splitIdx,
+            @NonNull @IntRange(from = 1) int[] configSplitIndices,
+            @IntRange(from = -1) int parentSplitIdx) throws E;
+
+    public static class IllegalDependencyException extends Exception {
+        private IllegalDependencyException(String message) {
+            super(message);
+        }
+    }
+
+    private static int[] append(int[] src, int elem) {
+        if (src == null) {
+            return new int[] { elem };
+        }
+        int[] dst = Arrays.copyOf(src, src.length + 1);
+        dst[src.length] = elem;
+        return dst;
+    }
+
+    public static @NonNull SparseArray<int[]> createDependenciesFromPackage(
+            PackageParser.PackageLite pkg) throws IllegalDependencyException {
+        // The data structure that holds the dependencies. In PackageParser, splits are stored
+        // in their own array, separate from the base. We treat all paths as equals, so
+        // we need to insert the base as index 0, and shift all other splits.
+        final SparseArray<int[]> splitDependencies = new SparseArray<>();
+
+        // The base depends on nothing.
+        splitDependencies.put(0, new int[] {-1});
+
+        // First write out the <uses-split> dependencies. These must appear first in the
+        // array of ints, as is convention in this class.
+        for (int splitIdx = 0; splitIdx < pkg.splitNames.length; splitIdx++) {
+            if (!pkg.isFeatureSplits[splitIdx]) {
+                // Non-feature splits don't have dependencies.
+                continue;
+            }
+
+            // Implicit dependency on the base.
+            final int targetIdx;
+            final String splitDependency = pkg.usesSplitNames[splitIdx];
+            if (splitDependency != null) {
+                final int depIdx = Arrays.binarySearch(pkg.splitNames, splitDependency);
+                if (depIdx < 0) {
+                    throw new IllegalDependencyException("Split '" + pkg.splitNames[splitIdx]
+                            + "' requires split '" + splitDependency + "', which is missing.");
+                }
+                targetIdx = depIdx + 1;
+            } else {
+                // Implicitly depend on the base.
+                targetIdx = 0;
+            }
+            splitDependencies.put(splitIdx + 1, new int[] {targetIdx});
+        }
+
+        // Write out the configForSplit reverse-dependencies. These appear after the <uses-split>
+        // dependencies and are considered leaves.
+        //
+        // At this point, all splits in splitDependencies have the first element in their array set.
+        for (int splitIdx = 0; splitIdx < pkg.splitNames.length; splitIdx++) {
+            if (pkg.isFeatureSplits[splitIdx]) {
+                // Feature splits are not configForSplits.
+                continue;
+            }
+
+            // Implicit feature for the base.
+            final int targetSplitIdx;
+            final String configForSplit = pkg.configForSplit[splitIdx];
+            if (configForSplit != null) {
+                final int depIdx = Arrays.binarySearch(pkg.splitNames, configForSplit);
+                if (depIdx < 0) {
+                    throw new IllegalDependencyException("Split '" + pkg.splitNames[splitIdx]
+                            + "' targets split '" + configForSplit + "', which is missing.");
+                }
+
+                if (!pkg.isFeatureSplits[depIdx]) {
+                    throw new IllegalDependencyException("Split '" + pkg.splitNames[splitIdx]
+                            + "' declares itself as configuration split for a non-feature split '"
+                            + pkg.splitNames[depIdx] + "'");
+                }
+                targetSplitIdx = depIdx + 1;
+            } else {
+                targetSplitIdx = 0;
+            }
+            splitDependencies.put(targetSplitIdx,
+                    append(splitDependencies.get(targetSplitIdx), splitIdx + 1));
+        }
+
+        // Verify that there are no cycles.
+        final BitSet bitset = new BitSet();
+        for (int i = 0, size = splitDependencies.size(); i < size; i++) {
+            int splitIdx = splitDependencies.keyAt(i);
+
+            bitset.clear();
+            while (splitIdx != -1) {
+                // Check if this split has been visited yet.
+                if (bitset.get(splitIdx)) {
+                    throw new IllegalDependencyException("Cycle detected in split dependencies.");
+                }
+
+                // Mark the split so that if we visit it again, we no there is a cycle.
+                bitset.set(splitIdx);
+
+                // Follow the first dependency only, the others are leaves by definition.
+                final int[] deps = splitDependencies.get(splitIdx);
+                splitIdx = deps != null ? deps[0] : -1;
+            }
+        }
+        return splitDependencies;
+    }
+}
diff --git a/android/content/res/ApkAssets.java b/android/content/res/ApkAssets.java
new file mode 100644
index 0000000..bc41806
--- /dev/null
+++ b/android/content/res/ApkAssets.java
@@ -0,0 +1,443 @@
+/*
+ * 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.content.res;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.om.OverlayableInfo;
+import android.content.res.loader.AssetsProvider;
+import android.content.res.loader.ResourcesProvider;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * The loaded, immutable, in-memory representation of an APK.
+ *
+ * The main implementation is native C++ and there is very little API surface exposed here. The APK
+ * is mainly accessed via {@link AssetManager}.
+ *
+ * Since the ApkAssets instance is immutable, it can be reused and shared across AssetManagers,
+ * making the creation of AssetManagers very cheap.
+ * @hide
+ */
+public final class ApkAssets {
+
+    /**
+     * The apk assets contains framework resource values specified by the system.
+     * This allows some functions to filter out this package when computing what
+     * configurations/resources are available.
+     */
+    public static final int PROPERTY_SYSTEM = 1 << 0;
+
+    /**
+     * The apk assets is a shared library or was loaded as a shared library by force.
+     * The package ids of dynamic apk assets are assigned at runtime instead of compile time.
+     */
+    public static final int PROPERTY_DYNAMIC = 1 << 1;
+
+    /**
+     * The apk assets has been loaded dynamically using a {@link ResourcesProvider}.
+     * Loader apk assets overlay resources like RROs except they are not backed by an idmap.
+     */
+    public static final int PROPERTY_LOADER = 1 << 2;
+
+    /**
+     * The apk assets is a RRO.
+     * An RRO overlays resource values of its target package.
+     */
+    private static final int PROPERTY_OVERLAY = 1 << 3;
+
+    /** Flags that change the behavior of loaded apk assets. */
+    @IntDef(prefix = { "PROPERTY_" }, value = {
+            PROPERTY_SYSTEM,
+            PROPERTY_DYNAMIC,
+            PROPERTY_LOADER,
+            PROPERTY_OVERLAY,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface PropertyFlags {}
+
+    /** The path used to load the apk assets represents an APK file. */
+    private static final int FORMAT_APK = 0;
+
+    /** The path used to load the apk assets represents an idmap file. */
+    private static final int FORMAT_IDMAP = 1;
+
+    /** The path used to load the apk assets represents an resources.arsc file. */
+    private static final int FORMAT_ARSC = 2;
+
+    /** the path used to load the apk assets represents a directory. */
+    private static final int FORMAT_DIR = 3;
+
+    // Format types that change how the apk assets are loaded.
+    @IntDef(prefix = { "FORMAT_" }, value = {
+            FORMAT_APK,
+            FORMAT_IDMAP,
+            FORMAT_ARSC,
+            FORMAT_DIR
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FormatType {}
+
+    @GuardedBy("this")
+    private final long mNativePtr;
+
+    @Nullable
+    @GuardedBy("this")
+    private final StringBlock mStringBlock;
+
+    @GuardedBy("this")
+    private boolean mOpen = true;
+
+    @PropertyFlags
+    private final int mFlags;
+
+    @Nullable
+    private final AssetsProvider mAssets;
+
+    /**
+     * Creates a new ApkAssets instance from the given path on disk.
+     *
+     * @param path The path to an APK on disk.
+     * @return a new instance of ApkAssets.
+     * @throws IOException if a disk I/O error or parsing error occurred.
+     */
+    public static @NonNull ApkAssets loadFromPath(@NonNull String path) throws IOException {
+        return loadFromPath(path, 0 /* flags */);
+    }
+
+    /**
+     * Creates a new ApkAssets instance from the given path on disk.
+     *
+     * @param path The path to an APK on disk.
+     * @param flags flags that change the behavior of loaded apk assets
+     * @return a new instance of ApkAssets.
+     * @throws IOException if a disk I/O error or parsing error occurred.
+     */
+    public static @NonNull ApkAssets loadFromPath(@NonNull String path, @PropertyFlags int flags)
+            throws IOException {
+        return new ApkAssets(FORMAT_APK, path, flags, null /* assets */);
+    }
+
+    /**
+     * Creates a new ApkAssets instance from the given path on disk.
+     *
+     * @param path The path to an APK on disk.
+     * @param flags flags that change the behavior of loaded apk assets
+     * @param assets The assets provider that overrides the loading of file-based resources
+     * @return a new instance of ApkAssets.
+     * @throws IOException if a disk I/O error or parsing error occurred.
+     */
+    public static @NonNull ApkAssets loadFromPath(@NonNull String path, @PropertyFlags int flags,
+            @Nullable AssetsProvider assets) throws IOException {
+        return new ApkAssets(FORMAT_APK, path, flags, assets);
+    }
+
+    /**
+     * Creates a new ApkAssets instance from the given file descriptor.
+     *
+     * Performs a dup of the underlying fd, so you must take care of still closing
+     * the FileDescriptor yourself (and can do that whenever you want).
+     *
+     * @param fd The FileDescriptor of an open, readable APK.
+     * @param friendlyName The friendly name used to identify this ApkAssets when logging.
+     * @param flags flags that change the behavior of loaded apk assets
+     * @param assets The assets provider that overrides the loading of file-based resources
+     * @return a new instance of ApkAssets.
+     * @throws IOException if a disk I/O error or parsing error occurred.
+     */
+    public static @NonNull ApkAssets loadFromFd(@NonNull FileDescriptor fd,
+            @NonNull String friendlyName, @PropertyFlags int flags,
+            @Nullable AssetsProvider assets) throws IOException {
+        return new ApkAssets(FORMAT_APK, fd, friendlyName, flags, assets);
+    }
+
+    /**
+     * Creates a new ApkAssets instance from the given file descriptor.
+     *
+     * Performs a dup of the underlying fd, so you must take care of still closing
+     * the FileDescriptor yourself (and can do that whenever you want).
+     *
+     * @param fd The FileDescriptor of an open, readable APK.
+     * @param friendlyName The friendly name used to identify this ApkAssets when logging.
+     * @param offset The location within the file that the apk starts. This must be 0 if length is
+     *               {@link AssetFileDescriptor#UNKNOWN_LENGTH}.
+     * @param length The number of bytes of the apk, or {@link AssetFileDescriptor#UNKNOWN_LENGTH}
+     *               if it extends to the end of the file.
+     * @param flags flags that change the behavior of loaded apk assets
+     * @param assets The assets provider that overrides the loading of file-based resources
+     * @return a new instance of ApkAssets.
+     * @throws IOException if a disk I/O error or parsing error occurred.
+     */
+    public static @NonNull ApkAssets loadFromFd(@NonNull FileDescriptor fd,
+            @NonNull String friendlyName, long offset, long length, @PropertyFlags int flags,
+            @Nullable AssetsProvider assets)
+            throws IOException {
+        return new ApkAssets(FORMAT_APK, fd, friendlyName, offset, length, flags, assets);
+    }
+
+    /**
+     * Creates a new ApkAssets instance from the IDMAP at idmapPath. The overlay APK path
+     * is encoded within the IDMAP.
+     *
+     * @param idmapPath Path to the IDMAP of an overlay APK.
+     * @param flags flags that change the behavior of loaded apk assets
+     * @return a new instance of ApkAssets.
+     * @throws IOException if a disk I/O error or parsing error occurred.
+     */
+    public static @NonNull ApkAssets loadOverlayFromPath(@NonNull String idmapPath,
+            @PropertyFlags int flags) throws IOException {
+        return new ApkAssets(FORMAT_IDMAP, idmapPath, flags, null /* assets */);
+    }
+
+    /**
+     * Creates a new ApkAssets instance from the given file descriptor representing a resources.arsc
+     * for use with a {@link ResourcesProvider}.
+     *
+     * Performs a dup of the underlying fd, so you must take care of still closing
+     * the FileDescriptor yourself (and can do that whenever you want).
+     *
+     * @param fd The FileDescriptor of an open, readable resources.arsc.
+     * @param friendlyName The friendly name used to identify this ApkAssets when logging.
+     * @param flags flags that change the behavior of loaded apk assets
+     * @param assets The assets provider that overrides the loading of file-based resources
+     * @return a new instance of ApkAssets.
+     * @throws IOException if a disk I/O error or parsing error occurred.
+     */
+    public static @NonNull ApkAssets loadTableFromFd(@NonNull FileDescriptor fd,
+            @NonNull String friendlyName, @PropertyFlags int flags,
+            @Nullable AssetsProvider assets) throws IOException {
+        return new ApkAssets(FORMAT_ARSC, fd, friendlyName, flags, assets);
+    }
+
+    /**
+     * Creates a new ApkAssets instance from the given file descriptor representing a resources.arsc
+     * for use with a {@link ResourcesProvider}.
+     *
+     * Performs a dup of the underlying fd, so you must take care of still closing
+     * the FileDescriptor yourself (and can do that whenever you want).
+     *
+     * @param fd The FileDescriptor of an open, readable resources.arsc.
+     * @param friendlyName The friendly name used to identify this ApkAssets when logging.
+     * @param offset The location within the file that the table starts. This must be 0 if length is
+     *               {@link AssetFileDescriptor#UNKNOWN_LENGTH}.
+     * @param length The number of bytes of the table, or {@link AssetFileDescriptor#UNKNOWN_LENGTH}
+     *               if it extends to the end of the file.
+     * @param flags flags that change the behavior of loaded apk assets
+     * @param assets The assets provider that overrides the loading of file-based resources
+     * @return a new instance of ApkAssets.
+     * @throws IOException if a disk I/O error or parsing error occurred.
+     */
+    public static @NonNull ApkAssets loadTableFromFd(@NonNull FileDescriptor fd,
+            @NonNull String friendlyName, long offset, long length, @PropertyFlags int flags,
+            @Nullable AssetsProvider assets) throws IOException {
+        return new ApkAssets(FORMAT_ARSC, fd, friendlyName, offset, length, flags, assets);
+    }
+
+    /**
+     * Creates a new ApkAssets instance from the given directory path. The directory should have the
+     * file structure of an APK.
+     *
+     * @param path The path to a directory on disk.
+     * @param flags flags that change the behavior of loaded apk assets
+     * @param assets The assets provider that overrides the loading of file-based resources
+     * @return a new instance of ApkAssets.
+     * @throws IOException if a disk I/O error or parsing error occurred.
+     */
+    public static @NonNull ApkAssets loadFromDir(@NonNull String path,
+            @PropertyFlags int flags, @Nullable AssetsProvider assets) throws IOException {
+        return new ApkAssets(FORMAT_DIR, path, flags, assets);
+    }
+
+    /**
+     * Generates an entirely empty ApkAssets. Needed because the ApkAssets instance and presence
+     * is required for a lot of APIs, and it's easier to have a non-null reference rather than
+     * tracking a separate identifier.
+     *
+     * @param flags flags that change the behavior of loaded apk assets
+     * @param assets The assets provider that overrides the loading of file-based resources
+     */
+    @NonNull
+    public static ApkAssets loadEmptyForLoader(@PropertyFlags int flags,
+            @Nullable AssetsProvider assets) {
+        return new ApkAssets(flags, assets);
+    }
+
+    private ApkAssets(@FormatType int format, @NonNull String path, @PropertyFlags int flags,
+            @Nullable AssetsProvider assets) throws IOException {
+        Objects.requireNonNull(path, "path");
+        mFlags = flags;
+        mNativePtr = nativeLoad(format, path, flags, assets);
+        mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/);
+        mAssets = assets;
+    }
+
+    private ApkAssets(@FormatType int format, @NonNull FileDescriptor fd,
+            @NonNull String friendlyName, @PropertyFlags int flags, @Nullable AssetsProvider assets)
+            throws IOException {
+        Objects.requireNonNull(fd, "fd");
+        Objects.requireNonNull(friendlyName, "friendlyName");
+        mFlags = flags;
+        mNativePtr = nativeLoadFd(format, fd, friendlyName, flags, assets);
+        mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/);
+        mAssets = assets;
+    }
+
+    private ApkAssets(@FormatType int format, @NonNull FileDescriptor fd,
+            @NonNull String friendlyName, long offset, long length, @PropertyFlags int flags,
+            @Nullable AssetsProvider assets) throws IOException {
+        Objects.requireNonNull(fd, "fd");
+        Objects.requireNonNull(friendlyName, "friendlyName");
+        mFlags = flags;
+        mNativePtr = nativeLoadFdOffsets(format, fd, friendlyName, offset, length, flags, assets);
+        mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/);
+        mAssets = assets;
+    }
+
+    private ApkAssets(@PropertyFlags int flags, @Nullable AssetsProvider assets) {
+        mFlags = flags;
+        mNativePtr = nativeLoadEmpty(flags, assets);
+        mStringBlock = null;
+        mAssets = assets;
+    }
+
+    @UnsupportedAppUsage
+    public @NonNull String getAssetPath() {
+        synchronized (this) {
+            return nativeGetAssetPath(mNativePtr);
+        }
+    }
+
+    CharSequence getStringFromPool(int idx) {
+        if (mStringBlock == null) {
+            return null;
+        }
+
+        synchronized (this) {
+            return mStringBlock.get(idx);
+        }
+    }
+
+    /** Returns whether this apk assets was loaded using a {@link ResourcesProvider}. */
+    public boolean isForLoader() {
+        return (mFlags & PROPERTY_LOADER) != 0;
+    }
+
+    /**
+     * Returns the assets provider that overrides the loading of assets present in this apk assets.
+     */
+    @Nullable
+    public AssetsProvider getAssetsProvider() {
+        return mAssets;
+    }
+
+    /**
+     * Retrieve a parser for a compiled XML file. This is associated with a single APK and
+     * <em>NOT</em> a full AssetManager. This means that shared-library references will not be
+     * dynamically assigned runtime package IDs.
+     *
+     * @param fileName The path to the file within the APK.
+     * @return An XmlResourceParser.
+     * @throws IOException if the file was not found or an error occurred retrieving it.
+     */
+    public @NonNull XmlResourceParser openXml(@NonNull String fileName) throws IOException {
+        Objects.requireNonNull(fileName, "fileName");
+        synchronized (this) {
+            long nativeXmlPtr = nativeOpenXml(mNativePtr, fileName);
+            try (XmlBlock block = new XmlBlock(null, nativeXmlPtr)) {
+                XmlResourceParser parser = block.newParser();
+                // If nativeOpenXml doesn't throw, it will always return a valid native pointer,
+                // which makes newParser always return non-null. But let's be paranoid.
+                if (parser == null) {
+                    throw new AssertionError("block.newParser() returned a null parser");
+                }
+                return parser;
+            }
+        }
+    }
+
+    /** @hide */
+    @Nullable
+    public OverlayableInfo getOverlayableInfo(String overlayableName) throws IOException {
+        return nativeGetOverlayableInfo(mNativePtr, overlayableName);
+    }
+
+    /** @hide */
+    public boolean definesOverlayable() throws IOException {
+        return nativeDefinesOverlayable(mNativePtr);
+    }
+
+    /**
+     * Returns false if the underlying APK was changed since this ApkAssets was loaded.
+     */
+    public boolean isUpToDate() {
+        synchronized (this) {
+            return nativeIsUpToDate(mNativePtr);
+        }
+    }
+
+    @Override
+    public String toString() {
+        return "ApkAssets{path=" + getAssetPath() + "}";
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        close();
+    }
+
+    /**
+     * Closes this class and the contained {@link #mStringBlock}.
+     */
+    public void close() {
+        synchronized (this) {
+            if (mOpen) {
+                mOpen = false;
+                if (mStringBlock != null) {
+                    mStringBlock.close();
+                }
+                nativeDestroy(mNativePtr);
+            }
+        }
+    }
+
+    private static native long nativeLoad(@FormatType int format, @NonNull String path,
+            @PropertyFlags int flags, @Nullable AssetsProvider asset) throws IOException;
+    private static native long nativeLoadEmpty(@PropertyFlags int flags,
+            @Nullable AssetsProvider asset);
+    private static native long nativeLoadFd(@FormatType int format, @NonNull FileDescriptor fd,
+            @NonNull String friendlyName, @PropertyFlags int flags,
+            @Nullable AssetsProvider asset) throws IOException;
+    private static native long nativeLoadFdOffsets(@FormatType int format,
+            @NonNull FileDescriptor fd, @NonNull String friendlyName, long offset, long length,
+            @PropertyFlags int flags, @Nullable AssetsProvider asset) throws IOException;
+    private static native void nativeDestroy(long ptr);
+    private static native @NonNull String nativeGetAssetPath(long ptr);
+    private static native long nativeGetStringBlock(long ptr);
+    private static native boolean nativeIsUpToDate(long ptr);
+    private static native long nativeOpenXml(long ptr, @NonNull String fileName) throws IOException;
+    private static native @Nullable OverlayableInfo nativeGetOverlayableInfo(long ptr,
+            String overlayableName) throws IOException;
+    private static native boolean nativeDefinesOverlayable(long ptr) throws IOException;
+}
diff --git a/android/content/res/AssetFileDescriptor.java b/android/content/res/AssetFileDescriptor.java
new file mode 100644
index 0000000..e93ec00
--- /dev/null
+++ b/android/content/res/AssetFileDescriptor.java
@@ -0,0 +1,383 @@
+/*
+ * 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.content.res;
+
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
+import android.os.Parcelable;
+
+import java.io.Closeable;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+/**
+ * File descriptor of an entry in the AssetManager.  This provides your own
+ * opened FileDescriptor that can be used to read the data, as well as the
+ * offset and length of that entry's data in the file.
+ */
+public class AssetFileDescriptor implements Parcelable, Closeable {
+    /**
+     * Length used with {@link #AssetFileDescriptor(ParcelFileDescriptor, long, long)}
+     * and {@link #getDeclaredLength} when a length has not been declared.  This means
+     * the data extends to the end of the file.
+     */
+    public static final long UNKNOWN_LENGTH = -1;
+    
+    @UnsupportedAppUsage
+    private final ParcelFileDescriptor mFd;
+    @UnsupportedAppUsage
+    private final long mStartOffset;
+    @UnsupportedAppUsage
+    private final long mLength;
+    private final Bundle mExtras;
+
+    /**
+     * Create a new AssetFileDescriptor from the given values.
+     *
+     * @param fd The underlying file descriptor.
+     * @param startOffset The location within the file that the asset starts.
+     *            This must be 0 if length is UNKNOWN_LENGTH.
+     * @param length The number of bytes of the asset, or
+     *            {@link #UNKNOWN_LENGTH} if it extends to the end of the file.
+     */
+    public AssetFileDescriptor(ParcelFileDescriptor fd, long startOffset,
+            long length) {
+        this(fd, startOffset, length, null);
+    }
+
+    /**
+     * Create a new AssetFileDescriptor from the given values.
+     *
+     * @param fd The underlying file descriptor.
+     * @param startOffset The location within the file that the asset starts.
+     *            This must be 0 if length is UNKNOWN_LENGTH.
+     * @param length The number of bytes of the asset, or
+     *            {@link #UNKNOWN_LENGTH} if it extends to the end of the file.
+     * @param extras additional details that can be used to interpret the
+     *            underlying file descriptor. May be null.
+     */
+    public AssetFileDescriptor(ParcelFileDescriptor fd, long startOffset,
+            long length, Bundle extras) {
+        if (fd == null) {
+            throw new IllegalArgumentException("fd must not be null");
+        }
+        if (length < 0 && startOffset != 0) {
+            throw new IllegalArgumentException(
+                    "startOffset must be 0 when using UNKNOWN_LENGTH");
+        }
+        mFd = fd;
+        mStartOffset = startOffset;
+        mLength = length;
+        mExtras = extras;
+    }
+
+    /**
+     * The AssetFileDescriptor contains its own ParcelFileDescriptor, which
+     * in addition to the normal FileDescriptor object also allows you to close
+     * the descriptor when you are done with it.
+     */
+    public ParcelFileDescriptor getParcelFileDescriptor() {
+        return mFd;
+    }
+    
+    /**
+     * Returns the FileDescriptor that can be used to read the data in the
+     * file.
+     */
+    public FileDescriptor getFileDescriptor() {
+        return mFd.getFileDescriptor();
+    }
+    
+    /**
+     * Returns the byte offset where this asset entry's data starts.
+     */
+    public long getStartOffset() {
+        return mStartOffset;
+    }
+
+    /**
+     * Returns any additional details that can be used to interpret the
+     * underlying file descriptor. May be null.
+     */
+    public Bundle getExtras() {
+        return mExtras;
+    }
+
+    /**
+     * Returns the total number of bytes of this asset entry's data.  May be
+     * {@link #UNKNOWN_LENGTH} if the asset extends to the end of the file.
+     * If the AssetFileDescriptor was constructed with {@link #UNKNOWN_LENGTH},
+     * this will use {@link ParcelFileDescriptor#getStatSize()
+     * ParcelFileDescriptor.getStatSize()} to find the total size of the file,
+     * returning that number if found or {@link #UNKNOWN_LENGTH} if it could
+     * not be determined.
+     * 
+     * @see #getDeclaredLength()
+     */
+    public long getLength() {
+        if (mLength >= 0) {
+            return mLength;
+        }
+        long len = mFd.getStatSize();
+        return len >= 0 ? len : UNKNOWN_LENGTH;
+    }
+    
+    /**
+     * Return the actual number of bytes that were declared when the
+     * AssetFileDescriptor was constructed.  Will be
+     * {@link #UNKNOWN_LENGTH} if the length was not declared, meaning data
+     * should be read to the end of the file.
+     * 
+     * @see #getDeclaredLength()
+     */
+    public long getDeclaredLength() {
+        return mLength;
+    }
+    
+    /**
+     * Convenience for calling <code>getParcelFileDescriptor().close()</code>.
+     */
+    @Override
+    public void close() throws IOException {
+        mFd.close();
+    }
+
+    /**
+     * Create and return a new auto-close input stream for this asset.  This
+     * will either return a full asset {@link AutoCloseInputStream}, or
+     * an underlying {@link ParcelFileDescriptor.AutoCloseInputStream
+     * ParcelFileDescriptor.AutoCloseInputStream} depending on whether the
+     * the object represents a complete file or sub-section of a file.  You
+     * should only call this once for a particular asset.
+     */
+    public FileInputStream createInputStream() throws IOException {
+        if (mLength < 0) {
+            return new ParcelFileDescriptor.AutoCloseInputStream(mFd);
+        }
+        return new AutoCloseInputStream(this);
+    }
+    
+    /**
+     * Create and return a new auto-close output stream for this asset.  This
+     * will either return a full asset {@link AutoCloseOutputStream}, or
+     * an underlying {@link ParcelFileDescriptor.AutoCloseOutputStream
+     * ParcelFileDescriptor.AutoCloseOutputStream} depending on whether the
+     * the object represents a complete file or sub-section of a file.  You
+     * should only call this once for a particular asset.
+     */
+    public FileOutputStream createOutputStream() throws IOException {
+        if (mLength < 0) {
+            return new ParcelFileDescriptor.AutoCloseOutputStream(mFd);
+        }
+        return new AutoCloseOutputStream(this);
+    }
+    
+    @Override
+    public String toString() {
+        return "{AssetFileDescriptor: " + mFd
+                + " start=" + mStartOffset + " len=" + mLength + "}";
+    }
+    
+    /**
+     * 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 ParcelFileDescriptor.AutoCloseInputStream {
+        private long mRemaining;
+        
+        public AutoCloseInputStream(AssetFileDescriptor fd) throws IOException {
+            super(fd.getParcelFileDescriptor());
+            super.skip(fd.getStartOffset());
+            mRemaining = (int)fd.getLength();
+        }
+
+        @Override
+        public int available() throws IOException {
+            return mRemaining >= 0
+                    ? (mRemaining < 0x7fffffff ? (int)mRemaining : 0x7fffffff)
+                    : super.available();
+        }
+
+        @Override
+        public int read() throws IOException {
+            byte[] buffer = new byte[1];
+            int result = read(buffer, 0, 1);
+            return result == -1 ? -1 : buffer[0] & 0xff;
+        }
+
+        @Override
+        public int read(byte[] buffer, int offset, int count) throws IOException {
+            if (mRemaining >= 0) {
+                if (mRemaining == 0) return -1;
+                if (count > mRemaining) count = (int)mRemaining;
+                int res = super.read(buffer, offset, count);
+                if (res >= 0) mRemaining -= res;
+                return res;
+            }
+            
+            return super.read(buffer, offset, count);
+        }
+
+        @Override
+        public int read(byte[] buffer) throws IOException {
+            return read(buffer, 0, buffer.length);
+        }
+
+        @Override
+        public long skip(long count) throws IOException {
+            if (mRemaining >= 0) {
+                if (mRemaining == 0) return -1;
+                if (count > mRemaining) count = mRemaining;
+                long res = super.skip(count);
+                if (res >= 0) mRemaining -= res;
+                return res;
+            }
+            
+            return super.skip(count);
+        }
+
+        @Override
+        public void mark(int readlimit) {
+            if (mRemaining >= 0) {
+                // Not supported.
+                return;
+            }
+            super.mark(readlimit);
+        }
+
+        @Override
+        public boolean markSupported() {
+            if (mRemaining >= 0) {
+                return false;
+            }
+            return super.markSupported();
+        }
+
+        @Override
+        public synchronized void reset() throws IOException {
+            if (mRemaining >= 0) {
+                // Not supported.
+                return;
+            }
+            super.reset();
+        }
+    }
+
+    /**
+     * 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 ParcelFileDescriptor.AutoCloseOutputStream {
+        private long mRemaining;
+        
+        public AutoCloseOutputStream(AssetFileDescriptor fd) throws IOException {
+            super(fd.getParcelFileDescriptor());
+            if (fd.getParcelFileDescriptor().seekTo(fd.getStartOffset()) < 0) {
+                throw new IOException("Unable to seek");
+            }
+            mRemaining = (int)fd.getLength();
+        }
+
+        @Override
+        public void write(byte[] buffer, int offset, int count) throws IOException {
+            if (mRemaining >= 0) {
+                if (mRemaining == 0) return;
+                if (count > mRemaining) count = (int)mRemaining;
+                super.write(buffer, offset, count);
+                mRemaining -= count;
+                return;
+            }
+            
+            super.write(buffer, offset, count);
+        }
+
+        @Override
+        public void write(byte[] buffer) throws IOException {
+            if (mRemaining >= 0) {
+                if (mRemaining == 0) return;
+                int count = buffer.length;
+                if (count > mRemaining) count = (int)mRemaining;
+                super.write(buffer);
+                mRemaining -= count;
+                return;
+            }
+            
+            super.write(buffer);
+        }
+
+        @Override
+        public void write(int oneByte) throws IOException {
+            if (mRemaining >= 0) {
+                if (mRemaining == 0) return;
+                super.write(oneByte);
+                mRemaining--;
+                return;
+            }
+            
+            super.write(oneByte);
+        }
+    }
+
+    /* Parcelable interface */
+    @Override
+    public int describeContents() {
+        return mFd.describeContents();
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        mFd.writeToParcel(out, flags);
+        out.writeLong(mStartOffset);
+        out.writeLong(mLength);
+        if (mExtras != null) {
+            out.writeInt(1);
+            out.writeBundle(mExtras);
+        } else {
+            out.writeInt(0);
+        }
+    }
+
+    AssetFileDescriptor(Parcel src) {
+        mFd = ParcelFileDescriptor.CREATOR.createFromParcel(src);
+        mStartOffset = src.readLong();
+        mLength = src.readLong();
+        if (src.readInt() != 0) {
+            mExtras = src.readBundle();
+        } else {
+            mExtras = null;
+        }
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<AssetFileDescriptor> CREATOR
+            = new Parcelable.Creator<AssetFileDescriptor>() {
+        public AssetFileDescriptor createFromParcel(Parcel in) {
+            return new AssetFileDescriptor(in);
+        }
+        public AssetFileDescriptor[] newArray(int size) {
+            return new AssetFileDescriptor[size];
+        }
+    };
+
+}
diff --git a/android/content/res/AssetManager.java b/android/content/res/AssetManager.java
new file mode 100644
index 0000000..15a184f
--- /dev/null
+++ b/android/content/res/AssetManager.java
@@ -0,0 +1,1599 @@
+/*
+ * 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.content.res;
+
+import android.annotation.AnyRes;
+import android.annotation.ArrayRes;
+import android.annotation.AttrRes;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StringRes;
+import android.annotation.StyleRes;
+import android.annotation.TestApi;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration.NativeConfig;
+import android.content.res.loader.ResourcesLoader;
+import android.os.ParcelFileDescriptor;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.SparseArray;
+import android.util.TypedValue;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.content.om.OverlayConfig;
+
+import java.io.FileDescriptor;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * Provides access to an application's raw asset files; see {@link Resources}
+ * for the way most applications will want to retrieve their resource data.
+ * This class presents a lower-level API that allows you to open and read raw
+ * files that have been bundled with the application as a simple stream of
+ * bytes.
+ */
+public final class AssetManager implements AutoCloseable {
+    private static final String TAG = "AssetManager";
+    private static final boolean DEBUG_REFS = false;
+
+    private static final String FRAMEWORK_APK_PATH = "/system/framework/framework-res.apk";
+
+    private static final Object sSync = new Object();
+
+    private static final ApkAssets[] sEmptyApkAssets = new ApkAssets[0];
+
+    // Not private for LayoutLib's BridgeAssetManager.
+    @UnsupportedAppUsage
+    @GuardedBy("sSync") static AssetManager sSystem = null;
+
+    @GuardedBy("sSync") private static ApkAssets[] sSystemApkAssets = new ApkAssets[0];
+    @GuardedBy("sSync") private static ArraySet<ApkAssets> sSystemApkAssetsSet;
+
+    /**
+     * Mode for {@link #open(String, int)}: no specific information about how
+     * data will be accessed.
+     */
+    public static final int ACCESS_UNKNOWN = 0;
+    /**
+     * Mode for {@link #open(String, int)}: Read chunks, and seek forward and
+     * backward.
+     */
+    public static final int ACCESS_RANDOM = 1;
+    /**
+     * Mode for {@link #open(String, int)}: Read sequentially, with an
+     * occasional forward seek.
+     */
+    public static final int ACCESS_STREAMING = 2;
+    /**
+     * Mode for {@link #open(String, int)}: Attempt to load contents into
+     * memory, for fast small reads.
+     */
+    public static final int ACCESS_BUFFER = 3;
+
+    @GuardedBy("this") private final TypedValue mValue = new TypedValue();
+    @GuardedBy("this") private final long[] mOffsets = new long[2];
+
+    // Pointer to native implementation, stuffed inside a long.
+    @UnsupportedAppUsage
+    @GuardedBy("this") private long mObject;
+
+    // The loaded asset paths.
+    @GuardedBy("this") private ApkAssets[] mApkAssets;
+
+    // Debug/reference counting implementation.
+    @GuardedBy("this") private boolean mOpen = true;
+    @GuardedBy("this") private int mNumRefs = 1;
+    @GuardedBy("this") private HashMap<Long, RuntimeException> mRefStacks;
+
+    private ResourcesLoader[] mLoaders;
+
+    /**
+     * A Builder class that helps create an AssetManager with only a single invocation of
+     * {@link AssetManager#setApkAssets(ApkAssets[], boolean)}. Without using this builder,
+     * AssetManager must ensure there are system ApkAssets loaded at all times, which when combined
+     * with the user's call to add additional ApkAssets, results in multiple calls to
+     * {@link AssetManager#setApkAssets(ApkAssets[], boolean)}.
+     * @hide
+     */
+    public static class Builder {
+        private ArrayList<ApkAssets> mUserApkAssets = new ArrayList<>();
+        private ArrayList<ResourcesLoader> mLoaders = new ArrayList<>();
+
+        public Builder addApkAssets(ApkAssets apkAssets) {
+            mUserApkAssets.add(apkAssets);
+            return this;
+        }
+
+        public Builder addLoader(ResourcesLoader loader) {
+            mLoaders.add(loader);
+            return this;
+        }
+
+        public AssetManager build() {
+            // Retrieving the system ApkAssets forces their creation as well.
+            final ApkAssets[] systemApkAssets = getSystem().getApkAssets();
+
+            // Filter ApkAssets so that assets provided by multiple loaders are only included once
+            // in the AssetManager assets. The last appearance of the ApkAssets dictates its load
+            // order.
+            final ArrayList<ApkAssets> loaderApkAssets = new ArrayList<>();
+            final ArraySet<ApkAssets> uniqueLoaderApkAssets = new ArraySet<>();
+            for (int i = mLoaders.size() - 1; i >= 0; i--) {
+                final List<ApkAssets> currentLoaderApkAssets = mLoaders.get(i).getApkAssets();
+                for (int j = currentLoaderApkAssets.size() - 1; j >= 0; j--) {
+                    final ApkAssets apkAssets = currentLoaderApkAssets.get(j);
+                    if (uniqueLoaderApkAssets.add(apkAssets)) {
+                        loaderApkAssets.add(0, apkAssets);
+                    }
+                }
+            }
+
+            final int totalApkAssetCount = systemApkAssets.length + mUserApkAssets.size()
+                    + loaderApkAssets.size();
+            final ApkAssets[] apkAssets = new ApkAssets[totalApkAssetCount];
+
+            System.arraycopy(systemApkAssets, 0, apkAssets, 0, systemApkAssets.length);
+
+            // Append user ApkAssets after system ApkAssets.
+            for (int i = 0, n = mUserApkAssets.size(); i < n; i++) {
+                apkAssets[i + systemApkAssets.length] = mUserApkAssets.get(i);
+            }
+
+            // Append ApkAssets provided by loaders to the end.
+            for (int i = 0, n = loaderApkAssets.size(); i < n; i++) {
+                apkAssets[i + systemApkAssets.length  + mUserApkAssets.size()] =
+                        loaderApkAssets.get(i);
+            }
+
+            // Calling this constructor prevents creation of system ApkAssets, which we took care
+            // of in this Builder.
+            final AssetManager assetManager = new AssetManager(false /*sentinel*/);
+            assetManager.mApkAssets = apkAssets;
+            AssetManager.nativeSetApkAssets(assetManager.mObject, apkAssets,
+                    false /*invalidateCaches*/);
+            assetManager.mLoaders = mLoaders.isEmpty() ? null
+                    : mLoaders.toArray(new ResourcesLoader[0]);
+
+            return assetManager;
+        }
+    }
+
+    /**
+     * Create a new AssetManager containing only the basic system assets.
+     * Applications will not generally use this method, instead retrieving the
+     * appropriate asset manager with {@link Resources#getAssets}.    Not for
+     * use by applications.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public AssetManager() {
+        final ApkAssets[] assets;
+        synchronized (sSync) {
+            createSystemAssetsInZygoteLocked(false, FRAMEWORK_APK_PATH);
+            assets = sSystemApkAssets;
+        }
+
+        mObject = nativeCreate();
+        if (DEBUG_REFS) {
+            mNumRefs = 0;
+            incRefsLocked(hashCode());
+        }
+
+        // Always set the framework resources.
+        setApkAssets(assets, false /*invalidateCaches*/);
+    }
+
+    /**
+     * Private constructor that doesn't call ensureSystemAssets.
+     * Used for the creation of system assets.
+     */
+    @SuppressWarnings("unused")
+    private AssetManager(boolean sentinel) {
+        mObject = nativeCreate();
+        if (DEBUG_REFS) {
+            mNumRefs = 0;
+            incRefsLocked(hashCode());
+        }
+    }
+
+    /**
+     * This must be called from Zygote so that system assets are shared by all applications.
+     * @hide
+     */
+    @GuardedBy("sSync")
+    @VisibleForTesting
+    public static void createSystemAssetsInZygoteLocked(boolean reinitialize,
+            String frameworkPath) {
+        if (sSystem != null && !reinitialize) {
+            return;
+        }
+
+        try {
+            final ArrayList<ApkAssets> apkAssets = new ArrayList<>();
+            apkAssets.add(ApkAssets.loadFromPath(frameworkPath, ApkAssets.PROPERTY_SYSTEM));
+
+            final String[] systemIdmapPaths =
+                    OverlayConfig.getZygoteInstance().createImmutableFrameworkIdmapsInZygote();
+            for (String idmapPath : systemIdmapPaths) {
+                apkAssets.add(ApkAssets.loadOverlayFromPath(idmapPath, ApkAssets.PROPERTY_SYSTEM));
+            }
+
+            sSystemApkAssetsSet = new ArraySet<>(apkAssets);
+            sSystemApkAssets = apkAssets.toArray(new ApkAssets[apkAssets.size()]);
+            if (sSystem == null) {
+                sSystem = new AssetManager(true /*sentinel*/);
+            }
+            sSystem.setApkAssets(sSystemApkAssets, false /*invalidateCaches*/);
+        } catch (IOException e) {
+            throw new IllegalStateException("Failed to create system AssetManager", e);
+        }
+    }
+
+    /**
+     * Return a global shared asset manager that provides access to only
+     * system assets (no application assets).
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static AssetManager getSystem() {
+        synchronized (sSync) {
+            createSystemAssetsInZygoteLocked(false, FRAMEWORK_APK_PATH);
+            return sSystem;
+        }
+    }
+
+    /**
+     * Close this asset manager.
+     */
+    @Override
+    public void close() {
+        synchronized (this) {
+            if (!mOpen) {
+                return;
+            }
+
+            mOpen = false;
+            decRefsLocked(hashCode());
+        }
+    }
+
+    /**
+     * Changes the asset paths in this AssetManager. This replaces the {@link #addAssetPath(String)}
+     * family of methods.
+     *
+     * @param apkAssets The new set of paths.
+     * @param invalidateCaches Whether to invalidate any caches. This should almost always be true.
+     *                         Set this to false if you are appending new resources
+     *                         (not new configurations).
+     * @hide
+     */
+    public void setApkAssets(@NonNull ApkAssets[] apkAssets, boolean invalidateCaches) {
+        Objects.requireNonNull(apkAssets, "apkAssets");
+
+        ApkAssets[] newApkAssets = new ApkAssets[sSystemApkAssets.length + apkAssets.length];
+
+        // Copy the system assets first.
+        System.arraycopy(sSystemApkAssets, 0, newApkAssets, 0, sSystemApkAssets.length);
+
+        // Copy the given ApkAssets if they are not already in the system list.
+        int newLength = sSystemApkAssets.length;
+        for (ApkAssets apkAsset : apkAssets) {
+            if (!sSystemApkAssetsSet.contains(apkAsset)) {
+                newApkAssets[newLength++] = apkAsset;
+            }
+        }
+
+        // Truncate if necessary.
+        if (newLength != newApkAssets.length) {
+            newApkAssets = Arrays.copyOf(newApkAssets, newLength);
+        }
+
+        synchronized (this) {
+            ensureOpenLocked();
+            mApkAssets = newApkAssets;
+            nativeSetApkAssets(mObject, mApkAssets, invalidateCaches);
+            if (invalidateCaches) {
+                // Invalidate all caches.
+                invalidateCachesLocked(-1);
+            }
+        }
+    }
+
+    /**
+     * Changes the {@link ResourcesLoader ResourcesLoaders} used in this AssetManager.
+     * @hide
+     */
+    void setLoaders(@NonNull List<ResourcesLoader> newLoaders) {
+        Objects.requireNonNull(newLoaders, "newLoaders");
+
+        final ArrayList<ApkAssets> apkAssets = new ArrayList<>();
+        for (int i = 0; i < mApkAssets.length; i++) {
+            // Filter out the previous loader apk assets.
+            if (!mApkAssets[i].isForLoader()) {
+                apkAssets.add(mApkAssets[i]);
+            }
+        }
+
+        if (!newLoaders.isEmpty()) {
+            // Filter so that assets provided by multiple loaders are only included once
+            // in the final assets list. The last appearance of the ApkAssets dictates its load
+            // order.
+            final int loaderStartIndex = apkAssets.size();
+            final ArraySet<ApkAssets> uniqueLoaderApkAssets = new ArraySet<>();
+            for (int i = newLoaders.size() - 1; i >= 0; i--) {
+                final List<ApkAssets> currentLoaderApkAssets = newLoaders.get(i).getApkAssets();
+                for (int j = currentLoaderApkAssets.size() - 1; j >= 0; j--) {
+                    final ApkAssets loaderApkAssets = currentLoaderApkAssets.get(j);
+                    if (uniqueLoaderApkAssets.add(loaderApkAssets)) {
+                        apkAssets.add(loaderStartIndex, loaderApkAssets);
+                    }
+                }
+            }
+        }
+
+        mLoaders = newLoaders.toArray(new ResourcesLoader[0]);
+        setApkAssets(apkAssets.toArray(new ApkAssets[0]), true /* invalidate_caches */);
+    }
+
+    /**
+     * Invalidates the caches in this AssetManager according to the bitmask `diff`.
+     *
+     * @param diff The bitmask of changes generated by {@link Configuration#diff(Configuration)}.
+     * @see ActivityInfo.Config
+     */
+    private void invalidateCachesLocked(int diff) {
+        // TODO(adamlesinski): Currently there are no caches to invalidate in Java code.
+    }
+
+    /**
+     * Returns the set of ApkAssets loaded by this AssetManager. If the AssetManager is closed, this
+     * returns a 0-length array.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public @NonNull ApkAssets[] getApkAssets() {
+        synchronized (this) {
+            if (mOpen) {
+                return mApkAssets;
+            }
+        }
+        return sEmptyApkAssets;
+    }
+
+    /** @hide */
+    @TestApi
+    public @NonNull String[] getApkPaths() {
+        synchronized (this) {
+            if (mOpen) {
+                String[] paths = new String[mApkAssets.length];
+                final int count = mApkAssets.length;
+                for (int i = 0; i < count; i++) {
+                    paths[i] = mApkAssets[i].getAssetPath();
+                }
+                return paths;
+            }
+        }
+        return new String[0];
+    }
+
+    /**
+     * Returns a cookie for use with the other APIs of AssetManager.
+     * @return 0 if the path was not found, otherwise a positive integer cookie representing
+     * this path in the AssetManager.
+     * @hide
+     */
+    public int findCookieForPath(@NonNull String path) {
+        Objects.requireNonNull(path, "path");
+        synchronized (this) {
+            ensureValidLocked();
+            final int count = mApkAssets.length;
+            for (int i = 0; i < count; i++) {
+                if (path.equals(mApkAssets[i].getAssetPath())) {
+                    return i + 1;
+                }
+            }
+        }
+        return 0;
+    }
+
+    /**
+     * @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)}
+     * @hide
+     */
+    @Deprecated
+    @UnsupportedAppUsage
+    public int addAssetPath(String path) {
+        return addAssetPathInternal(path, false /*overlay*/, false /*appAsLib*/);
+    }
+
+    /**
+     * @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)}
+     * @hide
+     */
+    @Deprecated
+    @UnsupportedAppUsage
+    public int addAssetPathAsSharedLibrary(String path) {
+        return addAssetPathInternal(path, false /*overlay*/, true /*appAsLib*/);
+    }
+
+    /**
+     * @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)}
+     * @hide
+     */
+    @Deprecated
+    @UnsupportedAppUsage
+    public int addOverlayPath(String path) {
+        return addAssetPathInternal(path, true /*overlay*/, false /*appAsLib*/);
+    }
+
+    private int addAssetPathInternal(String path, boolean overlay, boolean appAsLib) {
+        Objects.requireNonNull(path, "path");
+        synchronized (this) {
+            ensureOpenLocked();
+            final int count = mApkAssets.length;
+
+            // See if we already have it loaded.
+            for (int i = 0; i < count; i++) {
+                if (mApkAssets[i].getAssetPath().equals(path)) {
+                    return i + 1;
+                }
+            }
+
+            final ApkAssets assets;
+            try {
+                if (overlay) {
+                    // TODO(b/70343104): This hardcoded path will be removed once
+                    // addAssetPathInternal is deleted.
+                    final String idmapPath = "/data/resource-cache/"
+                            + path.substring(1).replace('/', '@')
+                            + "@idmap";
+                    assets = ApkAssets.loadOverlayFromPath(idmapPath, 0 /* flags */);
+                } else {
+                    assets = ApkAssets.loadFromPath(path,
+                            appAsLib ? ApkAssets.PROPERTY_DYNAMIC : 0);
+                }
+            } catch (IOException e) {
+                return 0;
+            }
+
+            mApkAssets = Arrays.copyOf(mApkAssets, count + 1);
+            mApkAssets[count] = assets;
+            nativeSetApkAssets(mObject, mApkAssets, true);
+            invalidateCachesLocked(-1);
+            return count + 1;
+        }
+    }
+
+    /** @hide */
+    @NonNull
+    public List<ResourcesLoader> getLoaders() {
+        return mLoaders == null ? Collections.emptyList() : Arrays.asList(mLoaders);
+    }
+
+    /**
+     * Ensures that the native implementation has not been destroyed.
+     * The AssetManager may have been closed, but references to it still exist
+     * and therefore the native implementation is not destroyed.
+     */
+    @GuardedBy("this")
+    private void ensureValidLocked() {
+        if (mObject == 0) {
+            throw new RuntimeException("AssetManager has been destroyed");
+        }
+    }
+
+    /**
+     * Ensures that the AssetManager has not been explicitly closed. If this method passes,
+     * then this implies that ensureValidLocked() also passes.
+     */
+    @GuardedBy("this")
+    private void ensureOpenLocked() {
+        // If mOpen is true, this implies that mObject != 0.
+        if (!mOpen) {
+            throw new RuntimeException("AssetManager has been closed");
+        }
+    }
+
+    /**
+     * Populates {@code outValue} with the data associated a particular
+     * resource identifier for the current configuration.
+     *
+     * @param resId the resource identifier to load
+     * @param densityDpi the density bucket for which to load the resource
+     * @param outValue the typed value in which to put the data
+     * @param resolveRefs {@code true} to resolve references, {@code false}
+     *                    to leave them unresolved
+     * @return {@code true} if the data was loaded into {@code outValue},
+     *         {@code false} otherwise
+     */
+    @UnsupportedAppUsage
+    boolean getResourceValue(@AnyRes int resId, int densityDpi, @NonNull TypedValue outValue,
+            boolean resolveRefs) {
+        Objects.requireNonNull(outValue, "outValue");
+        synchronized (this) {
+            ensureValidLocked();
+            final int cookie = nativeGetResourceValue(
+                    mObject, resId, (short) densityDpi, outValue, resolveRefs);
+            if (cookie <= 0) {
+                return false;
+            }
+
+            // Convert the changing configurations flags populated by native code.
+            outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava(
+                    outValue.changingConfigurations);
+
+            if (outValue.type == TypedValue.TYPE_STRING) {
+                outValue.string = getPooledStringForCookie(cookie, outValue.data);
+            }
+            return true;
+        }
+    }
+
+    /**
+     * Retrieves the string value associated with a particular resource
+     * identifier for the current configuration.
+     *
+     * @param resId the resource identifier to load
+     * @return the string value, or {@code null}
+     */
+    @UnsupportedAppUsage
+    @Nullable CharSequence getResourceText(@StringRes int resId) {
+        synchronized (this) {
+            final TypedValue outValue = mValue;
+            if (getResourceValue(resId, 0, outValue, true)) {
+                return outValue.coerceToString();
+            }
+            return null;
+        }
+    }
+
+    /**
+     * Retrieves the string value associated with a particular resource
+     * identifier for the current configuration.
+     *
+     * @param resId the resource identifier to load
+     * @param bagEntryId the index into the bag to load
+     * @return the string value, or {@code null}
+     */
+    @UnsupportedAppUsage
+    @Nullable CharSequence getResourceBagText(@StringRes int resId, int bagEntryId) {
+        synchronized (this) {
+            ensureValidLocked();
+            final TypedValue outValue = mValue;
+            final int cookie = nativeGetResourceBagValue(mObject, resId, bagEntryId, outValue);
+            if (cookie <= 0) {
+                return null;
+            }
+
+            // Convert the changing configurations flags populated by native code.
+            outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava(
+                    outValue.changingConfigurations);
+
+            if (outValue.type == TypedValue.TYPE_STRING) {
+                return getPooledStringForCookie(cookie, outValue.data);
+            }
+            return outValue.coerceToString();
+        }
+    }
+
+    int getResourceArraySize(@ArrayRes int resId) {
+        synchronized (this) {
+            ensureValidLocked();
+            return nativeGetResourceArraySize(mObject, resId);
+        }
+    }
+
+    /**
+     * Populates `outData` with array elements of `resId`. `outData` is normally
+     * used with
+     * {@link TypedArray}.
+     *
+     * Each logical element in `outData` is {@link TypedArray#STYLE_NUM_ENTRIES}
+     * long,
+     * with the indices of the data representing the type, value, asset cookie,
+     * resource ID,
+     * configuration change mask, and density of the element.
+     *
+     * @param resId The resource ID of an array resource.
+     * @param outData The array to populate with data.
+     * @return The length of the array.
+     *
+     * @see TypedArray#STYLE_TYPE
+     * @see TypedArray#STYLE_DATA
+     * @see TypedArray#STYLE_ASSET_COOKIE
+     * @see TypedArray#STYLE_RESOURCE_ID
+     * @see TypedArray#STYLE_CHANGING_CONFIGURATIONS
+     * @see TypedArray#STYLE_DENSITY
+     */
+    int getResourceArray(@ArrayRes int resId, @NonNull int[] outData) {
+        Objects.requireNonNull(outData, "outData");
+        synchronized (this) {
+            ensureValidLocked();
+            return nativeGetResourceArray(mObject, resId, outData);
+        }
+    }
+
+    /**
+     * Retrieves the string array associated with a particular resource
+     * identifier for the current configuration.
+     *
+     * @param resId the resource identifier of the string array
+     * @return the string array, or {@code null}
+     */
+    @Nullable String[] getResourceStringArray(@ArrayRes int resId) {
+        synchronized (this) {
+            ensureValidLocked();
+            return nativeGetResourceStringArray(mObject, resId);
+        }
+    }
+
+    /**
+     * Retrieve the text array associated with a particular resource
+     * identifier.
+     *
+     * @param resId the resource id of the string array
+     */
+    @Nullable CharSequence[] getResourceTextArray(@ArrayRes int resId) {
+        synchronized (this) {
+            ensureValidLocked();
+            final int[] rawInfoArray = nativeGetResourceStringArrayInfo(mObject, resId);
+            if (rawInfoArray == null) {
+                return null;
+            }
+
+            final int rawInfoArrayLen = rawInfoArray.length;
+            final int infoArrayLen = rawInfoArrayLen / 2;
+            final CharSequence[] retArray = new CharSequence[infoArrayLen];
+            for (int i = 0, j = 0; i < rawInfoArrayLen; i = i + 2, j++) {
+                int cookie = rawInfoArray[i];
+                int index = rawInfoArray[i + 1];
+                retArray[j] = (index >= 0 && cookie > 0)
+                        ? getPooledStringForCookie(cookie, index) : null;
+            }
+            return retArray;
+        }
+    }
+
+    @Nullable int[] getResourceIntArray(@ArrayRes int resId) {
+        synchronized (this) {
+            ensureValidLocked();
+            return nativeGetResourceIntArray(mObject, resId);
+        }
+    }
+
+    /**
+     * Get the attributes for a style resource. These are the &lt;item&gt;
+     * elements in
+     * a &lt;style&gt; resource.
+     * @param resId The resource ID of the style
+     * @return An array of attribute IDs.
+     */
+    @AttrRes int[] getStyleAttributes(@StyleRes int resId) {
+        synchronized (this) {
+            ensureValidLocked();
+            return nativeGetStyleAttributes(mObject, resId);
+        }
+    }
+
+    /**
+     * Populates {@code outValue} with the data associated with a particular
+     * resource identifier for the current configuration. Resolves theme
+     * attributes against the specified theme.
+     *
+     * @param theme the native pointer of the theme
+     * @param resId the resource identifier to load
+     * @param outValue the typed value in which to put the data
+     * @param resolveRefs {@code true} to resolve references, {@code false}
+     *                    to leave them unresolved
+     * @return {@code true} if the data was loaded into {@code outValue},
+     *         {@code false} otherwise
+     */
+    boolean getThemeValue(long theme, @AnyRes int resId, @NonNull TypedValue outValue,
+            boolean resolveRefs) {
+        Objects.requireNonNull(outValue, "outValue");
+        synchronized (this) {
+            ensureValidLocked();
+            final int cookie = nativeThemeGetAttributeValue(mObject, theme, resId, outValue,
+                    resolveRefs);
+            if (cookie <= 0) {
+                return false;
+            }
+
+            // Convert the changing configurations flags populated by native code.
+            outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava(
+                    outValue.changingConfigurations);
+
+            if (outValue.type == TypedValue.TYPE_STRING) {
+                outValue.string = getPooledStringForCookie(cookie, outValue.data);
+            }
+            return true;
+        }
+    }
+
+    void dumpTheme(long theme, int priority, String tag, String prefix) {
+        synchronized (this) {
+            ensureValidLocked();
+            nativeThemeDump(mObject, theme, priority, tag, prefix);
+        }
+    }
+
+    @UnsupportedAppUsage
+    @Nullable String getResourceName(@AnyRes int resId) {
+        synchronized (this) {
+            ensureValidLocked();
+            return nativeGetResourceName(mObject, resId);
+        }
+    }
+
+    @UnsupportedAppUsage
+    @Nullable String getResourcePackageName(@AnyRes int resId) {
+        synchronized (this) {
+            ensureValidLocked();
+            return nativeGetResourcePackageName(mObject, resId);
+        }
+    }
+
+    @UnsupportedAppUsage
+    @Nullable String getResourceTypeName(@AnyRes int resId) {
+        synchronized (this) {
+            ensureValidLocked();
+            return nativeGetResourceTypeName(mObject, resId);
+        }
+    }
+
+    @UnsupportedAppUsage
+    @Nullable String getResourceEntryName(@AnyRes int resId) {
+        synchronized (this) {
+            ensureValidLocked();
+            return nativeGetResourceEntryName(mObject, resId);
+        }
+    }
+
+    @UnsupportedAppUsage
+    @AnyRes int getResourceIdentifier(@NonNull String name, @Nullable String defType,
+            @Nullable String defPackage) {
+        synchronized (this) {
+            ensureValidLocked();
+            // name is checked in JNI.
+            return nativeGetResourceIdentifier(mObject, name, defType, defPackage);
+        }
+    }
+
+    /**
+     * Enable resource resolution logging to track the steps taken to resolve the last resource
+     * entry retrieved. Stores the configuration and package names for each step.
+     *
+     * Default disabled.
+     *
+     * @param enabled Boolean indicating whether to enable or disable logging.
+     *
+     * @hide
+     */
+    @TestApi
+    public void setResourceResolutionLoggingEnabled(boolean enabled) {
+        synchronized (this) {
+            ensureValidLocked();
+            nativeSetResourceResolutionLoggingEnabled(mObject, enabled);
+        }
+    }
+
+    /**
+     * Retrieve the last resource resolution path logged.
+     *
+     * @return Formatted string containing last resource ID/name and steps taken to resolve final
+     * entry, including configuration and package names.
+     *
+     * @hide
+     */
+    @TestApi
+    public @Nullable String getLastResourceResolution() {
+        synchronized (this) {
+            ensureValidLocked();
+            return nativeGetLastResourceResolution(mObject);
+        }
+    }
+
+    /**
+     * Returns whether the {@code resources.arsc} of any loaded apk assets is allocated in RAM
+     * (not mmapped).
+     *
+     * @hide
+     */
+    public boolean containsAllocatedTable() {
+        synchronized (this) {
+            ensureValidLocked();
+            return nativeContainsAllocatedTable(mObject);
+        }
+    }
+
+    CharSequence getPooledStringForCookie(int cookie, int id) {
+        // Cookies map to ApkAssets starting at 1.
+        return getApkAssets()[cookie - 1].getStringFromPool(id);
+    }
+
+    /**
+     * Open an asset using ACCESS_STREAMING mode.  This provides access to
+     * files that have been bundled with an application as assets -- that is,
+     * files placed in to the "assets" directory.
+     * 
+     * @param fileName The name of the asset to open.  This name can be hierarchical.
+     * 
+     * @see #open(String, int)
+     * @see #list
+     */
+    public @NonNull InputStream open(@NonNull String fileName) throws IOException {
+        return open(fileName, ACCESS_STREAMING);
+    }
+
+    /**
+     * Open an asset using an explicit access mode, returning an InputStream to
+     * read its contents.  This provides access to files that have been bundled
+     * with an application as assets -- that is, files placed in to the
+     * "assets" directory.
+     * 
+     * @param fileName The name of the asset to open.  This name can be hierarchical.
+     * @param accessMode Desired access mode for retrieving the data.
+     * 
+     * @see #ACCESS_UNKNOWN
+     * @see #ACCESS_STREAMING
+     * @see #ACCESS_RANDOM
+     * @see #ACCESS_BUFFER
+     * @see #open(String)
+     * @see #list
+     */
+    public @NonNull InputStream open(@NonNull String fileName, int accessMode) throws IOException {
+        Objects.requireNonNull(fileName, "fileName");
+        synchronized (this) {
+            ensureOpenLocked();
+            final long asset = nativeOpenAsset(mObject, fileName, accessMode);
+            if (asset == 0) {
+                throw new FileNotFoundException("Asset file: " + fileName);
+            }
+            final AssetInputStream assetInputStream = new AssetInputStream(asset);
+            incRefsLocked(assetInputStream.hashCode());
+            return assetInputStream;
+        }
+    }
+
+    /**
+     * Open an uncompressed asset by mmapping it and returning an {@link AssetFileDescriptor}.
+     * This provides access to files that have been bundled with an application as assets -- that
+     * is, files placed in to the "assets" directory.
+     *
+     * The asset must be uncompressed, or an exception will be thrown.
+     *
+     * @param fileName The name of the asset to open.  This name can be hierarchical.
+     * @return An open AssetFileDescriptor.
+     */
+    public @NonNull AssetFileDescriptor openFd(@NonNull String fileName) throws IOException {
+        Objects.requireNonNull(fileName, "fileName");
+        synchronized (this) {
+            ensureOpenLocked();
+            final ParcelFileDescriptor pfd = nativeOpenAssetFd(mObject, fileName, mOffsets);
+            if (pfd == null) {
+                throw new FileNotFoundException("Asset file: " + fileName);
+            }
+            return new AssetFileDescriptor(pfd, mOffsets[0], mOffsets[1]);
+        }
+    }
+
+    /**
+     * Return a String array of all the assets at the given path.
+     * 
+     * @param path A relative path within the assets, i.e., "docs/home.html".
+     * 
+     * @return String[] Array of strings, one for each asset.  These file
+     *         names are relative to 'path'.  You can open the file by
+     *         concatenating 'path' and a name in the returned string (via
+     *         File) and passing that to open().
+     * 
+     * @see #open
+     */
+    public @Nullable String[] list(@NonNull String path) throws IOException {
+        Objects.requireNonNull(path, "path");
+        synchronized (this) {
+            ensureValidLocked();
+            return nativeList(mObject, path);
+        }
+    }
+
+    /**
+     * Open a non-asset file as an asset using ACCESS_STREAMING mode.  This
+     * provides direct access to all of the files included in an application
+     * package (not only its assets).  Applications should not normally use
+     * this.
+     *
+     * @param fileName Name of the asset to retrieve.
+     *
+     * @see #open(String)
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public @NonNull InputStream openNonAsset(@NonNull String fileName) throws IOException {
+        return openNonAsset(0, fileName, ACCESS_STREAMING);
+    }
+
+    /**
+     * Open a non-asset file as an asset using a specific access mode.  This
+     * provides direct access to all of the files included in an application
+     * package (not only its assets).  Applications should not normally use
+     * this.
+     *
+     * @param fileName Name of the asset to retrieve.
+     * @param accessMode Desired access mode for retrieving the data.
+     *
+     * @see #ACCESS_UNKNOWN
+     * @see #ACCESS_STREAMING
+     * @see #ACCESS_RANDOM
+     * @see #ACCESS_BUFFER
+     * @see #open(String, int)
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public @NonNull InputStream openNonAsset(@NonNull String fileName, int accessMode)
+            throws IOException {
+        return openNonAsset(0, fileName, accessMode);
+    }
+
+    /**
+     * Open a non-asset in a specified package.  Not for use by applications.
+     *
+     * @param cookie Identifier of the package to be opened.
+     * @param fileName Name of the asset to retrieve.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public @NonNull InputStream openNonAsset(int cookie, @NonNull String fileName)
+            throws IOException {
+        return openNonAsset(cookie, fileName, ACCESS_STREAMING);
+    }
+
+    /**
+     * Open a non-asset in a specified package.  Not for use by applications.
+     *
+     * @param cookie Identifier of the package to be opened.
+     * @param fileName Name of the asset to retrieve.
+     * @param accessMode Desired access mode for retrieving the data.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public @NonNull InputStream openNonAsset(int cookie, @NonNull String fileName, int accessMode)
+            throws IOException {
+        Objects.requireNonNull(fileName, "fileName");
+        synchronized (this) {
+            ensureOpenLocked();
+            final long asset = nativeOpenNonAsset(mObject, cookie, fileName, accessMode);
+            if (asset == 0) {
+                throw new FileNotFoundException("Asset absolute file: " + fileName);
+            }
+            final AssetInputStream assetInputStream = new AssetInputStream(asset);
+            incRefsLocked(assetInputStream.hashCode());
+            return assetInputStream;
+        }
+    }
+
+    /**
+     * Open a non-asset as an asset by mmapping it and returning an {@link AssetFileDescriptor}.
+     * This provides direct access to all of the files included in an application
+     * package (not only its assets).  Applications should not normally use this.
+     *
+     * The asset must not be compressed, or an exception will be thrown.
+     *
+     * @param fileName Name of the asset to retrieve.
+     */
+    public @NonNull AssetFileDescriptor openNonAssetFd(@NonNull String fileName)
+            throws IOException {
+        return openNonAssetFd(0, fileName);
+    }
+
+    /**
+     * Open a non-asset as an asset by mmapping it and returning an {@link AssetFileDescriptor}.
+     * This provides direct access to all of the files included in an application
+     * package (not only its assets).  Applications should not normally use this.
+     *
+     * The asset must not be compressed, or an exception will be thrown.
+     *
+     * @param cookie Identifier of the package to be opened.
+     * @param fileName Name of the asset to retrieve.
+     */
+    public @NonNull AssetFileDescriptor openNonAssetFd(int cookie, @NonNull String fileName)
+            throws IOException {
+        Objects.requireNonNull(fileName, "fileName");
+        synchronized (this) {
+            ensureOpenLocked();
+            final ParcelFileDescriptor pfd =
+                    nativeOpenNonAssetFd(mObject, cookie, fileName, mOffsets);
+            if (pfd == null) {
+                throw new FileNotFoundException("Asset absolute file: " + fileName);
+            }
+            return new AssetFileDescriptor(pfd, mOffsets[0], mOffsets[1]);
+        }
+    }
+    
+    /**
+     * Retrieve a parser for a compiled XML file.
+     * 
+     * @param fileName The name of the file to retrieve.
+     */
+    public @NonNull XmlResourceParser openXmlResourceParser(@NonNull String fileName)
+            throws IOException {
+        return openXmlResourceParser(0, fileName);
+    }
+    
+    /**
+     * Retrieve a parser for a compiled XML file.
+     * 
+     * @param cookie Identifier of the package to be opened.
+     * @param fileName The name of the file to retrieve.
+     */
+    public @NonNull XmlResourceParser openXmlResourceParser(int cookie, @NonNull String fileName)
+            throws IOException {
+        try (XmlBlock block = openXmlBlockAsset(cookie, fileName)) {
+            XmlResourceParser parser = block.newParser();
+            // If openXmlBlockAsset doesn't throw, it will always return an XmlBlock object with
+            // a valid native pointer, which makes newParser always return non-null. But let's
+            // be paranoid.
+            if (parser == null) {
+                throw new AssertionError("block.newParser() returned a null parser");
+            }
+            return parser;
+        }
+    }
+
+    /**
+     * Retrieve a non-asset as a compiled XML file.  Not for use by applications.
+     * 
+     * @param fileName The name of the file to retrieve.
+     * @hide
+     */
+    @NonNull XmlBlock openXmlBlockAsset(@NonNull String fileName) throws IOException {
+        return openXmlBlockAsset(0, fileName);
+    }
+
+    /**
+     * Retrieve a non-asset as a compiled XML file.  Not for use by
+     * applications.
+     * 
+     * @param cookie Identifier of the package to be opened.
+     * @param fileName Name of the asset to retrieve.
+     * @hide
+     */
+    @NonNull XmlBlock openXmlBlockAsset(int cookie, @NonNull String fileName) throws IOException {
+        Objects.requireNonNull(fileName, "fileName");
+        synchronized (this) {
+            ensureOpenLocked();
+
+            final long xmlBlock = nativeOpenXmlAsset(mObject, cookie, fileName);
+            if (xmlBlock == 0) {
+                throw new FileNotFoundException("Asset XML file: " + fileName);
+            }
+            final XmlBlock block = new XmlBlock(this, xmlBlock);
+            incRefsLocked(block.hashCode());
+            return block;
+        }
+    }
+
+    void xmlBlockGone(int id) {
+        synchronized (this) {
+            decRefsLocked(id);
+        }
+    }
+
+    @UnsupportedAppUsage
+    void applyStyle(long themePtr, @AttrRes int defStyleAttr, @StyleRes int defStyleRes,
+            @Nullable XmlBlock.Parser parser, @NonNull int[] inAttrs, long outValuesAddress,
+            long outIndicesAddress) {
+        Objects.requireNonNull(inAttrs, "inAttrs");
+        synchronized (this) {
+            // Need to synchronize on AssetManager because we will be accessing
+            // the native implementation of AssetManager.
+            ensureValidLocked();
+            nativeApplyStyle(mObject, themePtr, defStyleAttr, defStyleRes,
+                    parser != null ? parser.mParseState : 0, inAttrs, outValuesAddress,
+                    outIndicesAddress);
+        }
+    }
+
+    int[] getAttributeResolutionStack(long themePtr, @AttrRes int defStyleAttr,
+            @StyleRes int defStyleRes, @StyleRes int xmlStyle) {
+        synchronized (this) {
+            return nativeAttributeResolutionStack(
+                    mObject, themePtr, xmlStyle, defStyleAttr, defStyleRes);
+        }
+    }
+
+    @UnsupportedAppUsage
+    boolean resolveAttrs(long themePtr, @AttrRes int defStyleAttr, @StyleRes int defStyleRes,
+            @Nullable int[] inValues, @NonNull int[] inAttrs, @NonNull int[] outValues,
+            @NonNull int[] outIndices) {
+        Objects.requireNonNull(inAttrs, "inAttrs");
+        Objects.requireNonNull(outValues, "outValues");
+        Objects.requireNonNull(outIndices, "outIndices");
+        synchronized (this) {
+            // Need to synchronize on AssetManager because we will be accessing
+            // the native implementation of AssetManager.
+            ensureValidLocked();
+            return nativeResolveAttrs(mObject,
+                    themePtr, defStyleAttr, defStyleRes, inValues, inAttrs, outValues, outIndices);
+        }
+    }
+
+    @UnsupportedAppUsage
+    boolean retrieveAttributes(@NonNull XmlBlock.Parser parser, @NonNull int[] inAttrs,
+            @NonNull int[] outValues, @NonNull int[] outIndices) {
+        Objects.requireNonNull(parser, "parser");
+        Objects.requireNonNull(inAttrs, "inAttrs");
+        Objects.requireNonNull(outValues, "outValues");
+        Objects.requireNonNull(outIndices, "outIndices");
+        synchronized (this) {
+            // Need to synchronize on AssetManager because we will be accessing
+            // the native implementation of AssetManager.
+            ensureValidLocked();
+            return nativeRetrieveAttributes(
+                    mObject, parser.mParseState, inAttrs, outValues, outIndices);
+        }
+    }
+
+    @UnsupportedAppUsage
+    long createTheme() {
+        synchronized (this) {
+            ensureValidLocked();
+            long themePtr = nativeThemeCreate(mObject);
+            incRefsLocked(themePtr);
+            return themePtr;
+        }
+    }
+
+    void releaseTheme(long themePtr) {
+        synchronized (this) {
+            nativeThemeDestroy(themePtr);
+            decRefsLocked(themePtr);
+        }
+    }
+
+    void applyStyleToTheme(long themePtr, @StyleRes int resId, boolean force) {
+        synchronized (this) {
+            // Need to synchronize on AssetManager because we will be accessing
+            // the native implementation of AssetManager.
+            ensureValidLocked();
+            nativeThemeApplyStyle(mObject, themePtr, resId, force);
+        }
+    }
+
+    @UnsupportedAppUsage
+    void setThemeTo(long dstThemePtr, @NonNull AssetManager srcAssetManager, long srcThemePtr) {
+        synchronized (this) {
+            ensureValidLocked();
+            synchronized (srcAssetManager) {
+                srcAssetManager.ensureValidLocked();
+                nativeThemeCopy(mObject, dstThemePtr, srcAssetManager.mObject, srcThemePtr);
+            }
+        }
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        if (DEBUG_REFS && mNumRefs != 0) {
+            Log.w(TAG, "AssetManager " + this + " finalized with non-zero refs: " + mNumRefs);
+            if (mRefStacks != null) {
+                for (RuntimeException e : mRefStacks.values()) {
+                    Log.w(TAG, "Reference from here", e);
+                }
+            }
+        }
+
+        synchronized (this) {
+            if (mObject != 0) {
+                nativeDestroy(mObject);
+                mObject = 0;
+            }
+        }
+    }
+
+    /* No Locking is needed for AssetInputStream because an AssetInputStream is not-thread
+    safe and it does not rely on AssetManager once it has been created. It completely owns the
+    underlying Asset. */
+    public final class AssetInputStream extends InputStream {
+        private long mAssetNativePtr;
+        private long mLength;
+        private long mMarkPos;
+
+        /**
+         * @hide
+         */
+        @UnsupportedAppUsage
+        public final int getAssetInt() {
+            throw new UnsupportedOperationException();
+        }
+
+        /**
+         * @hide
+         */
+        @UnsupportedAppUsage
+        public final long getNativeAsset() {
+            return mAssetNativePtr;
+        }
+
+        private AssetInputStream(long assetNativePtr) {
+            mAssetNativePtr = assetNativePtr;
+            mLength = nativeAssetGetLength(assetNativePtr);
+        }
+
+        @Override
+        public final int read() throws IOException {
+            ensureOpen();
+            return nativeAssetReadChar(mAssetNativePtr);
+        }
+
+        @Override
+        public final int read(@NonNull byte[] b) throws IOException {
+            ensureOpen();
+            Objects.requireNonNull(b, "b");
+            return nativeAssetRead(mAssetNativePtr, b, 0, b.length);
+        }
+
+        @Override
+        public final int read(@NonNull byte[] b, int off, int len) throws IOException {
+            ensureOpen();
+            Objects.requireNonNull(b, "b");
+            return nativeAssetRead(mAssetNativePtr, b, off, len);
+        }
+
+        @Override
+        public final long skip(long n) throws IOException {
+            ensureOpen();
+            long pos = nativeAssetSeek(mAssetNativePtr, 0, 0);
+            if ((pos + n) > mLength) {
+                n = mLength - pos;
+            }
+            if (n > 0) {
+                nativeAssetSeek(mAssetNativePtr, n, 0);
+            }
+            return n;
+        }
+
+        @Override
+        public final int available() throws IOException {
+            ensureOpen();
+            final long len = nativeAssetGetRemainingLength(mAssetNativePtr);
+            return len > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) len;
+        }
+
+        @Override
+        public final boolean markSupported() {
+            return true;
+        }
+
+        @Override
+        public final void mark(int readlimit) {
+            ensureOpen();
+            mMarkPos = nativeAssetSeek(mAssetNativePtr, 0, 0);
+        }
+
+        @Override
+        public final void reset() throws IOException {
+            ensureOpen();
+            nativeAssetSeek(mAssetNativePtr, mMarkPos, -1);
+        }
+
+        @Override
+        public final void close() throws IOException {
+            if (mAssetNativePtr != 0) {
+                nativeAssetDestroy(mAssetNativePtr);
+                mAssetNativePtr = 0;
+
+                synchronized (AssetManager.this) {
+                    decRefsLocked(hashCode());
+                }
+            }
+        }
+
+        @Override
+        protected void finalize() throws Throwable {
+            close();
+        }
+
+        private void ensureOpen() {
+            if (mAssetNativePtr == 0) {
+                throw new IllegalStateException("AssetInputStream is closed");
+            }
+        }
+    }
+
+    /**
+     * Determine whether the state in this asset manager is up-to-date with
+     * the files on the filesystem.  If false is returned, you need to
+     * instantiate a new AssetManager class to see the new data.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public boolean isUpToDate() {
+        synchronized (this) {
+            if (!mOpen) {
+                return false;
+            }
+
+            for (ApkAssets apkAssets : mApkAssets) {
+                if (!apkAssets.isUpToDate()) {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+    }
+
+    /**
+     * Get the locales that this asset manager contains data for.
+     *
+     * <p>On SDK 21 (Android 5.0: Lollipop) and above, Locale strings are valid
+     * <a href="https://tools.ietf.org/html/bcp47">BCP-47</a> language tags and can be
+     * parsed using {@link Locale#forLanguageTag(String)}.
+     *
+     * <p>On SDK 20 (Android 4.4W: Kitkat for watches) and below, locale strings
+     * are of the form {@code ll_CC} where {@code ll} is a two letter language code,
+     * and {@code CC} is a two letter country code.
+     */
+    public String[] getLocales() {
+        synchronized (this) {
+            ensureValidLocked();
+            return nativeGetLocales(mObject, false /*excludeSystem*/);
+        }
+    }
+
+    /**
+     * Same as getLocales(), except that locales that are only provided by the system (i.e. those
+     * present in framework-res.apk or its overlays) will not be listed.
+     *
+     * For example, if the "system" assets support English, French, and German, and the additional
+     * assets support Cherokee and French, getLocales() would return
+     * [Cherokee, English, French, German], while getNonSystemLocales() would return
+     * [Cherokee, French].
+     * @hide
+     */
+    public String[] getNonSystemLocales() {
+        synchronized (this) {
+            ensureValidLocked();
+            return nativeGetLocales(mObject, true /*excludeSystem*/);
+        }
+    }
+
+    /**
+     * @hide
+     */
+    Configuration[] getSizeConfigurations() {
+        synchronized (this) {
+            ensureValidLocked();
+            return nativeGetSizeConfigurations(mObject);
+        }
+    }
+
+    /**
+     * Change the configuration used when retrieving resources.  Not for use by
+     * applications.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public void setConfiguration(int mcc, int mnc, @Nullable String locale, int orientation,
+            int touchscreen, int density, int keyboard, int keyboardHidden, int navigation,
+            int screenWidth, int screenHeight, int smallestScreenWidthDp, int screenWidthDp,
+            int screenHeightDp, int screenLayout, int uiMode, int colorMode, int majorVersion) {
+        synchronized (this) {
+            ensureValidLocked();
+            nativeSetConfiguration(mObject, mcc, mnc, locale, orientation, touchscreen, density,
+                    keyboard, keyboardHidden, navigation, screenWidth, screenHeight,
+                    smallestScreenWidthDp, screenWidthDp, screenHeightDp, screenLayout, uiMode,
+                    colorMode, majorVersion);
+        }
+    }
+
+    /**
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public SparseArray<String> getAssignedPackageIdentifiers() {
+        return getAssignedPackageIdentifiers(true, true);
+    }
+
+    /**
+     * @hide
+     */
+    public SparseArray<String> getAssignedPackageIdentifiers(boolean includeOverlays,
+            boolean includeLoaders) {
+        synchronized (this) {
+            ensureValidLocked();
+            return nativeGetAssignedPackageIdentifiers(mObject, includeOverlays, includeLoaders);
+        }
+    }
+
+    /**
+     * @hide
+     */
+    @GuardedBy("this")
+    public @Nullable Map<String, String> getOverlayableMap(String packageName) {
+        synchronized (this) {
+            ensureValidLocked();
+            return nativeGetOverlayableMap(mObject, packageName);
+        }
+    }
+
+    /**
+     * @hide
+     */
+    @TestApi
+    @GuardedBy("this")
+    public @Nullable String getOverlayablesToString(String packageName) {
+        synchronized (this) {
+            ensureValidLocked();
+            return nativeGetOverlayablesToString(mObject, packageName);
+        }
+    }
+
+    @GuardedBy("this")
+    private void incRefsLocked(long id) {
+        if (DEBUG_REFS) {
+            if (mRefStacks == null) {
+                mRefStacks = new HashMap<>();
+            }
+            RuntimeException ex = new RuntimeException();
+            ex.fillInStackTrace();
+            mRefStacks.put(id, ex);
+        }
+        mNumRefs++;
+    }
+
+    @GuardedBy("this")
+    private void decRefsLocked(long id) {
+        if (DEBUG_REFS && mRefStacks != null) {
+            mRefStacks.remove(id);
+        }
+        mNumRefs--;
+        if (mNumRefs == 0 && mObject != 0) {
+            nativeDestroy(mObject);
+            mObject = 0;
+            mApkAssets = sEmptyApkAssets;
+        }
+    }
+
+    // AssetManager setup native methods.
+    private static native long nativeCreate();
+    private static native void nativeDestroy(long ptr);
+    private static native void nativeSetApkAssets(long ptr, @NonNull ApkAssets[] apkAssets,
+            boolean invalidateCaches);
+    private static native void nativeSetConfiguration(long ptr, int mcc, int mnc,
+            @Nullable String locale, int orientation, int touchscreen, int density, int keyboard,
+            int keyboardHidden, int navigation, int screenWidth, int screenHeight,
+            int smallestScreenWidthDp, int screenWidthDp, int screenHeightDp, int screenLayout,
+            int uiMode, int colorMode, int majorVersion);
+    private static native @NonNull SparseArray<String> nativeGetAssignedPackageIdentifiers(
+            long ptr, boolean includeOverlays, boolean includeLoaders);
+
+    // File native methods.
+    private static native boolean nativeContainsAllocatedTable(long ptr);
+    private static native @Nullable String[] nativeList(long ptr, @NonNull String path)
+            throws IOException;
+    private static native long nativeOpenAsset(long ptr, @NonNull String fileName, int accessMode);
+    private static native @Nullable ParcelFileDescriptor nativeOpenAssetFd(long ptr,
+            @NonNull String fileName, long[] outOffsets) throws IOException;
+    private static native long nativeOpenNonAsset(long ptr, int cookie, @NonNull String fileName,
+            int accessMode);
+    private static native @Nullable ParcelFileDescriptor nativeOpenNonAssetFd(long ptr, int cookie,
+            @NonNull String fileName, @NonNull long[] outOffsets) throws IOException;
+    private static native long nativeOpenXmlAsset(long ptr, int cookie, @NonNull String fileName);
+    private static native long nativeOpenXmlAssetFd(long ptr, int cookie,
+            @NonNull FileDescriptor fileDescriptor);
+
+    // Primitive resource native methods.
+    private static native int nativeGetResourceValue(long ptr, @AnyRes int resId, short density,
+            @NonNull TypedValue outValue, boolean resolveReferences);
+    private static native int nativeGetResourceBagValue(long ptr, @AnyRes int resId, int bagEntryId,
+            @NonNull TypedValue outValue);
+
+    private static native @Nullable @AttrRes int[] nativeGetStyleAttributes(long ptr,
+            @StyleRes int resId);
+    private static native @Nullable String[] nativeGetResourceStringArray(long ptr,
+            @ArrayRes int resId);
+    private static native @Nullable int[] nativeGetResourceStringArrayInfo(long ptr,
+            @ArrayRes int resId);
+    private static native @Nullable int[] nativeGetResourceIntArray(long ptr, @ArrayRes int resId);
+    private static native int nativeGetResourceArraySize(long ptr, @ArrayRes int resId);
+    private static native int nativeGetResourceArray(long ptr, @ArrayRes int resId,
+            @NonNull int[] outValues);
+
+    // Resource name/ID native methods.
+    private static native @AnyRes int nativeGetResourceIdentifier(long ptr, @NonNull String name,
+            @Nullable String defType, @Nullable String defPackage);
+    private static native @Nullable String nativeGetResourceName(long ptr, @AnyRes int resid);
+    private static native @Nullable String nativeGetResourcePackageName(long ptr,
+            @AnyRes int resid);
+    private static native @Nullable String nativeGetResourceTypeName(long ptr, @AnyRes int resid);
+    private static native @Nullable String nativeGetResourceEntryName(long ptr, @AnyRes int resid);
+    private static native @Nullable String[] nativeGetLocales(long ptr, boolean excludeSystem);
+    private static native @Nullable Configuration[] nativeGetSizeConfigurations(long ptr);
+    private static native void nativeSetResourceResolutionLoggingEnabled(long ptr, boolean enabled);
+    private static native @Nullable String nativeGetLastResourceResolution(long ptr);
+
+    // Style attribute retrieval native methods.
+    private static native int[] nativeAttributeResolutionStack(long ptr, long themePtr,
+            @StyleRes int xmlStyleRes, @AttrRes int defStyleAttr, @StyleRes int defStyleRes);
+    private static native void nativeApplyStyle(long ptr, long themePtr, @AttrRes int defStyleAttr,
+            @StyleRes int defStyleRes, long xmlParserPtr, @NonNull int[] inAttrs,
+            long outValuesAddress, long outIndicesAddress);
+    private static native boolean nativeResolveAttrs(long ptr, long themePtr,
+            @AttrRes int defStyleAttr, @StyleRes int defStyleRes, @Nullable int[] inValues,
+            @NonNull int[] inAttrs, @NonNull int[] outValues, @NonNull int[] outIndices);
+    private static native boolean nativeRetrieveAttributes(long ptr, long xmlParserPtr,
+            @NonNull int[] inAttrs, @NonNull int[] outValues, @NonNull int[] outIndices);
+
+    // Theme related native methods
+    private static native long nativeThemeCreate(long ptr);
+    private static native void nativeThemeDestroy(long themePtr);
+    private static native void nativeThemeApplyStyle(long ptr, long themePtr, @StyleRes int resId,
+            boolean force);
+    private static native void nativeThemeCopy(long dstAssetManagerPtr, long dstThemePtr,
+            long srcAssetManagerPtr, long srcThemePtr);
+    static native void nativeThemeClear(long themePtr);
+    private static native int nativeThemeGetAttributeValue(long ptr, long themePtr,
+            @AttrRes int resId, @NonNull TypedValue outValue, boolean resolve);
+    private static native void nativeThemeDump(long ptr, long themePtr, int priority, String tag,
+            String prefix);
+    static native @NativeConfig int nativeThemeGetChangingConfigurations(long themePtr);
+
+    // AssetInputStream related native methods.
+    private static native void nativeAssetDestroy(long assetPtr);
+    private static native int nativeAssetReadChar(long assetPtr);
+    private static native int nativeAssetRead(long assetPtr, byte[] b, int off, int len);
+    private static native long nativeAssetSeek(long assetPtr, long offset, int whence);
+    private static native long nativeAssetGetLength(long assetPtr);
+    private static native long nativeAssetGetRemainingLength(long assetPtr);
+
+    private static native String[] nativeCreateIdmapsForStaticOverlaysTargetingAndroid();
+    private static native @Nullable Map nativeGetOverlayableMap(long ptr,
+            @NonNull String packageName);
+    private static native @Nullable String nativeGetOverlayablesToString(long ptr,
+            @NonNull String packageName);
+
+    // Global debug native methods.
+    /**
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static native int getGlobalAssetCount();
+
+    /**
+     * @hide
+     */
+    public static native String getAssetAllocations();
+
+    /**
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static native int getGlobalAssetManagerCount();
+}
diff --git a/android/content/res/AssetManager_Delegate.java b/android/content/res/AssetManager_Delegate.java
new file mode 100644
index 0000000..c27df09
--- /dev/null
+++ b/android/content/res/AssetManager_Delegate.java
@@ -0,0 +1,78 @@
+/*
+ * 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.content.res;
+
+import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import android.util.SparseArray;
+
+/**
+ * Delegate used to provide implementation of a select few native methods of {@link AssetManager}
+ * <p/>
+ * Through the layoutlib_create tool, the original native methods of AssetManager have been replaced
+ * by calls to methods of the same name in this delegate class.
+ *
+ */
+public class AssetManager_Delegate {
+
+    // ---- delegate manager ----
+
+    private static final DelegateManager<AssetManager_Delegate> sManager =
+            new DelegateManager<>(AssetManager_Delegate.class);
+
+    // ---- delegate methods. ----
+
+    @LayoutlibDelegate
+    /*package*/ static long nativeCreate() {
+        AssetManager_Delegate delegate = new AssetManager_Delegate();
+        return sManager.addNewDelegate(delegate);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nativeDestroy(long ptr) {
+        sManager.removeJavaReferenceFor(ptr);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static long nativeThemeCreate(long ptr) {
+        return Resources_Theme_Delegate.getDelegateManager()
+                .addNewDelegate(new Resources_Theme_Delegate());
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nativeThemeDestroy(long theme) {
+        Resources_Theme_Delegate.getDelegateManager().removeJavaReferenceFor(theme);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static SparseArray<String> getAssignedPackageIdentifiers(AssetManager manager) {
+        return new SparseArray<>();
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static SparseArray<String> getAssignedPackageIdentifiers(AssetManager manager,
+            boolean includeOverlays, boolean includeLoaders) {
+        return new SparseArray<>();
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static String[] nativeCreateIdmapsForStaticOverlaysTargetingAndroid() {
+        // AssetManager requires this not to be null
+        return new String[0];
+    }
+}
diff --git a/android/content/res/BridgeAssetManager.java b/android/content/res/BridgeAssetManager.java
new file mode 100644
index 0000000..c9b7095
--- /dev/null
+++ b/android/content/res/BridgeAssetManager.java
@@ -0,0 +1,86 @@
+/*
+ * 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.content.res;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import com.android.ide.common.rendering.api.AssetRepository;
+import com.android.layoutlib.bridge.Bridge;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public class BridgeAssetManager extends AssetManager {
+    @Nullable private AssetRepository mAssetRepository;
+
+    /**
+     * This initializes the static field {@link AssetManager#sSystem} which is used
+     * by methods who get a global asset manager using {@link AssetManager#getSystem()}.
+     * <p/>
+     * They will end up using our bridge asset manager.
+     * <p/>
+     * {@link Bridge} calls this method after setting up a new bridge.
+     */
+    public static AssetManager initSystem() {
+        if (!(AssetManager.sSystem instanceof BridgeAssetManager)) {
+            // Note that AssetManager() creates a system AssetManager and we override it
+            // with our BridgeAssetManager.
+            AssetManager.sSystem = new BridgeAssetManager();
+        }
+        return AssetManager.sSystem;
+    }
+
+    /**
+     * Clears the static {@link AssetManager#sSystem} to make sure we don't leave objects
+     * around that would prevent us from unloading the library.
+     */
+    public static void clearSystem() {
+        AssetManager.sSystem = null;
+    }
+
+    public void setAssetRepository(@NonNull AssetRepository assetRepository) {
+        mAssetRepository = assetRepository;
+    }
+
+    /**
+     * Clears the AssetRepository reference.
+     */
+    public void releaseAssetRepository() {
+        mAssetRepository = null;
+    }
+
+    @NonNull
+    public AssetRepository getAssetRepository() {
+        if (mAssetRepository == null) {
+            throw new IllegalStateException("Asset repository is not set");
+        }
+        return mAssetRepository;
+    }
+
+    @Override
+    public InputStream open(String fileName, int accessMode) throws IOException {
+        return getAssetRepository().openAsset(fileName, accessMode);
+    }
+
+    @Override
+    public InputStream openNonAsset(int cookie, String fileName, int accessMode)
+            throws IOException {
+        return getAssetRepository().openNonAsset(cookie, fileName, accessMode);
+    }
+
+    public BridgeAssetManager() {
+    }
+}
diff --git a/android/content/res/BridgeTypedArray.java b/android/content/res/BridgeTypedArray.java
new file mode 100644
index 0000000..458e613
--- /dev/null
+++ b/android/content/res/BridgeTypedArray.java
@@ -0,0 +1,1034 @@
+/*
+ * 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.content.res;
+
+import com.android.ide.common.rendering.api.ArrayResourceValue;
+import com.android.ide.common.rendering.api.AttrResourceValue;
+import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.ide.common.rendering.api.RenderResources;
+import com.android.ide.common.rendering.api.ResourceNamespace;
+import com.android.ide.common.rendering.api.ResourceNamespace.Resolver;
+import com.android.ide.common.rendering.api.ResourceReference;
+import com.android.ide.common.rendering.api.ResourceValue;
+import com.android.ide.common.rendering.api.StyleResourceValue;
+import com.android.ide.common.rendering.api.TextResourceValue;
+import com.android.internal.util.XmlUtils;
+import com.android.layoutlib.bridge.Bridge;
+import com.android.layoutlib.bridge.android.BridgeContext;
+import com.android.layoutlib.bridge.android.UnresolvedResourceValue;
+import com.android.layoutlib.bridge.impl.ResourceHelper;
+import com.android.resources.ResourceType;
+import com.android.resources.ResourceUrl;
+
+import android.annotation.Nullable;
+import android.content.res.Resources.Theme;
+import android.graphics.Typeface;
+import android.graphics.Typeface_Accessor;
+import android.graphics.drawable.Drawable;
+import android.text.Html;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
+import android.view.LayoutInflater_Delegate;
+import android.view.ViewGroup.LayoutParams;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Map;
+
+import static android.text.Html.FROM_HTML_MODE_COMPACT;
+import static android.util.TypedValue.TYPE_ATTRIBUTE;
+import static android.util.TypedValue.TYPE_DIMENSION;
+import static android.util.TypedValue.TYPE_FLOAT;
+import static android.util.TypedValue.TYPE_INT_BOOLEAN;
+import static android.util.TypedValue.TYPE_INT_COLOR_ARGB4;
+import static android.util.TypedValue.TYPE_INT_COLOR_ARGB8;
+import static android.util.TypedValue.TYPE_INT_COLOR_RGB4;
+import static android.util.TypedValue.TYPE_INT_COLOR_RGB8;
+import static android.util.TypedValue.TYPE_INT_DEC;
+import static android.util.TypedValue.TYPE_INT_HEX;
+import static android.util.TypedValue.TYPE_NULL;
+import static android.util.TypedValue.TYPE_REFERENCE;
+import static android.util.TypedValue.TYPE_STRING;
+import static com.android.SdkConstants.PREFIX_RESOURCE_REF;
+import static com.android.SdkConstants.PREFIX_THEME_REF;
+import static com.android.ide.common.rendering.api.RenderResources.REFERENCE_EMPTY;
+import static com.android.ide.common.rendering.api.RenderResources.REFERENCE_NULL;
+import static com.android.ide.common.rendering.api.RenderResources.REFERENCE_UNDEFINED;
+
+/**
+ * Custom implementation of TypedArray to handle non compiled resources.
+ */
+public final class BridgeTypedArray extends TypedArray {
+
+    private final Resources mBridgeResources;
+    private final BridgeContext mContext;
+
+    private final int[] mResourceId;
+    private final ResourceValue[] mResourceData;
+    private final String[] mNames;
+    private final ResourceNamespace[] mNamespaces;
+
+    // Contains ids that are @empty. We still store null in mResourceData for that index, since we
+    // want to save on the check against empty, each time a resource value is requested.
+    @Nullable
+    private int[] mEmptyIds;
+
+    public BridgeTypedArray(Resources resources, BridgeContext context, int len) {
+        super(resources);
+        mBridgeResources = resources;
+        mContext = context;
+        mResourceId = new int[len];
+        mResourceData = new ResourceValue[len];
+        mNames = new String[len];
+        mNamespaces = new ResourceNamespace[len];
+    }
+
+    /**
+     * A bridge-specific method that sets a value in the type array
+     * @param index the index of the value in the TypedArray
+     * @param name the name of the attribute
+     * @param namespace namespace of the attribute
+     * @param resourceId the reference id of this resource
+     * @param value the value of the attribute
+     */
+    public void bridgeSetValue(int index, String name, ResourceNamespace namespace, int resourceId,
+            ResourceValue value) {
+        mResourceId[index] = resourceId;
+        mResourceData[index] = value;
+        mNames[index] = name;
+        mNamespaces[index] = namespace;
+    }
+
+    /**
+     * Seals the array after all calls to
+     * {@link #bridgeSetValue(int, String, ResourceNamespace, int, ResourceValue)} have been done.
+     * <p/>This allows to compute the list of non default values, permitting
+     * {@link #getIndexCount()} to return the proper value.
+     */
+    public void sealArray() {
+        // fills TypedArray.mIndices which is used to implement getIndexCount/getIndexAt
+        // first count the array size
+        int count = 0;
+        ArrayList<Integer> emptyIds = null;
+        for (int i = 0; i < mResourceData.length; i++) {
+            ResourceValue data = mResourceData[i];
+            if (data != null) {
+                String dataValue = data.getValue();
+                if (REFERENCE_NULL.equals(dataValue) || REFERENCE_UNDEFINED.equals(dataValue)) {
+                    mResourceData[i] = null;
+                } else if (REFERENCE_EMPTY.equals(dataValue)) {
+                    mResourceData[i] = null;
+                    if (emptyIds == null) {
+                        emptyIds = new ArrayList<>(4);
+                    }
+                    emptyIds.add(i);
+                } else {
+                    count++;
+                }
+            }
+        }
+
+        if (emptyIds != null) {
+            mEmptyIds = new int[emptyIds.size()];
+            for (int i = 0; i < emptyIds.size(); i++) {
+                mEmptyIds[i] = emptyIds.get(i);
+            }
+        }
+
+        // allocate the table with an extra to store the size
+        mIndices = new int[count+1];
+        mIndices[0] = count;
+
+        // fill the array with the indices.
+        int index = 1;
+        for (int i = 0 ; i < mResourceData.length ; i++) {
+            if (mResourceData[i] != null) {
+                mIndices[index++] = i;
+            }
+        }
+    }
+
+    /**
+     * Set the theme to be used for inflating drawables.
+     */
+    public void setTheme(Theme theme) {
+        mTheme = theme;
+    }
+
+    /**
+     * Return the number of values in this array.
+     */
+    @Override
+    public int length() {
+        return mResourceData.length;
+    }
+
+    /**
+     * Return the Resources object this array was loaded from.
+     */
+    @Override
+    public Resources getResources() {
+        return mBridgeResources;
+    }
+
+    /**
+     * Retrieve the styled string value for the attribute at <var>index</var>.
+     *
+     * @param index Index of attribute to retrieve.
+     *
+     * @return CharSequence holding string data.  May be styled.  Returns
+     *         null if the attribute is not defined.
+     */
+    @Override
+    public CharSequence getText(int index) {
+        if (!hasValue(index)) {
+            return null;
+        }
+        // As unfortunate as it is, it's possible to use enums with all attribute formats,
+        // not just integers/enums. So, we need to search the enums always. In case
+        // enums are used, the returned value is an integer.
+        Integer v = resolveEnumAttribute(index);
+        if (v != null) {
+            return String.valueOf((int) v);
+        }
+        ResourceValue resourceValue = mResourceData[index];
+        if (resourceValue instanceof TextResourceValue) {
+            String rawString = resourceValue.getRawXmlValue();
+            return Html.fromHtml(rawString, FROM_HTML_MODE_COMPACT);
+        }
+        return resourceValue.getValue();
+    }
+
+    /**
+     * Retrieve the string value for the attribute at <var>index</var>.
+     *
+     * @param index Index of attribute to retrieve.
+     *
+     * @return String holding string data.  Any styling information is
+     * removed.  Returns null if the attribute is not defined.
+     */
+    @Override
+    public String getString(int index) {
+        if (!hasValue(index)) {
+            return null;
+        }
+        // As unfortunate as it is, it's possible to use enums with all attribute formats,
+        // not just integers/enums. So, we need to search the enums always. In case
+        // enums are used, the returned value is an integer.
+        Integer v = resolveEnumAttribute(index);
+        return v == null ? mResourceData[index].getValue() : String.valueOf((int) v);
+    }
+
+    /**
+     * Retrieve the boolean value for the attribute at <var>index</var>.
+     *
+     * @param index Index of attribute to retrieve.
+     * @param defValue Value to return if the attribute is not defined.
+     *
+     * @return Attribute boolean value, or defValue if not defined.
+     */
+    @Override
+    public boolean getBoolean(int index, boolean defValue) {
+        String s = getString(index);
+        return s == null ? defValue : XmlUtils.convertValueToBoolean(s, defValue);
+
+    }
+
+    /**
+     * Retrieve the integer value for the attribute at <var>index</var>.
+     *
+     * @param index Index of attribute to retrieve.
+     * @param defValue Value to return if the attribute is not defined.
+     *
+     * @return Attribute int value, or defValue if not defined.
+     */
+    @Override
+    public int getInt(int index, int defValue) {
+        String s = getString(index);
+        try {
+            return convertValueToInt(s, defValue);
+        } catch (NumberFormatException e) {
+            Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
+                    String.format("\"%1$s\" in attribute \"%2$s\" is not a valid integer",
+                            s, mNames[index]),
+                    null, null);
+        }
+        return defValue;
+    }
+
+    /**
+     * Retrieve the float value for the attribute at <var>index</var>.
+     *
+     * @param index Index of attribute to retrieve.
+     *
+     * @return Attribute float value, or defValue if not defined..
+     */
+    @Override
+    public float getFloat(int index, float defValue) {
+        String s = getString(index);
+        try {
+            if (s != null) {
+                    return Float.parseFloat(s);
+            }
+        } catch (NumberFormatException e) {
+            Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
+                    String.format("\"%1$s\" in attribute \"%2$s\" cannot be converted to float.",
+                            s, mNames[index]),
+                    null, null);
+        }
+        return defValue;
+    }
+
+    /**
+     * Retrieve the color value for the attribute at <var>index</var>.  If
+     * the attribute references a color resource holding a complex
+     * {@link android.content.res.ColorStateList}, then the default color from
+     * the set is returned.
+     *
+     * @param index Index of attribute to retrieve.
+     * @param defValue Value to return if the attribute is not defined or
+     *                 not a resource.
+     *
+     * @return Attribute color value, or defValue if not defined.
+     */
+    @Override
+    public int getColor(int index, int defValue) {
+        if (index < 0 || index >= mResourceData.length) {
+            return defValue;
+        }
+
+        if (mResourceData[index] == null) {
+            return defValue;
+        }
+
+        ColorStateList colorStateList = ResourceHelper.getColorStateList(
+                mResourceData[index], mContext, mTheme);
+        if (colorStateList != null) {
+            return colorStateList.getDefaultColor();
+        }
+
+        return defValue;
+    }
+
+    @Override
+    public ColorStateList getColorStateList(int index) {
+        if (!hasValue(index)) {
+            return null;
+        }
+
+        return ResourceHelper.getColorStateList(mResourceData[index], mContext, mTheme);
+    }
+
+    @Override
+    public ComplexColor getComplexColor(int index) {
+        if (!hasValue(index)) {
+            return null;
+        }
+
+        return ResourceHelper.getComplexColor(mResourceData[index], mContext, mTheme);
+    }
+
+    /**
+     * Retrieve the integer value for the attribute at <var>index</var>.
+     *
+     * @param index Index of attribute to retrieve.
+     * @param defValue Value to return if the attribute is not defined or
+     *                 not a resource.
+     *
+     * @return Attribute integer value, or defValue if not defined.
+     */
+    @Override
+    public int getInteger(int index, int defValue) {
+        return getInt(index, defValue);
+    }
+
+    /**
+     * Retrieve a dimensional unit attribute at <var>index</var>.  Unit
+     * conversions are based on the current {@link DisplayMetrics}
+     * associated with the resources this {@link TypedArray} object
+     * came from.
+     *
+     * @param index Index of attribute to retrieve.
+     * @param defValue Value to return if the attribute is not defined or
+     *                 not a resource.
+     *
+     * @return Attribute dimension value multiplied by the appropriate
+     * metric, or defValue if not defined.
+     *
+     * @see #getDimensionPixelOffset
+     * @see #getDimensionPixelSize
+     */
+    @Override
+    public float getDimension(int index, float defValue) {
+        String s = getString(index);
+        if (s == null) {
+            return defValue;
+        }
+        // Check if the value is a magic constant that doesn't require a unit.
+        try {
+            int i = Integer.parseInt(s);
+            if (i == LayoutParams.MATCH_PARENT || i == LayoutParams.WRAP_CONTENT) {
+                return i;
+            }
+        } catch (NumberFormatException ignored) {
+            // pass
+        }
+
+        if (ResourceHelper.parseFloatAttribute(mNames[index], s, mValue, true)) {
+            return mValue.getDimension(mBridgeResources.getDisplayMetrics());
+        }
+
+        return defValue;
+    }
+
+    /**
+     * Retrieve a dimensional unit attribute at <var>index</var> for use
+     * as an offset in raw pixels.  This is the same as
+     * {@link #getDimension}, except the returned value is converted to
+     * integer pixels for you.  An offset conversion involves simply
+     * truncating the base value to an integer.
+     *
+     * @param index Index of attribute to retrieve.
+     * @param defValue Value to return if the attribute is not defined or
+     *                 not a resource.
+     *
+     * @return Attribute dimension value multiplied by the appropriate
+     * metric and truncated to integer pixels, or defValue if not defined.
+     *
+     * @see #getDimension
+     * @see #getDimensionPixelSize
+     */
+    @Override
+    public int getDimensionPixelOffset(int index, int defValue) {
+        return (int) getDimension(index, defValue);
+    }
+
+    /**
+     * Retrieve a dimensional unit attribute at <var>index</var> for use
+     * as a size in raw pixels.  This is the same as
+     * {@link #getDimension}, except the returned value is converted to
+     * integer pixels for use as a size.  A size conversion involves
+     * rounding the base value, and ensuring that a non-zero base value
+     * is at least one pixel in size.
+     *
+     * @param index Index of attribute to retrieve.
+     * @param defValue Value to return if the attribute is not defined or
+     *                 not a resource.
+     *
+     * @return Attribute dimension value multiplied by the appropriate
+     * metric and truncated to integer pixels, or defValue if not defined.
+     *
+     * @see #getDimension
+     * @see #getDimensionPixelOffset
+     */
+    @Override
+    public int getDimensionPixelSize(int index, int defValue) {
+        try {
+            return getDimension(index, null);
+        } catch (RuntimeException e) {
+            String s = getString(index);
+
+            if (s != null) {
+                // looks like we were unable to resolve the dimension value
+                Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
+                        String.format("\"%1$s\" in attribute \"%2$s\" is not a valid format.",
+                                s, mNames[index]),
+                        null, null);
+            }
+
+            return defValue;
+        }
+    }
+
+    /**
+     * Special version of {@link #getDimensionPixelSize} for retrieving
+     * {@link android.view.ViewGroup}'s layout_width and layout_height
+     * attributes.  This is only here for performance reasons; applications
+     * should use {@link #getDimensionPixelSize}.
+     *
+     * @param index Index of the attribute to retrieve.
+     * @param name Textual name of attribute for error reporting.
+     *
+     * @return Attribute dimension value multiplied by the appropriate
+     * metric and truncated to integer pixels.
+     */
+    @Override
+    public int getLayoutDimension(int index, String name) {
+        try {
+            // this will throw an exception if not found.
+            return getDimension(index, name);
+        } catch (RuntimeException e) {
+
+            if (LayoutInflater_Delegate.sIsInInclude) {
+                throw new RuntimeException("Layout Dimension '" + name + "' not found.");
+            }
+
+            Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
+                    "You must supply a " + name + " attribute.",
+                    null, null);
+
+            return 0;
+        }
+    }
+
+    @Override
+    public int getLayoutDimension(int index, int defValue) {
+        return getDimensionPixelSize(index, defValue);
+    }
+
+    /** @param name attribute name, used for error reporting. */
+    private int getDimension(int index, @Nullable String name) {
+        String s = getString(index);
+        if (s == null) {
+            if (name != null) {
+                throw new RuntimeException("Attribute '" + name + "' not found");
+            }
+            throw new RuntimeException();
+        }
+        // Check if the value is a magic constant that doesn't require a unit.
+        try {
+            int i = Integer.parseInt(s);
+            if (i == LayoutParams.MATCH_PARENT || i == LayoutParams.WRAP_CONTENT) {
+                return i;
+            }
+        } catch (NumberFormatException ignored) {
+            // pass
+        }
+        if (ResourceHelper.parseFloatAttribute(mNames[index], s, mValue, true)) {
+            float f = mValue.getDimension(mBridgeResources.getDisplayMetrics());
+
+            final int res = (int)(f+0.5f);
+            if (res != 0) return res;
+            if (f == 0) return 0;
+            if (f > 0) return 1;
+        }
+
+        throw new RuntimeException();
+    }
+
+    /**
+     * Retrieve a fractional unit attribute at <var>index</var>.
+     *
+     * @param index Index of attribute to retrieve.
+     * @param base The base value of this fraction.  In other words, a
+     *             standard fraction is multiplied by this value.
+     * @param pbase The parent base value of this fraction.  In other
+     *             words, a parent fraction (nn%p) is multiplied by this
+     *             value.
+     * @param defValue Value to return if the attribute is not defined or
+     *                 not a resource.
+     *
+     * @return Attribute fractional value multiplied by the appropriate
+     * base value, or defValue if not defined.
+     */
+    @Override
+    public float getFraction(int index, int base, int pbase, float defValue) {
+        String value = getString(index);
+        if (value == null) {
+            return defValue;
+        }
+
+        if (ResourceHelper.parseFloatAttribute(mNames[index], value, mValue, false)) {
+            return mValue.getFraction(base, pbase);
+        }
+
+        // looks like we were unable to resolve the fraction value
+        Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
+                String.format(
+                        "\"%1$s\" in attribute \"%2$s\" cannot be converted to a fraction.",
+                        value, mNames[index]),
+                null, null);
+
+        return defValue;
+    }
+
+    /**
+     * Retrieve the resource identifier for the attribute at
+     * <var>index</var>.  Note that attribute resource as resolved when
+     * the overall {@link TypedArray} object is retrieved.  As a
+     * result, this function will return the resource identifier of the
+     * final resource value that was found, <em>not</em> necessarily the
+     * original resource that was specified by the attribute.
+     *
+     * @param index Index of attribute to retrieve.
+     * @param defValue Value to return if the attribute is not defined or
+     *                 not a resource.
+     *
+     * @return Attribute resource identifier, or defValue if not defined.
+     */
+    @Override
+    public int getResourceId(int index, int defValue) {
+        if (index < 0 || index >= mResourceData.length) {
+            return defValue;
+        }
+
+        // get the Resource for this index
+        ResourceValue resValue = mResourceData[index];
+
+        // no data, return the default value.
+        if (resValue == null) {
+            return defValue;
+        }
+
+        // check if this is a style resource
+        if (resValue instanceof StyleResourceValue) {
+            // get the id that will represent this style.
+            return mContext.getDynamicIdByStyle((StyleResourceValue)resValue);
+        }
+
+        // If the attribute was a reference to a resource, and not a declaration of an id (@+id),
+        // then the xml attribute value was "resolved" which leads us to a ResourceValue with a
+        // valid type, name, namespace and a potentially null value.
+        if (!(resValue instanceof UnresolvedResourceValue)) {
+            return mContext.getResourceId(resValue.asReference(), defValue);
+        }
+
+        // else, try to get the value, and resolve it somehow.
+        String value = resValue.getValue();
+        if (value == null) {
+            return defValue;
+        }
+        value = value.trim();
+
+
+        // `resValue` failed to be resolved. We extract the interesting bits and get rid of this
+        // broken object. The namespace and resolver come from where the XML attribute was defined.
+        ResourceNamespace contextNamespace = resValue.getNamespace();
+        Resolver namespaceResolver = resValue.getNamespaceResolver();
+
+        if (value.startsWith("#")) {
+            // this looks like a color, do not try to parse it
+            return defValue;
+        }
+
+        if (Typeface_Accessor.isSystemFont(value)) {
+            // A system font family value, do not try to parse
+            return defValue;
+        }
+
+        // Handle the @id/<name>, @+id/<name> and @android:id/<name>
+        // We need to return the exact value that was compiled (from the various R classes),
+        // as these values can be reused internally with calls to findViewById().
+        // There's a trick with platform layouts that not use "android:" but their IDs are in
+        // fact in the android.R and com.android.internal.R classes.
+        // The field mPlatformFile will indicate that all IDs are to be looked up in the android R
+        // classes exclusively.
+
+        // if this is a reference to an id, find it.
+        ResourceUrl resourceUrl = ResourceUrl.parse(value);
+        if (resourceUrl != null) {
+            if (resourceUrl.type == ResourceType.ID) {
+                ResourceReference referencedId =
+                        resourceUrl.resolve(contextNamespace, namespaceResolver);
+
+                // Look for the idName in project or android R class depending on isPlatform.
+                if (resourceUrl.isCreate()) {
+                    int idValue;
+                    if (referencedId.getNamespace() == ResourceNamespace.ANDROID) {
+                        idValue = Bridge.getResourceId(ResourceType.ID, resourceUrl.name);
+                    } else {
+                        idValue = mContext.getLayoutlibCallback().getOrGenerateResourceId(referencedId);
+                    }
+                    return idValue;
+                }
+                // This calls the same method as in if(create), but doesn't create a dynamic id, if
+                // one is not found.
+                return mContext.getResourceId(referencedId, defValue);
+            }
+            else if (resourceUrl.type == ResourceType.AAPT) {
+                ResourceReference referencedId =
+                        resourceUrl.resolve(contextNamespace, namespaceResolver);
+                return mContext.getLayoutlibCallback().getOrGenerateResourceId(referencedId);
+            }
+        }
+        // not a direct id valid reference. First check if it's an enum (this is a corner case
+        // for attributes that have a reference|enum type), then fallback to resolve
+        // as an ID without prefix.
+        Integer enumValue = resolveEnumAttribute(index);
+        if (enumValue != null) {
+            return enumValue;
+        }
+
+        return defValue;
+    }
+
+    @Override
+    public int getThemeAttributeId(int index, int defValue) {
+        // TODO: Get the right Theme Attribute ID to enable caching of the drawables.
+        return defValue;
+    }
+
+    /**
+     * Retrieve the Drawable for the attribute at <var>index</var>.  This
+     * gets the resource ID of the selected attribute, and uses
+     * {@link Resources#getDrawable Resources.getDrawable} of the owning
+     * Resources object to retrieve its Drawable.
+     *
+     * @param index Index of attribute to retrieve.
+     *
+     * @return Drawable for the attribute, or null if not defined.
+     */
+    @Override
+    @Nullable
+    public Drawable getDrawable(int index) {
+        if (!hasValue(index)) {
+            return null;
+        }
+
+        ResourceValue value = mResourceData[index];
+        return ResourceHelper.getDrawable(value, mContext, mTheme);
+    }
+
+    /**
+     * Version of {@link #getDrawable(int)} that accepts an override density.
+     * @hide
+     */
+    @Override
+    @Nullable
+    public Drawable getDrawableForDensity(int index, int density) {
+        return getDrawable(index);
+    }
+
+    /**
+     * Retrieve the Typeface for the attribute at <var>index</var>.
+     * @param index Index of attribute to retrieve.
+     *
+     * @return Typeface for the attribute, or null if not defined.
+     */
+    @Override
+    public Typeface getFont(int index) {
+        if (!hasValue(index)) {
+            return null;
+        }
+
+        ResourceValue value = mResourceData[index];
+        return ResourceHelper.getFont(value, mContext, mTheme);
+    }
+
+    /**
+     * Retrieve the CharSequence[] for the attribute at <var>index</var>.
+     * This gets the resource ID of the selected attribute, and uses
+     * {@link Resources#getTextArray Resources.getTextArray} of the owning
+     * Resources object to retrieve its String[].
+     *
+     * @param index Index of attribute to retrieve.
+     *
+     * @return CharSequence[] for the attribute, or null if not defined.
+     */
+    @Override
+    public CharSequence[] getTextArray(int index) {
+        if (!hasValue(index)) {
+            return null;
+        }
+        ResourceValue resVal = mResourceData[index];
+        if (resVal instanceof ArrayResourceValue) {
+            ArrayResourceValue array = (ArrayResourceValue) resVal;
+            int count = array.getElementCount();
+            return count >= 0 ?
+                    Resources_Delegate.resolveValues(mBridgeResources, array) :
+                    null;
+        }
+        int id = getResourceId(index, 0);
+        String resIdMessage = id > 0 ? " (resource id 0x" + Integer.toHexString(id) + ')' : "";
+        assert false :
+                String.format("%1$s in %2$s%3$s is not a valid array resource.", resVal.getValue(),
+                        mNames[index], resIdMessage);
+
+        return new CharSequence[0];
+    }
+
+    @Override
+    public int[] extractThemeAttrs() {
+        // The drawables are always inflated with a Theme and we don't care about caching. So,
+        // just return.
+        return null;
+    }
+
+    @Override
+    public int getChangingConfigurations() {
+        // We don't care about caching. Any change in configuration is a fresh render. So,
+        // just return.
+        return 0;
+    }
+
+    /**
+     * Retrieve the raw TypedValue for the attribute at <var>index</var>.
+     *
+     * @param index Index of attribute to retrieve.
+     * @param outValue TypedValue object in which to place the attribute's
+     *                 data.
+     *
+     * @return Returns true if the value was retrieved, else false.
+     */
+    @Override
+    public boolean getValue(int index, TypedValue outValue) {
+        // TODO: more switch cases for other types.
+        outValue.type = getType(index);
+        switch (outValue.type) {
+            case TYPE_NULL:
+                return false;
+            case TYPE_STRING:
+                outValue.string = getString(index);
+                return true;
+            case TYPE_REFERENCE:
+                outValue.resourceId = mResourceId[index];
+                return true;
+            case TYPE_INT_COLOR_ARGB4:
+            case TYPE_INT_COLOR_ARGB8:
+            case TYPE_INT_COLOR_RGB4:
+            case TYPE_INT_COLOR_RGB8:
+                ColorStateList colorStateList = getColorStateList(index);
+                if (colorStateList == null) {
+                    return false;
+                }
+                outValue.data = colorStateList.getDefaultColor();
+                return true;
+            default:
+                // For back-compatibility, parse as float.
+                String s = getString(index);
+                return s != null &&
+                        ResourceHelper.parseFloatAttribute(mNames[index], s, outValue, false);
+        }
+    }
+
+    @Override
+    @SuppressWarnings("ResultOfMethodCallIgnored")
+    public int getType(int index) {
+        String value = getString(index);
+        if (value == null) {
+            return TYPE_NULL;
+        }
+        if (value.startsWith(PREFIX_RESOURCE_REF)) {
+            return TYPE_REFERENCE;
+        }
+        if (value.startsWith(PREFIX_THEME_REF)) {
+            return TYPE_ATTRIBUTE;
+        }
+        try {
+            // Don't care about the value. Only called to check if an exception is thrown.
+            convertValueToInt(value, 0);
+            if (value.startsWith("0x") || value.startsWith("0X")) {
+                return TYPE_INT_HEX;
+            }
+            // is it a color?
+            if (value.startsWith("#")) {
+                int length = value.length() - 1;
+                if (length == 3) {  // rgb
+                    return TYPE_INT_COLOR_RGB4;
+                }
+                if (length == 4) {  // argb
+                    return TYPE_INT_COLOR_ARGB4;
+                }
+                if (length == 6) {  // rrggbb
+                    return TYPE_INT_COLOR_RGB8;
+                }
+                if (length == 8) {  // aarrggbb
+                    return TYPE_INT_COLOR_ARGB8;
+                }
+            }
+            if (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("false")) {
+                return TYPE_INT_BOOLEAN;
+            }
+            return TYPE_INT_DEC;
+        } catch (NumberFormatException ignored) {
+            try {
+                Float.parseFloat(value);
+                return TYPE_FLOAT;
+            } catch (NumberFormatException ignore) {
+            }
+            // Might be a dimension.
+            if (ResourceHelper.parseFloatAttribute(null, value, new TypedValue(), false)) {
+                return TYPE_DIMENSION;
+            }
+        }
+        // TODO: handle fractions.
+        return TYPE_STRING;
+    }
+
+    /**
+     * Determines whether there is an attribute at <var>index</var>.
+     *
+     * @param index Index of attribute to retrieve.
+     *
+     * @return True if the attribute has a value, false otherwise.
+     */
+    @Override
+    public boolean hasValue(int index) {
+        return index >= 0 && index < mResourceData.length && mResourceData[index] != null;
+    }
+
+    @Override
+    public boolean hasValueOrEmpty(int index) {
+        return hasValue(index) || index >= 0 && index < mResourceData.length &&
+                mEmptyIds != null && Arrays.binarySearch(mEmptyIds, index) >= 0;
+    }
+
+    /**
+     * Retrieve the raw TypedValue for the attribute at <var>index</var>
+     * and return a temporary object holding its data.  This object is only
+     * valid until the next call on to {@link TypedArray}.
+     *
+     * @param index Index of attribute to retrieve.
+     *
+     * @return Returns a TypedValue object if the attribute is defined,
+     *         containing its data; otherwise returns null.  (You will not
+     *         receive a TypedValue whose type is TYPE_NULL.)
+     */
+    @Override
+    public TypedValue peekValue(int index) {
+        if (index < 0 || index >= mResourceData.length) {
+            return null;
+        }
+
+        if (getValue(index, mValue)) {
+            return mValue;
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns a message about the parser state suitable for printing error messages.
+     */
+    @Override
+    public String getPositionDescription() {
+        return "<internal -- stub if needed>";
+    }
+
+    /**
+     * Give back a previously retrieved TypedArray, for later re-use.
+     */
+    @Override
+    public void recycle() {
+        // pass
+    }
+
+    @Override
+    public String toString() {
+        return Arrays.toString(mResourceData);
+    }
+
+    /**
+     * Searches for the string in the attributes (flag or enums) and returns the integer.
+     * If found, it will return an integer matching the value.
+     *
+     * @param index Index of attribute to retrieve.
+     *
+     * @return Attribute int value, or null if not defined.
+     */
+    private Integer resolveEnumAttribute(int index) {
+        // Get the map of attribute-constant -> IntegerValue
+        Map<String, Integer> map = null;
+        if (mNamespaces[index] == ResourceNamespace.ANDROID) {
+            map = Bridge.getEnumValues(mNames[index]);
+        } else {
+            // get the styleable matching the resolved name
+            RenderResources res = mContext.getRenderResources();
+            ResourceValue attr = res.getResolvedResource(
+                    ResourceReference.attr(mNamespaces[index], mNames[index]));
+            if (attr instanceof AttrResourceValue) {
+                map = ((AttrResourceValue) attr).getAttributeValues();
+            }
+        }
+
+        if (map != null && !map.isEmpty()) {
+            // Accumulator to store the value of the 1+ constants.
+            int result = 0;
+            boolean found = false;
+
+            String value = mResourceData[index].getValue();
+            if (!value.isEmpty()) {
+                // Check if the value string is already representing an integer and return it if so.
+                // Resources coming from res.apk in an AAR may have flags and enums in integer form.
+                char c = value.charAt(0);
+                if (Character.isDigit(c) || c == '-' || c == '+') {
+                    try {
+                        return convertValueToInt(value, 0);
+                    } catch (NumberFormatException e) {
+                        // Ignore and continue.
+                    }
+                }
+                // Split the value in case it is a mix of several flags.
+                String[] keywords = value.split("\\|");
+                for (String keyword : keywords) {
+                    Integer i = map.get(keyword.trim());
+                    if (i != null) {
+                        result |= i;
+                        found = true;
+                    }
+                    // TODO: We should act smartly and log a warning for incorrect keywords. However,
+                    // this method is currently called even if the resourceValue is not an enum.
+                }
+                if (found) {
+                    return result;
+                }
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Copied from {@link XmlUtils#convertValueToInt(CharSequence, int)}, but adapted to account
+     * for aapt, and the fact that host Java VM's Integer.parseInt("XXXXXXXX", 16) cannot handle
+     * "XXXXXXXX" > 80000000.
+     */
+    private static int convertValueToInt(@Nullable String charSeq, int defValue) {
+        if (null == charSeq || charSeq.isEmpty())
+            return defValue;
+
+        int sign = 1;
+        int index = 0;
+        int len = charSeq.length();
+        int base = 10;
+
+        if ('-' == charSeq.charAt(0)) {
+            sign = -1;
+            index++;
+        }
+
+        if ('0' == charSeq.charAt(index)) {
+            //  Quick check for a zero by itself
+            if (index == (len - 1))
+                return 0;
+
+            char c = charSeq.charAt(index + 1);
+
+            if ('x' == c || 'X' == c) {
+                index += 2;
+                base = 16;
+            } else {
+                index++;
+                // Leave the base as 10. aapt removes the preceding zero, and thus when framework
+                // sees the value, it only gets the decimal value.
+            }
+        } else if ('#' == charSeq.charAt(index)) {
+            return ResourceHelper.getColor(charSeq) * sign;
+        } else if ("true".equals(charSeq) || "TRUE".equals(charSeq)) {
+            return -1;
+        } else if ("false".equals(charSeq) || "FALSE".equals(charSeq)) {
+            return 0;
+        }
+
+        // Use Long, since we want to handle hex ints > 80000000.
+        return ((int)Long.parseLong(charSeq.substring(index), base)) * sign;
+    }
+
+    static TypedArray obtain(Resources res, int len) {
+        return new BridgeTypedArray(res, null, len);
+    }
+}
diff --git a/android/content/res/ColorStateList.java b/android/content/res/ColorStateList.java
new file mode 100644
index 0000000..f23c802
--- /dev/null
+++ b/android/content/res/ColorStateList.java
@@ -0,0 +1,747 @@
+/*
+ * 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.content.res;
+
+import android.annotation.ColorInt;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.pm.ActivityInfo.Config;
+import android.content.res.Resources.Theme;
+import android.graphics.Color;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.MathUtils;
+import android.util.SparseArray;
+import android.util.StateSet;
+import android.util.Xml;
+
+import com.android.internal.R;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.GrowingArrayUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.lang.ref.WeakReference;
+import java.util.Arrays;
+
+/**
+ *
+ * Lets you map {@link android.view.View} state sets to colors.
+ * <p>
+ * {@link android.content.res.ColorStateList}s are created from XML resource files defined in the
+ * "color" subdirectory directory of an application's resource directory. The XML file contains
+ * a single "selector" element with a number of "item" elements inside. For example:
+ * <pre>
+ * &lt;selector xmlns:android="http://schemas.android.com/apk/res/android"&gt;
+ *   &lt;item android:state_focused="true"
+ *           android:color="@color/sample_focused" /&gt;
+ *   &lt;item android:state_pressed="true"
+ *           android:state_enabled="false"
+ *           android:color="@color/sample_disabled_pressed" /&gt;
+ *   &lt;item android:state_enabled="false"
+ *           android:color="@color/sample_disabled_not_pressed" /&gt;
+ *   &lt;item android:color="@color/sample_default" /&gt;
+ * &lt;/selector&gt;
+ * </pre>
+ *
+ * This defines a set of state spec / color pairs where each state spec specifies a set of
+ * states that a view must either be in or not be in and the color specifies the color associated
+ * with that spec.
+ *
+ * <a name="StateSpec"></a>
+ * <h3>State specs</h3>
+ * <p>
+ * Each item defines a set of state spec and color pairs, where the state spec is a series of
+ * attributes set to either {@code true} or {@code false} to represent inclusion or exclusion. If
+ * an attribute is not specified for an item, it may be any value.
+ * <p>
+ * For example, the following item will be matched whenever the focused state is set; any other
+ * states may be set or unset:
+ * <pre>
+ * &lt;item android:state_focused="true"
+ *         android:color="@color/sample_focused" /&gt;
+ * </pre>
+ * <p>
+ * Typically, a color state list will reference framework-defined state attributes such as
+ * {@link android.R.attr#state_focused android:state_focused} or
+ * {@link android.R.attr#state_enabled android:state_enabled}; however, app-defined attributes may
+ * also be used.
+ * <p>
+ * <strong>Note:</strong> The list of state specs will be matched against in the order that they
+ * appear in the XML file. For this reason, more-specific items should be placed earlier in the
+ * file. An item with no state spec is considered to match any set of states and is generally
+ * useful as a final item to be used as a default.
+ * <p>
+ * If an item with no state spec is placed before other items, those items
+ * will be ignored.
+ *
+ * <a name="ItemAttributes"></a>
+ * <h3>Item attributes</h3>
+ * <p>
+ * Each item must define an {@link android.R.attr#color android:color} attribute, which may be
+ * an HTML-style hex color, a reference to a color resource, or -- in API 23 and above -- a theme
+ * attribute that resolves to a color.
+ * <p>
+ * Starting with API 23, items may optionally define an {@link android.R.attr#alpha android:alpha}
+ * attribute to modify the base color's opacity. This attribute takes a either floating-point value
+ * between 0 and 1 or a theme attribute that resolves as such. The item's overall color is
+ * calculated by multiplying by the base color's alpha channel by the {@code alpha} value. For
+ * example, the following item represents the theme's accent color at 50% opacity:
+ * <pre>
+ * &lt;item android:state_enabled="false"
+ *         android:color="?android:attr/colorAccent"
+ *         android:alpha="0.5" /&gt;
+ * </pre>
+ *
+ * <a name="DeveloperGuide"></a>
+ * <h3>Developer guide</h3>
+ * <p>
+ * For more information, see the guide to
+ * <a href="{@docRoot}guide/topics/resources/color-list-resource.html">Color State
+ * List Resource</a>.
+ *
+ * @attr ref android.R.styleable#ColorStateListItem_alpha
+ * @attr ref android.R.styleable#ColorStateListItem_color
+ */
+public class ColorStateList extends ComplexColor implements Parcelable {
+    private static final String TAG = "ColorStateList";
+
+    private static final int DEFAULT_COLOR = Color.RED;
+    private static final int[][] EMPTY = new int[][] { new int[0] };
+
+    /** Thread-safe cache of single-color ColorStateLists. */
+    private static final SparseArray<WeakReference<ColorStateList>> sCache = new SparseArray<>();
+
+    /** Lazily-created factory for this color state list. */
+    @UnsupportedAppUsage
+    private ColorStateListFactory mFactory;
+
+    private int[][] mThemeAttrs;
+    private @Config int mChangingConfigurations;
+
+    @UnsupportedAppUsage
+    private int[][] mStateSpecs;
+    @UnsupportedAppUsage
+    private int[] mColors;
+    @UnsupportedAppUsage
+    private int mDefaultColor;
+    private boolean mIsOpaque;
+
+    @UnsupportedAppUsage
+    private ColorStateList() {
+        // Not publicly instantiable.
+    }
+
+    /**
+     * Creates a ColorStateList that returns the specified mapping from
+     * states to colors.
+     */
+    public ColorStateList(int[][] states, @ColorInt int[] colors) {
+        mStateSpecs = states;
+        mColors = colors;
+
+        onColorsChanged();
+    }
+
+    /**
+     * @return A ColorStateList containing a single color.
+     */
+    @NonNull
+    public static ColorStateList valueOf(@ColorInt int color) {
+        synchronized (sCache) {
+            final int index = sCache.indexOfKey(color);
+            if (index >= 0) {
+                final ColorStateList cached = sCache.valueAt(index).get();
+                if (cached != null) {
+                    return cached;
+                }
+
+                // Prune missing entry.
+                sCache.removeAt(index);
+            }
+
+            // Prune the cache before adding new items.
+            final int N = sCache.size();
+            for (int i = N - 1; i >= 0; i--) {
+                if (sCache.valueAt(i).get() == null) {
+                    sCache.removeAt(i);
+                }
+            }
+
+            final ColorStateList csl = new ColorStateList(EMPTY, new int[] { color });
+            sCache.put(color, new WeakReference<>(csl));
+            return csl;
+        }
+    }
+
+    /**
+     * Creates a ColorStateList with the same properties as another
+     * ColorStateList.
+     * <p>
+     * The properties of the new ColorStateList can be modified without
+     * affecting the source ColorStateList.
+     *
+     * @param orig the source color state list
+     */
+    private ColorStateList(ColorStateList orig) {
+        if (orig != null) {
+            mChangingConfigurations = orig.mChangingConfigurations;
+            mStateSpecs = orig.mStateSpecs;
+            mDefaultColor = orig.mDefaultColor;
+            mIsOpaque = orig.mIsOpaque;
+
+            // Deep copy, these may change due to applyTheme().
+            mThemeAttrs = orig.mThemeAttrs.clone();
+            mColors = orig.mColors.clone();
+        }
+    }
+
+    /**
+     * Creates a ColorStateList from an XML document.
+     *
+     * @param r Resources against which the ColorStateList should be inflated.
+     * @param parser Parser for the XML document defining the ColorStateList.
+     * @return A new color state list.
+     *
+     * @deprecated Use #createFromXml(Resources, XmlPullParser parser, Theme)
+     */
+    @NonNull
+    @Deprecated
+    public static ColorStateList createFromXml(Resources r, XmlPullParser parser)
+            throws XmlPullParserException, IOException {
+        return createFromXml(r, parser, null);
+    }
+
+    /**
+     * Creates a ColorStateList from an XML document using given a set of
+     * {@link Resources} and a {@link Theme}.
+     *
+     * @param r Resources against which the ColorStateList should be inflated.
+     * @param parser Parser for the XML document defining the ColorStateList.
+     * @param theme Optional theme to apply to the color state list, may be
+     *              {@code null}.
+     * @return A new color state list.
+     */
+    @NonNull
+    public static ColorStateList createFromXml(@NonNull Resources r, @NonNull XmlPullParser parser,
+            @Nullable Theme theme) throws XmlPullParserException, IOException {
+        final AttributeSet attrs = Xml.asAttributeSet(parser);
+
+        int type;
+        while ((type = parser.next()) != XmlPullParser.START_TAG
+                   && type != XmlPullParser.END_DOCUMENT) {
+            // Seek parser to start tag.
+        }
+
+        if (type != XmlPullParser.START_TAG) {
+            throw new XmlPullParserException("No start tag found");
+        }
+
+        return createFromXmlInner(r, parser, attrs, theme);
+    }
+
+    /**
+     * Create from inside an XML document. Called on a parser positioned at a
+     * tag in an XML document, tries to create a ColorStateList from that tag.
+     *
+     * @throws XmlPullParserException if the current tag is not &lt;selector>
+     * @return A new color state list for the current tag.
+     */
+    @NonNull
+    static ColorStateList createFromXmlInner(@NonNull Resources r,
+            @NonNull XmlPullParser parser, @NonNull AttributeSet attrs, @Nullable Theme theme)
+            throws XmlPullParserException, IOException {
+        final String name = parser.getName();
+        if (!name.equals("selector")) {
+            throw new XmlPullParserException(
+                    parser.getPositionDescription() + ": invalid color state list tag " + name);
+        }
+
+        final ColorStateList colorStateList = new ColorStateList();
+        colorStateList.inflate(r, parser, attrs, theme);
+        return colorStateList;
+    }
+
+    /**
+     * Creates a new ColorStateList that has the same states and colors as this
+     * one but where each color has the specified alpha value (0-255).
+     *
+     * @param alpha The new alpha channel value (0-255).
+     * @return A new color state list.
+     */
+    @NonNull
+    public ColorStateList withAlpha(int alpha) {
+        final int[] colors = new int[mColors.length];
+        final int len = colors.length;
+        for (int i = 0; i < len; i++) {
+            colors[i] = (mColors[i] & 0xFFFFFF) | (alpha << 24);
+        }
+
+        return new ColorStateList(mStateSpecs, colors);
+    }
+
+    /**
+     * Fill in this object based on the contents of an XML "selector" element.
+     */
+    private void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
+            @NonNull AttributeSet attrs, @Nullable Theme theme)
+            throws XmlPullParserException, IOException {
+        final int innerDepth = parser.getDepth()+1;
+        int depth;
+        int type;
+
+        @Config int changingConfigurations = 0;
+        int defaultColor = DEFAULT_COLOR;
+
+        boolean hasUnresolvedAttrs = false;
+
+        int[][] stateSpecList = ArrayUtils.newUnpaddedArray(int[].class, 20);
+        int[][] themeAttrsList = new int[stateSpecList.length][];
+        int[] colorList = new int[stateSpecList.length];
+        int listSize = 0;
+
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+               && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) {
+            if (type != XmlPullParser.START_TAG || depth > innerDepth
+                    || !parser.getName().equals("item")) {
+                continue;
+            }
+
+            final TypedArray a = Resources.obtainAttributes(r, theme, attrs,
+                    R.styleable.ColorStateListItem);
+            final int[] themeAttrs = a.extractThemeAttrs();
+            final int baseColor = a.getColor(R.styleable.ColorStateListItem_color, Color.MAGENTA);
+            final float alphaMod = a.getFloat(R.styleable.ColorStateListItem_alpha, 1.0f);
+
+            changingConfigurations |= a.getChangingConfigurations();
+
+            a.recycle();
+
+            // Parse all unrecognized attributes as state specifiers.
+            int j = 0;
+            final int numAttrs = attrs.getAttributeCount();
+            int[] stateSpec = new int[numAttrs];
+            for (int i = 0; i < numAttrs; i++) {
+                final int stateResId = attrs.getAttributeNameResource(i);
+                switch (stateResId) {
+                    case R.attr.color:
+                    case R.attr.alpha:
+                        // Recognized attribute, ignore.
+                        break;
+                    default:
+                        stateSpec[j++] = attrs.getAttributeBooleanValue(i, false)
+                                ? stateResId : -stateResId;
+                }
+            }
+            stateSpec = StateSet.trimStateSet(stateSpec, j);
+
+            // Apply alpha modulation. If we couldn't resolve the color or
+            // alpha yet, the default values leave us enough information to
+            // modulate again during applyTheme().
+            final int color = modulateColorAlpha(baseColor, alphaMod);
+            if (listSize == 0 || stateSpec.length == 0) {
+                defaultColor = color;
+            }
+
+            if (themeAttrs != null) {
+                hasUnresolvedAttrs = true;
+            }
+
+            colorList = GrowingArrayUtils.append(colorList, listSize, color);
+            themeAttrsList = GrowingArrayUtils.append(themeAttrsList, listSize, themeAttrs);
+            stateSpecList = GrowingArrayUtils.append(stateSpecList, listSize, stateSpec);
+            listSize++;
+        }
+
+        mChangingConfigurations = changingConfigurations;
+        mDefaultColor = defaultColor;
+
+        if (hasUnresolvedAttrs) {
+            mThemeAttrs = new int[listSize][];
+            System.arraycopy(themeAttrsList, 0, mThemeAttrs, 0, listSize);
+        } else {
+            mThemeAttrs = null;
+        }
+
+        mColors = new int[listSize];
+        mStateSpecs = new int[listSize][];
+        System.arraycopy(colorList, 0, mColors, 0, listSize);
+        System.arraycopy(stateSpecList, 0, mStateSpecs, 0, listSize);
+
+        onColorsChanged();
+    }
+
+    /**
+     * Returns whether a theme can be applied to this color state list, which
+     * usually indicates that the color state list has unresolved theme
+     * attributes.
+     *
+     * @return whether a theme can be applied to this color state list
+     * @hide only for resource preloading
+     */
+    @Override
+    @UnsupportedAppUsage
+    public boolean canApplyTheme() {
+        return mThemeAttrs != null;
+    }
+
+    /**
+     * Applies a theme to this color state list.
+     * <p>
+     * <strong>Note:</strong> Applying a theme may affect the changing
+     * configuration parameters of this color state list. After calling this
+     * method, any dependent configurations must be updated by obtaining the
+     * new configuration mask from {@link #getChangingConfigurations()}.
+     *
+     * @param t the theme to apply
+     */
+    private void applyTheme(Theme t) {
+        if (mThemeAttrs == null) {
+            return;
+        }
+
+        boolean hasUnresolvedAttrs = false;
+
+        final int[][] themeAttrsList = mThemeAttrs;
+        final int N = themeAttrsList.length;
+        for (int i = 0; i < N; i++) {
+            if (themeAttrsList[i] != null) {
+                final TypedArray a = t.resolveAttributes(themeAttrsList[i],
+                        R.styleable.ColorStateListItem);
+
+                final float defaultAlphaMod;
+                if (themeAttrsList[i][R.styleable.ColorStateListItem_color] != 0) {
+                    // If the base color hasn't been resolved yet, the current
+                    // color's alpha channel is either full-opacity (if we
+                    // haven't resolved the alpha modulation yet) or
+                    // pre-modulated. Either is okay as a default value.
+                    defaultAlphaMod = Color.alpha(mColors[i]) / 255.0f;
+                } else {
+                    // Otherwise, the only correct default value is 1. Even if
+                    // nothing is resolved during this call, we can apply this
+                    // multiple times without losing of information.
+                    defaultAlphaMod = 1.0f;
+                }
+
+                // Extract the theme attributes, if any, before attempting to
+                // read from the typed array. This prevents a crash if we have
+                // unresolved attrs.
+                themeAttrsList[i] = a.extractThemeAttrs(themeAttrsList[i]);
+                if (themeAttrsList[i] != null) {
+                    hasUnresolvedAttrs = true;
+                }
+
+                final int baseColor = a.getColor(
+                        R.styleable.ColorStateListItem_color, mColors[i]);
+                final float alphaMod = a.getFloat(
+                        R.styleable.ColorStateListItem_alpha, defaultAlphaMod);
+                mColors[i] = modulateColorAlpha(baseColor, alphaMod);
+
+                // Account for any configuration changes.
+                mChangingConfigurations |= a.getChangingConfigurations();
+
+                a.recycle();
+            }
+        }
+
+        if (!hasUnresolvedAttrs) {
+            mThemeAttrs = null;
+        }
+
+        onColorsChanged();
+    }
+
+    /**
+     * Returns an appropriately themed color state list.
+     *
+     * @param t the theme to apply
+     * @return a copy of the color state list with the theme applied, or the
+     *         color state list itself if there were no unresolved theme
+     *         attributes
+     * @hide only for resource preloading
+     */
+    @Override
+    @UnsupportedAppUsage
+    public ColorStateList obtainForTheme(Theme t) {
+        if (t == null || !canApplyTheme()) {
+            return this;
+        }
+
+        final ColorStateList clone = new ColorStateList(this);
+        clone.applyTheme(t);
+        return clone;
+    }
+
+    /**
+     * Returns a mask of the configuration parameters for which this color
+     * state list may change, requiring that it be re-created.
+     *
+     * @return a mask of the changing configuration parameters, as defined by
+     *         {@link android.content.pm.ActivityInfo}
+     *
+     * @see android.content.pm.ActivityInfo
+     */
+    public @Config int getChangingConfigurations() {
+        return super.getChangingConfigurations() | mChangingConfigurations;
+    }
+
+    private int modulateColorAlpha(int baseColor, float alphaMod) {
+        if (alphaMod == 1.0f) {
+            return baseColor;
+        }
+
+        final int baseAlpha = Color.alpha(baseColor);
+        final int alpha = MathUtils.constrain((int) (baseAlpha * alphaMod + 0.5f), 0, 255);
+        return (baseColor & 0xFFFFFF) | (alpha << 24);
+    }
+
+    /**
+     * Indicates whether this color state list contains at least one state spec
+     * and the first spec is not empty (e.g. match-all).
+     *
+     * @return True if this color state list changes color based on state, false
+     *         otherwise.
+     * @see #getColorForState(int[], int)
+     */
+    @Override
+    public boolean isStateful() {
+        return mStateSpecs.length >= 1 && mStateSpecs[0].length > 0;
+    }
+
+    /**
+     * Return whether the state spec list has at least one item explicitly specifying
+     * {@link android.R.attr#state_focused}.
+     * @hide
+     */
+    public boolean hasFocusStateSpecified() {
+        return StateSet.containsAttribute(mStateSpecs, R.attr.state_focused);
+    }
+
+    /**
+     * Indicates whether this color state list is opaque, which means that every
+     * color returned from {@link #getColorForState(int[], int)} has an alpha
+     * value of 255.
+     *
+     * @return True if this color state list is opaque.
+     */
+    public boolean isOpaque() {
+        return mIsOpaque;
+    }
+
+    /**
+     * Return the color associated with the given set of
+     * {@link android.view.View} states.
+     *
+     * @param stateSet an array of {@link android.view.View} states
+     * @param defaultColor the color to return if there's no matching state
+     *                     spec in this {@link ColorStateList} that matches the
+     *                     stateSet.
+     *
+     * @return the color associated with that set of states in this {@link ColorStateList}.
+     */
+    public int getColorForState(@Nullable int[] stateSet, int defaultColor) {
+        final int setLength = mStateSpecs.length;
+        for (int i = 0; i < setLength; i++) {
+            final int[] stateSpec = mStateSpecs[i];
+            if (StateSet.stateSetMatches(stateSpec, stateSet)) {
+                return mColors[i];
+            }
+        }
+        return defaultColor;
+    }
+
+    /**
+     * Return the default color in this {@link ColorStateList}.
+     *
+     * @return the default color in this {@link ColorStateList}.
+     */
+    @ColorInt
+    public int getDefaultColor() {
+        return mDefaultColor;
+    }
+
+    /**
+     * Return the states in this {@link ColorStateList}. The returned array
+     * should not be modified.
+     *
+     * @return the states in this {@link ColorStateList}
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public int[][] getStates() {
+        return mStateSpecs;
+    }
+
+    /**
+     * Return the colors in this {@link ColorStateList}. The returned array
+     * should not be modified.
+     *
+     * @return the colors in this {@link ColorStateList}
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public int[] getColors() {
+        return mColors;
+    }
+
+    /**
+     * Returns whether the specified state is referenced in any of the state
+     * specs contained within this ColorStateList.
+     * <p>
+     * Any reference, either positive or negative {ex. ~R.attr.state_enabled},
+     * will cause this method to return {@code true}. Wildcards are not counted
+     * as references.
+     *
+     * @param state the state to search for
+     * @return {@code true} if the state if referenced, {@code false} otherwise
+     * @hide Use only as directed. For internal use only.
+     */
+    public boolean hasState(int state) {
+        final int[][] stateSpecs = mStateSpecs;
+        final int specCount = stateSpecs.length;
+        for (int specIndex = 0; specIndex < specCount; specIndex++) {
+            final int[] states = stateSpecs[specIndex];
+            final int stateCount = states.length;
+            for (int stateIndex = 0; stateIndex < stateCount; stateIndex++) {
+                if (states[stateIndex] == state || states[stateIndex] == ~state) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return "ColorStateList{" +
+               "mThemeAttrs=" + Arrays.deepToString(mThemeAttrs) +
+               "mChangingConfigurations=" + mChangingConfigurations +
+               "mStateSpecs=" + Arrays.deepToString(mStateSpecs) +
+               "mColors=" + Arrays.toString(mColors) +
+               "mDefaultColor=" + mDefaultColor + '}';
+    }
+
+    /**
+     * Updates the default color and opacity.
+     */
+    @UnsupportedAppUsage
+    private void onColorsChanged() {
+        int defaultColor = DEFAULT_COLOR;
+        boolean isOpaque = true;
+
+        final int[][] states = mStateSpecs;
+        final int[] colors = mColors;
+        final int N = states.length;
+        if (N > 0) {
+            defaultColor = colors[0];
+
+            for (int i = N - 1; i > 0; i--) {
+                if (states[i].length == 0) {
+                    defaultColor = colors[i];
+                    break;
+                }
+            }
+
+            for (int i = 0; i < N; i++) {
+                if (Color.alpha(colors[i]) != 0xFF) {
+                    isOpaque = false;
+                    break;
+                }
+            }
+        }
+
+        mDefaultColor = defaultColor;
+        mIsOpaque = isOpaque;
+    }
+
+    /**
+     * @return a factory that can create new instances of this ColorStateList
+     * @hide only for resource preloading
+     */
+    public ConstantState<ComplexColor> getConstantState() {
+        if (mFactory == null) {
+            mFactory = new ColorStateListFactory(this);
+        }
+        return mFactory;
+    }
+
+    private static class ColorStateListFactory extends ConstantState<ComplexColor> {
+        private final ColorStateList mSrc;
+
+        @UnsupportedAppUsage
+        public ColorStateListFactory(ColorStateList src) {
+            mSrc = src;
+        }
+
+        @Override
+        public @Config int getChangingConfigurations() {
+            return mSrc.mChangingConfigurations;
+        }
+
+        @Override
+        public ColorStateList newInstance() {
+            return mSrc;
+        }
+
+        @Override
+        public ColorStateList newInstance(Resources res, Theme theme) {
+            return (ColorStateList) mSrc.obtainForTheme(theme);
+        }
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        if (canApplyTheme()) {
+            Log.w(TAG, "Wrote partially-resolved ColorStateList to parcel!");
+        }
+        final int N = mStateSpecs.length;
+        dest.writeInt(N);
+        for (int i = 0; i < N; i++) {
+            dest.writeIntArray(mStateSpecs[i]);
+        }
+        dest.writeIntArray(mColors);
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<ColorStateList> CREATOR =
+            new Parcelable.Creator<ColorStateList>() {
+        @Override
+        public ColorStateList[] newArray(int size) {
+            return new ColorStateList[size];
+        }
+
+        @Override
+        public ColorStateList createFromParcel(Parcel source) {
+            final int N = source.readInt();
+            final int[][] stateSpecs = new int[N][];
+            for (int i = 0; i < N; i++) {
+                stateSpecs[i] = source.createIntArray();
+            }
+            final int[] colors = source.createIntArray();
+            return new ColorStateList(stateSpecs, colors);
+        }
+    };
+}
diff --git a/android/content/res/CompatResources.java b/android/content/res/CompatResources.java
new file mode 100644
index 0000000..829b6b7
--- /dev/null
+++ b/android/content/res/CompatResources.java
@@ -0,0 +1,69 @@
+/*
+ * 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.content.res;
+
+import android.annotation.ColorRes;
+import android.annotation.DrawableRes;
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * Version of resources generated for apps targeting <26.
+ * @hide
+ */
+public class CompatResources extends Resources {
+
+    private WeakReference<Context> mContext;
+
+    public CompatResources(ClassLoader cls) {
+        super(cls);
+        mContext = new WeakReference<>(null);
+    }
+
+    /**
+     * @hide
+     */
+    public void setContext(Context context) {
+        mContext = new WeakReference<>(context);
+    }
+
+    @Override
+    public Drawable getDrawable(@DrawableRes int id) throws NotFoundException {
+        return getDrawable(id, getTheme());
+    }
+
+    @Override
+    public Drawable getDrawableForDensity(@DrawableRes int id, int density)
+            throws NotFoundException {
+        return getDrawableForDensity(id, density, getTheme());
+    }
+
+    @Override
+    public int getColor(@ColorRes int id) throws NotFoundException {
+        return getColor(id, getTheme());
+    }
+
+    @Override
+    public ColorStateList getColorStateList(@ColorRes int id) throws NotFoundException {
+        return getColorStateList(id, getTheme());
+    }
+
+    private Theme getTheme() {
+        Context c = mContext.get();
+        return c != null ? c.getTheme() : null;
+    }
+}
diff --git a/android/content/res/CompatibilityInfo.java b/android/content/res/CompatibilityInfo.java
new file mode 100644
index 0000000..c66c70a
--- /dev/null
+++ b/android/content/res/CompatibilityInfo.java
@@ -0,0 +1,635 @@
+/*
+ * 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.content.res;
+
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.pm.ApplicationInfo;
+import android.graphics.Canvas;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.os.Build;
+import android.os.Build.VERSION_CODES;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.DisplayMetrics;
+import android.view.MotionEvent;
+import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams;
+
+/**
+ * CompatibilityInfo class keeps the information about the screen compatibility mode that the
+ * application is running under.
+ * 
+ *  {@hide} 
+ */
+public class CompatibilityInfo implements Parcelable {
+    /** default compatibility info object for compatible applications */
+    @UnsupportedAppUsage
+    public static final CompatibilityInfo DEFAULT_COMPATIBILITY_INFO = new CompatibilityInfo() {
+    };
+
+    /**
+     * This is the number of pixels we would like to have along the
+     * short axis of an app that needs to run on a normal size screen.
+     */
+    public static final int DEFAULT_NORMAL_SHORT_DIMENSION = 320;
+
+    /**
+     * This is the maximum aspect ratio we will allow while keeping
+     * applications in a compatible screen size.
+     */
+    public static final float MAXIMUM_ASPECT_RATIO = (854f/480f);
+
+    /**
+     *  A compatibility flags
+     */
+    private final int mCompatibilityFlags;
+    
+    /**
+     * A flag mask to tell if the application needs scaling (when mApplicationScale != 1.0f)
+     * {@see compatibilityFlag}
+     */
+    private static final int SCALING_REQUIRED = 1; 
+
+    /**
+     * Application must always run in compatibility mode?
+     */
+    private static final int ALWAYS_NEEDS_COMPAT = 2;
+
+    /**
+     * Application never should run in compatibility mode?
+     */
+    private static final int NEVER_NEEDS_COMPAT = 4;
+
+    /**
+     * Set if the application needs to run in screen size compatibility mode.
+     */
+    private static final int NEEDS_SCREEN_COMPAT = 8;
+
+    /**
+     * Set if the application needs to run in with compat resources.
+     */
+    private static final int NEEDS_COMPAT_RES = 16;
+
+    /**
+     * The effective screen density we have selected for this application.
+     */
+    public final int applicationDensity;
+    
+    /**
+     * Application's scale.
+     */
+    @UnsupportedAppUsage
+    public final float applicationScale;
+
+    /**
+     * Application's inverted scale.
+     */
+    public final float applicationInvertedScale;
+
+    @UnsupportedAppUsage
+    public CompatibilityInfo(ApplicationInfo appInfo, int screenLayout, int sw,
+            boolean forceCompat) {
+        int compatFlags = 0;
+
+        if (appInfo.targetSdkVersion < VERSION_CODES.O) {
+            compatFlags |= NEEDS_COMPAT_RES;
+        }
+        if (appInfo.requiresSmallestWidthDp != 0 || appInfo.compatibleWidthLimitDp != 0
+                || appInfo.largestWidthLimitDp != 0) {
+            // New style screen requirements spec.
+            int required = appInfo.requiresSmallestWidthDp != 0
+                    ? appInfo.requiresSmallestWidthDp
+                    : appInfo.compatibleWidthLimitDp;
+            if (required == 0) {
+                required = appInfo.largestWidthLimitDp;
+            }
+            int compat = appInfo.compatibleWidthLimitDp != 0
+                    ? appInfo.compatibleWidthLimitDp : required;
+            if (compat < required)  {
+                compat = required;
+            }
+            int largest = appInfo.largestWidthLimitDp;
+
+            if (required > DEFAULT_NORMAL_SHORT_DIMENSION) {
+                // For now -- if they require a size larger than the only
+                // size we can do in compatibility mode, then don't ever
+                // allow the app to go in to compat mode.  Trying to run
+                // it at a smaller size it can handle will make it far more
+                // broken than running at a larger size than it wants or
+                // thinks it can handle.
+                compatFlags |= NEVER_NEEDS_COMPAT;
+            } else if (largest != 0 && sw > largest) {
+                // If the screen size is larger than the largest size the
+                // app thinks it can work with, then always force it in to
+                // compatibility mode.
+                compatFlags |= NEEDS_SCREEN_COMPAT | ALWAYS_NEEDS_COMPAT;
+            } else if (compat >= sw) {
+                // The screen size is something the app says it was designed
+                // for, so never do compatibility mode.
+                compatFlags |= NEVER_NEEDS_COMPAT;
+            } else if (forceCompat) {
+                // The app may work better with or without compatibility mode.
+                // Let the user decide.
+                compatFlags |= NEEDS_SCREEN_COMPAT;
+            }
+
+            // Modern apps always support densities.
+            applicationDensity = DisplayMetrics.DENSITY_DEVICE;
+            applicationScale = 1.0f;
+            applicationInvertedScale = 1.0f;
+
+        } else {
+            /**
+             * Has the application said that its UI is expandable?  Based on the
+             * <supports-screen> android:expandible in the manifest.
+             */
+            final int EXPANDABLE = 2;
+
+            /**
+             * Has the application said that its UI supports large screens?  Based on the
+             * <supports-screen> android:largeScreens in the manifest.
+             */
+            final int LARGE_SCREENS = 8;
+
+            /**
+             * Has the application said that its UI supports xlarge screens?  Based on the
+             * <supports-screen> android:xlargeScreens in the manifest.
+             */
+            final int XLARGE_SCREENS = 32;
+
+            int sizeInfo = 0;
+
+            // We can't rely on the application always setting
+            // FLAG_RESIZEABLE_FOR_SCREENS so will compute it based on various input.
+            boolean anyResizeable = false;
+
+            if ((appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS) != 0) {
+                sizeInfo |= LARGE_SCREENS;
+                anyResizeable = true;
+                if (!forceCompat) {
+                    // If we aren't forcing the app into compatibility mode, then
+                    // assume if it supports large screens that we should allow it
+                    // to use the full space of an xlarge screen as well.
+                    sizeInfo |= XLARGE_SCREENS | EXPANDABLE;
+                }
+            }
+            if ((appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS) != 0) {
+                anyResizeable = true;
+                if (!forceCompat) {
+                    sizeInfo |= XLARGE_SCREENS | EXPANDABLE;
+                }
+            }
+            if ((appInfo.flags & ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS) != 0) {
+                anyResizeable = true;
+                sizeInfo |= EXPANDABLE;
+            }
+
+            if (forceCompat) {
+                // If we are forcing compatibility mode, then ignore an app that
+                // just says it is resizable for screens.  We'll only have it fill
+                // the screen if it explicitly says it supports the screen size we
+                // are running in.
+                sizeInfo &= ~EXPANDABLE;
+            }
+
+            compatFlags |= NEEDS_SCREEN_COMPAT;
+            switch (screenLayout&Configuration.SCREENLAYOUT_SIZE_MASK) {
+                case Configuration.SCREENLAYOUT_SIZE_XLARGE:
+                    if ((sizeInfo&XLARGE_SCREENS) != 0) {
+                        compatFlags &= ~NEEDS_SCREEN_COMPAT;
+                    }
+                    if ((appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS) != 0) {
+                        compatFlags |= NEVER_NEEDS_COMPAT;
+                    }
+                    break;
+                case Configuration.SCREENLAYOUT_SIZE_LARGE:
+                    if ((sizeInfo&LARGE_SCREENS) != 0) {
+                        compatFlags &= ~NEEDS_SCREEN_COMPAT;
+                    }
+                    if ((appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS) != 0) {
+                        compatFlags |= NEVER_NEEDS_COMPAT;
+                    }
+                    break;
+            }
+
+            if ((screenLayout&Configuration.SCREENLAYOUT_COMPAT_NEEDED) != 0) {
+                if ((sizeInfo&EXPANDABLE) != 0) {
+                    compatFlags &= ~NEEDS_SCREEN_COMPAT;
+                } else if (!anyResizeable) {
+                    compatFlags |= ALWAYS_NEEDS_COMPAT;
+                }
+            } else {
+                compatFlags &= ~NEEDS_SCREEN_COMPAT;
+                compatFlags |= NEVER_NEEDS_COMPAT;
+            }
+
+            if ((appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES) != 0) {
+                applicationDensity = DisplayMetrics.DENSITY_DEVICE;
+                applicationScale = 1.0f;
+                applicationInvertedScale = 1.0f;
+            } else {
+                applicationDensity = DisplayMetrics.DENSITY_DEFAULT;
+                applicationScale = DisplayMetrics.DENSITY_DEVICE
+                        / (float) DisplayMetrics.DENSITY_DEFAULT;
+                applicationInvertedScale = 1.0f / applicationScale;
+                compatFlags |= SCALING_REQUIRED;
+            }
+        }
+
+        mCompatibilityFlags = compatFlags;
+    }
+
+    private CompatibilityInfo(int compFlags,
+            int dens, float scale, float invertedScale) {
+        mCompatibilityFlags = compFlags;
+        applicationDensity = dens;
+        applicationScale = scale;
+        applicationInvertedScale = invertedScale;
+    }
+
+    @UnsupportedAppUsage
+    private CompatibilityInfo() {
+        this(NEVER_NEEDS_COMPAT, DisplayMetrics.DENSITY_DEVICE,
+                1.0f,
+                1.0f);
+    }
+
+    /**
+     * @return true if the scaling is required
+     */
+    @UnsupportedAppUsage
+    public boolean isScalingRequired() {
+        return (mCompatibilityFlags&SCALING_REQUIRED) != 0;
+    }
+    
+    @UnsupportedAppUsage
+    public boolean supportsScreen() {
+        return (mCompatibilityFlags&NEEDS_SCREEN_COMPAT) == 0;
+    }
+    
+    public boolean neverSupportsScreen() {
+        return (mCompatibilityFlags&ALWAYS_NEEDS_COMPAT) != 0;
+    }
+
+    public boolean alwaysSupportsScreen() {
+        return (mCompatibilityFlags&NEVER_NEEDS_COMPAT) != 0;
+    }
+
+    public boolean needsCompatResources() {
+        return (mCompatibilityFlags&NEEDS_COMPAT_RES) != 0;
+    }
+
+    /**
+     * Returns the translator which translates the coordinates in compatibility mode.
+     * @param params the window's parameter
+     */
+    @UnsupportedAppUsage
+    public Translator getTranslator() {
+        return isScalingRequired() ? new Translator() : null;
+    }
+
+    /**
+     * A helper object to translate the screen and window coordinates back and forth.
+     * @hide
+     */
+    public class Translator {
+        @UnsupportedAppUsage
+        final public float applicationScale;
+        @UnsupportedAppUsage
+        final public float applicationInvertedScale;
+        
+        private Rect mContentInsetsBuffer = null;
+        private Rect mVisibleInsetsBuffer = null;
+        private Region mTouchableAreaBuffer = null;
+        
+        Translator(float applicationScale, float applicationInvertedScale) {
+            this.applicationScale = applicationScale;
+            this.applicationInvertedScale = applicationInvertedScale;
+        }
+
+        Translator() {
+            this(CompatibilityInfo.this.applicationScale,
+                    CompatibilityInfo.this.applicationInvertedScale);
+        }
+
+        /**
+         * Translate the screen rect to the application frame.
+         */
+        @UnsupportedAppUsage
+        public void translateRectInScreenToAppWinFrame(Rect rect) {
+            rect.scale(applicationInvertedScale);
+        }
+
+        /**
+         * Translate the region in window to screen. 
+         */
+        @UnsupportedAppUsage
+        public void translateRegionInWindowToScreen(Region transparentRegion) {
+            transparentRegion.scale(applicationScale);
+        }
+
+        /**
+         * Apply translation to the canvas that is necessary to draw the content.
+         */
+        @UnsupportedAppUsage
+        public void translateCanvas(Canvas canvas) {
+            if (applicationScale == 1.5f) {
+                /*  When we scale for compatibility, we can put our stretched
+                    bitmaps and ninepatches on exacty 1/2 pixel boundaries,
+                    which can give us inconsistent drawing due to imperfect
+                    float precision in the graphics engine's inverse matrix.
+                 
+                    As a work-around, we translate by a tiny amount to avoid
+                    landing on exact pixel centers and boundaries, giving us
+                    the slop we need to draw consistently.
+                 
+                    This constant is meant to resolve to 1/255 after it is
+                    scaled by 1.5 (applicationScale). Note, this is just a guess
+                    as to what is small enough not to create its own artifacts,
+                    and big enough to avoid the precision problems. Feel free
+                    to experiment with smaller values as you choose.
+                 */
+                final float tinyOffset = 2.0f / (3 * 255);
+                canvas.translate(tinyOffset, tinyOffset);
+            }
+            canvas.scale(applicationScale, applicationScale);
+        }
+
+        /**
+         * Translate the motion event captured on screen to the application's window.
+         */
+        @UnsupportedAppUsage
+        public void translateEventInScreenToAppWindow(MotionEvent event) {
+            event.scale(applicationInvertedScale);
+        }
+
+        /**
+         * Translate the window's layout parameter, from application's view to
+         * Screen's view.
+         */
+        @UnsupportedAppUsage
+        public void translateWindowLayout(WindowManager.LayoutParams params) {
+            params.scale(applicationScale);
+        }
+        
+        /**
+         * Translate a Rect in application's window to screen.
+         */
+        @UnsupportedAppUsage
+        public void translateRectInAppWindowToScreen(Rect rect) {
+            rect.scale(applicationScale);
+        }
+ 
+        /**
+         * Translate a Rect in screen coordinates into the app window's coordinates.
+         */
+        @UnsupportedAppUsage
+        public void translateRectInScreenToAppWindow(Rect rect) {
+            rect.scale(applicationInvertedScale);
+        }
+
+        /**
+         * Translate a Point in screen coordinates into the app window's coordinates.
+         */
+        public void translatePointInScreenToAppWindow(PointF point) {
+            final float scale = applicationInvertedScale;
+            if (scale != 1.0f) {
+                point.x *= scale;
+                point.y *= scale;
+            }
+        }
+
+        /**
+         * Translate the location of the sub window.
+         * @param params
+         */
+        public void translateLayoutParamsInAppWindowToScreen(LayoutParams params) {
+            params.scale(applicationScale);
+        }
+
+        /**
+         * Translate the content insets in application window to Screen. This uses
+         * the internal buffer for content insets to avoid extra object allocation.
+         */
+        @UnsupportedAppUsage
+        public Rect getTranslatedContentInsets(Rect contentInsets) {
+            if (mContentInsetsBuffer == null) mContentInsetsBuffer = new Rect();
+            mContentInsetsBuffer.set(contentInsets);
+            translateRectInAppWindowToScreen(mContentInsetsBuffer);
+            return mContentInsetsBuffer;
+        }
+
+        /**
+         * Translate the visible insets in application window to Screen. This uses
+         * the internal buffer for visible insets to avoid extra object allocation.
+         */
+        public Rect getTranslatedVisibleInsets(Rect visibleInsets) {
+            if (mVisibleInsetsBuffer == null) mVisibleInsetsBuffer = new Rect();
+            mVisibleInsetsBuffer.set(visibleInsets);
+            translateRectInAppWindowToScreen(mVisibleInsetsBuffer);
+            return mVisibleInsetsBuffer;
+        }
+
+        /**
+         * Translate the touchable area in application window to Screen. This uses
+         * the internal buffer for touchable area to avoid extra object allocation.
+         */
+        public Region getTranslatedTouchableArea(Region touchableArea) {
+            if (mTouchableAreaBuffer == null) mTouchableAreaBuffer = new Region();
+            mTouchableAreaBuffer.set(touchableArea);
+            mTouchableAreaBuffer.scale(applicationScale);
+            return mTouchableAreaBuffer;
+        }
+    }
+
+    public void applyToDisplayMetrics(DisplayMetrics inoutDm) {
+        if (!supportsScreen()) {
+            // This is a larger screen device and the app is not
+            // compatible with large screens, so diddle it.
+            CompatibilityInfo.computeCompatibleScaling(inoutDm, inoutDm);
+        } else {
+            inoutDm.widthPixels = inoutDm.noncompatWidthPixels;
+            inoutDm.heightPixels = inoutDm.noncompatHeightPixels;
+        }
+
+        if (isScalingRequired()) {
+            float invertedRatio = applicationInvertedScale;
+            inoutDm.density = inoutDm.noncompatDensity * invertedRatio;
+            inoutDm.densityDpi = (int)((inoutDm.noncompatDensityDpi * invertedRatio) + .5f);
+            inoutDm.scaledDensity = inoutDm.noncompatScaledDensity * invertedRatio;
+            inoutDm.xdpi = inoutDm.noncompatXdpi * invertedRatio;
+            inoutDm.ydpi = inoutDm.noncompatYdpi * invertedRatio;
+            inoutDm.widthPixels = (int) (inoutDm.widthPixels * invertedRatio + 0.5f);
+            inoutDm.heightPixels = (int) (inoutDm.heightPixels * invertedRatio + 0.5f);
+        }
+    }
+
+    public void applyToConfiguration(int displayDensity, Configuration inoutConfig) {
+        if (!supportsScreen()) {
+            // This is a larger screen device and the app is not
+            // compatible with large screens, so we are forcing it to
+            // run as if the screen is normal size.
+            inoutConfig.screenLayout =
+                    (inoutConfig.screenLayout&~Configuration.SCREENLAYOUT_SIZE_MASK)
+                    | Configuration.SCREENLAYOUT_SIZE_NORMAL;
+            inoutConfig.screenWidthDp = inoutConfig.compatScreenWidthDp;
+            inoutConfig.screenHeightDp = inoutConfig.compatScreenHeightDp;
+            inoutConfig.smallestScreenWidthDp = inoutConfig.compatSmallestScreenWidthDp;
+        }
+        inoutConfig.densityDpi = displayDensity;
+        if (isScalingRequired()) {
+            float invertedRatio = applicationInvertedScale;
+            inoutConfig.densityDpi = (int)((inoutConfig.densityDpi * invertedRatio) + .5f);
+        }
+    }
+
+    /**
+     * Compute the frame Rect for applications runs under compatibility mode.
+     *
+     * @param dm the display metrics used to compute the frame size.
+     * @param outDm If non-null the width and height will be set to their scaled values.
+     * @return Returns the scaling factor for the window.
+     */
+    @UnsupportedAppUsage
+    public static float computeCompatibleScaling(DisplayMetrics dm, DisplayMetrics outDm) {
+        final int width = dm.noncompatWidthPixels;
+        final int height = dm.noncompatHeightPixels;
+        int shortSize, longSize;
+        if (width < height) {
+            shortSize = width;
+            longSize = height;
+        } else {
+            shortSize = height;
+            longSize = width;
+        }
+        int newShortSize = (int)(DEFAULT_NORMAL_SHORT_DIMENSION * dm.density + 0.5f);
+        float aspect = ((float)longSize) / shortSize;
+        if (aspect > MAXIMUM_ASPECT_RATIO) {
+            aspect = MAXIMUM_ASPECT_RATIO;
+        }
+        int newLongSize = (int)(newShortSize * aspect + 0.5f);
+        int newWidth, newHeight;
+        if (width < height) {
+            newWidth = newShortSize;
+            newHeight = newLongSize;
+        } else {
+            newWidth = newLongSize;
+            newHeight = newShortSize;
+        }
+
+        float sw = width/(float)newWidth;
+        float sh = height/(float)newHeight;
+        float scale = sw < sh ? sw : sh;
+        if (scale < 1) {
+            scale = 1;
+        }
+
+        if (outDm != null) {
+            outDm.widthPixels = newWidth;
+            outDm.heightPixels = newHeight;
+        }
+
+        return scale;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        try {
+            CompatibilityInfo oc = (CompatibilityInfo)o;
+            if (mCompatibilityFlags != oc.mCompatibilityFlags) return false;
+            if (applicationDensity != oc.applicationDensity) return false;
+            if (applicationScale != oc.applicationScale) return false;
+            if (applicationInvertedScale != oc.applicationInvertedScale) return false;
+            return true;
+        } catch (ClassCastException e) {
+            return false;
+        }
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder(128);
+        sb.append("{");
+        sb.append(applicationDensity);
+        sb.append("dpi");
+        if (isScalingRequired()) {
+            sb.append(" ");
+            sb.append(applicationScale);
+            sb.append("x");
+        }
+        if (!supportsScreen()) {
+            sb.append(" resizing");
+        }
+        if (neverSupportsScreen()) {
+            sb.append(" never-compat");
+        }
+        if (alwaysSupportsScreen()) {
+            sb.append(" always-compat");
+        }
+        sb.append("}");
+        return sb.toString();
+    }
+
+    @Override
+    public int hashCode() {
+        int result = 17;
+        result = 31 * result + mCompatibilityFlags;
+        result = 31 * result + applicationDensity;
+        result = 31 * result + Float.floatToIntBits(applicationScale);
+        result = 31 * result + Float.floatToIntBits(applicationInvertedScale);
+        return result;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mCompatibilityFlags);
+        dest.writeInt(applicationDensity);
+        dest.writeFloat(applicationScale);
+        dest.writeFloat(applicationInvertedScale);
+    }
+
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    public static final @android.annotation.NonNull Parcelable.Creator<CompatibilityInfo> CREATOR
+            = new Parcelable.Creator<CompatibilityInfo>() {
+        @Override
+        public CompatibilityInfo createFromParcel(Parcel source) {
+            return new CompatibilityInfo(source);
+        }
+
+        @Override
+        public CompatibilityInfo[] newArray(int size) {
+            return new CompatibilityInfo[size];
+        }
+    };
+
+    private CompatibilityInfo(Parcel source) {
+        mCompatibilityFlags = source.readInt();
+        applicationDensity = source.readInt();
+        applicationScale = source.readFloat();
+        applicationInvertedScale = source.readFloat();
+    }
+}
diff --git a/android/content/res/ComplexColor.java b/android/content/res/ComplexColor.java
new file mode 100644
index 0000000..58c6fc5
--- /dev/null
+++ b/android/content/res/ComplexColor.java
@@ -0,0 +1,78 @@
+/*
+ * 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.content.res;
+
+import android.annotation.ColorInt;
+import android.content.res.Resources.Theme;
+import android.graphics.Color;
+
+/**
+ * Defines an abstract class for the complex color information, like
+ * {@link android.content.res.ColorStateList} or {@link android.content.res.GradientColor}
+ * @hide
+ */
+public abstract class ComplexColor {
+    private int mChangingConfigurations;
+
+    /**
+     * @return {@code true}  if this ComplexColor changes color based on state, {@code false}
+     * otherwise.
+     */
+    public boolean isStateful() { return false; }
+
+    /**
+     * @return the default color.
+     */
+    @ColorInt
+    public abstract int getDefaultColor();
+
+    /**
+     * @hide only for resource preloading
+     *
+     */
+    public abstract ConstantState<ComplexColor> getConstantState();
+
+    /**
+     * @hide only for resource preloading
+     */
+    public abstract boolean canApplyTheme();
+
+    /**
+     * @hide only for resource preloading
+     */
+    public abstract ComplexColor obtainForTheme(Theme t);
+
+    /**
+     * @hide only for resource preloading
+     */
+    final void setBaseChangingConfigurations(int changingConfigurations) {
+        mChangingConfigurations = changingConfigurations;
+    }
+
+    /**
+     * Returns a mask of the configuration parameters for which this color
+     * may change, requiring that it be re-created.
+     *
+     * @return a mask of the changing configuration parameters, as defined by
+     *         {@link android.content.pm.ActivityInfo}
+     *
+     * @see android.content.pm.ActivityInfo
+     */
+    public int getChangingConfigurations() {
+        return mChangingConfigurations;
+    }
+}
diff --git a/android/content/res/ComplexColor_Accessor.java b/android/content/res/ComplexColor_Accessor.java
new file mode 100644
index 0000000..09c0260
--- /dev/null
+++ b/android/content/res/ComplexColor_Accessor.java
@@ -0,0 +1,46 @@
+/*
+ * 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.content.res;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.res.Resources.Theme;
+import android.util.AttributeSet;
+
+import java.io.IOException;
+
+/**
+ * Class that provides access to the {@link GradientColor#createFromXmlInner(Resources,
+ * XmlPullParser, AttributeSet, Theme)} and {@link ColorStateList#createFromXmlInner(Resources,
+ * XmlPullParser, AttributeSet, Theme)} methods
+ */
+public class ComplexColor_Accessor {
+    public static GradientColor createGradientColorFromXmlInner(@NonNull Resources r,
+            @NonNull XmlPullParser parser, @NonNull AttributeSet attrs, @Nullable Theme theme)
+            throws IOException, XmlPullParserException {
+        return GradientColor.createFromXmlInner(r, parser, attrs, theme);
+    }
+
+    public static ColorStateList createColorStateListFromXmlInner(@NonNull Resources r,
+            @NonNull XmlPullParser parser, @NonNull AttributeSet attrs, @Nullable Theme theme)
+            throws IOException, XmlPullParserException {
+        return ColorStateList.createFromXmlInner(r, parser, attrs, theme);
+    }
+}
diff --git a/android/content/res/Configuration.java b/android/content/res/Configuration.java
new file mode 100644
index 0000000..6a9e0aa
--- /dev/null
+++ b/android/content/res/Configuration.java
@@ -0,0 +1,2745 @@
+/*
+ * 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.content.res;
+
+import static android.content.ConfigurationProto.COLOR_MODE;
+import static android.content.ConfigurationProto.DENSITY_DPI;
+import static android.content.ConfigurationProto.FONT_SCALE;
+import static android.content.ConfigurationProto.HARD_KEYBOARD_HIDDEN;
+import static android.content.ConfigurationProto.KEYBOARD;
+import static android.content.ConfigurationProto.KEYBOARD_HIDDEN;
+import static android.content.ConfigurationProto.LOCALES;
+import static android.content.ConfigurationProto.LOCALE_LIST;
+import static android.content.ConfigurationProto.MCC;
+import static android.content.ConfigurationProto.MNC;
+import static android.content.ConfigurationProto.NAVIGATION;
+import static android.content.ConfigurationProto.NAVIGATION_HIDDEN;
+import static android.content.ConfigurationProto.ORIENTATION;
+import static android.content.ConfigurationProto.SCREEN_HEIGHT_DP;
+import static android.content.ConfigurationProto.SCREEN_LAYOUT;
+import static android.content.ConfigurationProto.SCREEN_WIDTH_DP;
+import static android.content.ConfigurationProto.SMALLEST_SCREEN_WIDTH_DP;
+import static android.content.ConfigurationProto.TOUCHSCREEN;
+import static android.content.ConfigurationProto.UI_MODE;
+import static android.content.ConfigurationProto.WINDOW_CONFIGURATION;
+import static android.content.ResourcesConfigurationProto.CONFIGURATION;
+import static android.content.ResourcesConfigurationProto.SCREEN_HEIGHT_PX;
+import static android.content.ResourcesConfigurationProto.SCREEN_WIDTH_PX;
+import static android.content.ResourcesConfigurationProto.SDK_VERSION;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.TestApi;
+import android.app.UiModeManager;
+import android.app.WindowConfiguration;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.LocaleProto;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ActivityInfo.Config;
+import android.os.Build;
+import android.os.LocaleList;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+import android.util.DisplayMetrics;
+import android.util.Slog;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
+import android.util.proto.WireTypeMismatchException;
+import android.view.View;
+
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.IllformedLocaleException;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * This class describes all device configuration information that can
+ * impact the resources the application retrieves.  This includes both
+ * user-specified configuration options (locale list and scaling) as well
+ * as device configurations (such as input modes, screen size and screen orientation).
+ * <p>You can acquire this object from {@link Resources}, using {@link
+ * Resources#getConfiguration}. Thus, from an activity, you can get it by chaining the request
+ * with {@link android.app.Activity#getResources}:</p>
+ * <pre>Configuration config = getResources().getConfiguration();</pre>
+ */
+public final class Configuration implements Parcelable, Comparable<Configuration> {
+    /** @hide */
+    public static final Configuration EMPTY = new Configuration();
+
+    private static final String TAG = "Configuration";
+
+    /**
+     * Current user preference for the scaling factor for fonts, relative
+     * to the base density scaling.
+     */
+    public float fontScale;
+
+    /**
+     * IMSI MCC (Mobile Country Code), corresponding to
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#MccQualifier">mcc</a>
+     * resource qualifier.  0 if undefined.
+     */
+    public int mcc;
+
+    /**
+     * IMSI MNC (Mobile Network Code), corresponding to
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#MccQualifier">mnc</a>
+     * resource qualifier.  0 if undefined. Note that the actual MNC may be 0; in order to check
+     * for this use the {@link #MNC_ZERO} symbol.
+     */
+    public int mnc;
+
+    /**
+     * Constant used to to represent MNC (Mobile Network Code) zero.
+     * 0 cannot be used, since it is used to represent an undefined MNC.
+     */
+    public static final int MNC_ZERO = 0xffff;
+
+    /**
+     * Current user preference for the locale, corresponding to
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#LocaleQualifier">locale</a>
+     * resource qualifier.
+     *
+     * @deprecated Do not set or read this directly. Use {@link #getLocales()} and
+     * {@link #setLocales(LocaleList)}. If only the primary locale is needed,
+     * <code>getLocales().get(0)</code> is now the preferred accessor.
+     */
+    @Deprecated public Locale locale;
+
+    private LocaleList mLocaleList;
+
+    /**
+     * Locale should persist on setting.  This is hidden because it is really
+     * questionable whether this is the right way to expose the functionality.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public boolean userSetLocale;
+
+
+    /** Constant for {@link #colorMode}: bits that encode whether the screen is wide gamut. */
+    public static final int COLOR_MODE_WIDE_COLOR_GAMUT_MASK = 0x3;
+    /**
+     * Constant for {@link #colorMode}: a {@link #COLOR_MODE_WIDE_COLOR_GAMUT_MASK} value
+     * indicating that it is unknown whether or not the screen is wide gamut.
+     */
+    public static final int COLOR_MODE_WIDE_COLOR_GAMUT_UNDEFINED = 0x0;
+    /**
+     * Constant for {@link #colorMode}: a {@link #COLOR_MODE_WIDE_COLOR_GAMUT_MASK} value
+     * indicating that the screen is not wide gamut.
+     * <p>Corresponds to the <code>-nowidecg</code> resource qualifier.</p>
+     */
+    public static final int COLOR_MODE_WIDE_COLOR_GAMUT_NO = 0x1;
+    /**
+     * Constant for {@link #colorMode}: a {@link #COLOR_MODE_WIDE_COLOR_GAMUT_MASK} value
+     * indicating that the screen is wide gamut.
+     * <p>Corresponds to the <code>-widecg</code> resource qualifier.</p>
+     */
+    public static final int COLOR_MODE_WIDE_COLOR_GAMUT_YES = 0x2;
+
+    /** Constant for {@link #colorMode}: bits that encode the dynamic range of the screen. */
+    public static final int COLOR_MODE_HDR_MASK = 0xc;
+    /** Constant for {@link #colorMode}: bits shift to get the screen dynamic range. */
+    public static final int COLOR_MODE_HDR_SHIFT = 2;
+    /**
+     * Constant for {@link #colorMode}: a {@link #COLOR_MODE_HDR_MASK} value
+     * indicating that it is unknown whether or not the screen is HDR.
+     */
+    public static final int COLOR_MODE_HDR_UNDEFINED = 0x0;
+    /**
+     * Constant for {@link #colorMode}: a {@link #COLOR_MODE_HDR_MASK} value
+     * indicating that the screen is not HDR (low/standard dynamic range).
+     * <p>Corresponds to the <code>-lowdr</code> resource qualifier.</p>
+     */
+    public static final int COLOR_MODE_HDR_NO = 0x1 << COLOR_MODE_HDR_SHIFT;
+    /**
+     * Constant for {@link #colorMode}: a {@link #COLOR_MODE_HDR_MASK} value
+     * indicating that the screen is HDR (dynamic range).
+     * <p>Corresponds to the <code>-highdr</code> resource qualifier.</p>
+     */
+    public static final int COLOR_MODE_HDR_YES = 0x2 << COLOR_MODE_HDR_SHIFT;
+
+    /** Constant for {@link #colorMode}: a value indicating that the color mode is undefined */
+    @SuppressWarnings("PointlessBitwiseExpression")
+    public static final int COLOR_MODE_UNDEFINED = COLOR_MODE_WIDE_COLOR_GAMUT_UNDEFINED |
+            COLOR_MODE_HDR_UNDEFINED;
+
+    /**
+     * Bit mask of color capabilities of the screen. Currently there are two fields:
+     * <p>The {@link #COLOR_MODE_WIDE_COLOR_GAMUT_MASK} bits define the color gamut of
+     * the screen. They may be one of
+     * {@link #COLOR_MODE_WIDE_COLOR_GAMUT_NO} or {@link #COLOR_MODE_WIDE_COLOR_GAMUT_YES}.</p>
+     *
+     * <p>The {@link #COLOR_MODE_HDR_MASK} defines the dynamic range of the screen. They may be
+     * one of {@link #COLOR_MODE_HDR_NO} or {@link #COLOR_MODE_HDR_YES}.</p>
+     *
+     * <p>See <a href="{@docRoot}guide/practices/screens_support.html">Supporting
+     * Multiple Screens</a> for more information.</p>
+     */
+    public int colorMode;
+
+    /** Constant for {@link #screenLayout}: bits that encode the size. */
+    public static final int SCREENLAYOUT_SIZE_MASK = 0x0f;
+    /** Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_SIZE_MASK}
+     * value indicating that no size has been set. */
+    public static final int SCREENLAYOUT_SIZE_UNDEFINED = 0x00;
+    /** Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_SIZE_MASK}
+     * value indicating the screen is at least approximately 320x426 dp units,
+     * corresponds to the
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#ScreenSizeQualifier">small</a>
+     * resource qualifier.
+     * See <a href="{@docRoot}guide/practices/screens_support.html">Supporting
+     * Multiple Screens</a> for more information. */
+    public static final int SCREENLAYOUT_SIZE_SMALL = 0x01;
+    /** Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_SIZE_MASK}
+     * value indicating the screen is at least approximately 320x470 dp units,
+     * corresponds to the
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#ScreenSizeQualifier">normal</a>
+     * resource qualifier.
+     * See <a href="{@docRoot}guide/practices/screens_support.html">Supporting
+     * Multiple Screens</a> for more information. */
+    public static final int SCREENLAYOUT_SIZE_NORMAL = 0x02;
+    /** Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_SIZE_MASK}
+     * value indicating the screen is at least approximately 480x640 dp units,
+     * corresponds to the
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#ScreenSizeQualifier">large</a>
+     * resource qualifier.
+     * See <a href="{@docRoot}guide/practices/screens_support.html">Supporting
+     * Multiple Screens</a> for more information. */
+    public static final int SCREENLAYOUT_SIZE_LARGE = 0x03;
+    /** Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_SIZE_MASK}
+     * value indicating the screen is at least approximately 720x960 dp units,
+     * corresponds to the
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#ScreenSizeQualifier">xlarge</a>
+     * resource qualifier.
+     * See <a href="{@docRoot}guide/practices/screens_support.html">Supporting
+     * Multiple Screens</a> for more information.*/
+    public static final int SCREENLAYOUT_SIZE_XLARGE = 0x04;
+
+    /** Constant for {@link #screenLayout}: bits that encode the aspect ratio. */
+    public static final int SCREENLAYOUT_LONG_MASK = 0x30;
+    /** Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_LONG_MASK}
+     * value indicating that no size has been set. */
+    public static final int SCREENLAYOUT_LONG_UNDEFINED = 0x00;
+    /** Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_LONG_MASK}
+     * value that corresponds to the
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#ScreenAspectQualifier">notlong</a>
+     * resource qualifier. */
+    public static final int SCREENLAYOUT_LONG_NO = 0x10;
+    /** Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_LONG_MASK}
+     * value that corresponds to the
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#ScreenAspectQualifier">long</a>
+     * resource qualifier. */
+    public static final int SCREENLAYOUT_LONG_YES = 0x20;
+
+    /** Constant for {@link #screenLayout}: bits that encode the layout direction. */
+    public static final int SCREENLAYOUT_LAYOUTDIR_MASK = 0xC0;
+    /** Constant for {@link #screenLayout}: bits shift to get the layout direction. */
+    public static final int SCREENLAYOUT_LAYOUTDIR_SHIFT = 6;
+    /** Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_LAYOUTDIR_MASK}
+     * value indicating that no layout dir has been set. */
+    public static final int SCREENLAYOUT_LAYOUTDIR_UNDEFINED = 0x00;
+    /** Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_LAYOUTDIR_MASK}
+     * value indicating that a layout dir has been set to LTR. */
+    public static final int SCREENLAYOUT_LAYOUTDIR_LTR = 0x01 << SCREENLAYOUT_LAYOUTDIR_SHIFT;
+    /** Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_LAYOUTDIR_MASK}
+     * value indicating that a layout dir has been set to RTL. */
+    public static final int SCREENLAYOUT_LAYOUTDIR_RTL = 0x02 << SCREENLAYOUT_LAYOUTDIR_SHIFT;
+
+    /** Constant for {@link #screenLayout}: bits that encode roundness of the screen. */
+    public static final int SCREENLAYOUT_ROUND_MASK = 0x300;
+    /** @hide Constant for {@link #screenLayout}: bit shift to get to screen roundness bits */
+    public static final int SCREENLAYOUT_ROUND_SHIFT = 8;
+    /**
+     * Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_ROUND_MASK} value indicating
+     * that it is unknown whether or not the screen has a round shape.
+     */
+    public static final int SCREENLAYOUT_ROUND_UNDEFINED = 0x00;
+    /**
+     * Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_ROUND_MASK} value indicating
+     * that the screen does not have a rounded shape.
+     */
+    public static final int SCREENLAYOUT_ROUND_NO = 0x1 << SCREENLAYOUT_ROUND_SHIFT;
+    /**
+     * Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_ROUND_MASK} value indicating
+     * that the screen has a rounded shape. Corners may not be visible to the user;
+     * developers should pay special attention to the {@link android.view.WindowInsets} delivered
+     * to views for more information about ensuring content is not obscured.
+     *
+     * <p>Corresponds to the <code>-round</code> resource qualifier.</p>
+     */
+    public static final int SCREENLAYOUT_ROUND_YES = 0x2 << SCREENLAYOUT_ROUND_SHIFT;
+
+    /** Constant for {@link #screenLayout}: a value indicating that screenLayout is undefined */
+    public static final int SCREENLAYOUT_UNDEFINED = SCREENLAYOUT_SIZE_UNDEFINED |
+            SCREENLAYOUT_LONG_UNDEFINED | SCREENLAYOUT_LAYOUTDIR_UNDEFINED |
+            SCREENLAYOUT_ROUND_UNDEFINED;
+
+    /**
+     * Special flag we generate to indicate that the screen layout requires
+     * us to use a compatibility mode for apps that are not modern layout
+     * aware.
+     * @hide
+     */
+    public static final int SCREENLAYOUT_COMPAT_NEEDED = 0x10000000;
+
+    /**
+     * Bit mask of overall layout of the screen.  Currently there are four
+     * fields:
+     * <p>The {@link #SCREENLAYOUT_SIZE_MASK} bits define the overall size
+     * of the screen.  They may be one of
+     * {@link #SCREENLAYOUT_SIZE_SMALL}, {@link #SCREENLAYOUT_SIZE_NORMAL},
+     * {@link #SCREENLAYOUT_SIZE_LARGE}, or {@link #SCREENLAYOUT_SIZE_XLARGE}.</p>
+     *
+     * <p>The {@link #SCREENLAYOUT_LONG_MASK} defines whether the screen
+     * is wider/taller than normal.  They may be one of
+     * {@link #SCREENLAYOUT_LONG_NO} or {@link #SCREENLAYOUT_LONG_YES}.</p>
+     *
+     * <p>The {@link #SCREENLAYOUT_LAYOUTDIR_MASK} defines whether the screen layout
+     * is either LTR or RTL.  They may be one of
+     * {@link #SCREENLAYOUT_LAYOUTDIR_LTR} or {@link #SCREENLAYOUT_LAYOUTDIR_RTL}.</p>
+     *
+     * <p>The {@link #SCREENLAYOUT_ROUND_MASK} defines whether the screen has a rounded
+     * shape. They may be one of {@link #SCREENLAYOUT_ROUND_NO} or {@link #SCREENLAYOUT_ROUND_YES}.
+     * </p>
+     *
+     * <p>See <a href="{@docRoot}guide/practices/screens_support.html">Supporting
+     * Multiple Screens</a> for more information.</p>
+     */
+    public int screenLayout;
+
+    /**
+     * Configuration relating to the windowing state of the object associated with this
+     * Configuration. Contents of this field are not intended to affect resources, but need to be
+     * communicated and propagated at the same time as the rest of Configuration.
+     * @hide
+     */
+    @TestApi
+    public final WindowConfiguration windowConfiguration = new WindowConfiguration();
+
+    /** @hide */
+    static public int resetScreenLayout(int curLayout) {
+        return (curLayout&~(SCREENLAYOUT_LONG_MASK | SCREENLAYOUT_SIZE_MASK
+                        | SCREENLAYOUT_COMPAT_NEEDED))
+                | (SCREENLAYOUT_LONG_YES | SCREENLAYOUT_SIZE_XLARGE);
+    }
+
+    /** @hide */
+    static public int reduceScreenLayout(int curLayout, int longSizeDp, int shortSizeDp) {
+        int screenLayoutSize;
+        boolean screenLayoutLong;
+        boolean screenLayoutCompatNeeded;
+
+        // These semi-magic numbers define our compatibility modes for
+        // applications with different screens.  These are guarantees to
+        // app developers about the space they can expect for a particular
+        // configuration.  DO NOT CHANGE!
+        if (longSizeDp < 470) {
+            // This is shorter than an HVGA normal density screen (which
+            // is 480 pixels on its long side).
+            screenLayoutSize = SCREENLAYOUT_SIZE_SMALL;
+            screenLayoutLong = false;
+            screenLayoutCompatNeeded = false;
+        } else {
+            // What size is this screen screen?
+            if (longSizeDp >= 960 && shortSizeDp >= 720) {
+                // 1.5xVGA or larger screens at medium density are the point
+                // at which we consider it to be an extra large screen.
+                screenLayoutSize = SCREENLAYOUT_SIZE_XLARGE;
+            } else if (longSizeDp >= 640 && shortSizeDp >= 480) {
+                // VGA or larger screens at medium density are the point
+                // at which we consider it to be a large screen.
+                screenLayoutSize = SCREENLAYOUT_SIZE_LARGE;
+            } else {
+                screenLayoutSize = SCREENLAYOUT_SIZE_NORMAL;
+            }
+
+            // If this screen is wider than normal HVGA, or taller
+            // than FWVGA, then for old apps we want to run in size
+            // compatibility mode.
+            if (shortSizeDp > 321 || longSizeDp > 570) {
+                screenLayoutCompatNeeded = true;
+            } else {
+                screenLayoutCompatNeeded = false;
+            }
+
+            // Is this a long screen?
+            if (((longSizeDp*3)/5) >= (shortSizeDp-1)) {
+                // Anything wider than WVGA (5:3) is considering to be long.
+                screenLayoutLong = true;
+            } else {
+                screenLayoutLong = false;
+            }
+        }
+
+        // Now reduce the last screenLayout to not be better than what we
+        // have found.
+        if (!screenLayoutLong) {
+            curLayout = (curLayout&~SCREENLAYOUT_LONG_MASK) | SCREENLAYOUT_LONG_NO;
+        }
+        if (screenLayoutCompatNeeded) {
+            curLayout |= Configuration.SCREENLAYOUT_COMPAT_NEEDED;
+        }
+        int curSize = curLayout&SCREENLAYOUT_SIZE_MASK;
+        if (screenLayoutSize < curSize) {
+            curLayout = (curLayout&~SCREENLAYOUT_SIZE_MASK) | screenLayoutSize;
+        }
+        return curLayout;
+    }
+
+    /** @hide */
+    public static String configurationDiffToString(int diff) {
+        ArrayList<String> list = new ArrayList<>();
+        if ((diff & ActivityInfo.CONFIG_MCC) != 0) {
+            list.add("CONFIG_MCC");
+        }
+        if ((diff & ActivityInfo.CONFIG_MNC) != 0) {
+            list.add("CONFIG_MNC");
+        }
+        if ((diff & ActivityInfo.CONFIG_LOCALE) != 0) {
+            list.add("CONFIG_LOCALE");
+        }
+        if ((diff & ActivityInfo.CONFIG_TOUCHSCREEN) != 0) {
+            list.add("CONFIG_TOUCHSCREEN");
+        }
+        if ((diff & ActivityInfo.CONFIG_KEYBOARD) != 0) {
+            list.add("CONFIG_KEYBOARD");
+        }
+        if ((diff & ActivityInfo.CONFIG_KEYBOARD_HIDDEN) != 0) {
+            list.add("CONFIG_KEYBOARD_HIDDEN");
+        }
+        if ((diff & ActivityInfo.CONFIG_NAVIGATION) != 0) {
+            list.add("CONFIG_NAVIGATION");
+        }
+        if ((diff & ActivityInfo.CONFIG_ORIENTATION) != 0) {
+            list.add("CONFIG_ORIENTATION");
+        }
+        if ((diff & ActivityInfo.CONFIG_SCREEN_LAYOUT) != 0) {
+            list.add("CONFIG_SCREEN_LAYOUT");
+        }
+        if ((diff & ActivityInfo.CONFIG_COLOR_MODE) != 0) {
+            list.add("CONFIG_COLOR_MODE");
+        }
+        if ((diff & ActivityInfo.CONFIG_UI_MODE) != 0) {
+            list.add("CONFIG_UI_MODE");
+        }
+        if ((diff & ActivityInfo.CONFIG_SCREEN_SIZE) != 0) {
+            list.add("CONFIG_SCREEN_SIZE");
+        }
+        if ((diff & ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE) != 0) {
+            list.add("CONFIG_SMALLEST_SCREEN_SIZE");
+        }
+        if ((diff & ActivityInfo.CONFIG_DENSITY) != 0) {
+            list.add("CONFIG_DENSITY");
+        }
+        if ((diff & ActivityInfo.CONFIG_LAYOUT_DIRECTION) != 0) {
+            list.add("CONFIG_LAYOUT_DIRECTION");
+        }
+        if ((diff & ActivityInfo.CONFIG_FONT_SCALE) != 0) {
+            list.add("CONFIG_FONT_SCALE");
+        }
+        if ((diff & ActivityInfo.CONFIG_ASSETS_PATHS) != 0) {
+            list.add("CONFIG_ASSETS_PATHS");
+        }
+        if ((diff & ActivityInfo.CONFIG_WINDOW_CONFIGURATION) != 0) {
+            list.add("CONFIG_WINDOW_CONFIGURATION");
+        }
+        StringBuilder builder = new StringBuilder("{");
+        for (int i = 0, n = list.size(); i < n; i++) {
+            builder.append(list.get(i));
+            if (i != n - 1) {
+                builder.append(", ");
+            }
+        }
+        builder.append("}");
+        return builder.toString();
+    }
+
+    /**
+     * Check if the Configuration's current {@link #screenLayout} is at
+     * least the given size.
+     *
+     * @param size The desired size, either {@link #SCREENLAYOUT_SIZE_SMALL},
+     * {@link #SCREENLAYOUT_SIZE_NORMAL}, {@link #SCREENLAYOUT_SIZE_LARGE}, or
+     * {@link #SCREENLAYOUT_SIZE_XLARGE}.
+     * @return Returns true if the current screen layout size is at least
+     * the given size.
+     */
+    public boolean isLayoutSizeAtLeast(int size) {
+        int cur = screenLayout&SCREENLAYOUT_SIZE_MASK;
+        if (cur == SCREENLAYOUT_SIZE_UNDEFINED) return false;
+        return cur >= size;
+    }
+
+    /** Constant for {@link #touchscreen}: a value indicating that no value has been set. */
+    public static final int TOUCHSCREEN_UNDEFINED = 0;
+    /** Constant for {@link #touchscreen}, value corresponding to the
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#TouchscreenQualifier">notouch</a>
+     * resource qualifier. */
+    public static final int TOUCHSCREEN_NOTOUCH = 1;
+    /** @deprecated Not currently supported or used. */
+    @Deprecated public static final int TOUCHSCREEN_STYLUS = 2;
+    /** Constant for {@link #touchscreen}, value corresponding to the
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#TouchscreenQualifier">finger</a>
+     * resource qualifier. */
+    public static final int TOUCHSCREEN_FINGER = 3;
+
+    /**
+     * The kind of touch screen attached to the device.
+     * One of: {@link #TOUCHSCREEN_NOTOUCH}, {@link #TOUCHSCREEN_FINGER}.
+     */
+    public int touchscreen;
+
+    /** Constant for {@link #keyboard}: a value indicating that no value has been set. */
+    public static final int KEYBOARD_UNDEFINED = 0;
+    /** Constant for {@link #keyboard}, value corresponding to the
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#ImeQualifier">nokeys</a>
+     * resource qualifier. */
+    public static final int KEYBOARD_NOKEYS = 1;
+    /** Constant for {@link #keyboard}, value corresponding to the
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#ImeQualifier">qwerty</a>
+     * resource qualifier. */
+    public static final int KEYBOARD_QWERTY = 2;
+    /** Constant for {@link #keyboard}, value corresponding to the
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#ImeQualifier">12key</a>
+     * resource qualifier. */
+    public static final int KEYBOARD_12KEY = 3;
+
+    /**
+     * The kind of keyboard attached to the device.
+     * One of: {@link #KEYBOARD_NOKEYS}, {@link #KEYBOARD_QWERTY},
+     * {@link #KEYBOARD_12KEY}.
+     */
+    public int keyboard;
+
+    /** Constant for {@link #keyboardHidden}: a value indicating that no value has been set. */
+    public static final int KEYBOARDHIDDEN_UNDEFINED = 0;
+    /** Constant for {@link #keyboardHidden}, value corresponding to the
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#KeyboardAvailQualifier">keysexposed</a>
+     * resource qualifier. */
+    public static final int KEYBOARDHIDDEN_NO = 1;
+    /** Constant for {@link #keyboardHidden}, value corresponding to the
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#KeyboardAvailQualifier">keyshidden</a>
+     * resource qualifier. */
+    public static final int KEYBOARDHIDDEN_YES = 2;
+    /** Constant matching actual resource implementation. {@hide} */
+    public static final int KEYBOARDHIDDEN_SOFT = 3;
+
+    /**
+     * A flag indicating whether any keyboard is available.  Unlike
+     * {@link #hardKeyboardHidden}, this also takes into account a soft
+     * keyboard, so if the hard keyboard is hidden but there is soft
+     * keyboard available, it will be set to NO.  Value is one of:
+     * {@link #KEYBOARDHIDDEN_NO}, {@link #KEYBOARDHIDDEN_YES}.
+     */
+    public int keyboardHidden;
+
+    /** Constant for {@link #hardKeyboardHidden}: a value indicating that no value has been set. */
+    public static final int HARDKEYBOARDHIDDEN_UNDEFINED = 0;
+    /** Constant for {@link #hardKeyboardHidden}, value corresponding to the
+     * physical keyboard being exposed. */
+    public static final int HARDKEYBOARDHIDDEN_NO = 1;
+    /** Constant for {@link #hardKeyboardHidden}, value corresponding to the
+     * physical keyboard being hidden. */
+    public static final int HARDKEYBOARDHIDDEN_YES = 2;
+
+    /**
+     * A flag indicating whether the hard keyboard has been hidden.  This will
+     * be set on a device with a mechanism to hide the keyboard from the
+     * user, when that mechanism is closed.  One of:
+     * {@link #HARDKEYBOARDHIDDEN_NO}, {@link #HARDKEYBOARDHIDDEN_YES}.
+     */
+    public int hardKeyboardHidden;
+
+    /** Constant for {@link #navigation}: a value indicating that no value has been set. */
+    public static final int NAVIGATION_UNDEFINED = 0;
+    /** Constant for {@link #navigation}, value corresponding to the
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#NavigationQualifier">nonav</a>
+     * resource qualifier. */
+    public static final int NAVIGATION_NONAV = 1;
+    /** Constant for {@link #navigation}, value corresponding to the
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#NavigationQualifier">dpad</a>
+     * resource qualifier. */
+    public static final int NAVIGATION_DPAD = 2;
+    /** Constant for {@link #navigation}, value corresponding to the
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#NavigationQualifier">trackball</a>
+     * resource qualifier. */
+    public static final int NAVIGATION_TRACKBALL = 3;
+    /** Constant for {@link #navigation}, value corresponding to the
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#NavigationQualifier">wheel</a>
+     * resource qualifier. */
+    public static final int NAVIGATION_WHEEL = 4;
+
+    /**
+     * The kind of navigation method available on the device.
+     * One of: {@link #NAVIGATION_NONAV}, {@link #NAVIGATION_DPAD},
+     * {@link #NAVIGATION_TRACKBALL}, {@link #NAVIGATION_WHEEL}.
+     */
+    public int navigation;
+
+    /** Constant for {@link #navigationHidden}: a value indicating that no value has been set. */
+    public static final int NAVIGATIONHIDDEN_UNDEFINED = 0;
+    /** Constant for {@link #navigationHidden}, value corresponding to the
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#NavAvailQualifier">navexposed</a>
+     * resource qualifier. */
+    public static final int NAVIGATIONHIDDEN_NO = 1;
+    /** Constant for {@link #navigationHidden}, value corresponding to the
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#NavAvailQualifier">navhidden</a>
+     * resource qualifier. */
+    public static final int NAVIGATIONHIDDEN_YES = 2;
+
+    /**
+     * A flag indicating whether any 5-way or DPAD navigation available.
+     * This will be set on a device with a mechanism to hide the navigation
+     * controls from the user, when that mechanism is closed.  One of:
+     * {@link #NAVIGATIONHIDDEN_NO}, {@link #NAVIGATIONHIDDEN_YES}.
+     */
+    public int navigationHidden;
+
+    /** @hide **/
+    @IntDef(prefix = {"ORIENTATION_"}, value = {
+            ORIENTATION_UNDEFINED,
+            ORIENTATION_PORTRAIT,
+            ORIENTATION_LANDSCAPE,
+            ORIENTATION_SQUARE
+    })
+    public @interface Orientation {
+    }
+
+    /** Constant for {@link #orientation}: a value indicating that no value has been set. */
+    public static final int ORIENTATION_UNDEFINED = 0;
+    /** Constant for {@link #orientation}, value corresponding to the
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#OrientationQualifier">port</a>
+     * resource qualifier. */
+    public static final int ORIENTATION_PORTRAIT = 1;
+    /** Constant for {@link #orientation}, value corresponding to the
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#OrientationQualifier">land</a>
+     * resource qualifier. */
+    public static final int ORIENTATION_LANDSCAPE = 2;
+    /** @deprecated Not currently supported or used. */
+    @Deprecated public static final int ORIENTATION_SQUARE = 3;
+
+    /**
+     * Overall orientation of the screen.  May be one of
+     * {@link #ORIENTATION_LANDSCAPE}, {@link #ORIENTATION_PORTRAIT}.
+     */
+    @Orientation
+    public int orientation;
+
+    /** Constant for {@link #uiMode}: bits that encode the mode type. */
+    public static final int UI_MODE_TYPE_MASK = 0x0f;
+    /** Constant for {@link #uiMode}: a {@link #UI_MODE_TYPE_MASK}
+     * value indicating that no mode type has been set. */
+    public static final int UI_MODE_TYPE_UNDEFINED = 0x00;
+    /** Constant for {@link #uiMode}: a {@link #UI_MODE_TYPE_MASK}
+     * value that corresponds to
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#UiModeQualifier">no
+     * UI mode</a> resource qualifier specified. */
+    public static final int UI_MODE_TYPE_NORMAL = 0x01;
+    /** Constant for {@link #uiMode}: a {@link #UI_MODE_TYPE_MASK}
+     * value that corresponds to the
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#UiModeQualifier">desk</a>
+     * resource qualifier. */
+    public static final int UI_MODE_TYPE_DESK = 0x02;
+    /** Constant for {@link #uiMode}: a {@link #UI_MODE_TYPE_MASK}
+     * value that corresponds to the
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#UiModeQualifier">car</a>
+     * resource qualifier. */
+    public static final int UI_MODE_TYPE_CAR = 0x03;
+    /** Constant for {@link #uiMode}: a {@link #UI_MODE_TYPE_MASK}
+     * value that corresponds to the
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#UiModeQualifier">television</a>
+     * resource qualifier. */
+    public static final int UI_MODE_TYPE_TELEVISION = 0x04;
+    /** Constant for {@link #uiMode}: a {@link #UI_MODE_TYPE_MASK}
+     * value that corresponds to the
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#UiModeQualifier">appliance</a>
+     * resource qualifier. */
+    public static final int UI_MODE_TYPE_APPLIANCE = 0x05;
+    /** Constant for {@link #uiMode}: a {@link #UI_MODE_TYPE_MASK}
+     * value that corresponds to the
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#UiModeQualifier">watch</a>
+     * resource qualifier. */
+    public static final int UI_MODE_TYPE_WATCH = 0x06;
+    /** Constant for {@link #uiMode}: a {@link #UI_MODE_TYPE_MASK}
+     * value that corresponds to the
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#UiModeQualifier">vrheadset</a>
+     * resource qualifier. */
+    public static final int UI_MODE_TYPE_VR_HEADSET = 0x07;
+
+    /** Constant for {@link #uiMode}: bits that encode the night mode. */
+    public static final int UI_MODE_NIGHT_MASK = 0x30;
+    /** Constant for {@link #uiMode}: a {@link #UI_MODE_NIGHT_MASK}
+     * value indicating that no mode type has been set. */
+    public static final int UI_MODE_NIGHT_UNDEFINED = 0x00;
+    /** Constant for {@link #uiMode}: a {@link #UI_MODE_NIGHT_MASK}
+     * value that corresponds to the
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#NightQualifier">notnight</a>
+     * resource qualifier. */
+    public static final int UI_MODE_NIGHT_NO = 0x10;
+    /** Constant for {@link #uiMode}: a {@link #UI_MODE_NIGHT_MASK}
+     * value that corresponds to the
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#NightQualifier">night</a>
+     * resource qualifier. */
+    public static final int UI_MODE_NIGHT_YES = 0x20;
+
+    /**
+     * Bit mask of the ui mode.  Currently there are two fields:
+     * <p>The {@link #UI_MODE_TYPE_MASK} bits define the overall ui mode of the
+     * device. They may be one of {@link #UI_MODE_TYPE_UNDEFINED},
+     * {@link #UI_MODE_TYPE_NORMAL}, {@link #UI_MODE_TYPE_DESK},
+     * {@link #UI_MODE_TYPE_CAR}, {@link #UI_MODE_TYPE_TELEVISION},
+     * {@link #UI_MODE_TYPE_APPLIANCE}, {@link #UI_MODE_TYPE_WATCH},
+     * or {@link #UI_MODE_TYPE_VR_HEADSET}.
+     *
+     * <p>The {@link #UI_MODE_NIGHT_MASK} defines whether the screen
+     * is in a special mode. They may be one of {@link #UI_MODE_NIGHT_UNDEFINED},
+     * {@link #UI_MODE_NIGHT_NO} or {@link #UI_MODE_NIGHT_YES}.
+     */
+    public int uiMode;
+
+    /**
+     * Default value for {@link #screenWidthDp} indicating that no width
+     * has been specified.
+     */
+    public static final int SCREEN_WIDTH_DP_UNDEFINED = 0;
+
+    /**
+     * The current width of the available screen space, in dp units,
+     * corresponding to
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#ScreenWidthQualifier">screen
+     * width</a> resource qualifier.  Set to
+     * {@link #SCREEN_WIDTH_DP_UNDEFINED} if no width is specified.
+     */
+    public int screenWidthDp;
+
+    /**
+     * Default value for {@link #screenHeightDp} indicating that no width
+     * has been specified.
+     */
+    public static final int SCREEN_HEIGHT_DP_UNDEFINED = 0;
+
+    /**
+     * The current height of the available screen space, in dp units,
+     * corresponding to
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#ScreenHeightQualifier">screen
+     * height</a> resource qualifier.  Set to
+     * {@link #SCREEN_HEIGHT_DP_UNDEFINED} if no height is specified.
+     */
+    public int screenHeightDp;
+
+    /**
+     * Default value for {@link #smallestScreenWidthDp} indicating that no width
+     * has been specified.
+     */
+    public static final int SMALLEST_SCREEN_WIDTH_DP_UNDEFINED = 0;
+
+    /**
+     * The smallest screen size an application will see in normal operation,
+     * corresponding to
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#SmallestScreenWidthQualifier">smallest
+     * screen width</a> resource qualifier.
+     * This is the smallest value of both screenWidthDp and screenHeightDp
+     * in both portrait and landscape.  Set to
+     * {@link #SMALLEST_SCREEN_WIDTH_DP_UNDEFINED} if no width is specified.
+     */
+    public int smallestScreenWidthDp;
+
+    /**
+     * Default value for {@link #densityDpi} indicating that no width
+     * has been specified.
+     */
+    public static final int DENSITY_DPI_UNDEFINED = 0;
+
+    /**
+     * Value for {@link #densityDpi} for resources that scale to any density (vector drawables).
+     * {@hide}
+     */
+    public static final int DENSITY_DPI_ANY = 0xfffe;
+
+    /**
+     * Value for {@link #densityDpi} for resources that are not meant to be scaled.
+     * {@hide}
+     */
+    public static final int DENSITY_DPI_NONE = 0xffff;
+
+    /**
+     * The target screen density being rendered to,
+     * corresponding to
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#DensityQualifier">density</a>
+     * resource qualifier.  Set to
+     * {@link #DENSITY_DPI_UNDEFINED} if no density is specified.
+     */
+    public int densityDpi;
+
+    /** @hide Hack to get this information from WM to app running in compat mode. */
+    public int compatScreenWidthDp;
+    /** @hide Hack to get this information from WM to app running in compat mode. */
+    public int compatScreenHeightDp;
+    /** @hide Hack to get this information from WM to app running in compat mode. */
+    public int compatSmallestScreenWidthDp;
+
+    /**
+     * An undefined assetsSeq. This will not override an existing assetsSeq.
+     * @hide
+     */
+    public static final int ASSETS_SEQ_UNDEFINED = 0;
+
+    /**
+     * Internal counter that allows us to piggyback off the configuration change mechanism to
+     * signal to apps that the the assets for an Application have changed. A difference in these
+     * between two Configurations will yield a diff flag of
+     * {@link ActivityInfo#CONFIG_ASSETS_PATHS}.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    @TestApi
+    public int assetsSeq;
+
+    /**
+     * @hide Internal book-keeping.
+     */
+    @UnsupportedAppUsage
+    public int seq;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "NATIVE_CONFIG_" }, value = {
+            NATIVE_CONFIG_MCC,
+            NATIVE_CONFIG_MNC,
+            NATIVE_CONFIG_LOCALE,
+            NATIVE_CONFIG_TOUCHSCREEN,
+            NATIVE_CONFIG_KEYBOARD,
+            NATIVE_CONFIG_KEYBOARD_HIDDEN,
+            NATIVE_CONFIG_NAVIGATION,
+            NATIVE_CONFIG_ORIENTATION,
+            NATIVE_CONFIG_DENSITY,
+            NATIVE_CONFIG_SCREEN_SIZE,
+            NATIVE_CONFIG_VERSION,
+            NATIVE_CONFIG_SCREEN_LAYOUT,
+            NATIVE_CONFIG_UI_MODE,
+            NATIVE_CONFIG_SMALLEST_SCREEN_SIZE,
+            NATIVE_CONFIG_LAYOUTDIR,
+            NATIVE_CONFIG_COLOR_MODE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface NativeConfig {}
+
+    /** @hide Native-specific bit mask for MCC config; DO NOT USE UNLESS YOU ARE SURE. */
+    public static final int NATIVE_CONFIG_MCC = 0x0001;
+    /** @hide Native-specific bit mask for MNC config; DO NOT USE UNLESS YOU ARE SURE. */
+    public static final int NATIVE_CONFIG_MNC = 0x0002;
+    /** @hide Native-specific bit mask for LOCALE config; DO NOT USE UNLESS YOU ARE SURE. */
+    public static final int NATIVE_CONFIG_LOCALE = 0x0004;
+    /** @hide Native-specific bit mask for TOUCHSCREEN config; DO NOT USE UNLESS YOU ARE SURE. */
+    public static final int NATIVE_CONFIG_TOUCHSCREEN = 0x0008;
+    /** @hide Native-specific bit mask for KEYBOARD config; DO NOT USE UNLESS YOU ARE SURE. */
+    public static final int NATIVE_CONFIG_KEYBOARD = 0x0010;
+    /** @hide Native-specific bit mask for KEYBOARD_HIDDEN config; DO NOT USE UNLESS YOU
+     * ARE SURE. */
+    public static final int NATIVE_CONFIG_KEYBOARD_HIDDEN = 0x0020;
+    /** @hide Native-specific bit mask for NAVIGATION config; DO NOT USE UNLESS YOU ARE SURE. */
+    public static final int NATIVE_CONFIG_NAVIGATION = 0x0040;
+    /** @hide Native-specific bit mask for ORIENTATION config; DO NOT USE UNLESS YOU ARE SURE. */
+    public static final int NATIVE_CONFIG_ORIENTATION = 0x0080;
+    /** @hide Native-specific bit mask for DENSITY config; DO NOT USE UNLESS YOU ARE SURE. */
+    public static final int NATIVE_CONFIG_DENSITY = 0x0100;
+    /** @hide Native-specific bit mask for SCREEN_SIZE config; DO NOT USE UNLESS YOU ARE SURE. */
+    public static final int NATIVE_CONFIG_SCREEN_SIZE = 0x0200;
+    /** @hide Native-specific bit mask for VERSION config; DO NOT USE UNLESS YOU ARE SURE. */
+    public static final int NATIVE_CONFIG_VERSION = 0x0400;
+    /** @hide Native-specific bit mask for SCREEN_LAYOUT config; DO NOT USE UNLESS YOU ARE SURE. */
+    public static final int NATIVE_CONFIG_SCREEN_LAYOUT = 0x0800;
+    /** @hide Native-specific bit mask for UI_MODE config; DO NOT USE UNLESS YOU ARE SURE. */
+    public static final int NATIVE_CONFIG_UI_MODE = 0x1000;
+    /** @hide Native-specific bit mask for SMALLEST_SCREEN_SIZE config; DO NOT USE UNLESS YOU
+     * ARE SURE. */
+    public static final int NATIVE_CONFIG_SMALLEST_SCREEN_SIZE = 0x2000;
+    /** @hide Native-specific bit mask for LAYOUTDIR config ; DO NOT USE UNLESS YOU ARE SURE.*/
+    public static final int NATIVE_CONFIG_LAYOUTDIR = 0x4000;
+    /** @hide Native-specific bit mask for COLOR_MODE config ; DO NOT USE UNLESS YOU ARE SURE.*/
+    public static final int NATIVE_CONFIG_COLOR_MODE = 0x10000;
+
+    /**
+     * <p>Construct an invalid Configuration. This state is only suitable for constructing a
+     * Configuration delta that will be applied to some valid Configuration object. In order to
+     * create a valid standalone Configuration, you must call {@link #setToDefaults}. </p>
+     *
+     * <p>Example:</p>
+     * <pre class="prettyprint">
+     *     Configuration validConfig = new Configuration();
+     *     validConfig.setToDefaults();
+     *
+     *     Configuration deltaOnlyConfig = new Configuration();
+     *     deltaOnlyConfig.orientation = Configuration.ORIENTATION_LANDSCAPE;
+     *
+     *     validConfig.updateFrom(deltaOnlyConfig);
+     * </pre>
+     */
+    public Configuration() {
+        unset();
+    }
+
+    /**
+     * Makes a deep copy suitable for modification.
+     */
+    public Configuration(Configuration o) {
+        setTo(o);
+    }
+
+    /* This brings mLocaleList in sync with locale in case a user of the older API who doesn't know
+     * about setLocales() has changed locale directly. */
+    private void fixUpLocaleList() {
+        if ((locale == null && !mLocaleList.isEmpty()) ||
+                (locale != null && !locale.equals(mLocaleList.get(0)))) {
+            mLocaleList = locale == null ? LocaleList.getEmptyLocaleList() : new LocaleList(locale);
+        }
+    }
+
+    /**
+     * Sets the fields in this object to those in the given Configuration.
+     *
+     * @param o The Configuration object used to set the values of this Configuration's fields.
+     */
+    public void setTo(Configuration o) {
+        fontScale = o.fontScale;
+        mcc = o.mcc;
+        mnc = o.mnc;
+        locale = o.locale == null ? null : (Locale) o.locale.clone();
+        o.fixUpLocaleList();
+        mLocaleList = o.mLocaleList;
+        userSetLocale = o.userSetLocale;
+        touchscreen = o.touchscreen;
+        keyboard = o.keyboard;
+        keyboardHidden = o.keyboardHidden;
+        hardKeyboardHidden = o.hardKeyboardHidden;
+        navigation = o.navigation;
+        navigationHidden = o.navigationHidden;
+        orientation = o.orientation;
+        screenLayout = o.screenLayout;
+        colorMode = o.colorMode;
+        uiMode = o.uiMode;
+        screenWidthDp = o.screenWidthDp;
+        screenHeightDp = o.screenHeightDp;
+        smallestScreenWidthDp = o.smallestScreenWidthDp;
+        densityDpi = o.densityDpi;
+        compatScreenWidthDp = o.compatScreenWidthDp;
+        compatScreenHeightDp = o.compatScreenHeightDp;
+        compatSmallestScreenWidthDp = o.compatSmallestScreenWidthDp;
+        assetsSeq = o.assetsSeq;
+        seq = o.seq;
+        windowConfiguration.setTo(o.windowConfiguration);
+    }
+
+    public String toString() {
+        StringBuilder sb = new StringBuilder(128);
+        sb.append("{");
+        sb.append(fontScale);
+        sb.append(" ");
+        if (mcc != 0) {
+            sb.append(mcc);
+            sb.append("mcc");
+        } else {
+            sb.append("?mcc");
+        }
+        if (mnc != 0) {
+            sb.append(mnc);
+            sb.append("mnc");
+        } else {
+            sb.append("?mnc");
+        }
+        fixUpLocaleList();
+        if (!mLocaleList.isEmpty()) {
+            sb.append(" ");
+            sb.append(mLocaleList);
+        } else {
+            sb.append(" ?localeList");
+        }
+        int layoutDir = (screenLayout&SCREENLAYOUT_LAYOUTDIR_MASK);
+        switch (layoutDir) {
+            case SCREENLAYOUT_LAYOUTDIR_UNDEFINED: sb.append(" ?layoutDir"); break;
+            case SCREENLAYOUT_LAYOUTDIR_LTR: sb.append(" ldltr"); break;
+            case SCREENLAYOUT_LAYOUTDIR_RTL: sb.append(" ldrtl"); break;
+            default: sb.append(" layoutDir=");
+                sb.append(layoutDir >> SCREENLAYOUT_LAYOUTDIR_SHIFT); break;
+        }
+        if (smallestScreenWidthDp != SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) {
+            sb.append(" sw"); sb.append(smallestScreenWidthDp); sb.append("dp");
+        } else {
+            sb.append(" ?swdp");
+        }
+        if (screenWidthDp != SCREEN_WIDTH_DP_UNDEFINED) {
+            sb.append(" w"); sb.append(screenWidthDp); sb.append("dp");
+        } else {
+            sb.append(" ?wdp");
+        }
+        if (screenHeightDp != SCREEN_HEIGHT_DP_UNDEFINED) {
+            sb.append(" h"); sb.append(screenHeightDp); sb.append("dp");
+        } else {
+            sb.append(" ?hdp");
+        }
+        if (densityDpi != DENSITY_DPI_UNDEFINED) {
+            sb.append(" "); sb.append(densityDpi); sb.append("dpi");
+        } else {
+            sb.append(" ?density");
+        }
+        switch ((screenLayout&SCREENLAYOUT_SIZE_MASK)) {
+            case SCREENLAYOUT_SIZE_UNDEFINED: sb.append(" ?lsize"); break;
+            case SCREENLAYOUT_SIZE_SMALL: sb.append(" smll"); break;
+            case SCREENLAYOUT_SIZE_NORMAL: sb.append(" nrml"); break;
+            case SCREENLAYOUT_SIZE_LARGE: sb.append(" lrg"); break;
+            case SCREENLAYOUT_SIZE_XLARGE: sb.append(" xlrg"); break;
+            default: sb.append(" layoutSize=");
+                    sb.append(screenLayout&SCREENLAYOUT_SIZE_MASK); break;
+        }
+        switch ((screenLayout&SCREENLAYOUT_LONG_MASK)) {
+            case SCREENLAYOUT_LONG_UNDEFINED: sb.append(" ?long"); break;
+            case SCREENLAYOUT_LONG_NO: /* not-long is not interesting to print */ break;
+            case SCREENLAYOUT_LONG_YES: sb.append(" long"); break;
+            default: sb.append(" layoutLong=");
+                    sb.append(screenLayout&SCREENLAYOUT_LONG_MASK); break;
+        }
+        switch ((colorMode &COLOR_MODE_HDR_MASK)) {
+            case COLOR_MODE_HDR_UNDEFINED: sb.append(" ?ldr"); break; // most likely not HDR
+            case COLOR_MODE_HDR_NO: /* ldr is not interesting to print */ break;
+            case COLOR_MODE_HDR_YES: sb.append(" hdr"); break;
+            default: sb.append(" dynamicRange=");
+                sb.append(colorMode &COLOR_MODE_HDR_MASK); break;
+        }
+        switch ((colorMode &COLOR_MODE_WIDE_COLOR_GAMUT_MASK)) {
+            case COLOR_MODE_WIDE_COLOR_GAMUT_UNDEFINED: sb.append(" ?wideColorGamut"); break;
+            case COLOR_MODE_WIDE_COLOR_GAMUT_NO: /* not wide is not interesting to print */ break;
+            case COLOR_MODE_WIDE_COLOR_GAMUT_YES: sb.append(" widecg"); break;
+            default: sb.append(" wideColorGamut=");
+                sb.append(colorMode &COLOR_MODE_WIDE_COLOR_GAMUT_MASK); break;
+        }
+        switch (orientation) {
+            case ORIENTATION_UNDEFINED: sb.append(" ?orien"); break;
+            case ORIENTATION_LANDSCAPE: sb.append(" land"); break;
+            case ORIENTATION_PORTRAIT: sb.append(" port"); break;
+            default: sb.append(" orien="); sb.append(orientation); break;
+        }
+        switch ((uiMode&UI_MODE_TYPE_MASK)) {
+            case UI_MODE_TYPE_UNDEFINED: sb.append(" ?uimode"); break;
+            case UI_MODE_TYPE_NORMAL: /* normal is not interesting to print */ break;
+            case UI_MODE_TYPE_DESK: sb.append(" desk"); break;
+            case UI_MODE_TYPE_CAR: sb.append(" car"); break;
+            case UI_MODE_TYPE_TELEVISION: sb.append(" television"); break;
+            case UI_MODE_TYPE_APPLIANCE: sb.append(" appliance"); break;
+            case UI_MODE_TYPE_WATCH: sb.append(" watch"); break;
+            case UI_MODE_TYPE_VR_HEADSET: sb.append(" vrheadset"); break;
+            default: sb.append(" uimode="); sb.append(uiMode&UI_MODE_TYPE_MASK); break;
+        }
+        switch ((uiMode&UI_MODE_NIGHT_MASK)) {
+            case UI_MODE_NIGHT_UNDEFINED: sb.append(" ?night"); break;
+            case UI_MODE_NIGHT_NO: /* not-night is not interesting to print */ break;
+            case UI_MODE_NIGHT_YES: sb.append(" night"); break;
+            default: sb.append(" night="); sb.append(uiMode&UI_MODE_NIGHT_MASK); break;
+        }
+        switch (touchscreen) {
+            case TOUCHSCREEN_UNDEFINED: sb.append(" ?touch"); break;
+            case TOUCHSCREEN_NOTOUCH: sb.append(" -touch"); break;
+            case TOUCHSCREEN_STYLUS: sb.append(" stylus"); break;
+            case TOUCHSCREEN_FINGER: sb.append(" finger"); break;
+            default: sb.append(" touch="); sb.append(touchscreen); break;
+        }
+        switch (keyboard) {
+            case KEYBOARD_UNDEFINED: sb.append(" ?keyb"); break;
+            case KEYBOARD_NOKEYS: sb.append(" -keyb"); break;
+            case KEYBOARD_QWERTY: sb.append(" qwerty"); break;
+            case KEYBOARD_12KEY: sb.append(" 12key"); break;
+            default: sb.append(" keys="); sb.append(keyboard); break;
+        }
+        switch (keyboardHidden) {
+            case KEYBOARDHIDDEN_UNDEFINED: sb.append("/?"); break;
+            case KEYBOARDHIDDEN_NO: sb.append("/v"); break;
+            case KEYBOARDHIDDEN_YES: sb.append("/h"); break;
+            case KEYBOARDHIDDEN_SOFT: sb.append("/s"); break;
+            default: sb.append("/"); sb.append(keyboardHidden); break;
+        }
+        switch (hardKeyboardHidden) {
+            case HARDKEYBOARDHIDDEN_UNDEFINED: sb.append("/?"); break;
+            case HARDKEYBOARDHIDDEN_NO: sb.append("/v"); break;
+            case HARDKEYBOARDHIDDEN_YES: sb.append("/h"); break;
+            default: sb.append("/"); sb.append(hardKeyboardHidden); break;
+        }
+        switch (navigation) {
+            case NAVIGATION_UNDEFINED: sb.append(" ?nav"); break;
+            case NAVIGATION_NONAV: sb.append(" -nav"); break;
+            case NAVIGATION_DPAD: sb.append(" dpad"); break;
+            case NAVIGATION_TRACKBALL: sb.append(" tball"); break;
+            case NAVIGATION_WHEEL: sb.append(" wheel"); break;
+            default: sb.append(" nav="); sb.append(navigation); break;
+        }
+        switch (navigationHidden) {
+            case NAVIGATIONHIDDEN_UNDEFINED: sb.append("/?"); break;
+            case NAVIGATIONHIDDEN_NO: sb.append("/v"); break;
+            case NAVIGATIONHIDDEN_YES: sb.append("/h"); break;
+            default: sb.append("/"); sb.append(navigationHidden); break;
+        }
+        sb.append(" winConfig="); sb.append(windowConfiguration);
+        if (assetsSeq != 0) {
+            sb.append(" as.").append(assetsSeq);
+        }
+        if (seq != 0) {
+            sb.append(" s.").append(seq);
+        }
+        sb.append('}');
+        return sb.toString();
+    }
+
+    /**
+     * Write to a protocol buffer output stream.
+     * Protocol buffer message definition at {@link android.content.ConfigurationProto}
+     * Has the option to ignore fields that don't need to be persisted to disk.
+     *
+     * @param protoOutputStream Stream to write the Configuration object to.
+     * @param fieldId           Field Id of the Configuration as defined in the parent message
+     * @param persisted         Note if this proto will be persisted to disk
+     * @param critical          If true, reduce amount of data written.
+     * @hide
+     */
+    public void dumpDebug(ProtoOutputStream protoOutputStream, long fieldId, boolean persisted,
+            boolean critical) {
+        final long token = protoOutputStream.start(fieldId);
+        if (!critical) {
+            protoOutputStream.write(FONT_SCALE, fontScale);
+            protoOutputStream.write(MCC, mcc);
+            protoOutputStream.write(MNC, mnc);
+            if (mLocaleList != null) {
+                protoOutputStream.write(LOCALE_LIST, mLocaleList.toLanguageTags());
+            }
+            protoOutputStream.write(SCREEN_LAYOUT, screenLayout);
+            protoOutputStream.write(COLOR_MODE, colorMode);
+            protoOutputStream.write(TOUCHSCREEN, touchscreen);
+            protoOutputStream.write(KEYBOARD, keyboard);
+            protoOutputStream.write(KEYBOARD_HIDDEN, keyboardHidden);
+            protoOutputStream.write(HARD_KEYBOARD_HIDDEN, hardKeyboardHidden);
+            protoOutputStream.write(NAVIGATION, navigation);
+            protoOutputStream.write(NAVIGATION_HIDDEN, navigationHidden);
+            protoOutputStream.write(UI_MODE, uiMode);
+            protoOutputStream.write(SMALLEST_SCREEN_WIDTH_DP, smallestScreenWidthDp);
+            protoOutputStream.write(DENSITY_DPI, densityDpi);
+            // For persistence, we do not care about window configuration
+            if (!persisted && windowConfiguration != null) {
+                windowConfiguration.dumpDebug(protoOutputStream, WINDOW_CONFIGURATION);
+            }
+        }
+        protoOutputStream.write(ORIENTATION, orientation);
+        protoOutputStream.write(SCREEN_WIDTH_DP, screenWidthDp);
+        protoOutputStream.write(SCREEN_HEIGHT_DP, screenHeightDp);
+        protoOutputStream.end(token);
+    }
+
+    /**
+     * Write to a protocol buffer output stream.
+     * Protocol buffer message definition at {@link android.content.ConfigurationProto}
+     *
+     * @param protoOutputStream Stream to write the Configuration object to.
+     * @param fieldId           Field Id of the Configuration as defined in the parent message
+     * @hide
+     */
+    public void dumpDebug(ProtoOutputStream protoOutputStream, long fieldId) {
+        dumpDebug(protoOutputStream, fieldId, false /* persisted */, false /* critical */);
+    }
+
+    /**
+     * Write to a protocol buffer output stream.
+     * Protocol buffer message definition at {@link android.content.ConfigurationProto}
+     *
+     * @param protoOutputStream Stream to write the Configuration object to.
+     * @param fieldId           Field Id of the Configuration as defined in the parent message
+     * @param critical          If true, reduce amount of data written.
+     * @hide
+     */
+    public void dumpDebug(ProtoOutputStream protoOutputStream, long fieldId, boolean critical) {
+        dumpDebug(protoOutputStream, fieldId, false /* persisted */, critical);
+    }
+
+    /**
+     * Read from a protocol buffer output stream.
+     * Protocol buffer message definition at {@link android.content.ConfigurationProto}
+     *
+     * @param protoInputStream Stream to read the Configuration object from.
+     * @param fieldId          Field Id of the Configuration as defined in the parent message
+     * @hide
+     */
+    public void readFromProto(ProtoInputStream protoInputStream, long fieldId) throws IOException {
+        final long token = protoInputStream.start(fieldId);
+        final List<Locale> list = new ArrayList();
+        try {
+            while (protoInputStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+                switch (protoInputStream.getFieldNumber()) {
+                    case (int) FONT_SCALE:
+                        fontScale = protoInputStream.readFloat(FONT_SCALE);
+                        break;
+                    case (int) MCC:
+                        mcc = protoInputStream.readInt(MCC);
+                        break;
+                    case (int) MNC:
+                        mnc = protoInputStream.readInt(MNC);
+                        break;
+                    case (int) LOCALES:
+                        // Parse the Locale here to handle all the repeated Locales
+                        // The LocaleList will be created when the message is completed
+                        final long localeToken = protoInputStream.start(LOCALES);
+                        String language = "";
+                        String country = "";
+                        String variant = "";
+                        String script = "";
+                        try {
+                            while (protoInputStream.nextField()
+                                    != ProtoInputStream.NO_MORE_FIELDS) {
+                                switch (protoInputStream.getFieldNumber()) {
+                                    case (int) LocaleProto.LANGUAGE:
+                                        language = protoInputStream.readString(
+                                                LocaleProto.LANGUAGE);
+                                        break;
+                                    case (int) LocaleProto.COUNTRY:
+                                        country = protoInputStream.readString(LocaleProto.COUNTRY);
+                                        break;
+                                    case (int) LocaleProto.VARIANT:
+                                        variant = protoInputStream.readString(LocaleProto.VARIANT);
+                                        break;
+                                    case (int) LocaleProto.SCRIPT:
+                                        script = protoInputStream.readString(LocaleProto.SCRIPT);
+                                        break;
+                                }
+                            }
+                        } catch (WireTypeMismatchException wtme) {
+                            // rethrow for caller deal with
+                            throw wtme;
+                        } finally {
+                            protoInputStream.end(localeToken);
+                            try {
+                                final Locale locale = new Locale.Builder()
+                                                        .setLanguage(language)
+                                                        .setRegion(country)
+                                                        .setVariant(variant)
+                                                        .setScript(script)
+                                                        .build();
+                                // Log a WTF here if a repeated locale is found to avoid throwing an
+                                // exception in system server when LocaleList is created below
+                                final int inListIndex = list.indexOf(locale);
+                                if (inListIndex != -1) {
+                                    Slog.wtf(TAG, "Repeated locale (" + list.get(inListIndex) + ")"
+                                            + " found when trying to add: " + locale.toString());
+                                } else {
+                                    list.add(locale);
+                                }
+                            } catch (IllformedLocaleException e) {
+                                Slog.e(TAG, "readFromProto error building locale with: "
+                                        + "language-" + language + ";country-" + country
+                                        + ";variant-" + variant + ";script-" + script);
+                            }
+                        }
+                        break;
+                    case (int) SCREEN_LAYOUT:
+                        screenLayout = protoInputStream.readInt(SCREEN_LAYOUT);
+                        break;
+                    case (int) COLOR_MODE:
+                        colorMode = protoInputStream.readInt(COLOR_MODE);
+                        break;
+                    case (int) TOUCHSCREEN:
+                        touchscreen = protoInputStream.readInt(TOUCHSCREEN);
+                        break;
+                    case (int) KEYBOARD:
+                        keyboard = protoInputStream.readInt(KEYBOARD);
+                        break;
+                    case (int) KEYBOARD_HIDDEN:
+                        keyboardHidden = protoInputStream.readInt(KEYBOARD_HIDDEN);
+                        break;
+                    case (int) HARD_KEYBOARD_HIDDEN:
+                        hardKeyboardHidden = protoInputStream.readInt(HARD_KEYBOARD_HIDDEN);
+                        break;
+                    case (int) NAVIGATION:
+                        navigation = protoInputStream.readInt(NAVIGATION);
+                        break;
+                    case (int) NAVIGATION_HIDDEN:
+                        navigationHidden = protoInputStream.readInt(NAVIGATION_HIDDEN);
+                        break;
+                    case (int) ORIENTATION:
+                        orientation = protoInputStream.readInt(ORIENTATION);
+                        break;
+                    case (int) UI_MODE:
+                        uiMode = protoInputStream.readInt(UI_MODE);
+                        break;
+                    case (int) SCREEN_WIDTH_DP:
+                        screenWidthDp = protoInputStream.readInt(SCREEN_WIDTH_DP);
+                        break;
+                    case (int) SCREEN_HEIGHT_DP:
+                        screenHeightDp = protoInputStream.readInt(SCREEN_HEIGHT_DP);
+                        break;
+                    case (int) SMALLEST_SCREEN_WIDTH_DP:
+                        smallestScreenWidthDp = protoInputStream.readInt(SMALLEST_SCREEN_WIDTH_DP);
+                        break;
+                    case (int) DENSITY_DPI:
+                        densityDpi = protoInputStream.readInt(DENSITY_DPI);
+                        break;
+                    case (int) WINDOW_CONFIGURATION:
+                        windowConfiguration.readFromProto(protoInputStream, WINDOW_CONFIGURATION);
+                        break;
+                    case (int) LOCALE_LIST:
+                        try {
+                            setLocales(LocaleList.forLanguageTags(protoInputStream.readString(
+                                    LOCALE_LIST)));
+                        } catch (Exception e) {
+                            Slog.e(TAG, "error parsing locale list in configuration.", e);
+                        }
+                        break;
+                }
+            }
+        } finally {
+            // Let caller handle any exceptions
+            if (list.size() > 0) {
+                //Create the LocaleList from the collected Locales
+                setLocales(new LocaleList(list.toArray(new Locale[list.size()])));
+            }
+            protoInputStream.end(token);
+        }
+    }
+
+    /**
+     * Write full {@link android.content.ResourcesConfigurationProto} to protocol buffer output
+     * stream.
+     *
+     * @param protoOutputStream Stream to write the Configuration object to.
+     * @param fieldId           Field Id of the Configuration as defined in the parent message
+     * @param metrics           Current display information
+     * @hide
+     */
+    public void writeResConfigToProto(ProtoOutputStream protoOutputStream, long fieldId,
+            DisplayMetrics metrics) {
+        final int width, height;
+        if (metrics.widthPixels >= metrics.heightPixels) {
+            width = metrics.widthPixels;
+            height = metrics.heightPixels;
+        } else {
+            //noinspection SuspiciousNameCombination
+            width = metrics.heightPixels;
+            //noinspection SuspiciousNameCombination
+            height = metrics.widthPixels;
+        }
+
+        final long token = protoOutputStream.start(fieldId);
+        dumpDebug(protoOutputStream, CONFIGURATION);
+        protoOutputStream.write(SDK_VERSION, Build.VERSION.RESOURCES_SDK_INT);
+        protoOutputStream.write(SCREEN_WIDTH_PX, width);
+        protoOutputStream.write(SCREEN_HEIGHT_PX, height);
+        protoOutputStream.end(token);
+    }
+
+    /**
+     * Convert the UI mode to a human readable format.
+     * @hide
+     */
+    public static String uiModeToString(int uiMode) {
+        switch (uiMode) {
+            case UI_MODE_TYPE_UNDEFINED:
+                return "UI_MODE_TYPE_UNDEFINED";
+            case UI_MODE_TYPE_NORMAL:
+                return "UI_MODE_TYPE_NORMAL";
+            case UI_MODE_TYPE_DESK:
+                return "UI_MODE_TYPE_DESK";
+            case UI_MODE_TYPE_CAR:
+                return "UI_MODE_TYPE_CAR";
+            case UI_MODE_TYPE_TELEVISION:
+                return "UI_MODE_TYPE_TELEVISION";
+            case UI_MODE_TYPE_APPLIANCE:
+                return "UI_MODE_TYPE_APPLIANCE";
+            case UI_MODE_TYPE_WATCH:
+                return "UI_MODE_TYPE_WATCH";
+            case UI_MODE_TYPE_VR_HEADSET:
+                return "UI_MODE_TYPE_VR_HEADSET";
+            default:
+                return Integer.toString(uiMode);
+        }
+    }
+
+    /**
+     * Set this object to the system defaults.
+     */
+    public void setToDefaults() {
+        fontScale = 1;
+        mcc = mnc = 0;
+        mLocaleList = LocaleList.getEmptyLocaleList();
+        locale = null;
+        userSetLocale = false;
+        touchscreen = TOUCHSCREEN_UNDEFINED;
+        keyboard = KEYBOARD_UNDEFINED;
+        keyboardHidden = KEYBOARDHIDDEN_UNDEFINED;
+        hardKeyboardHidden = HARDKEYBOARDHIDDEN_UNDEFINED;
+        navigation = NAVIGATION_UNDEFINED;
+        navigationHidden = NAVIGATIONHIDDEN_UNDEFINED;
+        orientation = ORIENTATION_UNDEFINED;
+        screenLayout = SCREENLAYOUT_UNDEFINED;
+        colorMode = COLOR_MODE_UNDEFINED;
+        uiMode = UI_MODE_TYPE_UNDEFINED;
+        screenWidthDp = compatScreenWidthDp = SCREEN_WIDTH_DP_UNDEFINED;
+        screenHeightDp = compatScreenHeightDp = SCREEN_HEIGHT_DP_UNDEFINED;
+        smallestScreenWidthDp = compatSmallestScreenWidthDp = SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
+        densityDpi = DENSITY_DPI_UNDEFINED;
+        assetsSeq = ASSETS_SEQ_UNDEFINED;
+        seq = 0;
+        windowConfiguration.setToDefaults();
+    }
+
+    /**
+     * Set this object to completely undefined.
+     * @hide
+     */
+    public void unset() {
+        setToDefaults();
+        fontScale = 0;
+    }
+
+    /** {@hide} */
+    @UnsupportedAppUsage
+    @Deprecated public void makeDefault() {
+        setToDefaults();
+    }
+
+    /**
+     * Copies the fields from delta into this Configuration object, keeping
+     * track of which ones have changed. Any undefined fields in {@code delta}
+     * are ignored and not copied in to the current Configuration.
+     *
+     * @return a bit mask of the changed fields, as per {@link #diff}
+     */
+    public @Config int updateFrom(@NonNull Configuration delta) {
+        int changed = 0;
+        if (delta.fontScale > 0 && fontScale != delta.fontScale) {
+            changed |= ActivityInfo.CONFIG_FONT_SCALE;
+            fontScale = delta.fontScale;
+        }
+        if (delta.mcc != 0 && mcc != delta.mcc) {
+            changed |= ActivityInfo.CONFIG_MCC;
+            mcc = delta.mcc;
+        }
+        if (delta.mnc != 0 && mnc != delta.mnc) {
+            changed |= ActivityInfo.CONFIG_MNC;
+            mnc = delta.mnc;
+        }
+        fixUpLocaleList();
+        delta.fixUpLocaleList();
+        if (!delta.mLocaleList.isEmpty() && !mLocaleList.equals(delta.mLocaleList)) {
+            changed |= ActivityInfo.CONFIG_LOCALE;
+            mLocaleList = delta.mLocaleList;
+            // delta.locale can't be null, since delta.mLocaleList is not empty.
+            if (!delta.locale.equals(locale)) {
+                locale = (Locale) delta.locale.clone();
+                // If locale has changed, then layout direction is also changed ...
+                changed |= ActivityInfo.CONFIG_LAYOUT_DIRECTION;
+                // ... and we need to update the layout direction (represented by the first
+                // 2 most significant bits in screenLayout).
+                setLayoutDirection(locale);
+            }
+        }
+        final int deltaScreenLayoutDir = delta.screenLayout & SCREENLAYOUT_LAYOUTDIR_MASK;
+        if (deltaScreenLayoutDir != SCREENLAYOUT_LAYOUTDIR_UNDEFINED &&
+                deltaScreenLayoutDir != (screenLayout & SCREENLAYOUT_LAYOUTDIR_MASK)) {
+            screenLayout = (screenLayout & ~SCREENLAYOUT_LAYOUTDIR_MASK) | deltaScreenLayoutDir;
+            changed |= ActivityInfo.CONFIG_LAYOUT_DIRECTION;
+        }
+        if (delta.userSetLocale && (!userSetLocale || ((changed & ActivityInfo.CONFIG_LOCALE) != 0)))
+        {
+            changed |= ActivityInfo.CONFIG_LOCALE;
+            userSetLocale = true;
+        }
+        if (delta.touchscreen != TOUCHSCREEN_UNDEFINED
+                && touchscreen != delta.touchscreen) {
+            changed |= ActivityInfo.CONFIG_TOUCHSCREEN;
+            touchscreen = delta.touchscreen;
+        }
+        if (delta.keyboard != KEYBOARD_UNDEFINED
+                && keyboard != delta.keyboard) {
+            changed |= ActivityInfo.CONFIG_KEYBOARD;
+            keyboard = delta.keyboard;
+        }
+        if (delta.keyboardHidden != KEYBOARDHIDDEN_UNDEFINED
+                && keyboardHidden != delta.keyboardHidden) {
+            changed |= ActivityInfo.CONFIG_KEYBOARD_HIDDEN;
+            keyboardHidden = delta.keyboardHidden;
+        }
+        if (delta.hardKeyboardHidden != HARDKEYBOARDHIDDEN_UNDEFINED
+                && hardKeyboardHidden != delta.hardKeyboardHidden) {
+            changed |= ActivityInfo.CONFIG_KEYBOARD_HIDDEN;
+            hardKeyboardHidden = delta.hardKeyboardHidden;
+        }
+        if (delta.navigation != NAVIGATION_UNDEFINED
+                && navigation != delta.navigation) {
+            changed |= ActivityInfo.CONFIG_NAVIGATION;
+            navigation = delta.navigation;
+        }
+        if (delta.navigationHidden != NAVIGATIONHIDDEN_UNDEFINED
+                && navigationHidden != delta.navigationHidden) {
+            changed |= ActivityInfo.CONFIG_KEYBOARD_HIDDEN;
+            navigationHidden = delta.navigationHidden;
+        }
+        if (delta.orientation != ORIENTATION_UNDEFINED
+                && orientation != delta.orientation) {
+            changed |= ActivityInfo.CONFIG_ORIENTATION;
+            orientation = delta.orientation;
+        }
+        if (((delta.screenLayout & SCREENLAYOUT_SIZE_MASK) != SCREENLAYOUT_SIZE_UNDEFINED)
+                && (delta.screenLayout & SCREENLAYOUT_SIZE_MASK)
+                != (screenLayout & SCREENLAYOUT_SIZE_MASK)) {
+            changed |= ActivityInfo.CONFIG_SCREEN_LAYOUT;
+            screenLayout = (screenLayout & ~SCREENLAYOUT_SIZE_MASK)
+                    | (delta.screenLayout & SCREENLAYOUT_SIZE_MASK);
+        }
+        if (((delta.screenLayout & SCREENLAYOUT_LONG_MASK) != SCREENLAYOUT_LONG_UNDEFINED)
+                && (delta.screenLayout & SCREENLAYOUT_LONG_MASK)
+                != (screenLayout & SCREENLAYOUT_LONG_MASK)) {
+            changed |= ActivityInfo.CONFIG_SCREEN_LAYOUT;
+            screenLayout = (screenLayout & ~SCREENLAYOUT_LONG_MASK)
+                    | (delta.screenLayout & SCREENLAYOUT_LONG_MASK);
+        }
+        if (((delta.screenLayout & SCREENLAYOUT_ROUND_MASK) != SCREENLAYOUT_ROUND_UNDEFINED)
+                && (delta.screenLayout & SCREENLAYOUT_ROUND_MASK)
+                != (screenLayout & SCREENLAYOUT_ROUND_MASK)) {
+            changed |= ActivityInfo.CONFIG_SCREEN_LAYOUT;
+            screenLayout = (screenLayout & ~SCREENLAYOUT_ROUND_MASK)
+                    | (delta.screenLayout & SCREENLAYOUT_ROUND_MASK);
+        }
+        if ((delta.screenLayout & SCREENLAYOUT_COMPAT_NEEDED)
+                != (screenLayout & SCREENLAYOUT_COMPAT_NEEDED)
+                && delta.screenLayout != 0) {
+            changed |= ActivityInfo.CONFIG_SCREEN_LAYOUT;
+            screenLayout = (screenLayout & ~SCREENLAYOUT_COMPAT_NEEDED)
+                | (delta.screenLayout & SCREENLAYOUT_COMPAT_NEEDED);
+        }
+
+        if (((delta.colorMode & COLOR_MODE_WIDE_COLOR_GAMUT_MASK) !=
+                     COLOR_MODE_WIDE_COLOR_GAMUT_UNDEFINED)
+                && (delta.colorMode & COLOR_MODE_WIDE_COLOR_GAMUT_MASK)
+                != (colorMode & COLOR_MODE_WIDE_COLOR_GAMUT_MASK)) {
+            changed |= ActivityInfo.CONFIG_COLOR_MODE;
+            colorMode = (colorMode & ~COLOR_MODE_WIDE_COLOR_GAMUT_MASK)
+                    | (delta.colorMode & COLOR_MODE_WIDE_COLOR_GAMUT_MASK);
+        }
+
+        if (((delta.colorMode & COLOR_MODE_HDR_MASK) != COLOR_MODE_HDR_UNDEFINED)
+                && (delta.colorMode & COLOR_MODE_HDR_MASK)
+                != (colorMode & COLOR_MODE_HDR_MASK)) {
+            changed |= ActivityInfo.CONFIG_COLOR_MODE;
+            colorMode = (colorMode & ~COLOR_MODE_HDR_MASK)
+                    | (delta.colorMode & COLOR_MODE_HDR_MASK);
+        }
+
+        if (delta.uiMode != (UI_MODE_TYPE_UNDEFINED|UI_MODE_NIGHT_UNDEFINED)
+                && uiMode != delta.uiMode) {
+            changed |= ActivityInfo.CONFIG_UI_MODE;
+            if ((delta.uiMode&UI_MODE_TYPE_MASK) != UI_MODE_TYPE_UNDEFINED) {
+                uiMode = (uiMode&~UI_MODE_TYPE_MASK)
+                        | (delta.uiMode&UI_MODE_TYPE_MASK);
+            }
+            if ((delta.uiMode&UI_MODE_NIGHT_MASK) != UI_MODE_NIGHT_UNDEFINED) {
+                uiMode = (uiMode&~UI_MODE_NIGHT_MASK)
+                        | (delta.uiMode&UI_MODE_NIGHT_MASK);
+            }
+        }
+        if (delta.screenWidthDp != SCREEN_WIDTH_DP_UNDEFINED
+                && screenWidthDp != delta.screenWidthDp) {
+            changed |= ActivityInfo.CONFIG_SCREEN_SIZE;
+            screenWidthDp = delta.screenWidthDp;
+        }
+        if (delta.screenHeightDp != SCREEN_HEIGHT_DP_UNDEFINED
+                && screenHeightDp != delta.screenHeightDp) {
+            changed |= ActivityInfo.CONFIG_SCREEN_SIZE;
+            screenHeightDp = delta.screenHeightDp;
+        }
+        if (delta.smallestScreenWidthDp != SMALLEST_SCREEN_WIDTH_DP_UNDEFINED
+                && smallestScreenWidthDp != delta.smallestScreenWidthDp) {
+            changed |= ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
+            smallestScreenWidthDp = delta.smallestScreenWidthDp;
+        }
+        if (delta.densityDpi != DENSITY_DPI_UNDEFINED &&
+                densityDpi != delta.densityDpi) {
+            changed |= ActivityInfo.CONFIG_DENSITY;
+            densityDpi = delta.densityDpi;
+        }
+        if (delta.compatScreenWidthDp != SCREEN_WIDTH_DP_UNDEFINED) {
+            compatScreenWidthDp = delta.compatScreenWidthDp;
+        }
+        if (delta.compatScreenHeightDp != SCREEN_HEIGHT_DP_UNDEFINED) {
+            compatScreenHeightDp = delta.compatScreenHeightDp;
+        }
+        if (delta.compatSmallestScreenWidthDp != SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) {
+            compatSmallestScreenWidthDp = delta.compatSmallestScreenWidthDp;
+        }
+        if (delta.assetsSeq != ASSETS_SEQ_UNDEFINED && delta.assetsSeq != assetsSeq) {
+            changed |= ActivityInfo.CONFIG_ASSETS_PATHS;
+            assetsSeq = delta.assetsSeq;
+        }
+        if (delta.seq != 0) {
+            seq = delta.seq;
+        }
+        if (windowConfiguration.updateFrom(delta.windowConfiguration) != 0) {
+            changed |= ActivityInfo.CONFIG_WINDOW_CONFIGURATION;
+        }
+
+        return changed;
+    }
+
+    /**
+     * Copies the fields specified by mask from delta into this Configuration object. This will
+     * copy anything allowed by the mask (including undefined values).
+     * @hide
+     */
+    public void setTo(@NonNull Configuration delta, @Config int mask,
+            @WindowConfiguration.WindowConfig int windowMask) {
+        if ((mask & ActivityInfo.CONFIG_FONT_SCALE) != 0) {
+            fontScale = delta.fontScale;
+        }
+        if ((mask & ActivityInfo.CONFIG_MCC) != 0) {
+            mcc = delta.mcc;
+        }
+        if ((mask & ActivityInfo.CONFIG_MNC) != 0) {
+            mnc = delta.mnc;
+        }
+        if ((mask & ActivityInfo.CONFIG_LOCALE) != 0) {
+            mLocaleList = delta.mLocaleList;
+            if (!mLocaleList.isEmpty()) {
+                locale = (Locale) delta.locale.clone();
+            }
+        }
+        if ((mask & ActivityInfo.CONFIG_LAYOUT_DIRECTION) != 0) {
+            final int deltaScreenLayoutDir = delta.screenLayout & SCREENLAYOUT_LAYOUTDIR_MASK;
+            screenLayout = (screenLayout & ~SCREENLAYOUT_LAYOUTDIR_MASK) | deltaScreenLayoutDir;
+        }
+        if ((mask & ActivityInfo.CONFIG_LOCALE) != 0) {
+            userSetLocale = delta.userSetLocale;
+        }
+        if ((mask & ActivityInfo.CONFIG_TOUCHSCREEN) != 0) {
+            touchscreen = delta.touchscreen;
+        }
+        if ((mask & ActivityInfo.CONFIG_KEYBOARD) != 0) {
+            keyboard = delta.keyboard;
+        }
+        if ((mask & ActivityInfo.CONFIG_KEYBOARD_HIDDEN) != 0) {
+            keyboardHidden = delta.keyboardHidden;
+            hardKeyboardHidden = delta.hardKeyboardHidden;
+            navigationHidden = delta.navigationHidden;
+        }
+        if ((mask & ActivityInfo.CONFIG_NAVIGATION) != 0) {
+            navigation = delta.navigation;
+        }
+        if ((mask & ActivityInfo.CONFIG_ORIENTATION) != 0) {
+            orientation = delta.orientation;
+        }
+        if ((mask & ActivityInfo.CONFIG_SCREEN_LAYOUT) != 0) {
+            // Not enough granularity for each component unfortunately.
+            screenLayout = screenLayout | (delta.screenLayout & ~SCREENLAYOUT_LAYOUTDIR_MASK);
+        }
+        if ((mask & ActivityInfo.CONFIG_COLOR_MODE) != 0) {
+            colorMode = delta.colorMode;
+        }
+        if ((mask & ActivityInfo.CONFIG_UI_MODE) != 0) {
+            uiMode = delta.uiMode;
+        }
+        if ((mask & ActivityInfo.CONFIG_SCREEN_SIZE) != 0) {
+            screenWidthDp = delta.screenWidthDp;
+            screenHeightDp = delta.screenHeightDp;
+        }
+        if ((mask & ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE) != 0) {
+            smallestScreenWidthDp = delta.smallestScreenWidthDp;
+        }
+        if ((mask & ActivityInfo.CONFIG_DENSITY) != 0) {
+            densityDpi = delta.densityDpi;
+        }
+        if ((mask & ActivityInfo.CONFIG_ASSETS_PATHS) != 0) {
+            assetsSeq = delta.assetsSeq;
+        }
+        if ((mask & ActivityInfo.CONFIG_WINDOW_CONFIGURATION) != 0) {
+            windowConfiguration.setTo(delta.windowConfiguration, windowMask);
+        }
+    }
+
+    /**
+     * Return a bit mask of the differences between this Configuration
+     * object and the given one.  Does not change the values of either.  Any
+     * undefined fields in <var>delta</var> are ignored.
+     * @return Returns a bit mask indicating which configuration
+     * values has changed, containing any combination of
+     * {@link android.content.pm.ActivityInfo#CONFIG_FONT_SCALE
+     * PackageManager.ActivityInfo.CONFIG_FONT_SCALE},
+     * {@link android.content.pm.ActivityInfo#CONFIG_MCC
+     * PackageManager.ActivityInfo.CONFIG_MCC},
+     * {@link android.content.pm.ActivityInfo#CONFIG_MNC
+     * PackageManager.ActivityInfo.CONFIG_MNC},
+     * {@link android.content.pm.ActivityInfo#CONFIG_LOCALE
+     * PackageManager.ActivityInfo.CONFIG_LOCALE},
+     * {@link android.content.pm.ActivityInfo#CONFIG_TOUCHSCREEN
+     * PackageManager.ActivityInfo.CONFIG_TOUCHSCREEN},
+     * {@link android.content.pm.ActivityInfo#CONFIG_KEYBOARD
+     * PackageManager.ActivityInfo.CONFIG_KEYBOARD},
+     * {@link android.content.pm.ActivityInfo#CONFIG_NAVIGATION
+     * PackageManager.ActivityInfo.CONFIG_NAVIGATION},
+     * {@link android.content.pm.ActivityInfo#CONFIG_ORIENTATION
+     * PackageManager.ActivityInfo.CONFIG_ORIENTATION},
+     * {@link android.content.pm.ActivityInfo#CONFIG_SCREEN_LAYOUT
+     * PackageManager.ActivityInfo.CONFIG_SCREEN_LAYOUT}, or
+     * {@link android.content.pm.ActivityInfo#CONFIG_SCREEN_SIZE
+     * PackageManager.ActivityInfo.CONFIG_SCREEN_SIZE}, or
+     * {@link android.content.pm.ActivityInfo#CONFIG_SMALLEST_SCREEN_SIZE
+     * PackageManager.ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE}.
+     * {@link android.content.pm.ActivityInfo#CONFIG_LAYOUT_DIRECTION
+     * PackageManager.ActivityInfo.CONFIG_LAYOUT_DIRECTION}.
+     */
+    public int diff(Configuration delta) {
+        return diff(delta, false /* compareUndefined */, false /* publicOnly */);
+    }
+
+    /**
+     * Returns the diff against the provided {@link Configuration} excluding values that would
+     * publicly be equivalent, such as appBounds.
+     * @param delta {@link Configuration} to compare to.
+     *
+     * TODO(b/36812336): Remove once appBounds has been moved out of Configuration.
+     * {@hide}
+     */
+    public int diffPublicOnly(Configuration delta) {
+        return diff(delta, false /* compareUndefined */, true /* publicOnly */);
+    }
+
+    /**
+     * Variation of {@link #diff(Configuration)} with an option to skip checks for undefined values.
+     *
+     * @hide
+     */
+    public int diff(Configuration delta, boolean compareUndefined, boolean publicOnly) {
+        int changed = 0;
+        if ((compareUndefined || delta.fontScale > 0) && fontScale != delta.fontScale) {
+            changed |= ActivityInfo.CONFIG_FONT_SCALE;
+        }
+        if ((compareUndefined || delta.mcc != 0) && mcc != delta.mcc) {
+            changed |= ActivityInfo.CONFIG_MCC;
+        }
+        if ((compareUndefined || delta.mnc != 0) && mnc != delta.mnc) {
+            changed |= ActivityInfo.CONFIG_MNC;
+        }
+        fixUpLocaleList();
+        delta.fixUpLocaleList();
+        if ((compareUndefined || !delta.mLocaleList.isEmpty())
+                && !mLocaleList.equals(delta.mLocaleList)) {
+            changed |= ActivityInfo.CONFIG_LOCALE;
+            changed |= ActivityInfo.CONFIG_LAYOUT_DIRECTION;
+        }
+        final int deltaScreenLayoutDir = delta.screenLayout & SCREENLAYOUT_LAYOUTDIR_MASK;
+        if ((compareUndefined || deltaScreenLayoutDir != SCREENLAYOUT_LAYOUTDIR_UNDEFINED)
+                && deltaScreenLayoutDir != (screenLayout & SCREENLAYOUT_LAYOUTDIR_MASK)) {
+            changed |= ActivityInfo.CONFIG_LAYOUT_DIRECTION;
+        }
+        if ((compareUndefined || delta.touchscreen != TOUCHSCREEN_UNDEFINED)
+                && touchscreen != delta.touchscreen) {
+            changed |= ActivityInfo.CONFIG_TOUCHSCREEN;
+        }
+        if ((compareUndefined || delta.keyboard != KEYBOARD_UNDEFINED)
+                && keyboard != delta.keyboard) {
+            changed |= ActivityInfo.CONFIG_KEYBOARD;
+        }
+        if ((compareUndefined || delta.keyboardHidden != KEYBOARDHIDDEN_UNDEFINED)
+                && keyboardHidden != delta.keyboardHidden) {
+            changed |= ActivityInfo.CONFIG_KEYBOARD_HIDDEN;
+        }
+        if ((compareUndefined || delta.hardKeyboardHidden != HARDKEYBOARDHIDDEN_UNDEFINED)
+                && hardKeyboardHidden != delta.hardKeyboardHidden) {
+            changed |= ActivityInfo.CONFIG_KEYBOARD_HIDDEN;
+        }
+        if ((compareUndefined || delta.navigation != NAVIGATION_UNDEFINED)
+                && navigation != delta.navigation) {
+            changed |= ActivityInfo.CONFIG_NAVIGATION;
+        }
+        if ((compareUndefined || delta.navigationHidden != NAVIGATIONHIDDEN_UNDEFINED)
+                && navigationHidden != delta.navigationHidden) {
+            changed |= ActivityInfo.CONFIG_KEYBOARD_HIDDEN;
+        }
+        if ((compareUndefined || delta.orientation != ORIENTATION_UNDEFINED)
+                && orientation != delta.orientation) {
+            changed |= ActivityInfo.CONFIG_ORIENTATION;
+        }
+        if ((compareUndefined || getScreenLayoutNoDirection(delta.screenLayout) !=
+                (SCREENLAYOUT_SIZE_UNDEFINED | SCREENLAYOUT_LONG_UNDEFINED))
+                && getScreenLayoutNoDirection(screenLayout) !=
+                getScreenLayoutNoDirection(delta.screenLayout)) {
+            changed |= ActivityInfo.CONFIG_SCREEN_LAYOUT;
+        }
+        if ((compareUndefined ||
+                     (delta.colorMode & COLOR_MODE_HDR_MASK) != COLOR_MODE_HDR_UNDEFINED)
+                && (colorMode & COLOR_MODE_HDR_MASK) !=
+                        (delta.colorMode & COLOR_MODE_HDR_MASK)) {
+            changed |= ActivityInfo.CONFIG_COLOR_MODE;
+        }
+        if ((compareUndefined ||
+                     (delta.colorMode & COLOR_MODE_WIDE_COLOR_GAMUT_MASK) !=
+                             COLOR_MODE_WIDE_COLOR_GAMUT_UNDEFINED)
+                && (colorMode & COLOR_MODE_WIDE_COLOR_GAMUT_MASK) !=
+                        (delta.colorMode & COLOR_MODE_WIDE_COLOR_GAMUT_MASK)) {
+            changed |= ActivityInfo.CONFIG_COLOR_MODE;
+        }
+        if ((compareUndefined || delta.uiMode != (UI_MODE_TYPE_UNDEFINED|UI_MODE_NIGHT_UNDEFINED))
+                && uiMode != delta.uiMode) {
+            changed |= ActivityInfo.CONFIG_UI_MODE;
+        }
+        if ((compareUndefined || delta.screenWidthDp != SCREEN_WIDTH_DP_UNDEFINED)
+                && screenWidthDp != delta.screenWidthDp) {
+            changed |= ActivityInfo.CONFIG_SCREEN_SIZE;
+        }
+        if ((compareUndefined || delta.screenHeightDp != SCREEN_HEIGHT_DP_UNDEFINED)
+                && screenHeightDp != delta.screenHeightDp) {
+            changed |= ActivityInfo.CONFIG_SCREEN_SIZE;
+        }
+        if ((compareUndefined || delta.smallestScreenWidthDp != SMALLEST_SCREEN_WIDTH_DP_UNDEFINED)
+                && smallestScreenWidthDp != delta.smallestScreenWidthDp) {
+            changed |= ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
+        }
+        if ((compareUndefined || delta.densityDpi != DENSITY_DPI_UNDEFINED)
+                && densityDpi != delta.densityDpi) {
+            changed |= ActivityInfo.CONFIG_DENSITY;
+        }
+        if ((compareUndefined || delta.assetsSeq != ASSETS_SEQ_UNDEFINED)
+                && assetsSeq != delta.assetsSeq) {
+            changed |= ActivityInfo.CONFIG_ASSETS_PATHS;
+        }
+
+        // WindowConfiguration differences aren't considered public...
+        if (!publicOnly
+                && windowConfiguration.diff(delta.windowConfiguration, compareUndefined) != 0) {
+            changed |= ActivityInfo.CONFIG_WINDOW_CONFIGURATION;
+        }
+
+        return changed;
+    }
+
+    /**
+     * Determines if a new resource needs to be loaded from the bit set of
+     * configuration changes returned by {@link #updateFrom(Configuration)}.
+     *
+     * @param configChanges the mask of changes configurations as returned by
+     *                      {@link #updateFrom(Configuration)}
+     * @param interestingChanges the configuration changes that the resource
+     *                           can handle as given in
+     *                           {@link android.util.TypedValue#changingConfigurations}
+     * @return {@code true} if the resource needs to be loaded, {@code false}
+     *         otherwise
+     */
+    public static boolean needNewResources(@Config int configChanges,
+            @Config int interestingChanges) {
+        // CONFIG_ASSETS_PATHS and CONFIG_FONT_SCALE are higher level configuration changes that
+        // all resources are subject to change with.
+        interestingChanges = interestingChanges | ActivityInfo.CONFIG_ASSETS_PATHS
+                | ActivityInfo.CONFIG_FONT_SCALE;
+        return (configChanges & interestingChanges) != 0;
+    }
+
+    /**
+     * @hide Return true if the sequence of 'other' is better than this.  Assumes
+     * that 'this' is your current sequence and 'other' is a new one you have
+     * received some how and want to compare with what you have.
+     */
+    public boolean isOtherSeqNewer(Configuration other) {
+        if (other == null) {
+            // Sanity check.
+            return false;
+        }
+        if (other.seq == 0) {
+            // If the other sequence is not specified, then we must assume
+            // it is newer since we don't know any better.
+            return true;
+        }
+        if (seq == 0) {
+            // If this sequence is not specified, then we also consider the
+            // other is better.  Yes we have a preference for other.  Sue us.
+            return true;
+        }
+        int diff = other.seq - seq;
+        if (diff > 0x10000) {
+            // If there has been a sufficiently large jump, assume the
+            // sequence has wrapped around.
+            return false;
+        }
+        return diff > 0;
+    }
+
+    /**
+     * Parcelable methods
+     */
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeFloat(fontScale);
+        dest.writeInt(mcc);
+        dest.writeInt(mnc);
+
+        fixUpLocaleList();
+        dest.writeParcelable(mLocaleList, flags);
+
+        if(userSetLocale) {
+            dest.writeInt(1);
+        } else {
+            dest.writeInt(0);
+        }
+        dest.writeInt(touchscreen);
+        dest.writeInt(keyboard);
+        dest.writeInt(keyboardHidden);
+        dest.writeInt(hardKeyboardHidden);
+        dest.writeInt(navigation);
+        dest.writeInt(navigationHidden);
+        dest.writeInt(orientation);
+        dest.writeInt(screenLayout);
+        dest.writeInt(colorMode);
+        dest.writeInt(uiMode);
+        dest.writeInt(screenWidthDp);
+        dest.writeInt(screenHeightDp);
+        dest.writeInt(smallestScreenWidthDp);
+        dest.writeInt(densityDpi);
+        dest.writeInt(compatScreenWidthDp);
+        dest.writeInt(compatScreenHeightDp);
+        dest.writeInt(compatSmallestScreenWidthDp);
+        dest.writeValue(windowConfiguration);
+        dest.writeInt(assetsSeq);
+        dest.writeInt(seq);
+    }
+
+    public void readFromParcel(Parcel source) {
+        fontScale = source.readFloat();
+        mcc = source.readInt();
+        mnc = source.readInt();
+
+        mLocaleList = source.readParcelable(LocaleList.class.getClassLoader());
+        locale = mLocaleList.get(0);
+
+        userSetLocale = (source.readInt()==1);
+        touchscreen = source.readInt();
+        keyboard = source.readInt();
+        keyboardHidden = source.readInt();
+        hardKeyboardHidden = source.readInt();
+        navigation = source.readInt();
+        navigationHidden = source.readInt();
+        orientation = source.readInt();
+        screenLayout = source.readInt();
+        colorMode = source.readInt();
+        uiMode = source.readInt();
+        screenWidthDp = source.readInt();
+        screenHeightDp = source.readInt();
+        smallestScreenWidthDp = source.readInt();
+        densityDpi = source.readInt();
+        compatScreenWidthDp = source.readInt();
+        compatScreenHeightDp = source.readInt();
+        compatSmallestScreenWidthDp = source.readInt();
+        windowConfiguration.setTo((WindowConfiguration) source.readValue(null));
+        assetsSeq = source.readInt();
+        seq = source.readInt();
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<Configuration> CREATOR
+            = new Parcelable.Creator<Configuration>() {
+        public Configuration createFromParcel(Parcel source) {
+            return new Configuration(source);
+        }
+
+        public Configuration[] newArray(int size) {
+            return new Configuration[size];
+        }
+    };
+
+    /**
+     * Construct this Configuration object, reading from the Parcel.
+     */
+    private Configuration(Parcel source) {
+        readFromParcel(source);
+    }
+
+
+    /**
+     * Retuns whether the configuration is in night mode
+     * @return true if night mode is active and false otherwise
+     */
+    public boolean isNightModeActive() {
+        return (uiMode & UI_MODE_NIGHT_MASK) == UI_MODE_NIGHT_YES;
+    }
+
+    public int compareTo(Configuration that) {
+        int n;
+        float a = this.fontScale;
+        float b = that.fontScale;
+        if (a < b) return -1;
+        if (a > b) return 1;
+        n = this.mcc - that.mcc;
+        if (n != 0) return n;
+        n = this.mnc - that.mnc;
+        if (n != 0) return n;
+
+        fixUpLocaleList();
+        that.fixUpLocaleList();
+        // for backward compatibility, we consider an empty locale list to be greater
+        // than any non-empty locale list.
+        if (this.mLocaleList.isEmpty()) {
+            if (!that.mLocaleList.isEmpty()) return 1;
+        } else if (that.mLocaleList.isEmpty()) {
+            return -1;
+        } else {
+            final int minSize = Math.min(this.mLocaleList.size(), that.mLocaleList.size());
+            for (int i = 0; i < minSize; ++i) {
+                final Locale thisLocale = this.mLocaleList.get(i);
+                final Locale thatLocale = that.mLocaleList.get(i);
+                n = thisLocale.getLanguage().compareTo(thatLocale.getLanguage());
+                if (n != 0) return n;
+                n = thisLocale.getCountry().compareTo(thatLocale.getCountry());
+                if (n != 0) return n;
+                n = thisLocale.getVariant().compareTo(thatLocale.getVariant());
+                if (n != 0) return n;
+                n = thisLocale.toLanguageTag().compareTo(thatLocale.toLanguageTag());
+                if (n != 0) return n;
+            }
+            n = this.mLocaleList.size() - that.mLocaleList.size();
+            if (n != 0) return n;
+        }
+
+        n = this.touchscreen - that.touchscreen;
+        if (n != 0) return n;
+        n = this.keyboard - that.keyboard;
+        if (n != 0) return n;
+        n = this.keyboardHidden - that.keyboardHidden;
+        if (n != 0) return n;
+        n = this.hardKeyboardHidden - that.hardKeyboardHidden;
+        if (n != 0) return n;
+        n = this.navigation - that.navigation;
+        if (n != 0) return n;
+        n = this.navigationHidden - that.navigationHidden;
+        if (n != 0) return n;
+        n = this.orientation - that.orientation;
+        if (n != 0) return n;
+        n = this.colorMode - that.colorMode;
+        if (n != 0) return n;
+        n = this.screenLayout - that.screenLayout;
+        if (n != 0) return n;
+        n = this.uiMode - that.uiMode;
+        if (n != 0) return n;
+        n = this.screenWidthDp - that.screenWidthDp;
+        if (n != 0) return n;
+        n = this.screenHeightDp - that.screenHeightDp;
+        if (n != 0) return n;
+        n = this.smallestScreenWidthDp - that.smallestScreenWidthDp;
+        if (n != 0) return n;
+        n = this.densityDpi - that.densityDpi;
+        if (n != 0) return n;
+        n = this.assetsSeq - that.assetsSeq;
+        if (n != 0) return n;
+        n = windowConfiguration.compareTo(that.windowConfiguration);
+        if (n != 0) return n;
+
+        // if (n != 0) return n;
+        return n;
+    }
+
+    public boolean equals(Configuration that) {
+        if (that == null) return false;
+        if (that == this) return true;
+        return this.compareTo(that) == 0;
+    }
+
+    public boolean equals(Object that) {
+        try {
+            return equals((Configuration)that);
+        } catch (ClassCastException e) {
+        }
+        return false;
+    }
+
+    public int hashCode() {
+        int result = 17;
+        result = 31 * result + Float.floatToIntBits(fontScale);
+        result = 31 * result + mcc;
+        result = 31 * result + mnc;
+        result = 31 * result + mLocaleList.hashCode();
+        result = 31 * result + touchscreen;
+        result = 31 * result + keyboard;
+        result = 31 * result + keyboardHidden;
+        result = 31 * result + hardKeyboardHidden;
+        result = 31 * result + navigation;
+        result = 31 * result + navigationHidden;
+        result = 31 * result + orientation;
+        result = 31 * result + screenLayout;
+        result = 31 * result + colorMode;
+        result = 31 * result + uiMode;
+        result = 31 * result + screenWidthDp;
+        result = 31 * result + screenHeightDp;
+        result = 31 * result + smallestScreenWidthDp;
+        result = 31 * result + densityDpi;
+        result = 31 * result + assetsSeq;
+        return result;
+    }
+
+    /**
+     * Get the locale list. This is the preferred way for getting the locales (instead of using
+     * the direct accessor to {@link #locale}, which would only provide the primary locale).
+     *
+     * @return The locale list.
+     */
+    public @NonNull LocaleList getLocales() {
+        fixUpLocaleList();
+        return mLocaleList;
+    }
+
+    /**
+     * Set the locale list. This is the preferred way for setting up the locales (instead of using
+     * the direct accessor or {@link #setLocale(Locale)}). This will also set the layout direction
+     * according to the first locale in the list.
+     *
+     * Note that the layout direction will always come from the first locale in the locale list,
+     * even if the locale is not supported by the resources (the resources may only support
+     * another locale further down the list which has a different direction).
+     *
+     * @param locales The locale list. If null, an empty LocaleList will be assigned.
+     */
+    public void setLocales(@Nullable LocaleList locales) {
+        mLocaleList = locales == null ? LocaleList.getEmptyLocaleList() : locales;
+        locale = mLocaleList.get(0);
+        setLayoutDirection(locale);
+    }
+
+    /**
+     * Set the locale list to a list of just one locale. This will also set the layout direction
+     * according to the locale.
+     *
+     * Note that after this is run, calling <code>.equals()</code> on the input locale and the
+     * {@link #locale} attribute would return <code>true</code> if they are not null, but there is
+     * no guarantee that they would be the same object.
+     *
+     * See also the note about layout direction in {@link #setLocales(LocaleList)}.
+     *
+     * @param loc The locale. Can be null.
+     */
+    public void setLocale(@Nullable Locale loc) {
+        setLocales(loc == null ? LocaleList.getEmptyLocaleList() : new LocaleList(loc));
+    }
+
+    /**
+     * @hide
+     *
+     * Clears the locale without changing layout direction.
+     */
+    public void clearLocales() {
+        mLocaleList = LocaleList.getEmptyLocaleList();
+        locale = null;
+    }
+
+    /**
+     * Return the layout direction. Will be either {@link View#LAYOUT_DIRECTION_LTR} or
+     * {@link View#LAYOUT_DIRECTION_RTL}.
+     *
+     * @return Returns {@link View#LAYOUT_DIRECTION_RTL} if the configuration
+     * is {@link #SCREENLAYOUT_LAYOUTDIR_RTL}, otherwise {@link View#LAYOUT_DIRECTION_LTR}.
+     */
+    public int getLayoutDirection() {
+        return (screenLayout&SCREENLAYOUT_LAYOUTDIR_MASK) == SCREENLAYOUT_LAYOUTDIR_RTL
+                ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR;
+    }
+
+    /**
+     * Set the layout direction from a Locale.
+     *
+     * @param loc The Locale. If null will set the layout direction to
+     * {@link View#LAYOUT_DIRECTION_LTR}. If not null will set it to the layout direction
+     * corresponding to the Locale.
+     *
+     * @see View#LAYOUT_DIRECTION_LTR
+     * @see View#LAYOUT_DIRECTION_RTL
+     */
+    public void setLayoutDirection(Locale loc) {
+        // There is a "1" difference between the configuration values for
+        // layout direction and View constants for layout direction, just add "1".
+        final int layoutDirection = 1 + TextUtils.getLayoutDirectionFromLocale(loc);
+        screenLayout = (screenLayout&~SCREENLAYOUT_LAYOUTDIR_MASK)|
+                (layoutDirection << SCREENLAYOUT_LAYOUTDIR_SHIFT);
+    }
+
+    private static int getScreenLayoutNoDirection(int screenLayout) {
+        return screenLayout&~SCREENLAYOUT_LAYOUTDIR_MASK;
+    }
+
+    /**
+     * Return whether the screen has a round shape. Apps may choose to change styling based
+     * on this property, such as the alignment or layout of text or informational icons.
+     *
+     * @return true if the screen is rounded, false otherwise
+     */
+    public boolean isScreenRound() {
+        return (screenLayout & SCREENLAYOUT_ROUND_MASK) == SCREENLAYOUT_ROUND_YES;
+    }
+
+    /**
+     * Return whether the screen has a wide color gamut and wide color gamut rendering
+     * is supported by this device.
+     *
+     * When true, it implies the screen is colorspace aware but not
+     * necessarily color-managed. The final colors may still be changed by the
+     * screen depending on user settings.
+     *
+     * @return true if the screen has a wide color gamut and wide color gamut rendering
+     * is supported, false otherwise
+     */
+    public boolean isScreenWideColorGamut() {
+        return (colorMode & COLOR_MODE_WIDE_COLOR_GAMUT_MASK) == COLOR_MODE_WIDE_COLOR_GAMUT_YES;
+    }
+
+    /**
+     * Return whether the screen has a high dynamic range.
+     *
+     * @return true if the screen has a high dynamic range, false otherwise
+     */
+    public boolean isScreenHdr() {
+        return (colorMode & COLOR_MODE_HDR_MASK) == COLOR_MODE_HDR_YES;
+    }
+
+    /**
+     *
+     * @hide
+     */
+    public static String localesToResourceQualifier(LocaleList locs) {
+        final StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < locs.size(); i++) {
+            final Locale loc = locs.get(i);
+            final int l = loc.getLanguage().length();
+            if (l == 0) {
+                continue;
+            }
+            final int s = loc.getScript().length();
+            final int c = loc.getCountry().length();
+            final int v = loc.getVariant().length();
+            // We ignore locale extensions, since they are not supported by AAPT
+
+            if (sb.length() != 0) {
+                sb.append(",");
+            }
+            if (l == 2 && s == 0 && (c == 0 || c == 2) && v == 0) {
+                // Traditional locale format: xx or xx-rYY
+                sb.append(loc.getLanguage());
+                if (c == 2) {
+                    sb.append("-r").append(loc.getCountry());
+                }
+            } else {
+                sb.append("b+");
+                sb.append(loc.getLanguage());
+                if (s != 0) {
+                    sb.append("+");
+                    sb.append(loc.getScript());
+                }
+                if (c != 0) {
+                    sb.append("+");
+                    sb.append(loc.getCountry());
+                }
+                if (v != 0) {
+                    sb.append("+");
+                    sb.append(loc.getVariant());
+                }
+            }
+        }
+        return sb.toString();
+    }
+
+
+    /**
+     * Returns a string representation of the configuration that can be parsed
+     * by build tools (like AAPT), without display metrics included
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static String resourceQualifierString(Configuration config) {
+        return resourceQualifierString(config, null);
+    }
+
+    /**
+     * Returns a string representation of the configuration that can be parsed
+     * by build tools (like AAPT).
+     *
+     * @hide
+     */
+    public static String resourceQualifierString(Configuration config, DisplayMetrics metrics) {
+        ArrayList<String> parts = new ArrayList<String>();
+
+        if (config.mcc != 0) {
+            parts.add("mcc" + config.mcc);
+            if (config.mnc != 0) {
+                parts.add("mnc" + config.mnc);
+            }
+        }
+
+        if (!config.mLocaleList.isEmpty()) {
+            final String resourceQualifier = localesToResourceQualifier(config.mLocaleList);
+            if (!resourceQualifier.isEmpty()) {
+                parts.add(resourceQualifier);
+            }
+        }
+
+        switch (config.screenLayout & Configuration.SCREENLAYOUT_LAYOUTDIR_MASK) {
+            case Configuration.SCREENLAYOUT_LAYOUTDIR_LTR:
+                parts.add("ldltr");
+                break;
+            case Configuration.SCREENLAYOUT_LAYOUTDIR_RTL:
+                parts.add("ldrtl");
+                break;
+            default:
+                break;
+        }
+
+        if (config.smallestScreenWidthDp != 0) {
+            parts.add("sw" + config.smallestScreenWidthDp + "dp");
+        }
+
+        if (config.screenWidthDp != 0) {
+            parts.add("w" + config.screenWidthDp + "dp");
+        }
+
+        if (config.screenHeightDp != 0) {
+            parts.add("h" + config.screenHeightDp + "dp");
+        }
+
+        switch (config.screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) {
+            case Configuration.SCREENLAYOUT_SIZE_SMALL:
+                parts.add("small");
+                break;
+            case Configuration.SCREENLAYOUT_SIZE_NORMAL:
+                parts.add("normal");
+                break;
+            case Configuration.SCREENLAYOUT_SIZE_LARGE:
+                parts.add("large");
+                break;
+            case Configuration.SCREENLAYOUT_SIZE_XLARGE:
+                parts.add("xlarge");
+                break;
+            default:
+                break;
+        }
+
+        switch (config.screenLayout & Configuration.SCREENLAYOUT_LONG_MASK) {
+            case Configuration.SCREENLAYOUT_LONG_YES:
+                parts.add("long");
+                break;
+            case Configuration.SCREENLAYOUT_LONG_NO:
+                parts.add("notlong");
+                break;
+            default:
+                break;
+        }
+
+        switch (config.screenLayout & Configuration.SCREENLAYOUT_ROUND_MASK) {
+            case Configuration.SCREENLAYOUT_ROUND_YES:
+                parts.add("round");
+                break;
+            case Configuration.SCREENLAYOUT_ROUND_NO:
+                parts.add("notround");
+                break;
+            default:
+                break;
+        }
+
+        switch (config.colorMode & Configuration.COLOR_MODE_WIDE_COLOR_GAMUT_MASK) {
+            case Configuration.COLOR_MODE_WIDE_COLOR_GAMUT_YES:
+                parts.add("widecg");
+                break;
+            case Configuration.COLOR_MODE_WIDE_COLOR_GAMUT_NO:
+                parts.add("nowidecg");
+                break;
+            default:
+                break;
+        }
+
+        switch (config.colorMode & Configuration.COLOR_MODE_HDR_MASK) {
+            case Configuration.COLOR_MODE_HDR_YES:
+                parts.add("highdr");
+                break;
+            case Configuration.COLOR_MODE_HDR_NO:
+                parts.add("lowdr");
+                break;
+            default:
+                break;
+        }
+
+        switch (config.orientation) {
+            case Configuration.ORIENTATION_LANDSCAPE:
+                parts.add("land");
+                break;
+            case Configuration.ORIENTATION_PORTRAIT:
+                parts.add("port");
+                break;
+            default:
+                break;
+        }
+
+        switch (config.uiMode & Configuration.UI_MODE_TYPE_MASK) {
+            case Configuration.UI_MODE_TYPE_APPLIANCE:
+                parts.add("appliance");
+                break;
+            case Configuration.UI_MODE_TYPE_DESK:
+                parts.add("desk");
+                break;
+            case Configuration.UI_MODE_TYPE_TELEVISION:
+                parts.add("television");
+                break;
+            case Configuration.UI_MODE_TYPE_CAR:
+                parts.add("car");
+                break;
+            case Configuration.UI_MODE_TYPE_WATCH:
+                parts.add("watch");
+                break;
+            case Configuration.UI_MODE_TYPE_VR_HEADSET:
+                parts.add("vrheadset");
+                break;
+            default:
+                break;
+        }
+
+        switch (config.uiMode & Configuration.UI_MODE_NIGHT_MASK) {
+            case Configuration.UI_MODE_NIGHT_YES:
+                parts.add("night");
+                break;
+            case Configuration.UI_MODE_NIGHT_NO:
+                parts.add("notnight");
+                break;
+            default:
+                break;
+        }
+
+        switch (config.densityDpi) {
+            case DENSITY_DPI_UNDEFINED:
+                break;
+            case 120:
+                parts.add("ldpi");
+                break;
+            case 160:
+                parts.add("mdpi");
+                break;
+            case 213:
+                parts.add("tvdpi");
+                break;
+            case 240:
+                parts.add("hdpi");
+                break;
+            case 320:
+                parts.add("xhdpi");
+                break;
+            case 480:
+                parts.add("xxhdpi");
+                break;
+            case 640:
+                parts.add("xxxhdpi");
+                break;
+            case DENSITY_DPI_ANY:
+                parts.add("anydpi");
+                break;
+            case DENSITY_DPI_NONE:
+                parts.add("nodpi");
+                break;
+            default:
+                parts.add(config.densityDpi + "dpi");
+                break;
+        }
+
+        switch (config.touchscreen) {
+            case Configuration.TOUCHSCREEN_NOTOUCH:
+                parts.add("notouch");
+                break;
+            case Configuration.TOUCHSCREEN_FINGER:
+                parts.add("finger");
+                break;
+            default:
+                break;
+        }
+
+        switch (config.keyboardHidden) {
+            case Configuration.KEYBOARDHIDDEN_NO:
+                parts.add("keysexposed");
+                break;
+            case Configuration.KEYBOARDHIDDEN_YES:
+                parts.add("keyshidden");
+                break;
+            case Configuration.KEYBOARDHIDDEN_SOFT:
+                parts.add("keyssoft");
+                break;
+            default:
+                break;
+        }
+
+        switch (config.keyboard) {
+            case Configuration.KEYBOARD_NOKEYS:
+                parts.add("nokeys");
+                break;
+            case Configuration.KEYBOARD_QWERTY:
+                parts.add("qwerty");
+                break;
+            case Configuration.KEYBOARD_12KEY:
+                parts.add("12key");
+                break;
+            default:
+                break;
+        }
+
+        switch (config.navigationHidden) {
+            case Configuration.NAVIGATIONHIDDEN_NO:
+                parts.add("navexposed");
+                break;
+            case Configuration.NAVIGATIONHIDDEN_YES:
+                parts.add("navhidden");
+                break;
+            default:
+                break;
+        }
+
+        switch (config.navigation) {
+            case Configuration.NAVIGATION_NONAV:
+                parts.add("nonav");
+                break;
+            case Configuration.NAVIGATION_DPAD:
+                parts.add("dpad");
+                break;
+            case Configuration.NAVIGATION_TRACKBALL:
+                parts.add("trackball");
+                break;
+            case Configuration.NAVIGATION_WHEEL:
+                parts.add("wheel");
+                break;
+            default:
+                break;
+        }
+
+        if (metrics != null) {
+            final int width, height;
+            if (metrics.widthPixels >= metrics.heightPixels) {
+                width = metrics.widthPixels;
+                height = metrics.heightPixels;
+            } else {
+                //noinspection SuspiciousNameCombination
+                width = metrics.heightPixels;
+                //noinspection SuspiciousNameCombination
+                height = metrics.widthPixels;
+            }
+            parts.add(width + "x" + height);
+        }
+
+        parts.add("v" + Build.VERSION.RESOURCES_SDK_INT);
+        return TextUtils.join("-", parts);
+    }
+
+    /**
+     * Generate a delta Configuration between <code>base</code> and <code>change</code>. The
+     * resulting delta can be used with {@link #updateFrom(Configuration)}.
+     * <p />
+     * Caveat: If the any of the Configuration's members becomes undefined, then
+     * {@link #updateFrom(Configuration)} will treat it as a no-op and not update that member.
+     *
+     * This is fine for device configurations as no member is ever undefined.
+     * {@hide}
+     */
+    @UnsupportedAppUsage
+    public static Configuration generateDelta(Configuration base, Configuration change) {
+        final Configuration delta = new Configuration();
+        if (base.fontScale != change.fontScale) {
+            delta.fontScale = change.fontScale;
+        }
+
+        if (base.mcc != change.mcc) {
+            delta.mcc = change.mcc;
+        }
+
+        if (base.mnc != change.mnc) {
+            delta.mnc = change.mnc;
+        }
+
+        base.fixUpLocaleList();
+        change.fixUpLocaleList();
+        if (!base.mLocaleList.equals(change.mLocaleList))  {
+            delta.mLocaleList = change.mLocaleList;
+            delta.locale = change.locale;
+        }
+
+        if (base.touchscreen != change.touchscreen) {
+            delta.touchscreen = change.touchscreen;
+        }
+
+        if (base.keyboard != change.keyboard) {
+            delta.keyboard = change.keyboard;
+        }
+
+        if (base.keyboardHidden != change.keyboardHidden) {
+            delta.keyboardHidden = change.keyboardHidden;
+        }
+
+        if (base.navigation != change.navigation) {
+            delta.navigation = change.navigation;
+        }
+
+        if (base.navigationHidden != change.navigationHidden) {
+            delta.navigationHidden = change.navigationHidden;
+        }
+
+        if (base.orientation != change.orientation) {
+            delta.orientation = change.orientation;
+        }
+
+        if ((base.screenLayout & SCREENLAYOUT_SIZE_MASK) !=
+                (change.screenLayout & SCREENLAYOUT_SIZE_MASK)) {
+            delta.screenLayout |= change.screenLayout & SCREENLAYOUT_SIZE_MASK;
+        }
+
+        if ((base.screenLayout & SCREENLAYOUT_LAYOUTDIR_MASK) !=
+                (change.screenLayout & SCREENLAYOUT_LAYOUTDIR_MASK)) {
+            delta.screenLayout |= change.screenLayout & SCREENLAYOUT_LAYOUTDIR_MASK;
+        }
+
+        if ((base.screenLayout & SCREENLAYOUT_LONG_MASK) !=
+                (change.screenLayout & SCREENLAYOUT_LONG_MASK)) {
+            delta.screenLayout |= change.screenLayout & SCREENLAYOUT_LONG_MASK;
+        }
+
+        if ((base.screenLayout & SCREENLAYOUT_ROUND_MASK) !=
+                (change.screenLayout & SCREENLAYOUT_ROUND_MASK)) {
+            delta.screenLayout |= change.screenLayout & SCREENLAYOUT_ROUND_MASK;
+        }
+
+        if ((base.colorMode & COLOR_MODE_WIDE_COLOR_GAMUT_MASK) !=
+                (change.colorMode & COLOR_MODE_WIDE_COLOR_GAMUT_MASK)) {
+            delta.colorMode |= change.colorMode & COLOR_MODE_WIDE_COLOR_GAMUT_MASK;
+        }
+
+        if ((base.colorMode & COLOR_MODE_HDR_MASK) !=
+                (change.colorMode & COLOR_MODE_HDR_MASK)) {
+            delta.colorMode |= change.colorMode & COLOR_MODE_HDR_MASK;
+        }
+
+        if ((base.uiMode & UI_MODE_TYPE_MASK) != (change.uiMode & UI_MODE_TYPE_MASK)) {
+            delta.uiMode |= change.uiMode & UI_MODE_TYPE_MASK;
+        }
+
+        if ((base.uiMode & UI_MODE_NIGHT_MASK) != (change.uiMode & UI_MODE_NIGHT_MASK)) {
+            delta.uiMode |= change.uiMode & UI_MODE_NIGHT_MASK;
+        }
+
+        if (base.screenWidthDp != change.screenWidthDp) {
+            delta.screenWidthDp = change.screenWidthDp;
+        }
+
+        if (base.screenHeightDp != change.screenHeightDp) {
+            delta.screenHeightDp = change.screenHeightDp;
+        }
+
+        if (base.smallestScreenWidthDp != change.smallestScreenWidthDp) {
+            delta.smallestScreenWidthDp = change.smallestScreenWidthDp;
+        }
+
+        if (base.densityDpi != change.densityDpi) {
+            delta.densityDpi = change.densityDpi;
+        }
+
+        if (base.assetsSeq != change.assetsSeq) {
+            delta.assetsSeq = change.assetsSeq;
+        }
+
+        if (!base.windowConfiguration.equals(change.windowConfiguration)) {
+            delta.windowConfiguration.setTo(change.windowConfiguration);
+        }
+        return delta;
+    }
+
+    private static final String XML_ATTR_FONT_SCALE = "fs";
+    private static final String XML_ATTR_MCC = "mcc";
+    private static final String XML_ATTR_MNC = "mnc";
+    private static final String XML_ATTR_LOCALES = "locales";
+    private static final String XML_ATTR_TOUCHSCREEN = "touch";
+    private static final String XML_ATTR_KEYBOARD = "key";
+    private static final String XML_ATTR_KEYBOARD_HIDDEN = "keyHid";
+    private static final String XML_ATTR_HARD_KEYBOARD_HIDDEN = "hardKeyHid";
+    private static final String XML_ATTR_NAVIGATION = "nav";
+    private static final String XML_ATTR_NAVIGATION_HIDDEN = "navHid";
+    private static final String XML_ATTR_ORIENTATION = "ori";
+    private static final String XML_ATTR_ROTATION = "rot";
+    private static final String XML_ATTR_SCREEN_LAYOUT = "scrLay";
+    private static final String XML_ATTR_COLOR_MODE = "clrMod";
+    private static final String XML_ATTR_UI_MODE = "ui";
+    private static final String XML_ATTR_SCREEN_WIDTH = "width";
+    private static final String XML_ATTR_SCREEN_HEIGHT = "height";
+    private static final String XML_ATTR_SMALLEST_WIDTH = "sw";
+    private static final String XML_ATTR_DENSITY = "density";
+    private static final String XML_ATTR_APP_BOUNDS = "app_bounds";
+
+    /**
+     * Reads the attributes corresponding to Configuration member fields from the Xml parser.
+     * The parser is expected to be on a tag which has Configuration attributes.
+     *
+     * @param parser The Xml parser from which to read attributes.
+     * @param configOut The Configuration to populate from the Xml attributes.
+     * {@hide}
+     */
+    public static void readXmlAttrs(XmlPullParser parser, Configuration configOut)
+            throws XmlPullParserException, IOException {
+        configOut.fontScale = Float.intBitsToFloat(
+                XmlUtils.readIntAttribute(parser, XML_ATTR_FONT_SCALE, 0));
+        configOut.mcc = XmlUtils.readIntAttribute(parser, XML_ATTR_MCC, 0);
+        configOut.mnc = XmlUtils.readIntAttribute(parser, XML_ATTR_MNC, 0);
+
+        final String localesStr = XmlUtils.readStringAttribute(parser, XML_ATTR_LOCALES);
+        configOut.mLocaleList = LocaleList.forLanguageTags(localesStr);
+        configOut.locale = configOut.mLocaleList.get(0);
+
+        configOut.touchscreen = XmlUtils.readIntAttribute(parser, XML_ATTR_TOUCHSCREEN,
+                TOUCHSCREEN_UNDEFINED);
+        configOut.keyboard = XmlUtils.readIntAttribute(parser, XML_ATTR_KEYBOARD,
+                KEYBOARD_UNDEFINED);
+        configOut.keyboardHidden = XmlUtils.readIntAttribute(parser, XML_ATTR_KEYBOARD_HIDDEN,
+                KEYBOARDHIDDEN_UNDEFINED);
+        configOut.hardKeyboardHidden =
+                XmlUtils.readIntAttribute(parser, XML_ATTR_HARD_KEYBOARD_HIDDEN,
+                        HARDKEYBOARDHIDDEN_UNDEFINED);
+        configOut.navigation = XmlUtils.readIntAttribute(parser, XML_ATTR_NAVIGATION,
+                NAVIGATION_UNDEFINED);
+        configOut.navigationHidden = XmlUtils.readIntAttribute(parser, XML_ATTR_NAVIGATION_HIDDEN,
+                NAVIGATIONHIDDEN_UNDEFINED);
+        configOut.orientation = XmlUtils.readIntAttribute(parser, XML_ATTR_ORIENTATION,
+                ORIENTATION_UNDEFINED);
+        configOut.screenLayout = XmlUtils.readIntAttribute(parser, XML_ATTR_SCREEN_LAYOUT,
+                SCREENLAYOUT_UNDEFINED);
+        configOut.colorMode = XmlUtils.readIntAttribute(parser, XML_ATTR_COLOR_MODE,
+                COLOR_MODE_UNDEFINED);
+        configOut.uiMode = XmlUtils.readIntAttribute(parser, XML_ATTR_UI_MODE, 0);
+        configOut.screenWidthDp = XmlUtils.readIntAttribute(parser, XML_ATTR_SCREEN_WIDTH,
+                SCREEN_WIDTH_DP_UNDEFINED);
+        configOut.screenHeightDp = XmlUtils.readIntAttribute(parser, XML_ATTR_SCREEN_HEIGHT,
+                SCREEN_HEIGHT_DP_UNDEFINED);
+        configOut.smallestScreenWidthDp =
+                XmlUtils.readIntAttribute(parser, XML_ATTR_SMALLEST_WIDTH,
+                        SMALLEST_SCREEN_WIDTH_DP_UNDEFINED);
+        configOut.densityDpi = XmlUtils.readIntAttribute(parser, XML_ATTR_DENSITY,
+                DENSITY_DPI_UNDEFINED);
+
+        // For persistence, we don't care about assetsSeq and WindowConfiguration, so do not read it
+        // out.
+    }
+}
diff --git a/android/content/res/ConfigurationBoundResourceCache.java b/android/content/res/ConfigurationBoundResourceCache.java
new file mode 100644
index 0000000..5e10a57
--- /dev/null
+++ b/android/content/res/ConfigurationBoundResourceCache.java
@@ -0,0 +1,56 @@
+/*
+ * 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.content.res;
+
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.pm.ActivityInfo.Config;
+
+/**
+ * A Cache class which can be used to cache resource objects that are easy to clone but more
+ * expensive to inflate.
+ *
+ * @hide For internal use only.
+ */
+public class ConfigurationBoundResourceCache<T> extends ThemedResourceCache<ConstantState<T>> {
+
+    @UnsupportedAppUsage
+    public ConfigurationBoundResourceCache() {
+    }
+
+    /**
+     * If the resource is cached, creates and returns a new instance of it.
+     *
+     * @param key a key that uniquely identifies the drawable resource
+     * @param resources a Resources object from which to create new instances.
+     * @param theme the theme where the resource will be used
+     * @return a new instance of the resource, or {@code null} if not in
+     *         the cache
+     */
+    public T getInstance(long key, Resources resources, Resources.Theme theme) {
+        final ConstantState<T> entry = get(key, theme);
+        if (entry != null) {
+            return entry.newInstance(resources, theme);
+        }
+
+        return null;
+    }
+
+    @Override
+    public boolean shouldInvalidateEntry(ConstantState<T> entry, @Config int configChanges) {
+        return Configuration.needNewResources(configChanges, entry.getChangingConfigurations());
+    }
+}
diff --git a/android/content/res/ConstantState.java b/android/content/res/ConstantState.java
new file mode 100644
index 0000000..09d4a59
--- /dev/null
+++ b/android/content/res/ConstantState.java
@@ -0,0 +1,63 @@
+/*
+* 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.content.res;
+
+import android.content.pm.ActivityInfo.Config;
+
+/**
+ * A cache class that can provide new instances of a particular resource which may change
+ * depending on the current {@link Resources.Theme} or {@link Configuration}.
+ * <p>
+ * A constant state should be able to return a bitmask of changing configurations, which
+ * identifies the type of configuration changes that may invalidate this resource. These
+ * configuration changes can be obtained from {@link android.util.TypedValue}. Entities such as
+ * {@link android.animation.Animator} also provide a changing configuration method to include
+ * their dependencies (e.g. An AnimatorSet's changing configuration is the union of the
+ * changing configurations of each Animator in the set)
+ * @hide
+ */
+abstract public class ConstantState<T> {
+
+    /**
+     * Return a bit mask of configuration changes that will impact
+     * this resource (and thus require completely reloading it).
+     */
+    abstract public @Config int getChangingConfigurations();
+
+    /**
+     * Create a new instance without supplying resources the caller
+     * is running in.
+     */
+    public abstract T newInstance();
+
+    /**
+     * Create a new instance from its constant state.  This
+     * must be implemented for resources that change based on the target
+     * density of their caller (that is depending on whether it is
+     * in compatibility mode).
+     */
+    public T newInstance(Resources res) {
+        return newInstance();
+    }
+
+    /**
+     * Create a new instance from its constant state.  This must be
+     * implemented for resources that can have a theme applied.
+     */
+    public T newInstance(Resources res, Resources.Theme theme) {
+        return newInstance(res);
+    }
+}
diff --git a/android/content/res/DrawableCache.java b/android/content/res/DrawableCache.java
new file mode 100644
index 0000000..5497e61
--- /dev/null
+++ b/android/content/res/DrawableCache.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.content.res;
+
+import android.compat.annotation.UnsupportedAppUsage;
+import android.graphics.drawable.Drawable;
+
+/**
+ * Class which can be used to cache Drawable resources against a theme.
+ */
+class DrawableCache extends ThemedResourceCache<Drawable.ConstantState> {
+
+    @UnsupportedAppUsage
+    DrawableCache() {
+    }
+
+    /**
+     * If the resource is cached, creates and returns a new instance of it.
+     *
+     * @param key a key that uniquely identifies the drawable resource
+     * @param resources a Resources object from which to create new instances.
+     * @param theme the theme where the resource will be used
+     * @return a new instance of the resource, or {@code null} if not in
+     *         the cache
+     */
+    @UnsupportedAppUsage
+    public Drawable getInstance(long key, Resources resources, Resources.Theme theme) {
+        final Drawable.ConstantState entry = get(key, theme);
+        if (entry != null) {
+            return entry.newDrawable(resources, theme);
+        }
+
+        return null;
+    }
+
+    @Override
+    public boolean shouldInvalidateEntry(Drawable.ConstantState entry, int configChanges) {
+        return Configuration.needNewResources(configChanges, entry.getChangingConfigurations());
+    }
+}
diff --git a/android/content/res/FontResourcesParser.java b/android/content/res/FontResourcesParser.java
new file mode 100644
index 0000000..14eb11a
--- /dev/null
+++ b/android/content/res/FontResourcesParser.java
@@ -0,0 +1,251 @@
+/*
+ * 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.content.res;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.Typeface;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.Xml;
+
+import com.android.internal.R;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Parser for xml type font resources.
+ * @hide
+ */
+public class FontResourcesParser {
+    private static final String TAG = "FontResourcesParser";
+
+    // A class represents single entry of font-family in xml file.
+    public interface FamilyResourceEntry {}
+
+    // A class represents font provider based font-family element in xml file.
+    public static final class ProviderResourceEntry implements FamilyResourceEntry {
+        private final @NonNull String mProviderAuthority;
+        private final @NonNull String mProviderPackage;
+        private final @NonNull String mQuery;
+        private final @Nullable List<List<String>> mCerts;
+
+        public ProviderResourceEntry(@NonNull String authority, @NonNull String pkg,
+                @NonNull String query, @Nullable List<List<String>> certs) {
+            mProviderAuthority = authority;
+            mProviderPackage = pkg;
+            mQuery = query;
+            mCerts = certs;
+        }
+
+        public @NonNull String getAuthority() {
+            return mProviderAuthority;
+        }
+
+        public @NonNull String getPackage() {
+            return mProviderPackage;
+        }
+
+        public @NonNull String getQuery() {
+            return mQuery;
+        }
+
+        public @Nullable List<List<String>> getCerts() {
+            return mCerts;
+        }
+    }
+
+    // A class represents font element in xml file which points a file in resource.
+    public static final class FontFileResourceEntry {
+        public static final int RESOLVE_BY_FONT_TABLE = Typeface.RESOLVE_BY_FONT_TABLE;
+        public static final int UPRIGHT = 0;
+        public static final int ITALIC = 1;
+
+        private final @NonNull String mFileName;
+        private int mWeight;
+        private int mItalic;
+        private int mTtcIndex;
+        private String mVariationSettings;
+        private int mResourceId;
+
+        public FontFileResourceEntry(@NonNull String fileName, int weight, int italic,
+                @Nullable String variationSettings, int ttcIndex) {
+            mFileName = fileName;
+            mWeight = weight;
+            mItalic = italic;
+            mVariationSettings = variationSettings;
+            mTtcIndex = ttcIndex;
+        }
+
+        public @NonNull String getFileName() {
+            return mFileName;
+        }
+
+        public int getWeight() {
+            return mWeight;
+        }
+
+        public int getItalic() {
+            return mItalic;
+        }
+
+        public @Nullable String getVariationSettings() {
+            return mVariationSettings;
+        }
+
+        public int getTtcIndex() {
+            return mTtcIndex;
+        }
+    }
+
+    // A class represents file based font-family element in xml file.
+    public static final class FontFamilyFilesResourceEntry implements FamilyResourceEntry {
+        private final @NonNull FontFileResourceEntry[] mEntries;
+
+        public FontFamilyFilesResourceEntry(@NonNull FontFileResourceEntry[] entries) {
+            mEntries = entries;
+        }
+
+        public @NonNull FontFileResourceEntry[] getEntries() {
+            return mEntries;
+        }
+    }
+
+    public static @Nullable FamilyResourceEntry parse(XmlPullParser parser, Resources resources)
+            throws XmlPullParserException, IOException {
+        int type;
+        while ((type=parser.next()) != XmlPullParser.START_TAG
+                && type != XmlPullParser.END_DOCUMENT) {
+            // Empty loop.
+        }
+
+        if (type != XmlPullParser.START_TAG) {
+            throw new XmlPullParserException("No start tag found");
+        }
+        return readFamilies(parser, resources);
+    }
+
+    private static @Nullable FamilyResourceEntry readFamilies(XmlPullParser parser,
+            Resources resources) throws XmlPullParserException, IOException {
+        parser.require(XmlPullParser.START_TAG, null, "font-family");
+        String tag = parser.getName();
+        FamilyResourceEntry result = null;
+        if (tag.equals("font-family")) {
+            return readFamily(parser, resources);
+        } else {
+            skip(parser);
+            Log.e(TAG, "Failed to find font-family tag");
+            return null;
+        }
+    }
+
+    private static @Nullable FamilyResourceEntry readFamily(XmlPullParser parser,
+            Resources resources) throws XmlPullParserException, IOException {
+        AttributeSet attrs = Xml.asAttributeSet(parser);
+        TypedArray array = resources.obtainAttributes(attrs, R.styleable.FontFamily);
+        String authority = array.getString(R.styleable.FontFamily_fontProviderAuthority);
+        String providerPackage = array.getString(R.styleable.FontFamily_fontProviderPackage);
+        String query = array.getString(R.styleable.FontFamily_fontProviderQuery);
+        int certsId = array.getResourceId(R.styleable.FontFamily_fontProviderCerts, 0);
+        array.recycle();
+        if (authority != null && providerPackage != null && query != null) {
+            while (parser.next() != XmlPullParser.END_TAG) {
+                skip(parser);
+            }
+            List<List<String>> certs = null;
+            if (certsId != 0) {
+                TypedArray typedArray = resources.obtainTypedArray(certsId);
+                if (typedArray.length() > 0) {
+                    certs = new ArrayList<>();
+                    boolean isArrayOfArrays = typedArray.getResourceId(0, 0) != 0;
+                    if (isArrayOfArrays) {
+                        for (int i = 0; i < typedArray.length(); i++) {
+                            int certId = typedArray.getResourceId(i, 0);
+                            String[] certsArray = resources.getStringArray(certId);
+                            List<String> certsList = Arrays.asList(certsArray);
+                            certs.add(certsList);
+                        }
+                    } else {
+                        String[] certsArray = resources.getStringArray(certsId);
+                        List<String> certsList = Arrays.asList(certsArray);
+                        certs.add(certsList);
+                    }
+                }
+            }
+            return new ProviderResourceEntry(authority, providerPackage, query, certs);
+        }
+        List<FontFileResourceEntry> fonts = new ArrayList<>();
+        while (parser.next() != XmlPullParser.END_TAG) {
+            if (parser.getEventType() != XmlPullParser.START_TAG) continue;
+            String tag = parser.getName();
+            if (tag.equals("font")) {
+                final FontFileResourceEntry entry = readFont(parser, resources);
+                if (entry != null) {
+                    fonts.add(entry);
+                }
+            } else {
+                skip(parser);
+            }
+        }
+        if (fonts.isEmpty()) {
+            return null;
+        }
+        return new FontFamilyFilesResourceEntry(fonts.toArray(
+                new FontFileResourceEntry[fonts.size()]));
+    }
+
+    private static FontFileResourceEntry readFont(XmlPullParser parser, Resources resources)
+            throws XmlPullParserException, IOException {
+        AttributeSet attrs = Xml.asAttributeSet(parser);
+        TypedArray array = resources.obtainAttributes(attrs, R.styleable.FontFamilyFont);
+        int weight = array.getInt(R.styleable.FontFamilyFont_fontWeight,
+                Typeface.RESOLVE_BY_FONT_TABLE);
+        int italic = array.getInt(R.styleable.FontFamilyFont_fontStyle,
+                FontFileResourceEntry.RESOLVE_BY_FONT_TABLE);
+        String variationSettings = array.getString(
+                R.styleable.FontFamilyFont_fontVariationSettings);
+        int ttcIndex = array.getInt(R.styleable.FontFamilyFont_ttcIndex, 0);
+        String filename = array.getString(R.styleable.FontFamilyFont_font);
+        array.recycle();
+        while (parser.next() != XmlPullParser.END_TAG) {
+            skip(parser);
+        }
+        if (filename == null) {
+            return null;
+        }
+        return new FontFileResourceEntry(filename, weight, italic, variationSettings, ttcIndex);
+    }
+
+    private static void skip(XmlPullParser parser) throws XmlPullParserException, IOException {
+        int depth = 1;
+        while (depth > 0) {
+            switch (parser.next()) {
+                case XmlPullParser.START_TAG:
+                    depth++;
+                    break;
+                case XmlPullParser.END_TAG:
+                    depth--;
+                    break;
+            }
+        }
+    }
+}
diff --git a/android/content/res/GradientColor.java b/android/content/res/GradientColor.java
new file mode 100644
index 0000000..35ad503
--- /dev/null
+++ b/android/content/res/GradientColor.java
@@ -0,0 +1,601 @@
+/*
+ * 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.content.res;
+
+import android.annotation.ColorInt;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.ActivityInfo.Config;
+import android.content.res.Resources.Theme;
+
+import com.android.internal.R;
+import com.android.internal.util.GrowingArrayUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.graphics.LinearGradient;
+import android.graphics.RadialGradient;
+import android.graphics.Shader;
+import android.graphics.SweepGradient;
+import android.graphics.drawable.GradientDrawable;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.Xml;
+
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Lets you define a gradient color, which is used inside
+ * {@link android.graphics.drawable.VectorDrawable}.
+ *
+ * {@link android.content.res.GradientColor}s are created from XML resource files defined in the
+ * "color" subdirectory directory of an application's resource directory.  The XML file contains
+ * a single "gradient" element with a number of attributes and elements inside.  For example:
+ * <pre>
+ * &lt;gradient xmlns:android="http://schemas.android.com/apk/res/android"&gt;
+ *   &lt;android:startColor="?android:attr/colorPrimary"/&gt;
+ *   &lt;android:endColor="?android:attr/colorControlActivated"/&gt;
+ *   &lt;.../&gt;
+ *   &lt;android:type="linear"/&gt;
+ * &lt;/gradient&gt;
+ * </pre>
+ *
+ * This can describe either a {@link android.graphics.LinearGradient},
+ * {@link android.graphics.RadialGradient}, or {@link android.graphics.SweepGradient}.
+ *
+ * Note that different attributes are relevant for different types of gradient.
+ * For example, android:gradientRadius is only applied to RadialGradient.
+ * android:centerX and android:centerY are only applied to SweepGradient or RadialGradient.
+ * android:startX, android:startY, android:endX and android:endY are only applied to LinearGradient.
+ *
+ * Also note if any color "item" element is defined, then startColor, centerColor and endColor will
+ * be ignored.
+ * @hide
+ */
+public class GradientColor extends ComplexColor {
+    private static final String TAG = "GradientColor";
+
+    private static final boolean DBG_GRADIENT = false;
+
+    @IntDef(prefix = { "TILE_MODE_" }, value = {
+            TILE_MODE_CLAMP,
+            TILE_MODE_REPEAT,
+            TILE_MODE_MIRROR
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    private @interface GradientTileMode {}
+
+    private static final int TILE_MODE_CLAMP = 0;
+    private static final int TILE_MODE_REPEAT = 1;
+    private static final int TILE_MODE_MIRROR = 2;
+
+    /** Lazily-created factory for this GradientColor. */
+    private GradientColorFactory mFactory;
+
+    private @Config int mChangingConfigurations;
+    private int mDefaultColor;
+
+    // After parsing all the attributes from XML, this shader is the ultimate result containing
+    // all the XML information.
+    private Shader mShader = null;
+
+    // Below are the attributes at the root element <gradient>.
+    // NOTE: they need to be copied in the copy constructor!
+    private int mGradientType = GradientDrawable.LINEAR_GRADIENT;
+
+    private float mCenterX = 0f;
+    private float mCenterY = 0f;
+
+    private float mStartX = 0f;
+    private float mStartY = 0f;
+    private float mEndX = 0f;
+    private float mEndY = 0f;
+
+    private int mStartColor = 0;
+    private int mCenterColor = 0;
+    private int mEndColor = 0;
+    private boolean mHasCenterColor = false;
+
+    private int mTileMode = 0; // Clamp mode.
+
+    private float mGradientRadius = 0f;
+
+    // Below are the attributes for the <item> element.
+    private int[] mItemColors;
+    private float[] mItemOffsets;
+
+    // Theme attributes for the root and item elements.
+    private int[] mThemeAttrs;
+    private int[][] mItemsThemeAttrs;
+
+    private GradientColor() {
+    }
+
+    private GradientColor(GradientColor copy) {
+        if (copy != null) {
+            mChangingConfigurations = copy.mChangingConfigurations;
+            mDefaultColor = copy.mDefaultColor;
+            mShader = copy.mShader;
+            mGradientType = copy.mGradientType;
+            mCenterX = copy.mCenterX;
+            mCenterY = copy.mCenterY;
+            mStartX = copy.mStartX;
+            mStartY = copy.mStartY;
+            mEndX = copy.mEndX;
+            mEndY = copy.mEndY;
+            mStartColor = copy.mStartColor;
+            mCenterColor = copy.mCenterColor;
+            mEndColor = copy.mEndColor;
+            mHasCenterColor = copy.mHasCenterColor;
+            mGradientRadius = copy.mGradientRadius;
+            mTileMode = copy.mTileMode;
+
+            if (copy.mItemColors != null) {
+                mItemColors = copy.mItemColors.clone();
+            }
+            if (copy.mItemOffsets != null) {
+                mItemOffsets = copy.mItemOffsets.clone();
+            }
+
+            if (copy.mThemeAttrs != null) {
+                mThemeAttrs = copy.mThemeAttrs.clone();
+            }
+            if (copy.mItemsThemeAttrs != null) {
+                mItemsThemeAttrs = copy.mItemsThemeAttrs.clone();
+            }
+        }
+    }
+
+    // Set the default to clamp mode.
+    private static Shader.TileMode parseTileMode(@GradientTileMode int tileMode) {
+        switch (tileMode) {
+            case TILE_MODE_CLAMP:
+                return Shader.TileMode.CLAMP;
+            case TILE_MODE_REPEAT:
+                return Shader.TileMode.REPEAT;
+            case TILE_MODE_MIRROR:
+                return Shader.TileMode.MIRROR;
+            default:
+                return Shader.TileMode.CLAMP;
+        }
+    }
+
+    /**
+     * Update the root level's attributes, either for inflate or applyTheme.
+     */
+    private void updateRootElementState(TypedArray a) {
+        // Extract the theme attributes, if any.
+        mThemeAttrs = a.extractThemeAttrs();
+
+        mStartX = a.getFloat(
+                R.styleable.GradientColor_startX, mStartX);
+        mStartY = a.getFloat(
+                R.styleable.GradientColor_startY, mStartY);
+        mEndX = a.getFloat(
+                R.styleable.GradientColor_endX, mEndX);
+        mEndY = a.getFloat(
+                R.styleable.GradientColor_endY, mEndY);
+
+        mCenterX = a.getFloat(
+                R.styleable.GradientColor_centerX, mCenterX);
+        mCenterY = a.getFloat(
+                R.styleable.GradientColor_centerY, mCenterY);
+
+        mGradientType = a.getInt(
+                R.styleable.GradientColor_type, mGradientType);
+
+        mStartColor = a.getColor(
+                R.styleable.GradientColor_startColor, mStartColor);
+        mHasCenterColor |= a.hasValue(
+                R.styleable.GradientColor_centerColor);
+        mCenterColor = a.getColor(
+                R.styleable.GradientColor_centerColor, mCenterColor);
+        mEndColor = a.getColor(
+                R.styleable.GradientColor_endColor, mEndColor);
+
+        mTileMode = a.getInt(
+                R.styleable.GradientColor_tileMode, mTileMode);
+
+        if (DBG_GRADIENT) {
+            Log.v(TAG, "hasCenterColor is " + mHasCenterColor);
+            if (mHasCenterColor) {
+                Log.v(TAG, "centerColor:" + mCenterColor);
+            }
+            Log.v(TAG, "startColor: " + mStartColor);
+            Log.v(TAG, "endColor: " + mEndColor);
+            Log.v(TAG, "tileMode: " + mTileMode);
+        }
+
+        mGradientRadius = a.getFloat(R.styleable.GradientColor_gradientRadius,
+                mGradientRadius);
+    }
+
+    /**
+     * Check if the XML content is valid.
+     *
+     * @throws XmlPullParserException if errors were found.
+     */
+    private void validateXmlContent() throws XmlPullParserException {
+        if (mGradientRadius <= 0
+                && mGradientType == GradientDrawable.RADIAL_GRADIENT) {
+            throw new XmlPullParserException(
+                    "<gradient> tag requires 'gradientRadius' "
+                            + "attribute with radial type");
+        }
+    }
+
+    /**
+     * The shader information will be applied to the native VectorDrawable's path.
+     * @hide
+     */
+    public Shader getShader() {
+        return mShader;
+    }
+
+    /**
+     * A public method to create GradientColor from a XML resource.
+     */
+    public static GradientColor createFromXml(Resources r, XmlResourceParser parser, Theme theme)
+            throws XmlPullParserException, IOException {
+        final AttributeSet attrs = Xml.asAttributeSet(parser);
+
+        int type;
+        while ((type = parser.next()) != XmlPullParser.START_TAG
+                && type != XmlPullParser.END_DOCUMENT) {
+            // Seek parser to start tag.
+        }
+
+        if (type != XmlPullParser.START_TAG) {
+            throw new XmlPullParserException("No start tag found");
+        }
+
+        return createFromXmlInner(r, parser, attrs, theme);
+    }
+
+    /**
+     * Create from inside an XML document. Called on a parser positioned at a
+     * tag in an XML document, tries to create a GradientColor from that tag.
+     *
+     * @return A new GradientColor for the current tag.
+     * @throws XmlPullParserException if the current tag is not &lt;gradient>
+     */
+    @NonNull
+    static GradientColor createFromXmlInner(@NonNull Resources r,
+            @NonNull XmlPullParser parser, @NonNull AttributeSet attrs, @Nullable Theme theme)
+            throws XmlPullParserException, IOException {
+        final String name = parser.getName();
+        if (!name.equals("gradient")) {
+            throw new XmlPullParserException(
+                    parser.getPositionDescription() + ": invalid gradient color tag " + name);
+        }
+
+        final GradientColor gradientColor = new GradientColor();
+        gradientColor.inflate(r, parser, attrs, theme);
+        return gradientColor;
+    }
+
+    /**
+     * Fill in this object based on the contents of an XML "gradient" element.
+     */
+    private void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
+            @NonNull AttributeSet attrs, @Nullable Theme theme)
+            throws XmlPullParserException, IOException {
+        final TypedArray a = Resources.obtainAttributes(r, theme, attrs, R.styleable.GradientColor);
+        updateRootElementState(a);
+        mChangingConfigurations |= a.getChangingConfigurations();
+        a.recycle();
+
+        // Check correctness and throw exception if errors found.
+        validateXmlContent();
+
+        inflateChildElements(r, parser, attrs, theme);
+
+        onColorsChange();
+    }
+
+    /**
+     * Inflates child elements "item"s for each color stop.
+     *
+     * Note that at root level, we need to save ThemeAttrs for theme applied later.
+     * Here similarly, at each child item, we need to save the theme's attributes, and apply theme
+     * later as applyItemsAttrsTheme().
+     */
+    private void inflateChildElements(@NonNull Resources r, @NonNull XmlPullParser parser,
+            @NonNull AttributeSet attrs, @NonNull Theme theme)
+            throws XmlPullParserException, IOException {
+        final int innerDepth = parser.getDepth() + 1;
+        int type;
+        int depth;
+
+        // Pre-allocate the array with some size, for better performance.
+        float[] offsetList = new float[20];
+        int[] colorList = new int[offsetList.length];
+        int[][] themeAttrsList = new int[offsetList.length][];
+
+        int listSize = 0;
+        boolean hasUnresolvedAttrs = false;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && ((depth = parser.getDepth()) >= innerDepth
+                || type != XmlPullParser.END_TAG)) {
+            if (type != XmlPullParser.START_TAG) {
+                continue;
+            }
+            if (depth > innerDepth || !parser.getName().equals("item")) {
+                continue;
+            }
+
+            final TypedArray a = Resources.obtainAttributes(r, theme, attrs,
+                    R.styleable.GradientColorItem);
+            boolean hasColor = a.hasValue(R.styleable.GradientColorItem_color);
+            boolean hasOffset = a.hasValue(R.styleable.GradientColorItem_offset);
+            if (!hasColor || !hasOffset) {
+                throw new XmlPullParserException(
+                        parser.getPositionDescription()
+                                + ": <item> tag requires a 'color' attribute and a 'offset' "
+                                + "attribute!");
+            }
+
+            final int[] themeAttrs = a.extractThemeAttrs();
+            int color = a.getColor(R.styleable.GradientColorItem_color, 0);
+            float offset = a.getFloat(R.styleable.GradientColorItem_offset, 0);
+
+            if (DBG_GRADIENT) {
+                Log.v(TAG, "new item color " + color + " " + Integer.toHexString(color));
+                Log.v(TAG, "offset" + offset);
+            }
+            mChangingConfigurations |= a.getChangingConfigurations();
+            a.recycle();
+
+            if (themeAttrs != null) {
+                hasUnresolvedAttrs = true;
+            }
+
+            colorList = GrowingArrayUtils.append(colorList, listSize, color);
+            offsetList = GrowingArrayUtils.append(offsetList, listSize, offset);
+            themeAttrsList = GrowingArrayUtils.append(themeAttrsList, listSize, themeAttrs);
+            listSize++;
+        }
+        if (listSize > 0) {
+            if (hasUnresolvedAttrs) {
+                mItemsThemeAttrs = new int[listSize][];
+                System.arraycopy(themeAttrsList, 0, mItemsThemeAttrs, 0, listSize);
+            } else {
+                mItemsThemeAttrs = null;
+            }
+
+            mItemColors = new int[listSize];
+            mItemOffsets = new float[listSize];
+            System.arraycopy(colorList, 0, mItemColors, 0, listSize);
+            System.arraycopy(offsetList, 0, mItemOffsets, 0, listSize);
+        }
+    }
+
+    /**
+     * Apply theme to all the items.
+     */
+    private void applyItemsAttrsTheme(Theme t) {
+        if (mItemsThemeAttrs == null) {
+            return;
+        }
+
+        boolean hasUnresolvedAttrs = false;
+
+        final int[][] themeAttrsList = mItemsThemeAttrs;
+        final int N = themeAttrsList.length;
+        for (int i = 0; i < N; i++) {
+            if (themeAttrsList[i] != null) {
+                final TypedArray a = t.resolveAttributes(themeAttrsList[i],
+                        R.styleable.GradientColorItem);
+
+                // Extract the theme attributes, if any, before attempting to
+                // read from the typed array. This prevents a crash if we have
+                // unresolved attrs.
+                themeAttrsList[i] = a.extractThemeAttrs(themeAttrsList[i]);
+                if (themeAttrsList[i] != null) {
+                    hasUnresolvedAttrs = true;
+                }
+
+                mItemColors[i] = a.getColor(R.styleable.GradientColorItem_color, mItemColors[i]);
+                mItemOffsets[i] = a.getFloat(R.styleable.GradientColorItem_offset, mItemOffsets[i]);
+                if (DBG_GRADIENT) {
+                    Log.v(TAG, "applyItemsAttrsTheme Colors[i] " + i + " " +
+                            Integer.toHexString(mItemColors[i]));
+                    Log.v(TAG, "Offsets[i] " + i + " " + mItemOffsets[i]);
+                }
+
+                // Account for any configuration changes.
+                mChangingConfigurations |= a.getChangingConfigurations();
+
+                a.recycle();
+            }
+        }
+
+        if (!hasUnresolvedAttrs) {
+            mItemsThemeAttrs = null;
+        }
+    }
+
+    private void onColorsChange() {
+        int[] tempColors = null;
+        float[] tempOffsets = null;
+
+        if (mItemColors != null) {
+            int length = mItemColors.length;
+            tempColors = new int[length];
+            tempOffsets = new float[length];
+
+            for (int i = 0; i < length; i++) {
+                tempColors[i] = mItemColors[i];
+                tempOffsets[i] = mItemOffsets[i];
+            }
+        } else {
+            if (mHasCenterColor) {
+                tempColors = new int[3];
+                tempColors[0] = mStartColor;
+                tempColors[1] = mCenterColor;
+                tempColors[2] = mEndColor;
+
+                tempOffsets = new float[3];
+                tempOffsets[0] = 0.0f;
+                // Since 0.5f is default value, try to take the one that isn't 0.5f
+                tempOffsets[1] = 0.5f;
+                tempOffsets[2] = 1f;
+            } else {
+                tempColors = new int[2];
+                tempColors[0] = mStartColor;
+                tempColors[1] = mEndColor;
+            }
+        }
+        if (tempColors.length < 2) {
+            Log.w(TAG, "<gradient> tag requires 2 color values specified!" + tempColors.length
+                    + " " + tempColors);
+        }
+
+        if (mGradientType == GradientDrawable.LINEAR_GRADIENT) {
+            mShader = new LinearGradient(mStartX, mStartY, mEndX, mEndY, tempColors, tempOffsets,
+                    parseTileMode(mTileMode));
+        } else {
+            if (mGradientType == GradientDrawable.RADIAL_GRADIENT) {
+                mShader = new RadialGradient(mCenterX, mCenterY, mGradientRadius, tempColors,
+                        tempOffsets, parseTileMode(mTileMode));
+            } else {
+                mShader = new SweepGradient(mCenterX, mCenterY, tempColors, tempOffsets);
+            }
+        }
+        mDefaultColor = tempColors[0];
+    }
+
+    /**
+     * For Gradient color, the default color is not very useful, since the gradient will override
+     * the color information anyway.
+     */
+    @Override
+    @ColorInt
+    public int getDefaultColor() {
+        return mDefaultColor;
+    }
+
+    /**
+     * Similar to ColorStateList, setup constant state and its factory.
+     * @hide only for resource preloading
+     */
+    @Override
+    public ConstantState<ComplexColor> getConstantState() {
+        if (mFactory == null) {
+            mFactory = new GradientColorFactory(this);
+        }
+        return mFactory;
+    }
+
+    private static class GradientColorFactory extends ConstantState<ComplexColor> {
+        private final GradientColor mSrc;
+
+        public GradientColorFactory(GradientColor src) {
+            mSrc = src;
+        }
+
+        @Override
+        public @Config int getChangingConfigurations() {
+            return mSrc.mChangingConfigurations;
+        }
+
+        @Override
+        public GradientColor newInstance() {
+            return mSrc;
+        }
+
+        @Override
+        public GradientColor newInstance(Resources res, Theme theme) {
+            return mSrc.obtainForTheme(theme);
+        }
+    }
+
+    /**
+     * Returns an appropriately themed gradient color.
+     *
+     * @param t the theme to apply
+     * @return a copy of the gradient color the theme applied, or the
+     * gradient itself if there were no unresolved theme
+     * attributes
+     * @hide only for resource preloading
+     */
+    @Override
+    public GradientColor obtainForTheme(Theme t) {
+        if (t == null || !canApplyTheme()) {
+            return this;
+        }
+
+        final GradientColor clone = new GradientColor(this);
+        clone.applyTheme(t);
+        return clone;
+    }
+
+    /**
+     * Returns a mask of the configuration parameters for which this gradient
+     * may change, requiring that it be re-created.
+     *
+     * @return a mask of the changing configuration parameters, as defined by
+     *         {@link android.content.pm.ActivityInfo}
+     *
+     * @see android.content.pm.ActivityInfo
+     */
+    public int getChangingConfigurations() {
+        return super.getChangingConfigurations() | mChangingConfigurations;
+    }
+
+    private void applyTheme(Theme t) {
+        if (mThemeAttrs != null) {
+            applyRootAttrsTheme(t);
+        }
+        if (mItemsThemeAttrs != null) {
+            applyItemsAttrsTheme(t);
+        }
+        onColorsChange();
+    }
+
+    private void applyRootAttrsTheme(Theme t) {
+        final TypedArray a = t.resolveAttributes(mThemeAttrs, R.styleable.GradientColor);
+        // mThemeAttrs will be set to null if if there are no theme attributes in the
+        // typed array.
+        mThemeAttrs = a.extractThemeAttrs(mThemeAttrs);
+        // merging the attributes update inside the updateRootElementState().
+        updateRootElementState(a);
+
+        // Account for any configuration changes.
+        mChangingConfigurations |= a.getChangingConfigurations();
+        a.recycle();
+    }
+
+
+    /**
+     * Returns whether a theme can be applied to this gradient color, which
+     * usually indicates that the gradient color has unresolved theme
+     * attributes.
+     *
+     * @return whether a theme can be applied to this gradient color.
+     * @hide only for resource preloading
+     */
+    @Override
+    public boolean canApplyTheme() {
+        return mThemeAttrs != null || mItemsThemeAttrs != null;
+    }
+
+}
diff --git a/android/content/res/ObbInfo.java b/android/content/res/ObbInfo.java
new file mode 100644
index 0000000..c477abc
--- /dev/null
+++ b/android/content/res/ObbInfo.java
@@ -0,0 +1,109 @@
+/*
+ * 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.content.res;
+
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Basic information about a Opaque Binary Blob (OBB) that reflects the info
+ * from the footer on the OBB file. This information may be manipulated by a
+ * developer with the <code>obbtool</code> program in the Android SDK.
+ */
+public class ObbInfo implements Parcelable {
+    /** Flag noting that this OBB is an overlay patch for a base OBB. */
+    public static final int OBB_OVERLAY = 1 << 0;
+
+    /**
+     * The canonical filename of the OBB.
+     */
+    public String filename;
+
+    /**
+     * The name of the package to which the OBB file belongs.
+     */
+    public String packageName;
+
+    /**
+     * The version of the package to which the OBB file belongs.
+     */
+    public int version;
+
+    /**
+     * The flags relating to the OBB.
+     */
+    public int flags;
+
+    /**
+     * The salt for the encryption algorithm.
+     * 
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public byte[] salt;
+
+    // Only allow things in this package to instantiate.
+    /* package */ ObbInfo() {
+    }
+
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("ObbInfo{");
+        sb.append(Integer.toHexString(System.identityHashCode(this)));
+        sb.append(" packageName=");
+        sb.append(packageName);
+        sb.append(",version=");
+        sb.append(version);
+        sb.append(",flags=");
+        sb.append(flags);
+        sb.append('}');
+        return sb.toString();
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel dest, int parcelableFlags) {
+        // Keep this in sync with writeToParcel() in ObbInfo.cpp
+        dest.writeString(filename);
+        dest.writeString(packageName);
+        dest.writeInt(version);
+        dest.writeInt(flags);
+        dest.writeByteArray(salt);
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<ObbInfo> CREATOR
+            = new Parcelable.Creator<ObbInfo>() {
+        public ObbInfo createFromParcel(Parcel source) {
+            return new ObbInfo(source);
+        }
+
+        public ObbInfo[] newArray(int size) {
+            return new ObbInfo[size];
+        }
+    };
+
+    private ObbInfo(Parcel source) {
+        filename = source.readString();
+        packageName = source.readString();
+        version = source.readInt();
+        flags = source.readInt();
+        salt = source.createByteArray();
+    }
+}
diff --git a/android/content/res/ObbScanner.java b/android/content/res/ObbScanner.java
new file mode 100644
index 0000000..1b38eea
--- /dev/null
+++ b/android/content/res/ObbScanner.java
@@ -0,0 +1,63 @@
+/*
+ * 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.content.res;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * Class to scan Opaque Binary Blob (OBB) files. Use this to get information
+ * about an OBB file for use in a program via {@link ObbInfo}.
+ */
+public class ObbScanner {
+    // Don't allow others to instantiate this class
+    private ObbScanner() {}
+
+    /**
+     * Scan a file for OBB information.
+     * 
+     * @param filePath path to the OBB file to be scanned.
+     * @return ObbInfo object information corresponding to the file path
+     * @throws IllegalArgumentException if the OBB file couldn't be found
+     * @throws IOException if the OBB file couldn't be read
+     */
+    public static ObbInfo getObbInfo(String filePath) throws IOException {
+        if (filePath == null) {
+            throw new IllegalArgumentException("file path cannot be null");
+        }
+
+        final File obbFile = new File(filePath);
+        if (!obbFile.exists()) {
+            throw new IllegalArgumentException("OBB file does not exist: " + filePath);
+        }
+
+        /*
+         * XXX This will fail to find the real canonical path if bind mounts are
+         * used, but we don't use any bind mounts right now.
+         */
+        final String canonicalFilePath = obbFile.getCanonicalPath();
+
+        ObbInfo obbInfo = new ObbInfo();
+        obbInfo.filename = canonicalFilePath;
+        getObbInfo_native(canonicalFilePath, obbInfo);
+
+        return obbInfo;
+    }
+
+    private native static void getObbInfo_native(String filePath, ObbInfo obbInfo)
+            throws IOException;
+}
diff --git a/android/content/res/ResourceId.java b/android/content/res/ResourceId.java
new file mode 100644
index 0000000..3c7b5fc
--- /dev/null
+++ b/android/content/res/ResourceId.java
@@ -0,0 +1,38 @@
+/*
+ * 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.content.res;
+
+import android.annotation.AnyRes;
+
+/**
+ * Provides a set of utility methods for dealing with Resource IDs.
+ * @hide
+ */
+public final class ResourceId {
+    /**
+     * Checks whether the integer {@code id} is a valid resource ID, as generated by AAPT.
+     * <p>Note that a negative integer is not necessarily an invalid resource ID, and custom
+     * validations that compare the {@code id} against {@code 0} are incorrect.</p>
+     * @param id The integer to validate.
+     * @return {@code true} if the integer is a valid resource ID.
+     */
+    public static boolean isValid(@AnyRes int id) {
+        // With the introduction of packages with IDs > 0x7f, resource IDs can be negative when
+        // represented as a signed Java int. Some legacy code assumes -1 is an invalid resource ID,
+        // despite the existing documentation.
+        return id != -1 && (id & 0xff000000) != 0 && (id & 0x00ff0000) != 0;
+    }
+}
diff --git a/android/content/res/Resources.java b/android/content/res/Resources.java
new file mode 100644
index 0000000..c399bc7
--- /dev/null
+++ b/android/content/res/Resources.java
@@ -0,0 +1,2524 @@
+/*
+ * 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.content.res;
+
+import android.animation.Animator;
+import android.animation.StateListAnimator;
+import android.annotation.AnimRes;
+import android.annotation.AnimatorRes;
+import android.annotation.AnyRes;
+import android.annotation.ArrayRes;
+import android.annotation.AttrRes;
+import android.annotation.BoolRes;
+import android.annotation.ColorInt;
+import android.annotation.ColorRes;
+import android.annotation.DimenRes;
+import android.annotation.DrawableRes;
+import android.annotation.FontRes;
+import android.annotation.FractionRes;
+import android.annotation.IntegerRes;
+import android.annotation.LayoutRes;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.PluralsRes;
+import android.annotation.RawRes;
+import android.annotation.StringRes;
+import android.annotation.StyleRes;
+import android.annotation.StyleableRes;
+import android.annotation.XmlRes;
+import android.app.Application;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ActivityInfo.Config;
+import android.content.res.loader.ResourcesLoader;
+import android.graphics.Movie;
+import android.graphics.Typeface;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Drawable.ConstantState;
+import android.graphics.drawable.DrawableInflater;
+import android.os.Build;
+import android.os.Bundle;
+import android.util.ArraySet;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.util.LongSparseArray;
+import android.util.Pools.SynchronizedPool;
+import android.util.TypedValue;
+import android.view.Display;
+import android.view.DisplayAdjustments;
+import android.view.ViewDebug;
+import android.view.ViewHierarchyEncoder;
+import android.view.WindowManager;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.GrowingArrayUtils;
+import com.android.internal.util.Preconditions;
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Class for accessing an application's resources.  This sits on top of the
+ * asset manager of the application (accessible through {@link #getAssets}) and
+ * provides a high-level API for getting typed data from the assets.
+ *
+ * <p>The Android resource system keeps track of all non-code assets associated with an
+ * application. You can use this class to access your application's resources. You can generally
+ * acquire the {@link android.content.res.Resources} instance associated with your application
+ * with {@link android.content.Context#getResources getResources()}.</p>
+ *
+ * <p>The Android SDK tools compile your application's resources into the application binary
+ * at build time.  To use a resource, you must install it correctly in the source tree (inside
+ * your project's {@code res/} directory) and build your application.  As part of the build
+ * process, the SDK tools generate symbols for each resource, which you can use in your application
+ * code to access the resources.</p>
+ *
+ * <p>Using application resources makes it easy to update various characteristics of your
+ * application without modifying code, and&mdash;by providing sets of alternative
+ * resources&mdash;enables you to optimize your application for a variety of device configurations
+ * (such as for different languages and screen sizes). This is an important aspect of developing
+ * Android applications that are compatible on different types of devices.</p>
+ *
+ * <p>After {@link Build.VERSION_CODES#R}, {@link Resources} must be obtained by
+ * {@link android.app.Activity} or {@link android.content.Context} created with
+ * {@link android.content.Context#createWindowContext(int, Bundle)}.
+ * {@link Application#getResources()} may report wrong values in multi-window or on secondary
+ * displays.
+ *
+ * <p>For more information about using resources, see the documentation about <a
+ * href="{@docRoot}guide/topics/resources/index.html">Application Resources</a>.</p>
+ */
+public class Resources {
+    /**
+     * The {@code null} resource ID. This denotes an invalid resource ID that is returned by the
+     * system when a resource is not found or the value is set to {@code @null} in XML.
+     */
+    public static final @AnyRes int ID_NULL = 0;
+
+    static final String TAG = "Resources";
+
+    private static final Object sSync = new Object();
+    private final Object mUpdateLock = new Object();
+
+    // Used by BridgeResources in layoutlib
+    @UnsupportedAppUsage
+    static Resources mSystem = null;
+
+    @UnsupportedAppUsage
+    private ResourcesImpl mResourcesImpl;
+
+    // Pool of TypedArrays targeted to this Resources object.
+    @UnsupportedAppUsage
+    final SynchronizedPool<TypedArray> mTypedArrayPool = new SynchronizedPool<>(5);
+
+    /** Used to inflate drawable objects from XML. */
+    @UnsupportedAppUsage
+    private DrawableInflater mDrawableInflater;
+
+    /** Lock object used to protect access to {@link #mTmpValue}. */
+    private final Object mTmpValueLock = new Object();
+
+    /** Single-item pool used to minimize TypedValue allocations. */
+    @UnsupportedAppUsage
+    private TypedValue mTmpValue = new TypedValue();
+
+    @UnsupportedAppUsage
+    final ClassLoader mClassLoader;
+
+    @GuardedBy("mUpdateLock")
+    private UpdateCallbacks mCallbacks = null;
+
+    /**
+     * WeakReferences to Themes that were constructed from this Resources object.
+     * We keep track of these in case our underlying implementation is changed, in which case
+     * the Themes must also get updated ThemeImpls.
+     */
+    private final ArrayList<WeakReference<Theme>> mThemeRefs = new ArrayList<>();
+
+    /**
+     * To avoid leaking WeakReferences to garbage collected Themes on the
+     * mThemeRefs list, we flush the list of stale references any time the
+     * mThemeRefNextFlushSize is reached.
+     */
+    private static final int MIN_THEME_REFS_FLUSH_SIZE = 32;
+    private int mThemeRefsNextFlushSize = MIN_THEME_REFS_FLUSH_SIZE;
+
+    private int mBaseApkAssetsSize;
+
+    /**
+     * Returns the most appropriate default theme for the specified target SDK version.
+     * <ul>
+     * <li>Below API 11: Gingerbread
+     * <li>APIs 12 thru 14: Holo
+     * <li>APIs 15 thru 23: Device default dark
+     * <li>APIs 24 and above: Device default light with dark action bar
+     * </ul>
+     *
+     * @param curTheme The current theme, or 0 if not specified.
+     * @param targetSdkVersion The target SDK version.
+     * @return A theme resource identifier
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static int selectDefaultTheme(int curTheme, int targetSdkVersion) {
+        return selectSystemTheme(curTheme, targetSdkVersion,
+                com.android.internal.R.style.Theme,
+                com.android.internal.R.style.Theme_Holo,
+                com.android.internal.R.style.Theme_DeviceDefault,
+                com.android.internal.R.style.Theme_DeviceDefault_Light_DarkActionBar);
+    }
+
+    /** @hide */
+    public static int selectSystemTheme(int curTheme, int targetSdkVersion, int orig, int holo,
+            int dark, int deviceDefault) {
+        if (curTheme != ID_NULL) {
+            return curTheme;
+        }
+        if (targetSdkVersion < Build.VERSION_CODES.HONEYCOMB) {
+            return orig;
+        }
+        if (targetSdkVersion < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+            return holo;
+        }
+        if (targetSdkVersion < Build.VERSION_CODES.N) {
+            return dark;
+        }
+        return deviceDefault;
+    }
+
+    /**
+     * Return a global shared Resources object that provides access to only
+     * system resources (no application resources), is not configured for the
+     * current screen (can not use dimension units, does not change based on
+     * orientation, etc), and is not affected by Runtime Resource Overlay.
+     */
+    public static Resources getSystem() {
+        synchronized (sSync) {
+            Resources ret = mSystem;
+            if (ret == null) {
+                ret = new Resources();
+                mSystem = ret;
+            }
+            return ret;
+        }
+    }
+
+    /**
+     * This exception is thrown by the resource APIs when a requested resource
+     * can not be found.
+     */
+    public static class NotFoundException extends RuntimeException {
+        public NotFoundException() {
+        }
+
+        public NotFoundException(String name) {
+            super(name);
+        }
+
+        public NotFoundException(String name, Exception cause) {
+            super(name, cause);
+        }
+    }
+
+    /** @hide */
+    public interface UpdateCallbacks extends ResourcesLoader.UpdateCallbacks {
+        /**
+         * Invoked when a {@link Resources} instance has a {@link ResourcesLoader} added, removed,
+         * or reordered.
+         *
+         * @param resources the instance being updated
+         * @param newLoaders the new set of loaders for the instance
+         */
+        void onLoadersChanged(@NonNull Resources resources,
+                @NonNull List<ResourcesLoader> newLoaders);
+    }
+
+    /**
+     * Handler that propagates updates of the {@link Resources} instance to the underlying
+     * {@link AssetManager} when the Resources is not registered with a
+     * {@link android.app.ResourcesManager}.
+     * @hide
+     */
+    public class AssetManagerUpdateHandler implements UpdateCallbacks{
+
+        @Override
+        public void onLoadersChanged(@NonNull Resources resources,
+                @NonNull List<ResourcesLoader> newLoaders) {
+            Preconditions.checkArgument(Resources.this == resources);
+            final ResourcesImpl impl = mResourcesImpl;
+            impl.clearAllCaches();
+            impl.getAssets().setLoaders(newLoaders);
+        }
+
+        @Override
+        public void onLoaderUpdated(@NonNull ResourcesLoader loader) {
+            final ResourcesImpl impl = mResourcesImpl;
+            final AssetManager assets = impl.getAssets();
+            if (assets.getLoaders().contains(loader)) {
+                impl.clearAllCaches();
+                assets.setLoaders(assets.getLoaders());
+            }
+        }
+    }
+
+    /**
+     * Create a new Resources object on top of an existing set of assets in an
+     * AssetManager.
+     *
+     * @deprecated Resources should not be constructed by apps.
+     * See {@link android.content.Context#createConfigurationContext(Configuration)}.
+     *
+     * @param assets Previously created AssetManager.
+     * @param metrics Current display metrics to consider when
+     *                selecting/computing resource values.
+     * @param config Desired device configuration to consider when
+     *               selecting/computing resource values (optional).
+     */
+    @Deprecated
+    public Resources(AssetManager assets, DisplayMetrics metrics, Configuration config) {
+        this(null);
+        mResourcesImpl = new ResourcesImpl(assets, metrics, config, new DisplayAdjustments());
+    }
+
+    /**
+     * Creates a new Resources object with CompatibilityInfo.
+     *
+     * @param classLoader class loader for the package used to load custom
+     *                    resource classes, may be {@code null} to use system
+     *                    class loader
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public Resources(@Nullable ClassLoader classLoader) {
+        mClassLoader = classLoader == null ? ClassLoader.getSystemClassLoader() : classLoader;
+    }
+
+    /**
+     * Only for creating the System resources.
+     */
+    @UnsupportedAppUsage
+    private Resources() {
+        this(null);
+
+        final DisplayMetrics metrics = new DisplayMetrics();
+        metrics.setToDefaults();
+
+        final Configuration config = new Configuration();
+        config.setToDefaults();
+
+        mResourcesImpl = new ResourcesImpl(AssetManager.getSystem(), metrics, config,
+                new DisplayAdjustments());
+    }
+
+    /**
+     * Set the underlying implementation (containing all the resources and caches)
+     * and updates all Theme references to new implementations as well.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public void setImpl(ResourcesImpl impl) {
+        if (impl == mResourcesImpl) {
+            return;
+        }
+
+        mBaseApkAssetsSize = ArrayUtils.size(impl.getAssets().getApkAssets());
+        mResourcesImpl = impl;
+
+        // Create new ThemeImpls that are identical to the ones we have.
+        synchronized (mThemeRefs) {
+            final int count = mThemeRefs.size();
+            for (int i = 0; i < count; i++) {
+                WeakReference<Theme> weakThemeRef = mThemeRefs.get(i);
+                Theme theme = weakThemeRef != null ? weakThemeRef.get() : null;
+                if (theme != null) {
+                    theme.setImpl(mResourcesImpl.newThemeImpl(theme.getKey()));
+                }
+            }
+        }
+    }
+
+    /** @hide */
+    public void setCallbacks(UpdateCallbacks callbacks) {
+        if (mCallbacks != null) {
+            throw new IllegalStateException("callback already registered");
+        }
+
+        mCallbacks = callbacks;
+    }
+
+    /**
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public ResourcesImpl getImpl() {
+        return mResourcesImpl;
+    }
+
+    /**
+     * @hide
+     */
+    public ClassLoader getClassLoader() {
+        return mClassLoader;
+    }
+
+    /**
+     * @return the inflater used to create drawable objects
+     * @hide Pending API finalization.
+     */
+    @UnsupportedAppUsage
+    public final DrawableInflater getDrawableInflater() {
+        if (mDrawableInflater == null) {
+            mDrawableInflater = new DrawableInflater(this, mClassLoader);
+        }
+        return mDrawableInflater;
+    }
+
+    /**
+     * Used by AnimatorInflater.
+     *
+     * @hide
+     */
+    public ConfigurationBoundResourceCache<Animator> getAnimatorCache() {
+        return mResourcesImpl.getAnimatorCache();
+    }
+
+    /**
+     * Used by AnimatorInflater.
+     *
+     * @hide
+     */
+    public ConfigurationBoundResourceCache<StateListAnimator> getStateListAnimatorCache() {
+        return mResourcesImpl.getStateListAnimatorCache();
+    }
+
+    /**
+     * Return the string value associated with a particular resource ID.  The
+     * returned object will be a String if this is a plain string; it will be
+     * some other type of CharSequence if it is styled.
+     * {@more}
+     *
+     * @param id The desired resource identifier, as generated by the aapt
+     *           tool. This integer encodes the package, type, and resource
+     *           entry. The value 0 is an invalid identifier.
+     *
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     *
+     * @return CharSequence The string data associated with the resource, plus
+     *         possibly styled text information.
+     */
+    @NonNull public CharSequence getText(@StringRes int id) throws NotFoundException {
+        CharSequence res = mResourcesImpl.getAssets().getResourceText(id);
+        if (res != null) {
+            return res;
+        }
+        throw new NotFoundException("String resource ID #0x"
+                + Integer.toHexString(id));
+    }
+
+    /**
+     * Return the Typeface value associated with a particular resource ID.
+     * {@more}
+     *
+     * @param id The desired resource identifier, as generated by the aapt
+     *           tool. This integer encodes the package, type, and resource
+     *           entry. The value 0 is an invalid identifier.
+     *
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     *
+     * @return Typeface The Typeface data associated with the resource.
+     */
+    @NonNull public Typeface getFont(@FontRes int id) throws NotFoundException {
+        final TypedValue value = obtainTempTypedValue();
+        try {
+            final ResourcesImpl impl = mResourcesImpl;
+            impl.getValue(id, value, true);
+            Typeface typeface = impl.loadFont(this, value, id);
+            if (typeface != null) {
+                return typeface;
+            }
+        } finally {
+            releaseTempTypedValue(value);
+        }
+        throw new NotFoundException("Font resource ID #0x"
+                + Integer.toHexString(id));
+    }
+
+    @NonNull
+    Typeface getFont(@NonNull TypedValue value, @FontRes int id) throws NotFoundException {
+        return mResourcesImpl.loadFont(this, value, id);
+    }
+
+    /**
+     * @hide
+     */
+    public void preloadFonts(@ArrayRes int id) {
+        final TypedArray array = obtainTypedArray(id);
+        try {
+            final int size = array.length();
+            for (int i = 0; i < size; i++) {
+                array.getFont(i);
+            }
+        } finally {
+            array.recycle();
+        }
+    }
+
+    /**
+     * Returns the character sequence necessary for grammatically correct pluralization
+     * of the given resource ID for the given quantity.
+     * Note that the character sequence is selected based solely on grammatical necessity,
+     * and that such rules differ between languages. Do not assume you know which string
+     * will be returned for a given quantity. See
+     * <a href="{@docRoot}guide/topics/resources/string-resource.html#Plurals">String Resources</a>
+     * for more detail.
+     *
+     * @param id The desired resource identifier, as generated by the aapt
+     *           tool. This integer encodes the package, type, and resource
+     *           entry. The value 0 is an invalid identifier.
+     * @param quantity The number used to get the correct string for the current language's
+     *           plural rules.
+     *
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     *
+     * @return CharSequence The string data associated with the resource, plus
+     *         possibly styled text information.
+     */
+    @NonNull
+    public CharSequence getQuantityText(@PluralsRes int id, int quantity)
+            throws NotFoundException {
+        return mResourcesImpl.getQuantityText(id, quantity);
+    }
+
+    /**
+     * Return the string value associated with a particular resource ID.  It
+     * will be stripped of any styled text information.
+     * {@more}
+     *
+     * @param id The desired resource identifier, as generated by the aapt
+     *           tool. This integer encodes the package, type, and resource
+     *           entry. The value 0 is an invalid identifier.
+     *
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     *
+     * @return String The string data associated with the resource,
+     *         stripped of styled text information.
+     */
+    @NonNull
+    public String getString(@StringRes int id) throws NotFoundException {
+        return getText(id).toString();
+    }
+
+
+    /**
+     * Return the string value associated with a particular resource ID,
+     * substituting the format arguments as defined in {@link java.util.Formatter}
+     * and {@link java.lang.String#format}. It will be stripped of any styled text
+     * information.
+     * {@more}
+     *
+     * @param id The desired resource identifier, as generated by the aapt
+     *           tool. This integer encodes the package, type, and resource
+     *           entry. The value 0 is an invalid identifier.
+     *           
+     * @param formatArgs The format arguments that will be used for substitution.
+     *
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     *
+     * @return String The string data associated with the resource,
+     *         stripped of styled text information.
+     */
+    @NonNull
+    public String getString(@StringRes int id, Object... formatArgs) throws NotFoundException {
+        final String raw = getString(id);
+        return String.format(mResourcesImpl.getConfiguration().getLocales().get(0), raw,
+                formatArgs);
+    }
+
+    /**
+     * Formats the string necessary for grammatically correct pluralization
+     * of the given resource ID for the given quantity, using the given arguments.
+     * Note that the string is selected based solely on grammatical necessity,
+     * and that such rules differ between languages. Do not assume you know which string
+     * will be returned for a given quantity. See
+     * <a href="{@docRoot}guide/topics/resources/string-resource.html#Plurals">String Resources</a>
+     * for more detail.
+     *
+     * <p>Substitution of format arguments works as if using
+     * {@link java.util.Formatter} and {@link java.lang.String#format}.
+     * The resulting string will be stripped of any styled text information.
+     *
+     * @param id The desired resource identifier, as generated by the aapt
+     *           tool. This integer encodes the package, type, and resource
+     *           entry. The value 0 is an invalid identifier.
+     * @param quantity The number used to get the correct string for the current language's
+     *           plural rules.
+     * @param formatArgs The format arguments that will be used for substitution.
+     *
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     *
+     * @return String The string data associated with the resource,
+     * stripped of styled text information.
+     */
+    @NonNull
+    public String getQuantityString(@PluralsRes int id, int quantity, Object... formatArgs)
+            throws NotFoundException {
+        String raw = getQuantityText(id, quantity).toString();
+        return String.format(mResourcesImpl.getConfiguration().getLocales().get(0), raw,
+                formatArgs);
+    }
+
+    /**
+     * Returns the string necessary for grammatically correct pluralization
+     * of the given resource ID for the given quantity.
+     * Note that the string is selected based solely on grammatical necessity,
+     * and that such rules differ between languages. Do not assume you know which string
+     * will be returned for a given quantity. See
+     * <a href="{@docRoot}guide/topics/resources/string-resource.html#Plurals">String Resources</a>
+     * for more detail.
+     *
+     * @param id The desired resource identifier, as generated by the aapt
+     *           tool. This integer encodes the package, type, and resource
+     *           entry. The value 0 is an invalid identifier.
+     * @param quantity The number used to get the correct string for the current language's
+     *           plural rules.
+     *
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     *
+     * @return String The string data associated with the resource,
+     * stripped of styled text information.
+     */
+    @NonNull
+    public String getQuantityString(@PluralsRes int id, int quantity) throws NotFoundException {
+        return getQuantityText(id, quantity).toString();
+    }
+
+    /**
+     * Return the string value associated with a particular resource ID.  The
+     * returned object will be a String if this is a plain string; it will be
+     * some other type of CharSequence if it is styled.
+     * 
+     * @param id The desired resource identifier, as generated by the aapt
+     *           tool. This integer encodes the package, type, and resource
+     *           entry. The value 0 is an invalid identifier.
+     * 
+     * @param def The default CharSequence to return.
+     *
+     * @return CharSequence The string data associated with the resource, plus
+     *         possibly styled text information, or def if id is 0 or not found.
+     */
+    public CharSequence getText(@StringRes int id, CharSequence def) {
+        CharSequence res = id != 0 ? mResourcesImpl.getAssets().getResourceText(id) : null;
+        return res != null ? res : def;
+    }
+
+    /**
+     * Return the styled text array associated with a particular resource ID.
+     *
+     * @param id The desired resource identifier, as generated by the aapt
+     *           tool. This integer encodes the package, type, and resource
+     *           entry. The value 0 is an invalid identifier.
+     *
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     *
+     * @return The styled text array associated with the resource.
+     */
+    @NonNull
+    public CharSequence[] getTextArray(@ArrayRes int id) throws NotFoundException {
+        CharSequence[] res = mResourcesImpl.getAssets().getResourceTextArray(id);
+        if (res != null) {
+            return res;
+        }
+        throw new NotFoundException("Text array resource ID #0x" + Integer.toHexString(id));
+    }
+
+    /**
+     * Return the string array associated with a particular resource ID.
+     *
+     * @param id The desired resource identifier, as generated by the aapt
+     *           tool. This integer encodes the package, type, and resource
+     *           entry. The value 0 is an invalid identifier.
+     *
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     *
+     * @return The string array associated with the resource.
+     */
+    @NonNull
+    public String[] getStringArray(@ArrayRes int id)
+            throws NotFoundException {
+        String[] res = mResourcesImpl.getAssets().getResourceStringArray(id);
+        if (res != null) {
+            return res;
+        }
+        throw new NotFoundException("String array resource ID #0x" + Integer.toHexString(id));
+    }
+
+    /**
+     * Return the int array associated with a particular resource ID.
+     *
+     * @param id The desired resource identifier, as generated by the aapt
+     *           tool. This integer encodes the package, type, and resource
+     *           entry. The value 0 is an invalid identifier.
+     *
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     *
+     * @return The int array associated with the resource.
+     */
+    @NonNull
+    public int[] getIntArray(@ArrayRes int id) throws NotFoundException {
+        int[] res = mResourcesImpl.getAssets().getResourceIntArray(id);
+        if (res != null) {
+            return res;
+        }
+        throw new NotFoundException("Int array resource ID #0x" + Integer.toHexString(id));
+    }
+
+    /**
+     * Return an array of heterogeneous values.
+     *
+     * @param id The desired resource identifier, as generated by the aapt
+     *           tool. This integer encodes the package, type, and resource
+     *           entry. The value 0 is an invalid identifier.
+     *
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     *
+     * @return Returns a TypedArray holding an array of the array values.
+     * Be sure to call {@link TypedArray#recycle() TypedArray.recycle()}
+     * when done with it.
+     */
+    @NonNull
+    public TypedArray obtainTypedArray(@ArrayRes int id) throws NotFoundException {
+        final ResourcesImpl impl = mResourcesImpl;
+        int len = impl.getAssets().getResourceArraySize(id);
+        if (len < 0) {
+            throw new NotFoundException("Array resource ID #0x" + Integer.toHexString(id));
+        }
+        
+        TypedArray array = TypedArray.obtain(this, len);
+        array.mLength = impl.getAssets().getResourceArray(id, array.mData);
+        array.mIndices[0] = 0;
+        
+        return array;
+    }
+
+    /**
+     * Retrieve a dimensional for a particular resource ID.  Unit 
+     * conversions are based on the current {@link DisplayMetrics} associated
+     * with the resources.
+     * 
+     * @param id The desired resource identifier, as generated by the aapt
+     *           tool. This integer encodes the package, type, and resource
+     *           entry. The value 0 is an invalid identifier.
+     * 
+     * @return Resource dimension value multiplied by the appropriate metric to convert to pixels.
+     * 
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     *
+     * @see #getDimensionPixelOffset
+     * @see #getDimensionPixelSize
+     */
+    public float getDimension(@DimenRes int id) throws NotFoundException {
+        final TypedValue value = obtainTempTypedValue();
+        try {
+            final ResourcesImpl impl = mResourcesImpl;
+            impl.getValue(id, value, true);
+            if (value.type == TypedValue.TYPE_DIMENSION) {
+                return TypedValue.complexToDimension(value.data, impl.getDisplayMetrics());
+            }
+            throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id)
+                    + " type #0x" + Integer.toHexString(value.type) + " is not valid");
+        } finally {
+            releaseTempTypedValue(value);
+        }
+    }
+
+    /**
+     * Retrieve a dimensional for a particular resource ID for use
+     * as an offset in raw pixels.  This is the same as
+     * {@link #getDimension}, except the returned value is converted to
+     * integer pixels for you.  An offset conversion involves simply
+     * truncating the base value to an integer.
+     * 
+     * @param id The desired resource identifier, as generated by the aapt
+     *           tool. This integer encodes the package, type, and resource
+     *           entry. The value 0 is an invalid identifier.
+     * 
+     * @return Resource dimension value multiplied by the appropriate 
+     * metric and truncated to integer pixels.
+     * 
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     *
+     * @see #getDimension
+     * @see #getDimensionPixelSize
+     */
+    public int getDimensionPixelOffset(@DimenRes int id) throws NotFoundException {
+        final TypedValue value = obtainTempTypedValue();
+        try {
+            final ResourcesImpl impl = mResourcesImpl;
+            impl.getValue(id, value, true);
+            if (value.type == TypedValue.TYPE_DIMENSION) {
+                return TypedValue.complexToDimensionPixelOffset(value.data,
+                        impl.getDisplayMetrics());
+            }
+            throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id)
+                    + " type #0x" + Integer.toHexString(value.type) + " is not valid");
+        } finally {
+            releaseTempTypedValue(value);
+        }
+    }
+
+    /**
+     * Retrieve a dimensional for a particular resource ID for use
+     * as a size in raw pixels.  This is the same as
+     * {@link #getDimension}, except the returned value is converted to
+     * integer pixels for use as a size.  A size conversion involves
+     * rounding the base value, and ensuring that a non-zero base value
+     * is at least one pixel in size.
+     * 
+     * @param id The desired resource identifier, as generated by the aapt
+     *           tool. This integer encodes the package, type, and resource
+     *           entry. The value 0 is an invalid identifier.
+     * 
+     * @return Resource dimension value multiplied by the appropriate 
+     * metric and truncated to integer pixels.
+     *  
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     *
+     * @see #getDimension
+     * @see #getDimensionPixelOffset
+     */
+    public int getDimensionPixelSize(@DimenRes int id) throws NotFoundException {
+        final TypedValue value = obtainTempTypedValue();
+        try {
+            final ResourcesImpl impl = mResourcesImpl;
+            impl.getValue(id, value, true);
+            if (value.type == TypedValue.TYPE_DIMENSION) {
+                return TypedValue.complexToDimensionPixelSize(value.data, impl.getDisplayMetrics());
+            }
+            throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id)
+                    + " type #0x" + Integer.toHexString(value.type) + " is not valid");
+        } finally {
+            releaseTempTypedValue(value);
+        }
+    }
+
+    /**
+     * Retrieve a fractional unit for a particular resource ID.
+     * 
+     * @param id The desired resource identifier, as generated by the aapt
+     *           tool. This integer encodes the package, type, and resource
+     *           entry. The value 0 is an invalid identifier.
+     * @param base The base value of this fraction.  In other words, a 
+     *             standard fraction is multiplied by this value.
+     * @param pbase The parent base value of this fraction.  In other 
+     *             words, a parent fraction (nn%p) is multiplied by this
+     *             value.
+     * 
+     * @return Attribute fractional value multiplied by the appropriate 
+     * base value.
+     *  
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     */
+    public float getFraction(@FractionRes int id, int base, int pbase) {
+        final TypedValue value = obtainTempTypedValue();
+        try {
+            mResourcesImpl.getValue(id, value, true);
+            if (value.type == TypedValue.TYPE_FRACTION) {
+                return TypedValue.complexToFraction(value.data, base, pbase);
+            }
+            throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id)
+                    + " type #0x" + Integer.toHexString(value.type) + " is not valid");
+        } finally {
+            releaseTempTypedValue(value);
+        }
+    }
+    
+    /**
+     * Return a drawable object associated with a particular resource ID.
+     * Various types of objects will be returned depending on the underlying
+     * resource -- for example, a solid color, PNG image, scalable image, etc.
+     * The Drawable API hides these implementation details.
+     *
+     * <p class="note"><strong>Note:</strong> Prior to
+     * {@link android.os.Build.VERSION_CODES#JELLY_BEAN}, this function
+     * would not correctly retrieve the final configuration density when
+     * the resource ID passed here is an alias to another Drawable resource.
+     * This means that if the density configuration of the alias resource
+     * is different than the actual resource, the density of the returned
+     * Drawable would be incorrect, resulting in bad scaling. To work
+     * around this, you can instead manually resolve the aliased reference
+     * by using {@link #getValue(int, TypedValue, boolean)} and passing
+     * {@code true} for {@code resolveRefs}. The resulting
+     * {@link TypedValue#resourceId} value may be passed to this method.</p>
+     *
+     * <p class="note"><strong>Note:</strong> To obtain a themed drawable, use
+     * {@link android.content.Context#getDrawable(int) Context.getDrawable(int)}
+     * or {@link #getDrawable(int, Theme)} passing the desired theme.</p>
+     *
+     * @param id The desired resource identifier, as generated by the aapt
+     *           tool. This integer encodes the package, type, and resource
+     *           entry. The value 0 is an invalid identifier.
+     * @return Drawable An object that can be used to draw this resource.
+     * @throws NotFoundException Throws NotFoundException if the given ID does
+     *         not exist.
+     * @see #getDrawable(int, Theme)
+     * @deprecated Use {@link #getDrawable(int, Theme)} instead.
+     */
+    @Deprecated
+    public Drawable getDrawable(@DrawableRes int id) throws NotFoundException {
+        final Drawable d = getDrawable(id, null);
+        if (d != null && d.canApplyTheme()) {
+            Log.w(TAG, "Drawable " + getResourceName(id) + " has unresolved theme "
+                    + "attributes! Consider using Resources.getDrawable(int, Theme) or "
+                    + "Context.getDrawable(int).", new RuntimeException());
+        }
+        return d;
+    }
+
+    /**
+     * Return a drawable object associated with a particular resource ID and
+     * styled for the specified theme. Various types of objects will be
+     * returned depending on the underlying resource -- for example, a solid
+     * color, PNG image, scalable image, etc.
+     *
+     * @param id The desired resource identifier, as generated by the aapt
+     *           tool. This integer encodes the package, type, and resource
+     *           entry. The value 0 is an invalid identifier.
+     * @param theme The theme used to style the drawable attributes, may be {@code null}.
+     * @return Drawable An object that can be used to draw this resource.
+     * @throws NotFoundException Throws NotFoundException if the given ID does
+     *         not exist.
+     */
+    public Drawable getDrawable(@DrawableRes int id, @Nullable Theme theme)
+            throws NotFoundException {
+        return getDrawableForDensity(id, 0, theme);
+    }
+
+    /**
+     * Return a drawable object associated with a particular resource ID for the
+     * given screen density in DPI. This will set the drawable's density to be
+     * the device's density multiplied by the ratio of actual drawable density
+     * to requested density. This allows the drawable to be scaled up to the
+     * correct size if needed. Various types of objects will be returned
+     * depending on the underlying resource -- for example, a solid color, PNG
+     * image, scalable image, etc. The Drawable API hides these implementation
+     * details.
+     *
+     * <p class="note"><strong>Note:</strong> To obtain a themed drawable, use
+     * {@link android.content.Context#getDrawable(int) Context.getDrawable(int)}
+     * or {@link #getDrawableForDensity(int, int, Theme)} passing the desired
+     * theme.</p>
+     *
+     * @param id The desired resource identifier, as generated by the aapt tool.
+     *            This integer encodes the package, type, and resource entry.
+     *            The value 0 is an invalid identifier.
+     * @param density the desired screen density indicated by the resource as
+     *            found in {@link DisplayMetrics}. A value of 0 means to use the
+     *            density returned from {@link #getConfiguration()}.
+     *            This is equivalent to calling {@link #getDrawable(int)}.
+     * @return Drawable An object that can be used to draw this resource.
+     * @throws NotFoundException Throws NotFoundException if the given ID does
+     *             not exist.
+     * @see #getDrawableForDensity(int, int, Theme)
+     * @deprecated Use {@link #getDrawableForDensity(int, int, Theme)} instead.
+     */
+    @Nullable
+    @Deprecated
+    public Drawable getDrawableForDensity(@DrawableRes int id, int density)
+            throws NotFoundException {
+        return getDrawableForDensity(id, density, null);
+    }
+
+    /**
+     * Return a drawable object associated with a particular resource ID for the
+     * given screen density in DPI and styled for the specified theme.
+     *
+     * @param id The desired resource identifier, as generated by the aapt tool.
+     *            This integer encodes the package, type, and resource entry.
+     *            The value 0 is an invalid identifier.
+     * @param density The desired screen density indicated by the resource as
+     *            found in {@link DisplayMetrics}. A value of 0 means to use the
+     *            density returned from {@link #getConfiguration()}.
+     *            This is equivalent to calling {@link #getDrawable(int, Theme)}.
+     * @param theme The theme used to style the drawable attributes, may be {@code null} if the
+     *              drawable cannot be decoded.
+     * @return Drawable An object that can be used to draw this resource.
+     * @throws NotFoundException Throws NotFoundException if the given ID does
+     *             not exist.
+     */
+    @Nullable
+    public Drawable getDrawableForDensity(@DrawableRes int id, int density, @Nullable Theme theme) {
+        final TypedValue value = obtainTempTypedValue();
+        try {
+            final ResourcesImpl impl = mResourcesImpl;
+            impl.getValueForDensity(id, density, value, true);
+            return loadDrawable(value, id, density, theme);
+        } finally {
+            releaseTempTypedValue(value);
+        }
+    }
+
+    @NonNull
+    @UnsupportedAppUsage
+    Drawable loadDrawable(@NonNull TypedValue value, int id, int density, @Nullable Theme theme)
+            throws NotFoundException {
+        return mResourcesImpl.loadDrawable(this, value, id, density, theme);
+    }
+
+    /**
+     * Return a movie object associated with the particular resource ID.
+     * @param id The desired resource identifier, as generated by the aapt
+     *           tool. This integer encodes the package, type, and resource
+     *           entry. The value 0 is an invalid identifier.
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     *
+     * @deprecated Prefer {@link android.graphics.drawable.AnimatedImageDrawable}.
+     */
+    @Deprecated
+    public Movie getMovie(@RawRes int id) throws NotFoundException {
+        final InputStream is = openRawResource(id);
+        final Movie movie = Movie.decodeStream(is);
+        try {
+            is.close();
+        } catch (IOException e) {
+            // No one cares.
+        }
+        return movie;
+    }
+
+    /**
+     * Returns a color integer associated with a particular resource ID. If the
+     * resource holds a complex {@link ColorStateList}, then the default color
+     * from the set is returned.
+     *
+     * @param id The desired resource identifier, as generated by the aapt
+     *           tool. This integer encodes the package, type, and resource
+     *           entry. The value 0 is an invalid identifier.
+     *
+     * @throws NotFoundException Throws NotFoundException if the given ID does
+     *         not exist.
+     *
+     * @return A single color value in the form 0xAARRGGBB.
+     * @deprecated Use {@link #getColor(int, Theme)} instead.
+     */
+    @ColorInt
+    @Deprecated
+    public int getColor(@ColorRes int id) throws NotFoundException {
+        return getColor(id, null);
+    }
+
+    /**
+     * Returns a themed color integer associated with a particular resource ID.
+     * If the resource holds a complex {@link ColorStateList}, then the default
+     * color from the set is returned.
+     *
+     * @param id The desired resource identifier, as generated by the aapt
+     *           tool. This integer encodes the package, type, and resource
+     *           entry. The value 0 is an invalid identifier.
+     * @param theme The theme used to style the color attributes, may be
+     *              {@code null}.
+     *
+     * @throws NotFoundException Throws NotFoundException if the given ID does
+     *         not exist.
+     *
+     * @return A single color value in the form 0xAARRGGBB.
+     */
+    @ColorInt
+    public int getColor(@ColorRes int id, @Nullable Theme theme) throws NotFoundException {
+        final TypedValue value = obtainTempTypedValue();
+        try {
+            final ResourcesImpl impl = mResourcesImpl;
+            impl.getValue(id, value, true);
+            if (value.type >= TypedValue.TYPE_FIRST_INT
+                    && value.type <= TypedValue.TYPE_LAST_INT) {
+                return value.data;
+            } else if (value.type != TypedValue.TYPE_STRING) {
+                throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id)
+                        + " type #0x" + Integer.toHexString(value.type) + " is not valid");
+            }
+
+            final ColorStateList csl = impl.loadColorStateList(this, value, id, theme);
+            return csl.getDefaultColor();
+        } finally {
+            releaseTempTypedValue(value);
+        }
+    }
+
+    /**
+     * Returns a color state list associated with a particular resource ID. The
+     * resource may contain either a single raw color value or a complex
+     * {@link ColorStateList} holding multiple possible colors.
+     *
+     * @param id The desired resource identifier of a {@link ColorStateList},
+     *           as generated by the aapt tool. This integer encodes the
+     *           package, type, and resource entry. The value 0 is an invalid
+     *           identifier.
+     *
+     * @throws NotFoundException Throws NotFoundException if the given ID does
+     *         not exist.
+     *
+     * @return A ColorStateList object containing either a single solid color
+     *         or multiple colors that can be selected based on a state.
+     * @deprecated Use {@link #getColorStateList(int, Theme)} instead.
+     */
+    @NonNull
+    @Deprecated
+    public ColorStateList getColorStateList(@ColorRes int id) throws NotFoundException {
+        final ColorStateList csl = getColorStateList(id, null);
+        if (csl != null && csl.canApplyTheme()) {
+            Log.w(TAG, "ColorStateList " + getResourceName(id) + " has "
+                    + "unresolved theme attributes! Consider using "
+                    + "Resources.getColorStateList(int, Theme) or "
+                    + "Context.getColorStateList(int).", new RuntimeException());
+        }
+        return csl;
+    }
+
+    /**
+     * Returns a themed color state list associated with a particular resource
+     * ID. The resource may contain either a single raw color value or a
+     * complex {@link ColorStateList} holding multiple possible colors.
+     *
+     * @param id The desired resource identifier of a {@link ColorStateList},
+     *           as generated by the aapt tool. This integer encodes the
+     *           package, type, and resource entry. The value 0 is an invalid
+     *           identifier.
+     * @param theme The theme used to style the color attributes, may be
+     *              {@code null}.
+     *
+     * @throws NotFoundException Throws NotFoundException if the given ID does
+     *         not exist.
+     *
+     * @return A themed ColorStateList object containing either a single solid
+     *         color or multiple colors that can be selected based on a state.
+     */
+    @NonNull
+    public ColorStateList getColorStateList(@ColorRes int id, @Nullable Theme theme)
+            throws NotFoundException {
+        final TypedValue value = obtainTempTypedValue();
+        try {
+            final ResourcesImpl impl = mResourcesImpl;
+            impl.getValue(id, value, true);
+            return impl.loadColorStateList(this, value, id, theme);
+        } finally {
+            releaseTempTypedValue(value);
+        }
+    }
+
+    @NonNull
+    ColorStateList loadColorStateList(@NonNull TypedValue value, int id, @Nullable Theme theme)
+            throws NotFoundException {
+        return mResourcesImpl.loadColorStateList(this, value, id, theme);
+    }
+
+    /**
+     * @hide
+     */
+    @NonNull
+    public ComplexColor loadComplexColor(@NonNull TypedValue value, int id, @Nullable Theme theme) {
+        return mResourcesImpl.loadComplexColor(this, value, id, theme);
+    }
+
+    /**
+     * Return a boolean associated with a particular resource ID.  This can be
+     * used with any integral resource value, and will return true if it is
+     * non-zero.
+     *
+     * @param id The desired resource identifier, as generated by the aapt
+     *           tool. This integer encodes the package, type, and resource
+     *           entry. The value 0 is an invalid identifier.
+     *
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     *
+     * @return Returns the boolean value contained in the resource.
+     */
+    public boolean getBoolean(@BoolRes int id) throws NotFoundException {
+        final TypedValue value = obtainTempTypedValue();
+        try {
+            mResourcesImpl.getValue(id, value, true);
+            if (value.type >= TypedValue.TYPE_FIRST_INT
+                    && value.type <= TypedValue.TYPE_LAST_INT) {
+                return value.data != 0;
+            }
+            throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id)
+                    + " type #0x" + Integer.toHexString(value.type) + " is not valid");
+        } finally {
+            releaseTempTypedValue(value);
+        }
+    }
+
+    /**
+     * Return an integer associated with a particular resource ID.
+     *
+     * @param id The desired resource identifier, as generated by the aapt
+     *           tool. This integer encodes the package, type, and resource
+     *           entry. The value 0 is an invalid identifier.
+     *
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     *
+     * @return Returns the integer value contained in the resource.
+     */
+    public int getInteger(@IntegerRes int id) throws NotFoundException {
+        final TypedValue value = obtainTempTypedValue();
+        try {
+            mResourcesImpl.getValue(id, value, true);
+            if (value.type >= TypedValue.TYPE_FIRST_INT
+                    && value.type <= TypedValue.TYPE_LAST_INT) {
+                return value.data;
+            }
+            throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id)
+                    + " type #0x" + Integer.toHexString(value.type) + " is not valid");
+        } finally {
+            releaseTempTypedValue(value);
+        }
+    }
+
+    /**
+     * Retrieve a floating-point value for a particular resource ID.
+     *
+     * @param id The desired resource identifier, as generated by the aapt
+     *           tool. This integer encodes the package, type, and resource
+     *           entry. The value 0 is an invalid identifier.
+     *
+     * @return Returns the floating-point value contained in the resource.
+     *
+     * @throws NotFoundException Throws NotFoundException if the given ID does
+     *         not exist or is not a floating-point value.
+     */
+    public float getFloat(@DimenRes int id) {
+        final TypedValue value = obtainTempTypedValue();
+        try {
+            mResourcesImpl.getValue(id, value, true);
+            if (value.type == TypedValue.TYPE_FLOAT) {
+                return value.getFloat();
+            }
+            throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id)
+                    + " type #0x" + Integer.toHexString(value.type) + " is not valid");
+        } finally {
+            releaseTempTypedValue(value);
+        }
+    }
+
+    /**
+     * Return an XmlResourceParser through which you can read a view layout
+     * description for the given resource ID.  This parser has limited
+     * functionality -- in particular, you can't change its input, and only
+     * the high-level events are available.
+     * 
+     * <p>This function is really a simple wrapper for calling
+     * {@link #getXml} with a layout resource.
+     * 
+     * @param id The desired resource identifier, as generated by the aapt
+     *           tool. This integer encodes the package, type, and resource
+     *           entry. The value 0 is an invalid identifier.
+     *
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     * 
+     * @return A new parser object through which you can read
+     *         the XML data.
+     *         
+     * @see #getXml
+     */
+    @NonNull
+    public XmlResourceParser getLayout(@LayoutRes int id) throws NotFoundException {
+        return loadXmlResourceParser(id, "layout");
+    }
+
+    /**
+     * Return an XmlResourceParser through which you can read an animation
+     * description for the given resource ID.  This parser has limited
+     * functionality -- in particular, you can't change its input, and only
+     * the high-level events are available.
+     * 
+     * <p>This function is really a simple wrapper for calling
+     * {@link #getXml} with an animation resource.
+     * 
+     * @param id The desired resource identifier, as generated by the aapt
+     *           tool. This integer encodes the package, type, and resource
+     *           entry. The value 0 is an invalid identifier.
+     *
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     * 
+     * @return A new parser object through which you can read
+     *         the XML data.
+     *         
+     * @see #getXml
+     */
+    @NonNull
+    public XmlResourceParser getAnimation(@AnimatorRes @AnimRes int id) throws NotFoundException {
+        return loadXmlResourceParser(id, "anim");
+    }
+
+    /**
+     * Return an XmlResourceParser through which you can read a generic XML
+     * resource for the given resource ID.
+     * 
+     * <p>The XmlPullParser implementation returned here has some limited
+     * functionality.  In particular, you can't change its input, and only
+     * high-level parsing events are available (since the document was
+     * pre-parsed for you at build time, which involved merging text and
+     * stripping comments).
+     * 
+     * @param id The desired resource identifier, as generated by the aapt
+     *           tool. This integer encodes the package, type, and resource
+     *           entry. The value 0 is an invalid identifier.
+     *
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     * 
+     * @return A new parser object through which you can read
+     *         the XML data.
+     *         
+     * @see android.util.AttributeSet
+     */
+    @NonNull
+    public XmlResourceParser getXml(@XmlRes int id) throws NotFoundException {
+        return loadXmlResourceParser(id, "xml");
+    }
+
+    /**
+     * Open a data stream for reading a raw resource.  This can only be used
+     * with resources whose value is the name of an asset files -- that is, it can be
+     * used to open drawable, sound, and raw resources; it will fail on string
+     * and color resources.
+     * 
+     * @param id The resource identifier to open, as generated by the aapt tool.
+     * 
+     * @return InputStream Access to the resource data.
+     *
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     */
+    @NonNull
+    public InputStream openRawResource(@RawRes int id) throws NotFoundException {
+        final TypedValue value = obtainTempTypedValue();
+        try {
+            return openRawResource(id, value);
+        } finally {
+            releaseTempTypedValue(value);
+        }
+    }
+
+    /**
+     * Returns a TypedValue suitable for temporary use. The obtained TypedValue
+     * should be released using {@link #releaseTempTypedValue(TypedValue)}.
+     *
+     * @return a typed value suitable for temporary use
+     */
+    private TypedValue obtainTempTypedValue() {
+        TypedValue tmpValue = null;
+        synchronized (mTmpValueLock) {
+            if (mTmpValue != null) {
+                tmpValue = mTmpValue;
+                mTmpValue = null;
+            }
+        }
+        if (tmpValue == null) {
+            return new TypedValue();
+        }
+        return tmpValue;
+    }
+
+    /**
+     * Returns a TypedValue to the pool. After calling this method, the
+     * specified TypedValue should no longer be accessed.
+     *
+     * @param value the typed value to return to the pool
+     */
+    private void releaseTempTypedValue(TypedValue value) {
+        synchronized (mTmpValueLock) {
+            if (mTmpValue == null) {
+                mTmpValue = value;
+            }
+        }
+    }
+
+    /**
+     * Open a data stream for reading a raw resource.  This can only be used
+     * with resources whose value is the name of an asset file -- that is, it can be
+     * used to open drawable, sound, and raw resources; it will fail on string
+     * and color resources.
+     *
+     * @param id The resource identifier to open, as generated by the aapt tool.
+     * @param value The TypedValue object to hold the resource information.
+     *
+     * @return InputStream Access to the resource data.
+     *
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     */
+    @NonNull
+    public InputStream openRawResource(@RawRes int id, TypedValue value)
+            throws NotFoundException {
+        return mResourcesImpl.openRawResource(id, value);
+    }
+
+    /**
+     * Open a file descriptor for reading a raw resource.  This can only be used
+     * with resources whose value is the name of an asset files -- that is, it can be
+     * used to open drawable, sound, and raw resources; it will fail on string
+     * and color resources.
+     * 
+     * <p>This function only works for resources that are stored in the package
+     * as uncompressed data, which typically includes things like mp3 files
+     * and png images.
+     * 
+     * @param id The resource identifier to open, as generated by the aapt tool.
+     * 
+     * @return AssetFileDescriptor A new file descriptor you can use to read
+     * the resource.  This includes the file descriptor itself, as well as the
+     * offset and length of data where the resource appears in the file.  A
+     * null is returned if the file exists but is compressed.
+     *
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     * 
+     */
+    public AssetFileDescriptor openRawResourceFd(@RawRes int id)
+            throws NotFoundException {
+        final TypedValue value = obtainTempTypedValue();
+        try {
+            return mResourcesImpl.openRawResourceFd(id, value);
+        } finally {
+            releaseTempTypedValue(value);
+        }
+    }
+
+    /**
+     * Return the raw data associated with a particular resource ID.
+     * 
+     * @param id The desired resource identifier, as generated by the aapt
+     *           tool. This integer encodes the package, type, and resource
+     *           entry. The value 0 is an invalid identifier.
+     * @param outValue Object in which to place the resource data.
+     * @param resolveRefs If true, a resource that is a reference to another
+     *                    resource will be followed so that you receive the
+     *                    actual final resource data.  If false, the TypedValue
+     *                    will be filled in with the reference itself.
+     *
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     *
+     */
+    public void getValue(@AnyRes int id, TypedValue outValue, boolean resolveRefs)
+            throws NotFoundException {
+        mResourcesImpl.getValue(id, outValue, resolveRefs);
+    }
+
+    /**
+     * Get the raw value associated with a resource with associated density.
+     * 
+     * @param id resource identifier
+     * @param density density in DPI
+     * @param resolveRefs If true, a resource that is a reference to another
+     *            resource will be followed so that you receive the actual final
+     *            resource data. If false, the TypedValue will be filled in with
+     *            the reference itself.
+     * @throws NotFoundException Throws NotFoundException if the given ID does
+     *             not exist.
+     * @see #getValue(String, TypedValue, boolean)
+     */
+    public void getValueForDensity(@AnyRes int id, int density, TypedValue outValue,
+            boolean resolveRefs) throws NotFoundException {
+        mResourcesImpl.getValueForDensity(id, density, outValue, resolveRefs);
+    }
+
+    /**
+     * Return the raw data associated with a particular resource ID.
+     * See getIdentifier() for information on how names are mapped to resource
+     * IDs, and getString(int) for information on how string resources are
+     * retrieved.
+     * 
+     * <p>Note: use of this function is discouraged.  It is much more
+     * efficient to retrieve resources by identifier than by name.
+     * 
+     * @param name The name of the desired resource.  This is passed to
+     *             getIdentifier() with a default type of "string".
+     * @param outValue Object in which to place the resource data.
+     * @param resolveRefs If true, a resource that is a reference to another
+     *                    resource will be followed so that you receive the
+     *                    actual final resource data.  If false, the TypedValue
+     *                    will be filled in with the reference itself.
+     *
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     *
+     */
+    public void getValue(String name, TypedValue outValue, boolean resolveRefs)
+            throws NotFoundException {
+        mResourcesImpl.getValue(name, outValue, resolveRefs);
+    }
+
+
+    /**
+     * Returns the resource ID of the resource that was used to create this AttributeSet.
+     *
+     * @param set AttributeSet for which we want to find the source.
+     * @return The resource ID for the source that is backing the given AttributeSet or
+     * {@link Resources#ID_NULL} if the AttributeSet is {@code null}.
+     */
+    @AnyRes
+    public static int getAttributeSetSourceResId(@Nullable AttributeSet set) {
+        return ResourcesImpl.getAttributeSetSourceResId(set);
+    }
+
+    /**
+     * This class holds the current attribute values for a particular theme.
+     * In other words, a Theme is a set of values for resource attributes;
+     * these are used in conjunction with {@link TypedArray}
+     * to resolve the final value for an attribute.
+     * 
+     * <p>The Theme's attributes come into play in two ways: (1) a styled
+     * attribute can explicit reference a value in the theme through the
+     * "?themeAttribute" syntax; (2) if no value has been defined for a
+     * particular styled attribute, as a last resort we will try to find that
+     * attribute's value in the Theme.
+     * 
+     * <p>You will normally use the {@link #obtainStyledAttributes} APIs to
+     * retrieve XML attributes with style and theme information applied.
+     */
+    public final class Theme {
+        @UnsupportedAppUsage
+        private ResourcesImpl.ThemeImpl mThemeImpl;
+
+        private Theme() {
+        }
+
+        void setImpl(ResourcesImpl.ThemeImpl impl) {
+            mThemeImpl = impl;
+        }
+
+        /**
+         * Place new attribute values into the theme.  The style resource
+         * specified by <var>resid</var> will be retrieved from this Theme's
+         * resources, its values placed into the Theme object.
+         * 
+         * <p>The semantics of this function depends on the <var>force</var>
+         * argument:  If false, only values that are not already defined in
+         * the theme will be copied from the system resource; otherwise, if
+         * any of the style's attributes are already defined in the theme, the
+         * current values in the theme will be overwritten.
+         * 
+         * @param resId The resource ID of a style resource from which to
+         *              obtain attribute values.
+         * @param force If true, values in the style resource will always be
+         *              used in the theme; otherwise, they will only be used
+         *              if not already defined in the theme.
+         */
+        public void applyStyle(int resId, boolean force) {
+            mThemeImpl.applyStyle(resId, force);
+        }
+
+        /**
+         * Set this theme to hold the same contents as the theme
+         * <var>other</var>.  If both of these themes are from the same
+         * Resources object, they will be identical after this function
+         * returns.  If they are from different Resources, only the resources
+         * they have in common will be set in this theme.
+         * 
+         * @param other The existing Theme to copy from.
+         */
+        public void setTo(Theme other) {
+            mThemeImpl.setTo(other.mThemeImpl);
+        }
+
+        /**
+         * Return a TypedArray holding the values defined by
+         * <var>Theme</var> which are listed in <var>attrs</var>.
+         * 
+         * <p>Be sure to call {@link TypedArray#recycle() TypedArray.recycle()} when you are done
+         * with the array.
+         * 
+         * @param attrs The desired attributes. These attribute IDs must be sorted in ascending
+         *              order.
+         *
+         * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+         * 
+         * @return Returns a TypedArray holding an array of the attribute values.
+         * Be sure to call {@link TypedArray#recycle() TypedArray.recycle()}
+         * when done with it.
+         * 
+         * @see Resources#obtainAttributes
+         * @see #obtainStyledAttributes(int, int[])
+         * @see #obtainStyledAttributes(AttributeSet, int[], int, int)
+         */
+        @NonNull
+        public TypedArray obtainStyledAttributes(@NonNull @StyleableRes int[] attrs) {
+            return mThemeImpl.obtainStyledAttributes(this, null, attrs, 0, 0);
+        }
+
+        /**
+         * Return a TypedArray holding the values defined by the style
+         * resource <var>resid</var> which are listed in <var>attrs</var>.
+         * 
+         * <p>Be sure to call {@link TypedArray#recycle() TypedArray.recycle()} when you are done
+         * with the array.
+         * 
+         * @param resId The desired style resource.
+         * @param attrs The desired attributes in the style. These attribute IDs must be sorted in
+         *              ascending order.
+         * 
+         * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+         * 
+         * @return Returns a TypedArray holding an array of the attribute values.
+         * Be sure to call {@link TypedArray#recycle() TypedArray.recycle()}
+         * when done with it.
+         * 
+         * @see Resources#obtainAttributes
+         * @see #obtainStyledAttributes(int[])
+         * @see #obtainStyledAttributes(AttributeSet, int[], int, int)
+         */
+        @NonNull
+        public TypedArray obtainStyledAttributes(@StyleRes int resId,
+                @NonNull @StyleableRes int[] attrs)
+                throws NotFoundException {
+            return mThemeImpl.obtainStyledAttributes(this, null, attrs, 0, resId);
+        }
+
+        /**
+         * Return a TypedArray holding the attribute values in
+         * <var>set</var>
+         * that are listed in <var>attrs</var>.  In addition, if the given
+         * AttributeSet specifies a style class (through the "style" attribute),
+         * that style will be applied on top of the base attributes it defines.
+         * 
+         * <p>Be sure to call {@link TypedArray#recycle() TypedArray.recycle()} when you are done
+         * with the array.
+         * 
+         * <p>When determining the final value of a particular attribute, there
+         * are four inputs that come into play:</p>
+         * 
+         * <ol>
+         *     <li> Any attribute values in the given AttributeSet.
+         *     <li> The style resource specified in the AttributeSet (named
+         *     "style").
+         *     <li> The default style specified by <var>defStyleAttr</var> and
+         *     <var>defStyleRes</var>
+         *     <li> The base values in this theme.
+         * </ol>
+         * 
+         * <p>Each of these inputs is considered in-order, with the first listed
+         * taking precedence over the following ones.  In other words, if in the
+         * AttributeSet you have supplied <code>&lt;Button
+         * textColor="#ff000000"&gt;</code>, then the button's text will
+         * <em>always</em> be black, regardless of what is specified in any of
+         * the styles.
+         * 
+         * @param set The base set of attribute values.  May be null.
+         * @param attrs The desired attributes to be retrieved. These attribute IDs must be sorted
+         *              in ascending order.
+         * @param defStyleAttr An attribute in the current theme that contains a
+         *                     reference to a style resource that supplies
+         *                     defaults values for the TypedArray.  Can be
+         *                     0 to not look for defaults.
+         * @param defStyleRes A resource identifier of a style resource that
+         *                    supplies default values for the TypedArray,
+         *                    used only if defStyleAttr is 0 or can not be found
+         *                    in the theme.  Can be 0 to not look for defaults.
+         * 
+         * @return Returns a TypedArray holding an array of the attribute values.
+         * Be sure to call {@link TypedArray#recycle() TypedArray.recycle()}
+         * when done with it.
+         * 
+         * @see Resources#obtainAttributes
+         * @see #obtainStyledAttributes(int[])
+         * @see #obtainStyledAttributes(int, int[])
+         */
+        @NonNull
+        public TypedArray obtainStyledAttributes(@Nullable AttributeSet set,
+                @NonNull @StyleableRes int[] attrs, @AttrRes int defStyleAttr,
+                @StyleRes int defStyleRes) {
+            return mThemeImpl.obtainStyledAttributes(this, set, attrs, defStyleAttr, defStyleRes);
+        }
+
+        /**
+         * Retrieve the values for a set of attributes in the Theme. The
+         * contents of the typed array are ultimately filled in by
+         * {@link Resources#getValue}.
+         *
+         * @param values The base set of attribute values, must be equal in
+         *               length to {@code attrs}. All values must be of type
+         *               {@link TypedValue#TYPE_ATTRIBUTE}.
+         * @param attrs The desired attributes to be retrieved. These attribute IDs must be sorted
+         *              in ascending order.
+         * @return Returns a TypedArray holding an array of the attribute
+         *         values. Be sure to call {@link TypedArray#recycle()}
+         *         when done with it.
+         * @hide
+         */
+        @NonNull
+        @UnsupportedAppUsage
+        public TypedArray resolveAttributes(@NonNull int[] values, @NonNull int[] attrs) {
+            return mThemeImpl.resolveAttributes(this, values, attrs);
+        }
+
+        /**
+         * Retrieve the value of an attribute in the Theme.  The contents of
+         * <var>outValue</var> are ultimately filled in by
+         * {@link Resources#getValue}.
+         * 
+         * @param resid The resource identifier of the desired theme
+         *              attribute.
+         * @param outValue Filled in with the ultimate resource value supplied
+         *                 by the attribute.
+         * @param resolveRefs If true, resource references will be walked; if
+         *                    false, <var>outValue</var> may be a
+         *                    TYPE_REFERENCE.  In either case, it will never
+         *                    be a TYPE_ATTRIBUTE.
+         * 
+         * @return boolean Returns true if the attribute was found and
+         *         <var>outValue</var> is valid, else false.
+         */
+        public boolean resolveAttribute(int resid, TypedValue outValue, boolean resolveRefs) {
+            return mThemeImpl.resolveAttribute(resid, outValue, resolveRefs);
+        }
+
+        /**
+         * Gets all of the attribute ids associated with this {@link Theme}. For debugging only.
+         *
+         * @return The int array containing attribute ids associated with this {@link Theme}.
+         * @hide
+         */
+        public int[] getAllAttributes() {
+            return mThemeImpl.getAllAttributes();
+        }
+
+        /**
+         * Returns the resources to which this theme belongs.
+         *
+         * @return Resources to which this theme belongs.
+         */
+        public Resources getResources() {
+            return Resources.this;
+        }
+
+        /**
+         * Return a drawable object associated with a particular resource ID
+         * and styled for the Theme.
+         *
+         * @param id The desired resource identifier, as generated by the aapt
+         *           tool. This integer encodes the package, type, and resource
+         *           entry. The value 0 is an invalid identifier.
+         * @return Drawable An object that can be used to draw this resource.
+         * @throws NotFoundException Throws NotFoundException if the given ID
+         *         does not exist.
+         */
+        public Drawable getDrawable(@DrawableRes int id) throws NotFoundException {
+            return Resources.this.getDrawable(id, this);
+        }
+
+        /**
+         * Returns a bit mask of configuration changes that will impact this
+         * theme (and thus require completely reloading it).
+         *
+         * @return a bit mask of configuration changes, as defined by
+         *         {@link ActivityInfo}
+         * @see ActivityInfo
+         */
+        public @Config int getChangingConfigurations() {
+            return mThemeImpl.getChangingConfigurations();
+        }
+
+        /**
+         * Print contents of this theme out to the log.  For debugging only.
+         * 
+         * @param priority The log priority to use.
+         * @param tag The log tag to use.
+         * @param prefix Text to prefix each line printed.
+         */
+        public void dump(int priority, String tag, String prefix) {
+            mThemeImpl.dump(priority, tag, prefix);
+        }
+
+        // Needed by layoutlib.
+        /*package*/ long getNativeTheme() {
+            return mThemeImpl.getNativeTheme();
+        }
+
+        /*package*/ int getAppliedStyleResId() {
+            return mThemeImpl.getAppliedStyleResId();
+        }
+
+        /**
+         * @hide
+         */
+        public ThemeKey getKey() {
+            return mThemeImpl.getKey();
+        }
+
+        private String getResourceNameFromHexString(String hexString) {
+            return getResourceName(Integer.parseInt(hexString, 16));
+        }
+
+        /**
+         * Parses {@link #getKey()} and returns a String array that holds pairs of
+         * adjacent Theme data: resource name followed by whether or not it was
+         * forced, as specified by {@link #applyStyle(int, boolean)}.
+         *
+         * @hide
+         */
+        @ViewDebug.ExportedProperty(category = "theme", hasAdjacentMapping = true)
+        public String[] getTheme() {
+            return mThemeImpl.getTheme();
+        }
+
+        /** @hide */
+        public void encode(@NonNull ViewHierarchyEncoder encoder) {
+            encoder.beginObject(this);
+            final String[] properties = getTheme();
+            for (int i = 0; i < properties.length; i += 2) {
+                encoder.addProperty(properties[i], properties[i+1]);
+            }
+            encoder.endObject();
+        }
+
+        /**
+         * Rebases the theme against the parent Resource object's current
+         * configuration by re-applying the styles passed to
+         * {@link #applyStyle(int, boolean)}.
+         */
+        public void rebase() {
+            mThemeImpl.rebase();
+        }
+
+        /**
+         * Returns the resource ID for the style specified using {@code style="..."} in the
+         * {@link AttributeSet}'s backing XML element or {@link Resources#ID_NULL} otherwise if not
+         * specified or otherwise not applicable.
+         * <p>
+         * Each {@link android.view.View} can have an explicit style specified in the layout file.
+         * This style is used first during the {@link android.view.View} attribute resolution, then
+         * if an attribute is not defined there the resource system looks at default style and theme
+         * as fallbacks.
+         *
+         * @param set The base set of attribute values.
+         *
+         * @return The resource ID for the style specified using {@code style="..."} in the
+         *      {@link AttributeSet}'s backing XML element or {@link Resources#ID_NULL} otherwise
+         *      if not specified or otherwise not applicable.
+         */
+        @StyleRes
+        public int getExplicitStyle(@Nullable AttributeSet set) {
+            if (set == null) {
+                return ID_NULL;
+            }
+            int styleAttr = set.getStyleAttribute();
+            if (styleAttr == ID_NULL) {
+                return ID_NULL;
+            }
+            String styleAttrType = getResources().getResourceTypeName(styleAttr);
+            if ("attr".equals(styleAttrType)) {
+                TypedValue explicitStyle = new TypedValue();
+                boolean resolved = resolveAttribute(styleAttr, explicitStyle, true);
+                if (resolved) {
+                    return explicitStyle.resourceId;
+                }
+            } else if ("style".equals(styleAttrType)) {
+                return styleAttr;
+            }
+            return ID_NULL;
+        }
+
+        /**
+         * Returns the ordered list of resource ID that are considered when resolving attribute
+         * values when making an equivalent call to
+         * {@link #obtainStyledAttributes(AttributeSet, int[], int, int)} . The list will include
+         * a set of explicit styles ({@code explicitStyleRes} and it will include the default styles
+         * ({@code defStyleAttr} and {@code defStyleRes}).
+         *
+         * @param defStyleAttr An attribute in the current theme that contains a
+         *                     reference to a style resource that supplies
+         *                     defaults values for the TypedArray.  Can be
+         *                     0 to not look for defaults.
+         * @param defStyleRes A resource identifier of a style resource that
+         *                    supplies default values for the TypedArray,
+         *                    used only if defStyleAttr is 0 or can not be found
+         *                    in the theme.  Can be 0 to not look for defaults.
+         * @param explicitStyleRes A resource identifier of an explicit style resource.
+         * @return ordered list of resource ID that are considered when resolving attribute values.
+         */
+        @NonNull
+        public int[] getAttributeResolutionStack(@AttrRes int defStyleAttr,
+                @StyleRes int defStyleRes, @StyleRes int explicitStyleRes) {
+            int[] stack = mThemeImpl.getAttributeResolutionStack(
+                    defStyleAttr, defStyleRes, explicitStyleRes);
+            if (stack == null) {
+                return new int[0];
+            } else {
+                return stack;
+            }
+        }
+    }
+
+    static class ThemeKey implements Cloneable {
+        int[] mResId;
+        boolean[] mForce;
+        int mCount;
+
+        private int mHashCode = 0;
+
+        public void append(int resId, boolean force) {
+            if (mResId == null) {
+                mResId = new int[4];
+            }
+
+            if (mForce == null) {
+                mForce = new boolean[4];
+            }
+
+            mResId = GrowingArrayUtils.append(mResId, mCount, resId);
+            mForce = GrowingArrayUtils.append(mForce, mCount, force);
+            mCount++;
+
+            mHashCode = 31 * (31 * mHashCode + resId) + (force ? 1 : 0);
+        }
+
+        /**
+         * Sets up this key as a deep copy of another key.
+         *
+         * @param other the key to deep copy into this key
+         */
+        public void setTo(ThemeKey other) {
+            mResId = other.mResId == null ? null : other.mResId.clone();
+            mForce = other.mForce == null ? null : other.mForce.clone();
+            mCount = other.mCount;
+            mHashCode = other.mHashCode;
+        }
+
+        @Override
+        public int hashCode() {
+            return mHashCode;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+
+            if (o == null || getClass() != o.getClass() || hashCode() != o.hashCode()) {
+                return false;
+            }
+
+            final ThemeKey t = (ThemeKey) o;
+            if (mCount != t.mCount) {
+                return false;
+            }
+
+            final int N = mCount;
+            for (int i = 0; i < N; i++) {
+                if (mResId[i] != t.mResId[i] || mForce[i] != t.mForce[i]) {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+
+        /**
+         * @return a shallow copy of this key
+         */
+        @Override
+        public ThemeKey clone() {
+            final ThemeKey other = new ThemeKey();
+            other.mResId = mResId;
+            other.mForce = mForce;
+            other.mCount = mCount;
+            other.mHashCode = mHashCode;
+            return other;
+        }
+    }
+
+    /**
+     * Generate a new Theme object for this set of Resources.  It initially
+     * starts out empty.
+     *
+     * @return Theme The newly created Theme container.
+     */
+    public final Theme newTheme() {
+        Theme theme = new Theme();
+        theme.setImpl(mResourcesImpl.newThemeImpl());
+        synchronized (mThemeRefs) {
+            mThemeRefs.add(new WeakReference<>(theme));
+
+            // Clean up references to garbage collected themes
+            if (mThemeRefs.size() > mThemeRefsNextFlushSize) {
+                mThemeRefs.removeIf(ref -> ref.get() == null);
+                mThemeRefsNextFlushSize = Math.max(MIN_THEME_REFS_FLUSH_SIZE,
+                        2 * mThemeRefs.size());
+            }
+        }
+        return theme;
+    }
+
+    /**
+     * Retrieve a set of basic attribute values from an AttributeSet, not
+     * performing styling of them using a theme and/or style resources.
+     *
+     * @param set The current attribute values to retrieve.
+     * @param attrs The specific attributes to be retrieved. These attribute IDs must be sorted in
+     *              ascending order.
+     * @return Returns a TypedArray holding an array of the attribute values.
+     * Be sure to call {@link TypedArray#recycle() TypedArray.recycle()}
+     * when done with it.
+     * 
+     * @see Theme#obtainStyledAttributes(AttributeSet, int[], int, int)
+     */
+    public TypedArray obtainAttributes(AttributeSet set, @StyleableRes int[] attrs) {
+        int len = attrs.length;
+        TypedArray array = TypedArray.obtain(this, len);
+
+        // XXX note that for now we only work with compiled XML files.
+        // To support generic XML files we will need to manually parse
+        // out the attributes from the XML file (applying type information
+        // contained in the resources and such).
+        XmlBlock.Parser parser = (XmlBlock.Parser)set;
+        mResourcesImpl.getAssets().retrieveAttributes(parser, attrs, array.mData, array.mIndices);
+
+        array.mXml = parser;
+
+        return array;
+    }
+
+    /**
+     * Store the newly updated configuration.
+     *
+     * @deprecated See {@link android.content.Context#createConfigurationContext(Configuration)}.
+     */
+    @Deprecated
+    public void updateConfiguration(Configuration config, DisplayMetrics metrics) {
+        updateConfiguration(config, metrics, null);
+    }
+
+    /**
+     * @hide
+     */
+    public void updateConfiguration(Configuration config, DisplayMetrics metrics,
+                                    CompatibilityInfo compat) {
+        mResourcesImpl.updateConfiguration(config, metrics, compat);
+    }
+
+    /**
+     * Update the system resources configuration if they have previously
+     * been initialized.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static void updateSystemConfiguration(Configuration config, DisplayMetrics metrics,
+            CompatibilityInfo compat) {
+        if (mSystem != null) {
+            mSystem.updateConfiguration(config, metrics, compat);
+            //Log.i(TAG, "Updated system resources " + mSystem
+            //        + ": " + mSystem.getConfiguration());
+        }
+    }
+
+    /**
+     * Return the current display metrics that are in effect for this resource
+     * object. The returned object should be treated as read-only.
+     *
+     * <p>Note that the reported value may be different than the window this application is
+     * interested in.</p>
+     *
+     * <p>Best practices are to obtain metrics from {@link WindowManager#getCurrentWindowMetrics()}
+     * for window bounds, {@link Display#getRealMetrics(DisplayMetrics)} for display bounds and
+     * obtain density from {@link Configuration#densityDpi}. The value obtained from this API may be
+     * wrong if the {@link Resources} is from the context which is different than the window is
+     * attached such as {@link Application#getResources()}.
+     * <p/>
+     *
+     * @return The resource's current display metrics.
+     */
+    public DisplayMetrics getDisplayMetrics() {
+        return mResourcesImpl.getDisplayMetrics();
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage
+    public DisplayAdjustments getDisplayAdjustments() {
+        return mResourcesImpl.getDisplayAdjustments();
+    }
+
+    /**
+     * Return the current configuration that is in effect for this resource 
+     * object.  The returned object should be treated as read-only.
+     * 
+     * @return The resource's current configuration. 
+     */
+    public Configuration getConfiguration() {
+        return mResourcesImpl.getConfiguration();
+    }
+
+    /** @hide */
+    public Configuration[] getSizeConfigurations() {
+        return mResourcesImpl.getSizeConfigurations();
+    }
+
+    /**
+     * Return the compatibility mode information for the application.
+     * The returned object should be treated as read-only.
+     * 
+     * @return compatibility info.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public CompatibilityInfo getCompatibilityInfo() {
+        return mResourcesImpl.getCompatibilityInfo();
+    }
+
+    /**
+     * This is just for testing.
+     * @hide
+     */
+    @VisibleForTesting
+    @UnsupportedAppUsage
+    public void setCompatibilityInfo(CompatibilityInfo ci) {
+        if (ci != null) {
+            mResourcesImpl.updateConfiguration(null, null, ci);
+        }
+    }
+    
+    /**
+     * Return a resource identifier for the given resource name.  A fully
+     * qualified resource name is of the form "package:type/entry".  The first
+     * two components (package and type) are optional if defType and
+     * defPackage, respectively, are specified here.
+     * 
+     * <p>Note: use of this function is discouraged.  It is much more
+     * efficient to retrieve resources by identifier than by name.
+     * 
+     * @param name The name of the desired resource.
+     * @param defType Optional default resource type to find, if "type/" is
+     *                not included in the name.  Can be null to require an
+     *                explicit type.
+     * @param defPackage Optional default package to find, if "package:" is
+     *                   not included in the name.  Can be null to require an
+     *                   explicit package.
+     * 
+     * @return int The associated resource identifier.  Returns 0 if no such
+     *         resource was found.  (0 is not a valid resource ID.)
+     */
+    public int getIdentifier(String name, String defType, String defPackage) {
+        return mResourcesImpl.getIdentifier(name, defType, defPackage);
+    }
+
+    /**
+     * Return true if given resource identifier includes a package.
+     *
+     * @hide
+     */
+    public static boolean resourceHasPackage(@AnyRes int resid) {
+        return (resid >>> 24) != 0;
+    }
+
+    /**
+     * Return the full name for a given resource identifier.  This name is
+     * a single string of the form "package:type/entry".
+     * 
+     * @param resid The resource identifier whose name is to be retrieved.
+     * 
+     * @return A string holding the name of the resource.
+     * 
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     * 
+     * @see #getResourcePackageName
+     * @see #getResourceTypeName
+     * @see #getResourceEntryName
+     */
+    public String getResourceName(@AnyRes int resid) throws NotFoundException {
+        return mResourcesImpl.getResourceName(resid);
+    }
+    
+    /**
+     * Return the package name for a given resource identifier.
+     * 
+     * @param resid The resource identifier whose package name is to be
+     * retrieved.
+     * 
+     * @return A string holding the package name of the resource.
+     * 
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     * 
+     * @see #getResourceName
+     */
+    public String getResourcePackageName(@AnyRes int resid) throws NotFoundException {
+        return mResourcesImpl.getResourcePackageName(resid);
+    }
+    
+    /**
+     * Return the type name for a given resource identifier.
+     * 
+     * @param resid The resource identifier whose type name is to be
+     * retrieved.
+     * 
+     * @return A string holding the type name of the resource.
+     * 
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     * 
+     * @see #getResourceName
+     */
+    public String getResourceTypeName(@AnyRes int resid) throws NotFoundException {
+        return mResourcesImpl.getResourceTypeName(resid);
+    }
+
+    /**
+     * Return the entry name for a given resource identifier.
+     *
+     * @param resid The resource identifier whose entry name is to be
+     * retrieved.
+     *
+     * @return A string holding the entry name of the resource.
+     *
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     *
+     * @see #getResourceName
+     */
+    public String getResourceEntryName(@AnyRes int resid) throws NotFoundException {
+        return mResourcesImpl.getResourceEntryName(resid);
+    }
+
+    /**
+     * Return formatted log of the last retrieved resource's resolution path.
+     *
+     * @return A string holding a formatted log of the steps taken to resolve the last resource.
+     *
+     * @throws NotFoundException Throws NotFoundException if there hasn't been a resource
+     * resolved yet.
+     *
+     * @hide
+     */
+    public String getLastResourceResolution() throws NotFoundException {
+        return mResourcesImpl.getLastResourceResolution();
+    }
+    
+    /**
+     * Parse a series of {@link android.R.styleable#Extra &lt;extra&gt;} tags from
+     * an XML file.  You call this when you are at the parent tag of the
+     * extra tags, and it will return once all of the child tags have been parsed.
+     * This will call {@link #parseBundleExtra} for each extra tag encountered.
+     * 
+     * @param parser The parser from which to retrieve the extras.
+     * @param outBundle A Bundle in which to place all parsed extras.
+     * @throws XmlPullParserException
+     * @throws IOException
+     */
+    public void parseBundleExtras(XmlResourceParser parser, Bundle outBundle)
+            throws XmlPullParserException, IOException {
+        int outerDepth = parser.getDepth();
+        int type;
+        while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
+               && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+            
+            String nodeName = parser.getName();
+            if (nodeName.equals("extra")) {
+                parseBundleExtra("extra", parser, outBundle);
+                XmlUtils.skipCurrentTag(parser);
+
+            } else {
+                XmlUtils.skipCurrentTag(parser);
+            }
+        }        
+    }
+    
+    /**
+     * Parse a name/value pair out of an XML tag holding that data.  The
+     * AttributeSet must be holding the data defined by
+     * {@link android.R.styleable#Extra}.  The following value types are supported:
+     * <ul>
+     * <li> {@link TypedValue#TYPE_STRING}:
+     * {@link Bundle#putCharSequence Bundle.putCharSequence()}
+     * <li> {@link TypedValue#TYPE_INT_BOOLEAN}:
+     * {@link Bundle#putCharSequence Bundle.putBoolean()}
+     * <li> {@link TypedValue#TYPE_FIRST_INT}-{@link TypedValue#TYPE_LAST_INT}:
+     * {@link Bundle#putCharSequence Bundle.putBoolean()}
+     * <li> {@link TypedValue#TYPE_FLOAT}:
+     * {@link Bundle#putCharSequence Bundle.putFloat()}
+     * </ul>
+     * 
+     * @param tagName The name of the tag these attributes come from; this is
+     * only used for reporting error messages.
+     * @param attrs The attributes from which to retrieve the name/value pair.
+     * @param outBundle The Bundle in which to place the parsed value.
+     * @throws XmlPullParserException If the attributes are not valid.
+     */
+    public void parseBundleExtra(String tagName, AttributeSet attrs,
+            Bundle outBundle) throws XmlPullParserException {
+        TypedArray sa = obtainAttributes(attrs,
+                com.android.internal.R.styleable.Extra);
+
+        String name = sa.getString(
+                com.android.internal.R.styleable.Extra_name);
+        if (name == null) {
+            sa.recycle();
+            throw new XmlPullParserException("<" + tagName
+                    + "> requires an android:name attribute at "
+                    + attrs.getPositionDescription());
+        }
+
+        TypedValue v = sa.peekValue(
+                com.android.internal.R.styleable.Extra_value);
+        if (v != null) {
+            if (v.type == TypedValue.TYPE_STRING) {
+                CharSequence cs = v.coerceToString();
+                outBundle.putCharSequence(name, cs);
+            } else if (v.type == TypedValue.TYPE_INT_BOOLEAN) {
+                outBundle.putBoolean(name, v.data != 0);
+            } else if (v.type >= TypedValue.TYPE_FIRST_INT
+                    && v.type <= TypedValue.TYPE_LAST_INT) {
+                outBundle.putInt(name, v.data);
+            } else if (v.type == TypedValue.TYPE_FLOAT) {
+                outBundle.putFloat(name, v.getFloat());
+            } else {
+                sa.recycle();
+                throw new XmlPullParserException("<" + tagName
+                        + "> only supports string, integer, float, color, and boolean at "
+                        + attrs.getPositionDescription());
+            }
+        } else {
+            sa.recycle();
+            throw new XmlPullParserException("<" + tagName
+                    + "> requires an android:value or android:resource attribute at "
+                    + attrs.getPositionDescription());
+        }
+
+        sa.recycle();
+    }
+    
+    /**
+     * Retrieve underlying AssetManager storage for these resources.
+     */
+    public final AssetManager getAssets() {
+        return mResourcesImpl.getAssets();
+    }
+
+    /**
+     * Call this to remove all cached loaded layout resources from the
+     * Resources object.  Only intended for use with performance testing
+     * tools.
+     */
+    public final void flushLayoutCache() {
+        mResourcesImpl.flushLayoutCache();
+    }
+
+    /**
+     * Start preloading of resource data using this Resources object.  Only
+     * for use by the zygote process for loading common system resources.
+     * {@hide}
+     */
+    public final void startPreloading() {
+        mResourcesImpl.startPreloading();
+    }
+    
+    /**
+     * Called by zygote when it is done preloading resources, to change back
+     * to normal Resources operation.
+     */
+    public final void finishPreloading() {
+        mResourcesImpl.finishPreloading();
+    }
+
+    /**
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public LongSparseArray<ConstantState> getPreloadedDrawables() {
+        return mResourcesImpl.getPreloadedDrawables();
+    }
+
+    /**
+     * Loads an XML parser for the specified file.
+     *
+     * @param id the resource identifier for the file
+     * @param type the type of resource (used for logging)
+     * @return a parser for the specified XML file
+     * @throws NotFoundException if the file could not be loaded
+     */
+    @NonNull
+    @UnsupportedAppUsage
+    XmlResourceParser loadXmlResourceParser(@AnyRes int id, @NonNull String type)
+            throws NotFoundException {
+        final TypedValue value = obtainTempTypedValue();
+        try {
+            final ResourcesImpl impl = mResourcesImpl;
+            impl.getValue(id, value, true);
+            if (value.type == TypedValue.TYPE_STRING) {
+                return loadXmlResourceParser(value.string.toString(), id,
+                        value.assetCookie, type);
+            }
+            throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id)
+                    + " type #0x" + Integer.toHexString(value.type) + " is not valid");
+        } finally {
+            releaseTempTypedValue(value);
+        }
+    }
+
+    /**
+     * Loads an XML parser for the specified file.
+     *
+     * @param file the path for the XML file to parse
+     * @param id the resource identifier for the file
+     * @param assetCookie the asset cookie for the file
+     * @param type the type of resource (used for logging)
+     * @return a parser for the specified XML file
+     * @throws NotFoundException if the file could not be loaded
+     */
+    @NonNull
+    @UnsupportedAppUsage
+    XmlResourceParser loadXmlResourceParser(String file, int id, int assetCookie,
+                                            String type) throws NotFoundException {
+        return mResourcesImpl.loadXmlResourceParser(file, id, assetCookie, type);
+    }
+
+    /**
+     * Called by ConfigurationBoundResourceCacheTest.
+     * @hide
+     */
+    @VisibleForTesting
+    public int calcConfigChanges(Configuration config) {
+        return mResourcesImpl.calcConfigChanges(config);
+    }
+
+    /**
+     * Obtains styled attributes from the theme, if available, or unstyled
+     * resources if the theme is null.
+     *
+     * @hide
+     */
+    public static TypedArray obtainAttributes(
+            Resources res, Theme theme, AttributeSet set, int[] attrs) {
+        if (theme == null) {
+            return res.obtainAttributes(set, attrs);
+        }
+        return theme.obtainStyledAttributes(set, attrs, 0, 0);
+    }
+
+    private void checkCallbacksRegistered() {
+        if (mCallbacks == null) {
+            // Fallback to updating the underlying AssetManager if the Resources is not associated
+            // with a ResourcesManager.
+            mCallbacks = new AssetManagerUpdateHandler();
+        }
+    }
+
+    /**
+     * Retrieves the list of loaders.
+     *
+     * <p>Loaders are listed in increasing precedence order. A loader will override the resources
+     * and assets of loaders listed before itself.
+     * @hide
+     */
+    @NonNull
+    public List<ResourcesLoader> getLoaders() {
+        return mResourcesImpl.getAssets().getLoaders();
+    }
+
+    /**
+     * Adds a loader to the list of loaders. If the loader is already present in the list, the list
+     * will not be modified.
+     *
+     * <p>This should only be called from the UI thread to avoid lock contention when propagating
+     * loader changes.
+     *
+     * @param loaders the loaders to add
+     */
+    public void addLoaders(@NonNull ResourcesLoader... loaders) {
+        synchronized (mUpdateLock) {
+            checkCallbacksRegistered();
+            final List<ResourcesLoader> newLoaders =
+                    new ArrayList<>(mResourcesImpl.getAssets().getLoaders());
+            final ArraySet<ResourcesLoader> loaderSet = new ArraySet<>(newLoaders);
+
+            for (int i = 0; i < loaders.length; i++) {
+                final ResourcesLoader loader = loaders[i];
+                if (!loaderSet.contains(loader)) {
+                    newLoaders.add(loader);
+                }
+            }
+
+            if (loaderSet.size() == newLoaders.size()) {
+                return;
+            }
+
+            mCallbacks.onLoadersChanged(this, newLoaders);
+            for (int i = loaderSet.size(), n = newLoaders.size(); i < n; i++) {
+                newLoaders.get(i).registerOnProvidersChangedCallback(this, mCallbacks);
+            }
+        }
+    }
+
+    /**
+     * Removes loaders from the list of loaders. If the loader is not present in the list, the list
+     * will not be modified.
+     *
+     * <p>This should only be called from the UI thread to avoid lock contention when propagating
+     * loader changes.
+     *
+     * @param loaders the loaders to remove
+     */
+    public void removeLoaders(@NonNull ResourcesLoader... loaders) {
+        synchronized (mUpdateLock) {
+            checkCallbacksRegistered();
+            final ArraySet<ResourcesLoader> removedLoaders = new ArraySet<>(loaders);
+            final List<ResourcesLoader> newLoaders = new ArrayList<>();
+            final List<ResourcesLoader> oldLoaders = mResourcesImpl.getAssets().getLoaders();
+
+            for (int i = 0, n = oldLoaders.size(); i < n; i++) {
+                final ResourcesLoader loader = oldLoaders.get(i);
+                if (!removedLoaders.contains(loader)) {
+                    newLoaders.add(loader);
+                }
+            }
+
+            if (oldLoaders.size() == newLoaders.size()) {
+                return;
+            }
+
+            mCallbacks.onLoadersChanged(this, newLoaders);
+            for (int i = 0; i < loaders.length; i++) {
+                loaders[i].unregisterOnProvidersChangedCallback(this);
+            }
+        }
+    }
+
+    /**
+     * Removes all {@link ResourcesLoader ResourcesLoader(s)}.
+     *
+     * <p>This should only be called from the UI thread to avoid lock contention when propagating
+     * loader changes.
+     * @hide
+     */
+    @VisibleForTesting
+    public void clearLoaders() {
+        synchronized (mUpdateLock) {
+            checkCallbacksRegistered();
+            final List<ResourcesLoader> newLoaders = Collections.emptyList();
+            final List<ResourcesLoader> oldLoaders = mResourcesImpl.getAssets().getLoaders();
+            mCallbacks.onLoadersChanged(this, newLoaders);
+            for (ResourcesLoader loader : oldLoaders) {
+                loader.unregisterOnProvidersChangedCallback(this);
+            }
+        }
+    }
+}
diff --git a/android/content/res/ResourcesImpl.java b/android/content/res/ResourcesImpl.java
new file mode 100644
index 0000000..f40d60d
--- /dev/null
+++ b/android/content/res/ResourcesImpl.java
@@ -0,0 +1,1556 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.content.res;
+
+import static android.content.res.Resources.ID_NULL;
+
+import android.animation.Animator;
+import android.animation.StateListAnimator;
+import android.annotation.AnyRes;
+import android.annotation.AttrRes;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.PluralsRes;
+import android.annotation.RawRes;
+import android.annotation.StyleRes;
+import android.annotation.StyleableRes;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ActivityInfo.Config;
+import android.content.res.AssetManager.AssetInputStream;
+import android.content.res.Configuration.NativeConfig;
+import android.content.res.Resources.NotFoundException;
+import android.graphics.Bitmap;
+import android.graphics.ImageDecoder;
+import android.graphics.Typeface;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.ColorStateListDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.DrawableContainer;
+import android.icu.text.PluralRules;
+import android.os.Build;
+import android.os.LocaleList;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.os.Trace;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.util.LongSparseArray;
+import android.util.Slog;
+import android.util.TypedValue;
+import android.util.Xml;
+import android.view.DisplayAdjustments;
+
+import com.android.internal.util.GrowingArrayUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.Locale;
+
+/**
+ * The implementation of Resource access. This class contains the AssetManager and all caches
+ * associated with it.
+ *
+ * {@link Resources} is just a thing wrapper around this class. When a configuration change
+ * occurs, clients can retain the same {@link Resources} reference because the underlying
+ * {@link ResourcesImpl} object will be updated or re-created.
+ *
+ * @hide
+ */
+public class ResourcesImpl {
+    static final String TAG = "Resources";
+
+    private static final boolean DEBUG_LOAD = false;
+    private static final boolean DEBUG_CONFIG = false;
+
+    static final String TAG_PRELOAD = TAG + ".preload";
+
+    @UnsupportedAppUsage
+    private static final boolean TRACE_FOR_PRELOAD = false; // Do we still need it?
+    @UnsupportedAppUsage
+    private static final boolean TRACE_FOR_MISS_PRELOAD = false; // Do we still need it?
+
+    public static final boolean TRACE_FOR_DETAILED_PRELOAD =
+            SystemProperties.getBoolean("debug.trace_resource_preload", false);
+
+    /** Used only when TRACE_FOR_DETAILED_PRELOAD is true. */
+    private static int sPreloadTracingNumLoadedDrawables;
+    private long mPreloadTracingPreloadStartTime;
+    private long mPreloadTracingStartBitmapSize;
+    private long mPreloadTracingStartBitmapCount;
+
+    private static final int ID_OTHER = 0x01000004;
+
+    private static final Object sSync = new Object();
+
+    private static boolean sPreloaded;
+    @UnsupportedAppUsage
+    private boolean mPreloading;
+
+    // Information about preloaded resources.  Note that they are not
+    // protected by a lock, because while preloading in zygote we are all
+    // single-threaded, and after that these are immutable.
+    @UnsupportedAppUsage
+    private static final LongSparseArray<Drawable.ConstantState>[] sPreloadedDrawables;
+    @UnsupportedAppUsage
+    private static final LongSparseArray<Drawable.ConstantState> sPreloadedColorDrawables
+            = new LongSparseArray<>();
+    @UnsupportedAppUsage
+    private static final LongSparseArray<android.content.res.ConstantState<ComplexColor>>
+            sPreloadedComplexColors = new LongSparseArray<>();
+
+    /** Lock object used to protect access to caches and configuration. */
+    @UnsupportedAppUsage
+    private final Object mAccessLock = new Object();
+
+    // These are protected by mAccessLock.
+    private final Configuration mTmpConfig = new Configuration();
+    @UnsupportedAppUsage
+    private final DrawableCache mDrawableCache = new DrawableCache();
+    @UnsupportedAppUsage
+    private final DrawableCache mColorDrawableCache = new DrawableCache();
+    private final ConfigurationBoundResourceCache<ComplexColor> mComplexColorCache =
+            new ConfigurationBoundResourceCache<>();
+    @UnsupportedAppUsage
+    private final ConfigurationBoundResourceCache<Animator> mAnimatorCache =
+            new ConfigurationBoundResourceCache<>();
+    @UnsupportedAppUsage
+    private final ConfigurationBoundResourceCache<StateListAnimator> mStateListAnimatorCache =
+            new ConfigurationBoundResourceCache<>();
+
+    // A stack of all the resourceIds already referenced when parsing a resource. This is used to
+    // detect circular references in the xml.
+    // Using a ThreadLocal variable ensures that we have different stacks for multiple parallel
+    // calls to ResourcesImpl
+    private final ThreadLocal<LookupStack> mLookupStack =
+            ThreadLocal.withInitial(() -> new LookupStack());
+
+    /** Size of the cyclical cache used to map XML files to blocks. */
+    private static final int XML_BLOCK_CACHE_SIZE = 4;
+
+    // Cyclical cache used for recently-accessed XML files.
+    private int mLastCachedXmlBlockIndex = -1;
+    private final int[] mCachedXmlBlockCookies = new int[XML_BLOCK_CACHE_SIZE];
+    private final String[] mCachedXmlBlockFiles = new String[XML_BLOCK_CACHE_SIZE];
+    private final XmlBlock[] mCachedXmlBlocks = new XmlBlock[XML_BLOCK_CACHE_SIZE];
+
+
+    @UnsupportedAppUsage
+    final AssetManager mAssets;
+    private final DisplayMetrics mMetrics = new DisplayMetrics();
+    private final DisplayAdjustments mDisplayAdjustments;
+
+    private PluralRules mPluralRule;
+
+    @UnsupportedAppUsage
+    private final Configuration mConfiguration = new Configuration();
+
+    static {
+        sPreloadedDrawables = new LongSparseArray[2];
+        sPreloadedDrawables[0] = new LongSparseArray<>();
+        sPreloadedDrawables[1] = new LongSparseArray<>();
+    }
+
+    /**
+     * Creates a new ResourcesImpl object with CompatibilityInfo.
+     *
+     * @param assets Previously created AssetManager.
+     * @param metrics Current display metrics to consider when
+     *                selecting/computing resource values.
+     * @param config Desired device configuration to consider when
+     *               selecting/computing resource values (optional).
+     * @param displayAdjustments this resource's Display override and compatibility info.
+     *                           Must not be null.
+     */
+    @UnsupportedAppUsage
+    public ResourcesImpl(@NonNull AssetManager assets, @Nullable DisplayMetrics metrics,
+            @Nullable Configuration config, @NonNull DisplayAdjustments displayAdjustments) {
+        mAssets = assets;
+        mMetrics.setToDefaults();
+        mDisplayAdjustments = displayAdjustments;
+        mConfiguration.setToDefaults();
+        updateConfiguration(config, metrics, displayAdjustments.getCompatibilityInfo());
+    }
+
+    public DisplayAdjustments getDisplayAdjustments() {
+        return mDisplayAdjustments;
+    }
+
+    @UnsupportedAppUsage
+    public AssetManager getAssets() {
+        return mAssets;
+    }
+
+    @UnsupportedAppUsage
+    DisplayMetrics getDisplayMetrics() {
+        if (DEBUG_CONFIG) Slog.v(TAG, "Returning DisplayMetrics: " + mMetrics.widthPixels
+                + "x" + mMetrics.heightPixels + " " + mMetrics.density);
+        return mMetrics;
+    }
+
+    Configuration getConfiguration() {
+        return mConfiguration;
+    }
+
+    Configuration[] getSizeConfigurations() {
+        return mAssets.getSizeConfigurations();
+    }
+
+    CompatibilityInfo getCompatibilityInfo() {
+        return mDisplayAdjustments.getCompatibilityInfo();
+    }
+
+    private PluralRules getPluralRule() {
+        synchronized (sSync) {
+            if (mPluralRule == null) {
+                mPluralRule = PluralRules.forLocale(mConfiguration.getLocales().get(0));
+            }
+            return mPluralRule;
+        }
+    }
+
+    @UnsupportedAppUsage
+    void getValue(@AnyRes int id, TypedValue outValue, boolean resolveRefs)
+            throws NotFoundException {
+        boolean found = mAssets.getResourceValue(id, 0, outValue, resolveRefs);
+        if (found) {
+            return;
+        }
+        throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id));
+    }
+
+    void getValueForDensity(@AnyRes int id, int density, TypedValue outValue,
+            boolean resolveRefs) throws NotFoundException {
+        boolean found = mAssets.getResourceValue(id, density, outValue, resolveRefs);
+        if (found) {
+            return;
+        }
+        throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id));
+    }
+
+    void getValue(String name, TypedValue outValue, boolean resolveRefs)
+            throws NotFoundException {
+        int id = getIdentifier(name, "string", null);
+        if (id != 0) {
+            getValue(id, outValue, resolveRefs);
+            return;
+        }
+        throw new NotFoundException("String resource name " + name);
+    }
+
+    int getIdentifier(String name, String defType, String defPackage) {
+        if (name == null) {
+            throw new NullPointerException("name is null");
+        }
+        try {
+            return Integer.parseInt(name);
+        } catch (Exception e) {
+            // Ignore
+        }
+        return mAssets.getResourceIdentifier(name, defType, defPackage);
+    }
+
+    @NonNull
+    String getResourceName(@AnyRes int resid) throws NotFoundException {
+        String str = mAssets.getResourceName(resid);
+        if (str != null) return str;
+        throw new NotFoundException("Unable to find resource ID #0x"
+                + Integer.toHexString(resid));
+    }
+
+    @NonNull
+    String getResourcePackageName(@AnyRes int resid) throws NotFoundException {
+        String str = mAssets.getResourcePackageName(resid);
+        if (str != null) return str;
+        throw new NotFoundException("Unable to find resource ID #0x"
+                + Integer.toHexString(resid));
+    }
+
+    @NonNull
+    String getResourceTypeName(@AnyRes int resid) throws NotFoundException {
+        String str = mAssets.getResourceTypeName(resid);
+        if (str != null) return str;
+        throw new NotFoundException("Unable to find resource ID #0x"
+                + Integer.toHexString(resid));
+    }
+
+    @NonNull
+    String getResourceEntryName(@AnyRes int resid) throws NotFoundException {
+        String str = mAssets.getResourceEntryName(resid);
+        if (str != null) return str;
+        throw new NotFoundException("Unable to find resource ID #0x"
+                + Integer.toHexString(resid));
+    }
+
+    @NonNull
+    String getLastResourceResolution() throws NotFoundException {
+        String str = mAssets.getLastResourceResolution();
+        if (str != null) return str;
+        throw new NotFoundException("Associated AssetManager hasn't resolved a resource");
+    }
+
+    @NonNull
+    CharSequence getQuantityText(@PluralsRes int id, int quantity) throws NotFoundException {
+        PluralRules rule = getPluralRule();
+        CharSequence res = mAssets.getResourceBagText(id,
+                attrForQuantityCode(rule.select(quantity)));
+        if (res != null) {
+            return res;
+        }
+        res = mAssets.getResourceBagText(id, ID_OTHER);
+        if (res != null) {
+            return res;
+        }
+        throw new NotFoundException("Plural resource ID #0x" + Integer.toHexString(id)
+                + " quantity=" + quantity
+                + " item=" + rule.select(quantity));
+    }
+
+    private static int attrForQuantityCode(String quantityCode) {
+        switch (quantityCode) {
+            case PluralRules.KEYWORD_ZERO: return 0x01000005;
+            case PluralRules.KEYWORD_ONE:  return 0x01000006;
+            case PluralRules.KEYWORD_TWO:  return 0x01000007;
+            case PluralRules.KEYWORD_FEW:  return 0x01000008;
+            case PluralRules.KEYWORD_MANY: return 0x01000009;
+            default:                       return ID_OTHER;
+        }
+    }
+
+    @NonNull
+    AssetFileDescriptor openRawResourceFd(@RawRes int id, TypedValue tempValue)
+            throws NotFoundException {
+        getValue(id, tempValue, true);
+        try {
+            return mAssets.openNonAssetFd(tempValue.assetCookie, tempValue.string.toString());
+        } catch (Exception e) {
+            throw new NotFoundException("File " + tempValue.string.toString() + " from "
+                    + "resource ID #0x" + Integer.toHexString(id), e);
+        }
+    }
+
+    @NonNull
+    InputStream openRawResource(@RawRes int id, TypedValue value) throws NotFoundException {
+        getValue(id, value, true);
+        try {
+            return mAssets.openNonAsset(value.assetCookie, value.string.toString(),
+                    AssetManager.ACCESS_STREAMING);
+        } catch (Exception e) {
+            // Note: value.string might be null
+            NotFoundException rnf = new NotFoundException("File "
+                    + (value.string == null ? "(null)" : value.string.toString())
+                    + " from resource ID #0x" + Integer.toHexString(id));
+            rnf.initCause(e);
+            throw rnf;
+        }
+    }
+
+    ConfigurationBoundResourceCache<Animator> getAnimatorCache() {
+        return mAnimatorCache;
+    }
+
+    ConfigurationBoundResourceCache<StateListAnimator> getStateListAnimatorCache() {
+        return mStateListAnimatorCache;
+    }
+
+    public void updateConfiguration(Configuration config, DisplayMetrics metrics,
+                                    CompatibilityInfo compat) {
+        Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "ResourcesImpl#updateConfiguration");
+        try {
+            synchronized (mAccessLock) {
+                if (DEBUG_CONFIG) {
+                    Slog.i(TAG, "**** Updating config of " + this + ": old config is "
+                            + mConfiguration + " old compat is "
+                            + mDisplayAdjustments.getCompatibilityInfo());
+                    Slog.i(TAG, "**** Updating config of " + this + ": new config is "
+                            + config + " new compat is " + compat);
+                }
+                if (compat != null) {
+                    mDisplayAdjustments.setCompatibilityInfo(compat);
+                }
+                if (metrics != null) {
+                    mMetrics.setTo(metrics);
+                }
+                // NOTE: We should re-arrange this code to create a Display
+                // with the CompatibilityInfo that is used everywhere we deal
+                // with the display in relation to this app, rather than
+                // doing the conversion here.  This impl should be okay because
+                // we make sure to return a compatible display in the places
+                // where there are public APIs to retrieve the display...  but
+                // it would be cleaner and more maintainable to just be
+                // consistently dealing with a compatible display everywhere in
+                // the framework.
+                mDisplayAdjustments.getCompatibilityInfo().applyToDisplayMetrics(mMetrics);
+
+                final @Config int configChanges = calcConfigChanges(config);
+
+                // If even after the update there are no Locales set, grab the default locales.
+                LocaleList locales = mConfiguration.getLocales();
+                if (locales.isEmpty()) {
+                    locales = LocaleList.getDefault();
+                    mConfiguration.setLocales(locales);
+                }
+
+                if ((configChanges & ActivityInfo.CONFIG_LOCALE) != 0) {
+                    if (locales.size() > 1) {
+                        // The LocaleList has changed. We must query the AssetManager's available
+                        // Locales and figure out the best matching Locale in the new LocaleList.
+                        String[] availableLocales = mAssets.getNonSystemLocales();
+                        if (LocaleList.isPseudoLocalesOnly(availableLocales)) {
+                            // No app defined locales, so grab the system locales.
+                            availableLocales = mAssets.getLocales();
+                            if (LocaleList.isPseudoLocalesOnly(availableLocales)) {
+                                availableLocales = null;
+                            }
+                        }
+
+                        if (availableLocales != null) {
+                            final Locale bestLocale = locales.getFirstMatchWithEnglishSupported(
+                                    availableLocales);
+                            if (bestLocale != null && bestLocale != locales.get(0)) {
+                                mConfiguration.setLocales(new LocaleList(bestLocale, locales));
+                            }
+                        }
+                    }
+                }
+
+                if (mConfiguration.densityDpi != Configuration.DENSITY_DPI_UNDEFINED) {
+                    mMetrics.densityDpi = mConfiguration.densityDpi;
+                    mMetrics.density =
+                            mConfiguration.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
+                }
+
+                // Protect against an unset fontScale.
+                mMetrics.scaledDensity = mMetrics.density *
+                        (mConfiguration.fontScale != 0 ? mConfiguration.fontScale : 1.0f);
+
+                final int width, height;
+                if (mMetrics.widthPixels >= mMetrics.heightPixels) {
+                    width = mMetrics.widthPixels;
+                    height = mMetrics.heightPixels;
+                } else {
+                    //noinspection SuspiciousNameCombination
+                    width = mMetrics.heightPixels;
+                    //noinspection SuspiciousNameCombination
+                    height = mMetrics.widthPixels;
+                }
+
+                final int keyboardHidden;
+                if (mConfiguration.keyboardHidden == Configuration.KEYBOARDHIDDEN_NO
+                        && mConfiguration.hardKeyboardHidden
+                        == Configuration.HARDKEYBOARDHIDDEN_YES) {
+                    keyboardHidden = Configuration.KEYBOARDHIDDEN_SOFT;
+                } else {
+                    keyboardHidden = mConfiguration.keyboardHidden;
+                }
+
+                mAssets.setConfiguration(mConfiguration.mcc, mConfiguration.mnc,
+                        adjustLanguageTag(mConfiguration.getLocales().get(0).toLanguageTag()),
+                        mConfiguration.orientation,
+                        mConfiguration.touchscreen,
+                        mConfiguration.densityDpi, mConfiguration.keyboard,
+                        keyboardHidden, mConfiguration.navigation, width, height,
+                        mConfiguration.smallestScreenWidthDp,
+                        mConfiguration.screenWidthDp, mConfiguration.screenHeightDp,
+                        mConfiguration.screenLayout, mConfiguration.uiMode,
+                        mConfiguration.colorMode, Build.VERSION.RESOURCES_SDK_INT);
+
+                if (DEBUG_CONFIG) {
+                    Slog.i(TAG, "**** Updating config of " + this + ": final config is "
+                            + mConfiguration + " final compat is "
+                            + mDisplayAdjustments.getCompatibilityInfo());
+                }
+
+                mDrawableCache.onConfigurationChange(configChanges);
+                mColorDrawableCache.onConfigurationChange(configChanges);
+                mComplexColorCache.onConfigurationChange(configChanges);
+                mAnimatorCache.onConfigurationChange(configChanges);
+                mStateListAnimatorCache.onConfigurationChange(configChanges);
+
+                flushLayoutCache();
+            }
+            synchronized (sSync) {
+                if (mPluralRule != null) {
+                    mPluralRule = PluralRules.forLocale(mConfiguration.getLocales().get(0));
+                }
+            }
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
+        }
+    }
+
+    /**
+     * Applies the new configuration, returning a bitmask of the changes
+     * between the old and new configurations.
+     *
+     * @param config the new configuration
+     * @return bitmask of config changes
+     */
+    public @Config int calcConfigChanges(@Nullable Configuration config) {
+        if (config == null) {
+            // If there is no configuration, assume all flags have changed.
+            return 0xFFFFFFFF;
+        }
+
+        mTmpConfig.setTo(config);
+        int density = config.densityDpi;
+        if (density == Configuration.DENSITY_DPI_UNDEFINED) {
+            density = mMetrics.noncompatDensityDpi;
+        }
+
+        mDisplayAdjustments.getCompatibilityInfo().applyToConfiguration(density, mTmpConfig);
+
+        if (mTmpConfig.getLocales().isEmpty()) {
+            mTmpConfig.setLocales(LocaleList.getDefault());
+        }
+        return mConfiguration.updateFrom(mTmpConfig);
+    }
+
+    /**
+     * {@code Locale.toLanguageTag} will transform the obsolete (and deprecated)
+     * language codes "in", "ji" and "iw" to "id", "yi" and "he" respectively.
+     *
+     * All released versions of android prior to "L" used the deprecated language
+     * tags, so we will need to support them for backwards compatibility.
+     *
+     * Note that this conversion needs to take place *after* the call to
+     * {@code toLanguageTag} because that will convert all the deprecated codes to
+     * the new ones, even if they're set manually.
+     */
+    private static String adjustLanguageTag(String languageTag) {
+        final int separator = languageTag.indexOf('-');
+        final String language;
+        final String remainder;
+
+        if (separator == -1) {
+            language = languageTag;
+            remainder = "";
+        } else {
+            language = languageTag.substring(0, separator);
+            remainder = languageTag.substring(separator);
+        }
+
+        return Locale.adjustLanguageCode(language) + remainder;
+    }
+
+    /**
+     * Call this to remove all cached loaded layout resources from the
+     * Resources object.  Only intended for use with performance testing
+     * tools.
+     */
+    public void flushLayoutCache() {
+        synchronized (mCachedXmlBlocks) {
+            Arrays.fill(mCachedXmlBlockCookies, 0);
+            Arrays.fill(mCachedXmlBlockFiles, null);
+
+            final XmlBlock[] cachedXmlBlocks = mCachedXmlBlocks;
+            for (int i = 0; i < XML_BLOCK_CACHE_SIZE; i++) {
+                final XmlBlock oldBlock = cachedXmlBlocks[i];
+                if (oldBlock != null) {
+                    oldBlock.close();
+                }
+            }
+            Arrays.fill(cachedXmlBlocks, null);
+        }
+    }
+
+    /**
+     * Wipe all caches that might be read and return an outdated object when resolving a resource.
+     */
+    public void clearAllCaches() {
+        synchronized (mAccessLock) {
+            mDrawableCache.clear();
+            mColorDrawableCache.clear();
+            mComplexColorCache.clear();
+            mAnimatorCache.clear();
+            mStateListAnimatorCache.clear();
+            flushLayoutCache();
+        }
+    }
+
+    @Nullable
+    Drawable loadDrawable(@NonNull Resources wrapper, @NonNull TypedValue value, int id,
+            int density, @Nullable Resources.Theme theme)
+            throws NotFoundException {
+        // If the drawable's XML lives in our current density qualifier,
+        // it's okay to use a scaled version from the cache. Otherwise, we
+        // need to actually load the drawable from XML.
+        final boolean useCache = density == 0 || value.density == mMetrics.densityDpi;
+
+        // Pretend the requested density is actually the display density. If
+        // the drawable returned is not the requested density, then force it
+        // to be scaled later by dividing its density by the ratio of
+        // requested density to actual device density. Drawables that have
+        // undefined density or no density don't need to be handled here.
+        if (density > 0 && value.density > 0 && value.density != TypedValue.DENSITY_NONE) {
+            if (value.density == density) {
+                value.density = mMetrics.densityDpi;
+            } else {
+                value.density = (value.density * mMetrics.densityDpi) / density;
+            }
+        }
+
+        try {
+            if (TRACE_FOR_PRELOAD) {
+                // Log only framework resources
+                if ((id >>> 24) == 0x1) {
+                    final String name = getResourceName(id);
+                    if (name != null) {
+                        Log.d("PreloadDrawable", name);
+                    }
+                }
+            }
+
+            final boolean isColorDrawable;
+            final DrawableCache caches;
+            final long key;
+            if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT
+                    && value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
+                isColorDrawable = true;
+                caches = mColorDrawableCache;
+                key = value.data;
+            } else {
+                isColorDrawable = false;
+                caches = mDrawableCache;
+                key = (((long) value.assetCookie) << 32) | value.data;
+            }
+
+            // First, check whether we have a cached version of this drawable
+            // that was inflated against the specified theme. Skip the cache if
+            // we're currently preloading or we're not using the cache.
+            if (!mPreloading && useCache) {
+                final Drawable cachedDrawable = caches.getInstance(key, wrapper, theme);
+                if (cachedDrawable != null) {
+                    cachedDrawable.setChangingConfigurations(value.changingConfigurations);
+                    return cachedDrawable;
+                }
+            }
+
+            // Next, check preloaded drawables. Preloaded drawables may contain
+            // unresolved theme attributes.
+            final Drawable.ConstantState cs;
+            if (isColorDrawable) {
+                cs = sPreloadedColorDrawables.get(key);
+            } else {
+                cs = sPreloadedDrawables[mConfiguration.getLayoutDirection()].get(key);
+            }
+
+            Drawable dr;
+            boolean needsNewDrawableAfterCache = false;
+            if (cs != null) {
+                if (TRACE_FOR_DETAILED_PRELOAD) {
+                    // Log only framework resources
+                    if (((id >>> 24) == 0x1) && (android.os.Process.myUid() != 0)) {
+                        final String name = getResourceName(id);
+                        if (name != null) {
+                            Log.d(TAG_PRELOAD, "Hit preloaded FW drawable #"
+                                    + Integer.toHexString(id) + " " + name);
+                        }
+                    }
+                }
+                dr = cs.newDrawable(wrapper);
+            } else if (isColorDrawable) {
+                dr = new ColorDrawable(value.data);
+            } else {
+                dr = loadDrawableForCookie(wrapper, value, id, density);
+            }
+            // DrawableContainer' constant state has drawables instances. In order to leave the
+            // constant state intact in the cache, we need to create a new DrawableContainer after
+            // added to cache.
+            if (dr instanceof DrawableContainer)  {
+                needsNewDrawableAfterCache = true;
+            }
+
+            // Determine if the drawable has unresolved theme attributes. If it
+            // does, we'll need to apply a theme and store it in a theme-specific
+            // cache.
+            final boolean canApplyTheme = dr != null && dr.canApplyTheme();
+            if (canApplyTheme && theme != null) {
+                dr = dr.mutate();
+                dr.applyTheme(theme);
+                dr.clearMutated();
+            }
+
+            // If we were able to obtain a drawable, store it in the appropriate
+            // cache: preload, not themed, null theme, or theme-specific. Don't
+            // pollute the cache with drawables loaded from a foreign density.
+            if (dr != null) {
+                dr.setChangingConfigurations(value.changingConfigurations);
+                if (useCache) {
+                    cacheDrawable(value, isColorDrawable, caches, theme, canApplyTheme, key, dr);
+                    if (needsNewDrawableAfterCache) {
+                        Drawable.ConstantState state = dr.getConstantState();
+                        if (state != null) {
+                            dr = state.newDrawable(wrapper);
+                        }
+                    }
+                }
+            }
+
+            return dr;
+        } catch (Exception e) {
+            String name;
+            try {
+                name = getResourceName(id);
+            } catch (NotFoundException e2) {
+                name = "(missing name)";
+            }
+
+            // The target drawable might fail to load for any number of
+            // reasons, but we always want to include the resource name.
+            // Since the client already expects this method to throw a
+            // NotFoundException, just throw one of those.
+            final NotFoundException nfe = new NotFoundException("Drawable " + name
+                    + " with resource ID #0x" + Integer.toHexString(id), e);
+            nfe.setStackTrace(new StackTraceElement[0]);
+            throw nfe;
+        }
+    }
+
+    private void cacheDrawable(TypedValue value, boolean isColorDrawable, DrawableCache caches,
+            Resources.Theme theme, boolean usesTheme, long key, Drawable dr) {
+        final Drawable.ConstantState cs = dr.getConstantState();
+        if (cs == null) {
+            return;
+        }
+
+        if (mPreloading) {
+            final int changingConfigs = cs.getChangingConfigurations();
+            if (isColorDrawable) {
+                if (verifyPreloadConfig(changingConfigs, 0, value.resourceId, "drawable")) {
+                    sPreloadedColorDrawables.put(key, cs);
+                }
+            } else {
+                if (verifyPreloadConfig(
+                        changingConfigs, ActivityInfo.CONFIG_LAYOUT_DIRECTION, value.resourceId, "drawable")) {
+                    if ((changingConfigs & ActivityInfo.CONFIG_LAYOUT_DIRECTION) == 0) {
+                        // If this resource does not vary based on layout direction,
+                        // we can put it in all of the preload maps.
+                        sPreloadedDrawables[0].put(key, cs);
+                        sPreloadedDrawables[1].put(key, cs);
+                    } else {
+                        // Otherwise, only in the layout dir we loaded it for.
+                        sPreloadedDrawables[mConfiguration.getLayoutDirection()].put(key, cs);
+                    }
+                }
+            }
+        } else {
+            synchronized (mAccessLock) {
+                caches.put(key, theme, cs, usesTheme);
+            }
+        }
+    }
+
+    private boolean verifyPreloadConfig(@Config int changingConfigurations,
+            @Config int allowVarying, @AnyRes int resourceId, @Nullable String name) {
+        // We allow preloading of resources even if they vary by font scale (which
+        // doesn't impact resource selection) or density (which we handle specially by
+        // simply turning off all preloading), as well as any other configs specified
+        // by the caller.
+        if (((changingConfigurations&~(ActivityInfo.CONFIG_FONT_SCALE |
+                ActivityInfo.CONFIG_DENSITY)) & ~allowVarying) != 0) {
+            String resName;
+            try {
+                resName = getResourceName(resourceId);
+            } catch (NotFoundException e) {
+                resName = "?";
+            }
+            // This should never happen in production, so we should log a
+            // warning even if we're not debugging.
+            Log.w(TAG, "Preloaded " + name + " resource #0x"
+                    + Integer.toHexString(resourceId)
+                    + " (" + resName + ") that varies with configuration!!");
+            return false;
+        }
+        if (TRACE_FOR_PRELOAD) {
+            String resName;
+            try {
+                resName = getResourceName(resourceId);
+            } catch (NotFoundException e) {
+                resName = "?";
+            }
+            Log.w(TAG, "Preloading " + name + " resource #0x"
+                    + Integer.toHexString(resourceId)
+                    + " (" + resName + ")");
+        }
+        return true;
+    }
+
+    /**
+     * Loads a Drawable from an encoded image stream, or null.
+     *
+     * This call will handle closing ais.
+     */
+    @Nullable
+    private Drawable decodeImageDrawable(@NonNull AssetInputStream ais,
+            @NonNull Resources wrapper, @NonNull TypedValue value) {
+        ImageDecoder.Source src = new ImageDecoder.AssetInputStreamSource(ais,
+                            wrapper, value);
+        try {
+            return ImageDecoder.decodeDrawable(src, (decoder, info, s) -> {
+                decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+            });
+        } catch (IOException ioe) {
+            // This is okay. This may be something that ImageDecoder does not
+            // support, like SVG.
+            return null;
+        }
+    }
+
+    /**
+     * Loads a drawable from XML or resources stream.
+     *
+     * @return Drawable, or null if Drawable cannot be decoded.
+     */
+    @Nullable
+    private Drawable loadDrawableForCookie(@NonNull Resources wrapper, @NonNull TypedValue value,
+            int id, int density) {
+        if (value.string == null) {
+            throw new NotFoundException("Resource \"" + getResourceName(id) + "\" ("
+                    + Integer.toHexString(id) + ") is not a Drawable (color or path): " + value);
+        }
+
+        final String file = value.string.toString();
+
+        if (TRACE_FOR_MISS_PRELOAD) {
+            // Log only framework resources
+            if ((id >>> 24) == 0x1) {
+                final String name = getResourceName(id);
+                if (name != null) {
+                    Log.d(TAG, "Loading framework drawable #" + Integer.toHexString(id)
+                            + ": " + name + " at " + file);
+                }
+            }
+        }
+
+        // For preload tracing.
+        long startTime = 0;
+        int startBitmapCount = 0;
+        long startBitmapSize = 0;
+        int startDrawableCount = 0;
+        if (TRACE_FOR_DETAILED_PRELOAD) {
+            startTime = System.nanoTime();
+            startBitmapCount = Bitmap.sPreloadTracingNumInstantiatedBitmaps;
+            startBitmapSize = Bitmap.sPreloadTracingTotalBitmapsSize;
+            startDrawableCount = sPreloadTracingNumLoadedDrawables;
+        }
+
+        if (DEBUG_LOAD) {
+            Log.v(TAG, "Loading drawable for cookie " + value.assetCookie + ": " + file);
+        }
+
+
+        final Drawable dr;
+
+        Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file);
+        LookupStack stack = mLookupStack.get();
+        try {
+            // Perform a linear search to check if we have already referenced this resource before.
+            if (stack.contains(id)) {
+                throw new Exception("Recursive reference in drawable");
+            }
+            stack.push(id);
+            try {
+                if (file.endsWith(".xml")) {
+                    final String typeName = getResourceTypeName(id);
+                    if (typeName != null && typeName.equals("color")) {
+                        dr = loadColorOrXmlDrawable(wrapper, value, id, density, file);
+                    } else {
+                        dr = loadXmlDrawable(wrapper, value, id, density, file);
+                    }
+                } else {
+                    final InputStream is = mAssets.openNonAsset(
+                            value.assetCookie, file, AssetManager.ACCESS_STREAMING);
+                    final AssetInputStream ais = (AssetInputStream) is;
+                    dr = decodeImageDrawable(ais, wrapper, value);
+                }
+            } finally {
+                stack.pop();
+            }
+        } catch (Exception | StackOverflowError e) {
+            Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
+            final NotFoundException rnf = new NotFoundException(
+                    "File " + file + " from drawable resource ID #0x" + Integer.toHexString(id));
+            rnf.initCause(e);
+            throw rnf;
+        }
+        Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
+
+        if (TRACE_FOR_DETAILED_PRELOAD) {
+            if (((id >>> 24) == 0x1)) {
+                final String name = getResourceName(id);
+                if (name != null) {
+                    final long time = System.nanoTime() - startTime;
+                    final int loadedBitmapCount =
+                            Bitmap.sPreloadTracingNumInstantiatedBitmaps - startBitmapCount;
+                    final long loadedBitmapSize =
+                            Bitmap.sPreloadTracingTotalBitmapsSize - startBitmapSize;
+                    final int loadedDrawables =
+                            sPreloadTracingNumLoadedDrawables - startDrawableCount;
+
+                    sPreloadTracingNumLoadedDrawables++;
+
+                    final boolean isRoot = (android.os.Process.myUid() == 0);
+
+                    Log.d(TAG_PRELOAD,
+                            (isRoot ? "Preloaded FW drawable #"
+                                    : "Loaded non-preloaded FW drawable #")
+                            + Integer.toHexString(id)
+                            + " " + name
+                            + " " + file
+                            + " " + dr.getClass().getCanonicalName()
+                            + " #nested_drawables= " + loadedDrawables
+                            + " #bitmaps= " + loadedBitmapCount
+                            + " total_bitmap_size= " + loadedBitmapSize
+                            + " in[us] " + (time / 1000));
+                }
+            }
+        }
+
+        return dr;
+    }
+
+    private Drawable loadColorOrXmlDrawable(@NonNull Resources wrapper, @NonNull TypedValue value,
+            int id, int density, String file) {
+        try {
+            ColorStateList csl = loadColorStateList(wrapper, value, id, null);
+            return new ColorStateListDrawable(csl);
+        } catch (NotFoundException originalException) {
+            // If we fail to load as color, try as normal XML drawable
+            try {
+                return loadXmlDrawable(wrapper, value, id, density, file);
+            } catch (Exception ignored) {
+                // If fallback also fails, throw the original exception
+                throw originalException;
+            }
+        }
+    }
+
+    private Drawable loadXmlDrawable(@NonNull Resources wrapper, @NonNull TypedValue value,
+            int id, int density, String file)
+            throws IOException, XmlPullParserException {
+        try (
+                XmlResourceParser rp =
+                        loadXmlResourceParser(file, id, value.assetCookie, "drawable")
+        ) {
+            return Drawable.createFromXmlForDensity(wrapper, rp, density, null);
+        }
+    }
+
+    /**
+     * Loads a font from XML or resources stream.
+     */
+    @Nullable
+    public Typeface loadFont(Resources wrapper, TypedValue value, int id) {
+        if (value.string == null) {
+            throw new NotFoundException("Resource \"" + getResourceName(id) + "\" ("
+                    + Integer.toHexString(id) + ") is not a Font: " + value);
+        }
+
+        final String file = value.string.toString();
+        if (!file.startsWith("res/")) {
+            return null;
+        }
+
+        Typeface cached = Typeface.findFromCache(mAssets, file);
+        if (cached != null) {
+            return cached;
+        }
+
+        if (DEBUG_LOAD) {
+            Log.v(TAG, "Loading font for cookie " + value.assetCookie + ": " + file);
+        }
+
+        Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file);
+        try {
+            if (file.endsWith("xml")) {
+                final XmlResourceParser rp = loadXmlResourceParser(
+                        file, id, value.assetCookie, "font");
+                final FontResourcesParser.FamilyResourceEntry familyEntry =
+                        FontResourcesParser.parse(rp, wrapper);
+                if (familyEntry == null) {
+                    return null;
+                }
+                return Typeface.createFromResources(familyEntry, mAssets, file);
+            }
+            return new Typeface.Builder(mAssets, file, false /* isAsset */, value.assetCookie)
+                    .build();
+        } catch (XmlPullParserException e) {
+            Log.e(TAG, "Failed to parse xml resource " + file, e);
+        } catch (IOException e) {
+            Log.e(TAG, "Failed to read xml resource " + file, e);
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
+        }
+        return null;
+    }
+
+    /**
+     * Given the value and id, we can get the XML filename as in value.data, based on that, we
+     * first try to load CSL from the cache. If not found, try to get from the constant state.
+     * Last, parse the XML and generate the CSL.
+     */
+    @Nullable
+    private ComplexColor loadComplexColorFromName(Resources wrapper, Resources.Theme theme,
+            TypedValue value, int id) {
+        final long key = (((long) value.assetCookie) << 32) | value.data;
+        final ConfigurationBoundResourceCache<ComplexColor> cache = mComplexColorCache;
+        ComplexColor complexColor = cache.getInstance(key, wrapper, theme);
+        if (complexColor != null) {
+            return complexColor;
+        }
+
+        final android.content.res.ConstantState<ComplexColor> factory =
+                sPreloadedComplexColors.get(key);
+
+        if (factory != null) {
+            complexColor = factory.newInstance(wrapper, theme);
+        }
+        if (complexColor == null) {
+            complexColor = loadComplexColorForCookie(wrapper, value, id, theme);
+        }
+
+        if (complexColor != null) {
+            complexColor.setBaseChangingConfigurations(value.changingConfigurations);
+
+            if (mPreloading) {
+                if (verifyPreloadConfig(complexColor.getChangingConfigurations(),
+                        0, value.resourceId, "color")) {
+                    sPreloadedComplexColors.put(key, complexColor.getConstantState());
+                }
+            } else {
+                cache.put(key, theme, complexColor.getConstantState());
+            }
+        }
+        return complexColor;
+    }
+
+    @Nullable
+    ComplexColor loadComplexColor(Resources wrapper, @NonNull TypedValue value, int id,
+            Resources.Theme theme) {
+        if (TRACE_FOR_PRELOAD) {
+            // Log only framework resources
+            if ((id >>> 24) == 0x1) {
+                final String name = getResourceName(id);
+                if (name != null) android.util.Log.d("loadComplexColor", name);
+            }
+        }
+
+        final long key = (((long) value.assetCookie) << 32) | value.data;
+
+        // Handle inline color definitions.
+        if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT
+                && value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
+            return getColorStateListFromInt(value, key);
+        }
+
+        final String file = value.string.toString();
+
+        ComplexColor complexColor;
+        if (file.endsWith(".xml")) {
+            try {
+                complexColor = loadComplexColorFromName(wrapper, theme, value, id);
+            } catch (Exception e) {
+                final NotFoundException rnf = new NotFoundException(
+                        "File " + file + " from complex color resource ID #0x"
+                                + Integer.toHexString(id));
+                rnf.initCause(e);
+                throw rnf;
+            }
+        } else {
+            throw new NotFoundException(
+                    "File " + file + " from drawable resource ID #0x"
+                            + Integer.toHexString(id) + ": .xml extension required");
+        }
+
+        return complexColor;
+    }
+
+    @NonNull
+    ColorStateList loadColorStateList(Resources wrapper, TypedValue value, int id,
+            Resources.Theme theme)
+            throws NotFoundException {
+        if (TRACE_FOR_PRELOAD) {
+            // Log only framework resources
+            if ((id >>> 24) == 0x1) {
+                final String name = getResourceName(id);
+                if (name != null) android.util.Log.d("PreloadColorStateList", name);
+            }
+        }
+
+        final long key = (((long) value.assetCookie) << 32) | value.data;
+
+        // Handle inline color definitions.
+        if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT
+                && value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
+            return getColorStateListFromInt(value, key);
+        }
+
+        ComplexColor complexColor = loadComplexColorFromName(wrapper, theme, value, id);
+        if (complexColor != null && complexColor instanceof ColorStateList) {
+            return (ColorStateList) complexColor;
+        }
+
+        throw new NotFoundException(
+                "Can't find ColorStateList from drawable resource ID #0x"
+                        + Integer.toHexString(id));
+    }
+
+    @NonNull
+    private ColorStateList getColorStateListFromInt(@NonNull TypedValue value, long key) {
+        ColorStateList csl;
+        final android.content.res.ConstantState<ComplexColor> factory =
+                sPreloadedComplexColors.get(key);
+        if (factory != null) {
+            return (ColorStateList) factory.newInstance();
+        }
+
+        csl = ColorStateList.valueOf(value.data);
+
+        if (mPreloading) {
+            if (verifyPreloadConfig(value.changingConfigurations, 0, value.resourceId,
+                    "color")) {
+                sPreloadedComplexColors.put(key, csl.getConstantState());
+            }
+        }
+
+        return csl;
+    }
+
+    /**
+     * Load a ComplexColor based on the XML file content. The result can be a GradientColor or
+     * ColorStateList. Note that pure color will be wrapped into a ColorStateList.
+     *
+     * We deferred the parser creation to this function b/c we need to differentiate b/t gradient
+     * and selector tag.
+     *
+     * @return a ComplexColor (GradientColor or ColorStateList) based on the XML file content, or
+     *     {@code null} if the XML file is neither.
+     */
+    @NonNull
+    private ComplexColor loadComplexColorForCookie(Resources wrapper, TypedValue value, int id,
+            Resources.Theme theme) {
+        if (value.string == null) {
+            throw new UnsupportedOperationException(
+                    "Can't convert to ComplexColor: type=0x" + value.type);
+        }
+
+        final String file = value.string.toString();
+
+        if (TRACE_FOR_MISS_PRELOAD) {
+            // Log only framework resources
+            if ((id >>> 24) == 0x1) {
+                final String name = getResourceName(id);
+                if (name != null) {
+                    Log.d(TAG, "Loading framework ComplexColor #" + Integer.toHexString(id)
+                            + ": " + name + " at " + file);
+                }
+            }
+        }
+
+        if (DEBUG_LOAD) {
+            Log.v(TAG, "Loading ComplexColor for cookie " + value.assetCookie + ": " + file);
+        }
+
+        ComplexColor complexColor = null;
+
+        Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file);
+        if (file.endsWith(".xml")) {
+            try {
+                final XmlResourceParser parser = loadXmlResourceParser(
+                        file, id, value.assetCookie, "ComplexColor");
+
+                final AttributeSet attrs = Xml.asAttributeSet(parser);
+                int type;
+                while ((type = parser.next()) != XmlPullParser.START_TAG
+                        && type != XmlPullParser.END_DOCUMENT) {
+                    // Seek parser to start tag.
+                }
+                if (type != XmlPullParser.START_TAG) {
+                    throw new XmlPullParserException("No start tag found");
+                }
+
+                final String name = parser.getName();
+                if (name.equals("gradient")) {
+                    complexColor = GradientColor.createFromXmlInner(wrapper, parser, attrs, theme);
+                } else if (name.equals("selector")) {
+                    complexColor = ColorStateList.createFromXmlInner(wrapper, parser, attrs, theme);
+                }
+                parser.close();
+            } catch (Exception e) {
+                Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
+                final NotFoundException rnf = new NotFoundException(
+                        "File " + file + " from ComplexColor resource ID #0x"
+                                + Integer.toHexString(id));
+                rnf.initCause(e);
+                throw rnf;
+            }
+        } else {
+            Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
+            throw new NotFoundException(
+                    "File " + file + " from drawable resource ID #0x"
+                            + Integer.toHexString(id) + ": .xml extension required");
+        }
+        Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
+
+        return complexColor;
+    }
+
+    /**
+     * Loads an XML parser for the specified file.
+     *
+     * @param file the path for the XML file to parse
+     * @param id the resource identifier for the file
+     * @param assetCookie the asset cookie for the file
+     * @param type the type of resource (used for logging)
+     * @return a parser for the specified XML file
+     * @throws NotFoundException if the file could not be loaded
+     */
+    @NonNull
+    XmlResourceParser loadXmlResourceParser(@NonNull String file, @AnyRes int id, int assetCookie,
+            @NonNull String type)
+            throws NotFoundException {
+        if (id != 0) {
+            try {
+                synchronized (mCachedXmlBlocks) {
+                    final int[] cachedXmlBlockCookies = mCachedXmlBlockCookies;
+                    final String[] cachedXmlBlockFiles = mCachedXmlBlockFiles;
+                    final XmlBlock[] cachedXmlBlocks = mCachedXmlBlocks;
+                    // First see if this block is in our cache.
+                    final int num = cachedXmlBlockFiles.length;
+                    for (int i = 0; i < num; i++) {
+                        if (cachedXmlBlockCookies[i] == assetCookie && cachedXmlBlockFiles[i] != null
+                                && cachedXmlBlockFiles[i].equals(file)) {
+                            return cachedXmlBlocks[i].newParser(id);
+                        }
+                    }
+
+                    // Not in the cache, create a new block and put it at
+                    // the next slot in the cache.
+                    final XmlBlock block = mAssets.openXmlBlockAsset(assetCookie, file);
+                    if (block != null) {
+                        final int pos = (mLastCachedXmlBlockIndex + 1) % num;
+                        mLastCachedXmlBlockIndex = pos;
+                        final XmlBlock oldBlock = cachedXmlBlocks[pos];
+                        if (oldBlock != null) {
+                            oldBlock.close();
+                        }
+                        cachedXmlBlockCookies[pos] = assetCookie;
+                        cachedXmlBlockFiles[pos] = file;
+                        cachedXmlBlocks[pos] = block;
+                        return block.newParser(id);
+                    }
+                }
+            } catch (Exception e) {
+                final NotFoundException rnf = new NotFoundException("File " + file
+                        + " from xml type " + type + " resource ID #0x" + Integer.toHexString(id));
+                rnf.initCause(e);
+                throw rnf;
+            }
+        }
+
+        throw new NotFoundException("File " + file + " from xml type " + type + " resource ID #0x"
+                + Integer.toHexString(id));
+    }
+
+    /**
+     * Start preloading of resource data using this Resources object.  Only
+     * for use by the zygote process for loading common system resources.
+     * {@hide}
+     */
+    public final void startPreloading() {
+        synchronized (sSync) {
+            if (sPreloaded) {
+                throw new IllegalStateException("Resources already preloaded");
+            }
+            sPreloaded = true;
+            mPreloading = true;
+            mConfiguration.densityDpi = DisplayMetrics.DENSITY_DEVICE;
+            updateConfiguration(null, null, null);
+
+            if (TRACE_FOR_DETAILED_PRELOAD) {
+                mPreloadTracingPreloadStartTime = SystemClock.uptimeMillis();
+                mPreloadTracingStartBitmapSize = Bitmap.sPreloadTracingTotalBitmapsSize;
+                mPreloadTracingStartBitmapCount = Bitmap.sPreloadTracingNumInstantiatedBitmaps;
+                Log.d(TAG_PRELOAD, "Preload starting");
+            }
+        }
+    }
+
+    /**
+     * Called by zygote when it is done preloading resources, to change back
+     * to normal Resources operation.
+     */
+    void finishPreloading() {
+        if (mPreloading) {
+            if (TRACE_FOR_DETAILED_PRELOAD) {
+                final long time = SystemClock.uptimeMillis() - mPreloadTracingPreloadStartTime;
+                final long size =
+                        Bitmap.sPreloadTracingTotalBitmapsSize - mPreloadTracingStartBitmapSize;
+                final long count = Bitmap.sPreloadTracingNumInstantiatedBitmaps
+                        - mPreloadTracingStartBitmapCount;
+                Log.d(TAG_PRELOAD, "Preload finished, "
+                        + count + " bitmaps of " + size + " bytes in " + time + " ms");
+            }
+
+            mPreloading = false;
+            flushLayoutCache();
+        }
+    }
+
+    @AnyRes
+    static int getAttributeSetSourceResId(@Nullable AttributeSet set) {
+        if (set == null || !(set instanceof XmlBlock.Parser)) {
+            return ID_NULL;
+        }
+        return ((XmlBlock.Parser) set).getSourceResId();
+    }
+
+    LongSparseArray<Drawable.ConstantState> getPreloadedDrawables() {
+        return sPreloadedDrawables[0];
+    }
+
+    ThemeImpl newThemeImpl() {
+        return new ThemeImpl();
+    }
+
+    /**
+     * Creates a new ThemeImpl which is already set to the given Resources.ThemeKey.
+     */
+    ThemeImpl newThemeImpl(Resources.ThemeKey key) {
+        ThemeImpl impl = new ThemeImpl();
+        impl.mKey.setTo(key);
+        impl.rebase();
+        return impl;
+    }
+
+    public class ThemeImpl {
+        /**
+         * Unique key for the series of styles applied to this theme.
+         */
+        private final Resources.ThemeKey mKey = new Resources.ThemeKey();
+
+        @SuppressWarnings("hiding")
+        private final AssetManager mAssets;
+        private final long mTheme;
+
+        /**
+         * Resource identifier for the theme.
+         */
+        private int mThemeResId = 0;
+
+        /*package*/ ThemeImpl() {
+            mAssets = ResourcesImpl.this.mAssets;
+            mTheme = mAssets.createTheme();
+        }
+
+        @Override
+        protected void finalize() throws Throwable {
+            super.finalize();
+            mAssets.releaseTheme(mTheme);
+        }
+
+        /*package*/ Resources.ThemeKey getKey() {
+            return mKey;
+        }
+
+        /*package*/ long getNativeTheme() {
+            return mTheme;
+        }
+
+        /*package*/ int getAppliedStyleResId() {
+            return mThemeResId;
+        }
+
+        void applyStyle(int resId, boolean force) {
+            synchronized (mKey) {
+                mAssets.applyStyleToTheme(mTheme, resId, force);
+                mThemeResId = resId;
+                mKey.append(resId, force);
+            }
+        }
+
+        void setTo(ThemeImpl other) {
+            synchronized (mKey) {
+                synchronized (other.mKey) {
+                    mAssets.setThemeTo(mTheme, other.mAssets, other.mTheme);
+
+                    mThemeResId = other.mThemeResId;
+                    mKey.setTo(other.getKey());
+                }
+            }
+        }
+
+        @NonNull
+        TypedArray obtainStyledAttributes(@NonNull Resources.Theme wrapper,
+                AttributeSet set,
+                @StyleableRes int[] attrs,
+                @AttrRes int defStyleAttr,
+                @StyleRes int defStyleRes) {
+            synchronized (mKey) {
+                final int len = attrs.length;
+                final TypedArray array = TypedArray.obtain(wrapper.getResources(), len);
+
+                // XXX note that for now we only work with compiled XML files.
+                // To support generic XML files we will need to manually parse
+                // out the attributes from the XML file (applying type information
+                // contained in the resources and such).
+                final XmlBlock.Parser parser = (XmlBlock.Parser) set;
+                mAssets.applyStyle(mTheme, defStyleAttr, defStyleRes, parser, attrs,
+                        array.mDataAddress, array.mIndicesAddress);
+                array.mTheme = wrapper;
+                array.mXml = parser;
+                return array;
+            }
+        }
+
+        @NonNull
+        TypedArray resolveAttributes(@NonNull Resources.Theme wrapper,
+                @NonNull int[] values,
+                @NonNull int[] attrs) {
+            synchronized (mKey) {
+                final int len = attrs.length;
+                if (values == null || len != values.length) {
+                    throw new IllegalArgumentException(
+                            "Base attribute values must the same length as attrs");
+                }
+
+                final TypedArray array = TypedArray.obtain(wrapper.getResources(), len);
+                mAssets.resolveAttrs(mTheme, 0, 0, values, attrs, array.mData, array.mIndices);
+                array.mTheme = wrapper;
+                array.mXml = null;
+                return array;
+            }
+        }
+
+        boolean resolveAttribute(int resid, TypedValue outValue, boolean resolveRefs) {
+            synchronized (mKey) {
+                return mAssets.getThemeValue(mTheme, resid, outValue, resolveRefs);
+            }
+        }
+
+        int[] getAllAttributes() {
+            return mAssets.getStyleAttributes(getAppliedStyleResId());
+        }
+
+        @Config int getChangingConfigurations() {
+            synchronized (mKey) {
+                final @NativeConfig int nativeChangingConfig =
+                        AssetManager.nativeThemeGetChangingConfigurations(mTheme);
+                return ActivityInfo.activityInfoConfigNativeToJava(nativeChangingConfig);
+            }
+        }
+
+        public void dump(int priority, String tag, String prefix) {
+            synchronized (mKey) {
+                mAssets.dumpTheme(mTheme, priority, tag, prefix);
+            }
+        }
+
+        String[] getTheme() {
+            synchronized (mKey) {
+                final int N = mKey.mCount;
+                final String[] themes = new String[N * 2];
+                for (int i = 0, j = N - 1; i < themes.length; i += 2, --j) {
+                    final int resId = mKey.mResId[j];
+                    final boolean forced = mKey.mForce[j];
+                    try {
+                        themes[i] = getResourceName(resId);
+                    } catch (NotFoundException e) {
+                        themes[i] = Integer.toHexString(i);
+                    }
+                    themes[i + 1] = forced ? "forced" : "not forced";
+                }
+                return themes;
+            }
+        }
+
+        /**
+         * Rebases the theme against the parent Resource object's current
+         * configuration by re-applying the styles passed to
+         * {@link #applyStyle(int, boolean)}.
+         */
+        void rebase() {
+            synchronized (mKey) {
+                AssetManager.nativeThemeClear(mTheme);
+
+                // Reapply the same styles in the same order.
+                for (int i = 0; i < mKey.mCount; i++) {
+                    final int resId = mKey.mResId[i];
+                    final boolean force = mKey.mForce[i];
+                    mAssets.applyStyleToTheme(mTheme, resId, force);
+                }
+            }
+        }
+
+        /**
+         * Returns the ordered list of resource ID that are considered when resolving attribute
+         * values when making an equivalent call to
+         * {@link #obtainStyledAttributes(Resources.Theme, AttributeSet, int[], int, int)}. The list
+         * will include a set of explicit styles ({@code explicitStyleRes} and it will include the
+         * default styles ({@code defStyleAttr} and {@code defStyleRes}).
+         *
+         * @param defStyleAttr An attribute in the current theme that contains a
+         *                     reference to a style resource that supplies
+         *                     defaults values for the TypedArray.  Can be
+         *                     0 to not look for defaults.
+         * @param defStyleRes A resource identifier of a style resource that
+         *                    supplies default values for the TypedArray,
+         *                    used only if defStyleAttr is 0 or can not be found
+         *                    in the theme.  Can be 0 to not look for defaults.
+         * @param explicitStyleRes A resource identifier of an explicit style resource.
+         * @return ordered list of resource ID that are considered when resolving attribute values.
+         */
+        @Nullable
+        public int[] getAttributeResolutionStack(@AttrRes int defStyleAttr,
+                @StyleRes int defStyleRes, @StyleRes int explicitStyleRes) {
+            synchronized (mKey) {
+                return mAssets.getAttributeResolutionStack(
+                        mTheme, defStyleAttr, defStyleRes, explicitStyleRes);
+            }
+        }
+    }
+
+    private static class LookupStack {
+
+        // Pick a reasonable default size for the array, it is grown as needed.
+        private int[] mIds = new int[4];
+        private int mSize = 0;
+
+        public void push(int id) {
+            mIds = GrowingArrayUtils.append(mIds, mSize, id);
+            mSize++;
+        }
+
+        public boolean contains(int id) {
+            for (int i = 0; i < mSize; i++) {
+                if (mIds[i] == id) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        public void pop() {
+            mSize--;
+        }
+    }
+}
diff --git a/android/content/res/ResourcesKey.java b/android/content/res/ResourcesKey.java
new file mode 100644
index 0000000..9da0f20
--- /dev/null
+++ b/android/content/res/ResourcesKey.java
@@ -0,0 +1,198 @@
+/*
+ * 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.content.res;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.res.loader.ResourcesLoader;
+import android.text.TextUtils;
+
+import java.util.Arrays;
+import java.util.Objects;
+
+/** @hide */
+public final class ResourcesKey {
+    @Nullable
+    @UnsupportedAppUsage
+    public final String mResDir;
+
+    @Nullable
+    @UnsupportedAppUsage
+    public final String[] mSplitResDirs;
+
+    @Nullable
+    public final String[] mOverlayDirs;
+
+    @Nullable
+    public final String[] mLibDirs;
+
+    public final int mDisplayId;
+
+    @NonNull
+    public final Configuration mOverrideConfiguration;
+
+    @NonNull
+    public final CompatibilityInfo mCompatInfo;
+
+    @Nullable
+    public final ResourcesLoader[] mLoaders;
+
+    private final int mHash;
+
+    public ResourcesKey(@Nullable String resDir,
+                        @Nullable String[] splitResDirs,
+                        @Nullable String[] overlayDirs,
+                        @Nullable String[] libDirs,
+                        int displayId,
+                        @Nullable Configuration overrideConfig,
+                        @Nullable CompatibilityInfo compatInfo,
+                        @Nullable ResourcesLoader[] loader) {
+        mResDir = resDir;
+        mSplitResDirs = splitResDirs;
+        mOverlayDirs = overlayDirs;
+        mLibDirs = libDirs;
+        mLoaders = (loader != null && loader.length == 0) ? null : loader;
+        mDisplayId = displayId;
+        mOverrideConfiguration = new Configuration(overrideConfig != null
+                ? overrideConfig : Configuration.EMPTY);
+        mCompatInfo = compatInfo != null ? compatInfo : CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
+
+        int hash = 17;
+        hash = 31 * hash + Objects.hashCode(mResDir);
+        hash = 31 * hash + Arrays.hashCode(mSplitResDirs);
+        hash = 31 * hash + Arrays.hashCode(mOverlayDirs);
+        hash = 31 * hash + Arrays.hashCode(mLibDirs);
+        hash = 31 * hash + mDisplayId;
+        hash = 31 * hash + Objects.hashCode(mOverrideConfiguration);
+        hash = 31 * hash + Objects.hashCode(mCompatInfo);
+        hash = 31 * hash + Arrays.hashCode(mLoaders);
+        mHash = hash;
+    }
+
+    @UnsupportedAppUsage
+    public ResourcesKey(@Nullable String resDir,
+            @Nullable String[] splitResDirs,
+            @Nullable String[] overlayDirs,
+            @Nullable String[] libDirs,
+            int displayId,
+            @Nullable Configuration overrideConfig,
+            @Nullable CompatibilityInfo compatInfo) {
+        this(resDir, splitResDirs, overlayDirs, libDirs, displayId, overrideConfig, compatInfo,
+                null);
+    }
+
+    public boolean hasOverrideConfiguration() {
+        return !Configuration.EMPTY.equals(mOverrideConfiguration);
+    }
+
+    public boolean isPathReferenced(String path) {
+        if (mResDir != null && mResDir.startsWith(path)) {
+            return true;
+        } else {
+            return anyStartsWith(mSplitResDirs, path) || anyStartsWith(mOverlayDirs, path)
+                    || anyStartsWith(mLibDirs, path);
+        }
+    }
+
+    private static boolean anyStartsWith(String[] list, String prefix) {
+        if (list != null) {
+            for (String s : list) {
+                if (s != null && s.startsWith(prefix)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return mHash;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof ResourcesKey)) {
+            return false;
+        }
+
+        ResourcesKey peer = (ResourcesKey) obj;
+        if (mHash != peer.mHash) {
+            // If the hashes don't match, the objects can't match.
+            return false;
+        }
+
+        if (!Objects.equals(mResDir, peer.mResDir)) {
+            return false;
+        }
+        if (!Arrays.equals(mSplitResDirs, peer.mSplitResDirs)) {
+            return false;
+        }
+        if (!Arrays.equals(mOverlayDirs, peer.mOverlayDirs)) {
+            return false;
+        }
+        if (!Arrays.equals(mLibDirs, peer.mLibDirs)) {
+            return false;
+        }
+        if (mDisplayId != peer.mDisplayId) {
+            return false;
+        }
+        if (!Objects.equals(mOverrideConfiguration, peer.mOverrideConfiguration)) {
+            return false;
+        }
+        if (!Objects.equals(mCompatInfo, peer.mCompatInfo)) {
+            return false;
+        }
+        if (!Arrays.equals(mLoaders, peer.mLoaders)) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder().append("ResourcesKey{");
+        builder.append(" mHash=").append(Integer.toHexString(mHash));
+        builder.append(" mResDir=").append(mResDir);
+        builder.append(" mSplitDirs=[");
+        if (mSplitResDirs != null) {
+            builder.append(TextUtils.join(",", mSplitResDirs));
+        }
+        builder.append("]");
+        builder.append(" mOverlayDirs=[");
+        if (mOverlayDirs != null) {
+            builder.append(TextUtils.join(",", mOverlayDirs));
+        }
+        builder.append("]");
+        builder.append(" mLibDirs=[");
+        if (mLibDirs != null) {
+            builder.append(TextUtils.join(",", mLibDirs));
+        }
+        builder.append("]");
+        builder.append(" mDisplayId=").append(mDisplayId);
+        builder.append(" mOverrideConfig=").append(Configuration.resourceQualifierString(
+                mOverrideConfiguration));
+        builder.append(" mCompatInfo=").append(mCompatInfo);
+        builder.append(" mLoaders=[");
+        if (mLoaders != null) {
+            builder.append(TextUtils.join(",", mLoaders));
+        }
+        builder.append("]}");
+        return builder.toString();
+    }
+}
diff --git a/android/content/res/Resources_Delegate.java b/android/content/res/Resources_Delegate.java
new file mode 100644
index 0000000..cd57d5c
--- /dev/null
+++ b/android/content/res/Resources_Delegate.java
@@ -0,0 +1,1172 @@
+/*
+ * 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.content.res;
+
+import com.android.SdkConstants;
+import com.android.ide.common.rendering.api.ArrayResourceValue;
+import com.android.ide.common.rendering.api.AssetRepository;
+import com.android.ide.common.rendering.api.DensityBasedResourceValue;
+import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.ide.common.rendering.api.LayoutlibCallback;
+import com.android.ide.common.rendering.api.PluralsResourceValue;
+import com.android.ide.common.rendering.api.RenderResources;
+import com.android.ide.common.rendering.api.ResourceNamespace;
+import com.android.ide.common.rendering.api.ResourceNamespace.Resolver;
+import com.android.ide.common.rendering.api.ResourceReference;
+import com.android.ide.common.rendering.api.ResourceValue;
+import com.android.ide.common.rendering.api.ResourceValueImpl;
+import com.android.layoutlib.bridge.Bridge;
+import com.android.layoutlib.bridge.BridgeConstants;
+import com.android.layoutlib.bridge.android.BridgeContext;
+import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
+import com.android.layoutlib.bridge.android.UnresolvedResourceValue;
+import com.android.layoutlib.bridge.impl.ParserFactory;
+import com.android.layoutlib.bridge.impl.ResourceHelper;
+import com.android.layoutlib.bridge.util.NinePatchInputStream;
+import com.android.ninepatch.NinePatch;
+import com.android.resources.ResourceType;
+import com.android.resources.ResourceUrl;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+import com.android.tools.layoutlib.annotations.VisibleForTesting;
+import com.android.util.Pair;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.res.Resources.NotFoundException;
+import android.content.res.Resources.Theme;
+import android.graphics.Color;
+import android.graphics.Typeface;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.DrawableInflater_Delegate;
+import android.icu.text.PluralRules;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.LruCache;
+import android.util.TypedValue;
+import android.view.DisplayAdjustments;
+import android.view.ViewGroup.LayoutParams;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Objects;
+import java.util.WeakHashMap;
+
+import static android.content.res.AssetManager.ACCESS_STREAMING;
+import static com.android.SdkConstants.ANDROID_PKG;
+import static com.android.SdkConstants.PREFIX_RESOURCE_REF;
+
+@SuppressWarnings("deprecation")
+public class Resources_Delegate {
+    private static WeakHashMap<Resources, LayoutlibCallback> sLayoutlibCallbacks =
+            new WeakHashMap<>();
+    private static WeakHashMap<Resources, BridgeContext> sContexts = new WeakHashMap<>();
+
+    // TODO: This cache is cleared every time a render session is disposed. Look into making this
+    // more long lived.
+    private static LruCache<String, Drawable.ConstantState> sDrawableCache = new LruCache<>(50);
+
+    public static Resources initSystem(@NonNull BridgeContext context,
+            @NonNull AssetManager assets,
+            @NonNull DisplayMetrics metrics,
+            @NonNull Configuration config,
+            @NonNull LayoutlibCallback layoutlibCallback) {
+        assert Resources.mSystem == null  :
+                "Resources_Delegate.initSystem called twice before disposeSystem was called";
+        Resources resources = new Resources(Resources_Delegate.class.getClassLoader());
+        resources.setImpl(new ResourcesImpl(assets, metrics, config, new DisplayAdjustments()));
+        sContexts.put(resources, Objects.requireNonNull(context));
+        sLayoutlibCallbacks.put(resources, Objects.requireNonNull(layoutlibCallback));
+        return Resources.mSystem = resources;
+    }
+
+    /** Returns the {@link BridgeContext} associated to the given {@link Resources} */
+    @VisibleForTesting
+    @NonNull
+    public static BridgeContext getContext(@NonNull Resources resources) {
+        assert sContexts.containsKey(resources) :
+                "Resources_Delegate.getContext called before initSystem";
+        return sContexts.get(resources);
+    }
+
+    /** Returns the {@link LayoutlibCallback} associated to the given {@link Resources} */
+    @VisibleForTesting
+    @NonNull
+    public static LayoutlibCallback getLayoutlibCallback(@NonNull Resources resources) {
+        assert sLayoutlibCallbacks.containsKey(resources) :
+                "Resources_Delegate.getLayoutlibCallback called before initSystem";
+        return sLayoutlibCallbacks.get(resources);
+    }
+
+    /**
+     * Disposes the static {@link Resources#mSystem} to make sure we don't leave objects around that
+     * would prevent us from unloading the library.
+     */
+    public static void disposeSystem() {
+        sDrawableCache.evictAll();
+        sContexts.clear();
+        sLayoutlibCallbacks.clear();
+        DrawableInflater_Delegate.clearConstructorCache();
+        Resources.mSystem = null;
+    }
+
+    public static BridgeTypedArray newTypeArray(Resources resources, int numEntries) {
+        return new BridgeTypedArray(resources, getContext(resources), numEntries);
+    }
+
+    private static ResourceReference getResourceInfo(Resources resources, int id) {
+        // first get the String related to this id in the framework
+        ResourceReference resourceInfo = Bridge.resolveResourceId(id);
+
+        assert Resources.mSystem != null : "Resources_Delegate.initSystem wasn't called";
+        // Set the layoutlib callback and context for resources
+        if (resources != Resources.mSystem &&
+                (!sContexts.containsKey(resources) || !sLayoutlibCallbacks.containsKey(resources))) {
+            sLayoutlibCallbacks.put(resources, getLayoutlibCallback(Resources.mSystem));
+            sContexts.put(resources, getContext(Resources.mSystem));
+        }
+
+        if (resourceInfo == null) {
+            // Didn't find a match in the framework? Look in the project.
+            resourceInfo = getLayoutlibCallback(resources).resolveResourceId(id);
+        }
+
+        return resourceInfo;
+    }
+
+    private static Pair<String, ResourceValue> getResourceValue(Resources resources, int id) {
+        ResourceReference resourceInfo = getResourceInfo(resources, id);
+
+        if (resourceInfo != null) {
+            String attributeName = resourceInfo.getName();
+            RenderResources renderResources = getContext(resources).getRenderResources();
+            ResourceValue value = renderResources.getResolvedResource(resourceInfo);
+            if (value == null) {
+                // Unable to resolve the attribute, just leave the unresolved value.
+                value = new ResourceValueImpl(resourceInfo.getNamespace(),
+                        resourceInfo.getResourceType(), attributeName, attributeName);
+            }
+            return Pair.of(attributeName, value);
+        }
+
+        return null;
+    }
+
+    @LayoutlibDelegate
+    static Drawable getDrawable(Resources resources, int id) {
+        return getDrawable(resources, id, null);
+    }
+
+    @LayoutlibDelegate
+    static Drawable getDrawable(Resources resources, int id, Theme theme) {
+        Pair<String, ResourceValue> value = getResourceValue(resources, id);
+        if (value != null) {
+            String key = value.getSecond().getValue();
+
+            Drawable.ConstantState constantState = key != null ? sDrawableCache.get(key) : null;
+            Drawable drawable;
+            if (constantState != null) {
+                drawable = constantState.newDrawable(resources, theme);
+            } else {
+                drawable =
+                        ResourceHelper.getDrawable(value.getSecond(), getContext(resources), theme);
+
+                if (key != null) {
+                    sDrawableCache.put(key, drawable.getConstantState());
+                }
+            }
+
+            return drawable;
+        }
+
+        // id was not found or not resolved. Throw a NotFoundException.
+        throwException(resources, id);
+
+        // this is not used since the method above always throws
+        return null;
+    }
+
+    @LayoutlibDelegate
+    static int getColor(Resources resources, int id) {
+        return getColor(resources, id, null);
+    }
+
+    @LayoutlibDelegate
+    static int getColor(Resources resources, int id, Theme theme) throws NotFoundException {
+        Pair<String, ResourceValue> value = getResourceValue(resources, id);
+
+        if (value != null) {
+            ResourceValue resourceValue = value.getSecond();
+            try {
+                return ResourceHelper.getColor(resourceValue.getValue());
+            } catch (NumberFormatException e) {
+                // Check if the value passed is a file. If it is, mostly likely, user is referencing
+                // a color state list from a place where they should reference only a pure color.
+                AssetRepository repository = getAssetRepository(resources);
+                String message;
+                if (repository.isFileResource(resourceValue.getValue())) {
+                    String resource = (resourceValue.isFramework() ? "@android:" : "@") + "color/"
+                            + resourceValue.getName();
+                    message = "Hexadecimal color expected, found Color State List for " + resource;
+                } else {
+                    message = e.getMessage();
+                }
+                Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT, message, e, null, null);
+                return 0;
+            }
+        }
+
+        // Suppress possible NPE. getColorStateList will never return null, it will instead
+        // throw an exception, but intelliJ can't figure that out
+        //noinspection ConstantConditions
+        return getColorStateList(resources, id, theme).getDefaultColor();
+    }
+
+    @LayoutlibDelegate
+    static ColorStateList getColorStateList(Resources resources, int id) throws NotFoundException {
+        return getColorStateList(resources, id, null);
+    }
+
+    @LayoutlibDelegate
+    static ColorStateList getColorStateList(Resources resources, int id, Theme theme)
+            throws NotFoundException {
+        Pair<String, ResourceValue> resValue = getResourceValue(resources, id);
+
+        if (resValue != null) {
+            ColorStateList stateList = ResourceHelper.getColorStateList(resValue.getSecond(),
+                    getContext(resources), theme);
+            if (stateList != null) {
+                return stateList;
+            }
+        }
+
+        // id was not found or not resolved. Throw a NotFoundException.
+        throwException(resources, id);
+
+        // this is not used since the method above always throws
+        return null;
+    }
+
+    @LayoutlibDelegate
+    static CharSequence getText(Resources resources, int id, CharSequence def) {
+        Pair<String, ResourceValue> value = getResourceValue(resources, id);
+
+        if (value != null) {
+            ResourceValue resValue = value.getSecond();
+
+            assert resValue != null;
+            if (resValue != null) {
+                String v = resValue.getValue();
+                if (v != null) {
+                    return v;
+                }
+            }
+        }
+
+        return def;
+    }
+
+    @LayoutlibDelegate
+    static CharSequence getText(Resources resources, int id) throws NotFoundException {
+        Pair<String, ResourceValue> value = getResourceValue(resources, id);
+
+        if (value != null) {
+            ResourceValue resValue = value.getSecond();
+
+            assert resValue != null;
+            if (resValue != null) {
+                String v = resValue.getValue();
+                if (v != null) {
+                    return v;
+                }
+            }
+        }
+
+        // id was not found or not resolved. Throw a NotFoundException.
+        throwException(resources, id);
+
+        // this is not used since the method above always throws
+        return null;
+    }
+
+    @LayoutlibDelegate
+    static CharSequence[] getTextArray(Resources resources, int id) throws NotFoundException {
+        ResourceValue resValue = getArrayResourceValue(resources, id);
+        if (resValue == null) {
+            // Error already logged by getArrayResourceValue.
+            return new CharSequence[0];
+        }
+        if (resValue instanceof ArrayResourceValue) {
+            ArrayResourceValue arrayValue = (ArrayResourceValue) resValue;
+            return resolveValues(resources, arrayValue);
+        }
+        RenderResources renderResources = getContext(resources).getRenderResources();
+        return new CharSequence[] { renderResources.resolveResValue(resValue).getValue() };
+    }
+
+    @LayoutlibDelegate
+    static String[] getStringArray(Resources resources, int id) throws NotFoundException {
+        ResourceValue resValue = getArrayResourceValue(resources, id);
+        if (resValue == null) {
+            // Error already logged by getArrayResourceValue.
+            return new String[0];
+        }
+        if (resValue instanceof ArrayResourceValue) {
+            ArrayResourceValue arv = (ArrayResourceValue) resValue;
+            return resolveValues(resources, arv);
+        }
+        return new String[] { resolveReference(resources, resValue) };
+    }
+
+    /**
+     * Resolves each element in resValue and returns an array of resolved values. The returned array
+     * may contain nulls.
+     */
+    @NonNull
+    static String[] resolveValues(@NonNull Resources resources,
+            @NonNull ArrayResourceValue resValue) {
+        String[] result = new String[resValue.getElementCount()];
+        for (int i = 0; i < resValue.getElementCount(); i++) {
+            String value = resValue.getElement(i);
+            result[i] = resolveReference(resources, value,
+                    resValue.getNamespace(), resValue.getNamespaceResolver());
+        }
+        return result;
+    }
+
+    @LayoutlibDelegate
+    static int[] getIntArray(Resources resources, int id) throws NotFoundException {
+        ResourceValue rv = getArrayResourceValue(resources, id);
+        if (rv == null) {
+            // Error already logged by getArrayResourceValue.
+            return new int[0];
+        }
+        if (rv instanceof ArrayResourceValue) {
+            ArrayResourceValue resValue = (ArrayResourceValue) rv;
+            int n = resValue.getElementCount();
+            int[] values = new int[n];
+            for (int i = 0; i < n; i++) {
+                String element = resolveReference(resources, resValue.getElement(i),
+                        resValue.getNamespace(), resValue.getNamespaceResolver());
+                if (element != null) {
+                    try {
+                        if (element.startsWith("#")) {
+                            // This integer represents a color (starts with #).
+                            values[i] = Color.parseColor(element);
+                        } else {
+                            values[i] = getInt(element);
+                        }
+                    } catch (NumberFormatException e) {
+                        Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT,
+                                "Integer resource array contains non-integer value: \"" + element +
+                                        "\"", null, null);
+                    } catch (IllegalArgumentException e) {
+                        Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT,
+                                "Integer resource array contains wrong color format: \"" + element +
+                                        "\"", null, null);
+                    }
+                } else {
+                    Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT,
+                            "Integer resource array contains non-integer value: \"" +
+                                    resValue.getElement(i) + "\"", null, null);
+                }
+            }
+            return values;
+        }
+
+        // This is an older IDE that can only give us the first element of the array.
+        String firstValue = resolveReference(resources, rv);
+        if (firstValue != null) {
+            try {
+                return new int[]{getInt(firstValue)};
+            } catch (NumberFormatException e) {
+                Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT,
+                        "Integer resource array contains non-integer value: \"" + firstValue + "\"",
+                        null, null);
+                return new int[1];
+            }
+        } else {
+            Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT,
+                    "Integer resource array contains non-integer value: \"" +
+                            rv.getValue() + "\"", null, null);
+            return new int[1];
+        }
+    }
+
+    /**
+     * Try to find the ArrayResourceValue for the given id.
+     * <p/>
+     * If the ResourceValue found is not of type {@link ResourceType#ARRAY}, the method logs an
+     * error and return null. However, if the ResourceValue found has type {@code
+     * ResourceType.ARRAY}, but the value is not an instance of {@link ArrayResourceValue}, the
+     * method returns the ResourceValue. This happens on older versions of the IDE, which did not
+     * parse the array resources properly.
+     * <p/>
+     *
+     * @throws NotFoundException if no resource if found
+     */
+    @Nullable
+    private static ResourceValue getArrayResourceValue(Resources resources, int id)
+            throws NotFoundException {
+        Pair<String, ResourceValue> v = getResourceValue(resources, id);
+
+        if (v != null) {
+            ResourceValue resValue = v.getSecond();
+
+            assert resValue != null;
+            if (resValue != null) {
+                final ResourceType type = resValue.getResourceType();
+                if (type != ResourceType.ARRAY) {
+                    Bridge.getLog().error(LayoutLog.TAG_RESOURCES_RESOLVE,
+                            String.format(
+                                    "Resource with id 0x%1$X is not an array resource, but %2$s",
+                                    id, type == null ? "null" : type.getDisplayName()),
+                            null, null);
+                    return null;
+                }
+                if (!(resValue instanceof ArrayResourceValue)) {
+                    Bridge.getLog().warning(LayoutLog.TAG_UNSUPPORTED,
+                            "Obtaining resource arrays via getTextArray, getStringArray or getIntArray is not fully supported in this version of the IDE.",
+                            null, null);
+                }
+                return resValue;
+            }
+        }
+
+        // id was not found or not resolved. Throw a NotFoundException.
+        throwException(resources, id);
+
+        // this is not used since the method above always throws
+        return null;
+    }
+
+    @Nullable
+    private static String resolveReference(@NonNull Resources resources, @Nullable String value,
+            @NonNull ResourceNamespace contextNamespace,
+            @NonNull ResourceNamespace.Resolver resolver) {
+        if (value != null) {
+            ResourceValue resValue = new UnresolvedResourceValue(value, contextNamespace, resolver);
+            return resolveReference(resources, resValue);
+        }
+        return null;
+    }
+
+    @Nullable
+    private static String resolveReference(@NonNull Resources resources,
+            @NonNull ResourceValue value) {
+        RenderResources renderResources = getContext(resources).getRenderResources();
+        ResourceValue resolvedValue = renderResources.resolveResValue(value);
+        return resolvedValue == null ? null : resolvedValue.getValue();
+    }
+
+    @LayoutlibDelegate
+    static XmlResourceParser getLayout(Resources resources, int id) throws NotFoundException {
+        Pair<String, ResourceValue> v = getResourceValue(resources, id);
+
+        if (v != null) {
+            ResourceValue value = v.getSecond();
+
+            try {
+                BridgeXmlBlockParser parser =
+                        ResourceHelper.getXmlBlockParser(getContext(resources), value);
+                if (parser != null) {
+                    return parser;
+                }
+            } catch (XmlPullParserException e) {
+                Bridge.getLog().error(LayoutLog.TAG_BROKEN,
+                        "Failed to parse " + value.getValue(), e, null, null /*data*/);
+                // we'll return null below.
+            }
+        }
+
+        // id was not found or not resolved. Throw a NotFoundException.
+        throwException(resources, id, "layout");
+
+        // this is not used since the method above always throws
+        return null;
+    }
+
+    @LayoutlibDelegate
+    static XmlResourceParser getAnimation(Resources resources, int id) throws NotFoundException {
+        Pair<String, ResourceValue> v = getResourceValue(resources, id);
+
+        if (v != null) {
+            ResourceValue value = v.getSecond();
+
+            try {
+                return ResourceHelper.getXmlBlockParser(getContext(resources), value);
+            } catch (XmlPullParserException e) {
+                Bridge.getLog().error(LayoutLog.TAG_BROKEN,
+                        "Failed to parse " + value.getValue(), e, null, null /*data*/);
+                // we'll return null below.
+            }
+        }
+
+        // id was not found or not resolved. Throw a NotFoundException.
+        throwException(resources, id);
+
+        // this is not used since the method above always throws
+        return null;
+    }
+
+    @LayoutlibDelegate
+    static TypedArray obtainAttributes(Resources resources, AttributeSet set, int[] attrs) {
+        return getContext(resources).obtainStyledAttributes(set, attrs);
+    }
+
+    @LayoutlibDelegate
+    static TypedArray obtainAttributes(Resources resources, Resources.Theme theme, AttributeSet
+            set, int[] attrs) {
+        return Resources.obtainAttributes_Original(resources, theme, set, attrs);
+    }
+
+    @LayoutlibDelegate
+    static TypedArray obtainTypedArray(Resources resources, int id) throws NotFoundException {
+        BridgeContext context = getContext(resources);
+        ResourceReference reference = context.resolveId(id);
+        RenderResources renderResources = context.getRenderResources();
+        ResourceValue value = renderResources.getResolvedResource(reference);
+
+        if (!(value instanceof ArrayResourceValue)) {
+            throw new NotFoundException("Array resource ID #0x" + Integer.toHexString(id));
+        }
+
+        ArrayResourceValue arrayValue = (ArrayResourceValue) value;
+        int length = arrayValue.getElementCount();
+        ResourceNamespace namespace = arrayValue.getNamespace();
+        BridgeTypedArray typedArray = newTypeArray(resources, length);
+
+        for (int i = 0; i < length; i++) {
+            ResourceValue elementValue;
+            ResourceUrl resourceUrl = ResourceUrl.parse(arrayValue.getElement(i));
+            if (resourceUrl != null) {
+                ResourceReference elementRef =
+                  resourceUrl.resolve(namespace, arrayValue.getNamespaceResolver());
+                elementValue = renderResources.getResolvedResource(elementRef);
+            } else {
+                elementValue = new ResourceValueImpl(namespace, ResourceType.STRING, "element" + i,
+                  arrayValue.getElement(i));
+            }
+            typedArray.bridgeSetValue(i, elementValue.getName(), namespace, i, elementValue);
+        }
+
+        typedArray.sealArray();
+        return typedArray;
+    }
+
+    @LayoutlibDelegate
+    static float getDimension(Resources resources, int id) throws NotFoundException {
+        Pair<String, ResourceValue> value = getResourceValue(resources, id);
+
+        if (value != null) {
+            ResourceValue resValue = value.getSecond();
+
+            assert resValue != null;
+            if (resValue != null) {
+                String v = resValue.getValue();
+                if (v != null) {
+                    if (v.equals(BridgeConstants.MATCH_PARENT) ||
+                            v.equals(BridgeConstants.FILL_PARENT)) {
+                        return LayoutParams.MATCH_PARENT;
+                    } else if (v.equals(BridgeConstants.WRAP_CONTENT)) {
+                        return LayoutParams.WRAP_CONTENT;
+                    }
+                    TypedValue tmpValue = new TypedValue();
+                    if (ResourceHelper.parseFloatAttribute(
+                            value.getFirst(), v, tmpValue, true /*requireUnit*/) &&
+                            tmpValue.type == TypedValue.TYPE_DIMENSION) {
+                        return tmpValue.getDimension(resources.getDisplayMetrics());
+                    }
+                }
+            }
+        }
+
+        // id was not found or not resolved. Throw a NotFoundException.
+        throwException(resources, id);
+
+        // this is not used since the method above always throws
+        return 0;
+    }
+
+    @LayoutlibDelegate
+    static int getDimensionPixelOffset(Resources resources, int id) throws NotFoundException {
+        Pair<String, ResourceValue> value = getResourceValue(resources, id);
+
+        if (value != null) {
+            ResourceValue resValue = value.getSecond();
+
+            assert resValue != null;
+            if (resValue != null) {
+                String v = resValue.getValue();
+                if (v != null) {
+                    TypedValue tmpValue = new TypedValue();
+                    if (ResourceHelper.parseFloatAttribute(
+                            value.getFirst(), v, tmpValue, true /*requireUnit*/) &&
+                            tmpValue.type == TypedValue.TYPE_DIMENSION) {
+                        return TypedValue.complexToDimensionPixelOffset(tmpValue.data,
+                                resources.getDisplayMetrics());
+                    }
+                }
+            }
+        }
+
+        // id was not found or not resolved. Throw a NotFoundException.
+        throwException(resources, id);
+
+        // this is not used since the method above always throws
+        return 0;
+    }
+
+    @LayoutlibDelegate
+    static int getDimensionPixelSize(Resources resources, int id) throws NotFoundException {
+        Pair<String, ResourceValue> value = getResourceValue(resources, id);
+
+        if (value != null) {
+            ResourceValue resValue = value.getSecond();
+
+            assert resValue != null;
+            if (resValue != null) {
+                String v = resValue.getValue();
+                if (v != null) {
+                    TypedValue tmpValue = new TypedValue();
+                    if (ResourceHelper.parseFloatAttribute(
+                            value.getFirst(), v, tmpValue, true /*requireUnit*/) &&
+                            tmpValue.type == TypedValue.TYPE_DIMENSION) {
+                        return TypedValue.complexToDimensionPixelSize(tmpValue.data,
+                                resources.getDisplayMetrics());
+                    }
+                }
+            }
+        }
+
+        // id was not found or not resolved. Throw a NotFoundException.
+        throwException(resources, id);
+
+        // this is not used since the method above always throws
+        return 0;
+    }
+
+    @LayoutlibDelegate
+    static int getInteger(Resources resources, int id) throws NotFoundException {
+        Pair<String, ResourceValue> value = getResourceValue(resources, id);
+
+        if (value != null) {
+            ResourceValue resValue = value.getSecond();
+
+            assert resValue != null;
+            if (resValue != null) {
+                String v = resValue.getValue();
+                if (v != null) {
+                    try {
+                        return getInt(v);
+                    } catch (NumberFormatException e) {
+                        // return exception below
+                    }
+                }
+            }
+        }
+
+        // id was not found or not resolved. Throw a NotFoundException.
+        throwException(resources, id);
+
+        // this is not used since the method above always throws
+        return 0;
+    }
+
+    @LayoutlibDelegate
+    static float getFloat(Resources resources, int id) {
+        Pair<String, ResourceValue> value = getResourceValue(resources, id);
+
+        if (value != null) {
+            ResourceValue resValue = value.getSecond();
+
+            if (resValue != null) {
+                String v = resValue.getValue();
+                if (v != null) {
+                    try {
+                        return Float.parseFloat(v);
+                    } catch (NumberFormatException ignore) {
+                    }
+                }
+            }
+        }
+        return 0;
+    }
+
+    @LayoutlibDelegate
+    static boolean getBoolean(Resources resources, int id) throws NotFoundException {
+        Pair<String, ResourceValue> value = getResourceValue(resources, id);
+
+        if (value != null) {
+            ResourceValue resValue = value.getSecond();
+
+            if (resValue != null) {
+                String v = resValue.getValue();
+                if (v != null) {
+                    return Boolean.parseBoolean(v);
+                }
+            }
+        }
+
+        // id was not found or not resolved. Throw a NotFoundException.
+        throwException(resources, id);
+
+        // this is not used since the method above always throws
+        return false;
+    }
+
+    @LayoutlibDelegate
+    static String getResourceEntryName(Resources resources, int resid) throws NotFoundException {
+        ResourceReference resourceInfo = getResourceInfo(resources, resid);
+        if (resourceInfo != null) {
+            return resourceInfo.getName();
+        }
+        throwException(resid, null);
+        return null;
+    }
+
+    @LayoutlibDelegate
+    static String getResourceName(Resources resources, int resid) throws NotFoundException {
+        ResourceReference resourceInfo = getResourceInfo(resources, resid);
+        if (resourceInfo != null) {
+            String packageName = getPackageName(resourceInfo, resources);
+            return packageName + ':' + resourceInfo.getResourceType().getName() + '/' +
+                    resourceInfo.getName();
+        }
+        throwException(resid, null);
+        return null;
+    }
+
+    @LayoutlibDelegate
+    static String getResourcePackageName(Resources resources, int resid) throws NotFoundException {
+        ResourceReference resourceInfo = getResourceInfo(resources, resid);
+        if (resourceInfo != null) {
+            return getPackageName(resourceInfo, resources);
+        }
+        throwException(resid, null);
+        return null;
+    }
+
+    @LayoutlibDelegate
+    static String getResourceTypeName(Resources resources, int resid) throws NotFoundException {
+        ResourceReference resourceInfo = getResourceInfo(resources, resid);
+        if (resourceInfo != null) {
+            return resourceInfo.getResourceType().getName();
+        }
+        throwException(resid, null);
+        return null;
+    }
+
+    private static String getPackageName(ResourceReference resourceInfo, Resources resources) {
+        String packageName = resourceInfo.getNamespace().getPackageName();
+        if (packageName == null) {
+            packageName = getContext(resources).getPackageName();
+            if (packageName == null) {
+                packageName = SdkConstants.APP_PREFIX;
+            }
+        }
+        return packageName;
+    }
+
+    @LayoutlibDelegate
+    static String getString(Resources resources, int id, Object... formatArgs)
+            throws NotFoundException {
+        String s = getString(resources, id);
+        if (s != null) {
+            return String.format(s, formatArgs);
+
+        }
+
+        // id was not found or not resolved. Throw a NotFoundException.
+        throwException(resources, id);
+
+        // this is not used since the method above always throws
+        return null;
+    }
+
+    @LayoutlibDelegate
+    static String getString(Resources resources, int id) throws NotFoundException {
+        Pair<String, ResourceValue> value = getResourceValue(resources, id);
+
+        if (value != null && value.getSecond().getValue() != null) {
+            return value.getSecond().getValue();
+        }
+
+        // id was not found or not resolved. Throw a NotFoundException.
+        throwException(resources, id);
+
+        // this is not used since the method above always throws
+        return null;
+    }
+
+    @LayoutlibDelegate
+    static String getQuantityString(Resources resources, int id, int quantity) throws
+            NotFoundException {
+        Pair<String, ResourceValue> value = getResourceValue(resources, id);
+
+        if (value != null) {
+            if (value.getSecond() instanceof PluralsResourceValue) {
+                PluralsResourceValue pluralsResourceValue = (PluralsResourceValue) value.getSecond();
+                PluralRules pluralRules = PluralRules.forLocale(resources.getConfiguration().getLocales()
+                        .get(0));
+                String strValue = pluralsResourceValue.getValue(pluralRules.select(quantity));
+                if (strValue == null) {
+                    strValue = pluralsResourceValue.getValue(PluralRules.KEYWORD_OTHER);
+                }
+
+                return strValue;
+            }
+            else {
+                return value.getSecond().getValue();
+            }
+        }
+
+        // id was not found or not resolved. Throw a NotFoundException.
+        throwException(resources, id);
+
+        // this is not used since the method above always throws
+        return null;
+    }
+
+    @LayoutlibDelegate
+    static String getQuantityString(Resources resources, int id, int quantity, Object... formatArgs)
+            throws NotFoundException {
+        String raw = getQuantityString(resources, id, quantity);
+        return String.format(resources.getConfiguration().getLocales().get(0), raw, formatArgs);
+    }
+
+    @LayoutlibDelegate
+    static CharSequence getQuantityText(Resources resources, int id, int quantity) throws
+            NotFoundException {
+        return getQuantityString(resources, id, quantity);
+    }
+
+    @LayoutlibDelegate
+    static Typeface getFont(Resources resources, int id) throws
+            NotFoundException {
+        Pair<String, ResourceValue> value = getResourceValue(resources, id);
+        if (value != null) {
+            return ResourceHelper.getFont(value.getSecond(), getContext(resources), null);
+        }
+
+        throwException(resources, id);
+
+        // this is not used since the method above always throws
+        return null;
+    }
+
+    @LayoutlibDelegate
+    static Typeface getFont(Resources resources, TypedValue outValue, int id) throws
+            NotFoundException {
+        ResourceValue resVal = getResourceValue(resources, id, outValue);
+        if (resVal != null) {
+            return ResourceHelper.getFont(resVal, getContext(resources), null);
+        }
+
+        throwException(resources, id);
+        return null; // This is not used since the method above always throws.
+    }
+
+    @LayoutlibDelegate
+    static void getValue(Resources resources, int id, TypedValue outValue, boolean resolveRefs)
+            throws NotFoundException {
+        getResourceValue(resources, id, outValue);
+    }
+
+    private static ResourceValue getResourceValue(Resources resources, int id, TypedValue outValue)
+            throws NotFoundException {
+        Pair<String, ResourceValue> value = getResourceValue(resources, id);
+
+        if (value != null) {
+            ResourceValue resVal = value.getSecond();
+            String v = resVal != null ? resVal.getValue() : null;
+
+            if (v != null) {
+                if (ResourceHelper.parseFloatAttribute(value.getFirst(), v, outValue,
+                        false /*requireUnit*/)) {
+                    return resVal;
+                }
+                if (resVal instanceof DensityBasedResourceValue) {
+                    outValue.density =
+                            ((DensityBasedResourceValue) resVal).getResourceDensity().getDpiValue();
+                }
+
+                // else it's a string
+                outValue.type = TypedValue.TYPE_STRING;
+                outValue.string = v;
+                return resVal;
+            }
+        }
+
+        // id was not found or not resolved. Throw a NotFoundException.
+        throwException(resources, id);
+        return null; // This is not used since the method above always throws.
+    }
+
+    @LayoutlibDelegate
+    static void getValue(Resources resources, String name, TypedValue outValue, boolean resolveRefs)
+            throws NotFoundException {
+        throw new UnsupportedOperationException();
+    }
+
+    @LayoutlibDelegate
+    static void getValueForDensity(Resources resources, int id, int density, TypedValue outValue,
+            boolean resolveRefs) throws NotFoundException {
+        getValue(resources, id, outValue, resolveRefs);
+    }
+
+    @LayoutlibDelegate
+    static int getAttributeSetSourceResId(@Nullable AttributeSet set) {
+        // Not supported in layoutlib
+        return Resources.ID_NULL;
+    }
+
+    @LayoutlibDelegate
+    static XmlResourceParser getXml(Resources resources, int id) throws NotFoundException {
+        Pair<String, ResourceValue> v = getResourceValue(resources, id);
+
+        if (v != null) {
+            ResourceValue value = v.getSecond();
+
+            try {
+                return ResourceHelper.getXmlBlockParser(getContext(resources), value);
+            } catch (XmlPullParserException e) {
+                Bridge.getLog().error(LayoutLog.TAG_BROKEN,
+                        "Failed to parse " + value.getValue(), e, null, null /*data*/);
+                // we'll return null below.
+            }
+        }
+
+        // id was not found or not resolved. Throw a NotFoundException.
+        throwException(resources, id);
+
+        // this is not used since the method above always throws
+        return null;
+    }
+
+    @LayoutlibDelegate
+    static XmlResourceParser loadXmlResourceParser(Resources resources, int id,
+            String type) throws NotFoundException {
+        return resources.loadXmlResourceParser_Original(id, type);
+    }
+
+    @LayoutlibDelegate
+    static XmlResourceParser loadXmlResourceParser(Resources resources, String file, int id,
+            int assetCookie, String type) throws NotFoundException {
+        // even though we know the XML file to load directly, we still need to resolve the
+        // id so that we can know if it's a platform or project resource.
+        // (mPlatformResouceFlag will get the result and will be used later).
+        Pair<String, ResourceValue> result = getResourceValue(resources, id);
+
+        ResourceNamespace layoutNamespace;
+        if (result != null && result.getSecond() != null) {
+            layoutNamespace = result.getSecond().getNamespace();
+        } else {
+            // We need to pick something, even though the resource system never heard about a layout
+            // with this numeric id.
+            layoutNamespace = ResourceNamespace.RES_AUTO;
+        }
+
+        try {
+            XmlPullParser parser = ParserFactory.create(file);
+            return new BridgeXmlBlockParser(parser, getContext(resources), layoutNamespace);
+        } catch (XmlPullParserException e) {
+            NotFoundException newE = new NotFoundException();
+            newE.initCause(e);
+            throw newE;
+        }
+    }
+
+    @LayoutlibDelegate
+    static InputStream openRawResource(Resources resources, int id) throws NotFoundException {
+        Pair<String, ResourceValue> value = getResourceValue(resources, id);
+
+        if (value != null) {
+            String path = value.getSecond().getValue();
+            if (path != null) {
+                return openRawResource(resources, path);
+            }
+        }
+
+        // id was not found or not resolved. Throw a NotFoundException.
+        throwException(resources, id);
+
+        // this is not used since the method above always throws
+        return null;
+    }
+
+    @LayoutlibDelegate
+    static InputStream openRawResource(Resources resources, int id, TypedValue value)
+            throws NotFoundException {
+        getValue(resources, id, value, true);
+
+        String path = value.string.toString();
+        return openRawResource(resources, path);
+    }
+
+    private static InputStream openRawResource(Resources resources, String path)
+            throws NotFoundException {
+        AssetRepository repository = getAssetRepository(resources);
+        try {
+            InputStream stream = repository.openNonAsset(0, path, ACCESS_STREAMING);
+            if (stream == null) {
+                throw new NotFoundException(path);
+            }
+            // If it's a nine-patch return a custom input stream so that
+            // other methods (mainly bitmap factory) can detect it's a 9-patch
+            // and actually load it as a 9-patch instead of a normal bitmap.
+            if (path.toLowerCase().endsWith(NinePatch.EXTENSION_9PATCH)) {
+                return new NinePatchInputStream(stream);
+            }
+            return stream;
+        } catch (IOException e) {
+            NotFoundException exception = new NotFoundException();
+            exception.initCause(e);
+            throw exception;
+        }
+    }
+
+    @LayoutlibDelegate
+    static AssetFileDescriptor openRawResourceFd(Resources resources, int id)
+            throws NotFoundException {
+        throw new UnsupportedOperationException();
+    }
+
+    @VisibleForTesting
+    @Nullable
+    static ResourceUrl resourceUrlFromName(
+            @NonNull String name, @Nullable String defType, @Nullable String defPackage) {
+        int colonIdx = name.indexOf(':');
+        int slashIdx = name.indexOf('/');
+
+        if (colonIdx != -1 && slashIdx != -1) {
+            // Easy case
+            return ResourceUrl.parse(PREFIX_RESOURCE_REF + name);
+        }
+
+        if (colonIdx == -1 && slashIdx == -1) {
+            if (defType == null) {
+                throw new IllegalArgumentException("name does not define a type an no defType was" +
+                        " passed");
+            }
+
+            // It does not define package or type
+            return ResourceUrl.parse(
+                    PREFIX_RESOURCE_REF + (defPackage != null ? defPackage + ":" : "") + defType +
+                            "/" + name);
+        }
+
+        if (colonIdx != -1) {
+            if (defType == null) {
+                throw new IllegalArgumentException("name does not define a type an no defType was" +
+                        " passed");
+            }
+            // We have package but no type
+            String pkg = name.substring(0, colonIdx);
+            ResourceType type = ResourceType.getEnum(defType);
+            return type != null ? ResourceUrl.create(pkg, type, name.substring(colonIdx + 1)) :
+                    null;
+        }
+
+        ResourceType type = ResourceType.getEnum(name.substring(0, slashIdx));
+        if (type == null) {
+            return null;
+        }
+        // We have type but no package
+        return ResourceUrl.create(defPackage,
+                type,
+                name.substring(slashIdx + 1));
+    }
+
+    @LayoutlibDelegate
+    static int getIdentifier(Resources resources, String name, String defType, String defPackage) {
+        if (name == null) {
+            return 0;
+        }
+
+        ResourceUrl url = resourceUrlFromName(name, defType, defPackage);
+        if (url != null) {
+            if (ANDROID_PKG.equals(url.namespace)) {
+                return Bridge.getResourceId(url.type, url.name);
+            }
+
+            if (getContext(resources).getPackageName().equals(url.namespace)) {
+                return getLayoutlibCallback(resources).getOrGenerateResourceId(
+                        new ResourceReference(ResourceNamespace.RES_AUTO, url.type, url.name));
+            }
+        }
+
+        return 0;
+    }
+
+    /**
+     * Builds and throws a {@link Resources.NotFoundException} based on a resource id and a resource
+     * type.
+     *
+     * @param id the id of the resource
+     * @param expectedType the type of resource that was expected
+     *
+     * @throws NotFoundException
+     */
+    private static void throwException(Resources resources, int id, @Nullable String expectedType)
+            throws NotFoundException {
+        throwException(id, getResourceInfo(resources, id), expectedType);
+    }
+
+    private static void throwException(Resources resources, int id) throws NotFoundException {
+        throwException(resources, id, null);
+    }
+
+    private static void throwException(int id, @Nullable ResourceReference resourceInfo) {
+        throwException(id, resourceInfo, null);
+    }
+    private static void throwException(int id, @Nullable ResourceReference resourceInfo,
+            @Nullable String expectedType) {
+        String message;
+        if (resourceInfo != null) {
+            message = String.format(
+                    "Could not find %1$s resource matching value 0x%2$X (resolved name: %3$s) in current configuration.",
+                    resourceInfo.getResourceType(), id, resourceInfo.getName());
+        } else {
+            message = String.format("Could not resolve resource value: 0x%1$X.", id);
+        }
+
+        if (expectedType != null) {
+            message += " Or the resolved value was not of type " + expectedType + " as expected.";
+        }
+        throw new NotFoundException(message);
+    }
+
+    private static int getInt(String v) throws NumberFormatException {
+        int radix = 10;
+        if (v.startsWith("0x")) {
+            v = v.substring(2);
+            radix = 16;
+        } else if (v.startsWith("0")) {
+            radix = 8;
+        }
+        return Integer.parseInt(v, radix);
+    }
+
+    private static AssetRepository getAssetRepository(Resources resources) {
+        BridgeContext context = getContext(resources);
+        BridgeAssetManager assetManager = context.getAssets();
+        return assetManager.getAssetRepository();
+    }
+}
diff --git a/android/content/res/Resources_Theme_Delegate.java b/android/content/res/Resources_Theme_Delegate.java
new file mode 100644
index 0000000..4740fac
--- /dev/null
+++ b/android/content/res/Resources_Theme_Delegate.java
@@ -0,0 +1,147 @@
+/*
+ * 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.content.res;
+
+import com.android.ide.common.rendering.api.ResourceReference;
+import com.android.ide.common.rendering.api.StyleResourceValue;
+import com.android.layoutlib.bridge.android.BridgeContext;
+import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.layoutlib.bridge.impl.RenderSessionImpl;
+import com.android.resources.ResourceType;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import android.annotation.Nullable;
+import android.content.res.Resources.NotFoundException;
+import android.content.res.Resources.Theme;
+import android.content.res.Resources.ThemeKey;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+
+/**
+ * Delegate used to provide new implementation of a select few methods of {@link Resources.Theme}
+ *
+ * Through the layoutlib_create tool, the original  methods of Theme have been replaced
+ * by calls to methods of the same name in this delegate class.
+ *
+ */
+public class Resources_Theme_Delegate {
+
+    // ---- delegate manager ----
+
+    private static final DelegateManager<Resources_Theme_Delegate> sManager =
+            new DelegateManager<Resources_Theme_Delegate>(Resources_Theme_Delegate.class);
+
+    public static DelegateManager<Resources_Theme_Delegate> getDelegateManager() {
+        return sManager;
+    }
+
+    // ---- delegate methods. ----
+
+    @LayoutlibDelegate
+    /*package*/ static TypedArray obtainStyledAttributes(
+            Resources thisResources, Theme thisTheme,
+            int[] attrs) {
+        boolean changed = setupResources(thisTheme);
+        BridgeTypedArray ta = RenderSessionImpl.getCurrentContext().internalObtainStyledAttributes(
+                0, attrs);
+        ta.setTheme(thisTheme);
+        restoreResources(changed);
+        return ta;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static TypedArray obtainStyledAttributes(
+            Resources thisResources, Theme thisTheme,
+            int resid, int[] attrs)
+            throws NotFoundException {
+        boolean changed = setupResources(thisTheme);
+        BridgeTypedArray ta = RenderSessionImpl.getCurrentContext().internalObtainStyledAttributes(
+                resid, attrs);
+        ta.setTheme(thisTheme);
+        restoreResources(changed);
+        return ta;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static TypedArray obtainStyledAttributes(
+            Resources thisResources, Theme thisTheme,
+            AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes) {
+        boolean changed = setupResources(thisTheme);
+        BridgeTypedArray ta = RenderSessionImpl.getCurrentContext().internalObtainStyledAttributes(set,
+                attrs, defStyleAttr, defStyleRes);
+        ta.setTheme(thisTheme);
+        restoreResources(changed);
+        return ta;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean resolveAttribute(
+            Resources thisResources, Theme thisTheme,
+            int resid, TypedValue outValue,
+            boolean resolveRefs) {
+        boolean changed = setupResources(thisTheme);
+        boolean found =  RenderSessionImpl.getCurrentContext().resolveThemeAttribute(resid,
+                outValue, resolveRefs);
+        restoreResources(changed);
+        return found;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static TypedArray resolveAttributes(Resources thisResources, Theme thisTheme,
+            int[] values, int[] attrs) {
+        // FIXME
+        return null;
+    }
+
+    // ---- private helper methods ----
+
+    private static boolean setupResources(Theme thisTheme) {
+        // Key is a space-separated list of theme ids applied that have been merged into the
+        // BridgeContext's theme to make thisTheme.
+        final ThemeKey key = thisTheme.getKey();
+        final int[] resId = key.mResId;
+        final boolean[] force = key.mForce;
+
+        boolean changed = false;
+        for (int i = 0, N = key.mCount; i < N; i++) {
+            StyleResourceValue style = resolveStyle(resId[i]);
+            if (style != null) {
+                RenderSessionImpl.getCurrentContext().getRenderResources().applyStyle(
+                        style, force[i]);
+                changed = true;
+            }
+
+        }
+        return changed;
+    }
+
+    private static void restoreResources(boolean changed) {
+        if (changed) {
+            RenderSessionImpl.getCurrentContext().getRenderResources().clearStyles();
+        }
+    }
+
+    @Nullable
+    private static StyleResourceValue resolveStyle(int nativeResid) {
+        if (nativeResid == 0) {
+            return null;
+        }
+        BridgeContext context = RenderSessionImpl.getCurrentContext();
+        ResourceReference theme = context.resolveId(nativeResid);
+        return (StyleResourceValue) context.getRenderResources().getResolvedResource(theme);
+    }
+}
diff --git a/android/content/res/StringBlock.java b/android/content/res/StringBlock.java
new file mode 100644
index 0000000..8f3f77b
--- /dev/null
+++ b/android/content/res/StringBlock.java
@@ -0,0 +1,537 @@
+/*
+ * 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.content.res;
+
+import android.compat.annotation.UnsupportedAppUsage;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.Typeface;
+import android.text.Annotation;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.SpannedString;
+import android.text.TextPaint;
+import android.text.TextUtils;
+import android.text.style.AbsoluteSizeSpan;
+import android.text.style.BackgroundColorSpan;
+import android.text.style.BulletSpan;
+import android.text.style.CharacterStyle;
+import android.text.style.ForegroundColorSpan;
+import android.text.style.LineHeightSpan;
+import android.text.style.RelativeSizeSpan;
+import android.text.style.StrikethroughSpan;
+import android.text.style.StyleSpan;
+import android.text.style.SubscriptSpan;
+import android.text.style.SuperscriptSpan;
+import android.text.style.TextAppearanceSpan;
+import android.text.style.TypefaceSpan;
+import android.text.style.URLSpan;
+import android.text.style.UnderlineSpan;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.io.Closeable;
+import java.util.Arrays;
+
+/**
+ * Conveniences for retrieving data out of a compiled string resource.
+ *
+ * {@hide}
+ */
+public final class StringBlock implements Closeable {
+    private static final String TAG = "AssetManager";
+    private static final boolean localLOGV = false;
+
+    private final long mNative;
+    private final boolean mUseSparse;
+    private final boolean mOwnsNative;
+
+    private CharSequence[] mStrings;
+    private SparseArray<CharSequence> mSparseStrings;
+
+    @GuardedBy("this") private boolean mOpen = true;
+
+    StyleIDs mStyleIDs = null;
+
+    public StringBlock(byte[] data, boolean useSparse) {
+        mNative = nativeCreate(data, 0, data.length);
+        mUseSparse = useSparse;
+        mOwnsNative = true;
+        if (localLOGV) Log.v(TAG, "Created string block " + this
+                + ": " + nativeGetSize(mNative));
+    }
+
+    public StringBlock(byte[] data, int offset, int size, boolean useSparse) {
+        mNative = nativeCreate(data, offset, size);
+        mUseSparse = useSparse;
+        mOwnsNative = true;
+        if (localLOGV) Log.v(TAG, "Created string block " + this
+                + ": " + nativeGetSize(mNative));
+    }
+
+    @UnsupportedAppUsage
+    public CharSequence get(int idx) {
+        synchronized (this) {
+            if (mStrings != null) {
+                CharSequence res = mStrings[idx];
+                if (res != null) {
+                    return res;
+                }
+            } else if (mSparseStrings != null) {
+                CharSequence res = mSparseStrings.get(idx);
+                if (res != null) {
+                    return res;
+                }
+            } else {
+                final int num = nativeGetSize(mNative);
+                if (mUseSparse && num > 250) {
+                    mSparseStrings = new SparseArray<CharSequence>();
+                } else {
+                    mStrings = new CharSequence[num];
+                }
+            }
+            String str = nativeGetString(mNative, idx);
+            CharSequence res = str;
+            int[] style = nativeGetStyle(mNative, idx);
+            if (localLOGV) Log.v(TAG, "Got string: " + str);
+            if (localLOGV) Log.v(TAG, "Got styles: " + Arrays.toString(style));
+            if (style != null) {
+                if (mStyleIDs == null) {
+                    mStyleIDs = new StyleIDs();
+                }
+
+                // the style array is a flat array of <type, start, end> hence
+                // the magic constant 3.
+                for (int styleIndex = 0; styleIndex < style.length; styleIndex += 3) {
+                    int styleId = style[styleIndex];
+
+                    if (styleId == mStyleIDs.boldId || styleId == mStyleIDs.italicId
+                            || styleId == mStyleIDs.underlineId || styleId == mStyleIDs.ttId
+                            || styleId == mStyleIDs.bigId || styleId == mStyleIDs.smallId
+                            || styleId == mStyleIDs.subId || styleId == mStyleIDs.supId
+                            || styleId == mStyleIDs.strikeId || styleId == mStyleIDs.listItemId
+                            || styleId == mStyleIDs.marqueeId) {
+                        // id already found skip to next style
+                        continue;
+                    }
+
+                    String styleTag = nativeGetString(mNative, styleId);
+
+                    if (styleTag.equals("b")) {
+                        mStyleIDs.boldId = styleId;
+                    } else if (styleTag.equals("i")) {
+                        mStyleIDs.italicId = styleId;
+                    } else if (styleTag.equals("u")) {
+                        mStyleIDs.underlineId = styleId;
+                    } else if (styleTag.equals("tt")) {
+                        mStyleIDs.ttId = styleId;
+                    } else if (styleTag.equals("big")) {
+                        mStyleIDs.bigId = styleId;
+                    } else if (styleTag.equals("small")) {
+                        mStyleIDs.smallId = styleId;
+                    } else if (styleTag.equals("sup")) {
+                        mStyleIDs.supId = styleId;
+                    } else if (styleTag.equals("sub")) {
+                        mStyleIDs.subId = styleId;
+                    } else if (styleTag.equals("strike")) {
+                        mStyleIDs.strikeId = styleId;
+                    } else if (styleTag.equals("li")) {
+                        mStyleIDs.listItemId = styleId;
+                    } else if (styleTag.equals("marquee")) {
+                        mStyleIDs.marqueeId = styleId;
+                    }
+                }
+
+                res = applyStyles(str, style, mStyleIDs);
+            }
+            if (mStrings != null) mStrings[idx] = res;
+            else mSparseStrings.put(idx, res);
+            return res;
+        }
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            super.finalize();
+        } finally {
+            close();
+        }
+    }
+
+    @Override
+    public void close() {
+        synchronized (this) {
+            if (mOpen) {
+                mOpen = false;
+
+                if (mOwnsNative) {
+                    nativeDestroy(mNative);
+                }
+            }
+        }
+    }
+
+    static final class StyleIDs {
+        private int boldId = -1;
+        private int italicId = -1;
+        private int underlineId = -1;
+        private int ttId = -1;
+        private int bigId = -1;
+        private int smallId = -1;
+        private int subId = -1;
+        private int supId = -1;
+        private int strikeId = -1;
+        private int listItemId = -1;
+        private int marqueeId = -1;
+    }
+
+    private CharSequence applyStyles(String str, int[] style, StyleIDs ids) {
+        if (style.length == 0)
+            return str;
+
+        SpannableString buffer = new SpannableString(str);
+        int i=0;
+        while (i < style.length) {
+            int type = style[i];
+            if (localLOGV) Log.v(TAG, "Applying style span id=" + type
+                    + ", start=" + style[i+1] + ", end=" + style[i+2]);
+
+
+            if (type == ids.boldId) {
+                buffer.setSpan(new StyleSpan(Typeface.BOLD),
+                               style[i+1], style[i+2]+1,
+                               Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+            } else if (type == ids.italicId) {
+                buffer.setSpan(new StyleSpan(Typeface.ITALIC),
+                               style[i+1], style[i+2]+1,
+                               Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+            } else if (type == ids.underlineId) {
+                buffer.setSpan(new UnderlineSpan(),
+                               style[i+1], style[i+2]+1,
+                               Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+            } else if (type == ids.ttId) {
+                buffer.setSpan(new TypefaceSpan("monospace"),
+                               style[i+1], style[i+2]+1,
+                               Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+            } else if (type == ids.bigId) {
+                buffer.setSpan(new RelativeSizeSpan(1.25f),
+                               style[i+1], style[i+2]+1,
+                               Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+            } else if (type == ids.smallId) {
+                buffer.setSpan(new RelativeSizeSpan(0.8f),
+                               style[i+1], style[i+2]+1,
+                               Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+            } else if (type == ids.subId) {
+                buffer.setSpan(new SubscriptSpan(),
+                               style[i+1], style[i+2]+1,
+                               Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+            } else if (type == ids.supId) {
+                buffer.setSpan(new SuperscriptSpan(),
+                               style[i+1], style[i+2]+1,
+                               Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+            } else if (type == ids.strikeId) {
+                buffer.setSpan(new StrikethroughSpan(),
+                               style[i+1], style[i+2]+1,
+                               Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+            } else if (type == ids.listItemId) {
+                addParagraphSpan(buffer, new BulletSpan(10),
+                                style[i+1], style[i+2]+1);
+            } else if (type == ids.marqueeId) {
+                buffer.setSpan(TextUtils.TruncateAt.MARQUEE,
+                               style[i+1], style[i+2]+1,
+                               Spannable.SPAN_INCLUSIVE_INCLUSIVE);
+            } else {
+                String tag = nativeGetString(mNative, type);
+
+                if (tag.startsWith("font;")) {
+                    String sub;
+
+                    sub = subtag(tag, ";height=");
+                    if (sub != null) {
+                        int size = Integer.parseInt(sub);
+                        addParagraphSpan(buffer, new Height(size),
+                                       style[i+1], style[i+2]+1);
+                    }
+
+                    sub = subtag(tag, ";size=");
+                    if (sub != null) {
+                        int size = Integer.parseInt(sub);
+                        buffer.setSpan(new AbsoluteSizeSpan(size, true),
+                                       style[i+1], style[i+2]+1,
+                                       Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+                    }
+
+                    sub = subtag(tag, ";fgcolor=");
+                    if (sub != null) {
+                        buffer.setSpan(getColor(sub, true),
+                                       style[i+1], style[i+2]+1,
+                                       Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+                    }
+
+                    sub = subtag(tag, ";color=");
+                    if (sub != null) {
+                        buffer.setSpan(getColor(sub, true),
+                                style[i+1], style[i+2]+1,
+                                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+                    }
+
+                    sub = subtag(tag, ";bgcolor=");
+                    if (sub != null) {
+                        buffer.setSpan(getColor(sub, false),
+                                       style[i+1], style[i+2]+1,
+                                       Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+                    }
+
+                    sub = subtag(tag, ";face=");
+                    if (sub != null) {
+                        buffer.setSpan(new TypefaceSpan(sub),
+                                style[i+1], style[i+2]+1,
+                                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+                    }
+                } else if (tag.startsWith("a;")) {
+                    String sub;
+
+                    sub = subtag(tag, ";href=");
+                    if (sub != null) {
+                        buffer.setSpan(new URLSpan(sub),
+                                       style[i+1], style[i+2]+1,
+                                       Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+                    }
+                } else if (tag.startsWith("annotation;")) {
+                    int len = tag.length();
+                    int next;
+
+                    for (int t = tag.indexOf(';'); t < len; t = next) {
+                        int eq = tag.indexOf('=', t);
+                        if (eq < 0) {
+                            break;
+                        }
+
+                        next = tag.indexOf(';', eq);
+                        if (next < 0) {
+                            next = len;
+                        }
+
+                        String key = tag.substring(t + 1, eq);
+                        String value = tag.substring(eq + 1, next);
+
+                        buffer.setSpan(new Annotation(key, value),
+                                       style[i+1], style[i+2]+1,
+                                       Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+                    }
+                }
+            }
+
+            i += 3;
+        }
+        return new SpannedString(buffer);
+    }
+
+    /**
+     * Returns a span for the specified color string representation.
+     * If the specified string does not represent a color (null, empty, etc.)
+     * the color black is returned instead.
+     *
+     * @param color The color as a string. Can be a resource reference,
+     *              hexadecimal, octal or a name
+     * @param foreground True if the color will be used as the foreground color,
+     *                   false otherwise
+     *
+     * @return A CharacterStyle
+     *
+     * @see Color#parseColor(String)
+     */
+    private static CharacterStyle getColor(String color, boolean foreground) {
+        int c = 0xff000000;
+
+        if (!TextUtils.isEmpty(color)) {
+            if (color.startsWith("@")) {
+                Resources res = Resources.getSystem();
+                String name = color.substring(1);
+                int colorRes = res.getIdentifier(name, "color", "android");
+                if (colorRes != 0) {
+                    ColorStateList colors = res.getColorStateList(colorRes, null);
+                    if (foreground) {
+                        return new TextAppearanceSpan(null, 0, 0, colors, null);
+                    } else {
+                        c = colors.getDefaultColor();
+                    }
+                }
+            } else {
+                try {
+                    c = Color.parseColor(color);
+                } catch (IllegalArgumentException e) {
+                    c = Color.BLACK;
+                }
+            }
+        }
+
+        if (foreground) {
+            return new ForegroundColorSpan(c);
+        } else {
+            return new BackgroundColorSpan(c);
+        }
+    }
+
+    /**
+     * If a translator has messed up the edges of paragraph-level markup,
+     * fix it to actually cover the entire paragraph that it is attached to
+     * instead of just whatever range they put it on.
+     */
+    private static void addParagraphSpan(Spannable buffer, Object what,
+                                         int start, int end) {
+        int len = buffer.length();
+
+        if (start != 0 && start != len && buffer.charAt(start - 1) != '\n') {
+            for (start--; start > 0; start--) {
+                if (buffer.charAt(start - 1) == '\n') {
+                    break;
+                }
+            }
+        }
+
+        if (end != 0 && end != len && buffer.charAt(end - 1) != '\n') {
+            for (end++; end < len; end++) {
+                if (buffer.charAt(end - 1) == '\n') {
+                    break;
+                }
+            }
+        }
+
+        buffer.setSpan(what, start, end, Spannable.SPAN_PARAGRAPH);
+    }
+
+    private static String subtag(String full, String attribute) {
+        int start = full.indexOf(attribute);
+        if (start < 0) {
+            return null;
+        }
+
+        start += attribute.length();
+        int end = full.indexOf(';', start);
+
+        if (end < 0) {
+            return full.substring(start);
+        } else {
+            return full.substring(start, end);
+        }
+    }
+
+    /**
+     * Forces the text line to be the specified height, shrinking/stretching
+     * the ascent if possible, or the descent if shrinking the ascent further
+     * will make the text unreadable.
+     */
+    private static class Height implements LineHeightSpan.WithDensity {
+        private int mSize;
+        private static float sProportion = 0;
+
+        public Height(int size) {
+            mSize = size;
+        }
+
+        public void chooseHeight(CharSequence text, int start, int end,
+                                 int spanstartv, int v,
+                                 Paint.FontMetricsInt fm) {
+            // Should not get called, at least not by StaticLayout.
+            chooseHeight(text, start, end, spanstartv, v, fm, null);
+        }
+
+        public void chooseHeight(CharSequence text, int start, int end,
+                                 int spanstartv, int v,
+                                 Paint.FontMetricsInt fm, TextPaint paint) {
+            int size = mSize;
+            if (paint != null) {
+                size *= paint.density;
+            }
+
+            if (fm.bottom - fm.top < size) {
+                fm.top = fm.bottom - size;
+                fm.ascent = fm.ascent - size;
+            } else {
+                if (sProportion == 0) {
+                    /*
+                     * Calculate what fraction of the nominal ascent
+                     * the height of a capital letter actually is,
+                     * so that we won't reduce the ascent to less than
+                     * that unless we absolutely have to.
+                     */
+
+                    Paint p = new Paint();
+                    p.setTextSize(100);
+                    Rect r = new Rect();
+                    p.getTextBounds("ABCDEFG", 0, 7, r);
+
+                    sProportion = (r.top) / p.ascent();
+                }
+
+                int need = (int) Math.ceil(-fm.top * sProportion);
+
+                if (size - fm.descent >= need) {
+                    /*
+                     * It is safe to shrink the ascent this much.
+                     */
+
+                    fm.top = fm.bottom - size;
+                    fm.ascent = fm.descent - size;
+                } else if (size >= need) {
+                    /*
+                     * We can't show all the descent, but we can at least
+                     * show all the ascent.
+                     */
+
+                    fm.top = fm.ascent = -need;
+                    fm.bottom = fm.descent = fm.top + size;
+                } else {
+                    /*
+                     * Show as much of the ascent as we can, and no descent.
+                     */
+
+                    fm.top = fm.ascent = -size;
+                    fm.bottom = fm.descent = 0;
+                }
+            }
+        }
+    }
+
+    /**
+     * Create from an existing string block native object.  This is
+     * -extremely- dangerous -- only use it if you absolutely know what you
+     *  are doing!  The given native object must exist for the entire lifetime
+     *  of this newly creating StringBlock.
+     */
+    @UnsupportedAppUsage
+    public StringBlock(long obj, boolean useSparse) {
+        mNative = obj;
+        mUseSparse = useSparse;
+        mOwnsNative = false;
+        if (localLOGV) Log.v(TAG, "Created string block " + this
+                + ": " + nativeGetSize(mNative));
+    }
+
+    private static native long nativeCreate(byte[] data,
+                                                 int offset,
+                                                 int size);
+    private static native int nativeGetSize(long obj);
+    private static native String nativeGetString(long obj, int idx);
+    private static native int[] nativeGetStyle(long obj, int idx);
+    private static native void nativeDestroy(long obj);
+}
diff --git a/android/content/res/ThemedResourceCache.java b/android/content/res/ThemedResourceCache.java
new file mode 100644
index 0000000..3270944
--- /dev/null
+++ b/android/content/res/ThemedResourceCache.java
@@ -0,0 +1,251 @@
+/*
+ * 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.content.res;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.pm.ActivityInfo.Config;
+import android.content.res.Resources.Theme;
+import android.content.res.Resources.ThemeKey;
+import android.util.ArrayMap;
+import android.util.LongSparseArray;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * Data structure used for caching data against themes.
+ *
+ * @param <T> type of data to cache
+ */
+abstract class ThemedResourceCache<T> {
+    @UnsupportedAppUsage
+    private ArrayMap<ThemeKey, LongSparseArray<WeakReference<T>>> mThemedEntries;
+    private LongSparseArray<WeakReference<T>> mUnthemedEntries;
+    private LongSparseArray<WeakReference<T>> mNullThemedEntries;
+
+    /**
+     * Adds a new theme-dependent entry to the cache.
+     *
+     * @param key a key that uniquely identifies the entry
+     * @param theme the theme against which this entry was inflated, or
+     *              {@code null} if the entry has no theme applied
+     * @param entry the entry to cache
+     */
+    public void put(long key, @Nullable Theme theme, @NonNull T entry) {
+        put(key, theme, entry, true);
+    }
+
+    /**
+     * Adds a new entry to the cache.
+     *
+     * @param key a key that uniquely identifies the entry
+     * @param theme the theme against which this entry was inflated, or
+     *              {@code null} if the entry has no theme applied
+     * @param entry the entry to cache
+     * @param usesTheme {@code true} if the entry is affected theme changes,
+     *                  {@code false} otherwise
+     */
+    public void put(long key, @Nullable Theme theme, @NonNull T entry, boolean usesTheme) {
+        if (entry == null) {
+            return;
+        }
+
+        synchronized (this) {
+            final LongSparseArray<WeakReference<T>> entries;
+            if (!usesTheme) {
+                entries = getUnthemedLocked(true);
+            } else {
+                entries = getThemedLocked(theme, true);
+            }
+            if (entries != null) {
+                entries.put(key, new WeakReference<>(entry));
+            }
+        }
+    }
+
+    /**
+     * Returns an entry from the cache.
+     *
+     * @param key a key that uniquely identifies the entry
+     * @param theme the theme where the entry will be used
+     * @return a cached entry, or {@code null} if not in the cache
+     */
+    @Nullable
+    public T get(long key, @Nullable Theme theme) {
+        // The themed (includes null-themed) and unthemed caches are mutually
+        // exclusive, so we'll give priority to whichever one we think we'll
+        // hit first. Since most of the framework drawables are themed, that's
+        // probably going to be the themed cache.
+        synchronized (this) {
+            final LongSparseArray<WeakReference<T>> themedEntries = getThemedLocked(theme, false);
+            if (themedEntries != null) {
+                final WeakReference<T> themedEntry = themedEntries.get(key);
+                if (themedEntry != null) {
+                    return themedEntry.get();
+                }
+            }
+
+            final LongSparseArray<WeakReference<T>> unthemedEntries = getUnthemedLocked(false);
+            if (unthemedEntries != null) {
+                final WeakReference<T> unthemedEntry = unthemedEntries.get(key);
+                if (unthemedEntry != null) {
+                    return unthemedEntry.get();
+                }
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Prunes cache entries that have been invalidated by a configuration
+     * change.
+     *
+     * @param configChanges a bitmask of configuration changes
+     */
+    @UnsupportedAppUsage
+    public void onConfigurationChange(@Config int configChanges) {
+        prune(configChanges);
+    }
+
+    /**
+     * Returns whether a cached entry has been invalidated by a configuration
+     * change.
+     *
+     * @param entry a cached entry
+     * @param configChanges a non-zero bitmask of configuration changes
+     * @return {@code true} if the entry is invalid, {@code false} otherwise
+     */
+    protected abstract boolean shouldInvalidateEntry(@NonNull T entry, int configChanges);
+
+    /**
+     * Returns the cached data for the specified theme, optionally creating a
+     * new entry if one does not already exist.
+     *
+     * @param t the theme for which to return cached data
+     * @param create {@code true} to create an entry if one does not already
+     *               exist, {@code false} otherwise
+     * @return the cached data for the theme, or {@code null} if the cache is
+     *         empty and {@code create} was {@code false}
+     */
+    @Nullable
+    private LongSparseArray<WeakReference<T>> getThemedLocked(@Nullable Theme t, boolean create) {
+        if (t == null) {
+            if (mNullThemedEntries == null && create) {
+                mNullThemedEntries = new LongSparseArray<>(1);
+            }
+            return mNullThemedEntries;
+        }
+
+        if (mThemedEntries == null) {
+            if (create) {
+                mThemedEntries = new ArrayMap<>(1);
+            } else {
+                return null;
+            }
+        }
+
+        final ThemeKey key = t.getKey();
+        LongSparseArray<WeakReference<T>> cache = mThemedEntries.get(key);
+        if (cache == null && create) {
+            cache = new LongSparseArray<>(1);
+
+            final ThemeKey keyClone = key.clone();
+            mThemedEntries.put(keyClone, cache);
+        }
+
+        return cache;
+    }
+
+    /**
+     * Returns the theme-agnostic cached data.
+     *
+     * @param create {@code true} to create an entry if one does not already
+     *               exist, {@code false} otherwise
+     * @return the theme-agnostic cached data, or {@code null} if the cache is
+     *         empty and {@code create} was {@code false}
+     */
+    @Nullable
+    private LongSparseArray<WeakReference<T>> getUnthemedLocked(boolean create) {
+        if (mUnthemedEntries == null && create) {
+            mUnthemedEntries = new LongSparseArray<>(1);
+        }
+        return mUnthemedEntries;
+    }
+
+    /**
+     * Prunes cache entries affected by configuration changes or where weak
+     * references have expired.
+     *
+     * @param configChanges a bitmask of configuration changes, or {@code 0} to
+     *                      simply prune missing weak references
+     * @return {@code true} if the cache is completely empty after pruning
+     */
+    private boolean prune(@Config int configChanges) {
+        synchronized (this) {
+            if (mThemedEntries != null) {
+                for (int i = mThemedEntries.size() - 1; i >= 0; i--) {
+                    if (pruneEntriesLocked(mThemedEntries.valueAt(i), configChanges)) {
+                        mThemedEntries.removeAt(i);
+                    }
+                }
+            }
+
+            pruneEntriesLocked(mNullThemedEntries, configChanges);
+            pruneEntriesLocked(mUnthemedEntries, configChanges);
+
+            return mThemedEntries == null && mNullThemedEntries == null
+                    && mUnthemedEntries == null;
+        }
+    }
+
+    private boolean pruneEntriesLocked(@Nullable LongSparseArray<WeakReference<T>> entries,
+            @Config int configChanges) {
+        if (entries == null) {
+            return true;
+        }
+
+        for (int i = entries.size() - 1; i >= 0; i--) {
+            final WeakReference<T> ref = entries.valueAt(i);
+            if (ref == null || pruneEntryLocked(ref.get(), configChanges)) {
+                entries.removeAt(i);
+            }
+        }
+
+        return entries.size() == 0;
+    }
+
+    private boolean pruneEntryLocked(@Nullable T entry, @Config int configChanges) {
+        return entry == null || (configChanges != 0
+                && shouldInvalidateEntry(entry, configChanges));
+    }
+
+    public synchronized void clear() {
+        if (mThemedEntries != null) {
+            mThemedEntries.clear();
+        }
+
+        if (mUnthemedEntries != null) {
+            mUnthemedEntries.clear();
+        }
+
+        if (mNullThemedEntries != null) {
+            mNullThemedEntries.clear();
+        }
+    }
+}
diff --git a/android/content/res/TypedArray.java b/android/content/res/TypedArray.java
new file mode 100644
index 0000000..29c5c93
--- /dev/null
+++ b/android/content/res/TypedArray.java
@@ -0,0 +1,1390 @@
+/*
+ * 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.content.res;
+
+import android.annotation.AnyRes;
+import android.annotation.ColorInt;
+import android.annotation.Nullable;
+import android.annotation.StyleableRes;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ActivityInfo.Config;
+import android.graphics.Typeface;
+import android.graphics.drawable.Drawable;
+import android.os.StrictMode;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
+
+import com.android.internal.util.XmlUtils;
+
+import dalvik.system.VMRuntime;
+
+import java.util.Arrays;
+
+/**
+ * Container for an array of values that were retrieved with
+ * {@link Resources.Theme#obtainStyledAttributes(AttributeSet, int[], int, int)}
+ * or {@link Resources#obtainAttributes}.  Be
+ * sure to call {@link #recycle} when done with them.
+ *
+ * The indices used to retrieve values from this structure correspond to
+ * the positions of the attributes given to obtainStyledAttributes.
+ */
+public class TypedArray {
+
+    static TypedArray obtain(Resources res, int len) {
+        TypedArray attrs = res.mTypedArrayPool.acquire();
+        if (attrs == null) {
+            attrs = new TypedArray(res);
+        }
+
+        attrs.mRecycled = false;
+        // Reset the assets, which may have changed due to configuration changes
+        // or further resource loading.
+        attrs.mAssets = res.getAssets();
+        attrs.mMetrics = res.getDisplayMetrics();
+        attrs.resize(len);
+        return attrs;
+    }
+
+    // STYLE_ prefixed constants are offsets within the typed data array.
+    // Keep this in sync with libs/androidfw/include/androidfw/AttributeResolution.h
+    static final int STYLE_NUM_ENTRIES = 7;
+    static final int STYLE_TYPE = 0;
+    static final int STYLE_DATA = 1;
+    static final int STYLE_ASSET_COOKIE = 2;
+    static final int STYLE_RESOURCE_ID = 3;
+    static final int STYLE_CHANGING_CONFIGURATIONS = 4;
+    static final int STYLE_DENSITY = 5;
+    static final int STYLE_SOURCE_RESOURCE_ID = 6;
+
+    @UnsupportedAppUsage
+    private final Resources mResources;
+    @UnsupportedAppUsage
+    private DisplayMetrics mMetrics;
+    @UnsupportedAppUsage
+    private AssetManager mAssets;
+
+    @UnsupportedAppUsage
+    private boolean mRecycled;
+
+    @UnsupportedAppUsage
+    /*package*/ XmlBlock.Parser mXml;
+    @UnsupportedAppUsage
+    /*package*/ Resources.Theme mTheme;
+    /**
+     * mData is used to hold the value/id and other metadata about each attribute.
+     *
+     * [type, data, asset cookie, resource id, changing configuration, density]
+     *
+     * type - type of this attribute, see TypedValue#TYPE_*
+     *
+     * data - can be used in various ways:
+     *     a) actual value of the attribute if type is between #TYPE_FIRST_INT and #TYPE_LAST_INT
+     *        1) color represented by an integer (#TYPE_INT_COLOR_*)
+     *        2) boolean represented by an integer (#TYPE_INT_BOOLEAN)
+     *        3) integer number (#TYPE_TYPE_INT_DEC or #TYPE_INT_HEX)
+     *        4) float number where integer gets interpreted as float (#TYPE_FLOAT, #TYPE_FRACTION
+     *            and #TYPE_DIMENSION)
+     *     b) index into string block inside AssetManager (#TYPE_STRING)
+     *     c) attribute resource id in the current theme/style (#TYPE_ATTRIBUTE)
+     *
+     * asset cookie - used in two ways:
+     *     a) for strings, drawables, and fonts it specifies the set of apk assets to look at
+     *     (multi-apk case)
+     *     b) cookie + asset as a unique identifier for drawable caches
+     *
+     * resource id - id that was finally used to resolve this attribute
+     *
+     * changing configuration - a mask of the configuration parameters for which the values in this
+     * attribute may change
+     *
+     * density - density of drawable pointed to by this attribute
+     */
+    @UnsupportedAppUsage
+    /*package*/ int[] mData;
+    /**
+     * Pointer to the start of the memory address of mData. It is passed via JNI and used to write
+     * to mData array directly from native code (AttributeResolution.cpp).
+     */
+    /*package*/ long mDataAddress;
+    @UnsupportedAppUsage
+    /*package*/ int[] mIndices;
+    /**
+     * Similar to mDataAddress, but instead it is a pointer to mIndices address.
+     */
+    /*package*/ long mIndicesAddress;
+    @UnsupportedAppUsage
+    /*package*/ int mLength;
+    @UnsupportedAppUsage
+    /*package*/ TypedValue mValue = new TypedValue();
+
+    private void resize(int len) {
+        mLength = len;
+        final int dataLen = len * STYLE_NUM_ENTRIES;
+        final int indicesLen = len + 1;
+        final VMRuntime runtime = VMRuntime.getRuntime();
+        if (mDataAddress == 0 || mData.length < dataLen) {
+            mData = (int[]) runtime.newNonMovableArray(int.class, dataLen);
+            mDataAddress = runtime.addressOf(mData);
+            mIndices = (int[]) runtime.newNonMovableArray(int.class, indicesLen);
+            mIndicesAddress = runtime.addressOf(mIndices);
+        }
+    }
+
+    /**
+     * Returns the number of values in this array.
+     *
+     * @throws RuntimeException if the TypedArray has already been recycled.
+     */
+    public int length() {
+        if (mRecycled) {
+            throw new RuntimeException("Cannot make calls to a recycled instance!");
+        }
+
+        return mLength;
+    }
+
+    /**
+     * Returns the number of indices in the array that actually have data. Attributes with a value
+     * of @empty are included, as this is an explicit indicator.
+     *
+     * @throws RuntimeException if the TypedArray has already been recycled.
+     */
+    public int getIndexCount() {
+        if (mRecycled) {
+            throw new RuntimeException("Cannot make calls to a recycled instance!");
+        }
+
+        return mIndices[0];
+    }
+
+    /**
+     * Returns an index in the array that has data. Attributes with a value of @empty are included,
+     * as this is an explicit indicator.
+     *
+     * @param at The index you would like to returned, ranging from 0 to
+     *           {@link #getIndexCount()}.
+     *
+     * @return The index at the given offset, which can be used with
+     *         {@link #getValue} and related APIs.
+     * @throws RuntimeException if the TypedArray has already been recycled.
+     */
+    public int getIndex(int at) {
+        if (mRecycled) {
+            throw new RuntimeException("Cannot make calls to a recycled instance!");
+        }
+
+        return mIndices[1+at];
+    }
+
+    /**
+     * Returns the Resources object this array was loaded from.
+     *
+     * @throws RuntimeException if the TypedArray has already been recycled.
+     */
+    public Resources getResources() {
+        if (mRecycled) {
+            throw new RuntimeException("Cannot make calls to a recycled instance!");
+        }
+
+        return mResources;
+    }
+
+    /**
+     * Retrieves the styled string value for the attribute at <var>index</var>.
+     * <p>
+     * If the attribute is not a string, this method will attempt to coerce
+     * it to a string.
+     *
+     * @param index Index of attribute to retrieve.
+     *
+     * @return CharSequence holding string data. May be styled. Returns
+     *         {@code null} if the attribute is not defined or could not be
+     *         coerced to a string.
+     * @throws RuntimeException if the TypedArray has already been recycled.
+     */
+    public CharSequence getText(@StyleableRes int index) {
+        if (mRecycled) {
+            throw new RuntimeException("Cannot make calls to a recycled instance!");
+        }
+
+        index *= STYLE_NUM_ENTRIES;
+        final int[] data = mData;
+        final int type = data[index + STYLE_TYPE];
+        if (type == TypedValue.TYPE_NULL) {
+            return null;
+        } else if (type == TypedValue.TYPE_STRING) {
+            return loadStringValueAt(index);
+        }
+
+        final TypedValue v = mValue;
+        if (getValueAt(index, v)) {
+            return v.coerceToString();
+        }
+
+        // We already checked for TYPE_NULL. This should never happen.
+        throw new RuntimeException("getText of bad type: 0x" + Integer.toHexString(type));
+    }
+
+    /**
+     * Retrieves the string value for the attribute at <var>index</var>.
+     * <p>
+     * If the attribute is not a string, this method will attempt to coerce
+     * it to a string.
+     *
+     * @param index Index of attribute to retrieve.
+     *
+     * @return String holding string data. Any styling information is removed.
+     *         Returns {@code null} if the attribute is not defined or could
+     *         not be coerced to a string.
+     * @throws RuntimeException if the TypedArray has already been recycled.
+     */
+    @Nullable
+    public String getString(@StyleableRes int index) {
+        if (mRecycled) {
+            throw new RuntimeException("Cannot make calls to a recycled instance!");
+        }
+
+        index *= STYLE_NUM_ENTRIES;
+        final int[] data = mData;
+        final int type = data[index + STYLE_TYPE];
+        if (type == TypedValue.TYPE_NULL) {
+            return null;
+        } else if (type == TypedValue.TYPE_STRING) {
+            return loadStringValueAt(index).toString();
+        }
+
+        final TypedValue v = mValue;
+        if (getValueAt(index, v)) {
+            final CharSequence cs = v.coerceToString();
+            return cs != null ? cs.toString() : null;
+        }
+
+        // We already checked for TYPE_NULL. This should never happen.
+        throw new RuntimeException("getString of bad type: 0x" + Integer.toHexString(type));
+    }
+
+    /**
+     * Retrieves the string value for the attribute at <var>index</var>, but
+     * only if that string comes from an immediate value in an XML file.  That
+     * is, this does not allow references to string resources, string
+     * attributes, or conversions from other types.  As such, this method
+     * will only return strings for TypedArray objects that come from
+     * attributes in an XML file.
+     *
+     * @param index Index of attribute to retrieve.
+     *
+     * @return String holding string data. Any styling information is removed.
+     *         Returns {@code null} if the attribute is not defined or is not
+     *         an immediate string value.
+     * @throws RuntimeException if the TypedArray has already been recycled.
+     */
+    public String getNonResourceString(@StyleableRes int index) {
+        if (mRecycled) {
+            throw new RuntimeException("Cannot make calls to a recycled instance!");
+        }
+
+        index *= STYLE_NUM_ENTRIES;
+        final int[] data = mData;
+        final int type = data[index + STYLE_TYPE];
+        if (type == TypedValue.TYPE_STRING) {
+            final int cookie = data[index + STYLE_ASSET_COOKIE];
+            if (cookie < 0) {
+                return mXml.getPooledString(data[index + STYLE_DATA]).toString();
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Retrieves the string value for the attribute at <var>index</var> that is
+     * not allowed to change with the given configurations.
+     *
+     * @param index Index of attribute to retrieve.
+     * @param allowedChangingConfigs Bit mask of configurations from
+     *        {@link Configuration}.NATIVE_CONFIG_* that are allowed to change.
+     *
+     * @return String holding string data. Any styling information is removed.
+     *         Returns {@code null} if the attribute is not defined.
+     * @throws RuntimeException if the TypedArray has already been recycled.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public String getNonConfigurationString(@StyleableRes int index,
+            @Config int allowedChangingConfigs) {
+        if (mRecycled) {
+            throw new RuntimeException("Cannot make calls to a recycled instance!");
+        }
+
+        index *= STYLE_NUM_ENTRIES;
+        final int[] data = mData;
+        final int type = data[index + STYLE_TYPE];
+        final @Config int changingConfigs = ActivityInfo.activityInfoConfigNativeToJava(
+                data[index + STYLE_CHANGING_CONFIGURATIONS]);
+        if ((changingConfigs & ~allowedChangingConfigs) != 0) {
+            return null;
+        }
+        if (type == TypedValue.TYPE_NULL) {
+            return null;
+        } else if (type == TypedValue.TYPE_STRING) {
+            return loadStringValueAt(index).toString();
+        }
+
+        final TypedValue v = mValue;
+        if (getValueAt(index, v)) {
+            final CharSequence cs = v.coerceToString();
+            return cs != null ? cs.toString() : null;
+        }
+
+        // We already checked for TYPE_NULL. This should never happen.
+        throw new RuntimeException("getNonConfigurationString of bad type: 0x"
+                + Integer.toHexString(type));
+    }
+
+    /**
+     * Retrieve the boolean value for the attribute at <var>index</var>.
+     * <p>
+     * If the attribute is an integer value, this method returns false if the
+     * attribute is equal to zero, and true otherwise.
+     * If the attribute is not a boolean or integer value,
+     * this method will attempt to coerce it to an integer using
+     * {@link Integer#decode(String)} and return whether it is equal to zero.
+     *
+     * @param index Index of attribute to retrieve.
+     * @param defValue Value to return if the attribute is not defined or
+     *                 cannot be coerced to an integer.
+     *
+     * @return Boolean value of the attribute, or defValue if the attribute was
+     *         not defined or could not be coerced to an integer.
+     * @throws RuntimeException if the TypedArray has already been recycled.
+     */
+    public boolean getBoolean(@StyleableRes int index, boolean defValue) {
+        if (mRecycled) {
+            throw new RuntimeException("Cannot make calls to a recycled instance!");
+        }
+
+        index *= STYLE_NUM_ENTRIES;
+        final int[] data = mData;
+        final int type = data[index + STYLE_TYPE];
+        if (type == TypedValue.TYPE_NULL) {
+            return defValue;
+        } else if (type >= TypedValue.TYPE_FIRST_INT
+                && type <= TypedValue.TYPE_LAST_INT) {
+            return data[index + STYLE_DATA] != 0;
+        }
+
+        final TypedValue v = mValue;
+        if (getValueAt(index, v)) {
+            StrictMode.noteResourceMismatch(v);
+            return XmlUtils.convertValueToBoolean(v.coerceToString(), defValue);
+        }
+
+        // We already checked for TYPE_NULL. This should never happen.
+        throw new RuntimeException("getBoolean of bad type: 0x" + Integer.toHexString(type));
+    }
+
+    /**
+     * Retrieve the integer value for the attribute at <var>index</var>.
+     * <p>
+     * If the attribute is not an integer, this method will attempt to coerce
+     * it to an integer using {@link Integer#decode(String)}.
+     *
+     * @param index Index of attribute to retrieve.
+     * @param defValue Value to return if the attribute is not defined or
+     *                 cannot be coerced to an integer.
+     *
+     * @return Integer value of the attribute, or defValue if the attribute was
+     *         not defined or could not be coerced to an integer.
+     * @throws RuntimeException if the TypedArray has already been recycled.
+     */
+    public int getInt(@StyleableRes int index, int defValue) {
+        if (mRecycled) {
+            throw new RuntimeException("Cannot make calls to a recycled instance!");
+        }
+
+        index *= STYLE_NUM_ENTRIES;
+        final int[] data = mData;
+        final int type = data[index + STYLE_TYPE];
+        if (type == TypedValue.TYPE_NULL) {
+            return defValue;
+        } else if (type >= TypedValue.TYPE_FIRST_INT
+                && type <= TypedValue.TYPE_LAST_INT) {
+            return data[index + STYLE_DATA];
+        }
+
+        final TypedValue v = mValue;
+        if (getValueAt(index, v)) {
+            StrictMode.noteResourceMismatch(v);
+            return XmlUtils.convertValueToInt(v.coerceToString(), defValue);
+        }
+
+        // We already checked for TYPE_NULL. This should never happen.
+        throw new RuntimeException("getInt of bad type: 0x" + Integer.toHexString(type));
+    }
+
+    /**
+     * Retrieve the float value for the attribute at <var>index</var>.
+     * <p>
+     * If the attribute is not a float or an integer, this method will attempt
+     * to coerce it to a float using {@link Float#parseFloat(String)}.
+     *
+     * @param index Index of attribute to retrieve.
+     *
+     * @return Attribute float value, or defValue if the attribute was
+     *         not defined or could not be coerced to a float.
+     * @throws RuntimeException if the TypedArray has already been recycled.
+     */
+    public float getFloat(@StyleableRes int index, float defValue) {
+        if (mRecycled) {
+            throw new RuntimeException("Cannot make calls to a recycled instance!");
+        }
+
+        index *= STYLE_NUM_ENTRIES;
+        final int[] data = mData;
+        final int type = data[index + STYLE_TYPE];
+        if (type == TypedValue.TYPE_NULL) {
+            return defValue;
+        } else if (type == TypedValue.TYPE_FLOAT) {
+            return Float.intBitsToFloat(data[index + STYLE_DATA]);
+        } else if (type >= TypedValue.TYPE_FIRST_INT
+                && type <= TypedValue.TYPE_LAST_INT) {
+            return data[index + STYLE_DATA];
+        }
+
+        final TypedValue v = mValue;
+        if (getValueAt(index, v)) {
+            final CharSequence str = v.coerceToString();
+            if (str != null) {
+                StrictMode.noteResourceMismatch(v);
+                return Float.parseFloat(str.toString());
+            }
+        }
+
+        // We already checked for TYPE_NULL. This should never happen.
+        throw new RuntimeException("getFloat of bad type: 0x" + Integer.toHexString(type));
+    }
+
+    /**
+     * Retrieve the color value for the attribute at <var>index</var>.  If
+     * the attribute references a color resource holding a complex
+     * {@link android.content.res.ColorStateList}, then the default color from
+     * the set is returned.
+     * <p>
+     * This method will throw an exception if the attribute is defined but is
+     * not an integer color or color state list.
+     *
+     * @param index Index of attribute to retrieve.
+     * @param defValue Value to return if the attribute is not defined or
+     *                 not a resource.
+     *
+     * @return Attribute color value, or defValue if not defined.
+     * @throws RuntimeException if the TypedArray has already been recycled.
+     * @throws UnsupportedOperationException if the attribute is defined but is
+     *         not an integer color or color state list.
+     */
+    @ColorInt
+    public int getColor(@StyleableRes int index, @ColorInt int defValue) {
+        if (mRecycled) {
+            throw new RuntimeException("Cannot make calls to a recycled instance!");
+        }
+
+        final int attrIndex = index;
+        index *= STYLE_NUM_ENTRIES;
+
+        final int[] data = mData;
+        final int type = data[index + STYLE_TYPE];
+        if (type == TypedValue.TYPE_NULL) {
+            return defValue;
+        } else if (type >= TypedValue.TYPE_FIRST_INT
+                && type <= TypedValue.TYPE_LAST_INT) {
+            return data[index + STYLE_DATA];
+        } else if (type == TypedValue.TYPE_STRING) {
+            final TypedValue value = mValue;
+            if (getValueAt(index, value)) {
+                final ColorStateList csl = mResources.loadColorStateList(
+                        value, value.resourceId, mTheme);
+                return csl.getDefaultColor();
+            }
+            return defValue;
+        } else if (type == TypedValue.TYPE_ATTRIBUTE) {
+            final TypedValue value = mValue;
+            getValueAt(index, value);
+            throw new UnsupportedOperationException(
+                    "Failed to resolve attribute at index " + attrIndex + ": " + value);
+        }
+
+        throw new UnsupportedOperationException("Can't convert value at index " + attrIndex
+                + " to color: type=0x" + Integer.toHexString(type));
+    }
+
+    /**
+     * Retrieve the ComplexColor for the attribute at <var>index</var>.
+     * The value may be either a {@link android.content.res.ColorStateList} which can wrap a simple
+     * color value or a {@link android.content.res.GradientColor}
+     * <p>
+     * This method will return {@code null} if the attribute is not defined or
+     * is not an integer color, color state list or GradientColor.
+     *
+     * @param index Index of attribute to retrieve.
+     *
+     * @return ComplexColor for the attribute, or {@code null} if not defined.
+     * @throws RuntimeException if the attribute if the TypedArray has already
+     *         been recycled.
+     * @throws UnsupportedOperationException if the attribute is defined but is
+     *         not an integer color, color state list or GradientColor.
+     * @hide
+     */
+    @Nullable
+    public ComplexColor getComplexColor(@StyleableRes int index) {
+        if (mRecycled) {
+            throw new RuntimeException("Cannot make calls to a recycled instance!");
+        }
+
+        final TypedValue value = mValue;
+        if (getValueAt(index * STYLE_NUM_ENTRIES, value)) {
+            if (value.type == TypedValue.TYPE_ATTRIBUTE) {
+                throw new UnsupportedOperationException(
+                        "Failed to resolve attribute at index " + index + ": " + value);
+            }
+            return mResources.loadComplexColor(value, value.resourceId, mTheme);
+        }
+        return null;
+    }
+
+    /**
+     * Retrieve the ColorStateList for the attribute at <var>index</var>.
+     * The value may be either a single solid color or a reference to
+     * a color or complex {@link android.content.res.ColorStateList}
+     * description.
+     * <p>
+     * This method will return {@code null} if the attribute is not defined or
+     * is not an integer color or color state list.
+     *
+     * @param index Index of attribute to retrieve.
+     *
+     * @return ColorStateList for the attribute, or {@code null} if not
+     *         defined.
+     * @throws RuntimeException if the attribute if the TypedArray has already
+     *         been recycled.
+     * @throws UnsupportedOperationException if the attribute is defined but is
+     *         not an integer color or color state list.
+     */
+    @Nullable
+    public ColorStateList getColorStateList(@StyleableRes int index) {
+        if (mRecycled) {
+            throw new RuntimeException("Cannot make calls to a recycled instance!");
+        }
+
+        final TypedValue value = mValue;
+        if (getValueAt(index * STYLE_NUM_ENTRIES, value)) {
+            if (value.type == TypedValue.TYPE_ATTRIBUTE) {
+                throw new UnsupportedOperationException(
+                        "Failed to resolve attribute at index " + index + ": " + value);
+            }
+            return mResources.loadColorStateList(value, value.resourceId, mTheme);
+        }
+        return null;
+    }
+
+    /**
+     * Retrieve the integer value for the attribute at <var>index</var>.
+     * <p>
+     * Unlike {@link #getInt(int, int)}, this method will throw an exception if
+     * the attribute is defined but is not an integer.
+     *
+     * @param index Index of attribute to retrieve.
+     * @param defValue Value to return if the attribute is not defined or
+     *                 not a resource.
+     *
+     * @return Attribute integer value, or defValue if not defined.
+     * @throws RuntimeException if the TypedArray has already been recycled.
+     * @throws UnsupportedOperationException if the attribute is defined but is
+     *         not an integer.
+     */
+    public int getInteger(@StyleableRes int index, int defValue) {
+        if (mRecycled) {
+            throw new RuntimeException("Cannot make calls to a recycled instance!");
+        }
+
+        final int attrIndex = index;
+        index *= STYLE_NUM_ENTRIES;
+
+        final int[] data = mData;
+        final int type = data[index + STYLE_TYPE];
+        if (type == TypedValue.TYPE_NULL) {
+            return defValue;
+        } else if (type >= TypedValue.TYPE_FIRST_INT
+                && type <= TypedValue.TYPE_LAST_INT) {
+            return data[index + STYLE_DATA];
+        } else if (type == TypedValue.TYPE_ATTRIBUTE) {
+            final TypedValue value = mValue;
+            getValueAt(index, value);
+            throw new UnsupportedOperationException(
+                    "Failed to resolve attribute at index " + attrIndex + ": " + value);
+        }
+
+        throw new UnsupportedOperationException("Can't convert value at index " + attrIndex
+                + " to integer: type=0x" + Integer.toHexString(type));
+    }
+
+    /**
+     * Retrieve a dimensional unit attribute at <var>index</var>. Unit
+     * conversions are based on the current {@link DisplayMetrics}
+     * associated with the resources this {@link TypedArray} object
+     * came from.
+     * <p>
+     * This method will throw an exception if the attribute is defined but is
+     * not a dimension.
+     *
+     * @param index Index of attribute to retrieve.
+     * @param defValue Value to return if the attribute is not defined or
+     *                 not a resource.
+     *
+     * @return Attribute dimension value multiplied by the appropriate
+     *         metric, or defValue if not defined.
+     * @throws RuntimeException if the TypedArray has already been recycled.
+     * @throws UnsupportedOperationException if the attribute is defined but is
+     *         not an integer.
+     *
+     * @see #getDimensionPixelOffset
+     * @see #getDimensionPixelSize
+     */
+    public float getDimension(@StyleableRes int index, float defValue) {
+        if (mRecycled) {
+            throw new RuntimeException("Cannot make calls to a recycled instance!");
+        }
+
+        final int attrIndex = index;
+        index *= STYLE_NUM_ENTRIES;
+
+        final int[] data = mData;
+        final int type = data[index + STYLE_TYPE];
+        if (type == TypedValue.TYPE_NULL) {
+            return defValue;
+        } else if (type == TypedValue.TYPE_DIMENSION) {
+            return TypedValue.complexToDimension(data[index + STYLE_DATA], mMetrics);
+        } else if (type == TypedValue.TYPE_ATTRIBUTE) {
+            final TypedValue value = mValue;
+            getValueAt(index, value);
+            throw new UnsupportedOperationException(
+                    "Failed to resolve attribute at index " + attrIndex + ": " + value);
+        }
+
+        throw new UnsupportedOperationException("Can't convert value at index " + attrIndex
+                + " to dimension: type=0x" + Integer.toHexString(type));
+    }
+
+    /**
+     * Retrieve a dimensional unit attribute at <var>index</var> for use
+     * as an offset in raw pixels.  This is the same as
+     * {@link #getDimension}, except the returned value is converted to
+     * integer pixels for you.  An offset conversion involves simply
+     * truncating the base value to an integer.
+     * <p>
+     * This method will throw an exception if the attribute is defined but is
+     * not a dimension.
+     *
+     * @param index Index of attribute to retrieve.
+     * @param defValue Value to return if the attribute is not defined or
+     *                 not a resource.
+     *
+     * @return Attribute dimension value multiplied by the appropriate
+     *         metric and truncated to integer pixels, or defValue if not defined.
+     * @throws RuntimeException if the TypedArray has already been recycled.
+     * @throws UnsupportedOperationException if the attribute is defined but is
+     *         not an integer.
+     *
+     * @see #getDimension
+     * @see #getDimensionPixelSize
+     */
+    public int getDimensionPixelOffset(@StyleableRes int index, int defValue) {
+        if (mRecycled) {
+            throw new RuntimeException("Cannot make calls to a recycled instance!");
+        }
+
+        final int attrIndex = index;
+        index *= STYLE_NUM_ENTRIES;
+
+        final int[] data = mData;
+        final int type = data[index + STYLE_TYPE];
+        if (type == TypedValue.TYPE_NULL) {
+            return defValue;
+        } else if (type == TypedValue.TYPE_DIMENSION) {
+            return TypedValue.complexToDimensionPixelOffset(data[index + STYLE_DATA], mMetrics);
+        } else if (type == TypedValue.TYPE_ATTRIBUTE) {
+            final TypedValue value = mValue;
+            getValueAt(index, value);
+            throw new UnsupportedOperationException(
+                    "Failed to resolve attribute at index " + attrIndex + ": " + value);
+        }
+
+        throw new UnsupportedOperationException("Can't convert value at index " + attrIndex
+                + " to dimension: type=0x" + Integer.toHexString(type));
+    }
+
+    /**
+     * Retrieve a dimensional unit attribute at <var>index</var> for use
+     * as a size in raw pixels.  This is the same as
+     * {@link #getDimension}, except the returned value is converted to
+     * integer pixels for use as a size.  A size conversion involves
+     * rounding the base value, and ensuring that a non-zero base value
+     * is at least one pixel in size.
+     * <p>
+     * This method will throw an exception if the attribute is defined but is
+     * not a dimension.
+     *
+     * @param index Index of attribute to retrieve.
+     * @param defValue Value to return if the attribute is not defined or
+     *                 not a resource.
+     *
+     * @return Attribute dimension value multiplied by the appropriate
+     *         metric and truncated to integer pixels, or defValue if not defined.
+     * @throws RuntimeException if the TypedArray has already been recycled.
+     * @throws UnsupportedOperationException if the attribute is defined but is
+     *         not a dimension.
+     *
+     * @see #getDimension
+     * @see #getDimensionPixelOffset
+     */
+    public int getDimensionPixelSize(@StyleableRes int index, int defValue) {
+        if (mRecycled) {
+            throw new RuntimeException("Cannot make calls to a recycled instance!");
+        }
+
+        final int attrIndex = index;
+        index *= STYLE_NUM_ENTRIES;
+
+        final int[] data = mData;
+        final int type = data[index + STYLE_TYPE];
+        if (type == TypedValue.TYPE_NULL) {
+            return defValue;
+        } else if (type == TypedValue.TYPE_DIMENSION) {
+            return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics);
+        } else if (type == TypedValue.TYPE_ATTRIBUTE) {
+            final TypedValue value = mValue;
+            getValueAt(index, value);
+            throw new UnsupportedOperationException(
+                    "Failed to resolve attribute at index " + attrIndex + ": " + value);
+        }
+
+        throw new UnsupportedOperationException("Can't convert value at index " + attrIndex
+                + " to dimension: type=0x" + Integer.toHexString(type));
+    }
+
+    /**
+     * Special version of {@link #getDimensionPixelSize} for retrieving
+     * {@link android.view.ViewGroup}'s layout_width and layout_height
+     * attributes.  This is only here for performance reasons; applications
+     * should use {@link #getDimensionPixelSize}.
+     * <p>
+     * This method will throw an exception if the attribute is defined but is
+     * not a dimension or integer (enum).
+     *
+     * @param index Index of the attribute to retrieve.
+     * @param name Textual name of attribute for error reporting.
+     *
+     * @return Attribute dimension value multiplied by the appropriate
+     *         metric and truncated to integer pixels.
+     * @throws RuntimeException if the TypedArray has already been recycled.
+     * @throws UnsupportedOperationException if the attribute is defined but is
+     *         not a dimension or integer (enum).
+     */
+    public int getLayoutDimension(@StyleableRes int index, String name) {
+        if (mRecycled) {
+            throw new RuntimeException("Cannot make calls to a recycled instance!");
+        }
+
+        final int attrIndex = index;
+        index *= STYLE_NUM_ENTRIES;
+
+        final int[] data = mData;
+        final int type = data[index + STYLE_TYPE];
+        if (type >= TypedValue.TYPE_FIRST_INT
+                && type <= TypedValue.TYPE_LAST_INT) {
+            return data[index + STYLE_DATA];
+        } else if (type == TypedValue.TYPE_DIMENSION) {
+            return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics);
+        } else if (type == TypedValue.TYPE_ATTRIBUTE) {
+            final TypedValue value = mValue;
+            getValueAt(index, value);
+            throw new UnsupportedOperationException(
+                    "Failed to resolve attribute at index " + attrIndex + ": " + value);
+        }
+
+        throw new UnsupportedOperationException(getPositionDescription()
+                + ": You must supply a " + name + " attribute.");
+    }
+
+    /**
+     * Special version of {@link #getDimensionPixelSize} for retrieving
+     * {@link android.view.ViewGroup}'s layout_width and layout_height
+     * attributes.  This is only here for performance reasons; applications
+     * should use {@link #getDimensionPixelSize}.
+     *
+     * @param index Index of the attribute to retrieve.
+     * @param defValue The default value to return if this attribute is not
+     *                 default or contains the wrong type of data.
+     *
+     * @return Attribute dimension value multiplied by the appropriate
+     *         metric and truncated to integer pixels.
+     * @throws RuntimeException if the TypedArray has already been recycled.
+     */
+    public int getLayoutDimension(@StyleableRes int index, int defValue) {
+        if (mRecycled) {
+            throw new RuntimeException("Cannot make calls to a recycled instance!");
+        }
+
+        index *= STYLE_NUM_ENTRIES;
+        final int[] data = mData;
+        final int type = data[index + STYLE_TYPE];
+        if (type >= TypedValue.TYPE_FIRST_INT
+                && type <= TypedValue.TYPE_LAST_INT) {
+            return data[index + STYLE_DATA];
+        } else if (type == TypedValue.TYPE_DIMENSION) {
+            return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics);
+        }
+
+        return defValue;
+    }
+
+    /**
+     * Retrieves a fractional unit attribute at <var>index</var>.
+     *
+     * @param index Index of attribute to retrieve.
+     * @param base The base value of this fraction.  In other words, a
+     *             standard fraction is multiplied by this value.
+     * @param pbase The parent base value of this fraction.  In other
+     *             words, a parent fraction (nn%p) is multiplied by this
+     *             value.
+     * @param defValue Value to return if the attribute is not defined or
+     *                 not a resource.
+     *
+     * @return Attribute fractional value multiplied by the appropriate
+     *         base value, or defValue if not defined.
+     * @throws RuntimeException if the TypedArray has already been recycled.
+     * @throws UnsupportedOperationException if the attribute is defined but is
+     *         not a fraction.
+     */
+    public float getFraction(@StyleableRes int index, int base, int pbase, float defValue) {
+        if (mRecycled) {
+            throw new RuntimeException("Cannot make calls to a recycled instance!");
+        }
+
+        final int attrIndex = index;
+        index *= STYLE_NUM_ENTRIES;
+
+        final int[] data = mData;
+        final int type = data[index + STYLE_TYPE];
+        if (type == TypedValue.TYPE_NULL) {
+            return defValue;
+        } else if (type == TypedValue.TYPE_FRACTION) {
+            return TypedValue.complexToFraction(data[index + STYLE_DATA], base, pbase);
+        } else if (type == TypedValue.TYPE_ATTRIBUTE) {
+            final TypedValue value = mValue;
+            getValueAt(index, value);
+            throw new UnsupportedOperationException(
+                    "Failed to resolve attribute at index " + attrIndex + ": " + value);
+        }
+
+        throw new UnsupportedOperationException("Can't convert value at index " + attrIndex
+                + " to fraction: type=0x" + Integer.toHexString(type));
+    }
+
+    /**
+     * Retrieves the resource identifier for the attribute at
+     * <var>index</var>.  Note that attribute resource as resolved when
+     * the overall {@link TypedArray} object is retrieved.  As a
+     * result, this function will return the resource identifier of the
+     * final resource value that was found, <em>not</em> necessarily the
+     * original resource that was specified by the attribute.
+     *
+     * @param index Index of attribute to retrieve.
+     * @param defValue Value to return if the attribute is not defined or
+     *                 not a resource.
+     *
+     * @return Attribute resource identifier, or defValue if not defined.
+     * @throws RuntimeException if the TypedArray has already been recycled.
+     */
+    @AnyRes
+    public int getResourceId(@StyleableRes int index, int defValue) {
+        if (mRecycled) {
+            throw new RuntimeException("Cannot make calls to a recycled instance!");
+        }
+
+        index *= STYLE_NUM_ENTRIES;
+        final int[] data = mData;
+        if (data[index + STYLE_TYPE] != TypedValue.TYPE_NULL) {
+            final int resid = data[index + STYLE_RESOURCE_ID];
+            if (resid != 0) {
+                return resid;
+            }
+        }
+        return defValue;
+    }
+
+    /**
+     * Retrieves the theme attribute resource identifier for the attribute at
+     * <var>index</var>.
+     *
+     * @param index Index of attribute to retrieve.
+     * @param defValue Value to return if the attribute is not defined or not a
+     *                 resource.
+     *
+     * @return Theme attribute resource identifier, or defValue if not defined.
+     * @throws RuntimeException if the TypedArray has already been recycled.
+     * @hide
+     */
+    public int getThemeAttributeId(@StyleableRes int index, int defValue) {
+        if (mRecycled) {
+            throw new RuntimeException("Cannot make calls to a recycled instance!");
+        }
+
+        index *= STYLE_NUM_ENTRIES;
+        final int[] data = mData;
+        if (data[index + STYLE_TYPE] == TypedValue.TYPE_ATTRIBUTE) {
+            return data[index + STYLE_DATA];
+        }
+        return defValue;
+    }
+
+    /**
+     * Retrieve the Drawable for the attribute at <var>index</var>.
+     * <p>
+     * This method will throw an exception if the attribute is defined but is
+     * not a color or drawable resource.
+     *
+     * @param index Index of attribute to retrieve.
+     *
+     * @return Drawable for the attribute, or {@code null} if not defined.
+     * @throws RuntimeException if the TypedArray has already been recycled.
+     * @throws UnsupportedOperationException if the attribute is defined but is
+     *         not a color or drawable resource.
+     */
+    @Nullable
+    public Drawable getDrawable(@StyleableRes int index) {
+        return getDrawableForDensity(index, 0);
+    }
+
+    /**
+     * Version of {@link #getDrawable(int)} that accepts an override density.
+     * @hide
+     */
+    @Nullable
+    public Drawable getDrawableForDensity(@StyleableRes int index, int density) {
+        if (mRecycled) {
+            throw new RuntimeException("Cannot make calls to a recycled instance!");
+        }
+
+        final TypedValue value = mValue;
+        if (getValueAt(index * STYLE_NUM_ENTRIES, value)) {
+            if (value.type == TypedValue.TYPE_ATTRIBUTE) {
+                throw new UnsupportedOperationException(
+                        "Failed to resolve attribute at index " + index + ": " + value);
+            }
+
+            if (density > 0) {
+                // If the density is overridden, the value in the TypedArray will not reflect this.
+                // Do a separate lookup of the resourceId with the density override.
+                mResources.getValueForDensity(value.resourceId, density, value, true);
+            }
+            return mResources.loadDrawable(value, value.resourceId, density, mTheme);
+        }
+        return null;
+    }
+
+    /**
+     * Retrieve the Typeface for the attribute at <var>index</var>.
+     * <p>
+     * This method will throw an exception if the attribute is defined but is
+     * not a font.
+     *
+     * @param index Index of attribute to retrieve.
+     *
+     * @return Typeface for the attribute, or {@code null} if not defined.
+     * @throws RuntimeException if the TypedArray has already been recycled.
+     * @throws UnsupportedOperationException if the attribute is defined but is
+     *         not a font resource.
+     */
+    @Nullable
+    public Typeface getFont(@StyleableRes int index) {
+        if (mRecycled) {
+            throw new RuntimeException("Cannot make calls to a recycled instance!");
+        }
+
+        final TypedValue value = mValue;
+        if (getValueAt(index * STYLE_NUM_ENTRIES, value)) {
+            if (value.type == TypedValue.TYPE_ATTRIBUTE) {
+                throw new UnsupportedOperationException(
+                        "Failed to resolve attribute at index " + index + ": " + value);
+            }
+            return mResources.getFont(value, value.resourceId);
+        }
+        return null;
+    }
+
+    /**
+     * Retrieve the CharSequence[] for the attribute at <var>index</var>.
+     * This gets the resource ID of the selected attribute, and uses
+     * {@link Resources#getTextArray Resources.getTextArray} of the owning
+     * Resources object to retrieve its String[].
+     * <p>
+     * This method will throw an exception if the attribute is defined but is
+     * not a text array resource.
+     *
+     * @param index Index of attribute to retrieve.
+     *
+     * @return CharSequence[] for the attribute, or {@code null} if not
+     *         defined.
+     * @throws RuntimeException if the TypedArray has already been recycled.
+     */
+    public CharSequence[] getTextArray(@StyleableRes int index) {
+        if (mRecycled) {
+            throw new RuntimeException("Cannot make calls to a recycled instance!");
+        }
+
+        final TypedValue value = mValue;
+        if (getValueAt(index * STYLE_NUM_ENTRIES, value)) {
+            return mResources.getTextArray(value.resourceId);
+        }
+        return null;
+    }
+
+    /**
+     * Retrieve the raw TypedValue for the attribute at <var>index</var>.
+     *
+     * @param index Index of attribute to retrieve.
+     * @param outValue TypedValue object in which to place the attribute's
+     *                 data.
+     *
+     * @return {@code true} if the value was retrieved and not @empty, {@code false} otherwise.
+     * @throws RuntimeException if the TypedArray has already been recycled.
+     */
+    public boolean getValue(@StyleableRes int index, TypedValue outValue) {
+        if (mRecycled) {
+            throw new RuntimeException("Cannot make calls to a recycled instance!");
+        }
+
+        return getValueAt(index * STYLE_NUM_ENTRIES, outValue);
+    }
+
+    /**
+     * Returns the type of attribute at the specified index.
+     *
+     * @param index Index of attribute whose type to retrieve.
+     *
+     * @return Attribute type.
+     * @throws RuntimeException if the TypedArray has already been recycled.
+     */
+    public int getType(@StyleableRes int index) {
+        if (mRecycled) {
+            throw new RuntimeException("Cannot make calls to a recycled instance!");
+        }
+
+        index *= STYLE_NUM_ENTRIES;
+        return mData[index + STYLE_TYPE];
+    }
+
+    /**
+     * Returns the resource ID of the style or layout against which the specified attribute was
+     * resolved, otherwise returns defValue.
+     *
+     * For example, if you we resolving two attributes {@code android:attribute1} and
+     * {@code android:attribute2} and you were inflating a {@link android.view.View} from
+     * {@code layout/my_layout.xml}:
+     * <pre>
+     *     &lt;View
+     *         style="@style/viewStyle"
+     *         android:layout_width="wrap_content"
+     *         android:layout_height="wrap_content"
+     *         android:attribute1="foo"/&gt;
+     * </pre>
+     *
+     * and {@code @style/viewStyle} is:
+     * <pre>
+     *     &lt;style android:name="viewStyle"&gt;
+     *         &lt;item name="android:attribute2"&gt;bar&lt;item/&gt;
+     *     &lt;style/&gt;
+     * </pre>
+     *
+     * then resolved {@link TypedArray} will have values that return source resource ID of
+     * {@code R.layout.my_layout} for {@code android:attribute1} and {@code R.style.viewStyle} for
+     * {@code android:attribute2}.
+     *
+     * @param index Index of attribute whose source style to retrieve.
+     * @param defaultValue Value to return if the attribute is not defined or
+     *                     not a resource.
+     *
+     * @return Either a style resource ID, layout resource ID, or defaultValue if it was not
+     * resolved in a style or layout.
+     * @throws RuntimeException if the TypedArray has already been recycled.
+     */
+    @AnyRes
+    public int getSourceResourceId(@StyleableRes int index, @AnyRes int defaultValue) {
+        if (mRecycled) {
+            throw new RuntimeException("Cannot make calls to a recycled instance!");
+        }
+
+        index *= STYLE_NUM_ENTRIES;
+        final int resid = mData[index + STYLE_SOURCE_RESOURCE_ID];
+        if (resid != 0) {
+            return resid;
+        }
+        return defaultValue;
+    }
+
+    /**
+     * Determines whether there is an attribute at <var>index</var>.
+     * <p>
+     * <strong>Note:</strong> If the attribute was set to {@code @empty} or
+     * {@code @undefined}, this method returns {@code false}.
+     *
+     * @param index Index of attribute to retrieve.
+     *
+     * @return True if the attribute has a value, false otherwise.
+     * @throws RuntimeException if the TypedArray has already been recycled.
+     */
+    public boolean hasValue(@StyleableRes int index) {
+        if (mRecycled) {
+            throw new RuntimeException("Cannot make calls to a recycled instance!");
+        }
+
+        index *= STYLE_NUM_ENTRIES;
+        final int[] data = mData;
+        final int type = data[index + STYLE_TYPE];
+        return type != TypedValue.TYPE_NULL;
+    }
+
+    /**
+     * Determines whether there is an attribute at <var>index</var>, returning
+     * {@code true} if the attribute was explicitly set to {@code @empty} and
+     * {@code false} only if the attribute was undefined.
+     *
+     * @param index Index of attribute to retrieve.
+     *
+     * @return True if the attribute has a value or is empty, false otherwise.
+     * @throws RuntimeException if the TypedArray has already been recycled.
+     */
+    public boolean hasValueOrEmpty(@StyleableRes int index) {
+        if (mRecycled) {
+            throw new RuntimeException("Cannot make calls to a recycled instance!");
+        }
+
+        index *= STYLE_NUM_ENTRIES;
+        final int[] data = mData;
+        final int type = data[index + STYLE_TYPE];
+        return type != TypedValue.TYPE_NULL
+                || data[index + STYLE_DATA] == TypedValue.DATA_NULL_EMPTY;
+    }
+
+    /**
+     * Retrieve the raw TypedValue for the attribute at <var>index</var>
+     * and return a temporary object holding its data.  This object is only
+     * valid until the next call on to {@link TypedArray}.
+     *
+     * @param index Index of attribute to retrieve.
+     *
+     * @return Returns a TypedValue object if the attribute is defined,
+     *         containing its data; otherwise returns null.  (You will not
+     *         receive a TypedValue whose type is TYPE_NULL.)
+     * @throws RuntimeException if the TypedArray has already been recycled.
+     */
+    public TypedValue peekValue(@StyleableRes int index) {
+        if (mRecycled) {
+            throw new RuntimeException("Cannot make calls to a recycled instance!");
+        }
+
+        final TypedValue value = mValue;
+        if (getValueAt(index * STYLE_NUM_ENTRIES, value)) {
+            return value;
+        }
+        return null;
+    }
+
+    /**
+     * Returns a message about the parser state suitable for printing error messages.
+     *
+     * @return Human-readable description of current parser state.
+     * @throws RuntimeException if the TypedArray has already been recycled.
+     */
+    public String getPositionDescription() {
+        if (mRecycled) {
+            throw new RuntimeException("Cannot make calls to a recycled instance!");
+        }
+
+        return mXml != null ? mXml.getPositionDescription() : "<internal>";
+    }
+
+    /**
+     * Recycles the TypedArray, to be re-used by a later caller. After calling
+     * this function you must not ever touch the typed array again.
+     *
+     * @throws RuntimeException if the TypedArray has already been recycled.
+     */
+    public void recycle() {
+        if (mRecycled) {
+            throw new RuntimeException(toString() + " recycled twice!");
+        }
+
+        mRecycled = true;
+
+        // These may have been set by the client.
+        mXml = null;
+        mTheme = null;
+        mAssets = null;
+
+        mResources.mTypedArrayPool.release(this);
+    }
+
+    /**
+     * Extracts theme attributes from a typed array for later resolution using
+     * {@link android.content.res.Resources.Theme#resolveAttributes(int[], int[])}.
+     * Removes the entries from the typed array so that subsequent calls to typed
+     * getters will return the default value without crashing.
+     *
+     * @return an array of length {@link #getIndexCount()} populated with theme
+     *         attributes, or null if there are no theme attributes in the typed
+     *         array
+     * @throws RuntimeException if the TypedArray has already been recycled.
+     * @hide
+     */
+    @Nullable
+    @UnsupportedAppUsage
+    public int[] extractThemeAttrs() {
+        return extractThemeAttrs(null);
+    }
+
+    /**
+     * @hide
+     */
+    @Nullable
+    @UnsupportedAppUsage
+    public int[] extractThemeAttrs(@Nullable int[] scrap) {
+        if (mRecycled) {
+            throw new RuntimeException("Cannot make calls to a recycled instance!");
+        }
+
+        int[] attrs = null;
+
+        final int[] data = mData;
+        final int N = length();
+        for (int i = 0; i < N; i++) {
+            final int index = i * STYLE_NUM_ENTRIES;
+            if (data[index + STYLE_TYPE] != TypedValue.TYPE_ATTRIBUTE) {
+                // Not an attribute, ignore.
+                continue;
+            }
+
+            // Null the entry so that we can safely call getZzz().
+            data[index + STYLE_TYPE] = TypedValue.TYPE_NULL;
+
+            final int attr = data[index + STYLE_DATA];
+            if (attr == 0) {
+                // Useless data, ignore.
+                continue;
+            }
+
+            // Ensure we have a usable attribute array.
+            if (attrs == null) {
+                if (scrap != null && scrap.length == N) {
+                    attrs = scrap;
+                    Arrays.fill(attrs, 0);
+                } else {
+                    attrs = new int[N];
+                }
+            }
+
+            attrs[i] = attr;
+        }
+
+        return attrs;
+    }
+
+    /**
+     * Return a mask of the configuration parameters for which the values in
+     * this typed array may change.
+     *
+     * @return Returns a mask of the changing configuration parameters, as
+     *         defined by {@link android.content.pm.ActivityInfo}.
+     * @throws RuntimeException if the TypedArray has already been recycled.
+     * @see android.content.pm.ActivityInfo
+     */
+    public @Config int getChangingConfigurations() {
+        if (mRecycled) {
+            throw new RuntimeException("Cannot make calls to a recycled instance!");
+        }
+
+        @Config int changingConfig = 0;
+
+        final int[] data = mData;
+        final int N = length();
+        for (int i = 0; i < N; i++) {
+            final int index = i * STYLE_NUM_ENTRIES;
+            final int type = data[index + STYLE_TYPE];
+            if (type == TypedValue.TYPE_NULL) {
+                continue;
+            }
+            changingConfig |= ActivityInfo.activityInfoConfigNativeToJava(
+                    data[index + STYLE_CHANGING_CONFIGURATIONS]);
+        }
+        return changingConfig;
+    }
+
+    @UnsupportedAppUsage
+    private boolean getValueAt(int index, TypedValue outValue) {
+        final int[] data = mData;
+        final int type = data[index + STYLE_TYPE];
+        if (type == TypedValue.TYPE_NULL) {
+            return false;
+        }
+        outValue.type = type;
+        outValue.data = data[index + STYLE_DATA];
+        outValue.assetCookie = data[index + STYLE_ASSET_COOKIE];
+        outValue.resourceId = data[index + STYLE_RESOURCE_ID];
+        outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava(
+                data[index + STYLE_CHANGING_CONFIGURATIONS]);
+        outValue.density = data[index + STYLE_DENSITY];
+        outValue.string = (type == TypedValue.TYPE_STRING) ? loadStringValueAt(index) : null;
+        outValue.sourceResourceId = data[index + STYLE_SOURCE_RESOURCE_ID];
+        return true;
+    }
+
+    private CharSequence loadStringValueAt(int index) {
+        final int[] data = mData;
+        final int cookie = data[index + STYLE_ASSET_COOKIE];
+        if (cookie < 0) {
+            if (mXml != null) {
+                return mXml.getPooledString(data[index + STYLE_DATA]);
+            }
+            return null;
+        }
+        return mAssets.getPooledStringForCookie(cookie, data[index + STYLE_DATA]);
+    }
+
+    /** @hide */
+    protected TypedArray(Resources resources) {
+        mResources = resources;
+        mMetrics = mResources.getDisplayMetrics();
+        mAssets = mResources.getAssets();
+    }
+
+    @Override
+    public String toString() {
+        return Arrays.toString(mData);
+    }
+}
diff --git a/android/content/res/TypedArray_Delegate.java b/android/content/res/TypedArray_Delegate.java
new file mode 100644
index 0000000..faa8852
--- /dev/null
+++ b/android/content/res/TypedArray_Delegate.java
@@ -0,0 +1,35 @@
+/*
+ * 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.content.res;
+
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import android.util.TypedValue;
+
+public class TypedArray_Delegate {
+
+    @LayoutlibDelegate
+    public static boolean getValueAt(TypedArray theTypedArray, int index, TypedValue outValue) {
+        // pass
+        return false;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static TypedArray obtain(Resources res, int len) {
+        return BridgeTypedArray.obtain(res, len);
+    }
+}
diff --git a/android/content/res/XmlBlock.java b/android/content/res/XmlBlock.java
new file mode 100644
index 0000000..cb93cbf
--- /dev/null
+++ b/android/content/res/XmlBlock.java
@@ -0,0 +1,556 @@
+/*
+ * 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.content.res;
+
+import static android.content.res.Resources.ID_NULL;
+
+import android.annotation.AnyRes;
+import android.annotation.Nullable;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.util.TypedValue;
+
+import com.android.internal.util.XmlUtils;
+
+import dalvik.annotation.optimization.FastNative;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+
+/**
+ * Wrapper around a compiled XML file.
+ * 
+ * {@hide}
+ */
+final class XmlBlock implements AutoCloseable {
+    private static final boolean DEBUG=false;
+
+    @UnsupportedAppUsage
+    public XmlBlock(byte[] data) {
+        mAssets = null;
+        mNative = nativeCreate(data, 0, data.length);
+        mStrings = new StringBlock(nativeGetStringBlock(mNative), false);
+    }
+
+    public XmlBlock(byte[] data, int offset, int size) {
+        mAssets = null;
+        mNative = nativeCreate(data, offset, size);
+        mStrings = new StringBlock(nativeGetStringBlock(mNative), false);
+    }
+
+    @Override
+    public void close() {
+        synchronized (this) {
+            if (mOpen) {
+                mOpen = false;
+                decOpenCountLocked();
+            }
+        }
+    }
+
+    private void decOpenCountLocked() {
+        mOpenCount--;
+        if (mOpenCount == 0) {
+            nativeDestroy(mNative);
+            if (mAssets != null) {
+                mAssets.xmlBlockGone(hashCode());
+            }
+        }
+    }
+
+    @UnsupportedAppUsage
+    public XmlResourceParser newParser() {
+        return newParser(ID_NULL);
+    }
+
+    public XmlResourceParser newParser(@AnyRes int resId) {
+        synchronized (this) {
+            if (mNative != 0) {
+                return new Parser(nativeCreateParseState(mNative, resId), this);
+            }
+            return null;
+        }
+    }
+
+    /*package*/ final class Parser implements XmlResourceParser {
+        Parser(long parseState, XmlBlock block) {
+            mParseState = parseState;
+            mBlock = block;
+            block.mOpenCount++;
+        }
+
+        @AnyRes
+        public int getSourceResId() {
+            return nativeGetSourceResId(mParseState);
+        }
+
+        public void setFeature(String name, boolean state) throws XmlPullParserException {
+            if (FEATURE_PROCESS_NAMESPACES.equals(name) && state) {
+                return;
+            }
+            if (FEATURE_REPORT_NAMESPACE_ATTRIBUTES.equals(name) && state) {
+                return;
+            }
+            throw new XmlPullParserException("Unsupported feature: " + name);
+        }
+        public boolean getFeature(String name) {
+            if (FEATURE_PROCESS_NAMESPACES.equals(name)) {
+                return true;
+            }
+            if (FEATURE_REPORT_NAMESPACE_ATTRIBUTES.equals(name)) {
+                return true;
+            }
+            return false;
+        }
+        public void setProperty(String name, Object value) throws XmlPullParserException {
+            throw new XmlPullParserException("setProperty() not supported");
+        }
+        public Object getProperty(String name) {
+            return null;
+        }
+        public void setInput(Reader in) throws XmlPullParserException {
+            throw new XmlPullParserException("setInput() not supported");
+        }
+        public void setInput(InputStream inputStream, String inputEncoding) throws XmlPullParserException {
+            throw new XmlPullParserException("setInput() not supported");
+        }
+        public void defineEntityReplacementText(String entityName, String replacementText) throws XmlPullParserException {
+            throw new XmlPullParserException("defineEntityReplacementText() not supported");
+        }
+        public String getNamespacePrefix(int pos) throws XmlPullParserException {
+            throw new XmlPullParserException("getNamespacePrefix() not supported");
+        }
+        public String getInputEncoding() {
+            return null;
+        }
+        public String getNamespace(String prefix) {
+            throw new RuntimeException("getNamespace() not supported");
+        }
+        public int getNamespaceCount(int depth) throws XmlPullParserException {
+            throw new XmlPullParserException("getNamespaceCount() not supported");
+        }
+        public String getPositionDescription() {
+            return "Binary XML file line #" + getLineNumber();
+        }
+        public String getNamespaceUri(int pos) throws XmlPullParserException {
+            throw new XmlPullParserException("getNamespaceUri() not supported");
+        }
+        public int getColumnNumber() {
+            return -1;
+        }
+        public int getDepth() {
+            return mDepth;
+        }
+        public String getText() {
+            int id = nativeGetText(mParseState);
+            return id >= 0 ? mStrings.get(id).toString() : null;
+        }
+        public int getLineNumber() {
+            return nativeGetLineNumber(mParseState);
+        }
+        public int getEventType() throws XmlPullParserException {
+            return mEventType;
+        }
+        public boolean isWhitespace() throws XmlPullParserException {
+            // whitespace was stripped by aapt.
+            return false;
+        }
+        public String getPrefix() {
+            throw new RuntimeException("getPrefix not supported");
+        }
+        public char[] getTextCharacters(int[] holderForStartAndLength) {
+            String txt = getText();
+            char[] chars = null;
+            if (txt != null) {
+                holderForStartAndLength[0] = 0;
+                holderForStartAndLength[1] = txt.length();
+                chars = new char[txt.length()];
+                txt.getChars(0, txt.length(), chars, 0);
+            }
+            return chars;
+        }
+        public String getNamespace() {
+            int id = nativeGetNamespace(mParseState);
+            return id >= 0 ? mStrings.get(id).toString() : "";
+        }
+        public String getName() {
+            int id = nativeGetName(mParseState);
+            return id >= 0 ? mStrings.get(id).toString() : null;
+        }
+        public String getAttributeNamespace(int index) {
+            int id = nativeGetAttributeNamespace(mParseState, index);
+            if (DEBUG) System.out.println("getAttributeNamespace of " + index + " = " + id);
+            if (id >= 0) return mStrings.get(id).toString();
+            else if (id == -1) return "";
+            throw new IndexOutOfBoundsException(String.valueOf(index));
+        }
+        public String getAttributeName(int index) {
+            int id = nativeGetAttributeName(mParseState, index);
+            if (DEBUG) System.out.println("getAttributeName of " + index + " = " + id);
+            if (id >= 0) return mStrings.get(id).toString();
+            throw new IndexOutOfBoundsException(String.valueOf(index));
+        }
+        public String getAttributePrefix(int index) {
+            throw new RuntimeException("getAttributePrefix not supported");
+        }
+        public boolean isEmptyElementTag() throws XmlPullParserException {
+            // XXX Need to detect this.
+            return false;
+        }
+        public int getAttributeCount() {
+            return mEventType == START_TAG ? nativeGetAttributeCount(mParseState) : -1;
+        }
+        public String getAttributeValue(int index) {
+            int id = nativeGetAttributeStringValue(mParseState, index);
+            if (DEBUG) System.out.println("getAttributeValue of " + index + " = " + id);
+            if (id >= 0) return mStrings.get(id).toString();
+
+            // May be some other type...  check and try to convert if so.
+            int t = nativeGetAttributeDataType(mParseState, index);
+            if (t == TypedValue.TYPE_NULL) {
+                throw new IndexOutOfBoundsException(String.valueOf(index));
+            }
+
+            int v = nativeGetAttributeData(mParseState, index);
+            return TypedValue.coerceToString(t, v);
+        }
+        public String getAttributeType(int index) {
+            return "CDATA";
+        }
+        public boolean isAttributeDefault(int index) {
+            return false;
+        }
+        public int nextToken() throws XmlPullParserException,IOException {
+            return next();
+        }
+        public String getAttributeValue(String namespace, String name) {
+            int idx = nativeGetAttributeIndex(mParseState, namespace, name);
+            if (idx >= 0) {
+                if (DEBUG) System.out.println("getAttributeName of "
+                        + namespace + ":" + name + " index = " + idx);
+                if (DEBUG) System.out.println(
+                        "Namespace=" + getAttributeNamespace(idx)
+                        + "Name=" + getAttributeName(idx)
+                        + ", Value=" + getAttributeValue(idx));
+                return getAttributeValue(idx);
+            }
+            return null;
+        }
+        public int next() throws XmlPullParserException,IOException {
+            if (!mStarted) {
+                mStarted = true;
+                return START_DOCUMENT;
+            }
+            if (mParseState == 0) {
+                return END_DOCUMENT;
+            }
+            int ev = nativeNext(mParseState);
+            if (mDecNextDepth) {
+                mDepth--;
+                mDecNextDepth = false;
+            }
+            switch (ev) {
+            case START_TAG:
+                mDepth++;
+                break;
+            case END_TAG:
+                mDecNextDepth = true;
+                break;
+            }
+            mEventType = ev;
+            if (ev == END_DOCUMENT) {
+                // Automatically close the parse when we reach the end of
+                // a document, since the standard XmlPullParser interface
+                // doesn't have such an API so most clients will leave us
+                // dangling.
+                close();
+            }
+            return ev;
+        }
+        public void require(int type, String namespace, String name) throws XmlPullParserException,IOException {
+            if (type != getEventType()
+                || (namespace != null && !namespace.equals( getNamespace () ) )
+                || (name != null && !name.equals( getName() ) ) )
+                throw new XmlPullParserException( "expected "+ TYPES[ type ]+getPositionDescription());
+        }
+        public String nextText() throws XmlPullParserException,IOException {
+            if(getEventType() != START_TAG) {
+               throw new XmlPullParserException(
+                 getPositionDescription()
+                 + ": parser must be on START_TAG to read next text", this, null);
+            }
+            int eventType = next();
+            if(eventType == TEXT) {
+               String result = getText();
+               eventType = next();
+               if(eventType != END_TAG) {
+                 throw new XmlPullParserException(
+                    getPositionDescription()
+                    + ": event TEXT it must be immediately followed by END_TAG", this, null);
+                }
+                return result;
+            } else if(eventType == END_TAG) {
+               return "";
+            } else {
+               throw new XmlPullParserException(
+                 getPositionDescription()
+                 + ": parser must be on START_TAG or TEXT to read text", this, null);
+            }
+        }
+        public int nextTag() throws XmlPullParserException,IOException {
+            int eventType = next();
+            if(eventType == TEXT && isWhitespace()) {   // skip whitespace
+               eventType = next();
+            }
+            if (eventType != START_TAG && eventType != END_TAG) {
+               throw new XmlPullParserException(
+                   getPositionDescription() 
+                   + ": expected start or end tag", this, null);
+            }
+            return eventType;
+        }
+    
+        public int getAttributeNameResource(int index) {
+            return nativeGetAttributeResource(mParseState, index);
+        }
+    
+        public int getAttributeListValue(String namespace, String attribute,
+                String[] options, int defaultValue) {
+            int idx = nativeGetAttributeIndex(mParseState, namespace, attribute);
+            if (idx >= 0) {
+                return getAttributeListValue(idx, options, defaultValue);
+            }
+            return defaultValue;
+        }
+        public boolean getAttributeBooleanValue(String namespace, String attribute,
+                boolean defaultValue) {
+            int idx = nativeGetAttributeIndex(mParseState, namespace, attribute);
+            if (idx >= 0) {
+                return getAttributeBooleanValue(idx, defaultValue);
+            }
+            return defaultValue;
+        }
+        public int getAttributeResourceValue(String namespace, String attribute,
+                int defaultValue) {
+            int idx = nativeGetAttributeIndex(mParseState, namespace, attribute);
+            if (idx >= 0) {
+                return getAttributeResourceValue(idx, defaultValue);
+            }
+            return defaultValue;
+        }
+        public int getAttributeIntValue(String namespace, String attribute,
+                int defaultValue) {
+            int idx = nativeGetAttributeIndex(mParseState, namespace, attribute);
+            if (idx >= 0) {
+                return getAttributeIntValue(idx, defaultValue);
+            }
+            return defaultValue;
+        }
+        public int getAttributeUnsignedIntValue(String namespace, String attribute,
+                                                int defaultValue)
+        {
+            int idx = nativeGetAttributeIndex(mParseState, namespace, attribute);
+            if (idx >= 0) {
+                return getAttributeUnsignedIntValue(idx, defaultValue);
+            }
+            return defaultValue;
+        }
+        public float getAttributeFloatValue(String namespace, String attribute,
+                float defaultValue) {
+            int idx = nativeGetAttributeIndex(mParseState, namespace, attribute);
+            if (idx >= 0) {
+                return getAttributeFloatValue(idx, defaultValue);
+            }
+            return defaultValue;
+        }
+
+        public int getAttributeListValue(int idx,
+                String[] options, int defaultValue) {
+            int t = nativeGetAttributeDataType(mParseState, idx);
+            int v = nativeGetAttributeData(mParseState, idx);
+            if (t == TypedValue.TYPE_STRING) {
+                return XmlUtils.convertValueToList(
+                    mStrings.get(v), options, defaultValue);
+            }
+            return v;
+        }
+        public boolean getAttributeBooleanValue(int idx,
+                boolean defaultValue) {
+            int t = nativeGetAttributeDataType(mParseState, idx);
+            // Note: don't attempt to convert any other types, because
+            // we want to count on aapt doing the conversion for us.
+            if (t >= TypedValue.TYPE_FIRST_INT &&
+                t <= TypedValue.TYPE_LAST_INT) {
+                return nativeGetAttributeData(mParseState, idx) != 0;
+            }
+            return defaultValue;
+        }
+        public int getAttributeResourceValue(int idx, int defaultValue) {
+            int t = nativeGetAttributeDataType(mParseState, idx);
+            // Note: don't attempt to convert any other types, because
+            // we want to count on aapt doing the conversion for us.
+            if (t == TypedValue.TYPE_REFERENCE) {
+                return nativeGetAttributeData(mParseState, idx);
+            }
+            return defaultValue;
+        }
+        public int getAttributeIntValue(int idx, int defaultValue) {
+            int t = nativeGetAttributeDataType(mParseState, idx);
+            // Note: don't attempt to convert any other types, because
+            // we want to count on aapt doing the conversion for us.
+            if (t >= TypedValue.TYPE_FIRST_INT &&
+                t <= TypedValue.TYPE_LAST_INT) {
+                return nativeGetAttributeData(mParseState, idx);
+            }
+            return defaultValue;
+        }
+        public int getAttributeUnsignedIntValue(int idx, int defaultValue) {
+            int t = nativeGetAttributeDataType(mParseState, idx);
+            // Note: don't attempt to convert any other types, because
+            // we want to count on aapt doing the conversion for us.
+            if (t >= TypedValue.TYPE_FIRST_INT &&
+                t <= TypedValue.TYPE_LAST_INT) {
+                return nativeGetAttributeData(mParseState, idx);
+            }
+            return defaultValue;
+        }
+        public float getAttributeFloatValue(int idx, float defaultValue) {
+            int t = nativeGetAttributeDataType(mParseState, idx);
+            // Note: don't attempt to convert any other types, because
+            // we want to count on aapt doing the conversion for us.
+            if (t == TypedValue.TYPE_FLOAT) {
+                return Float.intBitsToFloat(
+                    nativeGetAttributeData(mParseState, idx));
+            }
+            throw new RuntimeException("not a float!");
+        }
+
+        public String getIdAttribute() {
+            int id = nativeGetIdAttribute(mParseState);
+            return id >= 0 ? mStrings.get(id).toString() : null;
+        }
+        public String getClassAttribute() {
+            int id = nativeGetClassAttribute(mParseState);
+            return id >= 0 ? mStrings.get(id).toString() : null;
+        }
+
+        public int getIdAttributeResourceValue(int defaultValue) {
+            //todo: create and use native method
+            return getAttributeResourceValue(null, "id", defaultValue);
+        }
+
+        public int getStyleAttribute() {
+            return nativeGetStyleAttribute(mParseState);
+        }
+
+        public void close() {
+            synchronized (mBlock) {
+                if (mParseState != 0) {
+                    nativeDestroyParseState(mParseState);
+                    mParseState = 0;
+                    mBlock.decOpenCountLocked();
+                }
+            }
+        }
+        
+        protected void finalize() throws Throwable {
+            close();
+        }
+
+        /*package*/ final CharSequence getPooledString(int id) {
+            return mStrings.get(id);
+        }
+
+        @UnsupportedAppUsage
+        /*package*/ long mParseState;
+        @UnsupportedAppUsage
+        private final XmlBlock mBlock;
+        private boolean mStarted = false;
+        private boolean mDecNextDepth = false;
+        private int mDepth = 0;
+        private int mEventType = START_DOCUMENT;
+    }
+
+    protected void finalize() throws Throwable {
+        close();
+    }
+
+    /**
+     * Create from an existing xml block native object.  This is
+     * -extremely- dangerous -- only use it if you absolutely know what you
+     *  are doing!  The given native object must exist for the entire lifetime
+     *  of this newly creating XmlBlock.
+     */
+    XmlBlock(@Nullable AssetManager assets, long xmlBlock) {
+        mAssets = assets;
+        mNative = xmlBlock;
+        mStrings = new StringBlock(nativeGetStringBlock(xmlBlock), false);
+    }
+
+    private @Nullable final AssetManager mAssets;
+    private final long mNative;
+    /*package*/ final StringBlock mStrings;
+    private boolean mOpen = true;
+    private int mOpenCount = 1;
+
+    private static final native long nativeCreate(byte[] data,
+                                                 int offset,
+                                                 int size);
+    private static final native long nativeGetStringBlock(long obj);
+    private static final native long nativeCreateParseState(long obj, int resId);
+    private static final native void nativeDestroyParseState(long state);
+    private static final native void nativeDestroy(long obj);
+
+    // ----------- @FastNative ------------------
+
+    @FastNative
+    /*package*/ static final native int nativeNext(long state);
+    @FastNative
+    private static final native int nativeGetNamespace(long state);
+    @FastNative
+    /*package*/ static final native int nativeGetName(long state);
+    @FastNative
+    private static final native int nativeGetText(long state);
+    @FastNative
+    private static final native int nativeGetLineNumber(long state);
+    @FastNative
+    private static final native int nativeGetAttributeCount(long state);
+    @FastNative
+    private static final native int nativeGetAttributeNamespace(long state, int idx);
+    @FastNative
+    private static final native int nativeGetAttributeName(long state, int idx);
+    @FastNative
+    private static final native int nativeGetAttributeResource(long state, int idx);
+    @FastNative
+    private static final native int nativeGetAttributeDataType(long state, int idx);
+    @FastNative
+    private static final native int nativeGetAttributeData(long state, int idx);
+    @FastNative
+    private static final native int nativeGetAttributeStringValue(long state, int idx);
+    @FastNative
+    private static final native int nativeGetIdAttribute(long state);
+    @FastNative
+    private static final native int nativeGetClassAttribute(long state);
+    @FastNative
+    private static final native int nativeGetStyleAttribute(long state);
+    @FastNative
+    private static final native int nativeGetAttributeIndex(long state, String namespace, String name);
+    @FastNative
+    private static final native int nativeGetSourceResId(long state);
+}
diff --git a/android/content/res/XmlResourceParser.java b/android/content/res/XmlResourceParser.java
new file mode 100644
index 0000000..86f4ba6
--- /dev/null
+++ b/android/content/res/XmlResourceParser.java
@@ -0,0 +1,37 @@
+/*
+ * 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.content.res;
+
+import android.util.AttributeSet;
+
+import org.xmlpull.v1.XmlPullParser;
+
+/**
+ * The XML parsing interface returned for an XML resource.  This is a standard
+ * {@link XmlPullParser} interface but also extends {@link AttributeSet} and
+ * adds an additional {@link #close()} method for the client to indicate when
+ * it is done reading the resource.
+ */
+public interface XmlResourceParser extends XmlPullParser, AttributeSet, AutoCloseable {
+    String getAttributeNamespace (int index);
+
+    /**
+     * Close this parser. Calls on the interface are no longer valid after this call.
+     */
+    public void close();
+}
+
diff --git a/android/content/res/loader/AssetsProvider.java b/android/content/res/loader/AssetsProvider.java
new file mode 100644
index 0000000..0f8f1d1
--- /dev/null
+++ b/android/content/res/loader/AssetsProvider.java
@@ -0,0 +1,60 @@
+/*
+ * 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.content.res.loader;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.res.AssetFileDescriptor;
+import android.content.res.AssetManager;
+import android.os.ParcelFileDescriptor;
+
+/**
+ * Provides callbacks that allow for the value of a file-based resources or assets of a
+ * {@link ResourcesProvider} to be specified or overridden.
+ */
+public interface AssetsProvider {
+
+    /**
+     * Callback that allows the value of a file-based resources or asset to be specified or
+     * overridden.
+     *
+     * <p>The system will take ownership of the file descriptor returned from this method, so
+     * {@link ParcelFileDescriptor#dup() dup} the file descriptor before returning if the system
+     * should not own it.
+     *
+     * <p>There are two situations in which this method will be called:
+     * <ul>
+     * <li>AssetManager is queried for an InputStream of an asset using APIs like
+     * {@link AssetManager#open} and {@link AssetManager#openXmlResourceParser}.
+     * <li>AssetManager is resolving the value of a file-based resource provided by the
+     * {@link ResourcesProvider} this instance is associated with.
+     * </ul>
+     *
+     * <p>If the value retrieved from this callback is null, AssetManager will attempt to find the
+     * file-based resource or asset within the APK provided by the ResourcesProvider this instance
+     * is associated with.
+     *
+     * @param path the asset path being loaded
+     * @param accessMode the {@link AssetManager} access mode
+     *
+     * @see AssetManager#open
+     */
+    @Nullable
+    default AssetFileDescriptor loadAssetFd(@NonNull String path, int accessMode) {
+        return null;
+    }
+}
diff --git a/android/content/res/loader/ResourcesLoader.java b/android/content/res/loader/ResourcesLoader.java
new file mode 100644
index 0000000..c308400
--- /dev/null
+++ b/android/content/res/loader/ResourcesLoader.java
@@ -0,0 +1,271 @@
+/*
+ * 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.content.res.loader;
+
+import android.annotation.NonNull;
+import android.content.res.ApkAssets;
+import android.content.res.Resources;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.ArrayUtils;
+
+import java.lang.ref.WeakReference;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A container for supplying {@link ResourcesProvider ResourcesProvider(s)} to {@link Resources}
+ * objects.
+ *
+ * <p>{@link ResourcesLoader ResourcesLoader(s)} are added to Resources objects to supply
+ * additional resources and assets or modify the values of existing resources and assets. Multiple
+ * Resources objects can share the same ResourcesLoaders and ResourcesProviders. Changes to the list
+ * of {@link ResourcesProvider ResourcesProvider(s)} a loader contains propagates to all Resources
+ * objects that use the loader.
+ *
+ * <p>Loaders must be added to Resources objects in increasing precedence order. A loader will
+ * override the resources and assets of loaders added before itself.
+ *
+ * <p>Providers retrieved with {@link #getProviders()} are listed in increasing precedence order. A
+ * provider will override the resources and assets of providers listed before itself.
+ *
+ * <p>Modifying the list of providers a loader contains or the list of loaders a Resources object
+ * contains can cause lock contention with the UI thread. APIs that modify the lists of loaders or
+ * providers should only be used on the UI thread. Providers can be instantiated on any thread
+ * without causing lock contention.
+ */
+public class ResourcesLoader {
+    private final Object mLock = new Object();
+
+    @GuardedBy("mLock")
+    private ApkAssets[] mApkAssets;
+
+    @GuardedBy("mLock")
+    private ResourcesProvider[] mPreviousProviders;
+
+    @GuardedBy("mLock")
+    private ResourcesProvider[] mProviders;
+
+    @GuardedBy("mLock")
+    private ArrayMap<WeakReference<Object>, UpdateCallbacks> mChangeCallbacks = new ArrayMap<>();
+
+    /** @hide */
+    public interface UpdateCallbacks {
+
+        /**
+         * Invoked when a {@link ResourcesLoader} has a {@link ResourcesProvider} added, removed,
+         * or reordered.
+         *
+         * @param loader the loader that was updated
+         */
+        void onLoaderUpdated(@NonNull ResourcesLoader loader);
+    }
+
+    /**
+     * Retrieves the list of providers loaded into this instance. Providers are listed in increasing
+     * precedence order. A provider will override the values of providers listed before itself.
+     */
+    @NonNull
+    public List<ResourcesProvider> getProviders() {
+        synchronized (mLock) {
+            return mProviders == null ? Collections.emptyList() : Arrays.asList(mProviders);
+        }
+    }
+
+    /**
+     * Appends a provider to the end of the provider list. If the provider is already present in the
+     * loader list, the list will not be modified.
+     *
+     * <p>This should only be called from the UI thread to avoid lock contention when propagating
+     * provider changes.
+     *
+     * @param resourcesProvider the provider to add
+     */
+    public void addProvider(@NonNull ResourcesProvider resourcesProvider) {
+        synchronized (mLock) {
+            mProviders = ArrayUtils.appendElement(ResourcesProvider.class, mProviders,
+                    resourcesProvider);
+            notifyProvidersChangedLocked();
+        }
+    }
+
+    /**
+     * Removes a provider from the provider list. If the provider is not present in the provider
+     * list, the list will not be modified.
+     *
+     * <p>This should only be called from the UI thread to avoid lock contention when propagating
+     * provider changes.
+     *
+     * @param resourcesProvider the provider to remove
+     */
+    public void removeProvider(@NonNull ResourcesProvider resourcesProvider) {
+        synchronized (mLock) {
+            mProviders = ArrayUtils.removeElement(ResourcesProvider.class, mProviders,
+                    resourcesProvider);
+            notifyProvidersChangedLocked();
+        }
+    }
+
+    /**
+     * Sets the list of providers.
+     *
+     * <p>This should only be called from the UI thread to avoid lock contention when propagating
+     * provider changes.
+     *
+     * @param resourcesProviders the new providers
+     */
+    public void setProviders(@NonNull List<ResourcesProvider> resourcesProviders) {
+        synchronized (mLock) {
+            mProviders = resourcesProviders.toArray(new ResourcesProvider[0]);
+            notifyProvidersChangedLocked();
+        }
+    }
+
+    /**
+     * Removes all {@link ResourcesProvider ResourcesProvider(s)}.
+     *
+     * <p>This should only be called from the UI thread to avoid lock contention when propagating
+     * provider changes.
+     */
+    public void clearProviders() {
+        synchronized (mLock) {
+            mProviders = null;
+            notifyProvidersChangedLocked();
+        }
+    }
+
+    /**
+     * Retrieves the list of {@link ApkAssets} used by the providers.
+     *
+     * @hide
+     */
+    @NonNull
+    public List<ApkAssets> getApkAssets() {
+        synchronized (mLock) {
+            if (mApkAssets == null) {
+                return Collections.emptyList();
+            }
+            return Arrays.asList(mApkAssets);
+        }
+    }
+
+    /**
+     * Registers a callback to be invoked when {@link ResourcesProvider ResourcesProvider(s)}
+     * change.
+     * @param instance the instance tied to the callback
+     * @param callbacks the callback to invoke
+     *
+     * @hide
+     */
+    public void registerOnProvidersChangedCallback(@NonNull Object instance,
+            @NonNull UpdateCallbacks callbacks) {
+        synchronized (mLock) {
+            mChangeCallbacks.put(new WeakReference<>(instance), callbacks);
+        }
+    }
+
+    /**
+     * Removes a previously registered callback.
+     * @param instance the instance tied to the callback
+     *
+     * @hide
+     */
+    public void unregisterOnProvidersChangedCallback(@NonNull Object instance) {
+        synchronized (mLock) {
+            for (int i = 0, n = mChangeCallbacks.size(); i < n; i++) {
+                final WeakReference<Object> key = mChangeCallbacks.keyAt(i);
+                if (instance == key.get()) {
+                    mChangeCallbacks.removeAt(i);
+                    return;
+                }
+            }
+        }
+    }
+
+    /** Returns whether the arrays contain the same provider instances in the same order. */
+    private static boolean arrayEquals(ResourcesProvider[] a1, ResourcesProvider[] a2) {
+        if (a1 == a2) {
+            return true;
+        }
+
+        if (a1 == null || a2 == null) {
+            return false;
+        }
+
+        if (a1.length != a2.length) {
+            return false;
+        }
+
+        // Check that the arrays contain the exact same instances in the same order. Providers do
+        // not have any form of equivalence checking of whether the contents of two providers have
+        // equivalent apk assets.
+        for (int i = 0, n = a1.length; i < n; i++) {
+            if (a1[i] != a2[i]) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Invokes registered callbacks when the list of {@link ResourcesProvider} instances this loader
+     * uses changes.
+     */
+    private void notifyProvidersChangedLocked() {
+        final ArraySet<UpdateCallbacks> uniqueCallbacks = new ArraySet<>();
+        if (arrayEquals(mPreviousProviders, mProviders)) {
+            return;
+        }
+
+        if (mProviders == null || mProviders.length == 0) {
+            mApkAssets = null;
+        } else {
+            mApkAssets = new ApkAssets[mProviders.length];
+            for (int i = 0, n = mProviders.length; i < n; i++) {
+                mProviders[i].incrementRefCount();
+                mApkAssets[i] = mProviders[i].getApkAssets();
+            }
+        }
+
+        // Decrement the ref count after incrementing the new provider ref count so providers
+        // present before and after this method do not drop to zero references.
+        if (mPreviousProviders != null) {
+            for (ResourcesProvider provider : mPreviousProviders) {
+                provider.decrementRefCount();
+            }
+        }
+
+        mPreviousProviders = mProviders;
+
+        for (int i = mChangeCallbacks.size() - 1; i >= 0; i--) {
+            final WeakReference<Object> key = mChangeCallbacks.keyAt(i);
+            if (key.get() == null) {
+                mChangeCallbacks.removeAt(i);
+            } else {
+                uniqueCallbacks.add(mChangeCallbacks.valueAt(i));
+            }
+        }
+
+        for (int i = 0, n = uniqueCallbacks.size(); i < n; i++) {
+            uniqueCallbacks.valueAt(i).onLoaderUpdated(this);
+        }
+    }
+}
diff --git a/android/content/res/loader/ResourcesProvider.java b/android/content/res/loader/ResourcesProvider.java
new file mode 100644
index 0000000..0a698d1
--- /dev/null
+++ b/android/content/res/loader/ResourcesProvider.java
@@ -0,0 +1,294 @@
+/*
+ * 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.content.res.loader;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.res.ApkAssets;
+import android.content.res.AssetFileDescriptor;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * Provides methods to load resources data from APKs ({@code .apk}) and resources tables
+ * (eg. {@code resources.arsc}) for use with {@link ResourcesLoader ResourcesLoader(s)}.
+ */
+public class ResourcesProvider implements AutoCloseable, Closeable {
+    private static final String TAG = "ResourcesProvider";
+    private final Object mLock = new Object();
+
+    @GuardedBy("mLock")
+    private boolean mOpen = true;
+
+    @GuardedBy("mLock")
+    private int mOpenCount = 0;
+
+    @GuardedBy("mLock")
+    private final ApkAssets mApkAssets;
+
+    /**
+     * Creates an empty ResourcesProvider with no resource data. This is useful for loading
+     * file-based assets not associated with resource identifiers.
+     *
+     * @param assetsProvider the assets provider that implements the loading of file-based resources
+     */
+    @NonNull
+    public static ResourcesProvider empty(@NonNull AssetsProvider assetsProvider) {
+        return new ResourcesProvider(ApkAssets.loadEmptyForLoader(ApkAssets.PROPERTY_LOADER,
+                assetsProvider));
+    }
+
+    /**
+     * Creates a ResourcesProvider from an APK ({@code .apk}) file descriptor.
+     *
+     * <p>The file descriptor is duplicated and the original may be closed by the application at any
+     * time without affecting the ResourcesProvider.
+     *
+     * @param fileDescriptor the file descriptor of the APK to load
+     *
+     * @see ParcelFileDescriptor#open(File, int)
+     * @see android.system.Os#memfd_create(String, int)
+     */
+    @NonNull
+    public static ResourcesProvider loadFromApk(@NonNull ParcelFileDescriptor fileDescriptor)
+            throws IOException {
+        return loadFromApk(fileDescriptor, null /* assetsProvider */);
+    }
+
+    /**
+     * Creates a ResourcesProvider from an APK ({@code .apk}) file descriptor.
+     *
+     * <p>The file descriptor is duplicated and the original may be closed by the application at any
+     * time without affecting the ResourcesProvider.
+     *
+     * <p>The assets provider can override the loading of files within the APK and can provide
+     * entirely new files that do not exist in the APK.
+     *
+     * @param fileDescriptor the file descriptor of the APK to load
+     * @param assetsProvider the assets provider that overrides the loading of file-based resources
+     *
+     * @see ParcelFileDescriptor#open(File, int)
+     * @see android.system.Os#memfd_create(String, int)
+     */
+    @NonNull
+    public static ResourcesProvider loadFromApk(@NonNull ParcelFileDescriptor fileDescriptor,
+            @Nullable AssetsProvider assetsProvider)
+            throws IOException {
+        return new ResourcesProvider(ApkAssets.loadFromFd(fileDescriptor.getFileDescriptor(),
+                fileDescriptor.toString(), ApkAssets.PROPERTY_LOADER, assetsProvider));
+    }
+
+    /**
+     * Creates a ResourcesProvider from an APK ({@code .apk}) file descriptor.
+     *
+     * <p>The file descriptor is duplicated and the original may be closed by the application at any
+     * time without affecting the ResourcesProvider.
+     *
+     * <p>The assets provider can override the loading of files within the APK and can provide
+     * entirely new files that do not exist in the APK.
+     *
+     * @param fileDescriptor the file descriptor of the APK to load
+     * @param offset The location within the file that the apk starts. This must be 0 if length is
+     *               {@link AssetFileDescriptor#UNKNOWN_LENGTH}.
+     * @param length The number of bytes of the apk, or {@link AssetFileDescriptor#UNKNOWN_LENGTH}
+     *               if it extends to the end of the file.
+     * @param assetsProvider the assets provider that overrides the loading of file-based resources
+     *
+     * @see ParcelFileDescriptor#open(File, int)
+     * @see android.system.Os#memfd_create(String, int)
+     * @hide
+     */
+    @VisibleForTesting
+    @NonNull
+    public static ResourcesProvider loadFromApk(@NonNull ParcelFileDescriptor fileDescriptor,
+            long offset, long length, @Nullable AssetsProvider assetsProvider)
+            throws IOException {
+        return new ResourcesProvider(ApkAssets.loadFromFd(fileDescriptor.getFileDescriptor(),
+                fileDescriptor.toString(), offset, length, ApkAssets.PROPERTY_LOADER,
+                assetsProvider));
+    }
+
+    /**
+     * Creates a ResourcesProvider from a resources table ({@code .arsc}) file descriptor.
+     *
+     * <p>The file descriptor is duplicated and the original may be closed by the application at any
+     * time without affecting the ResourcesProvider.
+     *
+     * <p>The resources table format is not an archive format and therefore cannot asset files
+     * within itself. The assets provider can instead provide files that are potentially referenced
+     * by path in the resources table.
+     *
+     * @param fileDescriptor the file descriptor of the resources table to load
+     * @param assetsProvider the assets provider that implements the loading of file-based resources
+     *
+     * @see ParcelFileDescriptor#open(File, int)
+     * @see android.system.Os#memfd_create(String, int)
+     */
+    @NonNull
+    public static ResourcesProvider loadFromTable(@NonNull ParcelFileDescriptor fileDescriptor,
+            @Nullable AssetsProvider assetsProvider)
+            throws IOException {
+        return new ResourcesProvider(
+                ApkAssets.loadTableFromFd(fileDescriptor.getFileDescriptor(),
+                        fileDescriptor.toString(), ApkAssets.PROPERTY_LOADER, assetsProvider));
+    }
+
+    /**
+     * Creates a ResourcesProvider from a resources table ({@code .arsc}) file descriptor.
+     *
+     * The file descriptor is duplicated and the original may be closed by the application at any
+     * time without affecting the ResourcesProvider.
+     *
+     * <p>The resources table format is not an archive format and therefore cannot asset files
+     * within itself. The assets provider can instead provide files that are potentially referenced
+     * by path in the resources table.
+     *
+     * @param fileDescriptor the file descriptor of the resources table to load
+     * @param offset The location within the file that the table starts. This must be 0 if length is
+     *               {@link AssetFileDescriptor#UNKNOWN_LENGTH}.
+     * @param length The number of bytes of the table, or {@link AssetFileDescriptor#UNKNOWN_LENGTH}
+     *               if it extends to the end of the file.
+     * @param assetsProvider the assets provider that overrides the loading of file-based resources
+     *
+     * @see ParcelFileDescriptor#open(File, int)
+     * @see android.system.Os#memfd_create(String, int)
+     * @hide
+     */
+    @VisibleForTesting
+    @NonNull
+    public static ResourcesProvider loadFromTable(@NonNull ParcelFileDescriptor fileDescriptor,
+            long offset, long length, @Nullable AssetsProvider assetsProvider)
+            throws IOException {
+        return new ResourcesProvider(
+                ApkAssets.loadTableFromFd(fileDescriptor.getFileDescriptor(),
+                        fileDescriptor.toString(), offset, length, ApkAssets.PROPERTY_LOADER,
+                        assetsProvider));
+    }
+
+    /**
+     * Read from a split installed alongside the application, which may not have been
+     * loaded initially because the application requested isolated split loading.
+     *
+     * @param context a context of the package that contains the split
+     * @param splitName the name of the split to load
+     */
+    @NonNull
+    public static ResourcesProvider loadFromSplit(@NonNull Context context,
+            @NonNull String splitName) throws IOException {
+        ApplicationInfo appInfo = context.getApplicationInfo();
+        int splitIndex = ArrayUtils.indexOf(appInfo.splitNames, splitName);
+        if (splitIndex < 0) {
+            throw new IllegalArgumentException("Split " + splitName + " not found");
+        }
+
+        String splitPath = appInfo.getSplitCodePaths()[splitIndex];
+        return new ResourcesProvider(ApkAssets.loadFromPath(splitPath, ApkAssets.PROPERTY_LOADER,
+                null /* assetsProvider */));
+    }
+
+    /**
+     * Creates a ResourcesProvider from a directory path.
+     *
+     * File-based resources will be resolved within the directory as if the directory is an APK.
+     *
+     * @param path the path of the directory to treat as an APK
+     * @param assetsProvider the assets provider that overrides the loading of file-based resources
+     */
+    @NonNull
+    public static ResourcesProvider loadFromDirectory(@NonNull String path,
+            @Nullable AssetsProvider assetsProvider) throws IOException {
+        return new ResourcesProvider(ApkAssets.loadFromDir(path, ApkAssets.PROPERTY_LOADER,
+                assetsProvider));
+    }
+
+
+    private ResourcesProvider(@NonNull ApkAssets apkAssets) {
+        this.mApkAssets = apkAssets;
+    }
+
+    /** @hide */
+    @NonNull
+    public ApkAssets getApkAssets() {
+        return mApkAssets;
+    }
+
+    final void incrementRefCount() {
+        synchronized (mLock) {
+            if (!mOpen) {
+                throw new IllegalStateException("Operation failed: resources provider is closed");
+            }
+            mOpenCount++;
+        }
+    }
+
+    final void decrementRefCount() {
+        synchronized (mLock) {
+            mOpenCount--;
+        }
+    }
+
+    /**
+     * Frees internal data structures. Closed providers can no longer be added to
+     * {@link ResourcesLoader ResourcesLoader(s)}.
+     *
+     * @throws IllegalStateException if provider is currently used by a ResourcesLoader
+     */
+    @Override
+    public void close() {
+        synchronized (mLock) {
+            if (!mOpen) {
+                return;
+            }
+
+            if (mOpenCount != 0) {
+                throw new IllegalStateException("Failed to close provider used by " + mOpenCount
+                        + " ResourcesLoader instances");
+            }
+            mOpen = false;
+        }
+
+        try {
+            mApkAssets.close();
+        } catch (Throwable ignored) {
+        }
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        synchronized (mLock) {
+            if (mOpenCount != 0) {
+                Log.w(TAG, "ResourcesProvider " + this + " finalized with non-zero refs: "
+                        + mOpenCount);
+            }
+
+            if (mOpen) {
+                mOpen = false;
+                mApkAssets.close();
+            }
+        }
+    }
+}
diff --git a/android/content/rollback/PackageRollbackInfo.java b/android/content/rollback/PackageRollbackInfo.java
new file mode 100644
index 0000000..b273cd6
--- /dev/null
+++ b/android/content/rollback/PackageRollbackInfo.java
@@ -0,0 +1,262 @@
+/*
+ * 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.content.rollback;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.content.pm.PackageManager;
+import android.content.pm.VersionedPackage;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.IntArray;
+import android.util.SparseLongArray;
+
+import java.util.ArrayList;
+
+/**
+ * Information about a rollback available for a particular package.
+ *
+ * @hide
+ */
+@SystemApi @TestApi
+public final class PackageRollbackInfo implements Parcelable {
+
+    private final VersionedPackage mVersionRolledBackFrom;
+    private final VersionedPackage mVersionRolledBackTo;
+
+    /**
+     * Encapsulates information required to restore a snapshot of an app's userdata.
+     *
+     * @hide
+     */
+    public static class RestoreInfo {
+        public final int userId;
+        public final int appId;
+        public final String seInfo;
+
+        public RestoreInfo(int userId, int appId, String seInfo) {
+            this.userId = userId;
+            this.appId = appId;
+            this.seInfo = seInfo;
+        }
+    }
+
+    /*
+     * The list of users for which we need to backup userdata for this package. Backups of
+     * credential encrypted data are listed as pending if the user hasn't unlocked their device
+     * with credentials yet.
+     */
+    // NOTE: Not a part of the Parcelable representation of this object.
+    private final IntArray mPendingBackups;
+
+    /**
+     * The list of users for which we need to restore userdata for this package. This field is
+     * non-null only after a rollback for this package has been committed.
+     */
+    // NOTE: Not a part of the Parcelable representation of this object.
+    private final ArrayList<RestoreInfo> mPendingRestores;
+
+    /**
+     * Whether this instance represents the PackageRollbackInfo for an APEX module.
+     */
+    private final boolean mIsApex;
+
+    /**
+     * Whether this instance represents the PackageRollbackInfo for an APK in APEX.
+     */
+    private final boolean mIsApkInApex;
+
+    /*
+     * The list of users for which snapshots have been saved.
+     */
+    // NOTE: Not a part of the Parcelable representation of this object.
+    private final IntArray mSnapshottedUsers;
+
+    /**
+     * A mapping between user and an inode of theirs CE data snapshot.
+     */
+    // NOTE: Not a part of the Parcelable representation of this object.
+    private final SparseLongArray mCeSnapshotInodes;
+
+    /**
+     * The userdata policy to execute when a rollback for this package is committed.
+     */
+    private final int mRollbackDataPolicy;
+
+    /**
+     * Returns the name of the package to roll back from.
+     */
+    @NonNull
+    public String getPackageName() {
+        return mVersionRolledBackFrom.getPackageName();
+    }
+
+    /**
+     * Returns the version of the package rolled back from.
+     */
+    @NonNull
+    public VersionedPackage getVersionRolledBackFrom() {
+        return mVersionRolledBackFrom;
+    }
+
+    /**
+     * Returns the version of the package rolled back to.
+     */
+    @NonNull
+    public VersionedPackage getVersionRolledBackTo() {
+        return mVersionRolledBackTo;
+    }
+
+    /** @hide */
+    public void addPendingBackup(int userId) {
+        mPendingBackups.add(userId);
+    }
+
+    /** @hide */
+    public IntArray getPendingBackups() {
+        return mPendingBackups;
+    }
+
+    /** @hide */
+    public ArrayList<RestoreInfo> getPendingRestores() {
+        return mPendingRestores;
+    }
+
+    /** @hide */
+    public RestoreInfo getRestoreInfo(int userId) {
+        for (RestoreInfo ri : mPendingRestores) {
+            if (ri.userId == userId) {
+                return ri;
+            }
+        }
+
+        return null;
+    }
+
+    /** @hide */
+    public void removeRestoreInfo(RestoreInfo ri) {
+        mPendingRestores.remove(ri);
+    }
+
+    /** @hide */
+    public boolean isApex() {
+        return mIsApex;
+    }
+
+    /** @hide */
+    public @PackageManager.RollbackDataPolicy int getRollbackDataPolicy() {
+        return mRollbackDataPolicy;
+    }
+    /** @hide */
+    public boolean isApkInApex() {
+        return mIsApkInApex;
+    }
+
+    /** @hide */
+    public IntArray getSnapshottedUsers() {
+        return mSnapshottedUsers;
+    }
+
+    /** @hide */
+    public SparseLongArray getCeSnapshotInodes() {
+        return mCeSnapshotInodes;
+    }
+
+    /** @hide */
+    public void putCeSnapshotInode(int userId, long ceSnapshotInode) {
+        mCeSnapshotInodes.put(userId, ceSnapshotInode);
+    }
+
+    /** @hide */
+    public void removePendingBackup(int userId) {
+        int idx = mPendingBackups.indexOf(userId);
+        if (idx != -1) {
+            mPendingBackups.remove(idx);
+        }
+    }
+
+    /** @hide */
+    public void removePendingRestoreInfo(int userId) {
+        removeRestoreInfo(getRestoreInfo(userId));
+    }
+
+    /** @hide */
+    public PackageRollbackInfo(VersionedPackage packageRolledBackFrom,
+            VersionedPackage packageRolledBackTo,
+            @NonNull IntArray pendingBackups, @NonNull ArrayList<RestoreInfo> pendingRestores,
+            boolean isApex, boolean isApkInApex, @NonNull IntArray snapshottedUsers,
+            @NonNull SparseLongArray ceSnapshotInodes) {
+        this(packageRolledBackFrom, packageRolledBackTo, pendingBackups, pendingRestores, isApex,
+                isApkInApex, snapshottedUsers, ceSnapshotInodes,
+                PackageManager.RollbackDataPolicy.RESTORE);
+    }
+
+    /** @hide */
+    public PackageRollbackInfo(VersionedPackage packageRolledBackFrom,
+            VersionedPackage packageRolledBackTo,
+            @NonNull IntArray pendingBackups, @NonNull ArrayList<RestoreInfo> pendingRestores,
+            boolean isApex, boolean isApkInApex, @NonNull IntArray snapshottedUsers,
+            @NonNull SparseLongArray ceSnapshotInodes,
+            @PackageManager.RollbackDataPolicy int rollbackDataPolicy) {
+        this.mVersionRolledBackFrom = packageRolledBackFrom;
+        this.mVersionRolledBackTo = packageRolledBackTo;
+        this.mPendingBackups = pendingBackups;
+        this.mPendingRestores = pendingRestores;
+        this.mIsApex = isApex;
+        this.mRollbackDataPolicy = rollbackDataPolicy;
+        this.mIsApkInApex = isApkInApex;
+        this.mSnapshottedUsers = snapshottedUsers;
+        this.mCeSnapshotInodes = ceSnapshotInodes;
+    }
+
+    private PackageRollbackInfo(Parcel in) {
+        this.mVersionRolledBackFrom = VersionedPackage.CREATOR.createFromParcel(in);
+        this.mVersionRolledBackTo = VersionedPackage.CREATOR.createFromParcel(in);
+        this.mIsApex = in.readBoolean();
+        this.mIsApkInApex = in.readBoolean();
+        this.mPendingRestores = null;
+        this.mPendingBackups = null;
+        this.mSnapshottedUsers = null;
+        this.mCeSnapshotInodes = null;
+        this.mRollbackDataPolicy = PackageManager.RollbackDataPolicy.RESTORE;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        mVersionRolledBackFrom.writeToParcel(out, flags);
+        mVersionRolledBackTo.writeToParcel(out, flags);
+        out.writeBoolean(mIsApex);
+        out.writeBoolean(mIsApkInApex);
+    }
+
+    public static final @NonNull Parcelable.Creator<PackageRollbackInfo> CREATOR =
+            new Parcelable.Creator<PackageRollbackInfo>() {
+        public PackageRollbackInfo createFromParcel(Parcel in) {
+            return new PackageRollbackInfo(in);
+        }
+
+        public PackageRollbackInfo[] newArray(int size) {
+            return new PackageRollbackInfo[size];
+        }
+    };
+}
diff --git a/android/content/rollback/RollbackInfo.java b/android/content/rollback/RollbackInfo.java
new file mode 100644
index 0000000..c09cfd5
--- /dev/null
+++ b/android/content/rollback/RollbackInfo.java
@@ -0,0 +1,140 @@
+/*
+ * 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.content.rollback;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.content.pm.VersionedPackage;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.List;
+
+/**
+ * Information about a set of packages that can be, or already have been
+ * rolled back together.
+ *
+ * @hide
+ */
+@SystemApi @TestApi
+public final class RollbackInfo implements Parcelable {
+
+    /**
+     * A unique identifier for the rollback.
+     */
+    private final int mRollbackId;
+
+    private final List<PackageRollbackInfo> mPackages;
+
+    private final List<VersionedPackage> mCausePackages;
+
+    private final boolean mIsStaged;
+    private int mCommittedSessionId;
+
+    /** @hide */
+    public RollbackInfo(int rollbackId, List<PackageRollbackInfo> packages, boolean isStaged,
+            List<VersionedPackage> causePackages,  int committedSessionId) {
+        this.mRollbackId = rollbackId;
+        this.mPackages = packages;
+        this.mIsStaged = isStaged;
+        this.mCausePackages = causePackages;
+        this.mCommittedSessionId = committedSessionId;
+    }
+
+    private RollbackInfo(Parcel in) {
+        mRollbackId = in.readInt();
+        mPackages = in.createTypedArrayList(PackageRollbackInfo.CREATOR);
+        mIsStaged = in.readBoolean();
+        mCausePackages = in.createTypedArrayList(VersionedPackage.CREATOR);
+        mCommittedSessionId = in.readInt();
+    }
+
+    /**
+     * Returns a unique identifier for this rollback.
+     */
+    public int getRollbackId() {
+        return mRollbackId;
+    }
+
+    /**
+     * Returns the list of package that are rolled back.
+     */
+    @NonNull
+    public List<PackageRollbackInfo> getPackages() {
+        return mPackages;
+    }
+
+    /**
+     * Returns true if this rollback requires reboot to take effect after
+     * being committed.
+     */
+    public boolean isStaged() {
+        return mIsStaged;
+    }
+
+    /**
+     * Returns the session ID for the committed rollback for staged rollbacks.
+     * Only applicable for rollbacks that have been committed.
+     */
+    public int getCommittedSessionId() {
+        return mCommittedSessionId;
+    }
+
+    /**
+     * Sets the session ID for the committed rollback for staged rollbacks.
+     * @hide
+     */
+    public void setCommittedSessionId(int sessionId) {
+        mCommittedSessionId = sessionId;
+    }
+
+    /**
+     * Gets the list of package versions that motivated this rollback.
+     * As provided to {@link #commitRollback} when the rollback was committed.
+     * This is only applicable for rollbacks that have been committed.
+     */
+    @NonNull
+    public List<VersionedPackage> getCausePackages() {
+        return mCausePackages;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(mRollbackId);
+        out.writeTypedList(mPackages);
+        out.writeBoolean(mIsStaged);
+        out.writeTypedList(mCausePackages);
+        out.writeInt(mCommittedSessionId);
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<RollbackInfo> CREATOR =
+            new Parcelable.Creator<RollbackInfo>() {
+        public RollbackInfo createFromParcel(Parcel in) {
+            return new RollbackInfo(in);
+        }
+
+        public RollbackInfo[] newArray(int size) {
+            return new RollbackInfo[size];
+        }
+    };
+}
diff --git a/android/content/rollback/RollbackManager.java b/android/content/rollback/RollbackManager.java
new file mode 100644
index 0000000..7ebeb21
--- /dev/null
+++ b/android/content/rollback/RollbackManager.java
@@ -0,0 +1,278 @@
+/*
+ * 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.content.rollback;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.annotation.TestApi;
+import android.content.Context;
+import android.content.IntentSender;
+import android.content.pm.PackageInstaller;
+import android.content.pm.ParceledListSlice;
+import android.content.pm.VersionedPackage;
+import android.os.RemoteException;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+
+/**
+ * Offers the ability to rollback packages after upgrade.
+ * <p>
+ * For packages installed with rollbacks enabled, the RollbackManager can be
+ * used to initiate rollback of those packages for a limited time period after
+ * upgrade.
+ *
+ * @see PackageInstaller.SessionParams#setEnableRollback(boolean)
+ * @hide
+ */
+@SystemApi @TestApi
+@SystemService(Context.ROLLBACK_SERVICE)
+public final class RollbackManager {
+    private final String mCallerPackageName;
+    private final IRollbackManager mBinder;
+
+    /**
+     * Lifetime duration of rollback packages in millis. A rollback will be available for
+     * at most that duration of time after a package is installed with
+     * {@link PackageInstaller.SessionParams#setEnableRollback(boolean)}.
+     *
+     * <p>If flag value is negative, the default value will be assigned.
+     *
+     * @see RollbackManager
+     *
+     * Flag type: {@code long}
+     * Namespace: NAMESPACE_ROLLBACK_BOOT
+     *
+     * @hide
+     */
+    @TestApi
+    public static final String PROPERTY_ROLLBACK_LIFETIME_MILLIS =
+            "rollback_lifetime_in_millis";
+
+    /** {@hide} */
+    public RollbackManager(Context context, IRollbackManager binder) {
+        mCallerPackageName = context.getPackageName();
+        mBinder = binder;
+    }
+
+    /**
+     * Returns a list of all currently available rollbacks.
+     *
+     * @throws SecurityException if the caller does not have appropriate permissions.
+     */
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_ROLLBACKS,
+            android.Manifest.permission.TEST_MANAGE_ROLLBACKS
+    })
+    @NonNull
+    public List<RollbackInfo> getAvailableRollbacks() {
+        try {
+            return mBinder.getAvailableRollbacks().getList();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Gets the list of all recently committed rollbacks.
+     * This is for the purposes of preventing re-install of a bad version of a
+     * package and monitoring the status of a staged rollback.
+     * <p>
+     * Returns an empty list if there are no recently committed rollbacks.
+     * <p>
+     * To avoid having to keep around complete rollback history forever on a
+     * device, the returned list of rollbacks is only guaranteed to include
+     * rollbacks that are still relevant. A rollback is no longer considered
+     * relevant if the package is subsequently uninstalled or upgraded
+     * (without the possibility of rollback) to a higher version code than was
+     * rolled back from.
+     *
+     * @return the recently committed rollbacks
+     * @throws SecurityException if the caller does not have appropriate permissions.
+     */
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_ROLLBACKS,
+            android.Manifest.permission.TEST_MANAGE_ROLLBACKS
+    })
+    public @NonNull List<RollbackInfo> getRecentlyCommittedRollbacks() {
+        try {
+            return mBinder.getRecentlyCommittedRollbacks().getList();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Status of a rollback commit. Will be one of
+     * {@link #STATUS_SUCCESS}, {@link #STATUS_FAILURE},
+     * {@link #STATUS_FAILURE_ROLLBACK_UNAVAILABLE}, {@link #STATUS_FAILURE_INSTALL}
+     *
+     * @see Intent#getIntExtra(String, int)
+     */
+    public static final String EXTRA_STATUS = "android.content.rollback.extra.STATUS";
+
+    /**
+     * Detailed string representation of the status, including raw details that
+     * are useful for debugging.
+     *
+     * @see Intent#getStringExtra(String)
+     */
+    public static final String EXTRA_STATUS_MESSAGE =
+            "android.content.rollback.extra.STATUS_MESSAGE";
+
+    /**
+     * Status result of committing a rollback.
+     *
+     * @hide
+     */
+    @IntDef(prefix = "STATUS_", value = {
+            STATUS_SUCCESS,
+            STATUS_FAILURE,
+            STATUS_FAILURE_ROLLBACK_UNAVAILABLE,
+            STATUS_FAILURE_INSTALL,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Status {};
+
+    /**
+     * The rollback was successfully committed.
+     */
+    public static final int STATUS_SUCCESS = 0;
+
+    /**
+     * The rollback could not be committed due to some generic failure.
+     *
+     * @see #EXTRA_STATUS_MESSAGE
+     */
+    public static final int STATUS_FAILURE = 1;
+
+    /**
+     * The rollback could not be committed because it was no longer available.
+     *
+     * @see #EXTRA_STATUS_MESSAGE
+     */
+    public static final int STATUS_FAILURE_ROLLBACK_UNAVAILABLE = 2;
+
+    /**
+     * The rollback failed to install successfully.
+     *
+     * @see #EXTRA_STATUS_MESSAGE
+     */
+    public static final int STATUS_FAILURE_INSTALL = 3;
+
+    /**
+     * Commit the rollback with given id, rolling back all versions of the
+     * packages to the last good versions previously installed on the device
+     * as specified in the corresponding RollbackInfo object. The
+     * rollback will fail if any of the installed packages or available
+     * rollbacks are inconsistent with the versions specified in the given
+     * rollback object, which can happen if a package has been updated or a
+     * rollback expired since the rollback object was retrieved from
+     * {@link #getAvailableRollbacks()}.
+     *
+     * @param rollbackId ID of the rollback to commit
+     * @param causePackages package versions to record as the motivation for this
+     *                      rollback.
+     * @param statusReceiver where to deliver the results. Intents sent to
+     *                       this receiver contain {@link #EXTRA_STATUS}
+     *                       and {@link #EXTRA_STATUS_MESSAGE}.
+     * @throws SecurityException if the caller does not have appropriate permissions.
+     */
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_ROLLBACKS,
+            android.Manifest.permission.TEST_MANAGE_ROLLBACKS
+    })
+    public void commitRollback(int rollbackId, @NonNull List<VersionedPackage> causePackages,
+            @NonNull IntentSender statusReceiver) {
+        try {
+            mBinder.commitRollback(rollbackId, new ParceledListSlice(causePackages),
+                    mCallerPackageName, statusReceiver);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Reload all persisted rollback data from device storage.
+     * This API is meant to test that rollback state is properly preserved
+     * across device reboot, by simulating what happens on reboot without
+     * actually rebooting the device.
+     *
+     * Note rollbacks in the process of enabling will be lost after calling
+     * this method since they are not persisted yet. Don't call this method
+     * in the middle of the install process.
+     *
+     * @throws SecurityException if the caller does not have appropriate permissions.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.TEST_MANAGE_ROLLBACKS)
+    @TestApi
+    public void reloadPersistedData() {
+        try {
+            mBinder.reloadPersistedData();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Expire the rollback data for a given package.
+     * This API is meant to facilitate testing of rollback logic for
+     * expiring rollback data. Removes rollback data for available and
+     * recently committed rollbacks that contain the given package.
+     *
+     * @param packageName the name of the package to expire data for.
+     * @throws SecurityException if the caller does not have appropriate permissions.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.TEST_MANAGE_ROLLBACKS)
+    @TestApi
+    public void expireRollbackForPackage(@NonNull String packageName) {
+        try {
+            mBinder.expireRollbackForPackage(packageName);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Block the RollbackManager for a specified amount of time.
+     * This API is meant to facilitate testing of race conditions in
+     * RollbackManager. Blocks RollbackManager from processing anything for
+     * the given number of milliseconds.
+     *
+     * @param millis number of milliseconds to block the RollbackManager for
+     * @throws SecurityException if the caller does not have appropriate permissions.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.TEST_MANAGE_ROLLBACKS)
+    @TestApi
+    public void blockRollbackManager(long millis) {
+        try {
+            mBinder.blockRollbackManager(millis);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+}
diff --git a/android/content/type/DefaultMimeMapFactory.java b/android/content/type/DefaultMimeMapFactory.java
new file mode 100644
index 0000000..11d20d4
--- /dev/null
+++ b/android/content/type/DefaultMimeMapFactory.java
@@ -0,0 +1,106 @@
+/*
+ * 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.content.type;
+
+import libcore.content.type.MimeMap;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.function.Function;
+
+/**
+ * Creates the framework default {@link MimeMap}, a bidirectional mapping
+ * between MIME types and file extensions.
+ *
+ * This default mapping is loaded from data files that start with some mappings
+ * recognized by IANA plus some custom extensions and overrides.
+ *
+ * @hide
+ */
+public class DefaultMimeMapFactory {
+
+    private DefaultMimeMapFactory() {
+    }
+
+    /**
+     * Creates and returns a new {@link MimeMap} instance that implements.
+     * Android's default mapping between MIME types and extensions.
+     */
+    public static MimeMap create() {
+        Class c = DefaultMimeMapFactory.class;
+        // The resources are placed into the res/ path by the "mimemap-res.jar" genrule.
+        return create(resourceName -> c.getResourceAsStream("/res/" + resourceName));
+    }
+
+    /**
+     * Creates a {@link MimeMap} instance whose resources are loaded from the
+     * InputStreams looked up in {@code resourceSupplier}.
+     *
+     * @hide
+     */
+    public static MimeMap create(Function<String, InputStream> resourceSupplier) {
+        MimeMap.Builder builder = MimeMap.builder();
+        // The files loaded here must be in minimized format with lines of the
+        // form "mime/type ext1 ext2 ext3", i.e. no comments, no empty lines, no
+        // leading/trailing whitespace and with a single space between entries on
+        // each line.  See http://b/142267887
+        //
+        // Note: the order here matters - later entries can overwrite earlier ones
+        // (except that vendor.mime.types entries are prefixed with '?' which makes
+        // them never overwrite).
+        parseTypes(builder, resourceSupplier, "debian.mime.types");
+        parseTypes(builder, resourceSupplier, "android.mime.types");
+        parseTypes(builder, resourceSupplier, "vendor.mime.types");
+        return builder.build();
+    }
+
+    private static void parseTypes(MimeMap.Builder builder,
+            Function<String, InputStream> resourceSupplier, String resourceName) {
+        try (InputStream inputStream = Objects.requireNonNull(resourceSupplier.apply(resourceName));
+             BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
+            String line;
+            List<String> specs = new ArrayList<>(10); // re-use for each line
+            while ((line = reader.readLine()) != null) {
+                specs.clear();
+                // Lines are of the form "mimeSpec extSpec extSpec[...]" with a single space
+                // separating them and no leading/trailing spaces and no empty lines.
+                int startIdx = 0;
+                do {
+                    int endIdx = line.indexOf(' ', startIdx);
+                    if (endIdx < 0) {
+                        endIdx = line.length();
+                    }
+                    String spec = line.substring(startIdx, endIdx);
+                    if (spec.isEmpty()) {
+                        throw new IllegalArgumentException("Malformed line: " + line);
+                    }
+                    specs.add(spec);
+                    startIdx = endIdx + 1; // skip over the space
+                } while (startIdx < line.length());
+                builder.put(specs.get(0), specs.subList(1, specs.size()));
+            }
+        } catch (IOException | RuntimeException e) {
+            throw new RuntimeException("Failed to parse " + resourceName, e);
+        }
+    }
+
+}