Merge "Explode WindowManagerService."

This commit is contained in:
Dianne Hackborn
2011-02-17 18:30:11 -08:00
committed by Android (Google) Code Review
14 changed files with 3760 additions and 3459 deletions

View File

@@ -0,0 +1,413 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.wm;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import com.android.server.wm.WindowManagerService.H;
import android.content.pm.ActivityInfo;
import android.os.Message;
import android.os.RemoteException;
import android.util.Slog;
import android.view.IApplicationToken;
import android.view.View;
import android.view.WindowManager;
import android.view.animation.Animation;
import android.view.animation.Transformation;
import java.io.PrintWriter;
import java.util.ArrayList;
/**
* Version of WindowToken that is specifically for a particular application (or
* really activity) that is displaying windows.
*/
class AppWindowToken extends WindowToken {
// Non-null only for application tokens.
final IApplicationToken appToken;
// All of the windows and child windows that are included in this
// application token. Note this list is NOT sorted!
final ArrayList<WindowState> allAppWindows = new ArrayList<WindowState>();
int groupId = -1;
boolean appFullscreen;
int requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
// The input dispatching timeout for this application token in nanoseconds.
long inputDispatchingTimeoutNanos;
// These are used for determining when all windows associated with
// an activity have been drawn, so they can be made visible together
// at the same time.
int lastTransactionSequence;
int numInterestingWindows;
int numDrawnWindows;
boolean inPendingTransaction;
boolean allDrawn;
// Is this token going to be hidden in a little while? If so, it
// won't be taken into account for setting the screen orientation.
boolean willBeHidden;
// Is this window's surface needed? This is almost like hidden, except
// it will sometimes be true a little earlier: when the token has
// been shown, but is still waiting for its app transition to execute
// before making its windows shown.
boolean hiddenRequested;
// Have we told the window clients to hide themselves?
boolean clientHidden;
// Last visibility state we reported to the app token.
boolean reportedVisible;
// Set to true when the token has been removed from the window mgr.
boolean removed;
// Have we been asked to have this token keep the screen frozen?
boolean freezingScreen;
boolean animating;
Animation animation;
boolean hasTransformation;
final Transformation transformation = new Transformation();
// Offset to the window of all layers in the token, for use by
// AppWindowToken animations.
int animLayerAdjustment;
// Information about an application starting window if displayed.
StartingData startingData;
WindowState startingWindow;
View startingView;
boolean startingDisplayed;
boolean startingMoved;
boolean firstWindowDrawn;
// Input application handle used by the input dispatcher.
InputApplicationHandle mInputApplicationHandle;
AppWindowToken(WindowManagerService _service, IApplicationToken _token) {
super(_service, _token.asBinder(),
WindowManager.LayoutParams.TYPE_APPLICATION, true);
appWindowToken = this;
appToken = _token;
mInputApplicationHandle = new InputApplicationHandle(this);
lastTransactionSequence = service.mTransactionSequence-1;
}
public void setAnimation(Animation anim) {
if (WindowManagerService.localLOGV) Slog.v(
WindowManagerService.TAG, "Setting animation in " + this + ": " + anim);
animation = anim;
animating = false;
anim.restrictDuration(WindowManagerService.MAX_ANIMATION_DURATION);
anim.scaleCurrentDuration(service.mTransitionAnimationScale);
int zorder = anim.getZAdjustment();
int adj = 0;
if (zorder == Animation.ZORDER_TOP) {
adj = WindowManagerService.TYPE_LAYER_OFFSET;
} else if (zorder == Animation.ZORDER_BOTTOM) {
adj = -WindowManagerService.TYPE_LAYER_OFFSET;
}
if (animLayerAdjustment != adj) {
animLayerAdjustment = adj;
updateLayers();
}
}
public void setDummyAnimation() {
if (animation == null) {
if (WindowManagerService.localLOGV) Slog.v(
WindowManagerService.TAG, "Setting dummy animation in " + this);
animation = WindowManagerService.sDummyAnimation;
}
}
public void clearAnimation() {
if (animation != null) {
animation = null;
animating = true;
}
}
void updateLayers() {
final int N = allAppWindows.size();
final int adj = animLayerAdjustment;
for (int i=0; i<N; i++) {
WindowState w = allAppWindows.get(i);
w.mAnimLayer = w.mLayer + adj;
if (WindowManagerService.DEBUG_LAYERS) Slog.v(WindowManagerService.TAG, "Updating layer " + w + ": "
+ w.mAnimLayer);
if (w == service.mInputMethodTarget && !service.mInputMethodTargetWaitingAnim) {
service.setInputMethodAnimLayerAdjustment(adj);
}
if (w == service.mWallpaperTarget && service.mLowerWallpaperTarget == null) {
service.setWallpaperAnimLayerAdjustmentLocked(adj);
}
}
}
void sendAppVisibilityToClients() {
final int N = allAppWindows.size();
for (int i=0; i<N; i++) {
WindowState win = allAppWindows.get(i);
if (win == startingWindow && clientHidden) {
// Don't hide the starting window.
continue;
}
try {
if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(WindowManagerService.TAG,
"Setting visibility of " + win + ": " + (!clientHidden));
win.mClient.dispatchAppVisibility(!clientHidden);
} catch (RemoteException e) {
}
}
}
void showAllWindowsLocked() {
final int NW = allAppWindows.size();
for (int i=0; i<NW; i++) {
WindowState w = allAppWindows.get(i);
if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(WindowManagerService.TAG,
"performing show on: " + w);
w.performShowLocked();
}
}
// This must be called while inside a transaction.
boolean stepAnimationLocked(long currentTime, int dw, int dh) {
if (!service.mDisplayFrozen && service.mPolicy.isScreenOn()) {
// We will run animations as long as the display isn't frozen.
if (animation == WindowManagerService.sDummyAnimation) {
// This guy is going to animate, but not yet. For now count
// it as not animating for purposes of scheduling transactions;
// when it is really time to animate, this will be set to
// a real animation and the next call will execute normally.
return false;
}
if ((allDrawn || animating || startingDisplayed) && animation != null) {
if (!animating) {
if (WindowManagerService.DEBUG_ANIM) Slog.v(
WindowManagerService.TAG, "Starting animation in " + this +
" @ " + currentTime + ": dw=" + dw + " dh=" + dh
+ " scale=" + service.mTransitionAnimationScale
+ " allDrawn=" + allDrawn + " animating=" + animating);
animation.initialize(dw, dh, dw, dh);
animation.setStartTime(currentTime);
animating = true;
}
transformation.clear();
final boolean more = animation.getTransformation(
currentTime, transformation);
if (WindowManagerService.DEBUG_ANIM) Slog.v(
WindowManagerService.TAG, "Stepped animation in " + this +
": more=" + more + ", xform=" + transformation);
if (more) {
// we're done!
hasTransformation = true;
return true;
}
if (WindowManagerService.DEBUG_ANIM) Slog.v(
WindowManagerService.TAG, "Finished animation in " + this +
" @ " + currentTime);
animation = null;
}
} else if (animation != null) {
// If the display is frozen, and there is a pending animation,
// clear it and make sure we run the cleanup code.
animating = true;
animation = null;
}
hasTransformation = false;
if (!animating) {
return false;
}
clearAnimation();
animating = false;
if (animLayerAdjustment != 0) {
animLayerAdjustment = 0;
updateLayers();
}
if (service.mInputMethodTarget != null && service.mInputMethodTarget.mAppToken == this) {
service.moveInputMethodWindowsIfNeededLocked(true);
}
if (WindowManagerService.DEBUG_ANIM) Slog.v(
WindowManagerService.TAG, "Animation done in " + this
+ ": reportedVisible=" + reportedVisible);
transformation.clear();
final int N = windows.size();
for (int i=0; i<N; i++) {
windows.get(i).finishExit();
}
updateReportedVisibilityLocked();
return false;
}
void updateReportedVisibilityLocked() {
if (appToken == null) {
return;
}
int numInteresting = 0;
int numVisible = 0;
boolean nowGone = true;
if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(WindowManagerService.TAG, "Update reported visibility: " + this);
final int N = allAppWindows.size();
for (int i=0; i<N; i++) {
WindowState win = allAppWindows.get(i);
if (win == startingWindow || win.mAppFreezing
|| win.mViewVisibility != View.VISIBLE
|| win.mAttrs.type == TYPE_APPLICATION_STARTING
|| win.mDestroying) {
continue;
}
if (WindowManagerService.DEBUG_VISIBILITY) {
Slog.v(WindowManagerService.TAG, "Win " + win + ": isDrawn="
+ win.isDrawnLw()
+ ", isAnimating=" + win.isAnimating());
if (!win.isDrawnLw()) {
Slog.v(WindowManagerService.TAG, "Not displayed: s=" + win.mSurface
+ " pv=" + win.mPolicyVisibility
+ " dp=" + win.mDrawPending
+ " cdp=" + win.mCommitDrawPending
+ " ah=" + win.mAttachedHidden
+ " th="
+ (win.mAppToken != null
? win.mAppToken.hiddenRequested : false)
+ " a=" + win.mAnimating);
}
}
numInteresting++;
if (win.isDrawnLw()) {
if (!win.isAnimating()) {
numVisible++;
}
nowGone = false;
} else if (win.isAnimating()) {
nowGone = false;
}
}
boolean nowVisible = numInteresting > 0 && numVisible >= numInteresting;
if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(WindowManagerService.TAG, "VIS " + this + ": interesting="
+ numInteresting + " visible=" + numVisible);
if (nowVisible != reportedVisible) {
if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(
WindowManagerService.TAG, "Visibility changed in " + this
+ ": vis=" + nowVisible);
reportedVisible = nowVisible;
Message m = service.mH.obtainMessage(
H.REPORT_APPLICATION_TOKEN_WINDOWS,
nowVisible ? 1 : 0,
nowGone ? 1 : 0,
this);
service.mH.sendMessage(m);
}
}
WindowState findMainWindow() {
int j = windows.size();
while (j > 0) {
j--;
WindowState win = windows.get(j);
if (win.mAttrs.type == WindowManager.LayoutParams.TYPE_BASE_APPLICATION
|| win.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING) {
return win;
}
}
return null;
}
void dump(PrintWriter pw, String prefix) {
super.dump(pw, prefix);
if (appToken != null) {
pw.print(prefix); pw.println("app=true");
}
if (allAppWindows.size() > 0) {
pw.print(prefix); pw.print("allAppWindows="); pw.println(allAppWindows);
}
pw.print(prefix); pw.print("groupId="); pw.print(groupId);
pw.print(" appFullscreen="); pw.print(appFullscreen);
pw.print(" requestedOrientation="); pw.println(requestedOrientation);
pw.print(prefix); pw.print("hiddenRequested="); pw.print(hiddenRequested);
pw.print(" clientHidden="); pw.print(clientHidden);
pw.print(" willBeHidden="); pw.print(willBeHidden);
pw.print(" reportedVisible="); pw.println(reportedVisible);
if (paused || freezingScreen) {
pw.print(prefix); pw.print("paused="); pw.print(paused);
pw.print(" freezingScreen="); pw.println(freezingScreen);
}
if (numInterestingWindows != 0 || numDrawnWindows != 0
|| inPendingTransaction || allDrawn) {
pw.print(prefix); pw.print("numInterestingWindows=");
pw.print(numInterestingWindows);
pw.print(" numDrawnWindows="); pw.print(numDrawnWindows);
pw.print(" inPendingTransaction="); pw.print(inPendingTransaction);
pw.print(" allDrawn="); pw.println(allDrawn);
}
if (animating || animation != null) {
pw.print(prefix); pw.print("animating="); pw.print(animating);
pw.print(" animation="); pw.println(animation);
}
if (hasTransformation) {
pw.print(prefix); pw.print("XForm: ");
transformation.printShortString(pw);
pw.println();
}
if (animLayerAdjustment != 0) {
pw.print(prefix); pw.print("animLayerAdjustment="); pw.println(animLayerAdjustment);
}
if (startingData != null || removed || firstWindowDrawn) {
pw.print(prefix); pw.print("startingData="); pw.print(startingData);
pw.print(" removed="); pw.print(removed);
pw.print(" firstWindowDrawn="); pw.println(firstWindowDrawn);
}
if (startingWindow != null || startingView != null
|| startingDisplayed || startingMoved) {
pw.print(prefix); pw.print("startingWindow="); pw.print(startingWindow);
pw.print(" startingView="); pw.print(startingView);
pw.print(" startingDisplayed="); pw.print(startingDisplayed);
pw.print(" startingMoved"); pw.println(startingMoved);
}
}
@Override
public String toString() {
if (stringName == null) {
StringBuilder sb = new StringBuilder();
sb.append("AppWindowToken{");
sb.append(Integer.toHexString(System.identityHashCode(this)));
sb.append(" token="); sb.append(token); sb.append('}');
stringName = sb.toString();
}
return stringName;
}
}

