Merge "Clearing screenshot bitmap references and recycling bitmap data immediately after saving. (Bug 7974275)"
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 f419273..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();
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..4a428ae 100644
--- a/services/java/com/android/server/ServiceWatcher.java
+++ b/services/java/com/android/server/ServiceWatcher.java
@@ -176,8 +176,9 @@
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_ALLOW_OOM_MANAGEMENT | 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 2238f17..9a925d6 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;
@@ -8409,8 +8409,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",