Merge "Add addExtras and getExtras to Notification.Builder." into klp-modular-dev
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 9414237..5763e72 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -463,13 +463,13 @@
     public ViewGroup(Context context, AttributeSet attrs) {
         super(context, attrs);
         initViewGroup();
-        initFromAttributes(context, attrs);
+        initFromAttributes(context, attrs, 0);
     }
 
     public ViewGroup(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
         initViewGroup();
-        initFromAttributes(context, attrs);
+        initFromAttributes(context, attrs, defStyle);
     }
 
     private boolean debugDraw() {
@@ -499,9 +499,8 @@
         mPersistentDrawingCache = PERSISTENT_SCROLLING_CACHE;
     }
 
-    private void initFromAttributes(Context context, AttributeSet attrs) {
-        TypedArray a = context.obtainStyledAttributes(attrs,
-                R.styleable.ViewGroup);
+    private void initFromAttributes(Context context, AttributeSet attrs, int defStyle) {
+        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ViewGroup, defStyle, 0);
 
         final int N = a.getIndexCount();
         for (int i = 0; i < N; i++) {
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index bc0d7e3..d779628 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -232,8 +232,6 @@
     InputStage mFirstInputStage;
     InputStage mFirstPostImeInputStage;
 
-    boolean mFlipControllerFallbackKeys;
-
     boolean mWindowAttributesChanged = false;
     int mWindowAttributesChangesFlag = 0;
 
@@ -370,8 +368,6 @@
         mNoncompatDensity = context.getResources().getDisplayMetrics().noncompatDensityDpi;
         mFallbackEventHandler = PolicyManager.makeNewFallbackEventHandler(context);
         mChoreographer = Choreographer.getInstance();
-        mFlipControllerFallbackKeys =
-            context.getResources().getBoolean(R.bool.flip_controller_fallback_keys);
 
         PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
         mAttachInfo.mScreenOn = powerManager.isScreenOn();
@@ -2912,11 +2908,8 @@
                 mView.dispatchConfigurationChanged(config);
             }
         }
-
-        mFlipControllerFallbackKeys =
-            mContext.getResources().getBoolean(R.bool.flip_controller_fallback_keys);
     }
-
+    
     /**
      * Return true if child is an ancestor of parent, (or equal to the parent).
      */
@@ -3985,7 +3978,6 @@
         private final SyntheticJoystickHandler mJoystick = new SyntheticJoystickHandler();
         private final SyntheticTouchNavigationHandler mTouchNavigation =
                 new SyntheticTouchNavigationHandler();
-        private final SyntheticKeyHandler mKeys = new SyntheticKeyHandler();
 
         public SyntheticInputStage() {
             super(null);
@@ -4008,12 +4000,7 @@
                     mTouchNavigation.process(event);
                     return FINISH_HANDLED;
                 }
-            } else if (q.mEvent instanceof KeyEvent) {
-                if (mKeys.process((KeyEvent) q.mEvent)) {
-                    return FINISH_HANDLED;
-                }
             }
-
             return FORWARD;
         }
 
@@ -4843,63 +4830,6 @@
         };
     }
 
-    final class SyntheticKeyHandler {
-
-        public boolean process(KeyEvent event) {
-            // In some locales (like Japan) controllers use B for confirm and A for back, rather
-            // than vice versa, so we need to special case this here since the input system itself
-            // is not locale-aware.
-            int keyCode;
-            switch(event.getKeyCode()) {
-                case KeyEvent.KEYCODE_BUTTON_A:
-                case KeyEvent.KEYCODE_BUTTON_C:
-                case KeyEvent.KEYCODE_BUTTON_X:
-                case KeyEvent.KEYCODE_BUTTON_Z:
-                    keyCode = mFlipControllerFallbackKeys ?
-                        KeyEvent.KEYCODE_BACK : KeyEvent.KEYCODE_DPAD_CENTER;
-                    break;
-                case KeyEvent.KEYCODE_BUTTON_B:
-                case KeyEvent.KEYCODE_BUTTON_Y:
-                    keyCode = mFlipControllerFallbackKeys ?
-                        KeyEvent.KEYCODE_DPAD_CENTER : KeyEvent.KEYCODE_BACK;
-                    break;
-                case KeyEvent.KEYCODE_BUTTON_THUMBL:
-                case KeyEvent.KEYCODE_BUTTON_THUMBR:
-                case KeyEvent.KEYCODE_BUTTON_START:
-                case KeyEvent.KEYCODE_BUTTON_1:
-                case KeyEvent.KEYCODE_BUTTON_2:
-                case KeyEvent.KEYCODE_BUTTON_3:
-                case KeyEvent.KEYCODE_BUTTON_4:
-                case KeyEvent.KEYCODE_BUTTON_5:
-                case KeyEvent.KEYCODE_BUTTON_6:
-                case KeyEvent.KEYCODE_BUTTON_7:
-                case KeyEvent.KEYCODE_BUTTON_8:
-                case KeyEvent.KEYCODE_BUTTON_9:
-                case KeyEvent.KEYCODE_BUTTON_10:
-                case KeyEvent.KEYCODE_BUTTON_11:
-                case KeyEvent.KEYCODE_BUTTON_12:
-                case KeyEvent.KEYCODE_BUTTON_13:
-                case KeyEvent.KEYCODE_BUTTON_14:
-                case KeyEvent.KEYCODE_BUTTON_15:
-                case KeyEvent.KEYCODE_BUTTON_16:
-                    keyCode = KeyEvent.KEYCODE_DPAD_CENTER;
-                    break;
-                case KeyEvent.KEYCODE_BUTTON_SELECT:
-                case KeyEvent.KEYCODE_BUTTON_MODE:
-                    keyCode = KeyEvent.KEYCODE_MENU;
-                default:
-                    return false;
-            }
-
-            enqueueInputEvent(new KeyEvent(event.getDownTime(), event.getEventTime(),
-                        event.getAction(), keyCode, event.getRepeatCount(), event.getMetaState(),
-                        event.getDeviceId(), event.getScanCode(),
-                        event.getFlags() | KeyEvent.FLAG_FALLBACK, event.getSource()));
-            return true;
-        }
-
-    }
-
     /**
      * Returns true if the key is used for keyboard navigation.
      * @param keyEvent The key event.
diff --git a/core/java/android/widget/CalendarView.java b/core/java/android/widget/CalendarView.java
index 0957ab4..e90b460 100644
--- a/core/java/android/widget/CalendarView.java
+++ b/core/java/android/widget/CalendarView.java
@@ -1210,35 +1210,38 @@
             child = (WeekView) view.getChildAt(offset);
         }
 
-        // Find out which month we're moving into
-        int month;
-        if (mIsScrollingUp) {
-            month = child.getMonthOfFirstWeekDay();
-        } else {
-            month = child.getMonthOfLastWeekDay();
-        }
-
-        // And how it relates to our current highlighted month
-        int monthDiff;
-        if (mCurrentMonthDisplayed == 11 && month == 0) {
-            monthDiff = 1;
-        } else if (mCurrentMonthDisplayed == 0 && month == 11) {
-            monthDiff = -1;
-        } else {
-            monthDiff = month - mCurrentMonthDisplayed;
-        }
-
-        // Only switch months if we're scrolling away from the currently
-        // selected month
-        if ((!mIsScrollingUp && monthDiff > 0) || (mIsScrollingUp && monthDiff < 0)) {
-            Calendar firstDay = child.getFirstDay();
+        if (child != null) {
+            // Find out which month we're moving into
+            int month;
             if (mIsScrollingUp) {
-                firstDay.add(Calendar.DAY_OF_MONTH, -DAYS_PER_WEEK);
+                month = child.getMonthOfFirstWeekDay();
             } else {
-                firstDay.add(Calendar.DAY_OF_MONTH, DAYS_PER_WEEK);
+                month = child.getMonthOfLastWeekDay();
             }
-            setMonthDisplayed(firstDay);
+
+            // And how it relates to our current highlighted month
+            int monthDiff;
+            if (mCurrentMonthDisplayed == 11 && month == 0) {
+                monthDiff = 1;
+            } else if (mCurrentMonthDisplayed == 0 && month == 11) {
+                monthDiff = -1;
+            } else {
+                monthDiff = month - mCurrentMonthDisplayed;
+            }
+
+            // Only switch months if we're scrolling away from the currently
+            // selected month
+            if ((!mIsScrollingUp && monthDiff > 0) || (mIsScrollingUp && monthDiff < 0)) {
+                Calendar firstDay = child.getFirstDay();
+                if (mIsScrollingUp) {
+                    firstDay.add(Calendar.DAY_OF_MONTH, -DAYS_PER_WEEK);
+                } else {
+                    firstDay.add(Calendar.DAY_OF_MONTH, DAYS_PER_WEEK);
+                }
+                setMonthDisplayed(firstDay);
+            }
         }
+
         mPreviousScrollPosition = currScroll;
         mPreviousScrollState = mCurrentScrollState;
     }
diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
index c0fde2e..9c6a2e3 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -427,12 +427,12 @@
      * Flag whether to ignore move events - we ignore such when we show in IME
      * to prevent the content from scrolling.
      */
