Implement ActivityView.

With an existing ActivityContainer a caller can now create an
ActivityView which consists of a new VirtualDisplay immediately
attached to the ActivityContainer.

Change-Id: Id70333dcbef55d524a87df8f8c92d72ca5579364
This commit is contained in:
Craig Mautner
2013-12-20 09:06:56 -08:00
parent 9c9975ac3d
commit 4504de5d5a
7 changed files with 285 additions and 23 deletions

View File

@ -3080,6 +3080,14 @@ package android.app {
method public void update(android.app.ActivityOptions); method public void update(android.app.ActivityOptions);
} }
public class ActivityView extends android.view.ViewGroup {
ctor public ActivityView(android.content.Context);
ctor public ActivityView(android.content.Context, android.util.AttributeSet);
ctor public ActivityView(android.content.Context, android.util.AttributeSet, int);
method protected void onLayout(boolean, int, int, int, int);
method public void startActivity(android.content.Intent);
}
public class AlarmManager { public class AlarmManager {
method public void cancel(android.app.PendingIntent); method public void cancel(android.app.PendingIntent);
method public void set(int, long, android.app.PendingIntent); method public void set(int, long, android.app.PendingIntent);

View File

@ -0,0 +1,163 @@
/*
* 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.app;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
import android.graphics.SurfaceTexture;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.Surface;
import android.view.TextureView;
import android.view.TextureView.SurfaceTextureListener;
import android.view.ViewGroup;
import android.view.WindowManager;
public class ActivityView extends ViewGroup {
private final TextureView mTextureView;
private IActivityContainer mActivityContainer;
private Activity mActivity;
private boolean mAttached;
private int mWidth;
private int mHeight;
public ActivityView(Context context) {
this(context, null);
}
public ActivityView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ActivityView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
while (context instanceof ContextWrapper) {
if (context instanceof Activity) {
mActivity = (Activity)context;
break;
}
context = ((ContextWrapper)context).getBaseContext();
}
if (mActivity == null) {
throw new IllegalStateException("The ActivityView's Context is not an Activity.");
}
mTextureView = new TextureView(context);
mTextureView.setSurfaceTextureListener(new ActivityViewSurfaceTextureListener());
addView(mTextureView);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
mTextureView.layout(l, t, r, b);
}
@Override
protected void onAttachedToWindow() {
try {
final IBinder token = mActivity.getActivityToken();
mActivityContainer =
ActivityManagerNative.getDefault().createActivityContainer(token, null);
} catch (RemoteException e) {
throw new IllegalStateException("ActivityView: Unable to create ActivityContainer. "
+ e);
}
final SurfaceTexture surfaceTexture = mTextureView.getSurfaceTexture();
if (surfaceTexture != null) {
createActivityView(surfaceTexture);
}
}
@Override
protected void onDetachedFromWindow() {
if (mActivityContainer != null) {
try {
mActivityContainer.deleteActivityView();
} catch (RemoteException e) {
}
mActivityContainer = null;
}
mAttached = false;
}
public void startActivity(Intent intent) {
if (mActivityContainer != null && mAttached) {
try {
mActivityContainer.startActivity(intent);
} catch (RemoteException e) {
throw new IllegalStateException("ActivityView: Unable to startActivity. " + e);
}
}
}
/** Call when both mActivityContainer and mTextureView's SurfaceTexture are not null */
private void createActivityView(SurfaceTexture surfaceTexture) {
WindowManager wm = (WindowManager)mActivity.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics metrics = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(metrics);
try {
mActivityContainer.createActivityView(new Surface(surfaceTexture), mWidth, mHeight,
metrics.densityDpi);
} catch (RemoteException e) {
mActivityContainer = null;
throw new IllegalStateException(
"ActivityView: Unable to create ActivityContainer. " + e);
}
mAttached = true;
}
private class ActivityViewSurfaceTextureListener implements SurfaceTextureListener {
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width,
int height) {
mWidth = width;
mHeight = height;
if (mActivityContainer != null) {
createActivityView(surfaceTexture);
}
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int width,
int height) {
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
try {
mActivityContainer.deleteActivityView();
// TODO: Add binderDied to handle this nullification.
mActivityContainer = null;
} catch (RemoteException r) {
}
mAttached = false;
return false;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {
}
}
}

View File

@ -19,6 +19,7 @@ package android.app;
import android.app.IActivityContainerCallback; import android.app.IActivityContainerCallback;
import android.content.Intent; import android.content.Intent;
import android.os.IBinder; import android.os.IBinder;
import android.view.Surface;
/** @hide */ /** @hide */
interface IActivityContainer { interface IActivityContainer {
@ -26,4 +27,6 @@ interface IActivityContainer {
int getDisplayId(); int getDisplayId();
void detachFromDisplay(); void detachFromDisplay();
int startActivity(in Intent intent); int startActivity(in Intent intent);
void createActivityView(in Surface surface, int width, int height, int density);
void deleteActivityView();
} }

View File

@ -26,7 +26,8 @@ import android.view.LayoutInflater;
public class PhoneLayoutInflater extends LayoutInflater { public class PhoneLayoutInflater extends LayoutInflater {
private static final String[] sClassPrefixList = { private static final String[] sClassPrefixList = {
"android.widget.", "android.widget.",
"android.webkit." "android.webkit.",
"android.app."
}; };
/** /**

View File

@ -59,6 +59,8 @@ import android.content.res.Configuration;
import android.graphics.Point; import android.graphics.Point;
import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager.DisplayListener; import android.hardware.display.DisplayManager.DisplayListener;
import android.hardware.display.DisplayManagerGlobal;
import android.hardware.display.VirtualDisplay;
import android.os.Binder; import android.os.Binder;
import android.os.Bundle; import android.os.Bundle;
import android.os.Debug; import android.os.Debug;
@ -79,6 +81,7 @@ import android.util.SparseArray;
import android.util.SparseIntArray; import android.util.SparseIntArray;
import android.view.Display; import android.view.Display;
import android.view.DisplayInfo; import android.view.DisplayInfo;
import android.view.Surface;
import com.android.internal.app.HeavyWeightSwitcherActivity; import com.android.internal.app.HeavyWeightSwitcherActivity;
import com.android.internal.os.TransferPipe; import com.android.internal.os.TransferPipe;
import com.android.server.am.ActivityManagerService.PendingActivityLaunch; import com.android.server.am.ActivityManagerService.PendingActivityLaunch;
@ -88,6 +91,7 @@ import com.android.server.wm.WindowManagerService;
import java.io.FileDescriptor; import java.io.FileDescriptor;
import java.io.IOException; import java.io.IOException;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -119,6 +123,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
static final int HANDLE_DISPLAY_CHANGED = FIRST_SUPERVISOR_STACK_MSG + 6; static final int HANDLE_DISPLAY_CHANGED = FIRST_SUPERVISOR_STACK_MSG + 6;
static final int HANDLE_DISPLAY_REMOVED = FIRST_SUPERVISOR_STACK_MSG + 7; static final int HANDLE_DISPLAY_REMOVED = FIRST_SUPERVISOR_STACK_MSG + 7;
private final static String VIRTUAL_DISPLAY_BASE_NAME = "ActivityViewVirtualDisplay";
// For debugging to make sure the caller when acquiring/releasing our // For debugging to make sure the caller when acquiring/releasing our
// wake lock is the system process. // wake lock is the system process.
@ -212,11 +217,13 @@ public final class ActivityStackSupervisor implements DisplayListener {
/** Stack id of the front stack when user switched, indexed by userId. */ /** Stack id of the front stack when user switched, indexed by userId. */
SparseIntArray mUserStackInFront = new SparseIntArray(2); SparseIntArray mUserStackInFront = new SparseIntArray(2);
// TODO: Add listener for removal of references.
/** Mapping from (ActivityStack/TaskStack).mStackId to their current state */ /** Mapping from (ActivityStack/TaskStack).mStackId to their current state */
SparseArray<ActivityContainer> mActivityContainers = new SparseArray<ActivityContainer>(); SparseArray<WeakReference<ActivityContainer>> mActivityContainers =
new SparseArray<WeakReference<ActivityContainer>>();
/** Mapping from displayId to display current state */ /** Mapping from displayId to display current state */
SparseArray<ActivityDisplay> mActivityDisplays = new SparseArray<ActivityDisplay>(); private SparseArray<ActivityDisplay> mActivityDisplays = new SparseArray<ActivityDisplay>();
public ActivityStackSupervisor(ActivityManagerService service) { public ActivityStackSupervisor(ActivityManagerService service) {
mService = service; mService = service;
@ -2098,9 +2105,14 @@ public final class ActivityStackSupervisor implements DisplayListener {
} }
ActivityStack getStack(int stackId) { ActivityStack getStack(int stackId) {
ActivityContainer activityContainer = mActivityContainers.get(stackId); WeakReference<ActivityContainer> weakReference = mActivityContainers.get(stackId);
if (weakReference != null) {
ActivityContainer activityContainer = weakReference.get();
if (activityContainer != null) { if (activityContainer != null) {
return activityContainer.mStack; return activityContainer.mStack;
} else {
mActivityContainers.remove(stackId);
}
} }
return null; return null;
} }
@ -2134,7 +2146,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
IActivityContainerCallback callback) { IActivityContainerCallback callback) {
ActivityContainer activityContainer = new ActivityContainer(parentActivity, stackId, ActivityContainer activityContainer = new ActivityContainer(parentActivity, stackId,
callback); callback);
mActivityContainers.put(stackId, activityContainer); mActivityContainers.put(stackId, new WeakReference<ActivityContainer>(activityContainer));
if (parentActivity != null) { if (parentActivity != null) {
parentActivity.mChildContainers.add(activityContainer.mStack); parentActivity.mChildContainers.add(activityContainer.mStack);
} }
@ -2728,12 +2740,18 @@ public final class ActivityStackSupervisor implements DisplayListener {
} }
public void handleDisplayAddedLocked(int displayId) { public void handleDisplayAddedLocked(int displayId) {
boolean newDisplay;
synchronized (mService) { synchronized (mService) {
newDisplay = mActivityDisplays.get(displayId) == null;
if (newDisplay) {
ActivityDisplay activityDisplay = new ActivityDisplay(displayId); ActivityDisplay activityDisplay = new ActivityDisplay(displayId);
mActivityDisplays.put(displayId, activityDisplay); mActivityDisplays.put(displayId, activityDisplay);
} }
}
if (newDisplay) {
mWindowManager.onDisplayAdded(displayId); mWindowManager.onDisplayAdded(displayId);
} }
}
public void handleDisplayRemovedLocked(int displayId) { public void handleDisplayRemovedLocked(int displayId) {
synchronized (mService) { synchronized (mService) {
@ -2960,6 +2978,49 @@ public final class ActivityStackSupervisor implements DisplayListener {
return this; return this;
} }
@Override
public void createActivityView(Surface surface, int width, int height, int density) {
DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();
VirtualDisplay virtualDisplay;
long ident = Binder.clearCallingIdentity();
try {
virtualDisplay = dm.createVirtualDisplay(mService.mContext,
VIRTUAL_DISPLAY_BASE_NAME, width, height, density, surface,
// TODO: Add VIRTUAL_DISPLAY_FLAG_DISABLE_MIRRORING when it is available.
DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC);
} finally {
Binder.restoreCallingIdentity(ident);
}
final Display display = virtualDisplay.getDisplay();
final int displayId = display.getDisplayId();
// Do WindowManager operation first so that it is ahead of CREATE_STACK in the H queue.
mWindowManager.onDisplayAdded(displayId);
synchronized (mService) {
ActivityDisplay activityDisplay = new ActivityDisplay(display);
mActivityDisplays.put(displayId, activityDisplay);
attachToDisplayLocked(activityDisplay);
activityDisplay.mVirtualDisplay = virtualDisplay;
}
}
@Override
public void deleteActivityView() {
synchronized (mService) {
if (!isAttached()) {
return;
}
VirtualDisplay virtualDisplay = mActivityDisplay.mVirtualDisplay;
if (virtualDisplay != null) {
virtualDisplay.release();
mActivityDisplay.mVirtualDisplay = null;
}
detachLocked();
}
}
ActivityStackSupervisor getOuter() { ActivityStackSupervisor getOuter() {
return ActivityStackSupervisor.this; return ActivityStackSupervisor.this;
} }
@ -2989,9 +3050,17 @@ public final class ActivityStackSupervisor implements DisplayListener {
* stacks, bottommost behind. Accessed directly by ActivityManager package classes */ * stacks, bottommost behind. Accessed directly by ActivityManager package classes */
final ArrayList<ActivityStack> mStacks = new ArrayList<ActivityStack>(); final ArrayList<ActivityStack> mStacks = new ArrayList<ActivityStack>();
/** If this display is for an ActivityView then the VirtualDisplay created for it is stored
* here. */
VirtualDisplay mVirtualDisplay;
ActivityDisplay(int displayId) { ActivityDisplay(int displayId) {
mDisplayId = displayId; this(mDisplayManager.getDisplay(displayId));
mDisplay = mDisplayManager.getDisplay(displayId); }
ActivityDisplay(Display display) {
mDisplay = display;
mDisplayId = display.getDisplayId();
mDisplay.getDisplayInfo(mDisplayInfo); mDisplay.getDisplayInfo(mDisplayInfo);
} }

View File

@ -31,6 +31,7 @@ import android.os.Handler;
import android.os.IBinder; import android.os.IBinder;
import android.os.Looper; import android.os.Looper;
import android.os.Message; import android.os.Message;
import android.os.Process;
import android.os.RemoteException; import android.os.RemoteException;
import android.os.SystemClock; import android.os.SystemClock;
import android.os.SystemProperties; import android.os.SystemProperties;
@ -675,7 +676,8 @@ public final class DisplayManagerService extends IDisplayManager.Stub {
if (surface == null) { if (surface == null) {
throw new IllegalArgumentException("surface must not be null"); throw new IllegalArgumentException("surface must not be null");
} }
if ((flags & DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC) != 0) { if (callingUid != Process.SYSTEM_UID &&
(flags & DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC) != 0) {
if (mContext.checkCallingPermission(android.Manifest.permission.CAPTURE_VIDEO_OUTPUT) if (mContext.checkCallingPermission(android.Manifest.permission.CAPTURE_VIDEO_OUTPUT)
!= PackageManager.PERMISSION_GRANTED != PackageManager.PERMISSION_GRANTED
&& mContext.checkCallingPermission( && mContext.checkCallingPermission(

View File

@ -4817,11 +4817,17 @@ public class WindowManagerService extends IWindowManager.Stub
} }
} }
public void createStack(int stackId, int displayId) {
mH.sendMessage(mH.obtainMessage(H.CREATE_STACK, stackId, displayId));
}
/** /**
* Create a new TaskStack and place it next to an existing stack. * Create a new TaskStack and place it next to an existing stack.
* @param stackId The unique identifier of the new stack. * @param stackId The unique identifier of the new stack.
*/ */
public void createStack(int stackId, int displayId) { private void createStackLocked(int stackId, int displayId) {
final long origId = Binder.clearCallingIdentity();
try {
synchronized (mWindowMap) { synchronized (mWindowMap) {
final int numDisplays = mDisplayContents.size(); final int numDisplays = mDisplayContents.size();
for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) { for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
@ -4834,6 +4840,9 @@ public class WindowManagerService extends IWindowManager.Stub
} }
} }
} }
} finally {
Binder.restoreCallingIdentity(origId);
}
} }
public int removeStack(int stackId) { public int removeStack(int stackId) {
@ -7038,6 +7047,8 @@ public class WindowManagerService extends IWindowManager.Stub
public static final int REMOVE_STARTING_TIMEOUT = 33; public static final int REMOVE_STARTING_TIMEOUT = 33;
public static final int CREATE_STACK = 34;
@Override @Override
public void handleMessage(Message msg) { public void handleMessage(Message msg) {
if (DEBUG_WINDOW_TRACE) { if (DEBUG_WINDOW_TRACE) {
@ -7481,6 +7492,11 @@ public class WindowManagerService extends IWindowManager.Stub
} catch (RemoteException e) { } catch (RemoteException e) {
} }
break; break;
case CREATE_STACK:
synchronized (mWindowMap) {
createStackLocked(msg.arg1, msg.arg2);
}
break;
} }
if (DEBUG_WINDOW_TRACE) { if (DEBUG_WINDOW_TRACE) {
Slog.v(TAG, "handleMessage: exit"); Slog.v(TAG, "handleMessage: exit");