Merge "More work on device idle mode."
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 3fca463..87fcd81 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -3348,7 +3348,7 @@
         // Minimum touch target size for handles
         private int mMinSize;
         // Indicates the line of text that the handle is on.
-        protected int mLine = -1;
+        protected int mPrevLine = -1;
 
         public HandleView(Drawable drawableLtr, Drawable drawableRtl) {
             super(mTextView.getContext());
@@ -3499,7 +3499,7 @@
                     addPositionToTouchUpFilter(offset);
                 }
                 final int line = layout.getLineForOffset(offset);
-                mLine = line;
+                mPrevLine = line;
 
                 mPositionX = (int) (layout.getPrimaryHorizontal(offset) - 0.5f - mHotspotX -
                         getHorizontalOffset() + getCursorOffset());
@@ -3839,19 +3839,22 @@
         public void updatePosition(float x, float y) {
             final int trueOffset = mTextView.getOffsetForPosition(x, y);
             final int currLine = mTextView.getLineAtCoordinate(y);
-            int offset = trueOffset;
-            boolean positionCursor = false;
 
+            // Don't select white space on different lines.
+            if (isWhitespaceLine(mPrevLine, currLine, trueOffset)) return;
+
+            boolean positionCursor = false;
+            int offset = trueOffset;
             int end = getWordEnd(offset, true);
             int start = getWordStart(offset);
 
             if (offset < mPrevOffset) {
                 // User is increasing the selection.
-                if (!mInWord || currLine < mLine) {
+                if (!mInWord || currLine < mPrevLine) {
                     // We're not in a word, or we're on a different line so we'll expand by
                     // word. First ensure the user has at least entered the next word.
                     int offsetToWord = Math.min((end - start) / 2, 2);
-                    if (offset <= end - offsetToWord || currLine < mLine) {
+                    if (offset <= end - offsetToWord || currLine < mPrevLine) {
                         offset = start;
                     } else {
                         offset = mPrevOffset;
@@ -3863,7 +3866,7 @@
                 positionCursor = true;
             } else if (offset - mTouchWordOffset > mPrevOffset) {
                 // User is shrinking the selection.
-                if (currLine > mLine) {
+                if (currLine > mPrevLine) {
                     // We're on a different line, so we'll snap to word boundaries.
                     offset = end;
                 }
@@ -3878,7 +3881,7 @@
                 final int selectionEnd = mTextView.getSelectionEnd();
                 if (offset >= selectionEnd) {
                     // We can't cross the handles so let's just constrain the Y value.
-                    int alteredOffset = mTextView.getOffsetAtCoordinate(mLine, x);
+                    int alteredOffset = mTextView.getOffsetAtCoordinate(mPrevLine, x);
                     if (alteredOffset >= selectionEnd) {
                         // Can't pass the other drag handle.
                         offset = Math.max(0, selectionEnd - 1);
@@ -3939,6 +3942,10 @@
         public void updatePosition(float x, float y) {
             final int trueOffset = mTextView.getOffsetForPosition(x, y);
             final int currLine = mTextView.getLineAtCoordinate(y);
+
+            // Don't select white space on different lines.
+            if (isWhitespaceLine(mPrevLine, currLine, trueOffset)) return;
+
             int offset = trueOffset;
             boolean positionCursor = false;
 
@@ -3947,11 +3954,11 @@
 
             if (offset > mPrevOffset) {
                 // User is increasing the selection.
-                if (!mInWord || currLine > mLine) {
+                if (!mInWord || currLine > mPrevLine) {
                     // We're not in a word, or we're on a different line so we'll expand by
                     // word. First ensure the user has at least entered the next word.
                     int midPoint = Math.min((end - start) / 2, 2);
-                    if (offset >= start + midPoint || currLine > mLine) {
+                    if (offset >= start + midPoint || currLine > mPrevLine) {
                         offset = end;
                     } else {
                         offset = mPrevOffset;
@@ -3963,7 +3970,7 @@
                 positionCursor = true;
             } else if (offset + mTouchWordOffset < mPrevOffset) {
                 // User is shrinking the selection.
-                if (currLine > mLine) {
+                if (currLine > mPrevLine) {
                     // We're on a different line, so we'll snap to word boundaries.
                     offset = getWordStart(offset);
                 }
@@ -3977,7 +3984,7 @@
                 final int selectionStart = mTextView.getSelectionStart();
                 if (offset <= selectionStart) {
                     // We can't cross the handles so let's just constrain the Y value.
-                    int alteredOffset = mTextView.getOffsetAtCoordinate(mLine, x);
+                    int alteredOffset = mTextView.getOffsetAtCoordinate(mPrevLine, x);
                     int length = mTextView.getText().length();
                     if (alteredOffset <= selectionStart) {
                         // Can't pass the other drag handle.
@@ -4002,6 +4009,36 @@
     }
 
     /**
+     * Checks whether selection is happening on a different line than previous and
+     * if that line only contains whitespace up to the touch location.
+     *
+     * @param prevLine The previous line the selection was on.
+     * @param currLine The current line being selected.
+     * @param offset The offset in the text where the touch occurred.
+     * @return Whether or not it was just a white space line being selected.
+     */
+    private boolean isWhitespaceLine(int prevLine, int currLine, int offset) {
+        if (prevLine == currLine) {
+            // Same line; don't care.
+            return false;
+        }
+        CharSequence text = mTextView.getText();
+        if (offset == text.length()) {
+            // No character at the last position.
+            return false;
+        }
+        int lineEndOffset = mTextView.getLayout().getLineEnd(currLine);
+        for (int cp, i = offset; i < lineEndOffset; i += Character.charCount(cp)) {
+            cp = Character.codePointAt(text, i);
+            if (!Character.isSpaceChar(cp) && !Character.isWhitespace(cp)) {
+                // There are non white space chars on the line.
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
      * A CursorController instance can be used to control a cursor in the text.
      */
     private interface CursorController extends ViewTreeObserver.OnTouchModeChangeListener {
@@ -4081,6 +4118,8 @@
         private int mStartOffset = -1;
         // Indicates whether the user is selecting text and using the drag accelerator.
         private boolean mDragAcceleratorActive;
+        // Indicates the line of text the drag accelerator is on.
+        private int mPrevLine = -1;
 
         SelectionModifierCursorController() {
             resetTouchOffsets();
@@ -4173,6 +4212,8 @@
                         }
                     }
 
+                    // New selection, reset line.
+                    mPrevLine = mTextView.getLineAtCoordinate(y);
                     mDownPositionX = x;
                     mDownPositionY = y;
                     mGestureStayedInTapRegion = true;
@@ -4229,6 +4270,13 @@
                             if (my > fingerOffset) my -= fingerOffset;
                             offset = mTextView.getOffsetForPosition(mx, my);
 
+                            int currLine = mTextView.getLineAtCoordinate(my);
+
+                            // Don't select white space on different lines.
+                            if (isWhitespaceLine(mPrevLine, currLine, offset)) return;
+
+                            mPrevLine = currLine;
+
                             // Perform the check for closeness at edge of view, if we're very close
                             // don't adjust the offset to be in front of the finger - otherwise the
                             // user can't select words at the edge.
diff --git a/core/res/res/layout-watch/progress_dialog_material.xml b/core/res/res/layout-watch/progress_dialog_material.xml
new file mode 100644
index 0000000..228f724
--- /dev/null
+++ b/core/res/res/layout-watch/progress_dialog_material.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content">
+
+    <LinearLayout
+        android:id="@+id/body"
+        android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:baselineAligned="false"
+        android:paddingStart="?attr/dialogPreferredPadding"
+        android:paddingTop="@dimen/dialog_padding_top_material"
+        android:paddingEnd="?attr/dialogPreferredPadding"
+        android:paddingBottom="@dimen/dialog_padding_top_material">
+
+        <ProgressBar
+            android:id="@id/progress"
+            style="?android:attr/progressBarStyleSmall"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:max="10000"
+            android:layout_marginEnd="?attr/dialogPreferredPadding" />
+
+        <TextView
+            android:id="@+id/message"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical" />
+    </LinearLayout>
+</FrameLayout>
diff --git a/libs/hwui/thread/Signal.h b/libs/hwui/thread/Signal.h
index dcf5449..d4cfeeb 100644
--- a/libs/hwui/thread/Signal.h
+++ b/libs/hwui/thread/Signal.h
@@ -30,8 +30,10 @@
     ~Signal() { }
 
     void signal() {
-        Mutex::Autolock l(mLock);
-        mSignaled = true;
+        {
+            Mutex::Autolock l(mLock);
+            mSignaled = true;
+        }
         mCondition.signal(mType);
     }
 
diff --git a/libs/hwui/thread/TaskManager.cpp b/libs/hwui/thread/TaskManager.cpp
index c69b2fd..f0ed0bb 100644
--- a/libs/hwui/thread/TaskManager.cpp
+++ b/libs/hwui/thread/TaskManager.cpp
@@ -109,8 +109,11 @@
         return false;
     }
 
-    Mutex::Autolock l(mLock);
-    ssize_t index = mTasks.add(task);
+    ssize_t index;
+    {
+        Mutex::Autolock l(mLock);
+        index = mTasks.add(task);
+    }
     mSignal.signal();
 
     return index >= 0;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
index 5c1a317..cb78deb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
@@ -17,7 +17,6 @@
 package com.android.systemui.qs.tiles;
 
 import android.app.ActivityManager;
-import android.os.SystemClock;
 
 import com.android.systemui.R;
 import com.android.systemui.qs.QSTile;
@@ -27,16 +26,11 @@
 public class FlashlightTile extends QSTile<QSTile.BooleanState> implements
         FlashlightController.FlashlightListener {
 
-    /** Grace period for which we consider the flashlight
-     * still available because it was recently on. */
-    private static final long RECENTLY_ON_DURATION_MILLIS = 500;
-
     private final AnimationIcon mEnable
             = new AnimationIcon(R.drawable.ic_signal_flashlight_enable_animation);
     private final AnimationIcon mDisable
             = new AnimationIcon(R.drawable.ic_signal_flashlight_disable_animation);
     private final FlashlightController mFlashlightController;
-    private long mWasLastOn;
 
     public FlashlightTile(Host host) {
         super(host);
@@ -69,33 +63,17 @@
             return;
         }
         boolean newState = !mState.value;
-        mFlashlightController.setFlashlight(newState);
         refreshState(newState ? UserBoolean.USER_TRUE : UserBoolean.USER_FALSE);
+        mFlashlightController.setFlashlight(newState);
     }
 
     @Override
     protected void handleUpdateState(BooleanState state, Object arg) {
-        if (state.value) {
-            mWasLastOn = SystemClock.uptimeMillis();
-        }
-
+        state.visible = mFlashlightController.isAvailable();
+        state.label = mHost.getContext().getString(R.string.quick_settings_flashlight_label);
         if (arg instanceof UserBoolean) {
             state.value = ((UserBoolean) arg).value;
         }
-
-        if (!state.value && mWasLastOn != 0) {
-            if (SystemClock.uptimeMillis() > mWasLastOn + RECENTLY_ON_DURATION_MILLIS) {
-                mWasLastOn = 0;
-            } else {
-                mHandler.removeCallbacks(mRecentlyOnTimeout);
-                mHandler.postAtTime(mRecentlyOnTimeout, mWasLastOn + RECENTLY_ON_DURATION_MILLIS);
-            }
-        }
-
-        // Always show the tile when the flashlight is or was recently on. This is needed because
-        // the camera is not available while it is being used for the flashlight.
-        state.visible = mWasLastOn != 0 || mFlashlightController.isAvailable();
-        state.label = mHost.getContext().getString(R.string.quick_settings_flashlight_label);
         final AnimationIcon icon = state.value ? mEnable : mDisable;
         icon.setAllowAnimation(arg instanceof UserBoolean && ((UserBoolean) arg).userInitiated);
         state.icon = icon;
@@ -115,8 +93,8 @@
     }
 
     @Override
-    public void onFlashlightOff() {
-        refreshState(UserBoolean.BACKGROUND_FALSE);
+    public void onFlashlightChanged(boolean enabled) {
+        refreshState(enabled ? UserBoolean.BACKGROUND_TRUE : UserBoolean.BACKGROUND_FALSE);
     }
 
     @Override
@@ -128,11 +106,4 @@
     public void onFlashlightAvailabilityChanged(boolean available) {
         refreshState();
     }
-
-    private Runnable mRecentlyOnTimeout = new Runnable() {
-        @Override
-        public void run() {
-            refreshState();
-        }
-    };
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 0c21b20..a247c8e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -336,7 +336,6 @@
     }
 
     public void launchCamera() {
-        mFlashlightController.killFlashlight();
         Intent intent = getCameraIntent();
         boolean wouldLaunchResolverActivity = PreviewInflater.wouldLaunchResolverActivity(
                 mContext, intent, mLockPatternUtils.getCurrentUser());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java
index c9ba8f6..cd1914c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java
@@ -17,20 +17,14 @@
 package com.android.systemui.statusbar.policy;
 
 import android.content.Context;
-import android.graphics.SurfaceTexture;
 import android.hardware.camera2.CameraAccessException;
-import android.hardware.camera2.CameraCaptureSession;
 import android.hardware.camera2.CameraCharacteristics;
-import android.hardware.camera2.CameraDevice;
 import android.hardware.camera2.CameraManager;
-import android.hardware.camera2.CameraMetadata;
-import android.hardware.camera2.CaptureRequest;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Process;
+import android.text.TextUtils;
 import android.util.Log;
-import android.util.Size;
-import android.view.Surface;
 
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
@@ -44,7 +38,7 @@
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     private static final int DISPATCH_ERROR = 0;
-    private static final int DISPATCH_OFF = 1;
+    private static final int DISPATCH_CHANGED = 1;
     private static final int DISPATCH_AVAILABILITY_CHANGED = 2;
 
     private final CameraManager mCameraManager;
@@ -57,52 +51,50 @@
     /** Lock on {@code this} when accessing */
     private boolean mFlashlightEnabled;
 
-    private String mCameraId;
-    private boolean mCameraAvailable;
-    private CameraDevice mCameraDevice;
-    private CaptureRequest mFlashlightRequest;
-    private CameraCaptureSession mSession;
-    private SurfaceTexture mSurfaceTexture;
-    private Surface mSurface;
+    private final String mCameraId;
+    private boolean mTorchAvailable;
 
     public FlashlightController(Context mContext) {
         mCameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
-        initialize();
-    }
 
-    public void initialize() {
+        String cameraId = null;
         try {
-            mCameraId = getCameraId();
+            cameraId = getCameraId();
         } catch (Throwable e) {
             Log.e(TAG, "Couldn't initialize.", e);
             return;
+        } finally {
+            mCameraId = cameraId;
         }
 
         if (mCameraId != null) {
             ensureHandler();
-            mCameraManager.registerAvailabilityCallback(mAvailabilityCallback, mHandler);
+            mCameraManager.registerTorchCallback(mTorchCallback, mHandler);
         }
     }
 
-    public synchronized void setFlashlight(boolean enabled) {
-        if (mFlashlightEnabled != enabled) {
-            mFlashlightEnabled = enabled;
-            postUpdateFlashlight();
-        }
-    }
-
-    public void killFlashlight() {
-        boolean enabled;
+    public void setFlashlight(boolean enabled) {
+        boolean pendingError = false;
         synchronized (this) {
-            enabled = mFlashlightEnabled;
+            if (mFlashlightEnabled != enabled) {
+                mFlashlightEnabled = enabled;
+                try {
+                    mCameraManager.setTorchMode(mCameraId, enabled);
+                } catch (CameraAccessException e) {
+                    Log.e(TAG, "Couldn't set torch mode", e);
+                    mFlashlightEnabled = false;
+                    pendingError = true;
+                }
+            }
         }
-        if (enabled) {
-            mHandler.post(mKillFlashlightRunnable);
+        dispatchModeChanged(mFlashlightEnabled);
+        if (pendingError) {
+            dispatchError();
         }
     }
 
     public synchronized boolean isAvailable() {
-        return mCameraAvailable;
+        return mTorchAvailable;
     }
 
     public void addListener(FlashlightListener l) {
@@ -126,42 +118,6 @@
         }
     }
 
-    private void startDevice() throws CameraAccessException {
-        mCameraManager.openCamera(getCameraId(), mCameraListener, mHandler);
-    }
-
-    private void startSession() throws CameraAccessException {
-        mSurfaceTexture = new SurfaceTexture(false);
-        Size size = getSmallestSize(mCameraDevice.getId());
-        mSurfaceTexture.setDefaultBufferSize(size.getWidth(), size.getHeight());
-        mSurface = new Surface(mSurfaceTexture);
-        ArrayList<Surface> outputs = new ArrayList<>(1);
-        outputs.add(mSurface);
-        mCameraDevice.createCaptureSession(outputs, mSessionListener, mHandler);
-    }
-
-    private Size getSmallestSize(String cameraId) throws CameraAccessException {
-        Size[] outputSizes = mCameraManager.getCameraCharacteristics(cameraId)
-                .get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
-                .getOutputSizes(SurfaceTexture.class);
-        if (outputSizes == null || outputSizes.length == 0) {
-            throw new IllegalStateException(
-                    "Camera " + cameraId + "doesn't support any outputSize.");
-        }
-        Size chosen = outputSizes[0];
-        for (Size s : outputSizes) {
-            if (chosen.getWidth() >= s.getWidth() && chosen.getHeight() >= s.getHeight()) {
-                chosen = s;
-            }
-        }
-        return chosen;
-    }
-
-    private void postUpdateFlashlight() {
-        ensureHandler();
-        mHandler.post(mUpdateFlashlightRunnable);
-    }
-
     private String getCameraId() throws CameraAccessException {
         String[] ids = mCameraManager.getCameraIdList();
         for (String id : ids) {
@@ -176,70 +132,12 @@
         return null;
     }
 
-    private void updateFlashlight(boolean forceDisable) {
-        try {
-            boolean enabled;
-            synchronized (this) {
-                enabled = mFlashlightEnabled && !forceDisable;
-            }
-            if (enabled) {
-                if (mCameraDevice == null) {
-                    startDevice();
-                    return;
-                }
-                if (mSession == null) {
-                    startSession();
-                    return;
-                }
-                if (mFlashlightRequest == null) {
-                    CaptureRequest.Builder builder = mCameraDevice.createCaptureRequest(
-                            CameraDevice.TEMPLATE_PREVIEW);
-                    builder.set(CaptureRequest.FLASH_MODE, CameraMetadata.FLASH_MODE_TORCH);
-                    builder.addTarget(mSurface);
-                    CaptureRequest request = builder.build();
-                    mSession.capture(request, null, mHandler);
-                    mFlashlightRequest = request;
-                }
-            } else {
-                if (mCameraDevice != null) {
-                    mCameraDevice.close();
-                    teardown();
-                }
-            }
-
-        } catch (CameraAccessException|IllegalStateException|UnsupportedOperationException e) {
-            Log.e(TAG, "Error in updateFlashlight", e);
-            handleError();
-        }
-    }
-
-    private void teardown() {
-        mCameraDevice = null;
-        mSession = null;
-        mFlashlightRequest = null;
-        if (mSurface != null) {
-            mSurface.release();
-            mSurfaceTexture.release();
-        }
-        mSurface = null;
-        mSurfaceTexture = null;
-    }
-
-    private void handleError() {
-        synchronized (this) {
-            mFlashlightEnabled = false;
-        }
-        dispatchError();
-        dispatchOff();
-        updateFlashlight(true /* forceDisable */);
-    }
-
-    private void dispatchOff() {
-        dispatchListeners(DISPATCH_OFF, false /* argument (ignored) */);
+    private void dispatchModeChanged(boolean enabled) {
+        dispatchListeners(DISPATCH_CHANGED, enabled);
     }
 
     private void dispatchError() {
-        dispatchListeners(DISPATCH_ERROR, false /* argument (ignored) */);
+        dispatchListeners(DISPATCH_CHANGED, false /* argument (ignored) */);
     }
 
     private void dispatchAvailabilityChanged(boolean available) {
@@ -255,8 +153,8 @@
                 if (l != null) {
                     if (message == DISPATCH_ERROR) {
                         l.onFlashlightError();
-                    } else if (message == DISPATCH_OFF) {
-                        l.onFlashlightOff();
+                    } else if (message == DISPATCH_CHANGED) {
+                        l.onFlashlightChanged(argument);
                     } else if (message == DISPATCH_AVAILABILITY_CHANGED) {
                         l.onFlashlightAvailabilityChanged(argument);
                     }
@@ -279,106 +177,57 @@
         }
     }
 
-    private final CameraDevice.StateListener mCameraListener = new CameraDevice.StateListener() {
-        @Override
-        public void onOpened(CameraDevice camera) {
-            mCameraDevice = camera;
-            postUpdateFlashlight();
-        }
+    private final CameraManager.TorchCallback mTorchCallback =
+            new CameraManager.TorchCallback() {
 
         @Override
-        public void onDisconnected(CameraDevice camera) {
-            if (mCameraDevice == camera) {
-                dispatchOff();
-                teardown();
-            }
-        }
-
-        @Override
-        public void onError(CameraDevice camera, int error) {
-            Log.e(TAG, "Camera error: camera=" + camera + " error=" + error);
-            if (camera == mCameraDevice || mCameraDevice == null) {
-                handleError();
-            }
-        }
-    };
-
-    private final CameraCaptureSession.StateListener mSessionListener =
-            new CameraCaptureSession.StateListener() {
-        @Override
-        public void onConfigured(CameraCaptureSession session) {
-            if (session.getDevice() == mCameraDevice) {
-                mSession = session;
-            } else {
-                session.close();
-            }
-            postUpdateFlashlight();
-        }
-
-        @Override
-        public void onConfigureFailed(CameraCaptureSession session) {
-            Log.e(TAG, "Configure failed.");
-            if (mSession == null || mSession == session) {
-                handleError();
-            }
-        }
-    };
-
-    private final Runnable mUpdateFlashlightRunnable = new Runnable() {
-        @Override
-        public void run() {
-            updateFlashlight(false /* forceDisable */);
-        }
-    };
-
-    private final Runnable mKillFlashlightRunnable = new Runnable() {
-        @Override
-        public void run() {
-            synchronized (this) {
-                mFlashlightEnabled = false;
-            }
-            updateFlashlight(true /* forceDisable */);
-            dispatchOff();
-        }
-    };
-
-    private final CameraManager.AvailabilityCallback mAvailabilityCallback =
-            new CameraManager.AvailabilityCallback() {
-        @Override
-        public void onCameraAvailable(String cameraId) {
-            if (DEBUG) Log.d(TAG, "onCameraAvailable(" + cameraId + ")");
-            if (cameraId.equals(mCameraId)) {
-                setCameraAvailable(true);
-            }
-        }
-
-        @Override
-        public void onCameraUnavailable(String cameraId) {
-            if (DEBUG) Log.d(TAG, "onCameraUnavailable(" + cameraId + ")");
-            if (cameraId.equals(mCameraId)) {
+        public void onTorchModeUnavailable(String cameraId) {
+            if (TextUtils.equals(cameraId, mCameraId)) {
                 setCameraAvailable(false);
             }
         }
 
+        @Override
+        public void onTorchModeChanged(String cameraId, boolean enabled) {
+            if (TextUtils.equals(cameraId, mCameraId)) {
+                setCameraAvailable(true);
+                setTorchMode(enabled);
+            }
+        }
+
         private void setCameraAvailable(boolean available) {
             boolean changed;
             synchronized (FlashlightController.this) {
-                changed = mCameraAvailable != available;
-                mCameraAvailable = available;
+                changed = mTorchAvailable != available;
+                mTorchAvailable = available;
             }
             if (changed) {
                 if (DEBUG) Log.d(TAG, "dispatchAvailabilityChanged(" + available + ")");
                 dispatchAvailabilityChanged(available);
             }
         }
+
+        private void setTorchMode(boolean enabled) {
+            boolean changed;
+            synchronized (FlashlightController.this) {
+                changed = mFlashlightEnabled != enabled;
+                mFlashlightEnabled = enabled;
+            }
+            if (changed) {
+                if (DEBUG) Log.d(TAG, "dispatchModeChanged(" + enabled + ")");
+                dispatchModeChanged(enabled);
+            }
+        }
     };
 
     public interface FlashlightListener {
 
         /**
-         * Called when the flashlight turns off unexpectedly.
+         * Called when the flashlight was turned off or on.
+         * @param enabled true if the flashlight is currently turned on.
          */
-        void onFlashlightOff();
+        void onFlashlightChanged(boolean enabled);
+
 
         /**
          * Called when there is an error that turns the flashlight off.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
index 2fbb812..f0dd943 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
@@ -190,7 +190,8 @@
             NetworkCapabilities networkCapabilities =
                     mConnectivityManager.getNetworkCapabilities(network);
             if (DEBUG) Log.d(TAG, "onAvailable " + network.netId + " : " + networkCapabilities);
-            if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN)) {
+            if (networkCapabilities != null &&
+                    networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN)) {
                 setCurrentNetid(network.netId);
             }
         };
diff --git a/services/core/java/com/android/server/MidiService.java b/services/core/java/com/android/server/MidiService.java
index 3418930..e753664 100644
--- a/services/core/java/com/android/server/MidiService.java
+++ b/services/core/java/com/android/server/MidiService.java
@@ -47,6 +47,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
 
 public class MidiService extends IMidiManager.Stub {
@@ -269,7 +270,9 @@
 
         public void binderDied() {
             synchronized (mDevicesByInfo) {
-                removeDeviceLocked(this);
+                if (mDevicesByInfo.remove(mDeviceInfo) != null) {
+                    removeDeviceLocked(this);
+                }
             }
         }
 
@@ -368,6 +371,7 @@
         synchronized (mDevicesByInfo) {
             Device device = mDevicesByServer.get(server.asBinder());
             if (device != null) {
+                mDevicesByInfo.remove(device.getDeviceInfo());
                 removeDeviceLocked(device);
             }
         }
@@ -454,16 +458,14 @@
 
     // synchronize on mDevicesByInfo
     private void removeDeviceLocked(Device device) {
-        if (mDevicesByInfo.remove(device.getDeviceInfo()) != null) {
-            IMidiDeviceServer server = device.getDeviceServer();
-            if (server != null) {
-                mDevicesByServer.remove(server);
-            }
+        IMidiDeviceServer server = device.getDeviceServer();
+        if (server != null) {
+            mDevicesByServer.remove(server);
+        }
 
-            synchronized (mClients) {
-                for (Client c : mClients.values()) {
-                    c.deviceRemoved(device);
-                }
+        synchronized (mClients) {
+            for (Client c : mClients.values()) {
+                c.deviceRemoved(device);
             }
         }
     }
@@ -616,8 +618,11 @@
 
     private void removePackageDeviceServers(String packageName) {
         synchronized (mDevicesByInfo) {
-            for (Device device : mDevicesByInfo.values()) {
+            Iterator<Device> iterator = mDevicesByInfo.values().iterator();
+            while (iterator.hasNext()) {
+                Device device = iterator.next();
                 if (packageName.equals(device.getPackageName())) {
+                    iterator.remove();
                     removeDeviceLocked(device);
                 }
             }
@@ -634,15 +639,19 @@
 
         pw.println("Devices:");
         pw.increaseIndent();
-        for (Device device : mDevicesByInfo.values()) {
-            pw.println(device.toString());
+        synchronized (mDevicesByInfo) {
+            for (Device device : mDevicesByInfo.values()) {
+                pw.println(device.toString());
+            }
         }
         pw.decreaseIndent();
 
         pw.println("Clients:");
         pw.increaseIndent();
-        for (Client client : mClients.values()) {
-            pw.println(client.toString());
+        synchronized (mClients) {
+            for (Client client : mClients.values()) {
+                pw.println(client.toString());
+            }
         }
         pw.decreaseIndent();
     }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 626b442..78bd15d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -3046,12 +3046,12 @@
                 int[] permGids = null;
                 try {
                     checkTime(startTime, "startProcess: getting gids from package manager");
-                    final PackageManager pm = mContext.getPackageManager();
-                    permGids = pm.getPackageGids(app.info.packageName);
+                    permGids = AppGlobals.getPackageManager().getPackageGids(app.info.packageName,
+                            app.userId);
 
                     if (Environment.isExternalStorageEmulated()) {
                         checkTime(startTime, "startProcess: checking external storage perm");
-                        if (pm.checkPermission(
+                        if (mContext.getPackageManager().checkPermission(
                                 android.Manifest.permission.ACCESS_ALL_EXTERNAL_STORAGE,
                                 app.info.packageName) == PERMISSION_GRANTED) {
                             mountExternal = Zygote.MOUNT_EXTERNAL_MULTIUSER_ALL;
@@ -3059,7 +3059,7 @@
                             mountExternal = Zygote.MOUNT_EXTERNAL_MULTIUSER;
                         }
                     }
-                } catch (PackageManager.NameNotFoundException e) {
+                } catch (RemoteException e) {
                     Slog.w(TAG, "Unable to retrieve gids", e);
                 }
 
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index b102a07..f5fef63 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1112,7 +1112,7 @@
         }
     }
 
-    private void setVisibile(ActivityRecord r, boolean visible) {
+    private void setVisible(ActivityRecord r, boolean visible) {
         r.visible = visible;
         mWindowManager.setAppVisibility(r.appToken, visible);
         final ArrayList<ActivityContainer> containers = r.mChildContainers;
@@ -1297,7 +1297,7 @@
                         if (!r.visible || r.mLaunchTaskBehind) {
                             if (DEBUG_VISBILITY) Slog.v(
                                     TAG, "Starting and making visible: " + r);
-                            setVisibile(r, true);
+                            setVisible(r, true);
                         }
                         if (r != starting) {
                             mStackSupervisor.startSpecificActivityLocked(r, false, false);
@@ -1329,7 +1329,7 @@
                                     r.updateOptionsLocked(r.returningOptions);
                                     mUndrawnActivitiesBelowTopTranslucent.add(r);
                                 }
-                                setVisibile(r, true);
+                                setVisible(r, true);
                                 r.sleeping = false;
                                 r.app.pendingUiClean = true;
                                 r.app.thread.scheduleWindowVisibility(r.appToken, true);
@@ -1364,7 +1364,7 @@
                     if (r.visible) {
                         if (DEBUG_VISBILITY) Slog.v(TAG, "Making invisible: " + r);
                         try {
-                            setVisibile(r, false);
+                            setVisible(r, false);
                             switch (r.state) {
                                 case STOPPING:
                                 case STOPPED:
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index e9e6496..cb96680 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -129,7 +129,7 @@
     static final boolean DEBUG_RELEASE = DEBUG || false;
     static final boolean DEBUG_SAVED_STATE = DEBUG || false;
     static final boolean DEBUG_SCREENSHOTS = DEBUG || false;
-    static final boolean DEBUG_STATES = DEBUG || false;
+    static final boolean DEBUG_STATES = DEBUG || true;
     static final boolean DEBUG_VISIBLE_BEHIND = DEBUG || false;
 
     public static final int HOME_STACK_ID = 0;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 1dc027a..09bc2ab 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -178,7 +178,7 @@
     static final boolean DEBUG_ORIENTATION = false;
     static final boolean DEBUG_APP_ORIENTATION = false;
     static final boolean DEBUG_CONFIGURATION = false;
-    static final boolean DEBUG_APP_TRANSITIONS = false;
+    static final boolean DEBUG_APP_TRANSITIONS = true;
     static final boolean DEBUG_STARTING_WINDOW = false;
     static final boolean DEBUG_WALLPAPER = false;
     static final boolean DEBUG_WALLPAPER_LIGHT = false || DEBUG_WALLPAPER;
@@ -189,7 +189,7 @@
     static final boolean DEBUG_LAYOUT_REPEATS = true;
     static final boolean DEBUG_SURFACE_TRACE = false;
     static final boolean DEBUG_WINDOW_TRACE = false;
-    static final boolean DEBUG_TASK_MOVEMENT = false;
+    static final boolean DEBUG_TASK_MOVEMENT = true;
     static final boolean DEBUG_STACK = false;
     static final boolean DEBUG_DISPLAY = false;
     static final boolean DEBUG_POWER = false;
@@ -3720,10 +3720,8 @@
         } else {
             // TODO(multidisplay): Change to the correct display.
             final WindowList windows = getDefaultWindowListLocked();
-            int pos = windows.size() - 1;
-            while (pos >= 0) {
+            for (int pos = windows.size() - 1; pos >= 0; --pos) {
                 WindowState win = windows.get(pos);
-                pos--;
                 if (win.mAppToken != null) {
                     // We hit an application window. so the orientation will be determined by the
                     // app window. No point in continuing further.