-    private boolean mIngonreMoveEvents;
+    private boolean mIgnoreMoveEvents;
 
     /**
-     * Flag whether to show soft input on tap.
+     * Flag whether to perform a click on tap.
      */
-    private boolean mShowSoftInputOnTap;
+    private boolean mPerformClickOnTap;
 
     /**
      * The top of the top selection divider.
@@ -808,8 +808,8 @@
                 mInputText.setVisibility(View.INVISIBLE);
                 mLastDownOrMoveEventY = mLastDownEventY = event.getY();
                 mLastDownEventTime = event.getEventTime();
-                mIngonreMoveEvents = false;
-                mShowSoftInputOnTap = false;
+                mIgnoreMoveEvents = false;
+                mPerformClickOnTap = false;
                 // Handle pressed state before any state change.
                 if (mLastDownEventY < mTopSelectionDividerTop) {
                     if (mScrollState == OnScrollListener.SCROLL_STATE_IDLE) {
@@ -840,7 +840,7 @@
                     postChangeCurrentByOneFromLongPress(
                             true, ViewConfiguration.getLongPressTimeout());
                 } else {
-                    mShowSoftInputOnTap = true;
+                    mPerformClickOnTap = true;
                     postBeginSoftInputOnLongPressCommand();
                 }
                 return true;
@@ -861,7 +861,7 @@
         int action = event.getActionMasked();
         switch (action) {
             case MotionEvent.ACTION_MOVE: {
-                if (mIngonreMoveEvents) {
+                if (mIgnoreMoveEvents) {
                     break;
                 }
                 float currentMoveY = event.getY();
@@ -893,9 +893,9 @@
                     int deltaMoveY = (int) Math.abs(eventY - mLastDownEventY);
                     long deltaTime = event.getEventTime() - mLastDownEventTime;
                     if (deltaMoveY <= mTouchSlop && deltaTime < ViewConfiguration.getTapTimeout()) {
-                        if (mShowSoftInputOnTap) {
-                            mShowSoftInputOnTap = false;
-                            showSoftInput();
+                        if (mPerformClickOnTap) {
+                            mPerformClickOnTap = false;
+                            performClick();
                         } else {
                             int selectorIndexOffset = (eventY / mSelectorElementHeight)
                                     - SELECTOR_MIDDLE_ITEM_INDEX;
@@ -1188,6 +1188,27 @@
         setValueInternal(value, false);
     }
 
+    @Override
+    public boolean performClick() {
+        if (!mHasSelectorWheel) {
+            return super.performClick();
+        } else if (!super.performClick()) {
+            showSoftInput();
+        }
+        return true;
+    }
+
+    @Override
+    public boolean performLongClick() {
+        if (!mHasSelectorWheel) {
+            return super.performLongClick();
+        } else if (!super.performLongClick()) {
+            showSoftInput();
+            mIgnoreMoveEvents = true;
+        }
+        return true;
+    }
+
     /**
      * Shows the soft input for its input text.
      */
@@ -2166,8 +2187,7 @@
 
         @Override
         public void run() {
-            showSoftInput();
-            mIngonreMoveEvents = true;
+            performLongClick();
         }
     }
 
