Merge "Move distortion correction to correct opcode list." into mnc-dev
diff --git a/api/current.txt b/api/current.txt
index 5126123..2094c7c 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -26564,7 +26564,6 @@
     field public static final java.lang.String DEBUG_APP = "debug_app";
     field public static final java.lang.String DEVELOPMENT_SETTINGS_ENABLED = "development_settings_enabled";
     field public static final java.lang.String DEVICE_PROVISIONED = "device_provisioned";
-    field public static final java.lang.String HIDE_CARRIER_NETWORK_SETTINGS = "hide_carrier_network_settings";
     field public static final java.lang.String HTTP_PROXY = "http_proxy";
     field public static final deprecated java.lang.String INSTALL_NON_MARKET_APPS = "install_non_market_apps";
     field public static final java.lang.String MODE_RINGER = "mode_ringer";
diff --git a/api/system-current.txt b/api/system-current.txt
index d3823fc..9386c50 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -28600,7 +28600,6 @@
     field public static final java.lang.String DEBUG_APP = "debug_app";
     field public static final java.lang.String DEVELOPMENT_SETTINGS_ENABLED = "development_settings_enabled";
     field public static final java.lang.String DEVICE_PROVISIONED = "device_provisioned";
-    field public static final java.lang.String HIDE_CARRIER_NETWORK_SETTINGS = "hide_carrier_network_settings";
     field public static final java.lang.String HTTP_PROXY = "http_proxy";
     field public static final deprecated java.lang.String INSTALL_NON_MARKET_APPS = "install_non_market_apps";
     field public static final java.lang.String MODE_RINGER = "mode_ringer";
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index c92c256..287e0c5 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -4024,7 +4024,7 @@
 
     public static final PublicKey parsePublicKey(final String encodedPublicKey) {
         if (encodedPublicKey == null) {
-            Slog.i(TAG, "Could not parse null public key");
+            Slog.w(TAG, "Could not parse null public key");
             return null;
         }
 
@@ -4033,7 +4033,7 @@
             final byte[] encoded = Base64.decode(encodedPublicKey, Base64.DEFAULT);
             keySpec = new X509EncodedKeySpec(encoded);
         } catch (IllegalArgumentException e) {
-            Slog.i(TAG, "Could not parse verifier public key; invalid Base64");
+            Slog.w(TAG, "Could not parse verifier public key; invalid Base64");
             return null;
         }
 
@@ -4042,23 +4042,32 @@
             final KeyFactory keyFactory = KeyFactory.getInstance("RSA");
             return keyFactory.generatePublic(keySpec);
         } catch (NoSuchAlgorithmException e) {
-            Log.wtf(TAG, "Could not parse public key because RSA isn't included in build");
-            return null;
+            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) {
-            Log.wtf(TAG, "Could not parse public key because DSA isn't included in build");
-            return null;
+            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;
     }
 
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index dfd72c2..3e9c9de 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7232,13 +7232,6 @@
                 "preferred_network_mode";
 
         /**
-         * Setting to 1 will hide carrier network settings.
-         * Default is 0.
-         */
-        public static final String HIDE_CARRIER_NETWORK_SETTINGS =
-                "hide_carrier_network_settings";
-
-        /**
          * Name of an application package to be debugged.
          */
         public static final String DEBUG_APP = "debug_app";
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 89743e5..73cfd8c 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -2939,11 +2939,9 @@
         }
     }
 
-    /** @hide */
     @Override
-    public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEventInternal(event);
-        event.setClassName(ViewGroup.class.getName());
+    public CharSequence getAccessibilityClassName() {
+        return ViewGroup.class.getName();
     }
 
     @Override
diff --git a/core/java/android/view/textservice/SpellCheckerSession.java b/core/java/android/view/textservice/SpellCheckerSession.java
index 195a335..e77dc0d 100644
--- a/core/java/android/view/textservice/SpellCheckerSession.java
+++ b/core/java/android/view/textservice/SpellCheckerSession.java
@@ -28,9 +28,6 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.util.Log;
-import android.view.textservice.SpellCheckerInfo;
-import android.view.textservice.SuggestionsInfo;
-import android.view.textservice.TextInfo;
 
 import java.util.LinkedList;
 import java.util.Queue;
@@ -226,17 +223,44 @@
         private static final int TASK_GET_SUGGESTIONS_MULTIPLE = 2;
         private static final int TASK_CLOSE = 3;
         private static final int TASK_GET_SUGGESTIONS_MULTIPLE_FOR_SENTENCE = 4;
-        private final Queue<SpellCheckerParams> mPendingTasks =
-                new LinkedList<SpellCheckerParams>();
+        private static String taskToString(int task) {
+            switch (task) {
+                case TASK_CANCEL:
+                    return "TASK_CANCEL";
+                case TASK_GET_SUGGESTIONS_MULTIPLE:
+                    return "TASK_GET_SUGGESTIONS_MULTIPLE";
+                case TASK_CLOSE:
+                    return "TASK_CLOSE";
+                case TASK_GET_SUGGESTIONS_MULTIPLE_FOR_SENTENCE:
+                    return "TASK_GET_SUGGESTIONS_MULTIPLE_FOR_SENTENCE";
+                default:
+                    return "Unexpected task=" + task;
+            }
+        }
+
+        private final Queue<SpellCheckerParams> mPendingTasks = new LinkedList<>();
         private Handler mHandler;
 
-        private boolean mOpened;
+        private static final int STATE_WAIT_CONNECTION = 0;
+        private static final int STATE_CONNECTED = 1;
+        private static final int STATE_CLOSED_AFTER_CONNECTION = 2;
+        private static final int STATE_CLOSED_BEFORE_CONNECTION = 3;
+        private static String stateToString(int state) {
+            switch (state) {
+                case STATE_WAIT_CONNECTION: return "STATE_WAIT_CONNECTION";
+                case STATE_CONNECTED: return "STATE_CONNECTED";
+                case STATE_CLOSED_AFTER_CONNECTION: return "STATE_CLOSED_AFTER_CONNECTION";
+                case STATE_CLOSED_BEFORE_CONNECTION: return "STATE_CLOSED_BEFORE_CONNECTION";
+                default: return "Unexpected state=" + state;
+            }
+        }
+        private int mState = STATE_WAIT_CONNECTION;
+
         private ISpellCheckerSession mISpellCheckerSession;
         private HandlerThread mThread;
         private Handler mAsyncHandler;
 
         public SpellCheckerSessionListenerImpl(Handler handler) {
-            mOpened = false;
             mHandler = handler;
         }
 
