Merge "AppOps: fix nested op tracking, new API to get apps using permissions."
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 1d394e8..4925bf1 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1405,12 +1405,13 @@
     public boolean bindService(Intent service, ServiceConnection conn,
             int flags) {
         warnIfCallingFromSystemProcess();
-        return bindService(service, conn, flags, UserHandle.getUserId(Process.myUid()));
+        return bindServiceAsUser(service, conn, flags, Process.myUserHandle());
     }
 
     /** @hide */
     @Override
-    public boolean bindService(Intent service, ServiceConnection conn, int flags, int userHandle) {
+    public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags,
+            UserHandle user) {
         IServiceConnection sd;
         if (conn == null) {
             throw new IllegalArgumentException("connection is null");
@@ -1432,7 +1433,7 @@
             int res = ActivityManagerNative.getDefault().bindService(
                 mMainThread.getApplicationThread(), getActivityToken(),
                 service, service.resolveTypeIfNeeded(getContentResolver()),
-                sd, flags, userHandle);
+                sd, flags, user.getIdentifier());
             if (res < 0) {
                 throw new SecurityException(
                         "Not allowed to bind to service " + service);
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 9510257..14f2847 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1677,7 +1677,7 @@
      * argument for use by system server and other multi-user aware code.
      * @hide
      */
-    public boolean bindService(Intent service, ServiceConnection conn, int flags, int userHandle) {
+    public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags, UserHandle user) {
         throw new RuntimeException("Not implemented. Must override in a subclass.");
     }
 
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 84ad667..6a61884 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -475,8 +475,9 @@
 
     /** @hide */
     @Override
-    public boolean bindService(Intent service, ServiceConnection conn, int flags, int userHandle) {
-        return mBase.bindService(service, conn, flags, userHandle);
+    public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags,
+            UserHandle user) {
+        return mBase.bindServiceAsUser(service, conn, flags, user);
     }
 
     @Override
diff --git a/core/java/android/ddm/DdmHandleGlTracing.java b/core/java/android/ddm/DdmHandleGlTracing.java
deleted file mode 100644
index 511cf44..0000000
--- a/core/java/android/ddm/DdmHandleGlTracing.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.ddm;
-
-import android.opengl.GLUtils;
-
-import org.apache.harmony.dalvik.ddmc.Chunk;
-import org.apache.harmony.dalvik.ddmc.ChunkHandler;
-import org.apache.harmony.dalvik.ddmc.DdmServer;
-
-import java.nio.ByteBuffer;
-
-public class DdmHandleGlTracing extends ChunkHandler {
-    /** GL TRace control packets. Packet data controls starting/stopping the trace. */
-    public static final int CHUNK_GLTR = type("GLTR");
-
-    private static final DdmHandleGlTracing sInstance = new DdmHandleGlTracing();
-
-    /** singleton, do not instantiate. */
-    private DdmHandleGlTracing() {}
-
-    public static void register() {
-        DdmServer.registerHandler(CHUNK_GLTR, sInstance);
-    }
-
-    @Override
-    public void connected() {
-    }
-
-    @Override
-    public void disconnected() {
-    }
-
-    @Override
-    public Chunk handleChunk(Chunk request) {
-        int type = request.type;
-
-        if (type != CHUNK_GLTR) {
-            throw new RuntimeException("Unknown packet " + ChunkHandler.name(type));
-        }
-
-        ByteBuffer in = wrapChunk(request);
-        GLUtils.setTracingLevel(in.getInt());
-        return null;    // empty response
-    }
-}
diff --git a/core/java/android/ddm/DdmHandleHello.java b/core/java/android/ddm/DdmHandleHello.java
index 70ad648..842a482 100644
--- a/core/java/android/ddm/DdmHandleHello.java
+++ b/core/java/android/ddm/DdmHandleHello.java
@@ -36,7 +36,10 @@
 
     private static DdmHandleHello mInstance = new DdmHandleHello();
 
-    private static final String[] NATIVE_FEATURES = new String[] { "opengl-tracing" };
+    private static final String[] FRAMEWORK_FEATURES = new String[] {
+        "opengl-tracing",
+        "view-hierarchy",
+    };
 
     /* singleton, do not instantiate */
     private DdmHandleHello() {}
@@ -155,22 +158,22 @@
         if (false)
             Log.v("ddm-heap", "Got feature list request");
 
-        int size = 4 + 4 * (vmFeatures.length + NATIVE_FEATURES.length);
+        int size = 4 + 4 * (vmFeatures.length + FRAMEWORK_FEATURES.length);
         for (int i = vmFeatures.length-1; i >= 0; i--)
             size += vmFeatures[i].length() * 2;
-        for (int i = NATIVE_FEATURES.length-1; i>= 0; i--)
-            size += NATIVE_FEATURES[i].length() * 2;
+        for (int i = FRAMEWORK_FEATURES.length-1; i>= 0; i--)
+            size += FRAMEWORK_FEATURES[i].length() * 2;
 
         ByteBuffer out = ByteBuffer.allocate(size);
         out.order(ChunkHandler.CHUNK_ORDER);
-        out.putInt(vmFeatures.length + NATIVE_FEATURES.length);
+        out.putInt(vmFeatures.length + FRAMEWORK_FEATURES.length);
         for (int i = vmFeatures.length-1; i >= 0; i--) {
             out.putInt(vmFeatures[i].length());
             putString(out, vmFeatures[i]);
         }
-        for (int i = NATIVE_FEATURES.length-1; i >= 0; i--) {
-            out.putInt(NATIVE_FEATURES[i].length());
-            putString(out, NATIVE_FEATURES[i]);
+        for (int i = FRAMEWORK_FEATURES.length-1; i >= 0; i--) {
+            out.putInt(FRAMEWORK_FEATURES[i].length());
+            putString(out, FRAMEWORK_FEATURES[i]);
         }
 
         return new Chunk(CHUNK_FEAT, out);
diff --git a/core/java/android/ddm/DdmHandleViewDebug.java b/core/java/android/ddm/DdmHandleViewDebug.java
new file mode 100644
index 0000000..a0578fb
--- /dev/null
+++ b/core/java/android/ddm/DdmHandleViewDebug.java
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.ddm;
+
+import android.opengl.GLUtils;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewDebug;
+import android.view.ViewRootImpl;
+import android.view.WindowManagerGlobal;
+
+import org.apache.harmony.dalvik.ddmc.Chunk;
+import org.apache.harmony.dalvik.ddmc.ChunkHandler;
+import org.apache.harmony.dalvik.ddmc.DdmServer;
+
+import java.io.BufferedWriter;
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+
+/**
+ * Handle various requests related to profiling / debugging of the view system.
+ * Support for these features are advertised via {@link DdmHandleHello}.
+ */
+public class DdmHandleViewDebug extends ChunkHandler {
+    /** Enable/Disable tracing of OpenGL calls. */
+    public static final int CHUNK_VUGL = type("VUGL");
+
+    /** List {@link ViewRootImpl}'s of this process. */
+    private static final int CHUNK_VULW = type("VULW");
+
+    /** Operation on view root, first parameter in packet should be one of VURT_* constants */
+    private static final int CHUNK_VURT = type("VURT");
+
+    /** Dump view hierarchy. */
+    private static final int VURT_DUMP_HIERARCHY = 1;
+
+    /** Capture View Layers. */
+    private static final int VURT_CAPTURE_LAYERS = 2;
+
+    /**
+     * Generic View Operation, first parameter in the packet should be one of the
+     * VUOP_* constants below.
+     */
+    private static final int CHUNK_VUOP = type("VUOP");
+
+    /** Capture View. */
+    private static final int VUOP_CAPTURE_VIEW = 1;
+
+    /** Obtain the Display List corresponding to the view. */
+    private static final int VUOP_DUMP_DISPLAYLIST = 2;
+
+    /** Invalidate View. */
+    private static final int VUOP_INVALIDATE_VIEW = 3;
+
+    /** Re-layout given view. */
+    private static final int VUOP_LAYOUT_VIEW = 4;
+
+    /** Profile a view. */
+    private static final int VUOP_PROFILE_VIEW = 5;
+
+    /** Error code indicating operation specified in chunk is invalid. */
+    private static final int ERR_INVALID_OP = -1;
+
+    /** Error code indicating that the parameters are invalid. */
+    private static final int ERR_INVALID_PARAM = -2;
+
+    private static final DdmHandleViewDebug sInstance = new DdmHandleViewDebug();
+
+    /** singleton, do not instantiate. */
+    private DdmHandleViewDebug() {}
+
+    public static void register() {
+        DdmServer.registerHandler(CHUNK_VUGL, sInstance);
+        DdmServer.registerHandler(CHUNK_VULW, sInstance);
+        DdmServer.registerHandler(CHUNK_VURT, sInstance);
+        DdmServer.registerHandler(CHUNK_VUOP, sInstance);
+    }
+
+    @Override
+    public void connected() {
+    }
+
+    @Override
+    public void disconnected() {
+    }
+
+    @Override
+    public Chunk handleChunk(Chunk request) {
+        int type = request.type;
+
+        if (type == CHUNK_VUGL) {
+            return handleOpenGlTrace(request);
+        } else if (type == CHUNK_VULW) {
+            return listWindows();
+        }
+
+        ByteBuffer in = wrapChunk(request);
+        int op = in.getInt();
+
+        View rootView = getRootView(in);
+        if (rootView == null) {
+            return createFailChunk(ERR_INVALID_PARAM, "Invalid View Root");
+        }
+
+        if (type == CHUNK_VURT) {
+            if (op == VURT_DUMP_HIERARCHY)
+                return dumpHierarchy(rootView, in);
+            else if (op == VURT_CAPTURE_LAYERS)
+                return captureLayers(rootView);
+            else
+                return createFailChunk(ERR_INVALID_OP, "Unknown view root operation: " + op);
+        }
+
+        final View targetView = getTargetView(rootView, in);
+        if (targetView == null) {
+            return createFailChunk(ERR_INVALID_PARAM, "Invalid target view");
+        }
+
+        if (type == CHUNK_VUOP) {
+            switch (op) {
+                case VUOP_CAPTURE_VIEW:
+                    return captureView(rootView, targetView);
+                case VUOP_DUMP_DISPLAYLIST:
+                    return dumpDisplayLists(rootView, targetView);
+                case VUOP_INVALIDATE_VIEW:
+                    return invalidateView(rootView, targetView);
+                case VUOP_LAYOUT_VIEW:
+                    return layoutView(rootView, targetView);
+                case VUOP_PROFILE_VIEW:
+                    return profileView(rootView, targetView);
+                default:
+                    return createFailChunk(ERR_INVALID_OP, "Unknown view operation: " + op);
+            }
+        } else {
+            throw new RuntimeException("Unknown packet " + ChunkHandler.name(type));
+        }
+    }
+
+    private Chunk handleOpenGlTrace(Chunk request) {
+        ByteBuffer in = wrapChunk(request);
+        GLUtils.setTracingLevel(in.getInt());
+        return null;    // empty response
+    }
+
+    /** Returns the list of windows owned by this client. */
+    private Chunk listWindows() {
+        String[] windowNames = WindowManagerGlobal.getInstance().getViewRootNames();
+
+        int responseLength = 4;                     // # of windows
+        for (String name : windowNames) {
+            responseLength += 4;                    // length of next window name
+            responseLength += name.length() * 2;    // window name
+        }
+
+        ByteBuffer out = ByteBuffer.allocate(responseLength);
+        out.order(ChunkHandler.CHUNK_ORDER);
+
+        out.putInt(windowNames.length);
+        for (String name : windowNames) {
+            out.putInt(name.length());
+            putString(out, name);
+        }
+
+        return new Chunk(CHUNK_VULW, out);
+    }
+
+    private View getRootView(ByteBuffer in) {
+        try {
+            int viewRootNameLength = in.getInt();
+            String viewRootName = getString(in, viewRootNameLength);
+            return WindowManagerGlobal.getInstance().getRootView(viewRootName);
+        } catch (BufferUnderflowException e) {
+            return null;
+        }
+    }
+
+    private View getTargetView(View root, ByteBuffer in) {
+        int viewLength;
+        String viewName;
+
+        try {
+            viewLength = in.getInt();
+            viewName = getString(in, viewLength);
+        } catch (BufferUnderflowException e) {
+            return null;
+        }
+
+        return ViewDebug.findView(root, viewName);
+    }
+
+    /**
+     * Returns the view hierarchy and/or view properties starting at the provided view.
+     * Based on the input options, the return data may include:
+     *  - just the view hierarchy
+     *  - view hierarchy & the properties for each of the views
+     *  - just the view properties for a specific view.
+     *  TODO: Currently this only returns views starting at the root, need to fix so that
+     *  it can return properties of any view.
+     */
+    private Chunk dumpHierarchy(View rootView, ByteBuffer in) {
+        boolean skipChildren = in.getInt() > 0;
+        boolean includeProperties = in.getInt() > 0;
+
+        ByteArrayOutputStream b = new ByteArrayOutputStream(1024);
+        try {
+            ViewDebug.dump(rootView, skipChildren, includeProperties, b);
+        } catch (IOException e) {
+            return createFailChunk(1, "Unexpected error while obtaining view hierarchy: "
+                    + e.getMessage());
+        }
+
+        byte[] data = b.toByteArray();
+        return new Chunk(CHUNK_VURT, data, 0, data.length);
+    }
+
+    /** Returns a buffer with region details & bitmap of every single view. */
+    private Chunk captureLayers(View rootView) {
+        ByteArrayOutputStream b = new ByteArrayOutputStream(1024);
+        DataOutputStream dos = new DataOutputStream(b);
+        try {
+            ViewDebug.captureLayers(rootView, dos);
+        } catch (IOException e) {
+            return createFailChunk(1, "Unexpected error while obtaining view hierarchy: "
+                    + e.getMessage());
+        } finally {
+            try {
+                dos.close();
+            } catch (IOException e) {
+                // ignore
+            }
+        }
+
+        byte[] data = b.toByteArray();
+        return new Chunk(CHUNK_VURT, data, 0, data.length);
+    }
+
+    private Chunk captureView(View rootView, View targetView) {
+        ByteArrayOutputStream b = new ByteArrayOutputStream(1024);
+        try {
+            ViewDebug.capture(rootView, b, targetView);
+        } catch (IOException e) {
+            return createFailChunk(1, "Unexpected error while capturing view: "
+                    + e.getMessage());
+        }
+
+        byte[] data = b.toByteArray();
+        return new Chunk(CHUNK_VUOP, data, 0, data.length);
+    }
+
+    /** Returns the display lists corresponding to the provided view. */
+    private Chunk dumpDisplayLists(final View rootView, final View targetView) {
+        rootView.post(new Runnable() {
+            @Override
+            public void run() {
+                ViewDebug.outputDisplayList(rootView, targetView);
+            }
+        });
+        return null;
+    }
+
+    /** Invalidates provided view. */
+    private Chunk invalidateView(final View rootView, final View targetView) {
+        targetView.postInvalidate();
+        return null;
+    }
+
+    /** Lays out provided view. */
+    private Chunk layoutView(View rootView, final View targetView) {
+        rootView.post(new Runnable() {
+            @Override
+            public void run() {
+                targetView.requestLayout();
+            }
+        });
+        return null;
+    }
+
+    /** Profiles provided view. */
+    private Chunk profileView(View rootView, final View targetView) {
+        ByteArrayOutputStream b = new ByteArrayOutputStream(32 * 1024);
+        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(b), 32 * 1024);
+        try {
+            ViewDebug.profileViewAndChildren(targetView, bw);
+        } catch (IOException e) {
+            return createFailChunk(1, "Unexpected error while profiling view: " + e.getMessage());
+        } finally {
+            try {
+                bw.close();
+            } catch (IOException e) {
+                // ignore
+            }
+        }
+
+        byte[] data = b.toByteArray();
+        return new Chunk(CHUNK_VUOP, data, 0, data.length);
+    }
+}
diff --git a/core/java/android/ddm/DdmRegister.java b/core/java/android/ddm/DdmRegister.java
index 2c82967..e0faa51 100644
--- a/core/java/android/ddm/DdmRegister.java
+++ b/core/java/android/ddm/DdmRegister.java
@@ -51,7 +51,7 @@
         DdmHandleNativeHeap.register();
         DdmHandleProfiling.register();
         DdmHandleExit.register();
-        DdmHandleGlTracing.register();
+        DdmHandleViewDebug.register();
 
         DdmServer.registrationComplete();
     }
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 023e58f..6e28268 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -406,7 +406,7 @@
         view = view.getRootView();
 
         if (REMOTE_COMMAND_DUMP.equalsIgnoreCase(command)) {
-            dump(view, clientStream);
+            dump(view, false, true, clientStream);
         } else if (REMOTE_COMMAND_CAPTURE_LAYERS.equalsIgnoreCase(command)) {
             captureLayers(view, new DataOutputStream(clientStream));
         } else {
@@ -425,7 +425,8 @@
         }
     }
 