@@ -2295,7 +2315,14 @@
                         }
                         case AccessibilityNodeInfo.ACTION_CLICK: {
                             if (NumberPicker.this.isEnabled()) {
-                                showSoftInput();
+                                performClick();
+                                return true;
+                            }
+                            return false;
+                        }
+                        case AccessibilityNodeInfo.ACTION_LONG_CLICK: {
+                            if (NumberPicker.this.isEnabled()) {
+                                performLongClick();
                                 return true;
                             }
                             return false;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 7a9809f..37121e2 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -8667,6 +8667,10 @@
         super.onRtlPropertiesChanged(layoutDirection);
 
         mTextDir = getTextDirectionHeuristic();
+
+        if (mLayout != null) {
+            checkForRelayout();
+        }
     }
 
     TextDirectionHeuristic getTextDirectionHeuristic() {
diff --git a/core/res/res/values-ja/bools.xml b/core/res/res/values-ja/bools.xml
deleted file mode 100644
index 59cf744..0000000
--- a/core/res/res/values-ja/bools.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<resources>
-    <bool name="flip_controller_fallback_keys">true</bool>
-</resources>
-
diff --git a/core/res/res/values/bools.xml b/core/res/res/values/bools.xml
index 10a5d85..18e4f2f 100644
--- a/core/res/res/values/bools.xml
+++ b/core/res/res/values/bools.xml
@@ -26,5 +26,4 @@
     <bool name="show_ongoing_ime_switcher">true</bool>
     <bool name="action_bar_expanded_action_views_exclusive">true</bool>
     <bool name="target_honeycomb_needs_options_menu">true</bool>
-    <bool name="flip_controller_fallback_keys">false</bool>
 </resources>
diff --git a/core/res/res/values/styles_micro.xml b/core/res/res/values/styles_micro.xml
index b368b65..c35bd48 100644
--- a/core/res/res/values/styles_micro.xml
+++ b/core/res/res/values/styles_micro.xml
@@ -29,5 +29,6 @@
         <item name="android:internalMinWidth">64dip</item>
         <item name="android:internalMaxHeight">180dip</item>
         <item name="virtualButtonPressedDrawable">?android:attr/selectableItemBackground</item>
+        <item name="android:descendantFocusability">blocksDescendants</item>
     </style>
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index c774451..f479806 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -288,7 +288,6 @@
   <java-symbol type="bool" name="config_useFixedVolume" />
   <java-symbol type="bool" name="config_forceDefaultOrientation" />
   <java-symbol type="bool" name="config_wifi_batched_scan_supported" />
-  <java-symbol type="bool" name="flip_controller_fallback_keys" />
 
   <java-symbol type="integer" name="config_cursorWindowSize" />
   <java-symbol type="integer" name="config_extraFreeKbytesAdjust" />
diff --git a/core/res/res/values/themes_micro.xml b/core/res/res/values/themes_micro.xml
index 42f64fe..e429f96 100644
--- a/core/res/res/values/themes_micro.xml
+++ b/core/res/res/values/themes_micro.xml
@@ -31,7 +31,7 @@
         <item name="windowSwipeToDismiss">true</item>
     </style>
     <style name="Theme.Micro.Light" parent="Theme.Holo.Light">
-		<item name="numberPickerStyle">@android:style/Widget.Micro.NumberPicker</item>
+        <item name="numberPickerStyle">@android:style/Widget.Micro.NumberPicker</item>
         <item name="windowAnimationStyle">@android:style/Animation.SwipeDismiss</item>
         <item name="windowIsFloating">false</item>
         <item name="windowIsTranslucent">true</item>
@@ -47,7 +47,7 @@
     </style>
     <style name="Theme.Micro.Light.DarkActionBar" parent="Theme.Holo.Light.DarkActionBar">
         <item name="textViewStyle">@android:style/Widget.Micro.TextView</item>
-		<item name="numberPickerStyle">@android:style/Widget.Micro.NumberPicker</item>
+        <item name="numberPickerStyle">@android:style/Widget.Micro.NumberPicker</item>
         <item name="windowAnimationStyle">@android:style/Animation.SwipeDismiss</item>
         <item name="windowIsFloating">false</item>
         <item name="windowIsTranslucent">true</item>
diff --git a/data/keyboards/Generic.kcm b/data/keyboards/Generic.kcm
index 695a74f..01d22ee 100644
--- a/data/keyboards/Generic.kcm
+++ b/data/keyboards/Generic.kcm
@@ -477,4 +477,128 @@
     ctrl:                               fallback MENU
 }
 
-### Gamepad buttons are handled by the view root ###
+### Gamepad buttons ###
+
+key BUTTON_A {
+    base:                               fallback DPAD_CENTER
+}
+
+key BUTTON_B {
+    base:                               fallback BACK
+}
+
+key BUTTON_C {
+    base:                               fallback DPAD_CENTER
+}
+
+key BUTTON_X {
+    base:                               fallback DPAD_CENTER
+}
+
+key BUTTON_Y {
+    base:                               fallback BACK
+}
+
+key BUTTON_Z {
+    base:                               fallback DPAD_CENTER
+}
+
+key BUTTON_L1 {
+    base:                               none
+}
+
+key BUTTON_R1 {
+    base:                               none
+}
+
+key BUTTON_L2 {
+    base:                               none
+}
+
+key BUTTON_R2 {
+    base:                               none
+}
+
+key BUTTON_THUMBL {
+    base:                               fallback DPAD_CENTER
+}
+
+key BUTTON_THUMBR {
+    base:                               fallback DPAD_CENTER
+}
+
+key BUTTON_START {
+    base:                               fallback DPAD_CENTER
+}
+
+key BUTTON_SELECT {
+    base:                               fallback MENU
+}
+
+key BUTTON_MODE {
+    base:                               fallback MENU
+}
+
+key BUTTON_1 {
+    base:                               fallback DPAD_CENTER
+}
+
+key BUTTON_2 {
+    base:                               fallback DPAD_CENTER
+}
+
+key BUTTON_3 {
+    base:                               fallback DPAD_CENTER
+}
+
+key BUTTON_4 {
+    base:                               fallback DPAD_CENTER
+}
+
+key BUTTON_5 {
+    base:                               fallback DPAD_CENTER
+}
+
+key BUTTON_6 {
+    base:                               fallback DPAD_CENTER
+}
+
+key BUTTON_7 {
+    base:                               fallback DPAD_CENTER
+}
+
+key BUTTON_8 {
+    base:                               fallback DPAD_CENTER
+}
+
+key BUTTON_9 {
+    base:                               fallback DPAD_CENTER
+}
+
+key BUTTON_10 {
+    base:                               fallback DPAD_CENTER
+}
+
+key BUTTON_11 {
+    base:                               fallback DPAD_CENTER
+}
+
+key BUTTON_12 {
+    base:                               fallback DPAD_CENTER
+}
+
+key BUTTON_13 {
+    base:                               fallback DPAD_CENTER
+}
+
+key BUTTON_14 {
+    base:                               fallback DPAD_CENTER
+}
+
+key BUTTON_15 {
+    base:                               fallback DPAD_CENTER
+}
+
+key BUTTON_16 {
+    base:                               fallback DPAD_CENTER
+}
diff --git a/data/keyboards/Vendor_18d1_Product_2c40.kl b/data/keyboards/Vendor_18d1_Product_2c40.kl
new file mode 100644
index 0000000..903f13b6
--- /dev/null
+++ b/data/keyboards/Vendor_18d1_Product_2c40.kl
@@ -0,0 +1,42 @@
+# Copyright (C) 2013 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Odie
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+key 316 BUTTON_MODE
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+key 158 BACK            WAKE_DROPPED
+key 172 HOME
+
+axis 0x00 X
+axis 0x01 Y
+axis 0x02 Z
+axis 0x05 RZ
+axis 0x09 RTRIGGER
+axis 0x0a LTRIGGER
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+led 0x00 CONTROLLER_1
+led 0x01 CONTROLLER_2
+led 0x02 CONTROLLER_3
+led 0x03 CONTROLLER_4
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index ca2f7b1..277fbac 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -82,7 +82,6 @@
 import com.android.internal.backup.BackupConstants;
 import com.android.internal.backup.IBackupTransport;
 import com.android.internal.backup.IObbBackupService;
-import com.android.internal.backup.LocalTransport;
 import com.android.server.EventLogTags;
 import com.android.server.SystemService;
 import com.android.server.backup.PackageManagerBackupAgent.Metadata;
@@ -143,11 +142,16 @@
     private static final boolean DEBUG = true;
     private static final boolean MORE_DEBUG = false;
 