@@ -257,12 +281,18 @@
 
         private void processTask(ISpellCheckerSession session, SpellCheckerParams scp,
                 boolean async) {
+            if (DBG) {
+                synchronized (this) {
+                    Log.d(TAG, "entering processTask:"
+                            + " session.hashCode()=#" + Integer.toHexString(session.hashCode())
+                            + " scp.mWhat=" + taskToString(scp.mWhat) + " async=" + async
+                            + " mAsyncHandler=" + mAsyncHandler
+                            + " mState=" + stateToString(mState));
+                }
+            }
             if (async || mAsyncHandler == null) {
                 switch (scp.mWhat) {
                     case TASK_CANCEL:
-                        if (DBG) {
-                            Log.w(TAG, "Cancel spell checker tasks.");
-                        }
                         try {
                             session.onCancel();
                         } catch (RemoteException e) {
@@ -270,9 +300,6 @@
                         }
                         break;
                     case TASK_GET_SUGGESTIONS_MULTIPLE:
-                        if (DBG) {
-                            Log.w(TAG, "Get suggestions from the spell checker.");
-                        }
                         try {
                             session.onGetSuggestionsMultiple(scp.mTextInfos,
                                     scp.mSuggestionsLimit, scp.mSequentialWords);
@@ -281,9 +308,6 @@
                         }
                         break;
                     case TASK_GET_SUGGESTIONS_MULTIPLE_FOR_SENTENCE:
-                        if (DBG) {
-                            Log.w(TAG, "Get sentence suggestions from the spell checker.");
-                        }
                         try {
                             session.onGetSentenceSuggestionsMultiple(
                                     scp.mTextInfos, scp.mSuggestionsLimit);
@@ -292,9 +316,6 @@
                         }
                         break;
                     case TASK_CLOSE:
-                        if (DBG) {
-                            Log.w(TAG, "Close spell checker tasks.");
-                        }
                         try {
                             session.onClose();
                         } catch (RemoteException e) {
@@ -313,21 +334,62 @@
                 // If we are closing, we want to clean up our state now even
                 // if it is pending as an async operation.
                 synchronized (this) {
-                    mISpellCheckerSession = null;
-                    mHandler = null;
-                    if (mThread != null) {
-                        mThread.quit();
-                    }
-                    mThread = null;
-                    mAsyncHandler = null;
+                    processCloseLocked();
                 }
             }
         }
 
+        private void processCloseLocked() {
+            if (DBG) Log.d(TAG, "entering processCloseLocked:"
+                    + " session" + (mISpellCheckerSession != null ? ".hashCode()=#"
+                            + Integer.toHexString(mISpellCheckerSession.hashCode()) : "=null")
+                    + " mState=" + stateToString(mState));
+            mISpellCheckerSession = null;
+            if (mThread != null) {
+                mThread.quit();
+            }
+            mHandler = null;
+            mPendingTasks.clear();
+            mThread = null;
+            mAsyncHandler = null;
+            switch (mState) {
+                case STATE_WAIT_CONNECTION:
+                    mState = STATE_CLOSED_BEFORE_CONNECTION;
+                    break;
+                case STATE_CONNECTED:
+                    mState = STATE_CLOSED_AFTER_CONNECTION;
+                    break;
+                default:
+                    Log.e(TAG, "processCloseLocked is called unexpectedly. mState=" +
+                            stateToString(mState));
+                    break;
+            }
+        }
+
         public synchronized void onServiceConnected(ISpellCheckerSession session) {
             synchronized (this) {
+                switch (mState) {
+                    case STATE_WAIT_CONNECTION:
+                        // OK, go ahead.
+                        break;
+                    case STATE_CLOSED_BEFORE_CONNECTION:
+                        // This is possible, and not an error.  The client no longer is interested
+                        // in this connection. OK to ignore.
+                        if (DBG) Log.i(TAG, "ignoring onServiceConnected since the session is"
+                                + " already closed.");
+                        return;
+                    default:
+                        Log.e(TAG, "ignoring onServiceConnected due to unexpected mState="
+                                + stateToString(mState));
+                        return;
+                }
+                if (session == null) {
+                    Log.e(TAG, "ignoring onServiceConnected due to session=null");
+                    return;
+                }
                 mISpellCheckerSession = session;
                 if (session.asBinder() instanceof Binder && mThread == null) {
+                    if (DBG) Log.d(TAG, "starting HandlerThread in onServiceConnected.");
                     // If this is a local object, we need to do our own threading
                     // to make sure we handle it asynchronously.
                     mThread = new HandlerThread("SpellCheckerSession",
@@ -340,62 +402,65 @@
                         }
                     };
                 }
-                mOpened = true;
+                mState = STATE_CONNECTED;
+                if (DBG) {
+                    Log.d(TAG, "processed onServiceConnected: mISpellCheckerSession.hashCode()=#"
+                            + Integer.toHexString(mISpellCheckerSession.hashCode())
+                            + " mPendingTasks.size()=" + mPendingTasks.size());
+                }
             }
-            if (DBG)
-                Log.d(TAG, "onServiceConnected - Success");
             while (!mPendingTasks.isEmpty()) {
                 processTask(session, mPendingTasks.poll(), false);
             }
         }
 
         public void cancel() {
-            if (DBG) {
-                Log.w(TAG, "cancel");
-            }
             processOrEnqueueTask(new SpellCheckerParams(TASK_CANCEL, null, 0, false));
         }
 
         public void getSuggestionsMultiple(
                 TextInfo[] textInfos, int suggestionsLimit, boolean sequentialWords) {
-            if (DBG) {
-                Log.w(TAG, "getSuggestionsMultiple");
-            }
             processOrEnqueueTask(
                     new SpellCheckerParams(TASK_GET_SUGGESTIONS_MULTIPLE, textInfos,
                             suggestionsLimit, sequentialWords));
         }
 
         public void getSentenceSuggestionsMultiple(TextInfo[] textInfos, int suggestionsLimit) {
-            if (DBG) {
-                Log.w(TAG, "getSentenceSuggestionsMultiple");
-            }
             processOrEnqueueTask(
                     new SpellCheckerParams(TASK_GET_SUGGESTIONS_MULTIPLE_FOR_SENTENCE,
                             textInfos, suggestionsLimit, false));
         }
 
         public void close() {
-            if (DBG) {
-                Log.w(TAG, "close");
-            }
             processOrEnqueueTask(new SpellCheckerParams(TASK_CLOSE, null, 0, false));
         }
 
         public boolean isDisconnected() {
-            return mOpened && mISpellCheckerSession == null;
+            synchronized (this) {
+                return mState != STATE_CONNECTED;
+            }
         }
 
         private void processOrEnqueueTask(SpellCheckerParams scp) {
-            if (DBG) {
-                Log.d(TAG, "process or enqueue task: " + mISpellCheckerSession);
-            }
             ISpellCheckerSession session;
             synchronized (this) {
-                session = mISpellCheckerSession;
-                if (session == null) {
+                if (mState != STATE_WAIT_CONNECTION && mState != STATE_CONNECTED) {
+                    Log.e(TAG, "ignoring processOrEnqueueTask due to unexpected mState="
+                            + taskToString(scp.mWhat)
+                            + " scp.mWhat=" + taskToString(scp.mWhat));
+                    return;
+                }
+
+                if (mState == STATE_WAIT_CONNECTION) {
+                    // If we are still waiting for the connection. Need to pay special attention.
+                    if (scp.mWhat == TASK_CLOSE) {
+                        processCloseLocked();
+                        return;
+                    }
+                    // Enqueue the task to task queue.
                     SpellCheckerParams closeTask = null;
                     if (scp.mWhat == TASK_CANCEL) {
+                        if (DBG) Log.d(TAG, "canceling pending tasks in processOrEnqueueTask.");
                         while (!mPendingTasks.isEmpty()) {
                             final SpellCheckerParams tmp = mPendingTasks.poll();
                             if (tmp.mWhat == TASK_CLOSE) {
@@ -409,9 +474,15 @@
                     if (closeTask != null) {
                         mPendingTasks.offer(closeTask);
                     }
+                    if (DBG) Log.d(TAG, "queueing tasks in processOrEnqueueTask since the"
+                            + " connection is not established."
+                            + " mPendingTasks.size()=" + mPendingTasks.size());
                     return;
                 }
+
+                session = mISpellCheckerSession;
             }
+            // session must never be null here.
             processTask(session, scp, false);
         }
 
@@ -467,9 +538,6 @@
 
         @Override
         public void onServiceConnected(ISpellCheckerSession session) {
-            if (DBG) {
-                Log.w(TAG, "SpellCheckerSession connected.");
-            }
             mParentSpellCheckerSessionListenerImpl.onServiceConnected(session);
         }
     }
diff --git a/core/java/android/widget/Toolbar.java b/core/java/android/widget/Toolbar.java
index 8ace0f3..2ea2667 100644
--- a/core/java/android/widget/Toolbar.java
+++ b/core/java/android/widget/Toolbar.java
@@ -26,7 +26,6 @@
 import android.app.ActionBar;
 import android.content.Context;
 import android.content.res.TypedArray;
-import android.graphics.RectF;
 import android.graphics.drawable.Drawable;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -148,6 +147,9 @@
     // Clear me after use.
     private final ArrayList<View> mTempViews = new ArrayList<View>();
 
+    // Used to hold views that will be removed while we have an expanded action view.
+    private final ArrayList<View> mHiddenViews = new ArrayList<>();
+
     private final int[] mTempMargins = new int[2];
 
     private OnMenuItemClickListener mOnMenuItemClickListener;
@@ -435,12 +437,12 @@
     public void setLogo(Drawable drawable) {
         if (drawable != null) {
             ensureLogoView();
-            if (mLogoView.getParent() == null) {
-                addSystemView(mLogoView);
-                updateChildVisibilityForExpandedActionView(mLogoView);
+            if (!isChildOrHidden(mLogoView)) {
+                addSystemView(mLogoView, true);
             }
-        } else if (mLogoView != null && mLogoView.getParent() != null) {
+        } else if (mLogoView != null && isChildOrHidden(mLogoView)) {
             removeView(mLogoView);
+            mHiddenViews.remove(mLogoView);
         }
         if (mLogoView != null) {
             mLogoView.setImageDrawable(drawable);
@@ -577,12 +579,12 @@
                     mTitleTextView.setTextColor(mTitleTextColor);
                 }
             }
-            if (mTitleTextView.getParent() == null) {
-                addSystemView(mTitleTextView);
-                updateChildVisibilityForExpandedActionView(mTitleTextView);
+            if (!isChildOrHidden(mTitleTextView)) {
+                addSystemView(mTitleTextView, true);
             }
-        } else if (mTitleTextView != null && mTitleTextView.getParent() != null) {
+        } else if (mTitleTextView != null && isChildOrHidden(mTitleTextView)) {
             removeView(mTitleTextView);
+            mHiddenViews.remove(mTitleTextView);
         }
         if (mTitleTextView != null) {
             mTitleTextView.setText(title);
@@ -631,12 +633,12 @@
                     mSubtitleTextView.setTextColor(mSubtitleTextColor);
                 }
             }
-            if (mSubtitleTextView.getParent() == null) {
-                addSystemView(mSubtitleTextView);
-                updateChildVisibilityForExpandedActionView(mSubtitleTextView);
+            if (!isChildOrHidden(mSubtitleTextView)) {
+                addSystemView(mSubtitleTextView, true);
             }
-        } else if (mSubtitleTextView != null && mSubtitleTextView.getParent() != null) {
+        } else if (mSubtitleTextView != null && isChildOrHidden(mSubtitleTextView)) {
             removeView(mSubtitleTextView);
+            mHiddenViews.remove(mSubtitleTextView);
         }
         if (mSubtitleTextView != null) {
             mSubtitleTextView.setText(subtitle);
@@ -772,12 +774,12 @@
     public void setNavigationIcon(@Nullable Drawable icon) {
         if (icon != null) {
             ensureNavButtonView();
-            if (mNavButtonView.getParent() == null) {
-                addSystemView(mNavButtonView);
-                updateChildVisibilityForExpandedActionView(mNavButtonView);
+            if (!isChildOrHidden(mNavButtonView)) {
+                addSystemView(mNavButtonView, true);
             }
-        } else if (mNavButtonView != null && mNavButtonView.getParent() != null) {
+        } else if (mNavButtonView != null && isChildOrHidden(mNavButtonView)) {
             removeView(mNavButtonView);
+            mHiddenViews.remove(mNavButtonView);
         }
         if (mNavButtonView != null) {
             mNavButtonView.setImageDrawable(icon);
@@ -866,7 +868,7 @@
             final LayoutParams lp = generateDefaultLayoutParams();
             lp.gravity = Gravity.END | (mButtonGravity & Gravity.VERTICAL_GRAVITY_MASK);
             mMenuView.setLayoutParams(lp);
-            addSystemView(mMenuView);
+            addSystemView(mMenuView, false);
         }
     }
 
@@ -1041,7 +1043,7 @@
         }
     }
 
-    private void addSystemView(View v) {
+    private void addSystemView(View v, boolean allowHide) {
         final ViewGroup.LayoutParams vlp = v.getLayoutParams();
         final LayoutParams lp;
         if (vlp == null) {
@@ -1052,7 +1054,13 @@
             lp = (LayoutParams) vlp;
         }
         lp.mViewType = LayoutParams.SYSTEM;
-        addView(v, lp);
+
+        if (allowHide && mExpandedActionView != null) {
+            v.setLayoutParams(lp);
+            mHiddenViews.add(v);
+        } else {
+            addView(v, lp);
+        }
     }
 
     @Override
@@ -1741,22 +1749,30 @@
         return mWrapper;
     }
 
-    private void setChildVisibilityForExpandedActionView(boolean expand) {
+    void removeChildrenForExpandedActionView() {
         final int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
+        // Go backwards since we're removing from the list
+        for (int i = childCount - 1; i >= 0; i--) {
             final View child = getChildAt(i);
             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
             if (lp.mViewType != LayoutParams.EXPANDED && child != mMenuView) {
-                child.setVisibility(expand ? GONE : VISIBLE);
+                removeViewAt(i);
+                mHiddenViews.add(child);
             }
         }
     }
 
-    private void updateChildVisibilityForExpandedActionView(View child) {
-        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-        if (lp.mViewType != LayoutParams.EXPANDED && child != mMenuView) {
-            child.setVisibility(mExpandedActionView != null ? GONE : VISIBLE);
+    void addChildrenForExpandedActionView() {
+        final int count = mHiddenViews.size();
+        // Re-add in reverse order since we removed in reverse order
+        for (int i = count - 1; i >= 0; i--) {
+            addView(mHiddenViews.get(i));
         }
+        mHiddenViews.clear();
+    }
+
+    private boolean isChildOrHidden(View child) {
+        return child.getParent() == this || mHiddenViews.contains(child);
     }
 
     /**
@@ -1971,7 +1987,7 @@
                 addView(mExpandedActionView);
             }
 
-            setChildVisibilityForExpandedActionView(true);
+            removeChildrenForExpandedActionView();
             requestLayout();
             item.setActionViewExpanded(true);
 
@@ -1994,7 +2010,7 @@
             removeView(mCollapseButtonView);
             mExpandedActionView = null;
 
-            setChildVisibilityForExpandedActionView(false);
+            addChildrenForExpandedActionView();
             mCurrentExpandedItem = null;
             requestLayout();
             item.setActionViewExpanded(false);
diff --git a/core/java/com/android/internal/app/ToolbarActionBar.java b/core/java/com/android/internal/app/ToolbarActionBar.java
index 34b9dcb..2b162af 100644
--- a/core/java/com/android/internal/app/ToolbarActionBar.java
+++ b/core/java/com/android/internal/app/ToolbarActionBar.java
@@ -83,7 +83,9 @@
 
     @Override
     public void setCustomView(View view, LayoutParams layoutParams) {
-        view.setLayoutParams(layoutParams);
+        if (view != null) {
+            view.setLayoutParams(layoutParams);
+        }
         mDecorToolbar.setCustomView(view);
     }
 
diff --git a/core/java/com/android/server/backup/AccountSyncSettingsBackupHelper.java b/core/java/com/android/server/backup/AccountSyncSettingsBackupHelper.java
index 3f4b980..c0215a8 100644
--- a/core/java/com/android/server/backup/AccountSyncSettingsBackupHelper.java
+++ b/core/java/com/android/server/backup/AccountSyncSettingsBackupHelper.java
@@ -28,8 +28,6 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.SyncAdapterType;
-import android.content.SyncStatusObserver;
-import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
 import android.util.Log;
 
@@ -47,8 +45,6 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
 
 /**
  * Helper for backing up account sync settings (whether or not a service should be synced). The
@@ -270,6 +266,10 @@
                     // yet won't be restored.
                     if (currentAccounts.contains(account)) {
                         restoreExistingAccountSyncSettingsFromJSON(accountJSON);
+                    } else {
+                        // TODO:
+                        // Stash the data to a file that the SyncManager can read from to restore
+                        // settings at a later date.
                     }
                 }
             } finally {
@@ -300,6 +300,31 @@
     /**
      * Restore account sync settings using the given JSON. This function won't work if the account
      * doesn't exist yet.
+     * This function will only be called during Setup Wizard, where we are guaranteed that there
+     * are no active syncs.
+     * There are 2 pieces of data to restore -
+     *      isSyncable (corresponds to {@link ContentResolver#getIsSyncable(Account, String)}
+     *      syncEnabled (corresponds to {@link ContentResolver#getSyncAutomatically(Account, String)}
+     * <strong>The restore favours adapters that were enabled on the old device, and doesn't care
+     * about adapters that were disabled.</strong>
+     *
+     * syncEnabled=true in restore data.
+     * syncEnabled will be true on this device. isSyncable will be left as the default in order to
+     * give the enabled adapter the chance to run an initialization sync.
+     *
+     * syncEnabled=false in restore data.
+     * syncEnabled will be false on this device. isSyncable will be set to 2, unless it was 0 on the
+     * old device in which case it will be set to 0 on this device. This is because isSyncable=0 is
+     * a rare state and was probably set to 0 for good reason (historically isSyncable is a way by
+     * which adapters control their own sync state independently of sync settings which is
+     * toggleable by the user).
+     * isSyncable=2 is a new isSyncable state we introduced specifically to allow adapters that are
+     * disabled after a restore to run initialization logic when the adapter is later enabled.
+     * See com.android.server.content.SyncStorageEngine#setSyncAutomatically
+     *
+     * The end result is that an adapter that the user had on will be turned on and get an
+     * initialization sync, while an adapter that the user had off will be off until the user
+     * enables it on this device at which point it will get an initialization sync.
      */
     private void restoreExistingAccountSyncSettingsFromJSON(JSONObject accountJSON)
             throws JSONException {
@@ -307,72 +332,27 @@
         JSONArray authorities = accountJSON.getJSONArray(KEY_ACCOUNT_AUTHORITIES);
         String accountName = accountJSON.getString(KEY_ACCOUNT_NAME);
         String accountType = accountJSON.getString(KEY_ACCOUNT_TYPE);
+
         final Account account = new Account(accountName, accountType);
         for (int i = 0; i < authorities.length(); i++) {
             JSONObject authority = (JSONObject) authorities.get(i);
             final String authorityName = authority.getString(KEY_AUTHORITY_NAME);
-            boolean syncEnabled = authority.getBoolean(KEY_AUTHORITY_SYNC_ENABLED);
+            boolean wasSyncEnabled = authority.getBoolean(KEY_AUTHORITY_SYNC_ENABLED);
+            int wasSyncable = authority.getInt(KEY_AUTHORITY_SYNC_STATE);
 
-            // Cancel any active syncs.
-            if (ContentResolver.isSyncActive(account, authorityName)) {
-                ContentResolver.cancelSync(account, authorityName);
-            }
+            ContentResolver.setSyncAutomaticallyAsUser(
+                    account, authorityName, wasSyncEnabled, 0 /* user Id */);
 
-            boolean overwriteSync = true;
-            Bundle initializationExtras = createSyncInitializationBundle();
-            int currentSyncState = ContentResolver.getIsSyncable(account, authorityName);
-            if (currentSyncState < 0) {
-                // Requesting a sync is an asynchronous operation, so we setup a countdown latch to
-                // wait for it to finish. Initialization syncs are generally very brief and
-                // shouldn't take too much time to finish.
-                final CountDownLatch latch = new CountDownLatch(1);
-                Object syncStatusObserverHandle = ContentResolver.addStatusChangeListener(
-                        ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE, new SyncStatusObserver() {
-                            @Override
-                            public void onStatusChanged(int which) {
-                                if (!ContentResolver.isSyncActive(account, authorityName)) {
-                                    latch.countDown();
-                                }
-                            }
-                        });
-
-                // If we set sync settings for a sync that hasn't been initialized yet, we run the
-                // risk of having our changes overwritten later on when the sync gets initialized.
-                // To prevent this from happening we will manually initiate the sync adapter. We
-                // also explicitly pass in a Bundle with SYNC_EXTRAS_INITIALIZE to prevent a data
-                // sync from running after the initialization sync. Two syncs will be scheduled, but
-                // the second one (data sync) will override the first one (initialization sync) and
-                // still behave as an initialization sync because of the Bundle.
-                ContentResolver.requestSync(account, authorityName, initializationExtras);
-
-                boolean done = false;
-                try {
-                    done = latch.await(SYNC_REQUEST_LATCH_TIMEOUT_SECONDS, TimeUnit.SECONDS);
-                } catch (InterruptedException e) {
-                    Log.e(TAG, "CountDownLatch interrupted\n" + e);
-                    done = false;
-                }
-                if (!done) {
-                    overwriteSync = false;
-                    Log.i(TAG, "CountDownLatch timed out, skipping '" + authorityName
-                            + "' authority.");
-                }
-                ContentResolver.removeStatusChangeListener(syncStatusObserverHandle);
-            }
-
-            if (overwriteSync) {
-                ContentResolver.setSyncAutomatically(account, authorityName, syncEnabled);
-                Log.i(TAG, "Set sync automatically for '" + authorityName + "': " + syncEnabled);
+            if (!wasSyncEnabled) {
+                ContentResolver.setIsSyncable(
+                        account,
+                        authorityName,
+                        wasSyncable == 0 ?
+                                0 /* not syncable */ : 2 /* syncable but needs initialization */);
             }
         }
     }
 
-    private Bundle createSyncInitializationBundle() {
-        Bundle extras = new Bundle();
-        extras.putBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, true);
-        return extras;
-    }
-
     @Override
     public void writeNewStateDescription(ParcelFileDescriptor newState) {
 
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 1cd07d1..ac00532 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -677,13 +677,14 @@
         // constructed by java code with correct class type (device, mix etc...)
         // and reference to AudioPort instance in this client
         jAudioPort = env->NewObject(gAudioPortClass, gAudioPortCstor,
-                                           jHandle,
-                                           0,
-                                           NULL,
-                                           NULL,
-                                           NULL,
-                                           NULL,
-                                           NULL);
+                                           jHandle, // handle
+                                           0,       // role
+                                           NULL,    // name
+                                           NULL,    // samplingRates
+                                           NULL,    // channelMasks
+                                           NULL,    // channelIndexMasks
+                                           NULL,    // formats
+                                           NULL);   // gains
         env->DeleteLocalRef(jHandle);
         if (jAudioPort == NULL) {
             return (jint)AUDIO_JAVA_ERROR;
@@ -837,11 +838,14 @@
     jint jStatus = (jint)AUDIO_JAVA_SUCCESS;
     jintArray jSamplingRates = NULL;
     jintArray jChannelMasks = NULL;
+    jintArray jChannelIndexMasks = NULL;
     jintArray jFormats = NULL;
     jobjectArray jGains = NULL;
     jobject jHandle = NULL;
     jstring jDeviceName = NULL;
     bool useInMask;
+    size_t numPositionMasks = 0;
+    size_t numIndexMasks = 0;
 
     ALOGV("convertAudioPortFromNative id %d role %d type %d name %s",
         nAudioPort->id, nAudioPort->role, nAudioPort->type, nAudioPort->name);
@@ -856,23 +860,43 @@
                                (jint *)nAudioPort->sample_rates);
     }
 
-    jChannelMasks = env->NewIntArray(nAudioPort->num_channel_masks);
+    // count up how many masks are positional and indexed
+    for(size_t index = 0; index < nAudioPort->num_channel_masks; index++) {
+        const audio_channel_mask_t mask = nAudioPort->channel_masks[index];
+        if (audio_channel_mask_get_representation(mask) == AUDIO_CHANNEL_REPRESENTATION_INDEX) {
+            numIndexMasks++;
+        } else {
+            numPositionMasks++;
+        }
+    }
+
+    jChannelMasks = env->NewIntArray(numPositionMasks);
     if (jChannelMasks == NULL) {
         jStatus = (jint)AUDIO_JAVA_ERROR;
         goto exit;
     }
+    jChannelIndexMasks = env->NewIntArray(numIndexMasks);
+    if (jChannelIndexMasks == NULL) {
+        jStatus = (jint)AUDIO_JAVA_ERROR;
+        goto exit;
+    }
     useInMask = useInChannelMask(nAudioPort->type, nAudioPort->role);
 
-    jint jMask;
-    for (size_t j = 0; j < nAudioPort->num_channel_masks; j++) {
-        if (useInMask) {
-            jMask = inChannelMaskFromNative(nAudioPort->channel_masks[j]);
+    // put the masks in the output arrays
+    for (size_t maskIndex = 0, posMaskIndex = 0, indexedMaskIndex = 0;
+         maskIndex < nAudioPort->num_channel_masks; maskIndex++) {
+        const audio_channel_mask_t mask = nAudioPort->channel_masks[maskIndex];
+        if (audio_channel_mask_get_representation(mask) == AUDIO_CHANNEL_REPRESENTATION_INDEX) {
+            jint jMask = audio_channel_mask_get_bits(mask);
+            env->SetIntArrayRegion(jChannelIndexMasks, indexedMaskIndex++, 1, &jMask);
         } else {
-            jMask = outChannelMaskFromNative(nAudioPort->channel_masks[j]);
+            jint jMask = useInMask ? inChannelMaskFromNative(mask)
+                                   : outChannelMaskFromNative(mask);
+            env->SetIntArrayRegion(jChannelMasks, posMaskIndex++, 1, &jMask);
         }
-        env->SetIntArrayRegion(jChannelMasks, j, 1, &jMask);
     }
 
+    // formats
     jFormats = env->NewIntArray(nAudioPort->num_formats);
     if (jFormats == NULL) {
         jStatus = (jint)AUDIO_JAVA_ERROR;
@@ -883,14 +907,17 @@
         env->SetIntArrayRegion(jFormats, j, 1, &jFormat);
     }
 
+    // gains
     jGains = env->NewObjectArray(nAudioPort->num_gains,
                                           gAudioGainClass, NULL);
     if (jGains == NULL) {
         jStatus = (jint)AUDIO_JAVA_ERROR;
         goto exit;
     }
+
     for (size_t j = 0; j < nAudioPort->num_gains; j++) {
         audio_channel_mask_t nMask = nAudioPort->gains[j].channel_mask;
+        jint jMask;
         if (useInMask) {
             jMask = inChannelMaskFromNative(nMask);
             ALOGV("convertAudioPortConfigFromNative IN mask java %x native %x", jMask, nMask);
@@ -931,7 +958,8 @@
         jstring jAddress = env->NewStringUTF(nAudioPort->ext.device.address);
         *jAudioPort = env->NewObject(gAudioDevicePortClass, gAudioDevicePortCstor,
                                      jHandle, jDeviceName,
-                                     jSamplingRates, jChannelMasks, jFormats, jGains,
+                                     jSamplingRates, jChannelMasks, jChannelIndexMasks,
+                                     jFormats, jGains,
                                      nAudioPort->ext.device.type, jAddress);
         env->DeleteLocalRef(jAddress);
     } else if (nAudioPort->type == AUDIO_PORT_TYPE_MIX) {
@@ -939,7 +967,7 @@
         *jAudioPort = env->NewObject(gAudioMixPortClass, gAudioMixPortCstor,
                                      jHandle, nAudioPort->ext.mix.handle,
                                      nAudioPort->role, jDeviceName,
-                                     jSamplingRates, jChannelMasks,
+                                     jSamplingRates, jChannelMasks, jChannelIndexMasks,
                                      jFormats, jGains);
     } else {
         ALOGE("convertAudioPortFromNative unknown nAudioPort type %d", nAudioPort->type);
@@ -1634,7 +1662,7 @@
     jclass audioPortClass = FindClassOrDie(env, "android/media/AudioPort");
     gAudioPortClass = MakeGlobalRefOrDie(env, audioPortClass);
     gAudioPortCstor = GetMethodIDOrDie(env, audioPortClass, "<init>",
-            "(Landroid/media/AudioHandle;ILjava/lang/String;[I[I[I[Landroid/media/AudioGain;)V");
+            "(Landroid/media/AudioHandle;ILjava/lang/String;[I[I[I[I[Landroid/media/AudioGain;)V");
     gAudioPortFields.mHandle = GetFieldIDOrDie(env, audioPortClass, "mHandle",
                                                "Landroid/media/AudioHandle;");
     gAudioPortFields.mRole = GetFieldIDOrDie(env, audioPortClass, "mRole", "I");
@@ -1672,12 +1700,12 @@
     jclass audioDevicePortClass = FindClassOrDie(env, "android/media/AudioDevicePort");
     gAudioDevicePortClass = MakeGlobalRefOrDie(env, audioDevicePortClass);
     gAudioDevicePortCstor = GetMethodIDOrDie(env, audioDevicePortClass, "<init>",
-            "(Landroid/media/AudioHandle;Ljava/lang/String;[I[I[I[Landroid/media/AudioGain;ILjava/lang/String;)V");
+            "(Landroid/media/AudioHandle;Ljava/lang/String;[I[I[I[I[Landroid/media/AudioGain;ILjava/lang/String;)V");
 
     jclass audioMixPortClass = FindClassOrDie(env, "android/media/AudioMixPort");
     gAudioMixPortClass = MakeGlobalRefOrDie(env, audioMixPortClass);
     gAudioMixPortCstor = GetMethodIDOrDie(env, audioMixPortClass, "<init>",
-            "(Landroid/media/AudioHandle;IILjava/lang/String;[I[I[I[Landroid/media/AudioGain;)V");
+            "(Landroid/media/AudioHandle;IILjava/lang/String;[I[I[I[I[Landroid/media/AudioGain;)V");
 
     jclass audioGainClass = FindClassOrDie(env, "android/media/AudioGain");
     gAudioGainClass = MakeGlobalRefOrDie(env, audioGainClass);
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index fb0455d..510f6a5 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1969,9 +1969,9 @@
     <string name="lockscreen_access_pattern_start">Pattern started</string>
     <!-- Accessibility description sent when the pattern times out and is cleared. [CHAR LIMIT=NONE] -->
     <string name="lockscreen_access_pattern_cleared">Pattern cleared</string>
-    <!-- Accessibility description sent when user adds a cell to the pattern. [CHAR LIMIT=NONE]  -->
+    <!-- Accessibility description sent when user adds a dot to the pattern. [CHAR LIMIT=NONE]  -->
     <string name="lockscreen_access_pattern_cell_added">Cell added</string>
-    <!-- Accessibility description sent when user adds a cell to the pattern. Announces the 
+    <!-- Accessibility description sent when user adds a dot to the pattern. Announces the 
     actual cell when headphones are connected [CHAR LIMIT=NONE]  -->
     <string name="lockscreen_access_pattern_cell_added_verbose">
             Cell <xliff:g id="cell_index" example="3">%1$s</xliff:g> added</string>
diff --git a/docs/html/index.jd b/docs/html/index.jd
index 3989b92..cd129d49 100644
--- a/docs/html/index.jd
+++ b/docs/html/index.jd
@@ -26,9 +26,9 @@
         <p class="dac-hero-description">Get your apps ready for the next version
         of Android. Test on Nexus 5, 6, 9, and Player. </p>
 
-        <a class="dac-hero-cta" href="{@docRoot}preview/index.html">
+        <a class="dac-hero-cta" href="{@docRoot}preview/overview.html">
           <span class="dac-sprite dac-auto-chevron"></span>
-          Learn more
+          Get started
         </a>
       </div>
     </div>
diff --git a/docs/html/jd_collections.js b/docs/html/jd_collections.js
index 82836e2..86089e6 100644
--- a/docs/html/jd_collections.js
+++ b/docs/html/jd_collections.js
@@ -1594,7 +1594,8 @@
       "training/enterprise/app-restrictions.html",
       "https://www.youtube.com/watch?v=39NkpWkaH8M&index=2&list=PLOU2XLYxmsIKAK2Bhv19H2THwF-22O5WX",
       "samples/AppRestrictionSchema/index.html",
-      "samples/AppRestrictionEnforcer/index.html"
+      "samples/AppRestrictionEnforcer/index.html",
+      "https://www.youtube.com/watch?v=dH41OutAMNM"
     ]
   },
   "training/work/admin": {
diff --git a/docs/html/preview/index.jd b/docs/html/preview/index.jd
index eb18aa6..68186bd 100644
--- a/docs/html/preview/index.jd
+++ b/docs/html/preview/index.jd
@@ -26,7 +26,7 @@
 
         <a class="dac-hero-cta" href="{@docRoot}preview/overview.html">
           <span class="dac-sprite dac-auto-chevron"></span>
-          Get Started!
+          Get started
         </a><br>
       </div>
     </div>
@@ -44,7 +44,7 @@
   <div class="dac-section-subtitle">
     Essential information to help you get your apps ready for Android M.
   </div>
- 
+
   <div class="resource-widget resource-flow-layout col-16"
        data-query="collection:preview/landing/more"
        data-cardSizes="6x6"
@@ -56,7 +56,7 @@
           <span class="dac-sprite dac-auto-chevron"></span>
           Report Issues
         </a>
-      </li> 
+      </li>
       <li class="dac-section-link"><a href="http://g.co/dev/AndroidMDevPreview">
         <span class="dac-sprite dac-auto-chevron"></span>
         Join G+ Community
diff --git a/docs/html/training/tv/discovery/searchable.jd b/docs/html/training/tv/discovery/searchable.jd
index 27a1c33..4ca7abb 100644
--- a/docs/html/training/tv/discovery/searchable.jd
+++ b/docs/html/training/tv/discovery/searchable.jd
@@ -90,7 +90,7 @@
    <td>The production year of your content <strong>(required)</strong></td>
 </tr><tr>
    <td>{@code SUGGEST_COLUMN_DURATION}</td>
-   <td>The duration in milliseconds of your media</td>
+   <td>The duration in milliseconds of your media <strong>(required)</strong></td>
 </tr>
 </table>
 
@@ -99,6 +99,7 @@
   <li>{@link android.app.SearchManager#SUGGEST_COLUMN_TEXT_1}</li>
   <li>{@link android.app.SearchManager#SUGGEST_COLUMN_CONTENT_TYPE}</li>
   <li>{@link android.app.SearchManager#SUGGEST_COLUMN_PRODUCTION_YEAR}</li>
+  <li>{@link android.app.SearchManager#SUGGEST_COLUMN_DURATION}</li>
 </ul>
 
 <p>When the values of these columns for your content match the values for the same content from other
diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java
index 82a592a..a0c407f 100644
--- a/graphics/java/android/graphics/drawable/BitmapDrawable.java
+++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java
@@ -815,6 +815,9 @@
         if (tileModeY != TILE_MODE_UNDEFINED) {
             setTileModeY(parseTileMode(tileModeY));
         }
+
+        final int densityDpi = r.getDisplayMetrics().densityDpi;
+        state.mTargetDensity = densityDpi == 0 ? DisplayMetrics.DENSITY_DEFAULT : densityDpi;
     }
 
     @Override
@@ -977,7 +980,8 @@
      */
     private void updateLocalState(Resources res) {
         if (res != null) {
-            mTargetDensity = res.getDisplayMetrics().densityDpi;
+            final int densityDpi = res.getDisplayMetrics().densityDpi;
+            mTargetDensity = densityDpi == 0 ? DisplayMetrics.DENSITY_DEFAULT : densityDpi;
         } else {
             mTargetDensity = mBitmapState.mTargetDensity;
         }
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index 532c888..415af0d 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -1147,6 +1147,7 @@
      * document, tries to create a Drawable from that tag. Returns {@code null}
      * if the tag is not a valid drawable.
      */
+    @SuppressWarnings("deprecation")
     public static Drawable createFromXmlInner(Resources r, XmlPullParser parser, AttributeSet attrs,
             Theme theme) throws XmlPullParserException, IOException {
         final Drawable drawable;
@@ -1202,16 +1203,10 @@
                 drawable = new InsetDrawable();
                 break;
             case "bitmap":
-                drawable = new BitmapDrawable(r);
-                if (r != null) {
-                    ((BitmapDrawable) drawable).setTargetDensity(r.getDisplayMetrics());
-                }
+                drawable = new BitmapDrawable();
                 break;
             case "nine-patch":
                 drawable = new NinePatchDrawable();
-                if (r != null) {
-                    ((NinePatchDrawable) drawable).setTargetDensity(r.getDisplayMetrics());
-                }
                 break;
             default:
                 throw new XmlPullParserException(parser.getPositionDescription() +
diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
index 91bbff7..0b7869b 100644
--- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java
+++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
@@ -482,6 +482,9 @@
         if (tint != null) {
             state.mTint = tint;
         }
+
+        final int densityDpi = r.getDisplayMetrics().densityDpi;
+        state.mTargetDensity = densityDpi == 0 ? DisplayMetrics.DENSITY_DEFAULT : densityDpi;
     }
 
     @Override
@@ -713,7 +716,8 @@
         final NinePatchState state = mNinePatchState;
 
         if (res != null) {
-            mTargetDensity = res.getDisplayMetrics().densityDpi;
+            final int densityDpi = res.getDisplayMetrics().densityDpi;
+            mTargetDensity = densityDpi == 0 ? DisplayMetrics.DENSITY_DEFAULT : densityDpi;
         } else {
             mTargetDensity = state.mTargetDensity;
         }
diff --git a/media/java/android/media/AudioDeviceInfo.java b/media/java/android/media/AudioDeviceInfo.java
index 431d37e..173d349 100644
--- a/media/java/android/media/AudioDeviceInfo.java
+++ b/media/java/android/media/AudioDeviceInfo.java
@@ -173,8 +173,7 @@
      * @see AudioFormat
      */
     public @NonNull int[] getChannelIndexMasks() {
-        // TODO: implement
-        return new int[0];
+        return mPort.channelIndexMasks();
     }
 
     /**
diff --git a/media/java/android/media/AudioDevicePort.java b/media/java/android/media/AudioDevicePort.java
index c078260..aea39a3 100644
--- a/media/java/android/media/AudioDevicePort.java
+++ b/media/java/android/media/AudioDevicePort.java
@@ -37,12 +37,12 @@
     private final String mAddress;
 
     AudioDevicePort(AudioHandle handle, String deviceName,
-            int[] samplingRates, int[] channelMasks,
+            int[] samplingRates, int[] channelMasks, int[] channelIndexMasks,
             int[] formats, AudioGain[] gains, int type, String address) {
         super(handle,
              (AudioManager.isInputDevice(type) == true)  ?
                         AudioPort.ROLE_SOURCE : AudioPort.ROLE_SINK,
-             deviceName, samplingRates, channelMasks, formats, gains);
+             deviceName, samplingRates, channelMasks, channelIndexMasks, formats, gains);
         mType = type;
         mAddress = address;
     }
diff --git a/media/java/android/media/AudioMixPort.java b/media/java/android/media/AudioMixPort.java
index ab55c8d..ba144bf 100644
--- a/media/java/android/media/AudioMixPort.java
+++ b/media/java/android/media/AudioMixPort.java
@@ -31,9 +31,10 @@
     private final int mIoHandle;
 
     AudioMixPort(AudioHandle handle, int ioHandle, int role, String deviceName,
-            int[] samplingRates, int[] channelMasks,
+            int[] samplingRates, int[] channelMasks, int[] channelIndexMasks,
             int[] formats, AudioGain[] gains) {
-        super(handle, role, deviceName, samplingRates, channelMasks, formats, gains);
+        super(handle, role, deviceName, samplingRates, channelMasks, channelIndexMasks,
+                formats, gains);
         mIoHandle = ioHandle;
     }
 
diff --git a/media/java/android/media/AudioPort.java b/media/java/android/media/AudioPort.java
index 7328d7a..19bf51d 100644
--- a/media/java/android/media/AudioPort.java
+++ b/media/java/android/media/AudioPort.java
@@ -71,12 +71,13 @@
     private final String mName;
     private final int[] mSamplingRates;
     private final int[] mChannelMasks;
+    private final int[] mChannelIndexMasks;
     private final int[] mFormats;
     private final AudioGain[] mGains;
     private AudioPortConfig mActiveConfig;
 
     AudioPort(AudioHandle handle, int role, String name,
-            int[] samplingRates, int[] channelMasks,
+            int[] samplingRates, int[] channelMasks, int[] channelIndexMasks,
             int[] formats, AudioGain[] gains) {
 
         mHandle = handle;
@@ -84,6 +85,7 @@
         mName = name;
         mSamplingRates = samplingRates;
         mChannelMasks = channelMasks;
+        mChannelIndexMasks = channelIndexMasks;
         mFormats = formats;
         mGains = gains;
     }
@@ -133,6 +135,15 @@
     }
 
     /**
+     * Get the list of supported channel index mask configurations
+     * (e.g 0x0003 means 2 channel, 0x000F means 4 channel....)
+     * Empty array if channel index mask is not relevant for this audio port
+     */
+    public int[] channelIndexMasks() {
+        return mChannelIndexMasks;
+    }
+
+    /**
      * Get the list of supported audio format configurations
      * (e.g AudioFormat.ENCODING_PCM_16BIT)
      * Empty array if format is not relevant for this audio port
diff --git a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
index bba33be..fc46716 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
@@ -25,7 +25,6 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
 import java.util.concurrent.Executor;
 
@@ -97,6 +96,22 @@
     }
 
     @Override
+    public void onResume() {
+        super.onResume();
+
+        final State state = getDisplayState();
+        final RootInfo root = getCurrentRoot();
+
+        // If we're browsing a specific root, and that root went away, then we
+        // have no reason to hang around
+        if (state.action == State.ACTION_BROWSE && root != null) {
+            if (mRoots.getRootBlocking(root.authority, root.rootId) == null) {
+                finish();
+            }
+        }
+    }
+
+    @Override
     public boolean onCreateOptionsMenu(Menu menu) {
         boolean showMenu = super.onCreateOptionsMenu(menu);
 
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 7bf2223..5a14967 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -2682,9 +2682,6 @@
             // Set default cdma call auto retry
             loadSetting(stmt, Settings.Global.CALL_AUTO_RETRY, 0);
 
-            // Set default simplified carrier network settings to 0
-            loadSetting(stmt, Settings.Global.HIDE_CARRIER_NETWORK_SETTINGS, 0);
-
             // Set the preferred network mode to target desired value or Default
             // value defined in RILConstants
             int type;
diff --git a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
index 05ee9c2..d360875 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
@@ -53,6 +53,7 @@
     private static final int MOVE_ID = 0x534d4f56; // SMOV
 
     private static final String ACTION_SNOOZE_VOLUME = "com.android.systemui.action.SNOOZE_VOLUME";
+    private static final String ACTION_FINISH_WIZARD = "com.android.systemui.action.FINISH_WIZARD";
 
     // TODO: delay some notifications to avoid bumpy fast operations
 
@@ -107,6 +108,15 @@
         }
     };
 
+    private final BroadcastReceiver mFinishReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            // When finishing the adoption wizard, clean up any notifications
+            // for moving primary storage
+            mNotificationManager.cancelAsUser(null, MOVE_ID, UserHandle.ALL);
+        }
+    };
+
     private final MoveCallback mMoveCallback = new MoveCallback() {
         @Override
         public void onCreated(int moveId, Bundle extras) {
@@ -146,6 +156,8 @@
 
         mContext.registerReceiver(mSnoozeReceiver, new IntentFilter(ACTION_SNOOZE_VOLUME),
                 android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS, null);
+        mContext.registerReceiver(mFinishReceiver, new IntentFilter(ACTION_FINISH_WIZARD),
+                android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS, null);
 
         // Kick current state into place
         final List<DiskInfo> disks = mStorageManager.getDisks();
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index b262442..667abb6 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -11495,7 +11495,7 @@
             for (int i=0; i<ris.size(); i++) {
                 ActivityInfo ai = ris.get(i).activityInfo;
                 ComponentName comp = new ComponentName(ai.packageName, ai.name);
-                if (false && lastDoneReceivers.contains(comp)) {
+                if (lastDoneReceivers.contains(comp)) {
                     // We already did the pre boot receiver for this app with the current
                     // platform version, so don't do it again...
                     ris.remove(i);
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index f222dba..2eb8797 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -801,7 +801,7 @@
             for (String authority : syncableAuthorities) {
                 int isSyncable = getIsSyncable(account.account, account.userId,
                         authority);
-                if (isSyncable == 0) {
+                if (isSyncable == AuthorityInfo.NOT_SYNCABLE) {
                     continue;
                 }
                 final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo;
@@ -813,8 +813,9 @@
                 final boolean allowParallelSyncs = syncAdapterInfo.type.allowParallelSyncs();
                 final boolean isAlwaysSyncable = syncAdapterInfo.type.isAlwaysSyncable();
                 if (isSyncable < 0 && isAlwaysSyncable) {
-                    mSyncStorageEngine.setIsSyncable(account.account, account.userId, authority, 1);
-                    isSyncable = 1;
+                    mSyncStorageEngine.setIsSyncable(
+                            account.account, account.userId, authority, AuthorityInfo.SYNCABLE);
+                    isSyncable = AuthorityInfo.SYNCABLE;
                 }
                 if (onlyThoseWithUnkownSyncableState && isSyncable >= 0) {
                     continue;
diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java
index d68b615..cca0c16 100644
--- a/services/core/java/com/android/server/content/SyncStorageEngine.java
+++ b/services/core/java/com/android/server/content/SyncStorageEngine.java
@@ -301,6 +301,30 @@
     }
 
     public static class AuthorityInfo {
+        // Legal values of getIsSyncable
+        /**
+         * Default state for a newly installed adapter. An uninitialized adapter will receive an
+         * initialization sync which are governed by a different set of rules to that of regular
+         * syncs.
+         */
+        public static final int NOT_INITIALIZED = -1;
+        /**
+         * The adapter will not receive any syncs. This is behaviourally equivalent to
+         * setSyncAutomatically -> false. However setSyncAutomatically is surfaced to the user
+         * while this is generally meant to be controlled by the developer.
+         */
+        public static final int NOT_SYNCABLE = 0;
+        /**
+         * The adapter is initialized and functioning. This is the normal state for an adapter.
+         */
+        public static final int SYNCABLE = 1;
+        /**
+         * The adapter is syncable but still requires an initialization sync. For example an adapter
+         * than has been restored from a previous device will be in this state. Not meant for
+         * external use.
+         */
+        public static final int SYNCABLE_NOT_INITIALIZED = 2;
+
         final EndPoint target;
         final int ident;
         boolean enabled;
@@ -349,12 +373,11 @@
         }
 
         private void defaultInitialisation() {
-            syncable = -1; // default to "unknown"
+            syncable = NOT_INITIALIZED; // default to "unknown"
             backoffTime = -1; // if < 0 then we aren't in backoff mode
             backoffDelay = -1; // if < 0 then we aren't in backoff mode
             PeriodicSync defaultSync;
-            // Old version is one sync a day. Empty bundle gets replaced by any addPeriodicSync()
-            // call.
+            // Old version is one sync a day.
             if (target.target_provider) {
                 defaultSync =
                         new PeriodicSync(target.account, target.provider,
@@ -663,6 +686,12 @@
                 }
                 return;
             }
+            // If the adapter was syncable but missing its initialization sync, set it to
+            // uninitialized now. This is to give it a chance to run any one-time initialization
+            // logic.
+            if (sync && authority.syncable == AuthorityInfo.SYNCABLE_NOT_INITIALIZED) {
+                authority.syncable = AuthorityInfo.NOT_INITIALIZED;
+            }
             authority.enabled = sync;
             writeAccountInfoLocked();
         }
@@ -682,7 +711,7 @@
                         new EndPoint(account, providerName, userId),
                         "get authority syncable");
                 if (authority == null) {
-                    return -1;
+                    return AuthorityInfo.NOT_INITIALIZED;
                 }
                 return authority.syncable;
             }
@@ -696,7 +725,7 @@
                     return authorityInfo.syncable;
                 }
             }
-            return -1;
+            return AuthorityInfo.NOT_INITIALIZED;
         }
     }
 
@@ -720,7 +749,8 @@
     }
 
     public void setIsTargetServiceActive(ComponentName cname, int userId, boolean active) {
-        setSyncableStateForEndPoint(new EndPoint(cname, userId), active ? 1 : 0);
+        setSyncableStateForEndPoint(new EndPoint(cname, userId), active ?
+                AuthorityInfo.SYNCABLE : AuthorityInfo.NOT_SYNCABLE);
     }
 
     /**
@@ -733,10 +763,8 @@
         AuthorityInfo aInfo;
         synchronized (mAuthorities) {
             aInfo = getOrCreateAuthorityLocked(target, -1, false);
-            if (syncable > 1) {
-                syncable = 1;
-            } else if (syncable < -1) {
-                syncable = -1;
+            if (syncable < AuthorityInfo.NOT_INITIALIZED) {
+                syncable = AuthorityInfo.NOT_INITIALIZED;
             }
             if (Log.isLoggable(TAG, Log.VERBOSE)) {
                 Log.d(TAG, "setIsSyncable: " + aInfo.toString() + " -> " + syncable);
@@ -750,7 +778,7 @@
             aInfo.syncable = syncable;
             writeAccountInfoLocked();
         }
-        if (syncable > 0) {
+        if (syncable == AuthorityInfo.SYNCABLE) {
             requestSync(aInfo, SyncOperation.REASON_IS_SYNCABLE, new Bundle());
         }
         reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
@@ -2012,7 +2040,7 @@
             int userId = user == null ? 0 : Integer.parseInt(user);
             if (accountType == null && packageName == null) {
                 accountType = "com.google";
-                syncable = "unknown";
+                syncable = String.valueOf(AuthorityInfo.NOT_INITIALIZED);
             }
             authority = mAuthorities.get(id);
             if (Log.isLoggable(TAG_FILE, Log.VERBOSE)) {
@@ -2052,11 +2080,19 @@
             }
             if (authority != null) {
                 authority.enabled = enabled == null || Boolean.parseBoolean(enabled);
-                if ("unknown".equals(syncable)) {
-                    authority.syncable = -1;
-                } else {
-                    authority.syncable =
-                            (syncable == null || Boolean.parseBoolean(syncable)) ? 1 : 0;
+                try {
+                    authority.syncable = (syncable == null) ?
+                            AuthorityInfo.NOT_INITIALIZED : Integer.parseInt(syncable);
+                } catch (NumberFormatException e) {
+                    // On L we stored this as {"unknown", "true", "false"} so fall back to this
+                    // format.
+                    if ("unknown".equals(syncable)) {
+                        authority.syncable = AuthorityInfo.NOT_INITIALIZED;
+                    } else {
+                        authority.syncable = Boolean.parseBoolean(syncable) ?
+                                AuthorityInfo.SYNCABLE : AuthorityInfo.NOT_SYNCABLE;
+                    }
+
                 }
             } else {
                 Log.w(TAG, "Failure adding authority: account="
@@ -2190,11 +2226,7 @@
                     out.attribute(null, "package", info.service.getPackageName());
                     out.attribute(null, "class", info.service.getClassName());
                 }
-                if (authority.syncable < 0) {
-                    out.attribute(null, "syncable", "unknown");
-                } else {
-                    out.attribute(null, "syncable", Boolean.toString(authority.syncable != 0));
-                }
+                out.attribute(null, "syncable", Integer.toString(authority.syncable));
                 for (PeriodicSync periodicSync : authority.periodicSyncs) {
                     out.startTag(null, "periodicSync");
                     out.attribute(null, "period", Long.toString(periodicSync.period));
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index 77b800e..7999321 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -246,6 +246,8 @@
                 return handleRequestActiveSource(message);
             case Constants.MESSAGE_GET_MENU_LANGUAGE:
                 return handleGetMenuLanguage(message);
+            case Constants.MESSAGE_SET_MENU_LANGUAGE:
+                return handleSetMenuLanguage(message);
             case Constants.MESSAGE_GIVE_PHYSICAL_ADDRESS:
                 return handleGivePhysicalAddress();
             case Constants.MESSAGE_GIVE_OSD_NAME:
@@ -377,6 +379,14 @@
     }
 
     @ServiceThreadOnly
+    protected boolean handleSetMenuLanguage(HdmiCecMessage message) {
+        assertRunOnServiceThread();
+        Slog.w(TAG, "Only Playback device can handle <Set Menu Language>:" + message.toString());
+        // 'return false' will cause to reply with <Feature Abort>.
+        return false;
+    }
+
+    @ServiceThreadOnly
     protected boolean handleGiveOsdName(HdmiCecMessage message) {
         assertRunOnServiceThread();
         // Note that since this method is called after logical address allocation is done,
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index 30a9b43..493471b 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -26,9 +26,14 @@
 import android.provider.Settings.Global;
 import android.util.Slog;
 
+import com.android.internal.app.LocalePicker;
+import com.android.internal.app.LocalePicker.LocaleInfo;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
 
+import java.io.UnsupportedEncodingException;
+import java.util.List;
+
 /**
  * Represent a logical device of type Playback residing in Android system.
  */
@@ -306,6 +311,35 @@
         return true;  // Broadcast message.
     }
 
+    @ServiceThreadOnly
+    protected boolean handleSetMenuLanguage(HdmiCecMessage message) {
+        assertRunOnServiceThread();
+
+        try {
+            String iso3Language = new String(message.getParams(), 0, 3, "US-ASCII");
+
+            // Don't use Locale.getAvailableLocales() since it returns a locale
+            // which is not available on Settings.
+            final List<LocaleInfo> localeInfos = LocalePicker.getAllAssetLocales(
+                    mService.getContext(), false);
+            for (LocaleInfo localeInfo : localeInfos) {
+                if (localeInfo.getLocale().getISO3Language().equals(iso3Language)) {
+                    // WARNING: CEC adopts ISO/FDIS-2 for language code, while Android requires
+                    // additional country variant to pinpoint the locale. This keeps the right
+                    // locale from being chosen. 'eng' in the CEC command, for instance,
+                    // will always be mapped to en-AU among other variants like en-US, en-GB,
+                    // an en-IN, which may not be the expected one.
+                    LocalePicker.updateLocale(localeInfo.getLocale());
+                    return true;
+                }
+            }
+            Slog.w(TAG, "Can't handle <Set Menu Language> of " + iso3Language);
+            return false;
+        } catch (UnsupportedEncodingException e) {
+            return false;
+        }
+    }
+
     @Override
     @ServiceThreadOnly
     protected void sendStandby(int deviceId) {
diff --git a/services/core/java/com/android/server/pm/KeySetManagerService.java b/services/core/java/com/android/server/pm/KeySetManagerService.java
index 1ee07a5..0de0c92 100644
--- a/services/core/java/com/android/server/pm/KeySetManagerService.java
+++ b/services/core/java/com/android/server/pm/KeySetManagerService.java
@@ -16,6 +16,9 @@
 
 package com.android.server.pm;
 
+import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
+
+import com.android.internal.util.Preconditions;
 import android.content.pm.PackageParser;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -98,6 +101,7 @@
             mRefCount++;
             return;
         }
+
         public long decrRefCountLPw() {
             mRefCount--;
             return mRefCount;
@@ -168,25 +172,82 @@
     }
 
     /**
+     * addScannedPackageLPw directly modifies the package metadata in  pm.Settings
+     * at a point of no-return.  We need to make sure that the scanned package does
+     * not contain bad keyset meta-data that could generate an incorrect
+     * PackageSetting. Verify that there is a signing keyset, there are no issues
+     * with null objects, and the upgrade and defined keysets match.
+     *
+     * Returns true if the package can safely be added to the keyset metadata.
+     */
+    public void assertScannedPackageValid(PackageParser.Package pkg)
+            throws PackageManagerException {
+        if (pkg == null || pkg.packageName == null) {
+            throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
+                    "Passed invalid package to keyset validation.");
+        }
+        ArraySet<PublicKey> signingKeys = pkg.mSigningKeys;
+        if (signingKeys == null || !(signingKeys.size() > 0) || signingKeys.contains(null)) {
+            throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
+                    "Package has invalid signing-key-set.");
+        }
+        ArrayMap<String, ArraySet<PublicKey>> definedMapping = pkg.mKeySetMapping;
+        if (definedMapping != null) {
+            if (definedMapping.containsKey(null) || definedMapping.containsValue(null)) {
+                throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
+                        "Package has null defined key set.");
+            }
+            int defMapSize = definedMapping.size();
+            for (int i = 0; i < defMapSize; i++) {
+                if (!(definedMapping.valueAt(i).size() > 0)
+                        || definedMapping.valueAt(i).contains(null)) {
+                    throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
+                            "Package has null/no public keys for defined key-sets.");
+                }
+            }
+        }
+        ArraySet<String> upgradeAliases = pkg.mUpgradeKeySets;
+        if (upgradeAliases != null) {
+            if (definedMapping == null || !(definedMapping.keySet().containsAll(upgradeAliases))) {
+                throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
+                        "Package has upgrade-key-sets without corresponding definitions.");
+            }
+        }
+    }
+
+    public void addScannedPackageLPw(PackageParser.Package pkg) {
+        Preconditions.checkNotNull(pkg, "Attempted to add null pkg to ksms.");
+        Preconditions.checkNotNull(pkg.packageName, "Attempted to add null pkg to ksms.");
+        PackageSetting ps = mPackages.get(pkg.packageName);
+        Preconditions.checkNotNull(ps, "pkg: " + pkg.packageName
+                    + "does not have a corresponding entry in mPackages.");
+        addSigningKeySetToPackageLPw(ps, pkg.mSigningKeys);
+        if (pkg.mKeySetMapping != null) {
+            addDefinedKeySetsToPackageLPw(ps, pkg.mKeySetMapping);
+            if (pkg.mUpgradeKeySets != null) {
+                addUpgradeKeySetsToPackageLPw(ps, pkg.mUpgradeKeySets);
+            }
+        }
+    }
+
+    /**
      * Informs the system that the given package was signed by the provided KeySet.
      */