-    private static View findView(View root, String parameter) {
+    /** @hide */
+    public static View findView(View root, String parameter) {
         // Look by type/hashcode
         if (parameter.indexOf('@') != -1) {
             final String[] ids = parameter.split("@");
@@ -488,7 +489,8 @@
         }
     }
 
-    private static void profileViewAndChildren(final View view, BufferedWriter out)
+    /** @hide */
+    public static void profileViewAndChildren(final View view, BufferedWriter out)
             throws IOException {
         profileViewAndChildren(view, out, true);
     }
@@ -623,7 +625,8 @@
         return duration[0];
     }
 
-    private static void captureLayers(View root, final DataOutputStream clientStream)
+    /** @hide */
+    public static void captureLayers(View root, final DataOutputStream clientStream)
             throws IOException {
 
         try {
@@ -695,10 +698,21 @@
         view.getViewRootImpl().outputDisplayList(view);
     }
 
+    /** @hide */
+    public static void outputDisplayList(View root, View target) {
+        root.getViewRootImpl().outputDisplayList(target);
+    }
+
     private static void capture(View root, final OutputStream clientStream, String parameter)
             throws IOException {
 
         final View captureView = findView(root, parameter);
+        capture(root, clientStream, captureView);
+    }
+
+    /** @hide */
+    public static void capture(View root, final OutputStream clientStream, View captureView)
+            throws IOException {
         Bitmap b = performViewCapture(captureView, false);
 
         if (b == null) {
@@ -752,14 +766,20 @@
         return null;
     }
 
-    private static void dump(View root, OutputStream clientStream) throws IOException {
+    /**
+     * Dumps the view hierarchy starting from the given view.
+     * @hide
+     */
+    public static void dump(View root, boolean skipChildren, boolean includeProperties,
+            OutputStream clientStream) throws IOException {
         BufferedWriter out = null;
         try {
             out = new BufferedWriter(new OutputStreamWriter(clientStream, "utf-8"), 32 * 1024);
             View view = root.getRootView();
             if (view instanceof ViewGroup) {
                 ViewGroup group = (ViewGroup) view;
-                dumpViewHierarchyWithProperties(group.getContext(), group, out, 0);
+                dumpViewHierarchy(group.getContext(), group, out, 0,
+                        skipChildren, includeProperties);
             }
             out.write("DONE.");
             out.newLine();
@@ -804,9 +824,13 @@
         return view.getClass().getName().equals(className) && view.hashCode() == hashCode;
     }
 
-    private static void dumpViewHierarchyWithProperties(Context context, ViewGroup group,
-            BufferedWriter out, int level) {
-        if (!dumpViewWithProperties(context, group, out, level)) {
+    private static void dumpViewHierarchy(Context context, ViewGroup group,
+            BufferedWriter out, int level, boolean skipChildren, boolean includeProperties) {
+        if (!dumpView(context, group, out, level, includeProperties)) {
+            return;
+        }
+
+        if (skipChildren) {
             return;
         }
 
@@ -814,9 +838,10 @@
         for (int i = 0; i < count; i++) {
             final View view = group.getChildAt(i);
             if (view instanceof ViewGroup) {
-                dumpViewHierarchyWithProperties(context, (ViewGroup) view, out, level + 1);
+                dumpViewHierarchy(context, (ViewGroup) view, out, level + 1, skipChildren,
+                        includeProperties);
             } else {
-                dumpViewWithProperties(context, view, out, level + 1);
+                dumpView(context, view, out, level + 1, includeProperties);
             }
         }
         if (group instanceof HierarchyHandler) {
@@ -824,8 +849,8 @@
         }
     }
 
-    private static boolean dumpViewWithProperties(Context context, View view,
-            BufferedWriter out, int level) {
+    private static boolean dumpView(Context context, View view,
+            BufferedWriter out, int level, boolean includeProperties) {
 
         try {
             for (int i = 0; i < level; i++) {
@@ -835,7 +860,9 @@
             out.write('@');
             out.write(Integer.toHexString(view.hashCode()));
             out.write(' ');
-            dumpViewProperties(context, view, out);
+            if (includeProperties) {
+                dumpViewProperties(context, view, out);
+            }
             out.newLine();
         } catch (IOException e) {
             Log.w("View", "Error while dumping hierarchy tree");
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index e8945aa..7eb26fa 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -160,6 +160,29 @@
         }
     }
 
+    public String[] getViewRootNames() {
+        synchronized (mLock) {
+            if (mRoots == null) return new String[0];
+            String[] mViewRoots = new String[mRoots.length];
+            int i = 0;
+            for (ViewRootImpl root : mRoots) {
+                mViewRoots[i++] = getWindowName(root);
+            }
+            return mViewRoots;
+        }
+    }
+
+    public View getRootView(String name) {
+        synchronized (mLock) {
+            if (mRoots == null) return null;
+            for (ViewRootImpl root : mRoots) {
+                if (name.equals(getWindowName(root))) return root.getView();
+            }
+        }
+
+        return null;
+    }
+
     public void addView(View view, ViewGroup.LayoutParams params,
             Display display, Window parentWindow) {
         if (view == null) {
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java
index ad3c342..8c76421 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java
@@ -38,6 +38,7 @@
     private MediaInserter mMediaInserter;
     private static final int TEST_BUFFER_SIZE = 10;
     private IContentProvider mMockProvider;
+    private String mPackageName;
 
     private int mFilesCounter;
     private int mAudioCounter;
@@ -83,7 +84,9 @@
     protected void setUp() throws Exception {
         super.setUp();
         mMockProvider = EasyMock.createMock(IContentProvider.class);
-        mMediaInserter = new MediaInserter(mMockProvider, TEST_BUFFER_SIZE);
+        mMediaInserter = new MediaInserter(mMockProvider,
+		mPackageName, TEST_BUFFER_SIZE);
+	mPackageName = getInstrumentation().getContext().getPackageName();
         mFilesCounter = 0;
         mAudioCounter = 0;
         mVideoCounter = 0;
@@ -144,7 +147,7 @@
 
     @SmallTest
     public void testInsertContentsEqualToBufferSize() throws Exception {
-        EasyMock.expect(mMockProvider.bulkInsert(
+        EasyMock.expect(mMockProvider.bulkInsert(mPackageName,
                 (Uri) EasyMock.anyObject(), (ContentValues[]) EasyMock.anyObject())).andReturn(1);
         EasyMock.expectLastCall().times(4);
         EasyMock.replay(mMockProvider);
@@ -159,7 +162,7 @@
 
     @SmallTest
     public void testInsertContentsMoreThanBufferSize() throws Exception {
-        EasyMock.expect(mMockProvider.bulkInsert(
+        EasyMock.expect(mMockProvider.bulkInsert(mPackageName,
                 (Uri) EasyMock.anyObject(), (ContentValues[]) EasyMock.anyObject())).andReturn(1);
         EasyMock.expectLastCall().times(4);
         EasyMock.replay(mMockProvider);
@@ -183,7 +186,7 @@
 
     @SmallTest
     public void testFlushAllWithSomeContents() throws Exception {
-        EasyMock.expect(mMockProvider.bulkInsert(
+        EasyMock.expect(mMockProvider.bulkInsert(mPackageName,
                 (Uri) EasyMock.anyObject(), (ContentValues[]) EasyMock.anyObject())).andReturn(1);
         EasyMock.expectLastCall().times(4);
         EasyMock.replay(mMockProvider);
@@ -199,7 +202,7 @@
 
     @SmallTest
     public void testInsertContentsAfterFlushAll() throws Exception {
-        EasyMock.expect(mMockProvider.bulkInsert(
+        EasyMock.expect(mMockProvider.bulkInsert(mPackageName,
                 (Uri) EasyMock.anyObject(), (ContentValues[]) EasyMock.anyObject())).andReturn(1);
         EasyMock.expectLastCall().times(8);
         EasyMock.replay(mMockProvider);
@@ -220,16 +223,20 @@
 
     @SmallTest
     public void testInsertContentsWithDifferentSizePerContentType() throws Exception {
-        EasyMock.expect(mMockProvider.bulkInsert(MediaUriMatcher.expectMediaUri(sFilesUri),
+        EasyMock.expect(mMockProvider.bulkInsert(mPackageName,
+		MediaUriMatcher.expectMediaUri(sFilesUri),
                 (ContentValues[]) EasyMock.anyObject())).andReturn(1);
         EasyMock.expectLastCall().times(1);
-        EasyMock.expect(mMockProvider.bulkInsert(MediaUriMatcher.expectMediaUri(sAudioUri),
+        EasyMock.expect(mMockProvider.bulkInsert(mPackageName,
+		MediaUriMatcher.expectMediaUri(sAudioUri),
                 (ContentValues[]) EasyMock.anyObject())).andReturn(1);
         EasyMock.expectLastCall().times(2);
-        EasyMock.expect(mMockProvider.bulkInsert(MediaUriMatcher.expectMediaUri(sVideoUri),
+        EasyMock.expect(mMockProvider.bulkInsert(mPackageName,
+		MediaUriMatcher.expectMediaUri(sVideoUri),
                 (ContentValues[]) EasyMock.anyObject())).andReturn(1);
         EasyMock.expectLastCall().times(3);
-        EasyMock.expect(mMockProvider.bulkInsert(MediaUriMatcher.expectMediaUri(sImagesUri),
+        EasyMock.expect(mMockProvider.bulkInsert(mPackageName,
+		MediaUriMatcher.expectMediaUri(sImagesUri),
                 (ContentValues[]) EasyMock.anyObject())).andReturn(1);
         EasyMock.expectLastCall().times(4);
         EasyMock.replay(mMockProvider);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 03de89bd..17b0b50 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -81,18 +81,18 @@
         SaveImageInBackgroundData> {
     private static final String SCREENSHOTS_DIR_NAME = "Screenshots";
     private static final String SCREENSHOT_FILE_NAME_TEMPLATE = "Screenshot_%s.png";
-    private static final String SCREENSHOT_FILE_PATH_TEMPLATE = "%s/%s/%s";
     private static final String SCREENSHOT_SHARE_SUBJECT_TEMPLATE = "Screenshot (%s)";
 
-    private int mNotificationId;
-    private NotificationManager mNotificationManager;
-    private Notification.Builder mNotificationBuilder;
-    private String mImageFileName;
-    private String mImageFilePath;
-    private long mImageTime;
-    private BigPictureStyle mNotificationStyle;
-    private int mImageWidth;
-    private int mImageHeight;
+    private final int mNotificationId;
+    private final NotificationManager mNotificationManager;
+    private final Notification.Builder mNotificationBuilder;
+    private final File mScreenshotDir;
+    private final String mImageFileName;
+    private final String mImageFilePath;
+    private final long mImageTime;
+    private final BigPictureStyle mNotificationStyle;
+    private final int mImageWidth;
+    private final int mImageHeight;
 
     // WORKAROUND: We want the same notification across screenshots that we update so that we don't
     // spam a user's notification drawer.  However, we only show the ticker for the saving state
@@ -108,11 +108,11 @@
         // Prepare all the output metadata
         mImageTime = System.currentTimeMillis();
         String imageDate = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date(mImageTime));
-        String imageDir = Environment.getExternalStoragePublicDirectory(
-                Environment.DIRECTORY_PICTURES).getAbsolutePath();
         mImageFileName = String.format(SCREENSHOT_FILE_NAME_TEMPLATE, imageDate);
-        mImageFilePath = String.format(SCREENSHOT_FILE_PATH_TEMPLATE, imageDir,
-                SCREENSHOTS_DIR_NAME, mImageFileName);
+
+        mScreenshotDir = new File(Environment.getExternalStoragePublicDirectory(
+                Environment.DIRECTORY_PICTURES), SCREENSHOTS_DIR_NAME);
+        mImageFilePath = new File(mScreenshotDir, mImageFileName).getAbsolutePath();
 
         // Create the large notification icon
         mImageWidth = data.image.getWidth();
@@ -175,6 +175,9 @@
         Resources r = context.getResources();
 
         try {
+            // Create screenshot directory if it doesn't exist
+            mScreenshotDir.mkdirs();
+
             // Save the screenshot to the MediaStore
             ContentValues values = new ContentValues();
             ContentResolver resolver = context.getContentResolver();
@@ -217,13 +220,21 @@
             resolver.update(uri, values, null, null);
 
             params[0].imageUri = uri;
+            params[0].image = null;
             params[0].result = 0;
         } catch (Exception e) {
             // IOException/UnsupportedOperationException may be thrown if external storage is not
             // mounted
+            params[0].imageUri = null;
+            params[0].image = null;
             params[0].result = 1;
         }
 
+        // Recycle the bitmap data
+        if (image != null) {
+            image.recycle();
+        }
+
         return params[0];
     }
 
@@ -458,6 +469,10 @@
                 // Save the screenshot once we have a bit of time now
                 saveScreenshotInWorkerThread(finisher);
                 mWindowManager.removeView(mScreenshotLayout);
+
+                // Clear any references to the bitmap
+                mScreenBitmap = null;
+                mScreenshotView.setImageBitmap(null);
             }
         });
         mScreenshotLayout.post(new Runnable() {
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 71e80831d..b5cbdd1 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -3349,8 +3349,8 @@
                 @Override
                 public void onServiceDisconnected(ComponentName name) {}
             };
-            if (mContext.bindService(
-                    intent, conn, Context.BIND_AUTO_CREATE, UserHandle.USER_CURRENT)) {
+            if (mContext.bindServiceAsUser(
+                    intent, conn, Context.BIND_AUTO_CREATE, UserHandle.CURRENT)) {
                 mScreenshotConnection = conn;
                 mHandler.postDelayed(mScreenshotTimeout, 10000);
             }
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/FaceUnlock.java b/policy/src/com/android/internal/policy/impl/keyguard/FaceUnlock.java
index 830471a..e58eb5b 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/FaceUnlock.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/FaceUnlock.java
@@ -128,10 +128,10 @@
         if (!mBoundToService) {
             Log.d(TAG, "Binding to Face Unlock service for user="
                     + mLockPatternUtils.getCurrentUser());
-            mContext.bindService(new Intent(IFaceLockInterface.class.getName()),
+            mContext.bindServiceAsUser(new Intent(IFaceLockInterface.class.getName()),
                     mConnection,
                     Context.BIND_AUTO_CREATE,
-                    mLockPatternUtils.getCurrentUser());
+                    new UserHandle(mLockPatternUtils.getCurrentUser()));
             mBoundToService = true;
         } else {
             Log.w(TAG, "Attempt to bind to Face Unlock when already bound");
diff --git a/services/java/com/android/server/AppWidgetServiceImpl.java b/services/java/com/android/server/AppWidgetServiceImpl.java
index e1e9eaf..d1829ab 100644
--- a/services/java/com/android/server/AppWidgetServiceImpl.java
+++ b/services/java/com/android/server/AppWidgetServiceImpl.java
@@ -718,7 +718,8 @@
             final long token = Binder.clearCallingIdentity();
             try {
                 conn = new ServiceConnectionProxy(key, connection);
-                mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE, userId);
+                mContext.bindServiceAsUser(intent, conn, Context.BIND_AUTO_CREATE,
+                        new UserHandle(userId));
                 mBoundRemoteViewsServices.put(key, conn);
             } finally {
                 Binder.restoreCallingIdentity(token);
@@ -806,7 +807,8 @@
         // RemoteViewsService.
         final long token = Binder.clearCallingIdentity();
         try {
-            mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE, userId);
+            mContext.bindServiceAsUser(intent, conn, Context.BIND_AUTO_CREATE,
+                    new UserHandle(userId));
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -1104,7 +1106,8 @@
                         // Bind to the service and call onDataSetChanged()
                         final long token = Binder.clearCallingIdentity();
                         try {
-                            mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE, userId);
+                            mContext.bindServiceAsUser(intent, conn, Context.BIND_AUTO_CREATE,
+                                    new UserHandle(userId));
                         } finally {
                             Binder.restoreCallingIdentity(token);
                         }
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 7ac314b..88ea1f1 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -836,8 +836,8 @@
             if ((info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
                 if (DEBUG) Slog.v(TAG, "Binding to Google transport");
                 Intent intent = new Intent().setComponent(transportComponent);
-                context.bindService(intent, mGoogleConnection, Context.BIND_AUTO_CREATE,
-                        UserHandle.USER_OWNER);
+                context.bindServiceAsUser(intent, mGoogleConnection, Context.BIND_AUTO_CREATE,
+                        UserHandle.OWNER);
             } else {
                 Slog.w(TAG, "Possible Google transport spoof: ignoring " + info);
             }
diff --git a/services/java/com/android/server/BluetoothManagerService.java b/services/java/com/android/server/BluetoothManagerService.java
index 5a2088c..33e712a 100755
--- a/services/java/com/android/server/BluetoothManagerService.java
+++ b/services/java/com/android/server/BluetoothManagerService.java
@@ -611,8 +611,8 @@
                             Message timeoutMsg = mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND);
                             mHandler.sendMessageDelayed(timeoutMsg,TIMEOUT_BIND_MS);
                             Intent i = new Intent(IBluetooth.class.getName());
-                            if (!mContext.bindService(i, mConnection,
-                                  Context.BIND_AUTO_CREATE, UserHandle.USER_CURRENT)) {
+                            if (!mContext.bindServiceAsUser(i, mConnection,
+                                  Context.BIND_AUTO_CREATE, UserHandle.CURRENT)) {
                                 mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);
                                 Log.e(TAG, "fail to bind to: " + IBluetooth.class.getName());
                             } else {
@@ -959,8 +959,8 @@
                 mHandler.sendMessageDelayed(timeoutMsg,TIMEOUT_BIND_MS);
                 mConnection.setGetNameAddressOnly(false);
                 Intent i = new Intent(IBluetooth.class.getName());
-                if (!mContext.bindService(i, mConnection,Context.BIND_AUTO_CREATE,
-                                          UserHandle.USER_CURRENT)) {
+                if (!mContext.bindServiceAsUser(i, mConnection,Context.BIND_AUTO_CREATE,
+                                          UserHandle.CURRENT)) {
                     mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);
                     Log.e(TAG, "Fail to bind to: " + IBluetooth.class.getName());
                 } else {
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 3aa04b3..2f12112 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -888,7 +888,8 @@
             Slog.e(TAG, "--- bind failed: service = " + service + ", conn = " + conn);
             return false;
         }
-        return mContext.bindService(service, conn, flags, mSettings.getCurrentUserId());
+        return mContext.bindServiceAsUser(service, conn, flags,
+                new UserHandle(mSettings.getCurrentUserId()));
     }
 
     @Override
diff --git a/services/java/com/android/server/ServiceWatcher.java b/services/java/com/android/server/ServiceWatcher.java
index 2e7c6d1..bca7faa 100644
--- a/services/java/com/android/server/ServiceWatcher.java
+++ b/services/java/com/android/server/ServiceWatcher.java
@@ -176,8 +176,8 @@
         mPackageName = packageName;
         mVersion = version;
         if (D) Log.d(mTag, "binding " + packageName + " (version " + version + ")");
-        mContext.bindService(intent, this, Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
-                | Context.BIND_ALLOW_OOM_MANAGEMENT | Context.BIND_NOT_VISIBLE, mCurrentUserId);
+        mContext.bindServiceAsUser(intent, this, Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
+                | Context.BIND_NOT_VISIBLE, new UserHandle(mCurrentUserId));
     }
 
     public static boolean isSignatureMatch(Signature[] signatures,
diff --git a/services/java/com/android/server/TextServicesManagerService.java b/services/java/com/android/server/TextServicesManagerService.java
index d0d8428..7dd9988 100644
--- a/services/java/com/android/server/TextServicesManagerService.java
+++ b/services/java/com/android/server/TextServicesManagerService.java
@@ -248,7 +248,8 @@
             Slog.e(TAG, "--- bind failed: service = " + service + ", conn = " + conn);
             return false;
         }
-        return mContext.bindService(service, conn, flags, mSettings.getCurrentUserId());
+        return mContext.bindServiceAsUser(service, conn, flags,
+                new UserHandle(mSettings.getCurrentUserId()));
     }
 
     private void unbindServiceLocked() {
diff --git a/services/java/com/android/server/WallpaperManagerService.java b/services/java/com/android/server/WallpaperManagerService.java
index 21a1956..6823f136 100644
--- a/services/java/com/android/server/WallpaperManagerService.java
+++ b/services/java/com/android/server/WallpaperManagerService.java
@@ -885,7 +885,8 @@
                     Intent.createChooser(new Intent(Intent.ACTION_SET_WALLPAPER),
                             mContext.getText(com.android.internal.R.string.chooser_wallpaper)),
                     0, null, new UserHandle(serviceUserId)));
-            if (!mContext.bindService(intent, newConn, Context.BIND_AUTO_CREATE, serviceUserId)) {
+            if (!mContext.bindServiceAsUser(intent, newConn, Context.BIND_AUTO_CREATE,
+                    new UserHandle(serviceUserId))) {
                 String msg = "Unable to bind service: "
                         + componentName;
                 if (fromUser) {
diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
index 3e3e7dc..0725df0 100644
--- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -1592,7 +1592,8 @@
          */
         public boolean bind() {
             if (!mIsAutomation && mService == null) {
-                return mContext.bindService(mIntent, this, Context.BIND_AUTO_CREATE, mUserId);
+                return mContext.bindServiceAsUser(mIntent, this, Context.BIND_AUTO_CREATE,
+                        new UserHandle(mUserId));
             }
             return false;
         }
diff --git a/services/java/com/android/server/accounts/AccountManagerService.java b/services/java/com/android/server/accounts/AccountManagerService.java
index 150df9e..88603dc 100644
--- a/services/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/java/com/android/server/accounts/AccountManagerService.java
@@ -1903,7 +1903,8 @@
             if (Log.isLoggable(TAG, Log.VERBOSE)) {
                 Log.v(TAG, "performing bindService to " + authenticatorInfo.componentName);
             }
-            if (!mContext.bindService(intent, this, Context.BIND_AUTO_CREATE, mAccounts.userId)) {
+            if (!mContext.bindServiceAsUser(intent, this, Context.BIND_AUTO_CREATE,
+                    new UserHandle(mAccounts.userId))) {
                 if (Log.isLoggable(TAG, Log.VERBOSE)) {
                     Log.v(TAG, "bindService to " + authenticatorInfo.componentName + " failed");
                 }
diff --git a/services/java/com/android/server/content/SyncManager.java b/services/java/com/android/server/content/SyncManager.java
index cd66cf2..b3f9bf1 100644
--- a/services/java/com/android/server/content/SyncManager.java
+++ b/services/java/com/android/server/content/SyncManager.java
@@ -1061,10 +1061,10 @@
                     mContext, 0, new Intent(Settings.ACTION_SYNC_SETTINGS), 0,
                     null, new UserHandle(userId)));
             mBound = true;
-            final boolean bindResult = mContext.bindService(intent, this,
+            final boolean bindResult = mContext.bindServiceAsUser(intent, this,
                     Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
                     | Context.BIND_ALLOW_OOM_MANAGEMENT,
-                    mSyncOperation.userId);
+                    new UserHandle(mSyncOperation.userId));
             if (!bindResult) {
                 mBound = false;
             }
diff --git a/services/java/com/android/server/dreams/DreamController.java b/services/java/com/android/server/dreams/DreamController.java
index 45ae2c5..85ef33e 100644
--- a/services/java/com/android/server/dreams/DreamController.java
+++ b/services/java/com/android/server/dreams/DreamController.java
@@ -116,8 +116,8 @@
         intent.setComponent(name);
         intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
         try {
-            if (!mContext.bindService(intent, mCurrentDream,
-                    Context.BIND_AUTO_CREATE, userId)) {
+            if (!mContext.bindServiceAsUser(intent, mCurrentDream,
+                    Context.BIND_AUTO_CREATE, new UserHandle(userId))) {
                 Slog.e(TAG, "Unable to bind dream service: " + intent);
                 stopDream();
                 return;
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index d2a330f..9b3795a 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -482,8 +482,8 @@
                     " DefaultContainerService");
             Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
             Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
-            if (mContext.bindService(service, mDefContainerConn,
-                    Context.BIND_AUTO_CREATE, UserHandle.USER_OWNER)) {
+            if (mContext.bindServiceAsUser(service, mDefContainerConn,
+                    Context.BIND_AUTO_CREATE, UserHandle.OWNER)) {
                 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                 mBound = true;
                 return true;
@@ -8477,8 +8477,8 @@
             users = new int[] { userId };
         }
         final ClearStorageConnection conn = new ClearStorageConnection();
-        if (mContext.bindService(
-                containerIntent, conn, Context.BIND_AUTO_CREATE, UserHandle.USER_OWNER)) {
+        if (mContext.bindServiceAsUser(
+                containerIntent, conn, Context.BIND_AUTO_CREATE, UserHandle.OWNER)) {
             try {
                 for (int curUser : users) {
                     long timeout = SystemClock.uptimeMillis() + 5000;
diff --git a/test-runner/src/android/test/mock/MockContext.java b/test-runner/src/android/test/mock/MockContext.java
index 1f815e7..3097811 100644
--- a/test-runner/src/android/test/mock/MockContext.java
+++ b/test-runner/src/android/test/mock/MockContext.java
@@ -411,7 +411,8 @@
 
     /** @hide */
     @Override
-    public boolean bindService(Intent service, ServiceConnection conn, int flags, int userId) {
+    public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags,
+            UserHandle user) {
         throw new UnsupportedOperationException();
     }
 
diff --git a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
index f0c3b22..c7cd975 100644
--- a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
+++ b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
@@ -237,7 +237,7 @@
                         Log.i(TAG, "Service disconnected " + name);
                     }
                 };
-                if (bindService(intent, conn, Context.BIND_AUTO_CREATE, 0)) {
+                if (bindServiceAsUser(intent, conn, Context.BIND_AUTO_CREATE, UserHandle.OWNER)) {
                     mConnections.add(conn);
                 } else {
                     Toast.makeText(ActivityTestMain.this, "Failed to bind",
@@ -260,7 +260,8 @@
                         Log.i(TAG, "Service disconnected " + name);
                     }
                 };
-                if (bindService(intent, conn, Context.BIND_AUTO_CREATE, mSecondUser)) {
+                if (bindServiceAsUser(intent, conn, Context.BIND_AUTO_CREATE,
+                        new UserHandle(mSecondUser))) {
                     mConnections.add(conn);
                 } else {
                     Toast.makeText(ActivityTestMain.this, "Failed to bind",