+    // Historical and current algorithm names
+    static final String PBKDF_CURRENT = "PBKDF2WithHmacSHA1";
+    static final String PBKDF_FALLBACK = "PBKDF2WithHmacSHA1And8bit";
+
     // Name and current contents version of the full-backup manifest file
     static final String BACKUP_MANIFEST_FILENAME = "_manifest";
     static final int BACKUP_MANIFEST_VERSION = 1;
     static final String BACKUP_FILE_HEADER_MAGIC = "ANDROID BACKUP\n";
-    static final int BACKUP_FILE_VERSION = 1;
+    static final int BACKUP_FILE_VERSION = 2;
+    static final int BACKUP_PW_FILE_VERSION = 2;
     static final boolean COMPRESS_FULL_BACKUPS = true; // should be true in production
 
     static final String SHARED_BACKUP_AGENT_PACKAGE = "com.android.sharedstoragebackup";
@@ -467,6 +471,8 @@
     private final SecureRandom mRng = new SecureRandom();
     private String mPasswordHash;
     private File mPasswordHashFile;
+    private int mPasswordVersion;
+    private File mPasswordVersionFile;
     private byte[] mPasswordSalt;
 
     // Configuration of PBKDF2 that we use for generating pw hashes and intermediate keys
@@ -827,6 +833,27 @@
         }
         mDataDir = Environment.getDownloadCacheDirectory();
 
+        mPasswordVersion = 1;       // unless we hear otherwise
+        mPasswordVersionFile = new File(mBaseStateDir, "pwversion");
+        if (mPasswordVersionFile.exists()) {
+            FileInputStream fin = null;
+            DataInputStream in = null;
+            try {
+                fin = new FileInputStream(mPasswordVersionFile);
+                in = new DataInputStream(fin);
+                mPasswordVersion = in.readInt();
+            } catch (IOException e) {
+                Slog.e(TAG, "Unable to read backup pw version");
+            } finally {
+                try {
+                    if (in != null) in.close();
+                    if (fin != null) fin.close();
+                } catch (IOException e) {
+                    Slog.w(TAG, "Error closing pw version files");
+                }
+            }
+        }
+
         mPasswordHashFile = new File(mBaseStateDir, "pwhash");
         if (mPasswordHashFile.exists()) {
             FileInputStream fin = null;
@@ -1127,13 +1154,13 @@
         }
     }
 