View File

@@ -0,0 +1,187 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.wm;
import android.content.res.Resources;
import android.graphics.PixelFormat;
import android.util.Slog;
import android.util.TypedValue;
import android.view.Surface;
import android.view.SurfaceSession;
import java.io.PrintWriter;
/**
* DimAnimator class that controls the dim animation. This holds the surface and
* all state used for dim animation.
*/
class DimAnimator {
Surface mDimSurface;
boolean mDimShown = false;
float mDimCurrentAlpha;
float mDimTargetAlpha;
float mDimDeltaPerMs;
long mLastDimAnimTime;
int mLastDimWidth, mLastDimHeight;
DimAnimator (SurfaceSession session) {
if (mDimSurface == null) {
if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, " DIM "
+ mDimSurface + ": CREATE");
try {
mDimSurface = new Surface(session, 0,
"DimSurface",
-1, 16, 16, PixelFormat.OPAQUE,
Surface.FX_SURFACE_DIM);
mDimSurface.setAlpha(0.0f);
} catch (Exception e) {
Slog.e(WindowManagerService.TAG, "Exception creating Dim surface", e);
}
}
}
/**
* Show the dim surface.
*/
void show(int dw, int dh) {
if (!mDimShown) {
if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, " DIM " + mDimSurface + ": SHOW pos=(0,0) (" +
dw + "x" + dh + ")");
mDimShown = true;
try {
mLastDimWidth = dw;
mLastDimHeight = dh;
mDimSurface.setPosition(0, 0);
mDimSurface.setSize(dw, dh);
mDimSurface.show();
} catch (RuntimeException e) {
Slog.w(WindowManagerService.TAG, "Failure showing dim surface", e);
}
} else if (mLastDimWidth != dw || mLastDimHeight != dh) {
mLastDimWidth = dw;
mLastDimHeight = dh;
mDimSurface.setSize(dw, dh);
}
}
/**
* Set's the dim surface's layer and update dim parameters that will be used in
* {@link updateSurface} after all windows are examined.
*/
void updateParameters(Resources res, WindowState w, long currentTime) {
mDimSurface.setLayer(w.mAnimLayer-1);
final float target = w.mExiting ? 0 : w.mAttrs.dimAmount;
if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, " DIM " + mDimSurface
+ ": layer=" + (w.mAnimLayer-1) + " target=" + target);
if (mDimTargetAlpha != target) {
// If the desired dim level has changed, then
// start an animation to it.
mLastDimAnimTime = currentTime;
long duration = (w.mAnimating && w.mAnimation != null)
? w.mAnimation.computeDurationHint()
: WindowManagerService.DEFAULT_DIM_DURATION;
if (target > mDimTargetAlpha) {
TypedValue tv = new TypedValue();
res.getValue(com.android.internal.R.fraction.config_dimBehindFadeDuration,
tv, true);
if (tv.type == TypedValue.TYPE_FRACTION) {
duration = (long)tv.getFraction((float)duration, (float)duration);
} else if (tv.type >= TypedValue.TYPE_FIRST_INT
&& tv.type <= TypedValue.TYPE_LAST_INT) {
duration = tv.data;
}
}
if (duration < 1) {
// Don't divide by zero
duration = 1;
}
mDimTargetAlpha = target;
mDimDeltaPerMs = (mDimTargetAlpha-mDimCurrentAlpha) / duration;
}
}
/**
* Updating the surface's alpha. Returns true if the animation continues, or returns
* false when the animation is finished and the dim surface is hidden.
*/
boolean updateSurface(boolean dimming, long currentTime, boolean displayFrozen) {
if (!dimming) {
if (mDimTargetAlpha != 0) {
mLastDimAnimTime = currentTime;
mDimTargetAlpha = 0;
mDimDeltaPerMs = (-mDimCurrentAlpha) / WindowManagerService.DEFAULT_DIM_DURATION;
}
}
boolean animating = false;
if (mLastDimAnimTime != 0) {
mDimCurrentAlpha += mDimDeltaPerMs
* (currentTime-mLastDimAnimTime);
boolean more = true;
if (displayFrozen) {
// If the display is frozen, there is no reason to animate.
more = false;
} else if (mDimDeltaPerMs > 0) {
if (mDimCurrentAlpha > mDimTargetAlpha) {
more = false;
}
} else if (mDimDeltaPerMs < 0) {
if (mDimCurrentAlpha < mDimTargetAlpha) {
more = false;
}
} else {
more = false;
}
// Do we need to continue animating?
if (more) {
if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, " DIM "
+ mDimSurface + ": alpha=" + mDimCurrentAlpha);
mLastDimAnimTime = currentTime;
mDimSurface.setAlpha(mDimCurrentAlpha);
animating = true;
} else {
mDimCurrentAlpha = mDimTargetAlpha;
mLastDimAnimTime = 0;
if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, " DIM "
+ mDimSurface + ": final alpha=" + mDimCurrentAlpha);
mDimSurface.setAlpha(mDimCurrentAlpha);
if (!dimming) {
if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, " DIM " + mDimSurface
+ ": HIDE");
try {
mDimSurface.hide();
} catch (RuntimeException e) {
Slog.w(WindowManagerService.TAG, "Illegal argument exception hiding dim surface");
}
mDimShown = false;
}
}
}
return animating;
}
public void printTo(PrintWriter pw) {
pw.print(" mDimShown="); pw.print(mDimShown);
pw.print(" current="); pw.print(mDimCurrentAlpha);
pw.print(" target="); pw.print(mDimTargetAlpha);
pw.print(" delta="); pw.print(mDimDeltaPerMs);
pw.print(" lastAnimTime="); pw.println(mLastDimAnimTime);
}
}

