Merge "Camera2: Doc updates for aeExposureCompensation." into lmp-mr1-dev
diff --git a/Android.mk b/Android.mk
index a596ee7..ea22d0f 100644
--- a/Android.mk
+++ b/Android.mk
@@ -174,6 +174,7 @@
 	core/java/android/hardware/usb/IUsbManager.aidl \
 	core/java/android/net/IConnectivityManager.aidl \
 	core/java/android/net/IEthernetManager.aidl \
+	core/java/android/net/IEthernetServiceListener.aidl \
 	core/java/android/net/INetworkManagementEventObserver.aidl \
 	core/java/android/net/INetworkPolicyListener.aidl \
 	core/java/android/net/INetworkPolicyManager.aidl \
diff --git a/api/current.txt b/api/current.txt
index b5055eb..d25ae96 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1056,6 +1056,7 @@
     field public static final int scrollbars = 16842974; // 0x10100de
     field public static final int scrollingCache = 16843006; // 0x10100fe
     field public static final deprecated int searchButtonText = 16843269; // 0x1010205
+    field public static final int searchHintIcon = 16844038; // 0x1010506
     field public static final int searchIcon = 16843907; // 0x1010483
     field public static final int searchMode = 16843221; // 0x10101d5
     field public static final int searchSettingsDescription = 16843402; // 0x101028a
@@ -2011,6 +2012,7 @@
     field public static final int Theme_DeviceDefault_Dialog = 16974126; // 0x103012e
     field public static final int Theme_DeviceDefault_DialogWhenLarge = 16974134; // 0x1030136
     field public static final int Theme_DeviceDefault_DialogWhenLarge_NoActionBar = 16974135; // 0x1030137
+    field public static final int Theme_DeviceDefault_Dialog_Alert = 16974545; // 0x10302d1
     field public static final int Theme_DeviceDefault_Dialog_MinWidth = 16974127; // 0x103012f
     field public static final int Theme_DeviceDefault_Dialog_NoActionBar = 16974128; // 0x1030130
     field public static final int Theme_DeviceDefault_Dialog_NoActionBar_MinWidth = 16974129; // 0x1030131
@@ -2020,6 +2022,7 @@
     field public static final int Theme_DeviceDefault_Light_Dialog = 16974130; // 0x1030132
     field public static final int Theme_DeviceDefault_Light_DialogWhenLarge = 16974136; // 0x1030138
     field public static final int Theme_DeviceDefault_Light_DialogWhenLarge_NoActionBar = 16974137; // 0x1030139
+    field public static final int Theme_DeviceDefault_Light_Dialog_Alert = 16974546; // 0x10302d2
     field public static final int Theme_DeviceDefault_Light_Dialog_MinWidth = 16974131; // 0x1030133
     field public static final int Theme_DeviceDefault_Light_Dialog_NoActionBar = 16974132; // 0x1030134
     field public static final int Theme_DeviceDefault_Light_Dialog_NoActionBar_MinWidth = 16974133; // 0x1030135
@@ -3111,6 +3114,7 @@
     method public void removeAllUpdateListeners();
     method public void removeUpdateListener(android.animation.ValueAnimator.AnimatorUpdateListener);
     method public void reverse();
+    method public void setCurrentFraction(float);
     method public void setCurrentPlayTime(long);
     method public android.animation.ValueAnimator setDuration(long);
     method public void setEvaluator(android.animation.TypeEvaluator);
@@ -4412,7 +4416,7 @@
     method public android.content.Intent createConfirmDeviceCredentialIntent(java.lang.CharSequence, java.lang.CharSequence);
     method public deprecated void exitKeyguardSecurely(android.app.KeyguardManager.OnKeyguardExitResult);
     method public boolean inKeyguardRestrictedInputMode();
-    method public boolean isKeyguardInTrustedState();
+    method public boolean isDeviceLocked();
     method public boolean isKeyguardLocked();
     method public boolean isKeyguardSecure();
     method public deprecated android.app.KeyguardManager.KeyguardLock newKeyguardLock(java.lang.String);
@@ -7817,6 +7821,7 @@
     field public static final int FLAG_RECEIVER_REGISTERED_ONLY = 1073741824; // 0x40000000
     field public static final int FLAG_RECEIVER_REPLACE_PENDING = 536870912; // 0x20000000
     field public static final java.lang.String METADATA_DOCK_HOME = "android.dock_home";
+    field public static final int URI_ALLOW_UNSAFE = 4; // 0x4
     field public static final int URI_ANDROID_APP_SCHEME = 2; // 0x2
     field public static final int URI_INTENT_SCHEME = 1; // 0x1
   }
@@ -16928,8 +16933,8 @@
     field public static final java.lang.String EXTRA_EXTRA_INFO = "extraInfo";
     field public static final java.lang.String EXTRA_IS_FAILOVER = "isFailover";
     field public static final deprecated java.lang.String EXTRA_NETWORK_INFO = "networkInfo";
-    field public static final java.lang.String EXTRA_NETWORK_REQUEST_NETWORK = "networkRequestNetwork";
-    field public static final java.lang.String EXTRA_NETWORK_REQUEST_NETWORK_REQUEST = "networkRequestNetworkRequest";
+    field public static final java.lang.String EXTRA_NETWORK = "android.net.extra.NETWORK";
+    field public static final java.lang.String EXTRA_NETWORK_REQUEST = "android.net.extra.NETWORK_REQUEST";
     field public static final java.lang.String EXTRA_NETWORK_TYPE = "networkType";
     field public static final java.lang.String EXTRA_NO_CONNECTIVITY = "noConnectivity";
     field public static final java.lang.String EXTRA_OTHER_NETWORK_INFO = "otherNetwork";
@@ -28689,7 +28694,7 @@
     method public java.lang.String getSubscriberId();
     method public java.lang.String getVoiceMailAlphaTag();
     method public java.lang.String getVoiceMailNumber();
-    method public int hasCarrierPrivileges();
+    method public boolean hasCarrierPrivileges();
     method public boolean hasIccCard();
     method public boolean iccCloseLogicalChannel(int);
     method public byte[] iccExchangeSimIO(int, int, int, int, int, java.lang.String);
@@ -28709,10 +28714,6 @@
     field public static final int CALL_STATE_IDLE = 0; // 0x0
     field public static final int CALL_STATE_OFFHOOK = 2; // 0x2
     field public static final int CALL_STATE_RINGING = 1; // 0x1
-    field public static final int CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES = -2; // 0xfffffffe
-    field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1
-    field public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; // 0x0
-    field public static final int CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED = -1; // 0xffffffff
     field public static final int DATA_ACTIVITY_DORMANT = 4; // 0x4
     field public static final int DATA_ACTIVITY_IN = 1; // 0x1
     field public static final int DATA_ACTIVITY_INOUT = 3; // 0x3
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 0cad17d..475d540 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -55,12 +55,9 @@
 import android.util.AndroidException;
 import android.util.ArrayMap;
 import android.view.IWindowManager;
-import android.view.View;
 
 import com.android.internal.os.BaseCommand;
 
-import dalvik.system.VMRuntime;
-
 import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileNotFoundException;
@@ -619,7 +616,7 @@
             // The argument is a URI.  Fully parse it, and use that result
             // to fill in any data not specified so far.
             baseIntent = Intent.parseUri(arg, Intent.URI_INTENT_SCHEME
-                    | Intent.URI_ANDROID_APP_SCHEME);
+                    | Intent.URI_ANDROID_APP_SCHEME | Intent.URI_ALLOW_UNSAFE);
         } else if (arg.indexOf('/') >= 0) {
             // The argument is a component name.  Build an Intent to launch
             // it.
diff --git a/cmds/content/src/com/android/commands/content/Content.java b/cmds/content/src/com/android/commands/content/Content.java
index 948c9a2..bd34a9c 100644
--- a/cmds/content/src/com/android/commands/content/Content.java
+++ b/cmds/content/src/com/android/commands/content/Content.java
@@ -501,7 +501,7 @@
 
         @Override
         public void onExecute(IContentProvider provider) throws Exception {
-            final ParcelFileDescriptor fd = provider.openFile(null, mUri, "r", null);
+            final ParcelFileDescriptor fd = provider.openFile(null, mUri, "r", null, null);
             copy(new FileInputStream(fd.getFileDescriptor()), System.out);
         }
 
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index a9eaf29..3f1845a 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -16,6 +16,7 @@
 
 package android.accessibilityservice;
 
+import android.annotation.NonNull;
 import android.app.Service;
 import android.content.Context;
 import android.content.Intent;
@@ -27,6 +28,7 @@
 import android.view.KeyEvent;
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
+import android.view.WindowManagerImpl;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityInteractionClient;
 import android.view.accessibility.AccessibilityNodeInfo;
@@ -618,6 +620,23 @@
         }
     }
 
+    @Override
+    public Object getSystemService(@ServiceName @NonNull String name) {
+        if (getBaseContext() == null) {
+            throw new IllegalStateException(
+                    "System services not available to Activities before onCreate()");
+        }
+
+        // Guarantee that we always return the same window manager instance.
+        if (WINDOW_SERVICE.equals(name)) {
+            if (mWindowManager == null) {
+                mWindowManager = (WindowManager) getBaseContext().getSystemService(name);
+            }
+            return mWindowManager;
+        }
+        return super.getSystemService(name);
+    }
+
     /**
      * Implement to return the implementation of the internal accessibility
      * service interface.
@@ -645,8 +664,10 @@
                 mConnectionId = connectionId;
                 mWindowToken = windowToken;
 
-                // Let the window manager know about our shiny new token.
-                WindowManagerGlobal.getInstance().setDefaultToken(mWindowToken);
+                // The client may have already obtained the window manager, so
+                // update the default token on whatever manager we gave them.
+                final WindowManagerImpl wm = (WindowManagerImpl) getSystemService(WINDOW_SERVICE);
+                wm.setDefaultToken(windowToken);
             }
 
             @Override
diff --git a/core/java/android/animation/TimeAnimator.java b/core/java/android/animation/TimeAnimator.java
index f9aa00e..1738ade 100644
--- a/core/java/android/animation/TimeAnimator.java
+++ b/core/java/android/animation/TimeAnimator.java
@@ -1,5 +1,23 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package android.animation;
 
+import android.view.animation.AnimationUtils;
+
 /**
  * This class provides a simple callback mechanism to listeners that is synchronized with all
  * other animators in the system. There is no duration, interpolation, or object value-setting
@@ -29,6 +47,13 @@
         return false;
     }
 
+    @Override
+    public void setCurrentPlayTime(long playTime) {
+        long currentTime = AnimationUtils.currentAnimationTimeMillis();
+        mStartTime = Math.max(mStartTime, currentTime - playTime);
+        animationFrame(currentTime);
+    }
+
     /**
      * Sets a listener that is sent update events throughout the life of
      * an animation.
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index 07f79b8..d65b490 100644
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -79,7 +79,7 @@
      * Set when setCurrentPlayTime() is called. If negative, animation is not currently seeked
      * to a value.
      */
-    long mSeekTime = -1;
+    float mSeekFraction = -1;
 
     /**
      * Set on the next frame after pause() is called, used to calculate a new startTime
@@ -537,14 +537,31 @@
      * @param playTime The time, in milliseconds, to which the animation is advanced or rewound.
      */
     public void setCurrentPlayTime(long playTime) {
+        float fraction = mUnscaledDuration > 0 ? (float) playTime / mUnscaledDuration :
+                playTime == 0 ? 0 : 1;
+        setCurrentFraction(fraction);
+    }
+
+    /**
+     * Sets the position of the animation to the specified fraction. This fraction should
+     * be between 0 and the total fraction of the animation, including any repetition. That is,
+     * a fraction of 0 will position the animation at the beginning, a value of 1 at the end,
+     * and a value of 2 at the beginning of a reversing animator that repeats once. If
+     * the animation has not yet been started, then it will not advance forward after it is
+     * set to this fraction; it will simply set the fraction to this value and perform any
+     * appropriate actions based on that fraction. If the animation is already running, then
+     * setCurrentFraction() will set the current fraction to this value and continue
+     * playing from that point.
+     *
+     * @param fraction The fraction to which the animation is advanced or rewound.
+     */
+    public void setCurrentFraction(float fraction) {
         initAnimation();
-        long currentTime = AnimationUtils.currentAnimationTimeMillis();
         if (mPlayingState != RUNNING) {
-            mSeekTime = playTime;
+            mSeekFraction = fraction;
             mPlayingState = SEEKED;
         }
-        mStartTime = currentTime - playTime;
-        doAnimationFrame(currentTime);
+        animateValue(fraction);
     }
 
     /**
@@ -948,6 +965,7 @@
         }
         mPlayingBackwards = playBackwards;
         mCurrentIteration = 0;
+        int prevPlayingState = mPlayingState;
         mPlayingState = STOPPED;
         mStarted = true;
         mStartedDelay = false;
@@ -957,7 +975,9 @@
         animationHandler.mPendingAnimations.add(this);
         if (mStartDelay == 0) {
             // This sets the initial value of the animation, prior to actually starting it running
-            setCurrentPlayTime(0);
+            if (prevPlayingState != SEEKED) {
+                setCurrentPlayTime(0);
+            }
             mPlayingState = STOPPED;
             mRunning = true;
             notifyStartListeners();
@@ -1221,12 +1241,12 @@
     final boolean doAnimationFrame(long frameTime) {
         if (mPlayingState == STOPPED) {
             mPlayingState = RUNNING;
-            if (mSeekTime < 0) {
+            if (mSeekFraction < 0) {
                 mStartTime = frameTime;
             } else {
-                mStartTime = frameTime - mSeekTime;
-                // Now that we're playing, reset the seek time
-                mSeekTime = -1;
+                long seekTime = (long) (mDuration * mSeekFraction);
+                mStartTime = frameTime - seekTime;
+                mSeekFraction = -1;
             }
         }
         if (mPaused) {
@@ -1292,7 +1312,7 @@
         if (mUpdateListeners != null) {
             anim.mUpdateListeners = new ArrayList<AnimatorUpdateListener>(mUpdateListeners);
         }
-        anim.mSeekTime = -1;
+        anim.mSeekFraction = -1;
         anim.mPlayingBackwards = false;
         anim.mCurrentIteration = 0;
         anim.mInitialized = false;
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 148527f..fab88a2 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -5620,6 +5620,16 @@
     }
 
     /**
+     * @hide
+     */
+    public void dispatchEnterAnimationComplete() {
+        onEnterAnimationComplete();
+        if (getWindow() != null && getWindow().getDecorView() != null) {
+            getWindow().getDecorView().getViewTreeObserver().dispatchOnEnterAnimationComplete();
+        }
+    }
+
+    /**
      * Adjust the current immersive mode setting.
      *
      * Note that changing this value will have no effect on the activity's
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index c3028b7..6ec48e5 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1186,6 +1186,18 @@
             return true;
         }
 
+        case CHECK_PERMISSION_WITH_TOKEN_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            String perm = data.readString();
+            int pid = data.readInt();
+            int uid = data.readInt();
+            IBinder token = data.readStrongBinder();
+            int res = checkPermissionWithToken(perm, pid, uid, token);
+            reply.writeNoException();
+            reply.writeInt(res);
+            return true;
+        }
+
         case CHECK_URI_PERMISSION_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             Uri uri = Uri.CREATOR.createFromParcel(data);
@@ -1193,7 +1205,8 @@
             int uid = data.readInt();
             int mode = data.readInt();
             int userId = data.readInt();
-            int res = checkUriPermission(uri, pid, uid, mode, userId);
+            IBinder callerToken = data.readStrongBinder();
+            int res = checkUriPermission(uri, pid, uid, mode, userId, callerToken);
             reply.writeNoException();
             reply.writeInt(res);
             return true;
@@ -3742,7 +3755,7 @@
         mRemote.transact(GET_INTENT_SENDER_TRANSACTION, data, reply, 0);
         reply.readException();
         IIntentSender res = IIntentSender.Stub.asInterface(
-            reply.readStrongBinder());
+                reply.readStrongBinder());
         data.recycle();
         reply.recycle();
         return res;
@@ -3851,6 +3864,22 @@
         reply.recycle();
         return res;
     }
+    public int checkPermissionWithToken(String permission, int pid, int uid, IBinder callerToken)
+            throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeString(permission);
+        data.writeInt(pid);
+        data.writeInt(uid);
+        data.writeStrongBinder(callerToken);
+        mRemote.transact(CHECK_PERMISSION_WITH_TOKEN_TRANSACTION, data, reply, 0);
+        reply.readException();
+        int res = reply.readInt();
+        data.recycle();
+        reply.recycle();
+        return res;
+    }
     public boolean clearApplicationUserData(final String packageName,
             final IPackageDataObserver observer, final int userId) throws RemoteException {
         Parcel data = Parcel.obtain();
@@ -3866,8 +3895,8 @@
         reply.recycle();
         return res;
     }
-    public int checkUriPermission(Uri uri, int pid, int uid, int mode, int userId)
-            throws RemoteException {
+    public int checkUriPermission(Uri uri, int pid, int uid, int mode, int userId,
+            IBinder callerToken) throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
@@ -3876,6 +3905,7 @@
         data.writeInt(uid);
         data.writeInt(mode);
         data.writeInt(userId);
+        data.writeStrongBinder(callerToken);
         mRemote.transact(CHECK_URI_PERMISSION_TRANSACTION, data, reply, 0);
         reply.readException();
         int res = reply.readInt();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index e3de44e..b64e724 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -84,6 +84,8 @@
 import android.util.SuperNotCalledException;
 import android.view.Display;
 import android.view.HardwareRenderer;
+import android.view.IWindowManager;
+import android.view.IWindowSessionCallback;
 import android.view.View;
 import android.view.ViewDebug;
 import android.view.ViewManager;
@@ -2366,6 +2368,9 @@
         if (localLOGV) Slog.v(
             TAG, "Handling launch of " + r);
 
+        // Initialize before creating the activity
+        WindowManagerGlobal.initialize();
+
         Activity a = performLaunchActivity(r, customIntent);
 
         if (a != null) {
@@ -2531,7 +2536,7 @@
     private void handleEnterAnimationComplete(IBinder token) {
         ActivityClientRecord r = mActivities.get(token);
         if (r != null) {
-            r.activity.onEnterAnimationComplete();
+            r.activity.dispatchEnterAnimationComplete();
         }
     }
 
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 7fafc38..1de9b47 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1863,6 +1863,21 @@
         }
     }
 
+    /** @hide */
+    @Override
+    public int checkPermission(String permission, int pid, int uid, IBinder callerToken) {
+        if (permission == null) {
+            throw new IllegalArgumentException("permission is null");
+        }
+
+        try {
+            return ActivityManagerNative.getDefault().checkPermissionWithToken(
+                    permission, pid, uid, callerToken);
+        } catch (RemoteException e) {
+            return PackageManager.PERMISSION_DENIED;
+        }
+    }
+
     @Override
     public int checkCallingPermission(String permission) {
         if (permission == null) {
@@ -1951,7 +1966,19 @@
         try {
             return ActivityManagerNative.getDefault().checkUriPermission(
                     ContentProvider.getUriWithoutUserId(uri), pid, uid, modeFlags,
-                    resolveUserId(uri));
+                    resolveUserId(uri), null);
+        } catch (RemoteException e) {
+            return PackageManager.PERMISSION_DENIED;
+        }
+    }
+
+    /** @hide */
+    @Override
+    public int checkUriPermission(Uri uri, int pid, int uid, int modeFlags, IBinder callerToken) {
+        try {
+            return ActivityManagerNative.getDefault().checkUriPermission(
+                    ContentProvider.getUriWithoutUserId(uri), pid, uid, modeFlags,
+                    resolveUserId(uri), callerToken);
         } catch (RemoteException e) {
             return PackageManager.PERMISSION_DENIED;
         }
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 6433f3f..5362303 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -219,9 +219,11 @@
 
     public int checkPermission(String permission, int pid, int uid)
             throws RemoteException;
-
-    public int checkUriPermission(Uri uri, int pid, int uid, int mode, int userId)
+    public int checkPermissionWithToken(String permission, int pid, int uid, IBinder callerToken)
             throws RemoteException;
+
+    public int checkUriPermission(Uri uri, int pid, int uid, int mode, int userId,
+            IBinder callerToken) throws RemoteException;
     public void grantUriPermission(IApplicationThread caller, String targetPkg, Uri uri,
             int mode, int userId) throws RemoteException;
     public void revokeUriPermission(IApplicationThread caller, Uri uri, int mode, int userId)
@@ -785,4 +787,5 @@
     int GET_TASK_DESCRIPTION_ICON_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+238;
     int LAUNCH_ASSIST_INTENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+239;
     int START_IN_PLACE_ANIMATION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+240;
+    int CHECK_PERMISSION_WITH_TOKEN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+241;
 }
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index 5038df9..ddd21e6 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -225,28 +225,28 @@
     }
 
     /**
-     * Return whether unlocking the device is currently not requiring a password
-     * because of a trust agent.
+     * Returns whether the device is currently locked and requires a PIN, pattern or
+     * password to unlock.
      *
-     * @return true if the keyguard can currently be unlocked without entering credentials
-     *         because the device is in a trusted environment.
+     * @return true if unlocking the device currently requires a PIN, pattern or
+     * password.
      */
-    public boolean isKeyguardInTrustedState() {
-        return isKeyguardInTrustedState(UserHandle.getCallingUserId());
+    public boolean isDeviceLocked() {
+        return isDeviceLocked(UserHandle.getCallingUserId());
     }
 
     /**
-     * Return whether unlocking the device is currently not requiring a password
-     * because of a trust agent.
+     * Returns whether the device is currently locked and requires a PIN, pattern or
+     * password to unlock.
      *
-     * @param userId the user for which the trusted state should be reported.
-     * @return true if the keyguard can currently be unlocked without entering credentials
-     *         because the device is in a trusted environment.
+     * @param userId the user for which the locked state should be reported.
+     * @return true if unlocking the device currently requires a PIN, pattern or
+     * password.
      * @hide
      */
-    public boolean isKeyguardInTrustedState(int userId) {
+    public boolean isDeviceLocked(int userId) {
         try {
-            return mTrustManager.isTrusted(userId);
+            return mTrustManager.isDeviceLocked(userId);
         } catch (RemoteException e) {
             return false;
         }
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index aa98e97..ece2a33 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -632,55 +632,60 @@
     public void removeContextRegistrations(Context context,
             String who, String what) {
         final boolean reportRegistrationLeaks = StrictMode.vmRegistrationLeaksEnabled();
-        ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> rmap =
-                mReceivers.remove(context);
-        if (rmap != null) {
-            for (int i=0; i<rmap.size(); i++) {
-                LoadedApk.ReceiverDispatcher rd = rmap.valueAt(i);
-                IntentReceiverLeaked leak = new IntentReceiverLeaked(
-                        what + " " + who + " has leaked IntentReceiver "
-                        + rd.getIntentReceiver() + " that was " +
-                        "originally registered here. Are you missing a " +
-                        "call to unregisterReceiver()?");
-                leak.setStackTrace(rd.getLocation().getStackTrace());
-                Slog.e(ActivityThread.TAG, leak.getMessage(), leak);
-                if (reportRegistrationLeaks) {
-                    StrictMode.onIntentReceiverLeaked(leak);
-                }
-                try {
-                    ActivityManagerNative.getDefault().unregisterReceiver(
-                            rd.getIIntentReceiver());
-                } catch (RemoteException e) {
-                    // system crashed, nothing we can do
+        synchronized (mReceivers) {
+            ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> rmap =
+                    mReceivers.remove(context);
+            if (rmap != null) {
+                for (int i = 0; i < rmap.size(); i++) {
+                    LoadedApk.ReceiverDispatcher rd = rmap.valueAt(i);
+                    IntentReceiverLeaked leak = new IntentReceiverLeaked(
+                            what + " " + who + " has leaked IntentReceiver "
+                            + rd.getIntentReceiver() + " that was " +
+                            "originally registered here. Are you missing a " +
+                            "call to unregisterReceiver()?");
+                    leak.setStackTrace(rd.getLocation().getStackTrace());
+                    Slog.e(ActivityThread.TAG, leak.getMessage(), leak);
+                    if (reportRegistrationLeaks) {
+                        StrictMode.onIntentReceiverLeaked(leak);
+                    }
+                    try {
+                        ActivityManagerNative.getDefault().unregisterReceiver(
+                                rd.getIIntentReceiver());
+                    } catch (RemoteException e) {
+                        // system crashed, nothing we can do
+                    }
                 }
             }
+            mUnregisteredReceivers.remove(context);
         }
-        mUnregisteredReceivers.remove(context);
-        //Slog.i(TAG, "Receiver registrations: " + mReceivers);
-        ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> smap =
-            mServices.remove(context);
-        if (smap != null) {
-            for (int i=0; i<smap.size(); i++) {
-                LoadedApk.ServiceDispatcher sd = smap.valueAt(i);
-                ServiceConnectionLeaked leak = new ServiceConnectionLeaked(
-                        what + " " + who + " has leaked ServiceConnection "
-                        + sd.getServiceConnection() + " that was originally bound here");
-                leak.setStackTrace(sd.getLocation().getStackTrace());
-                Slog.e(ActivityThread.TAG, leak.getMessage(), leak);
-                if (reportRegistrationLeaks) {
-                    StrictMode.onServiceConnectionLeaked(leak);
+
+        synchronized (mServices) {
+            //Slog.i(TAG, "Receiver registrations: " + mReceivers);
+            ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> smap =
+                    mServices.remove(context);
+            if (smap != null) {
+                for (int i = 0; i < smap.size(); i++) {
+                    LoadedApk.ServiceDispatcher sd = smap.valueAt(i);
+                    ServiceConnectionLeaked leak = new ServiceConnectionLeaked(
+                            what + " " + who + " has leaked ServiceConnection "
+                            + sd.getServiceConnection() + " that was originally bound here");
+                    leak.setStackTrace(sd.getLocation().getStackTrace());
+                    Slog.e(ActivityThread.TAG, leak.getMessage(), leak);
+                    if (reportRegistrationLeaks) {
+                        StrictMode.onServiceConnectionLeaked(leak);
+                    }
+                    try {
+                        ActivityManagerNative.getDefault().unbindService(
+                                sd.getIServiceConnection());
+                    } catch (RemoteException e) {
+                        // system crashed, nothing we can do
+                    }
+                    sd.doForget();
                 }
-                try {
-                    ActivityManagerNative.getDefault().unbindService(
-                            sd.getIServiceConnection());
-                } catch (RemoteException e) {
-                    // system crashed, nothing we can do
-                }
-                sd.doForget();
             }
+            mUnboundServices.remove(context);
+            //Slog.i(TAG, "Service registrations: " + mServices);
         }
-        mUnboundServices.remove(context);
-        //Slog.i(TAG, "Service registrations: " + mServices);
     }
 
     public IIntentReceiver getReceiverDispatcher(BroadcastReceiver r,
diff --git a/core/java/android/app/backup/IBackupManager.aidl b/core/java/android/app/backup/IBackupManager.aidl
index 0a2d4f5..41ad936 100644
--- a/core/java/android/app/backup/IBackupManager.aidl
+++ b/core/java/android/app/backup/IBackupManager.aidl
@@ -303,4 +303,11 @@
      *     {@code false} otherwise.
      */
     void setBackupServiceActive(int whichUser, boolean makeActive);
+
+    /**
+     * Queries the activity status of backup service as set by {@link #setBackupServiceActive}.
+     * @param whichUser User handle of the defined user whose backup active state
+     *     is being queried.
+     */
+    boolean isBackupServiceActive(int whichUser);
 }
diff --git a/core/java/android/app/backup/RecentsBackupHelper.java b/core/java/android/app/backup/RecentsBackupHelper.java
new file mode 100644
index 0000000..fd69d20
--- /dev/null
+++ b/core/java/android/app/backup/RecentsBackupHelper.java
@@ -0,0 +1,114 @@
+package android.app.backup;
+
+import android.content.Context;
+import android.os.Environment;
+import android.os.ParcelFileDescriptor;
+import android.util.Slog;
+
+import java.io.File;
+
+/**
+ * Helper for saving/restoring 'recent tasks' infrastructure.
+ * @hide
+ */
+public class RecentsBackupHelper implements BackupHelper {
+    private static final String TAG = "RecentsBackup";
+    private static final boolean DEBUG = false;
+
+    // This must match TaskPersister.TASKS_DIRNAME, but that class is not accessible from here
+    private static final String RECENTS_TASK_DIR = "recent_tasks";
+
+    // Must match TaskPersister.IMAGES_DIRNAME, as above
+    private static final String RECENTS_IMAGE_DIR = "recent_images";
+
+    // At restore time, tasks/thumbnails are placed in these directories alongside
+    // the "live" recents dirs named above.
+    private static final String RECENTS_TASK_RESTORE_DIR = "restored_" + RECENTS_TASK_DIR;
+    private static final String RECENTS_IMAGE_RESTORE_DIR = "restored_" + RECENTS_IMAGE_DIR;
+
+    // Prefixes for tagging the two kinds of recents backup records that we might generate
+    private static final String RECENTS_TASK_KEY = "task:";
+    private static final String RECENTS_IMAGE_KEY = "image:";
+
+    FileBackupHelperBase mTaskFileHelper;
+
+    final File mSystemDir;
+    final File mTasksDir;
+    final File mRestoredTasksDir;
+    final File mRestoredImagesDir;
+    final String[] mRecentFiles;
+    final String[] mRecentKeys;
+
+    /**
+     * @param context The agent context in which this helper instance will run
+     */
+    public RecentsBackupHelper(Context context) {
+        mTaskFileHelper = new FileBackupHelperBase(context);
+
+        mSystemDir = new File(Environment.getDataDirectory(), "system");
+        mTasksDir = new File(mSystemDir, RECENTS_TASK_DIR);
+        mRestoredTasksDir = new File(mSystemDir, RECENTS_TASK_RESTORE_DIR);
+        mRestoredImagesDir = new File(mSystemDir, RECENTS_IMAGE_RESTORE_DIR);
+
+        // Currently we back up only the recent-task descriptions, not the thumbnails
+        File[] recentFiles = mTasksDir.listFiles();
+        if (recentFiles != null) {
+            // We explicitly proceed even if this is a zero-size array
+            final int N = recentFiles.length;
+            mRecentKeys = new String[N];
+            mRecentFiles = new String[N];
+            if (DEBUG) {
+                Slog.i(TAG, "Identifying recents for backup: " + N);
+            }
+            for (int i = 0; i < N; i++) {
+                mRecentKeys[i] = new String(RECENTS_TASK_KEY + recentFiles[i].getName());
+                mRecentFiles[i] = recentFiles[i].getAbsolutePath();
+                if (DEBUG) {
+                    Slog.i(TAG, "   " + mRecentKeys[i]);
+                }
+            }
+        } else {
+            mRecentFiles = mRecentKeys = new String[0];
+        }
+    }
+
+    /**
+     * Task-file key:  RECENTS_TASK_KEY + leaf filename
+     * Thumbnail-file key: RECENTS_IMAGE_KEY + leaf filename
+     */
+    @Override
+    public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
+            ParcelFileDescriptor newState) {
+        FileBackupHelperBase.performBackup_checked(oldState, data, newState,
+                mRecentFiles, mRecentKeys);
+    }
+
+    @Override
+    public void restoreEntity(BackupDataInputStream data) {
+        final String key = data.getKey();
+        File output = null;
+        if (key.startsWith(RECENTS_TASK_KEY)) {
+            String name = key.substring(RECENTS_TASK_KEY.length());
+            output = new File(mRestoredTasksDir, name);
+            mRestoredTasksDir.mkdirs();
+        } else if (key.startsWith(RECENTS_IMAGE_KEY)) {
+            String name = key.substring(RECENTS_IMAGE_KEY.length());
+            output = new File(mRestoredImagesDir, name);
+            mRestoredImagesDir.mkdirs();
+        }
+
+        if (output != null) {
+            if (DEBUG) {
+                Slog.i(TAG, "Restoring key='"
+                        + key + "' to " + output.getAbsolutePath());
+            }
+            mTaskFileHelper.writeFile(output, data);
+        }
+    }
+
+    @Override
+    public void writeNewStateDescription(ParcelFileDescriptor newState) {
+        mTaskFileHelper.writeNewStateDescription(newState);
+    }
+
+}
diff --git a/core/java/android/app/trust/ITrustManager.aidl b/core/java/android/app/trust/ITrustManager.aidl
index 0193711..68ea0aa 100644
--- a/core/java/android/app/trust/ITrustManager.aidl
+++ b/core/java/android/app/trust/ITrustManager.aidl
@@ -29,5 +29,6 @@
     void reportRequireCredentialEntry(int userId);
     void registerTrustListener(in ITrustListener trustListener);
     void unregisterTrustListener(in ITrustListener trustListener);
-    boolean isTrusted(int userId);
+    void reportKeyguardShowingChanged();
+    boolean isDeviceLocked(int userId);
 }
diff --git a/core/java/android/app/trust/TrustManager.java b/core/java/android/app/trust/TrustManager.java
index 3d262b1..705a144 100644
--- a/core/java/android/app/trust/TrustManager.java
+++ b/core/java/android/app/trust/TrustManager.java
@@ -88,6 +88,19 @@
     }
 
     /**
+     * Reports that the visibility of the keyguard has changed.
+     *
+     * Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission.
+     */
+    public void reportKeyguardShowingChanged() {
+        try {
+            mService.reportKeyguardShowingChanged();
+        } catch (RemoteException e) {
+            onError(e);
+        }
+    }
+
+    /**
      * Registers a listener for trust events.
      *
      * Requires the {@link android.Manifest.permission#TRUST_LISTENER} permission.
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 4c82efd..360f308 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -31,6 +31,7 @@
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.CancellationSignal;
+import android.os.IBinder;
 import android.os.ICancellationSignal;
 import android.os.OperationCanceledException;
 import android.os.ParcelFileDescriptor;
@@ -201,7 +202,7 @@
                 ICancellationSignal cancellationSignal) {
             validateIncomingUri(uri);
             uri = getUriWithoutUserId(uri);
-            if (enforceReadPermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) {
+            if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
                 return rejectQuery(uri, projection, selection, selectionArgs, sortOrder,
                         CancellationSignal.fromTransport(cancellationSignal));
             }
@@ -227,7 +228,7 @@
             validateIncomingUri(uri);
             int userId = getUserIdFromUri(uri);
             uri = getUriWithoutUserId(uri);
-            if (enforceWritePermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) {
+            if (enforceWritePermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
                 return rejectInsert(uri, initialValues);
             }
             final String original = setCallingPackage(callingPkg);
@@ -242,7 +243,7 @@
         public int bulkInsert(String callingPkg, Uri uri, ContentValues[] initialValues) {
             validateIncomingUri(uri);
             uri = getUriWithoutUserId(uri);
-            if (enforceWritePermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) {
+            if (enforceWritePermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
                 return 0;
             }
             final String original = setCallingPackage(callingPkg);
@@ -270,13 +271,13 @@
                     operations.set(i, operation);
                 }
                 if (operation.isReadOperation()) {
-                    if (enforceReadPermission(callingPkg, uri)
+                    if (enforceReadPermission(callingPkg, uri, null)
                             != AppOpsManager.MODE_ALLOWED) {
                         throw new OperationApplicationException("App op not allowed", 0);
                     }
                 }
                 if (operation.isWriteOperation()) {
-                    if (enforceWritePermission(callingPkg, uri)
+                    if (enforceWritePermission(callingPkg, uri, null)
                             != AppOpsManager.MODE_ALLOWED) {
                         throw new OperationApplicationException("App op not allowed", 0);
                     }
@@ -301,7 +302,7 @@
         public int delete(String callingPkg, Uri uri, String selection, String[] selectionArgs) {
             validateIncomingUri(uri);
             uri = getUriWithoutUserId(uri);
-            if (enforceWritePermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) {
+            if (enforceWritePermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
                 return 0;
             }
             final String original = setCallingPackage(callingPkg);
@@ -317,7 +318,7 @@
                 String[] selectionArgs) {
             validateIncomingUri(uri);
             uri = getUriWithoutUserId(uri);
-            if (enforceWritePermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) {
+            if (enforceWritePermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
                 return 0;
             }
             final String original = setCallingPackage(callingPkg);
@@ -330,11 +331,11 @@
 
         @Override
         public ParcelFileDescriptor openFile(
-                String callingPkg, Uri uri, String mode, ICancellationSignal cancellationSignal)
-                throws FileNotFoundException {
+                String callingPkg, Uri uri, String mode, ICancellationSignal cancellationSignal,
+                IBinder callerToken) throws FileNotFoundException {
             validateIncomingUri(uri);
             uri = getUriWithoutUserId(uri);
-            enforceFilePermission(callingPkg, uri, mode);
+            enforceFilePermission(callingPkg, uri, mode, callerToken);
             final String original = setCallingPackage(callingPkg);
             try {
                 return ContentProvider.this.openFile(
@@ -350,7 +351,7 @@
                 throws FileNotFoundException {
             validateIncomingUri(uri);
             uri = getUriWithoutUserId(uri);
-            enforceFilePermission(callingPkg, uri, mode);
+            enforceFilePermission(callingPkg, uri, mode, null);
             final String original = setCallingPackage(callingPkg);
             try {
                 return ContentProvider.this.openAssetFile(
@@ -382,7 +383,7 @@
                 Bundle opts, ICancellationSignal cancellationSignal) throws FileNotFoundException {
             validateIncomingUri(uri);
             uri = getUriWithoutUserId(uri);
-            enforceFilePermission(callingPkg, uri, "r");
+            enforceFilePermission(callingPkg, uri, "r", null);
             final String original = setCallingPackage(callingPkg);
             try {
                 return ContentProvider.this.openTypedAssetFile(
@@ -402,7 +403,7 @@
             validateIncomingUri(uri);
             int userId = getUserIdFromUri(uri);
             uri = getUriWithoutUserId(uri);
-            if (enforceReadPermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) {
+            if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
                 return null;
             }
             final String original = setCallingPackage(callingPkg);
@@ -418,7 +419,7 @@
             validateIncomingUri(uri);
             int userId = getUserIdFromUri(uri);
             uri = getUriWithoutUserId(uri);
-            if (enforceReadPermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) {
+            if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
                 return null;
             }
             final String original = setCallingPackage(callingPkg);
@@ -429,29 +430,33 @@
             }
         }
 
-        private void enforceFilePermission(String callingPkg, Uri uri, String mode)
-                throws FileNotFoundException, SecurityException {
+        private void enforceFilePermission(String callingPkg, Uri uri, String mode,
+                IBinder callerToken) throws FileNotFoundException, SecurityException {
             if (mode != null && mode.indexOf('w') != -1) {
-                if (enforceWritePermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) {
+                if (enforceWritePermission(callingPkg, uri, callerToken)
+                        != AppOpsManager.MODE_ALLOWED) {
                     throw new FileNotFoundException("App op not allowed");
                 }
             } else {
-                if (enforceReadPermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) {
+                if (enforceReadPermission(callingPkg, uri, callerToken)
+                        != AppOpsManager.MODE_ALLOWED) {
                     throw new FileNotFoundException("App op not allowed");
                 }
             }
         }
 
-        private int enforceReadPermission(String callingPkg, Uri uri) throws SecurityException {
-            enforceReadPermissionInner(uri);
+        private int enforceReadPermission(String callingPkg, Uri uri, IBinder callerToken)
+                throws SecurityException {
+            enforceReadPermissionInner(uri, callerToken);
             if (mReadOp != AppOpsManager.OP_NONE) {
                 return mAppOpsManager.noteOp(mReadOp, Binder.getCallingUid(), callingPkg);
             }
             return AppOpsManager.MODE_ALLOWED;
         }
 
-        private int enforceWritePermission(String callingPkg, Uri uri) throws SecurityException {
-            enforceWritePermissionInner(uri);
+        private int enforceWritePermission(String callingPkg, Uri uri, IBinder callerToken)
+                throws SecurityException {
+            enforceWritePermissionInner(uri, callerToken);
             if (mWriteOp != AppOpsManager.OP_NONE) {
                 return mAppOpsManager.noteOp(mWriteOp, Binder.getCallingUid(), callingPkg);
             }
@@ -467,7 +472,8 @@
     }
 
     /** {@hide} */
-    protected void enforceReadPermissionInner(Uri uri) throws SecurityException {
+    protected void enforceReadPermissionInner(Uri uri, IBinder callerToken)
+            throws SecurityException {
         final Context context = getContext();
         final int pid = Binder.getCallingPid();
         final int uid = Binder.getCallingUid();
@@ -480,7 +486,8 @@
         if (mExported && checkUser(pid, uid, context)) {
             final String componentPerm = getReadPermission();
             if (componentPerm != null) {
-                if (context.checkPermission(componentPerm, pid, uid) == PERMISSION_GRANTED) {
+                if (context.checkPermission(componentPerm, pid, uid, callerToken)
+                        == PERMISSION_GRANTED) {
                     return;
                 } else {
                     missingPerm = componentPerm;
@@ -497,7 +504,8 @@
                 for (PathPermission pp : pps) {
                     final String pathPerm = pp.getReadPermission();
                     if (pathPerm != null && pp.match(path)) {
-                        if (context.checkPermission(pathPerm, pid, uid) == PERMISSION_GRANTED) {
+                        if (context.checkPermission(pathPerm, pid, uid, callerToken)
+                                == PERMISSION_GRANTED) {
                             return;
                         } else {
                             // any denied <path-permission> means we lose
@@ -518,8 +526,8 @@
         final int callingUserId = UserHandle.getUserId(uid);
         final Uri userUri = (mSingleUser && !UserHandle.isSameUser(mMyUid, uid))
                 ? maybeAddUserId(uri, callingUserId) : uri;
-        if (context.checkUriPermission(userUri, pid, uid, Intent.FLAG_GRANT_READ_URI_PERMISSION)
-                == PERMISSION_GRANTED) {
+        if (context.checkUriPermission(userUri, pid, uid, Intent.FLAG_GRANT_READ_URI_PERMISSION,
+                callerToken) == PERMISSION_GRANTED) {
             return;
         }
 
@@ -532,7 +540,8 @@
     }
 
     /** {@hide} */
-    protected void enforceWritePermissionInner(Uri uri) throws SecurityException {
+    protected void enforceWritePermissionInner(Uri uri, IBinder callerToken)
+            throws SecurityException {
         final Context context = getContext();
         final int pid = Binder.getCallingPid();
         final int uid = Binder.getCallingUid();
@@ -545,7 +554,8 @@
         if (mExported && checkUser(pid, uid, context)) {
             final String componentPerm = getWritePermission();
             if (componentPerm != null) {
-                if (context.checkPermission(componentPerm, pid, uid) == PERMISSION_GRANTED) {
+                if (context.checkPermission(componentPerm, pid, uid, callerToken)
+                        == PERMISSION_GRANTED) {
                     return;
                 } else {
                     missingPerm = componentPerm;
@@ -562,7 +572,8 @@
                 for (PathPermission pp : pps) {
                     final String pathPerm = pp.getWritePermission();
                     if (pathPerm != null && pp.match(path)) {
-                        if (context.checkPermission(pathPerm, pid, uid) == PERMISSION_GRANTED) {
+                        if (context.checkPermission(pathPerm, pid, uid, callerToken)
+                                == PERMISSION_GRANTED) {
                             return;
                         } else {
                             // any denied <path-permission> means we lose
@@ -580,8 +591,8 @@
         }
 
         // last chance, check against any uri grants
-        if (context.checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
-                == PERMISSION_GRANTED) {
+        if (context.checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
+                callerToken) == PERMISSION_GRANTED) {
             return;
         }
 
diff --git a/core/java/android/content/ContentProviderClient.java b/core/java/android/content/ContentProviderClient.java
index cefc27f..e15ac94 100644
--- a/core/java/android/content/ContentProviderClient.java
+++ b/core/java/android/content/ContentProviderClient.java
@@ -288,7 +288,7 @@
                 remoteSignal = mContentProvider.createCancellationSignal();
                 signal.setRemote(remoteSignal);
             }
-            return mContentProvider.openFile(mPackageName, url, mode, remoteSignal);
+            return mContentProvider.openFile(mPackageName, url, mode, remoteSignal, null);
         } catch (DeadObjectException e) {
             if (!mStable) {
                 mContentResolver.unstableProviderDied(mContentProvider);
diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java
index 39286d6..f2e7fc4 100644
--- a/core/java/android/content/ContentProviderNative.java
+++ b/core/java/android/content/ContentProviderNative.java
@@ -234,9 +234,10 @@
                     String mode = data.readString();
                     ICancellationSignal signal = ICancellationSignal.Stub.asInterface(
                             data.readStrongBinder());
+                    IBinder callerToken = data.readStrongBinder();
 
                     ParcelFileDescriptor fd;
-                    fd = openFile(callingPkg, url, mode, signal);
+                    fd = openFile(callingPkg, url, mode, signal, callerToken);
                     reply.writeNoException();
                     if (fd != null) {
                         reply.writeInt(1);
@@ -575,7 +576,7 @@
 
     @Override
     public ParcelFileDescriptor openFile(
-            String callingPkg, Uri url, String mode, ICancellationSignal signal)
+            String callingPkg, Uri url, String mode, ICancellationSignal signal, IBinder token)
             throws RemoteException, FileNotFoundException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
@@ -586,6 +587,7 @@
             url.writeToParcel(data, 0);
             data.writeString(mode);
             data.writeStrongBinder(signal != null ? signal.asBinder() : null);
+            data.writeStrongBinder(token);
 
             mRemote.transact(IContentProvider.OPEN_FILE_TRANSACTION, data, reply, 0);
 
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index c9b7d0a..a73ba74 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -37,6 +37,7 @@
 import android.os.Bundle;
 import android.os.Environment;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.Looper;
 import android.os.StatFs;
 import android.os.UserHandle;
@@ -2864,10 +2865,10 @@
 
     /**
      * Use with {@link #getSystemService} to retrieve a {@link
-     * android.app.UsageStatsManager} for interacting with the status bar.
+     * android.app.usage.UsageStatsManager} for interacting with the status bar.
      *
      * @see #getSystemService
-     * @see android.app.UsageStatsManager
+     * @see android.app.usage.UsageStatsManager
      * @hide
      */
     public static final String USAGE_STATS_SERVICE = "usagestats";
@@ -2921,6 +2922,11 @@
     @PackageManager.PermissionResult
     public abstract int checkPermission(@NonNull String permission, int pid, int uid);
 
+    /** @hide */
+    @PackageManager.PermissionResult
+    public abstract int checkPermission(@NonNull String permission, int pid, int uid,
+            IBinder callerToken);
+
     /**
      * Determine whether the calling process of an IPC you are handling has been
      * granted a particular permission.  This is basically the same as calling
@@ -3108,6 +3114,10 @@
     public abstract int checkUriPermission(Uri uri, int pid, int uid,
             @Intent.AccessUriMode int modeFlags);
 
+    /** @hide */
+    public abstract int checkUriPermission(Uri uri, int pid, int uid,
+            @Intent.AccessUriMode int modeFlags, IBinder callerToken);
+
     /**
      * Determine whether the calling process and user ID has been
      * granted permission to access a specific URI.  This is basically
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index ad7c350..cfae1cf 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -29,6 +29,7 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.Looper;
 import android.os.UserHandle;
 import android.view.DisplayAdjustments;
@@ -566,6 +567,12 @@
         return mBase.checkPermission(permission, pid, uid);
     }
 
+    /** @hide */
+    @Override
+    public int checkPermission(String permission, int pid, int uid, IBinder callerToken) {
+        return mBase.checkPermission(permission, pid, uid, callerToken);
+    }
+
     @Override
     public int checkCallingPermission(String permission) {
         return mBase.checkCallingPermission(permission);
@@ -608,6 +615,12 @@
         return mBase.checkUriPermission(uri, pid, uid, modeFlags);
     }
 
+    /** @hide */
+    @Override
+    public int checkUriPermission(Uri uri, int pid, int uid, int modeFlags, IBinder callerToken) {
+        return mBase.checkUriPermission(uri, pid, uid, modeFlags, callerToken);
+    }
+
     @Override
     public int checkCallingUriPermission(Uri uri, int modeFlags) {
         return mBase.checkCallingUriPermission(uri, modeFlags);
diff --git a/core/java/android/content/IContentProvider.java b/core/java/android/content/IContentProvider.java
index f92a404..f858406 100644
--- a/core/java/android/content/IContentProvider.java
+++ b/core/java/android/content/IContentProvider.java
@@ -47,7 +47,8 @@
     public int update(String callingPkg, Uri url, ContentValues values, String selection,
             String[] selectionArgs) throws RemoteException;
     public ParcelFileDescriptor openFile(
-            String callingPkg, Uri url, String mode, ICancellationSignal signal)
+            String callingPkg, Uri url, String mode, ICancellationSignal signal,
+            IBinder callerToken)
             throws RemoteException, FileNotFoundException;
     public AssetFileDescriptor openAssetFile(
             String callingPkg, Uri url, String mode, ICancellationSignal signal)
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 57f6028..de7fbab 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3763,7 +3763,7 @@
      * This flag is used to open a document into a new task rooted at the activity launched
      * by this Intent. Through the use of this flag, or its equivalent attribute,
      * {@link android.R.attr#documentLaunchMode} multiple instances of the same activity
-     * containing different douments will appear in the recent tasks list.
+     * containing different documents will appear in the recent tasks list.
      *
      * <p>The use of the activity attribute form of this,
      * {@link android.R.attr#documentLaunchMode}, is
@@ -4010,6 +4010,20 @@
      */
     public static final int URI_ANDROID_APP_SCHEME = 1<<1;
 
+    /**
+     * Flag for use with {@link #toUri} and {@link #parseUri}: allow parsing
+     * of unsafe information.  In particular, the flags {@link #FLAG_GRANT_READ_URI_PERMISSION},
+     * {@link #FLAG_GRANT_WRITE_URI_PERMISSION}, {@link #FLAG_GRANT_PERSISTABLE_URI_PERMISSION},
+     * and {@link #FLAG_GRANT_PREFIX_URI_PERMISSION} flags can not be set, so that the
+     * generated Intent can not cause unexpected data access to happen.
+     *
+     * <p>If you do not trust the source of the URI being parsed, you should still do further
+     * processing to protect yourself from it.  In particular, when using it to start an
+     * activity you should usually add in {@link #CATEGORY_BROWSABLE} to limit the activities
+     * that can handle it.</p>
+     */
+    public static final int URI_ALLOW_UNSAFE = 1<<2;
+
     // ---------------------------------------------------------------------
 
     private String mAction;
@@ -4309,7 +4323,7 @@
             // old format Intent URI
             } else if (!uri.startsWith("#Intent;", i)) {
                 if (!androidApp) {
-                    return getIntentOld(uri);
+                    return getIntentOld(uri, flags);
                 } else {
                     i = -1;
                 }
@@ -4359,6 +4373,9 @@
                 // launch flags
                 else if (uri.startsWith("launchFlags=", i)) {
                     intent.mFlags = Integer.decode(value).intValue();
+                    if ((flags& URI_ALLOW_UNSAFE) == 0) {
+                        intent.mFlags &= ~IMMUTABLE_FLAGS;
+                    }
                 }
 
                 // package
@@ -4488,6 +4505,10 @@
     }
 
     public static Intent getIntentOld(String uri) throws URISyntaxException {
+        return getIntentOld(uri, 0);
+    }
+
+    private static Intent getIntentOld(String uri, int flags) throws URISyntaxException {
         Intent intent;
 
         int i = uri.lastIndexOf('#');
@@ -4536,6 +4557,9 @@
                 i += 12;
                 int j = uri.indexOf(')', i);
                 intent.mFlags = Integer.decode(uri.substring(i, j)).intValue();
+                if ((flags& URI_ALLOW_UNSAFE) == 0) {
+                    intent.mFlags &= ~IMMUTABLE_FLAGS;
+                }
                 i = j + 1;
             }
 
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 271fc30..895ae04 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -490,12 +490,16 @@
      * <p>More specifically, this means that a size matching the
      * camera device's active array size is listed as a
      * supported size for the YUV_420_888 format in
-     * {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP android.scaler.streamConfigurationMap}, and the
-     * minimum frame duration for that format and size is &lt;=
-     * 1/20 s.</p>
+     * {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP android.scaler.streamConfigurationMap}, the minimum frame
+     * duration for that format and size is &lt;= 1/20 s, and
+     * the {@link CameraCharacteristics#CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES android.control.aeAvailableTargetFpsRanges} entry
+     * lists at least one FPS range where the minimum FPS
+     * is &gt;= 1 / minimumFrameDuration for the maximum-size
+     * YUV_420_888 format.</p>
      * <p>In addition, the {@link CameraCharacteristics#SYNC_MAX_LATENCY android.sync.maxLatency} field is
      * guaranted to have a value between 0 and 4, inclusive.</p>
      *
+     * @see CameraCharacteristics#CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES
      * @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP
      * @see CameraCharacteristics#SYNC_MAX_LATENCY
      * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 1c9f4c68..ec24ded 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -2381,15 +2381,14 @@
      * successfully finding a network for the applications request.  Retrieve it with
      * {@link android.content.Intent#getParcelableExtra(String)}.
      */
-    public static final String EXTRA_NETWORK_REQUEST_NETWORK = "networkRequestNetwork";
+    public static final String EXTRA_NETWORK = "android.net.extra.NETWORK";
 
     /**
      * The lookup key for a {@link NetworkRequest} object included with the intent after
      * successfully finding a network for the applications request.  Retrieve it with
      * {@link android.content.Intent#getParcelableExtra(String)}.
      */
-    public static final String EXTRA_NETWORK_REQUEST_NETWORK_REQUEST =
-            "networkRequestNetworkRequest";
+    public static final String EXTRA_NETWORK_REQUEST = "android.net.extra.NETWORK_REQUEST";
 
 
     /**
@@ -2405,8 +2404,8 @@
      * &lt;receiver&gt; tag in an AndroidManifest.xml file
      * <p>
      * The operation Intent is delivered with two extras, a {@link Network} typed
-     * extra called {@link #EXTRA_NETWORK_REQUEST_NETWORK} and a {@link NetworkRequest}
-     * typed extra called {@link #EXTRA_NETWORK_REQUEST_NETWORK_REQUEST} containing
+     * extra called {@link #EXTRA_NETWORK} and a {@link NetworkRequest}
+     * typed extra called {@link #EXTRA_NETWORK_REQUEST} containing
      * the original requests parameters.  It is important to create a new,
      * {@link NetworkCallback} based request before completing the processing of the
      * Intent to reserve the network or it will be released shortly after the Intent
diff --git a/core/java/android/net/EthernetManager.java b/core/java/android/net/EthernetManager.java
index d965f27..f45737a 100644
--- a/core/java/android/net/EthernetManager.java
+++ b/core/java/android/net/EthernetManager.java
@@ -18,11 +18,14 @@
 
 import android.content.Context;
 import android.net.IEthernetManager;
+import android.net.IEthernetServiceListener;
 import android.net.IpConfiguration;
-import android.net.IpConfiguration.IpAssignment;
-import android.net.IpConfiguration.ProxySettings;
+import android.os.Handler;
+import android.os.Message;
 import android.os.RemoteException;
 
+import java.util.ArrayList;
+
 /**
  * A class representing the IP configuration of the Ethernet network.
  *
@@ -30,9 +33,41 @@
  */
 public class EthernetManager {
     private static final String TAG = "EthernetManager";
+    private static final int MSG_AVAILABILITY_CHANGED = 1000;
 
     private final Context mContext;
     private final IEthernetManager mService;
+    private final Handler mHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            if (msg.what == MSG_AVAILABILITY_CHANGED) {
+                boolean isAvailable = (msg.arg1 == 1);
+                for (Listener listener : mListeners) {
+                    listener.onAvailabilityChanged(isAvailable);
+                }
+            }
+        }
+    };
+    private final ArrayList<Listener> mListeners = new ArrayList<Listener>();
+    private final IEthernetServiceListener.Stub mServiceListener =
+            new IEthernetServiceListener.Stub() {
+                @Override
+                public void onAvailabilityChanged(boolean isAvailable) {
+                    mHandler.obtainMessage(
+                            MSG_AVAILABILITY_CHANGED, isAvailable ? 1 : 0, 0, null).sendToTarget();
+                }
+            };
+
+    /**
+     * A listener interface to receive notification on changes in Ethernet.
+     */
+    public interface Listener {
+        /**
+         * Called when Ethernet port's availability is changed.
+         * @param isAvailable {@code true} if one or more Ethernet port exists.
+         */
+        public void onAvailabilityChanged(boolean isAvailable);
+    }
 
     /**
      * Create a new EthernetManager instance.
@@ -50,12 +85,9 @@
      * @return the Ethernet Configuration, contained in {@link IpConfiguration}.
      */
     public IpConfiguration getConfiguration() {
-        if (mService == null) {
-            return new IpConfiguration();
-        }
         try {
             return mService.getConfiguration();
-        } catch (RemoteException e) {
+        } catch (NullPointerException | RemoteException e) {
             return new IpConfiguration();
         }
     }
@@ -64,12 +96,57 @@
      * Set Ethernet configuration.
      */
     public void setConfiguration(IpConfiguration config) {
-        if (mService == null) {
-            return;
-        }
         try {
             mService.setConfiguration(config);
-        } catch (RemoteException e) {
+        } catch (NullPointerException | RemoteException e) {
+        }
+    }
+
+    /**
+     * Indicates whether the system currently has one or more
+     * Ethernet interfaces.
+     */
+    public boolean isAvailable() {
+        try {
+            return mService.isAvailable();
+        } catch (NullPointerException | RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Adds a listener.
+     * @param listener A {@link Listener} to add.
+     * @throws IllegalArgumentException If the listener is null.
+     */
+    public void addListener(Listener listener) {
+        if (listener == null) {
+            throw new IllegalArgumentException("listener must not be null");
+        }
+        mListeners.add(listener);
+        if (mListeners.size() == 1) {
+            try {
+                mService.addListener(mServiceListener);
+            } catch (NullPointerException | RemoteException e) {
+            }
+        }
+    }
+
+    /**
+     * Removes a listener.
+     * @param listener A {@link Listener} to remove.
+     * @throws IllegalArgumentException If the listener is null.
+     */
+    public void removeListener(Listener listener) {
+        if (listener == null) {
+            throw new IllegalArgumentException("listener must not be null");
+        }
+        mListeners.remove(listener);
+        if (mListeners.isEmpty()) {
+            try {
+                mService.removeListener(mServiceListener);
+            } catch (NullPointerException | RemoteException e) {
+            }
         }
     }
 }
diff --git a/core/java/android/net/IEthernetManager.aidl b/core/java/android/net/IEthernetManager.aidl
index 3fa08f8..7a92eb9 100644
--- a/core/java/android/net/IEthernetManager.aidl
+++ b/core/java/android/net/IEthernetManager.aidl
@@ -17,6 +17,7 @@
 package android.net;
 
 import android.net.IpConfiguration;
+import android.net.IEthernetServiceListener;
 
 /**
  * Interface that answers queries about, and allows changing
@@ -27,4 +28,7 @@
 {
     IpConfiguration getConfiguration();
     void setConfiguration(in IpConfiguration config);
+    boolean isAvailable();
+    void addListener(in IEthernetServiceListener listener);
+    void removeListener(in IEthernetServiceListener listener);
 }
diff --git a/core/java/android/net/IEthernetServiceListener.aidl b/core/java/android/net/IEthernetServiceListener.aidl
new file mode 100644
index 0000000..356690e8
--- /dev/null
+++ b/core/java/android/net/IEthernetServiceListener.aidl
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+/** @hide */
+oneway interface IEthernetServiceListener
+{
+    void onAvailabilityChanged(boolean isAvailable);
+}
diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl
index 5b926ad..961a3f4 100644
--- a/core/java/android/nfc/INfcAdapter.aidl
+++ b/core/java/android/nfc/INfcAdapter.aidl
@@ -60,4 +60,6 @@
 
     void addNfcUnlockHandler(INfcUnlockHandler unlockHandler, in int[] techList);
     void removeNfcUnlockHandler(INfcUnlockHandler unlockHandler);
+
+    void verifyNfcPermission();
 }
diff --git a/core/java/android/nfc/NfcActivityManager.java b/core/java/android/nfc/NfcActivityManager.java
index dd9765d..d009295 100644
--- a/core/java/android/nfc/NfcActivityManager.java
+++ b/core/java/android/nfc/NfcActivityManager.java
@@ -254,7 +254,11 @@
             isResumed = state.resumed;
         }
         if (isResumed) {
+            // requestNfcServiceCallback() verifies permission also
             requestNfcServiceCallback();
+        } else {
+            // Crash API calls early in case NFC permission is missing
+            verifyNfcPermission();
         }
     }
 
@@ -268,7 +272,11 @@
             isResumed = state.resumed;
         }
         if (isResumed) {
+            // requestNfcServiceCallback() verifies permission also
             requestNfcServiceCallback();
+        } else {
+            // Crash API calls early in case NFC permission is missing
+            verifyNfcPermission();
         }
     }
 
@@ -281,7 +289,11 @@
             isResumed = state.resumed;
         }
         if (isResumed) {
+            // requestNfcServiceCallback() verifies permission also
             requestNfcServiceCallback();
+        } else {
+            // Crash API calls early in case NFC permission is missing
+            verifyNfcPermission();
         }
     }
 
@@ -295,7 +307,11 @@
             isResumed = state.resumed;
         }
         if (isResumed) {
+            // requestNfcServiceCallback() verifies permission also
             requestNfcServiceCallback();
+        } else {
+            // Crash API calls early in case NFC permission is missing
+            verifyNfcPermission();
         }
     }
 
@@ -308,7 +324,11 @@
             isResumed = state.resumed;
         }
         if (isResumed) {
+            // requestNfcServiceCallback() verifies permission also
             requestNfcServiceCallback();
+        } else {
+            // Crash API calls early in case NFC permission is missing
+            verifyNfcPermission();
         }
     }
 
@@ -324,6 +344,14 @@
         }
     }
 
+    void verifyNfcPermission() {
+        try {
+            NfcAdapter.sService.verifyNfcPermission();
+        } catch (RemoteException e) {
+            mAdapter.attemptDeadServiceRecovery(e);
+        }
+    }
+
     /** Callback from NFC service, usually on binder thread */
     @Override
     public BeamShareData createBeamShareData() {
diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java
index 270d786..4135e8b 100644
--- a/core/java/android/provider/DocumentsProvider.java
+++ b/core/java/android/provider/DocumentsProvider.java
@@ -637,7 +637,7 @@
         final Bundle out = new Bundle();
         try {
             if (METHOD_CREATE_DOCUMENT.equals(method)) {
-                enforceWritePermissionInner(documentUri);
+                enforceWritePermissionInner(documentUri, null);
 
                 final String mimeType = extras.getString(Document.COLUMN_MIME_TYPE);
                 final String displayName = extras.getString(Document.COLUMN_DISPLAY_NAME);
@@ -651,7 +651,7 @@
                 out.putParcelable(DocumentsContract.EXTRA_URI, newDocumentUri);
 
             } else if (METHOD_RENAME_DOCUMENT.equals(method)) {
-                enforceWritePermissionInner(documentUri);
+                enforceWritePermissionInner(documentUri, null);
 
                 final String displayName = extras.getString(Document.COLUMN_DISPLAY_NAME);
                 final String newDocumentId = renameDocument(documentId, displayName);
@@ -675,7 +675,7 @@
                 }
 
             } else if (METHOD_DELETE_DOCUMENT.equals(method)) {
-                enforceWritePermissionInner(documentUri);
+                enforceWritePermissionInner(documentUri, null);
                 deleteDocument(documentId);
 
                 // Document no longer exists, clean up any grants
diff --git a/core/java/android/service/trust/ITrustAgentService.aidl b/core/java/android/service/trust/ITrustAgentService.aidl
index bb0c2b2..f07d0d0 100644
--- a/core/java/android/service/trust/ITrustAgentService.aidl
+++ b/core/java/android/service/trust/ITrustAgentService.aidl
@@ -25,6 +25,8 @@
 interface ITrustAgentService {
     oneway void onUnlockAttempt(boolean successful);
     oneway void onTrustTimeout();
+    oneway void onDeviceLocked();
+    oneway void onDeviceUnlocked();
     oneway void onConfigure(in List<PersistableBundle> options, IBinder token);
     oneway void setCallback(ITrustAgentServiceCallback callback);
 }
diff --git a/core/java/android/service/trust/TrustAgentService.java b/core/java/android/service/trust/TrustAgentService.java
index d6c997f..62fa978 100644
--- a/core/java/android/service/trust/TrustAgentService.java
+++ b/core/java/android/service/trust/TrustAgentService.java
@@ -92,6 +92,8 @@
     private static final int MSG_UNLOCK_ATTEMPT = 1;
     private static final int MSG_CONFIGURE = 2;
     private static final int MSG_TRUST_TIMEOUT = 3;
+    private static final int MSG_DEVICE_LOCKED = 4;
+    private static final int MSG_DEVICE_UNLOCKED = 5;
 
     /**
      * Class containing raw data for a given configuration request.
@@ -134,6 +136,12 @@
                 case MSG_TRUST_TIMEOUT:
                     onTrustTimeout();
                     break;
+                case MSG_DEVICE_LOCKED:
+                    onDeviceLocked();
+                    break;
+                case MSG_DEVICE_UNLOCKED:
+                    onDeviceUnlocked();
+                    break;
             }
         }
     };
@@ -173,6 +181,20 @@
     public void onTrustTimeout() {
     }
 
+    /**
+     * Called when the device enters a state where a PIN, pattern or
+     * password must be entered to unlock it.
+     */
+    public void onDeviceLocked() {
+    }
+
+    /**
+     * Called when the device leaves a state where a PIN, pattern or
+     * password must be entered to unlock it.
+     */
+    public void onDeviceUnlocked() {
+    }
+
     private void onError(String msg) {
         Slog.v(TAG, "Remote exception while " + msg);
     }
@@ -300,6 +322,16 @@
                     .sendToTarget();
         }
 
+        @Override
+        public void onDeviceLocked() throws RemoteException {
+            mHandler.obtainMessage(MSG_DEVICE_LOCKED).sendToTarget();
+        }
+
+        @Override
+        public void onDeviceUnlocked() throws RemoteException {
+            mHandler.obtainMessage(MSG_DEVICE_UNLOCKED).sendToTarget();
+        }
+
         @Override /* Binder API */
         public void setCallback(ITrustAgentServiceCallback callback) {
             synchronized (mLock) {
diff --git a/core/java/android/transition/ChangeTransform.java b/core/java/android/transition/ChangeTransform.java
index a159b40..9749121 100644
--- a/core/java/android/transition/ChangeTransform.java
+++ b/core/java/android/transition/ChangeTransform.java
@@ -376,7 +376,7 @@
         while (outerTransition.mParent != null) {
             outerTransition = outerTransition.mParent;
         }
-        GhostListener listener = new GhostListener(view, ghostView, endMatrix);
+        GhostListener listener = new GhostListener(view, startValues.view, ghostView);
         outerTransition.addListener(listener);
 
         if (startValues.view != endValues.view) {
@@ -466,13 +466,13 @@
 
     private static class GhostListener extends Transition.TransitionListenerAdapter {
         private View mView;
+        private View mStartView;
         private GhostView mGhostView;
-	private Matrix mEndMatrix;
 
-        public GhostListener(View view, GhostView ghostView, Matrix endMatrix) {
+        public GhostListener(View view, View startView, GhostView ghostView) {
             mView = view;
+            mStartView = startView;
             mGhostView = ghostView;
-            mEndMatrix = endMatrix;
         }
 
         @Override
@@ -481,6 +481,7 @@
             GhostView.removeGhost(mView);
             mView.setTagInternal(R.id.transitionTransform, null);
             mView.setTagInternal(R.id.parentMatrix, null);
+            mStartView.setTransitionAlpha(1);
         }
 
         @Override
diff --git a/core/java/android/transition/SidePropagation.java b/core/java/android/transition/SidePropagation.java
index 623cdd1..ad6c2dd 100644
--- a/core/java/android/transition/SidePropagation.java
+++ b/core/java/android/transition/SidePropagation.java
@@ -44,8 +44,8 @@
      * farther from the edge. The default is {@link Gravity#BOTTOM}.
      *
      * @param side The side that is used to calculate the transition propagation. Must be one of
-     *             {@link Gravity#LEFT}, {@link Gravity#TOP}, {@link Gravity#RIGHT}, or
-     *             {@link Gravity#BOTTOM}.
+     *             {@link Gravity#LEFT}, {@link Gravity#TOP}, {@link Gravity#RIGHT},
+     *             {@link Gravity#BOTTOM}, {@link Gravity#START}, or {@link Gravity#END}.
      */
     public void setSide(int side) {
         mSide = side;
@@ -106,7 +106,7 @@
             epicenterY = (top + bottom) / 2;
         }
 
-        float distance = distance(viewCenterX, viewCenterY, epicenterX, epicenterY,
+        float distance = distance(sceneRoot, viewCenterX, viewCenterY, epicenterX, epicenterY,
                 left, top, right, bottom);
         float maxDistance = getMaxDistance(sceneRoot);
         float distanceFraction = distance/maxDistance;
@@ -119,10 +119,20 @@
         return Math.round(duration * directionMultiplier / mPropagationSpeed * distanceFraction);
     }
 
-    private int distance(int viewX, int viewY, int epicenterX, int epicenterY,
+    private int distance(View sceneRoot, int viewX, int viewY, int epicenterX, int epicenterY,
             int left, int top, int right, int bottom) {
+        final int side;
+        if (mSide == Gravity.START) {
+            final boolean isRtl = sceneRoot.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
+            side = isRtl ? Gravity.RIGHT : Gravity.LEFT;
+        } else if (mSide == Gravity.END) {
+            final boolean isRtl = sceneRoot.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
+            side = isRtl ? Gravity.LEFT : Gravity.RIGHT;
+        } else {
+            side = mSide;
+        }
         int distance = 0;
-        switch (mSide) {
+        switch (side) {
             case Gravity.LEFT:
                 distance = right - viewX + Math.abs(epicenterY - viewY);
                 break;
@@ -143,6 +153,8 @@
         switch (mSide) {
             case Gravity.LEFT:
             case Gravity.RIGHT:
+            case Gravity.START:
+            case Gravity.END:
                 return sceneRoot.getWidth();
             default:
                 return sceneRoot.getHeight();
diff --git a/core/java/android/transition/Slide.java b/core/java/android/transition/Slide.java
index ae2e4aa..be1d907 100644
--- a/core/java/android/transition/Slide.java
+++ b/core/java/android/transition/Slide.java
@@ -76,6 +76,20 @@
         }
     };
 
+    private static final CalculateSlide sCalculateStart = new CalculateSlideHorizontal() {
+        @Override
+        public float getGoneX(ViewGroup sceneRoot, View view) {
+            final boolean isRtl = sceneRoot.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
+            final float x;
+            if (isRtl) {
+                x = view.getTranslationX() + sceneRoot.getWidth();
+            } else {
+                x = view.getTranslationX() - sceneRoot.getWidth();
+            }
+            return x;
+        }
+    };
+
     private static final CalculateSlide sCalculateTop = new CalculateSlideVertical() {
         @Override
         public float getGoneY(ViewGroup sceneRoot, View view) {
@@ -90,6 +104,20 @@
         }
     };
 
+    private static final CalculateSlide sCalculateEnd = new CalculateSlideHorizontal() {
+        @Override
+        public float getGoneX(ViewGroup sceneRoot, View view) {
+            final boolean isRtl = sceneRoot.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
+            final float x;
+            if (isRtl) {
+                x = view.getTranslationX() - sceneRoot.getWidth();
+            } else {
+                x = view.getTranslationX() + sceneRoot.getWidth();
+            }
+            return x;
+        }
+    };
+
     private static final CalculateSlide sCalculateBottom = new CalculateSlideVertical() {
         @Override
         public float getGoneY(ViewGroup sceneRoot, View view) {
@@ -144,7 +172,8 @@
      *
      * @param slideEdge The edge of the scene to use for Views appearing and disappearing. One of
      *                  {@link android.view.Gravity#LEFT}, {@link android.view.Gravity#TOP},
-     *                  {@link android.view.Gravity#RIGHT}, {@link android.view.Gravity#BOTTOM}.
+     *                  {@link android.view.Gravity#RIGHT}, {@link android.view.Gravity#BOTTOM},
+     *                  {@link android.view.Gravity#START}, {@link android.view.Gravity#END}.
      * @attr ref android.R.styleable#Slide_slideEdge
      */
     public void setSlideEdge(int slideEdge) {
@@ -161,6 +190,12 @@
             case Gravity.BOTTOM:
                 mSlideCalculator = sCalculateBottom;
                 break;
+            case Gravity.START:
+                mSlideCalculator = sCalculateStart;
+                break;
+            case Gravity.END:
+                mSlideCalculator = sCalculateEnd;
+                break;
             default:
                 throw new IllegalArgumentException("Invalid slide direction");
         }
@@ -175,7 +210,8 @@
      *
      * @return the edge of the scene to use for Views appearing and disappearing. One of
      *         {@link android.view.Gravity#LEFT}, {@link android.view.Gravity#TOP},
-     *         {@link android.view.Gravity#RIGHT}, {@link android.view.Gravity#BOTTOM}.
+     *         {@link android.view.Gravity#RIGHT}, {@link android.view.Gravity#BOTTOM},
+     *         {@link android.view.Gravity#START}, {@link android.view.Gravity#END}.
      * @attr ref android.R.styleable#Slide_slideEdge
      */
     public int getSlideEdge() {
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index d120d79..dbd580d 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -216,7 +216,7 @@
     ///////////////////////////////////////////////////////////////////////////
 
     @Override
-    public int callDrawGLFunction(long drawGLFunction) {
+    public int callDrawGLFunction2(long drawGLFunction) {
         return nCallDrawGLFunction(mRenderer, drawGLFunction);
     }
 
diff --git a/core/java/android/view/HardwareCanvas.java b/core/java/android/view/HardwareCanvas.java
index e3eee71..18accb8 100644
--- a/core/java/android/view/HardwareCanvas.java
+++ b/core/java/android/view/HardwareCanvas.java
@@ -105,10 +105,7 @@
      *
      * @hide
      */
-    public int callDrawGLFunction(long drawGLFunction) {
-        // Noop - this is done in the display list recorder subclass
-        return RenderNode.STATUS_DONE;
-    }
+    public abstract int callDrawGLFunction2(long drawGLFunction);
 
     public abstract void drawCircle(CanvasProperty<Float> cx, CanvasProperty<Float> cy,
             CanvasProperty<Float> radius, CanvasProperty<Paint> paint);
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 5579c13..131c039 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -274,7 +274,7 @@
     }
 
     private void updateRootDisplayList(View view, HardwareDrawCallbacks callbacks) {
-        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "getDisplayList");
+        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Record View#draw()");
         updateViewTreeDisplayList(view);
 
         if (mRootNodeNeedsUpdate || !mRootNode.isValid()) {
@@ -340,6 +340,7 @@
                 recordDuration, view.getResources().getDisplayMetrics().density);
         if ((syncResult & SYNC_LOST_SURFACE_REWARD_IF_FOUND) != 0) {
             setEnabled(false);
+            attachInfo.mViewRootImpl.mSurface.release();
             // Invalidate since we failed to draw. This should fetch a Surface
             // if it is still needed or do nothing if we are no longer drawing
             attachInfo.mViewRootImpl.invalidate();
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 1d19c9b..b54d462 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -56,6 +56,7 @@
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.SystemProperties;
+import android.os.Trace;
 import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.util.FloatProperty;
@@ -14405,143 +14406,158 @@
     public void buildDrawingCache(boolean autoScale) {
         if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0 || (autoScale ?
                 mDrawingCache == null : mUnscaledDrawingCache == null)) {
-            mCachingFailed = false;
+            if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
+                Trace.traceBegin(Trace.TRACE_TAG_VIEW,
+                        "buildDrawingCache/SW Layer for " + getClass().getSimpleName());
+            }
+            try {
+                buildDrawingCacheImpl(autoScale);
+            } finally {
+                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+            }
+        }
+    }
 
-            int width = mRight - mLeft;
-            int height = mBottom - mTop;
+    /**
+     * private, internal implementation of buildDrawingCache, used to enable tracing
+     */
+    private void buildDrawingCacheImpl(boolean autoScale) {
+        mCachingFailed = false;
 
-            final AttachInfo attachInfo = mAttachInfo;
-            final boolean scalingRequired = attachInfo != null && attachInfo.mScalingRequired;
+        int width = mRight - mLeft;
+        int height = mBottom - mTop;
 
-            if (autoScale && scalingRequired) {
-                width = (int) ((width * attachInfo.mApplicationScale) + 0.5f);
-                height = (int) ((height * attachInfo.mApplicationScale) + 0.5f);
+        final AttachInfo attachInfo = mAttachInfo;
+        final boolean scalingRequired = attachInfo != null && attachInfo.mScalingRequired;
+
+        if (autoScale && scalingRequired) {
+            width = (int) ((width * attachInfo.mApplicationScale) + 0.5f);
+            height = (int) ((height * attachInfo.mApplicationScale) + 0.5f);
+        }
+
+        final int drawingCacheBackgroundColor = mDrawingCacheBackgroundColor;
+        final boolean opaque = drawingCacheBackgroundColor != 0 || isOpaque();
+        final boolean use32BitCache = attachInfo != null && attachInfo.mUse32BitDrawingCache;
+
+        final long projectedBitmapSize = width * height * (opaque && !use32BitCache ? 2 : 4);
+        final long drawingCacheSize =
+                ViewConfiguration.get(mContext).getScaledMaximumDrawingCacheSize();
+        if (width <= 0 || height <= 0 || projectedBitmapSize > drawingCacheSize) {
+            if (width > 0 && height > 0) {
+                Log.w(VIEW_LOG_TAG, "View too large to fit into drawing cache, needs "
+                        + projectedBitmapSize + " bytes, only "
+                        + drawingCacheSize + " available");
+            }
+            destroyDrawingCache();
+            mCachingFailed = true;
+            return;
+        }
+
+        boolean clear = true;
+        Bitmap bitmap = autoScale ? mDrawingCache : mUnscaledDrawingCache;
+
+        if (bitmap == null || bitmap.getWidth() != width || bitmap.getHeight() != height) {
+            Bitmap.Config quality;
+            if (!opaque) {
+                // Never pick ARGB_4444 because it looks awful
+                // Keep the DRAWING_CACHE_QUALITY_LOW flag just in case
+                switch (mViewFlags & DRAWING_CACHE_QUALITY_MASK) {
+                    case DRAWING_CACHE_QUALITY_AUTO:
+                    case DRAWING_CACHE_QUALITY_LOW:
+                    case DRAWING_CACHE_QUALITY_HIGH:
+                    default:
+                        quality = Bitmap.Config.ARGB_8888;
+                        break;
+                }
+            } else {
+                // Optimization for translucent windows
+                // If the window is translucent, use a 32 bits bitmap to benefit from memcpy()
+                quality = use32BitCache ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565;
             }
 
-            final int drawingCacheBackgroundColor = mDrawingCacheBackgroundColor;
-            final boolean opaque = drawingCacheBackgroundColor != 0 || isOpaque();
-            final boolean use32BitCache = attachInfo != null && attachInfo.mUse32BitDrawingCache;
+            // Try to cleanup memory
+            if (bitmap != null) bitmap.recycle();
 
-            final long projectedBitmapSize = width * height * (opaque && !use32BitCache ? 2 : 4);
-            final long drawingCacheSize =
-                    ViewConfiguration.get(mContext).getScaledMaximumDrawingCacheSize();
-            if (width <= 0 || height <= 0 || projectedBitmapSize > drawingCacheSize) {
-                if (width > 0 && height > 0) {
-                    Log.w(VIEW_LOG_TAG, "View too large to fit into drawing cache, needs "
-                            + projectedBitmapSize + " bytes, only "
-                            + drawingCacheSize + " available");
+            try {
+                bitmap = Bitmap.createBitmap(mResources.getDisplayMetrics(),
+                        width, height, quality);
+                bitmap.setDensity(getResources().getDisplayMetrics().densityDpi);
+                if (autoScale) {
+                    mDrawingCache = bitmap;
+                } else {
+                    mUnscaledDrawingCache = bitmap;
                 }
-                destroyDrawingCache();
+                if (opaque && use32BitCache) bitmap.setHasAlpha(false);
+            } catch (OutOfMemoryError e) {
+                // If there is not enough memory to create the bitmap cache, just
+                // ignore the issue as bitmap caches are not required to draw the
+                // view hierarchy
+                if (autoScale) {
+                    mDrawingCache = null;
+                } else {
+                    mUnscaledDrawingCache = null;
+                }
                 mCachingFailed = true;
                 return;
             }
 
-            boolean clear = true;
-            Bitmap bitmap = autoScale ? mDrawingCache : mUnscaledDrawingCache;
+            clear = drawingCacheBackgroundColor != 0;
+        }
 
-            if (bitmap == null || bitmap.getWidth() != width || bitmap.getHeight() != height) {
-                Bitmap.Config quality;
-                if (!opaque) {
-                    // Never pick ARGB_4444 because it looks awful
-                    // Keep the DRAWING_CACHE_QUALITY_LOW flag just in case
-                    switch (mViewFlags & DRAWING_CACHE_QUALITY_MASK) {
-                        case DRAWING_CACHE_QUALITY_AUTO:
-                        case DRAWING_CACHE_QUALITY_LOW:
-                        case DRAWING_CACHE_QUALITY_HIGH:
-                        default:
-                            quality = Bitmap.Config.ARGB_8888;
-                            break;
-                    }
-                } else {
-                    // Optimization for translucent windows
-                    // If the window is translucent, use a 32 bits bitmap to benefit from memcpy()
-                    quality = use32BitCache ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565;
-                }
-
-                // Try to cleanup memory
-                if (bitmap != null) bitmap.recycle();
-
-                try {
-                    bitmap = Bitmap.createBitmap(mResources.getDisplayMetrics(),
-                            width, height, quality);
-                    bitmap.setDensity(getResources().getDisplayMetrics().densityDpi);
-                    if (autoScale) {
-                        mDrawingCache = bitmap;
-                    } else {
-                        mUnscaledDrawingCache = bitmap;
-                    }
-                    if (opaque && use32BitCache) bitmap.setHasAlpha(false);
-                } catch (OutOfMemoryError e) {
-                    // If there is not enough memory to create the bitmap cache, just
-                    // ignore the issue as bitmap caches are not required to draw the
-                    // view hierarchy
-                    if (autoScale) {
-                        mDrawingCache = null;
-                    } else {
-                        mUnscaledDrawingCache = null;
-                    }
-                    mCachingFailed = true;
-                    return;
-                }
-
-                clear = drawingCacheBackgroundColor != 0;
+        Canvas canvas;
+        if (attachInfo != null) {
+            canvas = attachInfo.mCanvas;
+            if (canvas == null) {
+                canvas = new Canvas();
             }
+            canvas.setBitmap(bitmap);
+            // Temporarily clobber the cached Canvas in case one of our children
+            // is also using a drawing cache. Without this, the children would
+            // steal the canvas by attaching their own bitmap to it and bad, bad
+            // thing would happen (invisible views, corrupted drawings, etc.)
+            attachInfo.mCanvas = null;
+        } else {
+            // This case should hopefully never or seldom happen
+            canvas = new Canvas(bitmap);
+        }
 
-            Canvas canvas;
-            if (attachInfo != null) {
-                canvas = attachInfo.mCanvas;
-                if (canvas == null) {
-                    canvas = new Canvas();
-                }
-                canvas.setBitmap(bitmap);
-                // Temporarily clobber the cached Canvas in case one of our children
-                // is also using a drawing cache. Without this, the children would
-                // steal the canvas by attaching their own bitmap to it and bad, bad
-                // thing would happen (invisible views, corrupted drawings, etc.)
-                attachInfo.mCanvas = null;
-            } else {
-                // This case should hopefully never or seldom happen
-                canvas = new Canvas(bitmap);
+        if (clear) {
+            bitmap.eraseColor(drawingCacheBackgroundColor);
+        }
+
+        computeScroll();
+        final int restoreCount = canvas.save();
+
+        if (autoScale && scalingRequired) {
+            final float scale = attachInfo.mApplicationScale;
+            canvas.scale(scale, scale);
+        }
+
+        canvas.translate(-mScrollX, -mScrollY);
+
+        mPrivateFlags |= PFLAG_DRAWN;
+        if (mAttachInfo == null || !mAttachInfo.mHardwareAccelerated ||
+                mLayerType != LAYER_TYPE_NONE) {
+            mPrivateFlags |= PFLAG_DRAWING_CACHE_VALID;
+        }
+
+        // Fast path for layouts with no backgrounds
+        if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
+            mPrivateFlags &= ~PFLAG_DIRTY_MASK;
+            dispatchDraw(canvas);
+            if (mOverlay != null && !mOverlay.isEmpty()) {
+                mOverlay.getOverlayView().draw(canvas);
             }
+        } else {
+            draw(canvas);
+        }
 
-            if (clear) {
-                bitmap.eraseColor(drawingCacheBackgroundColor);
-            }
+        canvas.restoreToCount(restoreCount);
+        canvas.setBitmap(null);
 
-            computeScroll();
-            final int restoreCount = canvas.save();
-
-            if (autoScale && scalingRequired) {
-                final float scale = attachInfo.mApplicationScale;
-                canvas.scale(scale, scale);
-            }
-
-            canvas.translate(-mScrollX, -mScrollY);
-
-            mPrivateFlags |= PFLAG_DRAWN;
-            if (mAttachInfo == null || !mAttachInfo.mHardwareAccelerated ||
-                    mLayerType != LAYER_TYPE_NONE) {
-                mPrivateFlags |= PFLAG_DRAWING_CACHE_VALID;
-            }
-
-            // Fast path for layouts with no backgrounds
-            if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
-                mPrivateFlags &= ~PFLAG_DIRTY_MASK;
-                dispatchDraw(canvas);
-                if (mOverlay != null && !mOverlay.isEmpty()) {
-                    mOverlay.getOverlayView().draw(canvas);
-                }
-            } else {
-                draw(canvas);
-            }
-
-            canvas.restoreToCount(restoreCount);
-            canvas.setBitmap(null);
-
-            if (attachInfo != null) {
-                // Restore the cached Canvas for our siblings
-                attachInfo.mCanvas = canvas;
-            }
+        if (attachInfo != null) {
+            // Restore the cached Canvas for our siblings
+            attachInfo.mCanvas = canvas;
         }
     }
 
@@ -15470,10 +15486,10 @@
                 && mAttachInfo.mHardwareRenderer != null) {
             mBackgroundRenderNode = getDrawableRenderNode(background, mBackgroundRenderNode);
 
-            final RenderNode displayList = mBackgroundRenderNode;
-            if (displayList != null && displayList.isValid()) {
-                setBackgroundDisplayListProperties(displayList);
-                ((HardwareCanvas) canvas).drawRenderNode(displayList);
+            final RenderNode renderNode = mBackgroundRenderNode;
+            if (renderNode != null && renderNode.isValid()) {
+                setBackgroundRenderNodeProperties(renderNode);
+                ((HardwareCanvas) canvas).drawRenderNode(renderNode);
                 return;
             }
         }
@@ -15489,14 +15505,9 @@
         }
     }
 
-    /**
-     * Set up background drawable display list properties.
-     *
-     * @param displayList Valid display list for the background drawable
-     */
-    private void setBackgroundDisplayListProperties(RenderNode displayList) {
-        displayList.setTranslationX(mScrollX);
-        displayList.setTranslationY(mScrollY);
+    private void setBackgroundRenderNodeProperties(RenderNode renderNode) {
+        renderNode.setTranslationX(mScrollX);
+        renderNode.setTranslationY(mScrollY);
     }
 
     /**
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index fe17417..b12c747 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -245,7 +245,7 @@
 
     // These can be accessed by any thread, must be protected with a lock.
     // Surface can never be reassigned or cleared (use Surface.clear()).
-    private final Surface mSurface = new Surface();
+    final Surface mSurface = new Surface();
 
     boolean mAdded;
     boolean mAddedTouchMode;
diff --git a/core/java/android/view/ViewTreeObserver.java b/core/java/android/view/ViewTreeObserver.java
index b85fec8..521fd31 100644
--- a/core/java/android/view/ViewTreeObserver.java
+++ b/core/java/android/view/ViewTreeObserver.java
@@ -37,6 +37,8 @@
     private CopyOnWriteArrayList<OnWindowAttachListener> mOnWindowAttachListeners;
     private CopyOnWriteArrayList<OnGlobalFocusChangeListener> mOnGlobalFocusListeners;
     private CopyOnWriteArrayList<OnTouchModeChangeListener> mOnTouchModeChangeListeners;
+    private CopyOnWriteArrayList<OnEnterAnimationCompleteListener>
+            mOnEnterAnimationCompleteListeners;
 
     // Non-recursive listeners use CopyOnWriteArray
     // Any listener invoked from ViewRootImpl.performTraversals() should not be recursive
@@ -316,6 +318,13 @@
     }
 
     /**
+     * @hide
+     */
+    public interface OnEnterAnimationCompleteListener {
+        public void onEnterAnimationComplete();
+    }
+
+    /**
      * Creates a new ViewTreeObserver. This constructor should not be called
      */
     ViewTreeObserver() {
@@ -780,6 +789,29 @@
         mOnComputeInternalInsetsListeners.remove(victim);
     }
 
+    /**
+     * @hide
+     */
+    public void addOnEnterAnimationCompleteListener(OnEnterAnimationCompleteListener listener) {
+        checkIsAlive();
+        if (mOnEnterAnimationCompleteListeners == null) {
+            mOnEnterAnimationCompleteListeners =
+                    new CopyOnWriteArrayList<OnEnterAnimationCompleteListener>();
+        }
+        mOnEnterAnimationCompleteListeners.add(listener);
+    }
+
+    /**
+     * @hide
+     */
+    public void removeOnEnterAnimationCompleteListener(OnEnterAnimationCompleteListener listener) {
+        checkIsAlive();
+        if (mOnEnterAnimationCompleteListeners == null) {
+            return;
+        }
+        mOnEnterAnimationCompleteListeners.remove(listener);
+    }
+
     private void checkIsAlive() {
         if (!mAlive) {
             throw new IllegalStateException("This ViewTreeObserver is not alive, call "
@@ -1022,6 +1054,23 @@
     }
 
     /**
+     * @hide
+     */
+    public final void dispatchOnEnterAnimationComplete() {
+        // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
+        // perform the dispatching. The iterator is a safe guard against listeners that
+        // could mutate the list by calling the various add/remove methods. This prevents
+        // the array from being modified while we iterate it.
+        final CopyOnWriteArrayList<OnEnterAnimationCompleteListener> listeners =
+                mOnEnterAnimationCompleteListeners;
+        if (listeners != null && !listeners.isEmpty()) {
+            for (OnEnterAnimationCompleteListener listener : listeners) {
+                listener.onEnterAnimationComplete();
+            }
+        }
+    }
+
+    /**
      * Copy on write array. This array is not thread safe, and only one loop can
      * iterate over this array at any given time. This class avoids allocations
      * until a concurrent modification happens.
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index 82b1073..0d82087 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -118,12 +118,13 @@
 
     private Runnable mSystemPropertyUpdater;
 
-    /** Default token to apply to added views. */
-    private IBinder mDefaultToken;
-
     private WindowManagerGlobal() {
     }
 
+    public static void initialize() {
+        getWindowManagerService();
+    }
+
     public static WindowManagerGlobal getInstance() {
         synchronized (WindowManagerGlobal.class) {
             if (sDefaultWindowManager == null) {
@@ -138,6 +139,12 @@
             if (sWindowManagerService == null) {
                 sWindowManagerService = IWindowManager.Stub.asInterface(
                         ServiceManager.getService("window"));
+                try {
+                    sWindowManagerService = getWindowManagerService();
+                    ValueAnimator.setDurationScale(sWindowManagerService.getCurrentAnimatorScale());
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Failed to get WindowManagerService, cannot set animator scale", e);
+                }
             }
             return sWindowManagerService;
         }
@@ -157,7 +164,6 @@
                                 }
                             },
                             imm.getClient(), imm.getInputContext());
-                    ValueAnimator.setDurationScale(windowManager.getCurrentAnimatorScale());
                 } catch (RemoteException e) {
                     Log.e(TAG, "Failed to open window session", e);
                 }
@@ -172,17 +178,6 @@
         }
     }
 
-    /**
-     * Sets the default token to use in {@link #addView} when no parent window
-     * token is available and no token has been explicitly set in the view's
-     * layout params.
-     *
-     * @param token Default window token to apply to added views.
-     */
-    public void setDefaultToken(IBinder token) {
-        mDefaultToken = token;
-    }
-
     public String[] getViewRootNames() {
         synchronized (mLock) {
             final int numRoots = mRoots.size();
@@ -230,10 +225,6 @@
             }
         }
 
-        if (wparams.token == null && mDefaultToken != null) {
-            wparams.token = mDefaultToken;
-        }
-
         ViewRootImpl root;
         View panelParentView = null;
 
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index 52d79f8..98e9f54c 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -16,6 +16,9 @@
 
 package android.view;
 
+import android.annotation.NonNull;
+import android.os.IBinder;
+
 /**
  * Provides low-level communication with the system window manager for
  * operations that are bound to a particular context, display or parent window.
@@ -47,6 +50,8 @@
     private final Display mDisplay;
     private final Window mParentWindow;
 
+    private IBinder mDefaultToken;
+
     public WindowManagerImpl(Display display) {
         this(display, null);
     }
@@ -64,16 +69,43 @@
         return new WindowManagerImpl(display, mParentWindow);
     }
 
+    /**
+     * Sets the window token to assign when none is specified by the client or
+     * available from the parent window.
+     *
+     * @param token The default token to assign.
+     */
+    public void setDefaultToken(IBinder token) {
+        mDefaultToken = token;
+    }
+
     @Override
-    public void addView(View view, ViewGroup.LayoutParams params) {
+    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
+        applyDefaultToken(params);
         mGlobal.addView(view, params, mDisplay, mParentWindow);
     }
 
     @Override
-    public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
+    public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
+        applyDefaultToken(params);
         mGlobal.updateViewLayout(view, params);
     }
 
+    private void applyDefaultToken(@NonNull ViewGroup.LayoutParams params) {
+        // Only use the default token if we don't have a parent window.
+        if (mDefaultToken != null && mParentWindow == null) {
+            if (!(params instanceof WindowManager.LayoutParams)) {
+                throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
+            }
+
+            // Only use the default token if we don't already have a token.
+            final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
+            if (wparams.token == null) {
+                wparams.token = mDefaultToken;
+            }
+        }
+    }
+
     @Override
     public void removeView(View view) {
         mGlobal.removeView(view, false);
diff --git a/core/java/android/webkit/WebViewDelegate.java b/core/java/android/webkit/WebViewDelegate.java
index 3dcfda3..e03445e 100644
--- a/core/java/android/webkit/WebViewDelegate.java
+++ b/core/java/android/webkit/WebViewDelegate.java
@@ -107,7 +107,7 @@
             throw new IllegalArgumentException(canvas.getClass().getName()
                     + " is not hardware accelerated");
         }
-        ((HardwareCanvas) canvas).callDrawGLFunction(nativeDrawGLFunctor);
+        ((HardwareCanvas) canvas).callDrawGLFunction2(nativeDrawGLFunctor);
     }
 
     /**
diff --git a/core/java/android/widget/ActionMenuPresenter.java b/core/java/android/widget/ActionMenuPresenter.java
index d779124..18f15a0 100644
--- a/core/java/android/widget/ActionMenuPresenter.java
+++ b/core/java/android/widget/ActionMenuPresenter.java
@@ -655,15 +655,19 @@
         protected boolean setFrame(int l, int t, int r, int b) {
             final boolean changed = super.setFrame(l, t, r, b);
 
-            // Set up the hotspot bounds to be centered on the image.
+            // Set up the hotspot bounds to square and centered on the image.
             final Drawable d = getDrawable();
             final Drawable bg = getBackground();
             if (d != null && bg != null) {
-                final float[] pts = mTempPts;
-                pts[0] = d.getBounds().centerX();
-                getImageMatrix().mapPoints(pts);
-                final int offset =  (int) pts[0] - getWidth() / 2;
-                bg.setHotspotBounds(offset, 0, getWidth() + offset, getHeight());
+                final int width = getWidth();
+                final int height = getHeight();
+                final int halfEdge = Math.max(width, height) / 2;
+                final int offsetX = getPaddingLeft() - getPaddingRight();
+                final int offsetY = getPaddingTop() - getPaddingBottom();
+                final int centerX = (width + offsetX) / 2;
+                final int centerY = (height + offsetY) / 2;
+                bg.setHotspotBounds(centerX - halfEdge, centerY - halfEdge,
+                        centerX + halfEdge, centerY + halfEdge);
             }
 
             return changed;
diff --git a/core/java/android/widget/CalendarView.java b/core/java/android/widget/CalendarView.java
index f380d68..ed59ea6 100644
--- a/core/java/android/widget/CalendarView.java
+++ b/core/java/android/widget/CalendarView.java
@@ -17,42 +17,24 @@
 package android.widget;
 
 import android.annotation.Widget;
-import android.app.Service;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.TypedArray;
-import android.database.DataSetObserver;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Paint.Align;
-import android.graphics.Paint.Style;
-import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
-import android.text.TextUtils;
-import android.text.format.DateUtils;
 import android.util.AttributeSet;
-import android.util.DisplayMetrics;
 import android.util.Log;
-import android.util.TypedValue;
-import android.view.GestureDetector;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityNodeInfo;
-import android.widget.AbsListView.OnScrollListener;
 
 import com.android.internal.R;
 
+import java.text.DateFormat;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.util.Calendar;
 import java.util.Locale;
 import java.util.TimeZone;
 
-import libcore.icu.LocaleData;
-
 /**
  * This class is a calendar widget for displaying and selecting dates. The range
  * of dates supported by this calendar is configurable. A user can select a date
@@ -74,13 +56,12 @@
  */
 @Widget
 public class CalendarView extends FrameLayout {
+    private static final String LOG_TAG = "CalendarView";
 
-    /**
-     * Tag for logging.
-     */
-    private static final String LOG_TAG = CalendarView.class.getSimpleName();
+    private static final int MODE_HOLO = 0;
+    private static final int MODE_MATERIAL = 1;
 
-    private CalendarViewDelegate mDelegate;
+    private final CalendarViewDelegate mDelegate;
 
     /**
      * The callback used to indicate the user changes the date.
@@ -113,7 +94,23 @@
     public CalendarView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
 
-        mDelegate = new LegacyCalendarViewDelegate(this, context, attrs, defStyleAttr, defStyleRes);
+        final TypedArray a = context.obtainStyledAttributes(
+                attrs, R.styleable.CalendarView, defStyleAttr, defStyleRes);
+        final int mode = a.getInt(R.styleable.CalendarView_calendarViewMode, MODE_HOLO);
+        a.recycle();
+
+        switch (mode) {
+            case MODE_HOLO:
+                mDelegate = new CalendarViewLegacyDelegate(
+                        this, context, attrs, defStyleAttr, defStyleRes);
+                break;
+            case MODE_MATERIAL:
+                mDelegate = new CalendarViewMaterialDelegate(
+                        this, context, attrs, defStyleAttr, defStyleRes);
+                break;
+            default:
+                throw new IllegalArgumentException("invalid calendarViewMode attribute");
+        }
     }
 
     /**
@@ -326,16 +323,6 @@
         return mDelegate.getDateTextAppearance();
     }
 
-    @Override
-    public void setEnabled(boolean enabled) {
-        mDelegate.setEnabled(enabled);
-    }
-
-    @Override
-    public boolean isEnabled() {
-        return mDelegate.isEnabled();
-    }
-
     /**
      * Gets the minimal date supported by this {@link CalendarView} in milliseconds
      * since January 1, 1970 00:00:00 in {@link TimeZone#getDefault()} time
@@ -516,14 +503,12 @@
 
     @Override
     public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEvent(event);
-        mDelegate.onInitializeAccessibilityEvent(event);
+        event.setClassName(CalendarView.class.getName());
     }
 
     @Override
     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfo(info);
-        mDelegate.onInitializeAccessibilityNodeInfo(info);
+        info.setClassName(CalendarView.class.getName());
     }
 
     /**
@@ -560,9 +545,6 @@
         void setDateTextAppearance(int resourceId);
         int getDateTextAppearance();
 
-        void setEnabled(boolean enabled);
-        boolean isEnabled();
-
         void setMinDate(long minDate);
         long getMinDate();
 
@@ -582,21 +564,26 @@
         void setOnDateChangeListener(OnDateChangeListener listener);
 
         void onConfigurationChanged(Configuration newConfig);
-        void onInitializeAccessibilityEvent(AccessibilityEvent event);
-        void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info);
     }
 
     /**
      * An abstract class which can be used as a start for CalendarView implementations
      */
     abstract static class AbstractCalendarViewDelegate implements CalendarViewDelegate {
-        // The delegator
+        /** String for parsing dates. */
+        private static final String DATE_FORMAT = "MM/dd/yyyy";
+
+        /** The default minimal date. */
+        protected static final String DEFAULT_MIN_DATE = "01/01/1900";
+
+        /** The default maximal date. */
+        protected static final String DEFAULT_MAX_DATE = "01/01/2100";
+
+        /** Date format for parsing dates. */
+        protected static final DateFormat DATE_FORMATTER = new SimpleDateFormat(DATE_FORMAT);
+
         protected CalendarView mDelegator;
-
-        // The context
         protected Context mContext;
-
-        // The current locale
         protected Locale mCurrentLocale;
 
         AbstractCalendarViewDelegate(CalendarView delegator, Context context) {
@@ -613,830 +600,6 @@
             }
             mCurrentLocale = locale;
         }
-    }
-
-    /**
-     * A delegate implementing the legacy CalendarView
-     */
-    private static class LegacyCalendarViewDelegate extends AbstractCalendarViewDelegate {
-
-        /**
-         * Default value whether to show week number.
-         */
-        private static final boolean DEFAULT_SHOW_WEEK_NUMBER = true;
-
-        /**
-         * The number of milliseconds in a day.e
-         */
-        private static final long MILLIS_IN_DAY = 86400000L;
-
-        /**
-         * The number of day in a week.
-         */
-        private static final int DAYS_PER_WEEK = 7;
-
-        /**
-         * The number of milliseconds in a week.
-         */
-        private static final long MILLIS_IN_WEEK = DAYS_PER_WEEK * MILLIS_IN_DAY;
-
-        /**
-         * Affects when the month selection will change while scrolling upe
-         */
-        private static final int SCROLL_HYST_WEEKS = 2;
-
-        /**
-         * How long the GoTo fling animation should last.
-         */
-        private static final int GOTO_SCROLL_DURATION = 1000;
-
-        /**
-         * The duration of the adjustment upon a user scroll in milliseconds.
-         */
-        private static final int ADJUSTMENT_SCROLL_DURATION = 500;
-
-        /**
-         * How long to wait after receiving an onScrollStateChanged notification
-         * before acting on it.
-         */
-        private static final int SCROLL_CHANGE_DELAY = 40;
-
-        /**
-         * String for parsing dates.
-         */
-        private static final String DATE_FORMAT = "MM/dd/yyyy";
-
-        /**
-         * The default minimal date.
-         */
-        private static final String DEFAULT_MIN_DATE = "01/01/1900";
-
-        /**
-         * The default maximal date.
-         */
-        private static final String DEFAULT_MAX_DATE = "01/01/2100";
-
-        private static final int DEFAULT_SHOWN_WEEK_COUNT = 6;
-
-        private static final int DEFAULT_DATE_TEXT_SIZE = 14;
-
-        private static final int UNSCALED_SELECTED_DATE_VERTICAL_BAR_WIDTH = 6;
-
-        private static final int UNSCALED_WEEK_MIN_VISIBLE_HEIGHT = 12;
-
-        private static final int UNSCALED_LIST_SCROLL_TOP_OFFSET = 2;
-
-        private static final int UNSCALED_BOTTOM_BUFFER = 20;
-
-        private static final int UNSCALED_WEEK_SEPARATOR_LINE_WIDTH = 1;
-
-        private static final int DEFAULT_WEEK_DAY_TEXT_APPEARANCE_RES_ID = -1;
-
-        private final int mWeekSeperatorLineWidth;
-
-        private int mDateTextSize;
-
-        private Drawable mSelectedDateVerticalBar;
-
-        private final int mSelectedDateVerticalBarWidth;
-
-        private int mSelectedWeekBackgroundColor;
-
-        private int mFocusedMonthDateColor;
-
-        private int mUnfocusedMonthDateColor;
-
-        private int mWeekSeparatorLineColor;
-
-        private int mWeekNumberColor;
-
-        private int mWeekDayTextAppearanceResId;
-
-        private int mDateTextAppearanceResId;
-
-        /**
-         * The top offset of the weeks list.
-         */
-        private int mListScrollTopOffset = 2;
-
-        /**
-         * The visible height of a week view.
-         */
-        private int mWeekMinVisibleHeight = 12;
-
-        /**
-         * The visible height of a week view.
-         */
-        private int mBottomBuffer = 20;
-
-        /**
-         * The number of shown weeks.
-         */
-        private int mShownWeekCount;
-
-        /**
-         * Flag whether to show the week number.
-         */
-        private boolean mShowWeekNumber;
-
-        /**
-         * The number of day per week to be shown.
-         */
-        private int mDaysPerWeek = 7;
-
-        /**
-         * The friction of the week list while flinging.
-         */
-        private float mFriction = .05f;
-
-        /**
-         * Scale for adjusting velocity of the week list while flinging.
-         */
-        private float mVelocityScale = 0.333f;
-
-        /**
-         * The adapter for the weeks list.
-         */
-        private WeeksAdapter mAdapter;
-
-        /**
-         * The weeks list.
-         */
-        private ListView mListView;
-
-        /**
-         * The name of the month to display.
-         */
-        private TextView mMonthName;
-
-        /**
-         * The header with week day names.
-         */
-        private ViewGroup mDayNamesHeader;
-
-        /**
-         * Cached abbreviations for day of week names.
-         */
-        private String[] mDayNamesShort;
-
-        /**
-         * Cached full-length day of week names.
-         */
-        private String[] mDayNamesLong;
-
-        /**
-         * The first day of the week.
-         */
-        private int mFirstDayOfWeek;
-
-        /**
-         * Which month should be displayed/highlighted [0-11].
-         */
-        private int mCurrentMonthDisplayed = -1;
-
-        /**
-         * Used for tracking during a scroll.
-         */
-        private long mPreviousScrollPosition;
-
-        /**
-         * Used for tracking which direction the view is scrolling.
-         */
-        private boolean mIsScrollingUp = false;
-
-        /**
-         * The previous scroll state of the weeks ListView.
-         */
-        private int mPreviousScrollState = OnScrollListener.SCROLL_STATE_IDLE;
-
-        /**
-         * The current scroll state of the weeks ListView.
-         */
-        private int mCurrentScrollState = OnScrollListener.SCROLL_STATE_IDLE;
-
-        /**
-         * Listener for changes in the selected day.
-         */
-        private OnDateChangeListener mOnDateChangeListener;
-
-        /**
-         * Command for adjusting the position after a scroll/fling.
-         */
-        private ScrollStateRunnable mScrollStateChangedRunnable = new ScrollStateRunnable();
-
-        /**
-         * Temporary instance to avoid multiple instantiations.
-         */
-        private Calendar mTempDate;
-
-        /**
-         * The first day of the focused month.
-         */
-        private Calendar mFirstDayOfMonth;
-
-        /**
-         * The start date of the range supported by this picker.
-         */
-        private Calendar mMinDate;
-
-        /**
-         * The end date of the range supported by this picker.
-         */
-        private Calendar mMaxDate;
-
-        /**
-         * Date format for parsing dates.
-         */
-        private final java.text.DateFormat mDateFormat = new SimpleDateFormat(DATE_FORMAT);
-
-        LegacyCalendarViewDelegate(CalendarView delegator, Context context, AttributeSet attrs,
-                                   int defStyleAttr, int defStyleRes) {
-            super(delegator, context);
-
-            // initialization based on locale
-            setCurrentLocale(Locale.getDefault());
-
-            TypedArray attributesArray = context.obtainStyledAttributes(attrs,
-                    R.styleable.CalendarView, defStyleAttr, defStyleRes);
-            mShowWeekNumber = attributesArray.getBoolean(R.styleable.CalendarView_showWeekNumber,
-                    DEFAULT_SHOW_WEEK_NUMBER);
-            mFirstDayOfWeek = attributesArray.getInt(R.styleable.CalendarView_firstDayOfWeek,
-                    LocaleData.get(Locale.getDefault()).firstDayOfWeek);
-            String minDate = attributesArray.getString(R.styleable.CalendarView_minDate);
-            if (TextUtils.isEmpty(minDate) || !parseDate(minDate, mMinDate)) {
-                parseDate(DEFAULT_MIN_DATE, mMinDate);
-            }
-            String maxDate = attributesArray.getString(R.styleable.CalendarView_maxDate);
-            if (TextUtils.isEmpty(maxDate) || !parseDate(maxDate, mMaxDate)) {
-                parseDate(DEFAULT_MAX_DATE, mMaxDate);
-            }
-            if (mMaxDate.before(mMinDate)) {
-                throw new IllegalArgumentException("Max date cannot be before min date.");
-            }
-            mShownWeekCount = attributesArray.getInt(R.styleable.CalendarView_shownWeekCount,
-                    DEFAULT_SHOWN_WEEK_COUNT);
-            mSelectedWeekBackgroundColor = attributesArray.getColor(
-                    R.styleable.CalendarView_selectedWeekBackgroundColor, 0);
-            mFocusedMonthDateColor = attributesArray.getColor(
-                    R.styleable.CalendarView_focusedMonthDateColor, 0);
-            mUnfocusedMonthDateColor = attributesArray.getColor(
-                    R.styleable.CalendarView_unfocusedMonthDateColor, 0);
-            mWeekSeparatorLineColor = attributesArray.getColor(
-                    R.styleable.CalendarView_weekSeparatorLineColor, 0);
-            mWeekNumberColor = attributesArray.getColor(R.styleable.CalendarView_weekNumberColor, 0);
-            mSelectedDateVerticalBar = attributesArray.getDrawable(
-                    R.styleable.CalendarView_selectedDateVerticalBar);
-
-            mDateTextAppearanceResId = attributesArray.getResourceId(
-                    R.styleable.CalendarView_dateTextAppearance, R.style.TextAppearance_Small);
-            updateDateTextSize();
-
-            mWeekDayTextAppearanceResId = attributesArray.getResourceId(
-                    R.styleable.CalendarView_weekDayTextAppearance,
-                    DEFAULT_WEEK_DAY_TEXT_APPEARANCE_RES_ID);
-            attributesArray.recycle();
-
-            DisplayMetrics displayMetrics = mDelegator.getResources().getDisplayMetrics();
-            mWeekMinVisibleHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
-                    UNSCALED_WEEK_MIN_VISIBLE_HEIGHT, displayMetrics);
-            mListScrollTopOffset = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
-                    UNSCALED_LIST_SCROLL_TOP_OFFSET, displayMetrics);
-            mBottomBuffer = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
-                    UNSCALED_BOTTOM_BUFFER, displayMetrics);
-            mSelectedDateVerticalBarWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
-                    UNSCALED_SELECTED_DATE_VERTICAL_BAR_WIDTH, displayMetrics);
-            mWeekSeperatorLineWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
-                    UNSCALED_WEEK_SEPARATOR_LINE_WIDTH, displayMetrics);
-
-            LayoutInflater layoutInflater = (LayoutInflater) mContext
-                    .getSystemService(Service.LAYOUT_INFLATER_SERVICE);
-            View content = layoutInflater.inflate(R.layout.calendar_view, null, false);
-            mDelegator.addView(content);
-
-            mListView = (ListView) mDelegator.findViewById(R.id.list);
-            mDayNamesHeader = (ViewGroup) content.findViewById(com.android.internal.R.id.day_names);
-            mMonthName = (TextView) content.findViewById(com.android.internal.R.id.month_name);
-
-            setUpHeader();
-            setUpListView();
-            setUpAdapter();
-
-            // go to today or whichever is close to today min or max date
-            mTempDate.setTimeInMillis(System.currentTimeMillis());
-            if (mTempDate.before(mMinDate)) {
-                goTo(mMinDate, false, true, true);
-            } else if (mMaxDate.before(mTempDate)) {
-                goTo(mMaxDate, false, true, true);
-            } else {
-                goTo(mTempDate, false, true, true);
-            }
-
-            mDelegator.invalidate();
-        }
-
-        @Override
-        public void setShownWeekCount(int count) {
-            if (mShownWeekCount != count) {
-                mShownWeekCount = count;
-                mDelegator.invalidate();
-            }
-        }
-
-        @Override
-        public int getShownWeekCount() {
-            return mShownWeekCount;
-        }
-
-        @Override
-        public void setSelectedWeekBackgroundColor(int color) {
-            if (mSelectedWeekBackgroundColor != color) {
-                mSelectedWeekBackgroundColor = color;
-                final int childCount = mListView.getChildCount();
-                for (int i = 0; i < childCount; i++) {
-                    WeekView weekView = (WeekView) mListView.getChildAt(i);
-                    if (weekView.mHasSelectedDay) {
-                        weekView.invalidate();
-                    }
-                }
-            }
-        }
-
-        @Override
-        public int getSelectedWeekBackgroundColor() {
-            return mSelectedWeekBackgroundColor;
-        }
-
-        @Override
-        public void setFocusedMonthDateColor(int color) {
-            if (mFocusedMonthDateColor != color) {
-                mFocusedMonthDateColor = color;
-                final int childCount = mListView.getChildCount();
-                for (int i = 0; i < childCount; i++) {
-                    WeekView weekView = (WeekView) mListView.getChildAt(i);
-                    if (weekView.mHasFocusedDay) {
-                        weekView.invalidate();
-                    }
-                }
-            }
-        }
-
-        @Override
-        public int getFocusedMonthDateColor() {
-            return mFocusedMonthDateColor;
-        }
-
-        @Override
-        public void setUnfocusedMonthDateColor(int color) {
-            if (mUnfocusedMonthDateColor != color) {
-                mUnfocusedMonthDateColor = color;
-                final int childCount = mListView.getChildCount();
-                for (int i = 0; i < childCount; i++) {
-                    WeekView weekView = (WeekView) mListView.getChildAt(i);
-                    if (weekView.mHasUnfocusedDay) {
-                        weekView.invalidate();
-                    }
-                }
-            }
-        }
-
-        @Override
-        public int getUnfocusedMonthDateColor() {
-            return mFocusedMonthDateColor;
-        }
-
-        @Override
-        public void setWeekNumberColor(int color) {
-            if (mWeekNumberColor != color) {
-                mWeekNumberColor = color;
-                if (mShowWeekNumber) {
-                    invalidateAllWeekViews();
-                }
-            }
-        }
-
-        @Override
-        public int getWeekNumberColor() {
-            return mWeekNumberColor;
-        }
-
-        @Override
-        public void setWeekSeparatorLineColor(int color) {
-            if (mWeekSeparatorLineColor != color) {
-                mWeekSeparatorLineColor = color;
-                invalidateAllWeekViews();
-            }
-        }
-
-        @Override
-        public int getWeekSeparatorLineColor() {
-            return mWeekSeparatorLineColor;
-        }
-
-        @Override
-        public void setSelectedDateVerticalBar(int resourceId) {
-            Drawable drawable = mDelegator.getContext().getDrawable(resourceId);
-            setSelectedDateVerticalBar(drawable);
-        }
-
-        @Override
-        public void setSelectedDateVerticalBar(Drawable drawable) {
-            if (mSelectedDateVerticalBar != drawable) {
-                mSelectedDateVerticalBar = drawable;
-                final int childCount = mListView.getChildCount();
-                for (int i = 0; i < childCount; i++) {
-                    WeekView weekView = (WeekView) mListView.getChildAt(i);
-                    if (weekView.mHasSelectedDay) {
-                        weekView.invalidate();
-                    }
-                }
-            }
-        }
-
-        @Override
-        public Drawable getSelectedDateVerticalBar() {
-            return mSelectedDateVerticalBar;
-        }
-
-        @Override
-        public void setWeekDayTextAppearance(int resourceId) {
-            if (mWeekDayTextAppearanceResId != resourceId) {
-                mWeekDayTextAppearanceResId = resourceId;
-                setUpHeader();
-            }
-        }
-
-        @Override
-        public int getWeekDayTextAppearance() {
-            return mWeekDayTextAppearanceResId;
-        }
-
-        @Override
-        public void setDateTextAppearance(int resourceId) {
-            if (mDateTextAppearanceResId != resourceId) {
-                mDateTextAppearanceResId = resourceId;
-                updateDateTextSize();
-                invalidateAllWeekViews();
-            }
-        }
-
-        @Override
-        public int getDateTextAppearance() {
-            return mDateTextAppearanceResId;
-        }
-
-        @Override
-        public void setEnabled(boolean enabled) {
-            mListView.setEnabled(enabled);
-        }
-
-        @Override
-        public boolean isEnabled() {
-            return mListView.isEnabled();
-        }
-
-        @Override
-        public void setMinDate(long minDate) {
-            mTempDate.setTimeInMillis(minDate);
-            if (isSameDate(mTempDate, mMinDate)) {
-                return;
-            }
-            mMinDate.setTimeInMillis(minDate);
-            // make sure the current date is not earlier than
-            // the new min date since the latter is used for
-            // calculating the indices in the adapter thus
-            // avoiding out of bounds error
-            Calendar date = mAdapter.mSelectedDate;
-            if (date.before(mMinDate)) {
-                mAdapter.setSelectedDay(mMinDate);
-            }
-            // reinitialize the adapter since its range depends on min date
-            mAdapter.init();
-            if (date.before(mMinDate)) {
-                setDate(mTempDate.getTimeInMillis());
-            } else {
-                // we go to the current date to force the ListView to query its
-                // adapter for the shown views since we have changed the adapter
-                // range and the base from which the later calculates item indices
-                // note that calling setDate will not work since the date is the same
-                goTo(date, false, true, false);
-            }
-        }
-
-        @Override
-        public long getMinDate() {
-            return mMinDate.getTimeInMillis();
-        }
-
-        @Override
-        public void setMaxDate(long maxDate) {
-            mTempDate.setTimeInMillis(maxDate);
-            if (isSameDate(mTempDate, mMaxDate)) {
-                return;
-            }
-            mMaxDate.setTimeInMillis(maxDate);
-            // reinitialize the adapter since its range depends on max date
-            mAdapter.init();
-            Calendar date = mAdapter.mSelectedDate;
-            if (date.after(mMaxDate)) {
-                setDate(mMaxDate.getTimeInMillis());
-            } else {
-                // we go to the current date to force the ListView to query its
-                // adapter for the shown views since we have changed the adapter
-                // range and the base from which the later calculates item indices
-                // note that calling setDate will not work since the date is the same
-                goTo(date, false, true, false);
-            }
-        }
-
-        @Override
-        public long getMaxDate() {
-            return mMaxDate.getTimeInMillis();
-        }
-
-        @Override
-        public void setShowWeekNumber(boolean showWeekNumber) {
-            if (mShowWeekNumber == showWeekNumber) {
-                return;
-            }
-            mShowWeekNumber = showWeekNumber;
-            mAdapter.notifyDataSetChanged();
-            setUpHeader();
-        }
-
-        @Override
-        public boolean getShowWeekNumber() {
-            return mShowWeekNumber;
-        }
-
-        @Override
-        public void setFirstDayOfWeek(int firstDayOfWeek) {
-            if (mFirstDayOfWeek == firstDayOfWeek) {
-                return;
-            }
-            mFirstDayOfWeek = firstDayOfWeek;
-            mAdapter.init();
-            mAdapter.notifyDataSetChanged();
-            setUpHeader();
-        }
-
-        @Override
-        public int getFirstDayOfWeek() {
-            return mFirstDayOfWeek;
-        }
-
-        @Override
-        public void setDate(long date) {
-            setDate(date, false, false);
-        }
-
-        @Override
-        public void setDate(long date, boolean animate, boolean center) {
-            mTempDate.setTimeInMillis(date);
-            if (isSameDate(mTempDate, mAdapter.mSelectedDate)) {
-                return;
-            }
-            goTo(mTempDate, animate, true, center);
-        }
-
-        @Override
-        public long getDate() {
-            return mAdapter.mSelectedDate.getTimeInMillis();
-        }
-
-        @Override
-        public void setOnDateChangeListener(OnDateChangeListener listener) {
-            mOnDateChangeListener = listener;
-        }
-
-        @Override
-        public void onConfigurationChanged(Configuration newConfig) {
-            setCurrentLocale(newConfig.locale);
-        }
-
-        @Override
-        public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
-            event.setClassName(CalendarView.class.getName());
-        }
-
-        @Override
-        public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
-            info.setClassName(CalendarView.class.getName());
-        }
-
-        /**
-         * Sets the current locale.
-         *
-         * @param locale The current locale.
-         */
-        @Override
-        protected void setCurrentLocale(Locale locale) {
-            super.setCurrentLocale(locale);
-
-            mTempDate = getCalendarForLocale(mTempDate, locale);
-            mFirstDayOfMonth = getCalendarForLocale(mFirstDayOfMonth, locale);
-            mMinDate = getCalendarForLocale(mMinDate, locale);
-            mMaxDate = getCalendarForLocale(mMaxDate, locale);
-        }
-        private void updateDateTextSize() {
-            TypedArray dateTextAppearance = mDelegator.getContext().obtainStyledAttributes(
-                    mDateTextAppearanceResId, R.styleable.TextAppearance);
-            mDateTextSize = dateTextAppearance.getDimensionPixelSize(
-                    R.styleable.TextAppearance_textSize, DEFAULT_DATE_TEXT_SIZE);
-            dateTextAppearance.recycle();
-        }
-
-        /**
-         * Invalidates all week views.
-         */
-        private void invalidateAllWeekViews() {
-            final int childCount = mListView.getChildCount();
-            for (int i = 0; i < childCount; i++) {
-                View view = mListView.getChildAt(i);
-                view.invalidate();
-            }
-        }
-
-        /**
-         * Gets a calendar for locale bootstrapped with the value of a given calendar.
-         *
-         * @param oldCalendar The old calendar.
-         * @param locale The locale.
-         */
-        private static Calendar getCalendarForLocale(Calendar oldCalendar, Locale locale) {
-            if (oldCalendar == null) {
-                return Calendar.getInstance(locale);
-            } else {
-                final long currentTimeMillis = oldCalendar.getTimeInMillis();
-                Calendar newCalendar = Calendar.getInstance(locale);
-                newCalendar.setTimeInMillis(currentTimeMillis);
-                return newCalendar;
-            }
-        }
-
-        /**
-         * @return True if the <code>firstDate</code> is the same as the <code>
-         * secondDate</code>.
-         */
-        private static boolean isSameDate(Calendar firstDate, Calendar secondDate) {
-            return (firstDate.get(Calendar.DAY_OF_YEAR) == secondDate.get(Calendar.DAY_OF_YEAR)
-                    && firstDate.get(Calendar.YEAR) == secondDate.get(Calendar.YEAR));
-        }
-
-        /**
-         * Creates a new adapter if necessary and sets up its parameters.
-         */
-        private void setUpAdapter() {
-            if (mAdapter == null) {
-                mAdapter = new WeeksAdapter(mContext);
-                mAdapter.registerDataSetObserver(new DataSetObserver() {
-                    @Override
-                    public void onChanged() {
-                        if (mOnDateChangeListener != null) {
-                            Calendar selectedDay = mAdapter.getSelectedDay();
-                            mOnDateChangeListener.onSelectedDayChange(mDelegator,
-                                    selectedDay.get(Calendar.YEAR),
-                                    selectedDay.get(Calendar.MONTH),
-                                    selectedDay.get(Calendar.DAY_OF_MONTH));
-                        }
-                    }
-                });
-                mListView.setAdapter(mAdapter);
-            }
-
-            // refresh the view with the new parameters
-            mAdapter.notifyDataSetChanged();
-        }
-
-        /**
-         * Sets up the strings to be used by the header.
-         */
-        private void setUpHeader() {
-            mDayNamesShort = new String[mDaysPerWeek];
-            mDayNamesLong = new String[mDaysPerWeek];
-            for (int i = mFirstDayOfWeek, count = mFirstDayOfWeek + mDaysPerWeek; i < count; i++) {
-                int calendarDay = (i > Calendar.SATURDAY) ? i - Calendar.SATURDAY : i;
-                mDayNamesShort[i - mFirstDayOfWeek] = DateUtils.getDayOfWeekString(calendarDay,
-                        DateUtils.LENGTH_SHORTEST);
-                mDayNamesLong[i - mFirstDayOfWeek] = DateUtils.getDayOfWeekString(calendarDay,
-                        DateUtils.LENGTH_LONG);
-            }
-
-            TextView label = (TextView) mDayNamesHeader.getChildAt(0);
-            if (mShowWeekNumber) {
-                label.setVisibility(View.VISIBLE);
-            } else {
-                label.setVisibility(View.GONE);
-            }
-            for (int i = 1, count = mDayNamesHeader.getChildCount(); i < count; i++) {
-                label = (TextView) mDayNamesHeader.getChildAt(i);
-                if (mWeekDayTextAppearanceResId > -1) {
-                    label.setTextAppearance(mContext, mWeekDayTextAppearanceResId);
-                }
-                if (i < mDaysPerWeek + 1) {
-                    label.setText(mDayNamesShort[i - 1]);
-                    label.setContentDescription(mDayNamesLong[i - 1]);
-                    label.setVisibility(View.VISIBLE);
-                } else {
-                    label.setVisibility(View.GONE);
-                }
-            }
-            mDayNamesHeader.invalidate();
-        }
-
-        /**
-         * Sets all the required fields for the list view.
-         */
-        private void setUpListView() {
-            // Configure the listview
-            mListView.setDivider(null);
-            mListView.setItemsCanFocus(true);
-            mListView.setVerticalScrollBarEnabled(false);
-            mListView.setOnScrollListener(new OnScrollListener() {
-                public void onScrollStateChanged(AbsListView view, int scrollState) {
-                    LegacyCalendarViewDelegate.this.onScrollStateChanged(view, scrollState);
-                }
-
-                public void onScroll(
-                        AbsListView view, int firstVisibleItem, int visibleItemCount,
-                        int totalItemCount) {
-                    LegacyCalendarViewDelegate.this.onScroll(view, firstVisibleItem,
-                            visibleItemCount, totalItemCount);
-                }
-            });
-            // Make the scrolling behavior nicer
-            mListView.setFriction(mFriction);
-            mListView.setVelocityScale(mVelocityScale);
-        }
-
-        /**
-         * This moves to the specified time in the view. If the time is not already
-         * in range it will move the list so that the first of the month containing
-         * the time is at the top of the view. If the new time is already in view
-         * the list will not be scrolled unless forceScroll is true. This time may
-         * optionally be highlighted as selected as well.
-         *
-         * @param date The time to move to.
-         * @param animate Whether to scroll to the given time or just redraw at the
-         *            new location.
-         * @param setSelected Whether to set the given time as selected.
-         * @param forceScroll Whether to recenter even if the time is already
-         *            visible.
-         *
-         * @throws IllegalArgumentException of the provided date is before the
-         *        range start of after the range end.
-         */
-        private void goTo(Calendar date, boolean animate, boolean setSelected,
-                boolean forceScroll) {
-            if (date.before(mMinDate) || date.after(mMaxDate)) {
-                throw new IllegalArgumentException("Time not between " + mMinDate.getTime()
-                        + " and " + mMaxDate.getTime());
-            }
-            // Find the first and last entirely visible weeks
-            int firstFullyVisiblePosition = mListView.getFirstVisiblePosition();
-            View firstChild = mListView.getChildAt(0);
-            if (firstChild != null && firstChild.getTop() < 0) {
-                firstFullyVisiblePosition++;
-            }
-            int lastFullyVisiblePosition = firstFullyVisiblePosition + mShownWeekCount - 1;
-            if (firstChild != null && firstChild.getTop() > mBottomBuffer) {
-                lastFullyVisiblePosition--;
-            }
-            if (setSelected) {
-                mAdapter.setSelectedDay(date);
-            }
-            // Get the week we're going to
-            int position = getWeeksSinceMinDate(date);
-
-            // Check if the selected day is now outside of our visible range
-            // and if so scroll to the month that contains it
-            if (position < firstFullyVisiblePosition || position > lastFullyVisiblePosition
-                    || forceScroll) {
-                mFirstDayOfMonth.setTimeInMillis(date.getTimeInMillis());
-                mFirstDayOfMonth.set(Calendar.DAY_OF_MONTH, 1);
-
-                setMonthDisplayed(mFirstDayOfMonth);
-
-                // the earliest time we can scroll to is the min date
-                if (mFirstDayOfMonth.before(mMinDate)) {
-                    position = 0;
-                } else {
-                    position = getWeeksSinceMinDate(mFirstDayOfMonth);
-                }
-
-                mPreviousScrollState = OnScrollListener.SCROLL_STATE_FLING;
-                if (animate) {
-                    mListView.smoothScrollToPositionFromTop(position, mListScrollTopOffset,
-                            GOTO_SCROLL_DURATION);
-                } else {
-                    mListView.setSelectionFromTop(position, mListScrollTopOffset);
-                    // Perform any after scroll operations that are needed
-                    onScrollStateChanged(mListView, OnScrollListener.SCROLL_STATE_IDLE);
-                }
-            } else if (setSelected) {
-                // Otherwise just set the selection
-                setMonthDisplayed(date);
-            }
-        }
 
         /**
          * Parses the given <code>date</code> and in case of success sets
@@ -1444,718 +607,15 @@
          *
          * @return True if the date was parsed.
          */
-        private boolean parseDate(String date, Calendar outDate) {
+        protected boolean parseDate(String date, Calendar outDate) {
             try {
-                outDate.setTime(mDateFormat.parse(date));
+                outDate.setTime(DATE_FORMATTER.parse(date));
                 return true;
             } catch (ParseException e) {
                 Log.w(LOG_TAG, "Date: " + date + " not in format: " + DATE_FORMAT);
                 return false;
             }
         }
-
-        /**
-         * Called when a <code>view</code> transitions to a new <code>scrollState
-         * </code>.
-         */
-        private void onScrollStateChanged(AbsListView view, int scrollState) {
-            mScrollStateChangedRunnable.doScrollStateChange(view, scrollState);
-        }
-
-        /**
-         * Updates the title and selected month if the <code>view</code> has moved to a new
-         * month.
-         */
-        private void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
-                              int totalItemCount) {
-            WeekView child = (WeekView) view.getChildAt(0);
-            if (child == null) {
-                return;
-            }
-
-            // Figure out where we are
-            long currScroll =
-                    view.getFirstVisiblePosition() * child.getHeight() - child.getBottom();
-
-            // If we have moved since our last call update the direction
-            if (currScroll < mPreviousScrollPosition) {
-                mIsScrollingUp = true;
-            } else if (currScroll > mPreviousScrollPosition) {
-                mIsScrollingUp = false;
-            } else {
-                return;
-            }
-
-            // Use some hysteresis for checking which month to highlight. This
-            // causes the month to transition when two full weeks of a month are
-            // visible when scrolling up, and when the first day in a month reaches
-            // the top of the screen when scrolling down.
-            int offset = child.getBottom() < mWeekMinVisibleHeight ? 1 : 0;
-            if (mIsScrollingUp) {
-                child = (WeekView) view.getChildAt(SCROLL_HYST_WEEKS + offset);
-            } else if (offset != 0) {
-                child = (WeekView) view.getChildAt(offset);
-            }
-
-            if (child != null) {
-                // 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 (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;
-        }
-
-        /**
-         * Sets the month displayed at the top of this view based on time. Override
-         * to add custom events when the title is changed.
-         *
-         * @param calendar A day in the new focus month.
-         */
-        private void setMonthDisplayed(Calendar calendar) {
-            mCurrentMonthDisplayed = calendar.get(Calendar.MONTH);
-            mAdapter.setFocusMonth(mCurrentMonthDisplayed);
-            final int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NO_MONTH_DAY
-                    | DateUtils.FORMAT_SHOW_YEAR;
-            final long millis = calendar.getTimeInMillis();
-            String newMonthName = DateUtils.formatDateRange(mContext, millis, millis, flags);
-            mMonthName.setText(newMonthName);
-            mMonthName.invalidate();
-        }
-
-        /**
-         * @return Returns the number of weeks between the current <code>date</code>
-         *         and the <code>mMinDate</code>.
-         */
-        private int getWeeksSinceMinDate(Calendar date) {
-            if (date.before(mMinDate)) {
-                throw new IllegalArgumentException("fromDate: " + mMinDate.getTime()
-                        + " does not precede toDate: " + date.getTime());
-            }
-            long endTimeMillis = date.getTimeInMillis()
-                    + date.getTimeZone().getOffset(date.getTimeInMillis());
-            long startTimeMillis = mMinDate.getTimeInMillis()
-                    + mMinDate.getTimeZone().getOffset(mMinDate.getTimeInMillis());
-            long dayOffsetMillis = (mMinDate.get(Calendar.DAY_OF_WEEK) - mFirstDayOfWeek)
-                    * MILLIS_IN_DAY;
-            return (int) ((endTimeMillis - startTimeMillis + dayOffsetMillis) / MILLIS_IN_WEEK);
-        }
-
-        /**
-         * Command responsible for acting upon scroll state changes.
-         */
-        private class ScrollStateRunnable implements Runnable {
-            private AbsListView mView;
-
-            private int mNewState;
-
-            /**
-             * Sets up the runnable with a short delay in case the scroll state
-             * immediately changes again.
-             *
-             * @param view The list view that changed state
-             * @param scrollState The new state it changed to
-             */
-            public void doScrollStateChange(AbsListView view, int scrollState) {
-                mView = view;
-                mNewState = scrollState;
-                mDelegator.removeCallbacks(this);
-                mDelegator.postDelayed(this, SCROLL_CHANGE_DELAY);
-            }
-
-            public void run() {
-                mCurrentScrollState = mNewState;
-                // Fix the position after a scroll or a fling ends
-                if (mNewState == OnScrollListener.SCROLL_STATE_IDLE
-                        && mPreviousScrollState != OnScrollListener.SCROLL_STATE_IDLE) {
-                    View child = mView.getChildAt(0);
-                    if (child == null) {
-                        // The view is no longer visible, just return
-                        return;
-                    }
-                    int dist = child.getBottom() - mListScrollTopOffset;
-                    if (dist > mListScrollTopOffset) {
-                        if (mIsScrollingUp) {
-                            mView.smoothScrollBy(dist - child.getHeight(),
-                                    ADJUSTMENT_SCROLL_DURATION);
-                        } else {
-                            mView.smoothScrollBy(dist, ADJUSTMENT_SCROLL_DURATION);
-                        }
-                    }
-                }
-                mPreviousScrollState = mNewState;
-            }
-        }
-
-        /**
-         * <p>
-         * This is a specialized adapter for creating a list of weeks with
-         * selectable days. It can be configured to display the week number, start
-         * the week on a given day, show a reduced number of days, or display an
-         * arbitrary number of weeks at a time.
-         * </p>
-         */
-        private class WeeksAdapter extends BaseAdapter implements OnTouchListener {
-
-            private int mSelectedWeek;
-
-            private GestureDetector mGestureDetector;
-
-            private int mFocusedMonth;
-
-            private final Calendar mSelectedDate = Calendar.getInstance();
-
-            private int mTotalWeekCount;
-
-            public WeeksAdapter(Context context) {
-                mContext = context;
-                mGestureDetector = new GestureDetector(mContext, new CalendarGestureListener());
-                init();
-            }
-
-            /**
-             * Set up the gesture detector and selected time
-             */
-            private void init() {
-                mSelectedWeek = getWeeksSinceMinDate(mSelectedDate);
-                mTotalWeekCount = getWeeksSinceMinDate(mMaxDate);
-                if (mMinDate.get(Calendar.DAY_OF_WEEK) != mFirstDayOfWeek
-                        || mMaxDate.get(Calendar.DAY_OF_WEEK) != mFirstDayOfWeek) {
-                    mTotalWeekCount++;
-                }
-                notifyDataSetChanged();
-            }
-
-            /**
-             * Updates the selected day and related parameters.
-             *
-             * @param selectedDay The time to highlight
-             */
-            public void setSelectedDay(Calendar selectedDay) {
-                if (selectedDay.get(Calendar.DAY_OF_YEAR) == mSelectedDate.get(Calendar.DAY_OF_YEAR)
-                        && selectedDay.get(Calendar.YEAR) == mSelectedDate.get(Calendar.YEAR)) {
-                    return;
-                }
-                mSelectedDate.setTimeInMillis(selectedDay.getTimeInMillis());
-                mSelectedWeek = getWeeksSinceMinDate(mSelectedDate);
-                mFocusedMonth = mSelectedDate.get(Calendar.MONTH);
-                notifyDataSetChanged();
-            }
-
-            /**
-             * @return The selected day of month.
-             */
-            public Calendar getSelectedDay() {
-                return mSelectedDate;
-            }
-
-            @Override
-            public int getCount() {
-                return mTotalWeekCount;
-            }
-
-            @Override
-            public Object getItem(int position) {
-                return null;
-            }
-
-            @Override
-            public long getItemId(int position) {
-                return position;
-            }
-
-            @Override
-            public View getView(int position, View convertView, ViewGroup parent) {
-                WeekView weekView = null;
-                if (convertView != null) {
-                    weekView = (WeekView) convertView;
-                } else {
-                    weekView = new WeekView(mContext);
-                    android.widget.AbsListView.LayoutParams params =
-                            new android.widget.AbsListView.LayoutParams(LayoutParams.WRAP_CONTENT,
-                                    LayoutParams.WRAP_CONTENT);
-                    weekView.setLayoutParams(params);
-                    weekView.setClickable(true);
-                    weekView.setOnTouchListener(this);
-                }
-
-                int selectedWeekDay = (mSelectedWeek == position) ? mSelectedDate.get(
-                        Calendar.DAY_OF_WEEK) : -1;
-                weekView.init(position, selectedWeekDay, mFocusedMonth);
-
-                return weekView;
-            }
-
-            /**
-             * Changes which month is in focus and updates the view.
-             *
-             * @param month The month to show as in focus [0-11]
-             */
-            public void setFocusMonth(int month) {
-                if (mFocusedMonth == month) {
-                    return;
-                }
-                mFocusedMonth = month;
-                notifyDataSetChanged();
-            }
-
-            @Override
-            public boolean onTouch(View v, MotionEvent event) {
-                if (mListView.isEnabled() && mGestureDetector.onTouchEvent(event)) {
-                    WeekView weekView = (WeekView) v;
-                    // if we cannot find a day for the given location we are done
-                    if (!weekView.getDayFromLocation(event.getX(), mTempDate)) {
-                        return true;
-                    }
-                    // it is possible that the touched day is outside the valid range
-                    // we draw whole weeks but range end can fall not on the week end
-                    if (mTempDate.before(mMinDate) || mTempDate.after(mMaxDate)) {
-                        return true;
-                    }
-                    onDateTapped(mTempDate);
-                    return true;
-                }
-                return false;
-            }
-
-            /**
-             * Maintains the same hour/min/sec but moves the day to the tapped day.
-             *
-             * @param day The day that was tapped
-             */
-            private void onDateTapped(Calendar day) {
-                setSelectedDay(day);
-                setMonthDisplayed(day);
-            }
-
-            /**
-             * This is here so we can identify single tap events and set the
-             * selected day correctly
-             */
-            class CalendarGestureListener extends GestureDetector.SimpleOnGestureListener {
-                @Override
-                public boolean onSingleTapUp(MotionEvent e) {
-                    return true;
-                }
-            }
-        }
-
-        /**
-         * <p>
-         * This is a dynamic view for drawing a single week. It can be configured to
-         * display the week number, start the week on a given day, or show a reduced
-         * number of days. It is intended for use as a single view within a
-         * ListView. See {@link WeeksAdapter} for usage.
-         * </p>
-         */
-        private class WeekView extends View {
-
-            private final Rect mTempRect = new Rect();
-
-            private final Paint mDrawPaint = new Paint();
-
-            private final Paint mMonthNumDrawPaint = new Paint();
-
-            // Cache the number strings so we don't have to recompute them each time
-            private String[] mDayNumbers;
-
-            // Quick lookup for checking which days are in the focus month
-            private boolean[] mFocusDay;
-
-            // Whether this view has a focused day.
-            private boolean mHasFocusedDay;
-
-            // Whether this view has only focused days.
-            private boolean mHasUnfocusedDay;
-
-            // The first day displayed by this item
-            private Calendar mFirstDay;
-
-            // The month of the first day in this week
-            private int mMonthOfFirstWeekDay = -1;
-
-            // The month of the last day in this week
-            private int mLastWeekDayMonth = -1;
-
-            // The position of this week, equivalent to weeks since the week of Jan
-            // 1st, 1900
-            private int mWeek = -1;
-
-            // Quick reference to the width of this view, matches parent
-            private int mWidth;
-
-            // The height this view should draw at in pixels, set by height param
-            private int mHeight;
-
-            // If this view contains the selected day
-            private boolean mHasSelectedDay = false;
-
-            // Which day is selected [0-6] or -1 if no day is selected
-            private int mSelectedDay = -1;
-
-            // The number of days + a spot for week number if it is displayed
-            private int mNumCells;
-
-            // The left edge of the selected day
-            private int mSelectedLeft = -1;
-
-            // The right edge of the selected day
-            private int mSelectedRight = -1;
-
-            public WeekView(Context context) {
-                super(context);
-
-                // Sets up any standard paints that will be used
-                initilaizePaints();
-            }
-
-            /**
-             * Initializes this week view.
-             *
-             * @param weekNumber The number of the week this view represents. The
-             *            week number is a zero based index of the weeks since
-             *            {@link CalendarView#getMinDate()}.
-             * @param selectedWeekDay The selected day of the week from 0 to 6, -1 if no
-             *            selected day.
-             * @param focusedMonth The month that is currently in focus i.e.
-             *            highlighted.
-             */
-            public void init(int weekNumber, int selectedWeekDay, int focusedMonth) {
-                mSelectedDay = selectedWeekDay;
-                mHasSelectedDay = mSelectedDay != -1;
-                mNumCells = mShowWeekNumber ? mDaysPerWeek + 1 : mDaysPerWeek;
-                mWeek = weekNumber;
-                mTempDate.setTimeInMillis(mMinDate.getTimeInMillis());
-
-                mTempDate.add(Calendar.WEEK_OF_YEAR, mWeek);
-                mTempDate.setFirstDayOfWeek(mFirstDayOfWeek);
-
-                // Allocate space for caching the day numbers and focus values
-                mDayNumbers = new String[mNumCells];
-                mFocusDay = new boolean[mNumCells];
-
-                // If we're showing the week number calculate it based on Monday
-                int i = 0;
-                if (mShowWeekNumber) {
-                    mDayNumbers[0] = String.format(Locale.getDefault(), "%d",
-                            mTempDate.get(Calendar.WEEK_OF_YEAR));
-                    i++;
-                }
-
-                // Now adjust our starting day based on the start day of the week
-                int diff = mFirstDayOfWeek - mTempDate.get(Calendar.DAY_OF_WEEK);
-                mTempDate.add(Calendar.DAY_OF_MONTH, diff);
-
-                mFirstDay = (Calendar) mTempDate.clone();
-                mMonthOfFirstWeekDay = mTempDate.get(Calendar.MONTH);
-
-                mHasUnfocusedDay = true;
-                for (; i < mNumCells; i++) {
-                    final boolean isFocusedDay = (mTempDate.get(Calendar.MONTH) == focusedMonth);
-                    mFocusDay[i] = isFocusedDay;
-                    mHasFocusedDay |= isFocusedDay;
-                    mHasUnfocusedDay &= !isFocusedDay;
-                    // do not draw dates outside the valid range to avoid user confusion
-                    if (mTempDate.before(mMinDate) || mTempDate.after(mMaxDate)) {
-                        mDayNumbers[i] = "";
-                    } else {
-                        mDayNumbers[i] = String.format(Locale.getDefault(), "%d",
-                                mTempDate.get(Calendar.DAY_OF_MONTH));
-                    }
-                    mTempDate.add(Calendar.DAY_OF_MONTH, 1);
-                }
-                // We do one extra add at the end of the loop, if that pushed us to
-                // new month undo it
-                if (mTempDate.get(Calendar.DAY_OF_MONTH) == 1) {
-                    mTempDate.add(Calendar.DAY_OF_MONTH, -1);
-                }
-                mLastWeekDayMonth = mTempDate.get(Calendar.MONTH);
-
-                updateSelectionPositions();
-            }
-
-            /**
-             * Initialize the paint instances.
-             */
-            private void initilaizePaints() {
-                mDrawPaint.setFakeBoldText(false);
-                mDrawPaint.setAntiAlias(true);
-                mDrawPaint.setStyle(Style.FILL);
-
-                mMonthNumDrawPaint.setFakeBoldText(true);
-                mMonthNumDrawPaint.setAntiAlias(true);
-                mMonthNumDrawPaint.setStyle(Style.FILL);
-                mMonthNumDrawPaint.setTextAlign(Align.CENTER);
-                mMonthNumDrawPaint.setTextSize(mDateTextSize);
-            }
-
-            /**
-             * Returns the month of the first day in this week.
-             *
-             * @return The month the first day of this view is in.
-             */
-            public int getMonthOfFirstWeekDay() {
-                return mMonthOfFirstWeekDay;
-            }
-
-            /**
-             * Returns the month of the last day in this week
-             *
-             * @return The month the last day of this view is in
-             */
-            public int getMonthOfLastWeekDay() {
-                return mLastWeekDayMonth;
-            }
-
-            /**
-             * Returns the first day in this view.
-             *
-             * @return The first day in the view.
-             */
-            public Calendar getFirstDay() {
-                return mFirstDay;
-            }
-
-            /**
-             * Calculates the day that the given x position is in, accounting for
-             * week number.
-             *
-             * @param x The x position of the touch event.
-             * @return True if a day was found for the given location.
-             */
-            public boolean getDayFromLocation(float x, Calendar outCalendar) {
-                final boolean isLayoutRtl = isLayoutRtl();
-
-                int start;
-                int end;
-
-                if (isLayoutRtl) {
-                    start = 0;
-                    end = mShowWeekNumber ? mWidth - mWidth / mNumCells : mWidth;
-                } else {
-                    start = mShowWeekNumber ? mWidth / mNumCells : 0;
-                    end = mWidth;
-                }
-
-                if (x < start || x > end) {
-                    outCalendar.clear();
-                    return false;
-                }
-
-                // Selection is (x - start) / (pixels/day) which is (x - start) * day / pixels
-                int dayPosition = (int) ((x - start) * mDaysPerWeek / (end - start));
-
-                if (isLayoutRtl) {
-                    dayPosition = mDaysPerWeek - 1 - dayPosition;
-                }
-
-                outCalendar.setTimeInMillis(mFirstDay.getTimeInMillis());
-                outCalendar.add(Calendar.DAY_OF_MONTH, dayPosition);
-
-                return true;
-            }
-
-            @Override
-            protected void onDraw(Canvas canvas) {
-                drawBackground(canvas);
-                drawWeekNumbersAndDates(canvas);
-                drawWeekSeparators(canvas);
-                drawSelectedDateVerticalBars(canvas);
-            }
-
-            /**
-             * This draws the selection highlight if a day is selected in this week.
-             *
-             * @param canvas The canvas to draw on
-             */
-            private void drawBackground(Canvas canvas) {
-                if (!mHasSelectedDay) {
-                    return;
-                }
-                mDrawPaint.setColor(mSelectedWeekBackgroundColor);
-
-                mTempRect.top = mWeekSeperatorLineWidth;
-                mTempRect.bottom = mHeight;
-
-                final boolean isLayoutRtl = isLayoutRtl();
-
-                if (isLayoutRtl) {
-                    mTempRect.left = 0;
-                    mTempRect.right = mSelectedLeft - 2;
-                } else {
-                    mTempRect.left = mShowWeekNumber ? mWidth / mNumCells : 0;
-                    mTempRect.right = mSelectedLeft - 2;
-                }
-                canvas.drawRect(mTempRect, mDrawPaint);
-
-                if (isLayoutRtl) {
-                    mTempRect.left = mSelectedRight + 3;
-                    mTempRect.right = mShowWeekNumber ? mWidth - mWidth / mNumCells : mWidth;
-                } else {
-                    mTempRect.left = mSelectedRight + 3;
-                    mTempRect.right = mWidth;
-                }
-                canvas.drawRect(mTempRect, mDrawPaint);
-            }
-
-            /**
-             * Draws the week and month day numbers for this week.
-             *
-             * @param canvas The canvas to draw on
-             */
-            private void drawWeekNumbersAndDates(Canvas canvas) {
-                final float textHeight = mDrawPaint.getTextSize();
-                final int y = (int) ((mHeight + textHeight) / 2) - mWeekSeperatorLineWidth;
-                final int nDays = mNumCells;
-                final int divisor = 2 * nDays;
-
-                mDrawPaint.setTextAlign(Align.CENTER);
-                mDrawPaint.setTextSize(mDateTextSize);
-
-                int i = 0;
-
-                if (isLayoutRtl()) {
-                    for (; i < nDays - 1; i++) {
-                        mMonthNumDrawPaint.setColor(mFocusDay[i] ? mFocusedMonthDateColor
-                                : mUnfocusedMonthDateColor);
-                        int x = (2 * i + 1) * mWidth / divisor;
-                        canvas.drawText(mDayNumbers[nDays - 1 - i], x, y, mMonthNumDrawPaint);
-                    }
-                    if (mShowWeekNumber) {
-                        mDrawPaint.setColor(mWeekNumberColor);
-                        int x = mWidth - mWidth / divisor;
-                        canvas.drawText(mDayNumbers[0], x, y, mDrawPaint);
-                    }
-                } else {
-                    if (mShowWeekNumber) {
-                        mDrawPaint.setColor(mWeekNumberColor);
-                        int x = mWidth / divisor;
-                        canvas.drawText(mDayNumbers[0], x, y, mDrawPaint);
-                        i++;
-                    }
-                    for (; i < nDays; i++) {
-                        mMonthNumDrawPaint.setColor(mFocusDay[i] ? mFocusedMonthDateColor
-                                : mUnfocusedMonthDateColor);
-                        int x = (2 * i + 1) * mWidth / divisor;
-                        canvas.drawText(mDayNumbers[i], x, y, mMonthNumDrawPaint);
-                    }
-                }
-            }
-
-            /**
-             * Draws a horizontal line for separating the weeks.
-             *
-             * @param canvas The canvas to draw on.
-             */
-            private void drawWeekSeparators(Canvas canvas) {
-                // If it is the topmost fully visible child do not draw separator line
-                int firstFullyVisiblePosition = mListView.getFirstVisiblePosition();
-                if (mListView.getChildAt(0).getTop() < 0) {
-                    firstFullyVisiblePosition++;
-                }
-                if (firstFullyVisiblePosition == mWeek) {
-                    return;
-                }
-                mDrawPaint.setColor(mWeekSeparatorLineColor);
-                mDrawPaint.setStrokeWidth(mWeekSeperatorLineWidth);
-                float startX;
-                float stopX;
-                if (isLayoutRtl()) {
-                    startX = 0;
-                    stopX = mShowWeekNumber ? mWidth - mWidth / mNumCells : mWidth;
-                } else {
-                    startX = mShowWeekNumber ? mWidth / mNumCells : 0;
-                    stopX = mWidth;
-                }
-                canvas.drawLine(startX, 0, stopX, 0, mDrawPaint);
-            }
-
-            /**
-             * Draws the selected date bars if this week has a selected day.
-             *
-             * @param canvas The canvas to draw on
-             */
-            private void drawSelectedDateVerticalBars(Canvas canvas) {
-                if (!mHasSelectedDay) {
-                    return;
-                }
-                mSelectedDateVerticalBar.setBounds(
-                        mSelectedLeft - mSelectedDateVerticalBarWidth / 2,
-                        mWeekSeperatorLineWidth,
-                        mSelectedLeft + mSelectedDateVerticalBarWidth / 2,
-                        mHeight);
-                mSelectedDateVerticalBar.draw(canvas);
-                mSelectedDateVerticalBar.setBounds(
-                        mSelectedRight - mSelectedDateVerticalBarWidth / 2,
-                        mWeekSeperatorLineWidth,
-                        mSelectedRight + mSelectedDateVerticalBarWidth / 2,
-                        mHeight);
-                mSelectedDateVerticalBar.draw(canvas);
-            }
-
-            @Override
-            protected void onSizeChanged(int w, int h, int oldw, int oldh) {
-                mWidth = w;
-                updateSelectionPositions();
-            }
-
-            /**
-             * This calculates the positions for the selected day lines.
-             */
-            private void updateSelectionPositions() {
-                if (mHasSelectedDay) {
-                    final boolean isLayoutRtl = isLayoutRtl();
-                    int selectedPosition = mSelectedDay - mFirstDayOfWeek;
-                    if (selectedPosition < 0) {
-                        selectedPosition += 7;
-                    }
-                    if (mShowWeekNumber && !isLayoutRtl) {
-                        selectedPosition++;
-                    }
-                    if (isLayoutRtl) {
-                        mSelectedLeft = (mDaysPerWeek - 1 - selectedPosition) * mWidth / mNumCells;
-
-                    } else {
-                        mSelectedLeft = selectedPosition * mWidth / mNumCells;
-                    }
-                    mSelectedRight = mSelectedLeft + mWidth / mNumCells;
-                }
-            }
-
-            @Override
-            protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-                mHeight = (mListView.getHeight() - mListView.getPaddingTop() - mListView
-                        .getPaddingBottom()) / mShownWeekCount;
-                setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), mHeight);
-            }
-        }
-
     }
 
 }
diff --git a/core/java/android/widget/CalendarViewLegacyDelegate.java b/core/java/android/widget/CalendarViewLegacyDelegate.java
new file mode 100644
index 0000000..2ab3548
--- /dev/null
+++ b/core/java/android/widget/CalendarViewLegacyDelegate.java
@@ -0,0 +1,1527 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget;
+
+import com.android.internal.R;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.TypedArray;
+import android.database.DataSetObserver;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.text.TextUtils;
+import android.text.format.DateUtils;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
+import android.view.GestureDetector;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.Calendar;
+import java.util.Locale;
+
+import libcore.icu.LocaleData;
+
+/**
+ * A delegate implementing the legacy CalendarView
+ */
+class CalendarViewLegacyDelegate extends CalendarView.AbstractCalendarViewDelegate {
+    /**
+     * Default value whether to show week number.
+     */
+    private static final boolean DEFAULT_SHOW_WEEK_NUMBER = true;
+
+    /**
+     * The number of milliseconds in a day.e
+     */
+    private static final long MILLIS_IN_DAY = 86400000L;
+
+    /**
+     * The number of day in a week.
+     */
+    private static final int DAYS_PER_WEEK = 7;
+
+    /**
+     * The number of milliseconds in a week.
+     */
+    private static final long MILLIS_IN_WEEK = DAYS_PER_WEEK * MILLIS_IN_DAY;
+
+    /**
+     * Affects when the month selection will change while scrolling upe
+     */
+    private static final int SCROLL_HYST_WEEKS = 2;
+
+    /**
+     * How long the GoTo fling animation should last.
+     */
+    private static final int GOTO_SCROLL_DURATION = 1000;
+
+    /**
+     * The duration of the adjustment upon a user scroll in milliseconds.
+     */
+    private static final int ADJUSTMENT_SCROLL_DURATION = 500;
+
+    /**
+     * How long to wait after receiving an onScrollStateChanged notification
+     * before acting on it.
+     */
+    private static final int SCROLL_CHANGE_DELAY = 40;
+
+    private static final int DEFAULT_SHOWN_WEEK_COUNT = 6;
+
+    private static final int DEFAULT_DATE_TEXT_SIZE = 14;
+
+    private static final int UNSCALED_SELECTED_DATE_VERTICAL_BAR_WIDTH = 6;
+
+    private static final int UNSCALED_WEEK_MIN_VISIBLE_HEIGHT = 12;
+
+    private static final int UNSCALED_LIST_SCROLL_TOP_OFFSET = 2;
+
+    private static final int UNSCALED_BOTTOM_BUFFER = 20;
+
+    private static final int UNSCALED_WEEK_SEPARATOR_LINE_WIDTH = 1;
+
+    private static final int DEFAULT_WEEK_DAY_TEXT_APPEARANCE_RES_ID = -1;
+
+    private final int mWeekSeperatorLineWidth;
+
+    private int mDateTextSize;
+
+    private Drawable mSelectedDateVerticalBar;
+
+    private final int mSelectedDateVerticalBarWidth;
+
+    private int mSelectedWeekBackgroundColor;
+
+    private int mFocusedMonthDateColor;
+
+    private int mUnfocusedMonthDateColor;
+
+    private int mWeekSeparatorLineColor;
+
+    private int mWeekNumberColor;
+
+    private int mWeekDayTextAppearanceResId;
+
+    private int mDateTextAppearanceResId;
+
+    /**
+     * The top offset of the weeks list.
+     */
+    private int mListScrollTopOffset = 2;
+
+    /**
+     * The visible height of a week view.
+     */
+    private int mWeekMinVisibleHeight = 12;
+
+    /**
+     * The visible height of a week view.
+     */
+    private int mBottomBuffer = 20;
+
+    /**
+     * The number of shown weeks.
+     */
+    private int mShownWeekCount;
+
+    /**
+     * Flag whether to show the week number.
+     */
+    private boolean mShowWeekNumber;
+
+    /**
+     * The number of day per week to be shown.
+     */
+    private int mDaysPerWeek = 7;
+
+    /**
+     * The friction of the week list while flinging.
+     */
+    private float mFriction = .05f;
+
+    /**
+     * Scale for adjusting velocity of the week list while flinging.
+     */
+    private float mVelocityScale = 0.333f;
+
+    /**
+     * The adapter for the weeks list.
+     */
+    private WeeksAdapter mAdapter;
+
+    /**
+     * The weeks list.
+     */
+    private ListView mListView;
+
+    /**
+     * The name of the month to display.
+     */
+    private TextView mMonthName;
+
+    /**
+     * The header with week day names.
+     */
+    private ViewGroup mDayNamesHeader;
+
+    /**
+     * Cached abbreviations for day of week names.
+     */
+    private String[] mDayNamesShort;
+
+    /**
+     * Cached full-length day of week names.
+     */
+    private String[] mDayNamesLong;
+
+    /**
+     * The first day of the week.
+     */
+    private int mFirstDayOfWeek;
+
+    /**
+     * Which month should be displayed/highlighted [0-11].
+     */
+    private int mCurrentMonthDisplayed = -1;
+
+    /**
+     * Used for tracking during a scroll.
+     */
+    private long mPreviousScrollPosition;
+
+    /**
+     * Used for tracking which direction the view is scrolling.
+     */
+    private boolean mIsScrollingUp = false;
+
+    /**
+     * The previous scroll state of the weeks ListView.
+     */
+    private int mPreviousScrollState = AbsListView.OnScrollListener.SCROLL_STATE_IDLE;
+
+    /**
+     * The current scroll state of the weeks ListView.
+     */
+    private int mCurrentScrollState = AbsListView.OnScrollListener.SCROLL_STATE_IDLE;
+
+    /**
+     * Listener for changes in the selected day.
+     */
+    private CalendarView.OnDateChangeListener mOnDateChangeListener;
+
+    /**
+     * Command for adjusting the position after a scroll/fling.
+     */
+    private ScrollStateRunnable mScrollStateChangedRunnable = new ScrollStateRunnable();
+
+    /**
+     * Temporary instance to avoid multiple instantiations.
+     */
+    private Calendar mTempDate;
+
+    /**
+     * The first day of the focused month.
+     */
+    private Calendar mFirstDayOfMonth;
+
+    /**
+     * The start date of the range supported by this picker.
+     */
+    private Calendar mMinDate;
+
+    /**
+     * The end date of the range supported by this picker.
+     */
+    private Calendar mMaxDate;
+
+    CalendarViewLegacyDelegate(CalendarView delegator, Context context, AttributeSet attrs,
+            int defStyleAttr, int defStyleRes) {
+        super(delegator, context);
+
+        final TypedArray a = context.obtainStyledAttributes(attrs,
+                R.styleable.CalendarView, defStyleAttr, defStyleRes);
+        mShowWeekNumber = a.getBoolean(R.styleable.CalendarView_showWeekNumber,
+                DEFAULT_SHOW_WEEK_NUMBER);
+        mFirstDayOfWeek = a.getInt(R.styleable.CalendarView_firstDayOfWeek,
+                LocaleData.get(Locale.getDefault()).firstDayOfWeek);
+        final String minDate = a.getString(R.styleable.CalendarView_minDate);
+        if (TextUtils.isEmpty(minDate) || !parseDate(minDate, mMinDate)) {
+            parseDate(DEFAULT_MIN_DATE, mMinDate);
+        }
+        final String maxDate = a.getString(R.styleable.CalendarView_maxDate);
+        if (TextUtils.isEmpty(maxDate) || !parseDate(maxDate, mMaxDate)) {
+            parseDate(DEFAULT_MAX_DATE, mMaxDate);
+        }
+        if (mMaxDate.before(mMinDate)) {
+            throw new IllegalArgumentException("Max date cannot be before min date.");
+        }
+        mShownWeekCount = a.getInt(R.styleable.CalendarView_shownWeekCount,
+                DEFAULT_SHOWN_WEEK_COUNT);
+        mSelectedWeekBackgroundColor = a.getColor(
+                R.styleable.CalendarView_selectedWeekBackgroundColor, 0);
+        mFocusedMonthDateColor = a.getColor(
+                R.styleable.CalendarView_focusedMonthDateColor, 0);
+        mUnfocusedMonthDateColor = a.getColor(
+                R.styleable.CalendarView_unfocusedMonthDateColor, 0);
+        mWeekSeparatorLineColor = a.getColor(
+                R.styleable.CalendarView_weekSeparatorLineColor, 0);
+        mWeekNumberColor = a.getColor(R.styleable.CalendarView_weekNumberColor, 0);
+        mSelectedDateVerticalBar = a.getDrawable(
+                R.styleable.CalendarView_selectedDateVerticalBar);
+
+        mDateTextAppearanceResId = a.getResourceId(
+                R.styleable.CalendarView_dateTextAppearance, R.style.TextAppearance_Small);
+        updateDateTextSize();
+
+        mWeekDayTextAppearanceResId = a.getResourceId(
+                R.styleable.CalendarView_weekDayTextAppearance,
+                DEFAULT_WEEK_DAY_TEXT_APPEARANCE_RES_ID);
+        a.recycle();
+
+        DisplayMetrics displayMetrics = mDelegator.getResources().getDisplayMetrics();
+        mWeekMinVisibleHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+                UNSCALED_WEEK_MIN_VISIBLE_HEIGHT, displayMetrics);
+        mListScrollTopOffset = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+                UNSCALED_LIST_SCROLL_TOP_OFFSET, displayMetrics);
+        mBottomBuffer = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+                UNSCALED_BOTTOM_BUFFER, displayMetrics);
+        mSelectedDateVerticalBarWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+                UNSCALED_SELECTED_DATE_VERTICAL_BAR_WIDTH, displayMetrics);
+        mWeekSeperatorLineWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+                UNSCALED_WEEK_SEPARATOR_LINE_WIDTH, displayMetrics);
+
+        LayoutInflater layoutInflater = (LayoutInflater) mContext
+                .getSystemService(Service.LAYOUT_INFLATER_SERVICE);
+        View content = layoutInflater.inflate(R.layout.calendar_view, null, false);
+        mDelegator.addView(content);
+
+        mListView = (ListView) mDelegator.findViewById(R.id.list);
+        mDayNamesHeader = (ViewGroup) content.findViewById(R.id.day_names);
+        mMonthName = (TextView) content.findViewById(R.id.month_name);
+
+        setUpHeader();
+        setUpListView();
+        setUpAdapter();
+
+        // go to today or whichever is close to today min or max date
+        mTempDate.setTimeInMillis(System.currentTimeMillis());
+        if (mTempDate.before(mMinDate)) {
+            goTo(mMinDate, false, true, true);
+        } else if (mMaxDate.before(mTempDate)) {
+            goTo(mMaxDate, false, true, true);
+        } else {
+            goTo(mTempDate, false, true, true);
+        }
+
+        mDelegator.invalidate();
+    }
+
+    @Override
+    public void setShownWeekCount(int count) {
+        if (mShownWeekCount != count) {
+            mShownWeekCount = count;
+            mDelegator.invalidate();
+        }
+    }
+
+    @Override
+    public int getShownWeekCount() {
+        return mShownWeekCount;
+    }
+
+    @Override
+    public void setSelectedWeekBackgroundColor(int color) {
+        if (mSelectedWeekBackgroundColor != color) {
+            mSelectedWeekBackgroundColor = color;
+            final int childCount = mListView.getChildCount();
+            for (int i = 0; i < childCount; i++) {
+                WeekView weekView = (WeekView) mListView.getChildAt(i);
+                if (weekView.mHasSelectedDay) {
+                    weekView.invalidate();
+                }
+            }
+        }
+    }
+
+    @Override
+    public int getSelectedWeekBackgroundColor() {
+        return mSelectedWeekBackgroundColor;
+    }
+
+    @Override
+    public void setFocusedMonthDateColor(int color) {
+        if (mFocusedMonthDateColor != color) {
+            mFocusedMonthDateColor = color;
+            final int childCount = mListView.getChildCount();
+            for (int i = 0; i < childCount; i++) {
+                WeekView weekView = (WeekView) mListView.getChildAt(i);
+                if (weekView.mHasFocusedDay) {
+                    weekView.invalidate();
+                }
+            }
+        }
+    }
+
+    @Override
+    public int getFocusedMonthDateColor() {
+        return mFocusedMonthDateColor;
+    }
+
+    @Override
+    public void setUnfocusedMonthDateColor(int color) {
+        if (mUnfocusedMonthDateColor != color) {
+            mUnfocusedMonthDateColor = color;
+            final int childCount = mListView.getChildCount();
+            for (int i = 0; i < childCount; i++) {
+                WeekView weekView = (WeekView) mListView.getChildAt(i);
+                if (weekView.mHasUnfocusedDay) {
+                    weekView.invalidate();
+                }
+            }
+        }
+    }
+
+    @Override
+    public int getUnfocusedMonthDateColor() {
+        return mFocusedMonthDateColor;
+    }
+
+    @Override
+    public void setWeekNumberColor(int color) {
+        if (mWeekNumberColor != color) {
+            mWeekNumberColor = color;
+            if (mShowWeekNumber) {
+                invalidateAllWeekViews();
+            }
+        }
+    }
+
+    @Override
+    public int getWeekNumberColor() {
+        return mWeekNumberColor;
+    }
+
+    @Override
+    public void setWeekSeparatorLineColor(int color) {
+        if (mWeekSeparatorLineColor != color) {
+            mWeekSeparatorLineColor = color;
+            invalidateAllWeekViews();
+        }
+    }
+
+    @Override
+    public int getWeekSeparatorLineColor() {
+        return mWeekSeparatorLineColor;
+    }
+
+    @Override
+    public void setSelectedDateVerticalBar(int resourceId) {
+        Drawable drawable = mDelegator.getContext().getDrawable(resourceId);
+        setSelectedDateVerticalBar(drawable);
+    }
+
+    @Override
+    public void setSelectedDateVerticalBar(Drawable drawable) {
+        if (mSelectedDateVerticalBar != drawable) {
+            mSelectedDateVerticalBar = drawable;
+            final int childCount = mListView.getChildCount();
+            for (int i = 0; i < childCount; i++) {
+                WeekView weekView = (WeekView) mListView.getChildAt(i);
+                if (weekView.mHasSelectedDay) {
+                    weekView.invalidate();
+                }
+            }
+        }
+    }
+
+    @Override
+    public Drawable getSelectedDateVerticalBar() {
+        return mSelectedDateVerticalBar;
+    }
+
+    @Override
+    public void setWeekDayTextAppearance(int resourceId) {
+        if (mWeekDayTextAppearanceResId != resourceId) {
+            mWeekDayTextAppearanceResId = resourceId;
+            setUpHeader();
+        }
+    }
+
+    @Override
+    public int getWeekDayTextAppearance() {
+        return mWeekDayTextAppearanceResId;
+    }
+
+    @Override
+    public void setDateTextAppearance(int resourceId) {
+        if (mDateTextAppearanceResId != resourceId) {
+            mDateTextAppearanceResId = resourceId;
+            updateDateTextSize();
+            invalidateAllWeekViews();
+        }
+    }
+
+    @Override
+    public int getDateTextAppearance() {
+        return mDateTextAppearanceResId;
+    }
+
+    @Override
+    public void setMinDate(long minDate) {
+        mTempDate.setTimeInMillis(minDate);
+        if (isSameDate(mTempDate, mMinDate)) {
+            return;
+        }
+        mMinDate.setTimeInMillis(minDate);
+        // make sure the current date is not earlier than
+        // the new min date since the latter is used for
+        // calculating the indices in the adapter thus
+        // avoiding out of bounds error
+        Calendar date = mAdapter.mSelectedDate;
+        if (date.before(mMinDate)) {
+            mAdapter.setSelectedDay(mMinDate);
+        }
+        // reinitialize the adapter since its range depends on min date
+        mAdapter.init();
+        if (date.before(mMinDate)) {
+            setDate(mTempDate.getTimeInMillis());
+        } else {
+            // we go to the current date to force the ListView to query its
+            // adapter for the shown views since we have changed the adapter
+            // range and the base from which the later calculates item indices
+            // note that calling setDate will not work since the date is the same
+            goTo(date, false, true, false);
+        }
+    }
+
+    @Override
+    public long getMinDate() {
+        return mMinDate.getTimeInMillis();
+    }
+
+    @Override
+    public void setMaxDate(long maxDate) {
+        mTempDate.setTimeInMillis(maxDate);
+        if (isSameDate(mTempDate, mMaxDate)) {
+            return;
+        }
+        mMaxDate.setTimeInMillis(maxDate);
+        // reinitialize the adapter since its range depends on max date
+        mAdapter.init();
+        Calendar date = mAdapter.mSelectedDate;
+        if (date.after(mMaxDate)) {
+            setDate(mMaxDate.getTimeInMillis());
+        } else {
+            // we go to the current date to force the ListView to query its
+            // adapter for the shown views since we have changed the adapter
+            // range and the base from which the later calculates item indices
+            // note that calling setDate will not work since the date is the same
+            goTo(date, false, true, false);
+        }
+    }
+
+    @Override
+    public long getMaxDate() {
+        return mMaxDate.getTimeInMillis();
+    }
+
+    @Override
+    public void setShowWeekNumber(boolean showWeekNumber) {
+        if (mShowWeekNumber == showWeekNumber) {
+            return;
+        }
+        mShowWeekNumber = showWeekNumber;
+        mAdapter.notifyDataSetChanged();
+        setUpHeader();
+    }
+
+    @Override
+    public boolean getShowWeekNumber() {
+        return mShowWeekNumber;
+    }
+
+    @Override
+    public void setFirstDayOfWeek(int firstDayOfWeek) {
+        if (mFirstDayOfWeek == firstDayOfWeek) {
+            return;
+        }
+        mFirstDayOfWeek = firstDayOfWeek;
+        mAdapter.init();
+        mAdapter.notifyDataSetChanged();
+        setUpHeader();
+    }
+
+    @Override
+    public int getFirstDayOfWeek() {
+        return mFirstDayOfWeek;
+    }
+
+    @Override
+    public void setDate(long date) {
+        setDate(date, false, false);
+    }
+
+    @Override
+    public void setDate(long date, boolean animate, boolean center) {
+        mTempDate.setTimeInMillis(date);
+        if (isSameDate(mTempDate, mAdapter.mSelectedDate)) {
+            return;
+        }
+        goTo(mTempDate, animate, true, center);
+    }
+
+    @Override
+    public long getDate() {
+        return mAdapter.mSelectedDate.getTimeInMillis();
+    }
+
+    @Override
+    public void setOnDateChangeListener(CalendarView.OnDateChangeListener listener) {
+        mOnDateChangeListener = listener;
+    }
+
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        setCurrentLocale(newConfig.locale);
+    }
+
+    /**
+     * Sets the current locale.
+     *
+     * @param locale The current locale.
+     */
+    @Override
+    protected void setCurrentLocale(Locale locale) {
+        super.setCurrentLocale(locale);
+
+        mTempDate = getCalendarForLocale(mTempDate, locale);
+        mFirstDayOfMonth = getCalendarForLocale(mFirstDayOfMonth, locale);
+        mMinDate = getCalendarForLocale(mMinDate, locale);
+        mMaxDate = getCalendarForLocale(mMaxDate, locale);
+    }
+    private void updateDateTextSize() {
+        TypedArray dateTextAppearance = mDelegator.getContext().obtainStyledAttributes(
+                mDateTextAppearanceResId, R.styleable.TextAppearance);
+        mDateTextSize = dateTextAppearance.getDimensionPixelSize(
+                R.styleable.TextAppearance_textSize, DEFAULT_DATE_TEXT_SIZE);
+        dateTextAppearance.recycle();
+    }
+
+    /**
+     * Invalidates all week views.
+     */
+    private void invalidateAllWeekViews() {
+        final int childCount = mListView.getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            View view = mListView.getChildAt(i);
+            view.invalidate();
+        }
+    }
+
+    /**
+     * Gets a calendar for locale bootstrapped with the value of a given calendar.
+     *
+     * @param oldCalendar The old calendar.
+     * @param locale The locale.
+     */
+    private static Calendar getCalendarForLocale(Calendar oldCalendar, Locale locale) {
+        if (oldCalendar == null) {
+            return Calendar.getInstance(locale);
+        } else {
+            final long currentTimeMillis = oldCalendar.getTimeInMillis();
+            Calendar newCalendar = Calendar.getInstance(locale);
+            newCalendar.setTimeInMillis(currentTimeMillis);
+            return newCalendar;
+        }
+    }
+
+    /**
+     * @return True if the <code>firstDate</code> is the same as the <code>
+     * secondDate</code>.
+     */
+    private static boolean isSameDate(Calendar firstDate, Calendar secondDate) {
+        return (firstDate.get(Calendar.DAY_OF_YEAR) == secondDate.get(Calendar.DAY_OF_YEAR)
+                && firstDate.get(Calendar.YEAR) == secondDate.get(Calendar.YEAR));
+    }
+
+    /**
+     * Creates a new adapter if necessary and sets up its parameters.
+     */
+    private void setUpAdapter() {
+        if (mAdapter == null) {
+            mAdapter = new WeeksAdapter(mContext);
+            mAdapter.registerDataSetObserver(new DataSetObserver() {
+                @Override
+                public void onChanged() {
+                    if (mOnDateChangeListener != null) {
+                        Calendar selectedDay = mAdapter.getSelectedDay();
+                        mOnDateChangeListener.onSelectedDayChange(mDelegator,
+                                selectedDay.get(Calendar.YEAR),
+                                selectedDay.get(Calendar.MONTH),
+                                selectedDay.get(Calendar.DAY_OF_MONTH));
+                    }
+                }
+            });
+            mListView.setAdapter(mAdapter);
+        }
+
+        // refresh the view with the new parameters
+        mAdapter.notifyDataSetChanged();
+    }
+
+    /**
+     * Sets up the strings to be used by the header.
+     */
+    private void setUpHeader() {
+        mDayNamesShort = new String[mDaysPerWeek];
+        mDayNamesLong = new String[mDaysPerWeek];
+        for (int i = mFirstDayOfWeek, count = mFirstDayOfWeek + mDaysPerWeek; i < count; i++) {
+            int calendarDay = (i > Calendar.SATURDAY) ? i - Calendar.SATURDAY : i;
+            mDayNamesShort[i - mFirstDayOfWeek] = DateUtils.getDayOfWeekString(calendarDay,
+                    DateUtils.LENGTH_SHORTEST);
+            mDayNamesLong[i - mFirstDayOfWeek] = DateUtils.getDayOfWeekString(calendarDay,
+                    DateUtils.LENGTH_LONG);
+        }
+
+        TextView label = (TextView) mDayNamesHeader.getChildAt(0);
+        if (mShowWeekNumber) {
+            label.setVisibility(View.VISIBLE);
+        } else {
+            label.setVisibility(View.GONE);
+        }
+        for (int i = 1, count = mDayNamesHeader.getChildCount(); i < count; i++) {
+            label = (TextView) mDayNamesHeader.getChildAt(i);
+            if (mWeekDayTextAppearanceResId > -1) {
+                label.setTextAppearance(mContext, mWeekDayTextAppearanceResId);
+            }
+            if (i < mDaysPerWeek + 1) {
+                label.setText(mDayNamesShort[i - 1]);
+                label.setContentDescription(mDayNamesLong[i - 1]);
+                label.setVisibility(View.VISIBLE);
+            } else {
+                label.setVisibility(View.GONE);
+            }
+        }
+        mDayNamesHeader.invalidate();
+    }
+
+    /**
+     * Sets all the required fields for the list view.
+     */
+    private void setUpListView() {
+        // Configure the listview
+        mListView.setDivider(null);
+        mListView.setItemsCanFocus(true);
+        mListView.setVerticalScrollBarEnabled(false);
+        mListView.setOnScrollListener(new AbsListView.OnScrollListener() {
+            public void onScrollStateChanged(AbsListView view, int scrollState) {
+                CalendarViewLegacyDelegate.this.onScrollStateChanged(view, scrollState);
+            }
+
+            public void onScroll(
+                    AbsListView view, int firstVisibleItem, int visibleItemCount,
+                    int totalItemCount) {
+                CalendarViewLegacyDelegate.this.onScroll(view, firstVisibleItem,
+                        visibleItemCount, totalItemCount);
+            }
+        });
+        // Make the scrolling behavior nicer
+        mListView.setFriction(mFriction);
+        mListView.setVelocityScale(mVelocityScale);
+    }
+
+    /**
+     * This moves to the specified time in the view. If the time is not already
+     * in range it will move the list so that the first of the month containing
+     * the time is at the top of the view. If the new time is already in view
+     * the list will not be scrolled unless forceScroll is true. This time may
+     * optionally be highlighted as selected as well.
+     *
+     * @param date The time to move to.
+     * @param animate Whether to scroll to the given time or just redraw at the
+     *            new location.
+     * @param setSelected Whether to set the given time as selected.
+     * @param forceScroll Whether to recenter even if the time is already
+     *            visible.
+     *
+     * @throws IllegalArgumentException of the provided date is before the
+     *        range start of after the range end.
+     */
+    private void goTo(Calendar date, boolean animate, boolean setSelected,
+            boolean forceScroll) {
+        if (date.before(mMinDate) || date.after(mMaxDate)) {
+            throw new IllegalArgumentException("Time not between " + mMinDate.getTime()
+                    + " and " + mMaxDate.getTime());
+        }
+        // Find the first and last entirely visible weeks
+        int firstFullyVisiblePosition = mListView.getFirstVisiblePosition();
+        View firstChild = mListView.getChildAt(0);
+        if (firstChild != null && firstChild.getTop() < 0) {
+            firstFullyVisiblePosition++;
+        }
+        int lastFullyVisiblePosition = firstFullyVisiblePosition + mShownWeekCount - 1;
+        if (firstChild != null && firstChild.getTop() > mBottomBuffer) {
+            lastFullyVisiblePosition--;
+        }
+        if (setSelected) {
+            mAdapter.setSelectedDay(date);
+        }
+        // Get the week we're going to
+        int position = getWeeksSinceMinDate(date);
+
+        // Check if the selected day is now outside of our visible range
+        // and if so scroll to the month that contains it
+        if (position < firstFullyVisiblePosition || position > lastFullyVisiblePosition
+                || forceScroll) {
+            mFirstDayOfMonth.setTimeInMillis(date.getTimeInMillis());
+            mFirstDayOfMonth.set(Calendar.DAY_OF_MONTH, 1);
+
+            setMonthDisplayed(mFirstDayOfMonth);
+
+            // the earliest time we can scroll to is the min date
+            if (mFirstDayOfMonth.before(mMinDate)) {
+                position = 0;
+            } else {
+                position = getWeeksSinceMinDate(mFirstDayOfMonth);
+            }
+
+            mPreviousScrollState = AbsListView.OnScrollListener.SCROLL_STATE_FLING;
+            if (animate) {
+                mListView.smoothScrollToPositionFromTop(position, mListScrollTopOffset,
+                        GOTO_SCROLL_DURATION);
+            } else {
+                mListView.setSelectionFromTop(position, mListScrollTopOffset);
+                // Perform any after scroll operations that are needed
+                onScrollStateChanged(mListView, AbsListView.OnScrollListener.SCROLL_STATE_IDLE);
+            }
+        } else if (setSelected) {
+            // Otherwise just set the selection
+            setMonthDisplayed(date);
+        }
+    }
+
+    /**
+     * Called when a <code>view</code> transitions to a new <code>scrollState
+     * </code>.
+     */
+    private void onScrollStateChanged(AbsListView view, int scrollState) {
+        mScrollStateChangedRunnable.doScrollStateChange(view, scrollState);
+    }
+
+    /**
+     * Updates the title and selected month if the <code>view</code> has moved to a new
+     * month.
+     */
+    private void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
+                          int totalItemCount) {
+        WeekView child = (WeekView) view.getChildAt(0);
+        if (child == null) {
+            return;
+        }
+
+        // Figure out where we are
+        long currScroll =
+                view.getFirstVisiblePosition() * child.getHeight() - child.getBottom();
+
+        // If we have moved since our last call update the direction
+        if (currScroll < mPreviousScrollPosition) {
+            mIsScrollingUp = true;
+        } else if (currScroll > mPreviousScrollPosition) {
+            mIsScrollingUp = false;
+        } else {
+            return;
+        }
+
+        // Use some hysteresis for checking which month to highlight. This
+        // causes the month to transition when two full weeks of a month are
+        // visible when scrolling up, and when the first day in a month reaches
+        // the top of the screen when scrolling down.
+        int offset = child.getBottom() < mWeekMinVisibleHeight ? 1 : 0;
+        if (mIsScrollingUp) {
+            child = (WeekView) view.getChildAt(SCROLL_HYST_WEEKS + offset);
+        } else if (offset != 0) {
+            child = (WeekView) view.getChildAt(offset);
+        }
+
+        if (child != null) {
+            // 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 (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;
+    }
+
+    /**
+     * Sets the month displayed at the top of this view based on time. Override
+     * to add custom events when the title is changed.
+     *
+     * @param calendar A day in the new focus month.
+     */
+    private void setMonthDisplayed(Calendar calendar) {
+        mCurrentMonthDisplayed = calendar.get(Calendar.MONTH);
+        mAdapter.setFocusMonth(mCurrentMonthDisplayed);
+        final int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NO_MONTH_DAY
+                | DateUtils.FORMAT_SHOW_YEAR;
+        final long millis = calendar.getTimeInMillis();
+        String newMonthName = DateUtils.formatDateRange(mContext, millis, millis, flags);
+        mMonthName.setText(newMonthName);
+        mMonthName.invalidate();
+    }
+
+    /**
+     * @return Returns the number of weeks between the current <code>date</code>
+     *         and the <code>mMinDate</code>.
+     */
+    private int getWeeksSinceMinDate(Calendar date) {
+        if (date.before(mMinDate)) {
+            throw new IllegalArgumentException("fromDate: " + mMinDate.getTime()
+                    + " does not precede toDate: " + date.getTime());
+        }
+        long endTimeMillis = date.getTimeInMillis()
+                + date.getTimeZone().getOffset(date.getTimeInMillis());
+        long startTimeMillis = mMinDate.getTimeInMillis()
+                + mMinDate.getTimeZone().getOffset(mMinDate.getTimeInMillis());
+        long dayOffsetMillis = (mMinDate.get(Calendar.DAY_OF_WEEK) - mFirstDayOfWeek)
+                * MILLIS_IN_DAY;
+        return (int) ((endTimeMillis - startTimeMillis + dayOffsetMillis) / MILLIS_IN_WEEK);
+    }
+
+    /**
+     * Command responsible for acting upon scroll state changes.
+     */
+    private class ScrollStateRunnable implements Runnable {
+        private AbsListView mView;
+
+        private int mNewState;
+
+        /**
+         * Sets up the runnable with a short delay in case the scroll state
+         * immediately changes again.
+         *
+         * @param view The list view that changed state
+         * @param scrollState The new state it changed to
+         */
+        public void doScrollStateChange(AbsListView view, int scrollState) {
+            mView = view;
+            mNewState = scrollState;
+            mDelegator.removeCallbacks(this);
+            mDelegator.postDelayed(this, SCROLL_CHANGE_DELAY);
+        }
+
+        public void run() {
+            mCurrentScrollState = mNewState;
+            // Fix the position after a scroll or a fling ends
+            if (mNewState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE
+                    && mPreviousScrollState != AbsListView.OnScrollListener.SCROLL_STATE_IDLE) {
+                View child = mView.getChildAt(0);
+                if (child == null) {
+                    // The view is no longer visible, just return
+                    return;
+                }
+                int dist = child.getBottom() - mListScrollTopOffset;
+                if (dist > mListScrollTopOffset) {
+                    if (mIsScrollingUp) {
+                        mView.smoothScrollBy(dist - child.getHeight(),
+                                ADJUSTMENT_SCROLL_DURATION);
+                    } else {
+                        mView.smoothScrollBy(dist, ADJUSTMENT_SCROLL_DURATION);
+                    }
+                }
+            }
+            mPreviousScrollState = mNewState;
+        }
+    }
+
+    /**
+     * <p>
+     * This is a specialized adapter for creating a list of weeks with
+     * selectable days. It can be configured to display the week number, start
+     * the week on a given day, show a reduced number of days, or display an
+     * arbitrary number of weeks at a time.
+     * </p>
+     */
+    private class WeeksAdapter extends BaseAdapter implements View.OnTouchListener {
+
+        private int mSelectedWeek;
+
+        private GestureDetector mGestureDetector;
+
+        private int mFocusedMonth;
+
+        private final Calendar mSelectedDate = Calendar.getInstance();
+
+        private int mTotalWeekCount;
+
+        public WeeksAdapter(Context context) {
+            mContext = context;
+            mGestureDetector = new GestureDetector(mContext, new WeeksAdapter.CalendarGestureListener());
+            init();
+        }
+
+        /**
+         * Set up the gesture detector and selected time
+         */
+        private void init() {
+            mSelectedWeek = getWeeksSinceMinDate(mSelectedDate);
+            mTotalWeekCount = getWeeksSinceMinDate(mMaxDate);
+            if (mMinDate.get(Calendar.DAY_OF_WEEK) != mFirstDayOfWeek
+                    || mMaxDate.get(Calendar.DAY_OF_WEEK) != mFirstDayOfWeek) {
+                mTotalWeekCount++;
+            }
+            notifyDataSetChanged();
+        }
+
+        /**
+         * Updates the selected day and related parameters.
+         *
+         * @param selectedDay The time to highlight
+         */
+        public void setSelectedDay(Calendar selectedDay) {
+            if (selectedDay.get(Calendar.DAY_OF_YEAR) == mSelectedDate.get(Calendar.DAY_OF_YEAR)
+                    && selectedDay.get(Calendar.YEAR) == mSelectedDate.get(Calendar.YEAR)) {
+                return;
+            }
+            mSelectedDate.setTimeInMillis(selectedDay.getTimeInMillis());
+            mSelectedWeek = getWeeksSinceMinDate(mSelectedDate);
+            mFocusedMonth = mSelectedDate.get(Calendar.MONTH);
+            notifyDataSetChanged();
+        }
+
+        /**
+         * @return The selected day of month.
+         */
+        public Calendar getSelectedDay() {
+            return mSelectedDate;
+        }
+
+        @Override
+        public int getCount() {
+            return mTotalWeekCount;
+        }
+
+        @Override
+        public Object getItem(int position) {
+            return null;
+        }
+
+        @Override
+        public long getItemId(int position) {
+            return position;
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            WeekView weekView = null;
+            if (convertView != null) {
+                weekView = (WeekView) convertView;
+            } else {
+                weekView = new WeekView(mContext);
+                AbsListView.LayoutParams params =
+                        new AbsListView.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT,
+                                FrameLayout.LayoutParams.WRAP_CONTENT);
+                weekView.setLayoutParams(params);
+                weekView.setClickable(true);
+                weekView.setOnTouchListener(this);
+            }
+
+            int selectedWeekDay = (mSelectedWeek == position) ? mSelectedDate.get(
+                    Calendar.DAY_OF_WEEK) : -1;
+            weekView.init(position, selectedWeekDay, mFocusedMonth);
+
+            return weekView;
+        }
+
+        /**
+         * Changes which month is in focus and updates the view.
+         *
+         * @param month The month to show as in focus [0-11]
+         */
+        public void setFocusMonth(int month) {
+            if (mFocusedMonth == month) {
+                return;
+            }
+            mFocusedMonth = month;
+            notifyDataSetChanged();
+        }
+
+        @Override
+        public boolean onTouch(View v, MotionEvent event) {
+            if (mListView.isEnabled() && mGestureDetector.onTouchEvent(event)) {
+                WeekView weekView = (WeekView) v;
+                // if we cannot find a day for the given location we are done
+                if (!weekView.getDayFromLocation(event.getX(), mTempDate)) {
+                    return true;
+                }
+                // it is possible that the touched day is outside the valid range
+                // we draw whole weeks but range end can fall not on the week end
+                if (mTempDate.before(mMinDate) || mTempDate.after(mMaxDate)) {
+                    return true;
+                }
+                onDateTapped(mTempDate);
+                return true;
+            }
+            return false;
+        }
+
+        /**
+         * Maintains the same hour/min/sec but moves the day to the tapped day.
+         *
+         * @param day The day that was tapped
+         */
+        private void onDateTapped(Calendar day) {
+            setSelectedDay(day);
+            setMonthDisplayed(day);
+        }
+
+        /**
+         * This is here so we can identify single tap events and set the
+         * selected day correctly
+         */
+        class CalendarGestureListener extends GestureDetector.SimpleOnGestureListener {
+            @Override
+            public boolean onSingleTapUp(MotionEvent e) {
+                return true;
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * This is a dynamic view for drawing a single week. It can be configured to
+     * display the week number, start the week on a given day, or show a reduced
+     * number of days. It is intended for use as a single view within a
+     * ListView. See {@link WeeksAdapter} for usage.
+     * </p>
+     */
+    private class WeekView extends View {
+
+        private final Rect mTempRect = new Rect();
+
+        private final Paint mDrawPaint = new Paint();
+
+        private final Paint mMonthNumDrawPaint = new Paint();
+
+        // Cache the number strings so we don't have to recompute them each time
+        private String[] mDayNumbers;
+
+        // Quick lookup for checking which days are in the focus month
+        private boolean[] mFocusDay;
+
+        // Whether this view has a focused day.
+        private boolean mHasFocusedDay;
+
+        // Whether this view has only focused days.
+        private boolean mHasUnfocusedDay;
+
+        // The first day displayed by this item
+        private Calendar mFirstDay;
+
+        // The month of the first day in this week
+        private int mMonthOfFirstWeekDay = -1;
+
+        // The month of the last day in this week
+        private int mLastWeekDayMonth = -1;
+
+        // The position of this week, equivalent to weeks since the week of Jan
+        // 1st, 1900
+        private int mWeek = -1;
+
+        // Quick reference to the width of this view, matches parent
+        private int mWidth;
+
+        // The height this view should draw at in pixels, set by height param
+        private int mHeight;
+
+        // If this view contains the selected day
+        private boolean mHasSelectedDay = false;
+
+        // Which day is selected [0-6] or -1 if no day is selected
+        private int mSelectedDay = -1;
+
+        // The number of days + a spot for week number if it is displayed
+        private int mNumCells;
+
+        // The left edge of the selected day
+        private int mSelectedLeft = -1;
+
+        // The right edge of the selected day
+        private int mSelectedRight = -1;
+
+        public WeekView(Context context) {
+            super(context);
+
+            // Sets up any standard paints that will be used
+            initilaizePaints();
+        }
+
+        /**
+         * Initializes this week view.
+         *
+         * @param weekNumber The number of the week this view represents. The
+         *            week number is a zero based index of the weeks since
+         *            {@link android.widget.CalendarView#getMinDate()}.
+         * @param selectedWeekDay The selected day of the week from 0 to 6, -1 if no
+         *            selected day.
+         * @param focusedMonth The month that is currently in focus i.e.
+         *            highlighted.
+         */
+        public void init(int weekNumber, int selectedWeekDay, int focusedMonth) {
+            mSelectedDay = selectedWeekDay;
+            mHasSelectedDay = mSelectedDay != -1;
+            mNumCells = mShowWeekNumber ? mDaysPerWeek + 1 : mDaysPerWeek;
+            mWeek = weekNumber;
+            mTempDate.setTimeInMillis(mMinDate.getTimeInMillis());
+
+            mTempDate.add(Calendar.WEEK_OF_YEAR, mWeek);
+            mTempDate.setFirstDayOfWeek(mFirstDayOfWeek);
+
+            // Allocate space for caching the day numbers and focus values
+            mDayNumbers = new String[mNumCells];
+            mFocusDay = new boolean[mNumCells];
+
+            // If we're showing the week number calculate it based on Monday
+            int i = 0;
+            if (mShowWeekNumber) {
+                mDayNumbers[0] = String.format(Locale.getDefault(), "%d",
+                        mTempDate.get(Calendar.WEEK_OF_YEAR));
+                i++;
+            }
+
+            // Now adjust our starting day based on the start day of the week
+            int diff = mFirstDayOfWeek - mTempDate.get(Calendar.DAY_OF_WEEK);
+            mTempDate.add(Calendar.DAY_OF_MONTH, diff);
+
+            mFirstDay = (Calendar) mTempDate.clone();
+            mMonthOfFirstWeekDay = mTempDate.get(Calendar.MONTH);
+
+            mHasUnfocusedDay = true;
+            for (; i < mNumCells; i++) {
+                final boolean isFocusedDay = (mTempDate.get(Calendar.MONTH) == focusedMonth);
+                mFocusDay[i] = isFocusedDay;
+                mHasFocusedDay |= isFocusedDay;
+                mHasUnfocusedDay &= !isFocusedDay;
+                // do not draw dates outside the valid range to avoid user confusion
+                if (mTempDate.before(mMinDate) || mTempDate.after(mMaxDate)) {
+                    mDayNumbers[i] = "";
+                } else {
+                    mDayNumbers[i] = String.format(Locale.getDefault(), "%d",
+                            mTempDate.get(Calendar.DAY_OF_MONTH));
+                }
+                mTempDate.add(Calendar.DAY_OF_MONTH, 1);
+            }
+            // We do one extra add at the end of the loop, if that pushed us to
+            // new month undo it
+            if (mTempDate.get(Calendar.DAY_OF_MONTH) == 1) {
+                mTempDate.add(Calendar.DAY_OF_MONTH, -1);
+            }
+            mLastWeekDayMonth = mTempDate.get(Calendar.MONTH);
+
+            updateSelectionPositions();
+        }
+
+        /**
+         * Initialize the paint instances.
+         */
+        private void initilaizePaints() {
+            mDrawPaint.setFakeBoldText(false);
+            mDrawPaint.setAntiAlias(true);
+            mDrawPaint.setStyle(Paint.Style.FILL);
+
+            mMonthNumDrawPaint.setFakeBoldText(true);
+            mMonthNumDrawPaint.setAntiAlias(true);
+            mMonthNumDrawPaint.setStyle(Paint.Style.FILL);
+            mMonthNumDrawPaint.setTextAlign(Paint.Align.CENTER);
+            mMonthNumDrawPaint.setTextSize(mDateTextSize);
+        }
+
+        /**
+         * Returns the month of the first day in this week.
+         *
+         * @return The month the first day of this view is in.
+         */
+        public int getMonthOfFirstWeekDay() {
+            return mMonthOfFirstWeekDay;
+        }
+
+        /**
+         * Returns the month of the last day in this week
+         *
+         * @return The month the last day of this view is in
+         */
+        public int getMonthOfLastWeekDay() {
+            return mLastWeekDayMonth;
+        }
+
+        /**
+         * Returns the first day in this view.
+         *
+         * @return The first day in the view.
+         */
+        public Calendar getFirstDay() {
+            return mFirstDay;
+        }
+
+        /**
+         * Calculates the day that the given x position is in, accounting for
+         * week number.
+         *
+         * @param x The x position of the touch event.
+         * @return True if a day was found for the given location.
+         */
+        public boolean getDayFromLocation(float x, Calendar outCalendar) {
+            final boolean isLayoutRtl = isLayoutRtl();
+
+            int start;
+            int end;
+
+            if (isLayoutRtl) {
+                start = 0;
+                end = mShowWeekNumber ? mWidth - mWidth / mNumCells : mWidth;
+            } else {
+                start = mShowWeekNumber ? mWidth / mNumCells : 0;
+                end = mWidth;
+            }
+
+            if (x < start || x > end) {
+                outCalendar.clear();
+                return false;
+            }
+
+            // Selection is (x - start) / (pixels/day) which is (x - start) * day / pixels
+            int dayPosition = (int) ((x - start) * mDaysPerWeek / (end - start));
+
+            if (isLayoutRtl) {
+                dayPosition = mDaysPerWeek - 1 - dayPosition;
+            }
+
+            outCalendar.setTimeInMillis(mFirstDay.getTimeInMillis());
+            outCalendar.add(Calendar.DAY_OF_MONTH, dayPosition);
+
+            return true;
+        }
+
+        @Override
+        protected void onDraw(Canvas canvas) {
+            drawBackground(canvas);
+            drawWeekNumbersAndDates(canvas);
+            drawWeekSeparators(canvas);
+            drawSelectedDateVerticalBars(canvas);
+        }
+
+        /**
+         * This draws the selection highlight if a day is selected in this week.
+         *
+         * @param canvas The canvas to draw on
+         */
+        private void drawBackground(Canvas canvas) {
+            if (!mHasSelectedDay) {
+                return;
+            }
+            mDrawPaint.setColor(mSelectedWeekBackgroundColor);
+
+            mTempRect.top = mWeekSeperatorLineWidth;
+            mTempRect.bottom = mHeight;
+
+            final boolean isLayoutRtl = isLayoutRtl();
+
+            if (isLayoutRtl) {
+                mTempRect.left = 0;
+                mTempRect.right = mSelectedLeft - 2;
+            } else {
+                mTempRect.left = mShowWeekNumber ? mWidth / mNumCells : 0;
+                mTempRect.right = mSelectedLeft - 2;
+            }
+            canvas.drawRect(mTempRect, mDrawPaint);
+
+            if (isLayoutRtl) {
+                mTempRect.left = mSelectedRight + 3;
+                mTempRect.right = mShowWeekNumber ? mWidth - mWidth / mNumCells : mWidth;
+            } else {
+                mTempRect.left = mSelectedRight + 3;
+                mTempRect.right = mWidth;
+            }
+            canvas.drawRect(mTempRect, mDrawPaint);
+        }
+
+        /**
+         * Draws the week and month day numbers for this week.
+         *
+         * @param canvas The canvas to draw on
+         */
+        private void drawWeekNumbersAndDates(Canvas canvas) {
+            final float textHeight = mDrawPaint.getTextSize();
+            final int y = (int) ((mHeight + textHeight) / 2) - mWeekSeperatorLineWidth;
+            final int nDays = mNumCells;
+            final int divisor = 2 * nDays;
+
+            mDrawPaint.setTextAlign(Paint.Align.CENTER);
+            mDrawPaint.setTextSize(mDateTextSize);
+
+            int i = 0;
+
+            if (isLayoutRtl()) {
+                for (; i < nDays - 1; i++) {
+                    mMonthNumDrawPaint.setColor(mFocusDay[i] ? mFocusedMonthDateColor
+                            : mUnfocusedMonthDateColor);
+                    int x = (2 * i + 1) * mWidth / divisor;
+                    canvas.drawText(mDayNumbers[nDays - 1 - i], x, y, mMonthNumDrawPaint);
+                }
+                if (mShowWeekNumber) {
+                    mDrawPaint.setColor(mWeekNumberColor);
+                    int x = mWidth - mWidth / divisor;
+                    canvas.drawText(mDayNumbers[0], x, y, mDrawPaint);
+                }
+            } else {
+                if (mShowWeekNumber) {
+                    mDrawPaint.setColor(mWeekNumberColor);
+                    int x = mWidth / divisor;
+                    canvas.drawText(mDayNumbers[0], x, y, mDrawPaint);
+                    i++;
+                }
+                for (; i < nDays; i++) {
+                    mMonthNumDrawPaint.setColor(mFocusDay[i] ? mFocusedMonthDateColor
+                            : mUnfocusedMonthDateColor);
+                    int x = (2 * i + 1) * mWidth / divisor;
+                    canvas.drawText(mDayNumbers[i], x, y, mMonthNumDrawPaint);
+                }
+            }
+        }
+
+        /**
+         * Draws a horizontal line for separating the weeks.
+         *
+         * @param canvas The canvas to draw on.
+         */
+        private void drawWeekSeparators(Canvas canvas) {
+            // If it is the topmost fully visible child do not draw separator line
+            int firstFullyVisiblePosition = mListView.getFirstVisiblePosition();
+            if (mListView.getChildAt(0).getTop() < 0) {
+                firstFullyVisiblePosition++;
+            }
+            if (firstFullyVisiblePosition == mWeek) {
+                return;
+            }
+            mDrawPaint.setColor(mWeekSeparatorLineColor);
+            mDrawPaint.setStrokeWidth(mWeekSeperatorLineWidth);
+            float startX;
+            float stopX;
+            if (isLayoutRtl()) {
+                startX = 0;
+                stopX = mShowWeekNumber ? mWidth - mWidth / mNumCells : mWidth;
+            } else {
+                startX = mShowWeekNumber ? mWidth / mNumCells : 0;
+                stopX = mWidth;
+            }
+            canvas.drawLine(startX, 0, stopX, 0, mDrawPaint);
+        }
+
+        /**
+         * Draws the selected date bars if this week has a selected day.
+         *
+         * @param canvas The canvas to draw on
+         */
+        private void drawSelectedDateVerticalBars(Canvas canvas) {
+            if (!mHasSelectedDay) {
+                return;
+            }
+            mSelectedDateVerticalBar.setBounds(
+                    mSelectedLeft - mSelectedDateVerticalBarWidth / 2,
+                    mWeekSeperatorLineWidth,
+                    mSelectedLeft + mSelectedDateVerticalBarWidth / 2,
+                    mHeight);
+            mSelectedDateVerticalBar.draw(canvas);
+            mSelectedDateVerticalBar.setBounds(
+                    mSelectedRight - mSelectedDateVerticalBarWidth / 2,
+                    mWeekSeperatorLineWidth,
+                    mSelectedRight + mSelectedDateVerticalBarWidth / 2,
+                    mHeight);
+            mSelectedDateVerticalBar.draw(canvas);
+        }
+
+        @Override
+        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+            mWidth = w;
+            updateSelectionPositions();
+        }
+
+        /**
+         * This calculates the positions for the selected day lines.
+         */
+        private void updateSelectionPositions() {
+            if (mHasSelectedDay) {
+                final boolean isLayoutRtl = isLayoutRtl();
+                int selectedPosition = mSelectedDay - mFirstDayOfWeek;
+                if (selectedPosition < 0) {
+                    selectedPosition += 7;
+                }
+                if (mShowWeekNumber && !isLayoutRtl) {
+                    selectedPosition++;
+                }
+                if (isLayoutRtl) {
+                    mSelectedLeft = (mDaysPerWeek - 1 - selectedPosition) * mWidth / mNumCells;
+
+                } else {
+                    mSelectedLeft = selectedPosition * mWidth / mNumCells;
+                }
+                mSelectedRight = mSelectedLeft + mWidth / mNumCells;
+            }
+        }
+
+        @Override
+        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+            mHeight = (mListView.getHeight() - mListView.getPaddingTop() - mListView
+                    .getPaddingBottom()) / mShownWeekCount;
+            setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), mHeight);
+        }
+    }
+
+}
diff --git a/core/java/android/widget/CalendarViewMaterialDelegate.java b/core/java/android/widget/CalendarViewMaterialDelegate.java
new file mode 100644
index 0000000..b0f3740
--- /dev/null
+++ b/core/java/android/widget/CalendarViewMaterialDelegate.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget;
+
+import com.android.internal.R;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.util.MathUtils;
+
+import java.util.Calendar;
+import java.util.Locale;
+
+import libcore.icu.LocaleData;
+
+class CalendarViewMaterialDelegate extends CalendarView.AbstractCalendarViewDelegate {
+    private final DayPickerView mDayPickerView;
+
+    private CalendarView.OnDateChangeListener mOnDateChangeListener;
+
+    public CalendarViewMaterialDelegate(CalendarView delegator, Context context, AttributeSet attrs,
+            int defStyleAttr, int defStyleRes) {
+        super(delegator, context);
+
+        final TypedArray a = context.obtainStyledAttributes(attrs,
+                R.styleable.CalendarView, defStyleAttr, defStyleRes);
+        final int firstDayOfWeek = a.getInt(R.styleable.CalendarView_firstDayOfWeek,
+                LocaleData.get(Locale.getDefault()).firstDayOfWeek);
+
+        final long minDate = parseDateToMillis(a.getString(
+                R.styleable.CalendarView_minDate), DEFAULT_MIN_DATE);
+        final long maxDate = parseDateToMillis(a.getString(
+                R.styleable.CalendarView_maxDate), DEFAULT_MAX_DATE);
+        if (maxDate < minDate) {
+            throw new IllegalArgumentException("max date cannot be before min date");
+        }
+
+        final long setDate = MathUtils.constrain(System.currentTimeMillis(), minDate, maxDate);
+        final int dateTextAppearanceResId = a.getResourceId(
+                R.styleable.CalendarView_dateTextAppearance,
+                R.style.TextAppearance_DeviceDefault_Small);
+
+        a.recycle();
+
+        mDayPickerView = new DayPickerView(context);
+        mDayPickerView.setFirstDayOfWeek(firstDayOfWeek);
+        mDayPickerView.setCalendarTextAppearance(dateTextAppearanceResId);
+        mDayPickerView.setMinDate(minDate);
+        mDayPickerView.setMaxDate(maxDate);
+        mDayPickerView.setDate(setDate, false, true);
+        mDayPickerView.setOnDaySelectedListener(mOnDaySelectedListener);
+
+        delegator.addView(mDayPickerView);
+    }
+
+    private long parseDateToMillis(String dateStr, String defaultDateStr) {
+        final Calendar tempCalendar = Calendar.getInstance();
+        if (TextUtils.isEmpty(dateStr) || !parseDate(dateStr, tempCalendar)) {
+            parseDate(defaultDateStr, tempCalendar);
+        }
+        return tempCalendar.getTimeInMillis();
+    }
+
+    @Override
+    public void setShownWeekCount(int count) {
+        // Deprecated.
+    }
+
+    @Override
+    public int getShownWeekCount() {
+        // Deprecated.
+        return 0;
+    }
+
+    @Override
+    public void setSelectedWeekBackgroundColor(int color) {
+        // TODO: Should use a ColorStateList. Deprecate?
+    }
+
+    @Override
+    public int getSelectedWeekBackgroundColor() {
+        return 0;
+    }
+
+    @Override
+    public void setFocusedMonthDateColor(int color) {
+        // TODO: Should use a ColorStateList. Deprecate?
+    }
+
+    @Override
+    public int getFocusedMonthDateColor() {
+        return 0;
+    }
+
+    @Override
+    public void setUnfocusedMonthDateColor(int color) {
+        // TODO: Should use a ColorStateList. Deprecate?
+    }
+
+    @Override
+    public int getUnfocusedMonthDateColor() {
+        return 0;
+    }
+
+    @Override
+    public void setWeekDayTextAppearance(int resourceId) {
+
+    }
+
+    @Override
+    public int getWeekDayTextAppearance() {
+        return 0;
+    }
+
+    @Override
+    public void setDateTextAppearance(int resourceId) {
+
+    }
+
+    @Override
+    public int getDateTextAppearance() {
+        return 0;
+    }
+
+    @Override
+    public void setWeekNumberColor(int color) {
+        // Deprecated.
+    }
+
+    @Override
+    public int getWeekNumberColor() {
+        // Deprecated.
+        return 0;
+    }
+
+    @Override
+    public void setWeekSeparatorLineColor(int color) {
+        // Deprecated.
+    }
+
+    @Override
+    public int getWeekSeparatorLineColor() {
+        // Deprecated.
+        return 0;
+    }
+
+    @Override
+    public void setSelectedDateVerticalBar(int resourceId) {
+        // Deprecated.
+    }
+
+    @Override
+    public void setSelectedDateVerticalBar(Drawable drawable) {
+        // Deprecated.
+    }
+
+    @Override
+    public Drawable getSelectedDateVerticalBar() {
+        // Deprecated.
+        return null;
+    }
+
+    @Override
+    public void setMinDate(long minDate) {
+        mDayPickerView.setMinDate(minDate);
+    }
+
+    @Override
+    public long getMinDate() {
+        return mDayPickerView.getMinDate();
+    }
+
+    @Override
+    public void setMaxDate(long maxDate) {
+        mDayPickerView.setMaxDate(maxDate);
+    }
+
+    @Override
+    public long getMaxDate() {
+        return mDayPickerView.getMaxDate();
+    }
+
+    @Override
+    public void setShowWeekNumber(boolean showWeekNumber) {
+        // Deprecated.
+    }
+
+    @Override
+    public boolean getShowWeekNumber() {
+        // Deprecated.
+        return false;
+    }
+
+    @Override
+    public void setFirstDayOfWeek(int firstDayOfWeek) {
+        mDayPickerView.setFirstDayOfWeek(firstDayOfWeek);
+    }
+
+    @Override
+    public int getFirstDayOfWeek() {
+        return mDayPickerView.getFirstDayOfWeek();
+    }
+
+    @Override
+    public void setDate(long date) {
+        mDayPickerView.setDate(date, true, false);
+    }
+
+    @Override
+    public void setDate(long date, boolean animate, boolean center) {
+        mDayPickerView.setDate(date, animate, center);
+    }
+
+    @Override
+    public long getDate() {
+        return mDayPickerView.getDate();
+    }
+
+    @Override
+    public void setOnDateChangeListener(CalendarView.OnDateChangeListener listener) {
+        mOnDateChangeListener = listener;
+    }
+
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        // Nothing to do here, configuration changes are already propagated
+        // by ViewGroup.
+    }
+
+    private final DayPickerView.OnDaySelectedListener mOnDaySelectedListener =
+            new DayPickerView.OnDaySelectedListener() {
+        @Override
+        public void onDaySelected(DayPickerView view, Calendar day) {
+            if (mOnDateChangeListener != null) {
+                final int year = day.get(Calendar.YEAR);
+                final int month = day.get(Calendar.MONTH);
+                final int dayOfMonth = day.get(Calendar.DAY_OF_MONTH);
+                mOnDateChangeListener.onSelectedDayChange(mDelegator, year, month, dayOfMonth);
+            }
+        }
+    };
+}
diff --git a/core/java/android/widget/DatePickerCalendarDelegate.java b/core/java/android/widget/DatePickerCalendarDelegate.java
index cf3dbab..820bf78 100644
--- a/core/java/android/widget/DatePickerCalendarDelegate.java
+++ b/core/java/android/widget/DatePickerCalendarDelegate.java
@@ -185,8 +185,9 @@
 
         mDayPickerView = new DayPickerView(mContext);
         mDayPickerView.setFirstDayOfWeek(mFirstDayOfWeek);
-        mDayPickerView.setRange(mMinDate, mMaxDate);
-        mDayPickerView.setDay(mCurrentDate);
+        mDayPickerView.setMinDate(mMinDate.getTimeInMillis());
+        mDayPickerView.setMaxDate(mMaxDate.getTimeInMillis());
+        mDayPickerView.setDate(mCurrentDate.getTimeInMillis());
         mDayPickerView.setOnDaySelectedListener(mOnDaySelectedListener);
 
         mYearPickerView = new YearPickerView(mContext);
@@ -336,7 +337,7 @@
 
         switch (viewIndex) {
             case MONTH_AND_DAY_VIEW:
-                mDayPickerView.setDay(getSelectedDay());
+                mDayPickerView.setDate(getSelectedDay().getTimeInMillis());
                 if (mCurrentView != viewIndex) {
                     mMonthAndDayLayout.setSelected(true);
                     mHeaderYearTextView.setSelected(false);
@@ -414,7 +415,7 @@
             updateDisplay(false);
         }
         mMinDate.setTimeInMillis(minDate);
-        mDayPickerView.setRange(mMinDate, mMaxDate);
+        mDayPickerView.setMinDate(minDate);
         mYearPickerView.setRange(mMinDate, mMaxDate);
     }
 
@@ -436,7 +437,7 @@
             updateDisplay(false);
         }
         mMaxDate.setTimeInMillis(maxDate);
-        mDayPickerView.setRange(mMinDate, mMaxDate);
+        mDayPickerView.setMaxDate(maxDate);
         mYearPickerView.setRange(mMinDate, mMaxDate);
     }
 
@@ -616,7 +617,7 @@
             listener.onDateChanged();
         }
 
-        mDayPickerView.setDay(getSelectedDay());
+        mDayPickerView.setDate(getSelectedDay().getTimeInMillis());
     }
 
     @Override
diff --git a/core/java/android/widget/DayPickerView.java b/core/java/android/widget/DayPickerView.java
index 6cb1c9d..7db3fb9 100644
--- a/core/java/android/widget/DayPickerView.java
+++ b/core/java/android/widget/DayPickerView.java
@@ -58,6 +58,8 @@
     private Calendar mMinDate = Calendar.getInstance();
     private Calendar mMaxDate = Calendar.getInstance();
 
+    private Calendar mTempCalendar;
+
     private OnDaySelectedListener mOnDaySelectedListener;
 
     // which month should be displayed/highlighted [0-11]
@@ -77,28 +79,65 @@
         setDrawSelectorOnTop(false);
         setUpListView();
 
-        goTo(mSelectedDay, false, true, true);
+        goTo(mSelectedDay.getTimeInMillis(), false, false, true);
 
         mAdapter.setOnDaySelectedListener(mProxyOnDaySelectedListener);
     }
 
-    public void setDay(Calendar day) {
-        goTo(day, false, true, true);
+    /**
+     * Sets the currently selected date to the specified timestamp. Jumps
+     * immediately to the new date. To animate to the new date, use
+     * {@link #setDate(long, boolean, boolean)}.
+     *
+     * @param timeInMillis
+     */
+    public void setDate(long timeInMillis) {
+        setDate(timeInMillis, false, true);
+    }
+
+    public void setDate(long timeInMillis, boolean animate, boolean forceScroll) {
+        goTo(timeInMillis, animate, true, forceScroll);
+    }
+
+    public long getDate() {
+        return mSelectedDay.getTimeInMillis();
     }
 
     public void setFirstDayOfWeek(int firstDayOfWeek) {
         mAdapter.setFirstDayOfWeek(firstDayOfWeek);
     }
 
-    public void setRange(Calendar minDate, Calendar maxDate) {
-        mMinDate.setTimeInMillis(minDate.getTimeInMillis());
-        mMaxDate.setTimeInMillis(maxDate.getTimeInMillis());
+    public int getFirstDayOfWeek() {
+        return mAdapter.getFirstDayOfWeek();
+    }
 
+    public void setMinDate(long timeInMillis) {
+        mMinDate.setTimeInMillis(timeInMillis);
+        onRangeChanged();
+    }
+
+    public long getMinDate() {
+        return mMinDate.getTimeInMillis();
+    }
+
+    public void setMaxDate(long timeInMillis) {
+        mMaxDate.setTimeInMillis(timeInMillis);
+        onRangeChanged();
+    }
+
+    public long getMaxDate() {
+        return mMaxDate.getTimeInMillis();
+    }
+
+    /**
+     * Handles changes to date range.
+     */
+    public void onRangeChanged() {
         mAdapter.setRange(mMinDate, mMaxDate);
 
         // Changing the min/max date changes the selection position since we
-        // don't really have stable IDs.
-        goTo(mSelectedDay, false, true, true);
+        // don't really have stable IDs. Jumps immediately to the new position.
+        goTo(mSelectedDay.getTimeInMillis(), false, false, true);
     }
 
     /**
@@ -136,12 +175,20 @@
         return diffMonths;
     }
 
-    private int getPositionFromDay(Calendar day) {
+    private int getPositionFromDay(long timeInMillis) {
         final int diffMonthMax = getDiffMonths(mMinDate, mMaxDate);
-        final int diffMonth = getDiffMonths(mMinDate, day);
+        final int diffMonth = getDiffMonths(mMinDate, getTempCalendarForTime(timeInMillis));
         return MathUtils.constrain(diffMonth, 0, diffMonthMax);
     }
 
+    private Calendar getTempCalendarForTime(long timeInMillis) {
+        if (mTempCalendar == null) {
+            mTempCalendar = Calendar.getInstance();
+        }
+        mTempCalendar.setTimeInMillis(timeInMillis);
+        return mTempCalendar;
+    }
+
     /**
      * This moves to the specified time in the view. If the time is not already
      * in range it will move the list so that the first of the month containing
@@ -157,14 +204,14 @@
      *            visible
      * @return Whether or not the view animated to the new location
      */
-    private boolean goTo(Calendar day, boolean animate, boolean setSelected, boolean forceScroll) {
+    private boolean goTo(long day, boolean animate, boolean setSelected, boolean forceScroll) {
 
         // Set the selected day
         if (setSelected) {
-            mSelectedDay.setTimeInMillis(day.getTimeInMillis());
+            mSelectedDay.setTimeInMillis(day);
         }
 
-        mTempDay.setTimeInMillis(day.getTimeInMillis());
+        mTempDay.setTimeInMillis(day);
         final int position = getPositionFromDay(day);
 
         View child;
@@ -258,6 +305,10 @@
         mAdapter.setCalendarTextColor(colors);
     }
 
+    void setCalendarTextAppearance(int resId) {
+        mAdapter.setCalendarTextAppearance(resId);
+    }
+
     protected class ScrollStateRunnable implements Runnable {
         private int mNewState;
         private View mParent;
@@ -415,7 +466,7 @@
     }
 
     private String getMonthAndYearString(Calendar day) {
-        StringBuffer sbuf = new StringBuffer();
+        final StringBuilder sbuf = new StringBuilder();
         sbuf.append(day.getDisplayName(Calendar.MONTH, Calendar.LONG, Locale.getDefault()));
         sbuf.append(" ");
         sbuf.append(mYearFormat.format(day.getTime()));
@@ -429,8 +480,8 @@
     @Override
     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
         super.onInitializeAccessibilityNodeInfo(info);
-        info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
-        info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
+        info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD);
+        info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD);
     }
 
     /**
@@ -474,7 +525,7 @@
 
         // Go to that month.
         announceForAccessibility(getMonthAndYearString(day));
-        goTo(day, true, false, true);
+        goTo(day.getTimeInMillis(), true, false, true);
         mPerformingScroll = true;
         return true;
     }
diff --git a/core/java/android/widget/FastScroller.java b/core/java/android/widget/FastScroller.java
index 06bb32c..fe143dec 100644
--- a/core/java/android/widget/FastScroller.java
+++ b/core/java/android/widget/FastScroller.java
@@ -106,6 +106,9 @@
      */
     private final int[] mPreviewResId = new int[2];
 
+    /** The minimum touch target size in pixels. */
+    private final int mMinimumTouchTarget;
+
     /**
      * Padding in pixels around the preview text. Applied as layout margins to
      * the preview text and padding to the preview image.
@@ -254,6 +257,9 @@
         mPrimaryText = createPreviewTextView(context);
         mSecondaryText = createPreviewTextView(context);
 
+        mMinimumTouchTarget = listView.getResources().getDimensionPixelSize(
+                com.android.internal.R.dimen.fast_scroller_minimum_touch_target);
+
         setStyle(styleResId);
 
         final ViewGroupOverlay overlay = listView.getOverlay();
@@ -1474,10 +1480,18 @@
     }
 
     private boolean isPointInsideX(float x) {
+        final float offset = mThumbImage.getTranslationX();
+        final float left = mThumbImage.getLeft() + offset;
+        final float right = mThumbImage.getRight() + offset;
+
+        // Apply the minimum touch target size.
+        final float targetSizeDiff = mMinimumTouchTarget - (right - left);
+        final float adjust = targetSizeDiff > 0 ? targetSizeDiff : 0;
+
         if (mLayoutFromRight) {
-            return x >= mThumbImage.getLeft();
+            return x >= mThumbImage.getLeft() - adjust;
         } else {
-            return x <= mThumbImage.getRight();
+            return x <= mThumbImage.getRight() + adjust;
         }
     }
 
@@ -1485,7 +1499,12 @@
         final float offset = mThumbImage.getTranslationY();
         final float top = mThumbImage.getTop() + offset;
         final float bottom = mThumbImage.getBottom() + offset;
-        return y >= top && y <= bottom;
+
+        // Apply the minimum touch target size.
+        final float targetSizeDiff = mMinimumTouchTarget - (bottom - top);
+        final float adjust = targetSizeDiff > 0 ? targetSizeDiff / 2 : 0;
+
+        return y >= (top - adjust) && y <= (bottom + adjust);
     }
 
     /**
diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java
index dfdf606..4ee6418 100644
--- a/core/java/android/widget/SearchView.java
+++ b/core/java/android/widget/SearchView.java
@@ -45,7 +45,6 @@
 import android.text.style.ImageSpan;
 import android.util.AttributeSet;
 import android.util.Log;
-import android.util.TypedValue;
 import android.view.CollapsibleActionView;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
@@ -99,17 +98,21 @@
      */
     private static final String IME_OPTION_NO_MICROPHONE = "nm";
 
-    private final SearchAutoComplete mQueryTextView;
+    private final SearchAutoComplete mSearchSrcTextView;
     private final View mSearchEditFrame;
     private final View mSearchPlate;
     private final View mSubmitArea;
     private final ImageView mSearchButton;
-    private final ImageView mSubmitButton;
+    private final ImageView mGoButton;
     private final ImageView mCloseButton;
     private final ImageView mVoiceButton;
-    private final ImageView mSearchHintIcon;
     private final View mDropDownAnchor;
-    private final int mSearchIconResId;
+
+    /** Icon optionally displayed when the SearchView is collapsed. */
+    private final ImageView mCollapsedIcon;
+
+    /** Drawable used as an EditText hint. */
+    private final Drawable mSearchHintIcon;
 
     // Resources used by SuggestionsAdapter to display suggestions.
     private final int mSuggestionRowLayout;
@@ -262,30 +265,38 @@
                 attrs, R.styleable.SearchView, defStyleAttr, defStyleRes);
         final LayoutInflater inflater = (LayoutInflater) context.getSystemService(
                 Context.LAYOUT_INFLATER_SERVICE);
-        final int layoutResId = a.getResourceId(R.styleable.SearchView_layout, R.layout.search_view);
+        final int layoutResId = a.getResourceId(
+                R.styleable.SearchView_layout, R.layout.search_view);
         inflater.inflate(layoutResId, this, true);
 
-        mQueryTextView = (SearchAutoComplete) findViewById(R.id.search_src_text);
-        mQueryTextView.setSearchView(this);
+        mSearchSrcTextView = (SearchAutoComplete) findViewById(R.id.search_src_text);
+        mSearchSrcTextView.setSearchView(this);
 
         mSearchEditFrame = findViewById(R.id.search_edit_frame);
         mSearchPlate = findViewById(R.id.search_plate);
         mSubmitArea = findViewById(R.id.submit_area);
         mSearchButton = (ImageView) findViewById(R.id.search_button);
-        mSubmitButton = (ImageView) findViewById(R.id.search_go_btn);
+        mGoButton = (ImageView) findViewById(R.id.search_go_btn);
         mCloseButton = (ImageView) findViewById(R.id.search_close_btn);
         mVoiceButton = (ImageView) findViewById(R.id.search_voice_btn);
-        mSearchHintIcon = (ImageView) findViewById(R.id.search_mag_icon);
+        mCollapsedIcon = (ImageView) findViewById(R.id.search_mag_icon);
 
         // Set up icons and backgrounds.
         mSearchPlate.setBackground(a.getDrawable(R.styleable.SearchView_queryBackground));
         mSubmitArea.setBackground(a.getDrawable(R.styleable.SearchView_submitBackground));
-        mSearchIconResId = a.getResourceId(R.styleable.SearchView_searchIcon, 0);
-        mSearchButton.setImageResource(mSearchIconResId);
-        mSubmitButton.setImageDrawable(a.getDrawable(R.styleable.SearchView_goIcon));
+        mSearchButton.setImageDrawable(a.getDrawable(R.styleable.SearchView_searchIcon));
+        mGoButton.setImageDrawable(a.getDrawable(R.styleable.SearchView_goIcon));
         mCloseButton.setImageDrawable(a.getDrawable(R.styleable.SearchView_closeIcon));
         mVoiceButton.setImageDrawable(a.getDrawable(R.styleable.SearchView_voiceIcon));
-        mSearchHintIcon.setImageDrawable(a.getDrawable(R.styleable.SearchView_searchIcon));
+        mCollapsedIcon.setImageDrawable(a.getDrawable(R.styleable.SearchView_searchIcon));
+
+        // Prior to L MR1, the search hint icon defaulted to searchIcon. If the
+        // style does not have an explicit value set, fall back to that.
+        if (a.hasValueOrEmpty(R.styleable.SearchView_searchHintIcon)) {
+            mSearchHintIcon = a.getDrawable(R.styleable.SearchView_searchHintIcon);
+        } else {
+            mSearchHintIcon = a.getDrawable(R.styleable.SearchView_searchIcon);
+        }
 
         // Extract dropdown layout resource IDs for later use.
         mSuggestionRowLayout = a.getResourceId(R.styleable.SearchView_suggestionRowLayout,
@@ -294,18 +305,18 @@
 
         mSearchButton.setOnClickListener(mOnClickListener);
         mCloseButton.setOnClickListener(mOnClickListener);
-        mSubmitButton.setOnClickListener(mOnClickListener);
+        mGoButton.setOnClickListener(mOnClickListener);
         mVoiceButton.setOnClickListener(mOnClickListener);
-        mQueryTextView.setOnClickListener(mOnClickListener);
+        mSearchSrcTextView.setOnClickListener(mOnClickListener);
 
-        mQueryTextView.addTextChangedListener(mTextWatcher);
-        mQueryTextView.setOnEditorActionListener(mOnEditorActionListener);
-        mQueryTextView.setOnItemClickListener(mOnItemClickListener);
-        mQueryTextView.setOnItemSelectedListener(mOnItemSelectedListener);
-        mQueryTextView.setOnKeyListener(mTextKeyListener);
+        mSearchSrcTextView.addTextChangedListener(mTextWatcher);
+        mSearchSrcTextView.setOnEditorActionListener(mOnEditorActionListener);
+        mSearchSrcTextView.setOnItemClickListener(mOnItemClickListener);
+        mSearchSrcTextView.setOnItemSelectedListener(mOnItemSelectedListener);
+        mSearchSrcTextView.setOnKeyListener(mTextKeyListener);
 
         // Inform any listener of focus changes
-        mQueryTextView.setOnFocusChangeListener(new OnFocusChangeListener() {
+        mSearchSrcTextView.setOnFocusChangeListener(new OnFocusChangeListener() {
 
             public void onFocusChange(View v, boolean hasFocus) {
                 if (mOnQueryTextFocusChangeListener != null) {
@@ -350,7 +361,7 @@
         mVoiceAppSearchIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
         mVoiceAppSearchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 
-        mDropDownAnchor = findViewById(mQueryTextView.getDropDownAnchor());
+        mDropDownAnchor = findViewById(mSearchSrcTextView.getDropDownAnchor());
         if (mDropDownAnchor != null) {
             mDropDownAnchor.addOnLayoutChangeListener(new OnLayoutChangeListener() {
                 @Override
@@ -393,7 +404,7 @@
         if (mVoiceButtonEnabled) {
             // Disable the microphone on the keyboard, as a mic is displayed near the text box
             // TODO: use imeOptions to disable voice input when the new API will be available
-            mQueryTextView.setPrivateImeOptions(IME_OPTION_NO_MICROPHONE);
+            mSearchSrcTextView.setPrivateImeOptions(IME_OPTION_NO_MICROPHONE);
         }
         updateViewsVisibility(isIconified());
     }
@@ -416,7 +427,7 @@
      * @attr ref android.R.styleable#SearchView_imeOptions
      */
     public void setImeOptions(int imeOptions) {
-        mQueryTextView.setImeOptions(imeOptions);
+        mSearchSrcTextView.setImeOptions(imeOptions);
     }
 
     /**
@@ -427,7 +438,7 @@
      * @attr ref android.R.styleable#SearchView_imeOptions
      */
     public int getImeOptions() {
-        return mQueryTextView.getImeOptions();
+        return mSearchSrcTextView.getImeOptions();
     }
 
     /**
@@ -439,7 +450,7 @@
      * @attr ref android.R.styleable#SearchView_inputType
      */
     public void setInputType(int inputType) {
-        mQueryTextView.setInputType(inputType);
+        mSearchSrcTextView.setInputType(inputType);
     }
 
     /**
@@ -449,7 +460,7 @@
      * @attr ref android.R.styleable#SearchView_inputType
      */
     public int getInputType() {
-        return mQueryTextView.getInputType();
+        return mSearchSrcTextView.getInputType();
     }
 
     /** @hide */
@@ -461,7 +472,7 @@
         if (!isFocusable()) return false;
         // If it is not iconified, then give the focus to the text field
         if (!isIconified()) {
-            boolean result = mQueryTextView.requestFocus(direction, previouslyFocusedRect);
+            boolean result = mSearchSrcTextView.requestFocus(direction, previouslyFocusedRect);
             if (result) {
                 updateViewsVisibility(false);
             }
@@ -477,7 +488,7 @@
         mClearingFocus = true;
         setImeVisibility(false);
         super.clearFocus();
-        mQueryTextView.clearFocus();
+        mSearchSrcTextView.clearFocus();
         mClearingFocus = false;
     }
 
@@ -536,7 +547,7 @@
      * @return the query string
      */
     public CharSequence getQuery() {
-        return mQueryTextView.getText();
+        return mSearchSrcTextView.getText();
     }
 
     /**
@@ -548,9 +559,9 @@
      * text field.
      */
     public void setQuery(CharSequence query, boolean submit) {
-        mQueryTextView.setText(query);
+        mSearchSrcTextView.setText(query);
         if (query != null) {
-            mQueryTextView.setSelection(mQueryTextView.length());
+            mSearchSrcTextView.setSelection(mSearchSrcTextView.length());
             mUserQuery = query;
         }
 
@@ -711,7 +722,7 @@
     public void setSuggestionsAdapter(CursorAdapter adapter) {
         mSuggestionsAdapter = adapter;
 
-        mQueryTextView.setAdapter(mSuggestionsAdapter);
+        mSearchSrcTextView.setAdapter(mSuggestionsAdapter);
     }
 
     /**
@@ -789,12 +800,12 @@
         // Visibility of views that are visible when collapsed
         final int visCollapsed = collapsed ? VISIBLE : GONE;
         // Is there text in the query
-        final boolean hasText = !TextUtils.isEmpty(mQueryTextView.getText());
+        final boolean hasText = !TextUtils.isEmpty(mSearchSrcTextView.getText());
 
         mSearchButton.setVisibility(visCollapsed);
         updateSubmitButton(hasText);
         mSearchEditFrame.setVisibility(collapsed ? GONE : VISIBLE);
-        mSearchHintIcon.setVisibility(mIconifiedByDefault ? GONE : VISIBLE);
+        mCollapsedIcon.setVisibility(mIconifiedByDefault ? GONE : VISIBLE);
         updateCloseButton();
         updateVoiceButton(!hasText);
         updateSubmitArea();
@@ -827,13 +838,13 @@
                 && (hasText || !mVoiceButtonEnabled)) {
             visibility = VISIBLE;
         }
-        mSubmitButton.setVisibility(visibility);
+        mGoButton.setVisibility(visibility);
     }
 
     private void updateSubmitArea() {
         int visibility = GONE;
         if (isSubmitAreaEnabled()
-                && (mSubmitButton.getVisibility() == VISIBLE
+                && (mGoButton.getVisibility() == VISIBLE
                         || mVoiceButton.getVisibility() == VISIBLE)) {
             visibility = VISIBLE;
         }
@@ -841,12 +852,15 @@
     }
 
     private void updateCloseButton() {
-        final boolean hasText = !TextUtils.isEmpty(mQueryTextView.getText());
+        final boolean hasText = !TextUtils.isEmpty(mSearchSrcTextView.getText());
         // Should we show the close button? It is not shown if there's no focus,
         // field is not iconified by default and there is no text in it.
         final boolean showClose = hasText || (mIconifiedByDefault && !mExpandedInActionView);
         mCloseButton.setVisibility(showClose ? VISIBLE : GONE);
-        mCloseButton.getDrawable().setState(hasText ? ENABLED_STATE_SET : EMPTY_STATE_SET);
+        final Drawable closeButtonImg = mCloseButton.getDrawable();
+        if (closeButtonImg != null){
+            closeButtonImg.setState(hasText ? ENABLED_STATE_SET : EMPTY_STATE_SET);
+        }
     }
 
     private void postUpdateFocusedState() {
@@ -854,9 +868,16 @@
     }
 
     private void updateFocusedState() {
-        boolean focused = mQueryTextView.hasFocus();
-        mSearchPlate.getBackground().setState(focused ? FOCUSED_STATE_SET : EMPTY_STATE_SET);
-        mSubmitArea.getBackground().setState(focused ? FOCUSED_STATE_SET : EMPTY_STATE_SET);
+        final boolean focused = mSearchSrcTextView.hasFocus();
+        final int[] stateSet = focused ? FOCUSED_STATE_SET : EMPTY_STATE_SET;
+        final Drawable searchPlateBg = mSearchPlate.getBackground();
+        if (searchPlateBg != null) {
+            searchPlateBg.setState(stateSet);
+        }
+        final Drawable submitAreaBg = mSubmitArea.getBackground();
+        if (submitAreaBg != null) {
+            submitAreaBg.setState(stateSet);
+        }
         invalidate();
     }
 
@@ -896,11 +917,11 @@
                 onSearchClicked();
             } else if (v == mCloseButton) {
                 onCloseClicked();
-            } else if (v == mSubmitButton) {
+            } else if (v == mGoButton) {
                 onSubmitQuery();
             } else if (v == mVoiceButton) {
                 onVoiceClicked();
-            } else if (v == mQueryTextView) {
+            } else if (v == mSearchSrcTextView) {
                 forceSuggestionQuery();
             }
         }
@@ -925,7 +946,7 @@
         // entered query with the action key
         SearchableInfo.ActionKeyInfo actionKey = mSearchable.findActionKey(keyCode);
         if ((actionKey != null) && (actionKey.getQueryActionMsg() != null)) {
-            launchQuerySearch(keyCode, actionKey.getQueryActionMsg(), mQueryTextView.getText()
+            launchQuerySearch(keyCode, actionKey.getQueryActionMsg(), mSearchSrcTextView.getText()
                     .toString());
             return true;
         }
@@ -947,25 +968,25 @@
 
             if (DBG) {
                 Log.d(LOG_TAG, "mTextListener.onKey(" + keyCode + "," + event + "), selection: "
-                        + mQueryTextView.getListSelection());
+                        + mSearchSrcTextView.getListSelection());
             }
 
             // If a suggestion is selected, handle enter, search key, and action keys
             // as presses on the selected suggestion
-            if (mQueryTextView.isPopupShowing()
-                    && mQueryTextView.getListSelection() != ListView.INVALID_POSITION) {
+            if (mSearchSrcTextView.isPopupShowing()
+                    && mSearchSrcTextView.getListSelection() != ListView.INVALID_POSITION) {
                 return onSuggestionsKey(v, keyCode, event);
             }
 
             // If there is text in the query box, handle enter, and action keys
             // The search key is handled by the dialog's onKeyDown().
-            if (!mQueryTextView.isEmpty() && event.hasNoModifiers()) {
+            if (!mSearchSrcTextView.isEmpty() && event.hasNoModifiers()) {
                 if (event.getAction() == KeyEvent.ACTION_UP) {
                     if (keyCode == KeyEvent.KEYCODE_ENTER) {
                         v.cancelLongPress();
 
                         // Launch as a regular search.
-                        launchQuerySearch(KeyEvent.KEYCODE_UNKNOWN, null, mQueryTextView.getText()
+                        launchQuerySearch(KeyEvent.KEYCODE_UNKNOWN, null, mSearchSrcTextView.getText()
                                 .toString());
                         return true;
                     }
@@ -973,7 +994,7 @@
                 if (event.getAction() == KeyEvent.ACTION_DOWN) {
                     SearchableInfo.ActionKeyInfo actionKey = mSearchable.findActionKey(keyCode);
                     if ((actionKey != null) && (actionKey.getQueryActionMsg() != null)) {
-                        launchQuerySearch(keyCode, actionKey.getQueryActionMsg(), mQueryTextView
+                        launchQuerySearch(keyCode, actionKey.getQueryActionMsg(), mSearchSrcTextView
                                 .getText().toString());
                         return true;
                     }
@@ -1001,7 +1022,7 @@
             // "click")
             if (keyCode == KeyEvent.KEYCODE_ENTER || keyCode == KeyEvent.KEYCODE_SEARCH
                     || keyCode == KeyEvent.KEYCODE_TAB) {
-                int position = mQueryTextView.getListSelection();
+                int position = mSearchSrcTextView.getListSelection();
                 return onItemClicked(position, KeyEvent.KEYCODE_UNKNOWN, null);
             }
 
@@ -1012,18 +1033,18 @@
                 // left key, at end if right key
                 // TODO: Reverse left/right for right-to-left languages, e.g.
                 // Arabic
-                int selPoint = (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) ? 0 : mQueryTextView
+                int selPoint = (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) ? 0 : mSearchSrcTextView
                         .length();
-                mQueryTextView.setSelection(selPoint);
-                mQueryTextView.setListSelection(0);
-                mQueryTextView.clearListSelection();
-                mQueryTextView.ensureImeVisible(true);
+                mSearchSrcTextView.setSelection(selPoint);
+                mSearchSrcTextView.setListSelection(0);
+                mSearchSrcTextView.clearListSelection();
+                mSearchSrcTextView.ensureImeVisible(true);
 
                 return true;
             }
 
             // Next, check for an "up and out" move
-            if (keyCode == KeyEvent.KEYCODE_DPAD_UP && 0 == mQueryTextView.getListSelection()) {
+            if (keyCode == KeyEvent.KEYCODE_DPAD_UP && 0 == mSearchSrcTextView.getListSelection()) {
                 // TODO: restoreUserQuery();
                 // let ACTV complete the move
                 return false;
@@ -1035,7 +1056,7 @@
                     && ((actionKey.getSuggestActionMsg() != null) || (actionKey
                             .getSuggestActionMsgColumn() != null))) {
                 // launch suggestion using action key column
-                int position = mQueryTextView.getListSelection();
+                int position = mSearchSrcTextView.getListSelection();
                 if (position != ListView.INVALID_POSITION) {
                     Cursor c = mSuggestionsAdapter.getCursor();
                     if (c.moveToPosition(position)) {
@@ -1078,24 +1099,24 @@
     }
 
     private CharSequence getDecoratedHint(CharSequence hintText) {
-        // If the field is always expanded, then don't add the search icon to the hint
-        if (!mIconifiedByDefault) {
+        // If the field is always expanded or we don't have a search hint icon,
+        // then don't add the search icon to the hint.
+        if (!mIconifiedByDefault || mSearchHintIcon == null) {
             return hintText;
         }
 
-        final Drawable searchIcon = getContext().getDrawable(mSearchIconResId);
-        final int textSize = (int) (mQueryTextView.getTextSize() * 1.25);
-        searchIcon.setBounds(0, 0, textSize, textSize);
+        final int textSize = (int) (mSearchSrcTextView.getTextSize() * 1.25);
+        mSearchHintIcon.setBounds(0, 0, textSize, textSize);
 
-        final SpannableStringBuilder ssb = new SpannableStringBuilder("   "); // for the icon
+        final SpannableStringBuilder ssb = new SpannableStringBuilder("   ");
+        ssb.setSpan(new ImageSpan(mSearchHintIcon), 1, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
         ssb.append(hintText);
-        ssb.setSpan(new ImageSpan(searchIcon), 1, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
         return ssb;
     }
 
     private void updateQueryHint() {
         if (mQueryHint != null) {
-            mQueryTextView.setHint(getDecoratedHint(mQueryHint));
+            mSearchSrcTextView.setHint(getDecoratedHint(mQueryHint));
         } else if (mSearchable != null) {
             CharSequence hint = null;
             int hintId = mSearchable.getHintId();
@@ -1103,10 +1124,10 @@
                 hint = getContext().getString(hintId);
             }
             if (hint != null) {
-                mQueryTextView.setHint(getDecoratedHint(hint));
+                mSearchSrcTextView.setHint(getDecoratedHint(hint));
             }
         } else {
-            mQueryTextView.setHint(getDecoratedHint(""));
+            mSearchSrcTextView.setHint(getDecoratedHint(""));
         }
     }
 
@@ -1114,9 +1135,9 @@
      * Updates the auto-complete text view.
      */
     private void updateSearchAutoComplete() {
-        mQueryTextView.setDropDownAnimationStyle(0); // no animation
-        mQueryTextView.setThreshold(mSearchable.getSuggestThreshold());
-        mQueryTextView.setImeOptions(mSearchable.getImeOptions());
+        mSearchSrcTextView.setDropDownAnimationStyle(0); // no animation
+        mSearchSrcTextView.setThreshold(mSearchable.getSuggestThreshold());
+        mSearchSrcTextView.setImeOptions(mSearchable.getImeOptions());
         int inputType = mSearchable.getInputType();
         // We only touch this if the input type is set up for text (which it almost certainly
         // should be, in the case of search!)
@@ -1135,7 +1156,7 @@
                 inputType |= InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS;
             }
         }
-        mQueryTextView.setInputType(inputType);
+        mSearchSrcTextView.setInputType(inputType);
         if (mSuggestionsAdapter != null) {
             mSuggestionsAdapter.changeCursor(null);
         }
@@ -1144,7 +1165,7 @@
         if (mSearchable.getSuggestAuthority() != null) {
             mSuggestionsAdapter = new SuggestionsAdapter(getContext(),
                     this, mSearchable, mOutsideDrawablesCache);
-            mQueryTextView.setAdapter(mSuggestionsAdapter);
+            mSearchSrcTextView.setAdapter(mSuggestionsAdapter);
             ((SuggestionsAdapter) mSuggestionsAdapter).setQueryRefinement(
                     mQueryRefinement ? SuggestionsAdapter.REFINE_ALL
                     : SuggestionsAdapter.REFINE_BY_ENTRY);
@@ -1161,7 +1182,7 @@
         int visibility = GONE;
         if (mVoiceButtonEnabled && !isIconified() && empty) {
             visibility = VISIBLE;
-            mSubmitButton.setVisibility(GONE);
+            mGoButton.setVisibility(GONE);
         }
         mVoiceButton.setVisibility(visibility);
     }
@@ -1178,7 +1199,7 @@
     };
 
     private void onTextChanged(CharSequence newText) {
-        CharSequence text = mQueryTextView.getText();
+        CharSequence text = mSearchSrcTextView.getText();
         mUserQuery = text;
         boolean hasText = !TextUtils.isEmpty(text);
         updateSubmitButton(hasText);
@@ -1192,7 +1213,7 @@
     }
 
     private void onSubmitQuery() {
-        CharSequence query = mQueryTextView.getText();
+        CharSequence query = mSearchSrcTextView.getText();
         if (query != null && TextUtils.getTrimmedLength(query) > 0) {
             if (mOnQueryChangeListener == null
                     || !mOnQueryChangeListener.onQueryTextSubmit(query.toString())) {
@@ -1206,11 +1227,11 @@
     }
 
     private void dismissSuggestions() {
-        mQueryTextView.dismissDropDown();
+        mSearchSrcTextView.dismissDropDown();
     }
 
     private void onCloseClicked() {
-        CharSequence text = mQueryTextView.getText();
+        CharSequence text = mSearchSrcTextView.getText();
         if (TextUtils.isEmpty(text)) {
             if (mIconifiedByDefault) {
                 // If the app doesn't override the close behavior
@@ -1222,8 +1243,8 @@
                 }
             }
         } else {
-            mQueryTextView.setText("");
-            mQueryTextView.requestFocus();
+            mSearchSrcTextView.setText("");
+            mSearchSrcTextView.requestFocus();
             setImeVisibility(true);
         }
 
@@ -1231,7 +1252,7 @@
 
     private void onSearchClicked() {
         updateViewsVisibility(false);
-        mQueryTextView.requestFocus();
+        mSearchSrcTextView.requestFocus();
         setImeVisibility(true);
         if (mOnSearchClickListener != null) {
             mOnSearchClickListener.onClick(this);
@@ -1266,7 +1287,7 @@
         // Delayed update to make sure that the focus has settled down and window focus changes
         // don't affect it. A synchronous update was not working.
         postUpdateFocusedState();
-        if (mQueryTextView.hasFocus()) {
+        if (mSearchSrcTextView.hasFocus()) {
             forceSuggestionQuery();
         }
     }
@@ -1286,7 +1307,7 @@
         setQuery("", false);
         clearFocus();
         updateViewsVisibility(true);
-        mQueryTextView.setImeOptions(mCollapsedImeOptions);
+        mSearchSrcTextView.setImeOptions(mCollapsedImeOptions);
         mExpandedInActionView = false;
     }
 
@@ -1298,9 +1319,9 @@
         if (mExpandedInActionView) return;
 
         mExpandedInActionView = true;
-        mCollapsedImeOptions = mQueryTextView.getImeOptions();
-        mQueryTextView.setImeOptions(mCollapsedImeOptions | EditorInfo.IME_FLAG_NO_FULLSCREEN);
-        mQueryTextView.setText("");
+        mCollapsedImeOptions = mSearchSrcTextView.getImeOptions();
+        mSearchSrcTextView.setImeOptions(mCollapsedImeOptions | EditorInfo.IME_FLAG_NO_FULLSCREEN);
+        mSearchSrcTextView.setText("");
         setIconified(false);
     }
 
@@ -1326,17 +1347,17 @@
                     ? res.getDimensionPixelSize(R.dimen.dropdownitem_icon_width)
                     + res.getDimensionPixelSize(R.dimen.dropdownitem_text_padding_left)
                     : 0;
-            mQueryTextView.getDropDownBackground().getPadding(dropDownPadding);
+            mSearchSrcTextView.getDropDownBackground().getPadding(dropDownPadding);
             int offset;
             if (isLayoutRtl) {
                 offset = - dropDownPadding.left;
             } else {
                 offset = anchorPadding - (dropDownPadding.left + iconOffset);
             }
-            mQueryTextView.setDropDownHorizontalOffset(offset);
+            mSearchSrcTextView.setDropDownHorizontalOffset(offset);
             final int width = mDropDownAnchor.getWidth() + dropDownPadding.left
                     + dropDownPadding.right + iconOffset - anchorPadding;
-            mQueryTextView.setDropDownWidth(width);
+            mSearchSrcTextView.setDropDownWidth(width);
         }
     }
 
@@ -1394,7 +1415,7 @@
      * Query rewriting.
      */
     private void rewriteQueryFromSuggestion(int position) {
-        CharSequence oldQuery = mQueryTextView.getText();
+        CharSequence oldQuery = mSearchSrcTextView.getText();
         Cursor c = mSuggestionsAdapter.getCursor();
         if (c == null) {
             return;
@@ -1460,9 +1481,9 @@
      * Sets the text in the query box, without updating the suggestions.
      */
     private void setQuery(CharSequence query) {
-        mQueryTextView.setText(query, true);
+        mSearchSrcTextView.setText(query, true);
         // Move the cursor to the end
-        mQueryTextView.setSelection(TextUtils.isEmpty(query) ? 0 : query.length());
+        mSearchSrcTextView.setSelection(TextUtils.isEmpty(query) ? 0 : query.length());
     }
 
     private void launchQuerySearch(int actionKey, String actionMsg, String query) {
@@ -1648,8 +1669,8 @@
     }
 
     private void forceSuggestionQuery() {
-        mQueryTextView.doBeforeTextChanged();
-        mQueryTextView.doAfterTextChanged();
+        mSearchSrcTextView.doBeforeTextChanged();
+        mSearchSrcTextView.doAfterTextChanged();
     }
 
     static boolean isLandscapeMode(Context context) {
diff --git a/core/java/android/widget/SimpleMonthAdapter.java b/core/java/android/widget/SimpleMonthAdapter.java
index ecd2912..24ebb2c 100644
--- a/core/java/android/widget/SimpleMonthAdapter.java
+++ b/core/java/android/widget/SimpleMonthAdapter.java
@@ -16,8 +16,12 @@
 
 package android.widget;
 
+import com.android.internal.R;
+
 import android.content.Context;
 import android.content.res.ColorStateList;
+import android.content.res.TypedArray;
+import android.graphics.Color;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.SimpleMonthView.OnDayClickListener;
@@ -33,15 +37,14 @@
 
     private final Context mContext;
 
-    private Calendar mSelectedDay;
-    private ColorStateList mCalendarTextColors;
+    private Calendar mSelectedDay = Calendar.getInstance();
+    private ColorStateList mCalendarTextColors = ColorStateList.valueOf(Color.BLACK);
     private OnDaySelectedListener mOnDaySelectedListener;
 
     private int mFirstDayOfWeek;
 
     public SimpleMonthAdapter(Context context) {
         mContext = context;
-        mSelectedDay = Calendar.getInstance();
     }
 
     public void setRange(Calendar min, Calendar max) {
@@ -57,6 +60,10 @@
         notifyDataSetInvalidated();
     }
 
+    public int getFirstDayOfWeek() {
+        return mFirstDayOfWeek;
+    }
+
     /**
      * Updates the selected day and related parameters.
      *
@@ -81,6 +88,24 @@
         mCalendarTextColors = colors;
     }
 
+    /**
+     * Sets the text color, size, style, hint color, and highlight color from
+     * the specified TextAppearance resource. This is mostly copied from
+     * {@link TextView#setTextAppearance(Context, int)}.
+     */
+    void setCalendarTextAppearance(int resId) {
+        final TypedArray a = mContext.obtainStyledAttributes(resId, R.styleable.TextAppearance);
+
+        final ColorStateList textColor = a.getColorStateList(R.styleable.TextAppearance_textColor);
+        if (textColor != null) {
+            mCalendarTextColors = textColor;
+        }
+
+        // TODO: Support font size, etc.
+
+        a.recycle();
+    }
+
     @Override
     public int getCount() {
         final int diffYear = mMaxDate.get(Calendar.YEAR) - mMinDate.get(Calendar.YEAR);
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 3ccced5..8d3db5b 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -384,8 +384,16 @@
      * @return Whether a saved pattern exists.
      */
     public boolean savedPatternExists() {
+        return savedPatternExists(getCurrentOrCallingUserId());
+    }
+
+    /**
+     * Check to see if the user has stored a lock pattern.
+     * @return Whether a saved pattern exists.
+     */
+    public boolean savedPatternExists(int userId) {
         try {
-            return getLockSettings().havePattern(getCurrentOrCallingUserId());
+            return getLockSettings().havePattern(userId);
         } catch (RemoteException re) {
             return false;
         }
@@ -396,8 +404,16 @@
      * @return Whether a saved pattern exists.
      */
     public boolean savedPasswordExists() {
+        return savedPasswordExists(getCurrentOrCallingUserId());
+    }
+
+     /**
+     * Check to see if the user has stored a lock pattern.
+     * @return Whether a saved pattern exists.
+     */
+    public boolean savedPasswordExists(int userId) {
         try {
-            return getLockSettings().havePassword(getCurrentOrCallingUserId());
+            return getLockSettings().havePassword(userId);
         } catch (RemoteException re) {
             return false;
         }
@@ -955,8 +971,15 @@
      * @return true if the lockscreen method is set to biometric weak
      */
     public boolean usingBiometricWeak() {
-        int quality =
-                (int) getLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED);
+        return usingBiometricWeak(getCurrentOrCallingUserId());
+    }
+
+    /**
+     * @return true if the lockscreen method is set to biometric weak
+     */
+    public boolean usingBiometricWeak(int userId) {
+        int quality = (int) getLong(
+                PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, userId);
         return quality == DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK;
     }
 
@@ -1096,15 +1119,22 @@
      * @return Whether the lock pattern is enabled, or if it is set as a backup for biometric weak
      */
     public boolean isLockPatternEnabled() {
+        return isLockPatternEnabled(getCurrentOrCallingUserId());
+    }
+
+    /**
+     * @return Whether the lock pattern is enabled, or if it is set as a backup for biometric weak
+     */
+    public boolean isLockPatternEnabled(int userId) {
         final boolean backupEnabled =
                 getLong(PASSWORD_TYPE_ALTERNATE_KEY,
-                        DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED)
+                        DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, userId)
                                 == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
 
-        return getBoolean(Settings.Secure.LOCK_PATTERN_ENABLED, false)
-                && (getLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED)
-                        == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING ||
-                        (usingBiometricWeak() && backupEnabled));
+        return getBoolean(Settings.Secure.LOCK_PATTERN_ENABLED, false, userId)
+                && (getLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED,
+                        userId) == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
+                        || (usingBiometricWeak(userId) && backupEnabled));
     }
 
     /**
@@ -1485,15 +1515,20 @@
     }
 
     public boolean isSecure() {
-        long mode = getKeyguardStoredPasswordQuality();
+        return isSecure(getCurrentOrCallingUserId());
+    }
+
+    public boolean isSecure(int userId) {
+        long mode = getKeyguardStoredPasswordQuality(userId);
         final boolean isPattern = mode == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
         final boolean isPassword = mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
                 || mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX
                 || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC
                 || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
                 || mode == DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
-        final boolean secure = isPattern && isLockPatternEnabled() && savedPatternExists()
-                || isPassword && savedPasswordExists();
+        final boolean secure =
+                isPattern && isLockPatternEnabled(userId) && savedPatternExists(userId)
+                || isPassword && savedPasswordExists(userId);
         return secure;
     }
 
diff --git a/core/java/com/android/internal/widget/SwipeDismissLayout.java b/core/java/com/android/internal/widget/SwipeDismissLayout.java
index 99b1bae..d617c05 100644
--- a/core/java/com/android/internal/widget/SwipeDismissLayout.java
+++ b/core/java/com/android/internal/widget/SwipeDismissLayout.java
@@ -26,6 +26,7 @@
 import android.view.View;
 import android.view.ViewConfiguration;
 import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
 import android.view.animation.AccelerateInterpolator;
 import android.view.animation.DecelerateInterpolator;
 import android.widget.FrameLayout;
@@ -76,6 +77,19 @@
 
     private OnDismissedListener mDismissedListener;
     private OnSwipeProgressChangedListener mProgressListener;
+    private ViewTreeObserver.OnEnterAnimationCompleteListener mOnEnterAnimationCompleteListener =
+            new ViewTreeObserver.OnEnterAnimationCompleteListener() {
+                @Override
+                public void onEnterAnimationComplete() {
+                    // SwipeDismissLayout assumes that the host Activity is translucent
+                    // and temporarily disables translucency when it is fully visible.
+                    // As soon as the user starts swiping, we will re-enable
+                    // translucency.
+                    if (getContext() instanceof Activity) {
+                        ((Activity) getContext()).convertFromTranslucent();
+                    }
+                }
+            };
 
     private float mLastX;
 
@@ -103,13 +117,6 @@
                 android.R.integer.config_shortAnimTime);
         mCancelInterpolator = new DecelerateInterpolator(1.5f);
         mDismissInterpolator = new AccelerateInterpolator(1.5f);
-        // SwipeDismissLayout assumes that the host Activity is translucent
-        // and temporarily disables translucency when it is fully visible.
-        // As soon as the user starts swiping, we will re-enable
-        // translucency.
-        if (context instanceof Activity) {
-            ((Activity) context).convertFromTranslucent();
-        }
     }
 
     public void setOnDismissedListener(OnDismissedListener listener) {
@@ -121,6 +128,24 @@
     }
 
     @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        if (getContext() instanceof Activity) {
+            getViewTreeObserver().addOnEnterAnimationCompleteListener(
+                    mOnEnterAnimationCompleteListener);
+        }
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        if (getContext() instanceof Activity) {
+            getViewTreeObserver().removeOnEnterAnimationCompleteListener(
+                    mOnEnterAnimationCompleteListener);
+        }
+    }
+
+    @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
         // offset because the view is translated during swipe
         ev.offsetLocation(mTranslationX, 0);
diff --git a/core/java/com/android/server/backup/SystemBackupAgent.java b/core/java/com/android/server/backup/SystemBackupAgent.java
index 26e2e2a..ed7ce63 100644
--- a/core/java/com/android/server/backup/SystemBackupAgent.java
+++ b/core/java/com/android/server/backup/SystemBackupAgent.java
@@ -23,6 +23,7 @@
 import android.app.backup.BackupAgentHelper;
 import android.app.backup.FullBackup;
 import android.app.backup.FullBackupDataOutput;
+import android.app.backup.RecentsBackupHelper;
 import android.app.backup.WallpaperBackupHelper;
 import android.content.Context;
 import android.os.Environment;
@@ -83,6 +84,8 @@
             }
         }
         addHelper("wallpaper", new WallpaperBackupHelper(SystemBackupAgent.this, files, keys));
+        addHelper("recents", new RecentsBackupHelper(SystemBackupAgent.this));
+
         super.onBackup(oldState, data, newState);
     }
 
@@ -113,6 +116,7 @@
         addHelper("system_files", new WallpaperBackupHelper(SystemBackupAgent.this,
                 new String[] { WALLPAPER_IMAGE },
                 new String[] { WALLPAPER_IMAGE_KEY} ));
+        addHelper("recents", new RecentsBackupHelper(SystemBackupAgent.this));
 
         try {
             super.onRestore(data, appVersionCode, newState);
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 4b4b367..cabe200 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -484,7 +484,8 @@
 
 static jint convertAudioPortConfigToNative(JNIEnv *env,
                                                struct audio_port_config *nAudioPortConfig,
-                                               const jobject jAudioPortConfig)
+                                               const jobject jAudioPortConfig,
+                                               bool useConfigMask)
 {
     jobject jAudioPort = env->GetObjectField(jAudioPortConfig, gAudioPortConfigFields.mPort);
     jobject jHandle = env->GetObjectField(jAudioPort, gAudioPortFields.mHandle);
@@ -503,8 +504,13 @@
     ALOGV("convertAudioPortConfigToNative handle %d role %d type %d",
           nAudioPortConfig->id, nAudioPortConfig->role, nAudioPortConfig->type);
 
+    unsigned int configMask = 0;
+
     nAudioPortConfig->sample_rate = env->GetIntField(jAudioPortConfig,
                                                      gAudioPortConfigFields.mSamplingRate);
+    if (nAudioPortConfig->sample_rate != 0) {
+        configMask |= AUDIO_PORT_CONFIG_SAMPLE_RATE;
+    }
 
     bool useInMask = useInChannelMask(nAudioPortConfig->type, nAudioPortConfig->role);
     audio_channel_mask_t nMask;
@@ -518,22 +524,34 @@
         ALOGV("convertAudioPortConfigToNative OUT mask java %x native %x", jMask, nMask);
     }
     nAudioPortConfig->channel_mask = nMask;
+    if (nAudioPortConfig->channel_mask != AUDIO_CHANNEL_NONE) {
+        configMask |= AUDIO_PORT_CONFIG_CHANNEL_MASK;
+    }
 
     jint jFormat = env->GetIntField(jAudioPortConfig, gAudioPortConfigFields.mFormat);
     audio_format_t nFormat = audioFormatToNative(jFormat);
     ALOGV("convertAudioPortConfigToNative format %d native %d", jFormat, nFormat);
     nAudioPortConfig->format = nFormat;
+    if (nAudioPortConfig->format != AUDIO_FORMAT_DEFAULT &&
+            nAudioPortConfig->format != AUDIO_FORMAT_INVALID) {
+        configMask |= AUDIO_PORT_CONFIG_FORMAT;
+    }
+
     jobject jGain = env->GetObjectField(jAudioPortConfig, gAudioPortConfigFields.mGain);
     if (jGain != NULL) {
         convertAudioGainConfigToNative(env, &nAudioPortConfig->gain, jGain, useInMask);
         env->DeleteLocalRef(jGain);
+        configMask |= AUDIO_PORT_CONFIG_GAIN;
     } else {
         ALOGV("convertAudioPortConfigToNative no gain");
         nAudioPortConfig->gain.index = -1;
     }
-    nAudioPortConfig->config_mask = env->GetIntField(jAudioPortConfig,
-                                                     gAudioPortConfigFields.mConfigMask);
-
+    if (useConfigMask) {
+        nAudioPortConfig->config_mask = env->GetIntField(jAudioPortConfig,
+                                                         gAudioPortConfigFields.mConfigMask);
+    } else {
+        nAudioPortConfig->config_mask = configMask;
+    }
     env->DeleteLocalRef(jAudioPort);
     env->DeleteLocalRef(jHandle);
     return (jint)AUDIO_JAVA_SUCCESS;
@@ -998,7 +1016,7 @@
             jStatus = (jint)AUDIO_JAVA_BAD_VALUE;
             goto exit;
         }
-        jStatus = convertAudioPortConfigToNative(env, &nPatch.sources[i], jSource);
+        jStatus = convertAudioPortConfigToNative(env, &nPatch.sources[i], jSource, false);
         env->DeleteLocalRef(jSource);
         jSource = NULL;
         if (jStatus != AUDIO_JAVA_SUCCESS) {
@@ -1013,7 +1031,7 @@
             jStatus = (jint)AUDIO_JAVA_BAD_VALUE;
             goto exit;
         }
-        jStatus = convertAudioPortConfigToNative(env, &nPatch.sinks[i], jSink);
+        jStatus = convertAudioPortConfigToNative(env, &nPatch.sinks[i], jSink, false);
         env->DeleteLocalRef(jSink);
         jSink = NULL;
         if (jStatus != AUDIO_JAVA_SUCCESS) {
@@ -1268,7 +1286,7 @@
         return AUDIO_JAVA_BAD_VALUE;
     }
     struct audio_port_config nAudioPortConfig;
-    jint jStatus = convertAudioPortConfigToNative(env, &nAudioPortConfig, jAudioPortConfig);
+    jint jStatus = convertAudioPortConfigToNative(env, &nAudioPortConfig, jAudioPortConfig, true);
     if (jStatus != AUDIO_JAVA_SUCCESS) {
         return jStatus;
     }
diff --git a/core/res/res/drawable-hdpi/fastscroll_thumb_mtrl_alpha.png b/core/res/res/drawable-hdpi/fastscroll_thumb_mtrl_alpha.png
deleted file mode 100644
index 2000422d..0000000
--- a/core/res/res/drawable-hdpi/fastscroll_thumb_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/fastscroll_track_mtrl_alpha.9.png b/core/res/res/drawable-hdpi/fastscroll_track_mtrl_alpha.9.png
deleted file mode 100644
index 61ef6f6..0000000
--- a/core/res/res/drawable-hdpi/fastscroll_track_mtrl_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrollbar_handle_mtrl_alpha.9.png b/core/res/res/drawable-hdpi/scrollbar_handle_mtrl_alpha.9.png
deleted file mode 100644
index a5166f2..0000000
--- a/core/res/res/drawable-hdpi/scrollbar_handle_mtrl_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/switch_track_mtrl_alpha.9.png b/core/res/res/drawable-hdpi/switch_track_mtrl_alpha.9.png
index 0ebe65e..9415bc0 100644
--- a/core/res/res/drawable-hdpi/switch_track_mtrl_alpha.9.png
+++ b/core/res/res/drawable-hdpi/switch_track_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldpi/fastscroll_thumb_mtrl_alpha.png b/core/res/res/drawable-ldpi/fastscroll_thumb_mtrl_alpha.png
deleted file mode 100644
index c901730..0000000
--- a/core/res/res/drawable-ldpi/fastscroll_thumb_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-ldpi/fastscroll_track_mtrl_alpha.9.png b/core/res/res/drawable-ldpi/fastscroll_track_mtrl_alpha.9.png
deleted file mode 100644
index 833dac9..0000000
--- a/core/res/res/drawable-ldpi/fastscroll_track_mtrl_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-ldpi/scrollbar_handle_mtrl_alpha.9.png b/core/res/res/drawable-ldpi/scrollbar_handle_mtrl_alpha.9.png
deleted file mode 100644
index 3e301ef..0000000
--- a/core/res/res/drawable-ldpi/scrollbar_handle_mtrl_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-ldpi/switch_track_mtrl_alpha.9.png b/core/res/res/drawable-ldpi/switch_track_mtrl_alpha.9.png
deleted file mode 100644
index a58128f..0000000
--- a/core/res/res/drawable-ldpi/switch_track_mtrl_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/fastscroll_thumb_mtrl_alpha.png b/core/res/res/drawable-mdpi/fastscroll_thumb_mtrl_alpha.png
deleted file mode 100644
index 4984f9c..0000000
--- a/core/res/res/drawable-mdpi/fastscroll_thumb_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/fastscroll_track_mtrl_alpha.9.png b/core/res/res/drawable-mdpi/fastscroll_track_mtrl_alpha.9.png
deleted file mode 100644
index 4041808..0000000
--- a/core/res/res/drawable-mdpi/fastscroll_track_mtrl_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrollbar_handle_mtrl_alpha.9.png b/core/res/res/drawable-mdpi/scrollbar_handle_mtrl_alpha.9.png
deleted file mode 100644
index 1834b2e..0000000
--- a/core/res/res/drawable-mdpi/scrollbar_handle_mtrl_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/fastscroll_thumb_mtrl_alpha.png b/core/res/res/drawable-xhdpi/fastscroll_thumb_mtrl_alpha.png
deleted file mode 100644
index 9534f78..0000000
--- a/core/res/res/drawable-xhdpi/fastscroll_thumb_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/fastscroll_track_mtrl_alpha.9.png b/core/res/res/drawable-xhdpi/fastscroll_track_mtrl_alpha.9.png
deleted file mode 100644
index 02f01748..0000000
--- a/core/res/res/drawable-xhdpi/fastscroll_track_mtrl_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/scrollbar_handle_mtrl_alpha.9.png b/core/res/res/drawable-xhdpi/scrollbar_handle_mtrl_alpha.9.png
deleted file mode 100644
index 3c816c7..0000000
--- a/core/res/res/drawable-xhdpi/scrollbar_handle_mtrl_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/fastscroll_thumb_mtrl_alpha.png b/core/res/res/drawable-xxhdpi/fastscroll_thumb_mtrl_alpha.png
deleted file mode 100644
index 55a73e7..0000000
--- a/core/res/res/drawable-xxhdpi/fastscroll_thumb_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/fastscroll_track_mtrl_alpha.9.png b/core/res/res/drawable-xxhdpi/fastscroll_track_mtrl_alpha.9.png
deleted file mode 100644
index be64a94..0000000
--- a/core/res/res/drawable-xxhdpi/fastscroll_track_mtrl_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/scrollbar_handle_mtrl_alpha.9.png b/core/res/res/drawable-xxhdpi/scrollbar_handle_mtrl_alpha.9.png
deleted file mode 100644
index c1c0622..0000000
--- a/core/res/res/drawable-xxhdpi/scrollbar_handle_mtrl_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/edit_text_material.xml b/core/res/res/drawable/edit_text_material.xml
index bbc7301..38ac567 100644
--- a/core/res/res/drawable/edit_text_material.xml
+++ b/core/res/res/drawable/edit_text_material.xml
@@ -15,21 +15,23 @@
 -->
 
 <inset xmlns:android="http://schemas.android.com/apk/res/android"
-        android:inset="@dimen/control_inset_material">
-    <ripple android:color="?attr/colorControlActivated">
-        <item>
-            <selector>
-                <item android:state_enabled="false">
-                    <nine-patch android:src="@drawable/textfield_default_mtrl_alpha"
-                        android:tint="?attr/colorControlNormal"
-                        android:alpha="?attr/disabledAlpha" />
-                </item>
-                <item>
-                    <nine-patch android:src="@drawable/textfield_default_mtrl_alpha"
-                        android:tint="?attr/colorControlNormal" />
-                </item>
-            </selector>
+       android:insetLeft="@dimen/edit_text_inset_horizontal_material"
+       android:insetRight="@dimen/edit_text_inset_horizontal_material"
+       android:insetTop="@dimen/edit_text_inset_top_material"
+       android:insetBottom="@dimen/edit_text_inset_bottom_material">
+    <selector>
+        <item android:state_enabled="false">
+            <nine-patch android:src="@drawable/textfield_default_mtrl_alpha"
+                android:tint="?attr/colorControlNormal"
+                android:alpha="?attr/disabledAlpha" />
         </item>
-        <item android:id="@+id/mask" android:drawable="@drawable/textfield_activated_mtrl_alpha" />
-    </ripple>
+        <item android:state_pressed="false" android:state_focused="false">
+            <nine-patch android:src="@drawable/textfield_default_mtrl_alpha"
+                android:tint="?attr/colorControlNormal" />
+        </item>
+        <item>
+            <nine-patch android:src="@drawable/textfield_activated_mtrl_alpha"
+                android:tint="?attr/colorControlActivated" />
+        </item>
+    </selector>
 </inset>
diff --git a/core/res/res/drawable/fastscroll_thumb_material.xml b/core/res/res/drawable/fastscroll_thumb_material.xml
index 1288f0d..4e8ccc7 100644
--- a/core/res/res/drawable/fastscroll_thumb_material.xml
+++ b/core/res/res/drawable/fastscroll_thumb_material.xml
@@ -16,11 +16,17 @@
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:state_pressed="true">
-        <bitmap android:src="@drawable/fastscroll_thumb_mtrl_alpha"
-            android:tint="?attr/colorControlActivated" />
+        <shape android:tint="?attr/colorControlActivated"
+               android:shape="rectangle">
+            <solid android:color="@color/white" />
+            <size android:width="8dp" android:height="48dp" />
+        </shape>
     </item>
     <item>
-        <bitmap android:src="@drawable/fastscroll_thumb_mtrl_alpha"
-            android:tint="?attr/colorControlNormal" />
+        <shape android:tint="?attr/colorControlNormal"
+               android:shape="rectangle">
+            <solid android:color="@color/white" />
+            <size android:width="8dp" android:height="48dp" />
+        </shape>
     </item>
 </selector>
diff --git a/core/res/res/drawable/fastscroll_track_material.xml b/core/res/res/drawable/fastscroll_track_material.xml
index 60f79b1..7882b054 100644
--- a/core/res/res/drawable/fastscroll_track_material.xml
+++ b/core/res/res/drawable/fastscroll_track_material.xml
@@ -14,6 +14,9 @@
      limitations under the License.
 -->
 
-<nine-patch xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@drawable/fastscroll_track_mtrl_alpha"
-    android:tint="?attr/colorControlNormal" />
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       android:tint="?attr/colorControlNormal"
+       android:shape="rectangle">
+    <solid android:color="#21ffffff" />
+    <size android:width="8dp" />
+</shape>
diff --git a/core/res/res/drawable/scrollbar_handle_material.xml b/core/res/res/drawable/scrollbar_handle_material.xml
index a241428..1b3bb9b 100644
--- a/core/res/res/drawable/scrollbar_handle_material.xml
+++ b/core/res/res/drawable/scrollbar_handle_material.xml
@@ -14,6 +14,9 @@
      limitations under the License.
 -->
 
-<nine-patch xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@drawable/scrollbar_handle_mtrl_alpha"
-    android:tint="?attr/colorControlNormal" />
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       android:tint="?attr/colorControlNormal"
+       android:shape="rectangle">
+    <solid android:color="#42ffffff" />
+    <size android:width="4dp" />
+</shape>
diff --git a/core/res/res/layout/notification_template_material_base.xml b/core/res/res/layout/notification_template_material_base.xml
index 674d7b8..3fdcaf7 100644
--- a/core/res/res/layout/notification_template_material_base.xml
+++ b/core/res/res/layout/notification_template_material_base.xml
@@ -28,6 +28,7 @@
         android:layout_height="@dimen/notification_large_icon_height"
         />
     <LinearLayout
+        android:id="@+id/notification_main_column"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:layout_gravity="top"
diff --git a/core/res/res/layout/notification_template_material_big_base.xml b/core/res/res/layout/notification_template_material_big_base.xml
index ef916ed1..935424a 100644
--- a/core/res/res/layout/notification_template_material_big_base.xml
+++ b/core/res/res/layout/notification_template_material_big_base.xml
@@ -28,6 +28,7 @@
         android:layout_height="@dimen/notification_large_icon_height"
         />
     <LinearLayout
+        android:id="@+id/notification_main_column"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_gravity="top"
diff --git a/core/res/res/layout/notification_template_material_big_text.xml b/core/res/res/layout/notification_template_material_big_text.xml
index 3415814..d0c10b2a 100644
--- a/core/res/res/layout/notification_template_material_big_text.xml
+++ b/core/res/res/layout/notification_template_material_big_text.xml
@@ -28,6 +28,7 @@
         android:layout_height="@dimen/notification_large_icon_height"
         />
     <LinearLayout
+        android:id="@+id/notification_main_column"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_gravity="top"
diff --git a/core/res/res/layout/notification_template_material_inbox.xml b/core/res/res/layout/notification_template_material_inbox.xml
index 8a66c3f..ac448ee 100644
--- a/core/res/res/layout/notification_template_material_inbox.xml
+++ b/core/res/res/layout/notification_template_material_inbox.xml
@@ -28,6 +28,7 @@
         android:layout_height="@dimen/notification_large_icon_height"
         />
     <LinearLayout
+        android:id="@+id/notification_main_column"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_gravity="top"
diff --git a/core/res/res/layout/select_dialog_material.xml b/core/res/res/layout/select_dialog_material.xml
index ee04039..19ad407 100644
--- a/core/res/res/layout/select_dialog_material.xml
+++ b/core/res/res/layout/select_dialog_material.xml
@@ -22,11 +22,14 @@
 -->
 <view class="com.android.internal.app.AlertController$RecycleListView"
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+android:id/select_dialog_listview"
+    android:id="@id/select_dialog_listview"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:cacheColorHint="@null"
-    android:divider="?android:attr/listDividerAlertDialog"
+    android:divider="?attr/listDividerAlertDialog"
     android:scrollbars="vertical"
     android:overScrollMode="ifContentScrolls"
-    android:textAlignment="viewStart" />
+    android:textAlignment="viewStart"
+    android:paddingTop="@dimen/dialog_list_padding_vertical_material"
+    android:paddingBottom="@dimen/dialog_list_padding_vertical_material"
+    android:clipToPadding="false" />
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index b1af4aa..2fd7d30 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -4662,6 +4662,11 @@
         <attr name="weekDayTextAppearance" format="reference" />
         <!-- The text appearance for the calendar dates. -->
         <attr name="dateTextAppearance" format="reference" />
+        <!-- The number of weeks to be shown. -->
+        <attr name="calendarViewMode">
+            <enum name="holo" value="0" />
+            <enum name="material" value="1" />
+        </attr>
     </declare-styleable>
 
     <declare-styleable name="NumberPicker">
@@ -5660,6 +5665,10 @@
             <enum name="right" value="0x05" />
             <!-- Slide to and from the bottom edge of the Scene. -->
             <enum name="bottom" value="0x50" />
+            <!-- Slide to and from the x-axis position at the start of the Scene root. -->
+            <enum name="start" value="0x00800003"/>
+            <!-- Slide to and from the x-axis position at the end of the Scene root. -->
+            <enum name="end" value="0x00800005"/>
         </attr>
     </declare-styleable>
 
@@ -7215,6 +7224,8 @@
         <attr name="goIcon" format="reference" />
         <!-- Search icon -->
         <attr name="searchIcon" format="reference" />
+        <!-- Search icon displayed as a text field hint -->
+        <attr name="searchHintIcon" format="reference" />
         <!-- Voice button icon -->
         <attr name="voiceIcon" format="reference" />
         <!-- Commit icon shown in the query suggestion row -->
diff --git a/core/res/res/values/colors_material.xml b/core/res/res/values/colors_material.xml
index 46ec838..a8fd8d4 100644
--- a/core/res/res/values/colors_material.xml
+++ b/core/res/res/values/colors_material.xml
@@ -22,7 +22,7 @@
     <color name="background_floating_material_light">#ffeeeeee</color>
 
     <color name="primary_material_dark">#ff212121</color>
-    <color name="primary_material_light">#ffe0e0e0</color>
+    <color name="primary_material_light">#ffefefef</color>
     <color name="primary_dark_material_dark">#ff000000</color>
     <color name="primary_dark_material_light">#ff757575</color>
 
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index cbdb256..6e635f31 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1942,4 +1942,8 @@
     <string-array translatable="false" name="config_sms_convert_destination_number_support">
         <item>false</item>
     </string-array>
+
+    <!-- The maximum bitmap size that can be written to a MediaMetadata object. This value
+         is the max width/height allowed in dips.-->
+    <dimen name="config_mediaMetadataBitmapMaxSize">320dp</dimen>
 </resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index b4e033c..3f840ab 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -397,6 +397,9 @@
     <dimen name="datepicker_header_height">30dp</dimen>
     <dimen name="datepicker_header_text_size">14dp</dimen>
 
+    <!-- Minimum size of the fast scroller thumb's touch target. -->
+    <dimen name="fast_scroller_minimum_touch_target">48dp</dimen>
+
     <!-- width of ImmersiveModeConfirmation (-1 for match_parent) -->
     <dimen name="immersive_mode_cling_width">-1px</dimen>
 
diff --git a/core/res/res/values/dimens_material.xml b/core/res/res/values/dimens_material.xml
index 4a2119b..54f483d 100644
--- a/core/res/res/values/dimens_material.xml
+++ b/core/res/res/values/dimens_material.xml
@@ -91,6 +91,13 @@
     <!-- Default rounded corner for controls -->
     <dimen name="control_corner_material">2dp</dimen>
 
+    <dimen name="edit_text_inset_horizontal_material">4dp</dimen>
+    <dimen name="edit_text_inset_top_material">4dp</dimen>
+    <dimen name="edit_text_inset_bottom_material">8dp</dimen>
+
     <dimen name="dialog_padding_material">24dp</dimen>
     <dimen name="dialog_padding_top_material">18dp</dimen>
+
+    <!-- Padding above and below selection dialog lists. -->
+    <dimen name="dialog_list_padding_vertical_material">8dp</dimen>
 </resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index c661c33..1123813 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2600,5 +2600,9 @@
     <public type="attr" name="accessibilityTraversalBefore" />
     <public type="attr" name="accessibilityTraversalAfter" />
     <public type="attr" name="dialogPreferredPadding" />
+    <public type="attr" name="searchHintIcon" />
+
+    <public type="style" name="Theme.DeviceDefault.Dialog.Alert" />
+    <public type="style" name="Theme.DeviceDefault.Light.Dialog.Alert" />
 
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 522c85c..7bc24c3 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -212,6 +212,12 @@
     <!-- Displayed to tell the user that all service is blocked by access control. -->
     <string name="RestrictedOnAll">All voice/data/SMS services are blocked.</string>
 
+    <!-- Displayed to tell the user that peer changed TTY mode -->
+    <string name="peerTtyModeFull">Peer requested TTY Mode FULL</string>
+    <string name="peerTtyModeHco">Peer requested TTY Mode HCO</string>
+    <string name="peerTtyModeVco">Peer requested TTY Mode VCO</string>
+    <string name="peerTtyModeOff">Peer requested TTY Mode OFF</string>
+
     <!-- Mappings between TS 27.007 +CFCC/+CLCK "service classes" and human-readable strings--> <skip />
     <!-- Example: Service was enabled for: Voice, Data -->
     <string name="serviceClassVoice">Voice</string>
@@ -3306,6 +3312,8 @@
     <!-- This is the default button label in the system-wide search UI.
          It is also used by the home screen's search "widget". It should be short -->
     <string name="search_go">Search</string>
+    <!-- Default hint text for the system-wide search UI's text field. [CHAR LIMIT=30] -->
+    <string name="search_hint">Search…</string>
     <!-- SearchView accessibility description for search button [CHAR LIMIT=NONE] -->
     <string name="searchview_description_search">Search</string>
     <!-- SearchView accessibility description for search text field [CHAR LIMIT=NONE] -->
@@ -5077,7 +5085,7 @@
     <string name="lock_to_app_unlock_password">Ask for password before unpinning</string>
 
     <!-- [CHAR_LIMIT=NONE] Battery saver: Feature description -->
-    <string name="battery_saver_description">To help improve battery life, battery saver reduces your device’s performance and limits vibration and most background data. Email, messaging, and other apps that rely on syncing may not update unless you open them.\n\nBattery saver turns off automatically when your device is charging.</string>
+    <string name="battery_saver_description">To help improve battery life, battery saver reduces your device’s performance and limits vibration, location services, and most background data. Email, messaging, and other apps that rely on syncing may not update unless you open them.\n\nBattery saver turns off automatically when your device is charging.</string>
 
     <!-- [CHAR_LIMIT=NONE] Zen mode: Condition summary for built-in downtime condition, if active -->
     <string name="downtime_condition_summary">Until your downtime ends at <xliff:g id="formattedTime" example="10:00 PM">%1$s</xliff:g></string>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 5a59afe..c520a46 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -565,6 +565,7 @@
         <item name="selectedDateVerticalBar">@drawable/day_picker_week_view_dayline_holo</item>
         <item name="weekDayTextAppearance">@style/TextAppearance.Small.CalendarViewWeekDayView</item>
         <item name="dateTextAppearance">?attr/textAppearanceSmall</item>
+        <item name="calendarViewMode">holo</item>
     </style>
 
     <style name="Widget.NumberPicker">
diff --git a/core/res/res/values/styles_holo.xml b/core/res/res/values/styles_holo.xml
index c7d2db1..41b8b25 100644
--- a/core/res/res/values/styles_holo.xml
+++ b/core/res/res/values/styles_holo.xml
@@ -358,6 +358,7 @@
         <item name="submitBackground">@drawable/textfield_searchview_right_holo_dark</item>
         <item name="closeIcon">@drawable/ic_clear_holo_dark</item>
         <item name="searchIcon">@drawable/ic_search_api_holo_dark</item>
+        <item name="searchHintIcon">@drawable/ic_search_api_holo_dark</item>
         <item name="goIcon">@drawable/ic_go_search_api_holo_dark</item>
         <item name="voiceIcon">@drawable/ic_voice_search_api_holo_dark</item>
         <item name="commitIcon">@drawable/ic_commit_search_api_holo_dark</item>
@@ -444,6 +445,7 @@
         <item name="weekSeparatorLineColor">#19FFFFFF</item>
         <item name="selectedDateVerticalBar">@drawable/day_picker_week_view_dayline_holo</item>
         <item name="weekDayTextAppearance">@style/TextAppearance.Holo.CalendarViewWeekDayView</item>
+        <item name="calendarViewMode">holo</item>
     </style>
 
     <style name="Widget.Holo.ImageButton" parent="Widget.ImageButton">
@@ -881,6 +883,7 @@
         <item name="weekNumberColor">#7F080021</item>
         <item name="weekSeparatorLineColor">#7F08002A</item>
         <item name="weekDayTextAppearance">@style/TextAppearance.Holo.Light.CalendarViewWeekDayView</item>
+        <item name="calendarViewMode">holo</item>
     </style>
 
     <style name="Widget.Holo.Light.NumberPicker" parent="Widget.Holo.NumberPicker" />
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index e04d901..c7e9ec9 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -517,12 +517,20 @@
         <item name="submitBackground">@drawable/textfield_search_material</item>
         <item name="closeIcon">@drawable/ic_clear_material</item>
         <item name="searchIcon">@drawable/ic_search_api_material</item>
+        <item name="searchHintIcon">@drawable/ic_search_api_material</item>
         <item name="goIcon">@drawable/ic_go_search_api_material</item>
         <item name="voiceIcon">@drawable/ic_voice_search_api_material</item>
         <item name="commitIcon">@drawable/ic_commit_search_api_material</item>
         <item name="suggestionRowLayout">@layout/search_dropdown_item_icons_2line</item>
     </style>
 
+    <style name="Widget.Material.SearchView.ActionBar">
+        <item name="queryBackground">@empty</item>
+        <item name="submitBackground">@empty</item>
+        <item name="searchHintIcon">@empty</item>
+        <item name="queryHint">@string/search_hint</item>
+    </style>
+
     <style name="Widget.Material.SegmentedButton" parent="SegmentedButton">
         <item name="background">@drawable/btn_group_holo_dark</item>
     </style>
@@ -612,6 +620,7 @@
         <item name="weekSeparatorLineColor">#19FFFFFF</item>
         <item name="selectedDateVerticalBar">@drawable/day_picker_week_view_dayline_holo</item>
         <item name="weekDayTextAppearance">@style/TextAppearance.Material.CalendarViewWeekDayView</item>
+        <item name="calendarViewMode">material</item>
     </style>
 
     <style name="Widget.Material.ImageButton" parent="Widget.ImageButton">
@@ -865,8 +874,8 @@
         <item name="contentDescription">@string/action_menu_overflow_description</item>
         <item name="minWidth">@dimen/action_button_min_width_overflow_material</item>
         <item name="minHeight">@dimen/action_button_min_height_material</item>
-        <item name="paddingStart">0dp</item>
-        <item name="paddingEnd">12dp</item>
+        <item name="paddingStart">6dp</item>
+        <item name="paddingEnd">10dp</item>
     </style>
 
     <style name="Widget.Material.ActionBar.TabView" parent="Widget.ActionBar.TabView">
@@ -961,6 +970,7 @@
     <style name="Widget.Material.Light.ButtonBar" parent="Widget.Material.ButtonBar"/>
     <style name="Widget.Material.Light.ButtonBar.AlertDialog" parent="Widget.Material.ButtonBar.AlertDialog"/>
     <style name="Widget.Material.Light.SearchView" parent="Widget.Material.SearchView"/>
+    <style name="Widget.Material.Light.SearchView.ActionBar" parent="Widget.Material.SearchView.ActionBar"/>
 
     <style name="Widget.Material.Light.SegmentedButton" parent="Widget.Material.SegmentedButton">
         <item name="background">@drawable/btn_group_holo_light</item>
@@ -999,6 +1009,7 @@
         <item name="weekNumberColor">#7F080021</item>
         <item name="weekSeparatorLineColor">#7F08002A</item>
         <item name="weekDayTextAppearance">@style/TextAppearance.Material.CalendarViewWeekDayView</item>
+        <item name="calendarViewMode">material</item>
     </style>
 
     <style name="Widget.Material.Light.NumberPicker" parent="Widget.Material.NumberPicker"/>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index b6cf81e..a91119f 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -202,6 +202,7 @@
   <java-symbol type="id" name="status_bar_latest_event_content" />
   <java-symbol type="id" name="action_divider" />
   <java-symbol type="id" name="overflow_divider" />
+  <java-symbol type="id" name="notification_main_column" />
   <java-symbol type="id" name="sms_short_code_confirm_message" />
   <java-symbol type="id" name="sms_short_code_detail_layout" />
   <java-symbol type="id" name="sms_short_code_detail_message" />
@@ -715,6 +716,10 @@
   <java-symbol type="string" name="perms_description_app" />
   <java-symbol type="string" name="perms_new_perm_prefix" />
   <java-symbol type="string" name="petabyteShort" />
+  <java-symbol type="string" name="peerTtyModeFull" />
+  <java-symbol type="string" name="peerTtyModeHco" />
+  <java-symbol type="string" name="peerTtyModeVco" />
+  <java-symbol type="string" name="peerTtyModeOff" />
   <java-symbol type="string" name="phoneTypeAssistant" />
   <java-symbol type="string" name="phoneTypeCallback" />
   <java-symbol type="string" name="phoneTypeCar" />
@@ -1805,6 +1810,8 @@
   <java-symbol type="color" name="notification_progress_background_color" />
   <java-symbol type="id" name="media_actions" />
 
+  <java-symbol type="dimen" name="config_mediaMetadataBitmapMaxSize" />
+
     <!-- From SystemUI -->
   <java-symbol type="anim" name="push_down_in" />
   <java-symbol type="anim" name="push_down_out" />
@@ -2119,5 +2126,6 @@
   <java-symbol type="string" name="android_system_label" />
   <java-symbol type="string" name="system_error_wipe_data" />
   <java-symbol type="string" name="system_error_manufacturer" />
+  <java-symbol type="dimen" name="fast_scroller_minimum_touch_target" />
 
 </resources>
diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml
index b320ae5..3f2062dc 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -828,6 +828,7 @@
          secondary text color, with the primary text color. -->
     <style name="ThemeOverlay.Material.ActionBar">
         <item name="colorControlNormal">?attr/textColorPrimary</item>
+        <item name="searchViewStyle">@style/Widget.Material.Light.SearchView.ActionBar</item>
     </style>
 
     <!-- Theme overlay that replaces colors with their dark versions and replaces the normal
@@ -835,6 +836,7 @@
          text color. -->
     <style name="ThemeOverlay.Material.Dark.ActionBar">
         <item name="colorControlNormal">?attr/textColorPrimary</item>
+        <item name="searchViewStyle">@style/Widget.Material.SearchView.ActionBar</item>
     </style>
 
     <!-- Variant of the material (dark) theme with no action bar. -->
diff --git a/core/tests/ConnectivityManagerTest/AndroidManifest.xml b/core/tests/ConnectivityManagerTest/AndroidManifest.xml
index 1649268..6bd8f6e 100644
--- a/core/tests/ConnectivityManagerTest/AndroidManifest.xml
+++ b/core/tests/ConnectivityManagerTest/AndroidManifest.xml
@@ -68,8 +68,9 @@
 
     <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
-    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
     <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
     <uses-permission android:name="android.permission.WRITE_SETTINGS" />
     <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
     <!-- This permission is added for API call setAirplaneMode() in ConnectivityManager -->
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiStressTest.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiStressTest.java
index f3d5c87..e04c214 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiStressTest.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiStressTest.java
@@ -199,7 +199,7 @@
                 Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0);
         // set wifi sleep policy to never on while in sleep
         Settings.Global.putInt(mRunner.getContext().getContentResolver(),
-                Settings.Global.WIFI_SLEEP_POLICY, Settings.Global.WIFI_SLEEP_POLICY_NEVER);
+                Settings.Global.WIFI_SLEEP_POLICY, Settings.Global.WIFI_SLEEP_POLICY_DEFAULT);
         // set idle timeout for wifi to 15s
         Settings.Global.putLong(mRunner.getContext().getContentResolver(),
                 Settings.Global.WIFI_IDLE_MS, WIFI_IDLE_MS);
diff --git a/docs/html-intl/intl/zh-cn/about/versions/android-5.0.jd b/docs/html-intl/intl/zh-cn/about/versions/android-5.0.jd
new file mode 100644
index 0000000..b4ce560
--- /dev/null
+++ b/docs/html-intl/intl/zh-cn/about/versions/android-5.0.jd
@@ -0,0 +1,633 @@
+page.title=Android 5.0 API
+excludeFromSuggestions=true
+sdk.platform.version=5.0
+sdk.platform.apiLevel=21
+@jd:body
+
+
+<div id="qv-wrapper">
+<div id="qv">
+
+<h2>このドキュメントの内容 <a href="#" onclick="hideNestedItems('#toc44',this);return false;" class="header-toggle"> <span class="more">詳細を表示</span> <span class="less" style="display:none">詳細を隠す</span></a></h2>
+
+<ol id="toc44" class="hide-nested">
+  <li><a href="#ApiLevel">対象 API レベルの更新</a></li>
+  <li><a href="#Behaviors">動作に関する重要な変更</a>
+    <ol>
+      <li><a href="#ART">まだ新しい Android Runtime(ART)に対してアプリをテストしていない場合</a></li>
+      <li><a href="#BehaviorNotifications">アプリで通知を実装する場合</a></li>
+      <li><a href="#BehaviorMediaControl">アプリで RemoteControlClient を使用する場合</a></li>
+<li><a href="#BehaviorGetRecentTasks">アプリで getRecentTasks() を使用する場合</a></li>
+<li><a href="#64BitSupport">Android Native Development Kit(NDK)を使用している場合</a></li>
+<li><a href="#BindService">アプリからサービスにバインドする場合</a></li>
+<li><a href="#BehaviorWebView">アプリで WebView を使用する場合</a></li>
+    </ol>
+  </li>
+  <li><a href="#UI">ユーザー インターフェース</a>
+    <ol>
+      <li><a href="#MaterialDesign">Material Design のサポート</a></li>
+      <li><a href="#Recents">最近使ったアプリ画面に表示される同時実行中のドキュメント / アクティビティ</a></li>
+      <li><a href="#WebView">WebView の更新</a></li>
+      <li><a href="#ScreenCapture">画面のキャプチャと共有</a></li>
+    </ol>
+  </li>
+  <li><a href="#Notifications">通知</a>
+    <ol>
+      <li><a href="#LockscreenNotifications">ロック画面の通知</a></li>
+      <li><a href="#NotificationsMetadata">通知メタデータ</a></li>
+    </ol>
+  </li>
+  <li><a href="#Graphics">グラフィック</a>
+    <ol>
+      <li><a href="#OpenGLES-3-1">OpenGL ES 3.1 のサポート</a></li>
+      <li><a href="#AndroidExtensionPack">Android Extension Pack</a></li>
+    </ol>
+  </li>
+  <li><a href="#Media">メディア</a>
+    <ol>
+      <li><a href="#Camera-v2">高度なカメラ機能に対応した Camera API</a></li>
+      <li><a href="#AudioPlayback">音声の再生</a></li>
+      <li><a href="#MediaPlaybackControl">メディア再生コントロール</a></li>
+      <li><a href="#MediaBrowsing">メディアの参照</a></li>
+    </ol>
+  </li>
+  <li><a href="#Storage">ストレージ</a>
+    <ol>
+      <li><a href="#DirectorySelection">ディレクトリの選択</a></li>
+    </ol>
+  </li>
+  <li><a href="#Wireless">ワイヤレスと接続</a>
+    <ol>
+      <li><a href="#Multinetwork">マルチネットワーク接続</a></li>
+      <li><a href="#BluetoothBroadcasting">Bluetooth によるブロードキャスト</a></li>
+      <li><a href="#NFCEnhancements">NFC の機能強化</a></li>
+    </ol>
+  </li>
+  <li><a href="#Power">Project Volta</a>
+    <ol>
+      <li><a href="#JobScheduler">ジョブのスケジューリング</a></li>
+      <li><a href="#PowerMeasurementTools">電池の使用統計情報を提供するデベロッパー向けツール</a>
+    </ol>
+  </li>
+  <li><a href="#Enterprise">職場向けや教育向けの Android</a>
+    <ol>
+      <li><a href="#ManagedProvisioning">管理対象プロビジョニング</a></li>
+      <li><a href="#DeviceOwner">端末所有者</a></li>
+      <li><a href="#ScreenPinning">画面の固定</a></li>
+    </ol>
+  </li>
+  <li><a href="#System">システム</a>
+    <ol>
+      <li><a href="#AppUsageStatistics">アプリの使用統計情報</a></li>
+    </ol>
+  </li>
+  <li><a href="#Printing">印刷フレームワーク</a>
+    <ol>
+      <li><a href="#PDFRender">PDF をビットマップとしてレンダリング</a></li>
+    </ol>
+  </li>
+  <li><a href="#TestingA11y">テストとユーザー補助</a>
+    <ol>
+      <li><a href="#TestingA11yImprovements">テストとユーザー補助の向上</a></li>
+    </ol>
+  </li>
+  <li><a href="#IME">IME</a>
+    <ol>
+      <li><a href="#Switching">入力言語の切り替えやすさの向上</a></li>
+    </ol>
+  </li>
+  <li><a href="#Manifest">マニフェスト宣言</a>
+    <ol>
+      <li><a href="#ManifestFeatures">宣言可能な必須機能</a></li>
+      <li><a href="#Permissions">ユーザー権限</a></li>
+    </ol>
+  </li>
+</ol>
+
+<h2>API の変更内容</h2>
+<ol>
+<li><a href="{@docRoot}sdk/api_diff/21/changes.html">API レベル: 20 から 21 へ »</a> </li>
+<li><a href="{@docRoot}sdk/api_diff/preview-21/changes.html">レベル: デベロッパー プレビューから 21 へ »</a> </li>
+</ol>
+
+</div>
+</div>
+
+<p>API レベル: {@sdkPlatformApiLevel}</p>
+
+<p>Android 5.0(<a href="{@docRoot}reference/android/os/Build.VERSION_CODES.html#LOLLIPOP">Lollipop</a>)は、ユーザーとアプリ デベロッパーに新しい機能を提供します。このドキュメントでは、最も重要な新しい API を紹介しています。</p>
+
+<p>新しいプラットフォーム機能の概要について詳しくは、<a href="{@docRoot}about/versions/lollipop.html">Android Lollipop の特長</a>をご覧ください。</p>
+
+
+<h3 id="Start">開発の開始</h3>
+
+<p>Android 5.0 対応アプリの開発を始めるには、最初に <a href="{@docRoot}sdk/index.html">Android SDK を入手する</a>必要があります。次に、<a href="{@docRoot}tools/help/sdk-manager.html">SDK Manager</a> を使用して Android 5.0 SDK プラットフォームとシステム イメージをダウンロードします。</p>
+
+<p style=" padding: 10px; background: #eee; width: 445px; border: 1px solid #ccc; margin-top: 20px;">アプリを実機でテストするには、<br><a href="/preview/index.html#Start"><b>Android プレビュー システム イメージ</b></a>を使用して Nexus 5 または Nexus 7 のフラッシュに書き込みます。</p>
+
+
+
+<h3 id="ApiLevel">対象 API レベルの更新</h3>
+
+<p>Android {@sdkPlatformVersion} 搭載端末向けにアプリの最適化を向上させるには、<a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code targetSdkVersion}</a> を <code>"{@sdkPlatformApiLevel}"</code> に設定し、Android {@sdkPlatformVersion} システム イメージにアプリをインストールした後、この変更を加えた更新済みのアプリを公開します。</p>
+
+<p>Android {@sdkPlatformVersion} API を使用しながら旧バージョンも同時にサポートするには、<a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code minSdkVersion}</a> でサポートされていない API を実行する前に、システムの API レベルをチェックする条件をコードに追加します。下位互換性の維持について詳しくは、<a href="{@docRoot}training/basics/supporting-devices/platforms.html">複数のプラットフォーム バージョンへの対応</a>をご覧ください。</p>
+
+<p>API レベルの仕組みについて詳しくは、<a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#ApiLevels">API レベルとは</a>をご覧ください。</p>
+
+<h2 id="Behaviors">動作に関する重要な変更</h2>
+
+<p>以前に Android 対応アプリを公開したことがある場合は、アプリが Android 5.0 の変更による影響を受ける可能性があることに注意してください。</p>
+
+<h3 id="ART">まだ新しい Android Runtime(ART)に対してアプリをテストしていない場合</h3>
+
+<p>リリース 4.4 では新しい Android ランタイムである ART が試験的に導入されていました。4.4 では、ART はオプションで、デフォルトのランタイムは Dalvik のままでした。Android 5.0 では、ART がデフォルトのランタイムになりました。</p>
+
+<p>ART の新機能の概要について詳しくは、<a href="https://source.android.com/devices/tech/dalvik/art.html">ART の紹介</a>をご覧ください。主に次のような新機能があります。</p>
+
+<ul>
+  <li>AOT(Ahead-of-time、事前)コンパイル</li>
+  <li>ガベージ コレクション(GC)の向上</li>
+  <li>デバッグ サポートの向上</li>
+</ul>
+
+<p>ほとんどの Android アプリは変更を加えなくても ART で問題なく動作します。ただし、Dalvik で動作する一部の技術が ART で動作しません。特に重要な問題について詳しくは、<a href="{@docRoot}guide/practices/verifying-apps-art.html">Android Runtime(ART)でアプリの動作を検証する</a>をご覧ください。特に次の点にご注意ください。</p>
+
+<ul>
+  <li>アプリで C/C++ のコードを実行する場合は Java Native Interface(JNI)を使用します。</li>
+  <li>非標準のコードを生成する開発ツール(難読化ツールなど)を使用します。</li>
+  <li>ガベージ コレクションのコンパクションと互換性のない技術を使用します(ART では現在 GC のコンパクションは実装されていませんが、Android オープンソース プロジェクトで GC のコンパクションの開発が進められています)。</li>
+</ul>
+
+<h3 id="BehaviorNotifications">アプリで通知を実装する場合</h3>
+
+<p>通知を実装する際は、Android 5.0 での変更点について必ず考慮してください。Android 5.0 以降に対応した通知の設計について詳しくは、<a href="{@docRoot}design/patterns/notifications.html">通知の設計に関するガイド</a>をご覧ください。
+</p>
+
+<h4 id="NotificationsMaterialDesignStyle">Material Design スタイル</h4>
+<p>通知は、新しい Material Design ウィジェットに合わせて、白色(または非常に明るい色)の背景の上に暗い色のテキストで描かれます。すべての通知が新しい配色で見やすくなるようにしてください。通知が見にくい場合は、次の方法で修正してください。</p>
+
+<ul>
+  <li>{@link android.app.Notification.Builder#setColor(int) setColor()} を使用してアクセントのある色を設定し、その色でアイコン イメージの背後に円を描きます。 </li>
+  <li>色を伴うアセットを更新または削除します。操作アイコンとメインの通知アイコンでは、非アルファ チャンネルはすべて無視されます。これらのアイコンはアルファのみとなることを前提としてください。通知アイコンは白で、操作アイコンは濃いグレーで、それぞれ描かれます。</li>
+</ul>
+
+<h4 id="NotificationsSoundVibration">音声とバイブレーション</h4>
+<p>現在 {@link android.media.Ringtone}、{@link android.media.MediaPlayer}、または {@link android.os.Vibrator} クラスを使用して音声やバイブレーションを通知に追加している場合は、システムが「優先」<em></em>モードで通知を正しく表示できるように、それらのコードを削除してください。代わりに {@link android.app.Notification.Builder} のメソッドを使用して音声やバイブレーションを追加してください。</p>
+
+<p>端末を {@link android.media.AudioManager#RINGER_MODE_SILENT RINGER_MODE_SILENT} に設定すると端末が新しい優先モードになります。端末を {@link android.media.AudioManager#RINGER_MODE_NORMAL RINGER_MODE_NORMAL} または {@link android.media.AudioManager#RINGER_MODE_NORMAL RINGER_MODE_VIBRATE} に設定すると端末の優先モードが終了します。</p>
+
+<p>従来、Android ではタブレット端末のボリュームをコントロールするために {@link android.media.AudioManager#STREAM_MUSIC STREAM_MUSIC} をマスター ストリームとして使用していました。Android 5.0 では、携帯電話とタブレット端末の両方のマスター ボリューム ストリームが統合され、{@link android.media.AudioManager#STREAM_RING STREAM_RING} または {@link android.media.AudioManager#STREAM_NOTIFICATION STREAM_NOTIFICATION} によってコントロールされるようになりました。</p>
+
+<h4 id="NotificationsLockscreenVisibility">ロック画面の表示</h4>
+<p>Android 5.0 ではデフォルトでユーザーのロック画面に通知が表示されるようになりました。ユーザーは機密情報の表示を防ぐことを選択できます。その場合は通知に表示されるテキストが自動的に編集されます。編集される通知をカスタマイズする場合は、{@link android.app.Notification.Builder#setPublicVersion(android.app.Notification) setPublicVersion()} を使用してください。</p>
+<p>通知に個人情報が含まれていない場合や、通知にメディア再生コントロールを表示できるようにしたい場合は、{@link android.app.Notification.Builder#setVisibility(int) setVisibility()} メソッドを呼び出して通知の表示レベルを {@link android.app.Notification#VISIBILITY_PUBLIC VISIBILITY_PUBLIC} に設定してください。
+</p>
+
+<h4 id="NotificationsMediaPlayback">メディアの再生</h4>
+<p>メディアの再生状態やトランスポート コントロールを表示する通知を実装する場合は、カスタムの {@link android.widget.RemoteViews.RemoteView} オブジェクトに代わって新しい {@link android.app.Notification.MediaStyle} テンプレートを使用することをおすすめします。どちらのアプローチの場合でも、必ず通知の表示を {@link android.app.Notification#VISIBILITY_PUBLIC VISIBILITY_PUBLIC} に設定して、ロック画面からコントロールにアクセスできるようにしてください。Android 5.0 以降では、ロック画面に {@link android.media.RemoteControlClient} オブジェクトが表示されなくなりました。詳しくは、<a href="#BehaviorMediaControl">アプリで RemoteControlClient を使用する場合</a>をご覧ください。</p>
+
+<h4 id="NotificationsHeadsup">警告通知</h4>
+<p>必要に応じて、端末がアクティブのとき(端末のロックが解除されていて画面が表示されているとき)に通知が小さいフローティング ウィンドウ(警告通知ともいいます)に表示されるようになりました。これらの通知の外観はデベロッパーによる簡易形式の通知に似ていますが、警告通知には操作ボタンも表示されます。ユーザーは、現在のアプリから離れることなく警告通知に応答したり、警告通知を拒否したりできます。</p>
+
+<p>たとえば次のような条件の場合に警告通知が起動されることがあります。</p>
+
+<ul>
+  <li>ユーザーのアクティビティが全画面表示モードになっている(アプリで {@link android.app.Notification#fullScreenIntent} を使用している)</li>
+  <li>通知の優先順位が高く、着信音やバイブレーションを使用している</li>
+</ul>
+
+<p>こうした条件の下でアプリで通知を実装する場合は、必ず警告通知が正しく表示されるようにしてください。</p>
+
+<h3 id="BehaviorMediaControl">アプリで RemoteControlClient を使用する場合</h3>
+<p>{@link android.media.RemoteControlClient} クラスは非推奨になりました。できるだけ速やかに新しい {@link android.media.session.MediaSession} API に切り替えてください。</p>
+
+<p>Android 5.0 のロック画面には {@link android.media.session.MediaSession} または {@link android.media.RemoteControlClient} に対応したトランスポート コントロールは表示されません。アプリでは代わりに、通知を通してロック画面からメディア再生コントロールを提供することができます。これにより、メディアボタンの表示をより詳細にコントロールできるようになると同時に、端末のロック時とロック解除時で一貫したユーザー エクスペリエンスが提供されます。</p>
+
+<p>この目的のために、Android 5.0 では新しい {@link android.app.Notification.MediaStyle} テンプレートが導入されています。{@link android.app.Notification.MediaStyle} は、{@link android.app.Notification.Builder#addAction(int, java.lang.CharSequence, android.app.PendingIntent) Notification.Builder.addAction()} で追加した通知操作をコンパクトなボタンに変換し、アプリのメディア再生通知に埋め込みます。セッション トークンを {@link android.app.Notification.MediaStyle#setMediaSession(android.media.session.MediaSession.Token) setSession()} メソッドに渡し、この通知によって以後のメディア セッションがコントロールされることをシステムに伝えます。</p>
+
+<p>必ず通知の表示を {@link android.app.Notification#VISIBILITY_PUBLIC VISIBILITY_PUBLIC} に設定し、どのようなロック画面でも安全に表示できる(それ以外の場合はセキュリティで保護される)ものとしてマークします。詳しくは、<a href="#LockscreenNotifications">ロック画面の通知</a>をご覧ください。</p>
+
+<p>アプリを Android <a href="{@docRoot}tv/index.html">TV</a> または <a href="{@docRoot}wear/index.html">Wear</a> プラットフォームで実行する場合、メディア再生コントロールを表示するには {@link android.media.session.MediaSession} クラスを実装してください。アプリで Android 端末のメディアボタン イベントを受信する必要がある場合は、{@link android.media.session.MediaSession} も実装してください。</p>
+
+<h3 id="BehaviorGetRecentTasks">アプリで getRecentTasks() を使用する場合</h3>
+
+<p>Android 5.0 で新しい「同時実行中のドキュメント / アクティビティ タスク」<em></em>機能が導入されたことに伴い(下記の<a href="#Recents">最近使ったアプリ画面に表示される同時実行中のドキュメント / アクティビティ</a>をご覧ください)、ユーザーのプライバシーを向上させるために {@link android.app.ActivityManager#getRecentTasks ActivityManager.getRecentTasks()} メソッドは非推奨になりました。下位互換性の目的から、このメソッドは引き続きデータの小さなサブセット(呼び出し側アプリの独自のタスクや、他の機密でないタスク(たとえばホーム)など)を返します。アプリで独自のタスクを取得するためにこのメソッドを使用している場合は、代わりに {@link android.app.ActivityManager#getAppTasks() getAppTasks()} を使用してその情報を取得してください。</p>
+
+<h3 id="64BitSupport">Android Native Development Kit(NDK)を使用している場合</h3>
+
+<p>Android 5.0 では 64 ビット システムのサポートが導入されています。64 ビットへの拡大によってアドレス空間が増加し、パフォーマンスが向上します。一方で、既存の 32 ビット アプリについても引き続き完全にサポートされます。また、64 ビットのサポートによって OpenSSL の暗号化のパフォーマンスも向上します。さらに、今回のリリースではネイティブの OpenGL ES(GLES)3.1 のサポートに加えて、新しいネイティブのメディア NDK API も導入されています。</p>
+
+<p>Android 5.0 で提供される 64 ビットのサポートを利用するには、<a href="{@docRoot}tools/sdk/ndk/index.html">Android NDK のページ</a>から NDK Revision 10c をダウンロードしてインストールしてください。NDK の重要な変更点とバグ修正について詳しくは、Revision 10c の<a href="{@docRoot}tools/sdk/ndk/index.html#Revisions">リリースノート</a>をご覧ください。</p>
+
+<h3 id="BindService">アプリからサービスにバインドする場合</h3>
+
+<p>{@link android.content.Context#bindService(android.content.Intent, android.content.ServiceConnection, int) Context.bindService()} メソッドについて、明示的な {@link android.content.Intent} の指定が必須となり、暗黙的なインテントを指定した場合は例外をスローするようになりました。アプリが確実にセキュリティで保護されるように、{@link android.app.Service} の起動やバインドの際は明示的なインテントを使用し、サービスに対してインテント フィルタを宣言しないでください。</p>
+
+<h3 id="BehaviorWebView">アプリが WebView を使用している場合</h3>
+
+<p>Android 5.0 ではアプリのデフォルトの動作が変更されています。</p>
+<ul>
+<li><strong>アプリの対象が API レベル 21 以降の場合:</strong>
+  <ul>
+    <li><a href="https://developer.mozilla.org/en-US/docs/Security/MixedContent" class="external-link">混合コンテンツ</a>とサードパーティの Cookie がデフォルトでブロックされます。混合コンテンツとサードパーティの Cookie を許可するには、それぞれ {@link android.webkit.WebSettings#setMixedContentMode(int) setMixedContentMode()} メソッドと {@link android.webkit.CookieManager#setAcceptThirdPartyCookies(android.webkit.WebView, boolean) setAcceptThirdPartyCookies()} メソッドを使用します。</li>
+    <li>描画する HTML ドキュメントの部分が適切に選択されるようになりました。この新しいデフォルトの動作は、メモリ量の減少とパフォーマンスの向上につながります。ドキュメント全体を一度に表示したい場合は、{@link android.webkit.WebView#enableSlowWholeDocumentDraw()} を呼び出してこの最適化を無効にしてください。</li>
+  </ul>
+</li>
+<li><strong>アプリの対象が API レベル 21 よりも前の場合:</strong> 混合コンテンツとサードパーティの Cookie が許可され、常にドキュメント全体が一度に表示されます。</li>
+</ul>
+
+<h2 id="UI">ユーザー インターフェース</h2>
+
+<h3 id="MaterialDesign">Material Design のサポート</h3>
+
+<p>次期リリースでは、Android の新しい「Material Design」<em></em>スタイルが新たにサポートされます。Material Design を使うと、外観が動的に変化し、ユーザーが UI 要素の切り替えを自然だと感じるようなアプリを作成できます。このサポートには以下が含まれます。</p>
+
+<ul>
+
+  <li>マテリアル テーマ</li>
+  <li>ビューシャドウ</li>
+  <li>{@link android.support.v7.widget.RecyclerView} ウィジェット</li>
+  <li>描画可能なアニメーションとスタイル効果</li>
+  <li>Material Design によるアニメーションとアクティビティ遷移効果</li>
+  <li>ビューの状態に基づいてビューのプロパティを決めるアニメータ</li>
+  <li>カスタマイズ可能な UI ウィジェットと、カラーパレットをコントロールできるアプリバー</li>
+  <li>XML ベクター グラフィックをベースとするアニメーション drawable と非アニメーション drawable</li>
+</ul>
+
+<p>アプリに Material Design の機能を追加する方法について詳しくは、<a href="{@docRoot}training/material/index.html">Material Design</a> をご覧ください。</p>
+
+<h3 id="Recents">最近使ったアプリ画面に表示される同時実行中のドキュメント / アクティビティ</h3>
+
+<p>以前のリリースでは、<a href="{@docRoot}guide/components/recents.html">最近使ったアプリ画面</a>には、ユーザーが最近操作したアプリごとにタスクを 1 つずつしか表示できませんでした。今後は、ドキュメント用の同時実行中のアクティビティが追加される場合、必要に応じて複数のタスクをアプリで開けるようになります。この機能により、ユーザーは最近使ったアプリ画面から個々のアクティビティやドキュメントをすばやく切り替えられるようになります。すべてのアプリにわたって一貫した切り替え操作になるので、マルチタスクの操作がしやすくなります。こうした同時実行中のタスクの例としては、ウェブブラウザ アプリで複数のタブを開く、生産性向上アプリで複数のドキュメントを開く、ゲームで複数の試合を同時に実行する、メッセージ アプリで複数のチャットを実行する、などが考えられます。アプリのタスクは {@link android.app.ActivityManager.AppTask} クラスを使って管理できます。</p>
+
+<p>論理的な切れ目を挿入してアクティビティが新しいタスクとして扱われるようにするには、{@link android.app.Activity#startActivity(android.content.Intent) startActivity()} を使用してアクティビティを起動するときに {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT} を使用します。または、マニフェストで <a href="{@docRoot}guide/topics/manifest/activity-element.html">&lt;activity&gt;</a> 要素の {@code documentLaunchMode} 属性を {@code "intoExisting"} または {@code "always"} に設定しても、この動作を実現できます。</p>
+
+<p>最近使ったアプリ画面が適切に整理されるように、最近使ったアプリ画面に表示できるタスクの最大数をアプリから設定することができます。それには、<a href="{@docRoot}guide/topics/manifest/application-element.html">&lt;application&gt;</a> 属性 {@link android.R.attr#maxRecents android:maxRecents} を設定します。指定できる現在の最大タスク数はユーザーあたり 50 個(RAM が不足している端末の場合は 25 個)です。</a></p>
+
+<p>最近使ったアプリ画面に表示されるタスクが再起動後も持続されるように設定することができます。この持続動作をコントロールするには <a href="{@docRoot}reference/android/R.attr.html#persistableMode">android:persistableMode</a> 属性を使用します。また、{@link android.app.Activity#setTaskDescription(android.app.ActivityManager.TaskDescription) setTaskDescription()} メソッドを呼び出して、最近使ったアプリ画面に表示されるアクティビティの表示プロパティ(アクティビティの色、ラベル、アイコンなど)を変更することもできます。</p>
+
+<h3 id="WebView">WebView の更新</h3>
+<p>Android 5.0 では Chromium M37 に対する {@link android.webkit.WebView} の実装が更新され、セキュリティと安定性が強化されているほか、バグが修正されています。Android 5.0 で実行される {@link android.webkit.WebView} のデフォルトのユーザーエージェント文字列が更新され、バージョン番号として 37.0.0.0 が組み込まれています。</p>
+
+<p>今回のリリースでは {@link android.webkit.PermissionRequest} クラスが導入されています。アプリでこのクラスを使用することで、<a href="https://developer.mozilla.org/en-US/docs/NavigatorUserMedia.getUserMedia" class="external-link">getUserMedia()</a> などのウェブ API を通じてカメラやマイクなどの保護リソースにアクセスするための権限を {@link android.webkit.WebView} に許可できます。{@link android.webkit.WebView} に権限を許可するためには、これらのリソースに対する適切な Android 権限をアプリに持たせる必要があります。</p>
+
+<p>新しい <code><a href="{@docRoot}reference/android/webkit/WebChromeClient.html#onShowFileChooser(android.webkit.WebView, android.webkit.ValueCallback<android.net.Uri[]>, android.webkit.WebChromeClient.FileChooserParams)">onShowFileChooser()</a></code> メソッドでは、{@link android.webkit.WebView} で入力フォーム項目を使用できるようになり、ファイル選択機能を起動して Android 端末から画像やファイルを選択できるようになりました。</p>
+
+<p>さらに、今回のリリースでは <a href="http://webaudio.github.io/web-audio-api/" class="external-link">WebAudio</a>、<a href="https://www.khronos.org/webgl/" class="external-link">WebGL</a>、<a href="http://www.webrtc.org/" class="external-link">WebRTC</a> の各オープン規格もサポートされるようになります。今回のリリースに含まれる新機能について詳しくは、<a href="https://developer.chrome.com/multidevice/webview/overview" class="external-link">Android 用 WebView</a> をご覧ください。</p>
+
+<h3 id="ScreenCapture">画面のキャプチャと共有</h3>
+<p>Android 5.0 では、新しい {@link android.media.projection} API を使用して画面キャプチャ機能や画面共有機能をアプリに追加できます。この機能は、たとえばビデオ会議アプリで画面の共有を有効にしたい場合などに便利です。</p>
+
+<p>アプリで新しい {@link android.media.projection.MediaProjection#createVirtualDisplay(java.lang.String, int, int, int, int, android.view.Surface, android.hardware.display.VirtualDisplay.Callback, android.os.Handler) createVirtualDisplay()} メソッドを使用すると、メイン画面(デフォルトの表示)の内容をキャプチャして {@link android.view.Surface} オブジェクトに取り込み、アプリからネットワーク経由で送信することができます。API では、セキュリティで保護されていない画面内容のキャプチャのみ可能であり、システム音声のキャプチャはできません。アプリで画面のキャプチャを開始するには、最初に {@link android.media.projection.MediaProjectionManager#createScreenCaptureIntent()} メソッドを通じて取得した {@link android.content.Intent} を使用して画面キャプチャ用のダイアログを起動し、ユーザーの許可をリクエストする必要があります。</p>
+
+<p>新しい API の使用例については、サンプル プロジェクトの {@code MediaProjectionDemo} クラスをご覧ください。</p>
+
+<h2 id="Notifications">通知</h2>
+
+<h3 id="LockscreenNotifications">ロック画面の通知</h3>
+<p>Android 5.0 のロック画面には通知を表示する機能が組み込まれています。ユーザーは [設定]<em></em> を通じて、機密性のある通知内容をセキュリティで保護されたロック画面に表示できるようにするかどうかを選択できます。</p>
+
+<p>アプリでは、セキュリティで保護されたロック画面にアプリの通知が表示されるときの表示の詳細レベルをコントロールできます。表示レベルをコントロールするには、{@link android.app.Notification.Builder#setVisibility(int) setVisibility()} を呼び出して次のいずれかの値を指定します。</p>
+
+<ul>
+<li>{@link android.app.Notification#VISIBILITY_PRIVATE VISIBILITY_PRIVATE}: 通知のアイコンなどの基本的な情報は表示しますが、通知内容全体は表示しません。</li>
+<li>{@link android.app.Notification#VISIBILITY_PUBLIC VISIBILITY_PUBLIC}: 通知内容全体を表示します。</li>
+<li>{@link android.app.Notification#VISIBILITY_SECRET VISIBILITY_SECRET}: 通知のアイコンも含めて何も表示しません。</li>
+</ul>
+
+<p>表示レベルが {@link android.app.Notification#VISIBILITY_PRIVATE VISIBILITY_PRIVATE} の場合は、詳細な個人情報を隠した編集済みの通知内容を表示することもできます。たとえば SMS アプリで、「3 個の新しいテキスト メッセージがあります」という通知は表示しますが、メッセージの内容や送信者を表示しないようにできます。こうした代わりの通知を表示するには、最初に {@link android.app.Notification.Builder} を使用して代わりとなる通知を作成します。プライベートの通知オブジェクトを作成するときに、{@link android.app.Notification.Builder#setPublicVersion(android.app.Notification) setPublicVersion()} メソッドを使用して代わりの通知をオブジェクトに添付します。</p>
+
+<h3 id="NotificationsMetadata">通知メタデータ</h3>
+<p>Android 5.0 では、アプリの通知がよりスマートに並べ替えられるように、通知に関連付けられたメタデータが使用されます。メタデータを設定するには、通知の作成時に {@link android.app.Notification.Builder} で次のメソッドを呼び出します。</p>
+
+<ul>
+<li>{@link android.app.Notification.Builder#setCategory(java.lang.String) setCategory()}: 端末が「優先」<em></em>モードのとき(通知が電話の着信、インスタント メッセージ、またはアラームを表している場合など)のアプリでの通知の処理方法をシステムに指示します。
+<li>{@link android.app.Notification.Builder#setPriority(int) setPriority()}: 対象の通知について、通常の通知と比べて重要度が高いことまたは低いことをマークします。優先項目が {@link android.app.Notification#PRIORITY_MAX PRIORITY_MAX} または {@link android.app.Notification#PRIORITY_HIGH PRIORITY_HIGH} に設定された通知は、通知に音声やバイブレーションが付いている場合に、小さいフローティング ウィンドウに表示されます。</li>
+<li>{@link android.app.Notification.Builder#addPerson(java.lang.String) addPerson()}: 通知への関連性がある人を 1 人以上追加できるようにします。アプリでこうしたメソッドを使用することで、指定した人からの通知をグループにまとめる必要があることや、それらの人からの通知をより重要なものとしてランク付けする必要があることを、システムに知らせることができます。</li>
+</ul>
+
+<h2 id="Graphics">グラフィック</h2>
+
+<h3 id="OpenGLES-3-1">OpenGL ES 3.1 のサポート</h3>
+<p>Android 5.0 では、OpenGL ES 3.1 のネイティブ サポートとそれに対応した Java インターフェースが追加されています。OpenGL ES 3.1 では主に次の新機能が提供されています。</p>
+
+<ul>
+<li>コンピュート シェーダ
+<li>個別のシェーダ オブジェクト
+<li>間接描画コマンド
+<li>マルチサンプル テクスチャとステンシル テクスチャ
+<li>シェーディング言語の向上
+<li>高度なブレンドモードとデバッグに対応した拡張機能
+<li>OpenGL ES 2.0 / 3.0 との下位互換性
+</ul>
+
+<p>Android での OpenGL ES 3.1 の Java インターフェースは {@link android.opengl.GLES31} で提供されます。OpenGL ES 3.1 を使用する場合は、必ずマニフェスト ファイルで <a href="{@docRoot}guide/topics/manifest/uses-feature-element.html">{@code <uses-feature>}</a> タグと {@code android:glEsVersion} 属性を使用して宣言してください。次に例を示します。</p>
+
+<pre>
+&lt;manifest&gt;
+    &lt;uses-feature android:glEsVersion="0x00030001" /&gt;
+    ...
+&lt;/manifest&gt;
+</pre>
+
+<p>端末でサポートされている OpenGL ES のバージョンを実行時にチェックする方法など、OpenGL ES の使い方について詳しくは、<a href="{@docRoot}guide/topics/graphics/opengl.html">OpenGL ES API ガイド</a>をご覧ください。</p>
+
+<h3 id="AndroidExtensionPack">Android Extension Pack</h3>
+
+<p>OpenGL ES 3.1 に加えて、今回のリリースでは、高度なグラフィック機能のネイティブ サポートとそれに対応した Java インターフェースを実現する拡張パックが提供されています。これらの拡張機能は Android で 1 つのパッケージとして扱われます({@code ANDROID_extension_pack_es31a} 拡張機能が存在する場合、アプリではパッケージ内のすべての拡張機能が存在すると見なして、1 つの {@code #extension} ステートメントでシェーディング言語機能を有効にすることができます)。</p>
+
+<p>拡張パックは以下をサポートします。</p>
+
+<ul>
+<li>保証されたフラグメント シェーダによる、シェーダ保存バッファ、イメージ、アトミックのサポート(OpenGL ES 3.1 ではフラグメント シェーダのサポートはオプションです)</li>
+<li>テッセレーション シェーダとジオメトリ シェーダ</li>
+<li>ASTC(LDR)テクスチャ圧縮形式</li>
+<li>サンプル単位の補間とシェーディング</li>
+<li>フレーム バッファ内の各カラー アタッチメントに対応した各種のブレンドモード</li>
+</ul>
+
+<p>拡張パックの Java インターフェースは {@link android.opengl.GLES31Ext} で提供されます。アプリのマニフェストで、拡張パックをサポートしている端末にのみアプリをインストールする必要があることを宣言できます。次に例を示します。</p>
+
+<pre>
+&lt;manifest&gt;
+    &lt;uses-feature android:name=“android.hardware.opengles.aep”
+        android:required="true" /&gt;
+    ...
+&lt;/manifest&gt;
+</pre>
+
+<h2 id="Media">メディア</h2>
+
+<h3 id="Camera-v2">高度なカメラ機能に対応した Camera API</h3>
+
+<p>Android 5.0 では、写真のキャプチャや画像処理をきめ細かく行えるように、新しい <a href="{@docRoot}reference/android/hardware/camera2/package-summary.html">android.hardware.camera2</a> API が導入されています。プログラムから、{@link android.hardware.camera2.CameraManager#getCameraIdList() getCameraIdList()} を通じてシステムで使用できるカメラデバイスにアクセスし、{@link android.hardware.camera2.CameraManager#openCamera(java.lang.String, android.hardware.camera2.CameraDevice.StateCallback, android.os.Handler) openCamera()} を通じて特定のデバイスに接続できるようになりました。画像のキャプチャを開始するには、{@link android.hardware.camera2.CameraCaptureSession} を作成し、キャプチャした画像を送信するための {@link android.view.Surface} オブジェクトを指定します。{@link android.hardware.camera2.CameraCaptureSession} を設定して、1 枚のショットを撮影するようにしたり、一度に複数の画像を撮影するようにしたりできます。</p>
+
+<p>新しい画像がキャプチャされたときに通知を受けるには、{@link android.hardware.camera2.CameraCaptureSession.CaptureCallback} リスナを実装してキャプチャ リクエストで設定します。画像キャプチャ リクエストが完了すると、{@link android.hardware.camera2.CameraCaptureSession.CaptureCallback} リスナが {@link android.hardware.camera2.CameraCaptureSession.CaptureCallback#onCaptureCompleted(android.hardware.camera2.CameraCaptureSession, android.hardware.camera2.CaptureRequest, android.hardware.camera2.TotalCaptureResult) onCaptureCompleted()} への呼び出しを受信し、{@link android.hardware.camera2.CaptureResult} に画像キャプチャ メタデータが設定されます。</p>
+
+<p>アプリで {@link android.hardware.camera2.CameraCharacteristics} クラスを使用すると、端末でどのカメラ機能が使用できるのかを検出できます。オブジェクトの {@link android.hardware.camera2.CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL INFO_SUPPORTED_HARDWARE_LEVEL} プロパティは、カメラの機能レベルを表します。</p>
+
+<ul>
+  <li>すべてのデバイスは少なくとも {@link android.hardware.camera2.CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY} のハードウェア レベルをサポートしています。このレベルは、非推奨となった {@link android.hardware.Camera} API のレベルにほぼ相当する機能を備えています。</li>
+  <li>{@link android.hardware.camera2.CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_FULL INFO_SUPPORTED_HARDWARE_LEVEL_FULL} のハードウェア レベルをサポートしているデバイスは、キャプチャとポストプロセスを手動でコントロールする機能と、高いフレームレートで高解像度画像をキャプチャする機能を備えています。</li>
+</ul>
+
+<p>最新の <a href="{@docRoot}reference/android/hardware/camera2/package-summary.html">Camera</a> API の使い方について詳しくは、このリリースに付属している {@code Camera2Basic} と {@code Camera2Video} の実装サンプルをご覧ください。</p>
+
+<h3 id="AudioPlayback">音声の再生</h3>
+<p>今回のリリースでは {@link android.media.AudioTrack} に次の変更が加えられています。</p>
+<ul>
+  <li>アプリから音声データを浮動小数点形式({@link android.media.AudioFormat#ENCODING_PCM_FLOAT ENCODING_PCM_FLOAT})で提供できるようになりました。これにより、ダイナミック レンジの向上、一貫性のある精度の向上、ヘッドルームの向上が可能になります。浮動小数点による計算は、特に中間計算の実行時に便利です。再生のエンドポイントでは、音声データに整数形式が使用され、ビット深度がより少なくなります(Android 5.0 では内部パイプラインの一部がまだ浮動小数点になっていません)。
+  <li>アプリから音声データを {@link java.nio.ByteBuffer} として、{@link android.media.MediaCodec} で提供されるものと同じ形式で提供できるようになりました。
+  <li>一部のアプリでは、{@link android.media.AudioTrack#WRITE_NON_BLOCKING WRITE_NON_BLOCKING} オプションを使用することでバッファ処理とマルチスレッド処理を簡素化できます。
+</ul>
+
+<h3 id="MediaPlaybackControl">メディア再生コントロール</h3>
+<p>メディアの再生についての情報をシステム UI に知らせ、アルバムアートの抽出と表示ができるように、新しい通知 / メディア API を使用してください。UI とサービスにまたがるメディアの再生のコントロールが、新しい {@link android.media.session.MediaSession} クラスと {@link android.media.session.MediaController} クラスを使用して、より簡単に行えるようになりました。</p>
+
+<p>新しい {@link android.media.session.MediaSession} クラスは非推奨となった {@link android.media.RemoteControlClient} クラスの代わりとなるもので、トランスポート コントロールとメディアボタンを処理するための単一のコールバック メソッド セットを提供します。アプリでメディアの再生を提供して Android <a href="{@docRoot}tv/index.html">TV</a> または <a href="{@docRoot}wear/index.html">Wear</a> プラットフォームで実行する場合は、{@link android.media.session.MediaSession} クラスを使用し、同じコールバック メソッドを通じてトランスポート コントロールを処理してください。</p>
+
+<p>新しい {@link android.media.session.MediaController} クラスを使用して独自のメディア コントローラ アプリを作成できるようになりました。このクラスは、アプリの UI プロセスからスレッドセーフな方法でメディアの再生を監視しコントロールするための手段を提供します。コントローラの作成時に {@link android.media.session.MediaSession.Token} オブジェクトを指定し、指定した {@link android.media.session.MediaSession} をアプリから操作できるようにします。{@link android.media.session.MediaController.TransportControls} のメソッドを使用すると、そのセッションで {@link android.media.session.MediaController.TransportControls#play() play()}、{@link android.media.session.MediaController.TransportControls#stop() stop()}、{@link android.media.session.MediaController.TransportControls#skipToNext() skipToNext()}、{@link android.media.session.MediaController.TransportControls#setRating(android.media.Rating) setRating()} などのコマンドを送信してメディアの再生をコントロールできます。また、コントローラで {@link android.media.session.MediaController.Callback} オブジェクトを登録し、そのセッションでメタデータや状態変更がないかをリッスンできます。</p>
+
+<p>さらに、新しい {@link android.app.Notification.MediaStyle} クラスを使用して、再生コントロールをメディア セッションに結び付けることができる高機能な通知を作成できます。</p>
+
+<h3 id="MediaBrowsing">メディアの参照</h3>
+<p>Android 5.0 では、新しい <a href="{@docRoot}reference/android/media/browse/package-summary.html">android.media.browse</a> API を通じて、あるアプリから別のアプリのメディア コンテンツ ライブラリを参照する機能が導入されています。アプリのメディア コンテンツを公開するには、{@link android.service.media.MediaBrowserService} クラスを拡張します。{@link android.service.media.MediaBrowserService} の実装で {@link android.media.session.MediaSession.Token} へのアクセスを提供することで、別のアプリがそのサービスを通じて提供されるメディア コンテンツを再生できるようになります。</p>
+<p>メディア ブラウザ サービスを操作するには、{@link android.media.browse.MediaBrowser} クラスを使用します。{@link android.media.browse.MediaBrowser} インスタンスの作成時に、{@link android.media.session.MediaSession} のコンポーネント名を指定します。その後、アプリからブラウザ インスタンスを使用して、関連付けられたサービスに接続し、{@link android.media.session.MediaSession.Token} オブジェクトを取得して、そのサービスを通じて公開されたコンテンツを再生できます。</p>
+
+<h2 id="Storage">ストレージ</h2>
+
+<h3 id="DirectorySelection">ディレクトリの選択</h3>
+
+<p>Android 5.0 では<a href="{@docRoot}guide/topics/providers/document-provider.html">ストレージ アクセス フレームワーク</a>が拡張され、ユーザーがディレクトリのサブツリー全体を選択し、それぞれのアイテムについてユーザーの確認を必要とせずに、含まれているすべてのドキュメントへの読み取り / 書き込みアクセス権をアプリに与えることができるようになります。</p>
+
+<p>ディレクトリのサブツリーを選択するには、{@link android.content.Intent#ACTION_OPEN_DOCUMENT_TREE OPEN_DOCUMENT_TREE} インテントを作成して送信します。サブツリーの選択をサポートしているすべての {@link android.provider.DocumentsProvider} インスタンスが表示され、ユーザーがディレクトリを参照して選択することができます。返される URI は、選択されたサブツリーへのアクセス手段を表します。その後、{@link android.provider.DocumentsContract#buildChildDocumentsUriUsingTree(android.net.Uri, java.lang.String) buildChildDocumentsUriUsingTree()} と {@link android.provider.DocumentsContract#buildDocumentUriUsingTree(android.net.Uri, java.lang.String) buildDocumentUriUsingTree()} を {@link android.content.ContentResolver#query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String) query()} と一緒に使用することでサブツリーを探索できます。</p>
+
+<p>新しい {@link android.provider.DocumentsContract#createDocument(android.content.ContentResolver, android.net.Uri, java.lang.String, java.lang.String) createDocument()} メソッドを使用すると、サブツリーの配下に新しいドキュメントやディレクトリを作成できます。既存のドキュメントを管理するには、{@link android.provider.DocumentsContract#renameDocument(android.content.ContentResolver, android.net.Uri, java.lang.String) renameDocument()} と {@link android.provider.DocumentsProvider#deleteDocument(java.lang.String) deleteDocument()} を使用します。これらの呼び出しを実行する前に、{@link android.provider.DocumentsContract.Document#COLUMN_FLAGS COLUMN_FLAGS} をチェックして、提供側が呼び出しをサポートしているかどうかを確認してください。</p>
+
+<p>{@link android.provider.DocumentsProvider} を実装しようとしていて、サブツリーの選択をサポートしたい場合は、{@link android.provider.DocumentsProvider#isChildDocument(java.lang.String, java.lang.String) isChildDocument()} を実装し、{@link android.provider.DocumentsContract.Root#FLAG_SUPPORTS_IS_CHILD FLAG_SUPPORTS_IS_CHILD} を {@link android.provider.DocumentsContract.Root#COLUMN_FLAGS COLUMN_FLAGS} に追加します。</p>
+
+<p>Android 5.0 ではまた、共有ストレージ上の新しいパッケージ固有ディレクトリが導入されており、{@link android.provider.MediaStore} に含めることでアプリからメディア ファイルを配置することができます。新しい {@link android.content.Context#getExternalMediaDirs()} は、すべての共有ストレージ デバイスにあるこれらのディレクトリへのパスを返します。{@link android.content.Context#getExternalFilesDir(java.lang.String) getExternalFilesDir()} と同様に、アプリから追加の権限がなくても、返されるパスにアクセスできます。これらのディレクトリはプラットフォームによって定期的にスキャンされ、新しいメディアがないかが調べられますが、{@link android.media.MediaScannerConnection} を使用して明示的にスキャンし、新しいコンテンツがないかを調べることもできます。</p>
+
+<h2 id="Wireless">ワイヤレスと接続</h2>
+
+<h3 id="Multinetwork">マルチネットワーク接続</h3>
+<p>Android 5.0 では新しいマルチネットワーク API が導入されています。これらの API を通じてアプリで動的にスキャンを実行し、特定の機能を持つネットワークが使用できないかを調べて、それらへの接続を確立することができます。この機能は、アプリで SUPL、MMS、キャリア課金ネットワークなどの特殊なネットワークを必要とする場合や、特定のタイプのトランスポート コントロールを使用してデータを送信したい場合に便利です。</p>
+
+<p>アプリでネットワークを動的に選択して接続する手順は次のとおりです。</p>
+
+<ol>
+ <li>{@link android.net.ConnectivityManager} を作成します。</li>
+ <li>{@link android.net.NetworkRequest.Builder} クラスを使用して {@link android.net.NetworkRequest} オブジェクトを作成し、アプリで必要なネットワーク機能とトランスポート タイプを指定します。</li>
+<li>適切なネットワークがないかどうかをスキャンして調べるには、{@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback) requestNetwork()} または {@link android.net.ConnectivityManager#registerNetworkCallback(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback) registerNetworkCallback()} を呼び出し、{@link android.net.NetworkRequest} オブジェクトと、{@link android.net.ConnectivityManager.NetworkCallback} の実装を渡します。検出した適切なネットワークに積極的に切り替えたい場合は、{@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback) requestNetwork()} メソッドを使用します。スキャンしたネットワークに積極的に切り替えずに通知のみを受信するには、代わりに {@link android.net.ConnectivityManager#registerNetworkCallback(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback) registerNetworkCallback()} メソッドを使用します。</li>
+</ol>
+
+<p>適切なネットワークが検出されると、システムはそのネットワークに接続し、{@link android.net.ConnectivityManager.NetworkCallback#onAvailable(android.net.Network) onAvailable()} コールバックを呼び出します。このコールバックの {@link android.net.Network} オブジェクトを使用して、ネットワークに関する詳細な情報を取得したり、選択されたネットワークを使用するようにトラフィックを設定したりできます。</p>
+
+<h3 id="BluetoothBroadcasting">Bluetooth Low Energy</h3>
+<p>Android 4.3 では、<a href="{@docRoot}guide/topics/connectivity/bluetooth-le.html">Bluetooth Low Energy</a>(<em>Bluetooth LE</em>)がプラットフォームでサポートされ、中央の役割として導入されました。Android 5.0 では、Android 搭載デバイスが Bluetooth LE の「周辺デバイス」<em></em>として機能できるようになりました。アプリからこの機能を使用することで、アプリの存在を付近のデバイスに知らせることができます。たとえば、デバイスを歩数計や健康管理機器として機能させるアプリを作成して、それらのデータを他の Bluetooth LE 対応デバイスとやり取りすることができます。</p>
+<p>新しい {@link android.bluetooth.le} API を使用すると、アプリからアドバタイズをブロードキャストしたり、応答の有無をスキャンして調べたり、付近の Bluetooth LE 対応デバイスとの接続を確立したりできます。新しいアドバタイズ機能やスキャン機能を使用するには、{@link android.Manifest.permission#BLUETOOTH_ADMIN BLUETOOTH_ADMIN} 権限をマニフェストに追加します。ユーザーは、Play ストアでアプリを更新またはダウンロードするときに、「Bluetooth接続情報: Bluetoothの制御(付近のBluetoothデバイスへの送信、詳細情報の取得など)をアプリに許可します」という権限をアプリに許可するかどうかたずねられます。</p>
+
+<p>他のデバイスがアプリを検出できるように Bluetooth LE によるアドバタイズを開始するには、{@link android.bluetooth.le.BluetoothLeAdvertiser#startAdvertising(android.bluetooth.le.AdvertiseSettings, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseCallback) startAdvertising()} を呼び出し、{@link android.bluetooth.le.AdvertiseCallback} クラスの実装を渡します。このコールバック オブジェクトは、アドバタイズ操作の成否を知らせる通知を受信します。</p>
+
+<p> Android 5.0 で導入された {@link android.bluetooth.le.ScanFilter} クラスをアプリで使用すると、必要な特定のタイプのデバイスのみを探すスキャンを実行できます。Bluetooth LE デバイスを探すためのスキャンを開始するには、{@link android.bluetooth.le.BluetoothLeScanner#startScan(android.bluetooth.le.ScanCallback) startScan()} を呼び出し、フィルタのリストを渡します。このメソッド呼び出しでは、{@link android.bluetooth.le.ScanCallback} の実装を渡し、Bluetooth LE のアドバタイズが検出されたときにそれを知らせるようにする必要もあります。 </p>
+
+<h3 id="NFCEnhancements">NFC の機能強化</h3>
+<p>Android 5.0 では、NFC をより幅広く柔軟に利用できるように、次の機能強化が追加されています。</p>
+
+<ul>
+<li>Android Beam が「共有」<em></em>メニューで使用できるようになりました。</li>
+<li>ユーザーの端末でアプリから Android Beam を起動し、{@link android.nfc.NfcAdapter#invokeBeam(android.app.Activity) invokeBeam()} を呼び出してデータを共有することができます。これにより、ユーザーが手動で端末をタップして別の NFC 対応端末へのデータ転送を実行する必要がなくなります。</li>
+<li>新しい {@link android.nfc.NdefRecord#createTextRecord(java.lang.String, java.lang.String) createTextRecord()} メソッドを使用して、UTF-8 テキストデータを含む NDEF レコードを作成できます。</li>
+<li>支払い機能を備えるアプリを開発している場合、<code><a href="{@docRoot}reference/android/nfc/cardemulation/CardEmulation.html#registerAidsForService(android.content.ComponentName, java.lang.String, java.util.List<java.lang.String>)">registerAidsForService()</a></code> を呼び出して NFC アプリケーション ID(AID)を動的に登録できるようになりました。また、{@link android.nfc.cardemulation.CardEmulation#setPreferredService(android.app.Activity, android.content.ComponentName) setPreferredService()} を使用して、特定のアクティビティがフォアグラウンドに存在するときに使用する優先のカード エミュレーション サービスを設定できます。</li>
+</ul>
+
+<h2 id="Power">Project Volta</h2>
+
+<p>新機能に加えて、Android 5.0 では特に電池寿命が向上しています。新しい API やツールを使用して、アプリの消費電力に関する情報の取得や消費電力の最適化を行ってください。</p>
+
+<h3 id="JobScheduler">ジョブのスケジューリング</h3>
+<p>Android 5.0 で提供される新しい {@link android.app.job.JobScheduler} API を使用すると、システムが後で、または指定の条件(端末の充電時など)の下で、非同期に実行するジョブを定義することによって、電池寿命を最適化できます。ジョブ スケジューリングは、アプリで次のような状況を処理する場合に便利です。</p>
+<ul>
+  <li>現在ユーザーが関与していない作業の実行を遅らせることができる場合。</li>
+  <li>実行を遅らせた作業を、ユニットが電源に接続されたときに実行する場合。</li>
+  <li>ネットワーク アクセスや Wi-Fi 接続を必要とするタスクがある場合。</li>
+  <li>定期的なスケジュールで一括して実行したいタスクが複数ある場合。</li>
+
+</ul>
+
+<p>作業ユニットは {@link android.app.job.JobInfo} オブジェクトによってカプセル化されます。このオブジェクトでスケジューリング条件を指定します。</p>
+
+<p>スケジューリング済みのタスクをどのように実行するのかを設定するには、{@link android.app.job.JobInfo.Builder} クラスを使用します。タスクが次のような特定の条件下で実行されるようにスケジューリングできます。</p>
+
+<ul>
+  <li>端末の充電時に開始する</li>
+  <li>端末が定額制ネットワークに接続されたときに開始する</li>
+  <li>端末がアイドル状態のときに開始する</li>
+  <li>特定の期限が過ぎる前に完了する、または最小限の遅延で完了する</li>
+</ul>
+
+<p>たとえば、次のようなコードを追加すると、タスクを定額制ネットワーク上で実行できます。</p>
+
+<pre>
+JobInfo uploadTask = new JobInfo.Builder(mJobId,
+                                         mServiceComponent /* JobService component */)
+        .setRequiredNetworkCapabilities(JobInfo.NetworkType.UNMETERED)
+        .build();
+JobScheduler jobScheduler =
+        (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
+jobScheduler.schedule(uploadTask);
+</pre>
+
+<p>端末の電力が安定している場合(つまり、端末が 2 分以上電源に接続されていて、電池が<a href="{@docRoot}reference/android/content/Intent.html#ACTION_BATTERY_OKAY">正常レベル</a>にある場合)には、ジョブがまだ期限切れになっていなくても、実行の準備ができているスケジューリング済みのジョブがすべて実行されます。</p>
+
+<p>{@link android.app.job.JobScheduler} API の使用例については、このリリースに付属している {@code JobSchedulerSample} の実装サンプルをご覧ください。</p>
+
+<h3 id="PowerMeasurementTools">電池の使用統計情報を提供するデベロッパー向けツール</h3>
+
+<p>新しい {@code dumpsys batterystats} コマンドでは、端末での電池の使用に関して役立つ統計データが、一意のユーザー ID(UID)別に整理されて生成されます。次のような統計情報が生成されます。</p>
+
+<ul>
+<li>電池関連のイベントの履歴
+<li>端末の全体的な統計情報
+<li>UID 別やシステム コンポーネント別の推定電力使用量
+<li>アプリ別のパケットあたりのモバイル ミリ秒
+<li>システム UID 別に集計される統計情報
+<li>アプリ UID 別に集計される統計情報
+</ul>
+
+<p>出力をカスタマイズするための各種オプションについて調べるには、{@code --help} オプションを使用します。たとえば対象のアプリ パッケージについて、端末が最後に充電されてから現在までの電池の使用統計情報を出力するには、次のコマンドを実行します。
+<pre>
+$ adb shell dumpsys batterystats --charged &lt;package-name&gt;
+</pre>
+
+<p>{@code dumpsys} コマンドの出力に対して<a href="https://github.com/google/battery-historian" class="external-link">電池履歴</a>ツールを使用すると、電力に関するイベントをログから取得して視覚的に表現する HTML を生成できます。これらの情報を利用することで、電池に関連した問題の把握や診断がしやすくなります。</p>
+
+<h2 id="Enterprise">職場向けや教育向けの Android</h2>
+<h3 id="ManagedProvisioning">管理対象プロビジョニング</h3>
+
+<p>Android 5.0 では社内環境の中でアプリを実行するための新機能が提供されています。<a href="{@docRoot}guide/topics/admin/device-admin.html">端末管理者</a>は、ユーザーが既存の個人用アカウントを持っている場合に、管理対象プロビジョニングのプロセスを開始して、同時かつ別々に存在する「管理対象プロフィール」<em></em>を端末に追加することができます。管理対象プロフィールに関連付けられたアプリは、管理対象外のアプリと一緒に、ユーザーのランチャー、最近使ったアプリ画面、通知に表示されます。</p>
+
+<p>管理対象プロビジョニングのプロセスを開始するには、{@link android.content.Intent} で {@link android.app.admin.DevicePolicyManager#ACTION_PROVISION_MANAGED_PROFILE ACTION_PROVISION_MANAGED_PROFILE} を送信します。呼び出しが成功すると、システムから {@link android.app.admin.DeviceAdminReceiver#onProfileProvisioningComplete(android.content.Context, android.content.Intent) onProfileProvisioningComplete()} コールバックが起動されます。その後、{@link android.app.admin.DevicePolicyManager#setProfileEnabled(android.content.ComponentName) setProfileEnabled()} を呼び出してこの管理対象プロフィールを有効にすることができます。</p>
+
+<p>デフォルトでは、一部の少数のアプリのみが管理対象プロフィールで有効になります。管理対象プロフィールで追加のアプリをインストールするには、{@link android.app.admin.DevicePolicyManager#enableSystemApp(android.content.ComponentName, android.content.Intent) enableSystemApp()} を呼び出します。</p>
+
+<p>ランチャー アプリを開発している場合は、新しい {@link android.content.pm.LauncherApps} クラスを使用して、現在のユーザーの起動可能なアクティビティのリストや、関連するすべての管理対象プロフィールを取得できます。ランチャーでアイコン drawable に作業バッジを付けると、管理対象アプリを視覚的に目立たせることができます。バッジ付きのアイコンを取得するには {@link android.content.pm.PackageManager#getUserBadgedIcon(android.graphics.drawable.Drawable, android.os.UserHandle) getUserBadgedIcon()} を呼び出します。</p>
+
+<p>新機能の使い方については、このリリースに付属している {@code BasicManagedProfile} の実装サンプルをご覧ください。</p>
+
+<h3 id="DeviceOwner">端末所有者</h3>
+<p>Android 5.0 では、端末所有者アプリの展開機能が導入されています。<em></em>「端末所有者」は特殊なタイプの<a href="{@docRoot}guide/topics/admin/device-admin.html">端末管理者</a>で、第 2 ユーザーの作成や削除、端末全体の設定について、さらに詳細な操作を実行できます。端末管理者アプリでは {@link android.app.admin.DevicePolicyManager} クラスのメソッドを使用して、管理対象端末の設定、セキュリティ、アプリをきめ細かくコントロールできます。端末で有効にできる端末管理者は一度に 1 つだけです。</p>
+
+<p>端末管理者を展開して有効にするには、端末がプロビジョニングされていない状態となっている間に、プログラミング アプリから端末への NFC データ転送を実行する必要があります。このデータ転送では、<a href="#ManagedProvisioning">管理対象プロビジョニング</a>で説明しているインテントのプロビジョニングの場合と同じ情報が送信されます。</p>
+
+<h3 id="ScreenPinning">画面の固定</h3>
+
+<p>Android 5.0 では、新しい画面固定 API が導入されています。これを使用すると、ユーザーがタスクから離れる操作や通知による割り込みを一時的に制限することができます。たとえば Android で、重要度の高い評価要件に対応した教育向けアプリや、単一用途のアプリ、またはキオスクアプリを開発している場合などに役立ちます。アプリで画面の固定を有効にすると、そのモードを終了するまで、ユーザーは通知を表示することも、他のアプリにアクセスすることも、ホーム画面に戻ることもできなくなります。</p>
+
+<p>次の 2 つの方法で画面の固定を有効にすることができます。</p>
+
+<ul>
+<li><strong>手動による方法:</strong> ユーザーは [設定] &gt; [セキュリティ] &gt; [画面の固定]<em></em> で画面の固定を有効にすることができ、最近使ったアプリ画面に表示される緑色のピンアイコンをタップすることで、固定したいタスクを選択できます。</li> <li><strong>プログラミングによる方法:</strong> プログラムから画面の固定を有効にするには、アプリで {@link android.app.Activity#startLockTask() startLockTask()} を呼び出します。リクエストする側のアプリが端末所有者でない場合は、確認のメッセージがユーザーに表示されます。端末所有者アプリから {@link android.app.admin.DevicePolicyManager#setLockTaskPackages(android.content.ComponentName, java.lang.String[]) setLockTaskPackages()} メソッドを呼び出すと、ユーザーの確認手順なしでアプリの固定を有効にすることができます。</li>
+</ul>
+
+<p>タスクのロックが有効になると、次のような動作になります。</p>
+
+<ul>
+<li>ステータスバーが空になり、ユーザーの通知やステータス情報が表示されません。</li>
+<li>ホームボタンと最近使ったアプリボタンが表示されません。</li>
+<li>他のアプリから新しいアクティビティを起動できません。</li>
+<li>現在のアプリからは、新しいタスクが作成されない限り、新しいアクティビティを起動できます。</li>
+<li>画面の固定が端末所有者によって開始されている場合は、アプリから {@link android.app.Activity#stopLockTask() stopLockTask()} を呼び出すまで、ユーザーは引き続きアプリにロックされます。</li>
+<li>画面の固定が端末所有者でない別のアプリによって、またはユーザーによって直接、有効にされている場合には、ユーザーは戻るボタンと最近使ったアプリボタンを同時に押すことで画面の固定を終了できます。</li>
+
+</ul>
+
+<h2 id="Printing">印刷フレームワーク</h2>
+
+<h3 id="PDFRender">PDF をビットマップとしてレンダリング</h3>
+<p>新しい {@link android.graphics.pdf.PdfRenderer} クラスを使用して、PDF ドキュメントのページを印刷用のビットマップ画像にレンダリングできるようになりました。印刷可能なコンテンツの書き込み先となる、シーク可能な(つまり、コンテンツへのランダム アクセスが可能な){@link android.os.ParcelFileDescriptor} を指定する必要があります。アプリでは、{@link android.graphics.pdf.PdfRenderer#openPage(int) openPage()} でレンダリング用のページを取得した後、{@link android.graphics.pdf.PdfRenderer.Page#render(android.graphics.Bitmap, android.graphics.Rect, android.graphics.Matrix, int) render()} を呼び出して、開いている {@link android.graphics.pdf.PdfRenderer.Page} をビットマップに変換できます。また、ドキュメントの一部分だけをビットマップ画像に変換したい場合は追加のパラメータを設定できます(たとえば、ドキュメントを拡大するために<a href="http://en.wikipedia.org/wiki/Tiled_rendering" class="external-link">タイル レンダリング</a>を実装する場合など)。</p>
+
+<p>新しいアプリの使用例については、{@code PdfRendererBasic} のサンプルをご覧ください。</p>
+
+<h2 id="System">システム</h2>
+<h3 id="AppUsageStatistics">アプリの使用統計情報</h3>
+<p>新しい {@link android.app.usage} API を使用して Android 端末のアプリ使用履歴にアクセスできるようになりました。この API では、非推奨となった {@link android.app.ActivityManager#getRecentTasks(int, int) getRecentTasks()} メソッドよりも詳細な使用統計情報が提供されます。この API を使用するには、最初にマニフェストで {@code "android.permission.PACKAGE_USAGE_STATS"} 権限を宣言する必要があります。また、ユーザーも [設定] &gt; [セキュリティ] &gt; [アプリ]<em></em> を通じてこのアプリの使用アクセス権を有効にする必要があります。</p>
+
+<p>使用統計データはアプリ単位で収集され、毎日、毎週、毎月、毎年の各期間でデータが集計されます。データの最長保持期間は次のとおりです。</p>
+
+<ul>
+  <li>毎日のデータ: 7 日間</li>
+  <li>毎週のデータ: 4 週間</li>
+  <li>毎月のデータ: 6 か月</li>
+  <li>毎年のデータ: 2 年</li>
+</ul>
+
+<p>各アプリについて次のデータが記録されます。</p>
+<ul>
+<li>アプリが最後に使用された時間</li>
+<li>対象期間(1 日、1 週間、1 か月、または 1 年)にアプリがフォアグラウンドに存在した合計時間</li>
+<li>1 日の間にコンポーネント(パッケージとアクティビティ名により識別される)がフォアグラウンドからバックグラウンドに移動したときに取得されるタイムスタンプ</li>
+<li>端末の設定が変更されたとき(回転の結果端末の向きが変化したときなど)に取得されるタイムスタンプ</li>
+</ul>
+
+<h2 id="TestingA11y">テストとユーザー補助 </h2>
+
+<h3 id="TestingA11yImprovements">テストとユーザー補助の向上</h3>
+<p>Android 5.0 ではテストとユーザー補助に関する次のサポートが追加されています。</p>
+
+<ul>
+<li>新しい {@link android.app.UiAutomation#getWindowAnimationFrameStats() getWindowAnimationFrameStats()} メソッドと {@link android.app.UiAutomation#getWindowContentFrameStats(int) getWindowContentFrameStats()} メソッドは、ウィンドウのアニメーションとコンテンツに関するフレーム統計情報を取得します。これらのメソッドを使用して計測テストを記述すれば、アプリがスムーズなユーザー エクスペリエンスを提供するのに十分な更新頻度でフレームを描画しているかどうかを評価できます。</li>
+
+<li>新しい {@link android.app.UiAutomation#executeShellCommand(java.lang.String) executeShellCommand()} メソッドを使用すると、計測テストからシェルコマンドを実行できます。コマンドの実行は端末に接続されたホストから {@code adb shell} を実行するのと同様に行え、{@code dumpsys}、{@code am}、{@code content}、{@code pm} などのシェルベースのツールを使用できます。</li>
+
+<li>ユーザー補助 API(<a href="{@docRoot}tools/help/uiautomator/index.html">{@code UiAutomator}</a> など)を使用するユーザー補助のサービスやテストツールで、視覚に障がいのないユーザーが操作できる画面上のウィンドウについて、そのプロパティに関する詳細情報を取得できるようになりました。{@link android.view.accessibility.AccessibilityWindowInfo} オブジェクトのリストを取得するには、新しい {@link android.accessibilityservice.AccessibilityService#getWindows() getWindows()} メソッドを呼び出します。</li>
+
+<li>新しい {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction} クラスを使用すると、{@link android.view.accessibility.AccessibilityNodeInfo} で実行する標準の操作やカスタマイズされた操作を定義できます。新しい {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction} クラスは、従来 {@link android.view.accessibility.AccessibilityNodeInfo} に存在した操作関連の API の代わりとなるものです。</li>
+
+<li>Android 5.0 では、音声合成をよりきめ細かくアプリでコントロールできます。アプリで新しい {@link android.speech.tts.Voice} クラスを使用すると、特定の言語、音質、レイテンシ速度に関連付けられた音声プロフィールや、音声合成エンジン固有のパラメータを使用できます。</li>
+</ul>
+
+<h2 id="IME">IME</h2>
+
+<h3 id="Switching">入力言語の切り替えやすさの向上</h3>
+
+<p>Android 5.0 以降、ユーザーはプラットフォームでサポートされているすべての<a href="{@docRoot}guide/topics/text/creating-input-method.html">インプット メソッド エディタ(IME)</a>を簡単に切り替えられるようになります。指定された切り替え操作(通常はソフト キーボード上の地球アイコンのタップ)を行うと、それらの IME のすべてが循環して切り替わります。この動作変更は {@link android.view.inputmethod.InputMethodManager#shouldOfferSwitchingToNextInputMethod(android.os.IBinder) shouldOfferSwitchingToNextInputMethod()} メソッドによって実装されます。</p>
+
+<p>また、次の IME に切り替え機能が組み込まれているかどうか(つまり、対象の IME が次に続く IME への切り替えをサポートしているかどうか)がチェックされるようになりました。切り替え機能のある IME から切り替え機能のない IME への循環切り替えは行われません。この動作変更は {@link android.view.inputmethod.InputMethodManager#switchToNextInputMethod(android.os.IBinder, boolean) switchToNextInputMethod()} メソッドによって実装されます。
+
+<p>最新の IME 切り替え API の使用例については、このリリースに付属している最新のソフト キーボードの実装サンプルをご覧ください。IME の切り替えを実装する方法について詳しくは、<a href="{@docRoot}guide/topics/text/creating-input-method.html">入力方法の作成</a>をご覧ください。
+</p>
+
+<h2 id="Manifest">マニフェスト宣言</h2>
+
+<h3 id="ManifestFeatures">宣言可能な必須機能</h3>
+<p>次の値が <a href="{@docRoot}guide/topics/manifest/uses-feature-element.html">{@code <uses-feature>}</a> 要素でサポートされるようになりました。これにより、アプリで必要としている機能を備えている端末にのみアプリをインストールできるようになります。</p>
+
+<ul>
+<li>{@link android.content.pm.PackageManager#FEATURE_AUDIO_OUTPUT}</li>
+<li>{@link android.content.pm.PackageManager#FEATURE_CAMERA_CAPABILITY_MANUAL_POST_PROCESSING}</li>
+<li>{@link android.content.pm.PackageManager#FEATURE_CAMERA_CAPABILITY_MANUAL_SENSOR}</li>
+<li>{@link android.content.pm.PackageManager#FEATURE_CAMERA_CAPABILITY_RAW}</li>
+<li>{@link android.content.pm.PackageManager#FEATURE_CAMERA_LEVEL_FULL}</li>
+<li>{@link android.content.pm.PackageManager#FEATURE_GAMEPAD}</li>
+<li>{@link android.content.pm.PackageManager#FEATURE_LIVE_TV}</li>
+<li>{@link android.content.pm.PackageManager#FEATURE_MANAGED_USERS}</li>
+<li>{@link android.content.pm.PackageManager#FEATURE_LEANBACK}</li>
+<li>{@link android.content.pm.PackageManager#FEATURE_OPENGLES_EXTENSION_PACK}</li>
+<li>{@link android.content.pm.PackageManager#FEATURE_SECURELY_REMOVES_USERS}</li>
+<li>{@link android.content.pm.PackageManager#FEATURE_SENSOR_AMBIENT_TEMPERATURE}</li>
+<li>{@link android.content.pm.PackageManager#FEATURE_SENSOR_HEART_RATE_ECG}</li>
+<li>{@link android.content.pm.PackageManager#FEATURE_SENSOR_RELATIVE_HUMIDITY}</li>
+<li>{@link android.content.pm.PackageManager#FEATURE_VERIFIED_BOOT}</li>
+<li>{@link android.content.pm.PackageManager#FEATURE_WEBVIEW}</li>
+</ul>
+
+<h3 id="Permissions">ユーザー権限</h3>
+
+<p>次の権限が <a href="{@docRoot}guide/topics/manifest/uses-permission-element.html">{@code <uses-permission>}</a> 要素でサポートされ、アプリで特定の API へのアクセスに必要な権限を宣言できるようになりました。</p>
+
+<ul>
+<li>{@link android.Manifest.permission#BIND_DREAM_SERVICE}: API レベル 21 以降を対象とする場合は、システムによるバインドのみが可能となるようにするため、<a href="{@docRoot}about/versions/android-4.2.html#Daydream">Daydream</a> サービスでこの権限が必要になります。</li>
+</ul>
\ No newline at end of file
diff --git a/docs/html-intl/intl/zh-cn/about/versions/lollipop.jd b/docs/html-intl/intl/zh-cn/about/versions/lollipop.jd
new file mode 100644
index 0000000..a63c56e
--- /dev/null
+++ b/docs/html-intl/intl/zh-cn/about/versions/lollipop.jd
@@ -0,0 +1,259 @@
+page.title=Android Lollipop
+
+@jd:body
+
+
+
+
+
+
+
+
+
+
+  <div style="padding:0px 0px 0px 20px;float:right;margin:0 -10px 0 0">
+    <img src="{@docRoot}images/home/l-hero_2x.png" srcset="{@docRoot}images/home/l-hero.png 1x, {@docRoot}images/home/l-hero_2x.png 2x" width="460" height="300" >
+  </div>
+
+  <div class="landing-docs" style="float:right;clear:both;margin:68px 0 2em 3em;">
+  <div class="col-4 normal-links highlights" style="font-size:12px;">
+    <h3 id="thisd" >主なデベロッパー機能</h3>
+    <ul style="list-style-type:none;">
+  <li><a href="#Material">Material Design</a></li>
+  <li><a href="#Perf">パフォーマンス重視</a></li>
+  <li><a href="#Notifications">通知</a></li>
+  <li><a href="#TV">アプリを大画面で</a></li>
+  <li><a href="#Documents">アプリのドキュメント化</a></li>
+  <li><a href="#Connectivity">進化した接続性</a></li>
+  <li><a href="#Graphics">高性能グラフィックス</a></li>
+  <li><a href="#Audio">さらに強化された音声</a></li>
+  <li><a href="#Camera">カメラと動画の拡張</a></li>
+  <li><a href="#Work">職場での Android</a></li>
+  <li><a href="#ScreenCapture">画面のキャプチャと共有</a></li>
+  <li><a href="#Sensors">新しいタイプのセンサー</a></li>
+  <li><a href="#WebView">Chromium WebView</a></li>
+  <li><a href="#Accessibility">ユーザー補助機能と入力</a></li>
+  <li><a href="#Battery">省電力アプリ用のツール</a></li>
+    </ul>
+  </div>
+</div>
+
+
+
+
+
+
+
+<p>Android 5.0 Lollipop は、これまでの Android の中で最も大きく刷新された意欲的なリリースです。</p>
+
+<p>このリリースには、ユーザー向けの新しい機能だけでなく、デベロッパー向けに数多くの API が追加されています。これらの API を使用することで、携帯電話、タブレット、ウェアラブルに加え、テレビや自動車用にも Android を拡張できます。</p>
+
+<p>新しいデベロッパー向け API の概要については、<a href="{@docRoot}about/versions/android-5.0.html">Android 5.0 API</a> をご覧ください。Android 5.0 のユーザー向けの新機能については <a href="http://www.android.com/versions/lollipop-5-0/">www.android.com</a> をご覧ください。</p>
+
+
+
+<p style="    padding: 10px;    background: #eee;    width: 250px;    border: 1px solid #ccc;    margin-top: 20px;">アプリを実機でテストするには、<br><a href="/preview/index.html#Start"><b>Android プレビュー システム イメージ</b></a>を使用して Nexus 5 または Nexus 7 のフラッシュに書き込みます。</p>
+
+
+<h2 id="Material">Material Design</h2>
+
+<p>Android 5.0 では新たに <a href="http://www.google.com/design/spec">Material Design</a> が導入され、拡張 UI ツールキットを使用することで新しいデザイン パターンをアプリに簡単に統合できるようになりました。  </p>
+
+
+
+<p>新しい <strong>3D ビュー</strong>機能では、z レベルを設定することでビュー階層から要素を持ち上げることができるようになりました。また、<strong>リアルタイム シャドウ</strong>機能により、要素が移動しても影を追随させることができます。</p>
+
+
+<p><strong>アクティビティ遷移</strong>機能が組み込まれたことで、美しいアニメーションを使って次の状態へスムーズに遷移できるようになりました。マテリアル テーマを使用すると、アクティビティの遷移がさらにスムーズになります。たとえば、アクティビティ間で<strong>共有視覚要素</strong>を使用できるようになりました。</p>
+
+
+
+<div style="width:290px;margin-right:35px;float:left">
+  <div class="framed-nexus5-port-span-5">
+  <video class="play-on-hover" autoplay="">
+    <source src="/design/material/videos/ContactsAnim.mp4">
+    <source src="/design/videos/ContactsAnim.webm">
+    <source src="/design/videos/ContactsAnim.ogv">
+  </video>
+  </div>
+  <div style="font-size:10pt;margin-left:20px;margin-bottom:30px">
+    <em></em>端末画面をクリックすると動作が再現されます
+  </div>
+</div>
+
+
+<p>アプリ内のタップ コントロール(ボタン、チェックボックスなど)にリップル アニメーションを適用できます。
+
+<p>XML でベクター ドローアブルを定義して、さまざまな方法でアニメーション化することも可能です。ベクター ドローアブルは定義を変更することなく拡大縮小できるため、単色のアプリ内アイコンに最適です。</p>
+
+<p>システム管理の処理スレッドとして <strong>RenderThread</strong> が追加されており、メインの UI スレッドに遅延が発生してもアニメーションをスムーズに再生できます。 </p>
+
+
+<h2 id="Perf">パフォーマンス重視</h2>
+
+<p>Android 5.0 では、より高速でスムーズな演算処理が実現します。</p>
+
+<p>新しい <strong>ART ランタイム</strong>に完全に移行しました。ART は基礎から徹底的に再構築したランタイムで、AOT(ahead-of-time)、JIT(just-in-time)、インタプリタ型のコードをまとめて処理できます。ARM、x86、MIPS アーキテクチャに対応した 64 ビット完全互換のランタイムです。</p>
+
+<p>ART は、アプリのパフォーマンスと応答性を改善します。ガベージ コレクションを効率的することで、GC イベントによる一時停止の回数や時間を減らしました。垂直同期の期間にうまく適合するようになったことで、アプリがフレームをスキップすることもなくなります。また、メモリを動的に移動してフォアグラウンドのパフォーマンスも最適化しています。 </p>
+
+<p>Android 5.0 では、Nexus 9 の NVIDIA Tegra K1 で採用されている<strong>64 ビット アーキテクチャ</strong>のプラットフォーム サポートを導入しています。最適化によって広いアドレス空間が提供され、特定の演算負荷の処理能力が向上しました。Java で記述されているアプリは、修正なしで自動的に 64 ビット アプリとして実行されます。ネイティブ コードを使用するアプリのためには、ARM v8、x86-64、MIPS-64 用の新しい ABI をサポートするため NDK を拡張しました。</p>
+
+<p>Android 5.0 では、パフォーマンスをさらに向上させるため、A/V 同期についても改善に取り組みました。音声と映像のパイプラインを搭載したことでタイムスタンプの正確性が向上し、動画アプリやゲームのコンテンツの同期がスムーズに行われるようになりました。</p>
+
+
+<h2 id="Notifications">通知</h2>
+
+<p>Android 5.0 の通知は、より見やすく、アクセスしやすく、カスタマイズしやすくなりました。 </p>
+
+<img src="{@docRoot}images/versions/notification-headsup.png" style="float:right; margin:0 0 40px 60px" width="300" height="224" />
+
+<p>さまざまな通知の詳細を<strong>ロック画面</strong>に表示できるようになり、通知内容をすべて表示するか、一部だけ表示するか、表示しないかをユーザーが選択できます。 </p>
+
+<p>着信などの重要な通知は<strong>警告通知</strong>と呼ばれるフローティング ウィンドウに表示されるため、使用中のアプリを表示したまま応答したり破棄したりできます。</p>
+
+<p>Android 5.0 では、通知に<strong>新しいメタデータ</strong>を追加して、関連付けられている連絡先(ランク付け用)、カテゴリ、優先度を収集できるようになりました。</p>
+
+<p>新たに追加されたメディア通知テンプレートを使用すると、最大 6 つの操作ボタンを使用して、一貫性のあるメディア コントロール(例: 「いいね」や「+1」)を通知に追加できます。RemoteViews はもう必要ありません。</p>
+
+
+
+<h2 id="TV">アプリを大画面で</h2>
+
+<p><a href="http://developer.android.com/tv/index.html">Android TV</a> は、アプリを大画面で楽しむためのテレビ用プラットフォームを提供します。シンプルなホーム画面をベースに構成されており、ユーザーへのおすすめや音声検索によってコンテンツを簡単に見つけることができます。</p>
+
+<p>Android TV なら、アプリやゲームを<strong>迫力の大画面</strong>で楽しむことができ、ゲーム コントローラなどの入力デバイスにも対応しています。Android では <a href="{@docRoot}tools/support-library/features.html#v17-leanback">v17 サポート ライブラリ</a>に <strong>Leanback UI フレームワーク</strong>が用意されており、離れた場所からでも操作しやすい 10 フィート UI を簡単に実現できます。</p>
+
+<p><strong>Android TV Input Framework</strong>(TIF)を使用すると、HDMI 入力、テレビチューナー、IPTV 対応受信機などからの動画ストリームを TV アプリで処理できます。TV Input が公開しているメタデータを使用することで、放送中のテレビ番組を検索できるようにしたり、ユーザーへのおすすめを表示したりできます。また、HDMI-CEC コントロール サービスを使用して、複数のデバイスを 1 つのリモコンで操作できるようにすることも可能です。 </p>
+
+<p>TV Input フレームワークを使用すると、さまざまな入力ソースからのテレビ番組をまとめ、ユーザーが 1 つのインターフェースで閲覧したり視聴したりできるようにすることができます。提供するコンテンツの TV Input サービスを作成することで、ユーザーが TV デバイスでコンテンツを視聴する可能性を高めることができます。</p>
+
+
+
+<img src="{@docRoot}images/versions/recents_screen_2x.png" srcset="{@docRoot}images/versions/recents_screen.png 1x, {@docRoot}images/versions/recents_screen_2x.png 2x" style="float:right; margin:0 0 40px 60px" width="300" height="521" />
+
+<h2 id="Documents">アプリのドキュメント化</h2>
+
+<p>Android 5.0 では、[概要](以前は [最近])のスペースをデザインしなおして用途を広げ、マルチタスクにも使えるようにしました。</p>
+
+<p>新しい API を使用することで、アプリ内の各アクティビティを個別のドキュメントとして、他の最近の画面と並べて表示できます。</p>
+
+<p>これにより、ユーザーがコンテンツやサービスにすばやくアクセスできるようになります。たとえば、生産性アプリのファイル、ゲームでのプレーヤーの組み合わせ、メッセージ アプリのチャットなどを、別々のドキュメントにして同時に表示できます。 </p>
+
+
+
+<h2 id="Connectivity">進化した接続性</h2>
+
+<p>Android 5.0 に追加された新しい API を使用すると、アプリと <strong>BLE</strong>(Bluetooth Low Energy)の同時処理が可能になり、スキャン(セントラル モード)とアドバタイズ(ペリフェラル モード)の両方を行うことができます。</p>
+
+<p>新しい<strong>マルチネットワーキング</strong>機能により、アプリから特定の機能を利用できるネットワーク(Wi-Fi、モバイル、従量制、特定のネットワーク機能を提供するネットワークなど)を探せるようになりました。ネットワークが見つかったら、アプリから接続を要求したり、切断やネットワーク変更に対して応答したりできます。</p>
+
+<p><strong>NFC</strong> API では、アプリから NFC AID(Application ID)を動的に登録できるようになりました。また、アクティブなサービスごとに望ましいカード エミュレーション サービスを設定したり、UTF-8 テキストデータを格納する NDEF レコードを作成したりすることも可能になりました。</p>
+
+
+
+<h2 id="Graphics">高性能グラフィックス</h2>
+
+<p><strong><a href="http://www.khronos.org/opengles/3_X/">Khronos OpenGL ES 3.1</a></strong> をサポートしたことで、対応デバイスで実行するゲームやアプリに高性能な 2D / 3D グラフィックス機能を利用できるようになりました。 </p>
+
+<p>OpenGL ES 3.1 により、コンピュート シェーダー、ステンシル テクスチャ、アクセラレータによる視覚効果、高品質 ETC2/EAC テクスチャ圧縮、高度なテクスチャ レンダリング、標準化されたテクスチャ サイズ、レンダー バッファ フォーマットなど、さまざまな機能が追加されました。</p>
+
+
+<div class="figure" style="width:350px; margin:0 0 0 60px">
+<img src="{@docRoot}images/versions/rivalknights.png" style="float:right;" width="350" height="525" />
+<p class="img-caption">Gameloft の Rival Knights では、AEP の ASTC(Adaptive Scalable Texture Compression)と ES 3.1 のコンピュート シェーダーを使用して、ハイ ダイナミック レンジ(HDR)によるブルーム効果や詳細なグラフィックスを実現しています。</p>
+</div>
+
+<p>Android 5.0 には OpenGL ES 拡張をまとめた <strong>Android Extension Pack</strong>(AEP)も追加されており、テッセレーション シェーダー、ジオメトリ シェーダー、ASTC テクスチャ圧縮、サンプル単位での補間とシェーディングなど、高度なレンダリング機能を使用できます。AEP を使用することで、GPU を無駄なく利用して高性能なグラフィックスを実現できます。</p>
+
+
+<h2 id="Audio">さらに強化された音声</h2>
+
+<p>新しい音声キャプチャ デザインにより、<strong>音声入力の待ち時間が短縮</strong>されました。新しいデザインには、ブロックが読み込み中にしか発生しない高速キャプチャ スレッド、ネイティブ サンプル レートでの高速トラック キャプチャ クライアント、チャンネル カウント、ビット深度、リサンプリングを提供する通常のキャプチャ クライアント、チャンネルのアップミックスとダウンミックス、ビット深度の調整などが含まれています。</p>
+
+<p>マルチチャンネルの<strong>音声ストリーム ミキシング</strong>を使うと、5.1 や 7.1 を含め最大 8 つのチャンネルをミキシングできる本格的なオーディオ アプリを実現できます。</p>
+
+<p>アプリでは、メディア コンテンツを公開したり、他のアプリが公開している<strong>メディアを閲覧</strong>したり、その再生をリクエストしたりできます。コンテンツはクエリ可能なインターフェースで公開されるため、端末に保持する必要はありません。</p>
+
+<p>また、特定のロケール、音質、レイテンシ速度に関連付けられた音声プロフィールを使うことで、<strong>合成音声によるテキスト読み上げ</strong>をきめ細かくコントロールできます。新しい API では、合成エラーチェック、ネットワーク合成、言語検出、ネットワーク フォールバックなどへの対応も進めました。</p>
+
+<p>Android が標準の <strong>USB オーディオ</strong>機器にも対応したことで、USB ヘッドセット、スピーカー、マイクなどの高性能デジタル機器も使用できるようになりました。Android 5.0 には、<strong>Opus</strong> 音声コーデックのサポートも追加されています。</p>
+
+<p>メディア再生をコントロールするための新しい <strong>{@link android.media.session.MediaSession}</strong> API を使用することで、複数の画面にわたって一貫性のあるメディア コントロールや各種コントローラを簡単に表示できるようになりました。</p>
+
+
+<h2 id="Camera">カメラと動画の拡張</h2>
+
+<p>Android 5.0 では<strong>カメラ用の API</strong> が一新されており、YUV、Bayer RAW などの未加工フォーマットのキャプチャや、露出時間、ISO 感度、フレーム単位のフレーム持続時間などのパラメータ調整が可能です。カメラ パイプラインが完全に同期するようになったことで、対応デバイスを使えば未圧縮のフル解像度 YUV イメージを 30 FPS でキャプチャできます。</p>
+
+<p>イメージと一緒に、ノイズモデルや光情報などのメタデータをカメラからキャプチャすることもできます。</p>
+
+<p>ネットワーク経由で動画ストリームを送信するアプリでは、H.265 <strong>HEVC(High Efficiency Video Coding)</strong>を利用して動画データのエンコードとデコードを最適化できるようになりました。 </p>
+
+<p>Android 5.0 には<strong>マルチメディア トンネリング</strong>のサポートも追加されており、超高解像度(4K)のコンテンツを楽しむための最適な環境を提供するほか、圧縮された音声データと動画データを一緒に再生することも可能です。 </p>
+
+
+
+<div class="figure" style="width:320px; margin:1em 0 0 20px;padding-left:2em;">
+<img style="float:right; margin:0 1em 1em 2em"    src="{@docRoot}images/android-5.0/managed_apps_launcher@2x.png"    srcset="{@docRoot}images/android-5.0/managed_apps_launcher@2x.png 2x"    alt="" width="300"  />
+<p class="img-caption">個人用と仕事用のアプリをまとめて表示し、バッジで簡単に識別できます。</p>
+</div>
+
+
+<h2 id="Work">職場での Android</h2>
+
+<p>企業環境において BYOD(Bring-Your-Own-Device)を実現するには、<a href="{@docRoot}about/versions/android-5.0.html#Enterprise">管理されたプロビジョニング手続き</a>によって端末の安全なワーク プロフィールを作成する必要があります。ランチャー内のアプリにワークバッジが表示されている場合は、そのアプリがワーク プロフィール内で IT 管理者によって管理されていることを表します。</p>
+
+<p>通知の表示は個人プロフィールとワーク プロフィールが 1 つに統合されていますが、データはプロフィール別に分けて管理されます。同じアプリを両方のプロフィールで使用しても、それぞれのデータは別々に保管されます。</p>
+
+<p>会社所有の端末は、IT 管理者が新しい端末として登録して<a href="{@docRoot}about/versions/android-5.0.html#DeviceOwner">端末所有者</a>を設定します。会社所有の端末にグローバル端末設定を定義する端末所有者アプリをインストールすることで、従業員はその端末を持ち出すことができるようになります。</p>
+
+
+
+<h2 id="ScreenCapture">画面のキャプチャと共有</h2>
+
+<p>Android 5.0 では、アプリに画面キャプチャや画面共有の機能を追加できます。 </p>
+
+<p>ユーザーの承諾が得られれば、画面から安全性の低い動画をキャプチャしてネットワークに配信することも可能です。</p>
+
+
+<h2 id="Sensors">新しいタイプのセンサー</h2>
+
+<p>Android 5.0 には、新たに<strong>傾き検出</strong>センサーが追加され、対応デバイスでの操作の認識精度が向上しました。また、<strong>心拍数センサー</strong>も追加され、デバイスに接触している人の心拍数を記録できるようになりました。 </p>
+
+<p>新しい<strong>操作複合センサー</strong>を使用すると、「ウェイクアップ」(オンにする)ジェスチャー、「ピックアップ」(手に取る)ジェスチャー、「グランス」(ちらりと見る)ジェスチャーなどの特殊な操作を検出できます。<em></em><em></em><em></em></p>
+
+
+
+<h2 id="WebView">Chromium WebView</h2>
+
+<div style="float:right;margin:1em 2em 1em 2em;">
+  <img src="/images/kk-chromium-icon.png" alt="" height="160" style="margin-bottom:0em;">
+</div>
+
+<p>Android 5.0 の初期リリースには、Chromium M37 リリースをベースとした Chromium for {@link android.webkit.WebView} が含まれており、これにより <strong>WebRTC</strong>、<strong>WebAudio</strong>、<strong>WebGL</strong> がサポートされます。 </p>
+
+<p>Chromium M37 には、すべての <strong>Web Components</strong> 仕様(Custom Elements、Shadow DOM、HTML Imports、Templates)のネイティブ サポートも含まれています。つまり、<a href="http://polymer-project.org/">Polymer</a> とその <a href="https://www.polymer-project.org/docs/elements/material.html">Material Design 要素</a>を WebView で、Polyfill なしで使用できるということです。</p>
+
+<p>Android 4.4 以降の WebView は Chromium をベースにしていますが、Chromium レイヤは今後は Google Play から更新できるようになります。</p>
+
+<p>Chromium の新バージョンがリリースされたとき、Android 5.0 以上で WebView を使用しているアプリのウェブ API の更新やバグ修正がある場合は、ユーザーが Google Play から更新することで WebView の最新の拡張とバグ修正を確実に適用できます。</p>
+
+
+
+<h2 id="Accessibility">ユーザー補助機能と入力</h2>
+
+<p>新しいユーザー補助機能 API を使用すると、視覚に障がいのないユーザーが操作できる画面上のウィンドウのプロパティに関する詳しい情報を取得し、UI 要素の標準入力操作とカスタム入力操作を定義できます。</p>
+
+<p>新しい IME(Input Method Editor)API を使用すると、入力方法から直接他の IME に切り替えることができます。</p>
+
+
+
+<h2 id="Battery">省電力アプリ用のツール</h2>
+
+<p>新しい<strong>ジョブ スケジューリング</strong> API を使用すると、システムのジョブの実行を延期することで電池消費量を最適化できます。延期したジョブは、後で実行するように指定したり、特定の条件(充電中、Wi-Fi 接続時など)を満たしたときに実行したりできます。</p>
+
+<p>新たに追加された <code>dumpsys batterystats</code> コマンドで<strong>電池の使用統計情報</strong>を生成すると、システム全体での電池使用状況や、アプリが電池使用量にどの程度影響しているかを理解できます。電池が消費されたイベントの履歴、UID やシステム コンポーネントごとのおおよその消費電力量なども把握できます。</p>
+
+<img src="{@docRoot}images/versions/battery_historian.png" srcset="{@docRoot}images/versions/battery_historian@2x.png 2x" alt="" width="760" height="462"  />
+<p class="img-caption">新しい電池履歴ツールを使用すると、<code>dumpsys batterystats</code> で生成した統計情報を視覚化でき、電池関連のデバッグに便利です。このツールは <a href="https://github.com/google/battery-historian">https://github.com/google/battery-historian</a> から入手できます。</p>
diff --git a/docs/html-intl/intl/zh-cn/design/style/writing.jd b/docs/html-intl/intl/zh-cn/design/style/writing.jd
new file mode 100644
index 0000000..7944c24
--- /dev/null
+++ b/docs/html-intl/intl/zh-cn/design/style/writing.jd
@@ -0,0 +1,311 @@
+page.title=写作风格
+page.type=设计
+
+@jd:body
+
+  <h2 id="voa">Android 的语言风格</h2>
+
+  <p>撰写应用中显示的文本时,应力求简明、简易和友好。</p>
+
+  <h4 id="concise">简明</h4>
+
+  <ul>
+    <li>仅告知用户最必要的信息。</li>
+    <li>避免冗余的表述,例如标题重复陈述信息框中的正文内容。</li>
+    <li>尽可能缩短文本长度。</li>
+  </ul>
+
+  <p><em>避免使用啰嗦、生硬的文字</em></p>
+
+  <div class="layout-content-row">
+    <div class="layout-content-col span-6 layout-with-list-item-margins">
+
+      <div class="do-dont-label bad">错误做法</div>
+
+      <table class="ui-table good"><tbody><tr><td>
+      如需更详细的说明,请参考手机随附的文档。
+      </td></tr></tbody></table>
+
+    </div>
+    <div class="layout-content-col span-6">
+
+      <div class="do-dont-label good">正确做法</div>
+
+      <table class="ui-table good"><tbody><tr><td>
+      请阅读手机随附的说明。
+      </td></tr></tbody></table>
+
+    </div>
+  </div>
+
+  <p><em>请勿提供无关信息</em></p>
+
+  <div class="layout-content-row">
+    <div class="layout-content-col span-6 layout-with-list-item-margins">
+
+      <div class="do-dont-label bad">从“设置向导”屏幕</div>
+
+      <table class="ui-table bad">
+      <thead>
+        <tr>
+          <th>
+          登录…
+          </th>
+        </tr>
+      </thead>
+      <tbody>
+        <tr>
+          <td>
+          你的手机需要与 Google 服务器通信<br />
+          然后才能登录到你的帐户。<br />
+          此过程最多可能需要 5 分钟。
+          </td>
+        </tr>
+      </tbody>
+      </table>
+
+    </div>
+    <div class="layout-content-col span-6">
+
+      <div class="do-dont-label good">从“设置向导”屏幕</div>
+
+      <table class="ui-table good">
+      <thead>
+        <tr>
+          <th>
+          登录…
+          </th>
+        </tr>
+      </thead>
+      <tbody>
+        <tr>
+          <td>
+          你的手机正在连接 Google。<br />
+          此过程最多可能需要 5 分钟。
+          </td>
+        </tr>
+      </tbody>
+      </table>
+
+    </div>
+  </div>
+
+  <h4 id="simple">简易</h4>
+
+  <ul>
+    <li>使用简短字词、主动动词和常用名词。</li>
+    <li>最重要的内容放在最前面。用句首的几个字词来传达全句最重要的信息。</li>
+    <li>不要费力解释细枝末节,大部分用户并不关心这些。</li>
+  </ul>
+
+  <p><em>关注用户的兴趣点,而不是技术细节</em></p>
+
+  <div class="layout-content-row">
+    <div class="layout-content-col span-6 layout-with-list-item-margins">
+
+      <div class="do-dont-label bad">错误做法</div>
+
+      <table class="ui-table good"><tbody><tr><td>
+      手动控制 GPS,以防止其他应用使用它
+      </td></tr></tbody></table>
+
+    </div>
+    <div class="layout-content-col span-6">
+
+      <div class="do-dont-label good">正确做法</div>
+
+      <table class="ui-table good"><tbody><tr><td>
+      若想省电,请将“位置信息模式”切换为“耗电量低”
+      </td></tr></tbody></table>
+
+    </div>
+  </div>
+
+  <p><em>将重要信息放在最前面</em></p>
+
+  <div class="layout-content-row">
+    <div class="layout-content-col span-6 layout-with-list-item-margins">
+
+      <div class="do-dont-label bad">错误做法</div>
+
+      <table class="ui-table good"><tbody><tr><td>
+      其他 77 人为此 +1 了,包括张三
+      </td></tr></tbody></table>
+
+    </div>
+    <div class="layout-content-col span-6">
+
+      <div class="do-dont-label good">正确做法</div>
+
+      <table class="ui-table good"><tbody><tr><td>
+      张三及其他 76 人为此 +1 了
+      </td></tr></tbody></table>
+
+    </div>
+  </div>
+
+  <p><em>将用户目标放在最前面</em></p>
+
+  <div class="layout-content-row">
+    <div class="layout-content-col span-6 layout-with-list-item-margins">
+
+      <div class="do-dont-label bad">错误做法</div>
+
+      <table class="ui-table good"><tbody><tr><td>
+      触摸“下一步”,以完成 Wi-Fi 连接设置
+      </td></tr></tbody></table>
+
+    </div>
+    <div class="layout-content-col span-6">
+
+      <div class="do-dont-label good">正确做法</div>
+
+      <table class="ui-table good"><tbody><tr><td>
+      要完成 Wi-Fi 连接设置,请触摸“下一步”
+      </td></tr></tbody></table>
+
+    </div>
+  </div>
+
+
+  <h4 id="friendly">友好</h4>
+
+  <ul>
+    <li>使用缩写词。</li>
+    <li>使用“您”或“你”直接与读者对话。</li>
+    <li>语气应轻松自然,但要避免使用俚语。</li>
+  
+  </ul>
+
+  <p><em>避免使用令人困惑或令人厌烦的表达</em></p>
+  <div class="layout-content-row">
+    <div class="layout-content-col span-6 layout-with-list-item-margins">
+      <div class="do-dont-label bad">错误做法</div>
+      <table class="ui-table bad">
+      <thead>
+        <tr>
+          <th>
+          抱歉!
+          </th>
+        </tr>
+      </thead>
+      <tbody>
+        <tr>
+          <td>
+          “MyAppActivity”活动<br />
+          (在应用“MyApp”中)无响应
+          </td>
+        </tr>
+      </tbody>
+      </table>
+
+    </div>
+    <div class="layout-content-col span-6">
+      <div class="do-dont-label good">正确做法</div>
+      <table class="ui-table good">
+      <thead>
+        <tr>
+          <th>
+          MyApp 无响应
+          </th>
+        </tr>
+      </thead>
+      <tbody>
+        <tr>
+          <td>
+          是否将其关闭?
+          </td>
+        </tr>
+      </tbody>
+      </table>
+    </div>
+  </div>
+
+
+  <h4>应避免使用的字词</h4>
+
+  <div style="padding:5px 2.1em">
+  <table>
+    <tr>
+      <td class="do-dont-label bad" style="width:40%">不使用</td>
+      <td class="do-dont-label good" style="width:40%">使用</td>
+    </tr>
+    <tr>
+      <td>一、二、三、四…</td>
+      <td>1、2、3、4…</td>
+    </tr>
+    <tr>
+      <td>应用程序</td>
+      <td>应用</td>
+    </tr>
+    <tr>
+      <td>cannot、could not、do not、did not、will not、you will</td>
+      <td><em></em>缩写词:can’t、couldn’t、don’t、didn’t、won’t、you’ll 等等</td>
+    </tr>
+      <tr>
+      <td>okay、ok</td>
+      <td>OK</td>
+    </tr>
+    <tr>
+      <td>请、抱歉、谢谢</td>
+      <td><em>故作礼貌可能会令用户生厌,特别是在提示“出错”的消息中。<br />
+          例外情况:在日语中,必须使用“请”。所以,祈使动词均应据此进行本地化(打开 -&gt; 请打开)。
+          </em></td>
+    </tr>
+      <tr>
+      <td>there is、there are、it is<br />
+          <em>以及其他“无主语”表述(语法上称为虚指)</em></td>
+      <td><em>使用名词作为主语</em></td>
+    </tr>
+    <tr>
+      <td>abort、kill、terminate</td>
+      <td>stop、cancel、end、exit</td>
+    </tr>
+      <tr>
+      <td>fail、failed 等“否定用法”<em></em></td>
+      <td><em>一般情况下,请使用肯定用语<br />
+          (例如,使用“do”而非“don’t”,除非在“不再显示…”、“无法连接”等情况下。)</em></td>
+    </tr>
+    <tr>
+      <td>me、I、my、mine</td>
+      <td>you、your、yours</td>
+    </tr>
+      <tr>
+      <td>确定吗?警告!</td>
+      <td><em>应告诉用户后果,例如,“你的所有照片和媒体内容都将丢失”</em></td>
+    </tr>
+  </table>
+
+  </div>
+
+  <h2 id="formatting_text">设置文本格式</h2>
+
+  <h4 id="capitalization">大写</h4>
+
+  <ul>
+    <li>所有界面字符串都使用句子样式的大写:“Words to live by”。</li>
+    <li>大写以下各项中的所有重要字词:
+      <ul>
+        <li>应用名称(Calendar、Google Drive)</li>
+        <li>正式功能的名称(Android Beam、Face Unlock)</li>
+        <li>专有名词(Statue of Liberty、San Francisco Giants)</li>
+      </ul>
+    </li>
+    <li>避免滥用大写。对于不属于正式功能名称中的字词,请不要使用大写:
+      <ul>
+        <li>Sim card lock、Home screen;而非 Sim Card Lock、Home Screen。</li>
+      </ul>
+    </li>
+  </ul>
+
+
+  <h4 id="punctuation">标点符号</h4>
+  <ul>
+    <li><strong>句号。</strong> 只使用单个句子或短语时(如在问候语、标签或通知中),句尾无需加句号。但如果结合使用两句或更多句,则每一句的句尾都需加句号。</li>
+    <li><strong>省略号。</strong> 省略号 (…)(MacOS 中的 Option-; 和 HTML 中的 &amp;hellip;)用于表示:
+      <ul>
+        <li>未完成的状态,例如操作正在进行(“正在下载…”)或文本未完全显示。</li>
+        <li>需要用户在随后显示的界面中做出重要选择的菜单项(例如“打印…”或“共享…”)。例外情况:如果命令的措辞已经表明需要进一步的界面操作(但有限制范围),则无需使用省略号,例如<strong>在网页中查找</strong>或<strong>选择日期</strong>。</li>
+      </ul>
+    </li>
+  </ul>
diff --git a/docs/html-intl/intl/zh-cn/distribute/essentials/index.jd b/docs/html-intl/intl/zh-cn/distribute/essentials/index.jd
new file mode 100644
index 0000000..26a61dc
--- /dev/null
+++ b/docs/html-intl/intl/zh-cn/distribute/essentials/index.jd
@@ -0,0 +1,34 @@
+page.title=Essentials for a Successful App
+meta.tags="landing, quality"
+page.tags="guidelines", "tablet", "quality"
+section.landing=true
+nonavpage=true
+
+@jd:body
+
+<p>
+  A focus on quality should be part of your entire app delivery process: from
+  initial concept through app and UI design, coding and testing and onto a
+  process of monitoring feedback and making improvement after launch.
+</p>
+
+<div class="dynamic-grid">
+<div class="resource-widget resource-flow-layout landing col-16"
+  data-query="collection:distribute/essentials/zhcn"
+  data-cardSizes="6x6"
+  data-maxResults="6">
+</div>
+
+<h3>Related resources</h3>
+
+<div class="resource-widget resource-flow-layout col-16"
+  data-query="type:blog+tag:quality"
+  data-cardSizes="6x3"
+  data-maxResults="3">
+</div>
+<div class="resource-widget resource-flow-layout col-16"
+  data-query="type:youtube+tag:appquality"
+  data-cardSizes="6x3"
+  data-maxResults="3">
+</div>
+</div>
\ No newline at end of file
diff --git a/docs/html-intl/intl/zh-cn/distribute/essentials/quality/core.jd b/docs/html-intl/intl/zh-cn/distribute/essentials/quality/core.jd
new file mode 100644
index 0000000..61598fd
--- /dev/null
+++ b/docs/html-intl/intl/zh-cn/distribute/essentials/quality/core.jd
@@ -0,0 +1,998 @@
+page.title=应用的核心质量
+page.image=/distribute/images/core-quality-guidelines.jpg
+page.type="分发"
+page.metaDescription=应用程序质量的好坏将直接影响到你的应用程序的长期成功 - 这包括安装,用户评分和评论,参与和用户保留方面。
+@jd:body
+
+      <div id="qv-wrapper"><div id="qv">
+  <h2>质量标准</h2>
+    <ol>
+      <li><a href="#ux">设计和互动</a></li>
+          <li><a href="#fn">功能</a></li>
+          <li><a href="#ps">性能和稳定性</a></li>
+          <li><a href="#listing">Google Play</a></li>
+
+    </ol>
+
+    <h2>测试</h2>
+    <ol>
+      <li><a href="#test-environment">设置测试环境</a></li>
+          <li><a href="#tests">测试流程</a></li>
+          </ol>
+
+    <h2>你还应阅读以下内容:</h2>
+    <ol>
+      <li><a href="{@docRoot}distribute/essentials/quality/tablets.html">平板电脑应用的质量</a></li>
+          <li><a href="{@docRoot}distribute/essentials/optimizing-your-app.html">优化你的应用</a></li>
+    </ol>
+
+
+  </div>
+  </div>
+
+  <div class="top-right-float">
+    <img src="{@docRoot}images/gp-core-quality.png" style="margin-left:20px" />
+  </div>
+
+  <p>
+    Android 用户期望获得高品质的应用。你的应用要获得长期成功(体现在安装量、用户评分和评论、参与度和用户留存等方面),应用质量起着直接的作用。
+  </p>
+
+  <p>
+    本文介绍一组简明的核心应用质量标准及相关的测试,旨在帮助你评估应用质量的基本要素。所有 Android 应用均应满足这些标准。
+  </p>
+
+  <p>
+    你在发布应用之前,请根据这些标准对应用进行测试,确保应用能在众多设备上正常运行,满足 Android 的导航和设计标准,并为在 Google Play 商店开展推广做好准备。当然你的测试范围远不止这里所介绍的内容,不过本文的目的是说明所有应用都应具备的基本质量特征,因此你可以在测试计划中纳入这些内容。
+  </p>
+
+  <p>
+    如果你要为平板电脑和/或 Google Play for Education 创建应用,还应考虑<a href="{@docRoot}distribute/essentials/quality/tablets.html">平板电脑应用的质量</a>指南和 <a href="{@docRoot}distribute/essentials/gpfe-guidelines.html">Education 指南</a>中规定的其他质量标准。
+  </p>
+
+  <div class="headerLine">
+    <h2 id="ux">
+    视觉设计和用户互动
+    </h2>
+
+
+  </div>
+
+  <p>
+    遵循这些标准能够确保你的应用具备标准的 Android 视觉设计和互动模式(如果适用),从而提供一致、直观的用户体验。
+  </p>
+
+  <table>
+    <tr>
+      <th style="width:2px">
+        领域
+      </th>
+      <th style="width:54px">
+        ID
+      </th>
+
+
+      <th>
+        说明
+      </th>
+      <th style="width:54px">
+        测试
+      </th>
+    </tr>
+    <tr id="UX-B1">
+    <td>标准设计</td>
+    <td>
+      UX-B1
+    </td>
+    <td>
+      <p style="margin-bottom:.5em">
+      应用遵循 <a href="{@docRoot}design/index.html">Android 设计</a>指南,采用常见的<a href="{@docRoot}design/patterns/index.html">界面模式和图标</a>:
+      </p>
+
+      <ol style="margin-bottom:.5em;list-style-type:lower-alpha">
+      <li>应用不会重新定义系统图标的预期功能(例如“返回”按钮)。
+      </li>
+
+      <li>如果系统图标触发的是标准界面行为,则应用不会使用完全不同的图标替换该系统图标。
+      </li>
+
+      <li>如果应用提供标准系统图标的自定义版本,那么该图标要与系统图标十分相似并能够触发标准系统行为。
+      </li>
+
+      <li>应用不会重新定义或滥用 Android 界面模式,例如有误导性或令用户困惑的图标或行为。
+      </li>
+      </ol>
+    </td>
+    <td>
+      <a href="#core">CR 全部</a>
+    </td>
+    </tr>
+
+    <tr>
+    <td rowspan="3">
+      导航
+    </td>
+    <td id="UX-N1">
+      UX-N1
+    </td>
+    <td>
+      <p>
+      应用支持标准的系统<a href="{@docRoot}design/patterns/navigation.html">“返回”按钮导航</a>,不会在屏幕上使用任何自定义的“返回按钮”提示。
+      </p>
+    </td>
+    <td>
+      <a href="#core">CR-3</a>
+    </td>
+    </tr>
+
+    <tr>
+    <td id="UX-N2">
+      UX-N2
+    </td>
+    <td>
+      <p>
+      所有对话框均可以使用“返回”按钮关闭。
+      </p>
+    </td>
+    <td>
+      <a href="#core">CR-3</a>
+    </td>
+    </tr>
+
+    <tr id="UX-N3">
+    <td>
+      UX-N3
+    </td>
+    <td>
+      在任何位置按下“主屏幕”按钮即可转到设备的主屏幕。
+    </td>
+    <td>
+      <a href="#core">CR-1</a>
+    </td>
+    </tr>
+
+    <tr id="UX-S1">
+    <td rowspan="2">
+      通知
+    </td>
+    <td>
+      UX-S1
+    </td>
+    <td>
+      <p style="margin-bottom:.5em">
+      通知遵循 Android 设计<a href="{@docRoot}design/patterns/notifications.html">指南</a>。尤其是:</p>
+
+      <ol style="margin-bottom:.5em;list-style-type:lower-alpha">
+      <li>如果可能,将多个通知放入一个通知对象中。
+      </li>
+
+      <li>只有与进行中的活动(例如音乐播放或通话)相关时,通知才会持续保留。
+      </li>
+
+      <li>通知中不包含广告或与应用核心功能无关的内容,除非用户选择接受这样的通知。
+      </li>
+      </ol>
+    </td>
+    <td>
+      <a href="#core">CR-11</a>
+    </td>
+    </tr>
+
+    <tr id="UX-S2">
+    <td>
+      UX-S2
+    </td>
+    <td>
+      <p style="margin-bottom:.5em">
+      应用只为实现以下目的才使用通知:
+      </p>
+
+      <ol style="margin-bottom:.5em;list-style-type:lower-alpha">
+      <li>说明与用户个人相关的上下文更改(例如收到消息)</li>
+
+      <li>展示与进行中的活动(例如音乐播放或通话)相关的信息/控件。
+      </li>
+      </ol>
+    </td>
+    <td>
+      <a href="#core">CR-11</a>
+    </td>
+    </tr>
+  </table>
+
+  <h3 class="rel-resources clearfloat">相关资源</h3>
+
+  <div class="resource-widget resource-flow-layout col-13" data-query="collection:distribute/essentials/corequalityguidelines/visualdesign" data-sortorder="-timestamp" data-cardsizes="9x3,9x3,6x3,6x3,6x3" data-maxresults="6">
+  </div>
+
+  <div class="headerLine">
+    <h2 id="fn">
+    功能
+    </h2>
+
+
+  </div>
+
+  <p>
+    遵循这些标准能够确保你的应用使用合适的权限级别,提供预期的功能行为。
+  </p>
+
+  <table>
+    <tr>
+    <th style="width:2px">
+      领域
+    </th>
+    <th style="width:54px">
+      ID
+    </th>
+    <th>
+      说明
+    </th>
+    <th style="width:54px">
+      测试
+    </th>
+    </tr>
+
+    <tr id="FN-P1">
+    <td rowspan="2">
+      权限
+    </td>
+    <td>
+      FN-P1
+    </td>
+    <td>
+      应用只请求为支持核心功能而需要的绝对最低级别权限。<em></em>
+    </td>
+    <td rowspan="2">
+      <a href="#core">CR-11</a>
+    </td>
+    </tr>
+
+    <tr id="FN-P2">
+    <td>
+      FN-P2
+    </td>
+    <td>
+      <p style="margin-bottom:.5em">
+      除非与应用的核心功能相关,否则应用不请求访问敏感数据(例如通讯录或系统日志)或访问用户付费服务(例如拨号或短信)的权限。
+      </p>
+    </td>
+    </tr>
+
+    <tr id="FN-L1">
+    <td>
+      安装位置
+    </td>
+    <td>
+      FN-L1
+    </td>
+    <td>
+      <p style="margin-bottom:.5em">
+      应用安装在 SD 卡上(如果应用支持)时可以正常工作。
+      </p>
+
+      <p style="margin-bottom:.25em">
+      建议大多数的大型应用(大于 10MB)支持安装到 SD 卡。请参阅<a href="{@docRoot}guide/topics/data/install-location.html">应用安装位置</a>开发者指南,了解哪些类型的应用应该支持安装到 SD 卡。
+      </p>
+    </td>
+    <td>
+      <a href="#SD-1">SD-1</a>
+    </td>
+    </tr>
+
+    <tr id="FN-A1">
+    <td rowspan="4">
+      音频
+    </td>
+    <td>
+      FN-A1
+    </td>
+    <td>
+      音频不会在屏幕关闭时播放,除非这是应用的核心功能(例如应用是音乐播放器)。
+    </td>
+    <td>
+      <a href="#core">CR-7</a>
+    </td>
+    </tr>
+
+    <tr id="FN-A2">
+    <td>
+      FN-A2
+    </td>
+    <td>
+      音频不会在<a href="http://android-developers.blogspot.com/2011/11/making-android-games-that-play-nice.html">锁定屏幕后播放</a>,除非这是应用的核心功能。
+    </td>
+    <td>
+      <a href="#core">CR-8</a>
+    </td>
+    </tr>
+
+    <tr id="FN-A3">
+    <td>
+      FN-A3
+    </td>
+    <td>
+      音频不会在主屏幕上或其他应用运行时播放,除非这是应用的核心功能。
+    </td>
+    <td>
+      <a href="#core">CR-1,<br />
+      CR-2</a>
+    </td>
+    </tr>
+
+    <tr id="FN-A4">
+    <td>
+      FN-A4
+    </td>
+    <td>
+      音频会在应用返回前台时恢复,或者提示用户播放已处于暂停状态。
+    </td>
+    <td>
+      <a href="#core">CR-1, CR-8</a>
+    </td>
+    </tr>
+
+    <tr id="FN-U1">
+    <td rowspan="3">
+      界面和图形
+    </td>
+    <td>
+      FN-U1
+    </td>
+    <td>
+      <p style="margin-bottom:.5em">
+      应用既支持横向模式也支持纵向模式(如果可能)。
+      </p>
+
+      <p style="margin-bottom:.25em">
+      不同的显示方向下,所展现的功能特性和操作应大致相同。对内容或视图做出细微更改是可接受的。
+      </p>
+    </td>
+    <td>
+      <a href="#core">CR-5</a>
+    </td>
+    </tr>
+
+    <tr id="FN-U2">
+    <td>
+      FN-U2
+    </td>
+    <td>
+      <p style="margin-bottom:.5em">
+      在两种显示方向下,应用都占用整个屏幕,不会因为显示方向更改而出现黑边。
+      </p>
+
+      <p style="margin-bottom:.25em">
+      为补偿屏幕图形的细微差异而使用少许黑边是可接受的。
+      </p>
+    </td>
+    <td>
+      <a href="#core">CR-5</a>
+    </td>
+    </tr>
+
+    <tr id="FN-U3">
+    <td>
+      FN-U3
+    </td>
+    <td>
+      <p style="margin-bottom:.5em">
+      应用能正确处理显示方向之间的快速转换,而不会出现渲染问题。
+      </p>
+    </td>
+    <td>
+      <a href="#core">CR-5</a>
+    </td>
+    </tr>
+
+    <tr id="FN-S1">
+    <td rowspan="2">
+      用户/应用状态
+    </td>
+    <td>
+      FN-S1
+    </td>
+    <td>
+      <p style="margin-bottom:.5em">
+      应用转入后台时,不应再有任何服务处于运行状态,除非该服务与应用核心功能相关。
+      </p>
+
+      <p style="margin-bottom:.25em">
+      例如,应用不得为了以下目的而让服务保持运行状态:为了接收通知而连接网络、保持蓝牙连接状态或为了让 GPS 保持开启状态。
+      </p>
+    </td>
+    <td>
+      <a href="#core">CR-6</a>
+    </td>
+    </tr>
+
+    <tr id="FN-S2">
+    <td>
+      FN-S2
+    </td>
+    <td>
+      <p style="margin-bottom:.5em">
+      应用能够正确保留和恢复用户或应用状态。
+      </p>
+
+      <p style="margin-bottom:.25em">
+      应用在离开前台时能够保留用户或应用状态,并防止因返回导航或其他状态更改导致的意外数据丢失。返回前台时,应用必须恢复保留的状态和其他任何重要的待处理事务,例如对可修改字段的更改、游戏进程、菜单、视频以及应用或游戏的其他部分。
+      </p>
+
+      <ol style="margin-bottom:.25em;list-style-type:lower-alpha">
+      <li>当应用从“最近使用的应用”切换器恢复时,它能够让用户返回到上次使用的准确状态。
+      </li>
+
+      <li>当在设备从休眠(锁定)中被唤醒后,应用能够恢复并让用户准确返回到上次使用的状态。
+      </li>
+
+      <li>当应用从主屏幕或“所有应用”重新启动时,应用会尽可能恢复为与之前状态最接近的状态。
+      </li>
+
+      <li>在“返回”按键上,应用允许用户保存会因返回导航而丢失的任何应用或用户状态。
+      </li>
+      </ol>
+    </td>
+    <td>
+      <a href="#core">CR-1, CR-3, CR-5</a>
+    </td>
+    </tr>
+  </table>
+
+  <h3 class="rel-resources clearfloat">相关资源</h3>
+
+  <div class="resource-widget resource-flow-layout col-13" data-query="collection:distribute/essentials/corequalityguidelines/functionality" data-sortorder="-timestamp" data-cardsizes="6x3" data-maxresults="6">
+  </div>
+
+  <div class="headerLine">
+    <h2 id="ps">
+    性能和稳定性
+    </h2>
+
+
+  </div>
+
+  <p>
+    遵循这些标准能够确保应用提供用户期望的性能、稳定性和响应速度。
+  </p>
+
+  <table>
+    <tr>
+    <th style="width:2px">
+      领域
+    </th>
+    <th style="width:54px">
+      ID
+    </th>
+    <th>
+      说明
+    </th>
+    <th style="width:54px">
+      测试
+    </th>
+    </tr>
+
+    <tr id="PS-S1">
+    <td>
+      稳定性
+    </td>
+    <td>
+      PS-S1
+    </td>
+    <td>
+      应用不会在适配设备上崩溃、强制关闭、冻结或不正常运行。
+    </td>
+    <td>
+      <a href="#core">CR 全部</a>、<a href="#SD-1">SD-1</a>、<a href="#HA-1">HA-1</a>
+    </td>
+    </tr>
+
+    <tr id="PS-P1">
+    <td rowspan="2">
+      性能
+    </td>
+    <td>
+      PS-P1
+    </td>
+    <td>
+      应用加载迅速,如果加载时间超过 2 秒则在屏幕上向用户提供反馈(进度指示器或类似提示)。
+    </td>
+    <td>
+      <a href="#core">CR 全部</a>、<a href="#SD-1">SD-1</a>
+    </td>
+    </tr>
+
+    <tr id="PS-P2">
+    <td>
+      PS-P2
+    </td>
+    <td>
+      启用 StrictMode 之后(请参阅下文的 <a href="#strictmode">StrictMode 测试</a>),在应用运行期间(包括玩游戏、动画和界面转换期间以及应用的其他部分)指示灯不会呈红色闪烁(StrictMode 的性能警告)。
+    </td>
+    <td>
+      <a href="#PM-1">PM-1</a>
+    </td>
+    </tr>
+
+    <tr id="PS-M1">
+    <td>
+      媒体
+    </td>
+    <td>
+      PS-M1
+    </td>
+    <td>
+      音乐和视频播放流畅,应用正常使用和加载期间没有中断、卡壳或其他异常声音。
+    </td>
+    <td>
+      <a href="#core">CR 全部</a>、<a href="#SD-1">SD-1</a>、<a href="#HA-1">HA-1</a>
+    </td>
+    </tr>
+
+    <tr id="PS-V1">
+    <td rowspan="2">
+      视觉质量
+    </td>
+    <td>
+      PS-V1
+    </td>
+    <td>
+      <p style="margin-bottom:.5em">
+      应用显示的图形、文本、图片和其他界面元素没有明显的失真、模糊或像素化。
+      </p>
+
+      <ol style="margin-bottom:.5em;list-style-type:lower-alpha">
+      <li>应用为所有适配的屏幕尺寸和外形规格提供高品质的图形,包括<a href="{@docRoot}distribute/essentials/quality/tablet.html">平板电脑等大屏幕设备</a>。
+      </li>
+
+      <li>菜单边缘、按钮和其他界面元素没有明显的走样。
+      </li>
+      </ol>
+    </td>
+    <td rowspan="2">
+      <a href="#core">CR-all</a>
+    </td>
+    </tr>
+
+    <tr id="PS-V2">
+    <td>
+      PS-V2
+    </td>
+    <td>
+      <p style="margin-bottom:.5em">
+      应用以可接受的方式显示文本和文本块。
+      </p>
+
+      <ol style="margin-bottom:.5em;list-style-type:lower-alpha">
+      <li>所用构图在所有支持的外形规格上都适用,包括平板电脑等大屏幕设备。
+      </li>
+
+      <li>没有明显的字母或词语丢失。
+      </li>
+
+      <li>按钮或图标中没有明显的不当文本换行。
+      </li>
+
+      <li>文本与周围元素之间有充分的空间。
+      </li>
+      </ol>
+    </td>
+    </tr>
+  </table>
+
+  <h3 class="rel-resources clearfloat">相关资源</h3>
+
+  <div class="resource-widget resource-flow-layout col-13" data-query="collection:distribute/essentials/core/performance" data-sortorder="-timestamp" data-cardsizes="6x3" data-maxresults="6">
+  </div>
+
+  <div class="headerLine">
+    <h2 id="listing">Google Play</h2>
+
+
+  </div>
+
+  <p>
+    遵循这些标准能够确保你的应用做好在 Google Play 上发布的准备。
+  </p>
+
+  <table>
+    <tr>
+    <th style="width:2px">
+      领域
+    </th>
+    <th style="width:54px">
+      ID
+    </th>
+    <th>
+      说明
+    </th>
+    <th style="width:54px">
+      测试
+    </th>
+    </tr>
+
+    <tr id="GP-P1">
+    <td rowspan="2">
+      政策
+    </td>
+    <td>
+      GP-P1
+    </td>
+    <td>
+      应用严格遵守 <a href="http://play.google.com/about/developer-content-policy.html">Google Play 开发者内容政策</a>的条款,不提供不当内容,不使用其他方的知识产权或品牌等。
+    </td>
+    <td>
+      <a href="#gp">GP 全部</a>
+    </td>
+    </tr>
+
+    <tr id="GP-P2">
+    <td>
+      GP-P2
+    </td>
+    <td>
+      <p style="margin-bottom:.5em">
+      根据<a href="http://support.google.com/googleplay/android-developer/bin/answer.py?hl=zh-CN&amp;answer=188189">内容分级指南</a>合理设置应用的心智成熟度。
+      </p>
+
+      <p style="margin-bottom:.25em">
+      尤其要注意的是,对于需要请求相应权限以使用设备位置的应用,其心智成熟度不能设置为“所有人”。
+      </p>
+    </td>
+    <td>
+      <a href="#gp">GP-1</a>
+    </td>
+    </tr>
+
+    <tr id="GP-D1">
+    <td rowspan="3">
+      应用详细信息页
+    </td>
+    <td>
+      GP-D1
+    </td>
+    <td>
+      <p style="margin-bottom:.5em">
+      应用置顶大图遵循此<a href="http://android-developers.blogspot.com/2011/10/android-market-featured-image.html">博文</a>中介绍的指南。请确保:
+      </p>
+
+      <ol style="margin-bottom:.5em;list-style-type:lower-alpha">
+      <li>应用商品详情包括高品质的置顶大图。
+      </li>
+
+      <li>置顶大图不包含设备图片、屏幕截图,也不包含缩小后以及在应用适配的最小尺寸屏幕上显示时难以辨认的小文字。
+      </li>
+
+      <li>置顶大图不能看起来像广告。
+      </li>
+      </ol>
+    </td>
+    <td>
+      <a href="#gp">GP-1、GP-2</a>
+    </td>
+    </tr>
+
+    <tr id="GP-D2">
+    <td>
+      GP-D2
+    </td>
+    <td>
+      应用屏幕截图和视频不显示或引用非 Android 设备。
+    </td>
+    <td rowspan="2">
+      <a href="#gp">GP-1</a>
+    </td>
+    </tr>
+
+    <tr id="GP-D3">
+    <td>
+      GP-D3
+    </td>
+    <td>
+      应用屏幕截图或视频不以误导性方式展示应用的内容和体验。
+    </td>
+    </tr>
+
+    <tr id="GP-X1">
+    <td>
+      用户支持
+    </td>
+    <td>
+      GP-X1
+    </td>
+    <td>
+      对于 Google Play 页面的“评论”标签中常见的用户报告的错误,如果错误可重现而且在多种不同的设备上发生,则应予以解决。
+      如果错误只在少数设备上出现,但是这些设备十分受欢迎或者是新设备,则你仍要予以解决。
+    </td>
+    <td>
+      <a href="#gp">GP-1</a>
+    </td>
+    </tr>
+  </table>
+
+  <h3 class="rel-resources clearfloat">相关资源</h3>
+
+  <div class="resource-widget resource-flow-layout col-13" data-query="collection:distribute/essentials/core/play/zhcn" data-sortorder="-timestamp" data-cardsizes="6x3,6x3,6x3,6x3,6x3,6x3" data-maxresults="6">
+  </div>
+
+  <div class="headerLine">
+    <h2 id="test-environment">
+    设置测试环境
+    </h2>
+
+
+  </div>
+
+  <p>
+    要掌控应用的质量,请设置合适的硬件或模拟器环境以进行测试。
+  </p>
+
+  <p>
+    理想的测试环境应包括少量实际硬件设备,并且这些设备能够代表目前消费者可用的主要外形规格和硬件/软件组合。你不需要测试市场上的每种设备,只需关注少量代表性的设备,甚至是仅针对每种外形规格选用一两个设备即可。<em></em>
+  </p>
+
+  <p>
+    如果你不能获得实际硬件设备来进行测试,则应该<a href="{@docRoot}tools/devices/index.html">设置模拟设备 (AVD)</a> 以代表最常见的外形规格和硬件/软件组合。
+  </p>
+
+  <p>
+    如果你不局限于基本测试,则可以向测试环境中添加更多设备、更多外形规格,或新的硬件/软件组合。你也可以增加测试次数、提高复杂性和质量标准。
+  </p>
+
+  <div class="headerLine">
+    <h2 id="tests">
+    测试流程
+    </h2>
+
+
+  </div>
+
+  <p>
+    这里的这些测试流程有助于你发现应用中的各类质量问题。你可以在你的测试计划中组合各项测试或综合运用各组测试。请参阅上文介绍的内容,了解与特定测试相关联的具体标准。
+  </p>
+
+  <table>
+    <tr>
+    <th style="width:2px">
+      类型
+
+
+    </th>
+    <th style="width:54px">
+      测试
+    </th>
+    <th>
+      说明
+    </th>
+    </tr>
+
+    <tr>
+    <td rowspan="12" id="core">
+      核心套件
+    </td>
+    <td>
+      CR-0
+    </td>
+    <td>
+      <p style="margin-bottom:.5em">
+      浏览应用的各个部分:所有屏幕、对话框、设置和所有用户流程。
+      </p>
+
+      <ol style="margin-bottom:.5em;list-style-type:lower-alpha">
+      <li>如果应用运行修改或创建内容、播放游戏或媒体,请务必进入这些流程以创建或修改内容。
+      </li>
+
+      <li>在使用应用期间,让网络连接、电池功能、GPS 或位置可用性以及系统加载等发生短暂变化。
+      </li>
+      </ol>
+    </td>
+    </tr>
+
+    <tr id="tg2">
+    <td id="core2">
+      CR-1
+    </td>
+    <td>
+      在每个应用屏幕上,按下设备的“主屏幕”键,然后从“所有应用”屏幕重新启动应用。
+    </td>
+    </tr>
+
+    <tr id="CR-2">
+    <td>
+      CR-2
+    </td>
+    <td>
+      从每个应用屏幕,使用“最近使用的应用”切换器切换到其他正在运行的应用,然后返回正在测试的应用。
+    </td>
+    </tr>
+
+    <tr id="CR-3">
+    <td>
+      CR-3
+    </td>
+    <td>
+      从每个应用屏幕(和对话框),按下“返回”按钮。
+    </td>
+    </tr>
+
+    <tr id="CR-5">
+    <td>
+      CR-5
+    </td>
+    <td>
+      从每个应用屏幕,旋转设备,在横屏和竖屏之间切换至少三次。
+    </td>
+    </tr>
+
+    <tr id="CR-6">
+    <td>
+      CR-6
+    </td>
+    <td>
+      切换到其他应用,以将测试应用转至后台。转到“设置”,检查测试应用在后台时是否有服务在运行。在 Android 4.0 及更高版本中,转到“应用”屏幕并找到“正在运行”标签中的应用。在早期版本中,使用“管理应用”查看运行中的服务。
+    </td>
+    </tr>
+
+    <tr id="CR-7">
+    <td>
+      CR-7
+    </td>
+    <td>
+      按下电源按钮,让设备进入休眠状态,然后再次按下电源按钮唤醒屏幕。
+    </td>
+    </tr>
+
+    <tr id="CR-8">
+    <td>
+      CR-8
+    </td>
+    <td>
+      将设备设置为按下电源按钮后锁定。按下电源按钮让设备进入休眠状态,然后再次按下电源按钮唤醒屏幕,然后解锁设备。
+    </td>
+    </tr>
+
+    <tr id="CR-9">
+    <td><!-- Hardware features -->
+      CR-9
+    </td>
+    <td>
+      对于带有滑动键盘的设备,将键盘滑进并滑出至少一次。对于带有键盘底座的设备,将设备连接到键盘底座。
+    </td>
+    </td></tr>
+
+    <tr id="CR-10">
+    <td>
+      CR-10
+    </td>
+    <td>
+      对于具有外部显示端口的设备,请插入外部显示器。
+    </td>
+    </tr>
+
+    <tr id="CR-11">
+    <td>
+      CR-11
+    </td>
+    <td>
+      在通知抽屉中触发并查看应用能够显示的所有通知类型。如果适用(Android 4.1 及更高版本)展开通知并触碰提供的所有操作。
+    </td>
+    </tr>
+
+    <tr id="CR-12">
+    <td>
+      CR-12
+    </td>
+    <td>
+      转到“设置”&gt;“应用信息”检查应用请求的权限</td>
+    </tr>
+
+    <tr id="tg3">
+    <td>
+      在 SD 卡上安装
+    </td>
+    <td>
+      SD-1
+    </td>
+    <td>
+      <p style="margin-bottom:.5em">
+      对安装到<a href="{@docRoot}guide/topics/data/install-location.html">设备 SD 卡</a>的应用(如果应用支持的话)重复核心套件测试。<em></em>
+      </p>
+
+      <p style="margin-bottom:.25em">
+      要将应用移动到 SD 卡,你可以使用“设置”&gt;“应用信息”&gt;“移动到 SD 卡”。
+      </p>
+    </td>
+    </tr>
+
+    <tr id="tg32">
+    <td>
+      硬件加速
+    </td>
+    <td>
+      HA-1
+    </td>
+    <td>
+      <p style="margin-bottom:.5em">
+      启用硬件加速并重复核心套件测试。<em></em>
+      </p>
+
+      <p style="margin-bottom:.25em">
+      要强制启动硬件加速(在设备支持的位置),请将 <code>hardware-accelerated="true"</code> 添加到AndroidManifest.xml中的 <code>&lt;application&gt;</code> 并重新编译。
+      </p>
+    </td>
+    </tr>
+
+    <tr id="tg33">
+    <td>
+      性能监控
+    </td>
+    <td>
+      PM-1
+    </td>
+    <td>
+      <p style="margin-bottom:.5em">
+      在启用 StrictMode 分析(<a href="#strictmode">如下所述</a>)的情况下重复核心套件测试。<em></em>
+      </p>
+
+      <p style="margin-bottom:.25em">
+      密切关注垃圾回收及其对用户体验的影响。
+      </p>
+    </td>
+    </tr>
+
+    <tr id="gp">
+    <td rowspan="3">Google Play</td>
+    <td>
+      GP-1
+    </td>
+    <td>
+      登录到<a href="https://play.google.com/apps/publish/">开发者控制台</a>查看你的开发者个人资料、应用说明、屏幕截图、置顶大图、心智成熟度设置以及用户反馈。
+    </td>
+    </tr>
+
+    <tr id="GP-2">
+    <td>
+      GP-2
+    </td>
+    <td>
+      下载置顶大图和屏幕截图,将其缩小以匹配设备上的显示屏尺寸以及你定位的外形规格。
+    </td>
+    </tr>
+
+    <tr id="GP-3">
+    <td>
+      GP-3
+    </td>
+    <td>
+      查看所有图形资源、媒体、文本、代码库,以及应用内其他打包的内容或下载的扩展文件。
+    </td>
+    </tr>
+
+    <tr id="GP-4">
+    <td>
+      付款
+    </td>
+    <td>
+      GP-4
+    </td>
+    <td>
+      浏览应用的所有屏幕,并进入所有应用内购买流程。
+    </td>
+    </tr>
+  </table>
+
+  <h3 id="strictmode">
+    使用 StrictMode 进行测试
+  </h3>
+
+  <p>
+    对于性能测试,我们建议在应用内启用 <code><a href="{@docRoot}reference/android/os/StrictMode.html">StrictMode</a></code>,通过它捕获主线程上和其他线程上可能影响性能、网络访问、文件读/写等的操作。
+  </p>
+
+  <p>
+    你可以使用 <code><a href="{@docRoot}reference/android/os/StrictMode.ThreadPolicy.Builder.html">StrictMode.ThreadPolicy.Builder</a></code> 设置每个线程的监控政策,并使用 <code><a href="{@docRoot}reference/android/os/StrictMode.ThreadPolicy.Builder.html#detectAll()">detectAll()</a></code> 在 <code>ThreadPolicy</code> 中启用所有受支持的监控。
+  </p>
+
+  <p>
+    请务必使用 <code><a href="{@docRoot}reference/android/os/StrictMode.ThreadPolicy.Builder.html#penaltyFlashScreen()">penaltyFlashScreen()</a></code> 针对 <code>ThreadPolicy</code> 为违反策略的行为启用<strong>可视通知</strong>。
+  </p>
diff --git a/docs/html-intl/intl/zh-cn/distribute/essentials/quality/tablets.jd b/docs/html-intl/intl/zh-cn/distribute/essentials/quality/tablets.jd
new file mode 100644
index 0000000..4b6eb50
--- /dev/null
+++ b/docs/html-intl/intl/zh-cn/distribute/essentials/quality/tablets.jd
@@ -0,0 +1,582 @@
+page.title=平板电脑应用的质量
+page.metaDescription=平板电脑是一个快速增长的Android的安装基础,提供了新的机会,为你的应用程序提供了新的机会。
+page.image=/distribute/images/tablet-guidelines-color.jpg
+Xnonavpage=true
+page.type="배포"
+
+@jd:body
+
+      <div id="qv-wrapper"><div id="qv">
+  <h2>检查清单</h2>
+  <ol>
+  <li><a href="#core-app-quality">1. 测试平板电脑应用的基本质量</a></li>
+  <li><a href="#optimize-layouts">2. 优化布局</a></li>
+  <li><a href="#use-extra-space">3. 利用多出来的屏幕区域</a></li>
+  <li><a href="#use-tablet-icons">4. 使用专为平板电脑设计的图形资源</a></li>
+  <li><a href="#adjust-font-sizes">5. 调整字体和触控目标</a></li>
+  <li><a href="#adjust-widgets">6. 调整主屏幕小部件</a></li>
+  <li><a href="#offer-full-feature-set">7. 提供全套功能</a></li>
+  <li><a href="#android-versions">8. 确定合适的 Android 版本</a></li>
+  <li><a href="#hardware-requirements">9. 准确声明硬件功能依赖关系</a></li>
+  <li><a href="#support-screens">10. 声明对平板电脑屏幕的支持</a></li>
+  <li><a href="#google-play">11. 展示你的平板电脑界面</a></li>
+  <li><a href="#google-play-best-practices">12. 遵循在 Google Play 中发布应用的最佳做法</a></li>
+
+  </ol>
+  <h2>测试</h2>
+  <ol>
+  <li><a href="#test-environment">设置测试环境</a></li>
+  </ol>
+  </div></div>
+
+  <div class="todp-right-float" style="padding-right:0;margin-bottom:1em">
+    <img src="{@docRoot}distribute/images/tablet-guidelines-color.jpg" style="width:480px" />
+  </div>
+
+  <p>
+    现在越来越多的平板电脑选择安装 Android 系统,这就为开发者提供了<a href="{@docRoot}distribute/stories/tablets.html">与用户互动和获利</a>的新机会。本文档中介绍的相关指南旨在帮助你通过引人入胜的功能、精心设计的直观界面满足平板电脑用户的预期。
+  </p>
+
+  <p>
+    虽然这些指南按照编号排列,但你从哪一项开始着手都没问题。对于每条指南,你都应根据你的应用酌情考虑是否采用其中的建议。不过,为了给客户提供最好的产品,建议你尽可能遵循这些指南。
+  </p>
+
+  <p>
+    本文档中提供了相关资源的链接,这些资源可帮助你了解文中给出的各条建议。
+  </p>
+
+  <div class="headerLine"><h2 id="core-app-quality">1. 测试平板电脑应用的基本质量</h2></div>
+
+  <p>为了打造上佳的平板电脑应用体验,首先要根据应用适配的设备类型和尺寸,确保你的应用满足相应的“应用核心质量标准”<em></em>。有关完整信息,请参阅<a href="{@docRoot}distribute/essentials/quality/core.html">应用核心质量指南</a>。
+  </p>
+
+  <p>
+  在发布应用前,还应确保你的应用通过了基本的技术检查并符合相关发布标准,例如:
+  </p>
+
+  <ul>
+    <li><a href="#android-versions">面向相应的Android版本</a></li>
+    <li><a href="#hardware-requirements">正确描述所依托的硬件功能</a></li>
+    <li><a href="#support-screens">声明对相应屏幕的支持</a></li>
+    <li><a href="#use-extra-space">利用可用的屏幕空间</a></li>
+    <li><a href="#google-play">上传屏幕截图到Google Play</a></li>
+  </ul>
+
+  <p>如果你的应用已上传至 Google Play 开发者控制台,你可以浏览<a href="#google-play-optimization-tips">“优化提示”页</a>,了解以上各项检查的结果。</p>
+
+
+  <div class="headerLine">
+  <h2 id="optimize-layouts">2. 针对大屏幕优化布局</h2></div>
+
+  <p>
+    借助于 Android,开发在屏幕尺寸各异、外形规格不同的各类设备上都能良好运行的应用不再是难事。这种广泛的兼容性对你极其有利,因为你只需设计一个可广泛分发到所有适配设备的应用即可。不过,要让用户在各种屏幕配置下(特别是在平板电脑上)都能获得最佳体验,你需要针对各种适用的屏幕配置,优化应用的布局及其他界面组件。在平板电脑上,优化界面可让你充分利用多出来的可用屏幕空间,例如用来提供新功能、展示新内容或以其他方式提升用户体验,以加深与用户的互动。
+  </p>
+
+  <p>
+    如果你的应用是针对手机开发的,而现在你准备将其分发到平板电脑,则可以先对布局、字体和间距稍作调整。在某些情况下,例如对于 7 英寸平板电脑或画面较大的游戏,你可能只需做这些调整就能让应用实现出色的视觉效果。而在另外一些情况下,例如对于大屏幕的平板电脑,你就需要重新设计界面的各个组成部分,将拉伸变形的的界面替换为高效的多窗格界面、更便捷的导航控件以及其他内容。
+  </p>
+
+
+  <div style="width:500px;margin:1.5em">
+  <img src="{@docRoot}images/training/app-navigation-multiple-sizes-multipane-bad.png" style="padding:4px;margin-bottom:0em" />
+  <p class="img-caption"><span style="font-weight:500">舍弃拉伸变形的界面</span>:在平板电脑上,单窗格布局会导致空白过多以及内容行过长。请利用内边距减少界面元素的宽度并考虑采用多窗格布局。</p>
+  </div>
+
+  <p>下面是一些建议:</p>
+
+
+  <ul>
+    <li>根据需要,针对 <code>large</code> 和 <code>xlarge</code> 屏幕提供自定义布局。你还可以提供会根据屏幕的<a href="{@docRoot}guide/practices/screens_support.html#NewQualifiers">最短尺寸</a>或<a href="{@docRoot}guide/practices/screens_support.html#NewQualifiers">最小可用宽度和高度</a>加载的布局。
+    </li>
+
+    <li>至少要针对大屏幕自定义字体大小、外边距和间距等规格,以提高空间利用率和内容易读性。
+    </li>
+
+    <li>调整界面控件的位置,以便用户在手持平板电脑时能轻易触碰到这些控件,例如在横屏时将控件放于两侧。
+    </li>
+
+    <li>通常情况下,平板电脑上界面元素的内边距应比手机上的大一些。建议遵守 <a href="{@docRoot}design/style/metrics-grids.html#48dp-rhythm">48dp 定律</a>(以及 16dp 格栅)。
+    </li>
+
+    <li>为文本内容设置合理的内边距,使其不会直接沿屏幕边缘对齐。内容与屏幕边缘间至少保留 <code>16dp</code> 的内边距。
+    </li>
+  </ul>
+
+  <p>尤其需要注意的是,不要让你的布局在屏幕上出现拉伸变形的情况:</p>
+
+  <ul>
+  <li>文本行的长度不应过长:经过优化后,每行最多 100 个字符,最好在 50 至 75 个字符之间。</li>
+  <li><code>ListView</code>和菜单不应使用全屏宽度。</li>
+  <li>使用内边距管理屏幕上元素的宽度,或切换为适用于平板电脑的多窗格界面(参见下一节)。</li>
+  </ul>
+
+  <h3 class="rel-resources clearfloat">相关资源</h3>
+
+  <div class="resource-widget resource-flow-layout col-13" data-query="collection:distribute/essentials/tabletguidelines/optimize" data-sortorder="-timestamp" data-cardsizes="6x3" data-maxresults="6"></div>
+
+
+  <div class="headerLine"><h2 id="use-extra-space">3. 充分利用多出来的屏幕区域</h2></div>
+
+  <div style="width:340px;float:right;margin:1.5em;margin-bottom:0;margin-top:0">
+  <img src="{@docRoot}images/training/app-navigation-multiple-sizes-multipane-good.png" style="padding:4px;margin-bottom:0em" />
+  <p class="img-caption"><span style="font-weight:500">多窗格布局</span>能在平板电脑屏幕上实现更好的视觉平衡效果,同时令实用性和可读性得到增强。</p>
+  </div>
+
+  <p>平板电脑屏幕可为你的应用提供更多的屏幕空间(横向模式下更是如此)。尤其是 10 英寸的平板电脑多出来的区域更大,但即使是 7 英寸的平板电脑,也提供了更多空间供你展示内容、吸引用户。</p>
+
+  <p>为在平板电脑上运行的应用设计界面时,请确保能够充分利用平板电脑上多出来的屏幕区域。下面是一些建议:</p>
+
+  <ul>
+  <li>看看是否有可能加入更多内容,或换种方式呈现现有内容。</li>
+  <li>在平板电脑屏幕上使用<a href="{@docRoot}design/patterns/multi-pane-layouts.html">多窗格布局</a>,将单一视图合并成复合视图。这样一来,你可以更有效地利用多出来的屏幕区域,让用户能更轻松地浏览你的应用。</li>
+  <li>确定在屏幕方向改变时如何重新排列复合视图的各个版块。</li>
+
+  <div style="width:490px;margin:1.5em auto 1.5em 0">
+  <div>
+  <img src="{@docRoot}images/ui-ex-single-panes.png" align="middle" style="width:490px;padding:4px;margin-bottom:0em" />
+  <img src="{@docRoot}images/ui-ex-multi-pane.png" style="width:490px;padding:4px;margin-bottom:0em" />
+  <p class="image-caption" style="padding:.5em"><span style="font-weight:500">复合视图</span>将手机界面的多个单一视图(如上图)合并成适用于平板电脑的更高效、内容更丰富的界面(如下图)。<em></em><em></em></p>
+  </div>
+  </div>
+
+  <li>如果单屏是用 <code><a href="{@docRoot}reference/android/app/Activity.html">Activity</a></code> 子类实现的,请考虑用 <code><a href="{@docRoot}reference/android/app/Fragment.html">Fragment</a></code> 子类实现各个内容版块。这样一来,你可以在不同外形规格的设备上以及内容相同的屏幕间最大程度地重复使用代码。</li>
+  <li>确定要在哪些屏幕尺寸上使用多窗格界面,然后在相应的屏幕尺寸单元(例如 <code>large</code>/<code>xlarge</code>)中提供合适的布局,或最小屏幕宽度(例如 <code>sw600dp</code>/<code>sw720</code>)。</li>
+  </ul>
+
+  <h3 class="rel-resources clearfloat">相关资源</h3>
+
+  <div class="resource-widget resource-flow-layout col-13" data-query="collection:distribute/essentials/tabletguidelines/extrascreen" data-sortorder="-timestamp" data-cardsizes="6x3,6x3,6x3" data-maxresults="6"></div>
+
+  <div class="headerLine"><h2 id="use-tablet-icons">4. 使用专为平板电脑屏幕设计的图形资源</h2></div>
+
+  <div><img src="{@docRoot}design/media/devices_displays_density@2x.png" /></div>
+
+  <p>为确保应用呈现最佳的视觉效果,请根据平板电脑普遍支持的屏幕密度,提供相应图标和位图。具体而言,你应按<a href="{@docRoot}design/style/iconography.html">图标</a>指南为操作栏、通知和启动器设计图标,并提供多种密度的图标,这样这些图标在所有屏幕上都会以合适的尺寸显示,而不会出现模糊或缩放等情况。</p>
+
+  <p class="table-caption"><strong>表 1</strong>:不同图标类型的原始资源尺寸。<table>
+  <tr>
+  <th>密度</th>
+  <th>启动器</th>
+  <th>操作栏</th>
+  <th>小图标/上下文图标</th>
+  <th>通知</th>
+  </tr>
+  <tr>
+  <td><code>mdpi</code></td>
+  <td>48x48 像素</td>
+  <td>32x32 像素</td>
+  <td>16x16 像素</td>
+  <td>24x24 像素</td>
+  </tr>
+  <tr>
+  <td><code>hdpi</code></td>
+  <td>72x72 像素</td>
+  <td>48x48 像素</td>
+  <td>24x24 像素</td>
+  <td>36x36 像素</td>
+  </tr>
+  <tr>
+  <td><code>tvdpi</code></td>
+  <td><em>(使用 hdpi)</em></td>
+  <td><em>(使用 hdpi)</em></td>
+  <td><em>(使用 hdpi)</em></td>
+  <td><em>(使用 hdpi)</em></td>
+  </tr>
+  <tr>
+  <td><code>xhdpi</code></td>
+  <td>96x96 像素</td>
+  <td>64x64 像素</td>
+  <td>32x32 像素</td>
+  <td>48x48 像素</td>
+  </tr>
+  <tr>
+  <td><code>xxhdpi</code></td>
+  <td>144x144 像素</td>
+  <td>96x96 像素</td>
+  <td>48x48 像素</td>
+  <td>72x72 像素</td>
+  </tr>
+
+  </table>
+
+  <p>
+    作为最低要求,要为以下常用的平板电脑屏幕密度中的<strong>至少一种</strong>密度,提供适用版本的图标和位图资源:
+  </p>
+  <ul>
+    <li><code>hdpi</code></li>
+    <li><code>xhdpi</code></li>
+    <li><code>xxhdpi</code></li>
+  </ul>
+
+  <p>其他提示:</p>
+
+  <ul>
+  <li>在设计图标时请使用矢量图形,这样图标在缩放时就不会损失细节或出现边缘模糊的情况。</li>
+  <li>使用针对特定密度的<a href="{@docRoot}guide/topics/resources/providing-resources.html#AlternativeResources">资源限定符</a>,以便能为各种屏幕密度加载对应尺寸的图标。</li>
+  <li>平板电脑及其他大屏设备通常需要使用启动器图标,其密度比设备实际密度要大。因此,你提供的启用器图标应尽可能采用最高密度。例如,如果平板电脑的屏幕密度是 <code>xhdpi</code>,那么需要使用 <code>xxhdpi</code> 版本的启动器图标。</li>
+  </ul>
+
+  <h3 class="rel-resources clearfloat">相关资源</h3>
+
+  <div class="resource-widget resource-flow-layout col-13" data-query="collection:distribute/essentials/tabletguidelines/assets" data-sortorder="-timestamp" data-cardsizes="9x3" data-maxresults="6"></div>
+
+  <div class="headerLine"><h2 id="adjust-font-sizes">5. 调整字体大小和触控目标</h2></div>
+
+  <p>要确保用户在平板电脑上能够轻松使用你的应用,请花些时间针对你要适配的各种屏幕配置调整平板电脑界面中的字体大小和触控目标。你可以通过<a href="{@docRoot}guide/topics/ui/themes.html">可设置样式的属性</a>或<a href="{@docRoot}guide/topics/resources/more-resources.html#Dimension">尺寸资源</a>调整字体大小,也可以通过布局和位图绘制工具调整触控目标,如上所述。</p>
+
+  <p>下面是一些注意事项:</p>
+  <ul>
+  <li>对于各种平板电脑屏幕尺寸和密度,显示的文字不应过大或过小。请确保标签的尺寸与其对应的界面元素大小相宜,并且不得在标签、标题及其他元素中使用不恰当的换行符。</li>
+  <li>建议将屏幕元素触控目标的尺寸设为 48dp(最小 32dp),但可能需要对平板电脑的界面做一些调整。请参阅<a href="{@docRoot}design/style/metrics-grids.html">度量单位和网格</a>,了解适用于大多数用户的实施策略。为了满足部分用户对辅助功能的需求,可能需要使用较大的触控目标尺寸。</li>
+  <li>如果可能,对于较小的图标,请使用 <code><a href="{@docRoot}reference/android/view/TouchDelegate.html">TouchDelegate</a></code> 将可触控区域扩展到 48dp 以上,或者将该图标放到透明按钮的中心位置。</li>
+  </ul>
+
+  <h3 class="rel-resources clearfloat">相关资源</h3>
+
+  <div class="resource-widget resource-flow-layout col-13" data-query="collection:distribute/essentials/tabletguidelines/fonts" data-sortorder="-timestamp" data-cardsizes="9x3,9x3,6x3,6x3,6x3" data-maxresults="6"></div>
+
+  <div class="headerLine"><h2 id="adjust-widgets">6. 调整主屏幕小部件的尺寸</h2></div>
+
+  <p>如果你的应用中包含主屏幕小部件,需要注意以下几点,以确保用户在平板电脑屏幕上获得良好体验:</p>
+
+  <ul>
+  <li>按照平板电脑的屏幕合理设置小部件的默认高度和宽度,以及最小和最大的伸缩高度和宽度。
+  </li>
+  <li>小部件应可拉伸到 420dp 或更高,占用主屏幕的 5 行或以上(如果是垂直或方形小部件),或者占用 5 列或以上(如果是水平或方形小部件)。</li>
+  <li>确保正确渲染 9-patch 图片。</li>
+  <li>使用默认的系统边距。</li>
+  <li>如果可能,将应用的 <code>targetSdkVersion</code> 设置为 14 或更高。</li>
+  </ul>
+
+  <h3 class="rel-resources clearfloat">相关资源</h3>
+
+  <div class="resource-widget resource-flow-layout col-13" data-query="collection:distribute/essentials/tabletguidelines/widgets" data-sortorder="-timestamp" data-cardsizes="6x3" data-maxresults="6"></div>
+
+
+  <div class="headerLine"><h2 id="offer-full-feature-set">7. 提供适用于平板电脑用户的全套功能</h2></div>
+
+  <div class="centered-full-image" style="width:600px;margin:1.5em"><img src="{@docRoot}images/gp-tablets-full-feature-set.png" alt="平板电脑功能集" /></div>
+
+  <p>让你的平板电脑用户能够体验应用的最佳功能。下面是一些建议:</p>
+
+  <ul>
+    <li>在设计应用时,应至少确保其在平板电脑上的功能与在手机上的一样。
+    </li>
+
+    <li>在特殊情况下,例如硬件不支持或用户使用场景受到限制,则可以去掉或替换应用的某些功能。例如:
+      <ul>
+        <li>如果手机使用电话功能,但当前的平板电脑无法使用此功能,那么你可以去掉或替换相关功能。
+        </li>
+
+        <li>许多平板电脑都装有 GPS 传感器,但大多数用户在跑步时通常不会带着平板电脑。如果手机应用中提供的某项功能可让用户在跑步时通过手机记录 GPS 路线,就无需在平板电脑上也提供这项功能,因为这么做并没有什么吸引力。
+        </li>
+      </ul>
+    </li>
+
+    <li>如果你要从平板电脑界面上去掉某功能,请确保用户无法通过其他方式使用这项功能;或确保该功能可以“优雅降级”,以便为用户提供替代功能(另请参见下面关于硬件功能的部分)。
+    </li>
+  </ul>
+
+  <div class="headerLine"><h2 id="android-versions">8. 确定合适的 Android 版本</h2></div>
+
+  <p>
+    为确保你的应用适配尽可能多的平板电脑,你必须让应用定位到支持平板电脑的 Android 版本。对平板电脑的支持是从 <a href="{@docRoot}about/versions/android-3.0.html">Android 3.0</a>(API 级别 11)开始的;对平板电脑、手机及其他设备的统一界面框架支持是从 <a href="{@docRoot}about/versions/android-4.0.html">Android 4.0</a> 开始的
+  </p>
+
+  <p>
+    你可以在清单文件的 <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html"><code>&lt;uses-sdk&gt;</code></a> 元素中设置应用定位的 Android 版本。在大多数情况下,你只要将该元素的 <code>targetSdkVersion</code> 属性设置为可用的最高 API 级别,即可正确定位合适的 Android 版本。
+  </p>
+
+  <p style="margin-bottom:.5em">
+    作为最低要求,请检查 <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html"><code>&lt;uses-sdk&gt;</code></a> 元素以确保:
+  </p>
+
+  <ol style="list-style-type:lower-alpha;margin-top:0em">
+    <li>
+      使用 11 或更高值声明 <code>targetSdkVersion</code>(建议使用 14 或更高值);或
+    </li>
+
+    <li>
+      使用 11 或更高值声明 <code>minSdkVersion</code>。
+    </li>
+
+    <li>如果声明 <code>maxSdkVersion</code> 属性,其值必须为 11 或更高。请注意,一般情况下不建议使用 <code>maxSdkVersion</code>。<em></em>
+    </li>
+  </ol>
+
+  <h3 class="rel-resources clearfloat">相关资源</h3>
+
+  <div class="resource-widget resource-flow-layout col-13" data-query="collection:distribute/essentials/tabletguidelines/versions" data-sortorder="-timestamp" data-cardsizes="6x3" data-maxresults="6"></div>
+
+  <div class="headerLine"><h2 id="hardware-requirements">9. 准确声明硬件功能依赖关系</h2></div>
+
+  <p>
+    通常情况下,手机和平板电脑对传感器、相机、电话及其他功能提供的硬件支持会稍有不同。例如,许多平板电脑可以在“WLAN”配置下使用却不支持电话功能。
+  </p>
+
+  <p>
+    因此,你可以向你的所有手机和平板电脑用户分发一个 APK,但要确保你的应用不会要求调用平板电脑通常不提供的硬件功能。否则,请在应用清单中将硬件功能声明为“未要求”,如下所述。<em></em>
+  </p>
+
+  <ul>
+  <li>在应用清单中,找到所有 <a href="{@docRoot}guide/topics/manifest/uses-feature-element.html"><code>&lt;uses-feature&gt;</code></a> 元素。尤其要注意的是,需要找到可能无法在某些平板电脑上使用的硬件功能,例如:
+
+  <ul>
+  <li><code>android.hardware.telephony</code></li>
+  <li><code>android.hardware.camera</code>(指后置摄像头),或</li>
+  <li><code>android.hardware.camera.front</code></li>
+  </ul></li>
+
+  <li>通过加入 <code>android:required=”false”</code> 属性将 <a href="{@docRoot}guide/topics/manifest/uses-feature-element.html"><code>&lt;uses-feature&gt;</code></a> 元素声明为“未要求”。<em></em>
+
+  <p>
+    例如,以下示例就准确声明了 <code>android.hardware.telephony</code> 的依赖关系。这样一来,你仍能广泛分发应用,甚至能分发到不提供电话功能的设备上:
+  </p>
+
+  <pre>&lt;uses-feature android:name="android.hardware.telephony" android:required="false" /&gt;</pre></li>
+
+  <li>与此类似,还请检查清单,找出<a href="{@docRoot}guide/topics/manifest/uses-feature-element.html#permissions">表明硬件功能要求</a>不适用于平板电脑的 <a href="{@docRoot}guide/topics/manifest/permission-element.html"><code>&lt;permission&gt;</code></a> 元素。如果你找到这样的权限,请务必为功能明确声明对应的 <code>&lt;uses-feature&gt;</code> 元素并加入 <code>android:required=”false”</code> 属性。</li>
+  </ul>
+
+
+  <p>
+    将硬件功能声明为“未要求”后,请务必在各种设备上测试你的应用。<em></em>即使应用所需的硬件功能无法使用,应用功能也应该正常运行;如果可以,应用应该提供“优雅降级”和替代功能。
+  </p>
+
+  <p>
+    例如,如果应用通常使用 GPS 设置地理位置,但设备不支持 GPS,那么此应用可以让用户手动设置地理位置。应用可以在运行时检查设备硬件功能并根据需要执行相应处理。
+  </p>
+
+  <h3 class="rel-resources clearfloat">相关资源</h3>
+
+  <div class="resource-widget resource-flow-layout col-13" data-query="collection:distribute/essentials/tabletguidelines/hardware" data-sortorder="-timestamp" data-cardsizes="9x3" data-maxresults="6"></div>
+
+  <div class="headerLine"><h2 id="support-screens">10. 声明对平板电脑屏幕的支持</h2></div>
+
+  <p>为确保能将应用分发到各式平板电脑上,你应在应用的清单文件中声明对各种平板电脑屏幕尺寸的支持,如下所示:</p>
+
+  <ul>
+    <li>如果声明 <a href="{@docRoot}guide/topics/manifest/supports-screens-element.html"><code>&lt;supports-screens&gt;</code></a> 元素,就不要指定 <code>android:largeScreens="false"</code> 或 <code>android:xlargeScreens="false"</code>。</li>
+    <li>如果应用定位的 <code>minSdkVersion</code> 值小于 13,必须使用 <code>android:largeScreens="true"</code> 和 <code>android:xlargeScreens="true"</code> 声明 <a href="{@docRoot}guide/topics/manifest/supports-screens-element.html"><code>&lt;supports-screens&gt;</code></a> 元素。</li>
+  </ul>
+
+  <p>如果应用在清单中声明了 <a href="{@docRoot}guide/topics/manifest/compatible-screens-element.html"><code>&lt;compatible-screens&gt;</code></a> 元素,该元素应包含相关属性,用以列举应用适配的平板电脑屏幕的尺寸和密度组合。<em></em>请注意,如果可能,你应避免在应用中使用 <a href="{@docRoot}guide/topics/manifest/compatible-screens-element.html"><code>&lt;compatible-screens&gt;</code></a> 元素。</p>
+
+  <h3 class="rel-resources clearfloat">相关资源</h3>
+
+  <div class="resource-widget resource-flow-layout col-13" data-query="collection:distribute/essentials/tabletguidelines/tabletscreens" data-sortorder="-timestamp" data-cardsizes="9x3,6x3,6x3" data-maxresults="6"></div>
+
+
+  <div class="headerLine"><h2 id="google-play">11. 在 Google Play 中展示你的平板电脑界面</h2></div>
+
+  <p>
+    为你的平板电脑应用创建了经过优化、内容丰富的界面后,请务必告知你的客户!下面是一些重要的方法,可帮助你向 Google Play 上的用户推广你的平板电脑应用。
+  </p>
+
+  <div><img class="border-img" src="{@docRoot}images/gp-tablet-quality-4.jpg" /></div>
+
+
+  <h4>
+    上传平板电脑界面的屏幕截图
+  </h4>
+
+  <p>
+    平板电脑用户需要了解你的应用在平板电脑设备上(而非手机上)的效果。如果你开发了一款平板电脑应用,请务必将平板电脑界面的屏幕截图上传至 Google Play 开发者控制台。下面是一些相关指南:
+    </p>
+
+  <ul style="margin-top:0">
+    <li>展示应用的核心功能,而不要展示启动或登录页面。用户会在应用的哪个位置花费最多时间,就在屏幕截图中展示这个位置。
+    </li>
+
+    <li>添加在 7 英寸和 10 英寸平板电脑上拍摄的屏幕截图。
+    </li>
+
+    <li>如果可能,添加横屏和竖屏拍摄的屏幕截图。
+    </li>
+
+    <li>如果可能,使用屏幕截图功能。避免在屏幕截图中展示实际的设备硬件。</li>
+
+    <li>建议对横屏和竖屏拍摄的平板电脑屏幕截图都使用 <strong>1280 x 720</strong> 或更高的分辨率。
+    </li>
+
+    <li>最好为 7 英寸的平板电脑界面上传 8 张屏幕截图,同时再为 10 英寸的平板电脑界面上传 8 张屏幕截图。
+    </li>
+  </ul>
+
+  <h4>
+    更新应用说明和版本说明
+  </h4>
+
+  <ul>
+    <li>在应用说明中,请务必着重介绍你的应用已针对平板电脑优化过界面,能为平板电脑用户提供卓越功能。添加一些详细信息,介绍平板电脑界面的工作原理及其独特优势。
+    </li>
+
+    <li>在应用的版本说明和更新信息中加入有关平板电脑支持的信息。
+    </li>
+  </ul>
+
+  <h4>
+    更新你的推广视频
+  </h4>
+
+  <p>
+    许多用户通过观看推广视频来了解应用,确定自己是否喜欢此应用。你可以根据用户的这一兴趣特点,在推广视频中着重介绍应用的平板电脑界面,以吸引平板电脑用户。下面是一些相关提示和指南:
+  </p>
+
+  <ul>
+    <li>添加一张或多张你的应用在平板电脑上运行时的截图。为了最有效地吸引平板电脑用户,建议你将平板电脑界面与手机界面的推广事宜尽量等同视之。
+    </li>
+
+    <li>尽早在视频中展示你的平板电脑界面。不要想当然地以为平板电脑用户会耐心看完手机界面上的功能演示。最好在前 10 秒内或者在介绍手机界面的同时就展示平板电脑界面,以立即吸引用户的注意力。
+    </li>
+
+    <li>为了让用户了解你是在展示平板电脑界面,请加入几张应用在手持平板电脑设备上运行时的截图。
+    </li>
+
+    <li>在视频的讲解或旁白中,着重介绍应用的平板电脑界面。
+    </li>
+  </ul>
+
+  <h4>
+    在推广活动中突显你的平板电脑界面
+  </h4>
+
+  <p>
+    确保平板电脑用户可以通过你的推广活动、网站、社交信息、广告及其他地方了解你的平板电脑界面。下面是一些建议:
+  </p>
+
+  <ul>
+    <li>安排营销或宣传活动,着重介绍你的应用在平板电脑上的使用情况。</li>
+
+    <li>在推广活动中展示你的平板电脑应用最突出的优势,使用 <a href="{@docRoot}distribute/tools/promote/device-art.html">Device Art Generator</a> 快速生成一张应用在 7 英寸或 10 英寸平板电脑上运行时的高品质推广图片,屏幕方向自定,有无下拉阴影和屏幕反光均可。这项操作非常简单,只需截图、拖放即可。
+    </li>
+
+    <li>进行在线推广时加入 Google Play 徽章,让用户可以直接转入商店查看你应用的商品详情。你可以使用<a href="{@docRoot}distribute/tools/promote/badges.html">徽章生成器</a>生成各种语言的徽章。
+    </li>
+  </ul>
+
+  <h3 class="rel-resources clearfloat">相关资源</h3>
+
+  <div class="resource-widget resource-flow-layout col-13" data-query="collection:distribute/essentials/tabletguidelines/showcase/zhcn" data-sortorder="-timestamp" data-cardsizes="9x3,9x3,9x3,9x3" data-maxresults="6"></div>
+
+  <div class="headerLine">
+    <h2 id="google-play-best-practices">
+      12. 遵循在 Google Play 中发布应用的最佳做法</h2>
+
+
+  </div>
+
+  <p>
+    下面是一些在 Google Play 中成功发布的平板电脑应用的最佳做法。
+  </p>
+
+  <div>
+    <img class="border-img" src="{@docRoot}images/gp-tablet-quality-5.jpg" />
+  </div>
+
+  <h4 id="google-play-optimization-tips">
+    检查应用的优化提示
+  </h4>
+
+  <p>Google Play 开发者控制台现在提供”优化提示“页,可让你快速了解你的应用是否很好地遵循了有关平板电脑应用分发和质量的基本指南。要访问此页面,请登录到开发者控制台,通过“所有应用”加载相应应用,然后点击左侧导航栏中的“优化提示”。</p>
+
+  <div class="sidebox-wrapper">
+  <div class="sidebox">
+  <h2>如何发送反馈意见</h2>
+
+  <p>请使用下面的链接发送反馈意见或申请人工审核你的“优化提示”。</p>
+
+  <p>在发送反馈意见之前,请务必先仔细阅读平板电脑应用质量指南的相关部分。</p>
+
+  <p><strong><a href="https://support.google.com/googleplay/android-developer/contact/tabletq" target="_googleplay" style="white-space:nowrap">平板电脑专用联系表单 »</a></strong></p>
+  </div>
+  </div>
+
+  <p>开发者控制台通过运行一系列检查来验证你的应用是否符合基本质量标准,然后为其创建“优化提示”页。如果发现任何问题,开发者控制台会在“优化提示”页上列出“待优化事项”来提醒你。</p>
+
+  <p>如果你为应用开发了平板电脑版本,请务必访问“优化提示”页,了解你应用的基本检查结果。如果此页中列出问题,建议你在应用中进行解决,并上传一个新的二进制文件以供分发(如果需要)。</p>
+
+  <p>如果你认为“优化提示”页列出的“待优化事项”不适用于你的应用或会影响你的应用在平板电脑上的运行质量,请使用<a href="https://support.google.com/googleplay/android-developer/contact/tabletq" target="_googleplay" style="white-space:nowrap">平板电脑专用联系表单 »</a> 通知我们。我们会审核你的应用并视情况更新你的“优化提示”页。</p>
+
+
+  <h4>确认应用的适配设备</h4>
+
+  <p>
+    将应用上传至<a href="https://play.google.com/apps/publish/">开发者控制台</a>后,请检查 APK 的“受支持设备”列表,确保未遗漏任何应用要适配的平板电脑设备。
+  </p>
+
+  <h4>用一个 APK 进行分发</h4>
+
+  <p>
+    建议你针对所有屏幕尺寸(手机和平板电脑)用一个 APK 发布你的应用,并使用一份 Google Play 商品详情。这种做法有以下几大优点:
+  </p>
+
+  <ul style="margin-top:.25em">
+    <li>便于用户通过搜索、浏览或推广信息找到你的应用。
+    </li>
+
+    <li>便于用户在更换新设备后自动恢复你的应用。
+    </li>
+
+    <li>你的应用在所有设备上的评分和下载统计信息会得到汇总统计。
+    </li>
+
+    <li>再用一份商品详情发布平板电脑应用可能会降低你品牌的评分。
+    </li>
+  </ul>
+
+  <p>
+    如果需要,你也可以选择使用<a href="{@docRoot}google/play/publishing/multiple-apks.html">多 APK 支持</a>提供你的应用,不过在大多数情况下,强烈建议你使用一个 APK 覆盖所有设备。
+  </p>
+
+  <h3 class="rel-resources clearfloat">相关资源</h3>
+  <div class="resource-widget resource-flow-layout col-13" data-query="collection:distribute/essentials/tabletguidelines/googleplay" data-sortorder="-timestamp" data-cardsizes="9x3" data-maxresults="6"></div>
+
+
+  <div class="headerLine">
+    <h2 id="test-environment">
+      为平板电脑创建测试环境
+    </h2>
+
+
+  </div>
+
+  <p>
+    使用合适的硬件或模拟器环境进行测试,既评估你的平板电脑应用的核心质量,也评估应用在平板电脑上的质量。
+  </p>
+
+  <p>
+    要测试应用对核心质量标准的遵循情况,可以使用<a href="{@docRoot}distribute/essentials/quality/core.html#test-environment">建议的测试环境</a>,要测试应用在平板电脑上的质量,还需要在建议环境的基础上加入中等大小的平板电脑以及具有更多或更少硬件/软件功能的平板电脑。
+  </p>
+
+  <p class="table-caption"><strong>表 1</strong>:下表列出了两种类型的设备,及其采用的平台版本、屏幕配置和硬件功能配置。典型的平板电脑测试环境可以包含其中的一类或两类设备。</p>
+
+  <table>
+  <tr>
+  <th>类型</th>
+  <th>尺寸</th>
+  <th>密度</th>
+  <th>版本</th>
+  <th>AVD 主题</th>
+  </tr>
+
+  <tr>
+  <td>7 英寸平板电脑</td>
+  <td><span style="white-space:nowrap"><code>large</code> 或</span><br /><code>-sw600</code></td>
+  <td><code>hdpi</code>、<br /><code>tvdpi</code></td>
+  <td>Android 4.0 及更高版本(API 级别 14 及更高)</td>
+  <td>WXGA800-7in</td>
+  </tr>
+  <tr>
+  <td><span style="white-space:nowrap">10 英寸</span>平板电脑</td>
+  <td><span style="white-space:nowrap"><code>xlarge</code> 或</span><br /><code>-sw800</code></td>
+  <td><code>mdpi</code>、<br /><code>hdpi</code>、<br /><code>xhdpi</code></td>
+  <td>Android 3.2 及更高版本(API 级别 13 及更高)</td>
+  <td>WXGA800</td>
+  </tr>
+  </table>
+
+  <div class="headerLine"><h2 id="related-resources">相关资源</h2></div>
+
+  <div class="resource-widget resource-flow-layout col-13" data-query="collection:distribute/essentials/tabletguidelines/zhcn" data-sortorder="-timestamp" data-cardsizes="9x3" data-maxresults="6"></div>
+      </p></div>
diff --git a/docs/html-intl/intl/zh-cn/distribute/resources.jd b/docs/html-intl/intl/zh-cn/distribute/resources.jd
new file mode 100644
index 0000000..71bd466
--- /dev/null
+++ b/docs/html-intl/intl/zh-cn/distribute/resources.jd
@@ -0,0 +1,56 @@
+page.title=你的语言的资源
+page.viewport_width=970
+section.landing=true
+header.hide=1
+nonavpage=true
+page.metaDescription=我们翻译了以下一些网站资源,希望能帮助你开始设计、开发和发布你的Android应用程序到全球。
+
+@jd:body
+
+    <div class="jd-descr" itemprop="articleBody">
+    <div class="resource-widget resource-carousel-layout col-16" 
+    style="height:420px;margin-top:0px;padding-top:0"
+    data-query="collection:overview/carousel/zhcn"
+    data-sortOdrder="-timestamp"
+    data-maxResults="4"></div>
+
+<div class="dynamic-grid">
+
+<h2>你的语言的资源</h2>
+<p style="margin-bottom:2em;">我们翻译了以下一些网站资源,希望能帮助你开始设计、开发和发布你的Android应用程序到全球。</p>
+
+<h3 style="font-size:18px;font-weight:bold">检查清单</h3>
+
+  <div class="resource-widget resource-flow-layout col-16"
+    data-query="collection:overview/zhcn/1"
+    data-sortOrder=""
+    data-cardSizes="6x6,6x6,6x2x3"
+    data-maxResults="5"></div>
+
+<h3 style="font-size:18px;font-weight:bold">Google Play 应用内结算</h3>
+
+  <div class="resource-widget resource-flow-layout col-16"
+    data-query="collection:overview/zhcn/2"
+    data-sortOrder=""
+    data-cardSizes="6x6,6x6,6x2x3"
+    data-maxResults="5"></div>
+
+<h3 style="font-size:18px;font-weight:bold">工具</h3>
+
+  <div class="resource-widget resource-flow-layout col-16"
+    data-query="collection:overview/zhcn/3"
+    data-sortOrder=""
+    data-cardSizes="6x2x3,6x6,6x6"
+    data-maxResults="5"></div>
+
+<h3 style="font-size:18px;font-weight:bold">培训</h3>
+
+  <div class="resource-widget resource-flow-layout col-16"
+    data-query="collection:overview/zhcn/4"
+    data-sortOrder=""
+    data-cardSizes="6x6,12x2x3"
+    data-maxResults="5"></div>
+
+
+</div>
+
diff --git a/docs/html-intl/intl/zh-cn/distribute/tools/index.jd b/docs/html-intl/intl/zh-cn/distribute/tools/index.jd
new file mode 100644
index 0000000..cf16d07
--- /dev/null
+++ b/docs/html-intl/intl/zh-cn/distribute/tools/index.jd
@@ -0,0 +1,56 @@
+page.title=工具与参考
+section.landing=true
+nonavpage=true
+
+@jd:body
+
+<p>
+此页面的资源将帮助你发布你的应用和游戏,获取用户,并从中获利。
+</p>
+
+<div class="dynamic-grid">
+
+<h3 style="font-size:18px;font-weight:bold">发布与推出</h3>
+  <div class="resource-widget resource-flow-layout landing col-16"
+    data-query="collection:distribute/tools/checklists/zhcn"
+    data-cardSizes="9x6"
+    data-maxResults="2">
+  </div>
+
+<h3 style="font-size:18px;font-weight:bold">宣传工具</h3>
+  <div class="resource-widget resource-flow-layout landing col-16"
+    data-query="collection:distribute/tools/promote/zhcn"
+    data-cardSizes="6x6"
+    data-maxResults="3">
+  </div>
+
+<h3 style="font-size:18px;font-weight:bold">开发者支持</h3>
+  <div class="resource-widget resource-flow-layout landing col-16"
+    data-query="collection:distribute/tools/support/zhcn"
+    data-cardSizes="6x6"
+    data-maxResults="3">
+  </div>
+
+<h3 style="font-size:18px;font-weight:bold">开发者消息</h3>
+  <div class="resource-widget resource-flow-layout landing col-16"
+    data-query="collection:distribute/tools/news"
+    data-cardSizes="9x6"
+    data-maxResults="2">
+  </div>
+
+<h3 style="font-size:18px;font-weight:bold">更多</h3>
+  <div class="resource-widget resource-flow-layout landing col-16"
+    data-query="collection:distribute/tools/more/zhcn"
+    data-cardSizes="6x6"
+    data-maxResults="3">
+  </div>
+
+<!--  <h3>Related Resources</h3>
+  <div class="resource-widget resource-stack-layout col-16"
+    data-query="tag:developersupport"
+    data-sortOrder="-timestamp"
+    data-numStacks="3"
+    data-maxResults="6">
+  </div> -->
+
+</div>
diff --git a/docs/html-intl/intl/zh-cn/distribute/tools/launch-checklist.jd b/docs/html-intl/intl/zh-cn/distribute/tools/launch-checklist.jd
new file mode 100644
index 0000000..f641dbe
--- /dev/null
+++ b/docs/html-intl/intl/zh-cn/distribute/tools/launch-checklist.jd
@@ -0,0 +1,709 @@
+page.title=发布检查清单 
+page.metaDescription=向用户发布应用的完整过程的基本概述。在开发的早期阅读这份清单将有助于你做出规划并在Google Play成功发布应用。
+page.image=/distribute/images/launch-checklist.jpg
+
+@jd:body
+
+    <div id="qv-wrapper">
+  <div id="qv" style="width:280px">
+    <h2>检查清单</h2>
+    <ol>
+      <li><a href="#understand-publishing">1. 了解发布流程</a></li>
+      <li><a href="#understand-policies">2. 了解 Google Play 政策</a></li>
+      <li><a href="#test-quality">3. 测试应用的核心质量</a></li>
+      <li><a href="#determine-rating">4. 确定内容分级</a></li>
+      <li><a href="#determine-country">5. 确定分发国家/地区</a></li>
+      <li><a href="#confirm-size">6. 确认总大小</a></li>
+      <li><a href="#confirm-platform">7. 确认平台和屏幕范围</a></li>
+      <li><a href="#decide-price">8. 决定免费或付费</a></li>
+      <li><a href="#consider-billing">9. 使用应用内结算</a></li>
+      <li><a href="#set-prices">10. 设置商品价格</a></li>
+      <li><a href="#start-localization">11. 开始本地化</a></li>
+      <li><a href="#prepare-graphics">12. 准备宣传图片、屏幕截图和视频</a></li>
+      <li><a href="#build-upload">13. 构建发布版 APK</a></li>
+      <li><a href="#plan-beta">14. 计划 Beta 版的发布</a></li>
+      <li><a href="#complete-details">15. 填写商品详情</a></li>
+      <li><a href="#use-badges">16. 使用 Google Play 徽章和链接</a></li>
+      <li><a href="#final-checks">17. 最终检查和发布</a></li>
+      <li><a href="#support-users">18. 在发布后为用户提供支持</a></li>
+    </ol>
+  </div>
+</div>
+
+<div class="top-right-float" style="width:194px"><img src="{@docRoot}distribute/images/launch-checklist.jpg" /></div>
+
+<p>
+  在 Google Play 上发布应用以将其分发给用户之前,你需要准备好要发布的应用、进行测试并准备相关宣传材料。
+</p>
+
+<p>
+  本页面旨在帮助你了解发布流程,以及在 Google Play 上成功发布商品所需的准备工作,另外还总结了一些需要你在通过 Google Play 发布应用之前完成的任务,例如创建已签名的发布版应用包 (APK)、了解应用的要求以及为各个应用创建商品页和图形资源。
+</p>
+
+<p>
+  准备和发布任务已按先后顺序列出,这是为了让你对任务执行顺序有个大致了解。不过,你可以按适合自己的顺序来执行这些任务,也可以根据具体情况跳过某些步骤。
+</p>
+
+<p>
+  在完成发布之前的各项工作时,你可以使用各种支持资源。每一步中都提供了相关链接。
+</p>
+
+<div class="headerLine">
+  <h2 id="understand-publishing">
+    1. 了解发布流程
+  </h2>
+
+
+</div>
+
+<p>
+  在开始执行此检查清单中的各个步骤之前,你应抽时间查看和了解整个发布工作流程,并熟悉该流程的工作原理。特别要注意的是,你或你的开发小组需要按照所有 Android 应用通用的流程准备应用以进行发布。<a href="/tools/publishing/publishing_overview.html">发布工作流程文档</a>中详细说明了发布的工作原理以及如何准备 APK 以进行发布。
+</p>
+
+<p>
+  当你基本熟悉发布流程后,请继续阅读以了解在 Google Play 上发布应用时应注意哪些问题。
+</p>
+
+<h3 class="rel-resources clearfloat">相关资源</h3>
+
+<div class="resource-widget resource-flow-layout col-13" data-query="collection:distribute/toolsreference/launchchecklist/understanding/zhcn" data-sortorder="-timestamp" data-cardsizes="9x3,9x3,6x3,9x3,9x3,9x3" data-maxresults="6">
+</div>
+
+<div class="headerLine">
+  <h2 id="understand-policies">
+    2. 了解 Google Play 政策和协议
+  </h2>
+
+
+</div>
+
+<p>
+  请务必了解并遵守你在注册时接受的 Google Play 计划政策。Google Play 要求强制执行这些政策,违反任何一条都可能导致你的应用被暂停。如果屡次违规,你的开发者帐户将被终止。
+</p>
+
+<h3 class="rel-resources clearfloat">相关资源</h3>
+
+<div class="resource-widget resource-flow-layout col-13" data-query="collection:distribute/toolsreference/launchchecklist/policies/zhcn" data-sortorder="-timestamp" data-cardsizes="6x3" data-maxresults="6">
+</div>
+
+<div class="headerLine">
+  <h2 id="test-quality">
+    3. 测试质量
+  </h2>
+
+
+</div>
+
+<p>
+  在 Google Play 上发布应用之前,请务必确保这些应用在所有目标设备上都符合适用于所有 Android 应用的基本质量要求。你可以设置测试环境,然后测试应用是否符合一系列<strong>适用于所有应用的质量标准</strong>,以此来检查应用的质量。有关完整信息,请参阅<a href="/distribute/essentials/quality/core.html">应用核心质量指南</a>。
+</p>
+
+<p>
+  如果你的应用适用于平板电脑,请确保其能为你的平板电脑用户提供引人入胜的丰富体验。请参阅<a href="/distribute/essentials/quality/tablets.html">平板电脑应用的质量</a>指南,了解如何针对平板电脑优化应用的相关建议。
+</p>
+
+<p>
+  如果你要将应用发布到 Google Play for Education,则需确保这些应用适合中小学课堂且能提供非凡的教育价值。请参阅<a href="/distribute/essentials/gpfe-guidelines.html">教育指南</a>,了解教育类应用应具备哪些特征。
+</p>
+
+<h3 class="rel-resources clearfloat">相关资源</h3>
+
+<div class="resource-widget resource-flow-layout col-13" data-query="collection:distribute/toolsreference/launchchecklist/quality/zhcn" data-sortorder="-timestamp" data-cardsizes="6x3,6x3,6x3,9x3,9x3,9x3" data-maxresults="6">
+</div>
+
+<div class="headerLine">
+  <h2 id="determine-rating">
+    4. 确定应用的内容分级
+  </h2>
+
+
+</div>
+
+<p>
+  Google Play 要求你为应用设置内容分级,以便 Google Play 用户了解应用的心智成熟度级别。发布应用之前,你应先确认自己要使用哪一分级。有以下四种内容分级可供选择:
+</p>
+
+<ul>
+  <li>
+    <p>所有人</p>
+  </li>
+
+  <li>
+    <p>心智成熟度-低</p>
+  </li>
+
+  <li>
+    <p>心智成熟度-中</p>
+  </li>
+
+  <li>
+    <p>心智成熟度-高</p>
+  </li>
+</ul>
+
+<p>
+  Android 用户可以在其 Android 设备上设置要浏览的心智成熟度级别,Google Play 会根据该设置对应用进行过滤。因此,你选择的内容分级会影响应用对用户的分发情况。你可以在开发者控制台中为应用指定(或更改)内容分级,无需在应用二进制文件中进行任何更改。
+</p>
+
+<h3 class="rel-resources clearfloat">相关资源</h3>
+
+<div class="resource-widget resource-flow-layout col-13" data-query="collection:distribute/toolsreference/launchchecklist/rating/zhcn" data-sortorder="-timestamp" data-cardsizes="9x3,6x3,6x3,9x3,9x3,9x3" data-maxresults="6">
+</div>
+
+<div class="headerLine">
+  <h2 id="determine-country">
+    5. 确定分发国家/地区
+  </h2>
+
+
+</div>
+
+<p>
+  借助 Google Play,你可以控制将应用分发到哪些国家和地区。为了尽可能扩大覆盖面和潜在客户群,你通常会希望将应用分发到所有适用的国家和地区。不过,由于业务需求、应用要求或发布时的依赖关系,你可能需要从分发范围中排除一个或多个国家/地区。
+</p>
+
+<p>
+  请务必尽早确定目标国家/地区,因为这可能会影响:
+</p>
+
+<ul>
+  <li>
+    <p>是否需要对应用中的资源进行本地化。
+    </p>
+  </li>
+
+  <li>
+    <p>是否需要对开发者控制台中的应用说明进行本地化。
+    </p>
+  </li>
+
+  <li>
+    <p>某些国家/地区是否对应用有相应的法律要求。
+    </p>
+  </li>
+
+  <li>
+    <p>时区支持、本地价格等。
+    </p>
+  </li>
+</ul>
+
+<p>
+  确定目标国家/地区后,你应该评估应用及其 Google Play 商品详情是否需要本地化,然后在预订的发布日期之前就开始本地化工作。
+</p>
+
+<p>
+  请参阅<a href="/distribute/tools/localization-checklist.html">本地化检查清单</a>,了解本地化流程中的关键步骤和注意事项。
+</p>
+
+<h3 class="rel-resources clearfloat">相关资源</h3>
+
+<div class="resource-widget resource-flow-layout col-13" data-query="collection:distribute/toolsreference/launchchecklist/country/zhcn" data-sortorder="-timestamp" data-cardsizes="9x3,9x3,6x3,9x3,9x3,9x3" data-maxresults="6">
+</div>
+
+<div class="headerLine">
+  <h2 id="confirm-size">
+    6. 确认应用的总大小
+  </h2>
+
+
+</div>
+
+<p>
+  应用的总大小会对应用的设计以及在 Google Play 上的发布产生影响。目前,在 Google Play 上发布的 APK 最大不得超过 <strong>50MB</strong>。如果你的应用超过该上限或要提供辅助下载,则可以使用 <a href="/google/play/expansion-files.html">APK 扩展文件</a>。Google Play 会将扩展文件免费托管在其服务器基础架构上,并自动处理这些文件下载到设备的过程。
+</p>
+
+<ul>
+  <li>
+    <p>在 Google Play 上发布的 APK 最大不得超过 50MB。
+    </p>
+  </li>
+
+  <li>
+    <p>对于每个 APK,你最多可以使用两 (2) 个 APK 扩展文件,每个都不得超过 2GB。
+    </p>
+  </li>
+</ul>
+
+<p>
+  在分发大型应用时,使用 APK 扩展文件是一种经济高效的简便方法。不过,使用 APK 扩展文件需要你在应用二进制文件中稍作更改,因此你需要在创建发布版 APK 之前完成这些更改。
+</p>
+
+<p>
+  为了尽可能精简应用的二进制文件,请务必在构建发布版 APK 时运行 <a href="/tools/help/proguard.html">Proguard</a> 工具或类似的代码混淆工具。
+</p>
+
+<h3 class="rel-resources clearfloat">相关资源</h3>
+
+<div class="resource-widget resource-flow-layout col-13" data-query="collection:distribute/toolsreference/launchchecklist/size/zhcn" data-sortorder="-timestamp" data-cardsizes="9x3,9x3,6x3,9x3,9x3,9x3" data-maxresults="6">
+</div>
+
+<div class="headerLine">
+  <h2 id="confirm-platform">
+    7. 确认应用的平台和屏幕兼容性范围
+  </h2>
+
+
+</div>
+
+<p>发布应用之前,请务必确保你的应用可在目标 Android 平台版本和设备屏幕尺寸上正常运行。
+</p>
+
+<p>
+  从应用兼容性角度来看,Android 平台版本由 <a href="/guide/topics/manifest/uses-sdk-element.html#ApiLevels">API 级别</a>定义。你应该使用 <a href="/guide/topics/manifest/uses-sdk-element.html">&lt;minSdkVersion&gt;</a> 确认你的应用兼容的最低版本,因为这将影响已发布的应用在 Android 设备上的分发情况。
+</p>
+
+<p>
+  对于屏幕尺寸,你应该确认应用可在支持的各种屏幕尺寸和像素密度上正常运行且拥有精美外观。你还应按照<a href="/guide/practices/screens_support.html">支持跨屏</a>中给出的建议,为多种屏幕尺寸提供相应的支持。如果你无法提供跨屏支持,请使用 <a href="/guide/topics/manifest/supports-screens-element.html">&lt;supports-screens&gt;</a> 声明应用支持的最小屏幕尺寸。Google Play 会据此对你的应用做出限制,只将其提供给屏幕尺寸大于或等于已声明尺寸的设备。
+</p>
+
+<p>
+  要更好地了解目前 Android 平台版本和屏幕尺寸在所有 Android 设备上的普及情况,请参阅<a href="/about/dashboards/index.html">设备信息中心</a>图表。
+</p>
+
+<h3 class="rel-resources clearfloat">相关资源</h3>
+
+<div class="resource-widget resource-flow-layout col-13" data-query="collection:distribute/toolsreference/launchchecklist/platform" data-sortorder="-timestamp" data-cardsizes="6x3,6x3,6x3" data-maxresults="6">
+</div>
+
+<div class="headerLine">
+  <h2 id="decide-price">
+    8. 决定你的应用是免费还是付费
+  </h2>
+
+
+</div>
+
+<div class="figure">
+  <img src="{@docRoot}images/gp-launch-checklist-1.png" />
+</div>
+
+<p>
+  在 Google Play 上,你可以发布免费下载的应用和付费下载的应用。免费应用可供 Google Play 中的所有 Android 用户下载。付费应用仅供其所在国家/地区支持付费下载且已在 Google Play 中注册了付款方式(例如信用卡或运营商直接代扣)的用户下载。
+</p>
+
+<p>
+  决定你的应用是否免费非常重要,因为在 Google Play 上,<strong>免费应用必须始终免费</strong>。
+</p>
+
+<ul>
+  <li>
+    <p>将应用发布为免费应用后,你无法再将其改成付费应用。不过,你仍能通过 Google Play 的<a href="/google/play/billing/index.html">应用内结算</a>服务销售<a href="/google/play/billing/billing_overview.html#products">应用内商品</a>和<a href="/google/play/billing/billing_subscriptions.html">订阅</a>。
+    </p>
+  </li>
+
+  <li>
+    <p>如果你将应用发布为付费应用,则可以随时将其改成免费应用(<strong>但之后无法再将其改回为付费应用</strong>)。<em></em>你也可以销售应用内商品和订阅。
+    </p>
+  </li>
+</ul>
+
+<p>
+  如果你的应用是付费应用或者你要销售应用内商品,你需要先<a href="https://developers.google.com/wallet/digital/training/getting-started/merchant-setup">设置 Google 电子钱包商家帐户</a>,然后才能发布应用。
+</p>
+
+<h3 class="rel-resources clearfloat">相关资源</h3>
+
+<div class="resource-widget resource-flow-layout col-13" data-query="collection:distribute/toolsreference/launchchecklist/price/zhcn" data-sortorder="-timestamp" data-cardsizes="9x3,9x3,6x3,9x3,9x3,9x3" data-maxresults="6">
+</div>
+
+<div class="headerLine">
+  <h2 id="consider-billing">
+    9. 考虑使用应用内结算
+  </h2>
+
+
+</div>
+
+<p>
+  借助 Google Play <a href="/google/play/billing/index.html">应用内结算</a>,你可以在应用中销售数字内容。你可以使用该服务销售众多内容,包括可下载内容(例如媒体文件或照片)和虚拟内容(例如游戏关卡或魔药)。借助应用内结算服务,你可以在应用内销售一次性购买的商品,还可以销售订阅。这有助于你在应用安装后的整个生命周期内都能获利。
+</p>
+
+<p>
+  如果你希望找到更多方法以通过应用获利并建立与用户的互动,则应考虑使用“应用内结算”或“Instant Buy”服务。这些服务深受用户和开发者的欢迎。要使用“应用内结算”和“Instant Buy”,你需要对应用的二进制文件进行更改。因此,你需要先完成更改并测试实施情况,然后才能创建发布版 APK。
+</p>
+
+<h3 class="rel-resources clearfloat">相关资源</h3>
+
+<div class="resource-widget resource-flow-layout col-13" data-query="collection:distribute/toolsreference/launchchecklist/purchasemethod/zhcn" data-sortorder="-timestamp" data-cardsizes="9x3,9x3,6x3,9x3,9x3,9x3" data-maxresults="6">
+</div>
+
+<div class="headerLine">
+  <h2 id="set-prices">
+    10. 设置商品价格
+  </h2>
+
+
+</div>
+
+<p>
+  如果你的应用是付费应用或者你要销售应用内商品或实体商品,Google Play 可让你针对全球市场的用户为商品设置以不同货币为单位的价格。你可以分别使用不同货币设置价格,以便根据市场情况和汇率灵活调整你的价格。
+</p>
+
+<p>
+  发布应用之前,请想好如何为你的商品定价及以不同货币为单位的价格各是多少。之后,你可以通过开发者控制台设置以所有可用货币为单位的各种价格。
+</p>
+
+<h3 class="rel-resources clearfloat">相关资源</h3>
+
+<div class="resource-widget resource-flow-layout col-13" data-query="collection:distribute/toolsreference/launchchecklist/setprice/zhcn" data-sortorder="-timestamp" data-cardsizes="9x3,9x3,9x3,9x3,9x3,9x3" data-maxresults="6">
+</div>
+
+<div class="headerLine">
+  <h2 id="start-localization">
+    11. 开始本地化
+  </h2>
+
+
+</div>
+
+<p>
+  确定目标国家/地区后,你最好评估一下你的本地化需求,确保你的应用已国际化,并在预订的发布日期之前就开始本地化工作。
+</p>
+
+<p>
+  除了应用设计外,你至少还应考虑本地化工作中的三个方面:
+</p>
+
+<ul>
+  <li>
+    <p>本地化应用中的字符串、图片及其他资源。
+    </p>
+  </li>
+
+  <li>
+    <p>本地化应用在 Google Play 商店中的商品详情。
+    </p>
+  </li>
+
+  <li>
+    <p>本地化应用的商品详情中随附的图形资源、屏幕截图和视频。
+    </p>
+  </li>
+</ul>
+
+<p>
+  请参阅<a href="/distribute/tools/localization-checklist.html">本地化检查清单</a>,了解本地化流程中的关键步骤和注意事项。
+</p>
+
+<p>
+  要本地化你的商品详情,请先创建并完成应用标题、说明和宣传文字。将所有这些信息收集起来,然后发送给相关人员进行本地化。在应用更新时,你也可以选择翻译“最近更改”的文本。之后,你可以在开发者控制台中添加本地化的商品详情,或者选择让 Google Play 将你的商品详情自动翻译成你支持的语言。
+</p>
+
+<p>
+  要让你的应用详情吸引全球的用户,关键是创建本地化版本的宣传图片、屏幕截图和视频。例如,应用的置顶大图中可能包含文字,这些文字应翻译成相应语言以获得最佳效果。你可以为每种目标语言创建相应版本的宣传图片,并将它们上传到开发者控制台。如果你提供宣传视频,则可以创建本地化版本的视频,然后添加指向各目标语言版本的视频的链接。
+</p>
+
+<p>
+  完成翻译后,根据需要将翻译放入应用的资源中并测试能否正常加载。在上传资源和配置商品详情时,请保存应用的翻译版商品详情以供日后使用。
+</p>
+
+<h3 class="rel-resources clearfloat">相关资源</h3>
+
+<div class="resource-widget resource-flow-layout col-13" data-query="collection:distribute/toolsreference/launchchecklist/localization/zhcn" data-sortorder="-timestamp" data-cardsizes="9x3,9x3,6x3,9x3,9x3,9x3" data-maxresults="6">
+</div>
+
+<div class="headerLine">
+  <h2 id="prepare-graphics">
+    12. 准备宣传图片、屏幕截图和视频
+  </h2>
+
+
+</div>
+
+<p>
+  在 Google Play 上发布应用时,你可以提供各种高品质的图形资源,用来宣传你的应用或品牌。应用发布后,这些资源将显示在你的商品详情页、搜索结果及其他位置上。这些图形资源是商品详情页成功与否的关键,它们能吸引用户的注意力并让用户参与互动。因此,你应考虑聘请专业人士为你制作这些资源。屏幕截图和视频也相当重要,因为它们将展示应用的外观、使用或操作方式及其独特之处。
+</p>
+
+<p>
+  所有的图形资源均应精心设计以便足够醒目,并以多彩、有趣的方式突显你的应用或品牌。这些资源应该采用相同的徽标和图标,以便用户在下载应用后可以在“所有应用”启动器中找到你的应用。你的图形资源还应与你发布的所有应用的图形资源(也会显示在商品详情页上供用户查看)相互配套。
+</p>
+
+<p>
+  为了帮你将应用更有效地推广给全球用户,Google Play 允许你创建本地化版本的宣传图片、屏幕截图和视频并上传到开发者控制台中。当用户访问你应用的商品详情时,Google Play 会向其展示你针对该用户所用语言提供的宣传图片、屏幕截图和视频。
+</p>
+
+<p>
+  要本地化你的宣传图片,你可以翻译所有嵌入的文本、使用不同的图像或展示方式,或者改变你的营销方式,以尽可能满足使用特定语言的用户的需求。例如,如果你的置顶大图或宣传图片中包含嵌入的商品名称或宣传口号,则可以将名称或口号翻译成相应语言,然后添加到本地化版本的宣传图片中。
+</p>
+
+<p>
+  由于本地化的图形资源和视频非常重要,因此你应在预订的发布日期之前就开始创建并进行本地化。
+</p>
+
+<h3 class="rel-resources clearfloat">相关资源</h3>
+
+<div class="resource-widget resource-flow-layout col-13" data-query="collection:distribute/toolsreference/launchchecklist/graphics/zhcn" data-sortorder="-timestamp" data-cardsizes="9x3,9x3,6x3,9x3,9x3,9x3" data-maxresults="6">
+</div>
+
+<div class="headerLine">
+  <h2 id="build-upload">
+    13. 构建并上传发布版 APK
+  </h2>
+
+
+</div>
+
+<p>
+  当确定你的应用符合界面、兼容性和质量要求后,你可以构建应用的发布版本。之后,你可以将发布版 APK 上传到开发者控制台并分发给用户。
+</p>
+
+<p>
+  所有应用的发布版 APK 准备流程都一样,无论应用采用哪种分发方式。一般情况下,该流程包括基本的代码清理和优化、构建和使用发布密钥签名以及最后的测试这几部分。
+</p>
+
+<p>
+  如需了解如何创建发布版应用的完整详情,请参阅<a href="/tools/publishing/preparing.html">准备发布</a>。
+</p>
+
+<p>
+  准备好发布版 APK 后,你可以将其上传到开发者控制台。如果需要,你可以在发布之前将 APK 替换为较新的版本。
+</p>
+<!--<h3 class="rel-resources clearfloat">Related resources</h3>
+
+<div class="resource-widget resource-flow-layout col-13" data-query="collection:distribute/toolsreference/launchchecklist/build/zhcn" data-sortorder="-timestamp" data-cardsizes="9x3,9x3,6x3,9x3,9x3,9x3" data-maxresults="6"></div>-->
+
+<div class="headerLine">
+  <h2 id="plan-beta">
+    14. 计划 Beta 版的发布
+  </h2>
+
+
+</div>
+
+<div class="sidebox-wrapper" style="float:right">
+  <div class="sidebox">
+    <h2>
+      简单的 Beta 测试
+    </h2>
+
+    <p>
+      Google Play 允许你在世界范围内设立 Alpha 版和 Beta 版测试者小组。下次登录到开发者控制台时请别忘记了解这一强大的功能。
+    </p>
+  </div>
+</div>
+
+<p>
+  在发布应用之前收集用户的真实反馈是很有必要的,特别是发布新应用时,应该收集更多反馈。我们强烈建议你向主要目标市场的用户分发应用的预览版本,并让这些用户能够方便地向你提供反馈和报告错误。
+</p>
+
+<p>
+  Google Play 可以帮助你为应用设置 Beta 测试计划。登录到开发者控制台并上传你的 APK 之后,你可以设立用户小组,以便进行应用的 Alpha 版和 Beta 版测试。你可以先进行小范围的 Alpha 版测试,然后推进到更大范围的 Beta 版测试。用户被添加到小组之后,他们可以访问你应用的商品详情并安装应用。
+  <strong>Alpha 或 Beta 版本的用户不能留下评论或评分</strong>,因此你在 Google Play 上的<strong>评分不会受到不利影响</strong>。你需要安排具体途径以便用户提供测试反馈,例如 Google 论坛或 Google+。
+</p>
+
+<p>
+  收到的反馈可以帮助你调整界面、翻译和商品详情,以确保为用户提供优质体验。
+</p>
+<!-- Related resources
+
+<table>
+  <tr>
+    <td>Beta-testing and Staged Rollouts
+See how you can facilitate testing with Google Play.</td>
+  </tr>
+</table> -->
+
+<div class="headerLine">
+  <h2 id="complete-details">
+    15. 填写应用的商品详情
+  </h2>
+
+
+</div>
+
+<p>
+  在 Google Play 上,应用的商品信息会在用户的 Android 设备或网络上的以下页面中显示给用户:应用的商品详情页;用户为详细了解应用而访问的页面;用户决定购买或下载应用的页面上。
+</p>
+
+<p>
+  为了让你宣传应用以及在商品详情页上与用户互动,Google Play 提供了许多方式,包括多彩的图片、屏幕截图和视频,本地化的说明、发布详情以及指向你其他应用的链接。当你准备发布应用时,请确保充分利用商品详情页提供的所有资源,让你的应用尽可能地引人入胜。
+</p>
+
+<p>
+  你应在预订的发布日期之前就开始规划商品页,准备本地化的说明以及高品质的图形资源、屏幕截图和视频等。
+</p>
+
+<p>
+  当临近预订的发布日期时,你应该熟悉开发者控制台中与商品详情配置页相关的所有字段、选项和资源。收集此页面上的信息和资源时,请将它们输入或上传到开发者控制台中,直到完成该页面,可以进行发布。
+</p>
+
+<p>
+  在开发者控制台中为应用设置地理位置定位后,请记得针对所有支持的目标语言添加本地化的商品详情、宣传图片等。
+</p>
+
+<p>
+  如果你的应用适用于平板电脑,请确保至少加入一张应用在平板电脑上运行时的屏幕截图,并在应用说明、版本备注、宣传活动及其他位置注重介绍你的应用支持平板电脑。
+</p>
+
+<h3 class="rel-resources clearfloat">相关资源</h3>
+
+<div class="resource-widget resource-flow-layout col-13" data-query="collection:distribute/toolsreference/launchchecklist/productdetails/zhcn" data-sortorder="-timestamp" data-cardsizes="9x3,9x3,6x3,9x3,9x3,9x3" data-maxresults="6">
+</div>
+
+<div class="headerLine">
+  <h2 id="use-badges">
+    16. 在宣传活动中使用 Google Play 徽章和链接
+  </h2>
+
+
+</div>
+
+<p>
+  Google Play 徽章是官方品牌标识,你可以使用该徽章向 Android 用户推广你的应用。使用 <a href="/distribute/tools/promote/badges.html">Google Play 徽章生成器</a>可快速创建徽章,让用户从网页、广告、评论等链接到你的商品。你还可以使用特殊的<a href="/distribute/tools/promote/linking.html">链接格式</a>,将用户直接链接到你的商品详情页、商品列表或搜索结果页。
+</p>
+
+<p>
+  为了帮你的应用在发布后获得用户关注,强烈建议你通过宣传活动来支持应用的发布,这些宣传活动应通过尽可能多的渠道、向尽可能多的国家/地区介绍你的商品。例如,你可以通过广告展示、社交网络或博客、视频及其他媒体、采访和评论,或者任何其他可用渠道推广应用的发布。
+</p>
+
+<h3 class="rel-resources clearfloat">相关资源</h3>
+
+<div class="resource-widget resource-flow-layout col-13" data-query="collection:distribute/toolsreference/launchchecklist/badges/zhcn" data-sortorder="-timestamp" data-cardsizes="9x3,9x3,6x3,9x3,9x3,9x3" data-maxresults="6">
+</div>
+
+<div class="headerLine">
+  <h2 id="final-checks">
+    17. 最终检查和发布</h2>
+
+
+</div>
+
+<p>
+  如果你认为已做好发布准备,请登录到开发者控制台,花些时间做最后几项检查。
+</p>
+
+<p>
+  请确保:
+</p>
+
+<ul>
+  <li>
+    <p>你的开发者资料准确无误且已与相应的 Google 电子钱包商家帐户相关联(如果你要销售商品)。
+    </p>
+  </li>
+
+  <li>
+    <p>你上传的应用版本准确无误。
+    </p>
+  </li>
+
+  <li>
+    <p>商品详情的各个方面都已准备完善,包括所有图形资源、屏幕截图、视频、本地化的说明等。
+    </p>
+  </li>
+
+  <li>
+    <p>你已将应用设置为免费应用或付费应用。
+    </p>
+  </li>
+
+  <li>
+    <p>你已设置目标国家/地区(以及运营商),并已确定以买家货币为单位的商品价格(如果适用)
+    </p>
+  </li>
+
+  <li>
+    <p>“兼容设备”显示你的应用当前覆盖的是你要定位的设备。如果不是,你应与开发小组联系以了解应用的要求和过滤规则。
+    </p>
+  </li>
+
+  <li>
+    <p>你已提供指向你网站的正确链接以及正确的支持电子邮件地址。
+    </p>
+  </li>
+
+  <li>
+    <p>你的应用未违反任何内容政策指南。
+    </p>
+  </li>
+
+  <li>
+    <p>你已确认你的应用符合 Google Play 上的 Android 内容指南及美国出口法。
+    </p>
+  </li>
+</ul>
+
+<p>
+  你的应用现在随时可以发布了!
+</p>
+
+<p>
+  如果你要发布更新,请务必阅读<a href="http://support.google.com/googleplay/android-developer/bin/answer.py?hl=zh-CN&amp;answer=113476&amp;topic=2365760&amp;ctx=topic">发布更新的相关要求</a>。
+</p>
+
+<p>
+  当一切就绪后,请点击开发者控制台中的<strong>发布</strong>按钮。几个小时内,你的应用就会提供给广大用户,你的商品页将显示在 Google Play 上,供用户浏览、搜索或从宣传资料中链接到该页面。
+</p>
+
+<h3 class="rel-resources clearfloat">相关资源</h3>
+
+<div class="resource-widget resource-flow-layout col-13" data-query="collection:distribute/toolsreference/launchchecklist/finalchecks/zhcn" data-sortorder="-timestamp" data-cardsizes="6x3,6x3,6x3,9x3,9x3,9x3" data-maxresults="6">
+</div>
+
+<div class="headerLine">
+  <h2 id="support-users">
+    18. 在发布后为用户提供支持
+  </h2>
+
+
+</div>
+
+<p>
+  发布应用或应用更新后,请务必为你的客户提供相关支持。及时周到的支持可让用户获得更好的体验,你的商品也就能获得更高的评分和更多的好评。如果你积极响应用户的需求和反馈,他们可能会与你的应用进行更多互动,还会向其他人推荐你的应用。如果你运用多种方式开展宣传活动,这种效果在应用发布后会更加明显。
+</p>
+
+<p>
+  你可以通过多种方式与用户保持联系并为其提供支持。其中最常见的就是在商品详情页上提供你的支持电子邮件地址。<em></em>除此之外,你还可以提供其他支持方式,例如论坛、邮寄名单或 Google+ 信息页。Google Play 小组会为用户提供下载、安装和付款方面的支持,但其他方面的问题则由你自行解决。由你向用户提供支持的示例包括:功能请求、应用的使用问题以及兼容性设置问题。
+</p>
+
+<p>
+  发布应用后,你应:
+</p>
+
+<ul>
+  <li>
+    <p>经常查看你应用的商品详情页上的评分和评论。注意反复出现的主题,这可能表示存在错误或其他问题。
+    </p>
+  </li>
+
+  <li>
+    <p>关注新发布的 Android 平台版本,因为你应用的兼容性设置可能需要更新。
+    </p>
+  </li>
+
+  <li>
+    <p>在你的网站上放置一个指向支持资源的链接,并设置其他支持方式,例如论坛。
+    </p>
+  </li>
+
+  <li>
+    <p>在商品详情页上提供相应的支持电子邮件地址并回复用户发来的电子邮件。
+    </p>
+  </li>
+
+  <li>
+    <p>除了 Google Play 提供的自动退款窗口外,请诚心制定你的退款政策,因为满意的用户将来更有可能购买你的商品。
+    </p>
+  </li>
+
+  <li>
+    <p>确认并解决你应用中出现的问题。保持公开透明并主动在商品详情页上列出已知问题是明智有益之举。
+    </p>
+  </li>
+
+  <li>
+    <p>尽可能经常发布更新,但不要牺牲质量,也不能过于频繁以免干扰用户。
+    </p>
+  </li>
+
+  <li>
+    <p>每次更新时,请务必总结一下变更内容。你可以在开发者控制台中输入此信息。用户会查看这些信息并认为你是在努力提高应用的质量。
+    </p>
+  </li>
+</ul>
+
+
+<h3 class="rel-resources clearfloat">相关资源</h3>
+
+<div class="resource-widget resource-flow-layout col-13" data-query="collection:distribute/toolsreference/launchchecklist/afterlaunch/zhcn" data-sortorder="-timestamp" data-cardsizes="9x3,9x3,9x3,9x3,9x3,9x3" data-maxresults="6"></div>
diff --git a/docs/html-intl/intl/zh-cn/distribute/tools/localization-checklist.jd b/docs/html-intl/intl/zh-cn/distribute/tools/localization-checklist.jd
new file mode 100644
index 0000000..b2ba2ff
--- /dev/null
+++ b/docs/html-intl/intl/zh-cn/distribute/tools/localization-checklist.jd
@@ -0,0 +1,698 @@
+page.title=本地化检查清单
+page.metaDescription=把握 Android 和 Google Play 所提供的全世界观众的优势。阅读此清单以获得如何将产品送到世界各地的市场的概述。
+page.image=/distribute/images/localization-checklist.jpg
+page.type="배포"
+
+@jd:body
+
+    <div id="qv-wrapper">
+  <div id="qv" style="width:280px">
+    <h2>检查清单</h2>
+    <ol>
+      <li><a href="#identify-languages">1. 确定目标语言和语言区域</a></li>
+      <li><a href="#design">2. 本地化设计</a></li>
+      <li><a href="#manage-strings">3. 管理字符串本地化</a></li>
+      <li><a href="#translate-strings">4. 翻译界面字符串和其他资源</a></li>
+      <li><a href="#test">5. 测试已本地化的应用</a></li>
+      <li><a href="#prepare-launch">6. 准备国际化发布</a></li>
+      <li><a href="#support-users">7. 在发布之后为国际用户提供支持</a></li>
+    </ol>
+  </div>
+</div>
+
+<div class="top-right-float" style="width:194px">
+  <img src="{@docRoot}distribute/images/localization-checklist.jpg">
+</div>
+<p>
+  Android 和 Google Play 让你可以面向全球的用户提供应用,其中日本、韩国、印度、巴西和俄罗斯等国家/地区的目标用户群正在迅速扩展。有鉴于此,我们强烈建议你本地化你的应用,因为这样可以最大程度地增加应用的分发范围,获得全球各地用户的评分。
+</p>
+
+<p>
+  本地化涉及到很多任务,贯穿应用开发的整个周期,因此务必要提前做出规划。本文旨在帮助你了解本地化的主要方面,以便你做好准备,通过 Google Play 向全球成功发布自己的应用。
+</p>
+
+<div class="headerLine">
+  <h2 id="identify-languages" style="margin-bottom:0px">
+    1. 确定目标语言和语言区域
+  </h2>
+
+
+</div>
+
+<p>
+  确定要向哪些国家/地区发布你的应用并了解这些国家/地区所用的语言,是准备本地化的过程中基本且重要的一步。对于具有巨大市场商机、但英语或其他国际性语言并不普及的国家/地区来讲,将应用本地化尤为重要。
+</p>
+
+<p>
+    对于国际用户,你可以从以下这三个主要方面来管理自己的应用:国家/地区、语言区域和语言。其中,语言是本地化的主要考虑因素(语言区域也很重要,因为日期的格式、时间、货币及其他类似信息也都存在差异)。用户可以控制其 Android 设备上使用的语言和语言区域,反过来这些因素也会影响应用的显示方式。
+
+</p>
+
+<p>
+  通常来说,你首先要根据整体市场规模和商机、应用类别、竞争格局、本地定价和金融状况等因素来确定要定位的国家/地区,然后再基于你的国家/地区定位,确定应用要支持的语言。
+</p>
+
+<p>
+  随后,你可以决定是针对目标国家/地区的部分语言还是全部语言进行本地化。合理的做法是,先针对一种主要的地区性语言进行本地化,然后随着用户群的扩大增添更多语言。
+</p>
+
+<p>
+  确定目标语言之后,你就可以安排开发、翻译、测试和市场营销工作重点围绕这些市场展开。
+</p>
+
+<h3 class="rel-resources clearfloat">相关资源</h3>
+
+<div class="resource-widget resource-flow-layout col-13" data-query=
+"collection:distribute/toolsreference/localizationchecklist/identifylocales/zhcn"
+data-sortorder="-timestamp" data-cardsizes="9x3," data-maxresults="6">
+</div>
+
+<div class="headerLine">
+  <h2 id="design" style="margin-bottom:0px">
+    2. 本地化设计
+  </h2>
+
+
+</div>
+
+<p>  确定本地化的目标语言之后,请评估在应用中支持这些语言需要完成哪些工作并提早做出规划。要考虑的事项包括每种语言的词汇扩展、脚本要求、字符间距、换行限制、支持文字“从左到右”和“从右到左”,以及其他可能遇到的问题。
+</p>
+
+<h4>
+  <strong>设计一套灵活的布局</strong>
+</h4>
+
+<p>
+  在创建布局时,确保包含文字的界面元素拥有充足的空间。最好在满足你的语言的基础上多出部分空间(通常留出 30% 的空间)以适应其他语言。
+</p>
+
+<p>
+  另外,元素应能够横向或纵向扩展,以便可以适应不同宽度和高度的界面字符串或输入文本。任何目标语言的字符串都不应与边框或屏幕边缘重叠。
+</p>
+
+<p>
+  如果界面设计得当,通常你就可以针对所有支持的语言使用一套布局。有关详情,请参阅<a href="{@docRoot}training/basics/fragments/fragment-ui.html">构建灵活的界面</a>。
+</p>
+
+<h4>
+  <strong>根据需要使用备用布局</strong>
+</h4>
+
+<p>
+  如果你的界面不能很好地适应某种目标语言,那么你可以单独为该语言创建一套<a href="{@docRoot}guide/topics/resources/providing-resources.html#AlternativeResources">备用布局</a>。Android 可让你很轻松地针对特定语言、语言区域、屏幕尺寸等,声明要加载的多种布局和其他资源,只需使用相应的资源限定符做出标记即可。虽然备用布局可提供相应的灵活性,但使用它会导致随着时间推移,应用的维护难度增加。因此一般来说,建议你最好使用一套更灵活的布局。
+</p>
+
+<h4>
+  <strong>支持 RTL 布局和文本</strong>
+</h4>
+
+<p>
+  如果你要发布应用的国家/地区使用从右到左 (RTL) 的文字阅读方向,则你应该考虑尽可能地支持 RTL 布局以及相应的文本显示和编辑。
+</p>
+
+<p>
+  Android 4.1 引入了对双向文字的有限支持,支持应用以从左到右 (LTR) 和从右到左 (RTL) 的方向显示和编辑文本。Android 4.2 增加了<a href="http://android-developers.blogspot.fr/2013/03/native-rtl-support-in-android-42.html">对 RTL 布局的完整原生支持</a>(包括布局镜像),因此你可以为所有用户提供一致的优质体验。
+</p>
+
+<p>
+  Android 4.2 用户至少可以轻松添加基本的 RTL 布局镜像,这对满足 RTL 用户需求大有帮助。
+</p>
+
+<h4>
+  <strong>针对日期、时间、数字和货币采用系统提供的格式</strong>
+</h4>
+
+<p>
+  如果你的应用需要指定因语言区域不同而不同的日期、时间、数字、货币和其他实体,请务必使用系统提供的格式,而不要使用应用专有的格式。请注意,并非所有的语言区域都使用相同的千位分隔符、小数点或百分比符号。
+</p>
+
+<p>
+  Android 提供了多种实用工具,用于在不同的语言区域中设置格式、进行转换:例如适用于日期的 <a href="{@docRoot}reference/android/text/format/DateUtils.html">DateUtils</a> 和 <a href="{@docRoot}reference/java/text/DateFormat.html">DateFormat</a>;适用于数字和货币的 <a href="{@docRoot}reference/java/lang/String.html#format(java.lang.String, java.lang.Object...)">String.format()</a> 或 <a href="{@docRoot}reference/java/text/DecimalFormat.html">DecimalFormat</a>;适用于电话号码的 <a href="{@docRoot}reference/android/telephony/PhoneNumberUtils.html">PhoneNumberUtils</a> 等。
+</p>
+
+<p>
+  假定用户的语言区域并据此强制设置格式,会导致在用户更改语言区域时引发问题。强烈建议你使用系统提供的格式和各种实用工具。
+</p>
+
+<h4>
+  <strong>包括一套完整的默认资源</strong>
+</h4>
+
+<p>
+  提供一整套默认资源可以确保无论是何种语言或语言区域,你的应用都能正常运行。应用的默认资源是指没有使用任何语言或语言区域限定符标记的资源,例如 res/drawable/ 和 res/values/ 中存储的资源。<em></em>如果你的应用试图加载目前的语言或默认资源集中没有的资源,则会崩溃。
+</p>
+
+<p>
+  无论你在应用中使用的默认语言是什么,请务必在默认的资源目录中存储相关的布局、图形内容和字符串,且不使用语言或语言区域限定符。
+</p>
+
+<h3 class="rel-resources clearfloat">相关资源</h3>
+
+<div class="resource-widget resource-flow-layout col-13" data-query=
+"collection:distribute/tools/loc/designforloc" data-sortorder="-timestamp"
+data-cardsizes="9x3" data-maxresults="6">
+</div>
+
+<div class="headerLine">
+  <h2 id="manage-strings" style="margin-bottom:0px">
+    3. 管理字符串的本地化</h2>
+
+
+</div>
+
+<p>
+  妥善管理应用的界面字符串非常重要,这样你才能为用户提供优质的体验,并让本地化工作事半功倍。
+</p>
+
+<h4>
+  <strong>将所有字符串放入 strings.xml 中</strong>
+</h4>
+
+<p>
+  在构建应用时,请注意不要硬编码任何字符串,而是在默认的 strings.xml 文件中将所有字符串声明为资源,这样便于更新和进行本地化。<em></em>strings.xml 文件中的字符串可以抽取出来、进行翻译,然后再集成回应用(使用相应的限定符),无需对编译后的代码进行任何更改。
+</p>
+
+<p>
+  如果你生成带有文字的图片,也请将这些字符串放入 strings.xml,并在翻译之后重新生成图片。
+</p>
+
+<h4>
+  <strong>遵循针对界面字符串的 Android 指南</strong>
+</h4>
+
+<p>
+  在设计和开发界面时,请务必密切注意你与用户交流的方式。<em></em>一般而言,使用友好而不失简洁、<a href="{@docRoot}design/style/writing.html">简明而且精炼的风格</a>,并且整个界面应采用一致的风格。</p>
+
+<p>
+  请务必阅读并遵循<a href="{@docRoot}design/style/writing.html">写作风格和字词选择</a>方面的 Android 设计建议。这样做能够让你的应用更加美观,并且有助于用户快速理解你的界面。
+</p>
+
+<p>
+  另外,请尽可能使用 Android 标准术语,例如对于界面元素,使用“操作栏”、“选项菜单”、“系统栏”、“通知”等。确保 Android 术语的正确性和一致性可让翻译工作更容易进行,从而为用户带来更好的最终产品。
+</p>
+
+<h4>
+  <strong>为声明的字符串提供充分的上下文</strong>
+</h4>
+
+<p>
+  在 strings.xml 文件中声明字符串时,确保清楚说明使用该字符串的上下文。此信息对翻译人员来说十分重要,有助于提高翻译质量,也有助于你始终有效地管理字符串。
+</p>
+
+<p>
+  下面是一个例子:
+</p>
+
+<pre class="prettyprint"><span class="com">&lt;!-- 用于提交表单的操作。此文本显示在可容纳 30 个字符的按钮上--&gt;</span><span class="pln"><br /></span><span class="tag">&lt;string</span><span class="pln"> </span><span class="atn">name</span><span class="pun">=</span><span class="atv">"login_submit_button"</span><span class="tag">&gt;</span><span class="pln">Sign in</span><span class="tag">&lt;/string&gt;</span></pre>
+<p>
+  你需要提供的上下文信息包括:
+</p>
+
+<ul>
+  <li>
+    <p>
+      字符串有何用途?它在何时/哪里呈现给用户?
+    </p>
+  </li>
+
+  <li>
+    <p>
+      它在布局中的什么位置?例如,如果它是按钮,那么翻译的灵活性就不如文本框。
+    </p>
+  </li>
+</ul>
+
+<h4>
+  <strong>标记不应翻译的信息部分</strong>
+</h4>
+
+<p>
+  有时候字符串中包含不应被翻译为其他语言的文本。常见的示例包括代码、某个值的占位符、特殊符号或名称。在准备翻译字符串时,请查找并标记应该保留原样而不用翻译的文本,这样翻译人员就不会更改这些内容。
+</p>
+
+<p>
+  要标记不应翻译的文本,请使用 <code>&lt;xliff:g&gt;</code> 占位符标记。以下示例标记可确保文本“%1$s”在翻译过程中不会被更改(否则这条消息会被破坏):
+</p>
+
+<pre class="prettyprint"><span class="tag">&lt;string</span><span class="pln"> </span><span class="atn">name</span><span class="pun">=</span><span class="atv">"countdown"</span><span class="tag">&gt;</span><span class="pln"><br />    </span><span class="tag">&lt;xliff:g</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"time"</span><span class="pln"> </span><span class="atn">example</span><span class="pun">=</span><span class="atv">"5 days&gt;</span><span class="pln">%1$s</span><span class="tag">&lt;/xliff:g&gt;</span><span class="pln">until holiday<br /></span><span class="tag">&lt;/string&gt;</span></pre>
+<p>
+  在声明占位符标记时,请务必添加说明此占位符用途的 ID 属性。如果你的应用稍后会替换占位符值,请务必提供示例属性来说明预期用途。
+</p>
+
+<p>
+  以下是其他一些占位符标记的示例;
+</p>
+
+<pre>
+&lt;resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"&gt;
+
+&lt;!-- 特殊 Unicode 符号的占位符示例 --&gt;
+
+&lt;string name="star_rating"&gt;Check out our 5
+
+    &lt;xliff:g id="star"&gt;\u2605&lt;/xliff:g&gt;
+
+&lt;/string&gt;
+
+&lt;!-- 网址的占位符示例 --&gt;
+
+&lt;string name="app_homeurl"&gt;
+
+    Visit us at &lt;xliff:g id="application_homepage"&gt;http://my/app/home.html&lt;/xliff:g&gt;
+
+&lt;/string&gt;
+
+&lt;!-- 名称的占位符示例 --&gt;
+
+&lt;string name="prod_name"&gt;
+
+    Learn more at &lt;xliff:g id="prod_gamegroup"&gt;Game Group&lt;/xliff:g&gt;
+
+&lt;/string&gt;
+
+&lt;!-- 实值的占位符示例 --&gt;
+
+&lt;string name="promo_message"&gt;
+
+    Please use the "&lt;xliff:g id="promotion_code"&gt;ABCDEFG&lt;/xliff:g&gt;” to get a discount.
+
+&lt;/string&gt;
+
+...
+
+&lt;/resources&gt;
+</pre>
+
+
+<h3 class="rel-resources clearfloat">相关资源</h3>
+
+<div class="resource-widget resource-flow-layout col-13" data-query=
+"collection:distribute/toolsreference/localizationchecklist/managestrings/zhcn"
+data-sortorder="-timestamp" data-cardsizes="9x3" data-maxresults="6">
+</div>
+
+<div class="headerLine">
+  <h2 id="translate-strings" style="margin-bottom:0px">
+    4. 翻译界面字符串和其他资源
+  </h2>
+
+
+</div>
+
+<p>
+  将应用的界面字符串和资源翻译为目标语言是本地化过程中的关键阶段,需要给予最多关注并做出规划。
+</p>
+
+<p>
+  我们建议你与专业翻译人员合作(请参阅<a href="{@docRoot}distribute/tools/localization-checklist.html#gp-trans">聘请专业翻译公司</a>),以确保获得高品质的翻译,提升应用的价值。你也可以选择机器翻译,但用户体验不如人工翻译。
+</p>
+
+<h4>
+  <strong>准备翻译</strong>
+</h4>
+
+<p>
+  翻译工作的质量在一定程度上取决于你为翻译提供的源资料,因此请确保你的 strings.xml 文件条理清晰、注释完善,而且完全准确。
+</p>
+
+<p>
+  以下是一些在翻译之前准备字符串的方法:
+</p>
+
+<ul>
+  <li>
+    <p>
+      确保字符串格式的正确性和一致性。
+    </p>
+  </li>
+
+  <li>
+    <p>
+      遵循上文<a href="{@docRoot}distribute/tools/localization-checklist.html#manage-strings">管理字符串的本地化</a>中列出的有关字符串的建议。
+    </p>
+  </li>
+
+  <li>
+    <p>
+       清理 strings.xml 文件,删除不用的字符串。
+    </p>
+  </li>
+
+  <li>
+    <p>
+      在文件中加入注释,以标识所有者、来源和文件版本,以及针对翻译人员的特别说明。
+    </p>
+  </li>
+
+  <li>
+    <p>
+      提供现有翻译(如果有的话),用 zip 文件或其他文件包发送给翻译人员。
+    </p>
+  </li>
+
+  <li>
+    <p>
+      标识需要翻译的图形内容或其他资源,并将它们加入给翻译人员的文件包中。
+    </p>
+  </li>
+
+  <li>
+    <p>
+      此外,考虑翻译你的应用在 Google Play 商店的商品详情(应用标题和说明、发行说明等)以及其他国际化营销资料。
+    </p>
+  </li>
+
+  <li>
+    <p>
+      创建术语表,用来解释你的产品、市场或所用重要技术中出现的关键术语的含义和用途。将列表添加到给翻译人员的文件包中。
+    </p>
+  </li>
+</ul>
+
+<h4>
+  <strong>发送字符串进行翻译</strong>
+</h4>
+
+<p>
+  在开发阶段的初期,你就可以与专业的翻译供应商联系,商定费用和周转时间,而且你的费用应该包含多次反复工作的费用。你可以寻找在线翻译供应商或直接使用 Google Play 开发者控制台提供的翻译服务(请参阅<a href="{@docRoot}distribute/tools/localization-checklist.html#gp-trans">聘请专业翻译公司</a>)。
+</p>
+
+<p>
+  翻译完成后,请进行初步检查。查看所有文件是否均已翻译,是否存在潜在编码问题,并确保声明格式未被破坏。
+</p>
+
+<p>
+  如果一切正常,请小心地将已本地化的目录和文件移回应用的资源中。请务必使用正确的语言和语言区域限定符标记目录,以便以后能够正确加载这些目录。
+</p>
+
+<p>
+  在将翻译合并到你的应用之后,请开始<a href="{@docRoot}distribute/tools/localization-checklist.html#test">测试已本地化的应用</a>。
+</p>
+
+<h4 id="gp-trans">
+  <strong>通过 Google Play 聘请专业翻译公司</strong>
+</h4>
+
+<p>
+  Google Play 应用翻译服务可以帮助你为应用快速寻找和购买翻译服务。在开发者控制台中,你可以浏览已由 Google 认证的第三方供应商列表,这些供应商能够以有竞争力的价格为你提供高品质的翻译。你可以上传要翻译的字符串,选择翻译的目标语言,并根据时间和价格选择翻译供应商。
+</p>
+
+<p>
+  购买翻译之后,你会收到来自供应商的电子邮件。这是你与供应商之间的直接业务往来,你需要直接与供应商合作,管理翻译流程并解决所有支持问题。
+</p>
+
+<div>
+  <img src="{@docRoot}images/gp-localization-trans-0.png" class="border-img">
+</div>
+
+<h3 class="rel-resources clearfloat">相关资源</h3>
+
+<div class="resource-widget resource-flow-layout col-13" data-query=
+"collection:distribute/toolsreference/localizationchecklist/translatestrings"
+data-sortorder="-timestamp" data-cardsizes="9x3" data-maxresults="6">
+</div>
+
+<div class="headerLine">
+  <h2 id="test" style="margin-bottom:0px">
+    5. 测试已本地化的应用
+  </h2>
+
+
+</div>
+
+<p>
+  收到翻译的字符串和资源并放入应用之后,你需要对应用进行测试,确保应用能够向国际用户发布。
+</p>
+
+<p>
+  人工测试可以帮助你发现布局和字符串中的本地化问题,这些问题可能影响用户满意度,最终影响应用的用户评分。
+</p>
+
+<h4>
+  <strong>设置测试环境</strong>
+</h4>
+
+<p>
+  要测试已本地化的应用,你需要根据你的目标市场和适配设备,设置包含多种设备(或虚拟设备)和屏幕尺寸的测试环境。请注意,不同地区可用的设备可能不同。如果可能,请将你的测试设备设置为用户能够使用的实际设备。
+</p>
+
+<h4>
+  <strong>查找常见的本地化问题</strong>
+</h4>
+
+<p>
+  在每台测试设备上,在“设置”中设定语言或语言区域。安装并启动应用,然后浏览所有界面流程、对话框和用户互动。在需要输入内容的部分输入文本。要查找的内容包括:
+</p>
+
+<ul>
+  <li>
+    <p>
+      截断的文本,或者与界面元素或屏幕边缘重叠的文本
+    </p>
+  </li>
+
+  <li>
+    <p>
+      不合理的换行
+    </p>
+  </li>
+
+  <li>
+    <p>
+      错误的字词中断或标点符号错误
+    </p>
+  </li>
+
+  <li>
+    <p>
+      错误的字母顺序排序
+    </p>
+  </li>
+
+  <li>
+    <p>
+      错误的布局方向或文字方向
+    </p>
+  </li>
+
+  <li>
+    <p>
+      未翻译的文本。如果显示的是默认字符串而不是翻译过的字符串,那么你可能忽略了这些字符串的翻译或是使用了错误的语言限定符标记了资源目录。
+    </p>
+  </li>
+</ul>
+
+<p>
+  如果翻译后的字符串变得过于臃肿,不能适应布局,建议你尝试简化默认文本、简化译文文本或调整默认布局。如果问题仍然不能解决,请为该语言创建自定义布局。
+</p>
+
+<h4>
+  <strong>测试默认资源</strong>
+</h4>
+
+<p>
+  使用所有目标语言和语言区域测试应用之后,请务必使用不支持的语言和语言区域再次测试。<em></em>这有助于确保你的应用包括完整的默认字符串和资源,可供所有用户使用,不论用户设置了哪种首选语言。
+</p>
+
+<h4>
+  <strong>邀请母语使用者进行复查</strong>
+</h4>
+
+<p>
+  在测试过程中或之后,建议你邀请相应的母语使用者对本地化的应用进行复查。一种方法是让目标地区的用户进行 Beta 版测试,Google Play 可以帮助你开展这项工作。<!-- --></p>
+
+
+<div class="headerLine">
+  <h2 id="prepare-launch" style="margin-bottom:0px">
+    6. 准备国际化发布
+  </h2>
+
+
+</div>
+
+<p>
+  翻译应用是本地化过程中的关键部分,但是要让你的产品吸引用户、获得关注,你应该做好在目标国家/地区发布应用的准备,并针对国际用户制定更广泛的发布和营销计划。
+</p>
+
+<h4>
+  <strong>本地化你的 Google Play 商品详情</strong>
+</h4>
+
+<div class="sidebox-wrapper" style="float:right">
+  <div class="sidebox">
+    <h2>
+      本地化你的 Google Play 商品详情
+    </h2>
+
+    <p>
+      Google Play 商店的商品详情是国际用户对你应用的第一印象。你应该向所有用户重点介绍应用的优势!在开发者控制台中本地化商品详情时,需要本地化的内容包括:</p>
+
+    <ul>
+      <li>应用标题和说明
+      </li>
+
+      <li>应用在手机和平板电脑上的屏幕截图
+      </li>
+
+      <li>宣传图片和视频
+      </li>
+    </ul>
+  </div>
+</div>
+
+<p>
+  如果你想让应用在国际市场中取得成功,则务必要本地化你的 Google Play 商品详情。你可以在开发者控制台中管理已本地化的商品详情。
+</p>
+
+<p>
+  在发布之前,请决定你的应用标题、说明、宣传文字、营销主题和计划以及其他文字和图片。请提早发送你的商品详情文字和图片进行翻译,确保这些内容在 Beta 版测试开始时就准备就绪。收到译文后,你可以通过开发者控制台添加译文。
+</p>
+
+<div class="sidebox-wrapper" style="float:right">
+  <div class="sidebox">
+    <h2>
+      Google Play 中的商品详情翻译
+    </h2>
+
+    <p>
+      你可以使用 Google Play 上的应用翻译服务翻译你的商品详情。准备包含商品详情信息的 XML 文件,并像上传 strings.xml 文件一样上传此文件(请参阅<a href="{@docRoot}distribute/tools/localization-checklist.html#gp-trans">聘请专业翻译公司</a>)
+    </p>
+  </div>
+</div>
+
+<p>
+  另外,你已经创建了优秀的本地化应用,那么就应该让用户知晓。截取各个语言版本的界面在手机和平板电脑(7 英寸和 10 英寸)上的截图。你可以将所有目标语言版本的屏幕截图上传到开发者控制台。这对于使用其他语言浏览你的应用商品详情的用户来说有很重要的参考价值。
+</p>
+
+<p>
+  你还有必要准备本地化版本的宣传图片和视频。例如,你应用的置顶大图可能包括应该翻译的文字,翻译之后能达到最佳效果,或者你可能希望在某个国家/地区呈现与众不同的视觉效果。你可以为每种目标语言创建相应版本的宣传图片,并将它们上传到开发者控制台。如果你提供宣传视频,则可以创建本地化版本的视频,然后添加指向各目标语言版本的视频的链接。
+</p>
+
+<h4>
+  <strong>在主要国家/地区计划 Beta 版的发布</strong>
+</h4>
+
+<div class="sidebox-wrapper" style="float:right">
+  <div class="sidebox">
+    <h2>
+      简单的 Beta 测试
+    </h2>
+
+    <p>
+      现在,Google Play 允许你在世界范围内设立 Alpha 版和 Beta 版测试者小组。下次登录到开发者控制台时请别忘记了解这一强大的功能。
+    </p>
+  </div>
+</div>
+
+<p>
+  在发布应用之前收集用户的真实反馈是很有必要的做法,特别是使用新的语言、在新的国家或地区发布应用时,应该收集更多反馈。在这种情况下,我们强烈建议你向主要目标市场的用户发布应用的预览版本,以便这些用户能够方便地向你提供反馈和报告错误。
+</p>
+
+<p>
+  Google Play 可以帮助你为应用设置 Beta 测试计划。登录到开发者控制台并上传你的 APK 之后,你可以设立用户小组,以便进行应用的 Alpha 版和 Beta 版测试。你可以先进行小范围的 Alpha 版测试,然后推进到更大范围的 Beta 版测试。
+</p>
+
+<p>
+  用户被添加到小组之后,他们可以访问你应用的商品详情并安装应用。<strong>Alpha 或 Beta 版本的用户不能留下评论或评分</strong>,因此你在 Google Play 上的<strong>评分不会受到不利影响</strong>,不过这意味着你需要为测试者设置一种机制以便他们提供反馈:不妨创建一个 <a href="http://www.google.com/+/business/">Google+</a> 页面或 <a href="https://groups.google.com/forum/#!overview">Google 网上论坛</a>。
+</p>
+
+<p>
+  收到的反馈可以帮助你调整界面、翻译和商品详情,以确保为用户提供优质体验。
+</p>
+
+<h4>
+  <strong>计划国际化营销</strong>
+</h4>
+
+<p>
+  为了在更多国家/地区获得最高的关注度,请考虑制定国际化营销或广告计划。计划的覆盖范围取决于你能支持的预算,但一般来说,在发布时和发布后针对特定国家/地区开展营销是经济、高效的措施。
+</p>
+
+<h4>
+  <strong>创建本地化的 Google Play 徽章</strong>
+</h4>
+
+<p>
+  如果你正在准备国际化营销,请务必加入<a href="{@docRoot}distribute/tools/promote/badges.html">本地化的 Google Play 徽章</a>,让用户知道你是在 Google Play 上发布应用的。你可以使用徽章生成器快速构建本地化的徽章,然后用到你的网站或营销材料中。你还可以获得高分辨率的资源。
+</p>
+
+<h4>
+  <strong>创建本地化的 Device Art</strong>
+</h4>
+
+<p>
+  如果你有应用在 Android 设备上运行的产品截图,请确保这些截图拥有精美外观,能够反映应用在 Android 设备上的最新状态。为帮助你准备高品质的营销材料,请使用 <a href="{@docRoot}distribute/tools/promote/device-art.html">Device Art Generator</a>,只需拖放即可快速获得在 Nexus 设备上的屏幕截图。
+</p>
+
+<h4>
+  <strong>查看你的优化提示</strong>
+</h4>
+
+<p>
+  在准备发布时,请务必登录到开发者控制台,查看为你的应用提供的“优化提示”。通过优化提示,你可以了解本地化的商品详情是否有所遗漏,还可以获得其他有用的提示,帮助你实现成功的本地化发布。
+</p>
+
+<h3 class="rel-resources clearfloat">相关资源</h3>
+
+<div class="resource-widget resource-flow-layout col-13" data-query=
+"collection:distribute/toolsreference/localizationchecklist/preplaunch/zhcn"
+data-sortorder="-timestamp" data-cardsizes="9x3,9x3,6x3,9x3,9x3,9x3"
+data-maxresults="6">
+</div>
+
+<div class="headerLine">
+  <h2 id="support-users" style="margin-bottom:0px">
+    7. 在发布后为国际用户提供支持
+  </h2>
+
+
+</div>
+
+<p>
+  在国际范围内发布应用之后,你应该准备好为各种语言和时区的用户提供支持。能为国际用户提供什么样的支持取决于你的预算,不过你至少要在发布后认真查看应用获得的评分、评论和下载统计信息。
+</p>
+
+<p>
+  以下是一些建议:
+</p>
+
+<ul>
+  <li>
+    <p>
+      使用开发者控制台中的应用统计信息比较不同语言和国家/地区的下载量、安装量、卸载量和评分。如果某些语言或国家/地区的下载量和评分相对落后,请考虑采取一些措施来改善你的产品或改变营销方法。
+    </p>
+  </li>
+
+  <li>
+    <p>
+      定期查看评论。Google Play 会为你翻译所有用户评论,因此你可以及时了解国际用户对应用的评价,以及用户喜欢的功能和困扰他们的问题。通过查看评论,你可以发现可能影响特定国家/地区用户的技术问题,然后进行修正并更新你的应用。
+    </p>
+  </li>
+
+  <li>
+    <p>
+      尽可能回复评论。使用用户的语言或通用语言与用户互动是一种很好的做法。如果不可行,你可以尝试使用翻译工具,不过效果可能会打折扣。如果你应用的某种语言版本十分受欢迎,请考虑由该语言的母语使用者帮助你为用户提供支持。
+    </p>
+  </li>
+
+  <li>
+    <p>
+      确保提供链接,让用户能够转到你网站上的所有支持资源。考虑建立按语言分组的用户小组、Google+ 社区或其他支持论坛。
+    </p>
+  </li>
+</ul>
+
+<p>
+  按照这里介绍的做法本地化你的应用、向国际用户宣传和营销你的应用,并为用户提供持续的支持,你的应用就能吸引很多新用户并始终赢得他们的青睐。
+</p>
+
+<p>
+  请务必阅读<a href="{@docRoot}distribute/tools/launch-checklist.html">发布检查清单</a>,以详细了解如何在 Google Play 上计划、构建和发布你的应用。
+</p>
+<h3 class="rel-resources clearfloat">相关资源</h3>
+
+<div class="resource-widget resource-flow-layout col-13"
+  data-query="collection:distribute/toolsreference/localizationchecklist/supportlaunch/zhcn"
+  data-sortOrder="-timestamp"
+  data-cardSizes="9x3,9x3,6x3,9x3,9x3,9x3"
+  data-maxResults="6"></div>
diff --git a/docs/html-intl/intl/zh-cn/distribute/tools/promote/badges.jd b/docs/html-intl/intl/zh-cn/distribute/tools/promote/badges.jd
new file mode 100644
index 0000000..1c82161
--- /dev/null
+++ b/docs/html-intl/intl/zh-cn/distribute/tools/promote/badges.jd
@@ -0,0 +1,278 @@
+page.title=Google Play 徽章生成器
+page.image=/images/gp-badges-set.png
+page.metaDescription=点击几下就可以为你的应用程序构建徽章,或者下载为各种语言本地化的高分辨率徽章资产。
+
+@jd:body
+
+  <p itemprop="description">借助 Google Play 徽章,你可以在在线广告、宣传材料或任何提供应用链接的地方使用 Google 的官方品牌标识,向大众推广你的应用。</p>
+
+  <p>请在下面的表单中输入你应用的应用包名称或发布者名称,选择徽章样式,点击“制作我的徽章”,然后将 HTML 代码粘贴至你的网页内容中。<em></em></p>
+
+  <p>如果你要为应用创建宣传网页,则还应使用 <a href="{@docRoot}distribute/tools/promote/device-art.html">Device Art Generator</a>,以方便快捷地将应用截图嵌入到设备效果图中。</p>
+
+  <p>有关 Google Play 徽章及其他品牌资源的使用指南,请参阅<a href="{@docRoot}distribute/tools/promote/brand.html#brand-google_play">品牌指南</a>。</p>
+
+  <style type="text/css">form.button-form {
+  margin-top: 2em;
+}
+/* the label and input elements are blocks that float left in order to   keep the left edgets of the input aligned, and IE 6/7 do not fully support     22inline-block    22 */
+label.block {
+  display: block;
+  float: left;
+  width: 100px;
+  padding-right: 10px;
+}
+input.text {
+  display: block;
+  float: left;
+  width: 250px;
+}
+div.button-row {
+  white-space: nowrap;
+  min-height: 80px;
+}
+div.button-row input {
+  vertical-align: middle;
+  margin: 0 5px 0 0;
+}
+#jd-content div.button-row img {
+  margin: 0;
+  vertical-align: middle;
+}
+</style>
+
+  <script type="text/javascript">
+
+  // locales for which we have the 'app' badge
+  var APP_LANGS = ['it','pt-br','pt-pt','nl','ko','ja','fr','es','es-419','en','de'];
+
+  // variables for creating 'try it out' demo button
+  var imagePath = "/images/brand/"
+  var linkStart = "<a href=\"https://play.google.com/store/";
+  var imageStart = "\">\n"
+          + "  <img alt=\"";
+    // leaves opening for the alt text value
+  var imageSrc = "\"\n       src=\"" + imagePath;
+    // leaves opening for the image file name
+  var imageEnd = ".png\" />\n</a>";
+
+  // variables for creating code snippet
+  var linkStartCode = "&lt;a href=\"https://play.google.com/store/";
+  var imageStartCode = "\"&gt;\n"
+          + "  &lt;img alt=\"";
+    // leaves opening for the alt text value
+  var imageSrcCode = "\"\n       src=\"" + imagePath;
+    // leaves opening for the image file name
+  var imageEndCode = ".png\" />\n&lt;/a>";
+
+  /** Generate the HTML snippet and demo based on form values */
+  function buildButton(form) {
+    var lang = $('#locale option:selected').val();
+    var selectedValue = lang + $('form input[type=radio]:checked').val();
+    var altText = selectedValue.indexOf("generic") != -1 ? "Get it on Google Play" : "Android app on Google Play";
+
+    if (form["package"].value != "com.example.android") {
+      $("#preview").show();
+      var packageName = escapeHTML(form["package"].value);
+      $("#snippet").show().html(linkStartCode + "apps/details?id=" + packageName
+              + imageStartCode + altText + imageSrcCode
+              + selectedValue + imageEndCode);
+      $("#button-preview").html(linkStart + "apps/details?id=" + packageName
+              + imageStart + altText + imageSrc
+              + selectedValue + imageEnd);
+
+      // Send the event to Analytics
+      ga('send', 'event', 'Distribute', 'Create Google Play Badge', 'Package ' + selectedValue);
+    } else if (form["publisher"].value != "Example, Inc.") {
+      $("#preview").show();
+      var publisherName = escapeHTML(form["publisher"].value);
+      $("#snippet").show().html(linkStartCode + "search?q=pub:" + publisherName
+              + imageStartCode + altText + imageSrcCode
+              + selectedValue + imageEndCode);
+      $("#button-preview").html(linkStart + "search?q=pub:" + publisherName
+              + imageStart + altText + imageSrc
+              + selectedValue + imageEnd);
+
+      // Send the event to Analytics
+      ga('send', 'event', 'Distribute', 'Create Google Play Badge', 'Publisher ' + selectedValue);
+    } else {
+      alert("Please enter your package name or publisher name");
+    }
+    return false;
+  }
+
+  /** Listen for Enter key */
+  function onTextEntered(event, form, me) {
+    // 13 = enter
+    if (event.keyCode == 13) {
+      buildButton(form);
+    }
+  }
+
+  /** When input is focused, remove example text and disable other input */
+  function onInputFocus(object, example) {
+    if (object.value == example) {
+      $(object).val('').css({'color' : '#000'});
+    }
+    $('input[type="text"]:not(input[name='+object.name+'])',
+            object.parentNode).attr('disabled','true');
+    $('#'+object.name+'-clear').show();
+  }
+
+  /** When input is blured, restore example text if appropriate and enable other input */
+  function onInputBlur(object, example) {
+    if (object.value.length < 1) {
+      $(object).attr('value',example).css({'color':'#ccc'});
+      $('input[type="text"]', object.parentNode).removeAttr('disabled');
+      $('#'+object.name+'-clear').hide();
+    }
+  }
+
+  /** Clear the form to start over */
+  function clearLabel(id, example) {
+    $("#preview").hide();
+    $('#'+id+'').html('').attr('value',example).css({'color':'#ccc'});
+    $('input[type="text"]', $('#'+id+'').parent()).removeAttr('disabled');
+    $('#'+id+'-clear').hide();
+    return false;
+  }
+
+  /** Switch the badge urls for selected language */
+  function changeBadgeLang() {
+    var lang = $('#locale option:selected').val();
+
+    // check if we have the 'app' badge for this lang and show notice if not
+    $("div.button-row.error").remove();  // remove any existing instance of error message
+    if ($.inArray(lang,APP_LANGS) == -1) {
+      $("div.button-row.app").hide();
+      $("div.button-row.app").after('<div class="button-row error"><p class="note" style="margin:1em 0 -1em">'
+          + 'Sorry, we currently don\'t have the '
+          + '<em>Android app on Google Play</em> badge translated for '
+          + $("select#locale option[value="+lang+"]").attr("title")
+          + '.<br>Please check back later or instead use the <em>Get it on Google Play</em> badge below.'
+          + '</p></div>');
+    } else {
+      $("div.button-row.app").show(); // show the 'app' badge row
+    }
+
+    $('.button-row img').each(function() {
+      var id = $(this).parent().attr('for');
+      var imgName = lang + $('input#'+id).attr('value') + '.png';
+      var lastSlash = $(this).attr('src').lastIndexOf('/');
+      var imgPath = $(this).attr('src').substring(0, lastSlash+1);
+      $(this).attr('src', imgPath + imgName);
+    });
+  }
+
+  /** When the doc is ready, find the inputs and color the input grey if the value is the example
+      text. This is necessary to handle back-navigation, which can auto-fill the form with previous
+      values (and text should not be grey) */
+  $(document).ready(function() {
+    $(".button-form input.text").each(function(index) {
+      if ($(this).val() == $(this).attr("default")) {
+        $(this).css("color","#ccc");
+      } else {
+        /* This is necessary to handle back-navigation to the page after form was filled */
+        $('input[type="text"]:not(input[name='+this.name+'])',
+                this.parentNode).attr('disabled','true');
+        $('#'+this.name+'-clear').show();
+      }
+    });
+  });
+
+  </script>
+
+  <form class="button-form">
+    <label class="block" for="locale">语言:</label>
+    <select id="locale" style="display:block;float:left;margin:0" onchange="changeBadgeLang()">
+      <option title="南非荷兰语" value="af">Afrikaans</option>
+      <option title="阿拉伯语" value="ar">العربية</option>
+      <option title="白俄罗斯语" value="be">Беларуская</option>
+      <option title="保加利亚语" value="bg">Български</option>
+      <option title="加泰罗尼亚语" value="ca">Català</option>
+      <option title="中文(中国)" value="zh-cn">中文(中国)</option>
+      <option title="中文(香港)" value="zh-hk">中文(香港)</option>
+      <option title="中文(台湾)" value="zh-tw">中文(台灣)</option>
+      <option title="克罗地亚语" value="hr">Hrvatski</option>
+      <option title="捷克语" value="cs">Česky</option>
+      <option title="丹麦语" value="da">Dansk</option>
+      <option title="荷兰语" value="nl">Nederlands</option>
+      <option title="爱沙尼亚语" value="et">Eesti</option>
+      <option title="波斯语" value="fa">فارسی</option>
+      <option title="菲律宾语" value="fil">Tagalog</option>
+      <option title="芬兰语" value="fi">Suomi</option>
+      <option title="法语" value="fr">Français</option>
+      <option title="德语" value="de">Deutsch</option>
+      <option title="希腊语" value="el">Ελληνικά</option>
+      <option title="英语" value="en" selected="">English</option>
+  <!--
+      <option title="希伯来语" value="iw-he">עברית</option>
+  -->
+      <option title="匈牙利语" value="hu">Magyar</option>
+      <option title="印度尼西亚语" value="id-in">Bahasa Indonesia</option>
+      <option title="意大利语" value="it">Italiano</option>
+      <option title="日语" value="ja">日本語</option>
+      <option title="韩语" value="ko">한국어</option>
+      <option title="拉脱维亚语" value="lv">Latviešu</option>
+      <option title="立陶宛语" value="lt">Lietuviškai</option>
+      <option title="马来语" value="ms">Bahasa Melayu</option>
+      <option title="挪威语" value="no">Norsk (bokmål)‎</option>
+      <option title="波兰语" value="pl">Polski</option>
+      <option title="葡萄牙语(巴西)" value="pt-br">Português (Brasil)</option>
+      <option title="葡萄牙语(葡萄牙)" value="pt-pt">Português (Portugal)</option>
+      <option title="罗马尼亚语" value="ro">Română</option>
+      <option title="俄语" value="ru">Русский</option>
+      <option title="塞尔维亚语" value="sr">Српски / srpski</option>
+      <option title="斯洛伐克语" value="sk">Slovenčina</option>
+      <option title="斯洛文尼亚语" value="sl">Slovenščina</option>
+      <option title="西班牙语(西班牙)" value="es">Español (España)</option>
+      <option title="西班牙语(拉丁美洲)" value="es-419">Español (Latinoamérica)</option>
+      <option title="瑞典语" value="sv">Svenska</option>
+      <option title="斯瓦希里语" value="sw">Kiswahili</option>
+      <option title="泰语" value="th">ไทย</option>
+      <option title="土耳其语" value="tr">Türkçe</option>
+      <option title="乌克兰语" value="uk">Українська</option>
+      <option title="越南语" value="vi">Tiếng Việt</option>
+      <option title="祖鲁语" value="zu">isiZulu</option>
+    </select>
+    <p style="clear:both;margin:0"> </p>
+    <label class="block" for="package" style="clear:left">  应用包名称:
+</label>
+    <input class="text" type="text" id="package" name="package" value="com.example.android" default="com.example.android" onfocus="onInputFocus(this, &#39;com.example.android&#39;)" onblur="onInputBlur(this, &#39;com.example.android&#39;)" onkeyup="return onTextEntered(event, this.parentNode, this)" /> 
+           <a id="package-clear" href="#" onclick="return clearLabel(&#39;package&#39;,&#39;com.example.android&#39;);">清除</a>
+    <p style="clear:both;margin:0"> <em>或</em></p>
+    <label class="block" style="margin-top:5px" for="publisher">  发布者名称:
+</label>
+    <input class="text" type="text" id="publisher" name="publisher" value="Example, Inc." default="Example, Inc." onfocus="onInputFocus(this, &#39;Example, Inc.&#39;)" onblur="onInputBlur(this, &#39;Example, Inc.&#39;)" onkeyup="return onTextEntered(event, this.parentNode, this)" /> 
+           <a id="publisher-clear" href="#" onclick="return clearLabel(&#39;publisher&#39;,&#39;Example, Inc.&#39;);">清除</a>
+           <br /><br />
+
+
+  <div class="button-row app">
+    <input type="radio" name="buttonStyle" value="_app_rgb_wo_45" id="ws" />
+      <label for="ws"><img src="{@docRoot}images/brand/en_app_rgb_wo_45.png" alt="即刻获取 Android 应用 Google Play(小图标)" /></label>
+          
+    <input type="radio" name="buttonStyle" value="_app_rgb_wo_60" id="wm" />
+      <label for="wm"><img src="{@docRoot}images/brand/en_app_rgb_wo_60.png" alt="即刻获取 Android 应用 Google Play(大图标)" /></label>
+  </div>
+
+  <div class="button-row">
+    <input type="radio" name="buttonStyle" value="_generic_rgb_wo_45" id="ns" checked="" />
+      <label for="ns"><img src="{@docRoot}images/brand/en_generic_rgb_wo_45.png" alt="即刻获取 Google Play(小图标)" /></label>
+          
+    <input type="radio" name="buttonStyle" value="_generic_rgb_wo_60" id="nm" />
+      <label for="nm"><img src="{@docRoot}images/brand/en_generic_rgb_wo_60.png" alt="即刻获取 Google Play(大图标)" /></label>
+  </div>
+
+    <input class="button" type="button" value="制作我的徽章" style="padding:10px" onclick="return buildButton(this.parentNode);" />
+    <br />
+  </form>
+
+  <div id="preview">
+    <p>请复制以下 HTML 代码并将其粘贴到你的网站上:</p>
+    <textarea id="snippet" cols="100" rows="5" style="font-family:monospace;background-color:#efefef;padding:5px;margin-bottom:1em" onclick="this.select()"></textarea>
+
+  <p>试试以下内容:</p>
+  <div id="button-preview" style="margin-top:1em"></div>
+  </div>
+
diff --git a/docs/html-intl/intl/zh-cn/distribute/tools/promote/brand.jd b/docs/html-intl/intl/zh-cn/distribute/tools/promote/brand.jd
new file mode 100644
index 0000000..065673e
--- /dev/null
+++ b/docs/html-intl/intl/zh-cn/distribute/tools/promote/brand.jd
@@ -0,0 +1,148 @@
+page.title=品牌指南
+page.image=/assets/images/resource-card-default-android.jpg
+page.metaDescription=关于Android 和 Goolge Play 品牌的指南和下载。
+
+@jd:body
+
+      <p>我们建议你在 Android 应用的宣传资料中使用 Android 和 Google Play 品牌。只要你遵循相关指南,就可以使用本页中的图标和其他资源。</p>
+
+  <p>对 Android 或 Google Play 品牌的使用必须经过 Android 合作伙伴营销小组的审核。请使用 <a href="https://docs.google.com/forms/d/1YE5gZpAAcFKjYcUddCsK1Bv9a9Y-luaLVnkazVlaJ2w/viewform">Android 和 Google Play 品牌权限咨询表单</a>提交你的营销资料以供审核。</p>
+
+  <h2 id="brand-android">Android</h2>
+
+   <p>以下是关于 Android 品牌和相关资源的指南。</p>
+
+
+    <h4 style="clear:right">在文本中使用 Android</h4>
+
+    <div style="float:right;clear:right;width:200px;margin:0 0 20px 30px">
+      <img alt="" src="{@docRoot}images/brand/mediaplayer.png" />
+    </div>
+      <ul>
+      <li>Android™ 首次出现在广告素材中时应标注商标符号。</li>
+      <li>Android 应始终采用大写形式,且一律不得使用复数或所有格形式。</li>
+      <li>“Android”不能在应用或配套产品(包括手机、平板电脑、电视、扬声器、耳机、手表及其他设备)的名称中使用。不过,你可以使用“for Android”。
+        <ul>
+          <li><span style="color:red">不正确</span>:“Android MediaPlayer”</li>
+          <li><span style="color:green">正确</span>:“MediaPlayer for Android”</li>
+        </ul>
+        <p>如果与你的徽标一起使用,则“for Android”不应超过徽标大小的 90%。这种用法第一次出现时,后面应标注 TM 符号,即“for Android™”。</p>
+      </li>
+      <li>如果后跟一个合适的通称,Android 可以用作描述词(即用“Android”表达“Android 平台”的意思)。
+        <ul>
+          <li><span style="color:red">不正确</span>:“Android MediaPlayer”或“Android XYZ 应用”</li>
+          <li><span style="color:green">正确</span>:“Android 功能”或“Android 应用”</li>
+        </ul>
+      </li>
+      </ul>
+
+      <p>每次使用 Android 名称时,你的资料中都应注明以下归属信息:</p>
+      <blockquote><em>Android 是 Google Inc. 的商标。</em></blockquote>
+
+
+   <h4>Android 机器人</h4>
+
+    <div style="float:right;width:200px;margin-left:30px">
+      <img alt="" src="{@docRoot}images/brand/Android_Robot_100.png" style="margin-left:50px" />
+      <p style="text-align:center">
+         <a href="{@docRoot}images/brand/Android_Robot_100.png">100x118</a> |
+         <a href="{@docRoot}images/brand/Android_Robot_200.png">200x237</a><br />
+         <a href="{@docRoot}downloads/brand/Android_Robot_outlined.ai">Illustrator 文件 (.ai)</a></p>
+    </div>
+
+      <p>你可以在营销资料中随意使用、复制和修改 Android 机器人。对于印刷版本,Android 机器人的颜色值为 PMS 376C;对于在线版本,请使用十六进制颜色 <span style="color:#a4c639">#A4C639</span>。</p>
+
+      <p>在使用 Android 机器人或其变体形式时,请遵循<a href="http://creativecommons.org/licenses/by/3.0/">知识共享署名 3.0 </a>许可的条款使用正确的归属信息:</p>
+
+      <blockquote><em>Android 机器人是在 Google 原创及共享成果的基础上再创作或修改而成,须遵照知识共享署名 3.0 许可所述条款付诸应用。</em></blockquote>
+
+      <p>你不得通过将 Android 机器人徽标或其变体形式加入自己的公司徽标或商家名称,来提出商标申请。我们希望确保 Android 机器人可供所有人使用。</p>
+
+
+  <h4 style="clear:right">Android 徽标</h4>
+
+  <div style="float:right;width:210px;margin-left:30px">
+    <img alt="" src="{@docRoot}images/brand/android_logo_no.png" />
+  </div>
+
+  <p>不得使用 Android 徽标。</p>
+
+  <p>不得使用 Android 徽标专用的字体。</p>
+
+  <h2 id="brand-google_play">Google Play</h2>
+
+
+   <p>以下是关于 Google Play 品牌和相关资源的指南。</p>
+
+  <h4>在文本中使用 Google Play</h4>
+
+  <p>在文本中,请务必为首次出现或位于显著位置的 Google Play™ 加上 TM 符号。</p>
+
+  <p>在提及移动设备体验时,除非相应文本中是很明显的说明性内容,否则请使用“Google Play”。例如,营销资料的标题可以是“从 Google Play™ 下载我们的游戏”,但是说明性内容可以是“使用 Google Play™ 商店应用下载我们的游戏”。
+
+   <p>每次使用 Google Play 名称或图标时,你的资料中都应注明以下归属信息:</p>
+
+  <blockquote><em>Google Play 是 Google Inc. 的商标。</em></blockquote>
+
+
+    <div style="float:right;width:96px;margin-left:30px">
+       <img src="{@docRoot}images/brand/Google_Play_Store_96.png" alt="" />
+      <p style="text-align:center">
+         <a href="{@docRoot}images/brand/Google_Play_Store_48.png">48x48</a> |
+         <a href="{@docRoot}images/brand/Google_Play_Store_96.png">96x96</a><br />
+         <a href="{@docRoot}images/brand/Google_Play_Store_600.png">600x576</a>
+         </p>
+    </div>
+
+  <h4>Google Play 商店图标</h4>
+
+  <p>你可以使用 Google Play 商店图标,但不得修改它。</p>
+
+  <p>如上文所述,在文案中提及 Google Play 商店应用时,请使用全称:“Google Play 商店”。但直接标注 Google Play 商店图标时,可以只使用“Play 商店”,以便与设备上显示的图标标签一致。</p>
+
+
+  <h4>Google Play 徽章</h4>
+
+    <div style="float:right;clear:right;width:172px;margin-left:30px">
+      <img src="{@docRoot}images/brand/en_app_rgb_wo_60.png" alt="" />
+      <p style="text-align:center">
+         <a href="{@docRoot}images/brand/en_app_rgb_wo_45.png">129x45</a> |
+         <a href="{@docRoot}images/brand/en_app_rgb_wo_60.png">172x60</a></p>
+    </div>
+
+    <div style="float:right;clear:right;width:172px;margin-left:30px">
+      <img src="{@docRoot}images/brand/en_generic_rgb_wo_60.png" alt="" />
+      <p style="text-align:center">
+         <a href="{@docRoot}images/brand/en_generic_rgb_wo_45.png">129x45</a> |
+         <a href="{@docRoot}images/brand/en_generic_rgb_wo_60.png">172x60</a></p>
+    </div>
+
+    <p>你可以在网站和宣传资料中使用“即刻获取 Google Play”和“即刻获取 Android 应用 Google Play”徽标作为徽章,以指向你在 Google Play 上的商品。你也可以使用其他 Google Play 徽章格式,以及用于音乐、图书、杂志、电影和电视节目的徽章。要申请这些徽章,请使用 <a href="https://docs.google.com/forms/d/1YE5gZpAAcFKjYcUddCsK1Bv9a9Y-luaLVnkazVlaJ2w/viewform">Android 和 Google Play 品牌权限咨询表单</a>。</p>
+
+    <ul>
+      <li>请不要修改徽章图片的颜色、比例、间距或其他任何方面。
+      </li>
+      <li>当与其他应用市场的徽标一起使用时,Google Play 徽标尺寸不应小于这些徽标的尺寸。</li>
+      <li>在线使用时,徽章应链接至以下内容之一:
+        <ul>
+          <li>由你发布的商品的列表,例如:<br />
+          <span style="margin-left:1em">http://play.google.com/store/search?q=publisherName<em></em></span>
+          </li>
+          <li>Google Play 中相应应用的商品详情页,例如:<br />
+          <span style="margin-left:1em">http://play.google.com/store/apps/details?id=packageName<em></em></span>
+          </li>
+        </ul>
+      </li>
+    </ul>
+
+    <p>要快速创建链接至你在 Google Play 上的应用的徽章,请使用 <a href="{@docRoot}distribute/tools/promote/badges.html">Google Play 徽章生成器</a>(支持 40 多种语言)。</p>
+
+    <p>要创建自定义尺寸的徽章,请下载<a href="{@docRoot}distribute/tools/promote/badge-files.html">支持 40 多种语言的 Google Play 徽章</a>的 Adobe® Illustrator® (.ai) 文件。</p>
+
+    <p>要详细了解如何链接至 Google Play 中的商品详情页,请参阅<a href="{@docRoot}distribute/tools/promote/linking.html">链接至你的商品</a>。</p>
+
+  <h2 id="Marketing_Review">营销审核和品牌咨询</h2>
+
+  <p>请使用 <a href="https://docs.google.com/forms/d/1YE5gZpAAcFKjYcUddCsK1Bv9a9Y-luaLVnkazVlaJ2w/viewform">Android 和 Google Play 品牌权限咨询表单</a>提交相关的营销审核或品牌咨询。一般情况下,我们至少需要一周时间才能做出回复。</p>
+
+      </p>
diff --git a/docs/html-intl/intl/zh-cn/distribute/tools/promote/device-art.jd b/docs/html-intl/intl/zh-cn/distribute/tools/promote/device-art.jd
new file mode 100644
index 0000000..b6517b3
--- /dev/null
+++ b/docs/html-intl/intl/zh-cn/distribute/tools/promote/device-art.jd
@@ -0,0 +1,675 @@
+page.title=Device Art Generator
+page.image=/images/device-art-ex-crop.jpg
+page.metaDescription=为了更好看的宣传图片和改善视觉语境,拖放你的应用程序的屏幕截图到真实设备的图稿。
+
+@jd:body
+
+<p>你可以使用 Device Art Generator 方便快捷地将应用截图嵌入到真实设备的效果图中。这样,当用户在你的网站上或其他宣传材料中看到你的应用截图时,就能更加直观地了解应用的内容环境</p>
+
+  <p class="note"><strong>注意</strong>:请勿将此处生成的图片用作 Google Play 应用商品详情中的 1024x500 置顶大图或屏幕截图</p>
+
+
+
+<div class="supported-browser">
+
+<div class="layout-content-row">
+  <div class="layout-content-col span-3">
+    <h4>第 1 步</h4>
+    <p>将屏幕截图从桌面拖动到右侧的设备上生成效果图。</p>
+  </div>
+  <div class="layout-content-col span-10">
+    <ul class="device-list primary"></ul>
+    <a href="#" id="archive-expando">旧款设备</a>
+    <ul class="device-list archive"></ul>
+  </div>
+</div>
+
+
+
+<div class="layout-content-row">
+  <div class="layout-content-col span-3">
+      <h4>第 2 步</h4>
+      <p>你可以对此效果图进行优化,然后将其拖动到桌面上保存。</p>
+    <p id="frame-customizations">
+      <input type="checkbox" id="output-shadow" checked="checked" class="form-field-checkbutton">
+      <label for="output-shadow">阴影</label><br>
+      <input type="checkbox" id="output-glare" checked="checked" class="form-field-checkbutton">
+      <label for="output-glare">屏幕反光</label><br><br>
+      <a class="button" id="rotate-button">旋转</a>
+    </p>
+  </div>
+  <div class="layout-content-col span-10">
+    <!-- position:relative fixes an issue where dragging an image out of a inline-block container
+         produced no drag feedback image in Chrome 28. -->
+    <div id="output" style="position:relative">无输入图片。</div>
+  </div>
+</div>
+
+</div>
+
+<div class="unsupported-browser" style="display: none">
+  <p class="warning"><strong>错误</strong>:此页面需要使用<span id="unsupported-browser-reason">特定功能</span>才能打开,但你的网络浏览器不支持这些功能。要继续,请在受支持的网络浏览器(如 <strong>Google Chrome</strong>)中打开此页面。</p>
+    <a href="https://www.google.com/chrome/" class="button">下载 Google Chrome</a>
+  <br><br>
+</div>
+
+<style>
+  h4 {
+    text-transform: uppercase;
+  }
+
+  .device-list {
+    padding: 1em 0 0 0;
+    margin: 0;
+  }
+
+  .device-list li {
+    display: inline-block;
+    vertical-align: bottom;
+    margin: 0;
+    margin-right: 20px;
+    text-align: center;
+  }
+
+  .device-list li .thumb-container {
+    display: inline-block;
+  }
+
+  .device-list li .thumb-container img {
+    margin-bottom: 8px;
+    opacity: 0.6;
+
+    -webkit-transition: -webkit-transform 0.2s, opacity 0.2s;
+       -moz-transition:    -moz-transform 0.2s, opacity 0.2s;
+            transition:         transform 0.2s, opacity 0.2s;
+  }
+
+  .device-list li.drag-hover .thumb-container img {
+    opacity: 1;
+
+    -webkit-transform: scale(1.1);
+       -moz-transform: scale(1.1);
+            transform: scale(1.1);
+  }
+
+  .device-list li .device-details {
+    font-size: 13px;
+    line-height: 16px;
+    color: #888;
+  }
+
+  .device-list li .device-url {
+    font-weight: bold;
+  }
+
+  #archive-expando {
+    display: block;
+    font-size: 13px;
+    font-weight: bold;
+    color: #333;
+    text-transform: uppercase;
+    margin-top: 16px;
+    padding-top: 16px;
+    padding-left: 28px;
+    border-top: 1px solid transparent;
+    background: transparent url({@docRoot}assets/images/styles/disclosure_down.png)
+                no-repeat scroll 0 8px;
+    -webkit-transition: border 0.2s;
+       -moz-transition: border 0.2s;
+            transition: border 0.2s;
+  }
+
+  #archive-expando.expanded {
+    background-image: url({@docRoot}assets/images/styles/disclosure_up.png);
+    border-top: 1px solid #ccc;
+  }
+
+  .device-list.archive {
+    max-height: 0;
+    overflow: hidden;
+    opacity: 0;
+
+    -webkit-transition: max-height 0.2s, opacity 0.2s;
+       -moz-transition: max-height 0.2s, opacity 0.2s;
+            transition: max-height 0.2s, opacity 0.2s;
+  }
+
+  .device-list.archive.expanded {
+    opacity: 1;
+    max-height: 300px;
+  }
+
+  #output {
+    color: #f44;
+    font-style: italic;
+  }
+
+  #output img {
+    max-height: 500px;
+  }
+</style>
+<script>
+  // Global variables
+  var g_currentImage;
+  var g_currentDevice;
+  var g_currentObjectURL;
+  var g_currentBlob;
+
+  // Global constants
+  var MSG_INVALID_INPUT_IMAGE = 'Invalid screenshot provided. Screenshots must be PNG files '
+      + 'matching the target device\'s screen aspect ratio in either portrait or landscape.';
+  var MSG_NO_INPUT_IMAGE = '将屏幕截图(.PNG)从桌面拖动到以上所列设备。';
+  var MSG_GENERATING_IMAGE = 'Generating device art&hellip;';
+
+  var MAX_DISPLAY_HEIGHT = 126; // XOOM, to fit into 200px wide
+
+  // Device manifest.
+  var DEVICES = [
+    {
+      id: 'nexus_5',
+      title: 'Nexus 5',
+      url: 'http://www.google.com/nexus/5/',
+      physicalSize: 5,
+      physicalHeight: 5.43,
+      density: 'XXHDPI',
+      landRes: ['shadow', 'back', 'fore'],
+      landOffset: [436,306],
+      portRes: ['shadow', 'back', 'fore'],
+      portOffset: [304,436],
+      portSize: [1080,1920],
+    },
+    {
+      id: 'nexus_6',
+      title: 'Nexus 6',
+      url: 'http://www.google.com/nexus/6/',
+      physicalSize: 6,
+      physicalHeight: 6.27,
+      density: '560DPI',
+      landRes: ['shadow', 'back', 'fore'],
+      landOffset: [489,327],
+      portRes: ['shadow', 'back', 'fore'],
+      portOffset: [327,489],
+      portSize: [1440, 2560],
+    },
+    {
+      id: 'nexus_7',
+      title: 'Nexus 7',
+      url: 'http://www.google.com/nexus/7/',
+      physicalSize: 7,
+      physicalHeight: 8,
+      actualResolution: [1200,1920],
+      density: 'XHDPI',
+      landRes: ['shadow', 'back', 'fore'],
+      landOffset: [326,245],
+      portRes: ['shadow', 'back', 'fore'],
+      portOffset: [244,326],
+      portSize: [800,1280]
+    },
+    {
+      id: 'nexus_9',
+      title: 'Nexus 9',
+      url: 'http://www.google.com/nexus/9/',
+      physicalSize: 9,
+      physicalHeight: 8.98,
+      actualResolution: [1536,2048],
+      density: 'XHDPI',
+      landRes: ['shadow', 'back', 'fore'],
+      landOffset: [514,350],
+      portRes: ['shadow', 'back', 'fore'],
+      portOffset: [348,514],
+      portSize: [1536,2048],
+    },
+    {
+      id: 'nexus_10',
+      title: 'Nexus 10',
+      url: 'http://www.google.com/nexus/10/',
+      physicalSize: 10,
+      physicalHeight: 7,
+      actualResolution: [1600,2560],
+      density: 'XHDPI',
+      landRes: ['shadow', 'back', 'fore'],
+      landOffset: [227,217],
+      portRes: ['shadow', 'back', 'fore'],
+      portOffset: [217,223],
+      portSize: [800,1280],
+      archived: true
+    },
+    {
+      id: 'nexus_7_2012',
+      title: 'Nexus 7 (2012)',
+      url: 'http://www.google.com/nexus/7/',
+      physicalSize: 7,
+      physicalHeight: 7.81,
+      density: '213dpi',
+      landRes: ['shadow', 'back', 'fore'],
+      landOffset: [315,270],
+      portRes: ['shadow', 'back', 'fore'],
+      portOffset: [264,311],
+      portSize: [800,1280],
+      archived: true
+    },
+    {
+      id: 'nexus_4',
+      title: 'Nexus 4',
+      url: 'http://www.google.com/nexus/4/',
+      physicalSize: 4.7,
+      physicalHeight: 5.27,
+      density: 'XHDPI',
+      landRes: ['shadow', 'back', 'fore'],
+      landOffset: [349,214],
+      portRes: ['shadow', 'back', 'fore'],
+      portOffset: [213,350],
+      portSize: [768,1280],
+      archived: true
+    },
+  ];
+
+  DEVICES = DEVICES.sort(function(x, y) { return x.physicalSize - y.physicalSize; });
+
+  var MAX_HEIGHT = 0;
+  for (var i = 0; i < DEVICES.length; i++) {
+    MAX_HEIGHT = Math.max(MAX_HEIGHT, DEVICES[i].physicalHeight);
+  }
+
+  // Setup performed once the DOM is ready.
+  $(document).ready(function() {
+    if (!checkBrowser()) {
+      return;
+    }
+
+    polyfillCanvasToBlob();
+    setupUI();
+
+    // Set up Chrome drag-out
+    $.event.props.push("dataTransfer");
+    document.body.addEventListener('dragstart', function(e) {
+      var target = e.target;
+      if (target.classList.contains('dragout')) {
+        e.dataTransfer.setData('DownloadURL', target.dataset.downloadurl);
+      }
+    }, false);
+  });
+
+  /**
+   * Returns the device from DEVICES with the given id.
+   */
+  function getDeviceById(id) {
+    for (var i = 0; i < DEVICES.length; i++) {
+      if (DEVICES[i].id == id)
+        return DEVICES[i];
+    }
+    return;
+  }
+
+  /**
+   * Checks to make sure the browser supports this page. If not,
+   * updates the UI accordingly and returns false.
+   */
+  function checkBrowser() {
+    // Check for browser support
+    var browserSupportError = null;
+
+    // Must have <canvas>
+    var elem = document.createElement('canvas');
+    if (!elem.getContext || !elem.getContext('2d')) {
+      browserSupportError = 'HTML5 canvas.';
+    }
+
+    // Must have FileReader
+    if (!window.FileReader) {
+      browserSupportError = 'desktop file access';
+    }
+
+    if (browserSupportError) {
+      $('.supported-browser').hide();
+
+      $('#unsupported-browser-reason').html(browserSupportError);
+      $('.unsupported-browser').show();
+      return false;
+    }
+
+    return true;
+  }
+
+  function setupUI() {
+    $('#output').html(MSG_NO_INPUT_IMAGE);
+
+    $('#frame-customizations').hide();
+
+    $('#output-shadow, #output-glare').click(function() {
+      createFrame();
+    });
+
+    // Build device list.
+    $.each(DEVICES, function() {
+      var resolution = this.actualResolution || this.portSize;
+      var scaleFactorText = '';
+      if (resolution[0] != this.portSize[0]) {
+        scaleFactorText = '<br>等比例缩小至' + (100 * (this.portSize[0] / resolution[0])).toFixed(0) +
+            '% 输出';
+      } else {
+        scaleFactorText = '<br>&nbsp;';
+      }
+
+      $('<li>')
+          .append($('<div>')
+              .addClass('thumb-container')
+              .append($('<img>')
+                  .attr('src', '../../../../../distribute/tools/promote/device-art-resources/' + this.id + '/thumb.png')
+                  .attr('height',
+                      Math.floor(MAX_DISPLAY_HEIGHT * this.physicalHeight / MAX_HEIGHT))))
+          .append($('<div>')
+              .addClass('device-details')
+              .html((this.url
+                  ? ('<a class="device-url" href="' + this.url + '">' + this.title + '</a>')
+                  : this.title) +
+                  '<br>' +  this.physicalSize + '" @ ' + this.density +
+                  '<br>' + (resolution[0] + 'x' + resolution[1]) + scaleFactorText))
+          .data('deviceId', this.id)
+          .appendTo(this.archived ? '.device-list.archive' : '.device-list.primary');
+    });
+
+    // Set up "older devices" expando.
+    $('#archive-expando').click(function() {
+      if ($(this).hasClass('expanded')) {
+        $(this).removeClass('expanded');
+        $('.device-list.archive').removeClass('expanded');
+      } else {
+        $(this).addClass('expanded');
+        $('.device-list.archive').addClass('expanded');
+      }
+      return false;
+    });
+
+    // Set up drag and drop.
+    $('.device-list li')
+        .live('dragover', function(evt) {
+          $(this).addClass('drag-hover');
+          evt.dataTransfer.dropEffect = 'link';
+          evt.preventDefault();
+        })
+        .live('dragleave', function(evt) {
+          $(this).removeClass('drag-hover');
+        })
+        .live('drop', function(evt) {
+          $('#output').empty().html(MSG_GENERATING_IMAGE);
+          $(this).removeClass('drag-hover');
+          g_currentDevice = getDeviceById($(this).closest('li').data('deviceId'));
+          evt.preventDefault();
+          loadImageFromFileList(evt.dataTransfer.files, function(data) {
+            if (data == null) {
+              $('#output').html(MSG_INVALID_INPUT_IMAGE);
+              return;
+            }
+            loadImageFromUri(data.uri, function(img) {
+              g_currentFilename = data.name;
+              g_currentImage = img;
+              createFrame();
+              // Send the event to Analytics
+              ga('send', 'event', 'Distribute', 'Create Device Art', g_currentDevice.title);
+            });
+          });
+        });
+
+    // Set up rotate button.
+    $('#rotate-button').click(function() {
+      if (!g_currentImage) {
+        return;
+      }
+
+      var w = g_currentImage.naturalHeight;
+      var h = g_currentImage.naturalWidth;
+      var canvas = $('<canvas>')
+          .attr('width', w)
+          .attr('height', h)
+          .get(0);
+
+      var ctx = canvas.getContext('2d');
+      ctx.rotate(-Math.PI / 2);
+      ctx.translate(-h, 0);
+      ctx.drawImage(g_currentImage, 0, 0);
+
+      loadImageFromUri(canvas.toDataURL('image/png'), function(img) {
+        g_currentImage = img;
+        createFrame();
+      });
+    });
+  }
+
+  /**
+   * Generates the frame from the current selections (g_currentImage and g_currentDevice).
+   */
+  function createFrame() {
+    var port;
+
+    var aspect1 = g_currentImage.naturalWidth / g_currentImage.naturalHeight;
+    var aspect2 = g_currentDevice.portSize[0] / g_currentDevice.portSize[1];
+
+    if (aspect1 == aspect2) {
+      port = true;
+    } else if (aspect1 == 1 / aspect2) {
+      port = false;
+    } else {
+      alert('The screenshot must have an aspect ratio of ' +
+          aspect2.toFixed(3) + ' or ' + (1 / aspect2).toFixed(3) +
+          ' (ideally ' + g_currentDevice.portSize[0] + 'x' + g_currentDevice.portSize[1] +
+          ' or ' + g_currentDevice.portSize[1] + 'x' + g_currentDevice.portSize[0] + ').');
+      $('#output').html(MSG_INVALID_INPUT_IMAGE);
+      return;
+    }
+
+    // Load image resources
+    var res = port ? g_currentDevice.portRes : g_currentDevice.landRes;
+    var resList = {};
+    for (var i = 0; i < res.length; i++) {
+      resList[res[i]] = '../../../../../distribute/tools/promote/device-art-resources/' + g_currentDevice.id + '/' +
+          (port ? 'port_' : 'land_') + res[i] + '.png'
+    }
+
+    var resourceImages = {};
+    loadImageResources(resList, function(r) {
+      resourceImages = r;
+      continueWithResources_();
+    });
+
+    function continueWithResources_() {
+      var width = resourceImages['back'].naturalWidth;
+      var height = resourceImages['back'].naturalHeight;
+      var offset = port ? g_currentDevice.portOffset : g_currentDevice.landOffset;
+      var size = port
+          ? g_currentDevice.portSize
+          : [g_currentDevice.portSize[1], g_currentDevice.portSize[0]];
+
+      var canvas = document.createElement('canvas');
+      canvas.width = width;
+      canvas.height = height;
+
+      var ctx = canvas.getContext('2d');
+      if (resourceImages['shadow'] && $('#output-shadow').is(':checked')) {
+        ctx.drawImage(resourceImages['shadow'], 0, 0);
+      }
+      ctx.drawImage(resourceImages['back'], 0, 0);
+      ctx.fillStyle = '#000';
+      ctx.fillRect(offset[0], offset[1], size[0], size[1]);
+      ctx.drawImage(g_currentImage, offset[0], offset[1], size[0], size[1]);
+      if (resourceImages['fore'] && $('#output-glare').is(':checked')) {
+        ctx.drawImage(resourceImages['fore'], 0, 0);
+      }
+
+      window.URL = window.URL || window.webkitURL;
+      if (canvas.toBlob && window.URL.createObjectURL) {
+        if (g_currentObjectURL) {
+          window.URL.revokeObjectURL(g_currentObjectURL);
+          g_currentObjectURL = null;
+        }
+        if (g_currentBlob) {
+          if (g_currentBlob.close) {
+            g_currentBlob.close();
+          }
+          g_currentBlob = null;
+        }
+
+        canvas.toBlob(function(blob) {
+          if (!blob) {
+            continueWithFinalUrl_(canvas.toDataURL('image/png'));
+            return;
+          }
+          g_currentBlob = blob;
+          g_currentObjectURL = window.URL.createObjectURL(blob);
+          continueWithFinalUrl_(g_currentObjectURL);
+        }, 'image/png');
+      } else {
+        continueWithFinalUrl_(canvas.toDataURL('image/png'));
+      }
+    }
+
+    function continueWithFinalUrl_(imageUrl) {
+      var filename = g_currentFilename
+          ? g_currentFilename.replace(/^(.+?)(\.\w+)?$/, '$1_framed.png')
+          : 'framed_screenshot.png';
+
+      var $link = $('<a>')
+          .attr('download', filename)
+          .attr('href', imageUrl)
+          .append($('<img>')
+              .addClass('dragout')
+              .attr('src', imageUrl)
+              .attr('draggable', true)
+              .attr('data-downloadurl', ['image/png', filename, imageUrl].join(':')))
+          .appendTo($('#output').empty());
+
+      $('#frame-customizations').show();
+    }
+  }
+
+  /**
+   * Loads an image from a data URI. The callback will be called with the <img> once
+   * it loads.
+   */
+  function loadImageFromUri(uri, callback) {
+    callback = callback || function(){};
+
+    var img = document.createElement('img');
+    img.src = uri;
+    img.onload = function() {
+      callback(img);
+    };
+    img.onerror = function() {
+      callback(null);
+    }
+  }
+
+  /**
+   * Loads a set of images (organized by ID). Once all images are loaded, the callback
+   * is triggered with a dictionary of <img>'s, organized by ID.
+   */
+  function loadImageResources(images, callback) {
+    var imageResources = {};
+
+    var checkForCompletion_ = function() {
+      for (var id in images) {
+        if (!(id in imageResources))
+          return;
+      }
+      (callback || function(){})(imageResources);
+      callback = null;
+    };
+
+    for (var id in images) {
+      var img = document.createElement('img');
+      img.src = images[id];
+      (function(img, id) {
+        img.onload = function() {
+          imageResources[id] = img;
+          checkForCompletion_();
+        };
+        img.onerror = function() {
+          imageResources[id] = null;
+          checkForCompletion_();
+        }
+      })(img, id);
+    }
+  }
+
+  /**
+   * Loads the first valid image from a FileList (e.g. drag + drop source), as a data URI. This
+   * method will throw an alert() in case of errors and call back with null.
+   *
+   * @param {FileList} fileList The FileList to load.
+   * @param {Function} callback The callback to fire once image loading is done (or fails).
+   * @return Returns an object containing 'uri' representing the loaded image. There will also be
+   *      a 'name' field indicating the file name, if one is available.
+   */
+  function loadImageFromFileList(fileList, callback) {
+    fileList = fileList || [];
+
+    var file = null;
+    for (var i = 0; i < fileList.length; i++) {
+      if (fileList[i].type.toLowerCase().match(/^image\/(png|jpeg|jpg)/)) {
+        file = fileList[i];
+        break;
+      }
+    }
+
+    if (!file) {
+      alert('Please use a valid screenshot file (PNG or JPEG format).');
+      callback(null);
+      return;
+    }
+
+    var fileReader = new FileReader();
+
+    // Closure to capture the file information.
+    fileReader.onload = function(e) {
+      callback({
+        uri: e.target.result,
+        name: file.name
+      });
+    };
+    fileReader.onerror = function(e) {
+      switch(e.target.error.code) {
+        case e.target.error.NOT_FOUND_ERR:
+          alert('File not found.');
+          break;
+        case e.target.error.NOT_READABLE_ERR:
+          alert('File is not readable.');
+          break;
+        case e.target.error.ABORT_ERR:
+          break; // noop
+        default:
+          alert('An error occurred reading this file.');
+      }
+      callback(null);
+    };
+    fileReader.onabort = function(e) {
+      alert('File read cancelled.');
+      callback(null);
+    };
+
+    fileReader.readAsDataURL(file);
+  }
+
+  /**
+   * Adds a simple version of Canvas.toBlob if toBlob isn't available.
+   */
+  function polyfillCanvasToBlob() {
+    if (!HTMLCanvasElement.prototype.toBlob && window.Blob) {
+      HTMLCanvasElement.prototype.toBlob = function(callback, mimeType, quality) {
+        if (typeof callback != 'function') {
+          throw new TypeError('Function expected');
+        }
+        var dataURL = this.toDataURL(mimeType, quality);
+        mimeType = dataURL.split(';')[0].split(':')[1];
+        var bs = window.atob(dataURL.split(',')[1]);
+        if (dataURL == 'data:,' || !bs.length) {
+          callback(null);
+          return;
+        }
+        for (var ui8arr = new Uint8Array(bs.length), i = 0; i < bs.length; ++i) {
+          ui8arr[i] = bs.charCodeAt(i);
+        }
+        callback(new Blob([ui8arr.buffer /* req'd for Safari */ || ui8arr], {type: mimeType}));
+      };
+    }
+  }
+</script>
diff --git a/docs/html-intl/intl/zh-cn/distribute/tools/promote/linking.jd b/docs/html-intl/intl/zh-cn/distribute/tools/promote/linking.jd
new file mode 100644
index 0000000..c7cf7cf
--- /dev/null
+++ b/docs/html-intl/intl/zh-cn/distribute/tools/promote/linking.jd
@@ -0,0 +1,186 @@
+page.title=链接到你的商品
+page.image=/images/gp-linking-ex-crop.png
+page.metaDescription=了解如何构建链接,把用户从浏览或搜索带到你在 Google Play 的应用程序。
+
+@jd:body
+
+	<div class="sidebox-wrapper">
+	<div class="sidebox">
+	<a href="badges.html">
+	  <img alt="即刻获取 Google Play" src="{@docRoot}images/brand/en_app_rgb_wo_45.png" />
+	</a>
+	<p>如需获得内含 Google Play 品牌图标的链接,请访问<a href="badges.html">徽章</a>页面。</p>
+	</div>
+	</div>
+
+	<p>Google Play 提供多种链接格式,可让你按自己需要的方式将用户从 Android 应用、网页、广告、评论、文章、社交媒体帖子等链接到你的商品。</p> 
+
+	<p>这些链接格式可让你:</p>
+	<ul>
+	<li>链接到特定应用的<a href="#OpeningDetails">商品详情页</a></li>
+	<li>链接到<a href="#OpeningPublisher">你所有应用的列表</a>,或</li>
+	<li>链接到你选择的<a href="#PerformingSearch">搜索结果</a></li>
+	<li>链接到 Google Play 上的某个<a href="#OpeningCollection">合集</a></li>
+	</ul>
+
+	<p>如果你从 Android 应用上提供链接,还可以控制此链接是启动 Play 商店应用还是启动浏览器,后者会将用户转至 Google Play 网站。</p>
+
+	<h2 id="OpeningDetails">链接到商品详情页</h2>
+
+	<p>如果提供下面这种格式的深层链接,你可以将用户直接指向特定应用的商品详情页。在商品详情页上,用户可以查看应用的说明、屏幕截图以及评论等,还可以安装该应用。</p>
+
+	<p>要创建此链接,你需要提供完全符合要求的“程序包名称”(可在相应应用的<a href="{@docRoot}guide/topics/manifest/manifest-element.html#package">AndroidManifest.xml</a>中找到)。<em></em>此外,程序包名称还会显示在开发者控制台中。</p>
+
+	<dl>
+	<dt><strong>从网站上:</strong></dt>
+	<dd>
+	<pre>http://play.google.com/store/apps/details?id=&lt;package_name&gt;</pre>
+	</dd>
+	<dt><strong>从 Android 应用上:</strong></dt>
+	<dd>
+	<pre>market://details?id=&lt;package_name&gt;</pre>
+	</dd>
+	</dl>
+
+	<p>示例如下:</p>
+
+	<p style="margin-left:1em"><code><a href="http://play.google.com/store/apps/details?id=com.google.android.apps.maps">http://play.google.com/store/apps/details?id=com.google.android.apps.maps</a></code></p>
+
+	<p>要详细了解如何在 Android 应用中发送链接,请参阅<a href="#android-app">从 Android 应用提供链接</a>。</p>
+
+
+
+	<h2 id="OpeningPublisher">链接到商品列表</h2>
+
+	<p>如果提供下面这种格式的链接,你可以将用户指向你所发布应用的列表。通过商品列表,用户可以查看特定发布者提供的所有应用,以及各个应用的评分、编辑徽章和“安装”按钮。</p>
+
+	<p>要创建此链接,你需要提供“发布者名称”(可在开发者控制台中找到)。<em></em></p>
+
+	<dl>
+	<dt><strong>从网站上:</strong></dt>
+	<dd>
+	<pre>http://play.google.com/store/search?q=pub:&lt;publisher_name&gt;</pre>
+	</dd>
+	<dt><strong>从 Android 应用上:</strong></dt>
+	<dd>
+	<pre>market://search?q=pub:&lt;publisher_name&gt;</pre>
+	</dd>
+	</dl>
+
+	<p>示例如下:</p>
+
+	<p style="margin-left:1em"><code><a href="http://play.google.com/store/search?q=pub:Google Inc.">http://play.google.com/store/search?q=pub:Google Inc.</a></code></p>
+
+	<p>要详细了解如何在 Android 应用中发送链接,请参阅<a href="#android-app">从 Android 应用提供链接</a>。</p>
+
+
+	<h2 id="PerformingSearch">链接到搜索结果</h2>
+
+	<p>如果提供下面这种格式的链接,你可以将用户指向 Google Play 上的搜索查询结果页。搜索结果页会显示与此次查询相匹配的应用的列表(可能还会显示其他内容),以及各个应用的评分、徽章和“安装”按钮。</p>
+
+	<p>要创建此链接,你只需提供一个搜索查询字符串。如果你希望此次查询搜索 Google Play 应用商品详情以外的地方,则可以去掉该链接网址中的 <code>&amp;c=apps</code> 部分。</p>
+
+	<dl>
+	<dt><strong>从网站上:</strong></dt>
+	<dd>
+	<pre>http://play.google.com/store/search?q=&lt;search_query&gt;&amp;c=apps</pre>
+	</dd>
+	<dt><strong>从 Android 应用上:</strong></dt>
+	<dd>
+	<pre>market://search?q=&lt;seach_query&gt;&amp;c=apps</pre>
+	</dd>
+	</dl>
+
+	<p>示例如下:</p>
+
+	<p style="margin-left:1em"><code><a href="http://play.google.com/store/search?q=maps&amp;c=apps">http://play.google.com/store/search?q=maps&amp;c=apps</a></code></p>
+
+	<p>要详细了解如何在 Android 应用中发送链接,请参阅<a href="#android-app">从 Android 应用提供链接</a>。</p>
+
+
+
+	<h2 id="OpeningCollection">链接到某个合集</h2>
+
+	<p>如果你的应用获得推荐或显示在某个 Google Play 排行榜或合集中,你可以提供下面这种格式的链接,将用户直接指向相应合集。该合集会按照一定顺序排列显示其中的应用,并会显示各个应用的评分、简短说明和“安装”按钮。</p>
+
+	<dl>
+	<dt><strong>从网站上:</strong></dt>
+	<dd>
+	<pre>http://play.google.com/store/apps/collection/&lt;collection_name&gt;</pre>
+	</dd>
+	<dt><strong>从 Android 应用上:</strong></dt>
+	<dd>
+	<pre>market://apps/collection/&lt;collection_name&gt;</pre>
+	</dd>
+	</dl>
+
+	<p>示例如下:</p>
+
+	<p style="margin-left:1em"><code><a href="http://play.google.com/store/apps/collection/editors_choice">http://play.google.com/store/apps/collection/editors_choice</a></code></p>
+
+	<p>要详细了解如何在 Android 应用中发送链接,请参阅<a href="#android-app">从 Android 应用提供链接</a>。</p>
+
+	<p class="table-caption"><strong>表 1:</strong> Google Play 上的合集</p>
+
+	<table>
+	<tr>
+	<th>合集</th><th>collection_name</th>
+	</tr>
+	<tr><td>店员推荐(精选)</td><td>featured</td></tr>
+	<tr><td>编辑精选</td><td>editors_choice</td></tr>
+	<tr><td>热门付费</td><td>topselling_paid</td></tr>
+	<tr><td>热门免费</td><td>topselling_free</td></tr>
+	<tr><td>热门免费新品</td><td>topselling_new_free</td></tr>
+	<tr><td>热门付费新品</td><td>topselling_new_paid</td></tr>
+	<tr><td>创收最高</td><td>topgrossing</td></tr>
+	<tr><td>上升最快</td><td>movers_shakers</td></tr>
+	<tr><td>最畅销游戏</td><td>topselling_paid_game</td></tr>
+	</table>
+
+
+	<h2 id="android-app">从 Android 应用提供链接</h2>
+
+	<p>用户在 Android 设备上可以访问两种常规格式的链接。这两种格式在设备上触发的行为略有不同:</p>
+
+	<ul>
+	<li><code>market://</code>    启动 Play 商店应用以加载目标网页。</li>
+	<li><code>http://</code>    让用户选择是启用 Play 商店应用还是浏览器来处理请求。如果选择浏览器处理请求,它会在 Google Play 网站上加载目标网页。</li>
+	</ul>
+
+	<p>一般情况下,你应对网页上的链接使用 <code>http://</code> 格式;对 Android 应用中的链接使用 <code>market://</code>。</p>
+
+	<p>如果你希望用户从 Android 应用链接到你的商品,请创建一个可打开 Google Play 网址的 <code><a href="{@docRoot}reference/android/content/Intent.html">Intent</a></code>,如下例所示。</p>
+
+	<pre>
+	Intent intent = new Intent(Intent.ACTION_VIEW);
+	intent.setData(Uri.parse("market://details?id=com.example.android"));
+	startActivity(intent);
+	</pre>
+
+
+	<h2 id="UriSummary">网址格式总结</h2>
+
+	<p>下表总结了 Google Play 当前支持的 URI(在网页上和在 Android 应用中),如前几部分所述。</p>
+
+	<table>
+	<tr>
+	<th>结果</th>
+	<th>网页链接</th>
+	<th>Android 应用链接</th>
+	</tr>
+	<tr>
+	<td style="width:72px"><span style="white-space:nowrap;">显示特定应用的商品详情页</span></td>
+	<td><code>http://play.google.com/store/apps/details?id=&lt;package_name&gt;</code>
+	<td><code>market://details?id=&lt;package_name&gt;</code></td>
+	</td></tr>
+	<tr>
+	<td>显示特定发布者的应用</td>
+	<td><nobr><code>http://play.google.com/store/search?q=pub:&lt;publisher_name&gt;</code></nobr></td>
+	<td><nobr><code>market://search?q=pub:&lt;publisher_name&gt;</code></nobr></td>
+	</tr>
+	<tr>
+	<td>使用常规字符串查询搜索应用</td>
+	<td><code>http://play.google.com/store/search?q=&lt;query&gt;</code></td>
+	<td><code>market://search?q=&lt;query&gt;</code></td>
+	</tr>
+	</table>
diff --git a/docs/html-intl/intl/zh-cn/google/play/billing/api.jd b/docs/html-intl/intl/zh-cn/google/play/billing/api.jd
new file mode 100644
index 0000000..fbdbac6
--- /dev/null
+++ b/docs/html-intl/intl/zh-cn/google/play/billing/api.jd
@@ -0,0 +1,117 @@
+page.title=应用内结算 API
+parent.title=In-app Billing
+parent.link=index.html
+@jd:body
+
+      <div id="qv-wrapper">
+  <div id="qv">
+
+    <h2>主题</h2>
+    <ol>
+      <li><a href="#producttypes">商品类型</a>
+         <ol>
+         <li><a href="#managed">托管的应用内商品</a><li>
+         <li><a href="#subs">订阅</a><li>
+         </li></li></li></li></ol>
+      </li>
+      <li><a href="#purchase">购买商品</a></li>
+      <li><a href="#consume">消耗应用内商品</a>
+         <ol>
+         <li><a href="#consumetypes">非消耗型和消耗型应用内商品</a><li>
+         <li><a href="#managingconsumables">管理消耗型商品购买流程</a><li>
+         </li></li></li></li></ol>
+      </li>
+      <li><a href="#caching">本地缓存</a></li>
+    </ol>
+
+    <h2>参考</h2>
+    <ol>
+      <li><a href="{@docRoot}google/play/billing/billing_reference.html">应用内结算参考 (V3)</a></li>
+    </ol>
+
+   <h2>另请参见</h2>
+    <ol>
+      <li><a href="{@docRoot}training/in-app-billing/index.html">销售应用内商品</a></li>
+    </ol>  
+  </div>
+  </div>
+
+  <p>借助应用内结算 API 第 3 版,您可以更轻松地将应用内结算功能集成到自己的应用中。此版本包括下列功能:经过改进的同步购买流程、可让您轻松跟踪消耗型商品所有权的 API 以及在本地缓存应用内购买数据。</p>
+
+  <h2 id="producttypes">商品类型</h2>
+  <p>您可以使用 Google Play 开发者控制台来定义您的商品,包括商品类型、SKU、价格和说明等等。有关详情,请参阅<a href="{@docRoot}google/play/billing/billing_admin.html">管理应用内结算</a>。API 第 3 版支持两种商品类型,即托管的应用内商品和订阅。</p>
+  <h3 id="managed">托管的应用内商品</h3>
+  <p>托管的应用内商品是指由 Google Play 跟踪和管理其所有权信息的商品。当用户购买托管的应用内商品时,Google Play 会针对每位用户分别存储各商品的购买信息。这使得您日后可随时向 Google Play 进行查询,恢复特定用户购买的商品的状态。这类信息会永久保存在 Google Play 服务器上,即使用户卸载应用或更换设备也是如此。</p>
+  <p>如果您使用的是 API 第 3 版,还可以在应用内消耗托管的商品。通常情况下,您可以对那些可供多次购买的商品(例如游戏中使用的货币、燃料或魔法药)实施消耗。托管的商品在被购买后,必须先向 Google Play 发送消耗请求进行消耗,然后才能供用户再次购买。要详细了解应用内商品的消耗,请参阅<a href="#consume">消耗商品</a></p>
+
+  <h3 id="subs">订阅</h3>
+  <p>订阅是应用内结算中提供的一种商品类型,可让您通过按月或按年结算的方式在应用内向用户销售内容、服务或功能。您可以在各类应用或游戏中销售对几乎所有类型的数字内容的订阅。要了解订阅的工作原理,请参阅<a href="{@docRoot}google/play/billing/billing_subscriptions.html">应用内结算订阅</a>。</p>
+  <p>使用 API 第 3 版,您可以使用与应用内商品相同的购买流程来购买订阅和检索订阅购买信息。有关代码示例,请参阅<a href="{@docRoot}google/play/billing/billing_integrate.html#Subs">实施订阅</a>。</p>
+  <p class="caution"><strong>重要提示</strong>:与应用内商品不同,订阅无法消耗。</p>
+
+  <h2 id="purchase">购买商品</h2>
+
+  <div class="figure" style="width:430px">
+  <img src="{@docRoot}images/in-app-billing/v3/iab_v3_purchase_flow.png" id="figure1" height="530" />
+  <p class="img-caption">
+    <strong>图 1. </strong> 购买请求的基本顺序。
+  </p>
+  </div>
+
+  <p>API 第 3 版中的典型购买流程如下所示:
+  <ol>
+  <li>您的应用向 Google Play 发送 <code>isBillingSupported</code> 请求,以确定您当前使用的应用内结算 API 目标版本是否受支持。</li>
+  <li>当您的应用启动或用户登录时,最好向 Google Play 进行查询,确定该用户拥有哪些商品。要查询用户的应用内购买,请发送 <code>getPurchases</code> 请求。如果该请求成功,Google Play 会返回一个 <code>Bundle</code>,其中包含所购商品的商品 ID 列表、各项购买详情的列表以及购买签名的列表。</li>
+  <li>通常情况下,您需要通知用户商品是否可供购买。要查询您在 Google Play 中定义的应用内商品的详细信息,应用可以发送 <code>getSkuDetails</code> 请求。您必须在该查询请求中指定商品 ID 列表。如果该请求成功,Google Play 会返回一个包含产品详情(包括商品的价格、标题、说明和购买类型)的 <code>Bundle</code>。
+  </li>
+  <li>如果该用户还未拥有应用内商品,您可以提示购买。为了发起购买请求,您的应用会发送 <code>getBuyIntent</code> 请求,指定要购买商品的商品 ID 以及其他参数。当您在开发者控制台中创建新的应用内商品时,应记录其商品 ID。
+    <ol type="a">
+    <li>Google Play 返回的 <code>Bundle</code> 中包含 <code>PendingIntent</code>,您的应用可用它来启动购买结帐界面。</li>
+    <li>您的应用通过调用 <code>startIntentSenderForResult</code> 方法来启动 PendingIntent。</li>
+    <li>当结帐流程结束后(即用户成功购买商品或取消购买),Google Play 会向您的 <code>onActivityResult</code> 方法发送响应 <code>Intent</code>。<code>onActivityResult</code> 的结果代码中有一个代码将用于表明用户是完成了购买还是取消了购买。响应 <code>Intent</code> 中会包含所购商品的相关信息,其中包括 Google Play 为了唯一标识此次购买交易而生成的 <code>purchaseToken</code> 字符串。<code>Intent</code> 中还包含使用您的私人开发者密钥签署的购买签名。</li> 
+    </ol>
+  </li>
+  </ol>
+  </p>
+  <p>要详细了解 API 第 3 版的调用和服务器响应,请参阅<a href="{@docRoot}google/play/billing/billing_reference.html">应用内结算参考</a>。</p>
+
+  <h2 id="consume">消耗应用内商品</h2>
+  <p>您可以使用消耗机制来跟踪用户对应用内商品的所有权。</p>
+  <p>在第 3 版中,所有应用内商品都是托管的。也就是说,用户对所购应用内商品的所有权由 Google Play 进行维护,您的应用可以在需要时查询用户的购买信息。当用户成功购买应用内商品后,该次购买就会记录在 Google Play 中。应用内商品一经售出,就会被视为“被拥有”。处于“被拥有”状态的应用内商品无法再通过 Google Play 购买。您必须对“被拥有”的应用内商品发送消耗请求,然后 Google Play 才能再次将其设成可购买状态。消耗应用内商品会将商品切换回“未被拥有”状态并删除之前的购买数据。</p>
+  <div class="figure" style="width:420px">
+  <img src="{@docRoot}images/in-app-billing/v3/iab_v3_consumption_flow.png" id="figure2" height="300" />
+  <p class="img-caption">
+    <strong>图 2. </strong> 消耗请求的基本顺序。
+  </p>
+  </div>
+  <p>为了检索用户所拥有商品的列表,您的应用会向 Google Play 发送 <code>getPurchases</code> 调用。您的应用可以通过发送 <code>consumePurchase</code> 调用提出消耗请求。在请求参数中,您必须指定应用内商品独一无二的 <code>purchaseToken</code> 字符串,此字符串是在商品售出时由 Google Play 指定的。Google Play 会返回一个状态代码,指明此次消耗是否已成功记录。</p>
+
+  <h3 id="consumetypes">非消耗型和消耗型应用内商品</h3>
+  <p>您需要决定您的应用内商品是非消耗型商品还是消耗型商品。</p>
+  <dl>
+  <dt>非消耗型商品</dt>
+  <dd>通常情况下,对于在应用内购买一次就能永久使用的应用内商品,您无需实施消耗。这些商品在购买后将永久与用户的 Google 帐户相关联。高级版升级和关卡包就属于非消耗型应用内商品。</dd>
+  <dt>消耗型商品</dt>
+  <dd>相反,对于可多次购买的商品,您可以实施消耗。通常情况下,这类商品提供某些临时效果。例如,用户在游戏中的角色可以从自己的存货中获得生命值或额外的金币。在您的应用中分配所购商品的收益或效果称之为“配置”应用内商品。<em></em>您负责控制和跟踪如何向用户配置应用内商品。
+  <p class="note"><strong>重要提示</strong>:您必须先向 Google Play 发送消耗请求,在收到表明此次消耗已成功记录的响应之后,您才能在应用中配置消耗型应用内商品。</p>
+  </dd>
+  </dl>
+  <h3 id="managingconsumables">在应用中管理消耗型商品购买</h3>
+  <p>下面是购买消耗型应用内商品的基本流程:</p>
+  <ol>
+  <li>调用 <code>getBuyIntent</code> 启动购买流程。</li>
+  <li>从 Google Play 接收指示购买是否成功完成的响应 <code>Bundle</code>。</li>
+  <li>如果购买成功,通过调用 <code>consumePurchase</code> 消耗此次购买。</li>
+  <li>从 Google Play 接收指示消耗是否成功完成的响应代码。</li>
+  <li>如果消耗成功,在应用中配置商品。</li>
+  </ol>
+  <p>之后,当用户启动或登录到您的应用时,您应检查该用户是否拥有任何尚未消耗的消耗型应用内商品。如果有,请务必消耗并配置这些商品。如果您在应用中实施消耗型应用内商品,则可以采用以下推荐的应用启动流程:</p>
+  <ol>
+  <li>发送 <code>getPurchases</code> 请求,查询该用户拥有的应用内商品。</li>
+  <li>如果有消耗型应用内商品,通过调用 <code>consumePurchase</code> 消耗这些商品。必须执行这步操作,因为应用虽可能已完成该消耗型商品的购买订单,但在其发送消耗请求之前仍有可能停止运行或断开连接。</li>
+  <li>从 Google Play 接收指示消耗是否成功完成的响应代码。</li>
+  <li>如果消耗成功,在应用中配置商品。</li>
+  </ol>
+
+  <h2 id="caching">本地缓存</h2>
+  <p>目前,由于 Google Play 客户端在设备上本地缓存应用内结算信息,因此您可以使用 API 第 3 版更频繁地查询此类信息,例如通过调用 <code>getPurchases</code> 进行查询。与旧版本的 API 不同,API 第 3 版的很多调用都是在缓存中进行查找,而不用通过网络连接到 Google Play,这样就显著加快了 API 的响应速度。</p>
diff --git a/docs/html-intl/intl/zh-cn/google/play/billing/billing_admin.jd b/docs/html-intl/intl/zh-cn/google/play/billing/billing_admin.jd
new file mode 100644
index 0000000..989c0e7
--- /dev/null
+++ b/docs/html-intl/intl/zh-cn/google/play/billing/billing_admin.jd
@@ -0,0 +1,364 @@
+page.title=管理应用内结算
+parent.title=In-app Billing
+parent.link=index.html
+@jd:body
+
+      <div id="qv-wrapper">
+  <div id="qv">
+    <h2>本文内容</h2>
+    <ol>
+      <li><a href="#billing-list-setup">创建商品列表</a></li>
+      <li><a href="#billing-purchase-type">选择商品类型</a></li>
+      <li><a href="#billing-testing-setup">设置测试帐户</a></li>
+      <li><a href="#billing-refunds">处理退款</a></li>
+      <li><a href="#billing-refunds">使用订单号</a></li>
+      <li><a href="#billing-support">获取相关支持</a></li>
+    </ol>
+
+    
+    <h2>另请参见</h2>
+    <ol>
+      <li><a href="{@docRoot}google/play/billing/billing_overview.html">应用内结算概述</a></li>
+    </ol>
+  </div>
+  </div>
+
+  <p>如果您使用应用内结算,您就无需处理任何财务交易。不过,您仍需执行一些管理任务,包括在 Google Play 开发者控制台上创建和维护您的商品列表、注册测试帐户以及在需要时处理退款。</p>
+
+  <p>您必须有 Google Play 发布商帐户才能注册测试帐户。而且,您必须有 Google 电子钱包商家帐户才能创建商品列表以及向用户发放退款。如果您已有 Google Play 发布商帐户,则可以使用您的现有帐户,无需注册新帐户就能使用应用内结算。</p>
+
+  <p>如果您没有发布商帐户,可以在 <a href="http://play.google.com/apps/publish">Google Play 开发者控制台</a>中以 Google Play 开发者的身份注册设置一个发布商帐户。如果您没有 Google 电子钱包商家帐户,也可以通过开发者控制台注册一个。</p>
+
+  <h2 id="billing-list-setup">创建商品列表</h2>
+
+  <p>Google Play 开发者控制台为您发布的每个应用都提供一份商品列表。您可以使用 Google Play 的应用内结算功能销售商品,但前提是该商品已在应用的商品列表中列出。每个应用都有自己的商品列表,您不能在某个应用中销售其他应用的商品列表中列出的商品。</p>
+
+  <p>您可以点击开发者帐户中列出的应用内的<strong>应用内商品</strong>链接,访问该应用的商品列表(参见图 1)。只有当您拥有 Google 电子钱包商家帐户且该应用的清单中包含 <code>com.android.vending.BILLING</code> 权限时,<strong>应用内商品</strong>链接才会显示。</p>
+
+  <p>商品列表列出了您在应用内销售的商品(应用内商品、订阅内容或两者的组合)。对于每件商品,商品列表中都会包含商品 ID、商品说明和价格等信息。商品列表只存储应用内所售商品的相关元数据,而不会存储任何数字内容。应用内销售的数字内容的存储和发送是由您负责的。</p>
+
+  <div style="margin:1em">
+  <img src="{@docRoot}images/in-app-billing/billing_product_list.png" id="figure1" style="border:1px solid #ddd;padding-bottom:.5em" xheight="548" />
+  <p class="img-caption" style="padding-left:.5em">
+    <strong>图 1. </strong> 您可以点击“应用”主导航面板中的<strong>应用内商品</strong>链接,访问应用的商品列表。
+  </p>
+  </div>
+
+  <p>您可以为所有已发布的应用或者 Alpha 或 Beta 渠道中的所有应用(这些应用上传并保存到开发者控制台)创建商品列表。不过,您必须有 Google 电子钱包商家帐户且该应用的清单中必须包含 <code>com.android.vending.BILLING</code> 权限。如果应用的清单中未包含此权限,您只能在商品列表中修改现有商品但不能向该列表添加新商品。要详细了解此权限,请参阅<a href="{@docRoot}google/play/billing/billing_integrate.html#billing-permission">更新您应用的清单</a>。</p>
+
+  <p class="note"><strong>注意</strong>:过去,您可以通过上传未发布的“草稿”版本来测试应用,但现在系统已不再支持此功能。因此,您必须将应用发布到 Alpha 或 Beta 分发渠道,才能进行测试。有关详情,请参阅<a href="{@docRoot}google/play/billing/billing_testing.html#draft_apps">草稿应用不再受支持</a>。
+
+  <p>此外,一个应用包只能有一个商品列表。如果您为某个应用创建商品列表且使用<a href="{@docRoot}google/play/publishing/multiple-apks.html">多 APK 功能</a>为该应用分发多个 APK,那么此商品列表将应用到与该应用商品详情相关联的所有 APK 版本。如果您使用的是多 APK 功能,则无法为各个 APK 创建单独的商品列表。</p>
+
+  <p>您可以通过两种方式向商品列表添加商品:通过应用内商品界面逐一添加商品(参见图 2);通过导入逗号分隔值 (CSV) 文件中的商品批量添加商品。如果您的应用只有几件应用内商品或您只是向商品列表添加几件测试商品,那么逐一添加商品这种方法就非常实用。如果您的应用有大量应用内商品,使用 CSV 文件的方法就较为实用。</p>
+
+  <p class="note"><strong>注意</strong>:目前系统尚不支持批量上传包含订阅内容的商品列表。</p>
+
+  <h3 id="billing-form-add">向商品列表逐一添加商品</h3>
+
+  <p>要通过应用内商品界面向商品列表添加一件商品,请按以下步骤操作:</p>
+
+  <ol>
+    <li><a href="http://play.google.com/apps/publish">登录</a>到您的发布商帐户。</li>
+    <li>在<strong>所有应用</strong>面板中,点击相应的应用名称,然后选择<strong>应用内商品</strong>。</li>
+    <li>点击<strong>添加新商品</strong>(参见图 2),提供所售商品的详细信息,然后点击<strong>保存</strong>或<strong>发布</strong>。</li>
+  </ol>
+
+  <div style="margin:1em">
+  <img src="{@docRoot}images/in-app-billing/billing_add.png" height="300" id="figure2" style="border:1px solid #ddd;padding-bottom:.5em" />
+  <p class="img-caption" style="padding-left:.5em">
+    <strong>图 2. </strong> 通过“添加新商品”页面,您可以向应用的商品列表添加商品。
+  </p>
+  </div>
+
+  <p>对于商品列表中的每件商品,您都必须输入以下信息:</p>
+  <ul>
+    <li><strong>应用内商品 ID</strong>
+      <p>每件商品的 ID 在应用的命名空间中都是独一无二的。商品 ID 必须以小写字母或数字开头,且只能由小写字母 (a-z)、数字 (0-9)、下划线 (_) 和点 (.) 组成。商品 ID“android.test”是系统预留的 ID,所有以“android.test”开头的商品 ID 都是如此。</p>
+      <p>此外,商品的商品 ID 创建后就无法再修改,而且您不能重复使用任何商品 ID。</p>
+    </li>
+    <li><strong>商品类型</strong>
+      <p>商品类型可以是<strong>按用户帐户管理的商品</strong>、<strong>不受管理的商品</strong>或<strong>订阅内容</strong>。商品类型设置后就无法再更改。有关详情,请参阅下文中的<a href="#billing-purchase-type">选择商品类型</a>。</p>
+    </li>
+    <li><strong>发布状态</strong>
+      <p>商品的发布状态可以是<strong>已发布</strong>或<strong>未发布</strong>。为了让用户在结帐时可以看到所购商品,该商品的发布状态必须设置为<strong>已发布</strong>且该商品所属的应用必须已发布到 Google Play 上。</p>
+      <p class="note"><strong>注意</strong>:这种情况并不适用于测试帐户。也就是说,对于测试帐户来说,即使应用未发布而商品已发布,测试帐户中的用户是可以看到该商品的。有关详情,请参阅<a href="{@docRoot}google/play/billing/billing_testing.html#billing-testing-real">测试应用内结算</a>。</p>
+    </li>
+    <li><strong>语言和翻译</strong>
+      <p>您可以使用“添加翻译”按钮,为您的应用内商品提供经过本地化的标题和说明。如果您希望 Google Play 根据您的默认语言为您翻译标题和说明,只需点击您要提供的语言即可。如果您想自行翻译成特定语言,也是可以的。默认情况下,应用内商品会沿用其所属应用的默认语言。</p>
+    </li>
+    <li><strong>标题</strong>
+      <p>标题是对商品的简短描述,例如“沉睡药水”。每个标题在应用的命名空间中必须是独一无二的,且每件商品都必须有标题。标题在用户结帐时必须显示。为了获得最佳的显示效果,标题不应超过 25 个字符;不过,标题最长可达 55 个字符。</p>
+    </li>
+    <li><strong>说明</strong>
+      <p>说明是对商品的详细描述,例如“立即让服用者陷入沉睡,但对愤怒的精灵无效”。每件商品都必须有一段说明,最长可达 80 个字符。</p>
+    </li>
+    <li><strong>价格</strong>
+      <p>您必须提供一个以您本地货币为单位的默认价格。您也可以提供以其他货币为单位的价格,但前提是此货币所属的国家/地区是您的应用要定位的目标国家/地区。您可以在 Google Play 开发者控制台中的“修改应用”页面上指定目标国家/地区。</p>
+      <p>要指定以其他货币为单位的价格,您可以手动输入与每种货币对应的价格,也可以点击<strong>自动填充</strong>,让 Google Play 一次性将您的本地货币换算成目标货币(参见图 3)。</p>
+      <p>请注意,订阅型商品发布后就无法再更改其价格。</p>
+    </li>
+  </ul>
+
+  <div style="margin:1em">
+  <img src="{@docRoot}images/in-app-billing/billing_list_form_2.png" id="figure3" style="border:1px solid #ddd;padding-bottom:.5em" xheight="1226" />
+  <p class="img-caption" style="padding-left:.5em">
+    <strong>图 3. </strong> 为应用内商品指定其他货币。
+  </p>
+  </div>
+
+  <p>要详细了解商品 ID 和商品列表,请参阅<a href="http://support.google.com/googleplay/android-developer/bin/answer.py?hl=zh-CN&amp;answer=1072599">创建应用内商品 ID</a>。要详细了解定价,请参阅<a href="http://support.google.com/googleplay/android-developer/bin/answer.py?hl=zh-CN&amp;answer=1153485">应用内结算定价</a>。</p>
+
+  <p class="note"><strong>注意</strong>:请务必规划好您的商品 ID 命名空间。商品 ID 保存后就无法再重复使用或更改。</p>
+
+  <h3 id="billing-bulk-add">向商品列表批量添加商品</h3>
+
+  <p>要使用 CSV 文件向商品列表批量添加商品,您需要先创建 CSV 文件。您在 CSV 文件中指定的数据值与您通过应用内商品界面手动指定的数据值相同(请参阅<a href="#billing-form-add">向商品列表逐一添加商品</a>)。
+
+  <p>如果您通过 CSV 文件导入和导出应用内商品,请记得提供含税价格。如果您使用自动填充,请提供不含税的默认价格,系统会自动填充含税的价格。如果您不使用自动填充,您提供的价格必须是含税价格。</p>
+
+  <p class="note"><strong>注意</strong>:目前系统尚不支持批量上传包含订阅内容的商品列表。</p>
+
+  CSV 文件使用逗号 (,) 和分号 (;) 来分隔数据值。逗号用来分隔主要数据值,分号用来分隔次要数据值。例如,CSV 文件的语法如下所示:</p>
+
+  <p>"<em>product_id</em>","<em>publish_state</em>","<em>purchase_type</em>","<em>autotranslate</em>
+  ","<em>locale</em>; <em>title</em>; <em>description</em>","<em>autofill</em>","<em>country</em>;
+  <em>price</em>"
+  </p>
+
+  <p>相关说明和使用详情如下所示。</p>
+
+  <ul>
+    <li><em>product_id</em>
+      <p>等同于应用内商品界面中的“应用内商品 ID”设置。如果您指定的“product_id”在商品列表中已经存在,且您选择在导入 CSV 文件时覆盖商品列表,则 CSV 文件中指定的值就会覆盖现有商品的数据。<em></em>覆盖功能不会删除商品列表中已列出但不包含在 CSV 文件中的商品。</p>
+    </li>
+    <li><em>publish_state</em>
+      <p>等同于应用内商品界面中的“发布状态”设置。值可以是 <code>published</code> 或 <code>unpublished</code>。</p>
+    </li>
+    <li><em>purchase_type</em>
+      <p>等同于应用内商品界面中的“商品类型”设置。值可以是 <code>managed_by_android</code>(等同于应用内商品界面中的<strong>按用户帐户管理的商品</strong>)或 <code>managed_by_publisher</code>(等同于应用内商品界面中的<strong>不受管理的商品</strong>)。</p>
+    </li>
+    <li><em>autotranslate</em>
+      <p>等同于选中应用内商品界面中的<strong>使用自动翻译填充字段</strong>复选框。值可以是 <code>true</code> 或 <code>false</code>。</p>
+    </li>
+    <li><em>locale</em>
+      <p>等同于应用内商品界面中的“语言”设置。您必须有一个默认语言区域项。默认语言区域必须是语言区域列表中的第一项,且必须包含“title”和“description”。<em></em><em></em>如果除默认语言外您还想提供翻译版本的“title”和“description”,则必须使用下面的语法规则:<em></em><em></em></p>
+      <p>如果“autotranslate”为 <code>true</code>,您必须按照以下格式指定默认语言区域、默认标题、默认说明及其他语言区域:<em></em></p>
+      <p>"true,"<em>default_locale</em>; <em>default_locale_title</em>;
+      <em>default_locale_description</em>; <em>locale_2</em>;    <em>locale_3</em>, ..."</p>
+      <p>如果“autotranslate”为 <code>false</code>,您必须按照以下格式指定默认语言区域、默认标题、默认说明以及翻译后的标题和说明:<em></em></p>
+      <p>"false,"<em>default_locale</em>; <em>default_locale_title</em>;
+      <em>default_locale_description</em>; <em>locale_2</em>; <em>locale_2_title</em>;
+      <em>local_2_description</em>; <em>locale_3</em>; <em>locale_3_title</em>;
+       <em>locale_3_description</em>; ..."</p>
+      <p>请参见表 1,了解您可以在<em></em>“locale”字段中使用的语言代码的列表。</p>
+    </li>
+    <li><em>title</em>
+      <p>等同于应用内商品界面中的“标题”设置。如果“title”包含分号,必须使用反斜杠进行转义(例如“\;”)。<em></em>如果标题本身包含反斜杠,也应使用反斜杠进行转义(例如“\\”)。</p>
+    </li>
+    <li><em>description</em>
+      <p>等同于应用内商品界面中的“说明”。如果“description”包含分号,则必须使用反斜杠进行转义(例如“\;”)。<em></em>如果说明本身包含反斜杠,也应使用反斜杠进行转义(例如“\\”)。</p>
+    </li>
+    <li><em>autofill</em>
+      <p>等同于点击应用内商品界面中的<strong>自动填充</strong>。值可以是 <code>true</code> 或 <code>false</code>。用于指定“country”和“price”的语法视您使用的“autofill”设置而定。<em></em><em></em><em></em></p>
+      <p>如果“autofill”设为 <code>true</code>,您只需指定采用本地货币的默认价格且必须使用以下语法:<em></em></p>
+      <p>"true","<em>default_price_in_home_currency</em>"
+      <p><em></em>如果“autofill”设为 <code>false</code>,您需要为每种货币指定“country”和“price”且必须使用以下语法:<em></em><em></em></p>
+      <p>"false", "<em>home_country</em>; <em>default_price_in_home_currency</em>; <em>country_2</em>;
+      <em>country_2_price</em>; <em>country_3</em>; <em>country_3_price</em>; ..."</p>
+    </p></li>
+    <li><em>country</em>
+      <p>您为其指定价格的国家/地区。您只能列出您的应用所定位的国家/地区。国家/地区代码是按照 <a href="http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">ISO 3166-2</a> 的定义,由两个大写字母组成的 ISO 国家/地区代码(例如“US”)。</p>
+    </li>
+    <li><em>price</em>
+      <p>等同于应用内商品界面中的“价格”。此价格必须使用微单位指定。要将货币值换算成微单位,请用实际值乘以 1,000,000。例如,如果您要以 1.99 元销售某件应用内商品,则需要在“price”字段中输入 1990000。<em></em></p>
+    </li>
+  </ul>
+
+  <p class="table-caption" id="language-table"><strong>表 1. </strong> 您可以在“locale”字段中使用的语言代码。<em></em></p>
+
+  <table>
+
+  <tr>
+  <th>语言</th>
+  <th>代码</th>
+  <th>语言</th>
+  <th>代码</th>
+  </tr>
+  <tr>
+  <td>中文</td>
+  <td>zh_TW</td>
+  <td>意大利语</td>
+  <td>it_IT</td>
+  </tr>
+  <tr>
+  <td>捷克语</td>
+  <td>cs_CZ</td>
+  <td>日语</td>
+  <td>ja_JP</td>
+  </tr>
+  <tr>
+  <td>丹麦语</td>
+  <td>da_DK</td>
+  <td>韩语</td>
+  <td>ko_KR</td>
+  </tr>
+  <tr>
+  <td>荷兰语</td>
+  <td>nl_NL</td>
+  <td>挪威语</td>
+  <td>no_NO</td>
+  </tr>
+  <tr>
+  <td>英语</td>
+  <td>en_US</td>
+  <td>波兰语</td>
+  <td>pl_PL</td>
+  </tr>
+  <tr>
+  <td>法语</td>
+  <td>fr_FR</td>
+  <td>葡萄牙语</td>
+  <td>pt_PT</td>
+  </tr>
+  <tr>
+  <td>芬兰语</td>
+  <td>fi_FI</td>
+  <td>俄语</td>
+  <td>ru_RU</td>
+  </tr>
+  <tr>
+  <td>德语</td>
+  <td>de_DE</td>
+  <td>西班牙语</td>
+  <td>es_ES</td>
+  </tr>
+  <tr>
+  <td>希伯来语</td>
+  <td>iw_IL</td>
+  <td>瑞典语</td>
+  <td>sv_SE</td>
+  </tr>
+  <tr>
+  <td>印地语</td>
+  <td>hi_IN</td>
+  <td>--</td>
+  <td>--</td>
+  </tr>
+  </table>
+
+  <p>要导入您在 CSV 文件中指定的商品,请执行以下操作:</p>
+
+  <ol>
+    <li><a href="http://play.google.com/apps/publish">登录</a>到您的发布商帐户。</li>
+    <li>在<strong>所有应用</strong>面板中,点击相应的应用名称,然后选择<strong>应用内商品</strong>。</li>
+    <li>在“应用内商品列表”页面上,点击<strong>导入/导出</strong> &gt; <strong>从 CSV 文件中导入应用内商品</strong>,然后选择 CSV 文件。
+      <p>CSV 文件必须位于您的本地计算机上或连接到您计算机的本地磁盘上。</p>
+    </li>
+    <li>如果您想覆盖商品列表中的现有商品,请选中<strong>覆盖</strong>复选框。
+      <p>只有当 CSV 文件中的“product_id”值与商品列表中现有商品的应用内商品 ID 一致时,此选项才会覆盖现有商品的值。<em></em>覆盖功能不会删除商品列表中已列出但不包含在 CSV 文件中的商品。</p>
+    </li>
+  </ol>
+
+  <p>您还可以点击“应用内商品列表”页面上的<strong>导出为 CSV</strong>,将现有商品列表导出为 CSV 文件。如果您已将商品手动添加到商品列表,但现在想开始通过 CSV 文件管理该商品列表,此功能就非常有用。</p>
+
+  <h3 id="billing-purchase-type">选择商品类型</h3>
+
+  <p>商品的商品类型用来控制 Google Play 如何管理该商品的购买流程。商品类型有以下三种:“按用户帐户管理的商品”、“不受管理的商品”和“订阅内容”。但请注意,不同版本的应用内结算服务支持的商品类型不同,因此务必根据您的应用所用的应用内结算版本,选择合适的商品类型。</p>
+
+  <p>有关详情,请参阅<a href="{@docRoot}google/play/billing/api.html#producttype">应用内结算第 3 版</a>或<a href="{@docRoot}google/play/billing/v2/api.html#producttype">应用内结算第 2 版</a>的相关文档。
+
+  <h2 id="billing-refunds">处理退款</h2>
+
+  <p>应用内结算不允许用户向 Google Play 发送退款申请。应用内购买的退款事宜必须直接发送给您(应用开发者)。然后,您可以通过您的 Google 电子钱包商家帐户处理这笔退款。当您处理退款时,Google Play 会收到 Google 电子钱包发来的退款通知,并且 Google Play 会向您的应用发送退款消息。有关详情,请参阅<a href="{@docRoot}google/play/billing/v2/api.html#billing-action-notify">处理 IN_APP_NOTIFY 消息</a>和<a href="http://support.google.com/googleplay/android-developer/bin/answer.py?hl=zh-CN&amp;answer=1153485">应用内结算定价</a>。</p>
+
+  <p class="caution"><strong>重要提示</strong>:您不能使用 Google Wallet API 发放退款或取消应用内结算交易,而是必须通过您的 Google 电子钱包商家帐户手动执行此类操作。不过,您可以使用 Google Wallet API 检索订单信息。</p>
+
+  <h2 id="orderId">使用订单号</h2>
+
+  <p>当用户购买应用内商品时,Google 电子钱包会为此次交易分配一个永久有效且独一无二的订单号。Google Play 在购买流程结束时会向您提供该订单号,作为 <code>PURCHASE_STATE_CHANGED</code> Intent 中的 <code>orderId</code> 字段的值。</p>
+
+  <p>在您的应用中,您可以使用该订单号作为此次应用内购买交易的通用标识符。购买后,您可以利用该订单号在对帐报表中跟踪此次交易,以及在提供客户支持时使用该订单号。</p>
+
+  <p>订单号是完全由数字构成的字符串,格式由 Google 电子钱包指定和管理。</p>
+
+  <p>对于发生在 2012 年 12 月 5 日及以后的交易,Google 电子钱包会分配一个商家订单号(而非 Google 订单号)并将该商家订单号报告为 <code>orderID</code> 的值。示例如下:</p>
+
+  <pre>"orderId" : "12999556515565155651.5565135565155651"</pre>
+
+  <p>对于发生在 2012 年 12 月 5 日之前的交易,Google Checkout 分配的是 Google 订单号并已将该编号报告为 <code>orderID</code> 的值。下面是使用 Google 订单号的 <code>orderID</code> 的示例:</p>
+
+  <pre>"orderId" : "556515565155651"</pre>
+
+  <h2 id="billing-testing-setup">设置测试帐户</h2>
+
+  <p>通过 Google Play 开发者控制台,您可以设置一个或多个测试帐户。测试帐户是您在开发者控制台上注册为测试帐户的常规 Google 帐户。测试帐户已获得授权,可从您已上传到 Google Play 开发者控制台但尚未发布的应用中进行应用内购买。</p>
+
+  <p>您可以使用任意 Google 帐户作为测试帐户。如果您想让多位用户在应用上测试应用内结算但又不想让他们获得您的发布商帐户的登录凭据,测试帐户就非常有用。如果您想拥有并控制测试帐户,可以自行创建帐户,然后将凭据分发给您的开发者或测试者。</p>
+
+  <p>测试帐户有三条限制:</p>
+
+  <ul>
+    <li>测试帐户用户只能在已上传到您的发布商帐户的应用(该应用可以是尚未发布的)中提出购买请求。</li>
+    <li>测试帐户只能用来购买应用的商品列表中已列出(并已发布)的商品。</li>
+    <li>测试帐户用户无法访问您的发布商帐户,也不能向您的发布商帐户上传应用。</li>
+  </ul>
+
+  <p>要向您的发布商帐户添加测试帐户,请按以下步骤操作:</p>
+
+  <ol>
+    <li><a href="http://play.google.com/apps/publish">登录</a>到您的发布商帐户。</li>
+    <li>点击<strong>设置</strong>图标。</li>
+    <li>找到“许可测试”面板。</li>
+    <li>添加您要注册为测试帐户的电子邮件地址,各帐户之间用英文逗号分隔。</li>
+    <li>点击<strong>保存</strong>,保存您的个人资料更改。</li>
+  </ol>
+
+  <h3 id="license_key">获取应用的许可密钥</h3>
+
+  <p>Google Play 开发者控制台为每个应用都提供一个公用的许可密钥。要获取应用的密钥,请按以下步骤操作:</p>
+  <ol>
+    <li>打开<strong>所有应用</strong>面板。</li>
+    <li>点击相应的应用名称,然后选择<strong>服务和 API</strong>。</li>
+    <li>向下滚动到<strong>此应用的许可密钥</strong>字段,找到应用的密钥,如下图所示。</li>
+  </ol>
+  <p>过去,开发者控制台为每个开发者帐户都提供一个公钥。为了将应用迁移到按应用分配的新公钥,开发者控制台将应用专用密钥设置为之前的开发者密钥。这样做可确保依赖于(之前的)开发者密钥的应用能与新公钥兼容。</p>
+
+  <div style="margin:1em">
+  <img src="{@docRoot}images/in-app-billing/billing_app_key.png" id="figure4" style="border:1px solid #ddd;padding-bottom:.5em" xheight="510" />
+  <p class="img-caption" style="padding-left:.5em">
+    <strong>图 4. </strong> 您可以在<strong>服务和 API</strong> 面板中找到各个应用的许可密钥。
+  </p>
+  </div>
+
+  <h2 id="billing-support">获取相关支持</h2>
+
+  <p>如果您在实施应用内结算时有疑问或遇到问题,请与下表列出的支持资源联系(参见表 2)。将您的问题发布到正确的论坛上,可以更快获得所需支持。</p>
+
+  <p class="table-caption" id="support-table"><strong>表 2. </strong> 适用于 Google Play 应用内结算的开发者支持资源</p>
+
+  <table>
+
+  <tr>
+  <th>支持类型</th>
+  <th>资源</th>
+  <th>主题范围</th>
+  </tr>
+  <tr>
+  <td rowspan="2">开发与测试问题</td>
+  <td>Google 网上论坛:<a href="http://groups.google.com/group/android-developers">android-developers</a> </td>
+  <td rowspan="2">应用内结算集成问题、有关用户体验的提示、对响应的处理、混淆代码、进程间通信 (IPC)、测试环境设置。</td>
+  </tr>
+  <tr>
+  <td>堆栈溢出:<a href="http://stackoverflow.com/questions/tagged/android">http://stackoverflow.com/questions/tagged/
+  android</a></td>
+  </tr>
+  <tr>
+  <td>结算问题跟踪器</td>
+  <td><a href="http://code.google.com/p/marketbilling/issues/">结算项目问题跟踪器</a></td>
+  <td>与应用内结算示例代码直接相关的错误和问题报告。</td>
+  </tr>
+  </table>
+
+  <p>要了解如何向上述所列论坛发帖的一般信息,请参阅“资源”标签中的<a href="{@docRoot}resources/community-groups.html">开发者论坛</a>一文。</p>
+
diff --git a/docs/html-intl/intl/zh-cn/google/play/billing/billing_best_practices.jd b/docs/html-intl/intl/zh-cn/google/play/billing/billing_best_practices.jd
new file mode 100644
index 0000000..4aff17d
--- /dev/null
+++ b/docs/html-intl/intl/zh-cn/google/play/billing/billing_best_practices.jd
@@ -0,0 +1,81 @@
+page.title=安全性和设计
+parent.title=In-app Billing
+parent.link=index.html
+@jd:body
+
+      <div id="qv-wrapper">
+  <div id="qv">
+    <h2>本文内容</h2>
+    <ol>
+      <li><a href="#billing-security">安全性方面的最佳做法</a>
+         <ol>
+         <li><a href="#unlocked">保护未锁定的内容</a></li>
+         <li><a href="#obfuscate">混淆你的代码</a></li>
+         <li><a href="#sample">修改示例代码</a></li>
+         <li><a href="#nonce">使用安全随机数</a></li>
+         <li><a href="#payload">设置开发者有效内容字符串</a></li>
+         <li><a href="#trademark">报告商标和版权侵权</a></li>
+         <li><a href="#revocable">实施可撤消的方案</a></li>
+         <li><a href="#key">保护你的公钥</a></li>
+         </ol>
+      </li>
+    </ol>
+    <h2>另请参见</h2>
+    <ol>
+      <li><a href="{@docRoot}google/play/billing/billing_overview.html">应用内结算概述</a></li>
+    </ol>
+  </div>
+  </div>
+
+  <p>在设计应用内结算的实施时,请务必遵循本文中介绍的安全性和设计指南。这些指南是推荐的最佳做法,适用于所有使用 Google Play 应用内结算服务的开发者。</p>
+
+  <h2>安全性方面的最佳做法</h2>
+
+  <h3 id="sign">在服务器上执行签名验证任务</h3>
+  <p>如果可能的话,你应该在远程服务器上(而不是设备上)执行签名验证。在服务器上实施验证过程,可有效防止攻击者通过对 .apk 文件进行反向工程来破坏验证过程。如果你将安全性流程放置到远程服务器上,请确保设备服务器的握手过程是安全的。</p>
+
+  <h3 id="unlocked">保护你的未锁定内容</h3>
+  <p>为防止恶意用户重新分发你未锁定的内容,请不要将这种内容放入你的 .apk 文件中,而是执行以下操作之一:</p>
+    <ul>
+      <li>使用实时服务发送内容,例如内容 Feed。通过实时服务发送内容可保持内容始终是最新的。</li>
+      <li>使用远程服务器发送内容。</li>
+    </ul>
+  <p>当你通过远程服务器或实时服务发送内容时,你可以将未锁定内容存储在设备内存中或设备的 SD 卡上。如果你将内容存储在 SD 卡上,请务必将内容加密并使用设备专有的密钥。</p>
+
+  <h3 id="obfuscate">混淆你的代码</h3>
+  <p>你应混淆应用内结算代码,让攻击者难以对你的安全协议和其他应用组件进行反向工程。我们建议你至少对代码运行 <a href="{@docRoot}tools/help/proguard.html">Proguard</a> 等代码混淆工具。</p>
+  <p>除了运行代码混淆程序以外,我们还建议你使用以下技术混淆你的应用内结算代码。</p>
+  <ul>
+    <li>将方法内嵌入其他方法中。</li>
+    <li>创建动态字符串,而不是将其定义为常量。</li>
+    <li>使用 Java 反射来调用方法。</li>
+  </ul>
+  <p>使用这些技术有助于缩小你应用的受攻击范围,并最大程度帮助你抵御会对应用内结算实施造成损害的攻击。</p>
+  <div class="note">
+    <p><strong>注意</strong>:如果你使用 Proguard 来对代码进行混淆处理,则必须将以下内容行添加到 Proguard 配置文件中:</p>
+    <p><code>-keep class com.android.vending.billing.**</code></p>
+  </div>
+
+  <h3 id="sample">修改所有示例应用代码</h3>
+  <p>应用内结算示例是可供任何人下载的公开发布应用。这意味着如果你直接使用发布的示例代码,则对攻击者而言比较容易对你的应用进行反向工程。请紧记示例应用只能作为示例使用。如果你使用示例应用中的任何一部分,则必须在发布之前或将其作为生产应用的一部分前对其进行修改。</p>
+  <p>尤其要注意的是,攻击者会寻找应用内已知的进入点和离开点,因此请务必修改与示例应用完全相同的代码部分。</p>
+
+  <h3 id="nonce">使用安全随机数</h3>
+  <p>随机数必须是不可预测的,也不能重复使用。在生成随机数时,请务必使用采用加密保护的随机数字生成器(例如 <code><a href="{@docRoot}reference/java/security/SecureRandom.html">SecureRandom</a></code>),这样可以减少重放攻击。</p>
+  <p>此外,如果你在服务器上执行随机数验证,请务必在服务器上生成随机数。</p>
+
+  <h3 id="payload">在提交购买请求时设置开发者有效内容字符串</h3>
+  <p>使用应用内结算 API 第 3 版,你可以在向 Google Play 发送购买请求时包括一个“开发者有效内容”字符串令牌。通常情况下,这样做是为了提交可唯一标识此购买请求的字符串令牌。如果你指定字符串值,那么 Google Play 会返回购买响应以及该字符串。随后,在你针对此购买进行查询时,Google Play 会返回此字符串以及购买详情。</p>
+  <p>提交字符串令牌可以帮助应用识别进行了购买的用户,以便你稍后验证该用户进行的购买是否合法。对于消耗型商品,你可以使用随机生成的字符串;但是对于非消耗型商品,则应该使用可唯一标识用户的字符串。</p>
+  <p>当你获得 Google Play 的响应时,请确保开发者有效内容字符串匹配你之前与购买请求一起发送的令牌。作为进一步的安全预防措施,你应该在自己的安全服务器上执行验证。</p>
+
+
+  <h3 id="trademark">针对商标和版权侵权采取行动</h3>
+  <p>如果你看到自己的内容在 Google Play 上被重新分发,请立即、果断地采取行动。发出<a href="http://support.google.com/googleplay/android-developer/bin/answer.py?hl=zh-CN&amp;answer=141511">商标侵权通知</a>或<a href="http://www.google.com/android_dmca.html">版权侵权通知</a>。</p>
+
+  <h3 id="revocable">针对未锁定内容实施可撤消方案</h3>
+  <p>如果你使用远程服务器发送或管理内容,请确保当用户访问内容时,应用能够验证未锁定内容的购买状态。这样你可以根据需要撤消使用,并最大限度地减少盗版。</p>
+
+  <h3 id="key">保护你的 Google Play 公钥</h3>
+  <p>为保护你的公钥安全并防止被恶意用户和黑客攻击,请不要将其作为文字字符串嵌入任何代码中。而是在运行时以拼凑方式构建字符串或者使用位操作处理(例如,XOR 搭配其他字符串)以隐藏真实密钥。公钥本身不是秘密信息,但是你一定不希望黑客或恶意用户能轻易将公钥替换为其他钥匙。</p>
+
diff --git a/docs/html-intl/intl/zh-cn/google/play/billing/billing_overview.jd b/docs/html-intl/intl/zh-cn/google/play/billing/billing_overview.jd
new file mode 100644
index 0000000..aceadcb
--- /dev/null
+++ b/docs/html-intl/intl/zh-cn/google/play/billing/billing_overview.jd
@@ -0,0 +1,93 @@
+page.title=应用内结算概述
+parent.title=In-app Billing
+parent.link=index.html
+@jd:body
+
+      <div id="qv-wrapper">
+  <div id="qv">
+    <h2>概述</h2>
+    <ul>
+      <li>使用应用内结算销售数字商品,包括一次性商品和定期订阅。</li>
+      <li>在 Google Play 上发布的所有应用均可使用该服务。您只需有 Google Play 开发者控制台帐户和 Google 电子钱包商家帐户即可。</li>
+      <li>结帐流程由 Google Play 自动处理,其界面与应用购买流程的界面相同。</li>
+    </ul>
+    <h2>本文内容</h2>
+    <ol>
+      <li><a href="#api">应用内结算 API</a></li>
+      <li><a href="#products">应用内商品</a>
+         <ol>
+         <li><a href="#prodtypes">商品类型</a>
+         </li></ol>
+      </li>
+      <li><a href="#console">Google Play 开发者控制台</a></li>
+      <li><a href="#checkout">Google Play 购买流程</a></li>
+      <li><a href="#samples">示例应用</a></li>
+      <li><a href="#migration">迁移注意事项</a></li>
+    </ol>
+     <h2>相关示例</h2>
+    <ol>
+      <li><a href="{@docRoot}training/in-app-billing/preparing-iab-app.html#GetSample">示例应用(第 3 版)</a></li>
+    </ol>
+  </div>
+  </div>
+
+  <p>本文将介绍您需了解的应用内结算基本组件和功能,以便您将应用内结算功能添加到应用中。</p>
+
+  <p class="note"><b>注意</b>:请务必遵守目标国家/地区(您要在这些国家/地区分发应用)的适用法律。例如,在欧盟国家,<a href="http://ec.europa.eu/justice/consumer-marketing/unfair-trade/unfair-practices/">不公平商业行为指令</a>的相关法律禁止直接规劝儿童购买广告产品或者劝说其父母或其他成年人为儿童购买广告产品。请参阅<a href="http://ec.europa.eu/consumers/enforcement/docs/common_position_on_online_games_en.pdf">欧盟消费者保护机构的立场</a>,详细了解此主题及其他主题。
+  </p>
+
+  <h2 id="api">应用内结算 API</h2>
+  <p>您的应用会通过相应 API (由设备上安装的 Google Play 应用提供)来访问应用内结算服务。然后,Google Play 应用会在您的应用和 Google Play 服务器之间传送结算请求和响应。实际上,您的应用从不直接与 Google Play 服务器通信,而是通过进程间通信 (IPC) 向 Google Play 应用发送结算请求并接收 Google Play 应用发来的响应。此外,您的应用也不管理其与 Google Play 服务器之间的任何网络连接。</p>
+  <p>应用内结算只能在您通过 Google Play 发布的应用中实施。要完成应用内购买请求,Google Play 应用必须能够通过网络访问 Google Play 服务器。</p>
+
+  <p>应用内结算第 3 版是最新版本,依然具有非常广泛的兼容性,可用于各种 Android 设备。运行 Android 2.2 或更高版本且安装了最新版本的 Google Play 商店的设备(<a href="{@docRoot}about/dashboards/index.html">绝大多数</a>有效设备)都支持使用应用内结算第 3 版。</p>
+
+  <h4>第 3 版的功能</h4>
+  <ul>
+  <li>通过简化的 API 发送请求。此 API 可让您轻松地向 Google Play 请求商品详情、订购应用内商品以及根据用户对商品的所有权快速恢复商品</li>
+  <li>订单信息会在购买完成时同步传送到设备上</li>
+  <li>所有购买都是“托管的”(也就是说,Google Play 负责记录用户对应用内商品的所有权)。对于同一件应用内商品,用户无论何时都只能拥有该商品的一个副本而不能拥有多个副本</li>
+  <li>您可以消耗所购商品。当消耗后,该商品将切换回“未被拥有”状态,且可被用户从 Google Play 中再次购买</li>
+  <li>支持<a href="{@docRoot}google/play/billing/billing_subscriptions.html">订阅</a></li>
+  </ul>
+  <p>要详细了解应用内结算的其他版本,请参阅<a href="{@docRoot}google/play/billing/versions.html">版本说明</a>。</p>
+
+  <h2 id="products">应用内商品</h2>
+  <p>应用内商品是您在应用内向用户出售的数字商品,包括游戏代币、旨在改善用户体验的应用功能升级以及应用中的新增内容。</p>
+  <p>应用内结算只能用于销售数字内容,而不能用于销售实体商品、个人服务或任何需要进行实物交付的商品/服务。与明码标价的应用不同,一旦用户购买了应用内商品,系统便不会提供退款窗口。</p>
+  <p>Google Play 不提供任何形式的内容交付,您需要自行交付在应用内销售的数字内容。应用内商品始终与一个且仅与一个应用明确关联。也就是说,在一个应用中不能购买为另一个应用发布的应用内商品,即使这两个应用来自相同的开发者也是如此。</p>
+
+  <h3 id="prodtypes">商品类型</h3>
+  <p>应用内结算支持多种商品类型,因此您可灵活选择如何通过您的应用获利。无论是哪种商品,您均能通过 Google Play 开发者控制台进行定义。</p>
+  <p>您可以为应用内结算应用指定以下两类商品:“托管的应用内商品”和“订阅”。<em></em><em></em>Google Play 会按用户帐户来处理和跟踪您应用中的应用内商品和订阅的所有权。<a href="{@docRoot}google/play/billing/api.html#producttypes">详细了解应用内结算第 3 版支持的商品类型</a>。</p>
+
+  <h2 id="console">Google Play 开发者控制台</h2>
+  <p>在开发者控制台上,您不仅能发布应用内结算应用,还能管理您应用中出售的各种应用内商品。</p>
+  <p>对于与您的应用相关联的数字商品(包括一次性商品和定期订阅),您可以为其创建商品列表。对于每件商品,您都可以定义下列信息:唯一商品 ID(也称为 SKU)、商品类型、定价、说明以及 Google Play 应如何处理和用户对该商品的购买。</p>
+  <p>您还可以创建测试帐户,授权这些帐户测试尚未发布的应用。</p>
+  <p>要了解如何使用开发者控制台配置您的应用内商品和商品列表,请参阅<a href="{@docRoot}google/play/billing/billing_admin.html">管理应用内结算</a>。</p>
+
+  <h2 id="checkout">Google Play 购买流程</h2>
+  <p>Google Play 使用的结帐后端服务与用于应用购买的服务相同,因此您的用户将获得一致且熟悉的购买流程使用体验。</p>
+  <p class="note"><strong>重要提示</strong>:您必须有 Google 电子钱包商家帐户才能在 Google Play 上使用应用内结算服务。</p>
+  <p>购买开始时,您的应用需要针对相应的应用内商品发送结算请求。然后,Google Play 会处理此次交易的所有结帐详情,包括请求和验证付款方式以及处理财务交易。</p>
+  <p>当结帐流程完成后,Google Play 会向您的应用发送购买详情,例如订单号、订单日期和时间以及所付价格。您的应用从不需要处理任何财务交易,这些事宜完全 Google Play 负责。</p>
+
+  <h2 id="samples">示例应用</h2>
+  <p>为了帮您将应用内结算集成到您的应用中,Android SDK 提供了一个示例应用,向您演示如何在应用内销售应用内商品和订阅。</p>
+
+  <p><a href="{@docRoot}training/in-app-billing/preparing-iab-app.html#GetSample">API 第 3 版的 TrivialDrive 示例</a>演示了如何使用应用内结算 API 第 3 版在赛车游戏中实施应用内商品和订阅购买。该应用不仅演示了如何发送应用内结算请求以及处理来自 Google Play 的同步响应,还演示了如何通过此 API 记录商品的消耗情况。第 3 版示例包括多种工具类,非常便于处理应用内结算操作和执行自动签名验证。</p>
+
+  <p class="caution"><strong>建议</strong>:请务必在发布应用前混淆其中的代码。有关详情,请参阅<a href="{@docRoot}google/play/billing/billing_best_practices.html">安全性和设计</a>。</p>
+
+  <h2 id="migration">迁移注意事项</h2>
+  <p>应用内结算 API 第 2 版已弃用,将于 2015 年 1 月关闭。如果您现有的应用内结算实施使用的是 API 第 2 版或更低版本,则您必须迁移到<a href="{@docRoot}google/play/billing/api.html">应用内结算第 3 版</a>。</p>
+
+  <p>如果您在发布的应用中销售应用内商品,请注意以下事项:</p>
+  <ul>
+  <li>您之前在开发者控制台中定义的托管商品和订阅仍能使用第 3 版。</li>
+  <li>对于您已经为现有应用定义的非托管商品,如果您使用 API 第 3 版针对这些商品提出购买请求,这些商品将被视为托管商品。您无需在开发者控制台中为这些商品创建新的商品条目,且能使用相同的商品 ID 来购买这些商品。如果您使用 API 第 2 版或更低版本提出购买请求,这些商品仍会被视为非托管商品。
+  </li></ul>
+
+
+
diff --git a/docs/html-intl/intl/zh-cn/google/play/billing/billing_testing.jd b/docs/html-intl/intl/zh-cn/google/play/billing/billing_testing.jd
new file mode 100644
index 0000000..2dcc7a4
--- /dev/null
+++ b/docs/html-intl/intl/zh-cn/google/play/billing/billing_testing.jd
@@ -0,0 +1,190 @@
+page.title=测试应用内结算
+parent.title=In-app Billing
+parent.link=index.html
+@jd:body
+
+      <div id="qv-wrapper">
+  <div id="qv">
+    <h2>本文内容</h2>
+    <ol>
+      <li><a href="#testing-purchases">测试应用内购买</a></li>
+      <li><a href="#billing-testing-static">使用静态回复进行测试</a></li>
+      <li><a href="#billing-testing-real">设置测试购买</a></li>
+      <li><a href="#draft_apps">草稿应用不再受支持</a></li>
+    </ol>
+    <h2>另请参见</h2>
+    <ol>
+      <li><a href="{@docRoot}google/play/billing/billing_overview.html">应用内结算概述</a></li>
+    <ol>
+  </ol></ol></div>
+  </div>
+
+  <p>Google Play 开发者控制台提供了一些帮助你测试应用内结算实施情况的工具:</p>
+
+  <ul>
+  <li>测试购买,让拥有测试帐户的用户真正购买你发布的应用内商品,但不向这些用户帐户实际收取费用。</li>
+  <li>来自 Google Play 的静态结算回复,用于在开发初期进行测试
+  </li></ul>
+
+  <p>要测试应用的应用内结算,必须将该应用安装在 Android 设备上。你不能使用 Android 模拟器测试应用内结算。用于测试的设备必须运行标准的 Android 1.6 或更高版本的平台(API 级别 4 或更高级别),并且要安装最新版本的 Google Play 应用。如果设备运行的不是最新的 Google Play 应用,你的应用将不能向 Google Play 发送应用内结算请求。有关如何设置设备以便用于开发 Android 应用的一般信息,请参阅<a href="{@docRoot}tools/device.html">使用硬件设备</a>。</p>
+
+  <h2 id="testing-purchases">测试应用内购买</h2>
+
+  <p>应用内结算实施就绪后,你可以通过两种方法测试应用内 SKU 的购买:</p>
+
+  <ul>
+  <li><strong>测试购买</strong>:让你选择的许可测试用户在应用发布之前购买应用内商品,但不向这些用户收取费用;</li>
+  <li><strong>真正购买</strong>:让普通用户真正购买你的应用内商品,并根据用户的付款方式实际收取费用。在这种情况下,你可以利用 Google Play 的 Alpha 和 Beta 测试用户小组管理哪些用户可以使用你的实施进行真正的购买。</li>
+  </ul>
+
+  <p>下面的各个部分介绍了有关如何使用这些方法进行测试和验证的详情。</p>
+
+  <h3 id="test-purchases">测试购买(应用内结算沙盒)</h3>
+
+  <p>测试购买提供了一种安全、方便的方法,让你在开发过程中或准备发布期间,对你的应用内结算实施情况进行大规模的测试。测试期间,经过授权的用户帐户可在你的应用尚未发布时通过 Google Play 购买应用内商品,不过这些用户帐户不用实际支付费用。</p>
+
+  <p>获得测试访问权限之后,这些用户可以侧载你的应用,并测试商品的完整销售、购买和订单完成流程。测试购买会产生真实的订单,Google Play 会采用与处理其他订单相同的方式对其进行处理。购买完成后,Google Play 不会让这些订单进入财务流程,从而确保不会向用户帐户实际收取费用,并在 14 天后自动取消已完成的订单。</p>
+
+  <h4 id="setup">设置测试购买</h4>
+
+  <p>设置测试购买很简单,你可以选择任何用户帐户作为测试帐户,而测试帐户中的任何用户都可以通过可用的付款方式进行测试购买(当然用户不需要通过付款方式支付任何费用)。</p>
+
+  <p>首先,上传并发布你希望测试者能够购买的应用内商品。你可以在开发者控制台中上传和发布应用内商品。请注意,你可以在发布 APK 之前上传并发布应用内商品。</p>
+
+  <p>然后,为获得授权的用户创建许可测试帐户。在开发者控制台中,转到<strong>设置</strong> &gt; <strong>帐户详情</strong>,然后在“许可测试”部分将相应地址添加到<strong>可用于测试的 Gmail 帐户</strong>字段。有关详情,请参阅<a href="#billing-testing-test">设置测试购买</a>。</p>
+
+  <p>在你将用户添加为许可测试帐户并保存更改之后,这些用户在 15 分钟内即可开始购买你的应用内商品。随后你可以向测试者发布你的应用并提供收集反馈的方法。</p>
+
+  <p class="note"><strong>注意</strong>:要进行测试购买,许可测试帐户必须设置在用户的 Android 设备上。如果设备上有多个帐户,则必须使用下载应用时所用的帐户进行购买。如果没有帐户下载过应用,则使用第一个帐户进行购买。用户可以通过展开购买对话框来确认执行购买的帐户。</p>
+
+  <h4 id="tp-account">测试购买和开发者帐户</h4>
+  <p>经过授权的许可测试帐户在 Google Play 中与你的开发者帐户相关联,而不是与特定 APK 或程序包名称相关联。被标识为测试帐户的帐户可以购买你帐户的任何应用内商品而不用支付费用。</p>
+
+  <h4 id="purchase-flow">购买流程的详情</h4>
+  <p>在测试购买期间,用户可以测试应用中实际的销售、购买和订单完成流程。购买时,应用内商品与普通商品一样显示,并包含实际价格。不过,Google Play 会在购买对话框中心处显示通知,说明这是测试购买,以便辨识。
+  </p>
+
+  <h4 id="cancelling">取消已完成的测试购买</h4>
+  <p>Google Play 会累积每个用户已完成的测试购买,但不会将相关信息传递到财务流程。经过一段时间后,Google Play 会自动取消这些购买以进行清理。</p>
+
+  <p>在某些情况下,你可能希望手动取消测试购买以便继续进行测试。要取消购买,你可以:</p>
+
+  <ul>
+  <li>等待交易到期:Google Play 会在购买日期的 14 天后清除已完成的测试购买。</li>
+  <li>手动取消购买:你可以转到 Google 电子钱包 Merchant Center,找到相应交易,将其取消。通过查找订单编号即可找到交易。</li>
+  </ul>
+
+  <h4 id="requirements">使用测试购买的要求</h4>
+  <p>如果你打算使用测试购买,请注意以下要求和限制:</p>
+  <ul>
+  <li>只有许可测试帐户可以进行测试购买,而且应用要使用应用内结算 API 第 3 版。</li>
+  <li>只有应用内商品可以进行测试购买,应用内订阅则不可以。</li>
+  </ul>
+
+  <h3 id="transations">使用真实交易进行测试</h3>
+  <p>在准备发布使用应用内结算的应用时,你可以先利用 Google Play Alpha/Beta 发布选项进行来验证和负荷测试你的应用内结算实施情况,然后再向所有用户发布应用。</p>
+
+  <p>Alpha/Beta 测试小组内的真实用户(由你选择)可以通过 Google Play 安装你的应用并测试应用内商品。他们可以通过其在 Google Play 中的常规付款方式进行真正的购买,而他们的帐户中会产生实际费用。请注意,如果你的 Alpha 和 Beta 发布小组中包括测试许可帐户,这些用户将只能进行测试购买。</p>
+
+
+  <h2 id="billing-testing-static">使用静态回复进行测试</h2>
+
+  <p>我们建议你先使用来自 Google Play 的静态回复测试应用内结算的实施情况。这样一来,你可以确认应用能否正确处理主要 Google Play 回复,以及你的应用能否正确验证签名。即使应用尚未发布,你也可以执行此测试。</p>
+
+  <p>要使用静态回复测试你的实施,请使用具有预留的商品 ID 的特定商品发送应用内结算请求。每个预留的商品 ID 均会从 Google Play 返回特定的静态回复。当你使用预留的商品 ID 发送应用内结算请求时不会发生资金转移。此外,当你使用预留的商品 ID 发送结算请求时不能指定付款方式。图 1 显示了商品 ID 为“android.test.purchased”的预留商品的结帐流程。</p>
+
+  <img src="{@docRoot}images/billing_test_flow.png" height="381" id="figure1" />
+  <p class="img-caption">
+    <strong>图 1. </strong>特定的预留商品“android.test.purchased”的电子钱包流程
+  </p>
+
+  <p>你不需要在应用的商品列表中列出预留的商品。Google Play 已经知道预留的商品 ID。此外,你不需要将应用上传到开发者控制台,就可以使用预留的商品 ID 执行静态回复测试。你只需在设备上安装你的应用,登录到设备,然后使用预留的商品 ID 发送结算请求。</p>
+
+  <p class="note"><strong>注意</strong>:过去,你可以通过上传未发布的“草稿”版本来测试应用,但现在系统已不再支持此功能。不过,现在你可以在将应用上传到 Google Play 商店之前就使用静态回复测试应用。有关详情,请参阅<a href="#draft_apps">草稿应用不再受支持</a>。
+
+  <p>用于测试静态应用内结算回复的预留商品 ID 有四种:</p>
+
+  <ul>
+    <li><strong>android.test.purchased</strong>
+      <p>当你使用此商品 ID 发送应用内结算请求时,Google Play 会假定你成功购买了商品而做出回复。回复内容包括一个 JSON 字符串,其中包含虚拟的购买信息(例如虚拟的订单 ID)。在某些情况下,JSON 字符串是经过签名的,并且回复中也包括签名,因此你可以利用这些回复测试你的签名验证实施情况。</p>
+    </li>
+    <li><strong>android.test.canceled</strong>
+      <p>当你使用此商品 ID 发送应用内结算请求时,Google Play 会假定购买已被取消而做出回复。如果订购流程出现问题(例如信用卡无效,或你在用户付款之前取消了订单),就会发生这种情况。</p>
+    </li>
+    <li><strong>android.test.refunded</strong>
+      <p>当你使用此商品 ID 发送应用内结算请求时,Google Play 会假定购买已退款而做出回复。退款不能通过 Google Play 的应用内结算服务发起,而是必须由你(商家)发起。在你通过 Google 电子钱包商家帐户处理退款请求之后,Google Play 会向你的应用发送一条退款信息。只有当 Google Play 收到来自 Google 电子钱包的退款已执行的通知时,Google Play 才会发送此消息。有关退款的更多详情,请参阅<a href="{@docRoot}google/play/billing/v2/api.html#billing-action-notify">处理 IN_APP_NOTIFY 信息</a>和<a href="http://support.google.com/googleplay/android-developer/bin/answer.py?hl=zh-CN&amp;answer=1153485">应用内结算定价</a>。</p>
+    </li>
+    <li><strong>android.test.item_unavailable</strong>
+      <p>当你使用此商品 ID 发送应用内结算请求时,Google Play 会假定所购商品在你应用的商品列表中不存在而做出回复。</p>
+    </li>
+  </ul>
+
+  <p>在某些情况下,预留的商品可能会返回经过签名的静态回复,这样你可以测试应用中的签名验证。只有当运行应用的用户拥有<a href="{@docRoot}distribute/googleplay/start.html">开发者帐户</a>或<a href="{@docRoot}google/play/billing/billing_admin.html#billing-testing-setup">测试帐户</a>时,预留的商品才返回经过签名的回复。
+
+  <p>要使用预留的商品 ID 发送应用内结算请求,你只需构建一个常规的 <code>REQUEST_PURCHASE</code> 请求即可,不过请使用上文介绍的一个预留商品 ID,而不要使用你的应用商品列表中的真实商品 ID。</p>
+
+  <p>要使用预留的商品 ID 测试应用,请按以下步骤操作:</p>
+
+  <ol>
+    <li><strong>在 Android 设备上安装你的应用。</strong>
+      <p>你不能使用模拟器测试应用内结算,而是必须在设备上安装应用才能测试应用内结算。</p>
+      <p>要了解如何在设备上安装应用,请参阅<a href="{@docRoot}tools/building/building-cmdline.html#RunningOnDevice">在设备上运行</a>。</p>
+    </li>
+    <li><strong>使用你的开发者帐户登录到设备。</strong>
+      <p>如果你只是使用预留的商品 ID 进行测试,则不需要使用测试帐户。</p>
+    </li>
+    <li><strong>确保你的设备运行的是受支持的 Google Play 应用版本或 MyApps 应用版本。</strong>
+      <p>如果你的设备运行的是 Android 3.0,则应用内结算要求使用版本为 5.0.12(或更高版本)的 MyApps 应用。如果你的设备运行的是其他版本的 Android,则应用内结算要求使用版本为 2.3.4(或更高版本)的 Google Play 应用。要了解如何查看 Google Play 应用的版本,请参阅<a href="http://market.android.com/support/bin/answer.py?answer=190860">更新 Google Play</a>。</p>
+    </li>
+    <li><strong>运行应用并购买预留的商品 ID。</strong></li>
+  </ol>
+
+  <p class="note"><strong>注意</strong>:使用预留的商品 ID 发送应用内结算请求会覆盖常用的 Google Play 生产系统。当你针对某个预留的商品 ID 发送应用内结算请求时,服务的质量会比不上生产环境。</p>
+
+  <h2 id="billing-testing-test">设置测试真实购买</h2>
+
+  <p>完成静态回复测试,并确认应用中的签名验证正常工作之后,你就可以通过执行实际的应用内购买,来测试应用内结算的实施情况。测试真实的应用内购买能让你测试端到端的应用内结算体验,包括从 Google Play 进行实际购买,以及用户在你的应用中体验的实际结帐流程。</p>
+
+  <p class="note"><strong>注意</strong>:你可以将应用发布到 <a href="{@docRoot}distribute/googleplay/developer-console.html#alpha-beta">Alpha 发布渠道</a>来进行端到端的测试。也就是说,这样做你可以将应用发布到 Google Play 商店,但限制为仅向你指定的测试者提供该应用。</p>
+
+  <p>要通过实际的应用内购买测试你的应用内结算实施情况,请在 Google Play 开发者控制台注册至少一个测试帐户。你不能使用自己的开发者帐户测试完整的应用内购买流程,因为 Google 电子钱包不允许你购买自己的商品。如果你之前没有设置过测试帐户,请参阅<a href="{@docRoot}google/play/billing/billing_admin.html#billing-testing-setup">设置测试帐户</a>。</p>
+
+  <p>此外,测试帐户只能购买你的商品列表中已经发布的商品。虽然你可以不发布应用,但必须发布商品。</p>
+
+  <p>要通过实际购买测试你的应用内结算实施情况,请按以下步骤操作:</p>
+
+  <ol>
+    <li><strong>通过开发者控制台将你的应用上传到 <a href="{@docRoot}distribute/googleplay/developer-console.html#alpha-beta">Alpha 发布渠道</a>。</strong>
+
+     <p class="note"><strong>注意</strong>:过去,你可以通过上传未发布的“草稿”版本来测试应用,但现在系统已不再支持此功能。现在你必须将应用发布到 Alpha 或 Beta 发布渠道,才能进行测试。有关详情,请参阅<a href="#draft_apps">草稿应用不再受支持</a>。
+
+    </p></li>
+    <li><strong>将商品添加到应用的商品列表。</strong>
+      <p>请务必发布商品(应用可以保持未发布状态)。要了解如何操作,请参阅<a href="{@docRoot}google/play/billing/billing_admin.html#billing-catalog">创建商品列表</a>。</p>
+    </li>
+    <li><strong>在 Android 设备上安装你的应用。</strong>
+      <p>你不能使用模拟器测试应用内结算,而是必须在设备上安装应用才能测试应用内结算。</p>
+      <p>要了解如何在设备上安装应用,请参阅<a href="{@docRoot}tools/building/building-cmdline.html#RunningOnDevice">在设备上运行</a>。</p>
+    </li>
+    <li><strong>确保你的设备运行的是受支持的 Google Play 应用版本或 MyApps 应用版本。</strong>
+      <p>如果你的设备运行的是 Android 3.0,则应用内结算要求使用版本为 5.0.12(或更高版本)的 MyApps 应用。如果你的设备运行的是其他版本的 Android,则应用内结算要求使用版本为 2.3.4(或更高版本)的 Google Play 应用。要了解如何查看 Google Play 应用的版本,请参阅<a href="http://market.android.com/support/bin/answer.py?answer=190860">更新 Google Play</a>。</p>
+    </li>
+    <li><strong>在应用中进行应用内购买。</strong></li>
+  </ol>
+
+  <p class="note"><strong>注意</strong>:在设备上更改主要帐户的唯一方法是恢复出厂设置,请确保先使用你的主要帐户在设备上登录。</p>
+
+  <p>完成对应用内结算实施情况的测试之后,你就可以在 Google Play 上发布应用了。你可以按照常规步骤<a href="{@docRoot}tools/publishing/preparing.html">准备</a>、<a href="{@docRoot}tools/publishing/app-signing.html">登录</a>以及<a href="{@docRoot}distribute/tools/launch-checklist.html">在 Google Play 上发布</a>。
+  </p>
+
+  <h2 id="draft_apps">草稿应用不再受支持</h2>
+
+  <p>过去,你可以发布应用的“草稿”版本进行测试,但现在系统已不再支持此功能。现在你可以通过两种方法在 Google Play 商店中测试预览版应用的功能运行情况:</p>
+
+  <ul>
+
+    <li>你可以将应用发布到 <a href="{@docRoot}distribute/googleplay/developer-console.html#alpha-beta">Alpha 或 Beta 发布渠道</a>。这样,应用将进入 Google Play 商店,但只提供给你添加到“白名单”中的测试者。</li>
+
+    <li>在少数情况下,你可以使用未发布的应用测试 Google Play 的功能。例如,你可以使用<a href="{@docRoot}google/play/billing/billing_testing.html#billing-testing-static">静态回复</a>和特殊的预留商品 ID(始终返回特定结果,例如“已购买”或“已退款”)来测试未发布应用的应用内结算支持。</li>
+
+  </ul>
\ No newline at end of file
diff --git a/docs/html-intl/intl/zh-cn/google/play/billing/index.jd b/docs/html-intl/intl/zh-cn/google/play/billing/index.jd
new file mode 100644
index 0000000..2ddd264
--- /dev/null
+++ b/docs/html-intl/intl/zh-cn/google/play/billing/index.jd
@@ -0,0 +1,47 @@
+page.title=Google Play 应用内结算
+page.metaDescription=应用内计费可让你以一次性的购买或订阅方式去销售数码内容。
+page.image=/images/play_dev.jpg
+
+@jd:body
+
+      <p>应用内结算是一项 Google Play 服务,可让你在自己的应用内销售数字内容。你可以使用该服务销售很多内容,包括媒体文件或照片等可下载的内容、游戏关卡或药水等虚拟内容、高级服务及功能,等等。你可以使用应用内结算销售下面这两类商品:</p>
+
+  <div class="sidebox-wrapper">
+  <div class="sidebox">
+    <h2><strong>应用内结算的新变化</strong></h2>
+    <ul>
+    <li><strong>IAB 第 2 版关闭</strong> - 应用内结算 API 第 2 版已弃用,将于 2015 年 1 月关闭。如果你的应用仍在使用应用内结算第 2 版,请尽快迁移到 API 第 3 版。</li>
+    <li><strong>季节性订阅</strong> - 你现在可以设置定期的<a href="billing_subscriptions.html#user-billing">季节性订阅</a>,即订阅在每年的同一天开始并在同一天结束(例如,每年 9 月 1 日开始、次年 4 月 10 日结束的体育订阅)。</li>
+    <li><strong>推迟订阅结算</strong> - 你可以将订阅者的下一个结算日<a href="billing_subscriptions.html#deferred-billing">推迟</a>到你选择的日期。用户在推迟期内仍能访问相关内容,但不用缴费。</li>
+    <li><strong>Google Play Developer API</strong> - 利用 <a href="{@docRoot}google/play/billing/gp-purchase-status-api.html">Google Play Developer API</a>,你可以执行各种发布和应用管理任务。该 API 中包含之前称为“Purchase Status API”的功能。<em></em> </li>
+    <li><strong>撤消订阅/退还订阅费</strong> - 你可以使用 Google Play Developer API <a href="billing_subscriptions.html#refunds">撤消用户的订阅并退还其订阅费</a>。如果你这样做,用户的订阅将立即结束,且系统会退还其最近的订阅付款。</li>
+    <li><strong>应用内结算第 3 版</strong> - <a href="{@docRoot}google/play/billing/api.html">最新版本</a>的应用内结算采用更易实施的同步 API,可让你更有效地管理应用内商品和订阅。</li>
+   </ul>
+  </div>
+  </div>
+
+  <ul>
+  <li>标准应用内商品(一次性结算);或</li>
+  <li>订阅(定期自动结算)</li>
+  </ul>
+
+  <p>如果你使用应用内结算服务来销售商品,那么无论你销售的是应用内商品还是订阅,Google Play 都会负责处理所有结帐详情,因此你的应用从不需要直接处理任何财务交易。Google Play 使用的结帐后端服务与用于应用购买的服务相同,因此你的用户将获得一致且熟悉的购买流程使用体验。</p>
+
+  <p>你通过 Google Play 发布的所有应用都可以实施应用内结算。只要你有 Google Play 开发者控制台帐户和 Google 电子钱包商家帐户(无需创建任何特殊帐户或进行任何注册),即可实施该服务。</p>
+
+  <p>为了帮你将应用内结算集成到你的应用中,Android SDK 提供了一个示例应用,向你演示如何在应用内销售标准的应用内商品和订阅。</p>
+
+  <p>要开始使用应用内结算,请先阅读下面介绍的文档或参加<a href="{@docRoot}training/in-app-billing/index.html">销售应用内商品</a>培训课程。</p>
+
+  <dl>
+    <dt><strong><a href="{@docRoot}google/play/billing/billing_overview.html">概述</a></strong></dt>
+      <dd>请阅读本文,了解应用内结算的基本概念。</dd>
+    <dt><strong><a href="{@docRoot}google/play/billing/api.html">API 第 3 版</a></strong></dt>
+      <dd>请阅读这部分中的文档,了解应用内结算最新版本概述以及实施详情和 API 参考。</dd>
+    <dt><strong><a href="{@docRoot}google/play/billing/billing_best_practices.html">安全性和设计</a></strong></dt>
+      <dd>请参阅这些最佳做法,以帮助确保你的应用内结算实施安全稳妥、设计合理。</dd>
+    <dt><strong><a href="{@docRoot}google/play/billing/billing_testing.html">测试应用内结算</a></strong></dt>
+      <dd>了解应用内结算测试工具的工作原理,以及如何测试应用内结算实施。</dd>
+    <dt><strong><a href="{@docRoot}google/play/billing/billing_admin.html">管理应用内结算</a></strong></dt>
+      <dd>了解如何设置商品列表、注册测试帐户以及处理退款。</dd>
+  </dl>
diff --git a/docs/html-intl/intl/zh-cn/index.jd b/docs/html-intl/intl/zh-cn/index.jd
new file mode 100644
index 0000000..590332f
--- /dev/null
+++ b/docs/html-intl/intl/zh-cn/index.jd
@@ -0,0 +1,107 @@
+fullpage=true
+page.viewport_width=970
+excludeFromSuggestions=true
+page.metaDescription=The official site for Android developers. Provides the Android SDK and documentation for app developers and designers.
+page.customHeadTag=<meta name="google-site-verification" content="sa-bIAI6GKvct3f61-WpRguHq-aNjtF7xJjMTSi79as" />
+
+@jd:body
+
+
+
+
+
+<div class="home-new-carousel-1">
+  <div class="fullscreen-carousel-content">
+    <div class="vcenter">
+      <div class="wrap clearfix">
+
+        <div class="static resource-flow-layout wrap col-16">
+          <div class="resource resource-card resource-card-18x6">
+
+      <div class="landing-section-header">
+            <div class="col-10"><img src="{@docRoot}images/home/l-hero_2x.png"
+                 srcset="{@docRoot}images/home/l-hero.png 1x, {@docRoot}images/home/l-hero_2x.png 2x"
+                 width="510" style="margin:20px 30px 0 30px"></div>
+            <div class="col-5" style=" margin-top:70px ">
+            <h3 stye="font-weight:300;">Android 5.0 Lollipop</h3>
+            <p>在 Android 5.0 的版本更新中,我们添加了多种可用于你的应用程序的新功能,比如锁屏通知,一个全新的相机API,OpenGL ES 3.1,以及新的 Material Design 界面等等。</p>
+            <a href="{@docRoot}about/versions/lollipop.html" class="landing-button landing-primary">了解详情</a>
+            </div>
+          </div>
+          </div>
+        </div>
+       <h2>&nbsp;</h2>
+        
+        <div style="margin-top:20px" class="resource-widget resource-flow-layout wrap col-16
+        no-section" data-query="collection:index/primary/zhcn" data-resourcestyle="card"
+        data-sortorder="-timestamp" data-maxresults="3" data-cardsizes="6x2,6x2,6x2"></div> 
+
+      </div> <!-- end .wrap -->
+    </div> <!-- end .vcenter -->
+  </div> <!-- end .fullscreen-carousel-content -->
+</div> <!-- end .fullscreen-carousel -->
+
+
+
+
+<div class="actions-bar" style="margin-top:20px">
+  <div class="wrap">
+    <div class="actions">
+      <div><a href="/sdk/index.html">下载 SDK</a></div>
+      <div><a href="/samples/index.html">浏览示例</a></div>
+      <div><a href="//www.youtube.com/user/androiddevelopers">观看视频</a></div>
+      <div><a href="/distribute/googleplay/developer-console.html">管理应用</a></div>
+    </div><!-- end .actions -->
+  </div><!-- end .wrap -->
+</div><!-- end .actions-bar -->
+
+
+
+<div class="landing-rest-of-page">
+  <div class="landing-section">
+    <div class="wrap">
+      <div class="landing-section-header">
+        
+            <div class="landing-h1" style="margin-top:0px">专为跨屏世界打造</div>
+        <div class="landing-subhead" style="margin-top:20px">
+          全球数亿手持设备安装的都是 Android 系统。<br />
+          现在 Android 系统还支持以下这些备受期待的新设备。
+        </div>
+      </div>
+      <div class="landing-body" style="margin-top:50px">
+        <div class="landing-breakout cols">
+          <div class="col-3-wide">
+              <img src="{@docRoot}images/home/wear-wordmark.png" />
+              <img src="{@docRoot}images/home/wear.png" />
+              <p class="landing-small">
+                让用户随时随地获得需要的信息。
+            </p>
+            <p class="landing-small">
+              <a href="/wear/index.html">了解 Android Wear</a>
+            </p>
+          </div>
+          <div class="col-3-wide">
+              <img src="{@docRoot}images/home/tv-wordmark.png" />
+             <img src="{@docRoot}images/home/tv.png" />
+              <p class="landing-small">
+                针对大屏幕打造应用,使你的内容引人入胜。
+              </p>
+            <p class="landing-small">
+              <a href="/tv/index.html">了解 Android TV</a>
+
+            </p>
+          </div>
+          <div class="col-3-wide">
+              <img src="{@docRoot}images/home/auto-wordmark.png" />
+              <img src="{@docRoot}images/home/auto.png" />
+              <p class="landing-small">
+                将你的音乐应用扩展到车载娱乐系统。
+             </p>
+            <p class="landing-small">
+              <a href="/auto/index.html">了解 Android Auto</a>
+            </p>
+          </div>
+        </div>
+      </div>
+    </div>  <!-- end .wrap -->
+  </div> <!-- end .landing-section -->
\ No newline at end of file
diff --git a/docs/html-intl/intl/zh-cn/resources.jd b/docs/html-intl/intl/zh-cn/resources.jd
new file mode 100644
index 0000000..3c2abd3
--- /dev/null
+++ b/docs/html-intl/intl/zh-cn/resources.jd
@@ -0,0 +1,57 @@
+page.title=中文资源
+page.viewport_width=970
+page.type=guide
+section.landing=true
+header.hide=1
+nonavpage=true
+page.metaDescription=我们翻译了以下一些网站资源,希望能帮助你开始设计、开发和在全球发布你的Android应用。
+
+@jd:body
+
+    <div class="jd-descr" itemprop="articleBody">
+    <div class="resource-widget resource-carousel-layout col-16" 
+    style="height:420px;margin-top:0px;padding-top:0"
+    data-query="collection:overview/carousel/zhcn"
+    data-sortOdrder="-timestamp"
+    data-maxResults="4"></div>
+
+<div class="dynamic-grid">
+
+<h2>你的语言的资源</h2>
+<p style="margin-bottom:2em;">我们翻译了以下一些网站资源,希望能帮助你开始设计、开发和发布你的Android应用程序到全球。</p>
+
+<h3 style="font-size:18px;font-weight:bold">检查清单</h3>
+
+  <div class="resource-widget resource-flow-layout col-16"
+    data-query="collection:overview/zhcn/1"
+    data-sortOrder=""
+    data-cardSizes="6x6,6x6,6x2x3"
+    data-maxResults="5"></div>
+
+<h3 style="font-size:18px;font-weight:bold">Google Play 应用内结算</h3>
+
+  <div class="resource-widget resource-flow-layout col-16"
+    data-query="collection:overview/zhcn/2"
+    data-sortOrder=""
+    data-cardSizes="6x6,6x6,6x2x3"
+    data-maxResults="5"></div>
+
+<h3 style="font-size:18px;font-weight:bold">工具</h3>
+
+  <div class="resource-widget resource-flow-layout col-16"
+    data-query="collection:overview/zhcn/3"
+    data-sortOrder=""
+    data-cardSizes="6x2x3,6x6,6x6"
+    data-maxResults="5"></div>
+
+<h3 style="font-size:18px;font-weight:bold">培训</h3>
+
+  <div class="resource-widget resource-flow-layout col-16"
+    data-query="collection:overview/zhcn/4"
+    data-sortOrder=""
+    data-cardSizes="6x6,12x2x3"
+    data-maxResults="5"></div>
+
+
+</div>
+
diff --git a/docs/html-intl/intl/zh-cn/tools/help/proguard.jd b/docs/html-intl/intl/zh-cn/tools/help/proguard.jd
new file mode 100644
index 0000000..0e8cc7a
--- /dev/null
+++ b/docs/html-intl/intl/zh-cn/tools/help/proguard.jd
@@ -0,0 +1,133 @@
+page.title=ProGuard
+parent.title=Tools
+parent.link=index.html
+page.type=工具
+@jd:body
+
+      <div id="qv-wrapper">
+      <div id="qv">
+        <h2>本文内容</h2>
+
+        <ol>
+          <li><a href="#enabling">启用 ProGuard</a></li>
+
+          <li><a href="#configuring">配置 ProGuard</a></li>
+
+          <li>
+            <a href="#decoding">解码混淆后的堆栈跟踪信息</a>
+
+            <ol>
+              <li><a href="#considerations">调试已发布应用时应注意的事项</a></li>
+            </ol>
+          </li>
+        </ol>
+
+        <h2>另请参见</h2>
+
+        <ol>
+          <li>
+            <a href="http://stuff.mit.edu/afs/sipb/project/android/sdk/android-sdk-linux/tools/proguard/docs/index.html#manual/introduction.html">ProGuard 手册 »</a>
+          </li>
+          <li>
+            <a href="http://stuff.mit.edu/afs/sipb/project/android/sdk/android-sdk-linux/tools/proguard/docs/index.html#manual/retrace/introduction.html">ProGuard ReTrace 手册 »</a>
+          </li>
+        </ol>
+      </div>
+    </div>
+
+    <p>ProGuard 工具通过移除无用的代码以及使用语义隐晦的名称来重命名类、字段和方法,从而达到压缩、优化和混淆代码的目的。最终您将获得一个较小的 <code>.apk</code> 文件,此文件更难于进行反向工程。由于 ProGuard 会使应用更难于进行反向工程,因此当应用使用对安全性要求极高的功能时(例如,当您<a href="{@docRoot}google/play/licensing/index.html">向应用授予许可</a>时),您必须使用此工具。</p>
+
+    <p>ProGuard 已集成到 Android 构建系统,所以您无需手动调用此工具。只有当您在发布模式下构建应用时,ProGuard 才会运行,因此当您在调试模式下构建应用时,就无需处理混淆后的代码。是否运行 ProGuard 完全由您决定,但我们强烈建议您运行该工具。</p>
+
+    <p>本文介绍如何启用和配置 ProGuard,以及如何使用 <code>retrace</code> 工具解码混淆后的堆栈跟踪信息。</p>
+
+    <h2 id="enabling">启用 ProGuard</h2>
+
+    <p>当您创建 Android 项目时,系统会在该项目的根目录中自动生成一个 <code>proguard.cfg</code> 文件。此文件将定义 ProGuard 会如何优化和混淆代码,因此您必须了解如何根据自己的需求对其进行自定义。默认的配置文件只涵盖一般的使用情形,因此您极有可能需要根据自己的需求对其进行修改。请参阅下文<a href="#configuring">配置 ProGuard</a> 这一部分,了解如何自定义 ProGuard 配置文件。</p>
+
+    <p>要启用 ProGuard,让其作为 Ant 或 Eclipse 构建环境的一部分运行,请在 <code>&lt;project_root&gt;/project.properties</code> 文件中设置 <code>proguard.config</code> 属性。该路径可以是绝对路径,也可以是项目根目录的相对路径。</p>
+
+  <p class="note"><strong>注意</strong>:在使用 Android Studio 时,您必须将 Proguard 添加到 <code>gradle.build</code> 文件的构建类型中。有关详情,请参阅 <a href="http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Running-ProGuard">Gradle 插件用户指南</a>。
+
+  <p>如果您将 <code>proguard.cfg</code> 文件留在默认位置(项目的根目录中),则可以按如下格式指定其位置:</p>
+  <pre class="no-pretty-print">
+  proguard.config=proguard.cfg
+  </pre>
+  <p>
+  您也可以将此文件移到任何所需的位置,然后按如下格式指定其绝对路径:
+  </p>
+  <pre class="no-pretty-print">
+  proguard.config=/path/to/proguard.cfg
+  </pre>
+
+
+    <p>当您在发布模式下构建应用时,无论是通过运行 <code>ant release</code> 还是通过使用 Eclipse 中的“导出向导”,构建系统都会自动检查是否设置了 <code>proguard.config</code> 属性。<em></em>如果已设置该属性,ProGuard 会自动处理应用的字节码,然后再将所有内容打包到 <code>.apk</code> 文件中。在调试模式下进行构建时之所以不调用 ProGuard,是因为该工具会加大调试难度。</p>
+
+    <p>ProGuard 在运行后会输出以下文件:</p>
+
+    <dl>
+      <dt><code>dump.txt</code></dt>
+      <dd>描述 <code>.apk</code> 文件中所有类文件的内部结构</dd>
+
+      <dt><code>mapping.txt</code></dt>
+      <dd>列出原始与混淆后的类、方法和字段名称之间的对应关系。如果您从发布版本收到问题报告,则必须使用此文件,因为通过它可将混淆后的堆栈跟踪信息转换为原始的类、方法和成员名称。有关详情,请参阅<a href="#decoding">解码混淆后的堆栈跟踪信息</a>。</dd>
+
+      <dt><code>seeds.txt</code></dt>
+      <dd>列出未混淆的类和成员</dd>
+
+      <dt><code>usage.txt</code></dt>
+      <dd>列出从 <code>.apk</code> 删除的代码</dd>
+    </dl>
+
+    <p>这些文件都位于以下目录中:</p>
+
+    <ul>
+      <li><code>&lt;project_root&gt;/bin/proguard</code>(如果您使用的是 Ant)。</li>
+
+      <li><code>&lt;project_root&gt;/proguard</code>(如果您使用的是 Eclipse)。</li>
+    </ul>
+
+
+    <p class="caution"><strong>注意</strong>:每当您在发布模式下构建版本时,这些文件都会被 ProGuard 最新生成的文件覆盖。请在每次发布应用时为这些文件保存一份副本,以便反混淆来自发布版本的问题报告。如需详细了解为何要保存这些文件,请参阅<a href="#considerations">调试已发布应用时应注意的事项</a>。
+    </p>
+
+    <h2 id="configuring">配置 ProGuard</h2>
+
+    <p>在某些情况下,<code>proguard.cfg</code> 文件中的默认配置足以满足您的需求。不过,在很多情况下,ProGuard 很难做出正确分析,因此可能会移除它认为无用而实际上您的应用却需要的代码。部分示例如下:</p>
+
+    <ul>
+      <li>一个只在 <code>AndroidManifest.xml</code> 文件中引用的类</li>
+
+      <li>一个通过 JNI 调用的方法</li>
+
+      <li>动态引用的字段和方法</li>
+    </ul>
+
+    <p>默认的 <code>proguard.cfg</code> 文件旨在涵盖一般的使用情形,但您可能会遇到异常情况,例如 <code>ClassNotFoundException</code>(此异常情况会在 ProGuard 删除您的应用调用的整个类时发生)。</p>
+
+    <p>您可以通过在 <code>proguard.cfg</code> 文件中添加一个 <code>-keep</code> 行,来修复因 ProGuard 在删除代码而造成的错误。例如:</p>
+    <pre>
+  -keep public class &lt;MyClass&gt;
+  </pre>
+
+    <p>在使用 <code>-keep</code> 选项时,您既有许多选择也有不少需要注意的方面,因此我们强烈建议您阅读 <a href="http://stuff.mit.edu/afs/sipb/project/android/sdk/android-sdk-linux/tools/proguard/docs/index.html#manual/introduction.html">ProGuard 手册</a>,详细了解如何自定义您的配置文件。该手册中的“Keep 选项概述”和“示例”部分尤其有用;<em></em><em></em><a href="http://stuff.mit.edu/afs/sipb/project/android/sdk/android-sdk-linux/tools/proguard/docs/index.html#manual/troubleshooting.html">问题排查</a>部分则概述了在 ProGuard 删除代码后您可能会遇到的其他常见问题。</p>
+
+    <h2 id="decoding">解码混淆后的堆栈跟踪信息</h2>
+
+    <p>当混淆后的代码输出堆栈跟踪信息时,方法名称会被混淆,即便仍能进行调试,难度也会很大。幸运的是,ProGuard 在每次运行时都会输出一个 <code>&lt;project_root&gt;/bin/proguard/mapping.txt</code> 文件,其中会显示与混淆后的名称相对应的原始的类、方法和字段名称。</p>
+
+    <p>Windows 上的 <code>retrace.bat</code> 脚本以及 Linux 或 Mac OS X 上的 <code>retrace.sh</code> 脚本可以将混淆后的堆栈跟踪信息转换成可读文件,此文件位于 <code>&lt;sdk_root&gt;/tools/proguard/</code> 目录中。执行 <code>retrace</code> 工具的语法如下:</p>
+    <pre>retrace.bat|retrace.sh [-verbose] mapping.txt [&lt;stacktrace_file&gt;]</pre>
+    <p>例如:</p>
+
+    <pre>retrace.bat -verbose mapping.txt obfuscated_trace.txt</pre>
+
+    <p>如果您不为“&lt;stacktrace_file&gt;”指定值,<code>retrace</code> 工具会从标准输入中读取。<em></em></p>
+
+    <h3 id="considerations">调试已发布应用时应注意的事项</h3>
+
+    <p>每次向用户发布应用时,都请保存所发布版本的 <code>mapping.txt</code> 文件。这样一来,如果用户遇到问题,并向您提交混淆后的堆栈跟踪信息,您就可以利用为每个发布版本保存的 <code>mapping.txt</code> 文件副本调试问题。每当您构建发布版本时,项目的 <code>mapping.txt</code> 文件都会被覆盖,因此您必须谨慎保存所需的版本。</p>
+
+    <p>例如,假设您发布了某个应用,并继续开发该应用的新功能,以便将来发布新版本。之后不久您使用 ProGuard 构建发布版本。此版本覆盖了之前的 <code>mapping.txt</code> 文件。之后,某位用户提交了问题报告,其中包含来自当前已发布的应用的堆栈跟踪信息。但您已无法调试该用户的堆栈跟踪信息,因为与该用户设备上的版本相关联的 <code>mapping.txt</code> 文件已被覆盖。除此之外,其他一些情况也可能会导致您的 <code>mapping.txt</code> 文件被覆盖。因此,如果您预计需要进行调试,请务必在每次发布应用时都保存一份副本。</p>
+
+    <p>如何保存 <code>mapping.txt</code> 文件由您自行决定。例如,您可以将其重命名以使其名称中包含版本号,也可以对其(连同源代码一起)进行版本管理。</p>
diff --git a/docs/html-intl/intl/zh-cn/tools/publishing/preparing.jd b/docs/html-intl/intl/zh-cn/tools/publishing/preparing.jd
new file mode 100644
index 0000000..46938b9
--- /dev/null
+++ b/docs/html-intl/intl/zh-cn/tools/publishing/preparing.jd
@@ -0,0 +1,186 @@
+page.title=准备发布
+page.type=工具
+
+@jd:body
+
+      <div id="qv-wrapper">
+    <div id="qv">
+      <h2>概述</h2>
+      <ul>
+        <li>了解发布应用需要哪些资源。</li>
+        <li>了解如何配置和构建应用以进行发布。</li>
+        <li>了解发布应用的最佳做法。</li>
+      </ul>
+      <h2>本文内容</h2>
+      <ol>
+        <li><a href="#publishing-intro">简介</a></li>
+        <li><a href="#publishing-gather">收集材料和资源</a></li>
+        <li><a href="#publishing-configure">配置应用</a></li>
+        <li><a href="#publishing-build">构建应用</a></li>
+        <li><a href="#publishing-resources">准备外部服务器和资源</a></li>
+        <li><a href="#publishing-test">测试应用以进行发布</a></li>
+      </ol>
+      <h2>另请参见</h2>
+      <ol>
+        <li><a href="{@docRoot}tools/publishing/publishing_overview.html">发布概述</a></li>
+        <li><a href="{@docRoot}tools/publishing/app-signing.html">为应用签名</a></li>
+        <li><a href="{@docRoot}distribute/tools/launch-checklist.html">Google Play 发布检查清单</a></li>
+      </ol>
+    </div>
+  </div>
+
+  <p>在将您的 Android 应用分发给用户之前,您需要先完成准备工作。对于所有 Android 应用,准备流程都是一项必须完成的<a href="{@docRoot}tools/workflow/index.html">开发任务</a>,也是发布流程中的第一步(参见图 1)。</p>
+
+  <p>当准备应用以进行发布时,您需要配置、构建并测试应用的发布版本。配置任务相当简单,涉及完成代码清理和代码修改这两项基本任务,从而达到优化应用的目的。构建流程与调试构建流程相似,可以使用 JDK 和 Android SDK 工具完成。测试任务就是做最终检查,以确保您的应用能在实际环境下正常运行。准备好要发布的应用后,您将获得一个已签名的 <code>.apk</code> 文件。您可以直接将该文件分发给用户,也可以通过 Google Play 等应用市场进行分发。</p>
+
+  <p>本文总结了您在准备应用以进行发布时需执行的几大任务。这些任务适用于所有 Android 应用,无论这些应用采用什么方式发布或分发给用户。如果您要通过 Google Play 发布应用,则还应查看 <a href="{@docRoot}distribute/tools/launch-checklist.html">Google Play 发布检查清单</a>,以确保您要发布的应用满足 Google Play 的所有要求。</p>
+
+  <p class="note"><strong>注意</strong>:在您执行本文所述任务之前,最好确保您的应用符合针对功能、性能和稳定性的所有发布标准。</p>
+
+  <img src="{@docRoot}images/publishing/publishing_overview_prep.png" alt="显示发布流程在整个开发流程中的位置" height="190" id="figure1" />
+  <p class="img-caption">
+    <strong>图 1.</strong>  准备发布是一项必须完成的<a href="{@docRoot}tools/workflow/index.html">开发任务</a>,也是发布流程中的第一步。
+  </p>
+
+  <h2 id="publishing-intro">简介</h2>
+
+  <p>为了向用户发布应用,您需要先创建发布版应用包,以供用户在其 Android 设备上安装和运行。发布版应用包与调试 <code>.apk</code> 文件中包含的组件相同(经过编译的源代码、资源、清单文件等),使用的构建工具也相同。不过,与调试 <code>.apk</code> 文件不同的是,发布版 <code>.apk</code> 文件已使用您自己的证书签名且使用 Zipalign 工具进行了优化。</p>
+
+  <div class="figure" style="width:331px">
+    <img src="{@docRoot}images/publishing/publishing_preparing.png" alt="显示准备应用以进行发布所需执行的五项任务" height="450" />
+    <p class="img-caption">
+      <strong>图 2.</strong>  要准备应用以进行发布,您需执行五大任务。
+    </p>
+  </div>
+
+  <p>通常情况下,如果您使用 Eclipse 和 ADT 插件或 Ant 构建脚本(包含在 Android SDK 中)构建应用,可以无缝地完成签名和优化任务。例如,您可以使用 Eclipse“导出向导”一次性完成对应用的编译、签名和优化操作。如果通过命令行进行构建,您还可以配置 Ant 构建脚本来执行相同的操作。</p>
+
+  <p>要准备应用以进行发布,您通常需要执行五大任务(参见图 2)。每项大任务可能包含一项或多项小任务,视您的应用发布方式而定。例如,如果您要通过 Google Play 发布应用,则可能需要在配置应用的发布版本时向清单中添加特殊的过滤规则。同样,为了满足 Google Play 发布指南的要求,您可能需要在收集发布材料时准备几张屏幕截图并撰写相关宣传文字。</p>
+
+  <p>一般来说,在对应用进行了全面调试和测试后,您需要执行图 2 中列出的任务。Android SDK 中提供的几种工具可帮您测试和调试您的 Android 应用。有关详情,请参阅开发者指南中的<a href="{@docRoot}tools/debugging/index.html">调试</a>和<a href="{@docRoot}tools/testing/index.html">测试</a>这两部分。</p>
+
+  <h2 id="publishing-gather">收集材料和资源</h2>
+
+  <p>要开始准备应用以进行发布,您需要收集几项支持性的信息,其中至少应包含用于为应用签名的加密密钥以及应用图标。此外,您可能还需要提供最终用户许可协议。</p>
+
+  <h4 id="publishing-keys">加密密钥</h4>
+
+  <p>Android 系统要求安装的每个应用都必须使用应用开发者拥有的证书(即开发者用于存放私钥的证书)进行数字签名。Android 系统利用该证书来识别应用作者并在应用之间建立信任关系。您用于签名的的证书无需由证书机构签名;Android 系统允许您使用自签名证书为您的应用签名。如需了解有关证书的要求,请参阅<a href="{@docRoot}tools/publishing/app-signing.html#cert">获取合适的私钥</a>。</p>
+
+  <p class="caution"><strong>重要提示</strong>:您的应用必须使用在 2033 年 10 月 22 日之后才会失效的加密密钥签名。</p>
+
+  <p>如果您应用访问的服务或使用的第三方库需要基于您的私钥的附加密钥,则您还必须获得其他发布密钥。例如,如果您的应用使用 <a href="http://code.google.com/android/add-ons/google-apis/reference/com/google/android/maps/MapView.html">MapView</a> 类(属于 <a href="http://code.google.com/android/add-ons/google-apis/maps-overview.html">Google 地图外部库</a>),您需要让您的应用注册 Google 地图服务并获取 Maps API 密钥。如需了解如何获取 Maps API 密钥,请参阅<a href="http://code.google.com/android/add-ons/google-apis/mapkey.html">获取 Maps API 密钥</a>。</p>
+
+  <h4>应用图标</h4>
+
+  <p>请确保您有应用图标而且图标遵守建议的<a href="{@docRoot}guide/practices/ui_guidelines/icon_design_launcher.html">图标指南</a>。应用的图标会显示在设备的主屏幕上和“启动器”窗口中,有助于用户识别您的应用。它还会显示在“管理应用”、“我的下载”等其他位置。此外,Google Play 等发布服务也会向用户显示您的应用图标。</p>
+
+  <p class="note"><strong>注意</strong>:如果您要在 Google Play 上发布应用,则需要创建高分辨率版本的图标。有关详情,请参阅<a href="https://www.google.com/support/androidmarket/developer/bin/answer.py?answer=1078870">应用的图形资源</a>。</p>
+
+  <h4>最终用户许可协议</h4>
+
+  <p>请考虑为您的应用准备最终用户许可协议 (EULA)。最终用户许可协议可帮助保护您的员工、公司和知识产权,因此我们建议您为自己的应用提供一份。</p>
+
+  <h4>其他材料</h4>
+
+  <p>您可能还需要准备宣传和营销材料,用来宣传您的应用。例如,如果您要在 Google Play 上发布应用,则需要准备几段宣传文字并创建几张应用截图。有关详情,请参阅<a href="https://www.google.com/support/androidmarket/developer/bin/answer.py?answer=1078870">应用的图形资源</a></p>
+
+  <h2 id="publishing-configure">配置应用以进行发布</h2>
+
+  <p>收集完所有支持材料后,您可以开始配置应用以进行发布。这部分总结了一些建议您在发布应用前对源代码、资源文件和应用清单进行的配置更改。虽然是否实施这里列出的大部分配置更改都由您决定,但这些都是良好的编码习惯。因此我们建议您实施这些更改。在某些情况下,您可能已在开发过程中实施了这些配置更改。</p>
+
+  <h4>选择合适的应用包名称</h4>
+
+  <p>请确保您选择的应用包名称适合在应用的整个生命周期中使用。将应用分发给用户后,您就无法再更改应用包名称。您可以在应用的清单文件中设置应用包名称。有关详情,请参阅介绍 <a href="{@docRoot}guide/topics/manifest/manifest-element.html#package">package</a> 属性的文档。</p>
+
+  <h4>停用日志记录和调试功能</h4>
+
+  <p>在构建应用的发布版本之前,请确保停用日志记录功能和调试选项。要停用日志记录功能,您可以移除源文件中对 <code><a href="{@docRoot}reference/android/util/Log.html">Log</a></code> 方法的调用。要停用调试功能,您可以从清单文件的 <code>&lt;application&gt;</code> 代码中移除 <code>android:debuggable</code> 属性或将 <code>android:debuggable</code> 属性设置为 <code>false</code>。此外,请移除您在项目中创建的所有日志文件或静态测试文件。</p>
+
+  <p>此外,您还应移除之前添加到代码中的所有 <code><a href="{@docRoot}reference/android/os/Debug.html">Debug</a></code> 跟踪调用,例如 <code><a href="{@docRoot}reference/android/os/Debug.html#startMethodTracing()">startMethodTracing()</a></code> 和 <code><a href="{@docRoot}reference/android/os/Debug.html#stopMethodTracing()">stopMethodTracing()</a></code> 方法调用。</p>
+
+  <p class="caution"><strong>重要提示</strong>:调试功能允许用户使用 Chrome DevTools 注入脚本和提取内容,因此如果您使用 <code><a href="{@docRoot}reference/android/webkit/WebView.html">WebView</a></code> 展示付费内容或使用 JavaScript 界面,请确保停用应用的调试功能。要停用调试功能,请使用 <code><a href="{@docRoot}reference/android/webkit/WebView.html#setWebContentsDebuggingEnabled(boolean)">WebView.setWebContentsDebuggingEnabled()</a></code> 方法。</p>
+
+  <h4>清理项目目录</h4>
+
+  <p>请清理您的项目,确保其符合 <a href="{@docRoot}tools/projects/index.html#ApplicationProjects">Android 项目</a>中所述的目录结构要求。项目中留有散乱或孤立的文件会妨碍对应用的编译,导致应用发生异常。您至少应执行下列清理任务:</p>
+
+  <ul>
+    <li>查看 <code>jni/</code>、<code>lib/</code> 和 <code>src/</code> 目录的内容。<code>jni/</code> 目录应只包含与 <a href="{@docRoot}tools/sdk/ndk/index.html">Android NDK</a> 相关联的源文件,例如 <code>.c</code>、<code>.cpp</code>、<code>.h</code> 和 <code>.mk</code> 文件。<code>lib/</code> 目录应只包含第三方库文件或专用库文件,包括预构建的共享库和静态库(例如 <code>.so</code> 文件)。<code>src/</code> 目录应只包含应用的源文件(<code>.java</code> 和 <code>.aidl</code> 文件)。<code>src/</code> 不应包含任何 <code>.jar</code> 文件。</li>
+    <li>检查项目中是否包含应用不需要的专用或专有数据文件,如果有请将其移除。例如,在项目的 <code>res/</code> 目录中查找是否包含旧的图形文件、布局文件和值文件,如果您不再使用这些文件,请将其删除。</li>
+    <li>检查 <code>lib/</code> 目录中是否包含测试库;如果您的应用不再使用这些库,请将其移除。</li>
+    <li>查看 <code>assets/</code> 目录和 <code>res/raw/</code> 目录的内容,查找其中是否包含需在应用发布前更新或移除的原始资源文件和静态文件。</li>
+  </ul>
+
+  <h4>查看并更新清单设置</h4>
+
+  <p>确认下列清单项是否已正确设置:</p>
+
+  <ul>
+    <li><a href="{@docRoot}guide/topics/manifest/uses-permission-element.html">
+    &lt;uses-permission&gt;</a> 元素
+      <p>您应该只指定应用必需的相关权限。</p>
+    </li>
+    <li><code>android:icon</code> 和 <code>android:label</code> 属性
+      <p>您必须为这些属性(位于 <a href="{@docRoot}guide/topics/manifest/application-element.html">&lt;application&gt;</a> 元素中)指定值。</p>
+    </li>
+    <li><code>android:versionCode</code> 和 <code>android:versionName</code> 属性。
+      <p>我们建议您为这些属性(位于 <a href="{@docRoot}guide/topics/manifest/manifest-element.html">&lt;manifest&gt;</a> 元素中)指定值。有关详情,请参阅<a href="{@docRoot}tools/publishing/versioning.html">应用的版本管理</a>。</p>
+    </li>
+  </ul>
+
+  <p>如果您要在 Google Play 上发布应用,还可以设置另外几个清单元素。例如,<code>android:minSdkVersion</code> 和 <code>android:targetSdkVersion</code> 属性(位于 <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html">&lt;uses-sdk&gt;</a> 元素中)。如需详细了解这些设置以及其他 Google Play 设置,请参阅 <a href="{@docRoot}google/play/filters.html">Google Play 上的过滤器</a>。</p>
+
+  <h4>解决兼容性问题</h4>
+
+  <p>Android 提供了多种工具和技术,可让您的应用与多种设备兼容。为了让您的应用尽可能地适合更多用户,建议您执行以下操作:</p>
+
+  <ul>
+    <li><strong>添加跨屏配置支持</strong>
+      <p>确保您采取了<a href="{@docRoot}guide/practices/screens_support.html#screen-independence">支持跨屏的最佳做法</a>。通过支持跨屏配置,您创建的应用可在 Android 支持的任何屏幕尺寸的设备上正常运行且拥有精美外观。</p>
+    </li>
+    <li><strong>针对 Android 平板电脑优化应用</strong>
+      <p>如果您的应用是专为运行 Android 3.0 以上版本的设备打造的,请遵照<a href="{@docRoot}guide/practices/optimizing-for-3.0.html">针对 Android 3.0 优化应用</a>中所述的指南和最佳做法,这样您的应用就能与运行 Android 3.0 的设备兼容。</p>
+    </li>
+    <li><strong>考虑使用支持库</strong>
+      <p>如果您的应用是专为运行 Android 3.x 的设备打造的,请向您的应用项目中添加<a href="{@docRoot}tools/support-library/index.html">支持库</a>,这样您的应用就能与较低版本的 Android 系统兼容。您可以将“支持库”提供的静态支持库添加到您的 Android 应用中,以便使用低版本的平台上未提供的 API 或使用不属于框架 API 的实用程序 API。</p>
+    </li>
+  </ul>
+
+  <h4>更新服务器和服务的网址</h4>
+
+  <p>如果您的应用需要访问远程服务器或服务,请确保您使用的是该服务器或服务的生产网址或路径,而非测试网址或路径。</p>
+
+  <h4>实施许可服务(如果您要在 Google Play 上发布应用)</h4>
+
+  <p>如果您要通过 Google Play 发布付费应用,请考虑支持 Google Play 许可服务。借助这项许可服务,您可以根据当前用户的购买情况控制其对您应用的访问权限。使用 Google Play 许可服务并不是强制的,即使您通过 Google Play 发布应用也可以不使用这项服务。</p>
+
+  <p>如需详细了解 Google Play 许可服务以及如何在应用中使用该服务,请参阅<a href="{@docRoot}google/play/licensing/index.html">应用许可</a>。</p>
+
+  <h2 id="publishing-build">构建应用以进行发布</h2>
+
+  <p>配置好应用后,您可以将其构建为一个经过签名和优化的发布版 <code>.apk</code> 文件。JDK 中包含用于为 <code>.apk</code> 文件签名的工具(Keytool 和 Jarsigner);Android SDK 中包含用于编译和优化 <code>.apk</code> 文件的工具。如果您使用的是带 ADT 插件的 Eclipse 或是从命令行使用 Ant 构建脚本,则整个构建流程可以自动完成。</p>
+
+  <h3>使用 Eclipse 进行构建</h3>
+
+  <p>您可以使用 Eclipse“导出向导”构建使用私钥签名并经过优化的发布版 <code>.apk</code> 文件。如需了解如何运行“导出向导”,请参阅<a href="{@docRoot}tools/publishing/app-signing.html#ExportWizard">使用 Eclipse ADT 进行编译和签名</a>。“导出向导”会对您要发布的应用进行编译、使用私钥为应用签名并使用 Zipalign 工具对应用进行优化。如果您已通过 Eclipse 运行或调试过应用且未在应用中发现错误,“导出向导”就应该能成功运行(有关详情,请参阅<a href="{@docRoot}tools/building/building-eclipse.html">使用带 ADT 的 Eclipse 构建和运行应用</a>)。</p>
+
+  <p>“导出向导”假设您有适合为应用签名的<a href="#billing-keys">证书和私钥</a>。如果您没有合适的证书和私钥,“导出向导”将帮您生成一个(如需详细了解签名流程和签名指南,请参阅<a href="{@docRoot}tools/publishing/app-signing.html">为应用签名</a>)。</p>
+
+  <h3>使用 Ant 进行构建</h3>
+
+  <p>您可以使用 Ant 构建脚本(包含在 Android SDK 中)来构建使用私钥签名并经过优化的发布版 <code>.apk</code> 文件。如需了解具体做法,请参阅<a href="{@docRoot}tools/building/building-cmdline.html#ReleaseMode">在发布模式下进行构建</a>。这种构建方式假设您有适合为应用签名的<a href="#billing-keys">证书和密钥</a>。如果您没有合适的证书和私钥,“导出向导”将帮您生成一个(如需详细了解签名流程和签名指南,请参阅<a href="{@docRoot}tools/publishing/app-signing.html">为应用签名</a>)。</p>
+
+  <h2 id="publishing-resources">准备外部服务器和资源</h2>
+
+  <p>如果您的应用依赖于远程服务器,请确保该服务器是安全的且已经过配置,随时可投入实际使用。如果您要在应用中实施<a href="{@docRoot}google/play/billing/index.html">应用内结算</a>,而且要在远程服务器上执行签名验证步骤,则必须这样做。</p>
+
+  <p>此外,如果您的应用从远程服务器或实时服务中抓取内容(例如内容 Feed),请确保您提供的内容是最新的且随时可投入实际使用。</p>
+
+  <h2 id="publishing-test">测试应用以进行发布</h2>
+
+  <p>测试应用的发布版本有助于确保您的应用能在实际设备上和实际网络条件下正常运行。理想情况下,您至少应在一部手持设备上和一台平板电脑上测试您的应用,以确认界面元素的尺寸是否合适,以及应用的性能和能耗是否可接受。</p>
+
+  <p>在测试之前,请先参阅<a href="{@docRoot}tools/testing/what_to_test.html">测试内容</a>。这篇文章总结了您在测试 Android 应用时应注意的常见情况。当您完成测试并确定应用的发布版本能正常运行后,就可以向用户发布您的应用了。有关详情,请参阅<a href="{@docRoot}tools/publishing/publishing_overview.html#publishing-release">向用户发布应用</a>。如果您要在 Google Play 上发布应用,请参阅 <a href="{@docRoot}distribute/tools/launch-checklist.html">Google Play 发布检查清单</a>。</p>
+
+
diff --git a/docs/html-intl/intl/zh-cn/tools/publishing/publishing_overview.jd b/docs/html-intl/intl/zh-cn/tools/publishing/publishing_overview.jd
new file mode 100644
index 0000000..eb6a617
--- /dev/null
+++ b/docs/html-intl/intl/zh-cn/tools/publishing/publishing_overview.jd
@@ -0,0 +1,142 @@
+page.title=发布概述
+page.type=工具
+
+@jd:body
+
+    <div id="qv-wrapper">
+<div id="qv">
+  <h2>概述</h2>
+  <ul>
+    <li>了解如何发布 Android 应用。</li>
+    <li>了解如何准备应用以进行发布。</li>
+    <li>了解如何向用户发布应用。</li>
+  </ul>
+  <h2>本文内容</h2>
+  <ol>
+    <li><a href="#publishing-prepare">准备应用以进行发布</a></li>
+    <li><a href="#publishing-release">向用户发布应用</a>
+  </li></ol>
+  <h2>另请参见</h2>
+  <ol>
+    <li><a href="{@docRoot}distribute/tools/launch-checklist.html">在 Google Play 上发布应用</a></li>
+  </ol>
+</div>
+</div>
+
+<p>发布是将您的 Android 应用提供给用户的常规流程。您在发布 Android 应用时需要执行两大任务:</p>
+
+<ul>
+  <li>准备应用以进行发布。
+    <p>在准备阶段,您需要构建应用的发布版本,以供 Android 设备用户下载和安装。</p>
+  </li>
+  <li>向用户发布应用。
+    <p>在发布阶段,您向用户宣传、销售和分发应用的发布版本。</p>
+  </li>
+</ul>
+
+<p>通常情况下,您可以通过 <a href="{@docRoot}distribute/googleplay/index.html">Google Play</a> 等应用市场发布应用。不过,您也可以通过以下方式发布应用:将应用直接发送给用户;让用户从您的网站下载应用。</p>
+
+<p>图 1 展示了发布流程在整个 Android <a href="{@docRoot}tools/workflow/index.html">应用开发流程</a>中所处的位置。一般来说,您要先在调试环境中完成对应用的测试,然后再执行发布流程。此外,在开始发布流程之前,您最好确保应用符合针对功能、性能和稳定性的所有发布标准。</p>
+
+<img src="{@docRoot}images/publishing/publishing_overview.png" alt="展示了发布流程在整个开发流程中所处的位置" height="86" id="figure1" />
+<p class="img-caption">
+  <strong>图 1. </strong> 发布是 Android <a href="{@docRoot}tools/workflow/index.html">应用开发流程</a>中的最后一个阶段。
+</p>
+
+<h2 id="publishing-prepare">准备应用以进行发布</h2>
+
+<p>发布之前的准备过程涉及多个步骤,需要您完成以下任务:</p>
+
+<ul>
+  <li>配置应用以进行发布。
+    <p>您至少需要从清单文件中移除 <code><a href="{@docRoot}reference/android/util/Log.html">Log</a></code> 调用和 <a href="{@docRoot}guide/topics/manifest/application-element.html#debug">android:debuggable</a> 属性,还应为 <code>android:versionCode</code> 和 <code>android:versionName</code> 属性(位于 <a href="{@docRoot}guide/topics/manifest/manifest-element.html">&lt;manifest&gt;</a> 元素中)提供值。此外,您还可能需要配置多项其他设置,以符合 Google Play 的发布要求,或者符合其他应用发布方式的要求。</p>
+  </li>
+  <li>构建应用的发布版本并签名。
+    <p>Android SDK 工具中随附的 Android Development Tools (ADT) 插件和 Ant 构建脚本可向您提供构建应用的发布版本并签名所需的全部信息。</p>
+  </li>
+  <li>测试应用的发布版本。
+    <p>在分发应用之前,您至少应在一部适配的手机和一台适配的平板电脑上全面测试应用的发布版本。</p>
+  </li>
+  <li>更新应用资源以进行发布。
+    <p>您需确保所有应用资源(例如多媒体文件和图形)都是最新的,且已包含在应用中或者已存储到正确的生产服务器上。</p>
+  </li>
+  <li>准备应用所依赖的远程服务器和服务。
+    <p>如果您的应用依赖于外部服务器或服务,您要确保这些服务器或服务是安全的,随时可投入实际使用。</p>
+  </li>
+</ul>
+
+<p>在准备阶段,您可能还需要执行其他一些任务。例如,您需要获取私钥来为应用签名;如果您使用 <a href="http://code.google.com/android/add-ons/google-apis/maps-overview.html">Google 地图外部库</a>,则需要获取 Maps API 发布密钥。此外,您还需要为应用创建一个图标,而且可能需要准备最终用户许可协议 (EULA) 以保护您的员工、公司和知识产权。</p>
+
+<p>当您完成发布应用所需的准备工作之后,您将得到一个已签名的 <code>.apk</code> 文件,可以将文件分发给用户。</p>
+
+<p>要了解如何准备应用以进行发布,请参阅开发者指南中的<a href="{@docRoot}tools/publishing/preparing.html">准备发布</a>。这篇文章提供了有关如何配置和构建应用的发布版本的分步说明。</p>
+
+<h2 id="publishing-release">向用户发布应用</h2>
+
+<p>您可以通过多种方式发布您的 Android 应用。通常情况下,您通过 Google Play 等应用市场发布应用,但您也可以在自己的网站上发布应用或直接将应用发送给用户。
+
+<h3 id="publishing-marketplace">通过应用市场发布应用</h3>
+
+<p>如果您希望向尽可能多的目标用户分发应用,最好通过 Google Play 等应用市场发布应用。</p>
+
+<p>Google Play 是 Android 应用的主要市场,它尤其适合向遍布全球的众多目标用户分发您的应用。不过,您也可以通过自己喜欢的其他应用市场分发您的应用,也可以在多个市场中分发。</p>
+
+
+<h4 id="publishing-market">在 Google Play 上发布应用</h4>
+
+<p>Google Play 是一个强大的应用发布平台,可帮助您向全球用户宣传、销售和分发您的 Android 应用。当通过 Google Play 发布应用时,您可以使用一系列开发者工具,分析您的销售数据、了解市场趋势以及控制向哪些用户分发应用。此外,您还能使用多种有助您增加收入的功能,如<a href="{@docRoot}google/play/billing/index.html">应用内结算</a>和<a href="{@docRoot}google/play/licensing/index.html">应用许可</a>。丰富的工具和功能,以及为数众多的最终用户社区功能,让 Google Play 成为销售和购买 Android 应用的主要平台。</p>
+
+<p>在 Google Play 上发布应用相当简单,只需三个基本步骤:</p>
+
+<ul>
+  <li>准备宣传材料。
+    <p>为了充分利用 Google Play 的营销和宣传功能,您需要为应用制作宣传材料,例如屏幕截图、视频、图形以及宣传文字。</p>
+  </li>
+  <li>配置选项并上传资源。
+    <p>利用 Google Play,您可以将应用定位到全球的用户和设备。通过配置各种 Google Play 设置,您可以选择要覆盖的国家/地区、要使用的商品详情语言以及应用在各个国家/地区的价格。此外,您还可以配置商品详情,例如应用类型、类别和内容分级。配置各种选项之后,您可以上传宣传材料和草稿版(未发布的)应用。</p>
+  </li>
+  <li>发布应用的发布版本。
+    <p>如果您确认各项发布设置均配置正确,而且您上传的应用可以公开发布,只需点击开发者控制台中的<strong>发布</strong>即可。在几分钟之内,您的应用就会发布到网络上供全球用户下载。</p>
+  </li>
+</ul>
+
+<p>如需了解完整信息,请访问 <a href="{@docRoot}distribute/googleplay/index.html">Google Play</a>。</p>
+
+
+<h3 id="publishing-email">通过电子邮件发布应用</h3>
+
+<div class="figure" style="width:246px">
+  <img src="{@docRoot}images/publishing/publishing_via_email.png" alt="此屏幕截图展示了用户在收到您发送的应用时看到的图形界面" style="width:240px" />
+  <p class="img-caption">
+    <strong>图 1. </strong> 当您通过电子邮件向用户发送应用时,用户只需点击<strong>安装</strong>即可。
+  </p>
+</div>
+
+<p>最简单快捷的应用发布方式就是通过电子邮件将应用发送给用户。为此,您需要准备好应用的发布版本,将其添加为电子邮件的附件,然后发送给用户。当用户在其 Android 设备上打开您的电子邮件时,Android 系统会识别出该 APK 并在电子邮件中显示<strong>立即安装</strong>按钮(参见图 1)。用户触摸该按钮即可安装您的应用。</p>
+
+<p class="note"><strong>注意</strong>:只有当用户将其设备配置为允许安装来自<a href="#unknown-sources">未知来源</a>的应用且通过本机的 Gmail 应用打开您的电子邮件时,才会出现图 1 所示的<strong>立即安装</strong>按钮。</p>
+
+<p>如果您只将应用发送给几位受信任的用户,那么通过电子邮件分发就非常方便。不过,这种发布方式不利于阻止盗版和未经授权的分发,也就是说,收到您应用的任何人都可以将应用转发给其他人。</p>
+
+<h2 id="publishing-website">通过网站发布应用</h2>
+
+<p>如果您不想在 Google Play 这样的市场上发布应用,则可以将应用放在您的网站或服务器(包括私人服务器或企业服务器)上供用户下载。为此,您必须先按正常流程准备好应用的发布版本,然后您只需将准备好的 APK 文件托管到网站上并向用户提供下载链接即可。
+</p>
+
+<p>当用户在其 Android 设备上访问该下载链接时,文件就会开始下载,Android 系统会在下载完成后自动将应用安装到设备上。不过,只有当用户将其“设置”配置为允许安装来自<a href="#unknown-sources">未知来源</a>的应用时,安装进程才会自动启动。</p>
+
+<p>虽然在您的网站上发布应用相对比较简单,但效率可能并不高。例如,如果您想通过自己的应用获利,则必须自行处理和跟踪所有财务交易且不能使用 Google Play 的<a href="{@docRoot}google/play/billing/index.html">应用内结算服务</a>来销售应用内商品。此外,您也不能使用<a href="{@docRoot}google/play/licensing/index.html">许可服务</a>来阻止他人未经授权安装和使用您的应用。</p>
+
+
+<h2 id="unknown-sources">用户选择安装来自未知来源的应用</h2>
+
+<div class="figure" style="width:246px;margin-top:0">
+  <img src="{@docRoot}images/publishing/publishing_unknown_sources_sm.png" alt="此屏幕截图展示了用于接受从未知来源下载和安装应用的设置" style="width:240px" />
+  <p class="img-caption">
+    <strong>图 2. </strong> 用户必须启用<strong>未知来源</strong>设置,才能安装从 Google Play 以外的位置下载的应用。
+  </p>
+</div> 
+
+<p>Android 可防止用户无意中从 Google Play(受信任的市场)以外的位置下载并安装应用。只要用户未启用“设置”<strong>&gt;</strong>“安全”下的<strong>未知来源</strong>(如图 2 所示),它就会阻止此类安装。要允许安装来自其他来源的应用,用户必须在其设备上启用“未知来源”设置,且必须先更改此项配置才能将您的应用下载到设备上。<em></em></p> 
+
+<p class="note">请注意,部分网络提供商不允许用户安装来自未知来源的应用。</p>
diff --git a/docs/html-intl/intl/zh-cn/training/basics/fragments/fragment-ui.jd b/docs/html-intl/intl/zh-cn/training/basics/fragments/fragment-ui.jd
new file mode 100644
index 0000000..51a4e27
--- /dev/null
+++ b/docs/html-intl/intl/zh-cn/training/basics/fragments/fragment-ui.jd
@@ -0,0 +1,139 @@
+page.title=构建灵活的界面
+trainingnavtop=true
+page.type=培训
+@jd:body
+
+    <div id="tb-wrapper">
+      <div id="tb"> 
+        <h2>本课程所教授的内容:</h2>
+    <ol>
+      <li><a href="#AddAtRuntime">在运行时向 Activity 添加 Fragment</a></li>
+      <li><a href="#Replace">用一个 Fragment 替换另一个 Fragment </a></li>
+    </ol>
+
+        <h2>你还应阅读以下内容:</h2>
+        <ul>
+          <li><a href="{@docRoot}guide/components/fragments.html"><code>fragment</code></a></li>
+          <li><a href="{@docRoot}guide/practices/tablets-and-handsets.html">支持平板电脑和手机</a></li>
+        </ul>
+
+    <h2>试试以下示例:</h2>
+
+    <div class="download-box">
+     <a href="http://developer.android.com/shareables/training/FragmentBasics.zip" class="button">下载示例</a>
+     <p class="filename">FragmentBasics.zip</p>
+    </div>
+
+      </div>
+    </div>
+
+
+    <p>在设计支持各种屏幕尺寸的应用时,你可以在不同的布局配置中重复使用 Fragment ,以便根据相应的屏幕空间提供更出色的用户体验。</p>
+
+    <p>例如,一次只显示一个 Fragment 可能就很适合手机这种单窗格界面,但在平板电脑上,你可能需要设置并列的 Fragment,因为平板电脑的屏幕尺寸较宽阔,可向用户显示更多信息。</p>
+
+    <img src="{@docRoot}images/training/basics/fragments-screen-mock.png" alt="" />
+    <p class="img-caption"><strong>图 1:</strong> 两个 Fragment,显示在不同尺寸屏幕上同一 Activity 的不同配置中。在较宽阔的屏幕上,两个 Fragment 可并列显示;在手机上,一次只能显示一个 Fragment,因此必须在用户导航时更换 Fragment。</p>
+
+    <p>利用 <code><a href="{@docRoot}reference/android/support/v4/app/FragmentManager.html">FragmentManager</a></code> 类提供的方法,你可以在运行时添加、移除和替换 Activity 中的 Fragment,以便为用户提供一种动态体验。</p>
+
+
+
+    <h2 id="AddAtRuntime">在运行时向 Activity 添加 Fragment</h2> 
+
+    <p>你可以在 Activity 运行时向其添加 Fragment,而不用像<a href="creating.html">上一课</a>中介绍的那样,使用 <code>&lt;fragment&gt;</code> 元素在布局文件中为 Activity 定义 Fragment。如果你打算在 Activity 运行周期内更改 Fragment,就必须这样做。</p>
+
+    <p>要执行添加或移除 Fragment 等事务,你必须使用 <code><a href="{@docRoot}reference/android/support/v4/app/FragmentManager.html">FragmentManager</a></code> 创建一个 <code><a href="{@docRoot}reference/android/support/v4/app/FragmentTransaction.html">FragmentTransaction</a></code>,后者可提供用于执行添加、移除、替换以及其他 Fragment 事务的 API。</p>
+
+    <p>如果 Activity 中的 Fragment 可以移除和替换,你应在调用 Activity 的 <code><a href="{@docRoot}reference/android/app/Activity.html#onCreate(android.os.Bundle)">onCreate()</a></code> 方法期间为 Activity 添加初始 Fragment(s)。</p>
+
+    <p>在处理 Fragment(特别是在运行时添加的 Fragment )时,请谨记以下重要规则:必须在布局中为 Fragment 提供 <code><a href="{@docRoot}reference/android/view/View.html">View</a></code> 容器,以便保存 Fragment 的布局。</p>
+
+    <p>下面是<a href="creating.html">上一课</a>所示布局的替代布局,这种布局一次只会显示一个 Fragment。要用一个 Fragment 替换另一个 Fragment, Activity 的布局中需要包含一个作为 Fragment 容器的空 <code><a href="{@docRoot}reference/android/widget/FrameLayout.html">FrameLayout</a></code>。</p>
+
+    <p><em></em>请注意,该文件名与上一课中布局文件的名称相同,但布局目录没有 <code>large</code> 这一限定符。因此,此布局会在设备屏幕小于“large”的情况下使用,原因是尺寸较小的屏幕不适合同时显示两个 Fragment。<em></em></p>
+
+    <p><code>res/layout/news_articles.xml:</code></p>
+    <pre>
+    &lt;FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/fragment_container"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" /&gt;
+    </pre>
+
+    <p>在 Activity 内部,使用 Support Library API 调用 <code><a href="{@docRoot}reference/android/support/v4/app/FragmentActivity.html#getSupportFragmentManager()">getSupportFragmentManager()</a></code> 以获取 <code><a href="{@docRoot}reference/android/support/v4/app/FragmentManager.html">FragmentManager</a></code>,然后调用 <code><a href="{@docRoot}reference/android/support/v4/app/FragmentManager.html#beginTransaction()">beginTransaction()</a></code> 创建 <code><a href="{@docRoot}reference/android/support/v4/app/FragmentTransaction.html">FragmentTransaction</a></code>,同时调用 <code><a href="{@docRoot}reference/android/support/v4/app/FragmentTransaction.html#add(android.support.v4.app.Fragment, java.lang.String)">add()</a></code> 添加 Fragment。</p>
+
+    <p>你可以使用同一个 <code><a href="{@docRoot}reference/android/support/v4/app/FragmentTransaction.html">FragmentTransaction</a></code> 对 Activity 执行多 Fragment 事务。当你准备好进行更改时,必须调用 <code><a href="{@docRoot}reference/android/support/v4/app/FragmentTransaction.html#commit()">commit()</a></code>。</p>
+
+    <p>例如,下面介绍了如何为上述布局添加 Fragment :</p>
+
+    <pre>
+    import android.os.Bundle;
+    import android.support.v4.app.FragmentActivity;
+
+    public class MainActivity extends FragmentActivity {
+        &amp;Override
+        public void onCreate(Bundle savedInstanceState) {
+            super.onCreate(savedInstanceState);
+            setContentView(R.layout.news_articles);
+
+            // 确认 Activity 使用的布局版本包含
+            // fragment_container FrameLayout
+            if (findViewById(R.id.fragment_container) != null) {
+
+                // 不过,如果我们要从先前的状态还原,
+                // 则无需执行任何操作而应返回
+                // 否则就会得到重叠的 Fragment 。
+                if (savedInstanceState != null) {
+                    return;
+                }
+
+                // 创建一个要放入 Activity 布局中的新 Fragment 
+                HeadlinesFragment firstFragment = new HeadlinesFragment();
+
+                // 如果此 Activity 是通过 Intent 发出的特殊指令来启动的,
+                // 请将该 Intent 的 extras 以参数形式传递给该 Fragment 
+                firstFragment.setArguments(getIntent().getExtras());
+
+                // 将该 Fragment 添加到“fragment_container”FrameLayout 中
+                getSupportFragmentManager().beginTransaction()
+                        .add(R.id.fragment_container, firstFragment).commit();
+            }
+        }
+    }
+    </pre>
+
+    <p>由于该 Fragment 已在运行时添加到 <code><a href="{@docRoot}reference/android/widget/FrameLayout.html">FrameLayout</a></code> 容器中,而不是在 Activity 布局中通过 <code>&lt;fragment&gt;</code> 元素进行定义,因此该 Activity 可以移除和替换这个 Fragment 。</p>
+
+
+
+    <h2 id="Replace">用一个 Fragment 替换另一个 Fragment </h2>
+
+    <p>替换 Fragment 的步骤与添加 Fragment 的步骤相似,但需要调用 <code><a href="{@docRoot}reference/android/support/v4/app/FragmentTransaction.html#replace(int, android.support.v4.app.Fragment)">replace()</a></code> 方法,而非 <code><a href="{@docRoot}reference/android/support/v4/app/FragmentTransaction.html#add(android.support.v4.app.Fragment, java.lang.String)">add()</a></code>。</p>
+
+    <p>请注意,当你执行替换或移除 Fragment 等 Fragment 事务时,最好能让用户向后导航和“撤消”所做更改。要通过 Fragment 事务允许用户向后导航,你必须调用 <code><a href="{@docRoot}reference/android/support/v4/app/FragmentTransaction.html#addToBackStack(java.lang.String)">addToBackStack()</a></code>,然后再执行 <code><a href="{@docRoot}reference/android/support/v4/app/FragmentTransaction.html">FragmentTransaction</a></code>。</p>
+
+    <p class="note"><strong>注意</strong>:当你移除或替换 Fragment 并向返回堆栈添加事务时,已移除的 Fragment 会停止(而不是销毁)。如果用户向后导航,还原该 Fragment,它会重新启动。如果你没有向返回堆栈添加事务,那么该 Fragment 在移除或替换时就会被销毁<em></em>。</p>
+
+    <p>替换 Fragment 的示例:</p>
+
+    <pre>
+    // 创建 Fragment 并为其添加一个参数,用来指定应显示的文章
+    ArticleFragment newFragment = new ArticleFragment();
+    Bundle args = new Bundle();
+    args.putInt(ArticleFragment.ARG_POSITION, position);
+    newFragment.setArguments(args);
+
+    FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+
+    // 将 fragment_container <code>View</code> 中的内容替换为此 Fragment ,
+    // 然后将该事务添加到返回堆栈,以便用户可以向后导航
+    transaction.replace(R.id.fragment_container, newFragment);
+    transaction.addToBackStack(null);
+
+    // 执行事务
+    transaction.commit();
+    </pre>
+
+    <p><code><a href="{@docRoot}reference/android/support/v4/app/FragmentTransaction.html#addToBackStack(java.lang.String)">addToBackStack()</a></code>  方法可接受可选的字符串参数,来为事务指定独一无二的名称。除非你打算使用 <code><a href="{@docRoot}reference/android/support/v4/app/FragmentManager.BackStackEntry.html">FragmentManager.BackStackEntry</a></code> API 执行高级 Fragment 操作,否则无需使用此名称。</p>
+
diff --git a/docs/html-intl/intl/zh-cn/training/monitoring-device-state/index.jd b/docs/html-intl/intl/zh-cn/training/monitoring-device-state/index.jd
index aa10753..308ad7b 100644
--- a/docs/html-intl/intl/zh-cn/training/monitoring-device-state/index.jd
+++ b/docs/html-intl/intl/zh-cn/training/monitoring-device-state/index.jd
@@ -1,5 +1,5 @@
 page.title=优化电池使用时间
-
+page.type=培训
 trainingnavtop=true
 startpage=true
 next.title=监控电池电量和充电状态
diff --git a/docs/html-intl/intl/zh-cn/training/multiscreen/index.jd b/docs/html-intl/intl/zh-cn/training/multiscreen/index.jd
index 3514bd6..02c687a 100644
--- a/docs/html-intl/intl/zh-cn/training/multiscreen/index.jd
+++ b/docs/html-intl/intl/zh-cn/training/multiscreen/index.jd
@@ -1,5 +1,5 @@
 page.title=针对多种屏幕进行设计
-
+page.type=培训
 trainingnavtop=true
 startpage=true
 next.title=支持各种屏幕尺寸
diff --git a/docs/html/design/design_toc.cs b/docs/html/design/design_toc.cs
index 52c7c52..416a93d 100644
--- a/docs/html/design/design_toc.cs
+++ b/docs/html/design/design_toc.cs
@@ -51,7 +51,7 @@
       <li><a href="<?cs var:toroot ?>design/style/color.html">Color</a></li>
       <li><a href="<?cs var:toroot ?>design/style/iconography.html">Iconography</a></li>
       <li><a href="<?cs var:toroot ?>design/style/branding.html">Your Branding</a></li>
-      <li><a href="<?cs var:toroot ?>design/style/writing.html">Writing Style</a></li>
+      <li><a href="<?cs var:toroot ?>design/style/writing.html" zh-cn-lang="写作风格">Writing Style</a></li>
     </ul>
   </li>
 
diff --git a/docs/html/distribute/essentials/essentials_toc.cs b/docs/html/distribute/essentials/essentials_toc.cs
index 985809a..0369d4d 100644
--- a/docs/html/distribute/essentials/essentials_toc.cs
+++ b/docs/html/distribute/essentials/essentials_toc.cs
@@ -1,11 +1,11 @@
 <ul id="nav">
   <li class="nav-section">
-    <div class="nav-section empty" style="font-weight:normal"><a href="<?cs var:toroot?>distribute/essentials/quality/core.html">
+    <div class="nav-section empty" style="font-weight:normal"><a href="<?cs var:toroot?>distribute/essentials/quality/core.html" zh-cn-lang="应用的核心质量">
             <span class="en">Core App Quality</span></a>
     </div>
   </li>
   <li class="nav-section">
-    <div class="nav-section empty" style="font-weight:normal"><a href="<?cs var:toroot?>distribute/essentials/quality/tablets.html">
+    <div class="nav-section empty" style="font-weight:normal"><a href="<?cs var:toroot?>distribute/essentials/quality/tablets.html" zh-cn-lang="平板电脑应用的质量">
             <span class="en">Tablet App Quality</span>
           </a>
     </div>
diff --git a/docs/html/distribute/tools/disttools_toc.cs b/docs/html/distribute/tools/disttools_toc.cs
index f4f39f0..758a297 100644
--- a/docs/html/distribute/tools/disttools_toc.cs
+++ b/docs/html/distribute/tools/disttools_toc.cs
@@ -1,32 +1,32 @@
 <ul id="nav">
 
   <li class="nav-section">
-    <div class="nav-section empty" style="font-weight:normal"><a href="<?cs var:toroot?>distribute/tools/launch-checklist.html">
+    <div class="nav-section empty" style="font-weight:normal"><a href="<?cs var:toroot?>distribute/tools/launch-checklist.html" zh-cn-lang="发布检查清单">
             <span class="en">Launch Checklist</span></a>
     </div>
   </li>
   <li class="nav-section">
-    <div class="nav-section empty" style="font-weight:normal"><a href="<?cs var:toroot?>distribute/tools/localization-checklist.html">
+    <div class="nav-section empty" style="font-weight:normal"><a href="<?cs var:toroot?>distribute/tools/localization-checklist.html" zh-cn-lang="本地化检查清单">
             <span class="en">Localization Checklist</span></a>
     </div>
   </li>
   <li class="nav-section">
-    <div class="nav-section empty" style="font-weight:normal"><a href="<?cs var:toroot?>distribute/tools/promote/device-art.html">
+    <div class="nav-section empty" style="font-weight:normal"><a href="<?cs var:toroot?>distribute/tools/promote/device-art.html" zh-cn-lang="Device Art Generator">
             <span class="en">Device Art Generator</span></a>
     </div>
   </li>
   <li class="nav-section">
-    <div class="nav-section empty" style="font-weight:normal"><a href="<?cs var:toroot?>distribute/tools/promote/badges.html">
+    <div class="nav-section empty" style="font-weight:normal"><a href="<?cs var:toroot?>distribute/tools/promote/badges.html" zh-cn-lang="Google Play 徽章生成器">
             <span class="en">Google Play Badges</span></a>
     </div>
   </li>
   <li class="nav-section">
-    <div class="nav-section empty" style="font-weight:normal"><a href="<?cs var:toroot?>distribute/tools/promote/linking.html">
+    <div class="nav-section empty" style="font-weight:normal"><a href="<?cs var:toroot?>distribute/tools/promote/linking.html" zh-cn-lang="链接到您的商品">
             <span class="en">Linking to Your Products</span></a>
     </div>
   </li>
   <li class="nav-section">
-    <div class="nav-section empty" style="font-weight:normal"><a href="<?cs var:toroot?>distribute/tools/promote/brand.html">
+    <div class="nav-section empty" style="font-weight:normal"><a href="<?cs var:toroot?>distribute/tools/promote/brand.html" zh-cn-lang="品牌指南">
             <span class="en">Brand Guidelines</span></a>
     </div>
   </li>
diff --git a/docs/html/google/google_toc.cs b/docs/html/google/google_toc.cs
index 7cce86b..0c48a0a 100644
--- a/docs/html/google/google_toc.cs
+++ b/docs/html/google/google_toc.cs
@@ -99,14 +99,14 @@
 
 
   <li class="nav-section">
-    <div class="nav-section-header"><a href="<?cs var:toroot ?>google/play/billing/index.html">
+    <div class="nav-section-header"><a href="<?cs var:toroot ?>google/play/billing/index.html" zh-cn-lang="应用内结算">
       <span class="en">Google Play In-app Billing</span></a>
     </div>
     <ul>
-      <li><a href="<?cs var:toroot?>google/play/billing/billing_overview.html">
+      <li><a href="<?cs var:toroot?>google/play/billing/billing_overview.html" zh-cn-lang="应用内结算概述">
               <span class="en">Overview</span></a>
       </li>
-      <li class="nav-section"><div class="nav-section-header"><a href="<?cs var:toroot?>google/play/billing/api.html">
+      <li class="nav-section"><div class="nav-section-header"><a href="<?cs var:toroot?>google/play/billing/api.html" zh-cn-lang="应用内结算 API">
               <span class="en">Version 3 API</span></a></div>
               <ul>
               <li><a href="<?cs var:toroot?>google/play/billing/billing_integrate.html">
@@ -129,13 +129,13 @@
       <li><a href="<?cs var:toroot?>google/play/billing/billing_subscriptions.html">
               <span class="en">Subscriptions</span></a>
       </li>
-      <li><a href="<?cs var:toroot?>google/play/billing/billing_best_practices.html">
+      <li><a href="<?cs var:toroot?>google/play/billing/billing_best_practices.html" zh-cn-lang="安全性和设计">
               <span class="en">Security and Design</span></a>
       </li>
-      <li><a href="<?cs var:toroot?>google/play/billing/billing_testing.html">
+      <li><a href="<?cs var:toroot?>google/play/billing/billing_testing.html" zh-cn-lang="测试应用内结算">
               <span class="en">Testing In-app Billing</span></a>
       </li>
-      <li><a href="<?cs var:toroot?>google/play/billing/billing_admin.html">
+      <li><a href="<?cs var:toroot?>google/play/billing/billing_admin.html" zh-cn-lang="管理应用内结算">
               <span class="en">Administering In-app Billing</span></a>
       </li>
       <li><a href="<?cs var:toroot?>google/play/billing/versions.html">
diff --git a/docs/html/google/play-services/setup.jd b/docs/html/google/play-services/setup.jd
index 180730d..d886454 100644
--- a/docs/html/google/play-services/setup.jd
+++ b/docs/html/google/play-services/setup.jd
@@ -60,7 +60,8 @@
       <a href="{@docRoot}sdk/installing/studio-build.html">Building Your Project with
       Gradle</a> for more information about Gradle.</p></li>
   <li>Add a new build rule under <code>dependencies</code> for the latest version of
-<code>play-services</code>. For example:
+      <code>play-services</code>.
+      <p>For example, for mobile modules:</p>
 <pre class="no-pretty-print">
 apply plugin: 'android'
 ...
@@ -70,7 +71,16 @@
     <strong>compile 'com.google.android.gms:play-services:6.1.+'</strong>
 }
 </pre>
-<p>Be sure you update this version number each time Google Play services is updated.</p>
+      <p>For wearable modules:</p>
+<pre class="no-pretty-print">
+apply plugin: 'android'
+...
+
+dependencies {
+    <strong>compile 'com.google.android.gms:play-services-wearable:6.1.+'</strong>
+}
+</pre>
+      <p>Be sure you update this version number each time Google Play services is updated.</p>
   </li>
   <li>Save the changes and click <strong>Sync Project with Gradle Files</strong>
 <img src="{@docRoot}images/tools/sync-project.png" style="vertical-align:bottom;margin:0;height:19px" />
diff --git a/docs/html/guide/topics/resources/string-resource.jd b/docs/html/guide/topics/resources/string-resource.jd
index e2326ec..cbfa82e 100644
--- a/docs/html/guide/topics/resources/string-resource.jd
+++ b/docs/html/guide/topics/resources/string-resource.jd
@@ -237,7 +237,8 @@
 <p>The selection of which string to use is made solely based on grammatical <i>necessity</i>.
 In English, a string for <code>zero</code> will be ignored even if the quantity is 0, because 0
 isn't grammatically different from 2, or any other number except 1 ("zero books", "one book",
-"two books", and so on).
+"two books", and so on). Conversely, in Korean <i>only</i> the <code>other</code> string will
+ever be used.
 
 <p>Don't be misled either by the fact that, say, <code>two</code> sounds like it could only apply to
 the quantity 2: a language may require that 2, 12, 102 (and so on) are all treated like one
@@ -343,7 +344,14 @@
 &lt;?xml version="1.0" encoding="utf-8"?>
 &lt;resources>
     &lt;plurals name="numberOfSongsAvailable">
-        &lt;item quantity="one">One song found.&lt;/item>
+        &lt;!--
+             As a developer, you should always supply "one" and "other"
+             strings. Your translators will know which strings are actually
+             needed for their language. Always include %d in "one" because
+             translators will need to use %d for languages where "one"
+             doesn't mean 1 (as explained above).
+          -->
+        &lt;item quantity="one">%d song found.&lt;/item>
         &lt;item quantity="other">%d songs found.&lt;/item>
     &lt;/plurals>
 &lt;/resources>
@@ -353,7 +361,7 @@
 &lt;?xml version="1.0" encoding="utf-8"?>
 &lt;resources>
     &lt;plurals name="numberOfSongsAvailable">
-        &lt;item quantity="one">Znaleziono jedn&#x0105; piosenk&#x0119;.&lt;/item>
+        &lt;item quantity="one">Znaleziono %d piosenk&#x0119;.&lt;/item>
         &lt;item quantity="few">Znaleziono %d piosenki.&lt;/item>
         &lt;item quantity="other">Znaleziono %d piosenek.&lt;/item>
     &lt;/plurals>
@@ -597,4 +605,4 @@
 // and bold the entire sequence.
 CharSequence text = bold(italic(res.getString(R.string.hello)),
     color(Color.RED, res.getString(R.string.world)));
-</pre>
\ No newline at end of file
+</pre>
diff --git a/docs/html/jd_collections.js b/docs/html/jd_collections.js
index a843350..f483e31 100644
--- a/docs/html/jd_collections.js
+++ b/docs/html/jd_collections.js
@@ -7,6 +7,14 @@
       "sdk/installing/studio.html"
     ]
   },
+  "index/primary/zhcn": {
+    "title": "",
+    "resources": [
+      "intl/zh-cn/resources.html",
+      "intl/zh-cn/distribute/tools/launch-checklist.html",
+      "intl/zh-cn/distribute/tools/localization-checklist.html"
+    ]
+  },
   "distribute/edu/videos/stories": {
     "title": "",
     "resources": [
@@ -72,6 +80,16 @@
       "https://developers.google.com/edu/guidelines"
     ]
   },
+  "distribute/essentials/zhcn": {
+    "resources": [
+      "intl/zh-cn/distribute/essentials/quality/core.html",
+      "intl/zh-cn/distribute/essentials/quality/tablets.html",
+      "distribute/essentials/quality/tv.html",
+      "distribute/essentials/quality/wear.html",
+      "https://developers.google.com/edu/guidelines",
+      "distribute/essentials/optimizing-your-app.html"
+    ]
+  },
   "distribute/users": {
     "title": "",
     "resources": [
@@ -114,6 +132,13 @@
       "distribute/tools/localization-checklist.html"
     ]
   },
+  "distribute/tools/checklists/zhcn": {
+    "title": "",
+    "resources": [
+      "intl/zh-cn/distribute/tools/launch-checklist.html",
+      "intl/zh-cn/distribute/tools/localization-checklist.html"
+    ]
+  },
   "distribute/tools/promote": {
     "resources": [
       "distribute/tools/promote/device-art.html",
@@ -121,6 +146,13 @@
       "distribute/tools/promote/linking.html"
     ]
   },
+  "distribute/tools/promote/zhcn": {
+    "resources": [
+      "intl/zh-cn/distribute/tools/promote/device-art.html",
+      "intl/zh-cn/distribute/tools/promote/badges.html",
+      "intl/zh-cn/distribute/tools/promote/linking.html"
+    ]
+  },
   "distribute/tools/support": {
     "title": "Google Play",
     "resources": [
@@ -129,6 +161,14 @@
       "support.html"
     ]
   },
+  "distribute/tools/support/zhcn": {
+    "title": "Google Play",
+    "resources": [
+      "https://support.google.com/googleplay/android-developer?hl=zh-Hans",
+      "https://support.google.com/googleplay/android-developer/answer/4430948?hl=zh-Hans",
+      "support.html"
+    ]
+  },
   "distribute/tools/news": {
     "title": "",
     "resources": [
@@ -144,6 +184,14 @@
       "about/dashboards/index.html"
     ]
   },
+  "distribute/tools/more/zhcn": {
+    "title": "Google Play",
+    "resources": [
+      "intl/zh-cn/distribute/tools/promote/brand.html",
+      "distribute/tools/open-distribution.html",
+      "about/dashboards/index.html"
+    ]
+  },
   "distribute/googleplay": {
     "title": "Google Play",
     "resources": [
@@ -367,11 +415,22 @@
     "title": "",
     "resources": [
       "distribute/tools/launch-checklist.html",
-      "http://play.google.com/about/developer-content-policy.html",
-      "https://support.google.com/googleplay/android-developer/answer/188189",
-      "https://support.google.com/googleplay/android-developer/answer/1078870",
+      "http://play.google.com/about/developer-content-policy.html?hl=zh-Hans",
+      "https://support.google.com/googleplay/android-developer/answer/188189?hl=zh-Hans",
+      "https://support.google.com/googleplay/android-developer/answer/1078870?hl=zh-Hans",
       "http://android-developers.blogspot.com/2011/10/android-market-featured-image.html",
-      "https://support.google.com/googleplay/android-developer/answer/113477"
+      "https://support.google.com/googleplay/android-developer/answer/113477?hl=zh-Hans"
+    ]
+  },
+  "distribute/essentials/core/play/zhcn": {
+    "title": "",
+    "resources": [
+      "intl/zh-cn/distribute/tools/launch-checklist.html",
+      "http://play.google.com/about/developer-content-policy.html",
+      "https://support.google.com/googleplay/android-developer/answer/188189?hl=zh-Hans",
+      "https://support.google.com/googleplay/android-developer/answer/1078870?hl=zh-Hans",
+      "http://android-developers.blogspot.com/2011/10/android-market-featured-image.html",
+      "https://support.google.com/googleplay/android-developer/answer/113477?hl=zh-Hans"
     ]
   },
   "distribute/essentials/tabletguidelines/optimize": {
@@ -448,6 +507,15 @@
       "distribute/tools/promote/device-art.html"
     ]
   },
+  "distribute/essentials/tabletguidelines/showcase/zhcn": {
+    "title": "",
+    "resources": [
+      "intl/zh-cn/distribute/tools/launch-checklist.html",
+      "https://play.google.com/apps/publish/?hl=zh-Hans",
+      "intl/zh-cn/distribute/tools/promote/badges.html",
+      "intl/zh-cn/distribute/tools/promote/device-art.html"
+    ]
+  },
   "distribute/essentials/tabletguidelines/googleplay": {
     "title": "",
     "resources": [
@@ -583,6 +651,12 @@
       "https://support.google.com/googleplay/android-developer/answer/138294"
     ]
   },
+  "distribute/toolsreference/localizationchecklist/identifylocales/zhcn": {
+    "title": "",
+    "resources": [
+      "https://support.google.com/googleplay/android-developer/answer/138294?hl=zh-Hans"
+    ]
+  },
   "distribute/tools/loc/designforloc": {
     "title": "",
     "resources": [
@@ -600,6 +674,14 @@
       "http://en.wikipedia.org/wiki/XLIFF"
     ]
   },
+  "distribute/toolsreference/localizationchecklist/managestrings/zhcn": {
+    "title": "",
+    "resources": [
+      "guide/topics/resources/string-resource.html",
+      "intl/zh-cn/design/style/writing.html",
+      "http://en.wikipedia.org/wiki/XLIFF"
+    ]
+  },
   "distribute/toolsreference/localizationchecklist/translatestrings": {
     "title": "",
     "resources": [
@@ -613,12 +695,25 @@
       "distribute/tools/promote/device-art.html"
     ]
   },
+  "distribute/toolsreference/localizationchecklist/preplaunch/zhcn": {
+    "title": "",
+    "resources": [
+      "intl/zh-cn/distribute/tools/promote/badges.html",
+      "intl/zh-cn/distribute/tools/promote/device-art.html"
+    ]
+  },
   "distribute/toolsreference/localizationchecklist/supportlaunch": {
     "title": "",
     "resources": [
       "distribute/tools/launch-checklist.html",
     ]
   },
+  "distribute/toolsreference/localizationchecklist/supportlaunch/zhcn": {
+    "title": "",
+    "resources": [
+      "intl/zh-cn/distribute/tools/launch-checklist.html",
+    ]
+  },
   "distribute/toolsreference/launchchecklist/understanding": {
     "title": "",
     "resources": [
@@ -736,6 +831,119 @@
       "distribute/essentials/optimizing-your-app.html"
     ]
   },
+
+
+
+  "distribute/toolsreference/launchchecklist/understanding/zhcn": {
+    "title": "",
+    "resources": [
+      "intl/zh-cn/tools/publishing/publishing_overview.html",
+      "intl/zh-cn/tools/publishing/preparing.html"
+    ]
+  },
+  "distribute/toolsreference/launchchecklist/policies/zhcn": {
+    "title": "",
+    "resources": [
+      "https://support.google.com/googleplay/android-developer/answer/4430948?hl=zh-Hans",
+      "https://support.google.com/googleplay/android-developer/topic/2364761?hl=zh-Hans",
+      "https://support.google.com/googleplay/android-developer?hl=zh-Hans"
+    ]
+  },
+  "distribute/toolsreference/launchchecklist/quality/zhcn": {
+    "title": "",
+    "resources": [
+      "intl/zh-cn/distribute/essentials/quality/core.html",
+      "intl/zh-cn/distribute/essentials/quality/tablets.html",
+      "https://developers.google.com/edu/guidelines?hl=zh-Hans"
+    ]
+  },
+
+  "distribute/toolsreference/launchchecklist/rating/zhcn": {
+    "title": "",
+    "resources": [
+      "https://support.google.com/googleplay/android-developer/answer/188189?hl=zh-Hans",
+    ]
+  },
+  "distribute/toolsreference/launchchecklist/country/zhcn": {
+    "title": "",
+    "resources": [
+      "https://support.google.com/googleplay/android-developer/answer/138294?hl=zh-Hans"
+    ]
+  },
+  "distribute/toolsreference/launchchecklist/size/zhcn": {
+    "title": "",
+    "resources": [
+      "google/play/expansion-files.html",
+      "intl/zh-cn/tools/help/proguard.html"
+    ]
+  },
+  "distribute/toolsreference/launchchecklist/price/zhcn": {
+    "title": "",
+    "resources": [
+      "https://support.google.com/googleplay/android-developer/table/3541286?hl=zh-Hans",
+    ]
+  },
+  "distribute/toolsreference/launchchecklist/purchasemethod/zhcn": {
+    "title": "",
+    "resources": [
+      "intl/zh-cn/google/play/billing/index.html",
+      "google/play/billing/billing_subscriptions.html"
+    ]
+  },
+  "distribute/toolsreference/launchchecklist/setprice/zhcn": {
+    "title": "",
+    "resources": [
+      "https://support.google.com/googleplay/android-developer/answer/1169947?hl=zh-Hans",
+      "https://support.google.com/googleplay/android-developer/answer/138412?hl=zh-Hans",
+      "https://support.google.com/googleplay/android-developer/answer/112622?hl=zh-Hans",
+      "https://support.google.com/googleplay/android-developer/answer/138000?hl=zh-Hans"
+    ]
+  },
+  "distribute/toolsreference/launchchecklist/localization/zhcn": {
+    "title": "",
+    "resources": [
+      "intl/zh-cn/distribute/tools/localization-checklist.html",
+      "guide/topics/resources/localization.html"
+    ]
+  },
+  "distribute/toolsreference/launchchecklist/graphics/zhcn": {
+    "title": "",
+    "resources": [
+      "https://support.google.com/googleplay/android-developer/answer/1078870?hl=zh-Hans",
+      "http://android-developers.blogspot.com/2011/10/android-market-featured-image.html"
+    ]
+  },
+  "distribute/toolsreference/launchchecklist/productdetails/zhcn": {
+    "title": "",
+    "resources": [
+      "https://support.google.com/googleplay/android-developer/answer/113475?hl=zh-Hans",
+      "https://support.google.com/googleplay/android-developer/answer/1078870?hl=zh-Hans"
+    ]
+  },
+  "distribute/toolsreference/launchchecklist/badges/zhcn": {
+    "title": "",
+    "resources": [
+      "intl/zh-cn/distribute/tools/promote/badges.html",
+      "intl/zh-cn/distribute/tools/promote/linking.html"
+    ]
+  },
+  "distribute/toolsreference/launchchecklist/finalchecks/zhcn": {
+    "title": "",
+    "resources": [
+      "http://play.google.com/about/developer-content-policy.html",
+      "https://support.google.com/googleplay/android-developer/answer/113476?hl=zh-Hans",
+      "support.html"
+    ]
+  },
+  "distribute/toolsreference/launchchecklist/afterlaunch/zhcn": {
+    "title": "",
+    "resources": [
+      "https://support.google.com/googleplay/android-developer/answer/113477?hl=zh-Hans",
+      "https://support.google.com/googleplay/android-developer/answer/1153479?hl=zh-Hans",
+      "https://support.google.com/payments/answer/2741495?hl=zh-Hans",
+      "distribute/essentials/optimizing-your-app.html"
+    ]
+  },
   "distribute/monetize/premium": {
     "title": "",
     "resources": [
@@ -816,5 +1024,92 @@
       "http://storage.googleapis.com/androiddevelopers/shareables/stories/TinyRebel_DoctorWhoLegacy_gpgs.pdf",
       "http://storage.googleapis.com/androiddevelopers/shareables/stories/Senri_LeosFortune_gpgs.pdf"
     ]
+  },
+  "overview/zhcn/1": {
+    "title": "",
+    "resources": [
+      "intl/zh-cn/distribute/essentials/quality/core.html",
+      "intl/zh-cn/distribute/essentials/quality/tablets.html",
+      "intl/zh-cn/distribute/tools/launch-checklist.html",
+      "intl/zh-cn/tools/publishing/publishing_overview.html",
+      "intl/zh-cn/distribute/tools/localization-checklist.html"
+    ]
+  },
+    "overview/zhcn/2": {
+    "title": "",
+    "resources": [
+      "intl/zh-cn/google/play/billing/index.html",
+      "intl/zh-cn/google/play/billing/api.html",
+      "intl/zh-cn/google/play/billing/billing_admin.html",
+      "intl/zh-cn/google/play/billing/billing_testing.html",
+      "intl/zh-cn/google/play/billing/billing_best_practices.html"
+    ]
+  },
+  "overview/zhcn/3": {
+    "title": "",
+    "resources": [
+      "intl/zh-cn/distribute/tools/promote/badges.html",
+
+      "intl/zh-cn/distribute/tools/promote/device-art.html",
+      "intl/zh-cn/distribute/tools/promote/linking.html",
+      "intl/zh-cn/distribute/tools/promote/brand.html",
+      "intl/zh-cn/tools/help/proguard.html"
+    ]
+  },
+  "overview/zhcn/4": {
+    "title": "",
+    "resources": [
+      "intl/zh-cn/design/style/writing.html",
+      "intl/zh-cn/training/basics/fragments/fragment-ui.html",
+      "intl/zh-cn/training/multiscreen/index.html",
+      "intl/zh-cn/training/monitoring-device-state/index.html"
+    ]
+  },
+  "overview/carousel/zhcn": {
+    "title": "",
+    "resources": [
+      "http://www.youtube.com/watch?v=vGV7FHGzpFU",
+      "http://www.youtube.com/watch?v=aqc3ZOTzpdk",
+      "http://www.youtube.com/watch?v=jaNrJ8uyLSc"
+    ]
+  },
+  "overview/1": {
+    "title": "",
+    "resources": [
+      "distribute/essentials/quality/core.html",
+      "distribute/essentials/quality/tablets.html",
+      "distribute/tools/launch-checklist.html",
+      "tools/publishing/publishing_overview.html",
+      "distribute/tools/localization-checklist.html"
+    ]
+  },
+    "overview/2": {
+    "title": "",
+    "resources": [
+      "google/play/billing/index.html",
+      "google/play/billing/api.html",
+      "google/play/billing/billing_admin.html",
+      "google/play/billing/billing_testing.html",
+      "google/play/billing/billing_best_practices.html"
+    ]
+  },
+  "overview/3": {
+    "title": "",
+    "resources": [
+      "distribute/tools/promote/badges.html",
+      "distribute/tools/promote/device-art.html",
+      "distribute/tools/promote/linking.html",
+      "distribute/tools/promote/brand.html",
+      "tools/help/proguard.html"
+    ]
+  },
+  "overview/4": {
+    "title": "",
+    "resources": [
+      "design/style/writing.html",
+      "training/basics/fragments/fragment-ui.html",
+      "training/multiscreen/index.html",
+      "training/monitoring-device-state/index.html"
+    ]
   }
-}
+}
\ No newline at end of file
diff --git a/docs/html/jd_extras.js b/docs/html/jd_extras.js
index a86ffeb..05d0f09 100644
--- a/docs/html/jd_extras.js
+++ b/docs/html/jd_extras.js
@@ -121,6 +121,18 @@
     "type":"google"
   },
   {
+    "title": "支持向Google Play用户发布应用的地区",
+    "lang": "zh-cn",
+    "titleFriendly":"",
+    "summary": "支持向Google Play用户发布应用的国家/地区。",
+    "url":"https://support.google.com/googleplay/android-developer/answer/138294?hl=zh-Hans",
+    "group":"",
+    "keywords": [],
+    "tags": [],
+    "image":"images/play_dev.jpg",
+    "type":"google"
+  },
+  {
     "title":"Google Play Content Policies",
     "titleFriendly":"",
     "summary":"Details on policies relating to your developer account and app distribution is governed.",
@@ -156,6 +168,17 @@
     "type":"google"
   },
   {
+    "title":"Google Play应用政策中心",
+    "titleFriendly":"",
+    "summary":"一个方便你了解Google Play政策和指南的中心资源。",
+    "url":"https://support.google.com/googleplay/android-developer/answer/4430948?hl=zh-Hans",
+    "group":"",
+    "keywords": [],
+    "tags": [],
+    "image":"http://storage.googleapis.com/support-kms-prod/SNP_712EA2784949DDF085C46E3BE7B1DC618A09_4389397_en_v0",
+    "type":"google"
+  },
+  {
     "title":"Developer Help Center",
     "titleFriendly":"",
     "summary":"Complete details on getting started, publishing, troubleshooting, and more.",
@@ -167,6 +190,17 @@
     "type":"google"
   },
   {
+    "title":"开发者帮助中心",
+    "titleFriendly":"",
+    "summary":"完整资料帮助开发者新手入手,发布,故障排除,等等",
+    "url":"https://support.google.com/googleplay/android-developer?hl=zh-Hans",
+    "group":"",
+    "keywords": [],
+    "tags": [],
+    "image":"images/play_dev.jpg",
+    "type":"google"
+  },
+  {
     "title":"Google for Education",
     "titleFriendly":"",
     "summary":"Find out more about how Google can support your work with apps and tablets.",
@@ -215,6 +249,7 @@
     "type": "blog",
     "titleFriendly": ""
   },
+
   {
     "lang": "en",
     "group": "",
@@ -268,6 +303,19 @@
     "titleFriendly": ""
   },
   {
+    "lang": "zh-cn",
+    "group": "",
+    "tags": [],
+    "url": "https://support.google.com/googleplay/android-developer/answer/188189?hl=zh-Hans",
+    "timestamp": 1194884220000,
+    "image": "images/play_dev.jpg",
+    "title": "针对Google Play为你的应用内容分级",
+    "summary": "如何为你的应用内容分级。",
+    "keywords": [],
+    "type": "support",
+    "titleFriendly": ""
+  },
+  {
     "lang": "en",
     "group": "",
     "tags": [],
@@ -293,6 +341,19 @@
     "type": "support",
     "titleFriendly": ""
   },
+{
+    "lang": "zh-cn",
+    "group": "",
+    "tags": [],
+    "url": "https://support.google.com/googleplay/android-developer/answer/113477?hl=zh-Hans",
+    "timestamp": 1194884220000,
+    "image": "images/play_dev.jpg",
+    "title": "为用户提供支持",
+    "summary": "为用户提供支持的各种选择。",
+    "keywords": [],
+    "type": "support",
+    "titleFriendly": ""
+  },
   {
     "lang": "en",
     "group": "",
@@ -424,6 +485,19 @@
     "titleFriendly": ""
   },
   {
+    "lang": "zh-cn",
+    "group": "",
+    "tags": [],
+    "url": "https://play.google.com/apps/publish/?hl=zh-Hans",
+    "timestamp": 1194884220000,
+    "image": null,
+    "title": "Google Play 开发者控制台",
+    "summary": "发布应用的开发者控制台",
+    "keywords": [],
+    "type": "guide",
+    "titleFriendly": ""
+  },
+  {
     "lang": "en",
     "group": "",
     "tags": [],
@@ -619,6 +693,19 @@
     "titleFriendly": ""
   },
   {
+    "lang": "zh-cn",
+    "group": "",
+    "tags": [],
+    "url": "https://support.google.com/googleplay/android-developer/topic/2364761?hl=zh-Hans",
+    "timestamp": 1194884220000,
+    "image": "images/play_dev.jpg",
+    "title": "政策和最佳做法",
+    "summary": "内容政策和流程",
+    "keywords": [],
+    "type": "distribute",
+    "titleFriendly": ""
+  },
+  {
     "lang": "en",
     "group": "",
     "tags": [],
@@ -686,6 +773,19 @@
     "lang": "en",
     "group": "",
     "tags": [],
+    "url": "https://support.google.com/googleplay/android-developer/answer/1169947?hl=zh-Hans",
+    "timestamp": 1194884220000,
+    "image": "images/play_dev.jpg",
+    "title": "以多种货币销售应用",
+    "summary": "如何在Google Play为应用定价",
+    "keywords": [],
+    "type": "guide",
+    "titleFriendly": ""
+  },
+  {
+    "lang": "en",
+    "group": "",
+    "tags": [],
     "url": "https://support.google.com/googleplay/android-developer/answer/138412",
     "timestamp": 1194884220000,
     "image": "images/play_dev.jpg",
@@ -695,6 +795,19 @@
     "type": "guide",
     "titleFriendly": ""
   },
+    {
+    "lang": "zh-cn",
+    "group": "",
+    "tags": [],
+    "url": "https://support.google.com/googleplay/android-developer/answer/138412?hl=zh-Hans",
+    "timestamp": 1194884220000,
+    "image": "images/play_dev.jpg",
+    "title": "各国家/地区获许定价范围和货币",
+    "summary": "各国家/地区获许定价范围和货币列表",
+    "keywords": [],
+    "type": "guide",
+    "titleFriendly": ""
+  },
   {
     "lang": "en",
     "group": "",
@@ -709,6 +822,19 @@
     "titleFriendly": ""
   },
   {
+    "lang": "zh-cn",
+    "group": "",
+    "tags": [],
+    "url": "https://support.google.com/googleplay/android-developer/answer/112622?hl=zh-Hans",
+    "timestamp": 1194884220000,
+    "image": "images/play_dev.jpg",
+    "title": "交易费用",
+    "summary": "销售的应用和应用内产品的交易费。",
+    "keywords": [],
+    "type": "guide",
+    "titleFriendly": ""
+  },
+  {
     "lang": "en",
     "group": "",
     "tags": [],
@@ -722,6 +848,19 @@
     "titleFriendly": ""
   },
   {
+    "lang": "zh-cn",
+    "group": "",
+    "tags": [],
+    "url": "https://support.google.com/googleplay/android-developer/answer/138000?hl=zh-Hans",
+    "timestamp": 1194884220000,
+    "image": "images/play_dev.jpg",
+    "title": "税率",
+    "summary": "如何设置不同国家/地区的税率",
+    "keywords": [],
+    "type": "guide",
+    "titleFriendly": ""
+  },
+  {
     "lang": "en",
     "group": "",
     "tags": [],
@@ -748,6 +887,19 @@
     "titleFriendly": ""
   },
   {
+    "lang": "zh-cn",
+    "group": "",
+    "tags": [],
+    "url": "https://support.google.com/googleplay/android-developer/answer/113475?hl=zh-Hans",
+    "timestamp": 1194884220000,
+    "image": "images/play_dev.jpg",
+    "title": "类别",
+    "summary": "应用的类别列表。",
+    "keywords": [],
+    "type": "guide",
+    "titleFriendly": ""
+  },
+  {
     "lang": "en",
     "group": "",
     "tags": [],
@@ -761,6 +913,19 @@
     "titleFriendly": ""
   },
   {
+    "lang": "zh-cn",
+    "group": "",
+    "tags": [],
+    "url": "https://support.google.com/googleplay/android-developer/answer/113476?hl=zh-Hans",
+    "timestamp": 1194884220000,
+    "image": "images/play_dev.jpg",
+    "title": "更新应用",
+    "summary": "更新Google Play应用的要求。",
+    "keywords": [],
+    "type": "guide",
+    "titleFriendly": ""
+  },
+  {
     "lang": "en",
     "group": "",
     "tags": [],
@@ -774,6 +939,19 @@
     "titleFriendly": ""
   },
   {
+    "lang": "zh-cn",
+    "group": "",
+    "tags": [],
+    "url": "https://support.google.com/googleplay/android-developer/answer/1153479?hl=zh-Hans",
+    "timestamp": 1194884220000,
+    "image": null,
+    "title": "应用内结算",
+    "summary": "如何正确设置应用内商品和订阅结算。",
+    "keywords": [],
+    "type": "guide",
+    "titleFriendly": ""
+  },
+  {
     "lang": "en",
     "group": "",
     "tags": [
@@ -1004,7 +1182,7 @@
     "type": "guide",
     "titleFriendly": ""
   },
-    {
+  {
     "lang": "en",
     "group": "",
     "tags": [],
@@ -1018,6 +1196,19 @@
     "titleFriendly": ""
   },
   {
+    "lang": "zh-cn",
+    "group": "",
+    "tags": [],
+    "url": "https://support.google.com/googleplay/android-developer/answer/1078870?hl=zh-Hans",
+    "timestamp": 1194884220000,
+    "image": "images/play_dev.jpg",
+    "title": "为你的应用的图片资源",
+    "summary": "如何在你的应用的商品详情页面上添加图片资源。",
+    "keywords": [],
+    "type": "guide",
+    "titleFriendly": ""
+  },
+  {
     "lang": "en",
     "group": "",
     "tags": [],
@@ -1031,6 +1222,19 @@
     "titleFriendly": ""
   },
   {
+    "lang": "zh-cn",
+    "group": "",
+    "tags": [],
+    "url": "https://support.google.com/payments/answer/2741495?hl=zh-Hans",
+    "timestamp": null,
+    "image": null,
+    "title": "退回訂單款項",
+    "summary": "如何退还已收取的订单款项。",
+    "keywords": [],
+    "type": "guide",
+    "titleFriendly": ""
+  },
+  {
     "lang": "en",
     "group": "",
     "tags": [],
@@ -1057,6 +1261,19 @@
     "titleFriendly": ""
   },
   {
+    "lang": "zh-cn",
+    "group": "",
+    "tags": ["localization", "pricing", "developer support"],
+    "url": "https://support.google.com/googleplay/android-developer/table/3541286?hl=zh-Hans",
+    "timestamp": null,
+    "image": "images/play_dev.jpg",
+    "title": "支持向Google Play用户发布应用的地区",
+    "summary": "支持向Google Play用户发布应用的国家/地区。",
+    "keywords": [],
+    "type": "distribute",
+    "titleFriendly": ""
+  },
+  {
     "lang": "en",
     "group": "",
     "tags": ["games", "localization", "quality"],
@@ -1227,6 +1444,19 @@
     "titleFriendly": ""
   },
   {
+    "lang": "zh-cn",
+    "group": "",
+    "tags": [],
+    "url": "https://developers.google.com/edu/guidelines?hl=zh-Hans",
+    "timestamp": null,
+    "image": "http://developer.android.com/distribute/images/edu-guidelines.jpg",
+    "title": "Education Guidelines",
+    "summary": "These guidelines and requirements help you develop great apps for students, which offer compelling content and an intuitive user experience on Android tablets.",
+    "keywords": [],
+    "type": "",
+    "titleFriendly": ""
+  },
+  {
     "lang": "en",
     "group": "",
     "tags": [],
diff --git a/docs/html/resources.jd b/docs/html/resources.jd
new file mode 100644
index 0000000..f1d342d
--- /dev/null
+++ b/docs/html/resources.jd
@@ -0,0 +1,52 @@
+page.title=Resources
+page.viewport_width=970
+page.type=guide
+section.landing=true
+header.hide=1
+nonavpage=true
+page.metaDescription=These resources will help you get started with Android and Google Play. 。
+
+@jd:body
+
+    <div class="jd-descr" itemprop="articleBody">
+
+<div class="dynamic-grid">
+
+<h2>Resources</h2>
+<p style="margin-bottom:2em;">These resources will help you get started with Android and Google Play.</p>
+
+<h3 style="font-size:18px;font-weight:bold">Checklists</h3>
+
+  <div class="resource-widget resource-flow-layout col-16"
+    data-query="collection:overview/1"
+    data-sortOrder=""
+    data-cardSizes="6x6,6x6,6x2x3"
+    data-maxResults="5"></div>
+
+<h3 style="font-size:18px;font-weight:bold">Google Play In-app Billing</h3>
+
+  <div class="resource-widget resource-flow-layout col-16"
+    data-query="collection:overview/2"
+    data-sortOrder=""
+    data-cardSizes="6x6,6x6,6x2x3"
+    data-maxResults="5"></div>
+
+<h3 style="font-size:18px;font-weight:bold">Tools</h3>
+
+  <div class="resource-widget resource-flow-layout col-16"
+    data-query="collection:overview/3"
+    data-sortOrder=""
+    data-cardSizes="6x2x3,6x6,6x6"
+    data-maxResults="5"></div>
+
+<h3 style="font-size:18px;font-weight:bold">Training</h3>
+
+  <div class="resource-widget resource-flow-layout col-16"
+    data-query="collection:overview/4"
+    data-sortOrder=""
+    data-cardSizes="6x6,12x2x3"
+    data-maxResults="5"></div>
+
+
+</div>
+
diff --git a/docs/html/tools/tools_toc.cs b/docs/html/tools/tools_toc.cs
index ac33185..7f93eed 100644
--- a/docs/html/tools/tools_toc.cs
+++ b/docs/html/tools/tools_toc.cs
@@ -143,9 +143,9 @@
     </ul>
   </li>
       <li class="nav-section">
-        <div class="nav-section-header"><a href="<?cs var:toroot ?>tools/publishing/publishing_overview.html"><span class="en">Publishing</span></a></div>
+        <div class="nav-section-header"><a href="<?cs var:toroot ?>tools/publishing/publishing_overview.html" zh-cn-lang="发布概述"><span class="en">Publishing</span></a></div>
         <ul>
-          <li><a href="<?cs var:toroot ?>tools/publishing/preparing.html"><span class="en">Preparing for Release</span></a></li>
+          <li><a href="<?cs var:toroot ?>tools/publishing/preparing.html" zh-cn-lang="准备发布"><span class="en">Preparing for Release</span></a></li>
           <li><a href="<?cs var:toroot ?>tools/publishing/versioning.html"><span class="en">Versioning Your Apps</span></a></li>
           <li><a href="<?cs var:toroot ?>tools/publishing/app-signing.html"><span class="en">Signing Your Apps</span></a></li>
         </ul>
@@ -201,7 +201,7 @@
 class="en">MonkeyRunner</span></a></li>
         </ul>
       </li>
-       <li><a href="<?cs var:toroot ?>tools/help/proguard.html">ProGuard</a></li>
+       <li><a href="<?cs var:toroot ?>tools/help/proguard.html" zh-cn-lang="ProGuard">ProGuard</a></li>
        <li><a href="<?cs var:toroot ?>tools/help/sdk-manager.html">SDK Manager</a></li>
        <li><a href="<?cs var:toroot ?>tools/help/systrace.html">Systrace</a></li>
        <li><a href="<?cs var:toroot ?>tools/help/gltracer.html">Tracer for OpenGL ES</a></li>
diff --git a/docs/html/training/auto/messaging/index.jd b/docs/html/training/auto/messaging/index.jd
index c51ad7e..2405d83 100644
--- a/docs/html/training/auto/messaging/index.jd
+++ b/docs/html/training/auto/messaging/index.jd
@@ -151,9 +151,9 @@
 </p>
 
 <p>
-  You define the read action and reply action intents types for your app and the {@code
+  You define the read action and reply action intents types for your app and the {@link
   android.content.BroadcastReceiver} classes that handle them in the manifest. The following code
-  example demonstrates how to declare these intents and thier associated receivers.
+  example demonstrates how to declare these intents and their associated receivers.
 </p>
 
 <pre>
@@ -175,7 +175,7 @@
 </pre>
 
 <p>
-  The definition of the {@code android.content.BroadcastReceiver} classes shown in this example
+  The definition of the {@link android.content.BroadcastReceiver} classes shown in this example
   is discussed in <a href="#handle_actions">Handle User Actions</a>.
 </p>
 
@@ -187,7 +187,7 @@
   <a href="{@docRoot}tools/support-library/features.html#v4">v4 support library</a>. Use the
   <a href="{@docRoot}tools/help/sdk-manager.html">Android SDK Manager</a> to update the
   <em>Extras > Android Support Repository</em> to version 9 or higher and the
-  <em>Extras > Android Support Library</em> to version 21.1.0 or higher.
+  <em>Extras > Android Support Library</em> to version 21.0.2 or higher.
 </p>
 
 <p>
@@ -199,7 +199,7 @@
 <pre>
 dependencies {
     ...
-    compile 'com.android.support:support-v4:21.1.+'
+    compile 'com.android.support:support-v4:21.0.+'
 }
 </pre>
 
@@ -244,14 +244,15 @@
 <h3 id="build_conversation">Build message conversations</h4>
 
 <p>
-  Messaging notifications for Auto organize messages into conversations using the {@code
-  NotificationCompat.CarExtender.UnreadConversation} class, that represents an unread or new
+  Messaging notifications for Auto organize messages into conversations using the {@link
+  android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation} class,
+  that represents an unread or new
   portion of a conversation from a particular sender. It contains a list of messages from the
   sender.
 </p>
 
 <p>
-  Use the {@code UnreadConversation.Builder} class to create an unread conversation object,
+  Use the {@link android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation.Builder} class to create an unread conversation object,
   as shown in the following example code:
 </p>
 
@@ -277,11 +278,12 @@
 </p>
 
 <p>
-  If your app supports replying to a conversation, you must call the {@code setReplyAction()}
-  method and provide a pending intent to pass that user action back to your app. The {@code
-  UnreadConversation} object you create must also include a {@link
-  android.support.v4.app.RemoteInput} object. This object is required because the Auto user
-  receiving this conversation speaks a reply, a the remote input objects lets your app get a text
+  If your app supports replying to a conversation, you must call the {@link android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation.Builder#setReplyAction setReplyAction()}
+  method and provide a pending intent to pass that user action back to your app. The
+  {@link android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation}
+  object you create must also include a {@link
+  android.support.v4.app.RemoteInput} object. When the Auto user
+  receiving this conversation speaks a reply, the remote input objects lets your app get a text
   version of the voice reply.
 </p>
 
@@ -289,8 +291,9 @@
 <h4 id="conversation-messages">Associate messages with conversations</h4>
 
 <p>
-  Messages provided for Auto must be associated with a conversation using the {@code
-  NotificationCompat.CarExtender.UnreadConversation} class. The following code example shows how
+  Messages provided for Auto must be associated with a conversation using the
+  {@link android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation}
+  class. The following code example shows how
   to associate individual messages with a conversation object.
 </p>
 
@@ -327,18 +330,18 @@
 Intent msgHeardIntent = new Intent()
     .addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES)
     .setAction(<em>com.myapp.messagingservice.ACTION_MESSAGE_HEARD</em>)
-    .putExtra("conversation_id", conversationId);
+    .putExtra("conversation_id", <em>conversationId</em>);
 
 PendingIntent msgHeardPendingIntent =
     PendingIntent.getBroadcast(getApplicationContext(),
-        conversationId,
+        <em>conversationId</em>,
         msgHeardIntent,
         PendingIntent.FLAG_UPDATE_CURRENT);
 </pre>
 
 <p>
   In this example, {@code conversationId} is an integer that identifies the current conversation.
-  The value of {@code setAction()} is an intent filter identifier for heard messages which is
+  The value of {@link android.content.Intent#setAction setAction()} is an intent filter identifier for heard messages which is
   defined in your app manifest, as shown in <a href="#manifest-intent">Define read and reply intent
   filters</a>.
 </p>
@@ -365,7 +368,7 @@
 
 <p>
   Once again, {@code conversationId} is an integer that uniquely identifies this conversation. The
-  value of {@code setAction()} is an intent filter identifier for replies which is defined in your
+  value of {@link android.content.Intent#setAction setAction()} is an intent filter identifier for replies which is defined in your
   app manifest, as shown in <a href="#manifest-intent">Define read and reply intent filters</a>.
 </p>
 
@@ -377,8 +380,9 @@
   notification to Auto.
 </p>
 
-<p>First, add the message to the <code>UnreadConversation.Builder</code> for
-this conversation, and update its timestamp:</p>
+<p>First, add the message to the {@link
+android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation.Builder}
+for this conversation, and update its timestamp:</p>
 
 <pre>
 unreadConvBuilder.addMessage(<em>messageString</em>)
@@ -402,10 +406,11 @@
 </pre>
 
 <p>You'll also need to extend the  {@link
-android.support.v4.app.NotificationCompat.Builder} with the
-<code>CarExtender</code>. This is where you actually create the
-<code>UnreadConversation</code> object using the builder you just
-created, and attach it to the <code>CarExtender</code>:</p>
+android.support.v4.app.NotificationCompat.Builder} with the {@link
+android.support.v4.app.NotificationCompat.CarExtender}. This is where you
+actually create the {@link android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation} object using the builder you
+just created, and attach it to the {@link
+android.support.v4.app.NotificationCompat.CarExtender}:</p>
 
 <pre>
 notificationBuilder.extend(new CarExtender()
@@ -416,14 +421,11 @@
 android.support.v4.app.NotificationManagerCompat} to send the notification:</p>
 
 <pre>
-mNotificationManager = NotificationManagerCompat.from(context);
-mNotificationManager.notify(<em>notificationId</em>, notificationBuilder.build());
+NotificationManagerCompat msgNotificationManager =
+    NotificationManagerCompat.from(context);
+msgNotificationManager.notify(<em>notificationId</em>, notificationBuilder.build());
 </pre>
 
-<p>In this example, <em>msgNotificationManager</em> is a
-{@link android.support.v4.app.NotificationManagerCompat} you created for your app.</p>
-
-
 <h2 id="handle_actions">Handle User Actions</h2>
 
 <p>
diff --git a/docs/html/training/auto/start/index.jd b/docs/html/training/auto/start/index.jd
index b955cef..8abe5c5 100644
--- a/docs/html/training/auto/start/index.jd
+++ b/docs/html/training/auto/start/index.jd
@@ -81,7 +81,8 @@
 </li>
 <li><strong><a href="{@docRoot}tools/support-library/setup.html">Install the
 support library</a></strong> - If you are building messaging apps for Auto, you
-need the {@code NotificationCompat.CarExtender} class contained in the
+need the {@link android.support.v4.app.NotificationCompat.CarExtender} class
+contained in the
 <a href="{@docRoot}tools/support-library/features.html#v4">v4 support library</a>.
 This class allows you to create notifications that are compatible with Auto
 devices.</li>
diff --git a/docs/html/training/basics/activity-lifecycle/index.jd b/docs/html/training/basics/activity-lifecycle/index.jd
index 127c1c2..afeab86 100644
--- a/docs/html/training/basics/activity-lifecycle/index.jd
+++ b/docs/html/training/basics/activity-lifecycle/index.jd
@@ -1,9 +1,9 @@
 page.title=Managing the Activity Lifecycle
+page.tags=activity lifecycle
+helpoutsWidget=true
 
 trainingnavtop=true
 startpage=true
-next.title=Launching an Activity
-next.link=starting.html
 
 @jd:body
 
diff --git a/docs/html/training/basics/activity-lifecycle/pausing.jd b/docs/html/training/basics/activity-lifecycle/pausing.jd
index f656fce..de2c92a 100644
--- a/docs/html/training/basics/activity-lifecycle/pausing.jd
+++ b/docs/html/training/basics/activity-lifecycle/pausing.jd
@@ -1,12 +1,8 @@
 page.title=Pausing and Resuming an Activity
-parent.title=Managing the Activity Lifecycle
-parent.link=index.html
+page.tags=activity lifecycle
+helpoutsWidget=true
 
 trainingnavtop=true
-previous.title=Starting an Activity
-previous.link=starting.html
-next.title=Stopping and Restarting an Activity
-next.link=stopping.html
 
 @jd:body
 
diff --git a/docs/html/training/basics/activity-lifecycle/recreating.jd b/docs/html/training/basics/activity-lifecycle/recreating.jd
index 71fd5dd..a52d5fd 100644
--- a/docs/html/training/basics/activity-lifecycle/recreating.jd
+++ b/docs/html/training/basics/activity-lifecycle/recreating.jd
@@ -1,10 +1,8 @@
 page.title=Recreating an Activity
-parent.title=Managing the Activity Lifecycle
-parent.link=index.html
+page.tags=activity lifecycle
+helpoutsWidget=true
 
 trainingnavtop=true
-previous.title=Stopping and Restarting an Activity
-previous.link=stopping.html
 
 @jd:body
 
diff --git a/docs/html/training/basics/activity-lifecycle/starting.jd b/docs/html/training/basics/activity-lifecycle/starting.jd
index 9046599..2501f38 100644
--- a/docs/html/training/basics/activity-lifecycle/starting.jd
+++ b/docs/html/training/basics/activity-lifecycle/starting.jd
@@ -1,10 +1,8 @@
 page.title=Starting an Activity
-parent.title=Managing the Activity Lifecycle
-parent.link=index.html
+page.tags=activity lifecycle
+helpoutsWidget=true
 
 trainingnavtop=true
-next.title=Pausing and Resuming an Activity
-next.link=pausing.html
 
 @jd:body
 
diff --git a/docs/html/training/basics/activity-lifecycle/stopping.jd b/docs/html/training/basics/activity-lifecycle/stopping.jd
index d56c921..51c95ea 100644
--- a/docs/html/training/basics/activity-lifecycle/stopping.jd
+++ b/docs/html/training/basics/activity-lifecycle/stopping.jd
@@ -1,12 +1,8 @@
 page.title=Stopping and Restarting an Activity
-parent.title=Managing the Activity Lifecycle
-parent.link=index.html
+page.tags=activity lifecycle
+helpoutsWidget=true
 
 trainingnavtop=true
-previous.title=Pausing and Resuming an Activity
-previous.link=pausing.html
-next.title=Recreating an Activity
-next.link=recreating.html
 
 @jd:body
 
diff --git a/docs/html/training/basics/fragments/communicating.jd b/docs/html/training/basics/fragments/communicating.jd
index b30045d..8c1ae21a 100644
--- a/docs/html/training/basics/fragments/communicating.jd
+++ b/docs/html/training/basics/fragments/communicating.jd
@@ -1,4 +1,6 @@
 page.title=Communicating with Other Fragments
+page.tags=fragments
+helpoutsWidget=true
 
 trainingnavtop=true
 
diff --git a/docs/html/training/basics/fragments/creating.jd b/docs/html/training/basics/fragments/creating.jd
index 377adfc..7afb149 100644
--- a/docs/html/training/basics/fragments/creating.jd
+++ b/docs/html/training/basics/fragments/creating.jd
@@ -1,4 +1,6 @@
 page.title=Creating a Fragment
+page.tags=fragments
+helpoutsWidget=true
 
 trainingnavtop=true
 
diff --git a/docs/html/training/basics/fragments/fragment-ui.jd b/docs/html/training/basics/fragments/fragment-ui.jd
index 4fa5b70..1061c15 100644
--- a/docs/html/training/basics/fragments/fragment-ui.jd
+++ b/docs/html/training/basics/fragments/fragment-ui.jd
@@ -1,4 +1,6 @@
 page.title=Building a Flexible UI
+page.tags=fragments
+helpoutsWidget=true
 
 trainingnavtop=true
 
diff --git a/docs/html/training/basics/fragments/index.jd b/docs/html/training/basics/fragments/index.jd
index e78b694..aba6459 100644
--- a/docs/html/training/basics/fragments/index.jd
+++ b/docs/html/training/basics/fragments/index.jd
@@ -1,5 +1,6 @@
 page.title=Building a Dynamic UI with Fragments
 page.tags=fragments,user interface,support library
+helpoutsWidget=true
 
 trainingnavtop=true
 startpage=true
diff --git a/docs/html/training/basics/fragments/support-lib.jd b/docs/html/training/basics/fragments/support-lib.jd
index 1d538af..a1d781b 100644
--- a/docs/html/training/basics/fragments/support-lib.jd
+++ b/docs/html/training/basics/fragments/support-lib.jd
@@ -1,5 +1,6 @@
 page.title=Using the Support Library
 page.tags=support library
+helpoutsWidget=true
 
 trainingnavtop=true
 
diff --git a/docs/html/training/basics/intents/filters.jd b/docs/html/training/basics/intents/filters.jd
index 10bf43d..221e31b 100644
--- a/docs/html/training/basics/intents/filters.jd
+++ b/docs/html/training/basics/intents/filters.jd
@@ -1,10 +1,8 @@
 page.title=Allowing Other Apps to Start Your Activity
-parent.title=Interacting with Other Apps
-parent.link=index.html
+page.tags=intents
+helpoutsWidget=true
 
 trainingnavtop=true
-previous.title=Getting a Result from an Activity
-previous.link=result.html
 
 @jd:body
 
diff --git a/docs/html/training/basics/intents/index.jd b/docs/html/training/basics/intents/index.jd
index aa0232a..d584161 100644
--- a/docs/html/training/basics/intents/index.jd
+++ b/docs/html/training/basics/intents/index.jd
@@ -1,5 +1,6 @@
 page.title=Interacting with Other Apps
 page.tags=intents,activity
+helpoutsWidget=true
 
 trainingnavtop=true
 startpage=true
diff --git a/docs/html/training/basics/intents/result.jd b/docs/html/training/basics/intents/result.jd
index 64fbb8b..b521488 100644
--- a/docs/html/training/basics/intents/result.jd
+++ b/docs/html/training/basics/intents/result.jd
@@ -1,12 +1,8 @@
 page.title=Getting a Result from an Activity
-parent.title=Interacting with Other Apps
-parent.link=index.html
+page.tags=intents
+helpoutsWidget=true
 
 trainingnavtop=true
-previous.title=Sending the User to Another App
-previous.link=sending.html
-next.title=Allowing Other Apps to Start Your Activity
-next.link=filters.html
 
 @jd:body
 
diff --git a/docs/html/training/basics/intents/sending.jd b/docs/html/training/basics/intents/sending.jd
index 2a4dae7..4698ba1 100644
--- a/docs/html/training/basics/intents/sending.jd
+++ b/docs/html/training/basics/intents/sending.jd
@@ -1,10 +1,8 @@
 page.title=Sending the User to Another App
-parent.title=Interacting with Other Apps
-parent.link=index.html
+page.tags=intents
+helpoutsWidget=true
 
 trainingnavtop=true
-next.title=Getting a Result from an Activity
-next.link=result.html
 
 @jd:body
 
diff --git a/docs/html/training/basics/supporting-devices/index.jd b/docs/html/training/basics/supporting-devices/index.jd
index 6f339f4..4644c31 100644
--- a/docs/html/training/basics/supporting-devices/index.jd
+++ b/docs/html/training/basics/supporting-devices/index.jd
@@ -1,5 +1,5 @@
 page.title=Supporting Different Devices
-page.tags=resources,screens,versions,localization
+page.tags=layouts,resources,screens,localization
 
 trainingnavtop=true
 startpage=true
diff --git a/docs/html/training/basics/supporting-devices/languages.jd b/docs/html/training/basics/supporting-devices/languages.jd
index aab1e38..098b556 100644
--- a/docs/html/training/basics/supporting-devices/languages.jd
+++ b/docs/html/training/basics/supporting-devices/languages.jd
@@ -1,11 +1,9 @@
 page.title=Supporting Different Languages
 parent.title=Supporting Different Devices
-page.tags=localizing,localization,resources,formats,l10n
-parent.link=index.html
+page.tags=strings,localizing,localization,resources,formats,l10n
+helpoutsWidget=true
 
 trainingnavtop=true
-next.title=Supporting Different Screens
-next.link=screens.html
 
 @jd:body
 
diff --git a/docs/html/training/basics/supporting-devices/platforms.jd b/docs/html/training/basics/supporting-devices/platforms.jd
index 60aaf6a..9890c98 100644
--- a/docs/html/training/basics/supporting-devices/platforms.jd
+++ b/docs/html/training/basics/supporting-devices/platforms.jd
@@ -1,11 +1,9 @@
 page.title=Supporting Different Platform Versions
 page.metaDescription=Training on how to declare support for minimum and target API levels.
-parent.title=Supporting Different Devices
-parent.link=index.html
+page.tags=styles
+helpoutsWidget=true
 
 trainingnavtop=true
-previous.title=Supporting Different Screens
-previous.link=screens.html
 
 @jd:body
 
diff --git a/docs/html/training/basics/supporting-devices/screens.jd b/docs/html/training/basics/supporting-devices/screens.jd
index e52ee70..4b54de8 100644
--- a/docs/html/training/basics/supporting-devices/screens.jd
+++ b/docs/html/training/basics/supporting-devices/screens.jd
@@ -1,12 +1,8 @@
 page.title=Supporting Different Screens
-parent.title=Supporting Different Devices
-parent.link=index.html
+page.tags=layouts
+helpoutsWidget=true
 
 trainingnavtop=true
-previous.title=Supporting Different Languages
-previous.link=languages.html
-next.title=Supporting Different Platform Versions
-next.link=platforms.html
 
 @jd:body
 
diff --git a/docs/html/training/training_toc.cs b/docs/html/training/training_toc.cs
index 61a5e3c..7a0e413 100644
--- a/docs/html/training/training_toc.cs
+++ b/docs/html/training/training_toc.cs
@@ -131,7 +131,7 @@
             Creating a Fragment
           </a>
           </li>
-          <li><a href="<?cs var:toroot ?>training/basics/fragments/fragment-ui.html">
+          <li><a href="<?cs var:toroot ?>training/basics/fragments/fragment-ui.html" zh-cn-lang="构建灵活的界面">
             Building a Flexible UI
           </a>
           </li>
diff --git a/docs/html/training/wearables/data-layer/accessing.jd b/docs/html/training/wearables/data-layer/accessing.jd
index b7ecf5b..0c0a2d5 100644
--- a/docs/html/training/wearables/data-layer/accessing.jd
+++ b/docs/html/training/wearables/data-layer/accessing.jd
@@ -20,7 +20,7 @@
 </div>
 </div>
 
-<p>To call the data layer API, create an instance of
+<p>To call the Data Layer API, create an instance of
 <a href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.html"><code>GoogleApiClient</code></a>,
 the main entry point for any of the Google Play services APIs.
 </p>
@@ -42,7 +42,7 @@
                 &#64;Override
                 public void onConnected(Bundle connectionHint) {
                     Log.d(TAG, "onConnected: " + connectionHint);
-                    // Now you can use the data layer API
+                    // Now you can use the Data Layer API
                 }
                 &#64;Override
                 public void onConnectionSuspended(int cause) {
@@ -71,8 +71,10 @@
 href="{@docRoot}google/auth/api-client.html#WearableApi">Access the Wearable API</a>.</p>
 
 <p>Before you use the data layer API, start a connection on your client by calling the
-<a href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.html#connect()">connect()</a>
+<a href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.html#connect()">
+<code>connect()</code></a>
 method, as described in
-<a href="{@docRoot}google/auth/api-client.html#Starting">Accessing Google Play services APIs</a>.
-When the system invokes the <code>onConnected()</code> callback for your client, you're ready
-to use the data layer API.</p>
+<a href="{@docRoot}google/auth/api-client.html#Starting">Start a Connection</a>.
+When the system invokes the
+<a href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)">
+<code>onConnected()</code></a> callback for your client, you're ready to use the Data Layer API.</p>
diff --git a/docs/html/training/wearables/data-layer/assets.jd b/docs/html/training/wearables/data-layer/assets.jd
index 5dc11cb..719ccbc3 100644
--- a/docs/html/training/wearables/data-layer/assets.jd
+++ b/docs/html/training/wearables/data-layer/assets.jd
@@ -16,18 +16,18 @@
 
 <p>
 To send large blobs of binary data over the Bluetooth transport, such as images, attach an
-<a href="{@docRoot}reference/com/google/android/gms/wearable/Asset.html">Asset</a> to a
+<a href="{@docRoot}reference/com/google/android/gms/wearable/Asset.html"><code>Asset</code></a> to a
 data item and the put the data item into the replicated data store.
 </p>
 
 <p>Assets automatically handle caching of data to prevent retransmission and conserve Bluetooth bandwidth.
 A common pattern is for a handheld app to download an image, shrink it to an appropriate size
-for display on the wearable, and transmit it to the wearable app as an Asset. The following examples
-demonstrates this pattern.
+for display on the wearable, and transmit it to the wearable app as an asset. The following examples
+demonstrate this pattern.
 </p>
 
-<p class="note"><b>Note:</b> Although the size of data items are limited to 100KB,
-assets can be as large as desired. However, transferring large assets affect the
+<p class="note"><b>Note:</b> Although the size of data items is limited to 100KB,
+assets can be as large as desired. However, transferring large assets affects the
 user experience in many cases, so test your apps to ensure that they perform well
 if you're transferring large assets.
 <p>
@@ -49,7 +49,6 @@
 </pre>
 
 <p>When you have an asset, attach it to a data item with the <code>putAsset()</code> method in
-
 <a href="{@docRoot}reference/com/google/android/gms/wearable/DataMap.html"><code>DataMap</code></a>
 or
 <a href="{@docRoot}reference/com/google/android/gms/wearable/PutDataRequest.html"><code>PutDataRequest</code></a>
@@ -77,12 +76,13 @@
         .putDataItem(mGoogleApiClient, request);
 </pre>
 
+
 <h2 id="ReceiveAsset">Receive assets</h2>
 
 <p>
 When an asset is created, you probably want to read and extract
 it on other side of the connection. Here's an example of how to implement the
-callback to detect an asset change and extract the Asset:
+callback to detect an asset change and extract the asset:
 </p>
 
 <pre>
diff --git a/docs/html/training/wearables/data-layer/data-items.jd b/docs/html/training/wearables/data-layer/data-items.jd
index f843bb67..12babbf 100644
--- a/docs/html/training/wearables/data-layer/data-items.jd
+++ b/docs/html/training/wearables/data-layer/data-items.jd
@@ -15,9 +15,9 @@
 </div>
 
 <p>
-A <a href="@{docRoot}reference/com/google/android/gms/wearable/DataItem.html"><code>DataItem</code></a>
+A <a href="{@docRoot}reference/com/google/android/gms/wearable/DataItem.html"><code>DataItem</code></a>
 defines the data interface that the system uses to synchronize data between handhelds
-and wearables. A <a href="@{docRoot}reference/com/google/android/gms/wearable/DataItem.html"><code>DataItem</code></a> generally
+and wearables. A <a href="{@docRoot}reference/com/google/android/gms/wearable/DataItem.html"><code>DataItem</code></a> generally
 consists of the following items:</p>
 <ul>
   <li><b>Payload</b> - A byte array, which you can set with whatever data you wish, allowing you
@@ -28,15 +28,15 @@
 </ul>
 
 <p>
-You normally don't implement <a href="@{docRoot}reference/com/google/android/gms/wearable/DataItem.html"><code>DataItem</code></a>
+You normally don't implement <a href="{@docRoot}reference/com/google/android/gms/wearable/DataItem.html"><code>DataItem</code></a>
 directly. Instead, you:
 
 <ol>
   <li>Create a <a href="{@docRoot}reference/com/google/android/gms/wearable/PutDataRequest.html"><code>PutDataRequest</code></a> object,
   specifying a string path to uniquely identify the item.
   </li>
-  <li>Call <a href="{@docRoot}reference/com/google/android/gms/wearable/PutDataRequest.html#setData(byte[])">setData()</a> to set
-  the payload.
+  <li>Call <a href="{@docRoot}reference/com/google/android/gms/wearable/PutDataRequest.html#setData(byte[])">
+  <code>setData()</code></a> to set the payload.
   </li>
   <li>Call <a href="{@docRoot}reference/com/google/android/gms/wearable/DataApi.html#putDataItem(com.google.android.gms.common.api.GoogleApiClient, com.google.android.gms.wearable.PutDataRequest)"><code>DataApi.putDataItem()</code></a> to request the system to create the data item.
   </li>
@@ -51,6 +51,7 @@
 a data item in an easy-to-use {@link android.os.Bundle}-like interface.
 </p>
 
+
 <h2 id="SyncData">Sync Data with a Data Map</h2>
 <p>
 When possible, use the <a href="{@docRoot}reference/com/google/android/gms/wearable/DataMap.html"><code>DataMap</code></a> class.
@@ -67,7 +68,7 @@
 <p class="note"><b>Note:</b> The path string is a unique identifier for the
 data item that allows you to access it from either side of the connection. The path must begin
 with a forward slash. If you're using hierarchical data in your
-app, you should create a path scheme that matches the structure of the data. 
+app, you should create a path scheme that matches the structure of the data.
 </p>
 </li>
 <li>Call
@@ -82,12 +83,12 @@
   <li>Call <a href="{@docRoot}reference/com/google/android/gms/wearable/DataApi.html#putDataItem(com.google.android.gms.common.api.GoogleApiClient, com.google.android.gms.wearable.PutDataRequest)"><code>DataApi.putDataItem()</code></a> to request the system to create the data item.
   <p class="note"><b>Note:</b>
   If the handset and wearable devices are disconnected,
-  the data is buffered and and synced when the connection is re-established.
+  the data is buffered and synced when the connection is re-established.
   </p>
   </li>
 </ol>
 
-<p>The following example shows how to create a data map, set data on it, and create it:</p>
+<p>The following example shows how to create a data map and put data on it:</p>
 
 <pre>
 PutDataMapRequest dataMap = PutDataMapRequest.create("/count");
@@ -103,7 +104,7 @@
 You can do this by implementing a listener for data item events.
 
 <p>For example, here's what a typical callback looks like to carry out certain actions
-when data changes.</p>
+when data changes:</p>
 
 <pre>
 &#64;Override
@@ -120,5 +121,6 @@
 <p>
 This is just a snippet that requires more implementation details. Learn about
 how to implement a full listener service or activity in
-<a href="{@docRoot}training/wearables/data-layer/events.html#Listen">Listening for Data Layer Events</a>.
+<a href="{@docRoot}training/wearables/data-layer/events.html#Listen">Listen for Data Layer
+Events</a>.
 </p>
\ No newline at end of file
diff --git a/docs/html/training/wearables/data-layer/events.jd b/docs/html/training/wearables/data-layer/events.jd
index 9196a2c..6a3949a 100644
--- a/docs/html/training/wearables/data-layer/events.jd
+++ b/docs/html/training/wearables/data-layer/events.jd
@@ -14,14 +14,14 @@
 </div>
 </div>
 
-<p>When you make calls with the data layer, you can receive the status
+<p>When you make calls to the Data Layer API, you can receive the status
 of the call when it completes as well as listen for any changes that
 the call ends up making with listeners.
 </p>
 
 <h2 id="Wait">Wait for the Status of Data Layer Calls</h2>
 
-<p>You'll notice that calls to the data layer API sometimes return a
+<p>You'll notice that calls to the Data Layer API sometimes return a
 <a href="{@docRoot}reference/com/google/android/gms/common/api/PendingResult.html"><code>PendingResult</code></a>,
 such as
 <a href="{@docRoot}reference/com/google/android/gms/wearable/DataApi.html#putDataItem(com.google.android.gms.common.api.GoogleApiClient, com.google.android.gms.wearable.PutDataRequest)"><code>putDataItem()</code></a>.
@@ -33,9 +33,9 @@
 lets you wait for the result status, either synchronously or asynchronously.
 </p>
 
-<h3 id="async-waiting">Asynchronously waiting</h3>
-<p>If your code is running on the main UI thread, do not making blocking calls
-to the data layer API. You can run the calls asynchronously by adding a callback
+<h3 id="async-waiting">Asynchronous calls</h3>
+<p>If your code is running on the main UI thread, do not make blocking calls
+to the Data Layer API. You can run the calls asynchronously by adding a callback method
 to the <a href="{@docRoot}reference/com/google/android/gms/common/api/PendingResult.html"><code>PendingResult</code></a> object,
 which fires when the operation is completed:</p>
 <pre>
@@ -49,12 +49,14 @@
 });
 </pre>
 
-<h3 id="sync-waiting">Synchronously waiting</h3>
+<h3 id="sync-waiting">Synchronous calls</h3>
 <p>If your code is running on a separate handler thread in a background service (which is the case
 in a <a href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html"><code>WearableListenerService</code></a>),
 it's fine for the calls to block. In this case, you can call
 <a href="{@docRoot}reference/com/google/android/gms/common/api/PendingResult.html#await()"><code>await()</code></a>
-on the PendingResult object, which will block until the request has completed, and return a Result
+on the <a href="{@docRoot}reference/com/google/android/gms/common/api/PendingResult.html"><code>PendingResult</code></a>
+object, which blocks until the request completes and returns a
+<a href="{@docRoot}reference/com/google/android/gms/common/api/Result.html"><code>Result</code></a>
 object:
 </p>
 
@@ -82,14 +84,14 @@
   </li>
 </ul>
 
-<p>With both these options, you override any of the data event callbacks that you care about
-handling in your implementation.</p>
+<p>With both these options, you override the data event callback methods for the events you
+are interested in handling.</p>
 
 <h3 id="listener-service">With a WearableListenerService</h3>
 
 <p>
 You typically create instances of this service in both your wearable and handheld apps. If you
-don't care about data events in one of these apps, then you don't need to implement this
+are not interested in data events in one of these apps, then you don't need to implement this
 service in that particular app.</p>
 
 <p>For example, you can have a handheld app that sets and gets data item objects and a wearable app
@@ -107,8 +109,9 @@
 -  A message sent from one side of a connection triggers this callback on the other side of the connection.</li>
   <li><a href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html#onMessageReceived(com.google.android.gms.wearable.MessageEvent)"><code>onPeerConnected()</code></a>
   and <a href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html#onPeerDisconnected(com.google.android.gms.wearable.Node)"><code>onPeerDisconnected()</code></a> -
-  Called when connection with the handheld or wearable is connected or disconnected.
-  Changes in connection state on one side of the connection triggers these callbacks on both sides of the connection.
+  Called when the connection with the handheld or wearable is connected or disconnected.
+  Changes in connection state on one side of the connection trigger these callbacks on both sides
+  of the connection.
   </li>
 </ul>
 
@@ -118,8 +121,8 @@
   <li>Create a class that extends
   <a href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html"><code>WearableListenerService</code></a>.
   </li>
-  <li>Listen for the events that you care about, such as
-  <a href="{@docRoot}/reference/com/google/android/gms/wearable/WearableListenerService.html#onDataChanged(com.google.android.gms.wearable.DataEventBuffer)"><code>onDataChanged()</code></a>.
+  <li>Listen for the events that you're interested in, such as
+  <a href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html#onDataChanged(com.google.android.gms.wearable.DataEventBuffer)"><code>onDataChanged()</code></a>.
   </li>
   <li>Declare an intent filter in your Android manifest to notify the system about your
   <a href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html"><code>WearableListenerService</code></a>.
@@ -165,7 +168,7 @@
 
             // Get the node id from the host value of the URI
             String nodeId = uri.getHost();
-            // Set the data of the message to be the bytes of the URI.
+            // Set the data of the message to be the bytes of the URI
             byte[] payload = uri.toString().getBytes();
 
             // Send the RPC
@@ -189,7 +192,8 @@
 
 <h4>Permissions within Data Layer Callbacks</h4>
 
-<p>In order to deliver callbacks to your application for data layer events, Google Play services
+<p>
+To deliver callbacks to your application for data layer events, Google Play services
 binds to your <a href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html"><code>WearableListenerService</code></a>,
 and calls your callbacks via IPC. This has the consequence
 that your callbacks inherit the permissions of the calling process.</p>
@@ -233,7 +237,7 @@
 <li>Implement the desired interfaces.</li>
 <li>In {@link android.app.Activity#onCreate}, create an instance of
 <a href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.html"><code>GoogleApiClient</code></a>
-to work with the data layer API.
+to work with the Data Layer API.
 <li>
 In {@link android.app.Activity#onStart onStart()}, call <a href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.html#connect()"><code>connect()</code></a> to connect the client to Google Play services.
 </li>
@@ -283,7 +287,7 @@
         }
     }
 
-   &#64;Override
+    &#64;Override
     public void onConnected(Bundle connectionHint) {
         if (Log.isLoggable(TAG, Log.DEBUG)) {
             Log.d(TAG, "Connected to Google Api Service");
@@ -306,8 +310,8 @@
             if (event.getType() == DataEvent.TYPE_DELETED) {
                 Log.d(TAG, "DataItem deleted: " + event.getDataItem().getUri());
             } else if (event.getType() == DataEvent.TYPE_CHANGED) {
-                 Log.d(TAG, "DataItem changed: " + event.getDataItem().getUri());
+                Log.d(TAG, "DataItem changed: " + event.getDataItem().getUri());
             }
         }
     }
-</pre>
\ No newline at end of file
+</pre>
diff --git a/docs/html/training/wearables/data-layer/index.jd b/docs/html/training/wearables/data-layer/index.jd
index 73d9ee5..8d42ae3 100644
--- a/docs/html/training/wearables/data-layer/index.jd
+++ b/docs/html/training/wearables/data-layer/index.jd
@@ -66,7 +66,7 @@
 
 <h2>Lessons</h2>
   <dl>
-    <dt><a href="{@docRoot}training/wearables/data-layer/data-items.html">Accessing the Wearable Data Layer</a></dt>
+    <dt><a href="{@docRoot}training/wearables/data-layer/accessing.html">Accessing the Wearable Data Layer</a></dt>
     <dd>This lesson shows you how to create a client to access the Data Layer APIs.</dd>
 
     <dt><a href="{@docRoot}training/wearables/data-layer/data-items.html">Syncing Data Items</a></dt>
diff --git a/docs/html/training/wearables/data-layer/messages.jd b/docs/html/training/wearables/data-layer/messages.jd
index b3afacb8..822e395 100644
--- a/docs/html/training/wearables/data-layer/messages.jd
+++ b/docs/html/training/wearables/data-layer/messages.jd
@@ -22,20 +22,16 @@
   <li>A path that uniquely identifies the message's action</li>
 </ul>
 <p>
-Unlike data items, there is no syncing between the handheld and wearable apps.
+Unlike with data items, there is no syncing between the handheld and wearable apps.
 Messages are a one-way communication mechanism that's good for remote procedure calls (RPC),
-such as sending a message to the wearable
-to start an activity. You can also use messages in request/response model
-where one side of the connection sends a message, does some work,
-and sends back a response message.</p>
+such as sending a message to the wearable to start an activity.</p>
 
 <h2 id="SendMessage">Send a Message</h2>
 
 <p>The following example shows how to send a message that indicates to the other
-side of the connect to start an activity.
-This call is made synchronously, which blocks until the message
-is received or when the request times out:
-</p>
+side of the connection to start an activity.
+This call is synchronous and blocks processing until the message is received or until the request
+times out:</p>
 
 <p class="note"><b>Note:</b> Read more about asynchronous and synchronous calls
 to Google Play services and when to use each in
@@ -61,7 +57,7 @@
 
 <pre>
 private Collection&lt;String&gt; getNodes() {
-    HashSet &lt;String&gt;results= new HashSet&lt;String&gt;();
+    HashSet &lt;String&gt;results = new HashSet&lt;String&gt;();
     NodeApi.GetConnectedNodesResult nodes =
             Wearable.NodeApi.getConnectedNodes(mGoogleApiClient).await();
     for (Node node : nodes.getNodes()) {
@@ -71,14 +67,17 @@
 }
 </pre>
 
-<h2 id="ReceiveMessage">Receiving a Message</h2>
+<h2 id="ReceiveMessage">Receive a Message</h2>
 
 <p>
-
-To be notified of received messages, you implement a listener for message events.
-This example shows how you might do this by checking the <code>START_ACTIVITY_PATH</code>
-that the previous example used to send the message. If this condition is <code>true</code>,
-a specific activity is started.
+To be notified of received messages, you implement the
+<a href="{@docRoot}reference/com/google/android/gms/wearable/MessageApi.MessageListener.html">
+<code>MessageListener</code></a> interface to provide a listener for message events. Then you register your
+listener with the
+<a href="{@docRoot}reference/com/google/android/gms/wearable/MessageApi.html#addListener(com.google.android.gms.common.api.GoogleApiClient, com.google.android.gms.wearable.MessageApi.MessageListener)">
+<code>MessageApi.addListener()</code></a> method. This example shows how you might implement the listener
+to check the <code>START_ACTIVITY_PATH</code> that the previous example used to send the message.
+If this condition is <code>true</code>, a specific activity is started.
 </p>
 
 <pre>
@@ -95,5 +94,6 @@
 <p>
 This is just a snippet that requires more implementation details. Learn about
 how to implement a full listener service or activity in
-<a href="{@docRoot}training/wearables/data-layer/events.html#Listen">Listening for Data Layer Events</a>.
+<a href="{@docRoot}training/wearables/data-layer/events.html#Listen">Listening for Data Layer
+Events</a>.
 </p>
\ No newline at end of file
diff --git a/graphics/java/android/graphics/ColorMatrix.java b/graphics/java/android/graphics/ColorMatrix.java
index 1242eb5..64f0c05 100644
--- a/graphics/java/android/graphics/ColorMatrix.java
+++ b/graphics/java/android/graphics/ColorMatrix.java
@@ -21,23 +21,43 @@
 import java.util.Arrays;
 
 /**
- *  4x5 matrix for transforming the color+alpha components of a Bitmap.
- *  The matrix is stored in a single array, and its treated as follows:
+ * 4x5 matrix for transforming the color and alpha components of a Bitmap.
+ * The matrix can be passed as single array, and is treated as follows:
+ *
  * <pre>
  *  [ a, b, c, d, e,
  *    f, g, h, i, j,
  *    k, l, m, n, o,
- *    p, q, r, s, t ]
- * </pre>
+ *    p, q, r, s, t ]</pre>
  *
- * When applied to a color <code>[r, g, b, a]</code>, the resulting color
- * is computed as (after clamping):
+ * <p>
+ * When applied to a color <code>[R, G, B, A]</code>, the resulting color
+ * is computed as:
+ * </p>
+ *
  * <pre>
- *   R' = a*R + b*G + c*B + d*A + e;
- *   G' = f*R + g*G + h*B + i*A + j;
- *   B' = k*R + l*G + m*B + n*A + o;
- *   A' = p*R + q*G + r*B + s*A + t;
- * </pre>
+ *   R&rsquo; = a*R + b*G + c*B + d*A + e;
+ *   G&rsquo; = f*R + g*G + h*B + i*A + j;
+ *   B&rsquo; = k*R + l*G + m*B + n*A + o;
+ *   A&rsquo; = p*R + q*G + r*B + s*A + t;</pre>
+ *
+ * <p>
+ * That resulting color <code>[R&rsquo;, G&rsquo;, B&rsquo;, A&rsquo;]</code>
+ * then has each channel clamped to the <code>0</code> to <code>255</code>
+ * range.
+ * </p>
+ *
+ * <p>
+ * The sample ColorMatrix below inverts incoming colors by scaling each
+ * channel by <code>-1</code>, and then shifting the result up by
+ * <code>255</code> to remain in the standard color space.
+ * </p>
+ *
+ * <pre>
+ *   [ -1, 0, 0, 0, 255,
+ *     0, -1, 0, 0, 255,
+ *     0, 0, -1, 0, 255,
+ *     0, 0, 0, 1, 0 ]</pre>
  */
 @SuppressWarnings({ "MismatchedReadAndWriteOfArray", "PointlessArithmeticExpression" })
 public class ColorMatrix {
@@ -52,24 +72,24 @@
     }
 
     /**
-        * Create a new colormatrix initialized with the specified array of values.
+     * Create a new colormatrix initialized with the specified array of values.
      */
     public ColorMatrix(float[] src) {
         System.arraycopy(src, 0, mArray, 0, 20);
     }
-    
+
     /**
      * Create a new colormatrix initialized with the specified colormatrix.
      */
     public ColorMatrix(ColorMatrix src) {
         System.arraycopy(src.mArray, 0, mArray, 0, 20);
     }
-    
+
     /**
      * Return the array of floats representing this colormatrix.
      */
     public final float[] getArray() { return mArray; }
-    
+
     /**
      * Set this colormatrix to identity:
      * <pre>
@@ -84,7 +104,7 @@
         Arrays.fill(a, 0);
         a[0] = a[6] = a[12] = a[18] = 1;
     }
-    
+
     /**
      * Assign the src colormatrix into this matrix, copying all of its values.
      */
@@ -98,7 +118,7 @@
     public void set(float[] src) {
         System.arraycopy(src, 0, mArray, 0, 20);
     }
-    
+
     /**
      * Set this colormatrix to scale by the specified values.
      */
@@ -114,12 +134,14 @@
         a[12] = bScale;
         a[18] = aScale;
     }
-    
+
     /**
      * Set the rotation on a color axis by the specified values.
+     * <p>
      * <code>axis=0</code> correspond to a rotation around the RED color
      * <code>axis=1</code> correspond to a rotation around the GREEN color
      * <code>axis=2</code> correspond to a rotation around the BLUE color
+     * </p>
      */
     public void setRotate(int axis, float degrees) {
         reset();
@@ -153,8 +175,10 @@
     /**
      * Set this colormatrix to the concatenation of the two specified
      * colormatrices, such that the resulting colormatrix has the same effect
-     * as applying matB and then applying matA. It is legal for either matA or
-     * matB to be the same colormatrix as this.
+     * as applying matB and then applying matA.
+     * <p>
+     * It is legal for either matA or matB to be the same colormatrix as this.
+     * </p>
      */
     public void setConcat(ColorMatrix matA, ColorMatrix matB) {
         float[] tmp;
@@ -163,7 +187,7 @@
         } else {
             tmp = mArray;
         }
-        
+
         final float[] a = matA.mArray;
         final float[] b = matB.mArray;
         int index = 0;
@@ -176,38 +200,43 @@
                            a[j + 2] * b[14] + a[j + 3] * b[19] +
                            a[j + 4];
         }
-        
+
         if (tmp != mArray) {
             System.arraycopy(tmp, 0, mArray, 0, 20);
         }
     }
 
     /**
-     * Concat this colormatrix with the specified prematrix. This is logically
-     * the same as calling setConcat(this, prematrix);
+     * Concat this colormatrix with the specified prematrix.
+     * <p>
+     * This is logically the same as calling setConcat(this, prematrix);
+     * </p>
      */
     public void preConcat(ColorMatrix prematrix) {
         setConcat(this, prematrix);
     }
 
     /**
-     * Concat this colormatrix with the specified postmatrix. This is logically
-     * the same as calling setConcat(postmatrix, this);
+     * Concat this colormatrix with the specified postmatrix.
+     * <p>
+     * This is logically the same as calling setConcat(postmatrix, this);
+     * </p>
      */
     public void postConcat(ColorMatrix postmatrix) {
         setConcat(postmatrix, this);
     }
 
     ///////////////////////////////////////////////////////////////////////////
-    
+
     /**
-     * Set the matrix to affect the saturation of colors. A value of 0 maps the
-     * color to gray-scale. 1 is identity.
+     * Set the matrix to affect the saturation of colors.
+     *
+     * @param sat A value of 0 maps the color to gray-scale. 1 is identity.
      */
     public void setSaturation(float sat) {
         reset();
         float[] m = mArray;
-        
+
         final float invSat = 1 - sat;
         final float R = 0.213f * invSat;
         final float G = 0.715f * invSat;
@@ -217,7 +246,7 @@
         m[5] = R;       m[6] = G + sat; m[7] = B;
         m[10] = R;      m[11] = G;      m[12] = B + sat;
     }
-    
+
     /**
      * Set the matrix to convert RGB to YUV
      */
@@ -229,7 +258,7 @@
         m[5]  = -0.16874f; m[6]  = -0.33126f; m[7]  = 0.5f;
         m[10] = 0.5f;      m[11] = -0.41869f; m[12] = -0.08131f;
     }
-    
+
     /**
      * Set the matrix to convert from YUV to RGB
      */
@@ -242,4 +271,3 @@
         m[10] = 1;  m[11] = 1.772f;     m[12] = 0;
     }
 }
-
diff --git a/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java b/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
index 6d23634..f58a765 100644
--- a/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
@@ -199,6 +199,8 @@
             return false;
         }
 
+        boolean hasReversibleFlag = state.transitionHasReversibleFlag(fromId, toId);
+
         // This may fail if we're already on the transition, but that's okay!
         selectDrawable(transitionIndex);
 
@@ -206,10 +208,14 @@
         final Drawable d = getCurrent();
         if (d instanceof AnimationDrawable) {
             final boolean reversed = state.isTransitionReversed(fromId, toId);
-            transition = new AnimationDrawableTransition((AnimationDrawable) d, reversed);
+
+            transition = new AnimationDrawableTransition((AnimationDrawable) d,
+                    reversed, hasReversibleFlag);
         } else if (d instanceof AnimatedVectorDrawable) {
             final boolean reversed = state.isTransitionReversed(fromId, toId);
-            transition = new AnimatedVectorDrawableTransition((AnimatedVectorDrawable) d, reversed);
+
+            transition = new AnimatedVectorDrawableTransition((AnimatedVectorDrawable) d,
+                    reversed, hasReversibleFlag);
         } else if (d instanceof Animatable) {
             transition = new AnimatableTransition((Animatable) d);
         } else {
@@ -260,7 +266,12 @@
     private static class AnimationDrawableTransition  extends Transition {
         private final ObjectAnimator mAnim;
 
-        public AnimationDrawableTransition(AnimationDrawable ad, boolean reversed) {
+        // Even AnimationDrawable is always reversible technically, but
+        // we should obey the XML's android:reversible flag.
+        private final boolean mHasReversibleFlag;
+
+        public AnimationDrawableTransition(AnimationDrawable ad,
+                boolean reversed, boolean hasReversibleFlag) {
             final int frameCount = ad.getNumberOfFrames();
             final int fromFrame = reversed ? frameCount - 1 : 0;
             final int toFrame = reversed ? 0 : frameCount - 1;
@@ -269,13 +280,13 @@
             anim.setAutoCancel(true);
             anim.setDuration(interp.getTotalDuration());
             anim.setInterpolator(interp);
-
+            mHasReversibleFlag = hasReversibleFlag;
             mAnim = anim;
         }
 
         @Override
         public boolean canReverse() {
-            return true;
+            return mHasReversibleFlag;
         }
 
         @Override
@@ -296,16 +307,28 @@
 
     private static class AnimatedVectorDrawableTransition  extends Transition {
         private final AnimatedVectorDrawable mAvd;
+
+        // mReversed is indicating the current transition's direction.
         private final boolean mReversed;
 
-        public AnimatedVectorDrawableTransition(AnimatedVectorDrawable avd, boolean reversed) {
+        // mHasReversibleFlag is indicating whether the whole transition has
+        // reversible flag set to true.
+        // If mHasReversibleFlag is false, then mReversed is always false.
+        private final boolean mHasReversibleFlag;
+
+        public AnimatedVectorDrawableTransition(AnimatedVectorDrawable avd,
+                boolean reversed, boolean hasReversibleFlag) {
             mAvd = avd;
             mReversed = reversed;
+            mHasReversibleFlag = hasReversibleFlag;
         }
 
         @Override
         public boolean canReverse() {
-            return mAvd.canReverse();
+            // When the transition's XML says it is not reversible, then we obey
+            // it, even if the AVD itself is reversible.
+            // This will help the single direction transition.
+            return mAvd.canReverse() && mHasReversibleFlag;
         }
 
         @Override
@@ -322,7 +345,8 @@
             if (canReverse()) {
                 mAvd.reverse();
             } else {
-                Log.w(LOGTAG, "Reverse() is called on a drawable can't reverse");
+                Log.w(LOGTAG, "Can't reverse, either the reversible is set to false,"
+                        + " or the AnimatedVectorDrawable can't reverse");
             }
         }
 
@@ -520,8 +544,12 @@
     }
 
     static class AnimatedStateListState extends StateListState {
-        private static final int REVERSE_SHIFT = 32;
-        private static final int REVERSE_MASK = 0x1;
+        // REVERSED_BIT is indicating the current transition's direction.
+        private static final long REVERSED_BIT = 0x100000000l;
+
+        // REVERSIBLE_FLAG_BIT is indicating whether the whole transition has
+        // reversible flag set to true.
+        private static final long REVERSIBLE_FLAG_BIT = 0x200000000l;
 
         int[] mAnimThemeAttrs;
 
@@ -545,11 +573,15 @@
         int addTransition(int fromId, int toId, @NonNull Drawable anim, boolean reversible) {
             final int pos = super.addChild(anim);
             final long keyFromTo = generateTransitionKey(fromId, toId);
-            mTransitions.append(keyFromTo, pos);
+            long reversibleBit = 0;
+            if (reversible) {
+                reversibleBit = REVERSIBLE_FLAG_BIT;
+            }
+            mTransitions.append(keyFromTo, pos | reversibleBit);
 
             if (reversible) {
                 final long keyToFrom = generateTransitionKey(toId, fromId);
-                mTransitions.append(keyToFrom, pos | (1L << REVERSE_SHIFT));
+                mTransitions.append(keyToFrom, pos | REVERSED_BIT | reversibleBit);
             }
 
             return addChild(anim);
@@ -581,7 +613,12 @@
 
         boolean isTransitionReversed(int fromId, int toId) {
             final long keyFromTo = generateTransitionKey(fromId, toId);
-            return (mTransitions.get(keyFromTo, -1) >> REVERSE_SHIFT & REVERSE_MASK) == 1;
+            return (mTransitions.get(keyFromTo, -1) & REVERSED_BIT) != 0;
+        }
+
+        boolean transitionHasReversibleFlag(int fromId, int toId) {
+            final long keyFromTo = generateTransitionKey(fromId, toId);
+            return (mTransitions.get(keyFromTo, -1) & REVERSIBLE_FLAG_BIT) != 0;
         }
 
         @Override
diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
index d0edeba..e9c8c2a 100644
--- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
@@ -462,19 +462,22 @@
     /**
      * Reverses ongoing animations or starts pending animations in reverse.
      * <p>
-     * NOTE: Only works of all animations are ValueAnimators.
+     * NOTE: Only works if all animations support reverse. Otherwise, this will
+     * do nothing.
      * @hide
      */
     public void reverse() {
+        // Only reverse when all the animators can be reverse. Otherwise, partially
+        // reverse is confusing.
+        if (!canReverse()) {
+            Log.w(LOGTAG, "AnimatedVectorDrawable can't reverse()");
+            return;
+        }
         final ArrayList<Animator> animators = mAnimatedVectorState.mAnimators;
         final int size = animators.size();
         for (int i = 0; i < size; i++) {
             final Animator animator = animators.get(i);
-            if (animator.canReverse()) {
-                animator.reverse();
-            } else {
-                Log.w(LOGTAG, "AnimatedVectorDrawable can't reverse()");
-            }
+            animator.reverse();
         }
     }
 
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index b95636b..9aa29ca 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -20,11 +20,18 @@
 
 #include "Caches.h"
 #include "DeferredDisplayList.h"
-#include "RenderState.h"
 #include "Layer.h"
 #include "LayerRenderer.h"
 #include "OpenGLRenderer.h"
 #include "RenderNode.h"
+#include "RenderState.h"
+#include "utils/TraceUtils.h"
+
+#define ATRACE_LAYER_WORK(label) \
+    ATRACE_FORMAT("%s HW Layer DisplayList %s %ux%u", \
+            label, \
+            (renderNode.get() != NULL) ? renderNode->getName() : "", \
+            getWidth(), getHeight())
 
 namespace android {
 namespace uirenderer {
@@ -223,6 +230,8 @@
 }
 
 void Layer::defer(const OpenGLRenderer& rootRenderer) {
+    ATRACE_LAYER_WORK("Optimize");
+
     updateLightPosFromRenderer(rootRenderer);
     const float width = layer.getWidth();
     const float height = layer.getHeight();
@@ -260,6 +269,9 @@
 void Layer::flush() {
     // renderer is checked as layer may be destroyed/put in layer cache with flush scheduled
     if (deferredList && renderer) {
+        ATRACE_LAYER_WORK("Issue");
+        renderer->startMark((renderNode.get() != NULL) ? renderNode->getName() : "Layer");
+
         renderer->setViewport(layer.getWidth(), layer.getHeight());
         renderer->prepareDirty(dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom,
                 !isBlend());
@@ -270,10 +282,14 @@
 
         dirtyRect.setEmpty();
         renderNode = NULL;
+
+        renderer->endMark();
     }
 }
 
 void Layer::render(const OpenGLRenderer& rootRenderer) {
+    ATRACE_LAYER_WORK("Direct-Issue");
+
     updateLightPosFromRenderer(rootRenderer);
     renderer->setViewport(layer.getWidth(), layer.getHeight());
     renderer->prepareDirty(dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom,
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index 394c647..83f9c6a 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -27,6 +27,7 @@
 #include "Matrix.h"
 #include "Properties.h"
 #include "Rect.h"
+#include "utils/TraceUtils.h"
 
 namespace android {
 namespace uirenderer {
@@ -185,7 +186,7 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 Layer* LayerRenderer::createRenderLayer(RenderState& renderState, uint32_t width, uint32_t height) {
-    ATRACE_CALL();
+    ATRACE_FORMAT("Allocate %ux%u HW Layer", width, height);
     LAYER_RENDERER_LOGD("Requesting new render layer %dx%d", width, height);
 
     Caches& caches = Caches::getInstance();
@@ -310,7 +311,7 @@
 
 void LayerRenderer::destroyLayer(Layer* layer) {
     if (layer) {
-        ATRACE_CALL();
+        ATRACE_FORMAT("Destroy %ux%u HW Layer", layer->getWidth(), layer->getHeight());
         LAYER_RENDERER_LOGD("Recycling layer, %dx%d fbo = %d",
                 layer->getWidth(), layer->getHeight(), layer->getFbo());
 
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index d570b0d..80b4c2a 100755
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -42,6 +42,7 @@
 #include "ShadowTessellator.h"
 #include "SkiaShader.h"
 #include "utils/GLUtils.h"
+#include "utils/TraceUtils.h"
 #include "Vector.h"
 #include "VertexBuffer.h"
 
@@ -51,21 +52,6 @@
     #define EVENT_LOGD(...)
 #endif
 
-static void atraceFormatBegin(const char* fmt, ...) {
-    const int BUFFER_SIZE = 256;
-    va_list ap;
-    char buf[BUFFER_SIZE];
-
-    va_start(ap, fmt);
-    vsnprintf(buf, BUFFER_SIZE, fmt, ap);
-    va_end(ap);
-
-    ATRACE_BEGIN(buf);
-}
-
-#define ATRACE_FORMAT_BEGIN(fmt, ...) \
-    if (CC_UNLIKELY(ATRACE_ENABLED())) atraceFormatBegin(fmt, ##__VA_ARGS__)
-
 namespace android {
 namespace uirenderer {
 
@@ -466,8 +452,6 @@
 bool OpenGLRenderer::updateLayer(Layer* layer, bool inFrame) {
     if (layer->deferredUpdateScheduled && layer->renderer
             && layer->renderNode.get() && layer->renderNode->isRenderable()) {
-        ATRACE_CALL();
-
         Rect& dirty = layer->dirtyRect;
 
         if (inFrame) {
@@ -525,20 +509,10 @@
     int count = mLayerUpdates.size();
     if (count > 0) {
         startMark("Apply Layer Updates");
-        char layerName[12];
 
         // Note: it is very important to update the layers in order
         for (int i = 0; i < count; i++) {
-            Layer* layer = mLayerUpdates.itemAt(i).get();
-
-            sprintf(layerName, "Layer #%d", i);
-            startMark(layerName);
-            ATRACE_FORMAT_BEGIN("flushLayer %ux%u", layer->getWidth(), layer->getHeight());
-
-            layer->flush();
-
-            ATRACE_END();
-            endMark();
+            mLayerUpdates.itemAt(i)->flush();
         }
 
         mLayerUpdates.clear();
@@ -575,7 +549,7 @@
 }
 
 void OpenGLRenderer::flushLayerUpdates() {
-    ATRACE_CALL();
+    ATRACE_NAME("Update HW Layers");
     syncState();
     updateLayers();
     flushLayers();
diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp
index 9ba8854..6f48e4d 100644
--- a/libs/hwui/PathCache.cpp
+++ b/libs/hwui/PathCache.cpp
@@ -255,7 +255,7 @@
 
 PathTexture* PathCache::addTexture(const PathDescription& entry, const SkPath *path,
         const SkPaint* paint) {
-    ATRACE_CALL();
+    ATRACE_NAME("Generate Path Texture");
 
     float left, top, offset;
     uint32_t width, height;
diff --git a/libs/hwui/Program.cpp b/libs/hwui/Program.cpp
index 0dad0dc..e6fd2dc 100644
--- a/libs/hwui/Program.cpp
+++ b/libs/hwui/Program.cpp
@@ -132,7 +132,7 @@
 }
 
 GLuint Program::buildShader(const char* source, GLenum type) {
-    ATRACE_CALL();
+    ATRACE_NAME("Build GL Shader");
 
     GLuint shader = glCreateShader(type);
     glShaderSource(shader, 1, &source, 0);
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 13c5499..eb0948f 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -25,7 +25,6 @@
 #include <SkCanvas.h>
 #include <algorithm>
 
-#include <utils/Trace.h>
 
 #include "DamageAccumulator.h"
 #include "Debug.h"
@@ -34,6 +33,7 @@
 #include "LayerRenderer.h"
 #include "OpenGLRenderer.h"
 #include "utils/MathUtils.h"
+#include "utils/TraceUtils.h"
 #include "renderthread/CanvasContext.h"
 
 namespace android {
@@ -426,6 +426,10 @@
                 clipFlags = 0; // all clipping done by saveLayer
             }
 
+            ATRACE_FORMAT("%s alpha caused %ssaveLayer %ux%u",
+                    getName(), clipFlags ? "" : "unclipped ",
+                    layerBounds.getWidth(), layerBounds.getHeight());
+
             SaveLayerOp* op = new (handler.allocator()) SaveLayerOp(
                     layerBounds.left, layerBounds.top, layerBounds.right, layerBounds.bottom,
                     properties().getAlpha() * 255, saveFlags);
diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp
index 3b8a9a4..5fcb194 100644
--- a/libs/hwui/TextureCache.cpp
+++ b/libs/hwui/TextureCache.cpp
@@ -26,6 +26,7 @@
 #include "Caches.h"
 #include "TextureCache.h"
 #include "Properties.h"
+#include "utils/TraceUtils.h"
 
 namespace android {
 namespace uirenderer {
@@ -266,7 +267,7 @@
         return;
     }
 
-    ATRACE_CALL();
+    ATRACE_FORMAT("Upload %ux%u Texture", bitmap->width(), bitmap->height());
 
     // We could also enable mipmapping if both bitmap dimensions are powers
     // of 2 but we'd have to deal with size changes. Let's keep this simple
diff --git a/libs/hwui/font/Font.cpp b/libs/hwui/font/Font.cpp
index ba878bac..af39f16 100644
--- a/libs/hwui/font/Font.cpp
+++ b/libs/hwui/font/Font.cpp
@@ -363,7 +363,7 @@
 }
 
 void Font::precache(const SkPaint* paint, const char* text, int numGlyphs) {
-    ATRACE_NAME("precacheText");
+    ATRACE_NAME("Precache Glyphs");
 
     if (numGlyphs == 0 || text == NULL) {
         return;
diff --git a/libs/hwui/utils/TraceUtils.h b/libs/hwui/utils/TraceUtils.h
new file mode 100644
index 0000000..ff8ccb8
--- /dev/null
+++ b/libs/hwui/utils/TraceUtils.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef TRACE_UTILS_H
+#define TRACE_UTILS_H
+
+#include <utils/Trace.h>
+
+#define ATRACE_FORMAT(fmt, ...) \
+    TraceUtils::TraceEnder __traceEnder = (TraceUtils::atraceFormatBegin(fmt, ##__VA_ARGS__), TraceUtils::TraceEnder())
+
+#define ATRACE_FORMAT_BEGIN(fmt, ...) \
+    TraceUtils::atraceFormatBegin(fmt, ##__VA_ARGS__)
+
+namespace android {
+namespace uirenderer {
+
+class TraceUtils {
+public:
+    class TraceEnder {
+    public:
+        ~TraceEnder() { ATRACE_END(); }
+    };
+
+    static void atraceFormatBegin(const char* fmt, ...) {
+        if (CC_UNLIKELY(!ATRACE_ENABLED())) return;
+
+        const int BUFFER_SIZE = 256;
+        va_list ap;
+        char buf[BUFFER_SIZE];
+
+        va_start(ap, fmt);
+        vsnprintf(buf, BUFFER_SIZE, fmt, ap);
+        va_end(ap);
+
+        ATRACE_BEGIN(buf);
+    }
+
+}; // class TraceUtils
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* TRACE_UTILS_H */
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index 58ed4f8..9a0266d 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -128,6 +128,29 @@
 
     /**
      * @hide
+     * Return the input channel mask corresponding to an output channel mask.
+     * This can be used for submix rerouting for the mask of the recorder to map to that of the mix.
+     * @param outMask a combination of the CHANNEL_OUT_* definitions, but not CHANNEL_OUT_DEFAULT
+     * @return a combination of CHANNEL_IN_* definitions matching an output channel mask
+     * @throws IllegalArgumentException
+     */
+    public static int inChannelMaskFromOutChannelMask(int outMask) throws IllegalArgumentException {
+        if (outMask == CHANNEL_OUT_DEFAULT) {
+            throw new IllegalArgumentException(
+                    "Illegal CHANNEL_OUT_DEFAULT channel mask for input.");
+        }
+        switch (channelCountFromOutChannelMask(outMask)) {
+            case 1:
+                return CHANNEL_IN_MONO;
+            case 2:
+                return CHANNEL_IN_STEREO;
+            default:
+                throw new IllegalArgumentException("Unsupported channel configuration for input.");
+        }
+    }
+
+    /**
+     * @hide
      * Return the number of channels from an input channel mask
      * @param mask a combination of the CHANNEL_IN_* definitions, even CHANNEL_IN_DEFAULT
      * @return number of channels for the mask
diff --git a/media/java/android/media/AudioManagerInternal.java b/media/java/android/media/AudioManagerInternal.java
index 7c0d758..a6cc493 100644
--- a/media/java/android/media/AudioManagerInternal.java
+++ b/media/java/android/media/AudioManagerInternal.java
@@ -35,4 +35,7 @@
 
     public abstract void setStreamVolumeForUid(int streamType, int direction, int flags,
             String callingPackage, int uid);
+
+    public abstract void adjustMasterVolumeForUid(int steps, int flags, String callingPackage,
+            int uid);
 }
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 6e14aba..c70ac55 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -50,7 +50,6 @@
 import android.media.MediaPlayer.OnErrorListener;
 import android.media.audiopolicy.AudioMix;
 import android.media.audiopolicy.AudioPolicyConfig;
-import android.media.session.MediaSessionLegacyHelper;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Environment;
@@ -61,7 +60,6 @@
 import android.os.PowerManager;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.UserHandle;
@@ -75,12 +73,11 @@
 import android.util.MathUtils;
 import android.util.Slog;
 import android.view.KeyEvent;
+import android.view.OrientationEventListener;
 import android.view.Surface;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityManager;
-import android.view.OrientationEventListener;
 
-import com.android.internal.telephony.ITelephony;
 import com.android.internal.util.XmlUtils;
 import com.android.server.LocalServices;
 
@@ -91,8 +88,6 @@
 import java.io.PrintWriter;
 import java.lang.reflect.Field;
 import java.util.ArrayList;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
@@ -100,6 +95,7 @@
 import java.util.NoSuchElementException;
 import java.util.Objects;
 import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
 
 /**
  * The implementation of the volume manager service.
@@ -1152,6 +1148,10 @@
 
     /** @see AudioManager#adjustMasterVolume(int, int) */
     public void adjustMasterVolume(int steps, int flags, String callingPackage) {
+        adjustMasterVolume(steps, flags, callingPackage, Binder.getCallingUid());
+    }
+
+    public void adjustMasterVolume(int steps, int flags, String callingPackage, int uid) {
         if (mUseFixedVolume) {
             return;
         }
@@ -1166,7 +1166,7 @@
         }
 
         //Log.d(TAG, "adjustMasterVolume volume: " + volume + " steps: " + steps);
-        setMasterVolume(volume, flags, callingPackage);
+        setMasterVolume(volume, flags, callingPackage, uid);
     }
 
     // StreamVolumeCommand contains the information needed to defer the process of
@@ -1679,18 +1679,24 @@
         }
     }
 
+    @Override
     public int getMasterVolume() {
         if (isMasterMute()) return 0;
         return getLastAudibleMasterVolume();
     }
 
+    @Override
     public void setMasterVolume(int volume, int flags, String callingPackage) {
+        setMasterVolume(volume, flags, callingPackage, Binder.getCallingUid());
+    }
+
+    public void setMasterVolume(int volume, int flags, String callingPackage, int uid) {
         if (mUseFixedVolume) {
             return;
         }
 
-        if (mAppOps.noteOp(AppOpsManager.OP_AUDIO_MASTER_VOLUME, Binder.getCallingUid(),
-                callingPackage) != AppOpsManager.MODE_ALLOWED) {
+        if (mAppOps.noteOp(AppOpsManager.OP_AUDIO_MASTER_VOLUME, uid, callingPackage)
+                != AppOpsManager.MODE_ALLOWED) {
             return;
         }
 
@@ -5667,6 +5673,12 @@
                 String callingPackage, int uid) {
             setStreamVolume(streamType, direction, flags, callingPackage, uid);
         }
+
+        @Override
+        public void adjustMasterVolumeForUid(int steps, int flags, String callingPackage,
+                int uid) {
+            adjustMasterVolume(steps, flags, callingPackage, uid);
+        }
     }
 
     //==========================================================================================
diff --git a/media/java/android/media/MediaMetadata.java b/media/java/android/media/MediaMetadata.java
index 924078c..754da0e 100644
--- a/media/java/android/media/MediaMetadata.java
+++ b/media/java/android/media/MediaMetadata.java
@@ -16,8 +16,6 @@
 package android.media;
 
 import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.ContentProviderClient;
 import android.content.ContentResolver;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
@@ -25,16 +23,14 @@
 import android.media.session.MediaController;
 import android.net.Uri;
 import android.os.Bundle;
-import android.os.CancellationSignal;
-import android.os.OperationCanceledException;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
-import android.util.Size;
 import android.util.SparseArray;
 
+import java.util.ArrayList;
 import java.util.Set;
 
 /**
@@ -569,6 +565,29 @@
         }
 
         /**
+         * Create a Builder using a {@link MediaMetadata} instance to set
+         * initial values, but replace bitmaps with a scaled down copy if they
+         * are larger than maxBitmapSize.
+         *
+         * @param source The original metadata to copy.
+         * @param maxBitmapSize The maximum height/width for bitmaps contained
+         *            in the metadata.
+         * @hide
+         */
+        public Builder(MediaMetadata source, int maxBitmapSize) {
+            this(source);
+            for (String key : mBundle.keySet()) {
+                Object value = mBundle.get(key);
+                if (value != null && value instanceof Bitmap) {
+                    Bitmap bmp = (Bitmap) value;
+                    if (bmp.getHeight() > maxBitmapSize || bmp.getWidth() > maxBitmapSize) {
+                        putBitmap(key, scaleBitmap(bmp, maxBitmapSize));
+                    }
+                }
+            }
+        }
+
+        /**
          * Put a CharSequence value into the metadata. Custom keys may be used,
          * but if the METADATA_KEYs defined in this class are used they may only
          * be one of the following:
@@ -707,6 +726,10 @@
          * <li>{@link #METADATA_KEY_ALBUM_ART}</li>
          * <li>{@link #METADATA_KEY_DISPLAY_ICON}</li>
          * </ul>
+         * <p>
+         * Large bitmaps may be scaled down by the system. To pass full
+         * resolution images {@link Uri Uris} should be used with
+         * {@link #putString}.
          *
          * @param key The key for referencing this value
          * @param value The Bitmap to store
@@ -731,5 +754,15 @@
         public MediaMetadata build() {
             return new MediaMetadata(mBundle);
         }
+
+        private Bitmap scaleBitmap(Bitmap bmp, int maxSize) {
+            float maxSizeF = maxSize;
+            float widthScale = maxSizeF / bmp.getWidth();
+            float heightScale = maxSizeF / bmp.getHeight();
+            float scale = Math.min(widthScale, heightScale);
+            int height = (int) (bmp.getHeight() * scale);
+            int width = (int) (bmp.getWidth() * scale);
+            return Bitmap.createScaledBitmap(bmp, width, height, true);
+        }
     }
 }
diff --git a/media/java/android/media/audiopolicy/AudioPolicy.java b/media/java/android/media/audiopolicy/AudioPolicy.java
index 255d828..e9dc3af 100644
--- a/media/java/android/media/audiopolicy/AudioPolicy.java
+++ b/media/java/android/media/audiopolicy/AudioPolicy.java
@@ -155,6 +155,7 @@
         {
             throw new IllegalArgumentException("Invalid AudioMix: not defined for loop back");
         }
+        // TODO also check mix is defined for playback or recording, and matches forTrack argument
     }
 
     /**
@@ -175,13 +176,19 @@
             return null;
         }
         checkMixReadyToUse(mix, false/*not for an AudioTrack*/);
+        // create an AudioFormat from the mix format compatible with recording, as the mix
+        // was defined for playback
+        AudioFormat mixFormat = new AudioFormat.Builder(mix.getFormat())
+                .setChannelMask(AudioFormat.inChannelMaskFromOutChannelMask(
+                        mix.getFormat().getChannelMask()))
+                .build();
         // create the AudioRecord, configured for loop back, using the same format as the mix
         AudioRecord ar = new AudioRecord(
                 new AudioAttributes.Builder()
                         .setInternalCapturePreset(MediaRecorder.AudioSource.REMOTE_SUBMIX)
                         .addTag(mix.getRegistration())
                         .build(),
-                mix.getFormat(),
+                mixFormat,
                 AudioRecord.getMinBufferSize(mix.getFormat().getSampleRate(),
                         // using stereo for buffer size to avoid the current poor support for masks
                         AudioFormat.CHANNEL_IN_STEREO, mix.getFormat().getEncoding()),
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 973527f..e13f008 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -104,6 +104,7 @@
     public @interface SessionFlags { }
 
     private final Object mLock = new Object();
+    private final int mMaxBitmapSize;
 
     private final MediaSession.Token mSessionToken;
     private final MediaController mController;
@@ -147,6 +148,8 @@
         if (TextUtils.isEmpty(tag)) {
             throw new IllegalArgumentException("tag cannot be null or empty");
         }
+        mMaxBitmapSize = context.getResources().getDimensionPixelSize(
+                com.android.internal.R.dimen.config_mediaMetadataBitmapMaxSize);
         mCbStub = new CallbackStub(this);
         MediaSessionManager manager = (MediaSessionManager) context
                 .getSystemService(Context.MEDIA_SESSION_SERVICE);
@@ -409,6 +412,7 @@
      * @param metadata The new metadata
      */
     public void setMetadata(@Nullable MediaMetadata metadata) {
+        metadata = (new MediaMetadata.Builder(metadata, mMaxBitmapSize)).build();
         try {
             mBinder.setMetadata(metadata);
         } catch (RemoteException e) {
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 51bd205..de9d54f 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -1012,6 +1012,12 @@
     /**
      * Returns the TvStreamConfig list of the given TV input.
      *
+     * If you are using {@link Hardware} object from {@link
+     * #acquireTvInputHardware}, you should get the list of available streams
+     * from {@link HardwareCallback#onStreamConfigChanged} method, not from
+     * here. This method is designed to be used with {@link #captureFrame} in
+     * capture scenarios specifically and not suitable for any other use.
+     *
      * @param inputId the id of the TV input.
      * @return List of {@link TvStreamConfig} which is available for capturing
      *   of the given TV input.
diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java
index f9d84c1..6fc1b82 100644
--- a/media/java/android/media/tv/TvView.java
+++ b/media/java/android/media/tv/TvView.java
@@ -18,7 +18,10 @@
 
 import android.annotation.SystemApi;
 import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.PorterDuff;
 import android.graphics.Rect;
+import android.graphics.Region;
 import android.media.tv.TvInputManager.Session;
 import android.media.tv.TvInputManager.Session.FinishedInputEventCallback;
 import android.media.tv.TvInputManager.SessionCallback;
@@ -593,6 +596,42 @@
     }
 
     @Override
+    public boolean gatherTransparentRegion(Region region) {
+        if (mWindowZOrder != ZORDER_ON_TOP) {
+            if (region != null) {
+                int width = getWidth();
+                int height = getHeight();
+                if (width > 0 && height > 0) {
+                    int location[] = new int[2];
+                    getLocationInWindow(location);
+                    int left = location[0];
+                    int top = location[1];
+                    region.op(left, top, left + width, top + height, Region.Op.UNION);
+                }
+            }
+        }
+        return super.gatherTransparentRegion(region);
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        if (mWindowZOrder != ZORDER_ON_TOP) {
+            // Punch a hole so that the underlying overlay view and surface can be shown.
+            canvas.drawColor(0, PorterDuff.Mode.CLEAR);
+        }
+        super.draw(canvas);
+    }
+
+    @Override
+    protected void dispatchDraw(Canvas canvas) {
+        if (mWindowZOrder != ZORDER_ON_TOP) {
+            // Punch a hole so that the underlying overlay view and surface can be shown.
+            canvas.drawColor(0, PorterDuff.Mode.CLEAR);
+        }
+        super.dispatchDraw(canvas);
+    }
+
+    @Override
     protected void onVisibilityChanged(View changedView, int visibility) {
         super.onVisibilityChanged(changedView, visibility);
         mSurfaceView.setVisibility(visibility);
diff --git a/packages/Keyguard/test/SampleTrustAgent/res/layout/sample_trust_agent_settings.xml b/packages/Keyguard/test/SampleTrustAgent/res/layout/sample_trust_agent_settings.xml
index 63694a8..00193ed8 100644
--- a/packages/Keyguard/test/SampleTrustAgent/res/layout/sample_trust_agent_settings.xml
+++ b/packages/Keyguard/test/SampleTrustAgent/res/layout/sample_trust_agent_settings.xml
@@ -44,15 +44,21 @@
             android:paddingTop="8dp"
             android:paddingBottom="8dp"
             android:text="Report unlock attempts" />
+    <CheckBox android:id="@+id/report_device_locked"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingTop="8dp"
+            android:paddingBottom="8dp"
+            android:text="Report device locked or unlocked" />
 
     <LinearLayout
             android:layout_width="match_parent"
             android:layout_height="wrap_content">
-        <Button android:id="@+id/check_trusted"
+        <Button android:id="@+id/check_device_locked"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:text="Keyguard in trusted state?" />
-        <TextView android:id="@+id/check_trusted_result"
+                android:text="Device locked?" />
+        <TextView android:id="@+id/check_device_locked_result"
                 android:layout_width="0dp"
                 android:layout_height="wrap_content"
                 android:layout_weight="1" />
diff --git a/packages/Keyguard/test/SampleTrustAgent/src/com/android/trustagent/test/SampleTrustAgent.java b/packages/Keyguard/test/SampleTrustAgent/src/com/android/trustagent/test/SampleTrustAgent.java
index f28d0e4..e6a0dd7 100644
--- a/packages/Keyguard/test/SampleTrustAgent/src/com/android/trustagent/test/SampleTrustAgent.java
+++ b/packages/Keyguard/test/SampleTrustAgent/src/com/android/trustagent/test/SampleTrustAgent.java
@@ -56,6 +56,7 @@
             = "preference.report_unlock_attempts";
     private static final String PREFERENCE_MANAGING_TRUST
             = "preference.managing_trust";
+    private static final String PREFERENCE_REPORT_DEVICE_LOCKED = "preference.report_device_locked";
 
     private static final String TAG = "SampleTrustAgent";
 
@@ -80,17 +81,37 @@
     @Override
     public void onTrustTimeout() {
         super.onTrustTimeout();
-        Toast.makeText(this, "onTrustTimeout(): timeout expired", Toast.LENGTH_SHORT).show();
+        logAndShowToast("onTrustTimeout(): timeout expired");
+    }
+
+    @Override
+    public void onDeviceLocked() {
+        super.onDeviceLocked();
+        if (getReportDeviceLocked(this)) {
+            logAndShowToast("onDeviceLocked(): device is now locked");
+        }
+    }
+
+    @Override
+    public void onDeviceUnlocked() {
+        super.onDeviceUnlocked();
+        if (getReportDeviceLocked(this)) {
+            logAndShowToast("onDeviceUnlocked(): device is now unlocked");
+        }
     }
 
     @Override
     public void onUnlockAttempt(boolean successful) {
         if (getReportUnlockAttempts(this)) {
-            Toast.makeText(this, "onUnlockAttempt(successful=" + successful + ")",
-                    Toast.LENGTH_SHORT).show();
+            logAndShowToast("onUnlockAttempt(successful=" + successful + ")");
         }
     }
 
+    private void logAndShowToast(String text) {
+        Log.i(TAG, text);
+        Toast.makeText(this, text, Toast.LENGTH_SHORT).show();
+    }
+
     @Override
     public boolean onConfigure(List<PersistableBundle> options) {
         if (options != null) {
@@ -125,8 +146,7 @@
                             intent.getLongExtra(EXTRA_DURATION, 0),
                             intent.getBooleanExtra(EXTRA_INITIATED_BY_USER, false));
                 } catch (IllegalStateException e) {
-                    Toast.makeText(context,
-                            "IllegalStateException: " + e.getMessage(), Toast.LENGTH_SHORT).show();
+                    logAndShowToast("IllegalStateException: " + e.getMessage());
                 }
             } else if (ACTION_REVOKE_TRUST.equals(action)) {
                 revokeTrust();
@@ -160,6 +180,18 @@
         return sharedPreferences.getBoolean(PREFERENCE_REPORT_UNLOCK_ATTEMPTS, false);
     }
 
+    public static void setReportDeviceLocked(Context context, boolean enabled) {
+        SharedPreferences sharedPreferences = PreferenceManager
+                .getDefaultSharedPreferences(context);
+        sharedPreferences.edit().putBoolean(PREFERENCE_REPORT_DEVICE_LOCKED, enabled).apply();
+    }
+
+    public static boolean getReportDeviceLocked(Context context) {
+        SharedPreferences sharedPreferences = PreferenceManager
+                .getDefaultSharedPreferences(context);
+        return sharedPreferences.getBoolean(PREFERENCE_REPORT_DEVICE_LOCKED, false);
+    }
+
     public static void setIsManagingTrust(Context context, boolean enabled) {
         SharedPreferences sharedPreferences = PreferenceManager
                 .getDefaultSharedPreferences(context);
diff --git a/packages/Keyguard/test/SampleTrustAgent/src/com/android/trustagent/test/SampleTrustAgentSettings.java b/packages/Keyguard/test/SampleTrustAgent/src/com/android/trustagent/test/SampleTrustAgentSettings.java
index 39a599e..29b15cb 100644
--- a/packages/Keyguard/test/SampleTrustAgent/src/com/android/trustagent/test/SampleTrustAgentSettings.java
+++ b/packages/Keyguard/test/SampleTrustAgent/src/com/android/trustagent/test/SampleTrustAgentSettings.java
@@ -31,8 +31,9 @@
     private static final int TRUST_DURATION_MS = 30 * 1000;
 
     private CheckBox mReportUnlockAttempts;
+    private CheckBox mReportDeviceLocked;
     private CheckBox mManagingTrust;
-    private TextView mCheckTrustedStateResult;
+    private TextView mCheckDeviceLockedResult;
 
     private KeyguardManager mKeyguardManager;
 
@@ -48,15 +49,18 @@
         findViewById(R.id.enable_trust).setOnClickListener(this);
         findViewById(R.id.revoke_trust).setOnClickListener(this);
         findViewById(R.id.crash).setOnClickListener(this);
-        findViewById(R.id.check_trusted).setOnClickListener(this);
+        findViewById(R.id.check_device_locked).setOnClickListener(this);
 
         mReportUnlockAttempts = (CheckBox) findViewById(R.id.report_unlock_attempts);
         mReportUnlockAttempts.setOnCheckedChangeListener(this);
 
+        mReportDeviceLocked = (CheckBox) findViewById(R.id.report_device_locked);
+        mReportDeviceLocked.setOnCheckedChangeListener(this);
+
         mManagingTrust = (CheckBox) findViewById(R.id.managing_trust);
         mManagingTrust.setOnCheckedChangeListener(this);
 
-        mCheckTrustedStateResult = (TextView) findViewById(R.id.check_trusted_result);
+        mCheckDeviceLockedResult = (TextView) findViewById(R.id.check_device_locked_result);
     }
 
     @Override
@@ -77,7 +81,7 @@
             SampleTrustAgent.sendRevokeTrust(this);
         } else if (id == R.id.crash) {
             throw new RuntimeException("crash");
-        } else if (id == R.id.check_trusted) {
+        } else if (id == R.id.check_device_locked) {
             updateTrustedState();
         }
     }
@@ -88,11 +92,13 @@
             SampleTrustAgent.setReportUnlockAttempts(this, isChecked);
         } else if (buttonView == mManagingTrust) {
             SampleTrustAgent.setIsManagingTrust(this, isChecked);
+        } else if (buttonView == mReportDeviceLocked) {
+            SampleTrustAgent.setReportDeviceLocked(this, isChecked);
         }
     }
 
     private void updateTrustedState() {
-        mCheckTrustedStateResult.setText(Boolean.toString(
-                mKeyguardManager.isKeyguardInTrustedState()));
+        mCheckDeviceLockedResult.setText(Boolean.toString(
+                mKeyguardManager.isDeviceLocked()));
     }
 }
diff --git a/packages/SystemUI/res/layout-sw600dp/navigation_bar.xml b/packages/SystemUI/res/layout-sw600dp/navigation_bar.xml
index 5c776e6..456d2f9 100644
--- a/packages/SystemUI/res/layout-sw600dp/navigation_bar.xml
+++ b/packages/SystemUI/res/layout-sw600dp/navigation_bar.xml
@@ -57,6 +57,7 @@
                 android:layout_width="128dp" android:paddingStart="25dp" android:paddingEnd="25dp"
                 android:layout_height="match_parent"
                 android:src="@drawable/ic_sysbar_back"
+                android:scaleType="centerInside"
                 systemui:keyCode="4"
                 android:layout_weight="0"
                 android:contentDescription="@string/accessibility_back"
@@ -65,6 +66,7 @@
                 android:layout_width="128dp" android:paddingStart="25dp" android:paddingEnd="25dp"
                 android:layout_height="match_parent"
                 android:src="@drawable/ic_sysbar_home"
+                android:scaleType="centerInside"
                 systemui:keyCode="3"
                 systemui:keyRepeat="true"
                 android:layout_weight="0"
@@ -74,6 +76,7 @@
                 android:layout_width="128dp" android:paddingStart="25dp" android:paddingEnd="25dp"
                 android:layout_height="match_parent"
                 android:src="@drawable/ic_sysbar_recent"
+                android:scaleType="centerInside"
                 android:layout_weight="0"
                 android:contentDescription="@string/accessibility_recent"
                 />
@@ -91,6 +94,7 @@
                     android:layout_width="@dimen/navigation_extra_key_width"
                     android:layout_height="match_parent"
                     android:src="@drawable/ic_sysbar_menu"
+                    android:scaleType="centerInside"
                     android:layout_marginEnd="2dp"
                     systemui:keyCode="82"
                     android:visibility="invisible"
@@ -198,6 +202,7 @@
                 android:layout_width="162dp" android:paddingStart="42dp" android:paddingEnd="42dp"
                 android:layout_height="match_parent"
                 android:src="@drawable/ic_sysbar_back"
+                android:scaleType="centerInside"
                 systemui:keyCode="4"
                 android:layout_weight="0"
                 android:contentDescription="@string/accessibility_back"
@@ -206,6 +211,7 @@
                 android:layout_width="162dp" android:paddingStart="42dp" android:paddingEnd="42dp"
                 android:layout_height="match_parent"
                 android:src="@drawable/ic_sysbar_home"
+                android:scaleType="centerInside"
                 systemui:keyCode="3"
                 systemui:keyRepeat="true"
                 android:layout_weight="0"
@@ -215,6 +221,7 @@
                 android:layout_width="162dp" android:paddingStart="42dp" android:paddingEnd="42dp"
                 android:layout_height="match_parent"
                 android:src="@drawable/ic_sysbar_recent"
+                android:scaleType="centerInside"
                 android:layout_weight="0"
                 android:contentDescription="@string/accessibility_recent"
                 />
@@ -233,6 +240,7 @@
                     android:layout_height="match_parent"
                     android:layout_marginEnd="2dp"
                     android:src="@drawable/ic_sysbar_menu"
+                    android:scaleType="centerInside"
                     systemui:keyCode="82"
                     android:visibility="invisible"
                     android:contentDescription="@string/accessibility_menu" />
diff --git a/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml b/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml
index ef85847..4526af5 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml
@@ -32,6 +32,7 @@
         />
 
     <LinearLayout
+        android:id="@+id/content"
         android:layout_width="match_parent"
         android:layout_height="match_parent">
         <TextView
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 1152eea..d799721 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -232,6 +232,9 @@
     <!-- Doze: should the pickup sensor be used as a pulse signal? -->
     <bool name="doze_pulse_on_pick_up">false</bool>
 
+    <!-- Doze: check proximity sensor before pulsing? -->
+    <bool name="doze_proximity_check_before_pulse">true</bool>
+
     <!-- Doze: should notifications be used as a pulse signal? -->
     <bool name="doze_pulse_on_notifications">true</bool>
 
diff --git a/packages/SystemUI/src/com/android/systemui/ViewInvertHelper.java b/packages/SystemUI/src/com/android/systemui/ViewInvertHelper.java
new file mode 100644
index 0000000..2ff8f8a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/ViewInvertHelper.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
+import android.graphics.Paint;
+import android.view.View;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+
+import com.android.systemui.statusbar.phone.NotificationPanelView;
+
+/**
+ * Helper to invert the colors of views and fade between the states.
+ */
+public class ViewInvertHelper {
+
+    private final Paint mDarkPaint = new Paint();
+    private final Interpolator mLinearOutSlowInInterpolator;
+    private final View mTarget;
+    private final ColorMatrix mMatrix = new ColorMatrix();
+    private final ColorMatrix mGrayscaleMatrix = new ColorMatrix();
+    private final long mFadeDuration;
+
+    public ViewInvertHelper(View target, long fadeDuration) {
+        mTarget = target;
+        mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(mTarget.getContext(),
+                android.R.interpolator.linear_out_slow_in);
+        mFadeDuration = fadeDuration;
+    }
+
+    public void fade(final boolean invert, long delay) {
+        float startIntensity = invert ? 0f : 1f;
+        float endIntensity = invert ? 1f : 0f;
+        ValueAnimator animator = ValueAnimator.ofFloat(startIntensity, endIntensity);
+        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                updateInvertPaint((Float) animation.getAnimatedValue());
+                mTarget.setLayerType(View.LAYER_TYPE_HARDWARE, mDarkPaint);
+            }
+        });
+        animator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                if (!invert) {
+                    mTarget.setLayerType(View.LAYER_TYPE_NONE, null);
+                }
+            }
+        });
+        animator.setDuration(mFadeDuration);
+        animator.setInterpolator(mLinearOutSlowInInterpolator);
+        animator.setStartDelay(delay);
+        animator.start();
+    }
+
+    public void update(boolean invert) {
+        if (invert) {
+            updateInvertPaint(1f);
+            mTarget.setLayerType(View.LAYER_TYPE_HARDWARE, mDarkPaint);
+        } else {
+            mTarget.setLayerType(View.LAYER_TYPE_NONE, null);
+        }
+    }
+
+    public View getTarget() {
+        return mTarget;
+    }
+
+    private void updateInvertPaint(float intensity) {
+        float components = 1 - 2 * intensity;
+        final float[] invert = {
+                components, 0f,         0f,         0f, 255f * intensity,
+                0f,         components, 0f,         0f, 255f * intensity,
+                0f,         0f,         components, 0f, 255f * intensity,
+                0f,         0f,         0f,         1f, 0f
+        };
+        mMatrix.set(invert);
+        mGrayscaleMatrix.setSaturation(1 - intensity);
+        mMatrix.preConcat(mGrayscaleMatrix);
+        mDarkPaint.setColorFilter(new ColorMatrixColorFilter(mMatrix));
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
index 8f14abb..89b3e5b 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
@@ -202,6 +202,12 @@
             // Here we need a wakelock to stay awake until the pulse is finished.
             mWakeLock.acquire();
             mPulsing = true;
+            if (!mDozeParameters.getProxCheckBeforePulse()) {
+                // skip proximity check
+                continuePulsing();
+                return;
+            }
+            // perform a proximity check before pulsing
             final long start = SystemClock.uptimeMillis();
             new ProximityCheck() {
                 @Override
@@ -216,28 +222,32 @@
                     }
 
                     // not in-pocket, continue pulsing
-                    mHost.pulseWhileDozing(new DozeHost.PulseCallback() {
-                        @Override
-                        public void onPulseStarted() {
-                            if (mPulsing && mDreaming) {
-                                turnDisplayOn();
-                            }
-                        }
-
-                        @Override
-                        public void onPulseFinished() {
-                            if (mPulsing && mDreaming) {
-                                mPulsing = false;
-                                turnDisplayOff();
-                            }
-                            mWakeLock.release(); // needs to be unconditional to balance acquire
-                        }
-                    });
+                    continuePulsing();
                 }
             }.check();
         }
     }
 
+    private void continuePulsing() {
+        mHost.pulseWhileDozing(new DozeHost.PulseCallback() {
+            @Override
+            public void onPulseStarted() {
+                if (mPulsing && mDreaming) {
+                    turnDisplayOn();
+                }
+            }
+
+            @Override
+            public void onPulseFinished() {
+                if (mPulsing && mDreaming) {
+                    mPulsing = false;
+                    turnDisplayOff();
+                }
+                mWakeLock.release(); // needs to be unconditional to balance acquire
+            }
+        });
+    }
+
     private void turnDisplayOff() {
         if (DEBUG) Log.d(mTag, "Display off");
         setDozeScreenState(Display.STATE_OFF);
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 20e418c..5f92dc6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -23,6 +23,7 @@
 import android.app.PendingIntent;
 import android.app.SearchManager;
 import android.app.StatusBarManager;
+import android.app.trust.TrustManager;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -187,8 +188,9 @@
     /** High level access to the window manager for dismissing keyguard animation */
     private IWindowManager mWM;
 
-    /** UserManager for querying number of users */
-    private UserManager mUserManager;
+
+    /** TrustManager for letting it know when we change visibility */
+    private TrustManager mTrustManager;
 
     /** SearchManager for determining whether or not search assistant is available */
     private SearchManager mSearchManager;
@@ -484,7 +486,8 @@
     private void setup() {
         mPM = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
         mWM = WindowManagerGlobal.getWindowManagerService();
-        mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+        mTrustManager = (TrustManager) mContext.getSystemService(Context.TRUST_SERVICE);
+
         mShowKeyguardWakeLock = mPM.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "show keyguard");
         mShowKeyguardWakeLock.setReferenceCounted(false);
 
@@ -501,6 +504,7 @@
 
         // Assume keyguard is showing (unless it's disabled) until we know for sure...
         mShowing = !shouldWaitForProvisioning() && !mLockPatternUtils.isLockScreenDisabled();
+        mTrustManager.reportKeyguardShowingChanged();
 
         mStatusBarKeyguardViewManager = new StatusBarKeyguardViewManager(mContext,
                 mViewMediatorCallback, mLockPatternUtils);
@@ -931,7 +935,7 @@
         if (mLockPatternUtils.checkVoldPassword()) {
             if (DEBUG) Log.d(TAG, "Not showing lock screen since just decrypted");
             // Without this, settings is not enabled until the lock screen first appears
-            mShowing = false;
+            setShowing(false);
             hideLocked();
             return;
         }
@@ -1249,7 +1253,7 @@
 
             mStatusBarKeyguardViewManager.show(options);
             mHiding = false;
-            mShowing = true;
+            setShowing(true);
             resetKeyguardDonePendingLocked();
             mHideAnimationRun = false;
             updateActivityLockScreenState();
@@ -1328,7 +1332,7 @@
             }
 
             mStatusBarKeyguardViewManager.hide(startTime, fadeoutDuration);
-            mShowing = false;
+            setShowing(false);
             resetKeyguardDonePendingLocked();
             mHideAnimationRun = false;
             updateActivityLockScreenState();
@@ -1389,7 +1393,7 @@
         synchronized (KeyguardViewMediator.this) {
             if (DEBUG) Log.d(TAG, "handleVerifyUnlock");
             mStatusBarKeyguardViewManager.verifyUnlock();
-            mShowing = true;
+            setShowing(true);
             updateActivityLockScreenState();
         }
     }
@@ -1471,4 +1475,12 @@
             this.fadeoutDuration = fadeoutDuration;
         }
     }
+
+    private void setShowing(boolean showing) {
+        boolean changed = (showing != mShowing);
+        mShowing = showing;
+        if (changed) {
+            mTrustManager.reportKeyguardShowingChanged();
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/SecureSetting.java b/packages/SystemUI/src/com/android/systemui/qs/SecureSetting.java
index 0ab6626..4f812bc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/SecureSetting.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/SecureSetting.java
@@ -26,24 +26,26 @@
 
 /** Helper for managing a secure setting. **/
 public abstract class SecureSetting extends ContentObserver implements Listenable {
+    private static final int DEFAULT = 0;
+
     private final Context mContext;
     private final String mSettingName;
 
     private boolean mListening;
     private int mUserId;
+    private int mObservedValue = DEFAULT;
 
-    protected abstract void handleValueChanged(int value);
+    protected abstract void handleValueChanged(int value, boolean observedChange);
 
     public SecureSetting(Context context, Handler handler, String settingName) {
         super(handler);
         mContext = context;
         mSettingName = settingName;
         mUserId = ActivityManager.getCurrentUser();
-        setListening(true);
     }
 
     public int getValue() {
-        return Secure.getIntForUser(mContext.getContentResolver(), mSettingName, 0, mUserId);
+        return Secure.getIntForUser(mContext.getContentResolver(), mSettingName, DEFAULT, mUserId);
     }
 
     public void setValue(int value) {
@@ -52,18 +54,23 @@
 
     @Override
     public void setListening(boolean listening) {
+        if (listening == mListening) return;
         mListening = listening;
         if (listening) {
+            mObservedValue = getValue();
             mContext.getContentResolver().registerContentObserver(
                     Secure.getUriFor(mSettingName), false, this, mUserId);
         } else {
             mContext.getContentResolver().unregisterContentObserver(this);
+            mObservedValue = DEFAULT;
         }
     }
 
     @Override
     public void onChange(boolean selfChange) {
-        handleValueChanged(getValue());
+        final int value = getValue();
+        handleValueChanged(value, value != mObservedValue);
+        mObservedValue = value;
     }
 
     public void setUserId(int userId) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
index b565afa..5963a45e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
@@ -41,8 +41,10 @@
         mSetting = new SecureSetting(mContext, mHandler,
                 Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED) {
             @Override
-            protected void handleValueChanged(int value) {
-                mUsageTracker.trackUsage();
+            protected void handleValueChanged(int value, boolean observedChange) {
+                if (value != 0 || observedChange) {
+                    mUsageTracker.trackUsage();
+                }
                 if (mListening) {
                     handleRefreshState(value);
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
index bb9a105..38ce467 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
@@ -32,6 +32,7 @@
 import android.graphics.Canvas;
 import android.graphics.Rect;
 import android.os.Handler;
+import android.os.SystemClock;
 import android.os.UserHandle;
 import android.util.Pair;
 import android.view.LayoutInflater;
@@ -361,8 +362,7 @@
         // If the user has toggled it too quickly, then just eat up the event here (it's better than
         // showing a janky screenshot).
         // NOTE: Ideally, the screenshot mechanism would take the window transform into account
-        long currentTime = System.currentTimeMillis();
-        if ((currentTime > mLastToggleTime) && (currentTime - mLastToggleTime) < sMinToggleDelay) {
+        if ((SystemClock.elapsedRealtime() - mLastToggleTime) < sMinToggleDelay) {
             return;
         }
 
@@ -377,7 +377,7 @@
             intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT |
                     Intent.FLAG_RECEIVER_FOREGROUND);
             mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
-            mLastToggleTime = System.currentTimeMillis();
+            mLastToggleTime = SystemClock.elapsedRealtime();
             return;
         } else {
             // Otherwise, start the recents activity
@@ -559,7 +559,7 @@
                 startAlternateRecentsActivity(topTask, opts, EXTRA_FROM_HOME, stackVr);
             }
         }
-        mLastToggleTime = System.currentTimeMillis();
+        mLastToggleTime = SystemClock.elapsedRealtime();
     }
 
     /** Starts the recents activity */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index 676f1ab..6dc2edb 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -28,6 +28,7 @@
 import android.content.IntentFilter;
 import android.content.SharedPreferences;
 import android.os.Bundle;
+import android.os.SystemClock;
 import android.os.UserHandle;
 import android.util.Pair;
 import android.view.KeyEvent;
@@ -564,13 +565,13 @@
     public boolean onKeyDown(int keyCode, KeyEvent event) {
         switch (keyCode) {
             case KeyEvent.KEYCODE_TAB: {
-                boolean hasRepKeyTimeElapsed = (System.currentTimeMillis() -
+                boolean hasRepKeyTimeElapsed = (SystemClock.elapsedRealtime() -
                         mLastTabKeyEventTime) > mConfig.altTabKeyDelay;
                 if (event.getRepeatCount() <= 0 || hasRepKeyTimeElapsed) {
                     // Focus the next task in the stack
                     final boolean backward = event.isShiftPressed();
                     mRecentsView.focusNextTask(!backward);
-                    mLastTabKeyEventTime = System.currentTimeMillis();
+                    mLastTabKeyEventTime = SystemClock.elapsedRealtime();
                 }
                 return true;
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
index 02b9378..7b60307 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
@@ -43,6 +43,7 @@
 import android.view.animation.PathInterpolator;
 
 import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.NotificationPanelView;
 
 /**
  * Base class for both {@link ExpandableNotificationRow} and {@link NotificationOverflowContainer}
@@ -53,6 +54,7 @@
     private static final long DOUBLETAP_TIMEOUT_MS = 1200;
     private static final int BACKGROUND_ANIMATION_LENGTH_MS = 220;
     private static final int ACTIVATE_ANIMATION_LENGTH = 220;
+    private static final int DARK_ANIMATION_LENGTH = 170;
 
     /**
      * The amount of width, which is kept in the end when performing a disappear animation (also
@@ -84,6 +86,11 @@
      */
     private static final float VERTICAL_ANIMATION_START = 1.0f;
 
+    /**
+     * Scale for the background to animate from when exiting dark mode.
+     */
+    private static final float DARK_EXIT_SCALE_START = 0.93f;
+
     private static final Interpolator ACTIVATE_INVERSE_INTERPOLATOR
             = new PathInterpolator(0.6f, 0, 0.5f, 1);
     private static final Interpolator ACTIVATE_INVERSE_ALPHA_INTERPOLATOR
@@ -94,7 +101,6 @@
 
     private boolean mDimmed;
     private boolean mDark;
-    private final Paint mDarkPaint = createDarkPaint();
 
     private int mBgTint = 0;
     private final int mRoundedRectCornerRadius;
@@ -332,40 +338,32 @@
         if (mDimmed != dimmed) {
             mDimmed = dimmed;
             if (fade) {
-                fadeBackground();
+                fadeDimmedBackground();
             } else {
                 updateBackground();
             }
         }
     }
 
-    public void setDark(boolean dark, boolean fade) {
-        // TODO implement fade
-        if (mDark != dark) {
-            mDark = dark;
-            if (mDark) {
-                setLayerType(View.LAYER_TYPE_HARDWARE, mDarkPaint);
-            } else {
-                setLayerType(View.LAYER_TYPE_NONE, null);
-            }
+    public void setDark(boolean dark, boolean fade, long delay) {
+        super.setDark(dark, fade, delay);
+        if (mDark == dark) {
+            return;
         }
-    }
-
-    private static Paint createDarkPaint() {
-        final Paint p = new Paint();
-        final float[] invert = {
-            -1f,  0f,  0f, 1f, 1f,
-             0f, -1f,  0f, 1f, 1f,
-             0f,  0f, -1f, 1f, 1f,
-             0f,  0f,  0f, 1f, 0f
-        };
-        final ColorMatrix m = new ColorMatrix(invert);
-        final ColorMatrix grayscale = new ColorMatrix();
-        grayscale.setSaturation(0);
-        m.preConcat(grayscale);
-        p.setColorFilter(new ColorMatrixColorFilter(m));
-        return p;
-    }
+        mDark = dark;
+        if (!dark && fade) {
+            if (mActivated) {
+                mBackgroundDimmed.setVisibility(View.VISIBLE);
+                mBackgroundNormal.setVisibility(View.VISIBLE);
+            } else {
+                mBackgroundDimmed.setVisibility(View.VISIBLE);
+                mBackgroundNormal.setVisibility(View.INVISIBLE);
+            }
+            fadeDarkToDimmed(delay);
+        } else {
+            updateBackground();
+        }
+     }
 
     public void setShowingLegacyBackground(boolean showing) {
         mShowingLegacyBackground = showing;
@@ -402,7 +400,39 @@
         mBackgroundNormal.setRippleColor(rippleColor);
     }
 
-    private void fadeBackground() {
+    /**
+     * Fades the dimmed background when exiting dark mode.
+     */
+    private void fadeDarkToDimmed(long delay) {
+        mBackgroundDimmed.setAlpha(0f);
+        mBackgroundDimmed.setPivotX(mBackgroundDimmed.getWidth() / 2f);
+        mBackgroundDimmed.setPivotY(getActualHeight() / 2f);
+        mBackgroundDimmed.setScaleX(DARK_EXIT_SCALE_START);
+        mBackgroundDimmed.setScaleY(DARK_EXIT_SCALE_START);
+        mBackgroundDimmed.animate()
+                .alpha(1f)
+                .scaleX(1f)
+                .scaleY(1f)
+                .setDuration(DARK_ANIMATION_LENGTH)
+                .setStartDelay(delay)
+                .setInterpolator(mLinearOutSlowInInterpolator)
+                .setListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationCancel(Animator animation) {
+                        // Jump state if we are cancelled
+                        mBackgroundDimmed.setScaleX(1f);
+                        mBackgroundDimmed.setScaleY(1f);
+                        mBackgroundDimmed.setAlpha(1f);
+                    }
+                })
+                .start();
+    }
+
+    /**
+     * Fades the background when the dimmed state changes.
+     */
+    private void fadeDimmedBackground() {
+        mBackgroundDimmed.animate().cancel();
         mBackgroundNormal.animate().cancel();
         if (mDimmed) {
             mBackgroundDimmed.setVisibility(View.VISIBLE);
@@ -443,11 +473,14 @@
     }
 
     private void updateBackground() {
-        if (mDimmed) {
+        cancelFadeAnimations();
+        if (mDark) {
+            mBackgroundDimmed.setVisibility(View.INVISIBLE);
+            mBackgroundNormal.setVisibility(View.INVISIBLE);
+        } else if (mDimmed) {
             mBackgroundDimmed.setVisibility(View.VISIBLE);
             mBackgroundNormal.setVisibility(View.INVISIBLE);
         } else {
-            cancelFadeAnimations();
             mBackgroundDimmed.setVisibility(View.INVISIBLE);
             mBackgroundNormal.setVisibility(View.VISIBLE);
             mBackgroundNormal.setAlpha(1f);
@@ -459,6 +492,7 @@
         if (mBackgroundAnimator != null) {
             mBackgroundAnimator.cancel();
         }
+        mBackgroundDimmed.animate().cancel();
         mBackgroundNormal.animate().cancel();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 7345440..8ad8406 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -207,11 +207,11 @@
     }
 
     @Override
-    public void setDark(boolean dark, boolean fade) {
-        super.setDark(dark, fade);
+    public void setDark(boolean dark, boolean fade, long delay) {
+        super.setDark(dark, fade, delay);
         final NotificationContentView showing = getShowingLayout();
         if (showing != null) {
-            showing.setDark(dark, fade);
+            showing.setDark(dark, fade, delay);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
index bf1e78e..ebc663c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
@@ -39,6 +39,7 @@
     private int mActualHeight;
     protected int mClipTopAmount;
     private boolean mActualHeightInitialized;
+    private boolean mDark;
     private ArrayList<View> mMatchParentViews = new ArrayList<View>();
 
     public ExpandableView(Context context, AttributeSet attrs) {
@@ -185,8 +186,14 @@
      *
      * @param dark Whether the notification should be dark.
      * @param fade Whether an animation should be played to change the state.
+     * @param delay If fading, the delay of the animation.
      */
-    public void setDark(boolean dark, boolean fade) {
+    public void setDark(boolean dark, boolean fade, long delay) {
+        mDark = dark;
+    }
+
+    public boolean isDark() {
+        return mDark;
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index 99214a0..27da6fd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -42,14 +42,14 @@
 public class NotificationContentView extends FrameLayout {
 
     private static final long ANIMATION_DURATION_LENGTH = 170;
-    private static final Paint INVERT_PAINT = createInvertPaint();
-    private static final ColorFilter NO_COLOR_FILTER = new ColorFilter();
 
     private final Rect mClipBounds = new Rect();
 
     private View mContractedChild;
     private View mExpandedChild;
 
+    private NotificationViewWrapper mContractedWrapper;
+
     private int mSmallHeight;
     private int mClipTopAmount;
     private int mActualHeight;
@@ -84,8 +84,8 @@
     }
 
     @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
         updateVisibility();
     }
 
@@ -122,6 +122,7 @@
         sanitizeContractedLayoutParams(child);
         addView(child);
         mContractedChild = child;
+        mContractedWrapper = NotificationViewWrapper.wrap(getContext(), child);
         selectLayout(false /* animate */, true /* force */);
     }
 
@@ -249,38 +250,10 @@
         return mExpandedChild != null;
     }
 
-    public void setDark(boolean dark, boolean fade) {
+    public void setDark(boolean dark, boolean fade, long delay) {
         if (mDark == dark || mContractedChild == null) return;
         mDark = dark;
-        setImageViewDark(dark, fade, com.android.internal.R.id.right_icon);
-        setImageViewDark(dark, fade, com.android.internal.R.id.icon);
-    }
-
-    private void setImageViewDark(boolean dark, boolean fade, int imageViewId) {
-        // TODO: implement fade
-        final ImageView v = (ImageView) mContractedChild.findViewById(imageViewId);
-        if (v == null) return;
-        final Drawable d = v.getBackground();
-        if (dark) {
-            v.setLayerType(LAYER_TYPE_HARDWARE, INVERT_PAINT);
-            if (d != null) {
-                v.setTag(R.id.doze_saved_filter_tag, d.getColorFilter() != null ? d.getColorFilter()
-                        : NO_COLOR_FILTER);
-                d.setColorFilter(getResources().getColor(R.color.doze_small_icon_background_color),
-                        PorterDuff.Mode.SRC_ATOP);
-                v.setImageAlpha(getResources().getInteger(R.integer.doze_small_icon_alpha));
-            }
-        } else {
-            v.setLayerType(LAYER_TYPE_NONE, null);
-            if (d != null)  {
-                final ColorFilter filter = (ColorFilter) v.getTag(R.id.doze_saved_filter_tag);
-                if (filter != null) {
-                    d.setColorFilter(filter == NO_COLOR_FILTER ? null : filter);
-                    v.setTag(R.id.doze_saved_filter_tag, null);
-                }
-                v.setImageAlpha(0xff);
-            }
-        }
+        mContractedWrapper.setDark(dark, fade, delay);
     }
 
     @Override
@@ -290,16 +263,4 @@
         // layout, and saves us some layers.
         return false;
     }
-
-    private static Paint createInvertPaint() {
-        final Paint p = new Paint();
-        final float[] invert = {
-            -1f,  0f,  0f, 1f, 1f,
-             0f, -1f,  0f, 1f, 1f,
-             0f,  0f, -1f, 1f, 1f,
-             0f,  0f,  0f, 1f, 0f
-        };
-        p.setColorFilter(new ColorMatrixColorFilter(new ColorMatrix(invert)));
-        return p;
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationCustomViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationCustomViewWrapper.java
new file mode 100644
index 0000000..045be3e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationCustomViewWrapper.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar;
+
+import android.view.View;
+
+import com.android.systemui.ViewInvertHelper;
+import com.android.systemui.statusbar.phone.NotificationPanelView;
+
+/**
+ * Wraps a notification containing a custom view.
+ */
+public class NotificationCustomViewWrapper extends NotificationViewWrapper {
+
+    private final ViewInvertHelper mInvertHelper;
+    private boolean mDark;
+
+    protected NotificationCustomViewWrapper(View view) {
+        super(view);
+        mInvertHelper = new ViewInvertHelper(view, NotificationPanelView.DOZE_ANIMATION_DURATION);
+    }
+
+    @Override
+    public void setDark(boolean dark, boolean fade, long delay) {
+        if (mDark != dark) {
+            mDark = dark;
+            if (fade) {
+                mInvertHelper.fade(dark, delay);
+            } else {
+                mInvertHelper.update(dark);
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaViewWrapper.java
new file mode 100644
index 0000000..8f63a79
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaViewWrapper.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar;
+
+import android.content.Context;
+import android.view.View;
+
+/**
+ * Wraps a media notification.
+ */
+public class NotificationMediaViewWrapper extends NotificationTemplateViewWrapper {
+
+    private boolean mDark;
+
+    protected NotificationMediaViewWrapper(Context ctx, View view) {
+        super(ctx, view);
+    }
+
+    @Override
+    public void setDark(boolean dark, boolean fade, long delay) {
+        if (mDark != dark) {
+            mDark = dark;
+
+            // Only update the large icon, because the rest is already inverted.
+            setPictureGrayscale(dark, fade, delay);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java
index edfd205..bfa3aa5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java
@@ -21,6 +21,8 @@
 import android.widget.TextView;
 
 import com.android.systemui.R;
+import com.android.systemui.ViewInvertHelper;
+import com.android.systemui.statusbar.phone.NotificationPanelView;
 
 /**
  * Container view for overflowing notification icons on Keyguard.
@@ -28,6 +30,8 @@
 public class NotificationOverflowContainer extends ActivatableNotificationView {
 
     private NotificationOverflowIconsView mIconsView;
+    private ViewInvertHelper mViewInvertHelper;
+    private boolean mDark;
 
     public NotificationOverflowContainer(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -39,6 +43,20 @@
         mIconsView = (NotificationOverflowIconsView) findViewById(R.id.overflow_icons_view);
         mIconsView.setMoreText((TextView) findViewById(R.id.more_text));
         mIconsView.setOverflowIndicator(findViewById(R.id.more_icon_overflow));
+        mViewInvertHelper = new ViewInvertHelper(findViewById(R.id.content),
+                NotificationPanelView.DOZE_ANIMATION_DURATION);
+    }
+
+    @Override
+    public void setDark(boolean dark, boolean fade, long delay) {
+        super.setDark(dark, fade, delay);
+        if (mDark == dark) return;
+        mDark = dark;
+        if (fade) {
+            mViewInvertHelper.fade(dark, delay);
+        } else {
+            mViewInvertHelper.update(dark);
+        }
     }
 
     public NotificationOverflowIconsView getIconsView() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationTemplateViewWrapper.java
new file mode 100644
index 0000000..8dc14b0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationTemplateViewWrapper.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
+import android.view.View;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+import android.widget.ImageView;
+
+import com.android.systemui.R;
+import com.android.systemui.ViewInvertHelper;
+import com.android.systemui.statusbar.phone.NotificationPanelView;
+
+/**
+ * Wraps a notification view inflated from a template.
+ */
+public class NotificationTemplateViewWrapper extends NotificationViewWrapper {
+
+    private final ViewInvertHelper mInvertHelper;
+    private final ImageView mIcon;
+    protected final ImageView mPicture;
+    private final ColorMatrix mGrayscaleColorMatrix = new ColorMatrix();
+    private final PorterDuffColorFilter mIconColorFilter = new PorterDuffColorFilter(
+            0, PorterDuff.Mode.SRC_ATOP);
+    private final int mIconDarkAlpha;
+    private final int mIconBackgroundColor;
+    private final int mIconBackgroundDarkColor;
+    private final Interpolator mLinearOutSlowInInterpolator;
+
+    private boolean mDark;
+
+    protected NotificationTemplateViewWrapper(Context ctx, View view) {
+        super(view);
+        mIconDarkAlpha = ctx.getResources().getInteger(R.integer.doze_small_icon_alpha);
+        mIconBackgroundDarkColor =
+                ctx.getResources().getColor(R.color.doze_small_icon_background_color);
+        mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(ctx,
+                android.R.interpolator.linear_out_slow_in);
+        View mainColumn = view.findViewById(com.android.internal.R.id.notification_main_column);
+        mInvertHelper = mainColumn != null
+                ? new ViewInvertHelper(mainColumn, NotificationPanelView.DOZE_ANIMATION_DURATION)
+                : null;
+        ImageView largeIcon = (ImageView) view.findViewById(com.android.internal.R.id.icon);
+        ImageView rightIcon = (ImageView) view.findViewById(com.android.internal.R.id.right_icon);
+        mIcon = resolveIcon(largeIcon, rightIcon);
+        mPicture = resolvePicture(largeIcon);
+        mIconBackgroundColor = resolveBackgroundColor(mIcon);
+    }
+
+    private ImageView resolveIcon(ImageView largeIcon, ImageView rightIcon) {
+        return largeIcon != null && largeIcon.getBackground() != null ? largeIcon
+                : rightIcon != null && rightIcon.getBackground() != null ? rightIcon
+                : null;
+    }
+
+    private ImageView resolvePicture(ImageView largeIcon) {
+        return largeIcon != null && largeIcon.getBackground() == null
+                ? largeIcon
+                : null;
+    }
+
+    private int resolveBackgroundColor(ImageView icon) {
+        if (icon != null && icon.getBackground() != null) {
+            ColorFilter filter = icon.getBackground().getColorFilter();
+            if (filter instanceof PorterDuffColorFilter) {
+                return ((PorterDuffColorFilter) filter).getColor();
+            }
+        }
+        return 0;
+    }
+
+    @Override
+    public void setDark(boolean dark, boolean fade, long delay) {
+        if (mDark != dark) {
+            mDark = dark;
+            if (mInvertHelper != null) {
+                if (fade) {
+                    mInvertHelper.fade(dark, delay);
+                } else {
+                    mInvertHelper.update(dark);
+                }
+            }
+            if (mIcon != null) {
+                if (fade) {
+                    fadeIconColorFilter(mIcon, dark, delay);
+                    fadeIconAlpha(mIcon, dark, delay);
+                } else {
+                    updateIconColorFilter(mIcon, dark);
+                    updateIconAlpha(mIcon, dark);
+                }
+            }
+            setPictureGrayscale(dark, fade, delay);
+        }
+    }
+
+    protected void setPictureGrayscale(boolean grayscale, boolean fade, long delay) {
+        if (mPicture != null) {
+            if (fade) {
+                fadeGrayscale(mPicture, grayscale, delay);
+            } else {
+                updateGrayscale(mPicture, grayscale);
+            }
+        }
+    }
+
+    private void startIntensityAnimation(ValueAnimator.AnimatorUpdateListener updateListener,
+            boolean dark, long delay, Animator.AnimatorListener listener) {
+        float startIntensity = dark ? 0f : 1f;
+        float endIntensity = dark ? 1f : 0f;
+        ValueAnimator animator = ValueAnimator.ofFloat(startIntensity, endIntensity);
+        animator.addUpdateListener(updateListener);
+        animator.setDuration(NotificationPanelView.DOZE_ANIMATION_DURATION);
+        animator.setInterpolator(mLinearOutSlowInInterpolator);
+        animator.setStartDelay(delay);
+        if (listener != null) {
+            animator.addListener(listener);
+        }
+        animator.start();
+    }
+
+    private void fadeIconColorFilter(final ImageView target, boolean dark, long delay) {
+        startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                updateIconColorFilter(target, (Float) animation.getAnimatedValue());
+            }
+        }, dark, delay, null /* listener */);
+    }
+
+    private void fadeIconAlpha(final ImageView target, boolean dark, long delay) {
+        startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                float t = (float) animation.getAnimatedValue();
+                target.setImageAlpha((int) (255 * (1f - t) + mIconDarkAlpha * t));
+            }
+        }, dark, delay, null /* listener */);
+    }
+
+    protected void fadeGrayscale(final ImageView target, final boolean dark, long delay) {
+        startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                updateGrayscaleMatrix((float) animation.getAnimatedValue());
+                target.setColorFilter(new ColorMatrixColorFilter(mGrayscaleColorMatrix));
+            }
+        }, dark, delay, new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                if (!dark) {
+                    target.setColorFilter(null);
+                }
+            }
+        });
+    }
+
+    private void updateIconColorFilter(ImageView target, boolean dark) {
+        updateIconColorFilter(target, dark ? 1f : 0f);
+    }
+
+    private void updateIconColorFilter(ImageView target, float intensity) {
+        int color = interpolateColor(mIconBackgroundColor, mIconBackgroundDarkColor, intensity);
+        mIconColorFilter.setColor(color);
+        target.getBackground().mutate().setColorFilter(mIconColorFilter);
+    }
+
+    private void updateIconAlpha(ImageView target, boolean dark) {
+        target.setImageAlpha(dark ? mIconDarkAlpha : 255);
+    }
+
+    protected void updateGrayscale(ImageView target, boolean dark) {
+        if (dark) {
+            updateGrayscaleMatrix(1f);
+            target.setColorFilter(new ColorMatrixColorFilter(mGrayscaleColorMatrix));
+        } else {
+            target.setColorFilter(null);
+        }
+    }
+
+    private void updateGrayscaleMatrix(float intensity) {
+        mGrayscaleColorMatrix.setSaturation(1 - intensity);
+    }
+
+    private static int interpolateColor(int source, int target, float t) {
+        int aSource = Color.alpha(source);
+        int rSource = Color.red(source);
+        int gSource = Color.green(source);
+        int bSource = Color.blue(source);
+        int aTarget = Color.alpha(target);
+        int rTarget = Color.red(target);
+        int gTarget = Color.green(target);
+        int bTarget = Color.blue(target);
+        return Color.argb(
+                (int) (aSource * (1f - t) + aTarget * t),
+                (int) (rSource * (1f - t) + rTarget * t),
+                (int) (gSource * (1f - t) + gTarget * t),
+                (int) (bSource * (1f - t) + bTarget * t));
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewWrapper.java
new file mode 100644
index 0000000..0a02573
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewWrapper.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar;
+
+import android.content.Context;
+import android.view.View;
+
+import com.android.internal.R;
+
+/**
+ * Wraps the actual notification content view; used to implement behaviors which are different for
+ * the individual templates and custom views.
+ */
+public abstract class NotificationViewWrapper {
+
+    protected final View mView;
+
+    public static NotificationViewWrapper wrap(Context ctx, View v) {
+
+        // TODO: Figure out a better way to find out which template the view is.
+        if (v.findViewById(com.android.internal.R.id.media_actions) != null) {
+            return new NotificationMediaViewWrapper(ctx, v);
+        } else if (v.getId() == com.android.internal.R.id.status_bar_latest_event_content) {
+            return new NotificationTemplateViewWrapper(ctx, v);
+        } else {
+            return new NotificationCustomViewWrapper(v);
+        }
+    }
+
+    protected NotificationViewWrapper(View view) {
+        mView = view;
+    }
+
+    /**
+     * In dark mode, we draw as little as possible, assuming a black background.
+     *
+     * @param dark whether we should display ourselves in dark mode
+     * @param fade whether to animate the transition if the mode changes
+     * @param delay if fading, the delay of the animation
+     */
+    public abstract void setDark(boolean dark, boolean fade, long delay);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
index 6fd6758..3d4a1e0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -55,6 +55,7 @@
         pw.print("    getVibrateOnSigMotion(): "); pw.println(getVibrateOnSigMotion());
         pw.print("    getPulseOnPickup(): "); pw.println(getPulseOnPickup());
         pw.print("    getVibrateOnPickup(): "); pw.println(getVibrateOnPickup());
+        pw.print("    getProxCheckBeforePulse(): "); pw.println(getProxCheckBeforePulse());
         pw.print("    getPulseOnNotifications(): "); pw.println(getPulseOnNotifications());
         pw.print("    getPulseSchedule(): "); pw.println(getPulseSchedule());
         pw.print("    getPulseScheduleResets(): "); pw.println(getPulseScheduleResets());
@@ -101,6 +102,10 @@
         return SystemProperties.getBoolean("doze.vibrate.pickup", false);
     }
 
+    public boolean getProxCheckBeforePulse() {
+        return getBoolean("doze.pulse.proxcheck", R.bool.doze_proximity_check_before_pulse);
+    }
+
     public boolean getPulseOnNotifications() {
         return getBoolean("doze.pulse.notifications", R.bool.doze_pulse_on_notifications);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 9a1ac49..3fca56d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -3708,14 +3708,11 @@
         if (mState != StatusBarState.KEYGUARD && !mNotificationPanel.isDozing()) {
             return;
         }
-        mNotificationPanel.setDozing(mDozing, mDozeScrimController.isPulsing() /*animate*/);
-        if (mDozing) {
-            mStackScroller.setDark(true, false /*animate*/);
-        } else {
-            mStackScroller.setDark(false, false /*animate*/);
-        }
+        boolean animate = !mDozing && mDozeScrimController.isPulsing();
+        mNotificationPanel.setDozing(mDozing, animate);
+        mStackScroller.setDark(mDozing, animate);
         mScrimController.setDozing(mDozing);
-        mDozeScrimController.setDozing(mDozing, mDozeScrimController.isPulsing() /* animate */);
+        mDozeScrimController.setDozing(mDozing, animate);
     }
 
     public void updateStackScrollerState(boolean goingToFullShade) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
index 3c93b19..3d4cda6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
@@ -33,6 +33,7 @@
     boolean animateHideSensitive;
     boolean hasDelays;
     boolean hasGoToFullShadeEvent;
+    boolean hasDarkEvent;
 
     public AnimationFilter animateAlpha() {
         animateAlpha = true;
@@ -98,6 +99,10 @@
                     NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_GO_TO_FULL_SHADE) {
                 hasGoToFullShadeEvent = true;
             }
+            if (events.get(i).animationType ==
+                    NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_DARK) {
+                hasDarkEvent = true;
+            }
         }
     }
 
@@ -126,5 +131,6 @@
         animateHideSensitive = false;
         hasDelays = false;
         hasGoToFullShadeEvent = false;
+        hasDarkEvent = false;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index e63be97..c5f1161 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -2455,7 +2455,8 @@
 
                 // ANIMATION_TYPE_DARK
                 new AnimationFilter()
-                        .animateDark(),
+                        .animateDark()
+                        .hasDelays(),
 
                 // ANIMATION_TYPE_GO_TO_FULL_SHADE
                 new AnimationFilter()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
index 4611370..0b1ce8f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
@@ -154,7 +154,7 @@
                 child.setDimmed(state.dimmed, false /* animate */);
 
                 // apply dark
-                child.setDark(state.dark, false /* animate */);
+                child.setDark(state.dark, false /* animate */, 0 /* delay */);
 
                 // apply hiding sensitive
                 child.setHideSensitive(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
index a56440c..05077bf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
@@ -46,6 +46,7 @@
     public static final int ANIMATION_DELAY_PER_ELEMENT_INTERRUPTING = 80;
     public static final int ANIMATION_DELAY_PER_ELEMENT_MANUAL = 32;
     public static final int ANIMATION_DELAY_PER_ELEMENT_GO_TO_FULL_SHADE = 48;
+    public static final int ANIMATION_DELAY_PER_ELEMENT_DARK = 24;
     private static final int DELAY_EFFECT_MAX_INDEX_DIFFERENCE = 2;
 
     private static final int TAG_ANIMATOR_TRANSLATION_Y = R.id.translation_y_animator_tag;
@@ -161,11 +162,12 @@
         boolean scaleChanging = child.getScaleX() != viewState.scale;
         boolean alphaChanging = alpha != child.getAlpha();
         boolean heightChanging = viewState.height != child.getActualHeight();
+        boolean darkChanging = viewState.dark != child.isDark();
         boolean topInsetChanging = viewState.clipTopAmount != child.getClipTopAmount();
         boolean wasAdded = mNewAddChildren.contains(child);
         boolean hasDelays = mAnimationFilter.hasDelays;
         boolean isDelayRelevant = yTranslationChanging || zTranslationChanging || scaleChanging ||
-                alphaChanging || heightChanging || topInsetChanging;
+                alphaChanging || heightChanging || topInsetChanging || darkChanging;
         boolean noAnimation = wasAdded;
         long delay = 0;
         long duration = mCurrentLength;
@@ -242,7 +244,7 @@
                 && !noAnimation);
 
         // start dark animation
-        child.setDark(viewState.dark, mAnimationFilter.animateDark && !noAnimation);
+        child.setDark(viewState.dark, mAnimationFilter.animateDark && !noAnimation, delay);
 
         // apply speed bump state
         child.setBelowSpeedBump(viewState.belowSpeedBump);
@@ -262,6 +264,9 @@
 
     private long calculateChildAnimationDelay(StackScrollState.ViewState viewState,
             StackScrollState finalState) {
+        if (mAnimationFilter.hasDarkEvent) {
+            return calculateDelayDark(viewState);
+        }
         if (mAnimationFilter.hasGoToFullShadeEvent) {
             return calculateDelayGoToFullShade(viewState);
         }
@@ -309,6 +314,10 @@
         return minDelay;
     }
 
+    private long calculateDelayDark(StackScrollState.ViewState viewState) {
+        return viewState.notGoneIndex * ANIMATION_DELAY_PER_ELEMENT_DARK;
+    }
+
     private long calculateDelayGoToFullShade(StackScrollState.ViewState viewState) {
         float index = viewState.notGoneIndex;
         index = (float) Math.pow(index, 0.7f);
diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java
index 5d2187f..8bd7132 100644
--- a/services/backup/java/com/android/server/backup/Trampoline.java
+++ b/services/backup/java/com/android/server/backup/Trampoline.java
@@ -94,7 +94,7 @@
 
         if (userHandle == UserHandle.USER_OWNER) {
             synchronized (this) {
-                if (makeActive != (mService != null)) {
+                if (makeActive != isBackupServiceActive(userHandle)) {
                     Slog.i(TAG, "Making backup "
                             + (makeActive ? "" : "in") + "active in user " + userHandle);
                     if (makeActive) {
@@ -113,6 +113,21 @@
         }
     }
 
+    /**
+     * Querying activity state of backup service. Calling this method before initialize yields
+     * undefined result.
+     * @param userHandle The user in which the activity state of backup service is queried.
+     * @return true if the service is active.
+     */
+    public boolean isBackupServiceActive(final int userHandle) {
+        if (userHandle == UserHandle.USER_OWNER) {
+            synchronized (this) {
+                return mService != null;
+            }
+        }
+        return false;
+    }
+
     // IBackupManager binder API
     @Override
     public void dataChanged(String packageName) throws RemoteException {
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 17889ea..fa6d349 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -2598,7 +2598,7 @@
             String exclList = "";
             String pacFileUrl = "";
             if (proxyProperties != null && (!TextUtils.isEmpty(proxyProperties.getHost()) ||
-                    (proxyProperties.getPacFileUrl() != null))) {
+                    !Uri.EMPTY.equals(proxyProperties.getPacFileUrl()))) {
                 if (!proxyProperties.isValid()) {
                     if (DBG)
                         log("Invalid proxy properties, ignoring: " + proxyProperties.toString());
@@ -2608,7 +2608,7 @@
                 host = mGlobalProxy.getHost();
                 port = mGlobalProxy.getPort();
                 exclList = mGlobalProxy.getExclusionListAsString();
-                if (proxyProperties.getPacFileUrl() != null) {
+                if (!Uri.EMPTY.equals(proxyProperties.getPacFileUrl())) {
                     pacFileUrl = proxyProperties.getPacFileUrl().toString();
                 }
             } else {
@@ -2670,7 +2670,7 @@
 
     private void handleApplyDefaultProxy(ProxyInfo proxy) {
         if (proxy != null && TextUtils.isEmpty(proxy.getHost())
-                && (proxy.getPacFileUrl() == null)) {
+                && Uri.EMPTY.equals(proxy.getPacFileUrl())) {
             proxy = null;
         }
         synchronized (mProxyLock) {
@@ -2686,7 +2686,8 @@
             // global (to get the correct local port), and send a broadcast.
             // TODO: Switch PacManager to have its own message to send back rather than
             // reusing EVENT_HAS_CHANGED_PROXY and this call to handleApplyDefaultProxy.
-            if ((mGlobalProxy != null) && (proxy != null) && (proxy.getPacFileUrl() != null)
+            if ((mGlobalProxy != null) && (proxy != null)
+                    && (!Uri.EMPTY.equals(proxy.getPacFileUrl()))
                     && proxy.getPacFileUrl().equals(mGlobalProxy.getPacFileUrl())) {
                 mGlobalProxy = proxy;
                 sendProxyBroadcast(mGlobalProxy);
@@ -3839,9 +3840,8 @@
             int notificationType) {
         if (notificationType == ConnectivityManager.CALLBACK_AVAILABLE) {
             Intent intent = new Intent();
-            intent.putExtra(ConnectivityManager.EXTRA_NETWORK_REQUEST_NETWORK, nri.request);
-            intent.putExtra(ConnectivityManager.EXTRA_NETWORK_REQUEST_NETWORK_REQUEST,
-                    networkAgent.network);
+            intent.putExtra(ConnectivityManager.EXTRA_NETWORK, nri.request);
+            intent.putExtra(ConnectivityManager.EXTRA_NETWORK_REQUEST, networkAgent.network);
             sendIntent(nri.mPendingIntent, intent);
         }
         // else not handled
@@ -3973,7 +3973,7 @@
     //                 another higher scoring network by another call to rematchNetworkAndRequests()
     //                 and this other call also lingered newNetwork.
     private void rematchNetworkAndRequests(NetworkAgentInfo newNetwork, boolean nascent) {
-        if (!newNetwork.created) loge("ERROR: uncreated network being rematched.");
+        if (!newNetwork.created) return;
         if (nascent && !newNetwork.validated) loge("ERROR: nascent network not validated.");
         boolean keep = newNetwork.isVPN();
         boolean isNewDefault = false;
@@ -4272,7 +4272,7 @@
         final int oldScore = nai.getCurrentScore();
         nai.setCurrentScore(score);
 
-        if (nai.created) rematchAllNetworksAndRequests(nai, oldScore);
+        rematchAllNetworksAndRequests(nai, oldScore);
 
         sendUpdatedScoreToFactories(nai);
     }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index fc6e73f..f0fb9e6 100755
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -838,10 +838,12 @@
      * indirect content-provider access.
      */
     private class Identity {
-        public int pid;
-        public int uid;
+        public final IBinder token;
+        public final int pid;
+        public final int uid;
 
-        Identity(int _pid, int _uid) {
+        Identity(IBinder _token, int _pid, int _uid) {
+            token = _token;
             pid = _pid;
             uid = _uid;
         }
@@ -2275,15 +2277,19 @@
      * process when the bindApplication() IPC is sent to the process. They're
      * lazily setup to make sure the services are running when they're asked for.
      */
-    private HashMap<String, IBinder> getCommonServicesLocked() {
+    private HashMap<String, IBinder> getCommonServicesLocked(boolean isolated) {
         if (mAppBindArgs == null) {
-            mAppBindArgs = new HashMap<String, IBinder>();
+            mAppBindArgs = new HashMap<>();
 
-            // Setup the application init args
-            mAppBindArgs.put("package", ServiceManager.getService("package"));
-            mAppBindArgs.put("window", ServiceManager.getService("window"));
-            mAppBindArgs.put(Context.ALARM_SERVICE,
-                    ServiceManager.getService(Context.ALARM_SERVICE));
+            // Isolated processes won't get this optimization, so that we don't
+            // violate the rules about which services they have access to.
+            if (!isolated) {
+                // Setup the application init args
+                mAppBindArgs.put("package", ServiceManager.getService("package"));
+                mAppBindArgs.put("window", ServiceManager.getService("window"));
+                mAppBindArgs.put(Context.ALARM_SERVICE,
+                        ServiceManager.getService(Context.ALARM_SERVICE));
+            }
         }
         return mAppBindArgs;
     }
@@ -5906,7 +5912,8 @@
                     profilerInfo, app.instrumentationArguments, app.instrumentationWatcher,
                     app.instrumentationUiAutomationConnection, testMode, enableOpenGlTrace,
                     isRestrictedBackupMode || !normalMode, app.persistent,
-                    new Configuration(mConfiguration), app.compat, getCommonServicesLocked(),
+                    new Configuration(mConfiguration), app.compat,
+                    getCommonServicesLocked(app.isolated),
                     mCoreSettingsObserver.getCoreSettingsLocked());
             updateLruProcessLocked(app, false, null);
             app.lastRequestedGc = app.lastLowMemory = SystemClock.uptimeMillis();
@@ -6260,7 +6267,7 @@
             synchronized (this) {
                 ActivityStack stack = ActivityRecord.getStackLocked(token);
                 if (stack != null) {
-                    stack.backgroundResourcesReleased(token);
+                    stack.backgroundResourcesReleased();
                 }
             }
         } finally {
@@ -6726,21 +6733,9 @@
      */
     int checkComponentPermission(String permission, int pid, int uid,
             int owningUid, boolean exported) {
-        // We might be performing an operation on behalf of an indirect binder
-        // invocation, e.g. via {@link #openContentUri}.  Check and adjust the
-        // client identity accordingly before proceeding.
-        Identity tlsIdentity = sCallerIdentity.get();
-        if (tlsIdentity != null) {
-            Slog.d(TAG, "checkComponentPermission() adjusting {pid,uid} to {"
-                    + tlsIdentity.pid + "," + tlsIdentity.uid + "}");
-            uid = tlsIdentity.uid;
-            pid = tlsIdentity.pid;
-        }
-
         if (pid == MY_PID) {
             return PackageManager.PERMISSION_GRANTED;
         }
-
         return ActivityManager.checkComponentPermission(permission, uid,
                 owningUid, exported);
     }
@@ -6762,6 +6757,26 @@
         return checkComponentPermission(permission, pid, UserHandle.getAppId(uid), -1, true);
     }
 
+    @Override
+    public int checkPermissionWithToken(String permission, int pid, int uid, IBinder callerToken) {
+        if (permission == null) {
+            return PackageManager.PERMISSION_DENIED;
+        }
+
+        // We might be performing an operation on behalf of an indirect binder
+        // invocation, e.g. via {@link #openContentUri}.  Check and adjust the
+        // client identity accordingly before proceeding.
+        Identity tlsIdentity = sCallerIdentity.get();
+        if (tlsIdentity != null && tlsIdentity.token == callerToken) {
+            Slog.d(TAG, "checkComponentPermission() adjusting {pid,uid} to {"
+                    + tlsIdentity.pid + "," + tlsIdentity.uid + "}");
+            uid = tlsIdentity.uid;
+            pid = tlsIdentity.pid;
+        }
+
+        return checkComponentPermission(permission, pid, UserHandle.getAppId(uid), -1, true);
+    }
+
     /**
      * Binder IPC calls go through the public entry point.
      * This can be called with or without the global lock held.
@@ -6967,13 +6982,13 @@
      */
     @Override
     public int checkUriPermission(Uri uri, int pid, int uid,
-            final int modeFlags, int userId) {
+            final int modeFlags, int userId, IBinder callerToken) {
         enforceNotIsolatedCaller("checkUriPermission");
 
         // Another redirected-binder-call permissions check as in
-        // {@link checkComponentPermission}.
+        // {@link checkPermissionWithToken}.
         Identity tlsIdentity = sCallerIdentity.get();
-        if (tlsIdentity != null) {
+        if (tlsIdentity != null && tlsIdentity.token == callerToken) {
             uid = tlsIdentity.uid;
             pid = tlsIdentity.pid;
         }
@@ -9880,10 +9895,11 @@
             // we do the check against the caller's permissions even though it looks
             // to the content provider like the Activity Manager itself is making
             // the request.
+            Binder token = new Binder();
             sCallerIdentity.set(new Identity(
-                    Binder.getCallingPid(), Binder.getCallingUid()));
+                    token, Binder.getCallingPid(), Binder.getCallingUid()));
             try {
-                pfd = cph.provider.openFile(null, uri, "r", null);
+                pfd = cph.provider.openFile(null, uri, "r", null, token);
             } catch (FileNotFoundException e) {
                 // do nothing; pfd will be returned null
             } finally {
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 3a1fafe..c8b7205 100755
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -3317,17 +3317,18 @@
                 mHandler.sendEmptyMessageDelayed(RELEASE_BACKGROUND_RESOURCES_TIMEOUT_MSG, 500);
             } else {
                 Slog.e(TAG, "releaseBackgroundResources: activity " + r + " no longer running");
-                backgroundResourcesReleased(r.appToken);
+                backgroundResourcesReleased();
             }
         }
     }
 
-    final void backgroundResourcesReleased(IBinder token) {
+    final void backgroundResourcesReleased() {
         mHandler.removeMessages(RELEASE_BACKGROUND_RESOURCES_TIMEOUT_MSG);
         final ActivityRecord r = getVisibleBehindActivity();
         if (r != null) {
             mStackSupervisor.mStoppingActivities.add(r);
             setVisibleBehindActivity(null);
+            mStackSupervisor.scheduleIdleTimeoutLocked(null);
         }
         mStackSupervisor.resumeTopActivitiesLocked();
     }
diff --git a/services/core/java/com/android/server/am/TaskPersister.java b/services/core/java/com/android/server/am/TaskPersister.java
index b331c84..9311f25 100644
--- a/services/core/java/com/android/server/am/TaskPersister.java
+++ b/services/core/java/com/android/server/am/TaskPersister.java
@@ -63,6 +63,12 @@
     private static final String IMAGES_DIRNAME = "recent_images";
     static final String IMAGE_EXTENSION = ".png";
 
+    // Directory where restored historical task XML/PNG files are placed.  This directory
+    // contains subdirs named after TASKS_DIRNAME and IMAGES_DIRNAME mirroring the
+    // ancestral device's dataset.  This needs to match the RECENTS_TASK_RESTORE_DIR
+    // value in RecentsBackupHelper.
+    private static final String RESTORED_TASKS = "restored_" + TASKS_DIRNAME;
+
     private static final String TAG_TASK = "task";
 
     static File sImagesDir;
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 921b68b..9440697 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -90,6 +90,7 @@
     private final SessionStub mSession;
     private final SessionCb mSessionCb;
     private final MediaSessionService mService;
+    private final boolean mUseMasterVolume;
 
     private final Object mLock = new Object();
     private final ArrayList<ISessionControllerCallback> mControllerCallbacks =
@@ -139,6 +140,8 @@
         mAudioManager = (AudioManager) service.getContext().getSystemService(Context.AUDIO_SERVICE);
         mAudioManagerInternal = LocalServices.getService(AudioManagerInternal.class);
         mAudioAttrs = new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).build();
+        mUseMasterVolume = service.getContext().getResources().getBoolean(
+                com.android.internal.R.bool.config_useMasterVolume);
     }
 
     /**
@@ -248,6 +251,12 @@
             direction = -1;
         }
         if (mVolumeType == PlaybackInfo.PLAYBACK_TYPE_LOCAL) {
+            if (mUseMasterVolume) {
+                // If this device only uses master volume and playback is local
+                // just adjust the master volume and return.
+                mAudioManagerInternal.adjustMasterVolumeForUid(direction, flags, packageName, uid);
+                return;
+            }
             int stream = AudioAttributes.toLegacyStreamType(mAudioAttrs);
             if (useSuggested) {
                 if (AudioSystem.isStreamActive(stream, 0)) {
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index ba18f48..98a3970 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -86,6 +86,7 @@
     private final Object mLock = new Object();
     private final MessageHandler mHandler = new MessageHandler();
     private final PowerManager.WakeLock mMediaEventWakeLock;
+    private final boolean mUseMasterVolume;
 
     private KeyguardManager mKeyguardManager;
     private IAudioService mAudioService;
@@ -104,6 +105,8 @@
         mPriorityStack = new MediaSessionStack();
         PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
         mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent");
+        mUseMasterVolume = context.getResources().getBoolean(
+                com.android.internal.R.bool.config_useMasterVolume);
     }
 
     @Override
@@ -819,8 +822,13 @@
                     return;
                 }
                 try {
-                    mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream, flags,
-                            getContext().getOpPackageName());
+                    if (mUseMasterVolume) {
+                        mAudioService.adjustMasterVolume(direction, flags,
+                                getContext().getOpPackageName());
+                    } else {
+                        mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream, flags,
+                                getContext().getOpPackageName());
+                    }
                 } catch (RemoteException e) {
                     Log.e(TAG, "Error adjusting default volume.", e);
                 }
diff --git a/services/core/java/com/android/server/net/IpConfigStore.java b/services/core/java/com/android/server/net/IpConfigStore.java
index 857b9e9..b5a450d 100644
--- a/services/core/java/com/android/server/net/IpConfigStore.java
+++ b/services/core/java/com/android/server/net/IpConfigStore.java
@@ -122,8 +122,10 @@
                     out.writeUTF(proxyProperties.getHost());
                     out.writeUTF(PROXY_PORT_KEY);
                     out.writeInt(proxyProperties.getPort());
-                    out.writeUTF(EXCLUSION_LIST_KEY);
-                    out.writeUTF(exclusionList);
+                    if (exclusionList != null) {
+                        out.writeUTF(EXCLUSION_LIST_KEY);
+                        out.writeUTF(exclusionList);
+                    }
                     written = true;
                     break;
                 case PAC:
diff --git a/services/core/java/com/android/server/trust/TrustAgentWrapper.java b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
index 4906bd1..57b204d 100644
--- a/services/core/java/com/android/server/trust/TrustAgentWrapper.java
+++ b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
@@ -232,6 +232,12 @@
             mTrustManagerService.mArchive.logAgentConnected(mUserId, name);
             setCallback(mCallback);
             updateDevicePolicyFeatures();
+
+            if (mTrustManagerService.isDeviceLockedInner(mUserId)) {
+                onDeviceLocked();
+            } else {
+                onDeviceUnlocked();
+            }
         }
 
         @Override
@@ -287,6 +293,7 @@
             onError(e);
         }
     }
+
     /**
      * @see android.service.trust.TrustAgentService#onUnlockAttempt(boolean)
      */
@@ -298,6 +305,28 @@
         }
     }
 
+    /**
+     * @see android.service.trust.TrustAgentService#onDeviceLocked()
+     */
+    public void onDeviceLocked() {
+        try {
+            if (mTrustAgentService != null) mTrustAgentService.onDeviceLocked();
+        } catch (RemoteException e) {
+            onError(e);
+        }
+    }
+
+    /**
+     * @see android.service.trust.TrustAgentService#onDeviceUnlocked()
+     */
+    public void onDeviceUnlocked() {
+        try {
+            if (mTrustAgentService != null) mTrustAgentService.onDeviceUnlocked();
+        } catch (RemoteException e) {
+            onError(e);
+        }
+    }
+
     private void setCallback(ITrustAgentServiceCallback callback) {
         try {
             if (mTrustAgentService != null) {
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 2388c85..a2ea1c8 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -26,7 +26,6 @@
 
 import android.Manifest;
 import android.app.ActivityManager;
-import android.app.ActivityManagerNative;
 import android.app.admin.DevicePolicyManager;
 import android.app.trust.ITrustListener;
 import android.app.trust.ITrustManager;
@@ -61,6 +60,8 @@
 import android.util.Slog;
 import android.util.SparseBooleanArray;
 import android.util.Xml;
+import android.view.IWindowManager;
+import android.view.WindowManagerGlobal;
 
 import java.io.FileDescriptor;
 import java.io.IOException;
@@ -96,6 +97,10 @@
     private static final int MSG_DISPATCH_UNLOCK_ATTEMPT = 3;
     private static final int MSG_ENABLED_AGENTS_CHANGED = 4;
     private static final int MSG_REQUIRE_CREDENTIAL_ENTRY = 5;
+    private static final int MSG_KEYGUARD_SHOWING_CHANGED = 6;
+    private static final int MSG_START_USER = 7;
+    private static final int MSG_CLEANUP_USER = 8;
+    private static final int MSG_SWITCH_USER = 9;
 
     private final ArraySet<AgentInfo> mActiveAgents = new ArraySet<AgentInfo>();
     private final ArrayList<ITrustListener> mTrustListeners = new ArrayList<ITrustListener>();
@@ -110,7 +115,11 @@
     @GuardedBy("mUserIsTrusted")
     private final SparseBooleanArray mUserIsTrusted = new SparseBooleanArray();
 
+    @GuardedBy("mDeviceLockedForUser")
+    private final SparseBooleanArray mDeviceLockedForUser = new SparseBooleanArray();
+
     private boolean mTrustAgentsCanRun = false;
+    private int mCurrentUser = UserHandle.USER_OWNER;
 
     public TrustManagerService(Context context) {
         super(context);
@@ -177,10 +186,15 @@
     public void updateTrust(int userId, boolean initiatedByUser) {
         dispatchOnTrustManagedChanged(aggregateIsTrustManaged(userId), userId);
         boolean trusted = aggregateIsTrusted(userId);
+        boolean changed;
         synchronized (mUserIsTrusted) {
+            changed = mUserIsTrusted.get(userId) != trusted;
             mUserIsTrusted.put(userId, trusted);
         }
         dispatchOnTrustChanged(trusted, userId, initiatedByUser);
+        if (changed) {
+            refreshDeviceLockedForUser(userId);
+        }
     }
 
     void refreshAgentList(int userId) {
@@ -212,8 +226,7 @@
                     || userInfo.guestToRemove) continue;
             if (!userInfo.supportsSwitchTo()) continue;
             if (!mActivityManager.isUserRunning(userInfo.id)) continue;
-            if (lockPatternUtils.getKeyguardStoredPasswordQuality(userInfo.id)
-                    == DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) continue;
+            if (!lockPatternUtils.isSecure(userInfo.id)) continue;
             if (!mUserHasAuthenticatedSinceBoot.get(userInfo.id)) continue;
             DevicePolicyManager dpm = lockPatternUtils.getDevicePolicyManager();
             int disabledFeatures = dpm.getKeyguardDisabledFeatures(null, userInfo.id);
@@ -273,6 +286,73 @@
         }
     }
 
+    boolean isDeviceLockedInner(int userId) {
+        synchronized (mDeviceLockedForUser) {
+            return mDeviceLockedForUser.get(userId, true);
+        }
+    }
+
+    private void refreshDeviceLockedForUser(int userId) {
+        if (userId != UserHandle.USER_ALL && userId < UserHandle.USER_OWNER) {
+            Log.e(TAG, "refreshAgentList(userId=" + userId + "): Invalid user handle,"
+                    + " must be USER_ALL or a specific user.", new Throwable("here"));
+            userId = UserHandle.USER_ALL;
+        }
+
+        List<UserInfo> userInfos;
+        if (userId == UserHandle.USER_ALL) {
+            userInfos = mUserManager.getUsers(true /* excludeDying */);
+        } else {
+            userInfos = new ArrayList<>();
+            userInfos.add(mUserManager.getUserInfo(userId));
+        }
+
+        IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
+
+        for (int i = 0; i < userInfos.size(); i++) {
+            UserInfo info = userInfos.get(i);
+
+            if (info == null || info.partial || !info.isEnabled() || info.guestToRemove
+                    || !info.supportsSwitchTo()) {
+                continue;
+            }
+
+            int id = info.id;
+            boolean secure = mLockPatternUtils.isSecure(id);
+            boolean trusted = aggregateIsTrusted(id);
+            boolean showingKeyguard = true;
+            if (mCurrentUser == id) {
+                try {
+                    showingKeyguard = wm.isKeyguardLocked();
+                } catch (RemoteException e) {
+                }
+            }
+            boolean deviceLocked = secure && showingKeyguard && !trusted;
+
+            boolean changed;
+            synchronized (mDeviceLockedForUser) {
+                changed = isDeviceLockedInner(id) != deviceLocked;
+                mDeviceLockedForUser.put(id, deviceLocked);
+            }
+            if (changed) {
+                dispatchDeviceLocked(id, deviceLocked);
+            }
+        }
+    }
+
+    private void dispatchDeviceLocked(int userId, boolean isLocked) {
+        for (int i = 0; i < mActiveAgents.size(); i++) {
+            AgentInfo agent = mActiveAgents.valueAt(i);
+            if (agent.userId == userId) {
+                if (isLocked) {
+                    agent.agent.onDeviceLocked();
+                } else{
+                    agent.agent.onDeviceUnlocked();
+                }
+            }
+        }
+    }
+
     void updateDevicePolicyFeatures() {
         for (int i = 0; i < mActiveAgents.size(); i++) {
             AgentInfo info = mActiveAgents.valueAt(i);
@@ -540,12 +620,17 @@
 
     @Override
     public void onStartUser(int userId) {
-        refreshAgentList(userId);
+        mHandler.obtainMessage(MSG_START_USER, userId, 0, null).sendToTarget();
     }
 
     @Override
     public void onCleanupUser(int userId) {
-        refreshAgentList(userId);
+        mHandler.obtainMessage(MSG_CLEANUP_USER, userId, 0, null).sendToTarget();
+    }
+
+    @Override
+    public void onSwitchUser(int userId) {
+        mHandler.obtainMessage(MSG_SWITCH_USER, userId, 0, null).sendToTarget();
     }
 
     // Plumbing
@@ -578,6 +663,14 @@
         }
 
         @Override
+        public void reportKeyguardShowingChanged() throws RemoteException {
+            enforceReportPermission();
+            // coalesce refresh messages.
+            mHandler.removeMessages(MSG_KEYGUARD_SHOWING_CHANGED);
+            mHandler.sendEmptyMessage(MSG_KEYGUARD_SHOWING_CHANGED);
+        }
+
+        @Override
         public void registerTrustListener(ITrustListener trustListener) throws RemoteException {
             enforceListenerPermission();
             mHandler.obtainMessage(MSG_REGISTER_LISTENER, trustListener).sendToTarget();
@@ -590,13 +683,12 @@
         }
 
         @Override
-        public boolean isTrusted(int userId) throws RemoteException {
+        public boolean isDeviceLocked(int userId) throws RemoteException {
             userId = ActivityManager.handleIncomingUser(getCallingPid(), getCallingUid(), userId,
-                    false /* allowAll */, true /* requireFull */, "isTrusted", null);
+                    false /* allowAll */, true /* requireFull */, "isDeviceLocked", null);
             userId = resolveProfileParent(userId);
-            synchronized (mUserIsTrusted) {
-                return mUserIsTrusted.get(userId);
-            }
+
+            return isDeviceLockedInner(userId);
         }
 
         private void enforceReportPermission() {
@@ -621,19 +713,13 @@
                 fout.println("disabled because the third-party apps can't run yet.");
                 return;
             }
-            final UserInfo currentUser;
             final List<UserInfo> userInfos = mUserManager.getUsers(true /* excludeDying */);
-            try {
-                currentUser = ActivityManagerNative.getDefault().getCurrentUser();
-            } catch (RemoteException e) {
-                throw new RuntimeException(e);
-            }
             mHandler.runWithScissors(new Runnable() {
                 @Override
                 public void run() {
                     fout.println("Trust manager state:");
                     for (UserInfo user : userInfos) {
-                        dumpUser(fout, user, user.id == currentUser.id);
+                        dumpUser(fout, user, user.id == mCurrentUser);
                     }
                 }
             }, 1500);
@@ -642,11 +728,17 @@
         private void dumpUser(PrintWriter fout, UserInfo user, boolean isCurrent) {
             fout.printf(" User \"%s\" (id=%d, flags=%#x)",
                     user.name, user.id, user.flags);
+            if (!user.supportsSwitchTo()) {
+                fout.println("(managed profile)");
+                fout.println("   disabled because switching to this user is not possible.");
+                return;
+            }
             if (isCurrent) {
                 fout.print(" (current)");
             }
             fout.print(": trusted=" + dumpBool(aggregateIsTrusted(user.id)));
             fout.print(", trustManaged=" + dumpBool(aggregateIsTrustManaged(user.id)));
+            fout.print(", deviceLocked=" + dumpBool(isDeviceLockedInner(user.id)));
             fout.println();
             fout.println("   Enabled agents:");
             boolean duplicateSimpleNames = false;
@@ -711,10 +803,23 @@
                     break;
                 case MSG_ENABLED_AGENTS_CHANGED:
                     refreshAgentList(UserHandle.USER_ALL);
+                    // This is also called when the security mode of a user changes.
+                    refreshDeviceLockedForUser(UserHandle.USER_ALL);
                     break;
                 case MSG_REQUIRE_CREDENTIAL_ENTRY:
                     requireCredentialEntry(msg.arg1);
                     break;
+                case MSG_KEYGUARD_SHOWING_CHANGED:
+                    refreshDeviceLockedForUser(UserHandle.USER_CURRENT);
+                    break;
+                case MSG_START_USER:
+                case MSG_CLEANUP_USER:
+                    refreshAgentList(msg.arg1);
+                    break;
+                case MSG_SWITCH_USER:
+                    mCurrentUser = msg.arg1;
+                    refreshDeviceLockedForUser(UserHandle.USER_ALL);
+                    break;
             }
         }
     };
@@ -756,8 +861,14 @@
                 int userId = getUserId(intent);
                 if (userId > 0) {
                     mUserHasAuthenticatedSinceBoot.delete(userId);
-                    mUserIsTrusted.delete(userId);
+                    synchronized (mUserIsTrusted) {
+                        mUserIsTrusted.delete(userId);
+                    }
+                    synchronized (mDeviceLockedForUser) {
+                        mDeviceLockedForUser.delete(userId);
+                    }
                     refreshAgentList(userId);
+                    refreshDeviceLockedForUser(userId);
                 }
             }
         }
diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
index a9c8a61..716487c 100644
--- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java
+++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
@@ -299,6 +299,13 @@
         return -1;
     }
 
+    private static boolean intArrayContains(int[] array, int value) {
+        for (int element : array) {
+            if (element == value) return true;
+        }
+        return false;
+    }
+
     public void addHdmiTvInput(int id, TvInputInfo info) {
         if (info.getType() != TvInputInfo.TYPE_HDMI) {
             throw new IllegalArgumentException("info (" + info + ") has non-HDMI type.");
@@ -674,7 +681,7 @@
                     throw new IllegalStateException("Device already released.");
                 }
 
-                int result = TvInputHal.ERROR_UNKNOWN;
+                int result = TvInputHal.SUCCESS;
                 if (surface == null) {
                     // The value of config is ignored when surface == null.
                     if (mActiveConfig != null) {
@@ -762,20 +769,64 @@
             AudioPortConfig sinkConfig = mAudioSink.activeConfig();
             AudioPatch[] audioPatchArray = new AudioPatch[] { mAudioPatch };
             boolean shouldRecreateAudioPatch = sourceUpdated || sinkUpdated;
+
+            int sinkSamplingRate = mDesiredSamplingRate;
+            int sinkChannelMask = mDesiredChannelMask;
+            int sinkFormat = mDesiredFormat;
+            // If sinkConfig != null and values are set to default, fill in the sinkConfig values.
+            if (sinkConfig != null) {
+                if (sinkSamplingRate == 0) {
+                    sinkSamplingRate = sinkConfig.samplingRate();
+                }
+                if (sinkChannelMask == AudioFormat.CHANNEL_OUT_DEFAULT) {
+                    sinkChannelMask = sinkConfig.channelMask();
+                }
+                if (sinkFormat == AudioFormat.ENCODING_DEFAULT) {
+                    sinkChannelMask = sinkConfig.format();
+                }
+            }
+
             if (sinkConfig == null
-                    || (mDesiredSamplingRate != 0
-                            && sinkConfig.samplingRate() != mDesiredSamplingRate)
-                    || (mDesiredChannelMask != AudioFormat.CHANNEL_OUT_DEFAULT
-                            && sinkConfig.channelMask() != mDesiredChannelMask)
-                    || (mDesiredFormat != AudioFormat.ENCODING_DEFAULT
-                            && sinkConfig.format() != mDesiredFormat)) {
-                sinkConfig = mAudioSink.buildConfig(mDesiredSamplingRate, mDesiredChannelMask,
-                        mDesiredFormat, null);
+                    || sinkConfig.samplingRate() != sinkSamplingRate
+                    || sinkConfig.channelMask() != sinkChannelMask
+                    || sinkConfig.format() != sinkFormat) {
+                // Check for compatibility and reset to default if necessary.
+                if (!intArrayContains(mAudioSink.samplingRates(), sinkSamplingRate)
+                        && mAudioSink.samplingRates().length > 0) {
+                    sinkSamplingRate = mAudioSink.samplingRates()[0];
+                }
+                if (!intArrayContains(mAudioSink.channelMasks(), sinkChannelMask)) {
+                    sinkChannelMask = AudioFormat.CHANNEL_OUT_DEFAULT;
+                }
+                if (!intArrayContains(mAudioSink.formats(), sinkFormat)) {
+                    sinkFormat = AudioFormat.ENCODING_DEFAULT;
+                }
+                sinkConfig = mAudioSink.buildConfig(sinkSamplingRate, sinkChannelMask,
+                        sinkFormat, null);
                 shouldRecreateAudioPatch = true;
             }
             if (sourceConfig == null || sourceGainConfig != null) {
-                sourceConfig = mAudioSource.buildConfig(sinkConfig.samplingRate(),
-                        sinkConfig.channelMask(), sinkConfig.format(), sourceGainConfig);
+                int sourceSamplingRate = 0;
+                if (intArrayContains(mAudioSource.samplingRates(), sinkConfig.samplingRate())) {
+                    sourceSamplingRate = sinkConfig.samplingRate();
+                } else if (mAudioSource.samplingRates().length > 0) {
+                    // Use any sampling rate and hope audio patch can handle resampling...
+                    sourceSamplingRate = mAudioSource.samplingRates()[0];
+                }
+                int sourceChannelMask = AudioFormat.CHANNEL_IN_DEFAULT;
+                for (int inChannelMask : mAudioSource.channelMasks()) {
+                    if (AudioFormat.channelCountFromOutChannelMask(sinkConfig.channelMask())
+                            == AudioFormat.channelCountFromInChannelMask(inChannelMask)) {
+                        sourceChannelMask = inChannelMask;
+                        break;
+                    }
+                }
+                int sourceFormat = AudioFormat.ENCODING_DEFAULT;
+                if (intArrayContains(mAudioSource.formats(), sinkConfig.format())) {
+                    sourceFormat = sinkConfig.format();
+                }
+                sourceConfig = mAudioSource.buildConfig(sourceSamplingRate, sourceChannelMask,
+                        sourceFormat, sourceGainConfig);
                 shouldRecreateAudioPatch = true;
             }
             if (shouldRecreateAudioPatch) {
@@ -785,6 +836,9 @@
                         new AudioPortConfig[] { sourceConfig },
                         new AudioPortConfig[] { sinkConfig });
                 mAudioPatch = audioPatchArray[0];
+                if (sourceGainConfig != null) {
+                    mAudioManager.setAudioPortGain(mAudioSource, sourceGainConfig);
+                }
             }
         }
 
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 1086eb2..f859fd2 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -254,15 +254,18 @@
 
     @Override
     void removeAllWindows() {
-        int winNdx;
-        while ((winNdx = allAppWindows.size()) > 0) {
-            WindowState win = allAppWindows.get(winNdx - 1);
+        for (int winNdx = allAppWindows.size() - 1; winNdx >= 0;
+                // removeWindowLocked at bottom of loop may remove multiple entries from
+                // allAppWindows if the window to be removed has child windows. It also may
+                // not remove any windows from allAppWindows at all if win is exiting and
+                // currently animating away. This ensures that winNdx is monotonically decreasing
+                // and never beyond allAppWindows bounds.
+                winNdx = Math.min(winNdx - 1, allAppWindows.size() - 1)) {
+            WindowState win = allAppWindows.get(winNdx);
             if (WindowManagerService.DEBUG_WINDOW_MOVEMENT) {
                 Slog.w(WindowManagerService.TAG, "removeAllWindows: removing win=" + win);
             }
 
-            // {@link WindowManagerService.removeWindowLocked} may remove multiple entries from
-            // {@link #allAppWindows} if the window to be removed has child windows.
             win.mService.removeWindowLocked(win.mSession, win);
         }
     }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 3b0a511..b97f4f9 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -34,6 +34,7 @@
 import android.app.admin.DevicePolicyManager;
 import android.app.admin.DevicePolicyManagerInternal;
 import android.app.admin.IDevicePolicyManager;
+import android.app.backup.IBackupManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -3652,6 +3653,18 @@
                         "Trying to set device owner but device owner is already set.");
             }
 
+            // Shutting down backup manager service permanently.
+            long ident = Binder.clearCallingIdentity();
+            try {
+                IBackupManager ibm = IBackupManager.Stub.asInterface(
+                        ServiceManager.getService(Context.BACKUP_SERVICE));
+                ibm.setBackupServiceActive(UserHandle.USER_OWNER, false);
+            } catch (RemoteException e) {
+                throw new IllegalStateException("Failed deactivating backup service.", e);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+
             if (mDeviceOwner == null) {
                 // Device owner is not set and does not exist, set it.
                 mDeviceOwner = DeviceOwner.createWithDeviceOwner(packageName, ownerName);
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index c63eb18..23ba3b6 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -209,8 +209,13 @@
         mUseUsbNotification = !massStorageSupported;
 
         // make sure the ADB_ENABLED setting value matches the current state
-        Settings.Global.putInt(mContentResolver, Settings.Global.ADB_ENABLED, mAdbEnabled ? 1 : 0);
-
+        try {
+            Settings.Global.putInt(mContentResolver,
+                    Settings.Global.ADB_ENABLED, mAdbEnabled ? 1 : 0);
+        } catch (SecurityException e) {
+            // If UserManager.DISALLOW_DEBUGGING_FEATURES is on, that this setting can't be changed.
+            Slog.d(TAG, "ADB_ENABLED is restricted.");
+        }
         mHandler.sendEmptyMessage(MSG_SYSTEM_READY);
     }
 
diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java
index 215c682..6e404de 100644
--- a/telecomm/java/android/telecom/Conference.java
+++ b/telecomm/java/android/telecom/Conference.java
@@ -30,7 +30,7 @@
  * @hide
  */
 @SystemApi
-public abstract class Conference {
+public abstract class Conference implements IConferenceable {
 
     /** @hide */
     public abstract static class Listener {
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 63b44a6..fb63c85 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -47,7 +47,7 @@
  * @hide
  */
 @SystemApi
-public abstract class Connection {
+public abstract class Connection implements IConferenceable {
 
     public static final int STATE_INITIALIZING = 0;
 
@@ -82,8 +82,8 @@
                 Connection c, VideoProvider videoProvider) {}
         public void onAudioModeIsVoipChanged(Connection c, boolean isVoip) {}
         public void onStatusHintsChanged(Connection c, StatusHints statusHints) {}
-        public void onConferenceableConnectionsChanged(
-                Connection c, List<Connection> conferenceableConnections) {}
+        public void onConferenceablesChanged(
+                Connection c, List<IConferenceable> conferenceables) {}
         public void onConferenceChanged(Connection c, Conference conference) {}
         /** @hide */
         public void onConferenceParticipantsChanged(Connection c,
@@ -449,7 +449,16 @@
     private final Listener mConnectionDeathListener = new Listener() {
         @Override
         public void onDestroyed(Connection c) {
-            if (mConferenceableConnections.remove(c)) {
+            if (mConferenceables.remove(c)) {
+                fireOnConferenceableConnectionsChanged();
+            }
+        }
+    };
+
+    private final Conference.Listener mConferenceDeathListener = new Conference.Listener() {
+        @Override
+        public void onDestroyed(Conference c) {
+            if (mConferenceables.remove(c)) {
                 fireOnConferenceableConnectionsChanged();
             }
         }
@@ -462,9 +471,9 @@
      */
     private final Set<Listener> mListeners = Collections.newSetFromMap(
             new ConcurrentHashMap<Listener, Boolean>(8, 0.9f, 1));
-    private final List<Connection> mConferenceableConnections = new ArrayList<>();
-    private final List<Connection> mUnmodifiableConferenceableConnections =
-            Collections.unmodifiableList(mConferenceableConnections);
+    private final List<IConferenceable> mConferenceables = new ArrayList<>();
+    private final List<IConferenceable> mUnmodifiableConferenceables =
+            Collections.unmodifiableList(mConferenceables);
 
     private int mState = STATE_NEW;
     private AudioState mAudioState;
@@ -864,19 +873,44 @@
         for (Connection c : conferenceableConnections) {
             // If statement checks for duplicates in input. It makes it N^2 but we're dealing with a
             // small amount of items here.
-            if (!mConferenceableConnections.contains(c)) {
+            if (!mConferenceables.contains(c)) {
                 c.addConnectionListener(mConnectionDeathListener);
-                mConferenceableConnections.add(c);
+                mConferenceables.add(c);
             }
         }
         fireOnConferenceableConnectionsChanged();
     }
 
     /**
-     * Returns the connections with which this connection can be conferenced.
+     * Similar to {@link #setConferenceableConnections(java.util.List)}, sets a list of connections
+     * or conferences with which this connection can be conferenced.
+     *
+     * @param conferenceables The conferenceables.
      */
-    public final List<Connection> getConferenceableConnections() {
-        return mUnmodifiableConferenceableConnections;
+    public final void setConferenceables(List<IConferenceable> conferenceables) {
+        clearConferenceableList();
+        for (IConferenceable c : conferenceables) {
+            // If statement checks for duplicates in input. It makes it N^2 but we're dealing with a
+            // small amount of items here.
+            if (!mConferenceables.contains(c)) {
+                if (c instanceof Connection) {
+                    Connection connection = (Connection) c;
+                    connection.addConnectionListener(mConnectionDeathListener);
+                } else if (c instanceof Conference) {
+                    Conference conference = (Conference) c;
+                    conference.addListener(mConferenceDeathListener);
+                }
+                mConferenceables.add(c);
+            }
+        }
+        fireOnConferenceableConnectionsChanged();
+    }
+
+    /**
+     * Returns the connections or conferences with which this connection can be conferenced.
+     */
+    public final List<IConferenceable> getConferenceables() {
+        return mUnmodifiableConferenceables;
     }
 
     /*
@@ -1109,7 +1143,7 @@
 
     private final void  fireOnConferenceableConnectionsChanged() {
         for (Listener l : mListeners) {
-            l.onConferenceableConnectionsChanged(this, getConferenceableConnections());
+            l.onConferenceablesChanged(this, getConferenceables());
         }
     }
 
@@ -1120,10 +1154,16 @@
     }
 
     private final void clearConferenceableList() {
-        for (Connection c : mConferenceableConnections) {
-            c.removeConnectionListener(mConnectionDeathListener);
+        for (IConferenceable c : mConferenceables) {
+            if (c instanceof Connection) {
+                Connection connection = (Connection) c;
+                connection.removeConnectionListener(mConnectionDeathListener);
+            } else if (c instanceof Conference) {
+                Conference conference = (Conference) c;
+                conference.removeListener(mConferenceDeathListener);
+            }
         }
-        mConferenceableConnections.clear();
+        mConferenceables.clear();
     }
 
     /**
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 48e6ff3..08f3853 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -515,11 +515,11 @@
         }
 
         @Override
-        public void onConferenceableConnectionsChanged(
-                Connection connection, List<Connection> conferenceableConnections) {
+        public void onConferenceablesChanged(
+                Connection connection, List<IConferenceable> conferenceables) {
             mAdapter.setConferenceableConnections(
                     mIdByConnection.get(connection),
-                    createConnectionIdList(conferenceableConnections));
+                    createIdList(conferenceables));
         }
 
         @Override
@@ -602,7 +602,7 @@
                         connection.getAudioModeIsVoip(),
                         connection.getStatusHints(),
                         connection.getDisconnectCause(),
-                        createConnectionIdList(connection.getConferenceableConnections())));
+                        createIdList(connection.getConferenceables())));
     }
 
     private void abort(String callId) {
@@ -682,12 +682,19 @@
     private void conference(String callId1, String callId2) {
         Log.d(this, "conference %s, %s", callId1, callId2);
 
+        // Attempt to get second connection or conference.
         Connection connection2 = findConnectionForAction(callId2, "conference");
+        Conference conference2 = getNullConference();
         if (connection2 == getNullConnection()) {
-            Log.w(this, "Connection2 missing in conference request %s.", callId2);
-            return;
+            conference2 = findConferenceForAction(callId2, "conference");
+            if (conference2 == getNullConference()) {
+                Log.w(this, "Connection2 or Conference2 missing in conference request %s.",
+                        callId2);
+                return;
+            }
         }
 
+        // Attempt to get first connection or conference and perform merge.
         Connection connection1 = findConnectionForAction(callId1, "conference");
         if (connection1 == getNullConnection()) {
             Conference conference1 = findConferenceForAction(callId1, "addConnection");
@@ -696,10 +703,26 @@
                         "Connection1 or Conference1 missing in conference request %s.",
                         callId1);
             } else {
-                conference1.onMerge(connection2);
+                // Call 1 is a conference.
+                if (connection2 != getNullConnection()) {
+                    // Call 2 is a connection so merge via call 1 (conference).
+                    conference1.onMerge(connection2);
+                } else {
+                    // Call 2 is ALSO a conference; this should never happen.
+                    Log.wtf(this, "There can only be one conference and an attempt was made to " +
+                            "merge two conferences.");
+                    return;
+                }
             }
         } else {
-            onConference(connection1, connection2);
+            // Call 1 is a connection.
+            if (conference2 != getNullConference()) {
+                // Call 2 is a conference, so merge via call 2.
+                conference2.onMerge(connection1);
+            } else {
+                // Call 2 is a connection, so merge together.
+                onConference(connection1, connection2);
+            }
         }
     }
 
@@ -1111,6 +1134,33 @@
         return ids;
     }
 
+    /**
+     * Builds a list of {@link Connection} and {@link Conference} IDs based on the list of
+     * {@link IConferenceable}s passed in.
+     *
+     * @param conferenceables The {@link IConferenceable} connections and conferences.
+     * @return List of string conference and call Ids.
+     */
+    private List<String> createIdList(List<IConferenceable> conferenceables) {
+        List<String> ids = new ArrayList<>();
+        for (IConferenceable c : conferenceables) {
+            // Only allow Connection and Conference conferenceables.
+            if (c instanceof Connection) {
+                Connection connection = (Connection) c;
+                if (mIdByConnection.containsKey(connection)) {
+                    ids.add(mIdByConnection.get(connection));
+                }
+            } else if (c instanceof Conference) {
+                Conference conference = (Conference) c;
+                if (mIdByConference.containsKey(conference)) {
+                    ids.add(mIdByConference.get(conference));
+                }
+            }
+        }
+        Collections.sort(ids);
+        return ids;
+    }
+
     private Conference getNullConference() {
         if (sNullConference == null) {
             sNullConference = new Conference(null) {};
diff --git a/telecomm/java/android/telecom/IConferenceable.java b/telecomm/java/android/telecom/IConferenceable.java
new file mode 100644
index 0000000..095d7cb
--- /dev/null
+++ b/telecomm/java/android/telecom/IConferenceable.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telecom;
+
+import android.annotation.SystemApi;
+
+/**
+ * Interface used to identify entities with which another entity can participate in a conference
+ * call with.  The {@link ConnectionService} implementation will only recognize
+ * {@link IConferenceable}s which are {@link Connection}s or {@link Conference}s.
+ *
+ * @hide
+ */
+@SystemApi
+public interface IConferenceable {
+
+}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index f2d859f..7c03d42 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1797,10 +1797,9 @@
      * for display purpose only, for example, displayed in Phone Status. It won't
      * change the actual MSISDN/MDN. To unset alphatag or number, pass in a null
      * value.
-     * <p>
-     * Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     *
+     * <p>Requires that the calling app has carrier privileges.
+     * @see #hasCarrierPrivileges
      *
      * @param alphaTag alpha-tagging of the dailing nubmer
      * @param number The dialing number
@@ -1814,10 +1813,9 @@
      * for display purpose only, for example, displayed in Phone Status. It won't
      * change the actual MSISDN/MDN. To unset alphatag or number, pass in a null
      * value.
-     * <p>
-     * Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     *
+     * <p>Requires that the calling app has carrier privileges.
+     * @see #hasCarrierPrivileges
      *
      * @param subId the subscriber that the alphatag and dialing number belongs to.
      * @param alphaTag alpha-tagging of the dailing nubmer
@@ -1974,10 +1972,9 @@
 
     /**
      * Sets the voice mail number.
-     * <p>
-     * Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     *
+     * <p>Requires that the calling app has carrier privileges.
+     * @see #hasCarrierPrivileges
      *
      * @param alphaTag The alpha tag to display.
      * @param number The voicemail number.
@@ -1988,10 +1985,9 @@
 
     /**
      * Sets the voicemail number for the given subscriber.
-     * <p>
-     * Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     *
+     * <p>Requires that the calling app has carrier privileges.
+     * @see #hasCarrierPrivileges
      *
      * @param subId The subscriber id.
      * @param alphaTag The alpha tag to display.
@@ -3072,9 +3068,8 @@
      * Set the preferred network type to global mode which includes LTE, CDMA, EvDo and GSM/WCDMA.
      *
      * <p>
-     * Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * Requires that the calling app has carrier privileges.
+     * @see #hasCarrierPrivileges
      *
      * @return true on success; false on any failure.
      */
@@ -3085,9 +3080,13 @@
     /**
      * Values used to return status for hasCarrierPrivileges call.
      */
+    /** @hide */
     public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1;
+    /** @hide */
     public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0;
+    /** @hide */
     public static final int CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED = -1;
+    /** @hide */
     public static final int CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES = -2;
 
     /**
@@ -3099,21 +3098,18 @@
      *
      * TODO: Add a link to documentation.
      *
-     * @return CARRIER_PRIVILEGE_STATUS_HAS_ACCESS if the app has carrier privileges.
-     *         CARRIER_PRIVILEGE_STATUS_NO_ACCESS if the app does not have carrier privileges.
-     *         CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED if the carrier rules are not loaded.
-     *         CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES if there was an error loading carrier
-     *             rules (or if there are no rules).
+     * @return true if the app has carrier privileges.
      */
-    public int hasCarrierPrivileges() {
+    public boolean hasCarrierPrivileges() {
         try {
-            return getITelephony().hasCarrierPrivileges();
+            return getITelephony().getCarrierPrivilegeStatus() ==
+                CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
         } catch (RemoteException ex) {
             Rlog.e(TAG, "hasCarrierPrivileges RemoteException", ex);
         } catch (NullPointerException ex) {
             Rlog.e(TAG, "hasCarrierPrivileges NPE", ex);
         }
-        return CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
+        return false;
     }
 
     /**
@@ -3124,9 +3120,8 @@
      * brand value input. To unset the value, the same function should be
      * called with a null brand value.
      *
-     * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     *  or has to be carrier app - see #hasCarrierPrivileges.
+     * <p>Requires that the calling app has carrier privileges.
+     * @see #hasCarrierPrivileges
      *
      * @param brand The brand name to display/set.
      * @return true if the operation was executed correctly.
@@ -3188,9 +3183,9 @@
         try {
             return getITelephony().checkCarrierPrivilegesForPackage(pkgname);
         } catch (RemoteException ex) {
-            Rlog.e(TAG, "hasCarrierPrivileges RemoteException", ex);
+            Rlog.e(TAG, "checkCarrierPrivilegesForPackage RemoteException", ex);
         } catch (NullPointerException ex) {
-            Rlog.e(TAG, "hasCarrierPrivileges NPE", ex);
+            Rlog.e(TAG, "checkCarrierPrivilegesForPackage NPE", ex);
         }
         return CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
     }
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index cbfa9f6..adb3bc4 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -713,7 +713,7 @@
      *
      * @return carrier privilege status defined in TelephonyManager.
      */
-    int hasCarrierPrivileges();
+    int getCarrierPrivilegeStatus();
 
     /**
      * Similar to above, but check for pkg whose name is pkgname.
diff --git a/test-runner/src/android/test/mock/MockContentProvider.java b/test-runner/src/android/test/mock/MockContentProvider.java
index 28d52b0..5ef71df 100644
--- a/test-runner/src/android/test/mock/MockContentProvider.java
+++ b/test-runner/src/android/test/mock/MockContentProvider.java
@@ -91,8 +91,8 @@
 
         @Override
         public ParcelFileDescriptor openFile(
-                String callingPackage, Uri url, String mode, ICancellationSignal signal)
-                throws RemoteException, FileNotFoundException {
+                String callingPackage, Uri url, String mode, ICancellationSignal signal,
+                IBinder callerToken) throws RemoteException, FileNotFoundException {
             return MockContentProvider.this.openFile(url, mode);
         }
 
diff --git a/test-runner/src/android/test/mock/MockContext.java b/test-runner/src/android/test/mock/MockContext.java
index 46c81b6..3378872 100644
--- a/test-runner/src/android/test/mock/MockContext.java
+++ b/test-runner/src/android/test/mock/MockContext.java
@@ -37,6 +37,7 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.Looper;
 import android.os.UserHandle;
 import android.view.DisplayAdjustments;
@@ -483,6 +484,12 @@
         throw new UnsupportedOperationException();
     }
 
+    /** @hide */
+    @Override
+    public int checkPermission(String permission, int pid, int uid, IBinder callerToken) {
+        return checkPermission(permission, pid, uid);
+    }
+
     @Override
     public int checkCallingPermission(String permission) {
         throw new UnsupportedOperationException();
@@ -524,6 +531,12 @@
         throw new UnsupportedOperationException();
     }
 
+    /** @hide */
+    @Override
+    public int checkUriPermission(Uri uri, int pid, int uid, int modeFlags, IBinder callerToken) {
+        return checkUriPermission(uri, pid, uid, modeFlags);
+    }
+
     @Override
     public int checkCallingUriPermission(Uri uri, int modeFlags) {
         throw new UnsupportedOperationException();
diff --git a/test-runner/src/android/test/mock/MockIContentProvider.java b/test-runner/src/android/test/mock/MockIContentProvider.java
index c0dc7c3..ee8c376 100644
--- a/test-runner/src/android/test/mock/MockIContentProvider.java
+++ b/test-runner/src/android/test/mock/MockIContentProvider.java
@@ -62,7 +62,8 @@
     }
 
     public ParcelFileDescriptor openFile(
-            String callingPackage, Uri url, String mode, ICancellationSignal signal) {
+            String callingPackage, Uri url, String mode, ICancellationSignal signal,
+            IBinder callerToken) {
         throw new UnsupportedOperationException("unimplemented mock method");
     }
 
diff --git a/tests/ActivityTests/AndroidManifest.xml b/tests/ActivityTests/AndroidManifest.xml
index 3930fd6..513f622 100644
--- a/tests/ActivityTests/AndroidManifest.xml
+++ b/tests/ActivityTests/AndroidManifest.xml
@@ -57,6 +57,16 @@
         </service>
         <receiver android:name="UserTarget">
         </receiver>
+        <receiver android:name="StartEmpty" android:exported="true">
+            <intent-filter>
+                <action android:name="com.example.START_EMPTY" />
+            </intent-filter>
+        </receiver>
+        <service android:name="EmptyService" android:exported="true">
+            <intent-filter>
+                <action android:name="com.example.START_EMPTY" />
+            </intent-filter>
+        </service>
         <receiver android:name="SingleUserReceiver"
             android:singleUser="true" android:exported="true" >
         </receiver>
diff --git a/tests/ActivityTests/src/com/google/android/test/activity/EmptyService.java b/tests/ActivityTests/src/com/google/android/test/activity/EmptyService.java
new file mode 100644
index 0000000..1134d90
--- /dev/null
+++ b/tests/ActivityTests/src/com/google/android/test/activity/EmptyService.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.test.activity;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.util.Log;
+
+public class EmptyService extends Service {
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        Log.i("StartEmpty", "STARTED!");
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return null;
+    }
+}
diff --git a/tests/ActivityTests/src/com/google/android/test/activity/StartEmpty.java b/tests/ActivityTests/src/com/google/android/test/activity/StartEmpty.java
new file mode 100644
index 0000000..5e74ead
--- /dev/null
+++ b/tests/ActivityTests/src/com/google/android/test/activity/StartEmpty.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.test.activity;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+public class StartEmpty extends BroadcastReceiver {
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        Log.i("StartEmpty", "STARTED!");
+    }
+}
diff --git a/tests/VectorDrawableTest/res/anim/animation_grouping_1_01.xml b/tests/VectorDrawableTest/res/anim/animation_grouping_1_01.xml
index 8d82d05..8ab79a5 100644
--- a/tests/VectorDrawableTest/res/anim/animation_grouping_1_01.xml
+++ b/tests/VectorDrawableTest/res/anim/animation_grouping_1_01.xml
@@ -15,12 +15,8 @@
      limitations under the License.
 -->
 
-<set xmlns:android="http://schemas.android.com/apk/res/android" >
-
-    <objectAnimator
-        android:duration="3300"
-        android:propertyName="rotation"
-        android:valueFrom="0"
-        android:valueTo="360"
-        android:repeatCount="-1" />
-</set>
\ No newline at end of file
+<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:duration="3300"
+    android:propertyName="rotation"
+    android:valueFrom="0"
+    android:valueTo="450" />
diff --git a/tests/VectorDrawableTest/res/drawable/state_animation_drawable04.xml b/tests/VectorDrawableTest/res/drawable/state_animation_drawable04.xml
new file mode 100644
index 0000000..a0a801c
--- /dev/null
+++ b/tests/VectorDrawableTest/res/drawable/state_animation_drawable04.xml
@@ -0,0 +1,26 @@
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<animated-selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:id="@+id/on" android:state_checked="true"
+        android:drawable="@drawable/vector_drawable_favorite" />
+    <item android:id="@+id/off"
+        android:drawable="@drawable/vector_drawable_favorite" />
+    <transition android:fromId="@+id/off" android:toId="@+id/on"
+        android:drawable="@drawable/animation_drawable_vector"
+        android:reversible="true">
+    </transition>
+</animated-selector>
diff --git a/tests/VectorDrawableTest/res/drawable/state_animation_drawable04_false.xml b/tests/VectorDrawableTest/res/drawable/state_animation_drawable04_false.xml
new file mode 100644
index 0000000..3cf8e48
--- /dev/null
+++ b/tests/VectorDrawableTest/res/drawable/state_animation_drawable04_false.xml
@@ -0,0 +1,26 @@
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<animated-selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:id="@+id/on" android:state_checked="true"
+        android:drawable="@drawable/vector_drawable_favorite" />
+    <item android:id="@+id/off" android:state_checked="false"
+        android:drawable="@drawable/vector_drawable_favorite" />
+    <transition android:fromId="@+id/off" android:toId="@+id/on"
+        android:drawable="@drawable/animation_drawable_vector"
+        android:reversible="false">
+    </transition>
+</animated-selector>
diff --git a/tests/VectorDrawableTest/res/drawable/state_animation_vector_drawable01.xml b/tests/VectorDrawableTest/res/drawable/state_animation_vector_drawable01.xml
index 18d7755..768fe39 100644
--- a/tests/VectorDrawableTest/res/drawable/state_animation_vector_drawable01.xml
+++ b/tests/VectorDrawableTest/res/drawable/state_animation_vector_drawable01.xml
@@ -17,7 +17,7 @@
 <animated-selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:id="@+id/on" android:state_checked="true"
         android:drawable="@drawable/vector_drawable12" />
-    <item android:id="@+id/off"
+    <item android:id="@+id/off" android:state_checked="false"
         android:drawable="@drawable/vector_drawable12" />
     <transition android:fromId="@+id/off" android:toId="@+id/on" android:reversible="true">
         <animated-vector android:drawable="@drawable/vector_drawable12">
diff --git a/tests/VectorDrawableTest/res/drawable/state_animation_vector_drawable01_false.xml b/tests/VectorDrawableTest/res/drawable/state_animation_vector_drawable01_false.xml
new file mode 100644
index 0000000..96d378c
--- /dev/null
+++ b/tests/VectorDrawableTest/res/drawable/state_animation_vector_drawable01_false.xml
@@ -0,0 +1,47 @@
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<animated-selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:id="@+id/on" android:state_checked="true"
+        android:drawable="@drawable/vector_drawable12" />
+    <item android:id="@+id/off" android:state_checked="false"
+        android:drawable="@drawable/vector_drawable12" />
+    <transition android:fromId="@+id/off" android:toId="@+id/on" android:reversible="false">
+        <animated-vector android:drawable="@drawable/vector_drawable12">
+            <target
+                android:name="pie1"
+                android:animation="@anim/trim_path_animation01" />
+            <target
+                android:name="v"
+                android:animation="@anim/trim_path_animation02" />
+            <target
+                android:name="v"
+                android:animation="@anim/trim_path_animation05" />
+            <target
+                android:name="rotationGroup"
+                android:animation="@anim/trim_path_animation03" />
+            <target
+                android:name="rotationGroup3"
+                android:animation="@anim/trim_path_animation03" />
+            <target
+                android:name="rotationGroupBlue"
+                android:animation="@anim/trim_path_animation03" />
+            <target
+                android:name="rotationGroup"
+                android:animation="@anim/trim_path_animation04" />
+        </animated-vector>
+    </transition>
+</animated-selector>
diff --git a/tests/VectorDrawableTest/res/drawable/state_animation_vector_drawable02_false.xml b/tests/VectorDrawableTest/res/drawable/state_animation_vector_drawable02_false.xml
new file mode 100644
index 0000000..b722da1
--- /dev/null
+++ b/tests/VectorDrawableTest/res/drawable/state_animation_vector_drawable02_false.xml
@@ -0,0 +1,26 @@
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<animated-selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:id="@+id/on" android:state_checked="true"
+        android:drawable="@drawable/vector_drawable_grouping_1" />
+    <item android:id="@+id/off" android:state_checked="false"
+        android:drawable="@drawable/vector_drawable_grouping_1" />
+    <transition android:fromId="@+id/off" android:toId="@+id/on"
+        android:drawable="@drawable/animation_vector_drawable_grouping_1"
+        android:reversible="false">
+    </transition>
+</animated-selector>
diff --git a/tests/VectorDrawableTest/res/drawable/state_animation_vector_drawable03.xml b/tests/VectorDrawableTest/res/drawable/state_animation_vector_drawable03.xml
index 65cf25b..e24dd1f 100644
--- a/tests/VectorDrawableTest/res/drawable/state_animation_vector_drawable03.xml
+++ b/tests/VectorDrawableTest/res/drawable/state_animation_vector_drawable03.xml
@@ -17,7 +17,7 @@
 <animated-selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:id="@+id/on" android:state_checked="true"
         android:drawable="@drawable/vector_drawable_favorite" />
-    <item android:id="@+id/off"
+    <item android:id="@+id/off" android:state_checked="false"
         android:drawable="@drawable/vector_drawable_favorite" />
     <transition android:fromId="@+id/off" android:toId="@+id/on"
         android:drawable="@drawable/animation_vector_drawable_favorite"
diff --git a/tests/VectorDrawableTest/res/drawable/state_animation_vector_drawable03_false.xml b/tests/VectorDrawableTest/res/drawable/state_animation_vector_drawable03_false.xml
new file mode 100644
index 0000000..e788bc2
--- /dev/null
+++ b/tests/VectorDrawableTest/res/drawable/state_animation_vector_drawable03_false.xml
@@ -0,0 +1,26 @@
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<animated-selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:id="@+id/on" android:state_checked="true"
+        android:drawable="@drawable/vector_drawable_favorite" />
+    <item android:id="@+id/off" android:state_checked="false"
+        android:drawable="@drawable/vector_drawable_favorite" />
+    <transition android:fromId="@+id/off" android:toId="@+id/on"
+        android:drawable="@drawable/animation_vector_drawable_favorite"
+        android:reversible="false">
+    </transition>
+</animated-selector>
diff --git a/tests/VectorDrawableTest/src/com/android/test/dynamic/AnimatedStateVectorDrawableTest.java b/tests/VectorDrawableTest/src/com/android/test/dynamic/AnimatedStateVectorDrawableTest.java
index 566cc4b..5386555 100644
--- a/tests/VectorDrawableTest/src/com/android/test/dynamic/AnimatedStateVectorDrawableTest.java
+++ b/tests/VectorDrawableTest/src/com/android/test/dynamic/AnimatedStateVectorDrawableTest.java
@@ -27,9 +27,16 @@
     private static final String LOGCAT = "AnimatedStateVectorDrawableTest";
 
     protected int[] icon = {
+            // These shows pairs of ASLD , the left side set the reversible to true.
+            // the right side set to false.
             R.drawable.state_animation_vector_drawable01,
+            R.drawable.state_animation_vector_drawable01_false,
             R.drawable.state_animation_vector_drawable02,
+            R.drawable.state_animation_vector_drawable02_false,
             R.drawable.state_animation_vector_drawable03,
+            R.drawable.state_animation_vector_drawable03_false,
+            R.drawable.state_animation_drawable04,
+            R.drawable.state_animation_drawable04_false,
     };
 
     @Override
@@ -39,7 +46,7 @@
         ScrollView scrollView = new ScrollView(this);
         GridLayout container = new GridLayout(this);
         scrollView.addView(container);
-        container.setColumnCount(5);
+        container.setColumnCount(2);
 
         for (int i = 0; i < icon.length; i++) {
             CheckBox button = new CheckBox(this);
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java
index 89288bf..e4cbb2f 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java
@@ -90,7 +90,7 @@
 
     @Override
     public ParcelFileDescriptor openFile(
-            String callingPackage, Uri arg0, String arg1, ICancellationSignal signal)
+            String callingPackage, Uri arg0, String arg1, ICancellationSignal signal, IBinder token)
             throws RemoteException, FileNotFoundException {
         // TODO Auto-generated method stub
         return null;
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index aeb70e9..e1f24ce 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -16,6 +16,7 @@
 
 package com.android.layoutlib.bridge.android;
 
+import android.os.IBinder;
 import com.android.annotations.Nullable;
 import com.android.ide.common.rendering.api.ILayoutPullParser;
 import com.android.ide.common.rendering.api.IProjectCallback;
@@ -482,16 +483,7 @@
             // In some cases, style may not be a dynamic id, so we do a full search.
             ResourceReference ref = resolveId(resid);
             if (ref != null) {
-                if (ref.isFramework()) {
-                    ref =
-                      getRenderResources().getFrameworkResource(ResourceType.STYLE, ref.getName());
-                } else {
-                    ref =
-                      getRenderResources().getProjectResource(ResourceType.STYLE, ref.getName());
-                }
-                if (ref instanceof StyleResourceValue) {
-                    style = ((StyleResourceValue) ref);
-                }
+                mRenderResources.getStyle(ref.getName(), ref.isFramework());
             }
         }
 
@@ -938,12 +930,24 @@
     }
 
     @Override
+    public int checkPermission(String arg0, int arg1, int arg2, IBinder arg3) {
+        // pass
+        return 0;
+    }
+
+    @Override
     public int checkUriPermission(Uri arg0, int arg1, int arg2, int arg3) {
         // pass
         return 0;
     }
 
     @Override
+    public int checkUriPermission(Uri arg0, int arg1, int arg2, int arg3, IBinder arg4) {
+        // pass
+        return 0;
+    }
+
+    @Override
     public int checkUriPermission(Uri arg0, String arg1, String arg2, int arg3,
             int arg4, int arg5) {
         // pass