-    private SecretKey buildPasswordKey(String pw, byte[] salt, int rounds) {
-        return buildCharArrayKey(pw.toCharArray(), salt, rounds);
+    private SecretKey buildPasswordKey(String algorithm, String pw, byte[] salt, int rounds) {
+        return buildCharArrayKey(algorithm, pw.toCharArray(), salt, rounds);
     }
 
-    private SecretKey buildCharArrayKey(char[] pwArray, byte[] salt, int rounds) {
+    private SecretKey buildCharArrayKey(String algorithm, char[] pwArray, byte[] salt, int rounds) {
         try {
-            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
+            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(algorithm);
             KeySpec ks = new PBEKeySpec(pwArray, salt, rounds, PBKDF2_KEY_SIZE);
             return keyFactory.generateSecret(ks);
         } catch (InvalidKeySpecException e) {
@@ -1144,8 +1171,8 @@
         return null;
     }
 
-    private String buildPasswordHash(String pw, byte[] salt, int rounds) {
-        SecretKey key = buildPasswordKey(pw, salt, rounds);
+    private String buildPasswordHash(String algorithm, String pw, byte[] salt, int rounds) {
+        SecretKey key = buildPasswordKey(algorithm, pw, salt, rounds);
         if (key != null) {
             return byteArrayToHex(key.getEncoded());
         }
@@ -1173,13 +1200,13 @@
         return result;
     }
 
-    private byte[] makeKeyChecksum(byte[] pwBytes, byte[] salt, int rounds) {
+    private byte[] makeKeyChecksum(String algorithm, byte[] pwBytes, byte[] salt, int rounds) {
         char[] mkAsChar = new char[pwBytes.length];
         for (int i = 0; i < pwBytes.length; i++) {
             mkAsChar[i] = (char) pwBytes[i];
         }
 
-        Key checksum = buildCharArrayKey(mkAsChar, salt, rounds);
+        Key checksum = buildCharArrayKey(algorithm, mkAsChar, salt, rounds);
         return checksum.getEncoded();
     }
 
@@ -1191,7 +1218,7 @@
     }
 
     // Backup password management
-    boolean passwordMatchesSaved(String candidatePw, int rounds) {
+    boolean passwordMatchesSaved(String algorithm, String candidatePw, int rounds) {
         // First, on an encrypted device we require matching the device pw
         final boolean isEncrypted;
         try {
@@ -1235,7 +1262,7 @@
         } else {
             // hash the stated current pw and compare to the stored one
             if (candidatePw != null && candidatePw.length() > 0) {
-                String currentPwHash = buildPasswordHash(candidatePw, mPasswordSalt, rounds);
+                String currentPwHash = buildPasswordHash(algorithm, candidatePw, mPasswordSalt, rounds);
                 if (mPasswordHash.equalsIgnoreCase(currentPwHash)) {
                     // candidate hash matches the stored hash -- the password matches
                     return true;
@@ -1250,11 +1277,37 @@
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
                 "setBackupPassword");
 
-        // If the supplied pw doesn't hash to the the saved one, fail
-        if (!passwordMatchesSaved(currentPw, PBKDF2_HASH_ROUNDS)) {
+        // When processing v1 passwords we may need to try two different PBKDF2 checksum regimes
+        final boolean pbkdf2Fallback = (mPasswordVersion < BACKUP_PW_FILE_VERSION);
+
+        // If the supplied pw doesn't hash to the the saved one, fail.  The password
+        // might be caught in the legacy crypto mismatch; verify that too.
+        if (!passwordMatchesSaved(PBKDF_CURRENT, currentPw, PBKDF2_HASH_ROUNDS)
+                && !(pbkdf2Fallback && passwordMatchesSaved(PBKDF_FALLBACK,
+                        currentPw, PBKDF2_HASH_ROUNDS))) {
             return false;
         }
 
+        // Snap up to current on the pw file version
+        mPasswordVersion = BACKUP_PW_FILE_VERSION;
+        FileOutputStream pwFout = null;
+        DataOutputStream pwOut = null;
+        try {
+            pwFout = new FileOutputStream(mPasswordVersionFile);
+            pwOut = new DataOutputStream(pwFout);
+            pwOut.writeInt(mPasswordVersion);
+        } catch (IOException e) {
+            Slog.e(TAG, "Unable to write backup pw version; password not changed");
+            return false;
+        } finally {
+            try {
+                if (pwOut != null) pwOut.close();
+                if (pwFout != null) pwFout.close();
+            } catch (IOException e) {
+                Slog.w(TAG, "Unable to close pw version record");
+            }
+        }
+
         // Clearing the password is okay
         if (newPw == null || newPw.isEmpty()) {
             if (mPasswordHashFile.exists()) {
@@ -1272,7 +1325,7 @@
         try {
             // Okay, build the hash of the new backup password
             byte[] salt = randomBytes(PBKDF2_SALT_SIZE);
-            String newPwHash = buildPasswordHash(newPw, salt, PBKDF2_HASH_ROUNDS);
+            String newPwHash = buildPasswordHash(PBKDF_CURRENT, newPw, salt, PBKDF2_HASH_ROUNDS);
 
             OutputStream pwf = null, buffer = null;
             DataOutputStream out = null;
@@ -1315,6 +1368,19 @@
         }
     }
 
+    private boolean backupPasswordMatches(String currentPw) {
+        if (hasBackupPassword()) {
+            final boolean pbkdf2Fallback = (mPasswordVersion < BACKUP_PW_FILE_VERSION);
+            if (!passwordMatchesSaved(PBKDF_CURRENT, currentPw, PBKDF2_HASH_ROUNDS)
+                    && !(pbkdf2Fallback && passwordMatchesSaved(PBKDF_FALLBACK,
+                            currentPw, PBKDF2_HASH_ROUNDS))) {
+                if (DEBUG) Slog.w(TAG, "Backup password mismatch; aborting");
+                return false;
+            }
+        }
+        return true;
+    }
+
     // Maintain persistent state around whether need to do an initialize operation.
     // Must be called with the queue lock held.
     void recordInitPendingLocked(boolean isPending, String transportName) {
@@ -2735,11 +2801,9 @@
 
                 // Verify that the given password matches the currently-active
                 // backup password, if any
-                if (hasBackupPassword()) {
-                    if (!passwordMatchesSaved(mCurrentPassword, PBKDF2_HASH_ROUNDS)) {
-                        if (DEBUG) Slog.w(TAG, "Backup password mismatch; aborting");
-                        return;
-                    }
+                if (!backupPasswordMatches(mCurrentPassword)) {
+                    if (DEBUG) Slog.w(TAG, "Backup password mismatch; aborting");
+                    return;
                 }
 
                 // Write the global file header.  All strings are UTF-8 encoded; lines end
@@ -2747,7 +2811,7 @@
                 // final '\n'.
                 //
                 // line 1: "ANDROID BACKUP"
-                // line 2: backup file format version, currently "1"
+                // line 2: backup file format version, currently "2"
                 // line 3: compressed?  "0" if not compressed, "1" if compressed.
                 // line 4: name of encryption algorithm [currently only "none" or "AES-256"]
                 //
@@ -2855,7 +2919,7 @@
                 OutputStream ofstream) throws Exception {
             // User key will be used to encrypt the master key.
             byte[] newUserSalt = randomBytes(PBKDF2_SALT_SIZE);
-            SecretKey userKey = buildPasswordKey(mEncryptPassword, newUserSalt,
+            SecretKey userKey = buildPasswordKey(PBKDF_CURRENT, mEncryptPassword, newUserSalt,
                     PBKDF2_HASH_ROUNDS);
 
             // the master key is random for each backup
@@ -2902,7 +2966,7 @@
             // stated number of PBKDF2 rounds
             IV = c.getIV();
             byte[] mk = masterKeySpec.getEncoded();
-            byte[] checksum = makeKeyChecksum(masterKeySpec.getEncoded(),
+            byte[] checksum = makeKeyChecksum(PBKDF_CURRENT, masterKeySpec.getEncoded(),
                     checksumSalt, PBKDF2_HASH_ROUNDS);
 
             ByteArrayOutputStream blob = new ByteArrayOutputStream(IV.length + mk.length
@@ -3245,11 +3309,9 @@
             FileInputStream rawInStream = null;
             DataInputStream rawDataIn = null;
             try {
-                if (hasBackupPassword()) {
-                    if (!passwordMatchesSaved(mCurrentPassword, PBKDF2_HASH_ROUNDS)) {
-                        if (DEBUG) Slog.w(TAG, "Backup password mismatch; aborting");
-                        return;
-                    }
+                if (!backupPasswordMatches(mCurrentPassword)) {
+                    if (DEBUG) Slog.w(TAG, "Backup password mismatch; aborting");
+                    return;
                 }
 
                 mBytes = 0;
@@ -3270,8 +3332,12 @@
                 if (Arrays.equals(magicBytes, streamHeader)) {
                     // okay, header looks good.  now parse out the rest of the fields.
                     String s = readHeaderLine(rawInStream);
-                    if (Integer.parseInt(s) == BACKUP_FILE_VERSION) {
-                        // okay, it's a version we recognize
+                    final int archiveVersion = Integer.parseInt(s);
+                    if (archiveVersion <= BACKUP_FILE_VERSION) {
+                        // okay, it's a version we recognize.  if it's version 1, we may need
+                        // to try two different PBKDF2 regimes to compare checksums.
+                        final boolean pbkdf2Fallback = (archiveVersion == 1);
+
                         s = readHeaderLine(rawInStream);
                         compressed = (Integer.parseInt(s) != 0);
                         s = readHeaderLine(rawInStream);
@@ -3279,7 +3345,8 @@
                             // no more header to parse; we're good to go
                             okay = true;
                         } else if (mDecryptPassword != null && mDecryptPassword.length() > 0) {
-                            preCompressStream = decodeAesHeaderAndInitialize(s, rawInStream);
+                            preCompressStream = decodeAesHeaderAndInitialize(s, pbkdf2Fallback,
+                                    rawInStream);
                             if (preCompressStream != null) {
                                 okay = true;
                             }
@@ -3339,7 +3406,71 @@
             return buffer.toString();
         }
 
-        InputStream decodeAesHeaderAndInitialize(String encryptionName, InputStream rawInStream) {
+        InputStream attemptMasterKeyDecryption(String algorithm, byte[] userSalt, byte[] ckSalt,
+                int rounds, String userIvHex, String masterKeyBlobHex, InputStream rawInStream,
+                boolean doLog) {
+            InputStream result = null;
+
+            try {
+                Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
+                SecretKey userKey = buildPasswordKey(algorithm, mDecryptPassword, userSalt,
+                        rounds);
+                byte[] IV = hexToByteArray(userIvHex);
+                IvParameterSpec ivSpec = new IvParameterSpec(IV);
+                c.init(Cipher.DECRYPT_MODE,
+                        new SecretKeySpec(userKey.getEncoded(), "AES"),
+                        ivSpec);
+                byte[] mkCipher = hexToByteArray(masterKeyBlobHex);
+                byte[] mkBlob = c.doFinal(mkCipher);
+
+                // first, the master key IV
+                int offset = 0;
+                int len = mkBlob[offset++];
+                IV = Arrays.copyOfRange(mkBlob, offset, offset + len);
+                offset += len;
+                // then the master key itself
+                len = mkBlob[offset++];
+                byte[] mk = Arrays.copyOfRange(mkBlob,
+                        offset, offset + len);
+                offset += len;
+                // and finally the master key checksum hash
+                len = mkBlob[offset++];
+                byte[] mkChecksum = Arrays.copyOfRange(mkBlob,
+                        offset, offset + len);
+
+                // now validate the decrypted master key against the checksum
+                byte[] calculatedCk = makeKeyChecksum(algorithm, mk, ckSalt, rounds);
+                if (Arrays.equals(calculatedCk, mkChecksum)) {
+                    ivSpec = new IvParameterSpec(IV);
+                    c.init(Cipher.DECRYPT_MODE,
+                            new SecretKeySpec(mk, "AES"),
+                            ivSpec);
+                    // Only if all of the above worked properly will 'result' be assigned
+                    result = new CipherInputStream(rawInStream, c);
+                } else if (doLog) Slog.w(TAG, "Incorrect password");
+            } catch (InvalidAlgorithmParameterException e) {
+                if (doLog) Slog.e(TAG, "Needed parameter spec unavailable!", e);
+            } catch (BadPaddingException e) {
+                // This case frequently occurs when the wrong password is used to decrypt
+                // the master key.  Use the identical "incorrect password" log text as is
+                // used in the checksum failure log in order to avoid providing additional
+                // information to an attacker.
+                if (doLog) Slog.w(TAG, "Incorrect password");
+            } catch (IllegalBlockSizeException e) {
+                if (doLog) Slog.w(TAG, "Invalid block size in master key");
+            } catch (NoSuchAlgorithmException e) {
+                if (doLog) Slog.e(TAG, "Needed decryption algorithm unavailable!");
+            } catch (NoSuchPaddingException e) {
+                if (doLog) Slog.e(TAG, "Needed padding mechanism unavailable!");
+            } catch (InvalidKeyException e) {
+                if (doLog) Slog.w(TAG, "Illegal password; aborting");
+            }
+
+            return result;
+        }
+
+        InputStream decodeAesHeaderAndInitialize(String encryptionName, boolean pbkdf2Fallback,
+                InputStream rawInStream) {
             InputStream result = null;
             try {
                 if (encryptionName.equals(ENCRYPTION_ALGORITHM_NAME)) {
@@ -3356,59 +3487,13 @@
                     String masterKeyBlobHex = readHeaderLine(rawInStream); // 9
 
                     // decrypt the master key blob
-                    Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
-                    SecretKey userKey = buildPasswordKey(mDecryptPassword, userSalt,
-                            rounds);
-                    byte[] IV = hexToByteArray(userIvHex);
-                    IvParameterSpec ivSpec = new IvParameterSpec(IV);
-                    c.init(Cipher.DECRYPT_MODE,
-                            new SecretKeySpec(userKey.getEncoded(), "AES"),
-                            ivSpec);
-                    byte[] mkCipher = hexToByteArray(masterKeyBlobHex);
-                    byte[] mkBlob = c.doFinal(mkCipher);
-
-                    // first, the master key IV
-                    int offset = 0;
-                    int len = mkBlob[offset++];
-                    IV = Arrays.copyOfRange(mkBlob, offset, offset + len);
-                    offset += len;
-                    // then the master key itself
-                    len = mkBlob[offset++];
-                    byte[] mk = Arrays.copyOfRange(mkBlob,
-                            offset, offset + len);
-                    offset += len;
-                    // and finally the master key checksum hash
-                    len = mkBlob[offset++];
-                    byte[] mkChecksum = Arrays.copyOfRange(mkBlob,
-                            offset, offset + len);
-
-                    // now validate the decrypted master key against the checksum
-                    byte[] calculatedCk = makeKeyChecksum(mk, ckSalt, rounds);
-                    if (Arrays.equals(calculatedCk, mkChecksum)) {
-                        ivSpec = new IvParameterSpec(IV);
-                        c.init(Cipher.DECRYPT_MODE,
-                                new SecretKeySpec(mk, "AES"),
-                                ivSpec);
-                        // Only if all of the above worked properly will 'result' be assigned
-                        result = new CipherInputStream(rawInStream, c);
-                    } else Slog.w(TAG, "Incorrect password");
+                    result = attemptMasterKeyDecryption(PBKDF_CURRENT, userSalt, ckSalt,
+                            rounds, userIvHex, masterKeyBlobHex, rawInStream, false);
+                    if (result == null && pbkdf2Fallback) {
+                        result = attemptMasterKeyDecryption(PBKDF_FALLBACK, userSalt, ckSalt,
+                                rounds, userIvHex, masterKeyBlobHex, rawInStream, true);
+                    }
                 } else Slog.w(TAG, "Unsupported encryption method: " + encryptionName);
-            } catch (InvalidAlgorithmParameterException e) {
-                Slog.e(TAG, "Needed parameter spec unavailable!", e);
-            } catch (BadPaddingException e) {
-                // This case frequently occurs when the wrong password is used to decrypt
-                // the master key.  Use the identical "incorrect password" log text as is
-                // used in the checksum failure log in order to avoid providing additional
-                // information to an attacker.
-                Slog.w(TAG, "Incorrect password");
-            } catch (IllegalBlockSizeException e) {
-                Slog.w(TAG, "Invalid block size in master key");
-            } catch (NoSuchAlgorithmException e) {
-                Slog.e(TAG, "Needed decryption algorithm unavailable!");
-            } catch (NoSuchPaddingException e) {
-                Slog.e(TAG, "Needed padding mechanism unavailable!");
-            } catch (InvalidKeyException e) {
-                Slog.w(TAG, "Illegal password; aborting");
             } catch (NumberFormatException e) {
                 Slog.w(TAG, "Can't parse restore data header");
             } catch (IOException e) {
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 8d158cf..560ec54 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -2312,9 +2312,9 @@
             mInetConditionChangeInFlight = false;
             // Don't do this - if we never sign in stay, grey
             //reportNetworkCondition(mActiveDefaultNetwork, 100);
+            updateNetworkSettings(thisNet);
         }
         thisNet.setTeardownRequested(false);
-        updateNetworkSettings(thisNet);
         updateMtuSizeSettings(thisNet);
         handleConnectivityChange(newNetType, false);
         sendConnectedBroadcastDelayed(info, getConnectivityChangeDelay());
@@ -3041,7 +3041,7 @@
                 case NetworkStateTracker.EVENT_NETWORK_SUBTYPE_CHANGED: {
                     info = (NetworkInfo) msg.obj;
                     int type = info.getType();
-                    updateNetworkSettings(mNetTrackers[type]);
+                    if (mNetConfigs[type].isDefault()) updateNetworkSettings(mNetTrackers[type]);
                     break;
                 }
             }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 8b06420..c90978a 100755
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -10617,7 +10617,8 @@
 
         DumpState dumpState = new DumpState();
         boolean fullPreferred = false;
-        
+        boolean checkin = false;
+
         String packageName = null;
         
         int opti = 0;
@@ -10631,7 +10632,8 @@
                 // Right now we only know how to print all.
             } else if ("-h".equals(opt)) {
                 pw.println("Package manager dump options:");
-                pw.println("  [-h] [-f] [cmd] ...");
+                pw.println("  [-h] [-f] [--checkin] [cmd] ...");
+                pw.println("    --checkin: dump for a checkin");
                 pw.println("    -f: print details of intent filters");
                 pw.println("    -h: print this help");
                 pw.println("  cmd may be one of:");
@@ -10649,13 +10651,15 @@
                 pw.println("    <package.name>: info about given package");
                 pw.println("    k[eysets]: print known keysets");
                 return;
+            } else if ("--checkin".equals(opt)) {
+                checkin = true;
             } else if ("-f".equals(opt)) {
                 dumpState.setOptionEnabled(DumpState.OPTION_SHOW_FILTERS);
             } else {
                 pw.println("Unknown argument: " + opt + "; use -h for help");
             }
         }
-        
+
         // Is the caller requesting to dump a particular piece of data?
         if (opti < args.length) {
             String cmd = args[opti];
@@ -10697,17 +10701,26 @@
             }
         }
 
+        if (checkin) {
+            pw.println("vers,1");
+        }
+
         // reader
         synchronized (mPackages) {
             if (dumpState.isDumping(DumpState.DUMP_VERIFIERS) && packageName == null) {
-                if (dumpState.onTitlePrinted())
-                    pw.println();
-                pw.println("Verifiers:");
-                pw.print("  Required: ");
-                pw.print(mRequiredVerifierPackage);
-                pw.print(" (uid=");
-                pw.print(getPackageUid(mRequiredVerifierPackage, 0));
-                pw.println(")");
+                if (!checkin) {
+                    if (dumpState.onTitlePrinted())
+                        pw.println();
+                    pw.println("Verifiers:");
+                    pw.print("  Required: ");
+                    pw.print(mRequiredVerifierPackage);
+                    pw.print(" (uid=");
+                    pw.print(getPackageUid(mRequiredVerifierPackage, 0));
+                    pw.println(")");
+                } else if (mRequiredVerifierPackage != null) {
+                    pw.print("vrfy,"); pw.print(mRequiredVerifierPackage);
+                    pw.print(","); pw.println(getPackageUid(mRequiredVerifierPackage, 0));
+                }
             }
 
             if (dumpState.isDumping(DumpState.DUMP_LIBS) && packageName == null) {
@@ -10716,21 +10729,37 @@
                 while (it.hasNext()) {
                     String name = it.next();
                     SharedLibraryEntry ent = mSharedLibraries.get(name);
-                    if (!printedHeader) {
-                        if (dumpState.onTitlePrinted())
-                            pw.println();
-                        pw.println("Libraries:");
-                        printedHeader = true;
-                    }
-                    pw.print("  ");
-                    pw.print(name);
-                    pw.print(" -> ");
-                    if (ent.path != null) {
-                        pw.print("(jar) ");
-                        pw.print(ent.path);
+                    if (!checkin) {
+                        if (!printedHeader) {
+                            if (dumpState.onTitlePrinted())
+                                pw.println();
+                            pw.println("Libraries:");
+                            printedHeader = true;
+                        }
+                        pw.print("  ");
                     } else {
-                        pw.print("(apk) ");
-                        pw.print(ent.apk);
+                        pw.print("lib,");
+                    }
+                    pw.print(name);
+                    if (!checkin) {
+                        pw.print(" -> ");
+                    }
+                    if (ent.path != null) {
+                        if (!checkin) {
+                            pw.print("(jar) ");
+                            pw.print(ent.path);
+                        } else {
+                            pw.print(",jar,");
+                            pw.print(ent.path);
+                        }
+                    } else {
+                        if (!checkin) {
+                            pw.print("(apk) ");
+                            pw.print(ent.apk);
+                        } else {
+                            pw.print(",apk,");
+                            pw.print(ent.apk);
+                        }
                     }
                     pw.println();
                 }
@@ -10739,16 +10768,22 @@
             if (dumpState.isDumping(DumpState.DUMP_FEATURES) && packageName == null) {
                 if (dumpState.onTitlePrinted())
                     pw.println();
-                pw.println("Features:");
+                if (!checkin) {
+                    pw.println("Features:");
+                }
                 Iterator<String> it = mAvailableFeatures.keySet().iterator();
                 while (it.hasNext()) {
                     String name = it.next();
-                    pw.print("  ");
+                    if (!checkin) {
+                        pw.print("  ");
+                    } else {
+                        pw.print("feat,");
+                    }
                     pw.println(name);
                 }
             }
 
-            if (dumpState.isDumping(DumpState.DUMP_RESOLVERS)) {
+            if (!checkin && dumpState.isDumping(DumpState.DUMP_RESOLVERS)) {
                 if (mActivities.dump(pw, dumpState.getTitlePrinted() ? "\nActivity Resolver Table:"
                         : "Activity Resolver Table:", "  ", packageName,
                         dumpState.isOptionEnabled(DumpState.OPTION_SHOW_FILTERS))) {
@@ -10771,7 +10806,7 @@
                 }
             }
 
-            if (dumpState.isDumping(DumpState.DUMP_PREFERRED)) {
+            if (!checkin && dumpState.isDumping(DumpState.DUMP_PREFERRED)) {
                 for (int i=0; i<mSettings.mPreferredActivities.size(); i++) {
                     PreferredIntentResolver pir = mSettings.mPreferredActivities.valueAt(i);
                     int user = mSettings.mPreferredActivities.keyAt(i);
@@ -10785,7 +10820,7 @@
                 }
             }
 
-            if (dumpState.isDumping(DumpState.DUMP_PREFERRED_XML)) {
+            if (!checkin && dumpState.isDumping(DumpState.DUMP_PREFERRED_XML)) {
                 pw.flush();
                 FileOutputStream fout = new FileOutputStream(fd);
                 BufferedOutputStream str = new BufferedOutputStream(fout);
@@ -10807,11 +10842,11 @@
                 }
             }
 
-            if (dumpState.isDumping(DumpState.DUMP_PERMISSIONS)) {
+            if (!checkin && dumpState.isDumping(DumpState.DUMP_PERMISSIONS)) {
                 mSettings.dumpPermissionsLPr(pw, packageName, dumpState);
             }
 
-            if (dumpState.isDumping(DumpState.DUMP_PROVIDERS)) {
+            if (!checkin && dumpState.isDumping(DumpState.DUMP_PROVIDERS)) {
                 boolean printedSomething = false;
                 for (PackageParser.Provider p : mProviders.mProviders.values()) {
                     if (packageName != null && !packageName.equals(p.info.packageName)) {
@@ -10848,19 +10883,19 @@
                 }
             }
 
-            if (dumpState.isDumping(DumpState.DUMP_KEYSETS)) {
+            if (!checkin && dumpState.isDumping(DumpState.DUMP_KEYSETS)) {
                 mSettings.mKeySetManager.dump(pw, packageName, dumpState);
             }
 
             if (dumpState.isDumping(DumpState.DUMP_PACKAGES)) {
-                mSettings.dumpPackagesLPr(pw, packageName, dumpState);
+                mSettings.dumpPackagesLPr(pw, packageName, dumpState, checkin);
             }
 
-            if (dumpState.isDumping(DumpState.DUMP_SHARED_USERS)) {
+            if (!checkin && dumpState.isDumping(DumpState.DUMP_SHARED_USERS)) {
                 mSettings.dumpSharedUsersLPr(pw, packageName, dumpState);
             }
 
-            if (dumpState.isDumping(DumpState.DUMP_MESSAGES) && packageName == null) {
+            if (!checkin && dumpState.isDumping(DumpState.DUMP_MESSAGES) && packageName == null) {
                 if (dumpState.onTitlePrinted())
                     pw.println();
                 mSettings.dumpReadMessagesLPr(pw, dumpState);
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index e599409..8f18b23 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -2892,8 +2892,44 @@
         ApplicationInfo.FLAG_CANT_SAVE_STATE, "CANT_SAVE_STATE",
     };
 
-    void dumpPackageLPr(PrintWriter pw, String prefix, PackageSetting ps, SimpleDateFormat sdf,
-            Date date, List<UserInfo> users) {
+    void dumpPackageLPr(PrintWriter pw, String prefix, String checkinTag, PackageSetting ps,
+            SimpleDateFormat sdf, Date date, List<UserInfo> users) {
+        if (checkinTag != null) {
+            pw.print(checkinTag);
+            pw.print(",");
+            pw.print(ps.realName != null ? ps.realName : ps.name);
+            pw.print(",");
+            pw.print(ps.appId);
+            pw.print(",");
+            pw.print(ps.versionCode);
+            pw.print(",");
+            pw.print(ps.firstInstallTime);
+            pw.print(",");
+            pw.print(ps.lastUpdateTime);
+            pw.print(",");
+            pw.print(ps.installerPackageName != null ? ps.installerPackageName : "?");
+            pw.println();
+            for (UserInfo user : users) {
+                pw.print(checkinTag);
+                pw.print("-");
+                pw.print("usr");
+                pw.print(",");
+                pw.print(user.id);
+                pw.print(",");
+                pw.print(ps.getInstalled(user.id) ? "I" : "i");
+                pw.print(ps.getBlocked(user.id) ? "B" : "b");
+                pw.print(ps.getStopped(user.id) ? "S" : "s");
+                pw.print(ps.getNotLaunched(user.id) ? "l" : "L");
+                pw.print(",");
+                pw.print(ps.getEnabled(user.id));
+                String lastDisabledAppCaller = ps.getLastDisabledAppCaller(user.id);
+                pw.print(",");
+                pw.print(lastDisabledAppCaller != null ? lastDisabledAppCaller : "?");
+                pw.println();
+            }
+            return;
+        }
+
         pw.print(prefix); pw.print("Package [");
             pw.print(ps.realName != null ? ps.realName : ps.name);
             pw.print("] (");
@@ -3055,7 +3091,7 @@
         }
     }
 
-    void dumpPackagesLPr(PrintWriter pw, String packageName, DumpState dumpState) {
+    void dumpPackagesLPr(PrintWriter pw, String packageName, DumpState dumpState, boolean checkin) {
         final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
         final Date date = new Date();
         boolean printedSomething = false;
@@ -3066,35 +3102,39 @@
                 continue;
             }
 
-            if (packageName != null) {
+            if (!checkin && packageName != null) {
                 dumpState.setSharedUser(ps.sharedUser);
             }
 
-            if (!printedSomething) {
+            if (!checkin && !printedSomething) {
                 if (dumpState.onTitlePrinted())
                     pw.println();
                 pw.println("Packages:");
                 printedSomething = true;
             }
-            dumpPackageLPr(pw, "  ", ps, sdf, date, users);
+            dumpPackageLPr(pw, "  ", checkin ? "pkg" : null, ps, sdf, date, users);
         }
 
         printedSomething = false;
-        if (mRenamedPackages.size() > 0) {
+        if (!checkin && mRenamedPackages.size() > 0) {
             for (final Map.Entry<String, String> e : mRenamedPackages.entrySet()) {
                 if (packageName != null && !packageName.equals(e.getKey())
                         && !packageName.equals(e.getValue())) {
                     continue;
                 }
-                if (!printedSomething) {
-                    if (dumpState.onTitlePrinted())
-                        pw.println();
-                    pw.println("Renamed packages:");
-                    printedSomething = true;
+                if (!checkin) {
+                    if (!printedSomething) {
+                        if (dumpState.onTitlePrinted())
+                            pw.println();
+                        pw.println("Renamed packages:");
+                        printedSomething = true;
+                    }
+                    pw.print("  ");
+                } else {
+                    pw.print("ren,");
                 }
-                pw.print("  ");
                 pw.print(e.getKey());
-                pw.print(" -> ");
+                pw.print(checkin ? " -> " : ",");
                 pw.println(e.getValue());
             }
         }
@@ -3106,13 +3146,13 @@
                         && !packageName.equals(ps.name)) {
                     continue;
                 }
-                if (!printedSomething) {
+                if (!checkin && !printedSomething) {
                     if (dumpState.onTitlePrinted())
                         pw.println();
                     pw.println("Hidden system packages:");
                     printedSomething = true;
                 }
-                dumpPackageLPr(pw, "  ", ps, sdf, date, users);
+                dumpPackageLPr(pw, "  ", checkin ? "dis" : null, ps, sdf, date, users);
             }
         }
     }
diff --git a/tests/LegacyRestoreTest/README b/tests/LegacyRestoreTest/README
new file mode 100644
index 0000000..cdd157e
--- /dev/null
+++ b/tests/LegacyRestoreTest/README
@@ -0,0 +1,18 @@
+The file "jbmr2-encrypted-settings-abcd.ab" in this directory is an encrypted
+"adb backup" archive of the settings provider package.  It was generated on a
+Nexus 4 running Android 4.3 (API 18), and so predates the Android 4.4 changes
+to the PBKDF2 implementation.  The archive's encryption password, entered on-screen,
+is "abcd" (with no quotation marks).
+
+'adb restore' decrypts and applies the restored archive successfully on a device
+running Android 4.3, but fails to restore correctly on a device running Android 4.4,
+reporting an invalid password in logcat.  This is the situation reported in bug
+<https://code.google.com/p/android/issues/detail?id=63880>.
+
+The file "kk-fixed-encrypted-settings-abcd.ab" is a similar encrypted "adb backup"
+archive, using the same key, generated on a Nexus 4 running Android 4.4 with a fix
+to this bug in place.  This archive should be successfully restorable on any
+version of Android which incorporates the fix.
+
+These archives can be used as an ongoing test to verify that historical encrypted
+archives from various points in Android's history can be successfully restored.
diff --git a/tests/LegacyRestoreTest/jbmr2-encrypted-settings-abcd.ab b/tests/LegacyRestoreTest/jbmr2-encrypted-settings-abcd.ab
new file mode 100644
index 0000000..192dcf5
--- /dev/null
+++ b/tests/LegacyRestoreTest/jbmr2-encrypted-settings-abcd.ab
Binary files differ
diff --git a/tests/LegacyRestoreTest/kk-fixed-encrypted-settings-abcd.ab b/tests/LegacyRestoreTest/kk-fixed-encrypted-settings-abcd.ab
new file mode 100644
index 0000000..bf2b558
--- /dev/null
+++ b/tests/LegacyRestoreTest/kk-fixed-encrypted-settings-abcd.ab
Binary files differ