View File

@@ -0,0 +1,368 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.wm;
import com.android.server.wm.WindowManagerService.H;
import android.content.ClipData;
import android.content.ClipDescription;
import android.graphics.Region;
import android.os.IBinder;
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
import android.util.Slog;
import android.view.DragEvent;
import android.view.InputChannel;
import android.view.InputQueue;
import android.view.Surface;
import android.view.View;
import android.view.WindowManager;
import android.view.WindowManagerPolicy;
import java.util.ArrayList;
/**
* Drag/drop state
*/
class DragState {
final WindowManagerService mService;
IBinder mToken;
Surface mSurface;
int mFlags;
IBinder mLocalWin;
ClipData mData;
ClipDescription mDataDescription;
boolean mDragResult;
float mCurrentX, mCurrentY;
float mThumbOffsetX, mThumbOffsetY;
InputChannel mServerChannel, mClientChannel;
WindowState mTargetWindow;
ArrayList<WindowState> mNotifiedWindows;
boolean mDragInProgress;
private final Region mTmpRegion = new Region();
DragState(WindowManagerService service, IBinder token, Surface surface,
int flags, IBinder localWin) {
mService = service;
mToken = token;
mSurface = surface;
mFlags = flags;
mLocalWin = localWin;
mNotifiedWindows = new ArrayList<WindowState>();
}
void reset() {
if (mSurface != null) {
mSurface.destroy();
}
mSurface = null;
mFlags = 0;
mLocalWin = null;
mToken = null;
mData = null;
mThumbOffsetX = mThumbOffsetY = 0;
mNotifiedWindows = null;
}
void register() {
if (WindowManagerService.DEBUG_DRAG) Slog.d(WindowManagerService.TAG, "registering drag input channel");
if (mClientChannel != null) {
Slog.e(WindowManagerService.TAG, "Duplicate register of drag input channel");
} else {
InputChannel[] channels = InputChannel.openInputChannelPair("drag");
mServerChannel = channels[0];
mClientChannel = channels[1];
mService.mInputManager.registerInputChannel(mServerChannel, null);
InputQueue.registerInputChannel(mClientChannel, mService.mDragInputHandler,
mService.mH.getLooper().getQueue());
}
}
void unregister() {
if (WindowManagerService.DEBUG_DRAG) Slog.d(WindowManagerService.TAG, "unregistering drag input channel");
if (mClientChannel == null) {
Slog.e(WindowManagerService.TAG, "Unregister of nonexistent drag input channel");
} else {
mService.mInputManager.unregisterInputChannel(mServerChannel);
InputQueue.unregisterInputChannel(mClientChannel);
mClientChannel.dispose();
mServerChannel.dispose();
mClientChannel = null;
mServerChannel = null;
}
}
int getDragLayerLw() {
return mService.mPolicy.windowTypeToLayerLw(WindowManager.LayoutParams.TYPE_DRAG)
* WindowManagerService.TYPE_LAYER_MULTIPLIER
+ WindowManagerService.TYPE_LAYER_OFFSET;
}
/* call out to each visible window/session informing it about the drag
*/
void broadcastDragStartedLw(final float touchX, final float touchY) {
// Cache a base-class instance of the clip metadata so that parceling
// works correctly in calling out to the apps.
mDataDescription = (mData != null) ? mData.getDescription() : null;
mNotifiedWindows.clear();
mDragInProgress = true;
if (WindowManagerService.DEBUG_DRAG) {
Slog.d(WindowManagerService.TAG, "broadcasting DRAG_STARTED at (" + touchX + ", " + touchY + ")");
}
final int N = mService.mWindows.size();
for (int i = 0; i < N; i++) {
sendDragStartedLw(mService.mWindows.get(i), touchX, touchY, mDataDescription);
}
}
/* helper - send a caller-provided event, presumed to be DRAG_STARTED, if the
* designated window is potentially a drop recipient. There are race situations
* around DRAG_ENDED broadcast, so we make sure that once we've declared that
* the drag has ended, we never send out another DRAG_STARTED for this drag action.
*
* This method clones the 'event' parameter if it's being delivered to the same
* process, so it's safe for the caller to call recycle() on the event afterwards.
*/
private void sendDragStartedLw(WindowState newWin, float touchX, float touchY,
ClipDescription desc) {
// Don't actually send the event if the drag is supposed to be pinned
// to the originating window but 'newWin' is not that window.
if ((mFlags & View.DRAG_FLAG_GLOBAL) == 0) {
final IBinder winBinder = newWin.mClient.asBinder();
if (winBinder != mLocalWin) {
if (WindowManagerService.DEBUG_DRAG) {
Slog.d(WindowManagerService.TAG, "Not dispatching local DRAG_STARTED to " + newWin);
}
return;
}
}
if (mDragInProgress && newWin.isPotentialDragTarget()) {
DragEvent event = DragEvent.obtain(DragEvent.ACTION_DRAG_STARTED,
touchX - newWin.mFrame.left, touchY - newWin.mFrame.top,
null, desc, null, false);
try {
newWin.mClient.dispatchDragEvent(event);
// track each window that we've notified that the drag is starting
mNotifiedWindows.add(newWin);
} catch (RemoteException e) {
Slog.w(WindowManagerService.TAG, "Unable to drag-start window " + newWin);
} finally {
// if the callee was local, the dispatch has already recycled the event
if (Process.myPid() != newWin.mSession.mPid) {
event.recycle();
}
}
}
}
/* helper - construct and send a DRAG_STARTED event only if the window has not
* previously been notified, i.e. it became visible after the drag operation
* was begun. This is a rare case.
*/
void sendDragStartedIfNeededLw(WindowState newWin) {
if (mDragInProgress) {
// If we have sent the drag-started, we needn't do so again
for (WindowState ws : mNotifiedWindows) {
if (ws == newWin) {
return;
}
}
if (WindowManagerService.DEBUG_DRAG) {
Slog.d(WindowManagerService.TAG, "need to send DRAG_STARTED to new window " + newWin);
}
sendDragStartedLw(newWin, mCurrentX, mCurrentY, mDataDescription);
}
}
void broadcastDragEndedLw() {
if (WindowManagerService.DEBUG_DRAG) {
Slog.d(WindowManagerService.TAG, "broadcasting DRAG_ENDED");
}
DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_ENDED,
0, 0, null, null, null, mDragResult);
for (WindowState ws: mNotifiedWindows) {
try {
ws.mClient.dispatchDragEvent(evt);
} catch (RemoteException e) {
Slog.w(WindowManagerService.TAG, "Unable to drag-end window " + ws);
}
}
mNotifiedWindows.clear();
mDragInProgress = false;
evt.recycle();
}
void endDragLw() {
mService.mDragState.broadcastDragEndedLw();
// stop intercepting input
mService.mDragState.unregister();
mService.mInputMonitor.updateInputWindowsLw(true /*force*/);
// free our resources and drop all the object references
mService.mDragState.reset();
mService.mDragState = null;
if (WindowManagerService.DEBUG_ORIENTATION) Slog.d(WindowManagerService.TAG, "Performing post-drag rotation");
boolean changed = mService.setRotationUncheckedLocked(
WindowManagerPolicy.USE_LAST_ROTATION, 0, false);
if (changed) {
mService.mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION);
}
}
void notifyMoveLw(float x, float y) {
final int myPid = Process.myPid();
// Move the surface to the given touch
if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, ">>> OPEN TRANSACTION notifyMoveLw");
Surface.openTransaction();
try {
mSurface.setPosition((int)(x - mThumbOffsetX), (int)(y - mThumbOffsetY));
if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, " DRAG "
+ mSurface + ": pos=(" +
(int)(x - mThumbOffsetX) + "," + (int)(y - mThumbOffsetY) + ")");
} finally {
Surface.closeTransaction();
if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, "<<< CLOSE TRANSACTION notifyMoveLw");
}
// Tell the affected window
WindowState touchedWin = getTouchedWinAtPointLw(x, y);
if (touchedWin == null) {
if (WindowManagerService.DEBUG_DRAG) Slog.d(WindowManagerService.TAG, "No touched win at x=" + x + " y=" + y);
return;
}
if ((mFlags & View.DRAG_FLAG_GLOBAL) == 0) {
final IBinder touchedBinder = touchedWin.mClient.asBinder();
if (touchedBinder != mLocalWin) {
// This drag is pinned only to the originating window, but the drag
// point is outside that window. Pretend it's over empty space.
touchedWin = null;
}
}
try {
// have we dragged over a new window?
if ((touchedWin != mTargetWindow) && (mTargetWindow != null)) {
if (WindowManagerService.DEBUG_DRAG) {
Slog.d(WindowManagerService.TAG, "sending DRAG_EXITED to " + mTargetWindow);
}
// force DRAG_EXITED_EVENT if appropriate
DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_EXITED,
x - mTargetWindow.mFrame.left, y - mTargetWindow.mFrame.top,
null, null, null, false);
mTargetWindow.mClient.dispatchDragEvent(evt);
if (myPid != mTargetWindow.mSession.mPid) {
evt.recycle();
}
}
if (touchedWin != null) {
if (false && WindowManagerService.DEBUG_DRAG) {
Slog.d(WindowManagerService.TAG, "sending DRAG_LOCATION to " + touchedWin);
}
DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_LOCATION,
x - touchedWin.mFrame.left, y - touchedWin.mFrame.top,
null, null, null, false);
touchedWin.mClient.dispatchDragEvent(evt);
if (myPid != touchedWin.mSession.mPid) {
evt.recycle();
}
}
} catch (RemoteException e) {
Slog.w(WindowManagerService.TAG, "can't send drag notification to windows");
}
mTargetWindow = touchedWin;
}
// Tell the drop target about the data. Returns 'true' if we can immediately
// dispatch the global drag-ended message, 'false' if we need to wait for a
// result from the recipient.
boolean notifyDropLw(float x, float y) {
WindowState touchedWin = getTouchedWinAtPointLw(x, y);
if (touchedWin == null) {
// "drop" outside a valid window -- no recipient to apply a
// timeout to, and we can send the drag-ended message immediately.
mDragResult = false;
return true;
}
if (WindowManagerService.DEBUG_DRAG) {
Slog.d(WindowManagerService.TAG, "sending DROP to " + touchedWin);
}
final int myPid = Process.myPid();
final IBinder token = touchedWin.mClient.asBinder();
DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DROP,
x - touchedWin.mFrame.left, y - touchedWin.mFrame.top,
null, null, mData, false);
try {
touchedWin.mClient.dispatchDragEvent(evt);
// 5 second timeout for this window to respond to the drop
mService.mH.removeMessages(H.DRAG_END_TIMEOUT, token);
Message msg = mService.mH.obtainMessage(H.DRAG_END_TIMEOUT, token);
mService.mH.sendMessageDelayed(msg, 5000);
} catch (RemoteException e) {
Slog.w(WindowManagerService.TAG, "can't send drop notification to win " + touchedWin);
return true;
} finally {
if (myPid != touchedWin.mSession.mPid) {
evt.recycle();
}
}
mToken = token;
return false;
}
// Find the visible, touch-deliverable window under the given point
private WindowState getTouchedWinAtPointLw(float xf, float yf) {
WindowState touchedWin = null;
final int x = (int) xf;
final int y = (int) yf;
final ArrayList<WindowState> windows = mService.mWindows;
final int N = windows.size();
for (int i = N - 1; i >= 0; i--) {
WindowState child = windows.get(i);
final int flags = child.mAttrs.flags;
if (!child.isVisibleLw()) {
// not visible == don't tell about drags
continue;
}
if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) {
// not touchable == don't tell about drags
continue;
}
child.getTouchableRegion(mTmpRegion);
final int touchFlags = flags &
(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
if (mTmpRegion.contains(x, y) || touchFlags == 0) {
// Found it
touchedWin = child;
break;
}
}
return touchedWin;
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.wm;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.Animation;
import android.view.animation.Transformation;
/**
* Animation that fade in after 0.5 interpolate time, or fade out in reverse order.
* This is used for opening/closing transition for apps in compatible mode.
*/
class FadeInOutAnimation extends Animation {
boolean mFadeIn;
public FadeInOutAnimation(boolean fadeIn) {
setInterpolator(new AccelerateInterpolator());
setDuration(WindowManagerService.DEFAULT_FADE_IN_OUT_DURATION);
mFadeIn = fadeIn;
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
float x = interpolatedTime;
if (!mFadeIn) {
x = 1.0f - x; // reverse the interpolation for fade out
}
t.setAlpha(x);
}
}

View File

@@ -30,11 +30,11 @@ public final class InputApplicationHandle {
private int ptr;
// The window manager's application window token.
public final WindowManagerService.AppWindowToken appWindowToken;
public final AppWindowToken appWindowToken;
private native void nativeDispose();
public InputApplicationHandle(WindowManagerService.AppWindowToken appWindowToken) {
public InputApplicationHandle(AppWindowToken appWindowToken) {
this.appWindowToken = appWindowToken;
}

View File

@@ -0,0 +1,364 @@
/**
*
*/
package com.android.server.wm;
import android.graphics.Rect;
import android.os.Process;
import android.os.RemoteException;
import android.util.Log;
import android.util.Slog;
import android.view.KeyEvent;
import android.view.WindowManager;
import java.util.ArrayList;
final class InputMonitor {
private final WindowManagerService mService;
// Current window with input focus for keys and other non-touch events. May be null.
private WindowState mInputFocus;
// When true, prevents input dispatch from proceeding until set to false again.
private boolean mInputDispatchFrozen;
// When true, input dispatch proceeds normally. Otherwise all events are dropped.
private boolean mInputDispatchEnabled = true;
// When true, need to call updateInputWindowsLw().
private boolean mUpdateInputWindowsNeeded = true;
// Temporary list of windows information to provide to the input dispatcher.
private InputWindowList mTempInputWindows = new InputWindowList();
// Temporary input application object to provide to the input dispatcher.
private InputApplication mTempInputApplication = new InputApplication();
// Set to true when the first input device configuration change notification
// is received to indicate that the input devices are ready.
private final Object mInputDevicesReadyMonitor = new Object();
private boolean mInputDevicesReady;
public InputMonitor(WindowManagerService service) {
mService = service;
}
/* Notifies the window manager about a broken input channel.
*
* Called by the InputManager.
*/
public void notifyInputChannelBroken(InputWindowHandle inputWindowHandle) {
if (inputWindowHandle == null) {
return;
}
synchronized (mService.mWindowMap) {
WindowState windowState = (WindowState) inputWindowHandle.windowState;
Slog.i(WindowManagerService.TAG, "WINDOW DIED " + windowState);
mService.removeWindowLocked(windowState.mSession, windowState);
}
}
/* Notifies the window manager about an application that is not responding.
* Returns a new timeout to continue waiting in nanoseconds, or 0 to abort dispatch.
*
* Called by the InputManager.
*/
public long notifyANR(InputApplicationHandle inputApplicationHandle,
InputWindowHandle inputWindowHandle) {
AppWindowToken appWindowToken = null;
if (inputWindowHandle != null) {
synchronized (mService.mWindowMap) {
WindowState windowState = (WindowState) inputWindowHandle.windowState;
if (windowState != null) {
Slog.i(WindowManagerService.TAG, "Input event dispatching timed out sending to "
+ windowState.mAttrs.getTitle());
appWindowToken = windowState.mAppToken;
}
}
}
if (appWindowToken == null && inputApplicationHandle != null) {
appWindowToken = inputApplicationHandle.appWindowToken;
Slog.i(WindowManagerService.TAG, "Input event dispatching timed out sending to application "
+ appWindowToken.stringName);
}
if (appWindowToken != null && appWindowToken.appToken != null) {
try {
// Notify the activity manager about the timeout and let it decide whether
// to abort dispatching or keep waiting.
boolean abort = appWindowToken.appToken.keyDispatchingTimedOut();
if (! abort) {
// The activity manager declined to abort dispatching.
// Wait a bit longer and timeout again later.
return appWindowToken.inputDispatchingTimeoutNanos;
}
} catch (RemoteException ex) {
}
}
return 0; // abort dispatching
}
private void addDragInputWindowLw(InputWindowList windowList) {
final InputWindow inputWindow = windowList.add();
inputWindow.inputChannel = mService.mDragState.mServerChannel;
inputWindow.name = "drag";
inputWindow.layoutParamsFlags = 0;
inputWindow.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG;
inputWindow.dispatchingTimeoutNanos = WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
inputWindow.visible = true;
inputWindow.canReceiveKeys = false;
inputWindow.hasFocus = true;
inputWindow.hasWallpaper = false;
inputWindow.paused = false;
inputWindow.layer = mService.mDragState.getDragLayerLw();
inputWindow.ownerPid = Process.myPid();
inputWindow.ownerUid = Process.myUid();
// The drag window covers the entire display
inputWindow.frameLeft = 0;
inputWindow.frameTop = 0;
inputWindow.frameRight = mService.mDisplay.getWidth();
inputWindow.frameBottom = mService.mDisplay.getHeight();
// The drag window cannot receive new touches.
inputWindow.touchableRegion.setEmpty();
}
public void setUpdateInputWindowsNeededLw() {
mUpdateInputWindowsNeeded = true;
}
/* Updates the cached window information provided to the input dispatcher. */
public void updateInputWindowsLw(boolean force) {
if (!force && !mUpdateInputWindowsNeeded) {
return;
}
mUpdateInputWindowsNeeded = false;
// Populate the input window list with information about all of the windows that
// could potentially receive input.
// As an optimization, we could try to prune the list of windows but this turns
// out to be difficult because only the native code knows for sure which window
// currently has touch focus.
final ArrayList<WindowState> windows = mService.mWindows;
// If there's a drag in flight, provide a pseudowindow to catch drag input
final boolean inDrag = (mService.mDragState != null);
if (inDrag) {
if (WindowManagerService.DEBUG_DRAG) {
Log.d(WindowManagerService.TAG, "Inserting drag window");
}
addDragInputWindowLw(mTempInputWindows);
}
final int N = windows.size();
for (int i = N - 1; i >= 0; i--) {
final WindowState child = windows.get(i);
if (child.mInputChannel == null || child.mRemoved) {
// Skip this window because it cannot possibly receive input.
continue;
}
final int flags = child.mAttrs.flags;
final int type = child.mAttrs.type;
final boolean hasFocus = (child == mInputFocus);
final boolean isVisible = child.isVisibleLw();
final boolean hasWallpaper = (child == mService.mWallpaperTarget)
&& (type != WindowManager.LayoutParams.TYPE_KEYGUARD);
// If there's a drag in progress and 'child' is a potential drop target,
// make sure it's been told about the drag
if (inDrag && isVisible) {
mService.mDragState.sendDragStartedIfNeededLw(child);
}
// Add a window to our list of input windows.
final InputWindow inputWindow = mTempInputWindows.add();
inputWindow.inputWindowHandle = child.mInputWindowHandle;
inputWindow.inputChannel = child.mInputChannel;
inputWindow.name = child.toString();
inputWindow.layoutParamsFlags = flags;
inputWindow.layoutParamsType = type;
inputWindow.dispatchingTimeoutNanos = child.getInputDispatchingTimeoutNanos();
inputWindow.visible = isVisible;
inputWindow.canReceiveKeys = child.canReceiveKeys();
inputWindow.hasFocus = hasFocus;
inputWindow.hasWallpaper = hasWallpaper;
inputWindow.paused = child.mAppToken != null ? child.mAppToken.paused : false;
inputWindow.layer = child.mLayer;
inputWindow.ownerPid = child.mSession.mPid;
inputWindow.ownerUid = child.mSession.mUid;
final Rect frame = child.mFrame;
inputWindow.frameLeft = frame.left;
inputWindow.frameTop = frame.top;
inputWindow.frameRight = frame.right;
inputWindow.frameBottom = frame.bottom;
child.getTouchableRegion(inputWindow.touchableRegion);
}
// Send windows to native code.
mService.mInputManager.setInputWindows(mTempInputWindows.toNullTerminatedArray());
// Clear the list in preparation for the next round.
// Also avoids keeping InputChannel objects referenced unnecessarily.
mTempInputWindows.clear();
}
/* Notifies that the input device configuration has changed. */
public void notifyConfigurationChanged() {
mService.sendNewConfiguration();
synchronized (mInputDevicesReadyMonitor) {
if (!mInputDevicesReady) {
mInputDevicesReady = true;
mInputDevicesReadyMonitor.notifyAll();
}
}
}
/* Waits until the built-in input devices have been configured. */
public boolean waitForInputDevicesReady(long timeoutMillis) {
synchronized (mInputDevicesReadyMonitor) {
if (!mInputDevicesReady) {
try {
mInputDevicesReadyMonitor.wait(timeoutMillis);
} catch (InterruptedException ex) {
}
}
return mInputDevicesReady;
}
}
/* Notifies that the lid switch changed state. */
public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) {
mService.mPolicy.notifyLidSwitchChanged(whenNanos, lidOpen);
}
/* Provides an opportunity for the window manager policy to intercept early key
* processing as soon as the key has been read from the device. */
public int interceptKeyBeforeQueueing(
KeyEvent event, int policyFlags, boolean isScreenOn) {
return mService.mPolicy.interceptKeyBeforeQueueing(event, policyFlags, isScreenOn);
}
/* Provides an opportunity for the window manager policy to process a key before
* ordinary dispatch. */
public boolean interceptKeyBeforeDispatching(
InputWindowHandle focus, KeyEvent event, int policyFlags) {
WindowState windowState = focus != null ? (WindowState) focus.windowState : null;
return mService.mPolicy.interceptKeyBeforeDispatching(windowState, event, policyFlags);
}
/* Provides an opportunity for the window manager policy to process a key that
* the application did not handle. */
public KeyEvent dispatchUnhandledKey(
InputWindowHandle focus, KeyEvent event, int policyFlags) {
WindowState windowState = focus != null ? (WindowState) focus.windowState : null;
return mService.mPolicy.dispatchUnhandledKey(windowState, event, policyFlags);
}
/* Called when the current input focus changes.
* Layer assignment is assumed to be complete by the time this is called.
*/
public void setInputFocusLw(WindowState newWindow, boolean updateInputWindows) {
if (WindowManagerService.DEBUG_INPUT) {
Slog.d(WindowManagerService.TAG, "Input focus has changed to " + newWindow);
}
if (newWindow != mInputFocus) {
if (newWindow != null && newWindow.canReceiveKeys()) {
// Displaying a window implicitly causes dispatching to be unpaused.
// This is to protect against bugs if someone pauses dispatching but
// forgets to resume.
newWindow.mToken.paused = false;
}
mInputFocus = newWindow;
setUpdateInputWindowsNeededLw();
if (updateInputWindows) {
updateInputWindowsLw(false /*force*/);
}
}
}
public void setFocusedAppLw(AppWindowToken newApp) {
// Focused app has changed.
if (newApp == null) {
mService.mInputManager.setFocusedApplication(null);
} else {
mTempInputApplication.inputApplicationHandle = newApp.mInputApplicationHandle;
mTempInputApplication.name = newApp.toString();
mTempInputApplication.dispatchingTimeoutNanos =
newApp.inputDispatchingTimeoutNanos;
mService.mInputManager.setFocusedApplication(mTempInputApplication);
mTempInputApplication.recycle();
}
}
public void pauseDispatchingLw(WindowToken window) {
if (! window.paused) {
if (WindowManagerService.DEBUG_INPUT) {
Slog.v(WindowManagerService.TAG, "Pausing WindowToken " + window);
}
window.paused = true;
updateInputWindowsLw(true /*force*/);
}
}
public void resumeDispatchingLw(WindowToken window) {
if (window.paused) {
if (WindowManagerService.DEBUG_INPUT) {
Slog.v(WindowManagerService.TAG, "Resuming WindowToken " + window);
}
window.paused = false;
updateInputWindowsLw(true /*force*/);
}
}
public void freezeInputDispatchingLw() {
if (! mInputDispatchFrozen) {
if (WindowManagerService.DEBUG_INPUT) {
Slog.v(WindowManagerService.TAG, "Freezing input dispatching");
}
mInputDispatchFrozen = true;
updateInputDispatchModeLw();
}
}
public void thawInputDispatchingLw() {
if (mInputDispatchFrozen) {
if (WindowManagerService.DEBUG_INPUT) {
Slog.v(WindowManagerService.TAG, "Thawing input dispatching");
}
mInputDispatchFrozen = false;
updateInputDispatchModeLw();
}
}
public void setEventDispatchingLw(boolean enabled) {
if (mInputDispatchEnabled != enabled) {
if (WindowManagerService.DEBUG_INPUT) {
Slog.v(WindowManagerService.TAG, "Setting event dispatching to " + enabled);
}
mInputDispatchEnabled = enabled;
updateInputDispatchModeLw();
}
}
private void updateInputDispatchModeLw() {
mService.mInputManager.setInputDispatchMode(mInputDispatchEnabled, mInputDispatchFrozen);
}
}

View File

@@ -14,8 +14,7 @@
* limitations under the License.
*/
package com.android.server.wm; // TODO: use com.android.server.wm, once things move there
package com.android.server.wm;
import android.content.Context;
import android.graphics.Bitmap;

View File

@@ -0,0 +1,419 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.wm;
import com.android.internal.view.IInputContext;
import com.android.internal.view.IInputMethodClient;
import com.android.internal.view.IInputMethodManager;
import com.android.server.wm.WindowManagerService.H;
import android.content.ClipData;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.graphics.Region;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Slog;
import android.view.IWindow;
import android.view.IWindowSession;
import android.view.InputChannel;
import android.view.Surface;
import android.view.SurfaceSession;
import android.view.WindowManager;
import java.io.PrintWriter;
/**
* This class represents an active client session. There is generally one
* Session object per process that is interacting with the window manager.
*/
final class Session extends IWindowSession.Stub
implements IBinder.DeathRecipient {
final WindowManagerService mService;
final IInputMethodClient mClient;
final IInputContext mInputContext;
final int mUid;
final int mPid;
final String mStringName;
SurfaceSession mSurfaceSession;
int mNumWindow = 0;
boolean mClientDead = false;
public Session(WindowManagerService service, IInputMethodClient client,
IInputContext inputContext) {
mService = service;
mClient = client;
mInputContext = inputContext;
mUid = Binder.getCallingUid();
mPid = Binder.getCallingPid();
StringBuilder sb = new StringBuilder();
sb.append("Session{");
sb.append(Integer.toHexString(System.identityHashCode(this)));
sb.append(" uid ");
sb.append(mUid);
sb.append("}");
mStringName = sb.toString();
synchronized (mService.mWindowMap) {
if (mService.mInputMethodManager == null && mService.mHaveInputMethods) {
IBinder b = ServiceManager.getService(
Context.INPUT_METHOD_SERVICE);
mService.mInputMethodManager = IInputMethodManager.Stub.asInterface(b);
}
}
long ident = Binder.clearCallingIdentity();
try {
// Note: it is safe to call in to the input method manager
// here because we are not holding our lock.
if (mService.mInputMethodManager != null) {
mService.mInputMethodManager.addClient(client, inputContext,
mUid, mPid);
} else {
client.setUsingInputMethod(false);
}
client.asBinder().linkToDeath(this, 0);
} catch (RemoteException e) {
// The caller has died, so we can just forget about this.
try {
if (mService.mInputMethodManager != null) {
mService.mInputMethodManager.removeClient(client);
}
} catch (RemoteException ee) {
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
try {
return super.onTransact(code, data, reply, flags);
} catch (RuntimeException e) {
// Log all 'real' exceptions thrown to the caller
if (!(e instanceof SecurityException)) {
Slog.e(WindowManagerService.TAG, "Window Session Crash", e);
}
throw e;
}
}
public void binderDied() {
// Note: it is safe to call in to the input method manager
// here because we are not holding our lock.
try {
if (mService.mInputMethodManager != null) {
mService.mInputMethodManager.removeClient(mClient);
}
} catch (RemoteException e) {
}
synchronized(mService.mWindowMap) {
mClient.asBinder().unlinkToDeath(this, 0);
mClientDead = true;
killSessionLocked();
}
}
public int add(IWindow window, WindowManager.LayoutParams attrs,
int viewVisibility, Rect outContentInsets, InputChannel outInputChannel) {
return mService.addWindow(this, window, attrs, viewVisibility, outContentInsets,
outInputChannel);
}
public int addWithoutInputChannel(IWindow window, WindowManager.LayoutParams attrs,
int viewVisibility, Rect outContentInsets) {
return mService.addWindow(this, window, attrs, viewVisibility, outContentInsets, null);
}
public void remove(IWindow window) {
mService.removeWindow(this, window);
}
public int relayout(IWindow window, WindowManager.LayoutParams attrs,
int requestedWidth, int requestedHeight, int viewFlags,
boolean insetsPending, Rect outFrame, Rect outContentInsets,
Rect outVisibleInsets, Configuration outConfig, Surface outSurface) {
//Log.d(TAG, ">>>>>> ENTERED relayout from " + Binder.getCallingPid());
int res = mService.relayoutWindow(this, window, attrs,
requestedWidth, requestedHeight, viewFlags, insetsPending,
outFrame, outContentInsets, outVisibleInsets, outConfig, outSurface);
//Log.d(TAG, "<<<<<< EXITING relayout to " + Binder.getCallingPid());
return res;
}
public void setTransparentRegion(IWindow window, Region region) {
mService.setTransparentRegionWindow(this, window, region);
}
public void setInsets(IWindow window, int touchableInsets,
Rect contentInsets, Rect visibleInsets, Region touchableArea) {
mService.setInsetsWindow(this, window, touchableInsets, contentInsets,
visibleInsets, touchableArea);
}
public void getDisplayFrame(IWindow window, Rect outDisplayFrame) {
mService.getWindowDisplayFrame(this, window, outDisplayFrame);
}
public void finishDrawing(IWindow window) {
if (WindowManagerService.localLOGV) Slog.v(
WindowManagerService.TAG, "IWindow finishDrawing called for " + window);
mService.finishDrawingWindow(this, window);
}
public void setInTouchMode(boolean mode) {
synchronized(mService.mWindowMap) {
mService.mInTouchMode = mode;
}
}
public boolean getInTouchMode() {
synchronized(mService.mWindowMap) {
return mService.mInTouchMode;
}
}
public boolean performHapticFeedback(IWindow window, int effectId,
boolean always) {
synchronized(mService.mWindowMap) {
long ident = Binder.clearCallingIdentity();
try {
return mService.mPolicy.performHapticFeedbackLw(
mService.windowForClientLocked(this, window, true),
effectId, always);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
}
/* Drag/drop */
public IBinder prepareDrag(IWindow window, int flags,
int width, int height, Surface outSurface) {
return mService.prepareDragSurface(window, mSurfaceSession, flags,
width, height, outSurface);
}
public boolean performDrag(IWindow window, IBinder dragToken,
float touchX, float touchY, float thumbCenterX, float thumbCenterY,
ClipData data) {
if (WindowManagerService.DEBUG_DRAG) {
Slog.d(WindowManagerService.TAG, "perform drag: win=" + window + " data=" + data);
}
synchronized (mService.mWindowMap) {
if (mService.mDragState == null) {
Slog.w(WindowManagerService.TAG, "No drag prepared");
throw new IllegalStateException("performDrag() without prepareDrag()");
}
if (dragToken != mService.mDragState.mToken) {
Slog.w(WindowManagerService.TAG, "Performing mismatched drag");
throw new IllegalStateException("performDrag() does not match prepareDrag()");
}
WindowState callingWin = mService.windowForClientLocked(null, window, false);
if (callingWin == null) {
Slog.w(WindowManagerService.TAG, "Bad requesting window " + window);
return false; // !!! TODO: throw here?
}
// !!! TODO: if input is not still focused on the initiating window, fail
// the drag initiation (e.g. an alarm window popped up just as the application
// called performDrag()
mService.mH.removeMessages(H.DRAG_START_TIMEOUT, window.asBinder());
// !!! TODO: extract the current touch (x, y) in screen coordinates. That
// will let us eliminate the (touchX,touchY) parameters from the API.
// !!! FIXME: put all this heavy stuff onto the mH looper, as well as
// the actual drag event dispatch stuff in the dragstate
mService.mDragState.register();
mService.mInputMonitor.updateInputWindowsLw(true /*force*/);
if (!mService.mInputManager.transferTouchFocus(callingWin.mInputChannel,
mService.mDragState.mServerChannel)) {
Slog.e(WindowManagerService.TAG, "Unable to transfer touch focus");
mService.mDragState.unregister();
mService.mDragState = null;
mService.mInputMonitor.updateInputWindowsLw(true /*force*/);
return false;
}
mService.mDragState.mData = data;
mService.mDragState.mCurrentX = touchX;
mService.mDragState.mCurrentY = touchY;
mService.mDragState.broadcastDragStartedLw(touchX, touchY);
// remember the thumb offsets for later
mService.mDragState.mThumbOffsetX = thumbCenterX;
mService.mDragState.mThumbOffsetY = thumbCenterY;
// Make the surface visible at the proper location
final Surface surface = mService.mDragState.mSurface;
if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, ">>> OPEN TRANSACTION performDrag");
Surface.openTransaction();
try {
surface.setPosition((int)(touchX - thumbCenterX),
(int)(touchY - thumbCenterY));
surface.setAlpha(.7071f);
surface.setLayer(mService.mDragState.getDragLayerLw());
surface.show();
} finally {
Surface.closeTransaction();
if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, "<<< CLOSE TRANSACTION performDrag");
}
}
return true; // success!
}
public void reportDropResult(IWindow window, boolean consumed) {
IBinder token = window.asBinder();
if (WindowManagerService.DEBUG_DRAG) {
Slog.d(WindowManagerService.TAG, "Drop result=" + consumed + " reported by " + token);
}
synchronized (mService.mWindowMap) {
long ident = Binder.clearCallingIdentity();
try {
if (mService.mDragState == null || mService.mDragState.mToken != token) {
Slog.w(WindowManagerService.TAG, "Invalid drop-result claim by " + window);
throw new IllegalStateException("reportDropResult() by non-recipient");
}
// The right window has responded, even if it's no longer around,
// so be sure to halt the timeout even if the later WindowState
// lookup fails.
mService.mH.removeMessages(H.DRAG_END_TIMEOUT, window.asBinder());
WindowState callingWin = mService.windowForClientLocked(null, window, false);
if (callingWin == null) {
Slog.w(WindowManagerService.TAG, "Bad result-reporting window " + window);
return; // !!! TODO: throw here?
}
mService.mDragState.mDragResult = consumed;
mService.mDragState.endDragLw();
} finally {
Binder.restoreCallingIdentity(ident);
}
}
}
public void dragRecipientEntered(IWindow window) {
if (WindowManagerService.DEBUG_DRAG) {
Slog.d(WindowManagerService.TAG, "Drag into new candidate view @ " + window.asBinder());
}
}
public void dragRecipientExited(IWindow window) {
if (WindowManagerService.DEBUG_DRAG) {
Slog.d(WindowManagerService.TAG, "Drag from old candidate view @ " + window.asBinder());
}
}
public void setWallpaperPosition(IBinder window, float x, float y, float xStep, float yStep) {
synchronized(mService.mWindowMap) {
long ident = Binder.clearCallingIdentity();
try {
mService.setWindowWallpaperPositionLocked(
mService.windowForClientLocked(this, window, true),
x, y, xStep, yStep);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
}
public void wallpaperOffsetsComplete(IBinder window) {
mService.wallpaperOffsetsComplete(window);
}
public Bundle sendWallpaperCommand(IBinder window, String action, int x, int y,
int z, Bundle extras, boolean sync) {
synchronized(mService.mWindowMap) {
long ident = Binder.clearCallingIdentity();
try {
return mService.sendWindowWallpaperCommandLocked(
mService.windowForClientLocked(this, window, true),
action, x, y, z, extras, sync);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
}
public void wallpaperCommandComplete(IBinder window, Bundle result) {
mService.wallpaperCommandComplete(window, result);
}
void windowAddedLocked() {
if (mSurfaceSession == null) {
if (WindowManagerService.localLOGV) Slog.v(
WindowManagerService.TAG, "First window added to " + this + ", creating SurfaceSession");
mSurfaceSession = new SurfaceSession();
if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(
WindowManagerService.TAG, " NEW SURFACE SESSION " + mSurfaceSession);
mService.mSessions.add(this);
}
mNumWindow++;
}
void windowRemovedLocked() {
mNumWindow--;
killSessionLocked();
}
void killSessionLocked() {
if (mNumWindow <= 0 && mClientDead) {
mService.mSessions.remove(this);
if (mSurfaceSession != null) {
if (WindowManagerService.localLOGV) Slog.v(
WindowManagerService.TAG, "Last window removed from " + this
+ ", destroying " + mSurfaceSession);
if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(
WindowManagerService.TAG, " KILL SURFACE SESSION " + mSurfaceSession);
try {
mSurfaceSession.kill();
} catch (Exception e) {
Slog.w(WindowManagerService.TAG, "Exception thrown when killing surface session "
+ mSurfaceSession + " in session " + this
+ ": " + e.toString());
}
mSurfaceSession = null;
}
}
}
void dump(PrintWriter pw, String prefix) {
pw.print(prefix); pw.print("mNumWindow="); pw.print(mNumWindow);
pw.print(" mClientDead="); pw.print(mClientDead);
pw.print(" mSurfaceSession="); pw.println(mSurfaceSession);
}
@Override
public String toString() {
return mStringName;
}
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.wm;
final class StartingData {
final String pkg;
final int theme;
final CharSequence nonLocalizedLabel;
final int labelRes;
final int icon;
final int windowFlags;
StartingData(String _pkg, int _theme, CharSequence _nonLocalizedLabel,
int _labelRes, int _icon, int _windowFlags) {
pkg = _pkg;
theme = _theme;
nonLocalizedLabel = _nonLocalizedLabel;
labelRes = _labelRes;
icon = _icon;
windowFlags = _windowFlags;
}
}

View File

@@ -14,13 +14,12 @@
* limitations under the License.
*/
package com.android.server.wm; // TODO: use com.android.server.wm, once things move there
package com.android.server.wm;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.Region;
import android.util.DisplayMetrics;

View File

@@ -0,0 +1,177 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.wm;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.Paint.FontMetricsInt;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
import android.view.Display;
import android.view.Surface;
import android.view.SurfaceSession;
import android.view.Surface.OutOfResourcesException;
/**
* Displays a watermark on top of the window manager's windows.
*/
class Watermark {
final String[] mTokens;
final String mText;
final Paint mTextPaint;
final int mTextWidth;
final int mTextHeight;
final int mTextAscent;
final int mTextDescent;
final int mDeltaX;
final int mDeltaY;
Surface mSurface;
int mLastDW;
int mLastDH;
boolean mDrawNeeded;
Watermark(Display display, SurfaceSession session, String[] tokens) {
final DisplayMetrics dm = new DisplayMetrics();
display.getMetrics(dm);
if (false) {
Log.i(WindowManagerService.TAG, "*********************** WATERMARK");
for (int i=0; i<tokens.length; i++) {
Log.i(WindowManagerService.TAG, " TOKEN #" + i + ": " + tokens[i]);
}
}
mTokens = tokens;
StringBuilder builder = new StringBuilder(32);
int len = mTokens[0].length();
len = len & ~1;
for (int i=0; i<len; i+=2) {
int c1 = mTokens[0].charAt(i);
int c2 = mTokens[0].charAt(i+1);
if (c1 >= 'a' && c1 <= 'f') c1 = c1 - 'a' + 10;
else if (c1 >= 'A' && c1 <= 'F') c1 = c1 - 'A' + 10;
else c1 -= '0';
if (c2 >= 'a' && c2 <= 'f') c2 = c2 - 'a' + 10;
else if (c2 >= 'A' && c2 <= 'F') c2 = c2 - 'A' + 10;
else c2 -= '0';
builder.append((char)(255-((c1*16)+c2)));
}
mText = builder.toString();
if (false) {
Log.i(WindowManagerService.TAG, "Final text: " + mText);
}
int fontSize = WindowManagerService.getPropertyInt(tokens, 1,
TypedValue.COMPLEX_UNIT_DIP, 20, dm);
mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mTextPaint.setTextSize(fontSize);
mTextPaint.setTypeface(Typeface.create(Typeface.SANS_SERIF, Typeface.BOLD));
FontMetricsInt fm = mTextPaint.getFontMetricsInt();
mTextWidth = (int)mTextPaint.measureText(mText);
mTextAscent = fm.ascent;
mTextDescent = fm.descent;
mTextHeight = fm.descent - fm.ascent;
mDeltaX = WindowManagerService.getPropertyInt(tokens, 2,
TypedValue.COMPLEX_UNIT_PX, mTextWidth*2, dm);
mDeltaY = WindowManagerService.getPropertyInt(tokens, 3,
TypedValue.COMPLEX_UNIT_PX, mTextHeight*3, dm);
int shadowColor = WindowManagerService.getPropertyInt(tokens, 4,
TypedValue.COMPLEX_UNIT_PX, 0xb0000000, dm);
int color = WindowManagerService.getPropertyInt(tokens, 5,
TypedValue.COMPLEX_UNIT_PX, 0x60ffffff, dm);
int shadowRadius = WindowManagerService.getPropertyInt(tokens, 6,
TypedValue.COMPLEX_UNIT_PX, 7, dm);
int shadowDx = WindowManagerService.getPropertyInt(tokens, 8,
TypedValue.COMPLEX_UNIT_PX, 0, dm);
int shadowDy = WindowManagerService.getPropertyInt(tokens, 9,
TypedValue.COMPLEX_UNIT_PX, 0, dm);
mTextPaint.setColor(color);
mTextPaint.setShadowLayer(shadowRadius, shadowDx, shadowDy, shadowColor);
try {
mSurface = new Surface(session, 0,
"WatermarkSurface", -1, 1, 1, PixelFormat.TRANSLUCENT, 0);
mSurface.setLayer(WindowManagerService.TYPE_LAYER_MULTIPLIER*100);
mSurface.setPosition(0, 0);
mSurface.show();
} catch (OutOfResourcesException e) {
}
}
void positionSurface(int dw, int dh) {
if (mLastDW != dw || mLastDH != dh) {
mLastDW = dw;
mLastDH = dh;
mSurface.setSize(dw, dh);
mDrawNeeded = true;
}
}
void drawIfNeeded() {
if (mDrawNeeded) {
final int dw = mLastDW;
final int dh = mLastDH;
mDrawNeeded = false;
Rect dirty = new Rect(0, 0, dw, dh);
Canvas c = null;
try {
c = mSurface.lockCanvas(dirty);
} catch (IllegalArgumentException e) {
} catch (OutOfResourcesException e) {
}
if (c != null) {
c.drawColor(0, PorterDuff.Mode.CLEAR);
int deltaX = mDeltaX;
int deltaY = mDeltaY;
// deltaX shouldn't be close to a round fraction of our
// x step, or else things will line up too much.
int div = (dw+mTextWidth)/deltaX;
int rem = (dw+mTextWidth) - (div*deltaX);
int qdelta = deltaX/4;
if (rem < qdelta || rem > (deltaX-qdelta)) {
deltaX += deltaX/3;
}
int y = -mTextHeight;
int x = -mTextWidth;
while (y < (dh+mTextHeight)) {
c.drawText(mText, x, y, mTextPaint);
x += deltaX;
if (x >= dw) {
x -= (dw+mTextWidth);
y += deltaY;
}
}
mSurface.unlockCanvasAndPost(c);
}
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,110 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.wm;
import android.os.IBinder;
import java.io.PrintWriter;
import java.util.ArrayList;
/**
* Container of a set of related windows in the window manager. Often this
* is an AppWindowToken, which is the handle for an Activity that it uses
* to display windows. For nested windows, there is a WindowToken created for
* the parent window to manage its children.
*/
class WindowToken {
// The window manager!
final WindowManagerService service;
// The actual token.
final IBinder token;
// The type of window this token is for, as per WindowManager.LayoutParams.
final int windowType;
// Set if this token was explicitly added by a client, so should
// not be removed when all windows are removed.
final boolean explicit;
// For printing.
String stringName;
// If this is an AppWindowToken, this is non-null.
AppWindowToken appWindowToken;
// All of the windows associated with this token.
final ArrayList<WindowState> windows = new ArrayList<WindowState>();
// Is key dispatching paused for this token?
boolean paused = false;
// Should this token's windows be hidden?
boolean hidden;
// Temporary for finding which tokens no longer have visible windows.
boolean hasVisible;
// Set to true when this token is in a pending transaction where it
// will be shown.
boolean waitingToShow;
// Set to true when this token is in a pending transaction where it
// will be hidden.
boolean waitingToHide;
// Set to true when this token is in a pending transaction where its
// windows will be put to the bottom of the list.
boolean sendingToBottom;
// Set to true when this token is in a pending transaction where its
// windows will be put to the top of the list.
boolean sendingToTop;
WindowToken(WindowManagerService _service, IBinder _token, int type, boolean _explicit) {
service = _service;
token = _token;
windowType = type;
explicit = _explicit;
}
void dump(PrintWriter pw, String prefix) {
pw.print(prefix); pw.print("token="); pw.println(token);
pw.print(prefix); pw.print("windows="); pw.println(windows);
pw.print(prefix); pw.print("windowType="); pw.print(windowType);
pw.print(" hidden="); pw.print(hidden);
pw.print(" hasVisible="); pw.println(hasVisible);
if (waitingToShow || waitingToHide || sendingToBottom || sendingToTop) {
pw.print(prefix); pw.print("waitingToShow="); pw.print(waitingToShow);
pw.print(" waitingToHide="); pw.print(waitingToHide);
pw.print(" sendingToBottom="); pw.print(sendingToBottom);
pw.print(" sendingToTop="); pw.println(sendingToTop);
}
}
@Override
public String toString() {
if (stringName == null) {
StringBuilder sb = new StringBuilder();
sb.append("WindowToken{");
sb.append(Integer.toHexString(System.identityHashCode(this)));
sb.append(" token="); sb.append(token); sb.append('}');
stringName = sb.toString();
}
return stringName;
}
}