-    public void addSigningKeySetToPackageLPw(String packageName,
+    void addSigningKeySetToPackageLPw(PackageSetting pkg,
             ArraySet<PublicKey> signingKeys) {
 
         /* check existing keyset for reuse or removal */
-        PackageSetting pkg = mPackages.get(packageName);
         long signingKeySetId = pkg.keySetData.getProperSigningKeySet();
 
         if (signingKeySetId != PackageKeySetData.KEYSET_UNASSIGNED) {
             ArraySet<PublicKey> existingKeys = getPublicKeysFromKeySetLPr(signingKeySetId);
-            if (existingKeys.equals(signingKeys)) {
+            if (existingKeys != null && existingKeys.equals(signingKeys)) {
 
                 /* no change in signing keys, leave PackageSetting alone */
                 return;
             } else {
 
                 /* old keyset no longer valid, remove ref */
-                KeySetHandle ksh = mKeySets.get(signingKeySetId);
                 decrementKeySetLPw(signingKeySetId);
             }
         }
@@ -212,13 +273,12 @@
         return KEYSET_NOT_FOUND;
     }
 
-    /*
+    /**
      * Inform the system that the given package defines the given KeySets.
      * Remove any KeySets the package no longer defines.
      */
-    public void addDefinedKeySetsToPackageLPw(String packageName,
+    void addDefinedKeySetsToPackageLPw(PackageSetting pkg,
             ArrayMap<String, ArraySet<PublicKey>> definedMapping) {
-        PackageSetting pkg = mPackages.get(packageName);
         ArrayMap<String, Long> prevDefinedKeySets = pkg.keySetData.getAliases();
 
         /* add all of the newly defined KeySets */
@@ -227,7 +287,7 @@
         for (int i = 0; i < defMapSize; i++) {
             String alias = definedMapping.keyAt(i);
             ArraySet<PublicKey> pubKeys = definedMapping.valueAt(i);
-            if (alias != null && pubKeys != null && pubKeys.size() > 0) {
+            if (alias != null && pubKeys != null || pubKeys.size() > 0) {
                 KeySetHandle ks = addKeySetLPw(pubKeys);
                 newKeySetAliases.put(alias, ks.getId());
             }
@@ -250,9 +310,8 @@
      * alias in its manifest to be an upgradeKeySet.  This must be called
      * after all of the defined KeySets have been added.
      */
-    public void addUpgradeKeySetsToPackageLPw(String packageName,
-        ArraySet<String> upgradeAliases) {
-        PackageSetting pkg = mPackages.get(packageName);
+    void addUpgradeKeySetsToPackageLPw(PackageSetting pkg,
+            ArraySet<String> upgradeAliases) {
         final int uaSize = upgradeAliases.size();
         for (int i = 0; i < uaSize; i++) {
             pkg.keySetData.addUpgradeKeySet(upgradeAliases.valueAt(i));
@@ -290,11 +349,11 @@
      * identify a {@link KeySetHandle}.
      */
     public ArraySet<PublicKey> getPublicKeysFromKeySetLPr(long id) {
-        if(mKeySetMapping.get(id) == null) {
+        ArraySet<Long> pkIds = mKeySetMapping.get(id);
+        if (pkIds == null) {
             return null;
         }
         ArraySet<PublicKey> mPubKeys = new ArraySet<PublicKey>();
-        ArraySet<Long> pkIds = mKeySetMapping.get(id);
         final int pkSize = pkIds.size();
         for (int i = 0; i < pkSize; i++) {
             mPubKeys.add(mPublicKeys.get(pkIds.valueAt(i)).getKey());
@@ -376,6 +435,10 @@
      */
     private void decrementKeySetLPw(long id) {
         KeySetHandle ks = mKeySets.get(id);
+        if (ks == null) {
+            /* nothing to do */
+            return;
+        }
         if (ks.decrRefCountLPw() <= 0) {
             ArraySet<Long> pubKeys = mKeySetMapping.get(id);
             final int pkSize = pubKeys.size();
@@ -385,7 +448,6 @@
             mKeySets.delete(id);
             mKeySetMapping.delete(id);
         }
-        return;
     }
 
     /*
@@ -394,16 +456,20 @@
      */
     private void decrementPublicKeyLPw(long id) {
         PublicKeyHandle pk = mPublicKeys.get(id);
+        if (pk == null) {
+            /* nothing to do */
+            return;
+        }
         if (pk.decrRefCountLPw() <= 0) {
             mPublicKeys.delete(id);
         }
-        return;
     }
 
     /**
      * Adds the given PublicKey to the system, deduping as it goes.
      */
     private long addPublicKeyLPw(PublicKey key) {
+        Preconditions.checkNotNull(key, "Cannot add null public key!");
         long id = getIdForPublicKeyLPr(key);
         if (id != PUBLIC_KEY_NOT_FOUND) {
 
@@ -473,6 +539,8 @@
 
         /* remove refs from common keysets and public keys */
         PackageSetting pkg = mPackages.get(packageName);
+        Preconditions.checkNotNull(pkg, "pkg name: " + packageName
+                + "does not have a corresponding entry in mPackages.");
         long signingKeySetId = pkg.keySetData.getProperSigningKeySet();
         decrementKeySetLPw(signingKeySetId);
         ArrayMap<String, Long> definedKeySets = pkg.keySetData.getAliases();
@@ -715,16 +783,36 @@
         final int numRefCounts = keySetRefCounts.size();
         for (int i = 0; i < numRefCounts; i++) {
             KeySetHandle ks = mKeySets.get(keySetRefCounts.keyAt(i));
+            if (ks == null) {
+                /* something went terribly wrong and we have references to a non-existent key-set */
+                Slog.wtf(TAG, "Encountered non-existent key-set reference when reading settings");
+                continue;
+            }
             ks.setRefCountLPw(keySetRefCounts.valueAt(i));
         }
 
+        /*
+         * In case something went terribly wrong and we have keysets with no associated packges
+         * that refer to them, record the orphaned keyset ids, and remove them using
+         * decrementKeySetLPw() after all keyset references have been set so that the associtaed
+         * public keys have the appropriate references from all keysets.
+         */
+        ArraySet<Long> orphanedKeySets = new ArraySet<Long>();
         final int numKeySets = mKeySets.size();
         for (int i = 0; i < numKeySets; i++) {
+            if (mKeySets.valueAt(i).getRefCountLPr() == 0) {
+                Slog.wtf(TAG, "Encountered key-set w/out package references when reading settings");
+                orphanedKeySets.add(mKeySets.keyAt(i));
+            }
             ArraySet<Long> pubKeys = mKeySetMapping.valueAt(i);
             final int pkSize = pubKeys.size();
             for (int j = 0; j < pkSize; j++) {
                 mPublicKeys.get(pubKeys.valueAt(j)).incrRefCountLPw();
             }
         }
+        final int numOrphans = orphanedKeySets.size();
+        for (int i = 0; i < numOrphans; i++) {
+            decrementKeySetLPw(orphanedKeySets.valueAt(i));
+        }
     }
 }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 548d93c..5b7dd70 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -5109,16 +5109,18 @@
                 && !isCompatSignatureUpdateNeeded(pkg)
                 && !isRecoverSignatureUpdateNeeded(pkg)) {
             long mSigningKeySetId = ps.keySetData.getProperSigningKeySet();
+            KeySetManagerService ksms = mSettings.mKeySetManagerService;
+            ArraySet<PublicKey> signingKs;
+            synchronized (mPackages) {
+                signingKs = ksms.getPublicKeysFromKeySetLPr(mSigningKeySetId);
+            }
             if (ps.signatures.mSignatures != null
                     && ps.signatures.mSignatures.length != 0
-                    && mSigningKeySetId != PackageKeySetData.KEYSET_UNASSIGNED) {
+                    && signingKs != null) {
                 // Optimization: reuse the existing cached certificates
                 // if the package appears to be unchanged.
                 pkg.mSignatures = ps.signatures.mSignatures;
-                KeySetManagerService ksms = mSettings.mKeySetManagerService;
-                synchronized (mPackages) {
-                    pkg.mSigningKeys = ksms.getPublicKeysFromKeySetLPr(mSigningKeySetId);
-                }
+                pkg.mSigningKeys = signingKs;
                 return;
             }
 
@@ -6590,6 +6592,10 @@
             }
         }
 
+        // Make sure we're not adding any bogus keyset info
+        KeySetManagerService ksms = mSettings.mKeySetManagerService;
+        ksms.assertScannedPackageValid(pkg);
+
         // writer
         synchronized (mPackages) {
             // We don't expect installation to fail beyond this point
@@ -6626,20 +6632,7 @@
             }
 
             // Add the package's KeySets to the global KeySetManagerService
-            KeySetManagerService ksms = mSettings.mKeySetManagerService;
-            try {
-                ksms.addSigningKeySetToPackageLPw(pkg.packageName, pkg.mSigningKeys);
-                if (pkg.mKeySetMapping != null) {
-                    ksms.addDefinedKeySetsToPackageLPw(pkg.packageName, pkg.mKeySetMapping);
-                    if (pkg.mUpgradeKeySets != null) {
-                        ksms.addUpgradeKeySetsToPackageLPw(pkg.packageName, pkg.mUpgradeKeySets);
-                    }
-                }
-            } catch (NullPointerException e) {
-                Slog.e(TAG, "Could not add KeySet to " + pkg.packageName, e);
-            } catch (IllegalArgumentException e) {
-                Slog.e(TAG, "Could not add KeySet to malformed package" + pkg.packageName, e);
-            }
+            ksms.addScannedPackageLPw(pkg);
 
             int N = pkg.providers.size();
             StringBuilder r = null;
@@ -11188,7 +11181,7 @@
         KeySetManagerService ksms = mSettings.mKeySetManagerService;
         for (int i = 0; i < upgradeKeySets.length; i++) {
             Set<PublicKey> upgradeSet = ksms.getPublicKeysFromKeySetLPr(upgradeKeySets[i]);
-            if (newPkg.mSigningKeys.containsAll(upgradeSet)) {
+            if (upgradeSet != null && newPkg.mSigningKeys.containsAll(upgradeSet)) {
                 return true;
             }
         }
diff --git a/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java
index 2557974..7f9a0de 100644
--- a/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java
@@ -118,7 +118,7 @@
         ArraySet<PublicKey> signingKeys = new ArraySet<PublicKey>();
         PublicKey keyA = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
         signingKeys.add(keyA);
-        mKsms.addSigningKeySetToPackageLPw(ps.name, signingKeys);
+        mKsms.addSigningKeySetToPackageLPw(ps, signingKeys);
 
         assertEquals(1, KeySetUtils.getKeySetRefCount(mKsms, 1));
         assertEquals(1, KeySetUtils.getPubKeyRefCount(mKsms, 1));
@@ -145,10 +145,10 @@
         ArraySet<PublicKey> signingKeys = new ArraySet<PublicKey>();
         PublicKey keyA = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
         signingKeys.add(keyA);
-        mKsms.addSigningKeySetToPackageLPw(ps.name, signingKeys);
+        mKsms.addSigningKeySetToPackageLPw(ps, signingKeys);
 
         /* add again, to represent upgrade of package */
-        mKsms.addSigningKeySetToPackageLPw(ps.name, signingKeys);
+        mKsms.addSigningKeySetToPackageLPw(ps, signingKeys);
 
         assertEquals(1, KeySetUtils.getKeySetRefCount(mKsms, 1));
         assertEquals(1, KeySetUtils.getPubKeyRefCount(mKsms, 1));
@@ -175,13 +175,13 @@
         ArraySet<PublicKey> signingKeys = new ArraySet<PublicKey>();
         PublicKey keyA = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
         signingKeys.add(keyA);
-        mKsms.addSigningKeySetToPackageLPw(ps.name, signingKeys);
+        mKsms.addSigningKeySetToPackageLPw(ps, signingKeys);
 
         /* now upgrade with new key */
         PublicKey keyB = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyB);
         signingKeys.removeAt(0);
         signingKeys.add(keyB);
-        mKsms.addSigningKeySetToPackageLPw(ps.name, signingKeys);
+        mKsms.addSigningKeySetToPackageLPw(ps, signingKeys);
 
         assertEquals(0, KeySetUtils.getKeySetRefCount(mKsms, 1));
         assertEquals(1, KeySetUtils.getKeySetRefCount(mKsms, 2));
@@ -212,14 +212,14 @@
         ArraySet<PublicKey> signingKeys = new ArraySet<PublicKey>();
         PublicKey keyA = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
         signingKeys.add(keyA);
-        mKsms.addSigningKeySetToPackageLPw(ps1.name, signingKeys);
-        mKsms.addSigningKeySetToPackageLPw(ps2.name, signingKeys);
+        mKsms.addSigningKeySetToPackageLPw(ps1, signingKeys);
+        mKsms.addSigningKeySetToPackageLPw(ps2, signingKeys);
 
         /* now upgrade with new key */
         PublicKey keyB = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyB);
         signingKeys.removeAt(0);
         signingKeys.add(keyB);
-        mKsms.addSigningKeySetToPackageLPw(ps1.name, signingKeys);
+        mKsms.addSigningKeySetToPackageLPw(ps1, signingKeys);
 
         assertEquals(1, KeySetUtils.getKeySetRefCount(mKsms, 1));
         assertEquals(1, KeySetUtils.getKeySetRefCount(mKsms, 2));
@@ -255,13 +255,13 @@
         ArraySet<PublicKey> signingKeys1 = new ArraySet<PublicKey>();
         PublicKey keyA = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
         signingKeys1.add(keyA);
-        mKsms.addSigningKeySetToPackageLPw(ps1.name, signingKeys1);
+        mKsms.addSigningKeySetToPackageLPw(ps1, signingKeys1);
 
         /* collect second signing key and add */
         ArraySet<PublicKey> signingKeys2 = new ArraySet<PublicKey>();
         PublicKey keyB = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyB);
         signingKeys2.add(keyB);
-        mKsms.addSigningKeySetToPackageLPw(ps2.name, signingKeys2);
+        mKsms.addSigningKeySetToPackageLPw(ps2, signingKeys2);
 
         /* verify first is unchanged */
         assertEquals(1, KeySetUtils.getKeySetRefCount(mKsms, 1));
@@ -300,10 +300,10 @@
         ArraySet<PublicKey> signingKeys = new ArraySet<PublicKey>();
         PublicKey keyA = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
         signingKeys.add(keyA);
-        mKsms.addSigningKeySetToPackageLPw(ps1.name, signingKeys);
+        mKsms.addSigningKeySetToPackageLPw(ps1, signingKeys);
 
         /* add again for second package */
-        mKsms.addSigningKeySetToPackageLPw(ps2.name, signingKeys);
+        mKsms.addSigningKeySetToPackageLPw(ps2, signingKeys);
 
         assertEquals(2, KeySetUtils.getKeySetRefCount(mKsms, 1));
         assertEquals(1, KeySetUtils.getPubKeyRefCount(mKsms, 1));
@@ -333,12 +333,12 @@
         ArraySet<PublicKey> signingKeys = new ArraySet<PublicKey>();
         PublicKey keyA = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
         signingKeys.add(keyA);
-        mKsms.addSigningKeySetToPackageLPw(ps1.name, signingKeys);
+        mKsms.addSigningKeySetToPackageLPw(ps1, signingKeys);
 
         /* give ps2 a superset (add keyB) */
         PublicKey keyB = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyB);
         signingKeys.add(keyB);
-        mKsms.addSigningKeySetToPackageLPw(ps2.name, signingKeys);
+        mKsms.addSigningKeySetToPackageLPw(ps2, signingKeys);
 
         assertEquals(1, KeySetUtils.getKeySetRefCount(mKsms, 1));
         assertEquals(1, KeySetUtils.getKeySetRefCount(mKsms, 2));
@@ -374,12 +374,12 @@
         ArraySet<PublicKey> signingKeys = new ArraySet<PublicKey>();
         PublicKey keyA = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
         signingKeys.add(keyA);
-        mKsms.addSigningKeySetToPackageLPw(ps.name, signingKeys);
+        mKsms.addSigningKeySetToPackageLPw(ps, signingKeys);
 
         /* now with additional key */
         PublicKey keyB = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyB);
         signingKeys.add(keyB);
-        mKsms.addSigningKeySetToPackageLPw(ps.name, signingKeys);
+        mKsms.addSigningKeySetToPackageLPw(ps, signingKeys);
 
         assertEquals(0, KeySetUtils.getKeySetRefCount(mKsms, 1));
         assertEquals(1, KeySetUtils.getKeySetRefCount(mKsms, 2));
@@ -413,7 +413,7 @@
         PublicKey keyA = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
         keys.add(keyA);
         definedKS.put("aliasA", keys);
-        mKsms.addDefinedKeySetsToPackageLPw(ps.name, definedKS);
+        mKsms.addDefinedKeySetsToPackageLPw(ps, definedKS);
 
         assertEquals(1, KeySetUtils.getKeySetRefCount(mKsms, 1));
         assertEquals(1, KeySetUtils.getPubKeyRefCount(mKsms, 1));
@@ -441,7 +441,7 @@
         keys.add(keyA);
         definedKS.put("aliasA", keys);
         definedKS.put("aliasA2", keys);
-        mKsms.addDefinedKeySetsToPackageLPw(ps.name, definedKS);
+        mKsms.addDefinedKeySetsToPackageLPw(ps, definedKS);
 
         assertEquals(2, KeySetUtils.getKeySetRefCount(mKsms, 1));
         assertEquals(1, KeySetUtils.getPubKeyRefCount(mKsms, 1));
@@ -470,7 +470,7 @@
         PublicKey keyA = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
         keys.add(keyA);
         definedKS.put("aliasA", keys);
-        mKsms.addDefinedKeySetsToPackageLPw(ps.name, definedKS);
+        mKsms.addDefinedKeySetsToPackageLPw(ps, definedKS);
 
         /* now upgrade to different defined key-set */
         keys = new ArraySet<PublicKey>();
@@ -478,7 +478,7 @@
         keys.add(keyB);
         definedKS.remove("aliasA");
         definedKS.put("aliasB", keys);
-        mKsms.addDefinedKeySetsToPackageLPw(ps.name, definedKS);
+        mKsms.addDefinedKeySetsToPackageLPw(ps, definedKS);
 
         assertEquals(0, KeySetUtils.getKeySetRefCount(mKsms, 1));
         assertEquals(0, KeySetUtils.getPubKeyRefCount(mKsms, 1));
@@ -510,14 +510,14 @@
         PublicKey keyA = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
         keys.add(keyA);
         definedKS.put("aliasA", keys);
-        mKsms.addDefinedKeySetsToPackageLPw(ps.name, definedKS);
+        mKsms.addDefinedKeySetsToPackageLPw(ps, definedKS);
 
         /* now upgrade to different set w/same alias as before */
         keys = new ArraySet<PublicKey>();
         PublicKey keyB = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyB);
         keys.add(keyB);
         definedKS.put("aliasA", keys);
-        mKsms.addDefinedKeySetsToPackageLPw(ps.name, definedKS);
+        mKsms.addDefinedKeySetsToPackageLPw(ps, definedKS);
 
         assertEquals(0, KeySetUtils.getKeySetRefCount(mKsms, 1));
         assertEquals(0, KeySetUtils.getPubKeyRefCount(mKsms, 1));
@@ -551,7 +551,7 @@
         keys2.add(keyB);
         definedKS.put("aliasA", keys1);
         definedKS.put("aliasB", keys2);
-        mKsms.addDefinedKeySetsToPackageLPw(ps.name, definedKS);
+        mKsms.addDefinedKeySetsToPackageLPw(ps, definedKS);
 
         /* now upgrade to different set (B, C) */
         keys1 = new ArraySet<PublicKey>();
@@ -559,7 +559,7 @@
         keys1.add(keyC);
         definedKS.remove("aliasA");
         definedKS.put("aliasC", keys1);
-        mKsms.addDefinedKeySetsToPackageLPw(ps.name, definedKS);
+        mKsms.addDefinedKeySetsToPackageLPw(ps, definedKS);
 
         assertEquals(1, KeySetUtils.getKeySetRefCount(mKsms, 3));
         assertEquals(1, KeySetUtils.getPubKeyRefCount(mKsms, 3));
@@ -612,7 +612,7 @@
         PublicKey keyA = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
         keys1.add(keyA);
         definedKS.put("aliasA", keys1);
-        mKsms.addDefinedKeySetsToPackageLPw(ps.name, definedKS);
+        mKsms.addDefinedKeySetsToPackageLPw(ps, definedKS);
 
         /* now upgrade to different set */
         ArraySet<PublicKey> keys2 = new ArraySet<PublicKey>();
@@ -620,12 +620,12 @@
         keys2.add(keyB);
         definedKS.remove("aliasA");
         definedKS.put("aliasB", keys2);
-        mKsms.addDefinedKeySetsToPackageLPw(ps.name, definedKS);
+        mKsms.addDefinedKeySetsToPackageLPw(ps, definedKS);
 
         /* upgrade back to original */
         definedKS.remove("aliasB");
         definedKS.put("aliasA", keys1);
-        mKsms.addDefinedKeySetsToPackageLPw(ps.name, definedKS);
+        mKsms.addDefinedKeySetsToPackageLPw(ps, definedKS);
 
         assertEquals(0, KeySetUtils.getKeySetRefCount(mKsms, 1));
         assertEquals(0, KeySetUtils.getKeySetRefCount(mKsms, 2));
@@ -655,10 +655,10 @@
         PublicKey keyA = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
         keys.add(keyA);
         definedKS.put("aliasA", keys);
-        mKsms.addDefinedKeySetsToPackageLPw(ps.name, definedKS);
+        mKsms.addDefinedKeySetsToPackageLPw(ps, definedKS);
         ArraySet<String> upgradeKS = new ArraySet<String>();
         upgradeKS.add("aliasA");
-        mKsms.addUpgradeKeySetsToPackageLPw(ps.name, upgradeKS);
+        mKsms.addUpgradeKeySetsToPackageLPw(ps, upgradeKS);
 
         assertEquals(1, ps.keySetData.getUpgradeKeySets().length);
         assertEquals(1, ps.keySetData.getUpgradeKeySets()[0]);
@@ -677,11 +677,11 @@
         PublicKey keyA = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
         keys.add(keyA);
         definedKS.put("aliasA", keys);
-        mKsms.addDefinedKeySetsToPackageLPw(ps.name, definedKS);
+        mKsms.addDefinedKeySetsToPackageLPw(ps, definedKS);
         ArraySet<String> upgradeKS = new ArraySet<String>();
         upgradeKS.add("aliasB");
         try {
-            mKsms.addUpgradeKeySetsToPackageLPw(ps.name, upgradeKS);
+            mKsms.addUpgradeKeySetsToPackageLPw(ps, upgradeKS);
         } catch (IllegalArgumentException e) {
 
             /* should have been caught in packagemanager, so exception thrown */
@@ -704,17 +704,17 @@
         PublicKey keyA = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
         keys.add(keyA);
         definedKS.put("aliasA", keys);
-        mKsms.addDefinedKeySetsToPackageLPw(ps.name, definedKS);
+        mKsms.addDefinedKeySetsToPackageLPw(ps, definedKS);
         ArraySet<String> upgradeKS = new ArraySet<String>();
         upgradeKS.add("aliasA");
-        mKsms.addUpgradeKeySetsToPackageLPw(ps.name, upgradeKS);
+        mKsms.addUpgradeKeySetsToPackageLPw(ps, upgradeKS);
 
         keys = new ArraySet<PublicKey>();
         PublicKey keyB = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyB);
         keys.add(keyB);
         definedKS.remove("aliasA");
         definedKS.put("aliasB", keys);
-        mKsms.addDefinedKeySetsToPackageLPw(ps.name, definedKS);
+        mKsms.addDefinedKeySetsToPackageLPw(ps, definedKS);
         assertNull(ps.keySetData.getUpgradeKeySets());
     }
 
@@ -729,7 +729,7 @@
         ArraySet<PublicKey> signingKeys = new ArraySet<PublicKey>();
         PublicKey keyA = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
         signingKeys.add(keyA);
-        mKsms.addSigningKeySetToPackageLPw(ps.name, signingKeys);
+        mKsms.addSigningKeySetToPackageLPw(ps, signingKeys);
 
         /* remove its references */
         mKsms.removeAppKeySetDataLPw(ps.name);
@@ -754,8 +754,8 @@
         ArraySet<PublicKey> signingKeys = new ArraySet<PublicKey>();
         PublicKey keyA = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
         signingKeys.add(keyA);
-        mKsms.addSigningKeySetToPackageLPw(ps1.name, signingKeys);
-        mKsms.addSigningKeySetToPackageLPw(ps2.name, signingKeys);
+        mKsms.addSigningKeySetToPackageLPw(ps1, signingKeys);
+        mKsms.addSigningKeySetToPackageLPw(ps2, signingKeys);
 
         /* remove references from first package */
         mKsms.removeAppKeySetDataLPw(ps1.name);
@@ -784,13 +784,13 @@
         /* removal requires signing keyset to be specified (since all apps are
          * assumed to have it).  We skipped this in the defined tests, but can't
          * here. */
-        mKsms.addSigningKeySetToPackageLPw(ps.name, keys);
+        mKsms.addSigningKeySetToPackageLPw(ps, keys);
 
         definedKS.put("aliasA", keys);
-        mKsms.addDefinedKeySetsToPackageLPw(ps.name, definedKS);
+        mKsms.addDefinedKeySetsToPackageLPw(ps, definedKS);
         ArraySet<String> upgradeKS = new ArraySet<String>();
         upgradeKS.add("aliasA");
-        mKsms.addUpgradeKeySetsToPackageLPw(ps.name, upgradeKS);
+        mKsms.addUpgradeKeySetsToPackageLPw(ps, upgradeKS);
         mKsms.removeAppKeySetDataLPw(ps.name);
 
         assertEquals(0, KeySetUtils.getKeySetRefCount(mKsms, 1));
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index a1d5dd5..5d55ec6 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -684,6 +684,18 @@
 
     /**
      * @hide
+     * For debug: date at which the config was last updated
+     */
+    public String updateTime;
+
+    /**
+     * @hide
+     * For debug: date at which the config was last updated
+     */
+    public String creationTime;
+
+    /**
+     * @hide
      * The WiFi configuration is considered to have no internet access for purpose of autojoining
      * if there has been a report of it having no internet access, and, it never have had
      * internet access in the past.
@@ -1000,6 +1012,12 @@
             sbuf.append(" numNoInternetAccessReports ");
             sbuf.append(this.numNoInternetAccessReports).append("\n");
         }
+        if (this.updateTime != null) {
+            sbuf.append("creation=").append(this.updateTime).append("\n");
+        }
+        if (this.creationTime != null) {
+            sbuf.append("update=").append(this.creationTime).append("\n");
+        }
         if (this.didSelfAdd) sbuf.append(" didSelfAdd");
         if (this.selfAdded) sbuf.append(" selfAdded");
         if (this.validatedInternetAccess) sbuf.append(" validatedInternetAccess");
@@ -1505,6 +1523,8 @@
             userApproved = source.userApproved;
             numNoInternetAccessReports = source.numNoInternetAccessReports;
             noInternetAccessExpected = source.noInternetAccessExpected;
+            creationTime = source.creationTime;
+            updateTime = source.updateTime;
         }
     }