Merge "LayoutLib: support defStyleRes in obtainStyledAttributes."
diff --git a/Android.mk b/Android.mk
index 1407631..08ee65e 100644
--- a/Android.mk
+++ b/Android.mk
@@ -416,6 +416,8 @@
resources/samples/NotePad "Note Pad" \
-samplecode $(sample_dir)/SampleSyncAdapter \
resources/samples/SampleSyncAdapter "Sample Sync Adapter" \
+ -samplecode frameworks/base/libs/rs/java \
+ resources/samples/Renderscript "Renderscript" \
-samplecode $(sample_dir)/SearchableDictionary \
resources/samples/SearchableDictionary "Searchable Dictionary v2" \
-samplecode $(sample_dir)/SipDemo \
diff --git a/api/current.xml b/api/current.xml
index 553676c..6279d8d 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -119000,6 +119000,17 @@
visibility="public"
>
</method>
+<method name="getAuthUserName"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getAutoRegistration"
return="boolean"
abstract="false"
@@ -119202,6 +119213,19 @@
visibility="public"
>
</method>
+<method name="setAuthUserName"
+ return="android.net.sip.SipProfile.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="name" type="java.lang.String">
+</parameter>
+</method>
<method name="setAutoRegistration"
return="android.net.sip.SipProfile.Builder"
abstract="false"
@@ -220046,6 +220070,19 @@
<parameter name="focusableMode" type="int">
</parameter>
</method>
+<method name="addOnAttachStateChangeListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.view.View.OnAttachStateChangeListener">
+</parameter>
+</method>
<method name="addOnLayoutChangeListener"
return="void"
abstract="false"
@@ -223049,6 +223086,19 @@
<parameter name="action" type="java.lang.Runnable">
</parameter>
</method>
+<method name="removeOnAttachStateChangeListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.view.View.OnAttachStateChangeListener">
+</parameter>
+</method>
<method name="removeOnLayoutChangeListener"
return="void"
abstract="false"
@@ -225370,6 +225420,40 @@
>
</field>
</class>
+<interface name="View.OnAttachStateChangeListener"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="onViewAttachedToWindow"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="v" type="android.view.View">
+</parameter>
+</method>
+<method name="onViewDetachedFromWindow"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="v" type="android.view.View">
+</parameter>
+</method>
+</interface>
<interface name="View.OnClickListener"
abstract="true"
static="true"
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index d143243..eca06c5 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -69,6 +69,43 @@
public static native long getMobileRxBytes();
/**
+ * Get the total number of packets transmitted through the specified interface.
+ *
+ * @return number of packets. If the statistics are not supported by this interface,
+ * {@link #UNSUPPORTED} will be returned.
+ * @hide
+ */
+ public static native long getTxPackets(String iface);
+
+ /**
+ * Get the total number of packets received through the specified interface.
+ *
+ * @return number of packets. If the statistics are not supported by this interface,
+ * {@link #UNSUPPORTED} will be returned.
+ * @hide
+ */
+ public static native long getRxPackets(String iface);
+
+ /**
+ * Get the total number of bytes transmitted through the specified interface.
+ *
+ * @return number of bytes. If the statistics are not supported by this interface,
+ * {@link #UNSUPPORTED} will be returned.
+ * @hide
+ */
+ public static native long getTxBytes(String iface);
+
+ /**
+ * Get the total number of bytes received through the specified interface.
+ *
+ * @return number of bytes. If the statistics are not supported by this interface,
+ * {@link #UNSUPPORTED} will be returned.
+ * @hide
+ */
+ public static native long getRxBytes(String iface);
+
+
+ /**
* Get the total number of packets sent through all network interfaces.
*
* @return the number of packets. If the statistics are not supported by this device,
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 6e6731e..90e2e79 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -23,6 +23,7 @@
import java.util.Map;
import android.content.pm.ApplicationInfo;
+import android.telephony.SignalStrength;
import android.util.Log;
import android.util.Printer;
import android.util.SparseArray;
@@ -608,18 +609,6 @@
* {@hide}
*/
public abstract long getPhoneOnTime(long batteryRealtime, int which);
-
- public static final int SIGNAL_STRENGTH_NONE_OR_UNKNOWN = 0;
- public static final int SIGNAL_STRENGTH_POOR = 1;
- public static final int SIGNAL_STRENGTH_MODERATE = 2;
- public static final int SIGNAL_STRENGTH_GOOD = 3;
- public static final int SIGNAL_STRENGTH_GREAT = 4;
-
- static final String[] SIGNAL_STRENGTH_NAMES = {
- "none", "poor", "moderate", "good", "great"
- };
-
- public static final int NUM_SIGNAL_STRENGTH_BINS = 5;
/**
* Returns the time in microseconds that the phone has been running with
@@ -710,7 +699,7 @@
SCREEN_BRIGHTNESS_NAMES),
new BitDescription(HistoryItem.STATE_SIGNAL_STRENGTH_MASK,
HistoryItem.STATE_SIGNAL_STRENGTH_SHIFT, "signal_strength",
- SIGNAL_STRENGTH_NAMES),
+ SignalStrength.SIGNAL_STRENGTH_NAMES),
new BitDescription(HistoryItem.STATE_PHONE_STATE_MASK,
HistoryItem.STATE_PHONE_STATE_SHIFT, "phone_state",
new String[] {"in", "out", "emergency", "off"}),
@@ -1095,14 +1084,14 @@
dumpLine(pw, 0 /* uid */, category, SCREEN_BRIGHTNESS_DATA, args);
// Dump signal strength stats
- args = new Object[NUM_SIGNAL_STRENGTH_BINS];
- for (int i=0; i<NUM_SIGNAL_STRENGTH_BINS; i++) {
+ args = new Object[SignalStrength.NUM_SIGNAL_STRENGTH_BINS];
+ for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
args[i] = getPhoneSignalStrengthTime(i, batteryRealtime, which) / 1000;
}
dumpLine(pw, 0 /* uid */, category, SIGNAL_STRENGTH_TIME_DATA, args);
dumpLine(pw, 0 /* uid */, category, SIGNAL_SCANNING_TIME_DATA,
getPhoneSignalScanningTime(batteryRealtime, which) / 1000);
- for (int i=0; i<NUM_SIGNAL_STRENGTH_BINS; i++) {
+ for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
args[i] = getPhoneSignalStrengthCount(i, which);
}
dumpLine(pw, 0 /* uid */, category, SIGNAL_STRENGTH_COUNT_DATA, args);
@@ -1408,14 +1397,14 @@
sb.append(prefix);
sb.append(" Signal levels: ");
didOne = false;
- for (int i=0; i<NUM_SIGNAL_STRENGTH_BINS; i++) {
+ for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
final long time = getPhoneSignalStrengthTime(i, batteryRealtime, which);
if (time == 0) {
continue;
}
if (didOne) sb.append(", ");
didOne = true;
- sb.append(SIGNAL_STRENGTH_NAMES[i]);
+ sb.append(SignalStrength.SIGNAL_STRENGTH_NAMES[i]);
sb.append(" ");
formatTimeMs(sb, time/1000);
sb.append("(");
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 48451ba..eefce06 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -74,6 +74,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.WeakHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
/**
* <p>
@@ -2099,6 +2100,11 @@
private ArrayList<OnLayoutChangeListener> mOnLayoutChangeListeners;
/**
+ * Listeners for attach events.
+ */
+ private CopyOnWriteArrayList<OnAttachStateChangeListener> mOnAttachStateChangeListeners;
+
+ /**
* Listener used to dispatch click events.
* This field should be made private, so it is hidden from the SDK.
* {@hide}
@@ -2996,6 +3002,37 @@
}
/**
+ * Add a listener for attach state changes.
+ *
+ * This listener will be called whenever this view is attached or detached
+ * from a window. Remove the listener using
+ * {@link #removeOnAttachStateChangeListener(OnAttachStateChangeListener)}.
+ *
+ * @param listener Listener to attach
+ * @see #removeOnAttachStateChangeListener(OnAttachStateChangeListener)
+ */
+ public void addOnAttachStateChangeListener(OnAttachStateChangeListener listener) {
+ if (mOnAttachStateChangeListeners == null) {
+ mOnAttachStateChangeListeners = new CopyOnWriteArrayList<OnAttachStateChangeListener>();
+ }
+ mOnAttachStateChangeListeners.add(listener);
+ }
+
+ /**
+ * Remove a listener for attach state changes. The listener will receive no further
+ * notification of window attach/detach events.
+ *
+ * @param listener Listener to remove
+ * @see #addOnAttachStateChangeListener(OnAttachStateChangeListener)
+ */
+ public void removeOnAttachStateChangeListener(OnAttachStateChangeListener listener) {
+ if (mOnAttachStateChangeListeners == null) {
+ return;
+ }
+ mOnAttachStateChangeListeners.remove(listener);
+ }
+
+ /**
* Returns the focus-change callback registered for this view.
*
* @return The callback, or null if one is not registered.
@@ -7953,6 +7990,19 @@
}
performCollectViewAttributes(visibility);
onAttachedToWindow();
+
+ final CopyOnWriteArrayList<OnAttachStateChangeListener> listeners =
+ mOnAttachStateChangeListeners;
+ if (listeners != null && listeners.size() > 0) {
+ // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
+ // perform the dispatching. The iterator is a safe guard against listeners that
+ // could mutate the list by calling the various add/remove methods. This prevents
+ // the array from being modified while we iterate it.
+ for (OnAttachStateChangeListener listener : listeners) {
+ listener.onViewAttachedToWindow(this);
+ }
+ }
+
int vis = info.mWindowVisibility;
if (vis != GONE) {
onWindowVisibilityChanged(vis);
@@ -7974,6 +8024,18 @@
onDetachedFromWindow();
+ final CopyOnWriteArrayList<OnAttachStateChangeListener> listeners =
+ mOnAttachStateChangeListeners;
+ if (listeners != null && listeners.size() > 0) {
+ // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
+ // perform the dispatching. The iterator is a safe guard against listeners that
+ // could mutate the list by calling the various add/remove methods. This prevents
+ // the array from being modified while we iterate it.
+ for (OnAttachStateChangeListener listener : listeners) {
+ listener.onViewDetachedFromWindow(this);
+ }
+ }
+
if ((mPrivateFlags & SCROLL_CONTAINER_ADDED) != 0) {
mAttachInfo.mScrollContainers.remove(this);
mPrivateFlags &= ~SCROLL_CONTAINER_ADDED;
@@ -11767,6 +11829,23 @@
public void onSystemUiVisibilityChange(int visibility);
}
+ /**
+ * Interface definition for a callback to be invoked when this view is attached
+ * or detached from its window.
+ */
+ public interface OnAttachStateChangeListener {
+ /**
+ * Called when the view is attached to a window.
+ * @param v The view that was attached
+ */
+ public void onViewAttachedToWindow(View v);
+ /**
+ * Called when the view is detached from a window.
+ * @param v The view that was detached
+ */
+ public void onViewDetachedFromWindow(View v);
+ }
+
private final class UnsetPressedState implements Runnable {
public void run() {
setPressed(false);
diff --git a/core/java/android/view/VolumePanel.java b/core/java/android/view/VolumePanel.java
index 89b7aaa..9e9f46f 100644
--- a/core/java/android/view/VolumePanel.java
+++ b/core/java/android/view/VolumePanel.java
@@ -129,6 +129,7 @@
// List of stream types and their order
// RING and VOICE_CALL are hidden unless explicitly requested
private static final int [] STREAM_TYPES = {
+ AudioManager.STREAM_BLUETOOTH_SCO,
AudioManager.STREAM_RING,
AudioManager.STREAM_VOICE_CALL,
AudioManager.STREAM_MUSIC,
@@ -137,6 +138,7 @@
// These icons need to correspond to the ones above.
private static final int [] STREAM_ICONS_NORMAL = {
+ R.drawable.ic_audio_bt,
R.drawable.ic_audio_phone,
R.drawable.ic_audio_phone,
R.drawable.ic_audio_vol,
@@ -145,6 +147,7 @@
// These icons need to correspond to the ones above.
private static final int [] STREAM_ICONS_MUTED = {
+ R.drawable.ic_audio_bt,
R.drawable.ic_audio_phone,
R.drawable.ic_audio_phone,
R.drawable.ic_audio_vol_mute,
@@ -277,6 +280,7 @@
final int streamType = STREAM_TYPES[i];
if (streamType == AudioManager.STREAM_RING
|| streamType == AudioManager.STREAM_VOICE_CALL
+ || streamType == AudioManager.STREAM_BLUETOOTH_SCO
|| streamType == activeStreamType) {
continue;
}
diff --git a/core/java/android/webkit/HTML5VideoViewProxy.java b/core/java/android/webkit/HTML5VideoViewProxy.java
index d8f34e0..3caf345 100644
--- a/core/java/android/webkit/HTML5VideoViewProxy.java
+++ b/core/java/android/webkit/HTML5VideoViewProxy.java
@@ -46,6 +46,8 @@
import java.io.ByteArrayOutputStream;
import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.Timer;
@@ -344,7 +346,7 @@
private static RequestQueue mRequestQueue;
private static int mQueueRefCount = 0;
// The poster URL
- private String mUrl;
+ private URL mUrl;
// The proxy we're doing this for.
private final HTML5VideoViewProxy mProxy;
// The poster bytes. We only touch this on the network thread.
@@ -359,14 +361,30 @@
private Handler mHandler;
public PosterDownloader(String url, HTML5VideoViewProxy proxy) {
- mUrl = url;
+ try {
+ mUrl = new URL(url);
+ } catch (MalformedURLException e) {
+ mUrl = null;
+ }
mProxy = proxy;
mHandler = new Handler();
}
// Start the download. Called on WebCore thread.
public void start() {
retainQueue();
- mRequestHandle = mRequestQueue.queueRequest(mUrl, "GET", null, this, null, 0);
+
+ if (mUrl == null) {
+ return;
+ }
+
+ // Only support downloading posters over http/https.
+ // FIXME: Add support for other schemes. WebKit seems able to load
+ // posters over other schemes e.g. file://, but gets the dimensions wrong.
+ String protocol = mUrl.getProtocol();
+ if ("http".equals(protocol) || "https".equals(protocol)) {
+ mRequestHandle = mRequestQueue.queueRequest(mUrl.toString(), "GET", null,
+ this, null, 0);
+ }
}
// Cancel the download if active and release the queue. Called on WebCore thread.
public void cancelAndReleaseQueue() {
@@ -405,12 +423,16 @@
cleanup();
} else if (mStatusCode >= 300 && mStatusCode < 400) {
// We have a redirect.
- mUrl = mHeaders.getLocation();
+ try {
+ mUrl = new URL(mHeaders.getLocation());
+ } catch (MalformedURLException e) {
+ mUrl = null;
+ }
if (mUrl != null) {
mHandler.post(new Runnable() {
public void run() {
if (mRequestHandle != null) {
- mRequestHandle.setupRedirect(mUrl, mStatusCode,
+ mRequestHandle.setupRedirect(mUrl.toString(), mStatusCode,
new HashMap<String, String>());
}
}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 72e3190..74e6628 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -5508,8 +5508,8 @@
}
}
// Trigger the link
- if (mTouchMode == TOUCH_INIT_MODE
- || mTouchMode == TOUCH_DOUBLE_TAP_MODE) {
+ if (!mSelectingText && (mTouchMode == TOUCH_INIT_MODE
+ || mTouchMode == TOUCH_DOUBLE_TAP_MODE)) {
mPrivateHandler.sendEmptyMessageDelayed(
SWITCH_TO_SHORTPRESS, TAP_TIMEOUT);
mPrivateHandler.sendEmptyMessageDelayed(
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 42889cb..da4ce43 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -1814,42 +1814,7 @@
Log.w(LOGTAG, "skip viewSizeChanged as w is 0");
return;
}
- int width = w;
- if (mSettings.getUseWideViewPort()) {
- if (mViewportWidth == -1) {
- if (mSettings.getLayoutAlgorithm() ==
- WebSettings.LayoutAlgorithm.NORMAL || mSettings.getUseFixedViewport()) {
- width = WebView.DEFAULT_VIEWPORT_WIDTH;
- } else {
- /*
- * if a page's minimum preferred width is wider than the
- * given "w", use it instead to get better layout result. If
- * we start a page with MAX_ZOOM_WIDTH, "w" will be always
- * wider. If we start a page with screen width, due to the
- * delay between {@link #didFirstLayout} and
- * {@link #viewSizeChanged},
- * {@link #nativeGetContentMinPrefWidth} will return a more
- * accurate value than initial 0 to result a better layout.
- * In the worse case, the native width will be adjusted when
- * next zoom or screen orientation change happens.
- */
- width = Math.min(WebView.sMaxViewportWidth, Math.max(w,
- Math.max(WebView.DEFAULT_VIEWPORT_WIDTH,
- nativeGetContentMinPrefWidth())));
- }
- } else if (mViewportWidth > 0) {
- if (mSettings.getUseFixedViewport()) {
- // Use website specified or desired fixed viewport width.
- width = mViewportWidth;
- } else {
- width = Math.max(w, mViewportWidth);
- }
- } else if (mSettings.getUseFixedViewport()) {
- width = mWebView.getViewWidth();
- } else {
- width = textwrapWidth;
- }
- }
+ int width = calculateWindowWidth(w, textwrapWidth);
int height = h;
if (width != w) {
float heightWidthRatio = data.mHeightWidthRatio;
@@ -1874,6 +1839,47 @@
EventHub.UPDATE_CACHE_AND_TEXT_ENTRY));
}
+ // Calculate width to be used in webkit window.
+ private int calculateWindowWidth(int viewWidth, int textwrapWidth) {
+ int width = viewWidth;
+ if (mSettings.getUseWideViewPort()) {
+ if (mViewportWidth == -1) {
+ if (mSettings.getLayoutAlgorithm() ==
+ WebSettings.LayoutAlgorithm.NORMAL || mSettings.getUseFixedViewport()) {
+ width = WebView.DEFAULT_VIEWPORT_WIDTH;
+ } else {
+ /*
+ * if a page's minimum preferred width is wider than the
+ * given "w", use it instead to get better layout result. If
+ * we start a page with MAX_ZOOM_WIDTH, "w" will be always
+ * wider. If we start a page with screen width, due to the
+ * delay between {@link #didFirstLayout} and
+ * {@link #viewSizeChanged},
+ * {@link #nativeGetContentMinPrefWidth} will return a more
+ * accurate value than initial 0 to result a better layout.
+ * In the worse case, the native width will be adjusted when
+ * next zoom or screen orientation change happens.
+ */
+ width = Math.min(WebView.sMaxViewportWidth, Math.max(viewWidth,
+ Math.max(WebView.DEFAULT_VIEWPORT_WIDTH,
+ nativeGetContentMinPrefWidth())));
+ }
+ } else if (mViewportWidth > 0) {
+ if (mSettings.getUseFixedViewport()) {
+ // Use website specified or desired fixed viewport width.
+ width = mViewportWidth;
+ } else {
+ width = Math.max(viewWidth, mViewportWidth);
+ }
+ } else if (mSettings.getUseFixedViewport()) {
+ width = mWebView.getViewWidth();
+ } else {
+ width = textwrapWidth;
+ }
+ }
+ return width;
+ }
+
private void sendUpdateTextEntry() {
if (mWebView != null) {
Message.obtain(mWebView.mPrivateHandler,
@@ -2370,7 +2376,7 @@
// to syncing an incorrect height.
data.mHeight = mCurrentViewHeight == 0 ?
Math.round(mWebView.getViewHeight() / data.mScale)
- : mCurrentViewHeight * data.mWidth / viewportWidth;
+ : Math.round((float) mCurrentViewHeight * data.mWidth / viewportWidth);
data.mTextWrapWidth = Math.round(webViewWidth
/ mInitialViewState.mTextWrapScale);
data.mIgnoreHeight = false;
diff --git a/core/java/android/webkit/ZoomManager.java b/core/java/android/webkit/ZoomManager.java
index 9d472e0..86aef4c 100644
--- a/core/java/android/webkit/ZoomManager.java
+++ b/core/java/android/webkit/ZoomManager.java
@@ -361,6 +361,7 @@
// returns TRUE if zoom out succeeds and FALSE if no zoom changes.
private boolean zoom(float zoomMultiplier) {
+ mInitialZoomOverview = false;
// TODO: alternatively we can disallow this during draw history mode
mWebView.switchOutDrawHistory();
// Center zooming to the center of the screen.
@@ -378,6 +379,7 @@
* @return true if the new scale triggered an animation and false otherwise.
*/
public boolean startZoomAnimation(float scale, boolean reflowText) {
+ mInitialZoomOverview = false;
float oldScale = mActualScale;
mInitialScrollX = mWebView.getScrollX();
mInitialScrollY = mWebView.getScrollY();
@@ -421,6 +423,7 @@
* in progress by calling isFixedLengthAnimationInProgress().
*/
public void animateZoom(Canvas canvas) {
+ mInitialZoomOverview = false;
if (mZoomScale == 0) {
Log.w(LOGTAG, "A WebView is attempting to perform a fixed length "
+ "zoom animation when no zoom is in progress");
@@ -568,6 +571,8 @@
* C. If the page is in overmode then change to the default scale.
*/
public void handleDoubleTap(float lastTouchX, float lastTouchY) {
+ // User takes action, set initial zoom overview to false.
+ mInitialZoomOverview = false;
WebSettings settings = mWebView.getSettings();
if (!isDoubleTapEnabled()) {
return;
@@ -706,6 +711,7 @@
private class ScaleDetectorListener implements ScaleGestureDetector.OnScaleGestureListener {
public boolean onScaleBegin(ScaleGestureDetector detector) {
+ mInitialZoomOverview = false;
dismissZoomPicker();
mWebView.mViewManager.startZoom();
mWebView.onPinchToZoomAnimationStart();
@@ -884,7 +890,7 @@
!exceedsMinScaleIncrement(newZoomOverviewScale, 1.0f);
if (!mWebView.drawHistory() &&
(mInitialZoomOverview || scaleLessThanOverview || mobileSiteInOverview) &&
- scaleHasDiff) {
+ scaleHasDiff && zoomOverviewWidthChanged) {
mInitialZoomOverview = false;
setZoomScale(newZoomOverviewScale, !willScaleTriggerZoom(mTextWrapScale) &&
!mWebView.getSettings().getUseFixedViewport());
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 993af31..d90d5be 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -4403,12 +4403,8 @@
(mCurrentAlpha * Color.alpha(cursorcolor)) / 255);
}
mHighlightPaint.setStyle(Paint.Style.STROKE);
-
- if (mCursorCount > 0) {
- drawCursor = true;
- } else {
- highlight = mHighlightPath;
- }
+ highlight = mHighlightPath;
+ drawCursor = true;
}
} else {
if (mHighlightPathBogus) {
@@ -4491,7 +4487,12 @@
mCorrectionHighlighter.draw(canvas, cursorOffsetVertical);
}
- if (drawCursor) drawCursor(canvas, cursorOffsetVertical);
+ if (drawCursor) {
+ drawCursor(canvas, cursorOffsetVertical);
+ // Rely on the drawable entirely, do not draw the cursor line.
+ // Has to be done after the IMM related code above which relies on the highlight.
+ highlight = null;
+ }
layout.draw(canvas, highlight, mHighlightPaint, cursorOffsetVertical);
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index b3b80f6..1cca21d 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -194,7 +194,7 @@
int mPhoneSignalStrengthBin = -1;
int mPhoneSignalStrengthBinRaw = -1;
final StopwatchTimer[] mPhoneSignalStrengthsTimer =
- new StopwatchTimer[NUM_SIGNAL_STRENGTH_BINS];
+ new StopwatchTimer[SignalStrength.NUM_SIGNAL_STRENGTH_BINS];
StopwatchTimer mPhoneSignalScanningTimer;
@@ -1659,7 +1659,7 @@
}
void stopAllSignalStrengthTimersLocked(int except) {
- for (int i = 0; i < NUM_SIGNAL_STRENGTH_BINS; i++) {
+ for (int i = 0; i < SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
if (i == except) {
continue;
}
@@ -1674,7 +1674,7 @@
// In this case we will always be STATE_OUT_OF_SERVICE, so need
// to infer that we are scanning from other data.
if (state == ServiceState.STATE_OUT_OF_SERVICE
- && signalBin > SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
+ && signalBin > SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
state = ServiceState.STATE_IN_SERVICE;
}
}
@@ -1694,7 +1694,7 @@
// In this case we will always be STATE_OUT_OF_SERVICE, so need
// to infer that we are scanning from other data.
if (state == ServiceState.STATE_OUT_OF_SERVICE
- && bin > SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
+ && bin > SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
state = ServiceState.STATE_IN_SERVICE;
}
}
@@ -1711,7 +1711,7 @@
// bin and have the scanning bit set.
} else if (state == ServiceState.STATE_OUT_OF_SERVICE) {
scanning = true;
- bin = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+ bin = SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
if (!mPhoneSignalScanningTimer.isRunningLocked()) {
mHistoryCur.states |= HistoryItem.STATE_PHONE_SCANNING_FLAG;
newHistory = true;
@@ -1775,24 +1775,7 @@
public void notePhoneSignalStrengthLocked(SignalStrength signalStrength) {
// Bin the strength.
- int bin;
-
- if (!signalStrength.isGsm()) {
- int dBm = signalStrength.getCdmaDbm();
- if (dBm >= -75) bin = SIGNAL_STRENGTH_GREAT;
- else if (dBm >= -85) bin = SIGNAL_STRENGTH_GOOD;
- else if (dBm >= -95) bin = SIGNAL_STRENGTH_MODERATE;
- else if (dBm >= -100) bin = SIGNAL_STRENGTH_POOR;
- else bin = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
- } else {
- int asu = signalStrength.getGsmSignalStrength();
- if (asu < 0 || asu >= 99) bin = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
- else if (asu >= 16) bin = SIGNAL_STRENGTH_GREAT;
- else if (asu >= 8) bin = SIGNAL_STRENGTH_GOOD;
- else if (asu >= 4) bin = SIGNAL_STRENGTH_MODERATE;
- else bin = SIGNAL_STRENGTH_POOR;
- }
-
+ int bin = signalStrength.getLevel();
updateAllPhoneStateLocked(mPhoneServiceStateRaw, mPhoneSimStateRaw, bin);
}
@@ -3903,7 +3886,7 @@
}
mInputEventCounter = new Counter(mUnpluggables);
mPhoneOnTimer = new StopwatchTimer(null, -2, null, mUnpluggables);
- for (int i=0; i<NUM_SIGNAL_STRENGTH_BINS; i++) {
+ for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
mPhoneSignalStrengthsTimer[i] = new StopwatchTimer(null, -200-i, null, mUnpluggables);
}
mPhoneSignalScanningTimer = new StopwatchTimer(null, -200+1, null, mUnpluggables);
@@ -4017,7 +4000,7 @@
mPhoneOnTimer.reset(this, false);
mAudioOnTimer.reset(this, false);
mVideoOnTimer.reset(this, false);
- for (int i=0; i<NUM_SIGNAL_STRENGTH_BINS; i++) {
+ for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
mPhoneSignalStrengthsTimer[i].reset(this, false);
}
mPhoneSignalScanningTimer.reset(this, false);
@@ -4780,7 +4763,7 @@
mInputEventCounter.readSummaryFromParcelLocked(in);
mPhoneOn = false;
mPhoneOnTimer.readSummaryFromParcelLocked(in);
- for (int i=0; i<NUM_SIGNAL_STRENGTH_BINS; i++) {
+ for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
mPhoneSignalStrengthsTimer[i].readSummaryFromParcelLocked(in);
}
mPhoneSignalScanningTimer.readSummaryFromParcelLocked(in);
@@ -4973,7 +4956,7 @@
}
mInputEventCounter.writeSummaryFromParcelLocked(out);
mPhoneOnTimer.writeSummaryFromParcelLocked(out, NOWREAL);
- for (int i=0; i<NUM_SIGNAL_STRENGTH_BINS; i++) {
+ for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
mPhoneSignalStrengthsTimer[i].writeSummaryFromParcelLocked(out, NOWREAL);
}
mPhoneSignalScanningTimer.writeSummaryFromParcelLocked(out, NOWREAL);
@@ -5172,7 +5155,7 @@
mInputEventCounter = new Counter(mUnpluggables, in);
mPhoneOn = false;
mPhoneOnTimer = new StopwatchTimer(null, -2, null, mUnpluggables, in);
- for (int i=0; i<NUM_SIGNAL_STRENGTH_BINS; i++) {
+ for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
mPhoneSignalStrengthsTimer[i] = new StopwatchTimer(null, -200-i,
null, mUnpluggables, in);
}
@@ -5285,7 +5268,7 @@
}
mInputEventCounter.writeToParcel(out);
mPhoneOnTimer.writeToParcel(out, batteryRealtime);
- for (int i=0; i<NUM_SIGNAL_STRENGTH_BINS; i++) {
+ for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
mPhoneSignalStrengthsTimer[i].writeToParcel(out, batteryRealtime);
}
mPhoneSignalScanningTimer.writeToParcel(out, batteryRealtime);
@@ -5382,7 +5365,7 @@
mInputEventCounter.logState(pr, " ");
pr.println("*** Phone timer:");
mPhoneOnTimer.logState(pr, " ");
- for (int i=0; i<NUM_SIGNAL_STRENGTH_BINS; i++) {
+ for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
pr.println("*** Signal strength #" + i + ":");
mPhoneSignalStrengthsTimer[i].logState(pr, " ");
}
diff --git a/core/java/com/android/internal/view/menu/MenuPopupHelper.java b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
index 6c9e7bb..04a059e 100644
--- a/core/java/com/android/internal/view/menu/MenuPopupHelper.java
+++ b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
@@ -36,14 +36,15 @@
* @hide
*/
public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.OnKeyListener,
- ViewTreeObserver.OnGlobalLayoutListener, PopupWindow.OnDismissListener {
+ ViewTreeObserver.OnGlobalLayoutListener, PopupWindow.OnDismissListener,
+ View.OnAttachStateChangeListener {
private static final String TAG = "MenuPopupHelper";
private Context mContext;
private ListPopupWindow mPopup;
private MenuBuilder mMenu;
private int mPopupMaxWidth;
- private WeakReference<View> mAnchorView;
+ private View mAnchorView;
private boolean mOverflowOnly;
private ViewTreeObserver mTreeObserver;
@@ -66,13 +67,11 @@
final DisplayMetrics metrics = context.getResources().getDisplayMetrics();
mPopupMaxWidth = metrics.widthPixels / 2;
- if (anchorView != null) {
- mAnchorView = new WeakReference<View>(anchorView);
- }
+ mAnchorView = anchorView;
}
public void setAnchorView(View anchor) {
- mAnchorView = new WeakReference<View>(anchor);
+ mAnchorView = anchor;
}
public void show() {
@@ -92,19 +91,19 @@
mPopup.setAdapter(adapter);
mPopup.setModal(true);
- View anchor = mAnchorView != null ? mAnchorView.get() : null;
+ View anchor = mAnchorView;
if (anchor == null && mMenu instanceof SubMenuBuilder) {
SubMenuBuilder subMenu = (SubMenuBuilder) mMenu;
final MenuItemImpl itemImpl = (MenuItemImpl) subMenu.getItem();
anchor = itemImpl.getItemView(MenuBuilder.TYPE_ACTION_BUTTON, null);
- mAnchorView = new WeakReference<View>(anchor);
+ mAnchorView = anchor;
}
if (anchor != null) {
- if (mTreeObserver == null) {
- mTreeObserver = anchor.getViewTreeObserver();
- mTreeObserver.addOnGlobalLayoutListener(this);
- }
+ final boolean addGlobalListener = mTreeObserver == null;
+ mTreeObserver = anchor.getViewTreeObserver(); // Refresh to latest
+ if (addGlobalListener) mTreeObserver.addOnGlobalLayoutListener(this);
+ anchor.addOnAttachStateChangeListener(this);
mPopup.setAnchorView(anchor);
} else {
return false;
@@ -125,10 +124,12 @@
public void onDismiss() {
mPopup = null;
- if (mTreeObserver != null && mTreeObserver.isAlive()) {
+ if (mTreeObserver != null) {
+ if (!mTreeObserver.isAlive()) mTreeObserver = mAnchorView.getViewTreeObserver();
mTreeObserver.removeGlobalOnLayoutListener(this);
+ mTreeObserver = null;
}
- mTreeObserver = null;
+ mAnchorView.removeOnAttachStateChangeListener(this);
}
public boolean isShowing() {
@@ -187,13 +188,8 @@
@Override
public void onGlobalLayout() {
- if (!isShowing()) {
- if (mTreeObserver.isAlive()) {
- mTreeObserver.removeGlobalOnLayoutListener(this);
- }
- mTreeObserver = null;
- } else {
- final View anchor = mAnchorView != null ? mAnchorView.get() : null;
+ if (isShowing()) {
+ final View anchor = mAnchorView;
if (anchor == null || !anchor.isShown()) {
dismiss();
} else if (isShowing()) {
@@ -202,4 +198,17 @@
}
}
}
+
+ @Override
+ public void onViewAttachedToWindow(View v) {
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(View v) {
+ if (mTreeObserver != null) {
+ if (!mTreeObserver.isAlive()) mTreeObserver = v.getViewTreeObserver();
+ mTreeObserver.removeGlobalOnLayoutListener(this);
+ }
+ v.removeOnAttachStateChangeListener(this);
+ }
}
diff --git a/core/jni/android_net_TrafficStats.cpp b/core/jni/android_net_TrafficStats.cpp
index dfa36ce..0c84f11 100644
--- a/core/jni/android_net_TrafficStats.cpp
+++ b/core/jni/android_net_TrafficStats.cpp
@@ -130,6 +130,33 @@
"/sys/class/net/ppp0/statistics/rx_bytes");
}
+static jlong getData(JNIEnv* env, char *what, jstring interface) {
+ char filename[80];
+ jboolean isCopy;
+
+ const char *interfaceStr = env->GetStringUTFChars(interface, &isCopy);
+ snprintf(filename, sizeof(filename), "/sys/class/net/%s/statistics/%s", interfaceStr, what);
+
+ return readNumber(filename);
+}
+
+static jlong getTxPackets(JNIEnv* env, jobject clazz, jstring interface) {
+ return getData(env, "tx_packets", interface);
+}
+
+static jlong getRxPackets(JNIEnv* env, jobject clazz, jstring interface) {
+ return getData(env, "rx_packets", interface);
+}
+
+static jlong getTxBytes(JNIEnv* env, jobject clazz, jstring interface) {
+ return getData(env, "tx_bytes", interface);
+}
+
+static jlong getRxBytes(JNIEnv* env, jobject clazz, jstring interface) {
+ return getData(env, "rx_bytes", interface);
+}
+
+
// Total stats are read less often, so we're willing to put up
// with listing the directory and concatenating filenames.
@@ -288,6 +315,10 @@
{"getMobileRxPackets", "()J", (void*) getMobileRxPackets},
{"getMobileTxBytes", "()J", (void*) getMobileTxBytes},
{"getMobileRxBytes", "()J", (void*) getMobileRxBytes},
+ {"getTxPackets", "(Ljava/lang/String;)J", (void*) getTxPackets},
+ {"getRxPackets", "(Ljava/lang/String;)J", (void*) getRxPackets},
+ {"getTxBytes", "(Ljava/lang/String;)J", (void*) getTxBytes},
+ {"getRxBytes", "(Ljava/lang/String;)J", (void*) getRxBytes},
{"getTotalTxPackets", "()J", (void*) getTotalTxPackets},
{"getTotalRxPackets", "()J", (void*) getTotalRxPackets},
{"getTotalTxBytes", "()J", (void*) getTotalTxBytes},
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 8cc5944..72f1801 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -589,7 +589,7 @@
</style>
<style name="Widget.ListView.White" parent="Widget.AbsListView">
- <item name="android:listSelector">@android:drawable/list_selector_background_light</item>
+ <item name="android:listSelector">@android:drawable/list_selector_background</item>
<item name="android:cacheColorHint">?android:attr/colorBackgroundCacheHint</item>
<item name="android:divider">@android:drawable/divider_horizontal_bright_opaque</item>
</style>
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index 927a668..82164e5 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -105,7 +105,7 @@
<item name="listChoiceIndicatorSingle">@android:drawable/btn_radio</item>
<item name="listChoiceIndicatorMultiple">@android:drawable/btn_check</item>
- <item name="listChoiceBackgroundIndicator">@android:drawable/list_selected_background</item>
+ <item name="listChoiceBackgroundIndicator">@android:drawable/list_selector_background</item>
<item name="activatedBackgroundIndicator">@android:drawable/activated_background</item>
@@ -358,7 +358,7 @@
<item name="textColorLinkInverse">@android:color/link_text_dark</item>
<item name="editTextColor">?android:attr/textColorPrimary</item>
- <item name="listChoiceBackgroundIndicator">@android:drawable/list_selected_background_light</item>
+ <item name="listChoiceBackgroundIndicator">@android:drawable/list_selector_background</item>
<item name="activatedBackgroundIndicator">@android:drawable/activated_background_light</item>
<item name="quickContactBadgeOverlay">@android:drawable/quickcontact_badge_overlay_light</item>
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java
index fb8b5ce..7756135 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java
@@ -30,7 +30,9 @@
import android.util.Log;
import android.view.KeyEvent;
+import java.io.IOException;
import java.io.InputStream;
+import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import android.widget.LinearLayout;
@@ -485,6 +487,44 @@
}
/**
+ * @param pingServerList a list of servers that can be used for ping test, can be null
+ * @return true if the ping test is successful, false otherwise.
+ */
+ public boolean pingTest(String[] pingServerList) {
+ boolean result = false;
+ String[] hostList = {"www.google.com", "www.yahoo.com",
+ "www.bing.com", "www.facebook.com", "www.ask.com"};
+ if (pingServerList != null) {
+ hostList = pingServerList;
+ }
+ try {
+ // assume the chance that all servers are down is very small
+ for (int i = 0; i < hostList.length; i++ ) {
+ String host = hostList[i];
+ log("Start ping test, ping " + host);
+ Process p = Runtime.getRuntime().exec("ping -c 10 -w 100 " + host);
+ int status = p.waitFor();
+ if (status == 0) {
+ // if any of the ping test is successful, return true
+ result = true;
+ break;
+ } else {
+ result = false;
+ log("ping " + host + " failed.");
+ }
+ }
+ } catch (UnknownHostException e) {
+ log("Ping test Fail: Unknown Host");
+ } catch (IOException e) {
+ log("Ping test Fail: IOException");
+ } catch (InterruptedException e) {
+ log("Ping test Fail: InterruptedException");
+ }
+ log("return");
+ return result;
+ }
+
+ /**
* Associate the device to given SSID
* If the device is already associated with a WiFi, disconnect and forget it,
* We don't verify whether the connection is successful or not, leave this to the test
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiApStress.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiApStress.java
index 4457de9..1374e7f 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiApStress.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiApStress.java
@@ -107,6 +107,17 @@
} catch (Exception e) {
fail("thread in sleep is interrupted");
}
+ assertTrue("no uplink data connection after Wi-Fi tethering", mAct.pingTest(null));
+ // Wait for 5 minutes, and verify the data connection again.
+ // bug id: 3400027
+ try {
+ Thread.sleep(5 * 60 * 1000);
+ } catch (Exception e) {
+ fail("thread in sleep is interrupted");
+ }
+ // Verify the uplink data connection
+ assertTrue("no uplink data connection", mAct.pingTest(null));
+ // Disable soft AP
assertTrue(mAct.mWifiManager.setWifiApEnabled(config, false));
// Wait for 30 seconds until Wi-Fi tethering is stopped
try {
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiStressTest.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiStressTest.java
index ae009ca6..2f2a283 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiStressTest.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiStressTest.java
@@ -243,6 +243,9 @@
ConnectivityManagerTestActivity.SHORT_TIMEOUT));
assertTrue(mAct.waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED,
ConnectivityManagerTestActivity.LONG_TIMEOUT));
+ // Run ping test to verify the data connection
+ assertTrue("Wi-Fi is connected, but no data connection.", mAct.pingTest(null));
+
int i;
for (i = 0; i < mReconnectIterations; i++) {
// 1. Put device into sleep mode
@@ -271,6 +274,9 @@
mAct.mCM.getNetworkInfo(ConnectivityManager.TYPE_WIFI).getState());
assertEquals("Cellular connection is down", State.CONNECTED,
mAct.mCM.getNetworkInfo(ConnectivityManager.TYPE_MOBILE).getState());
+
+ assertTrue("Mobile is connected, but no data connection.", mAct.pingTest(null));
+
// Turn screen on again
mAct.turnScreenOn();
assertTrue("Wait for Wi-Fi enable timeout after wake up",
@@ -279,6 +285,7 @@
assertTrue("Wait for Wi-Fi connection timeout after wake up",
mAct.waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED,
ConnectivityManagerTestActivity.LONG_TIMEOUT));
+ assertTrue("Reconnect to Wi-Fi network, but no data connection.", mAct.pingTest(null));
}
if (i == mReconnectIterations) {
writeOutput(String.format("iteration %d out of %d",
diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteCursorTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteCursorTest.java
index 963c8ed..f6b1d04 100644
--- a/core/tests/coretests/src/android/database/sqlite/SQLiteCursorTest.java
+++ b/core/tests/coretests/src/android/database/sqlite/SQLiteCursorTest.java
@@ -22,7 +22,6 @@
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.LargeTest;
import android.test.suitebuilder.annotation.SmallTest;
-import android.test.suitebuilder.annotation.Suppress;
import android.util.Log;
import java.io.File;
@@ -55,7 +54,6 @@
super.tearDown();
}
- @Suppress
@SmallTest
public void testQueryObjReassignment() {
mDatabase.enableWriteAheadLogging();
diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java
index 4516510..39258ae 100644
--- a/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java
+++ b/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java
@@ -74,7 +74,6 @@
mDatabase.setVersion(CURRENT_DATABASE_VERSION);
}
- @Suppress
@SmallTest
public void testEnableWriteAheadLogging() {
mDatabase.disableWriteAheadLogging();
@@ -87,7 +86,6 @@
assertEquals(pool, mDatabase.mConnectionPool);
}
- @Suppress
@SmallTest
public void testDisableWriteAheadLogging() {
mDatabase.execSQL("create table test (i int);");
@@ -104,7 +102,6 @@
assertFalse(db.isOpen());
}
- @Suppress
@SmallTest
public void testCursorsWithClosedDbConnAfterDisableWriteAheadLogging() {
mDatabase.disableWriteAheadLogging();
@@ -141,7 +138,6 @@
/**
* a transaction should be started before a standalone-update/insert/delete statement
*/
- @Suppress
@SmallTest
public void testStartXactBeforeUpdateSql() throws InterruptedException {
runTestForStartXactBeforeUpdateSql(INSERT);
@@ -753,7 +749,6 @@
*
* @throws InterruptedException
*/
- @Suppress
@SmallTest
public void testTransactionAndWalInterplay1() throws InterruptedException {
createTableAndClearCache();
@@ -812,7 +807,6 @@
* instead of mDatabase.beginTransactionNonExclusive(), use execSQL("BEGIN transaction")
* and instead of mDatabase.endTransaction(), use execSQL("END");
*/
- @Suppress
@SmallTest
public void testTransactionAndWalInterplay2() throws InterruptedException {
createTableAndClearCache();
@@ -869,7 +863,6 @@
* instead of committing the data, do rollback and make sure the data seen by the query
* within the transaction is now gone.
*/
- @Suppress
@SmallTest
public void testTransactionAndWalInterplay3() {
createTableAndClearCache();
diff --git a/docs/html/guide/developing/tools/aidl.jd b/docs/html/guide/developing/tools/aidl.jd
index d3da285..731aef7 100644
--- a/docs/html/guide/developing/tools/aidl.jd
+++ b/docs/html/guide/developing/tools/aidl.jd
@@ -1,4 +1,4 @@
-page.title=Designing a Remote Interface Using AIDL
+page.title=Android Interface Definition Language (AIDL)
@jd:body
@@ -6,209 +6,367 @@
<div id="qv">
<h2>In this document</h2>
<ol>
- <li><a href="#implementing">Implementing IPC Using AIDL</a>
+ <li><a href="#Defining">Defining an AIDL Interface</a>
<ol>
- <li><a href="#aidlsyntax">Create an .aidl File</a></li>
- <li><a href="#implementtheinterface">Implementing the Interface</a></li>
- <li><a href="#exposingtheinterface">Exposing Your Interface to Clients</a></li>
- <li><a href="#parcelable">Pass by value Parameters using Parcelables</a></li>
+ <li><a href="#Create">Create the .aidl file</a></li>
+ <li><a href="#Implement">Implement the interface</a></li>
+ <li><a href="#Expose">Expose the interface to clients</a></li>
</ol>
</li>
- <li><a href="#calling">Calling an IPC Method</a></li>
+ <li><a href="#PassingObjects">Passing Objects over IPC</a></li>
+ <li><a href="#Calling">Calling an IPC Method</a></li>
</ol>
-</div>
-</div>
-
-<p>Since each application runs in its own process, and you can write a service that
-runs in a different process from your Application's UI, sometimes you need to pass objects
-between processes. On the Android platform, one process can not normally access the memory
-of another process. So to talk, they need to decompose their objects into primitives that
-the operating system can understand, and "marshall" the object across that boundary for you.</p>
-
-<p>The code to do that marshalling is tedious to write, so we provide the AIDL tool to do it
-for you.</p>
-
-<p>AIDL (Android Interface Definition Language) is an <a
-href="http://en.wikipedia.org/wiki/Interface_description_language">IDL</a>
-language used to generate code that enables two processes on an Android-powered device
-to talk using interprocess communication (IPC). If you have code
-in one process (for example, in an Activity) that needs to call methods on an
-object in another process (for example, a Service), you would use AIDL to
-generate code to marshall the parameters.</p>
-<p>The AIDL IPC mechanism
- is interface-based, similar to COM or Corba, but lighter weight. It uses a proxy
- class to pass values between the client and the implementation. </p>
-
-
-<h2 id="implementing">Implementing IPC Using AIDL</h2>
-<p>Follow these steps to implement an IPC service using AIDL.</p>
+<h2>See also</h2>
<ol>
- <li><strong><a href="#aidlsyntax">Create your .aidl file</a> </strong>- This
- file defines an interface (<em>YourInterface</em>.aidl) that defines the
- methods and fields available to a client. </li>
- <li><strong>Add the .aidl file to your makefile</strong> - (the ADT Plugin for Eclipse
- manages this for you). Android includes the compiler, called
- AIDL, in the <code>tools/</code> directory. </li>
- <li><strong><a href="#implementtheinterface">Implement your interface methods</a></strong> -
- The AIDL compiler creates an interface in the Java programming language from your AIDL interface.
- This interface has an inner abstract class named Stub that inherits the
- interface (and implements a few additional methods necessary for the IPC
- call). You must create a class that extends <em>YourInterface</em>.Stub
- and implements the methods you declared in your .aidl file. </li>
- <li><strong><a href="#exposingtheinterface">Expose your interface to clients</a></strong> -
- If you're writing a service, you should extend {@link
- android.app.Service Service} and override {@link android.app.Service#onBind
- Service.onBind(Intent)} to return an instance of your class that implements your
- interface. </li>
+ <li><a href="{@docRoot}guide/topics/fundamentals/bound-services.html">Bound Services</a></li>
</ol>
-<h3 id="aidlsyntax">Create an .aidl File</h3>
-<p>AIDL is a simple syntax that lets you declare an interface with one or more
- methods, that can take parameters and return values. These parameters and return
- values can be of any type, even other AIDL-generated interfaces. <em>However, it
- is important to note</em> that you <em>must</em> import all non-built-in types,
- <em>even if they are defined in the same package as your interface</em>.
- Here are the data types that AIDL can support: </p>
+</div>
+</div>
+
+
+<p>AIDL (Android Interface Definition Language) is similar to other IDLs you might have
+worked with. It allows you to define the programming interface that both
+the client and service agree upon in order to communicate with each other using
+interprocess communication (IPC). On Android, one process cannot normally access the
+memory of another process. So to talk, they need to decompose their objects into primitives that the
+operating system can understand, and marshall the objects across that boundary for you. The code to
+do that marshalling is tedious to write, so Android handles it for you with AIDL.</p>
+
+<p class="note"><strong>Note:</strong> Using AIDL is necessary only if you allow clients from
+different applications to access your service for IPC and want to handle multithreading in your
+service. If you do not need to perform concurrent IPC across
+different applications, you should create your interface by <a
+href="{@docRoot}guide/topics/fundamentals/bound-services.html#Binder">implementing a
+Binder</a> or, if you want to perform IPC, but do <em>not</em> need to handle multithreading,
+implement your interface <a
+href="{@docRoot}guide/topics/fundamentals/bound-services.html#Messenger">using a Messenger</a>.
+Regardless, be sure that you understand <a
+href="{@docRoot}guide/topics/fundamentals/bound-services.html">Bound Services</a> before
+implementing an AIDL.</p>
+
+<p>Before you begin designing your AIDL interface, be aware that calls to an AIDL interface are
+direct function calls. You should not make assumptions about the thread in which the call
+occurs. What happens is different depending on whether the call is from a thread in the
+local process or a remote process. Specifically:</p>
+
<ul>
- <li>Primitive Java programming language types (int, boolean, etc)
- — No <code>import</code> statement is needed. </li>
- <li>One of the following classes (no <code>import</code> statements needed):
- <ul>
- <li><strong>String</strong></li>
- <li><strong>List</strong> - All elements in the List must be one of the types
- in this list, including other AIDL-generated interfaces and
- parcelables. List may optionally be used as a "generic" class (e.g.
- List<String>).
- The actual concrete class that the other side will receive
- will always be an ArrayList, although the method will be generated
- to use the List interface. </li>
- <li><strong>Map</strong> - All elements in the Map must be of one of the
- types in this list, including other AIDL-generated interfaces and
- parcelables. Generic maps, (e.g. of the form Map<String,Integer>
- are not supported.
- The actual concrete class that the other side will receive
- will always be a HashMap, although the method will be generated
- to use the Map interface.</li>
- <li><strong>CharSequence</strong> - This is useful for the CharSequence
- types used by {@link android.widget.TextView TextView} and other
- widget objects. </li>
- </ul>
- </li>
- <li>Other AIDL-generated interfaces, which are always passed by reference.
- An <code>import</code> statement is always needed for these.</li>
- <li>Custom classes that implement the <a href="#parcelable">Parcelable
- protocol</a> and are passed by value.
- An <code>import</code> statement is always needed for these.</li>
+<li>Calls made from the local process are executed in the same thread that is making the call. If
+this is your main UI thread, that thread continues to execute in the AIDL interface. If it is
+another thread, that is the one that executes your code in the service. Thus, if only local
+threads are accessing the service, you can completely control which threads are executing in it (but
+if that is the case, then you shouldn't be using AIDL at all, but should instead create the
+interface by <a href="{@docRoot}guide/topics/fundamentals/bound-services.html#Binder">implementing a
+Binder</a>).</li>
+
+<li>Calls from a remote process are dispatched from a thread pool the platform maintains inside of
+your own process. You must be prepared for incoming calls from unknown threads, with multiple calls
+happening at the same time. In other words, an implementation of an AIDL interface must be
+completely thread-safe.</li>
+
+<li>The {@code oneway} keyword modifies the behavior of remote calls. When used, a remote call does
+not block; it simply sends the transaction data and immediately returns.
+The implementation of the interface eventually receives this as a regular call from the {@link
+android.os.Binder} thread pool as a normal remote call. If {@code oneway} is used with a local call,
+there is no impact and the call is still synchronous.</li>
</ul>
-<p>Here is the basic AIDL syntax:</p>
-<pre>// My AIDL file, named <em>SomeClass</em>.aidl
-// Note that standard comment syntax is respected.
-// Comments before the import or package statements are not bubbled up
-// to the generated interface, but comments above interface/method/field
-// declarations are added to the generated interface.
-// Include your fully-qualified package statement.
-package com.android.sample;
-// See the list above for which classes need
-// import statements (hint--most of them)
-import com.android.sample.IAtmService;
+<h2 id="Defining">Defining an AIDL Interface</h2>
-// Declare the interface.
-interface IBankAccountService {
-
- // Methods can take 0 or more parameters, and
- // return a value or void.
- int getAccountBalance();
- void setOwnerNames(in List<String> names);
-
- // Methods can even take other AIDL-defined parameters.
- BankAccount createAccount(in String name, int startingDeposit, in IAtmService atmService);
+<p>You must define your AIDL interface in an {@code .aidl} file using the Java
+programming language syntax, then save it in the source code (in the {@code src/} directory) of both
+the application hosting the service and any other application that binds to the service.</p>
- // All non-Java primitive parameters (e.g., int, bool, etc) require
- // a directional tag indicating which way the data will go. Available
- // values are in, out, inout. (Primitives are in by default, and cannot be otherwise).
- // Limit the direction to what is truly needed, because marshalling parameters
- // is expensive.
- int getCustomerList(in String branch, out String[] customerList);
-}</pre>
+<p>When you build each application that contains the {@code .aidl} file, the Android SDK tools
+generate an {@link android.os.IBinder} interface based on the {@code .aidl} file and save it in
+the project's {@code gen/} directory. The service must implement the {@link android.os.IBinder}
+interface as appropriate. The client applications can then bind to the service and call methods from
+the {@link android.os.IBinder} to perform IPC.</p>
-<h3 id="implementtheinterface">Implementing the Interface</h3>
-<p>AIDL generates an interface file for you with the same name as your .aidl
- file. If you are using the Eclipse plugin, AIDL will automatically be run as part of
- the build process (you don't need to run AIDL first and then build your project).
- If you are not using the plugin, you should run AIDL first. </p>
-<p>The generated interface
- includes an abstract inner class named Stub that declares all the methods
- that you declared in your .aidl file. Stub also defines a few helper methods,
- most notably asInterface(), which takes an IBinder (passed to a client's onServiceConnected()
- implementation when applicationContext.bindService() succeeds), and returns an
- instance of the interface used to call the IPC methods. See the section
- <a href="#calling">Calling an IPC Method</a> for more details on how to make this cast.</p>
-<p>To implement your interface, extend <em>YourInterface</em>.Stub,
- and implement the methods. (You can create the .aidl file and implement the stub
- methods without building between--the Android build process will process .aidl
-files before .java files.) </p>
-<p>Here is an example of implementing an interface called IRemoteService, which exposes
- a single method, getPid(), using an anonymous instance:</p>
-<pre>// No need to import IRemoteService if it's in the same project.
-private final IRemoteService.Stub mBinder = new IRemoteService.Stub(){
+<p>To create a bounded service using AIDL, follow these steps:</p>
+<ol>
+ <li><a href="#CreateAidl">Create the .aidl file</a>
+ <p>This file defines the programming interface with method signatures.</p>
+ </li>
+ <li><a href="#ImplementTheInterface">Implement the interface</a>
+ <p>The Android SDK tools generate an interface in the Java programming language, based on your
+{@code .aidl} file. This interface has an inner abstract class named {@code Stub} that extends
+{@link android.os.Binder} and implements methods from your AIDL interface. You must extend the
+{@code Stub} class and implement the methods.</p>
+ </li>
+ <li><a href="#ExposeTheInterface">Expose the interface to clients</a>
+ <p>Implement a {@link android.app.Service Service} and override {@link
+android.app.Service#onBind onBind()} to return your implementation of the {@code Stub}
+class.</p>
+ </li>
+</ol>
+
+<p class="caution"><strong>Caution:</strong> Any changes that you make to your AIDL interface after
+your first release must remain backward compatible in order to avoid breaking other applications
+that use your service. That is, because your {@code .aidl} file must be copied to other applications
+in order for them to access your service's interface, you must maintain support for the original
+interface.</p>
+
+
+<h3 id="Create">1. Create the .aidl file</h3>
+
+<p>AIDL uses a simple syntax that lets you declare an interface with one or more methods that can
+take parameters and return values. The parameters and return values can be of any type, even other
+AIDL-generated interfaces.</p>
+
+<p>You must construct the {@code .aidl} file using the Java programming language. Each {@code .aidl}
+file must define a single interface and requires only the interface declaration and method
+signatures.</p>
+
+<p>By default, AIDL supports the following data types:</p>
+
+<ul>
+ <li>All primitive types in the Java programming language (such as {@code int}, {@code long},
+{@code char}, {@code boolean}, and so on)</li>
+ <li>{@link java.lang.String}</li>
+ <li>{@link java.lang.CharSequence}</li>
+ <li>{@link java.util.List}
+ <p>All elements in the {@link java.util.List} must be one of the supported data types in this
+list or one of the other AIDL-generated interfaces or parcelables you've declared. A {@link
+java.util.List} may optionally be used as a "generic" class (for example,
+<code>List<String></code>).
+The actual concrete class that the other side receives is always an {@link
+java.util.ArrayList}, although the method is generated to use the {@link
+java.util.List} interface.</p>
+ </li>
+ <li>{@link java.util.Map}
+ <p>All elements in the {@link java.util.Map} must be one of the supported data types in this
+list or one of the other AIDL-generated interfaces or parcelables you've declared. Generic maps,
+(such as those of the form
+{@code Map<String,Integer>} are not supported. The actual concrete class that the other side
+receives is always a {@link java.util.HashMap}, although the method is generated to
+use the {@link java.util.Map} interface.</p>
+ </li>
+</ul>
+
+<p>You must include an {@code import} statement for each additional type not listed above, even if
+they are defined in the same package as your interface.</p>
+
+<p>When defining your service interface, be aware that:</p>
+<ul>
+ <li>Methods can take zero or more parameters, and return a value or void.</li>
+ <li>All non-primitive parameters require a directional tag indicating which way the data goes.
+Either <code>in</code>, <code>out</code>, or <code>inout</code> (see the example below).
+ <p>Primitives are <code>in</code> by default, and cannot be otherwise.</p>
+ <p class="caution"><strong>Caution:</strong> You should limit the direction to what is truly
+needed, because marshalling parameters is expensive.</p></li>
+ <li>All code comments included in the {@code .aidl} file are included in the generated {@link
+android.os.IBinder} interface (except for comments before the import and package
+statements).</li>
+ <li>Only methods are supported; you cannot expose static fields in AIDL.</li>
+</ul>
+
+<p>Here is an example {@code .aidl} file:</p>
+
+<pre>
+// IRemoteService.aidl
+package com.example.android;
+
+// Declare any non-default types here with import statements
+
+/** Example service interface */
+interface IRemoteService {
+ /** Request the process ID of this service, to do evil things with it. */
+ int getPid();
+
+ /** Demonstrates some basic types that you can use as parameters
+ * and return values in AIDL.
+ */
+ void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
+ double aDouble, String aString);
+}
+</pre>
+
+<p>Simply save your {@code .aidl} file in your project's {@code src/} directory and when you
+build your application, the SDK tools generate the {@link android.os.IBinder} interface file in your
+project's {@code gen/} directory. The generated file name matches the {@code .aidl} file name, but
+with a {@code .java} extension (for example, {@code IRemoteService.aidl} results in {@code
+IRemoteService.java}).</p>
+
+<p>If you use Eclipse, the incremental build generates the binder class almost immediately. If you
+do not use Eclipse, then the Ant tool generates the binder class next time you build your
+application—you should build your project with <code>ant debug</code> (or <code>ant
+release</code>) as soon as you're finished writing the {@code .aidl} file, so that your code can
+link against the generated class.</p>
+
+
+<h3 id="Implement">2. Implement the interface</h3>
+
+<p>When you build your application, the Android SDK tools generate a {@code .java} interface file
+named after your {@code .aidl} file. The generated interface includes a subclass named {@code Stub}
+that is an abstract implementation of its parent interface (for example, {@code
+YourInterface.Stub}) and declares all the methods from the {@code .aidl} file.</p>
+
+<p class="note"><strong>Note:</strong> {@code Stub} also
+defines a few helper methods, most notably {@code asInterface()}, which takes an {@link
+android.os.IBinder} (usually the one passed to a client's {@link
+android.content.ServiceConnection#onServiceConnected onServiceConnected()} callback method) and
+returns an instance of the stub interface. See the section <a href="#calling">Calling an IPC
+Method</a> for more details on how to make this cast.</p>
+
+<p>To implement the interface generated from the {@code .aidl}, extend the generated {@link
+android.os.Binder} interface (for example, {@code YourInterface.Stub}) and implement the methods
+inherited from the {@code .aidl} file.</p>
+
+<p>Here is an example implementation of an interface called {@code IRemoteService} (defined by the
+{@code IRemoteService.aidl} example, above) using an anonymous instance:</p>
+
+<pre>
+private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
public int getPid(){
return Process.myPid();
}
-}</pre>
-<p>A few rules about implementing your interface: </p>
+ public void basicTypes(int anInt, long aLong, boolean aBoolean,
+ float aFloat, double aDouble, String aString) {
+ // Does nothing
+ }
+};
+</pre>
+
+<p>Now the {@code mBinder} is an instance of the {@code Stub} class (a {@link android.os.Binder}),
+which defines the RPC interface for the service. In the next step, this instance is exposed to
+clients so they can interact with the service.</p>
+
+<p>There are a few rules you should be aware of when implementing your AIDL interface: </p>
<ul>
- <li>No exceptions that you throw will be sent back to the caller.</li>
- <li>By default, IPC calls are synchronous. If you know that an IPC service takes more than
- a few milliseconds to complete, you should not call it in the Activity/View thread,
- because it might hang the application (Android might display an "Application
- is Not Responding" dialog).
- Try to call them in a separate thread. </li>
- <li>Only methods are supported; you cannot declare static fields in an AIDL interface.</li>
+ <li>Incoming calls are not guaranteed to be executed on the main thread, so you need to think
+about multithreading from the start and properly build your service to be thread-safe.</li>
+ <li>By default, RPC calls are synchronous. If you know that the service takes more than a few
+milliseconds to complete a request, you should not call it from the activity's main thread, because
+it might hang the application (Android might display an "Application is Not Responding"
+dialog)—you should usually call them from a separate thread in the client. </li>
+ <li>No exceptions that you throw are sent back to the caller.</li>
</ul>
-<h3 id="exposingtheinterface">Exposing Your Interface to Clients</h3>
-<p>Now that you've got your interface implementation, you need to expose it to clients.
- This is known as "publishing your service." To publish a service,
- inherit {@link android.app.Service Service} and implement {@link android.app.Service#onBind
- Service.onBind(Intent)} to return an instance of the class that implements your interface.
- Here's a code snippet of a service that exposes the IRemoteService
- interface to clients. </p>
-<pre>public class RemoteService extends Service {
-...
-{@include development/samples/ApiDemos/src/com/example/android/apis/app/RemoteService.java
- exposing_a_service}
-}</pre>
+
+<h3 id="Expose">3. Expose the interface to clients</h3>
+
+<p>Once you've implemented the interface for your service, you need to expose it to
+clients so they can bind to it. To expose the interface
+for your service, extend {@link android.app.Service Service} and implement {@link
+android.app.Service#onBind onBind()} to return an instance of your class that implements
+the generated {@code Stub} (as discussed in the previous section). Here's an example
+service that exposes the {@code IRemoteService} example interface to clients. </p>
+
+<pre>
+public class RemoteService extends Service {
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ // Return the interface
+ return mBinder;
+ }
+
+ private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
+ public int getPid(){
+ return Process.myPid();
+ }
+ public void basicTypes(int anInt, long aLong, boolean aBoolean,
+ float aFloat, double aDouble, String aString) {
+ // Does nothing
+ }
+ };
+}
+</pre>
+
+<p>Now, when a client (such as an activity) calls {@link android.content.Context#bindService
+bindService()} to connect to this service, the client's {@link
+android.content.ServiceConnection#onServiceConnected onServiceConnected()} callback receives the
+{@code mBinder} instance returned by the service's {@link android.app.Service#onBind onBind()}
+method.</p>
+
+<p>The client must also have access to the interface class, so if the client and service are in
+separate applications, then the client's application must have a copy of the {@code .aidl} file
+in its {@code src/} directory (which generates the {@code android.os.Binder}
+interface—providing the client access to the AIDL methods).</p>
+
+<p>When the client receives the {@link android.os.IBinder} in the {@link
+android.content.ServiceConnection#onServiceConnected onServiceConnected()} callback, it must call
+<code><em>YourServiceInterface</em>.Stub.asInterface(service)</code> to cast the returned
+parameter to <code><em>YourServiceInterface</em></code> type. For example:</p>
+
+<pre>
+IRemoteService mIRemoteService;
+private ServiceConnection mConnection = new ServiceConnection() {
+ // Called when the connection with the service is established
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ // Following the example above for an AIDL interface,
+ // this gets an instance of the IRemoteInterface, which we can use to call on the service
+ mIRemoteService = IRemoteService.Stub.asInterface(service);
+ }
+
+ // Called when the connection with the service disconnects unexpectedly
+ public void onServiceDisconnected(ComponentName className) {
+ Log.e(TAG, "Service has unexpectedly disconnected");
+ mIRemoteService = null;
+ }
+};
+</pre>
+
+<p>For more sample code, see the <a
+href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/RemoteService.html">{@code
+RemoteService.java}</a> class in <a
+href="{@docRoot}resources/samples/ApiDemos/index.html">ApiDemos</a>.</p>
-<h3 id="parcelable">Pass by value Parameters using Parcelables</h3>
+
+
+
+
+
+
+<h2 id="PassingObjects">Passing Objects over IPC</h2>
<p>If you have a class that you would like to send from one process to another through
-an AIDL interface, you can do that. You must ensure that the code for your class is available
-to the other side of the IPC. Generally, that means that you're talking to a service that you
-started.</p>
-<p>There are five parts to making a class support the Parcelable protocol:</b>
+an IPC interface, you can do that. However, you must ensure that the code for your class is
+available to the other side of the IPC channel and your class must support the {@link
+android.os.Parcelable} interface. Supporting the {@link android.os.Parcelable} interface is
+important because it allows the Android system to decompose objects into primitives that can be
+marshalled across processes.</p>
+
+<p>To create a class that supports the {@link android.os.Parcelable} protocol, you must do the
+following:</b>
<ol>
<li>Make your class implement the {@link android.os.Parcelable} interface.</li>
-<li>Implement the method <code>public void writeToParcel(Parcel out)</code> that takes the
-current state of the object and writes it to a parcel.</li>
-value in a parcel into your object.</li>
+<li>Implement {@link android.os.Parcelable#writeToParcel writeToParcel}, which takes the
+current state of the object and writes it to a {@link android.os.Parcel}.</li>
<li>Add a static field called <code>CREATOR</code> to your class which is an object implementing
the {@link android.os.Parcelable.Creator Parcelable.Creator} interface.</li>
-<li>Last but not least, create an aidl file
-that declares your parcelable class (as shown below). If you are using a custom build process,
-do not add the aidl file to your build. Similar to a header file in C, the aidl file isn't
-compiled.</li>
+<li>Finally, create an {@code .aidl} file that declares your parcelable class (as shown for the
+{@code Rect.aidl} file, below).
+ <p>If you are using a custom build process, do <em>not</em> add the {@code .aidl} file to your
+build. Similar to a header file in the C language, this {@code .aidl} file isn't compiled.</p></li>
</ol>
-<p>AIDL will use these methods and fields in the code it generates to marshall and unmarshall
+<p>AIDL uses these methods and fields in the code it generates to marshall and unmarshall
your objects.</p>
-<p>Here is an example of how the {@link android.graphics.Rect} class implements the
-Parcelable protocol.</p>
-<pre class="prettyprint">
+<p>For example, here is a {@code Rect.aidl} file to create a {@code Rect} class that's
+parcelable:</p>
+
+<pre>
+package android.graphics;
+
+// Declare Rect so AIDL can find it and knows that it implements
+// the parcelable protocol.
+parcelable Rect;
+</pre>
+
+<p>And here is an example of how the {@link android.graphics.Rect} class implements the
+{@link android.os.Parcelable} protocol.</p>
+
+<pre>
import android.os.Parcel;
import android.os.Parcelable;
@@ -218,7 +376,8 @@
public int right;
public int bottom;
- public static final Parcelable.Creator<Rect> CREATOR = new Parcelable.Creator<Rect>() {
+ public static final Parcelable.Creator<Rect> CREATOR = new
+Parcelable.Creator<Rect>() {
public Rect createFromParcel(Parcel in) {
return new Rect(in);
}
@@ -251,43 +410,42 @@
}
</pre>
-<p>Here is Rect.aidl for this example</p>
-
-<pre class="prettyprint">
-package android.graphics;
-
-// Declare Rect so AIDL can find it and knows that it implements
-// the parcelable protocol.
-parcelable Rect;
-</pre>
-
-<p>The marshalling in the Rect class is pretty simple. Take a look at the other
+<p>The marshalling in the {@code Rect} class is pretty simple. Take a look at the other
methods on {@link android.os.Parcel} to see the other kinds of values you can write
to a Parcel.</p>
-<p class="warning"><b>Warning:</b> Don't forget the security implications of receiving data from
-other processes. In this case, the rect will read four numbers from the parcel,
-but it is up to you to ensure that these are within the acceptable range of
-values for whatever the caller is trying to do. See
-<a href="{@docRoot}guide/topics/security/security.html">Security and Permissions</a> for more
-on how to keep your application secure from malware.</p>
+<p class="warning"><strong>Warning:</strong> Don't forget the security implications of receiving
+data from other processes. In this case, the {@code Rect} reads four numbers from the {@link
+android.os.Parcel}, but it is up to you to ensure that these are within the acceptable range of
+values for whatever the caller is trying to do. See <a
+href="{@docRoot}guide/topics/security/security.html">Security and Permissions</a> for more
+information about how to keep your application secure from malware.</p>
-<h2 id="calling">Calling an IPC Method</h2>
-<p>Here are the steps a calling class should make to call your remote interface: </p>
+
+
+<h2 id="Calling">Calling an IPC Method</h2>
+
+<p>Here are the steps a calling class must take to call a remote interface defined with AIDL: </p>
<ol>
- <li>Declare a variable of the interface type that your .aidl file defined. </li>
+ <li>Include the {@code .aidl} file in the project {@code src/} directory.</li>
+ <li>Declare an instance of the {@link android.os.IBinder} interface (generated based on the
+AIDL). </li>
<li>Implement {@link android.content.ServiceConnection ServiceConnection}. </li>
- <li>Call {@link android.content.Context#bindService(android.content.Intent,android.content.ServiceConnection,int)
- Context.bindService()}, passing in your ServiceConnection implementation. </li>
- <li>In your implementation of {@link android.content.ServiceConnection#onServiceConnected(android.content.ComponentName,android.os.IBinder)
- ServiceConnection.onServiceConnected()}, you will receive an {@link android.os.IBinder
- IBinder} instance (called <em>service</em>). Call <code><em>YourInterfaceName</em>.Stub.asInterface((IBinder)<em>service</em>)</code> to
+ <li>Call {@link
+android.content.Context#bindService(android.content.Intent,android.content.ServiceConnection,int)
+ Context.bindService()}, passing in your {@link
+android.content.ServiceConnection} implementation. </li>
+ <li>In your implementation of {@link
+android.content.ServiceConnection#onServiceConnected onServiceConnected()},
+you will receive an {@link android.os.IBinder} instance (called <code>service</code>). Call
+<code><em>YourInterfaceName</em>.Stub.asInterface((IBinder)<em>service</em>)</code> to
cast the returned parameter to <em>YourInterface</em> type.</li>
<li>Call the methods that you defined on your interface. You should always trap
{@link android.os.DeadObjectException} exceptions, which are thrown when
the connection has broken; this will be the only exception thrown by remote
methods.</li>
- <li>To disconnect, call {@link android.content.Context#unbindService(android.content.ServiceConnection)
+ <li>To disconnect, call {@link
+android.content.Context#unbindService(android.content.ServiceConnection)
Context.unbindService()} with the instance of your interface. </li>
</ol>
<p>A few comments on calling an IPC service:</p>
@@ -296,10 +454,12 @@
<li>You can send anonymous objects
as method arguments. </li>
</ul>
+
+<p>For more information about binding to a service, read the <a
+href="{@docRoot}guide/topics/fundamentals/bound-services.html#Binding">Bound Services</a>
+document.</p>
+
<p>Here is some sample code demonstrating calling an AIDL-created service, taken
from the Remote Service sample in the ApiDemos project.</p>
<p>{@sample development/samples/ApiDemos/src/com/example/android/apis/app/RemoteService.java
calling_a_service}</p>
-
-
-
diff --git a/docs/html/guide/guide_toc.cs b/docs/html/guide/guide_toc.cs
index 92230a9..d81b416 100644
--- a/docs/html/guide/guide_toc.cs
+++ b/docs/html/guide/guide_toc.cs
@@ -554,7 +554,6 @@
<ul>
<li><a href="<?cs var:toroot ?>guide/developing/tools/adb.html">adb</a></li>
<li><a href="<?cs var:toroot ?>guide/developing/tools/android.html">android</a></li>
- <li><a href="<?cs var:toroot ?>guide/developing/tools/aidl.html">aidl</a></li>
<li><a href="<?cs var:toroot ?>guide/developing/tools/bmgr.html">bmgr</a>
<li><a href="<?cs var:toroot ?>guide/developing/tools/dmtracedump.html">dmtracedump</a></li>
<li><a href="<?cs var:toroot ?>guide/developing/tools/draw9patch.html" >Draw 9-Patch</a></li>
@@ -754,6 +753,7 @@
<li><a href="<?cs var:toroot ?>guide/appendix/g-app-intents.html">
<span class="en">Intents List: Google Apps</span>
</a></li>
+ <li><a href="<?cs var:toroot ?>guide/developing/tools/aidl.html">AIDL</a></li>
<li><a href="<?cs var:toroot ?>guide/appendix/glossary.html">
<span class="en">Glossary</span>
</a></li>
diff --git a/docs/html/guide/topics/advanced/aidl.jd b/docs/html/guide/topics/advanced/aidl.jd
deleted file mode 100644
index 419048a..0000000
--- a/docs/html/guide/topics/advanced/aidl.jd
+++ /dev/null
@@ -1,389 +0,0 @@
-page.title=Android Interface Definition Language (AIDL)
-@jd:body
-
-
-
-<p>AIDL (Android Interface Definition Language) is similar to other IDLs you might have
-worked with. It allows you to define the programming interface that both
-the client and service agree upon in order to communicate with each other and allows for
-interprocess communication (IPC). On Android, one process can not normally access the
-memory of another process. So to talk, they need to decompose their objects into primitives that the
-operating system can understand, and "marshall" the object across that boundary for you. The code to
-do that marshalling is tedious to write, so Android handles it for you with AIDL.</p>
-
-<p class="note"><strong>Note:</strong> Using AIDL is necessary only if you allow clients from
-different applications to access your service for IPC and want to handle multithreading in your
-service. If you do not need to perform IPC across
-different applications, you should create your interface <a
-href="{@docRoot}guide/topics/fundamentals/bound-services.html#Binder">implementing a
-Binder</a> or, if you want to perform IPC, but do not need to handle multithreading, then you
-should implement your interface <a
-href="{@docRoot}guide/topics/fundamentals/bound-services.html#Messenger">using a Messenger</a>.</p>
-
-<p>Before you begin designing your AIDL interface, be aware that calls on to an AIDL interface are
-direct function calls. You can not generally make assumptions about the thread in which the call
-will happen. What happens is different depending on whether the call is from a thread in the
-local process, or coming from a remote process. Specifically:</p>
-
-<ul>
-<li>Calls from the local process are executed in the same thread that is making the call. If this is
-your main UI thread, that thread will continue executing into the aidl interface. If it is another
-thread, that is one that will be executing your code. Thus if only local threads are accessing the
-interface, you can completely control which threads are executing in it (but if that is the case,
-why are you defining an aidl interface at all?).</li>
-
-<li>Calls from a remote process are dispatched from a thread pool the platform maintains inside of
-your own process. You must be prepared for incoming calls from unknown threads, with multiple calls
-happening at the same time. In other words, an implementation of an aidl interface must be
-completely thread-safe.</li>
-
-<li>The "oneway" keyword modifies the behavior of remote calls. When used, a remote call will not
-block until its call completes; it simply sends the transaction data and immediately returns. The
-implementation of the interface will eventually receive this as a regular call from the {@link
-android.os.Binder} thread pool as a normal remote call. If "oneway" is used with a local call,
-there is no impact and the call is still synchronous.</li>
-</ul>
-
-
-<h2 id="Defining">Defining an AIDL Interface</h2>
-
-<p>You must define your AIDL interface in an {@code .aidl} file using the Java
-programming language syntax, then save it in the source code (in the {@code src/} directory) of both
-the application hosting the service and any other application that will bind to the service.</p>
-
-<p>When you build the projects containing the {@code .aidl} file, the Android SDK tools generate an
-{@link android.os.IBinder} class based on your AIDL interface (and saves the file in the {@code
-gen/} directory). This class defines the APIs you can call to perform RPC as an interface—you
-must implement the interface in your service.</p>
-
-<p>To create a bounded service using AIDL, follow these steps:</p>
-<ol>
- <li><a href="#CreateAidl">Create the .aidl file</a>
- <p>This file defines the programming interface with method signatures.</p>
- </li>
- <li><a href="#ImplementTheInterface">Implement the interface</a>
- <p>The Android SDK tools generate an interface in the Java programming language, based on your
-{@code .aidl} file. This interface has an inner abstract class named {@code Stub} that extends
-{@link android.os.Binder} and implements methods from your AIDL interface. You must extend the
-{@code Stub} class and implement the methods.</p>
- </li>
- <li><a href="#ExposeTheInterface">Expose the interface to clients</a>
- <p>Implement a {@link android.app.Service Service} and override {@link
-android.app.Service#onBind onBind()} to return your implementation of the {@code Stub}
-class.</p>
- </li>
-</ol>
-
-<p class="caution"><strong>Caution:</strong> Any changes that you make to your AIDL interface after
-your first release must remain backward compatible in order to avoid breaking other applications
-that use your service. That is, because your {@code .aidl} file must be copied to other applications
-in order for them to access your service's interface, you must maintain support for the original
-interface.</p>
-
-
-<h3 id="CreateAidl">1. Create the .aidl file</h3>
-
-<p>AIDL uses a simple syntax that lets you declare an interface with one or more methods that can
-take parameters and return values. The parameters and return values can be of any type, even other
-AIDL-generated interfaces.</p>
-
-<p>The syntax for the {@code .aidl} file uses the Java programming language. The file defines a
-single interface and requires only the interface declaration and method signatures.</p>
-
-<p>By default, AIDL supports the following data types:</p>
-
-<ul>
- <li>All primitive types in the Java programming language ({@code int}, {@code long}, {@code
-char}, {@code boolean}, etc.)</li>
- <li>{@link java.lang.String}</li>
- <li>{@link java.lang.CharSequence}</li>
- <li>{@link java.util.List}
- <p>All elements in the {@link java.util.List} must be one of the supported data types in this
-list or one of the other AIDL-generated interfaces or parcelables you've declared. A {@link
-java.util.List} may optionally be used as a "generic" class (e.g. <code>List<String></code>).
-The actual concrete class that the other side will receive will always be an {@link
-java.util.ArrayList}, although the method will be generated to use the {@link
-java.util.List} interface.</p>
- </li>
- <li>{@link java.util.Map}
- <p>All elements in the {@link java.util.Map} must be one of the supported data types in this
-list or one of the other AIDL-generated interfaces or parcelables you've declared. Generic maps,
-(such as those of the form
-{@code Map<String,Integer>} are not supported. The actual concrete class that the other side
-will receive will always be a {@link java.util.HashMap}, although the method will be generated to
-use the {@link java.util.Map} interface.</p>
- </li>
-</ul>
-
-<p>You must include an {@code import} statement for each additional type not listed above, even if
-they are defined in the same package as your interface.</p>
-
-<p>When defining methods for your service interface, be aware that:</p>
-<ul>
- <li>Methods can take zero or more parameters, and return a value or void.</li>
- <li>All non-primitive parameters require a directional tag indicating which way the data will go.
-Either <code>in</code>, <code>out</code>, or <code>inout</code> (see the example below).
- <p>Primitives are <code>in</code> by default, and cannot be otherwise.</p>
- <p class="caution"><strong>Caution:</strong> You should limit the direction to what is truly
-needed, because marshalling parameters is expensive.</p></li>
- <li>All code comments included in the {@code .aidl} file are included in the generated {@link
-android.os.IBinder} interface (except for comments before the import and package
-statements).</li>
-</ul>
-
-<p>Here is an example {@code .aidl} file:</p>
-
-<pre>
-// IRemoteService.aidl
-package com.example.android;
-
-// Declare any non-default types here with import statements
-
-/** Example service interface */
-interface IRemoteService {
- /** Request the process ID of this service, to do evil things with it. */
- int getPid();
-
- /** Demonstrates some basic types that you can use as parameters
- * and return values in AIDL.
- */
- void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
- double aDouble, String aString);
-}
-</pre>
-
-<p>Simply save your {@code .aidl} file in your project's {@code src/} directory and when you
-build your application, the SDK tools will generate the binder class file in your project's
-{@code gen/} directory. The generated file name matches the {@code .aidl} file name, but with a
-{@code .java} extension (for example, {@code IRemoteService.aidl} results in {@code
-IRemoteService.java}).</p>
-
-<p>If you use Eclipse, the incremental build generates the binder class almost immediately. If you
-do not use Eclipse, then the Ant tool generates the binder class next time you build your
-application—you should build your project with <code>ant debug</code> (or <code>ant
-release</code>) as soon as you're finished writing the {@code .aidl} file, so that your code can
-link against the generated class.</p>
-
-
-<h3 id="ImplementTheInterface">2. Implement the interface</h3>
-
-<p>When you build your application, the Android SDK tools generate a {@code .java} interface file
-named after your {@code .aidl} file. The generated interface includes a subclass named {@code Stub}
-that is an abstract implementation of its parent interface (for example, {@code
-YourInterface.Stub}) and declares all the methods from the {@code .aidl} file.</p>
-
-<p class="note"><strong>Note:</strong> {@code Stub} also
-defines a few helper methods, most notably {@code asInterface()}, which takes an {@link
-android.os.IBinder} (usually the one passed to a client's {@link
-android.content.ServiceConnection#onServiceConnected onServiceConnected()} callback method) and
-returns an instance of the stub interface. See the section <a href="#calling">Calling an IPC
-Method</a> for more details on how to make this cast.</p></p>
-
-<p>To implement the interface generated from the {@code .aidl}, extend the generated {@link
-android.os.Binder} interface (for example, {@code YourInterface.Stub}) and implement the methods
-inherited from the {@code .aidl} file.</p>
-
-<p>Here is an example implementation of an interface called {@code IRemoteService} (defined by the
-{@code IRemoteService.aidl} example, above) using an anonymous instance:</p>
-
-<pre>
-private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
- public int getPid(){
- return Process.myPid();
- }
- public void basicTypes(int anInt, long aLong, boolean aBoolean,
- float aFloat, double aDouble, String aString) {
- // Does nothing
- }
-};
-</pre>
-
-<p>Now the {@code mBinder} is an instance of the {@code Stub} class (a {@link android.os.Binder}),
-which defines the RPC interface for the service. In the next step, this instance is exposed to
-clients so they can interact with the service.</p>
-
-<p>There are a few rules you should be aware of when implementing your AIDL interface: </p>
-<ul>
- <li>Incoming calls are not guaranteed to be executed on the main thread, so you need to think
-about multithreading from the start and properly build your service to be thread-safe.</li>
- <li>By default, RPC calls are synchronous. If you know that the service takes more than a few
-milliseconds to complete a request, you should not call it from the activity's main thread, because
-it might hang the application (Android might display an "Application is Not Responding"
-dialog)—you should usually call them from a separate thread in the client. </li>
- <li>No exceptions that you throw are sent back to the caller.</li>
- <li>Only methods are supported; you cannot expose static fields in AIDL.</li>
-</ul>
-
-
-<h3 id="ExposeTheInterface">3. Expose the interface to clients</h3>
-
-<p>Once you've implemented the interface for your service, you need to expose it to
-clients so they can bind to it. To expose the interface
-for your service, extend {@link android.app.Service Service} and implement {@link
-android.app.Service#onBind onBind()} to return an instance of your class that implements
-the generated {@code Stub} (as discussed in the previous section). Here's an example
-service that exposes the {@code IRemoteService} example interface to clients. </p>
-
-<pre>
-public class RemoteService extends Service {
- @Override
- public void onCreate() {
- super.onCreate();
- }
-
- @Override
- public IBinder onBind(Intent intent) {
- // Return the interface
- return mBinder;
- }
-
- private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
- public int getPid(){
- return Process.myPid();
- }
- public void basicTypes(int anInt, long aLong, boolean aBoolean,
- float aFloat, double aDouble, String aString) {
- // Does nothing
- }
- };
-}
-</pre>
-
-<p>Now, when a client (such as an activity) calls {@link android.content.Context#bindService
-bindService()} to connect to this service, the client's {@link
-android.content.ServiceConnection#onServiceConnected onServiceConnected()} callback receives the
-{@code mBinder} instance returned by the service's {@link android.app.Service#onBind onBind()}
-method.</p>
-
-<p>The client must also have access to the interface class, so if the client and service are in
-separate applications, then the client's application must have a copy of the {@code .aidl} file
-in its {@code src/} directory (which generates the {@code android.os.Binder}
-interface—providing the client access to the AIDL methods).</p>
-
-<p>When the client receives the {@link android.os.IBinder} in the {@link
-android.content.ServiceConnection#onServiceConnected onServiceConnected()} callback, it must call
-<code><em>YourServiceInterface</em>.Stub.asInterface(service)</code> to cast the returned
-parameter to <code><em>YourServiceInterface</em></code> type. For example:</p>
-
-<pre>
-IRemoteService mIRemoteService;
-private ServiceConnection mConnection = new ServiceConnection() {
- // Called when the connection with the service is established
- public void onServiceConnected(ComponentName className, IBinder service) {
- // Following the example above for an AIDL interface,
- // this gets an instance of the IRemoteInterface, which we can use to call on the service
- mIRemoteService = IRemoteService.Stub.asInterface(service);
- }
-
- // Called when the connection with the service disconnects unexpectedly
- public void onServiceDisconnected(ComponentName className) {
- Log.e(TAG, "onServiceDisconnected");
- }
-};
-</pre>
-
-<p>For more sample code, see the <a
-href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/RemoteService.html">{@code
-RemoteService.java}</a> class in <a
-href="{@docRoot}resources/samples/ApiDemos/index.html">ApiDemos</a>.</p>
-
-
-
-
-
-
-
-
-<h2 id="PassingObjects">Passing Objects over IPC</h2>
-
-<p>If you have a class that you would like to send from one process to another through
-an IPC interface, you can do that. However, you must ensure that the code for your class is
-available to the other side of the IPC and the class must support the {@link
-android.os.Parcelable} interface, in order for the objects to be decomposed into primitives and
-marshalled across processes by the Android system.</p>
-
-<p>There are five parts to making a class support the {@link android.os.Parcelable} protocol:</b>
-<ol>
-<li>Make your class implement the {@link android.os.Parcelable} interface.</li>
-<li>Implement {@link android.os.Parcelable#writeToParcel writeToParcel}, which takes the
-current state of the object and writes it to a {@link android.os.Parcel}.</li>
-<li>Add a static field called <code>CREATOR</code> to your class which is an object implementing
-the {@link android.os.Parcelable.Creator Parcelable.Creator} interface.</li>
-<li>Finally, create an {@code .aidl} file that declares your parcelable class (as shown for the
-{@code Rect.aidl} file, below).
- <p>If you are using a custom build process, do <em>not</em> add the {@code .aidl} file to your
-build. Similar to a header file in the C language, this {@code .aidl} file isn't compiled.</p></li>
-</ol>
-
-<p>AIDL will use these methods and fields in the code it generates to marshall and unmarshall
-your objects.</p>
-
-<p>For example, here is a {@code Rect.aidl} file to create a {@code Rect} class that's
-parcelable:</p>
-
-<pre>
-package android.graphics;
-
-// Declare Rect so AIDL can find it and knows that it implements
-// the parcelable protocol.
-parcelable Rect;
-</pre>
-
-<p>And here is an example of how the {@link android.graphics.Rect} class implements the
-{@link android.os.Parcelable} protocol.</p>
-
-<pre>
-import android.os.Parcel;
-import android.os.Parcelable;
-
-public final class Rect implements Parcelable {
- public int left;
- public int top;
- public int right;
- public int bottom;
-
- public static final Parcelable.Creator<Rect> CREATOR = new
-Parcelable.Creator<Rect>() {
- public Rect createFromParcel(Parcel in) {
- return new Rect(in);
- }
-
- public Rect[] newArray(int size) {
- return new Rect[size];
- }
- };
-
- public Rect() {
- }
-
- private Rect(Parcel in) {
- readFromParcel(in);
- }
-
- public void writeToParcel(Parcel out) {
- out.writeInt(left);
- out.writeInt(top);
- out.writeInt(right);
- out.writeInt(bottom);
- }
-
- public void readFromParcel(Parcel in) {
- left = in.readInt();
- top = in.readInt();
- right = in.readInt();
- bottom = in.readInt();
- }
-}
-</pre>
-
-<p>The marshalling in the {@code Rect} class is pretty simple. Take a look at the other
-methods on {@link android.os.Parcel} to see the other kinds of values you can write
-to a Parcel.</p>
-
-<p class="warning"><strong>Warning:</strong> Don't forget the security implications of receiving
-data from other processes. In this case, the {@code Rect} will read four numbers from the {@link
-android.os.Parcel}, but it is up to you to ensure that these are within the acceptable range of
-values for whatever the caller is trying to do. See <a
-href="{@docRoot}guide/topics/security/security.html">Security and Permissions</a> for more
-information about how to keep your application secure from malware.</p>
-
diff --git a/docs/html/guide/topics/fundamentals/bound-services.jd b/docs/html/guide/topics/fundamentals/bound-services.jd
index e5d626c..ec7d723 100644
--- a/docs/html/guide/topics/fundamentals/bound-services.jd
+++ b/docs/html/guide/topics/fundamentals/bound-services.jd
@@ -170,7 +170,7 @@
create a bound service, because it may require multithreading capabilities and
can result in a more complicated implementation. As such, AIDL is not suitable for most applications
and this document does not discuss how to use it for your service. If you're certain that you need
-to use AIDL directly, see the <a href="{@docRoot}guide/topics/advanced/aidl.html">AIDL</a>
+to use AIDL directly, see the <a href="{@docRoot}guide/developing/tools/aidl.html">AIDL</a>
document.</p>
@@ -341,7 +341,7 @@
<p>For most applications, the service doesn't need to perform multi-threading, so using a {@link
android.os.Messenger} allows the service to handle one call at a time. If it's important
that your service be multi-threaded, then you should use <a
-href="{@docRoot}guide/topics/advanced/aidl.html">AIDL</a> to define your interface.</p>
+href="{@docRoot}guide/developing/tools/aidl.html">AIDL</a> to define your interface.</p>
</div>
</div>
diff --git a/docs/html/guide/topics/graphics/renderscript.jd b/docs/html/guide/topics/graphics/renderscript.jd
index 54737ad..0e64c78 100644
--- a/docs/html/guide/topics/graphics/renderscript.jd
+++ b/docs/html/guide/topics/graphics/renderscript.jd
@@ -16,7 +16,7 @@
<ol>
<li><a href="#native-api">Native Renderscript APIs</a></li>
- <li><a href="#reflective-api">Reflective layer APIs</a></li>
+ <li><a href="#reflective-api">Reflected layer APIs</a></li>
<li><a href="#graphics-api">Graphics APIs</a></li>
</ol>
@@ -30,10 +30,18 @@
</ol>
</li>
</ol>
+ <h2>Related Samples</h2>
+ <ol>
+ <li><a href="{@docRoot}resources/samples/Renderscript/Balls/index.html">Balls</a></li>
+ <li><a href="{@docRoot}resources/samples/Renderscript/Fountain/index.html">Fountain</a></li>
+ <li><a href="{@docRoot}resources/samples/Renderscript/HelloCompute/index.html">Hello Compute</a></li>
+ <li><a href="{@docRoot}resources/samples/Renderscript/HelloWorld/index.html">Hello World</a></li>
+ <li><a href="{@docRoot}resources/samples/Renderscript/Samples/index.html">Samples</a></li>
+ </ol>
</div>
</div>
- <p>The Renderscript system offers high performance 3D rendering and mathematical computations at
+ <p>The Renderscript system offers high performance 3D rendering and mathematical computation at
the native level. The Renderscript APIs are intended for developers who are comfortable with
developing in C (C99 standard) and want to maximize performance in their applications. The
Renderscript system improves performance by running as native code on the device, but it also
@@ -46,47 +54,46 @@
intermediate code.</p>
<p>The disadvantage of the Renderscript system is that it adds complexity to the development and
- debugging processes and is not a substitute for the Android system APIs. It is a portable native
- language with pointers and explicit resource management. The target use is for performance
- critical code where the existing Android APIs are not sufficient. If what you are rendering or
- computing is very simple and does not require much processing power, you should still use the
- Android APIs for ease of development. Debugging visibility can be limited, because the
+ debugging processes. Debugging visibility can be limited, because the
Renderscript system can execute on processors other than the main CPU (such as the GPU), so if
- this occurs, debugging becomes more difficult. Remember the tradeoffs between development and
- debugging complexity versus performance when deciding to use Renderscript.</p>
+ this occurs, debugging becomes more difficult. The target use is for performance
+ critical code where the traditional framework APIs (such as using {@link android.opengl}) are not sufficient.
+ If what you are rendering or computing is very simple and does not require much processing power, you should still use the
+ traditional framework APIs for ease of development. Remember the tradeoffs between development and
+ debugging complexity versus performance when deciding to use Renderscript. </p>
<p>For an example of Renderscript in action, see the 3D carousel view in the Android 3.0 versions
of Google Books and YouTube or install the Renderscript sample applications that are shipped with
- the SDK in <code><sdk_root>/platforms/android-3.0/samples</code>.</p>
+ the SDK in <code><sdk_root>/samples/android-11/Renderscript</code>.</p>
<h2 id="overview">Renderscript System Overview</h2>
<p>The Renderscript system adopts a control and slave architecture where the low-level native
code is controlled by the higher level Android system that runs in the virtual machine (VM). When
- you use the Renderscript system, there are three layers of APIs that exist:</p>
+ you use the Renderscript system, there are three layers that exist:</p>
<ul>
- <li>The native Renderscript layer consists of the native Renderscript <code>.rs</code> files
- that you write to compute mathematical operations, render graphics, or both. This layer does
- the intensive computation or graphics rendering and returns the result back to the Android VM
- through the reflected layer.</li>
+ <li>The native Renderscript layer consists of native libraries that are packaged with the SDK.
+ The native Renderscript <code>.rs</code> files compute mathematical operations, render graphics,
+ or both. This layer does the intensive computation or graphics rendering and returns the result
+ back to the Android VM through the reflected layer.</li>
- <li>The reflected layer is a set of generated Android system classes (through reflection) based
- on the native layer interface that you define. This layer acts as a bridge between the native
+ <li>The reflected layer is a set of generated Android framework classes reflected from
+ the native Renderscript code that you wrote. This layer acts as a bridge between the native
Renderscript layer and the Android system layer. The Android build tools automatically generate
- the APIs for this layer during the build process.</li>
+ the classes for this layer during the build process. This layer also includes a set of Android
+ framework APIs that provide the memory and resource allocation classes to support this layer.</li>
- <li>The Android system layer consists of your normal Android APIs along with the Renderscript
+ <li>The Android system layer consists of the traditional framework APIs, which include the Renderscript
APIs in {@link android.renderscript}. This layer handles things such as the Activity lifecycle
- management of your application and calls the native Renderscript layer through the reflected
- layer.</li>
+ management of your application and calls the reflected layer to communicate with the native Renderscript code.</li>
</ul>
<p>To fully understand how the Renderscript system works, you must understand how the reflected
layer is generated and how it interacts with the native Renderscript layer and Android system
layer. The reflected layer provides the entry points into the native code, enabling the Android
- system code to give high level commands like, "rotate the view" or "filter the bitmap." It
- delegates all the heavy lifting to the native layer. To accomplish this, you need to create logic
+ system to give high level commands like, "rotate the view" or "filter the bitmap" to the
+ native layer, which does the heavy lifting. To accomplish this, you need to create logic
to hook together all of these layers so that they can correctly communicate.</p>
<p>At the root of everything is your Renderscript, which is the actual C code that you write and
@@ -94,11 +101,10 @@
and graphics. A compute Renderscript does not do any graphics rendering while a graphics
Renderscript does.</p>
- <p>When you create a Renderscript <code>.rs</code> file, an equivalent, reflective layer class,
- {@link android.renderscript.ScriptC}, is generated by the build tools and exposes the native
- functions to the Android system. This class is named
- <code><em>ScriptC_renderscript_filename</em></code>. The following list describes the major
- components of your native Renderscript code that is reflected:</p>
+ <p>When you create Renderscript <code>.rs</code> files, equivalent, reflected classes
+ are generated by the build tools and expose the native functions and data types and structures
+ to the Android system. The following list describes the major components of your native Renderscript
+ code that is reflected:</p>
<ul>
<li>The non-static functions in your Renderscript (<code>.rs</code> file) are reflected into
@@ -108,12 +114,12 @@
<li>Any non-static, global Renderscript variables are reflected into
<code><em>ScriptC_renderscript_filename</em></code>.
Accessor methods are generated, so the Android system layer can access the values.
- The <code>get()</code> method comes with a one-way communication restriction.
- The Android system layer always caches the last value that is set and returns that during a call to get.
- If the native Renderscript code has changed the value, the change does propagate back to the Android system layer
- for efficiency. If the global variables are initialized in the native Renderscript code, those values are used
- to initialize the Android system versions. If global variables are marked as <code>const</code>,
- then a <code>set()</code> method is not generated.
+ The <code>get</code> method comes with a one-way communication restriction.
+ The Android system layer always caches the last value that is set and returns that during a call to a <code>get<code> method.
+ If the native Renderscript code changes the value, the change does not propagate back to the Android system layer.
+ If the global variables are initialized in the native Renderscript code, those values are used
+ to initialize the corresponding values in the Android system. If global variables are marked as <code>const</code>,
+ then a <code>set</code> method is not generated.
</li>
<li>Structs are reflected into their own classes, one for each struct, into a class named
@@ -128,15 +134,14 @@
Renderscripts should not directly set the exported global pointers.</li>
</ul>
- <p>The Android system also has a corresponding Renderscript context object, {@link
+ <p>The Android framework API also has a corresponding Renderscript context object, {@link
android.renderscript.RenderScript} (for a compute Renderscript) or {@link
android.renderscript.RenderScriptGL} (for a graphics Renderscript). This context object allows
you to bind to the reflected Renderscript class, so that the Renderscript context knows what its
corresponding native Renderscript is. If you have a graphics Renderscript context, you can also
specify a variety of Programs (stages in the graphics pipeline) to tweek how your graphics are
rendered. A graphics Renderscript context also needs a surface to render on, {@link
- android.renderscript.RSSurfaceView}, which gets passed into its constructor. When all three of
- the layers are connected, the Renderscript system can compute or render graphics.</p>
+ android.renderscript.RSSurfaceView}, which gets passed into its constructor.</p>
<h2 id="api">API overview</h2>
@@ -145,15 +150,15 @@
because these functions are assumed to be running on a standard CPU. The Renderscript runtime
chooses the best processor to execute the code, which may not be the CPU, so it cannot guarantee
support for standard C libraries. What Renderscript does offer is an API that supports intensive
- computation with an extensive collection of math APIs. Some key features of the Renderscript APIs
- are:</p>
+ computation with an extensive collection of math APIs. The following sections group the APIs
+ into three distinct categories.</p>
<h3 id="native-api">Native Renderscript APIs</h3>
<p>The Renderscript headers are located in the <code>include</code> and
<code>clang-include</code> directories in the
- <code><sdk_root>/platforms/android-3.0/renderscript</code> directory of the Android SDK.
+ <code><sdk_root>/platforms/android-11/renderscript</code> directory of the Android SDK.
The headers are automatically included for you, except for the graphics specific header,
which you can define as follows:</p>
@@ -170,16 +175,14 @@
<li>Graphics rendering functions</li>
<li>Memory allocation request features</li>
<li>Data types and structures to support the Renderscript system such as
- Vector types for defining two-, three-, or four-vectors.</li></li>
- </ul>
+ Vector types for defining two-, three-, or four-vectors.</li>
</ul>
- <h3 id="reflective-api">Reflective layer APIs</h3>
+ <h3 id="reflective-api">Reflected layer APIs</h3>
- <p>These classes are not generated by the reflection process, and are actually part of the
- Android system APIs, but they are mainly used by the reflective layer classes to handle memory
- allocation and management for your Renderscript. You normally do not need to be call these classes
- directly.</p>
+ <p>These classes are mainly used by the reflected classes that are generated from your native Renderscript
+ code. They allocate and manage memory for your Renderscript on the Android system side.
+ You normally do not need to call these classes directly.</p>
<p>Because of the constraints of the Renderscript native layer, you cannot do any dynamic
memory allocation in your Renderscript <code>.rs</code> file.
@@ -195,7 +198,7 @@
The Android system object, which at this point is just an empty shell, is eventually garbage collected.
</p>
- <p>The following classes are mainly used by the reflective layer classes:</p>
+ <p>The following classes are mainly used by the reflected layer classes:</p>
<table>
<tr>
@@ -214,9 +217,9 @@
<td>
An {@link android.renderscript.Element} is the most basic element of a memory type. An
element represents one cell of a memory allocation. An element can have two forms: Basic or
- Complex. They are typically created from C structures that are used within Renderscript
- code and cannot contain pointers or nested arrays. The other common source of elements is
- bitmap formats.
+ Complex. They are typically created from C structures in your Renderscript
+ code during the reflection process. Elements cannot contain pointers or nested arrays.
+ The other common source of elements is bitmap formats.
<p>A basic element contains a single component of data of any valid Renderscript data type.
Examples of basic element data types include a single float value, a float4 vector, or a
@@ -253,12 +256,11 @@
<td>rs_allocation</td>
<td>
- An {@link android.renderscript.Allocation} provides the memory for applications. An {@link
+ <p>An {@link android.renderscript.Allocation} provides the memory for applications. An {@link
android.renderscript.Allocation} allocates memory based on a description of the memory that
- is represented by a {@link android.renderscript.Type}. The {@link
- android.renderscript.Type} describes an array of {@link android.renderscript.Element}s that
+ is represented by a {@link android.renderscript.Type}. The type describes an array of elements that
represent the memory to be allocated. Allocations are the primary way data moves into and
- out of scripts.
+ out of scripts.</p>
<p>Memory is user-synchronized and it's possible for allocations to exist in multiple
memory spaces concurrently. For example, if you make a call to the graphics card to load a
@@ -270,9 +272,9 @@
<p>Allocation data is uploaded in one of two primary ways: type checked and type unchecked.
For simple arrays there are <code>copyFrom()</code> functions that take an array from the
- Android system code and copy it to the native layer memory store. Both type checked and
+ Android system and copy it to the native layer memory store. Both type checked and
unchecked copies are provided. The unchecked variants allow the Android system to copy over
- arrays of structures because it not support inherently support structures. For example, if
+ arrays of structures because it does not support structures. For example, if
there is an allocation that is an array n floats, you can copy the data contained in a
float[n] array or a byte[n*4] array.</p>
</td>
@@ -331,7 +333,7 @@
state is taken from the bind points as set in the {@link android.renderscript.RenderScriptGL}
bind methods in the control environment (VM environment).</p>
- <p>For example, you can define this at the top of your native Renderscript code:</p>
+ <p>For example, you can define this at the top of your native graphics Renderscript code:</p>
<pre>
#pragma stateVertex(parent)
#pragma stateStore(parent)
@@ -354,9 +356,9 @@
<td>rs_program_vertex</td>
<td>
- The Renderscript vertex program, also known as a vertex shader, describes the stage in the
+ <p>The Renderscript vertex program, also known as a vertex shader, describes the stage in the
graphics pipeline responsible for manipulating geometric data in a user-defined way. The
- object is constructed by providing Renderscript with the following data:
+ object is constructed by providing Renderscript with the following data:</p>
<ul>
<li>An Element describing its varying inputs or attributes</li>
@@ -367,7 +369,9 @@
inputs</li>
</ul>
- <p>Once the program is created, bind it to the graphics context. It is then used for all
+ <p>Once the program is created, bind it to the {@link android.renderscript.RenderScriptGL}
+ graphics context by calling
+ {@link android.renderscript.RenderScriptGL#bindProgramVertex bindProgramVertex()}. It is then used for all
subsequent draw calls until you bind a new program. If the program has constant inputs, the
user needs to bind an allocation containing those inputs. The allocation’s type must match
the one provided during creation. The Renderscript library then does all the necessary
@@ -391,7 +395,7 @@
<td>rs_program_fragment</td>
- <td>The Renderscript fragment program, also known as the fragment shader, is responsible for
+ <td><p>The Renderscript fragment program, also known as the fragment shader, is responsible for
manipulating pixel data in a user-defined way. It’s constructed from a GLSL shader string
containing the program body, textures inputs, and a Type object describing the constants used
by the program. Like the vertex programs, when an allocation with constant input values is
@@ -401,7 +405,7 @@
rsgAllocationSyncAll so it could send the new values to hardware. Communication between the
vertex and fragment programs is handled internally in the GLSL code. For example, if the
fragment program is expecting a varying input called varTex0, the GLSL code inside the
- program vertex must provide it.
+ program vertex must provide it.</p>
<p> To bind shader constructs to the this Program, declare a struct containing the necessary shader constants in your native Renderscript code.
This struct is generated into a reflected class that you can use as a constant input element
during the Program's creation. It is an easy way to create an instance of this struct as an allocation.
@@ -416,7 +420,7 @@
<td>rs_program_store</td>
<td>The Renderscript ProgramStore contains a set of parameters that control how the graphics
- hardware writes to the framebuffer. It could be used to enable/disable depth writes and
+ hardware writes to the framebuffer. It could be used to enable and disable depth writes and
testing, setup various blending modes for effects like transparency and define write masks
for color components.</td>
</tr>
@@ -493,19 +497,19 @@
<ol>
<li>Analyze your application's requirements and figure out what you want to develop with
- Renderscript. To take full advantage of Renderscript, you want to use it when the computation
- or graphics performance you're getting with the normal Android system APIs is
+ Renderscript. To take full advantage of the Renderscript system, you want to use it when the computation
+ or graphics performance you're getting with the traditional framework APIs is
insufficient.</li>
<li>Design the interface of your Renderscript code and implement it using the native
Renderscript APIs that are included in the Android SDK in
- <code><sdk_root>/platforms/android-3.0/renderscript</code>.</li>
+ <code><sdk_root>/platforms/android-11/renderscript</code>.</li>
<li>Create an Android project as you would normally, in Eclipse or with the
<code>android</code> tool.</li>
<li>Place your Renderscript files in <code>src</code> folder of the Android project so that the
- build tools can generate the reflective layer classes.</li>
+ build tools can generate the reflected layer classes.</li>
<li>Create your application, calling the Renderscript through the reflected class layer when
you need to.</li>
@@ -513,16 +517,16 @@
<li>Build, install, and run your application as you would normally.</li>
</ol>
- <p>To see how a simple Renderscript application is put together, see <a href="#hello-world">The
- Hello World Renderscript Graphics Application</a>. The SDK also ships with many Renderscript
- samples in the<code><sdk_root>/samples/android-3.0/</code> directory.</p>
+ <p>To see how a simple Renderscript application is put together, see the
+ <a href="{@docRoot}resources/samples/Renderscript/index.html">Renderscript samples</a>
+ and <a href="#hello-graphics">The Hello Graphics Application</a> section of the documentation.</p>
<h3 id="hello-graphics">The Hello Graphics Application</h3>
<p>This small application demonstrates the structure of a simple Renderscript application. You
can model your Renderscript application after the basic structure of this application. You can
find the complete source in the SDK in the
- <code><android-sdk>/platforms/android-3.0/samples/HelloWorldRS directory</code>. The
+ <code><android-sdk>/samples/android-11/HelloWorldRS directory</code>. The
application uses Renderscript to draw the string, "Hello World!" to the screen and redraws the
text whenever the user touches the screen at the location of the touch. This application is only
a demonstration and you should not use the Renderscript system to do something this trivial. The
@@ -544,7 +548,7 @@
screen.</li>
<li>
- <p>The <code><project_root>/gen</code> directory contains the reflective layer classes
+ <p>The <code><project_root>/gen</code> directory contains the reflected layer classes
that are generated by the Android build tools. You will notice a
<code>ScriptC_helloworld</code> class, which is the reflective version of the Renderscript
and contains the entry points into the <code>helloworld.rs</code> native code. This file does
@@ -552,8 +556,8 @@
</li>
</ul>
- <p>Each file has its own distinct use. The following section demonstrates in detail how the
- sample works:</p>
+ <p>Each file has its own distinct use. The following files comprise the main parts of the sample and
+ demonstrate in detail how the sample works:</p>
<dl>
<dt><code>helloworld.rs</code></dt>
@@ -628,7 +632,7 @@
<dd>This class is generated by the Android build tools and is the reflected version of the
<code>helloworld.rs</code> Renderscript. It provides a a high level entry point into the
<code>helloworld.rs</code> native code by defining the corresponding methods that you can call
- from Android system APIs.</dd>
+ from the traditional framework APIs.</dd>
<dt><code>helloworld.bc</code> bytecode</dt>
diff --git a/docs/html/guide/topics/manifest/uses-library-element.jd b/docs/html/guide/topics/manifest/uses-library-element.jd
index f1b5e70..1d38c1a 100644
--- a/docs/html/guide/topics/manifest/uses-library-element.jd
+++ b/docs/html/guide/topics/manifest/uses-library-element.jd
@@ -3,34 +3,101 @@
<dl class="xml">
<dt>syntax:</dt>
-<dd><pre><uses-library android:<a href="#nm">name</a>="<i>string</i>" /></pre></dd>
-
+<dd>
+<pre class="stx">
+<uses-library android:<a href="#nm">name</a>="<var>string</var>"
+ android:<a href="#rq">required</a>=["true" | "false"] />
+</pre>
+</dd>
<dt>contained in:</dt>
-<dd><code><a href="{@docRoot}guide/topics/manifest/application-element.html"><application></a></code></dd>
-
+<dd>
+ <code>
+ <a href="{@docRoot}guide/topics/manifest/application-element.html"><application></a>
+ </code>
+</dd>
<dt>description:</dt>
-<dd>Specifies a shared library that the application must be linked against.
-This element tells the system to include the library's code in the class
-loader for the package.
-
-<p>
-All of the {@code android} packages (such as {@link android.app},
-{@link android.content}, {@link android.view}, and {@link android.widget})
-are in the default library that all applications are automatically linked
-against. However, some packages (such as {@code maps} and {@code awt} are
-in separate libraries that are not automatically linked. Consult the
-documentation for the packages you're using to determine which library
-contains the package code.
-</p></dd>
-
+<dd>
+ Specifies a shared library that the application must be linked against.
+ This element tells the system to include the library's code in the class
+ loader for the package.
+ <p>
+ All of the {@code android} packages (such as {@link android.app},
+ {@link android.content}, {@link android.view}, and {@link android.widget})
+ are in the default library that all applications are automatically linked
+ against. However, some packages (such as {@code maps}) are
+ in separate libraries that are not automatically linked. Consult the
+ documentation for the packages you're using to determine which library
+ contains the package code.
+ </p>
+ <p>
+ This element also affects the installation of the application on a particular device and
+ the availability of the application in Android Market:
+ </p>
+ <dl>
+ <dt><em>Installation</em></dt>
+ <dd>
+ If this element is present and its {@code android:required} attribute is set to
+ {@code true}, the {@link android.content.pm.PackageManager} framework won't let the user
+ install the application unless the library is present on the user's device.
+ </dd>
+ <dt><em>Market</em></dt>
+ <dd>
+ Android Market filters applications based on the libraries installed on the
+ user's device. For more information about filtering, see the topic
+ <a href="{@docRoot}/guide/appendix/market-filters.html">Market Filters</a>.
+ </dd>
+ </dl>
+ <p>
+ The {@code android:required} attribute is described in detail in the following section.
+ </p>
+</dd>
<dt>attributes:</dt>
-<dd><dl class="attr">
-<dt><a name="nm"></a>{@code android:name}</dt>
-<dd>The name of the library.</dd>
-</dl></dd>
-
+<dd>
+ <dl class="attr">
+ <dt><a name="nm"></a>{@code android:name}</dt>
+ <dd>
+ The name of the library. The name is provided by the
+ documentation for the package you are using. An example of this is
+ "<code>android.test.runner</code>", a package that contains Android test
+ classes.
+ </dd>
+ <dt><a name="rq"></a>{@code android:required}</dt>
+ <dd>
+ Boolean value that indicates whether the application requires the
+ library specified by {@code android:name}:
+ <ul>
+ <li>
+ <code>"true"</code>: The application does not function without this
+ library. The system will not allow the application on a device that does not
+ have the library.
+ </li>
+ <li>
+ <code>"false"</code>: The application can use the
+ library if present, but is designed to function without it if necessary.
+ The system will allow the application to be installed, even if the library is
+ not present. If you use <code>"false"</code>, you are responsible for
+ checking at runtime that the library is available.
+ <p>
+ To check for a library, you can use reflection to determine
+ if a particular class is available.
+ </p>
+ </li>
+ </ul>
+ <p>
+ The default is <code>"true"</code>.
+ </p>
+ <p>Introduced in: API Level 7.</p>
+ </dd>
+ </dl>
+</dd>
<!-- ##api level indication## -->
<dt>introduced in:</dt>
<dd>API Level 1</dd>
-</dl>
+<dt>see also:</dt>
+<dd>
+ <ul>
+ <li>{@link android.content.pm.PackageManager}</li>
+ </ul>
+</dd>
+</dl>
\ No newline at end of file
diff --git a/docs/html/resources/resources-data.js b/docs/html/resources/resources-data.js
index 5839064..b80e59a 100644
--- a/docs/html/resources/resources-data.js
+++ b/docs/html/resources/resources-data.js
@@ -496,6 +496,16 @@
}
},
{
+ tags: ['sample', 'new'],
+ path: 'samples/Renderscript/index.html',
+ title: {
+ en: 'Renderscript'
+ },
+ description: {
+ en: 'A set of samples that demonstrate how to use various features of the Renderscript APIs.'
+ }
+ },
+ {
tags: ['sample', 'accountsync'],
path: 'samples/SampleSyncAdapter/index.html',
title: {
diff --git a/docs/html/sdk/android-3.0-optimize.jd b/docs/html/sdk/android-3.0-optimize.jd
new file mode 100644
index 0000000..a22e69a
--- /dev/null
+++ b/docs/html/sdk/android-3.0-optimize.jd
@@ -0,0 +1,397 @@
+page.title=Optimizing Apps for Android 3.0
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+<h2>In this document</h2>
+<ol>
+<li><a href="#Setup">Set Up Your SDK with Android 3.0</a></li>
+<li><a href="#SearchableConfiguration">Optimize Your App for Tablets and Similar Devices</a></li>
+<li><a href="#SearchableActivity">Upgrade or Develop a New App for Tablets and Similar
+Devices</a></li>
+</ol>
+
+</div>
+</div>
+
+<p>If you're developing an Android application, Android 3.0 introduces several features that allow
+you to enhance your user's experience on tablets and similar devices. Any application you've already
+published is compatible with devices running Android 3.0, by default, because Android applications
+are forward-compatible. However, there are some simple changes you should make to optimize your
+application for tablet-type devices.</p>
+
+<p>This document shows how you can optimize your existing application for Android 3.0 and
+maintain compatibility with older versions or upgrade your application completely with new APIs.</p>
+
+
+<p><b>To get started:</b></p>
+
+<ol>
+ <li><a href="#Setup">Set up your SDK with Android 3.0</a>.</li>
+ <li>Then choose to either optimize or upgrade:
+ <ol type="a">
+ <li><a href="#Optimize">Optimize Your App for Tablets and Similar Devices</a>.
+ <p>When you have an existing application and want to maintain compatibility with
+older versions of Android.</p>
+ </li>
+ <li><a href="#Upgrade">Upgrade or Develop a New App for Tablets and Similar Devices</a>.
+ <p>When you want to upgrade your application to use APIs introduced in Android 3.0 or
+ create a new application targeted to tablets and similar devices.</p></li>
+ </ol>
+ </li>
+</ol>
+
+
+<h2 id="Setup">Set Up Your SDK with Android 3.0</h2>
+
+<p>To start testing and developing your application on Android 3.0, set up your existing Android
+SDK with the new platform:</p>
+
+<p>(If you don't have an existing Android SDK, <a href="{@docRoot}sdk/index.html">download the
+SDK starter package now</a>.)</p>
+
+<ol>
+ <li><a href="{@docRoot}sdk/adding-components.html#launching">Launch the Android SDK and AVD
+Manager</a> and install the following:
+ <ul>
+ <li>SDK Platform Android 3.0</li>
+ <li>Android SDK Tools, revision 10</li>
+ <li>Android SDK Platform-tools, revision 3</li>
+ <li>Documentation for Android SDK, API 11</li>
+ <li>Samples for SDK API 11</li>
+ </ul>
+ </li>
+ <li><a href="{@docRoot}guide/developing/other-ide.html#AVD">Create an AVD</a> for a tablet-type
+device:
+ <p>Set the target to "Android 3.0" and the skin to "WXGA" (the default skin).</p></li>
+</ol>
+
+
+<h3>About emulator performance</h3>
+
+<p>Because the Android emulator must simulate the ARM instruction set on your computer
+and the WXGA screen is significantly larger than a typical virtual device, emulator performance is
+much slower than a real device.</p>
+
+<p>In particular, initializing the emulator can be slow and can take several minutes, depending on
+your hardware. When the emulator is booting, there is limited user feedback, so please be patient
+and wait until you see the home screen (or lock screen) appear. </p>
+
+<p>However, you don't need to boot the emulator each time you rebuild your
+application—typically you only need to boot at the start of a session and keep it running.
+Also see the tip below for information about using a snapshot to drastically reduce startup time
+after the first initialization. </p>
+
+<p>We're working hard to resolve the performance issues and it will improve in future tools
+releases. For the time being, the emulator is still best way to evaluate your application's
+appearance and functionality on Android 3.0 without a real device.</p>
+
+<p class="note"><strong>Tip:</strong> To improve the startup time for the emulator, enable snapshots
+for the AVD when you create it with the SDK and AVD Manager (there's a checkbox in the AVD creator
+to <strong>Enable</strong> snapshots). Then, start the AVD from the AVD manager and check <b>Launch
+from snapshot</b> and <b>Save to snapshot</b>. This way, when you close the emulator, a snapshot of
+the AVD state is saved and used to quickly relaunch the AVD next time. However, when you choose to
+save a snapshot, the emulator will be slow to close, so you might want to disable <b>Save to
+snapshot</b> after you've acquired an initial snapshot (after you close the AVD for the first
+time).</p>
+
+
+
+<h2 id="Optimize">Optimize Your Application for Tablets and Similar Devices</h2>
+
+<p>If you've already developed an application for an earlier version of Android, there are a few
+things you can do to optimize it for a tablet-style experience on Android 3.0 without changing the
+minimum version required (you don't need to change your manifest's <a
+href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code
+android:minSdkVersion}</a>).</p>
+
+<p class="note"><strong>Note:</strong> All Android applications are forward-compatible, so
+there's nothing you <em>have to</em> do—if your application is a good citizen of the Android
+APIs, your app should work fine on devices running Android 3.0. However, in order to provide users
+a better experience when using your app on an Android 3.0 tablet or similar-size device, you
+should update your application to inherit the new system theme and provide some optimizations for
+larger screens.</p>
+
+<p>Here are a few things you can do to optimize your application for devices running Android
+3.0:</p>
+
+<ol>
+ <li><b>Test your current application on Android 3.0</b>
+ <ol>
+ <li>Build your application as-is and install it on your Android 3.0 AVD (created above during
+<a href="#Setup">setup</a>).</li>
+ <li>Perform your usual tests to be sure everything works and looks as expected.</li>
+ </ol>
+ </li>
+
+ <li><b>Apply the new "holographic" theme to your application</b>
+ <ol>
+ <li>Open your manifest file and update the <a
+href="{@docRoot}guide/topics/manifest/uses-sdk-element.html">{@code <uses-sdk>}</a> element to
+set <a
+href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
+android:targetSdkVersion}</a> to {@code "11"}. For example:
+<pre>
+<manifest ... >
+ <uses-sdk android:minSdkVersion="4"
+ android:targetSdkVersion="11" />
+ <application ... >
+ ...
+ <application>
+</manifest>
+</pre>
+ <p>By targeting the Android 3.0 platform, the system automatically applies the holographic theme
+to each activity when your application runs on an Android 3.0 device. The holographic theme
+provides a new design for widgets, such as buttons and text boxes, and restyles other
+visual elements. This is the standard theme in applications built for Android 3.0, so your
+application will look more at home by enabling the theme.</p>
+ <p>Additionally, the holographic theme enables the <a
+href="{@docRoot}guide/topics/ui/actionbar.html">Action Bar</a> in your activities when running on an
+Android 3.0 device. The Action Bar replaces the traditional title bar at the top of the activity
+window and provides the user access to the activity's Options Menu.</p>
+ </li>
+ <li>Continue to build your application against the minimum version specified by <a
+href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code android:minSdkVersion}</a>,
+but install it on the Android 3.0 AVD. Repeat your tests to be sure that your user interface works
+well with the holographic theme.
+ <p class="note"><strong>Note:</strong> If you have applied other themes directly to your
+activities, they will override the inherited holographic theme. To resolve this, you can use
+the <a href="{@docRoot}guide/topics/resources/providing-resources.html#VersionQualifier">system
+version qualifier</a> to provide an alternative theme for Android 3.0 devices that's based on the
+holographic theme. For more information, read how to <a
+href="{@docRoot}guide/topics/ui/themes.html#SelectATheme">select a theme based on platform
+version</a>.</p>
+ </ol>
+ </li>
+
+ <li><b>Supply alternative layout resources for xlarge screens</b>
+ <p>By providing <a
+href="{@docRoot}guide/topics/resources/providing-resources.html#AlternativeResources">alternative
+resources</a> when running on extra large screens (using the <code>xlarge</code> resource
+qualifier), you can improve the user experience of your application on tablet-type devices without
+using new APIs.</p>
+ <p>For example, here are some things to consider when creating a new layout for extra large
+screens:</p>
+ <ul>
+ <li>Landscape layout: The "normal" orientation for tablet-type devices is usually landscape
+(wide), so you should be sure that your activities offer a layout that's optimized for a wide
+viewing area. <p>You can specify landscape resources with the <code>land</code> resource
+qualifier, but if you want alternative resources for an extra large landscape screen, you
+should use both <code>xlarge</code> and <code>land</code> qualifiers. For example, {@code
+res/layout-xlarge-land/}. The order of the qualifier names is important; see <a
+href="{@docRoot}guide/topics/resources/providing-resources.html#AlternativeResources">
+Providing Alternative Resources</a> for more information.</p></li>
+ <li>Button position: Consider whether the position of the most common buttons in your UI are
+easily accessible while holding a tablet with two hands.</li>
+ <li>Font sizes: Be sure your application uses {@code sp} units when setting font
+sizes. This alone should ensure a readable experience on tablet-style devices. In some cases,
+however, you might want to consider larger font sizes for <code>xlarge</code> configurations.</li>
+ </ul>
+ <p>In general, always be sure that your application follows the <a
+href="{@docRoot}guide/practices/screens_support.html#screen-independence">Best Practices
+for Screen Independence</a>.</p>
+ </li>
+</ol>
+
+
+
+
+
+<h2 id="Upgrade">Upgrade or Develop a New App for Tablets and Similar Devices</h2>
+
+<p>If you want to develop an application that's fully enhanced for tablet-type devices running
+Android 3.0, then you need to use new APIs in Android 3.0. This section introduces some of
+the new features you should use.</p>
+
+
+<h3>Declare the minimum system version</h3>
+
+<p>The first thing to do when you create a project for Android 3.0 is set your manifest's <a
+href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code android:minSdkVersion}</a>
+to {@code "11"}. For example:</p>
+
+<pre>
+<manifest ... >
+ <uses-sdk android:minSdkVersion="11" />
+ <application ... >
+ ...
+ <application>
+</manifest>
+</pre>
+
+<p>By targeting the Android 3.0 platform, the system automatically applies the new holographic theme
+to each of your activities.</p>
+
+<p>Additionally, the holographic theme enables the Action Bar for each activity.</p>
+
+
+<h3>Use the Action Bar</h3>
+
+<p>The Action Bar is a widget for activities that replaces the traditional title bar at the top of
+the screen. By default, the Action Bar includes the application logo on the left side, followed by
+the activity title, and any available items from the Options Menu on the right side.</p>
+
+<p>You can enable items from your activity's Options Menu to appear directly in the Action Bar as
+"action items" by adding {@code showAsAction="ifRoom"} to specific items in your <a
+href="{@docRoot}guide/topics/resources/menu-resource.html">menu resource</a>. You can also add
+navigation features to the Action Bar, such as tabs, and use the application icon to navigate to
+your application's "home" activity or "up" the activity hierarchy.</p>
+
+<p>For more information, read <a href="{@docRoot}guide/topics/ui/actionbar.html">Using the
+Action Bar</a>.</p>
+
+
+
+<h3>Divide your activities into fragments</h3>
+
+<p>A fragment represents a behavior or a portion of user interface in an activity. You can combine
+multiple fragments in a single activity to build a multi-pane UI and reuse a fragment in multiple
+activities. You can think of a fragment as a modular section of an activity, which has its own
+lifecycle, receives its own input events, and which you can add or remove while the activity is
+running.</p>
+
+<p>For example, a news application can use one fragment to show a list of articles on the left and
+another fragment to display an article on the right—both fragments appear in one activity,
+side by side, and each fragment has its own set of lifecycle callback methods and handles its own
+input events. Thus, instead of using one activity to select an article and another activity to
+read the article, the user can select an article and read it all within the same activity.</p>
+
+<p>For more information, read the <a
+href="{@docRoot}guide/topics/fundamentals/fragments.html">Fragments</a> document.</p>
+
+
+<h3>Use new animation APIs for transitions</h3>
+
+<p>An all new flexible animation framework allows you to animate arbitrary properties of any object
+(View, Drawable, Fragment, Object, or anything else). You can define several animation aspects
+(such as duration, repeat, interpolation, and more) for an object's int, float, and hexadecimal
+color values, by default. That is, when an object has a property field for one of these types, you
+can change its value over time to affect an animation.</p>
+
+<p>The {@link android.view.View} class also provides new APIs that leverage the new animation
+framework, allowing you to easily apply 2D and 3D transformations to views in your activity layout.
+New transformations are made possible with a set of object properties that define the view's layout
+position, orientation, transparency and more.</p>
+
+<p>For more information, read the <a
+href="{@docRoot}guide/topics/graphics/animation.html">Property Animation</a> document.</p>
+
+
+<h3>Enable hardware acceleration</h3>
+
+<p>You can now enable the OpenGL renderer for your application by setting {@code
+android:hardwareAccelerated="true"} in your manifest's <a
+href="{@docRoot}guide/topics/manifest/application-element.html">{@code <application>}</a>
+element or for individual <a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code
+<activity>}</a> elements. Hardware acceleration results in smoother animations, smoother
+scrolling, and overall better performance and response to user interaction. When enabled, be sure
+that you thoroughly test your application on a device that supports hardware acceleration.</p>
+
+
+<h3>Enhance your app widgets</h3>
+
+<p>App widgets allow users to access information from your application directly from the Home
+screen and interact with ongoing services (such as preview their email and control music playback).
+Android 3.0 enhances these capabilities by enabling collections, created with widgets such as
+{@link android.widget.ListView}, {@link android.widget.GridView}, and the new {@link
+android.widget.StackView}. These widgets allow you to create more interactive app
+widgets, such as one with a scrolling list, and can automatically update their data through a {@link
+android.widget.RemoteViewsService}.</p>
+
+<p>Additionally, you should create a preview image of your app widget using the Widget Preview
+application (pre-installed in an Android 3.0 AVD) and reference it with the {@link
+android.appwidget.AppWidgetProviderInfo#previewImage android:previewImage} attribute, so that users
+can see what the app widget looks like before adding it to their Home screen.</p>
+
+
+<h3>Add other new features</h3>
+
+<p>Android 3.0 introduces many more APIs that you might find valuable for your
+application, such as drag and drop APIs, new Bluetooth APIs, a system-wide clipboard framework, a
+new graphics engine called Renderscript, and more.</p>
+
+<p>To learn more about the APIs mentioned above and more, see the <a
+href="{@docRoot}sdk/android-3.0.html">Android 3.0 Platform</a> document.</p>
+
+
+<h3>Publish your app for extra large screens</h3>
+
+<p>You should also decide whether your application is <em>only</em> for
+tablet-type devices (specifically, <em>xlarge</em> devices) or for all types of screen sizes.</p>
+
+<p>If you want your application to be available to all screen sizes (for example, for all
+phones and tablets), there's nothing you need to do. By default, an application with <a
+href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code
+android:minSdkVersion}</a> set to {@code "4"} or higher will resize to fit any screen size.</p>
+
+<p>If your application is <em>only</em> for <em>xlarge</em> screens, include the <a
+href="{@docRoot}guide/topics/manifest/supports-screens-element.html">{@code
+<supports-screens>}</a> element in your manifest and declare that the application supports
+only <em>xlarge</em> screens, by declaring all other sizes {@code "false"}. For example:</p>
+
+<pre>
+<manifest ... >
+ ...
+ <supports-screens android:smallScreens="false"
+ android:normalScreens="false"
+ android:largeScreens="false"
+ android:xlargeScreens="true" />
+ <application ... >
+ ...
+ <application>
+</manifest>
+</pre>
+
+<p>With this declaration, you indicate that your application does not support any screen size except
+extra large. External services such as Android Market may then use this information to filter your
+application from devices that do not have an extra large screen.</p>
+
+
+
+<h3>Look at some samples</h3>
+
+<p>Many of the new features and APIs that are described in the <a
+href="{@docRoot}sdk/android-3.0.html#api">Android 3.0 Platform Preview</a> also have accompanying
+samples that can help you understand how to use them. To get the samples, download them from the SDK
+repository using the Android SDK Manager. After downloading the samples ("Samples for SDK API 11"),
+you can find them in <code><sdk_root>/samples/android-11/</code>. The links below can help you
+find samples for the features you are interested in:</p>
+
+<ul>
+ <li><a href="{@docRoot}resources/samples/HoneycombGallery/index.html">Honeycomb Gallery</a>:
+Demonstrates many new APIs in Android 3.0, including fragments, the action bar, drag and drop, and
+animations.</li>
+ <li><a
+href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/index.html#Fragment">
+Fragments</a>: Various samples that demonstrate fragment layouts, back stack, restoring state, and
+more.</li>
+ <li><a
+href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/ActionBarMechanics.html"
+>Action Bar</a>: Samples that demonstrate various Action Bar features, such as tabs, logos, and
+action items.</li>
+ <li><a
+href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/content/ClipboardSample.
+html">Clipboard</a>: An example of how to use the clipboard for copy and paste operations.</li>
+ <li><a
+href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/view/DragAndDropDemo.html">
+Drag and Drop</a>: An example of how to perform drag and drop with new View events.</li>
+ <li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/view/List15.html">
+Multi-choice List</a>: An example of how to provide multiple-choice selection for ListView and
+GridView.</li>
+ <li><a
+href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LoaderThrottle.html">
+Content Loaders</a>: An example using new Loader APIs to asynchronously load data.</li>
+ <li><a
+href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/animation/index.html">
+Property Animation</a>: Several samples using the new animation APIs to animate object
+properties.</li>
+ <li><a
+href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/view/SearchViewActionBar.
+html">Search View Widget</a>: Example using the new search widget in the Action Bar (as an
+"action view").</li>
+ <li><a
+href="{@docRoot}resources/samples/Renderscript/index.html">Renderscript</a>: Contains several
+different applications that demonstrate using renderscript APIs for computations and 3D
+graphics.</li>
+</ul>
+
diff --git a/docs/html/sdk/android-3.0.jd b/docs/html/sdk/android-3.0.jd
index 8088e32..6c88146 100644
--- a/docs/html/sdk/android-3.0.jd
+++ b/docs/html/sdk/android-3.0.jd
@@ -25,7 +25,7 @@
<h2>See Also</h2>
<ol>
- <li><a href="{@docRoot}sdk/preview/start.html">Getting Started</a></li>
+ <li><a href="{@docRoot}sdk/android-3.0-optimize.html">Optimizing Apps for Android 3.0</a></li>
</ol>
</div>
@@ -48,6 +48,10 @@
href="{@docRoot}sdk/android-{@sdkPlatformVersion}-highlights.html">Platform
Highlights</a>.</p>
+<p>Also see the <a href="{@docRoot}sdk/android-3.0-optimize.html">Optimizing Apps for Android
+3.0</a> document for information about how to optimize your existing applications for Android 3.0
+devices, even if you want to remain compatible with previous versions.</p>
+
<h2 id="relnotes">Revisions</h2>
diff --git a/docs/html/sdk/preview/start.jd b/docs/html/sdk/preview/start.jd
deleted file mode 100644
index d6e442e..0000000
--- a/docs/html/sdk/preview/start.jd
+++ /dev/null
@@ -1,294 +0,0 @@
-page.title=Getting Started with the Android 3.0 Preview
-@jd:body
-
-<p>Welcome to Android 3.0!</p>
-
-<p>Android 3.0 is the next major release of the Android platform and is optimized for larger screen
-devices, particularly tablets. We're offering a preview SDK so you can get a head-start developing
-applications for it or simply test and optimize your existing application for upcoming devices.</p>
-
-<p><strong>Be aware that:</strong></p>
-<ul>
- <li>The APIs in the preview SDK are <strong>not final</strong>. Some APIs may change in behavior
-or availability when the final SDK is made available.</li>
- <li>You <strong>cannot</strong> publish an application that's built against the preview
-SDK—you can only run an application built against the preview SDK on the Android
-emulator.</li>
- <li>The documentation on <a href="http://developer.android.com">developer.android.com</a>
-does <strong>not</strong> include the Android 3.0 documentation—to read the API reference and
-developer guides for Android 3.0, you must install the Android 3.0 preview documentation from
-the AVD and SDK Manager.</li>
-</ul>
-
-
-
-<h3>How do I start?</h3>
-
-<ol>
- <li><a href="#Setup">Set up the preview SDK</a></li>
- <li>Then choose your app adventure:
- <ol type="a">
- <li><a href="#Optimize">Optimize Your App for Tablets and Similar Devices</a>
- <p>When you have an existing application and you want to maintain compatibility with
-older versions of Android.</p>
- </li>
- <li><a href="#Upgrade">Upgrade or Develop a New App for Tablets and Similar Devices</a>
- <p>When you want to upgrade your application to use APIs introduced in Android 3.0 or
- create a new application targeted to tablets and similar devices.</p></li>
- </ol>
- </li>
-</ol>
-
-<h3>Code samples</h3>
-<p>Many of the new features and APIs that are described in the <a href="{@docRoot}sdk/android-3.0.html#api">
-Android 3.0 Platform Preview</a> also have accompanying samples that help you understand how to use them.
-To get the samples, download them from the SDK repository using the Android SDK Manager. After download
-the samples are located in <code><sdk_root>/samples/android-Honeycomb</code>. The list of links
-below helps you find samples for the features you are interested in:</p>
-<ul>
- <li><a href="{@docRoot}resources/samples/HoneycombGallery/index.html">Honeycomb Gallery</a> -
- A demo application highlighting how to use some of the new APIs in Honeycomb, including fragments, the action bar,
- drag and drop, transition animations, and a stack widget.</li>
- <li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/index.html#Fragment">Fragments</a>
- <li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/ActionBarMechanics.html">Action Bar</a></li>
- <li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/content/ClipboardSample.html">Clipboard</a></li>
- <li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/view/DragAndDropDemo.html">Drag and Drop</a></li>
- <li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/view/List15.html">
- Multiple-choice selection for ListView and GridView</a></li>
- <li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LoaderThrottle.html">Content Loaders</a></li>
- <li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/animation/index.html">Property Animation</a></li>
- <li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/view/SearchViewActionBar.html">Search View Widget</a></li>
- <li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/view/PopupMenu1.html">Popup Menu Widget</a></li>
-</ul>
-
-
-<h2 id="Setup">Set Up the Preview SDK</h2>
-
-<p>To start using the Android 3.0 preview SDK, set up your existing Android SDK with the new
-platform:</p>
-<p>(If you don't have an existing SDK, <a href="{@docRoot}sdk/index.html">download it
-now</a>.)</p>
-<ol>
- <li><a href="{@docRoot}sdk/adding-components.html#launching">Launch the Android SDK and AVD
-Manager</a> and install the following:
- <ul>
- <li>SDK Platform Android 3.0 Preview</li>
- <li>Android SDK Tools, revision 9</li>
- <li>Documentation for Android 'Honeycomb' Preview</li>
- <li>Samples for SDK API Honeycomb Preview</li>
- </ul>
- </li>
- <li><a href="{@docRoot}guide/developing/other-ide.html#AVD">Create an AVD</a> for tablets: set
-the target to "Android 3.0 (Preview)" and the skin to "WXGA".</li>
-</ol>
-
-
-<h3>About emulator performance</h3>
-
-<p>Because the Android emulator must simulate the ARM instruction set architecture on your
-computer and the WXGA screen is significantly larger than what the emulator
-normally handles, emulator performance is much slower than usual.</p>
-
-<p>In particular, initializing the emulator can be slow and can take several
-minutes, depending on your hardware. When the emulator is booting there is
-limited user feedback, so please be patient and continue waiting until you see
-the home screen appear. </p>
-
-<p>Note that you do not need to do a full boot of your emulator each time you
-rebuild your application — typically you only need to boot at the start of
-a session. See the Tips section below for information about using Snapshots to
-cut startup time after first initialization. </p>
-
-<p>We're working hard to resolve the performance issues and it will improve in future releases.
-Unfortunately, the emulator will perform slowly during your trial with the preview SDK. For the time
-being, the emulator is still best way to evaluate your application's appearance and functionality on
-Android 3.0.</p>
-
-<p class="note"><strong>Tip:</strong> To improve the startup time for the emulator, enable
-snapshots for the AVD when you create it with the SDK and AVD Manager (there's a checkbox in
-the GUI). Then, start the AVD from the manager and check <b>Launch from snapshot</b> and <b>Save to
-snapshot</b>. This way, when you close the emulator, a snapshot of the AVD state is saved and
-used to quickly relaunch the AVD next time. However, when you choose to save a snapshot, the
-emulator will be slow to close, so you might want to enable <b>Save to
-snapshot</b> only for the first time you launch the AVD.</p>
-
-
-<h3>Known issues</h3>
-
-<p>The following known issues occur for Android 3.0 AVDs that are loaded in the emulator:</p>
- <ul>
- <li>You cannot take screenshots of an emulator screen. The Device Screen
- Capture window displays <strong>Screen not available</strong>.</li>
- <li>The emulator cannot receive incoming SMS messages.</li>
- <li>GPS emulation is currently not supported.</li>
- <li>When rotating the emulator screen by pressing Ctrl-F11, the screen turns green momentarily,
-then displays the normal interface.</li>
- <li>In some circumstances, the emulator displays a rotated portrait screen while in landscape
-mode. To view the screen correctly, rotate the emulator to portrait mode by pressing Ctrl-F11 or
-turn off the auto-rotate setting in <strong>Settings > Screen > Auto-rotate screen</strong>.</li>
- <li>The Dev Tools application sometimes crashes when trying to use the Package Browser
-feature.</li>
- <li>On Ubuntu 10.04 64-bit machines, you cannot create an AVD that has an SD card.</li>
- </ul>
-
-
-
-<h2 id="Optimize">Optimize Your Application for Tablets and Similar Devices</h2>
-
-<p>If you've already developed an application for Android, there are a few things you can do
-to optimize it for a tablet-style experience, without changing the minimum platform version required
-(you don't need to change the manifest {@code minSdkVersion}).</p>
-
-<p class="note"><strong>Note:</strong> All Android applications are forward-compatible, so
-there's nothing you <em>have to</em> do—if your application is a good citizen of the Android
-APIs, your app should work fine on devices running Android 3.0. However, in order to provide users
-a better experience when running your app on an Android 3.0 tablet or similar-size device, we
-recommend that you update your application to adapt to the new system theme and optimize your
-application for larger screens.</p>
-
-<p>Here's what you can do to optimize your application for tablets running Android
-3.0:</p>
-
-<ol>
- <li><b>Test your current application on Android 3.0</b>
- <ol>
- <li>Build your application as-is and install it on your WXGA AVD (created above).</li>
- <li>Perform your usual tests to be sure everything works and looks as expected.</li>
- </ol>
- </li>
-
- <li><b>Apply the new "holographic" theme to your application</b>
- <ol>
- <li>Open your manifest file and update the <a
-href="{@docRoot}guide/topics/manifest/uses-sdk-element.html">{@code <uses-sdk>}</a> element to
-set {@code android:targetSdkVersion} to {@code "Honeycomb"}. For example:
-<pre>
-<manifest ... >
- <uses-sdk android:minSdkVersion="4"
- android:targetSdkVersion="Honeycomb" />
- <application ... >
- ...
- <application>
-</manifest>
-</pre>
- <p class="note"><strong>Note:</strong> The API Level value "Honeycomb" is a provisional API
-Level that is valid only while testing against the preview SDK. You
-<strong>should not</strong> publish your application using this API Level. When the final version of
-the Android 3.0 SDK is made available, you must change this value to the real API Level that will be
-specified for Android 3.0. For more information, read about <a
-href="{@docRoot}guide/appendix/api-levels.html">Android API Levels</a>.</p>
- <p>By targeting the Android 3.0 platform, the system automatically applies the Holographic theme
-to each of your activities, when running on an Android 3.0 device.</p>
- </li>
- <li>Continue to build against your application's {@code minSdkVersion}, but install it
-on the Android 3.0 AVD. Perform more testing on your application to be sure that your user interface
-works well with the Holographic theme.
- <p class="note"><strong>Note:</strong> If you've applied themes to your activities already,
-they will override the Holographic theme that the system applies when you set the {@code
-android:targetSdkVersion} to {@code "Honeycomb"}.
-Once the Android 3.0 APIs are finalized and an official API Level is assigned, you can use
-the <a href="{@docRoot}guide/topics/resources/providing-resources.html#VersionQualifier">system
-version qualifier</a> to provide an alternative theme that's based on the Holographic theme when
-your application is running on Android 3.0.</p>
- </ol>
- </li>
-
- <li><b>Supply alternative layout resources for xlarge screens</b>
- <p>As discussed in the guide to <a
-href="{@docRoot}guide/practices/screens_support.html">Supporting Multiple Screens</a>, Android
-2.3 and above support the <code>xlarge</code> resource qualifier, which you should use to supply
-alternative layouts for extra large screens.</p>
- <p>By providing alternative layouts for some of your activities when running on extra large
-screens, you can improve the user experience of your application on a tablet without using any
-new APIs.</p>
- <p>For example, here are some things to consider when creating a new layout for tables:</p>
- <ul>
- <li>Landscape layout: The "normal" orientation for tablets is usually landscape (wide), so
-you should be sure that your activities offer an appropriate layout for such a wide viewing
-area.</li>
- <li>Button position: Consider whether the position of the most common buttons in your UI are
-easily accessible while holding a tablet with two hands.</li>
- </ul>
- </li>
-</ol>
-
- <p>In general, always be sure that your application follows the <a
-href="{@docRoot}guide/practices/screens_support.html#screen-independence">Best Practices
-for Screen Independence</a>.</p>
-
-
-<h2 id="Upgrade">Upgrade or Develop a New App for Tablets and Similar Devices</h2>
-
-<p>If you want to develop something truly for tablet-type devices running Android 3.0, then you need
-to use new APIs available in Android 3.0. This section introduces some of the new features that you
-should use.</p>
-
-<p>The first thing to do when you create a project with the Android 3.0 preview is set the <a
-href="{@docRoot}guide/topics/manifest/uses-sdk-element.html">{@code <uses-sdk>}</a> element to
-use {@code "Honeycomb"} for the {@code android:minSdkVersion}. For example:</p>
-
-<pre>
-<manifest ... >
- <uses-sdk android:minSdkVersion="Honeycomb" />
- <application ... >
- ...
- <application>
-</manifest>
-</pre>
-
-<p class="note"><strong>Note:</strong> The API Level value "Honeycomb" is a provisional API
-Level that is valid only while building and testing against the preview SDK. You
-<strong>cannot</strong> publish your application using this API Level. When the final version of the
-Android 3.0 SDK is made available, you must change this value to the real API Level that is
-specified for Android 3.0. For more information, read about <a
-href="{@docRoot}guide/appendix/api-levels.html">Android API Levels</a>.</p>
-
-<p>Be sure that the <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html">{@code
-<uses-sdk>}</a> element appears <strong>before</strong> the <a
-href="{@docRoot}guide/topics/manifest/application-element.html">{@code <application>}</a>
-element.</p>
-
-<p>By targeting the Android 3.0 platform (and declaring it before <a
-href="{@docRoot}guide/topics/manifest/application-element.html">{@code <application>}</a>),
-the system automatically applies the new Holographic theme to each of your
-activities.</p>
-
-
-
-<h3>Publishing your app for tablet-type devices only</h3>
-
-<p>Additionally, you should decide whether your application is for <em>only</em> tablet devices
-(specifically, <em>xlarge</em> devices) or for devices of all sizes that may run Android 3.0.</p>
-
-<p>If your application is <em>only</em> for tablets (<em>xlarge</em> screens; not for mobile
-devices/phones), then you should include the <a
-href="{@docRoot}guide/topics/manifest/supports-screens-element.html">{@code
-<supports-screens>}</a> element in your manifest with all sizes except for xlarge declared
-false. For example:</p>
-
-<pre>
-<manifest ... >
- <uses-sdk android:minSdkVersion="Honeycomb" />
- <supports-screens android:smallScreens="false"
- android:normalScreens="false"
- android:largeScreens="false"
- android:xlargeScreens="true" />
- <application ... >
- ...
- <application>
-</manifest>
-</pre>
-
-<p>With this declaration, you indicate that your application does not support any screen size except
-extra large. External services such as Android Market may use this to filter your application
-from devices that do not have an extra large screen.</p>
-
-<p>Otherwise, if you want your application to be available to both small devices (phones) and large
-devices (tablets), do <em>not</em> include the <a
-href="{@docRoot}guide/topics/manifest/supports-screens-element.html">{@code
-<supports-screens>}</a> element.</p>
-
-<div class="special">
-<p>To learn more about some of the new APIs,
-see the <a href="{@docRoot}sdk/android-3.0.html">Android 3.0 Platform Preview</a> document.</p>
-</div>
diff --git a/docs/html/sdk/sdk_toc.cs b/docs/html/sdk/sdk_toc.cs
index 1b94b2c..c1894d8 100644
--- a/docs/html/sdk/sdk_toc.cs
+++ b/docs/html/sdk/sdk_toc.cs
@@ -80,6 +80,7 @@
<div><a href="<?cs var:toroot ?>sdk/android-3.0.html">
<span class="en">Android 3.0 Platform</span></a> <span class="new">new!</span></div>
<ul>
+ <li><a href="<?cs var:toroot ?>sdk/android-3.0-optimize.html">Optimizing Apps for 3.0</a></li>
<li><a href="<?cs var:toroot ?>sdk/android-3.0-highlights.html">Platform Highlights</a></li>
<li><a href="<?cs var:toroot ?>sdk/api_diff/11/changes.html">API Differences Report »</a></li>
</ul>
@@ -88,10 +89,17 @@
<div><a href="<?cs var:toroot ?>sdk/android-2.3.3.html">
<span class="en">Android 2.3.3 Platform</span></a> <span class="new">new!</span></div>
<ul>
- <li><a href="<?cs var:toroot ?>sdk/android-2.3-highlights.html">Platform Highlights</a></li>
<li><a href="<?cs var:toroot ?>sdk/api_diff/10/changes.html">API Differences Report »</a></li>
</ul>
</li>
+ <li class="toggle-list">
+ <div><a href="<?cs var:toroot ?>sdk/android-2.3.html">
+ <span class="en">Android 2.3 Platform</span></a></div>
+ <ul>
+ <li><a href="<?cs var:toroot ?>sdk/android-2.3-highlights.html">Platform Highlights</a></li>
+ <li><a href="<?cs var:toroot ?>sdk/api_diff/9/changes.html">API Differences Report »</a></li>
+ </ul>
+ </li>
<li><a href="<?cs var:toroot ?>sdk/android-2.2.html">Android 2.2 Platform</a></li>
<li><a href="<?cs var:toroot ?>sdk/android-2.1.html">Android 2.1 Platform</a></li>
<li><a href="<?cs var:toroot ?>sdk/android-1.6.html">Android 1.6 Platform</a></li>
@@ -99,13 +107,6 @@
<li class="toggle-list">
<div><a href="#" onclick="toggle(this.parentNode.parentNode,true); return false;">Older Platforms</a></div>
<ul>
- <li class="toggle-list">
- <div><a href="<?cs var:toroot ?>sdk/android-2.3.html">
- <span class="en">Android 2.3 Platform</span></a></div>
- <ul>
- <li><a href="<?cs var:toroot ?>sdk/api_diff/9/changes.html">API Differences Report »</a></li>
- </ul>
- </li>
<li><a href="<?cs var:toroot ?>sdk/android-2.0.1.html">Android 2.0.1 Platform</a></li>
<li><a href="<?cs var:toroot ?>sdk/android-2.0.html">Android 2.0 Platform</a></li>
<li><a href="<?cs var:toroot ?>sdk/android-1.1.html">Android 1.1 Platform</a></li>
diff --git a/drm/libdrmframework/plugins/widevine/Android.mk b/drm/libdrmframework/plugins/widevine/Android.mk
new file mode 100644
index 0000000..60c8fad
--- /dev/null
+++ b/drm/libdrmframework/plugins/widevine/Android.mk
@@ -0,0 +1,48 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+ifeq ($(TARGET_ARCH),arm)
+
+LOCAL_SRC_FILES:= \
+ src/WVMDrmPlugin.cpp \
+ src/WVMLogging.cpp
+
+LOCAL_C_INCLUDES:= \
+ bionic \
+ bionic/libstdc++/include \
+ external/stlport/stlport \
+ vendor/widevine/proprietary/include \
+ frameworks/base/drm/libdrmframework/plugins/widevine/include
+
+LOCAL_MODULE := libdrmwvmplugin
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/drm
+
+LOCAL_STATIC_LIBRARIES := \
+ libdrmframeworkcommon
+
+LOCAL_SHARED_LIBRARIES := \
+ libutils \
+ libcutils \
+ libstlport \
+ libz \
+ libwvdrm \
+ libWVStreamControlAPI
+
+ifeq ($(TARGET_SIMULATOR),true)
+ LOCAL_LDLIBS += -ldl
+else
+ LOCAL_SHARED_LIBRARIES += libdl
+endif
+
+LOCAL_PRELINK_MODULE := false
+
+LOCAL_C_INCLUDES += \
+ $(TOP)/frameworks/base/drm/libdrmframework/include \
+ $(TOP)/frameworks/base/drm/libdrmframework/plugins/common/include \
+ $(TOP)/frameworks/base/include
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_SHARED_LIBRARY)
+
+endif
diff --git a/drm/libdrmframework/plugins/widevine/include/WVMDrmPlugin.h b/drm/libdrmframework/plugins/widevine/include/WVMDrmPlugin.h
new file mode 100644
index 0000000..efce025
--- /dev/null
+++ b/drm/libdrmframework/plugins/widevine/include/WVMDrmPlugin.h
@@ -0,0 +1,111 @@
+/*
+ * 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.
+ */
+
+#ifndef __WVMDRMPLUGIN_H__
+#define __WVMDRMPLUGIN_H__
+
+
+#include <AndroidConfig.h>
+#include <DrmEngineBase.h>
+
+#include "WVDRMPluginAPI.h"
+
+
+namespace android {
+
+ class WVMDrmPlugin : public DrmEngineBase
+ {
+
+public:
+ WVMDrmPlugin();
+ virtual ~WVMDrmPlugin();
+
+protected:
+ DrmConstraints* onGetConstraints(int uniqueId, const String8* path, int action);
+
+ DrmMetadata* onGetMetadata(int uniqueId, const String8* path);
+
+ status_t onInitialize(int uniqueId);
+
+ status_t onSetOnInfoListener(int uniqueId, const IDrmEngine::OnInfoListener* infoListener);
+
+ status_t onTerminate(int uniqueId);
+
+ bool onCanHandle(int uniqueId, const String8& path);
+
+ DrmInfoStatus* onProcessDrmInfo(int uniqueId, const DrmInfo* drmInfo);
+
+ status_t onSaveRights(int uniqueId, const DrmRights& drmRights,
+ const String8& rightsPath, const String8& contentPath);
+
+ DrmInfo* onAcquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInfoRequest);
+
+ String8 onGetOriginalMimeType(int uniqueId, const String8& path);
+
+ int onGetDrmObjectType(int uniqueId, const String8& path, const String8& mimeType);
+
+ int onCheckRightsStatus(int uniqueId, const String8& path, int action);
+
+ status_t onConsumeRights(int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve);
+
+ status_t onSetPlaybackStatus(
+ int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, off64_t position);
+
+ bool onValidateAction(
+ int uniqueId, const String8& path, int action, const ActionDescription& description);
+
+ status_t onRemoveRights(int uniqueId, const String8& path);
+
+ status_t onRemoveAllRights(int uniqueId);
+
+ status_t onOpenConvertSession(int uniqueId, int convertId);
+
+ DrmConvertedStatus* onConvertData(int uniqueId, int convertId, const DrmBuffer* inputData);
+
+ DrmConvertedStatus* onCloseConvertSession(int uniqueId, int convertId);
+
+ DrmSupportInfo* onGetSupportInfo(int uniqueId);
+
+ status_t onOpenDecryptSession(int uniqueId, DecryptHandle *decryptHandle,
+ int fd, off64_t offset, off64_t length);
+
+ status_t onOpenDecryptSession(int uniqueId, DecryptHandle *decryptHandle,
+ const char *uri);
+
+ status_t onCloseDecryptSession(int uniqueId, DecryptHandle* decryptHandle);
+
+ status_t onInitializeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle,
+ int decryptUnitId, const DrmBuffer* headerInfo);
+
+ status_t onDecrypt(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId,
+ const DrmBuffer* encBuffer, DrmBuffer** decBuffer,
+ DrmBuffer *ivBuffer);
+
+
+ status_t onFinalizeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId);
+
+ ssize_t onPread(int uniqueId, DecryptHandle* decryptHandle,
+ void* buffer, ssize_t numBytes, off64_t offset);
+
+private:
+ const IDrmEngine::OnInfoListener *mOnInfoListener;
+ WVDRMPluginAPI *mDrmPluginImpl;
+};
+
+};
+
+#endif /* __WVMDRMPLUGIN__ */
+
diff --git a/drm/libdrmframework/plugins/widevine/include/WVMLogging.h b/drm/libdrmframework/plugins/widevine/include/WVMLogging.h
new file mode 100644
index 0000000..1e46c7a
--- /dev/null
+++ b/drm/libdrmframework/plugins/widevine/include/WVMLogging.h
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+
+#ifndef __WVMLOGGING_H__
+#define __WVMLOGGING_H__
+
+void android_printbuf(const char *buf);
+
+#endif
diff --git a/drm/libdrmframework/plugins/widevine/src/WVMDrmPlugin.cpp b/drm/libdrmframework/plugins/widevine/src/WVMDrmPlugin.cpp
new file mode 100644
index 0000000..b39599c
--- /dev/null
+++ b/drm/libdrmframework/plugins/widevine/src/WVMDrmPlugin.cpp
@@ -0,0 +1,726 @@
+/*
+ * 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.
+ */
+#define LOG_NDEBUG 0
+#define LOG_TAG "WVMDrmPlugIn"
+#include <utils/Log.h>
+#include <vector>
+
+#include <drm/DrmRights.h>
+#include <drm/DrmConstraints.h>
+#include <drm/DrmInfo.h>
+#include <drm/DrmInfoStatus.h>
+#include <drm/DrmConvertedStatus.h>
+#include <drm/DrmInfoRequest.h>
+#include <drm/DrmSupportInfo.h>
+#include <drm/DrmMetadata.h>
+
+#include "WVMDrmPlugin.h"
+#include "WVMLogging.h"
+#include "AndroidHooks.h"
+
+using namespace std;
+using namespace android;
+
+
+// This extern "C" is mandatory to be managed by TPlugInManager
+extern "C" IDrmEngine* create() {
+ return new WVMDrmPlugin();
+}
+
+// This extern "C" is mandatory to be managed by TPlugInManager
+extern "C" void destroy(IDrmEngine* pPlugIn) {
+ delete pPlugIn;
+}
+
+WVMDrmPlugin::WVMDrmPlugin()
+ : DrmEngineBase(),
+ mOnInfoListener(NULL),
+ mDrmPluginImpl(WVDRMPluginAPI::create())
+{
+}
+
+WVMDrmPlugin::~WVMDrmPlugin() {
+ WVDRMPluginAPI::destroy(mDrmPluginImpl);
+}
+
+
+/**
+ * Initialize plug-in
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+status_t WVMDrmPlugin::onInitialize(int uniqueId) {
+ //LOGD("WVMDrmPlugin::onInitialize : %d", uniqueId);
+ AndroidSetLogCallout(android_printbuf);
+ return DRM_NO_ERROR;
+}
+
+/**
+ * Terminate the plug-in
+ * and release resource bound to plug-in
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+status_t WVMDrmPlugin::onTerminate(int uniqueId) {
+ //LOGD("WVMDrmPlugin::onTerminate : %d", uniqueId);
+ return DRM_NO_ERROR;
+}
+
+/**
+ * Register a callback to be invoked when the caller required to
+ * receive necessary information
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] infoListener Listener
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+status_t WVMDrmPlugin::onSetOnInfoListener(
+ int uniqueId, const IDrmEngine::OnInfoListener* infoListener) {
+ //LOGD("WVMDrmPlugin::onSetOnInfoListener : %d", uniqueId);
+ mOnInfoListener = infoListener;
+ return DRM_NO_ERROR;
+}
+
+/**
+ * Retrieves necessary information for registration, unregistration or rights
+ * acquisition information.
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] drmInfoRequest Request information to retrieve drmInfo
+ * @return DrmInfo
+ * instance as a result of processing given input
+ */
+DrmInfo* WVMDrmPlugin::onAcquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInfoRequest) {
+ //LOGD("WVMDrmPlugin::onAcquireDrmInfo : %d", uniqueId);
+ DrmInfo* drmInfo = NULL;
+
+ std::string assetPath;
+
+ if (NULL != drmInfoRequest) {
+ switch(drmInfoRequest->getInfoType()) {
+ case DrmInfoRequest::TYPE_RIGHTS_ACQUISITION_INFO: {
+
+ assetPath = drmInfoRequest->get(String8("WVAssetURIKey")).string();
+
+ WVCredentials credentials;
+
+ // creates a data store object per each portal
+ credentials.portal = drmInfoRequest->get(String8("WVPortalKey")).string();
+ if ( (assetPath.size() == 0) || (credentials.portal.size() == 0) ) {
+ LOGE("onAcquireDrmInfo: Empty asset path or portal string, must specify both");
+ return NULL;
+ }
+
+ std::string assetDbPath = drmInfoRequest->get(String8("WVAssetDBPathKey")).string();
+ //LOGV("onAcquireDrmInfo: portal=%s, dsPath=%s", credentials.portal.c_str(), assetDbPath.c_str());
+
+ credentials.drmServerURL = drmInfoRequest->get(String8("WVDRMServerKey")).string();
+ credentials.userData = drmInfoRequest->get(String8("WVCAUserDataKey")).string();
+ credentials.deviceID = drmInfoRequest->get(String8("WVDeviceIDKey")).string();
+ credentials.streamID = drmInfoRequest->get(String8("WVStreamIDKey")).string();
+
+ string systemIdStr = drmInfoRequest->get(String8("WVSystemIDKey")).string();
+ string assetIdStr = drmInfoRequest->get(String8("WVAssetIDKey")).string();
+ string keyIdStr = drmInfoRequest->get(String8("WVKeyIDKey")).string();
+
+ uint32_t systemId, assetId, keyId;
+
+ if (!mDrmPluginImpl->AcquireDrmInfo(assetPath, credentials, assetDbPath,
+ systemIdStr, assetIdStr, keyIdStr,
+ &systemId, &assetId, &keyId))
+ return NULL;
+
+
+ String8 dataString("dummy_acquistion_string");
+ int length = dataString.length();
+ char* data = NULL;
+ data = new char[length];
+ memcpy(data, dataString.string(), length);
+ drmInfo = new DrmInfo(drmInfoRequest->getInfoType(),
+ DrmBuffer(data, length), drmInfoRequest->getMimeType());
+
+ // Sets additional drmInfo attributes
+ drmInfo->put(String8("WVAssetURIKey"), String8(assetPath.c_str()));
+ drmInfo->put(String8("WVDRMServerKey"), String8(credentials.drmServerURL.c_str()));
+ drmInfo->put(String8("WVAssetDbPathKey"), String8(assetDbPath.c_str()));
+ drmInfo->put(String8("WVPortalKey"), String8(credentials.portal.c_str()));
+ drmInfo->put(String8("WVCAUserDataKey"), String8(credentials.userData.c_str()));
+ drmInfo->put(String8("WVDeviceIDKey"), String8(credentials.deviceID.c_str()));
+ drmInfo->put(String8("WVStreamIDKey"), String8(credentials.streamID.c_str()));
+
+ char buffer[16];
+ sprintf(buffer, "%lu", (unsigned long)systemId);
+ drmInfo->put(String8("WVSystemIDKey"), String8(buffer));
+ sprintf(buffer, "%lu", (unsigned long)assetId);
+ drmInfo->put(String8("WVAssetIDKey"), String8(buffer));
+ sprintf(buffer, "%lu", (unsigned long)keyId);
+ drmInfo->put(String8("WVKeyIDKey"), String8(buffer));
+ break;
+ }
+ case DrmInfoRequest::TYPE_REGISTRATION_INFO:
+ case DrmInfoRequest::TYPE_UNREGISTRATION_INFO:
+ case DrmInfoRequest::TYPE_RIGHTS_ACQUISITION_PROGRESS_INFO: {
+ LOGE("onAcquireDrmInfo: Unsupported DrmInfoRequest type %d",
+ drmInfoRequest->getInfoType());
+ break;
+ }
+ default: {
+ LOGE("onAcquireDrmInfo: Unknown info type %d", drmInfoRequest->getInfoType());
+ break;
+ }
+ }
+ }
+ return drmInfo;
+}
+
+/**
+ * Executes given drm information based on its type
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] drmInfo Information needs to be processed
+ * @return DrmInfoStatus
+ * instance as a result of processing given input
+ */
+DrmInfoStatus* WVMDrmPlugin::onProcessDrmInfo(int uniqueId, const DrmInfo* drmInfo) {
+ //LOGD("WVMDrmPlugin::onProcessDrmInfo: %d", uniqueId);
+
+ int status = DrmInfoStatus::STATUS_ERROR;
+
+ if (NULL != drmInfo) {
+ if (drmInfo->getInfoType() == DrmInfoRequest::TYPE_RIGHTS_ACQUISITION_INFO) {
+ std::string assetPath = drmInfo->get(String8("WVAssetURIKey")).string();
+
+ if (mDrmPluginImpl->ProcessDrmInfo(assetPath))
+ status = DrmInfoStatus::STATUS_OK;
+ } else {
+ LOGE("onProcessDrmInfo : drmInfo type %d not supported", drmInfo->getInfoType());
+ }
+ } else {
+ LOGE("onProcessDrmInfo : drmInfo cannot be NULL");
+ }
+
+ String8 licenseString("dummy_license_string");
+ const int bufferSize = licenseString.size();
+ char* data = NULL;
+ data = new char[bufferSize];
+ memcpy(data, licenseString.string(), bufferSize);
+ const DrmBuffer* buffer = new DrmBuffer(data, bufferSize);
+ DrmInfoStatus* drmInfoStatus =
+ new DrmInfoStatus(status, drmInfo->getInfoType(), buffer, drmInfo->getMimeType());
+
+ return drmInfoStatus;
+}
+
+/**
+ * Get constraint information associated with input content
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] path Path of the protected content
+ * @param[in] action Actions defined such as,
+ * Action::DEFAULT, Action::PLAY, etc
+ * @return DrmConstraints
+ * key-value pairs of constraint are embedded in it
+ * @note
+ * In case of error, return NULL
+ */
+DrmConstraints* WVMDrmPlugin::onGetConstraints(int uniqueId, const String8* path, int action)
+{
+ //LOGD("WVMDrmPlugin::onGetConstraints : %d", uniqueId);
+
+ if ( (Action::DEFAULT != action) && (Action::PLAY != action) ) {
+ LOGE("onGetConstraints : action %d not supported", action);
+ return NULL;
+ }
+
+ uint32_t licenseDuration = 0;
+ uint32_t timeSincePlayback = 0;
+ uint32_t timeRemaining = 0;
+
+ std::string assetPath(path->string());
+ if (!mDrmPluginImpl->GetConstraints(assetPath, &timeSincePlayback, &timeRemaining, &licenseDuration))
+ return NULL;
+
+ DrmConstraints* drmConstraints = new DrmConstraints();
+ char charValue[16]; // max uint32 = 0xffffffff + terminating char
+
+ memset(charValue, 0, 16);
+ sprintf(charValue, "%lu", (unsigned long)timeSincePlayback);
+ drmConstraints->put(&(DrmConstraints::LICENSE_START_TIME), charValue);
+
+ memset(charValue, 0, 16);
+ sprintf(charValue, "%lu", (unsigned long)timeRemaining);
+ drmConstraints->put(&(DrmConstraints::LICENSE_EXPIRY_TIME), charValue);
+
+ memset(charValue, 0, 16);
+ sprintf(charValue, "%lu", (unsigned long)licenseDuration);
+ drmConstraints->put(&(DrmConstraints::LICENSE_AVAILABLE_TIME), charValue);
+
+ return drmConstraints;
+}
+
+
+/**
+ * Returns the information about the Drm Engine capabilities which includes
+ * supported MimeTypes and file suffixes.
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @return DrmSupportInfo
+ * instance which holds the capabilities of a plug-in
+ */
+DrmSupportInfo* WVMDrmPlugin::onGetSupportInfo(int uniqueId) {
+ //LOGD("WVMDrmPlugin::onGetSupportInfo : %d", uniqueId);
+ DrmSupportInfo* drmSupportInfo = new DrmSupportInfo();
+ // Add mimetype's
+ drmSupportInfo->addMimeType(String8("video/wvm"));
+ // Add File Suffixes
+ drmSupportInfo->addFileSuffix(String8(".wvm"));
+ // Add plug-in description
+ drmSupportInfo->setDescription(String8("Widevine DRM plug-in"));
+ return drmSupportInfo;
+}
+
+/**
+ * Get meta data from protected content
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] path Path of the protected content
+ *
+ * @return DrmMetadata
+ * key-value pairs of meta data; NULL if failed
+ */
+DrmMetadata* WVMDrmPlugin::onGetMetadata(int uniqueId, const String8* path) {
+ //LOGD("WVDrmPlugin::onGetMetadata returns NULL\n");
+ return NULL;
+}
+
+/**
+ * Save DRM rights to specified rights path
+ * and make association with content path
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] drmRights DrmRights to be saved
+ * @param[in] rightsPath File path where rights to be saved
+ * @param[in] contentPath File path where content was saved
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+status_t WVMDrmPlugin::onSaveRights(int uniqueId, const DrmRights& drmRights,
+ const String8& rightsPath, const String8& contentPath) {
+ //LOGD("WVMDrmPlugin::onSaveRights : %d", uniqueId);
+ return DRM_NO_ERROR;
+}
+
+/**
+ * Get whether the given content can be handled by this plugin or not
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] path Path the protected object
+ * @return bool
+ * Returns true if this plugin can handle , false in case of not able to handle
+ */
+bool WVMDrmPlugin::onCanHandle(int uniqueId, const String8& path) {
+ //LOGD("WVMDrmPlugin::canHandle('%s') ", path.string());
+ String8 extension = path.getPathExtension();
+ extension.toLower();
+ return (String8(".wvm") == extension);
+}
+
+/**
+ * Retrieves the mime type embedded inside the original content
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] path Path of the protected content
+ * @return String8
+ * Returns mime-type of the original content, such as "video/mpeg"
+ */
+String8 WVMDrmPlugin::onGetOriginalMimeType(int uniqueId, const String8& path) {
+ //LOGD("WVMDrmPlugin::onGetOriginalMimeType() : %d", uniqueId);
+ return String8("video/wvm");
+}
+
+/**
+ * Retrieves the type of the protected object (content, rights, etc..)
+ * using specified path or mimetype. At least one parameter should be non null
+ * to retrieve DRM object type
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] path Path of the content or null.
+ * @param[in] mimeType Mime type of the content or null.
+ * @return type of the DRM content,
+ * such as DrmObjectType::CONTENT, DrmObjectType::RIGHTS_OBJECT
+ */
+int WVMDrmPlugin::onGetDrmObjectType(
+ int uniqueId, const String8& path, const String8& mimeType) {
+ //LOGD("WVMDrmPlugin::onGetDrmObjectType() : %d", uniqueId);
+ return DrmObjectType::UNKNOWN;
+}
+
+/**
+ * Check whether the given content has valid rights or not
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] path Path of the protected content
+ * @param[in] action Action to perform (Action::DEFAULT, Action::PLAY, etc)
+ * @return the status of the rights for the protected content,
+ * such as RightsStatus::RIGHTS_VALID, RightsStatus::RIGHTS_EXPIRED, etc.
+ */
+int WVMDrmPlugin::onCheckRightsStatus(int uniqueId, const String8& path, int action) {
+ //LOGD("WVMDrmPlugin::onCheckRightsStatus() : %d", uniqueId);
+
+ if ( (Action::DEFAULT != action) && (Action::PLAY != action) ) {
+ LOGE("onCheckRightsStatus : action %d not supported", action);
+ return RightsStatus::RIGHTS_INVALID;
+ }
+
+ std::string assetPath(path.string());
+ int rightsStatus = mDrmPluginImpl->CheckRightsStatus(assetPath);
+
+ switch(rightsStatus) {
+ case WVDRMPluginAPI::RIGHTS_INVALID:
+ return RightsStatus::RIGHTS_INVALID;
+ break;
+ case WVDRMPluginAPI::RIGHTS_EXPIRED:
+ return RightsStatus::RIGHTS_EXPIRED;
+ break;
+ case WVDRMPluginAPI::RIGHTS_VALID:
+ return RightsStatus::RIGHTS_VALID;
+ break;
+ case WVDRMPluginAPI::RIGHTS_NOT_ACQUIRED:
+ return RightsStatus::RIGHTS_NOT_ACQUIRED;
+ break;
+ }
+ return RightsStatus::RIGHTS_INVALID;
+}
+
+/**
+ * Consumes the rights for a content.
+ * If the reserve parameter is true the rights is reserved until the same
+ * application calls this api again with the reserve parameter set to false.
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] decryptHandle Handle for the decryption session
+ * @param[in] action Action to perform. (Action::DEFAULT, Action::PLAY, etc)
+ * @param[in] reserve True if the rights should be reserved.
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+status_t WVMDrmPlugin::onConsumeRights(int uniqueId, DecryptHandle* decryptHandle,
+ int action, bool reserve) {
+ //LOGD("WVMDrmPlugin::onConsumeRights() : %d", uniqueId);
+ return DRM_NO_ERROR;
+}
+
+/**
+ * Informs the DRM Engine about the playback actions performed on the DRM files.
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] decryptHandle Handle for the decryption session
+ * @param[in] playbackStatus Playback action (Playback::START, Playback::STOP, Playback::PAUSE)
+ * @param[in] position Position in the file (in milliseconds) where the start occurs.
+ * Only valid together with Playback::START.
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+status_t WVMDrmPlugin::onSetPlaybackStatus(int uniqueId, DecryptHandle* decryptHandle,
+ int playbackStatus, off64_t position) {
+ //LOGD("WVMDrmPlugin::onSetPlaybackStatus");
+
+ int op;
+
+ switch(playbackStatus) {
+ case Playback::START:
+ op = WVDRMPluginAPI::PLAYBACK_START;
+ break;
+ case Playback::STOP:
+ op = WVDRMPluginAPI::PLAYBACK_STOP;
+ break;
+ case Playback::PAUSE:
+ op = WVDRMPluginAPI::PLAYBACK_PAUSE;
+ break;
+ default:
+ op = WVDRMPluginAPI::PLAYBACK_INVALID;
+ break;
+ }
+
+ if (mDrmPluginImpl->SetPlaybackStatus(op, position))
+ return DRM_NO_ERROR;
+
+ return DRM_ERROR_UNKNOWN;
+}
+
+/**
+ * Validates whether an action on the DRM content is allowed or not.
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] path Path of the protected content
+ * @param[in] action Action to validate (Action::PLAY, Action::TRANSFER, etc)
+ * @param[in] description Detailed description of the action
+ * @return true if the action is allowed.
+ */
+bool WVMDrmPlugin::onValidateAction(int uniqueId, const String8& path,
+ int action, const ActionDescription& description) {
+ //LOGD("WVMDrmPlugin::onValidateAction() : %d", uniqueId);
+ return true;
+}
+
+/**
+ * Removes the rights associated with the given protected content
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] path Path of the protected content
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+status_t WVMDrmPlugin::onRemoveRights(int uniqueId, const String8& path) {
+ //LOGD("WVMDrmPlugin::onRemoveRights() : %d", uniqueId);
+
+ std::string assetPath(path.string());
+ if (mDrmPluginImpl->RemoveRights(assetPath))
+ return DRM_NO_ERROR;
+
+ return DRM_ERROR_UNKNOWN;
+}
+
+/**
+ * Removes all the rights information of each plug-in associated with
+ * DRM framework. Will be used in master reset
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+status_t WVMDrmPlugin::onRemoveAllRights(int uniqueId) {
+ //LOGD("WVMDrmPlugin::onRemoveAllRights() : %d", uniqueId);
+
+ if (mDrmPluginImpl->RemoveAllRights())
+ return DRM_NO_ERROR;
+
+ return DRM_ERROR_UNKNOWN;
+}
+
+/**
+ * Open the decrypt session to decrypt the given protected content
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] decryptHandle Handle for the current decryption session
+ * @param[in] fd File descriptor of the protected content to be decrypted
+ * @param[in] offset Start position of the content
+ * @param[in] length The length of the protected content
+ * @return
+ * DRM_ERROR_CANNOT_HANDLE for failure and DRM_NO_ERROR for success
+ */
+status_t WVMDrmPlugin::onOpenDecryptSession(
+ int uniqueId, DecryptHandle *decryptHandle, int fd, off64_t offset, off64_t length)
+{
+ status_t result = DRM_ERROR_CANNOT_HANDLE;
+
+ //LOGD("onOpenDecryptSession: fd=%d, offset=%lld, length=%lld", fd, offset, length);
+
+ char buffer[64 * 1024];
+ int dupfd = dup(fd);
+ if (dupfd == -1)
+ return result;
+
+ FILE *f = fdopen(dupfd, "rb");
+ if (f) {
+ fseek(f, 0, SEEK_SET);
+ if (fread(buffer, 1, sizeof(buffer), f) != sizeof(buffer)) {
+ fclose(f);
+ return DRM_ERROR_CANNOT_HANDLE;
+ }
+ fclose(f);
+ } else {
+ close(dupfd);
+ return result;
+ }
+
+ if (WV_IsWidevineMedia(buffer, sizeof(buffer))) {
+ //LOGD("WVMDrmPlugin::onOpenDecryptSession - WV_IsWidevineMedia: true");
+ decryptHandle->mimeType = String8("video/wvm");
+ decryptHandle->decryptApiType = DecryptApiType::WV_BASED;
+ decryptHandle->status = DRM_NO_ERROR;
+ decryptHandle->decryptInfo = NULL;
+ result = DRM_NO_ERROR;
+ } else {
+ //LOGD("WVMDrmPlugin::onOpenDecryptSession - not Widevine media");
+ }
+
+ return result;
+}
+
+/**
+ * Open the decrypt session to decrypt the given protected content
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] decryptHandle Handle for the current decryption session
+ * @param[in] uri Path of the protected content to be decrypted
+ * @return
+ * DRM_ERROR_CANNOT_HANDLE for failure and DRM_NO_ERROR for success
+ */
+status_t WVMDrmPlugin::onOpenDecryptSession(
+ int uniqueId, DecryptHandle* decryptHandle, const char* uri)
+{
+ status_t result = DRM_ERROR_CANNOT_HANDLE;
+
+ if (!uri)
+ return result;
+
+ size_t len = strlen(uri);
+
+ if ((len >= 4 && !strncmp(&uri[len - 4], ".wvm", 4)) ||
+ (strstr(uri, ".wvm?") != NULL) ||
+ (len >= 5 && !strncmp(&uri[len - 5], ".m3u8", 5)) ||
+ (strstr(uri, ".m3u8?") != NULL))
+ {
+ //LOGD("WVMDrmPlugin::onOpenDecryptSession(uri) : %d - match", uniqueId);
+ decryptHandle->mimeType = String8("video/wvm");
+ decryptHandle->decryptApiType = DecryptApiType::WV_BASED;
+ decryptHandle->status = DRM_NO_ERROR;
+ decryptHandle->decryptInfo = NULL;
+
+ mDrmPluginImpl->OpenSession();
+ result = DRM_NO_ERROR;
+ } else {
+ //LOGD("WVMDrmPlugin::onOpenDecryptSession(uri) - not Widevine media");
+ }
+
+
+ return result;
+}
+
+
+/**
+ * Close the decrypt session for the given handle
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] decryptHandle Handle for the decryption session
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+status_t WVMDrmPlugin::onCloseDecryptSession(int uniqueId, DecryptHandle* decryptHandle) {
+ //LOGD("WVMDrmPlugin::onCloseDecryptSession() : %d", uniqueId);
+ if (NULL != decryptHandle) {
+ if (NULL != decryptHandle->decryptInfo) {
+ delete decryptHandle->decryptInfo; decryptHandle->decryptInfo = NULL;
+ }
+ delete decryptHandle; decryptHandle = NULL;
+ }
+ mDrmPluginImpl->CloseSession();
+ return DRM_NO_ERROR;
+}
+
+/**
+ * Initialize decryption for the given unit of the protected content
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] decryptId Handle for the decryption session
+ * @param[in] decryptUnitId ID Specifies decryption unit, such as track ID
+ * @param[in] headerInfo Information for initializing decryption of this decrypUnit
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+status_t WVMDrmPlugin::onInitializeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle,
+ int decryptUnitId, const DrmBuffer* headerInfo) {
+ //LOGD("WVMDrmPlugin::onInitializeDecryptUnit(): %d", uniqueId);
+ if (!mDrmPluginImpl->Prepare(headerInfo->data, headerInfo->length))
+ return DRM_ERROR_CANNOT_HANDLE;
+
+ return DRM_NO_ERROR;
+}
+
+/**
+ * Decrypt the protected content buffers for the given unit
+ * This method will be called any number of times, based on number of
+ * encrypted streams received from application.
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] decryptId Handle for the decryption session
+ * @param[in] decryptUnitId ID Specifies decryption unit, such as track ID
+ * @param[in] encBuffer Encrypted data block
+ * @param[out] decBuffer Decrypted data block
+ * @param[in] IV Optional buffer
+ * @return status_t
+ * Returns the error code for this API
+ * DRM_NO_ERROR for success, and one of DRM_ERROR_UNKNOWN, DRM_ERROR_LICENSE_EXPIRED
+ * DRM_ERROR_SESSION_NOT_OPENED, DRM_ERROR_DECRYPT_UNIT_NOT_INITIALIZED,
+ * DRM_ERROR_DECRYPT for failure.
+ */
+status_t WVMDrmPlugin::onDecrypt(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId,
+ const DrmBuffer* encBuffer, DrmBuffer** decBuffer,
+ DrmBuffer *ivBuffer)
+{
+ //LOGD("WVMDrmPlugin::onDecrypt\n");
+#define AES_BLOCK_SIZE 16
+ char iv[AES_BLOCK_SIZE];
+ memcpy(iv, ivBuffer->data, sizeof(iv));
+
+ if (*decBuffer == NULL)
+ return DRM_ERROR_DECRYPT;
+
+ (*decBuffer)->length = encBuffer->length;
+
+ if (!mDrmPluginImpl->Operate(encBuffer->data, (*decBuffer)->data, encBuffer->length, iv)) {
+ (*decBuffer)->length = 0;
+ usleep(1000000); // prevent spinning
+ return DRM_ERROR_LICENSE_EXPIRED;
+ }
+
+ return DRM_NO_ERROR;
+}
+
+/**
+ * Finalize decryption for the given unit of the protected content
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] decryptHandle Handle for the decryption session
+ * @param[in] decryptUnitId ID Specifies decryption unit, such as track ID
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+status_t WVMDrmPlugin::onFinalizeDecryptUnit(
+ int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId) {
+ //LOGD("WVMDrmPlugin::onFinalizeDecryptUnit() : %d", uniqueId);
+ return DRM_NO_ERROR;
+}
+
+/**
+ * The following methods are not required for the Widevine DRM plugin
+ */
+ssize_t WVMDrmPlugin::onPread(int uniqueId, DecryptHandle* decryptHandle,
+ void* buffer, ssize_t numBytes, off64_t offset) {
+ return DRM_ERROR_UNKNOWN;
+}
+
+
+status_t WVMDrmPlugin::onOpenConvertSession(int uniqueId, int convertId) {
+ return DRM_ERROR_UNKNOWN;
+}
+
+DrmConvertedStatus* WVMDrmPlugin::onConvertData(
+ int uniqueId, int convertId, const DrmBuffer* inputData) {
+ return NULL;
+}
+
+DrmConvertedStatus* WVMDrmPlugin::onCloseConvertSession(int uniqueId, int convertId) {
+ return NULL;
+}
+
+
diff --git a/drm/libdrmframework/plugins/widevine/src/WVMLogging.cpp b/drm/libdrmframework/plugins/widevine/src/WVMLogging.cpp
new file mode 100644
index 0000000..a3b9bd7
--- /dev/null
+++ b/drm/libdrmframework/plugins/widevine/src/WVMLogging.cpp
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+#define LOG_TAG "WVMLogging"
+#include <utils/Log.h>
+#include "WVMLogging.h"
+
+// Connect Widevine debug logging into Android logging
+
+void android_printbuf(const char *buf)
+{
+ LOGD("%s", buf);
+}
diff --git a/drm/libdrmframework/plugins/widevine/test/Android.mk b/drm/libdrmframework/plugins/widevine/test/Android.mk
new file mode 100644
index 0000000..d3fa57c
--- /dev/null
+++ b/drm/libdrmframework/plugins/widevine/test/Android.mk
@@ -0,0 +1,32 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ TestPlugin.cpp \
+ ../src/WVMLogging.cpp
+
+LOCAL_C_INCLUDES+= \
+ bionic \
+ vendor/widevine/proprietary/include \
+ external/stlport/stlport \
+ frameworks/base/drm/libdrmframework/include \
+ frameworks/base/drm/libdrmframework/plugins/common/include \
+ frameworks/base/drm/libdrmframework/plugins/widevine/include
+
+LOCAL_SHARED_LIBRARIES := \
+ libstlport \
+ libwvdrm \
+ liblog \
+ libutils \
+ libz \
+ libdl
+
+LOCAL_STATIC_LIBRARIES := \
+ libdrmframeworkcommon
+
+LOCAL_MODULE:=test-wvdrmplugin
+
+LOCAL_MODULE_TAGS := tests
+
+include $(BUILD_EXECUTABLE)
+
diff --git a/drm/libdrmframework/plugins/widevine/test/TestPlugin.cpp b/drm/libdrmframework/plugins/widevine/test/TestPlugin.cpp
new file mode 100644
index 0000000..1942257
--- /dev/null
+++ b/drm/libdrmframework/plugins/widevine/test/TestPlugin.cpp
@@ -0,0 +1,237 @@
+/*
+ * 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.
+ */
+
+#include <dlfcn.h>
+
+#include <iostream>
+
+#include "WVMDrmPlugin.h"
+#include "drm/DrmInfoRequest.h"
+#include "drm/DrmInfoStatus.h"
+#include "drm/DrmConstraints.h"
+#include "drm/DrmInfo.h"
+
+using namespace android;
+using namespace std;
+
+class WVMDrmPluginTest
+{
+public:
+ WVMDrmPluginTest() {}
+ ~WVMDrmPluginTest() {}
+
+ void TestAsset(IDrmEngine *plugin, String8 &url);
+
+ void TestRemoveAllRights(IDrmEngine *plugin);
+ void TestAcquireRights(IDrmEngine *plugin, String8 &url);
+ void TestCheckRightsNotAcquired(IDrmEngine *plugin, String8 &url);
+ void TestCheckValidRights(IDrmEngine *plugin, String8 &url);
+ void TestGetConstraints(IDrmEngine *plugin, String8 &url);
+ void TestRemoveRights(IDrmEngine *plugin, String8 &url);
+
+ // Tests
+ void Run();
+};
+
+void WVMDrmPluginTest::Run()
+{
+ cout << "WVDrmPluginTest::Run" << endl;
+ const char *path = "/system/lib/drm/libdrmwvmplugin.so";
+ void *handle = dlopen(path, RTLD_NOW);
+ if (handle == NULL) {
+ fprintf(stderr, "Can't open plugin: %s\n", path);
+ exit(-1);
+ }
+
+ typedef IDrmEngine *(*create_t)();
+ create_t creator = (create_t)dlsym(handle, "create");
+ if (!creator) {
+ fprintf(stderr, "Can't find create method\n");
+ exit(-1);
+ }
+
+ typedef void (*destroy_t)(IDrmEngine *);
+ destroy_t destroyer = (destroy_t)dlsym(handle, "destroy");
+ if (!destroyer) {
+ fprintf(stderr, "Can't find destroy method\n");
+ exit(-1);
+ }
+
+ // Basic test - see if we can instantiate the object and call a method
+ IDrmEngine *plugin = (*creator)();
+ if (plugin->initialize(0) != DRM_NO_ERROR) {
+ fprintf(stderr, "onInitialize failed!\n");
+ exit(-1);
+ }
+
+ // Remote asset
+ String8 url;
+ url = String8("http://seawwws001.cdn.shibboleth.tv/videos/qa/adventures_d_ch_444169.wvm");
+ TestAsset(plugin, url);
+
+ // Local asset
+ url = String8("file:///sdcard/Widevine/trailers_d_ch_444169.wvm");
+ TestAsset(plugin, url);
+
+ // Remote asset with query parameters
+ url = String8("http://seawwws001.cdn.shibboleth.tv/videos/qa/adventures_d_ch_444169.wvm?a=b");
+ TestAsset(plugin, url);
+
+ // Shut down and clean up
+ if (plugin->terminate(0) != DRM_NO_ERROR) {
+ fprintf(stderr, "onTerminate failed!\n");
+ exit(-1);
+ }
+ destroyer(plugin);
+ dlclose(handle);
+ printf("Test successful!\n");
+ exit(0);
+}
+
+void WVMDrmPluginTest::TestAcquireRights(IDrmEngine *plugin, String8 &url)
+{
+ cout << "WVDrmPluginTest::TestAcquireRights url=" << url << endl;
+
+ String8 mimeType("video/wvm");
+ DrmInfoRequest rightsAcquisitionInfo(DrmInfoRequest::TYPE_RIGHTS_ACQUISITION_INFO, mimeType);
+ rightsAcquisitionInfo.put(String8("WVDRMServerKey"), String8("http://wstfcps005.shibboleth.tv/widevine/cypherpc/cgi-bin/GetEMMs.cgi"));
+ rightsAcquisitionInfo.put(String8("WVAssetURIKey"), url);
+ rightsAcquisitionInfo.put(String8("WVDeviceIDKey"), String8("device1234"));
+ rightsAcquisitionInfo.put(String8("WVPortalKey"), String8("YouTube"));
+
+ DrmInfo *info = plugin->acquireDrmInfo(0, &rightsAcquisitionInfo);
+ if (info == NULL) {
+ fprintf(stderr, "acquireDrmInfo failed!\n");
+ exit(-1);
+ }
+
+ DrmInfoStatus *status = plugin->processDrmInfo(0, info);
+ if (status == NULL || status->statusCode != DrmInfoStatus::STATUS_OK) {
+ fprintf(stderr, "processDrmInfo failed!\n");
+ exit(-1);
+ }
+
+ delete status;
+ delete info;
+}
+
+void WVMDrmPluginTest::TestCheckRightsNotAcquired(IDrmEngine *plugin, String8 &url)
+{
+ cout << "WVDrmPluginTest::TestCheckRightsNotAcquired url=" << url << endl;
+
+ if (plugin->checkRightsStatus(0, url, Action::DEFAULT) != RightsStatus::RIGHTS_NOT_ACQUIRED) {
+ fprintf(stderr, "checkRightsNotAcquired default action failed!\n");
+ exit(-1);
+ }
+
+ if (plugin->checkRightsStatus(0, url, Action::PLAY) != RightsStatus::RIGHTS_NOT_ACQUIRED) {
+ fprintf(stderr, "checkRightsNotAcquired failed!\n");
+ exit(-1);
+ }
+}
+
+void WVMDrmPluginTest::TestCheckValidRights(IDrmEngine *plugin, String8 &url)
+{
+ cout << "WVDrmPluginTest::TestCheckValidRights url=" << url << endl;
+
+ if (plugin->checkRightsStatus(0, url, Action::DEFAULT) != RightsStatus::RIGHTS_VALID) {
+ fprintf(stderr, "checkValidRights default action failed!\n");
+ exit(-1);
+ }
+
+ if (plugin->checkRightsStatus(0, url, Action::PLAY) != RightsStatus::RIGHTS_VALID) {
+ fprintf(stderr, "checkValidRights play action failed!\n");
+ exit(-1);
+ }
+}
+
+void WVMDrmPluginTest::TestGetConstraints(IDrmEngine *plugin, String8 &url)
+{
+ cout << "WVDrmPluginTest::TestGetConstraints url=" << url << endl;
+
+ DrmConstraints *constraints;
+ constraints = plugin->getConstraints(0, &url, Action::PLAY);
+ if (constraints == NULL) {
+ fprintf(stderr, "getConstraints returned NULL constraints!\n");
+ exit(-1);
+ }
+
+ if (constraints->getCount() != 3) {
+ fprintf(stderr, "getConstraints returned unexpected count!\n");
+ exit(-1);
+ }
+
+ if (constraints->get(DrmConstraints::LICENSE_START_TIME) == "") {
+ fprintf(stderr, "getConstraints returned unexpected count!\n");
+ exit(-1);
+ }
+
+ if (constraints->get(DrmConstraints::LICENSE_AVAILABLE_TIME) == "") {
+ fprintf(stderr, "getConstraints returned unexpected count!\n");
+ exit(-1);
+ }
+
+ if (constraints->get(DrmConstraints::LICENSE_EXPIRY_TIME) == "") {
+ fprintf(stderr, "getConstraints returned unexpected count!\n");
+ exit(-1);
+ }
+
+ delete constraints;
+}
+
+void WVMDrmPluginTest::TestRemoveRights(IDrmEngine *plugin, String8 &url)
+{
+ cout << "WVDrmPluginTest::TestRemoveRights url=" << url << endl;
+
+ status_t status = plugin->removeRights(0, url);
+ if (status != DRM_NO_ERROR) {
+ fprintf(stderr, "removeRights returned error: %d!\n", (int)status);
+ exit(-1);
+ }
+}
+
+void WVMDrmPluginTest::TestRemoveAllRights(IDrmEngine *plugin)
+{
+ cout << "WVDrmPluginTest::TestRemoveAllRights" << endl;
+
+ status_t status = plugin->removeAllRights(0);
+ if (status != DRM_NO_ERROR) {
+ fprintf(stderr, "removeAllRights returned error: %d!\n", (int)status);
+ exit(-1);
+ }
+}
+
+void WVMDrmPluginTest::TestAsset(IDrmEngine *plugin, String8 &url)
+{
+ cout << "WVDrmPluginTest::TestAsset url=" << url << endl;
+
+ TestAcquireRights(plugin, url);
+ TestCheckValidRights(plugin, url);
+ TestGetConstraints(plugin, url);
+ TestRemoveRights(plugin, url);
+ TestCheckRightsNotAcquired(plugin, url);
+ TestAcquireRights(plugin, url);
+ TestRemoveAllRights(plugin);
+ TestCheckRightsNotAcquired(plugin, url);
+}
+
+int main(int argc, char **argv)
+{
+ // turn off some noisy printing in WVStreamControl
+ setenv("WV_SILENT", "true", 1);
+ WVMDrmPluginTest test;
+ test.Run();
+}
diff --git a/drm/libdrmframework/plugins/widevine/test/kill.sh b/drm/libdrmframework/plugins/widevine/test/kill.sh
new file mode 100755
index 0000000..acc7151
--- /dev/null
+++ b/drm/libdrmframework/plugins/widevine/test/kill.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+PID=`adb shell ps | grep test-wvdrmplugin | awk '{print $2}'`
+adb shell kill -9 $PID
diff --git a/include/media/stagefright/AudioSource.h b/include/media/stagefright/AudioSource.h
index b35a6e6..9e6f0e2 100644
--- a/include/media/stagefright/AudioSource.h
+++ b/include/media/stagefright/AudioSource.h
@@ -59,12 +59,12 @@
// After the initial mute, we raise the volume linearly
// over kAutoRampDurationUs.
- kAutoRampDurationUs = 700000,
+ kAutoRampDurationUs = 300000,
// This is the initial mute duration to suppress
// the video recording signal tone
- kAutoRampStartUs = 1000000,
- };
+ kAutoRampStartUs = 0,
+ };
Mutex mLock;
Condition mFrameAvailableCondition;
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 0042f49..8bae684 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -316,6 +316,8 @@
mTextTexture = NULL;
mIndexBufferID = 0;
+ mPositionAttrSlot = -1;
+ mTexcoordAttrSlot = -1;
mCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
mCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
@@ -565,13 +567,8 @@
float* vtx = mTextMeshPtr;
float* tex = vtx + 3;
- // position is slot 0
- uint32_t slot = 0;
- glVertexAttribPointer(slot, 3, GL_FLOAT, false, 20, vtx);
-
- // texture0 is slot 1
- slot = 1;
- glVertexAttribPointer(slot, 2, GL_FLOAT, false, 20, tex);
+ glVertexAttribPointer(mPositionAttrSlot, 3, GL_FLOAT, false, 20, vtx);
+ glVertexAttribPointer(mTexcoordAttrSlot, 2, GL_FLOAT, false, 20, tex);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
@@ -718,6 +715,11 @@
return false;
}
+ if (mPositionAttrSlot < 0 || mTexcoordAttrSlot < 0) {
+ LOGE("Font renderer unable to draw, attribute slots undefined");
+ return false;
+ }
+
mDrawn = false;
mBounds = bounds;
mClip = clip;
diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h
index 1005812..46f332e 100644
--- a/libs/hwui/FontRenderer.h
+++ b/libs/hwui/FontRenderer.h
@@ -138,6 +138,11 @@
mGammaTable = gammaTable;
}
+ void setAttributeBindingSlots(int positionSlot, int texCoordSlot) {
+ mPositionAttrSlot = positionSlot;
+ mTexcoordAttrSlot = texCoordSlot;
+ }
+
void setFont(SkPaint* paint, uint32_t fontId, float fontSize);
bool renderText(SkPaint* paint, const Rect* clip, const char *text, uint32_t startIndex,
uint32_t len, int numGlyphs, int x, int y, Rect* bounds);
@@ -263,6 +268,9 @@
uint32_t mIndexBufferID;
+ int32_t mPositionAttrSlot;
+ int32_t mTexcoordAttrSlot;
+
const Rect* mClip;
Rect* mBounds;
bool mDrawn;
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 68b54fe..4a08f53 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -1585,8 +1585,12 @@
#else
bool hasActiveLayer = false;
#endif
-
mCaches.unbindMeshBuffer();
+
+ // Tell font renderer the locations of position and texture coord
+ // attributes so it can bind its data properly
+ int positionSlot = mCaches.currentProgram->position;
+ fontRenderer.setAttributeBindingSlots(positionSlot, mTexCoordsSlot);
if (fontRenderer.renderText(paint, clip, text, 0, bytesCount, count, x, y,
hasActiveLayer ? &bounds : NULL)) {
#if RENDER_LAYERS_AS_REGIONS
diff --git a/libs/rs/java/Balls/_index.html b/libs/rs/java/Balls/_index.html
new file mode 100644
index 0000000..8760485
--- /dev/null
+++ b/libs/rs/java/Balls/_index.html
@@ -0,0 +1 @@
+<p>A brute force physics simulation that renders many balls onto the screen and moves them according to user touch and gravity.</p>
\ No newline at end of file
diff --git a/libs/rs/java/Fountain/_index.html b/libs/rs/java/Fountain/_index.html
new file mode 100644
index 0000000..223242f
--- /dev/null
+++ b/libs/rs/java/Fountain/_index.html
@@ -0,0 +1,5 @@
+<p>An example that renders many dots on the screen that follow a user's touch. The dots fall
+to the bottom of the screen when the user releases the finger.</p>
+
+
+
diff --git a/libs/rs/java/HelloCompute/_index.html b/libs/rs/java/HelloCompute/_index.html
new file mode 100644
index 0000000..abfd978
--- /dev/null
+++ b/libs/rs/java/HelloCompute/_index.html
@@ -0,0 +1,2 @@
+<p>A Renderscript compute sample that filters a bitmap. No Renderscript graphics APIs are used
+in this sample.</p>
\ No newline at end of file
diff --git a/libs/rs/java/HelloWorld/_index.html b/libs/rs/java/HelloWorld/_index.html
new file mode 100644
index 0000000..4cab738
--- /dev/null
+++ b/libs/rs/java/HelloWorld/_index.html
@@ -0,0 +1 @@
+<p>A Renderscript graphics application that draws the text "Hello, World!" where the user touches.</p>
\ No newline at end of file
diff --git a/libs/rs/java/ImageProcessing/AndroidManifest.xml b/libs/rs/java/ImageProcessing/AndroidManifest.xml
index 0fcbf1e..69a33bc 100644
--- a/libs/rs/java/ImageProcessing/AndroidManifest.xml
+++ b/libs/rs/java/ImageProcessing/AndroidManifest.xml
@@ -6,8 +6,7 @@
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-sdk android:minSdkVersion="11" />
<application android:label="Image Processing">
- <activity android:name="ImageProcessingActivity"
- android:screenOrientation="portrait">
+ <activity android:name="ImageProcessingActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
diff --git a/libs/rs/java/ImageProcessing/res/layout/main.xml b/libs/rs/java/ImageProcessing/res/layout/main.xml
index c6ec729..b271b43 100644
--- a/libs/rs/java/ImageProcessing/res/layout/main.xml
+++ b/libs/rs/java/ImageProcessing/res/layout/main.xml
@@ -14,174 +14,140 @@
limitations under the License.
-->
-<merge xmlns:android="http://schemas.android.com/apk/res/android">
-
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
<SurfaceView
android:id="@+id/surface"
android:layout_width="1dip"
android:layout_height="1dip" />
-
<ImageView
android:id="@+id/display"
android:layout_width="320dip"
android:layout_height="266dip" />
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content">
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/benchmark"
+ android:onClick="benchmark"/>
+ <TextView
+ android:id="@+id/benchmarkText"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textSize="8pt"
+ android:text="@string/saturation"/>
+ </LinearLayout>
+ <ScrollView
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+ <TextView
+ android:id="@+id/inSaturationText"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textSize="8pt"
+ android:layout_marginLeft="10sp"
+ android:layout_marginTop="15sp"
+ android:text="@string/saturation"/>
+ <SeekBar
+ android:id="@+id/inSaturation"
+ android:layout_marginLeft="10sp"
+ android:layout_marginRight="10sp"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+ <TextView
+ android:id="@+id/inGammaText"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textSize="8pt"
+ android:layout_marginLeft="10sp"
+ android:layout_marginTop="15sp"
+ android:text="@string/gamma"/>
+ <SeekBar
+ android:id="@+id/inGamma"
+ android:layout_marginLeft="10sp"
+ android:layout_marginRight="10sp"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+ <TextView
+ android:id="@+id/outWhiteText"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10sp"
+ android:layout_marginTop="15sp"
+ android:textSize="8pt"
+ android:text="@string/out_white"/>
+ <SeekBar
+ android:id="@+id/outWhite"
+ android:layout_marginLeft="10sp"
+ android:layout_marginRight="10sp"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+ <TextView
+ android:id="@+id/inWhiteText"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textSize="8pt"
+ android:layout_marginLeft="10sp"
+ android:layout_marginTop="15sp"
+ android:text="@string/in_white"/>
+ <SeekBar
+ android:id="@+id/inWhite"
+ android:layout_marginLeft="10sp"
+ android:layout_marginRight="10sp"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+ <TextView
+ android:id="@+id/outBlackText"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textSize="8pt"
+ android:layout_marginLeft="10sp"
+ android:layout_marginTop="15sp"
+ android:text="@string/out_black"/>
+ <SeekBar
+ android:id="@+id/outBlack"
+ android:layout_marginLeft="10sp"
+ android:layout_marginRight="10sp"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+ <TextView
+ android:id="@+id/inBlackText"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textSize="8pt"
+ android:layout_marginLeft="10sp"
+ android:layout_marginTop="15sp"
+ android:text="@string/in_black"/>
+ <SeekBar
+ android:id="@+id/inBlack"
+ android:layout_marginLeft="10sp"
+ android:layout_marginRight="10sp"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+ <TextView
+ android:id="@+id/blurText"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textSize="8pt"
+ android:layout_marginLeft="10sp"
+ android:layout_marginTop="15sp"
+ android:text="@string/blur_description"/>
+ <SeekBar
+ android:id="@+id/radius"
+ android:layout_marginLeft="10sp"
+ android:layout_marginRight="10sp"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+ </LinearLayout>
+ </ScrollView>
+</LinearLayout>
- <Button
- android:layout_marginBottom="170dip"
- android:layout_width="wrap_content"
- android:layout_height="40dip"
- android:text="@string/benchmark"
- android:onClick="benchmark"
- android:layout_gravity="bottom"/>
-
- <TextView
- android:id="@+id/benchmarkText"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textSize="18sp"
- android:layout_marginLeft="100dip"
- android:layout_marginBottom="175dip"
- android:layout_gravity="bottom"
- android:text="@string/saturation"/>
-
- <SeekBar
- android:id="@+id/inSaturation"
- android:layout_marginBottom="140dip"
- android:layout_marginLeft="10dip"
- android:layout_marginRight="10dip"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="bottom" />
-
- <TextView
- android:id="@+id/inSaturationText"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textSize="18sp"
- android:layout_marginLeft="50dip"
- android:layout_marginBottom="142dip"
- android:textColor="#000"
- android:layout_gravity="bottom"
- android:text="@string/saturation"/>
-
- <SeekBar
- android:id="@+id/inGamma"
- android:layout_marginBottom="110dip"
- android:layout_marginLeft="10dip"
- android:layout_marginRight="10dip"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="bottom" />
-
- <TextView
- android:id="@+id/inGammaText"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textSize="18sp"
- android:layout_marginLeft="50dip"
- android:layout_marginBottom="112dip"
- android:textColor="#000"
- android:layout_gravity="bottom"
- android:text="@string/gamma"/>
-
- <SeekBar
- android:id="@+id/outWhite"
- android:layout_marginBottom="80dip"
- android:layout_marginLeft="170dip"
- android:layout_marginRight="10dip"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="bottom" />
-
- <TextView
- android:id="@+id/outWhiteText"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textSize="18sp"
- android:layout_marginLeft="220dip"
- android:layout_marginBottom="82dip"
- android:textColor="#000"
- android:layout_gravity="bottom"
- android:text="@string/out_white"/>
-
- <SeekBar
- android:id="@+id/inWhite"
- android:layout_marginBottom="80dip"
- android:layout_marginLeft="10dip"
- android:layout_marginRight="170dip"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="bottom" />
-
- <TextView
- android:id="@+id/inWhiteText"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textSize="18sp"
- android:layout_marginLeft="50dip"
- android:layout_marginBottom="82dip"
- android:textColor="#000"
- android:layout_gravity="bottom"
- android:text="@string/in_white"/>
-
- <SeekBar
- android:id="@+id/outBlack"
- android:layout_marginBottom="50dip"
- android:layout_marginLeft="170dip"
- android:layout_marginRight="10dip"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="bottom" />
-
- <TextView
- android:id="@+id/outBlackText"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textSize="18sp"
- android:layout_marginLeft="220dip"
- android:layout_marginBottom="52dip"
- android:textColor="#000"
- android:layout_gravity="bottom"
- android:text="@string/out_black"/>
-
- <SeekBar
- android:id="@+id/inBlack"
- android:layout_marginBottom="50dip"
- android:layout_marginLeft="10dip"
- android:layout_marginRight="170dip"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="bottom" />
-
- <TextView
- android:id="@+id/inBlackText"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textSize="18sp"
- android:layout_marginLeft="50dip"
- android:layout_marginBottom="52dip"
- android:textColor="#000"
- android:layout_gravity="bottom"
- android:text="@string/in_black"/>
-
- <SeekBar
- android:id="@+id/radius"
- android:layout_marginBottom="10dip"
- android:layout_marginLeft="10dip"
- android:layout_marginRight="10dip"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="bottom" />
-
- <TextView
- android:id="@+id/blurText"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textSize="18sp"
- android:layout_marginLeft="50dip"
- android:layout_marginBottom="12dip"
- android:textColor="#000"
- android:layout_gravity="bottom"
- android:text="@string/blur_description"/>
-
-</merge>
\ No newline at end of file
diff --git a/libs/rs/java/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java b/libs/rs/java/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
index 5de09f7..4f2f52ab 100644
--- a/libs/rs/java/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
+++ b/libs/rs/java/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
@@ -346,7 +346,7 @@
mSaturationSeekBar.setProgress(50);
mBenchmarkResult = (TextView) findViewById(R.id.benchmarkText);
- mBenchmarkResult.setText("Benchmark not yet run");
+ mBenchmarkResult.setText("Result: not run");
}
public void surfaceCreated(SurfaceHolder holder) {
@@ -430,7 +430,7 @@
//long javaTime = javaFilter();
//mBenchmarkResult.setText("RS: " + t + " ms Java: " + javaTime + " ms");
- mBenchmarkResult.setText("RS: " + t + " ms");
+ mBenchmarkResult.setText("Result: " + t + " ms");
mRadius = oldRadius;
mScript.set_radius(mRadius);
diff --git a/libs/rs/java/Samples/_index.html b/libs/rs/java/Samples/_index.html
new file mode 100644
index 0000000..5872431
--- /dev/null
+++ b/libs/rs/java/Samples/_index.html
@@ -0,0 +1 @@
+<p>A set of samples that demonstrate how to use various features of the Renderscript APIs.</p>
\ No newline at end of file
diff --git a/libs/rs/java/_index.html b/libs/rs/java/_index.html
new file mode 100644
index 0000000..5872431
--- /dev/null
+++ b/libs/rs/java/_index.html
@@ -0,0 +1 @@
+<p>A set of samples that demonstrate how to use various features of the Renderscript APIs.</p>
\ No newline at end of file
diff --git a/media/java/android/media/videoeditor/MediaArtistNativeHelper.java b/media/java/android/media/videoeditor/MediaArtistNativeHelper.java
index 8c78d60..ad2bf95 100644
--- a/media/java/android/media/videoeditor/MediaArtistNativeHelper.java
+++ b/media/java/android/media/videoeditor/MediaArtistNativeHelper.java
@@ -3065,8 +3065,7 @@
* This function is responsible for stopping the preview
*/
long stopPreview() {
- nativeStopPreview();
- return mPreviewProgress;
+ return nativeStopPreview();
}
/**
@@ -3999,7 +3998,7 @@
int framewidth, int frameheight, int surfacewidth, int surfaceheight, long timeMs)
throws IllegalArgumentException, IllegalStateException, RuntimeException;
- private native void nativeStopPreview();
+ private native int nativeStopPreview();
private native int nativeGenerateAudioGraph(String pcmFilePath, String outGraphPath,
int frameDuration, int channels, int sampleCount);
diff --git a/media/jni/mediaeditor/VideoEditorMain.cpp b/media/jni/mediaeditor/VideoEditorMain.cpp
index 1ba5beb..23081f8 100755
--- a/media/jni/mediaeditor/VideoEditorMain.cpp
+++ b/media/jni/mediaeditor/VideoEditorMain.cpp
@@ -208,7 +208,7 @@
jobject object,
jobject audioSettingObject);
-static void videoEditor_stopPreview(JNIEnv* pEnv,
+static int videoEditor_stopPreview(JNIEnv* pEnv,
jobject thiz);
static jobject
@@ -283,8 +283,8 @@
{"nativeRenderMediaItemPreviewFrame",
"(Landroid/view/Surface;Ljava/lang/String;IIIIJ)I",
(int *)videoEditor_renderMediaItemPreviewFrame },
- {"nativeStopPreview", "()V",
- (void *)videoEditor_stopPreview },
+ {"nativeStopPreview", "()I",
+ (int *)videoEditor_stopPreview },
{"stopEncoding", "()V",
(void *)videoEditor_stopEncoding },
{"release", "()V",
@@ -482,11 +482,13 @@
pContext->pVM->DetachCurrentThread();
}
-static void videoEditor_stopPreview(JNIEnv* pEnv,
+static int videoEditor_stopPreview(JNIEnv* pEnv,
jobject thiz)
{
ManualEditContext* pContext = M4OSA_NULL;
bool needToBeLoaded = true;
+ M4OSA_UInt32 lastProgressTimeMs = 0;
+
// Get the context.
pContext =
(ManualEditContext*)videoEditClasses_getContext(&needToBeLoaded, pEnv, thiz);
@@ -495,12 +497,14 @@
videoEditJava_checkAndThrowIllegalStateException(&needToBeLoaded, pEnv,
(M4OSA_NULL == pContext),
"not initialized");
- pContext->mPreviewController->stopPreview();
+ lastProgressTimeMs = pContext->mPreviewController->stopPreview();
if (pContext->mOverlayFileName != NULL) {
M4OSA_free((M4OSA_MemAddr32)pContext->mOverlayFileName);
pContext->mOverlayFileName = NULL;
}
+
+ return lastProgressTimeMs;
}
static void videoEditor_clearSurface(JNIEnv* pEnv,
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 53435f8..80eb59f 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -58,7 +58,8 @@
$(TOP)/frameworks/base/include/media/stagefright/openmax \
$(TOP)/external/flac/include \
$(TOP)/external/tremolo \
- $(TOP)/frameworks/base/media/libstagefright/rtsp
+ $(TOP)/frameworks/base/media/libstagefright/rtsp \
+ $(TOP)/external/openssl/include \
LOCAL_SHARED_LIBRARIES := \
libbinder \
@@ -72,7 +73,8 @@
libstagefright_yuv \
libcamera_client \
libdrmframework \
- libcrypto
+ libcrypto \
+ libssl
LOCAL_STATIC_LIBRARIES := \
libstagefright_color_conversion \
diff --git a/media/libstagefright/AudioSource.cpp b/media/libstagefright/AudioSource.cpp
index cd0e021..bbdec02 100644
--- a/media/libstagefright/AudioSource.cpp
+++ b/media/libstagefright/AudioSource.cpp
@@ -263,6 +263,13 @@
return OK;
}
+ // Drop retrieved and previously lost audio data.
+ if (mNumFramesReceived == 0 && timeUs < mStartTimeUs) {
+ mRecord->getInputFramesLost();
+ LOGV("Drop audio data at %lld/%lld us", timeUs, mStartTimeUs);
+ return OK;
+ }
+
if (mNumFramesReceived == 0 && mPrevSampleTimeUs == 0) {
mInitialReadTimeUs = timeUs;
// Initial delay
@@ -277,7 +284,13 @@
int64_t timestampUs = mPrevSampleTimeUs;
- size_t numLostBytes = mRecord->getInputFramesLost();
+ size_t numLostBytes = 0;
+ if (mNumFramesReceived > 0) { // Ignore earlier frame lost
+ // getInputFramesLost() returns the number of lost frames.
+ // Convert number of frames lost to number of bytes lost.
+ numLostBytes = mRecord->getInputFramesLost() * mRecord->frameSize();
+ }
+
CHECK_EQ(numLostBytes & 1, 0u);
CHECK_EQ(audioBuffer.size & 1, 0u);
size_t bufferSize = numLostBytes + audioBuffer.size;
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index 8866750..b1d3630 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -1481,7 +1481,8 @@
status_t AwesomePlayer::finishSetDataSource_l() {
sp<DataSource> dataSource;
- if (!strncasecmp("http://", mUri.string(), 7)) {
+ if (!strncasecmp("http://", mUri.string(), 7)
+ || !strncasecmp("https://", mUri.string(), 8)) {
mConnectingDataSource = new NuHTTPDataSource;
mLock.unlock();
diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp
index 66e0657..8a24bc45 100644
--- a/media/libstagefright/CameraSource.cpp
+++ b/media/libstagefright/CameraSource.cpp
@@ -598,8 +598,7 @@
}
if (mNumGlitches > 0) {
- LOGW("%d long delays between neighboring video frames during",
- mNumGlitches);
+ LOGW("%d long delays between neighboring video frames", mNumGlitches);
}
CHECK_EQ(mNumFramesReceived, mNumFramesEncoded + mNumFramesDropped);
@@ -696,10 +695,9 @@
int32_t msgType, const sp<IMemory> &data) {
LOGV("dataCallbackTimestamp: timestamp %lld us", timestampUs);
Mutex::Autolock autoLock(mLock);
- if (!mStarted) {
+ if (!mStarted || (mNumFramesReceived == 0 && timestampUs < mStartTimeUs)) {
+ LOGV("Drop frame at %lld/%lld us", timestampUs, mStartTimeUs);
releaseOneRecordingFrame(data);
- ++mNumFramesReceived;
- ++mNumFramesDropped;
return;
}
diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp
index 8f9c150..3b38208 100644
--- a/media/libstagefright/DataSource.cpp
+++ b/media/libstagefright/DataSource.cpp
@@ -125,7 +125,8 @@
sp<DataSource> source;
if (!strncasecmp("file://", uri, 7)) {
source = new FileSource(uri + 7);
- } else if (!strncasecmp("http://", uri, 7)) {
+ } else if (!strncasecmp("http://", uri, 7)
+ || !strncasecmp("https://", uri, 8)) {
sp<NuHTTPDataSource> httpSource = new NuHTTPDataSource;
if (httpSource->connect(uri, headers) != OK) {
return NULL;
diff --git a/media/libstagefright/HTTPStream.cpp b/media/libstagefright/HTTPStream.cpp
index 77a61a5..498c7b8 100644
--- a/media/libstagefright/HTTPStream.cpp
+++ b/media/libstagefright/HTTPStream.cpp
@@ -34,6 +34,8 @@
#include <media/stagefright/foundation/ADebug.h>
+#include <openssl/ssl.h>
+
namespace android {
// static
@@ -41,11 +43,18 @@
HTTPStream::HTTPStream()
: mState(READY),
- mSocket(-1) {
+ mSocket(-1),
+ mSSLContext(NULL),
+ mSSL(NULL) {
}
HTTPStream::~HTTPStream() {
disconnect();
+
+ if (mSSLContext != NULL) {
+ SSL_CTX_free((SSL_CTX *)mSSLContext);
+ mSSLContext = NULL;
+ }
}
static bool MakeSocketBlocking(int s, bool blocking) {
@@ -198,7 +207,11 @@
return MySendReceive(s, data, size, flags, false /* sendData */);
}
-status_t HTTPStream::connect(const char *server, int port) {
+status_t HTTPStream::connect(const char *server, int port, bool https) {
+ if (port < 0) {
+ port = https ? 443 : 80;
+ }
+
Mutex::Autolock autoLock(mLock);
status_t err = OK;
@@ -249,6 +262,47 @@
return res;
}
+ if (https) {
+ CHECK(mSSL == NULL);
+
+ if (mSSLContext == NULL) {
+ SSL_library_init();
+
+ mSSLContext = SSL_CTX_new(TLSv1_client_method());
+
+ if (mSSLContext == NULL) {
+ LOGE("failed to create SSL context");
+ mState = READY;
+ return ERROR_IO;
+ }
+ }
+
+ mSSL = SSL_new((SSL_CTX *)mSSLContext);
+
+ if (mSSL == NULL) {
+ LOGE("failed to create SSL session");
+
+ mState = READY;
+ return ERROR_IO;
+ }
+
+ int res = SSL_set_fd((SSL *)mSSL, mSocket);
+
+ if (res == 1) {
+ res = SSL_connect((SSL *)mSSL);
+ }
+
+ if (res != 1) {
+ SSL_free((SSL *)mSSL);
+ mSSL = NULL;
+
+ LOGE("failed to connect over SSL");
+ mState = READY;
+
+ return ERROR_IO;
+ }
+ }
+
mState = CONNECTED;
return OK;
@@ -261,6 +315,13 @@
return ERROR_NOT_CONNECTED;
}
+ if (mSSL != NULL) {
+ SSL_shutdown((SSL *)mSSL);
+
+ SSL_free((SSL *)mSSL);
+ mSSL = NULL;
+ }
+
CHECK(mSocket >= 0);
close(mSocket);
mSocket = -1;
@@ -276,7 +337,16 @@
}
while (size > 0) {
- ssize_t n = MySend(mSocket, data, size, 0);
+ ssize_t n;
+ if (mSSL != NULL) {
+ n = SSL_write((SSL *)mSSL, data, size);
+
+ if (n < 0) {
+ n = -SSL_get_error((SSL *)mSSL, n);
+ }
+ } else {
+ n = MySend(mSocket, data, size, 0);
+ }
if (n < 0) {
disconnect();
@@ -317,7 +387,17 @@
for (;;) {
char c;
- ssize_t n = MyReceive(mSocket, &c, 1, 0);
+ ssize_t n;
+ if (mSSL != NULL) {
+ n = SSL_read((SSL *)mSSL, &c, 1);
+
+ if (n < 0) {
+ n = -SSL_get_error((SSL *)mSSL, n);
+ }
+ } else {
+ n = MyReceive(mSocket, &c, 1, 0);
+ }
+
if (n < 0) {
disconnect();
@@ -437,7 +517,16 @@
ssize_t HTTPStream::receive(void *data, size_t size) {
size_t total = 0;
while (total < size) {
- ssize_t n = MyReceive(mSocket, (char *)data + total, size - total, 0);
+ ssize_t n;
+ if (mSSL != NULL) {
+ n = SSL_read((SSL *)mSSL, (char *)data + total, size - total);
+
+ if (n < 0) {
+ n = -SSL_get_error((SSL *)mSSL, n);
+ }
+ } else {
+ n = MyReceive(mSocket, (char *)data + total, size - total, 0);
+ }
if (n < 0) {
LOGE("recv failed, errno = %d (%s)", (int)n, strerror(-n));
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index b11789d..5d6ea7c 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -1281,7 +1281,21 @@
initTrackingProgressStatus(params);
sp<MetaData> meta = new MetaData;
+ if (mIsRealTimeRecording && mOwner->numTracks() > 1) {
+ /*
+ * This extra delay of accepting incoming audio/video signals
+ * helps to align a/v start time at the beginning of a recording
+ * session, and it also helps eliminate the "recording" sound for
+ * camcorder applications.
+ *
+ * Ideally, this platform-specific value should be defined
+ * in media_profiles.xml file
+ */
+ startTimeUs += 700000;
+ }
+
meta->setInt64(kKeyTime, startTimeUs);
+
status_t err = mSource->start(meta.get());
if (err != OK) {
mDone = mReachedEOS = true;
@@ -1944,7 +1958,11 @@
((timestampUs * mTimeScale + 500000LL) / 1000000LL -
(lastTimestampUs * mTimeScale + 500000LL) / 1000000LL);
- if (currDurationTicks != lastDurationTicks) {
+ // Force the first sample to have its own stts entry so that
+ // we can adjust its value later to maintain the A/V sync.
+ if (mNumSamples == 3 || currDurationTicks != lastDurationTicks) {
+ LOGV("%s lastDurationUs: %lld us, currDurationTicks: %lld us",
+ mIsAudio? "Audio": "Video", lastDurationUs, currDurationTicks);
addOneSttsTableEntry(sampleCount, lastDurationUs);
sampleCount = 1;
} else {
@@ -1957,6 +1975,8 @@
}
previousSampleSize = sampleSize;
}
+ LOGV("%s timestampUs/lastTimestampUs: %lld/%lld",
+ mIsAudio? "Audio": "Video", timestampUs, lastTimestampUs);
lastDurationUs = timestampUs - lastTimestampUs;
lastDurationTicks = currDurationTicks;
lastTimestampUs = timestampUs;
@@ -2028,7 +2048,16 @@
} else {
++sampleCount; // Count for the last sample
}
- addOneSttsTableEntry(sampleCount, lastDurationUs);
+
+ if (mNumSamples <= 2) {
+ addOneSttsTableEntry(1, lastDurationUs);
+ if (sampleCount - 1 > 0) {
+ addOneSttsTableEntry(sampleCount - 1, lastDurationUs);
+ }
+ } else {
+ addOneSttsTableEntry(sampleCount, lastDurationUs);
+ }
+
mTrackDurationUs += lastDurationUs;
mReachedEOS = true;
LOGI("Received total/0-length (%d/%d) buffers and encoded %d frames. - %s",
@@ -2153,6 +2182,9 @@
int32_t mvhdTimeScale = mOwner->getTimeScale();
int64_t trakDurationUs = getDurationUs();
+ // Compensate for small start time difference from different media tracks
+ int64_t trackStartTimeOffsetUs = 0;
+
mOwner->beginBox("trak");
mOwner->beginBox("tkhd");
@@ -2191,26 +2223,8 @@
int64_t moovStartTimeUs = mOwner->getStartTimestampUs();
if (mStartTimestampUs != moovStartTimeUs) {
- mOwner->beginBox("edts");
- mOwner->beginBox("elst");
- mOwner->writeInt32(0); // version=0, flags=0: 32-bit time
- mOwner->writeInt32(2); // never ends with an empty list
-
- // First elst entry: specify the starting time offset
- int64_t offsetUs = mStartTimestampUs - moovStartTimeUs;
- LOGV("OffsetUs: %lld", offsetUs);
- int32_t seg = (offsetUs * mvhdTimeScale + 5E5) / 1E6;
- mOwner->writeInt32(seg); // in mvhd timecale
- mOwner->writeInt32(-1); // starting time offset
- mOwner->writeInt32(1 << 16); // rate = 1.0
-
- // Second elst entry: specify the track duration
- seg = (trakDurationUs * mvhdTimeScale + 5E5) / 1E6;
- mOwner->writeInt32(seg); // in mvhd timescale
- mOwner->writeInt32(0);
- mOwner->writeInt32(1 << 16);
- mOwner->endBox();
- mOwner->endBox();
+ CHECK(mStartTimestampUs > moovStartTimeUs);
+ trackStartTimeOffsetUs = mStartTimestampUs - moovStartTimeUs;
}
mOwner->beginBox("mdia");
@@ -2466,7 +2480,7 @@
mOwner->beginBox("stts");
mOwner->writeInt32(0); // version=0, flags=0
mOwner->writeInt32(mNumSttsTableEntries);
- int64_t prevTimestampUs = 0;
+ int64_t prevTimestampUs = trackStartTimeOffsetUs;
for (List<SttsTableEntry>::iterator it = mSttsTableEntries.begin();
it != mSttsTableEntries.end(); ++it) {
mOwner->writeInt32(it->sampleCount);
diff --git a/media/libstagefright/NuHTTPDataSource.cpp b/media/libstagefright/NuHTTPDataSource.cpp
index 0376e1c..e39fab3 100644
--- a/media/libstagefright/NuHTTPDataSource.cpp
+++ b/media/libstagefright/NuHTTPDataSource.cpp
@@ -24,22 +24,30 @@
}
static bool ParseURL(
- const char *url, String8 *host, unsigned *port, String8 *path) {
+ const char *url, String8 *host, unsigned *port,
+ String8 *path, bool *https) {
host->setTo("");
*port = 0;
path->setTo("");
- if (strncasecmp("http://", url, 7)) {
+ size_t hostStart;
+ if (!strncasecmp("http://", url, 7)) {
+ hostStart = 7;
+ *https = false;
+ } else if (!strncasecmp("https://", url, 8)) {
+ hostStart = 8;
+ *https = true;
+ } else {
return false;
}
- const char *slashPos = strchr(&url[7], '/');
+ const char *slashPos = strchr(&url[hostStart], '/');
if (slashPos == NULL) {
- host->setTo(&url[7]);
+ host->setTo(&url[hostStart]);
path->setTo("/");
} else {
- host->setTo(&url[7], slashPos - &url[7]);
+ host->setTo(&url[hostStart], slashPos - &url[hostStart]);
path->setTo(slashPos);
}
@@ -57,7 +65,7 @@
String8 tmp(host->string(), colonOffset);
*host = tmp;
} else {
- *port = 80;
+ *port = (*https) ? 443 : 80;
}
return true;
@@ -66,6 +74,7 @@
NuHTTPDataSource::NuHTTPDataSource()
: mState(DISCONNECTED),
mPort(0),
+ mHTTPS(false),
mOffset(0),
mContentLength(0),
mContentLengthValid(false),
@@ -111,11 +120,12 @@
mUri = uri;
- if (!ParseURL(uri, &host, &port, &path)) {
+ bool https;
+ if (!ParseURL(uri, &host, &port, &path, &https)) {
return ERROR_MALFORMED;
}
- return connect(host, port, path, headers, offset);
+ return connect(host, port, path, https, headers, offset);
}
static bool IsRedirectStatusCode(int httpStatus) {
@@ -125,6 +135,7 @@
status_t NuHTTPDataSource::connect(
const char *host, unsigned port, const char *path,
+ bool https,
const String8 &headers,
off64_t offset) {
LOGI("connect to %s:%u%s @%lld", host, port, path, offset);
@@ -132,7 +143,7 @@
bool needsToReconnect = true;
if (mState == CONNECTED && host == mHost && port == mPort
- && offset == mOffset) {
+ && https == mHTTPS && offset == mOffset) {
if (mContentLengthValid && mOffset == mContentLength) {
LOGI("Didn't have to reconnect, old one's still good.");
needsToReconnect = false;
@@ -142,6 +153,7 @@
mHost = host;
mPort = port;
mPath = path;
+ mHTTPS = https;
mHeaders = headers;
status_t err = OK;
@@ -150,7 +162,7 @@
if (needsToReconnect) {
mHTTP.disconnect();
- err = mHTTP.connect(host, port);
+ err = mHTTP.connect(host, port, https);
}
if (err != OK) {
@@ -353,7 +365,7 @@
String8 host = mHost;
String8 path = mPath;
String8 headers = mHeaders;
- status_t err = connect(host, mPort, path, headers, offset);
+ status_t err = connect(host, mPort, path, mHTTPS, headers, offset);
if (err != OK) {
return err;
diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp
index f7a9085..ee845a3 100644
--- a/media/libstagefright/httplive/LiveSession.cpp
+++ b/media/libstagefright/httplive/LiveSession.cpp
@@ -189,7 +189,8 @@
if (!strncasecmp(url, "file://", 7)) {
source = new FileSource(url + 7);
- } else if (strncasecmp(url, "http://", 7)) {
+ } else if (strncasecmp(url, "http://", 7)
+ && strncasecmp(url, "https://", 8)) {
return ERROR_UNSUPPORTED;
} else {
{
diff --git a/media/libstagefright/include/HTTPStream.h b/media/libstagefright/include/HTTPStream.h
index 545cd0c..09e6a5f 100644
--- a/media/libstagefright/include/HTTPStream.h
+++ b/media/libstagefright/include/HTTPStream.h
@@ -32,7 +32,7 @@
HTTPStream();
~HTTPStream();
- status_t connect(const char *server, int port = 80);
+ status_t connect(const char *server, int port = -1, bool https = false);
status_t disconnect();
status_t send(const char *data, size_t size);
@@ -71,6 +71,9 @@
KeyedVector<AString, AString> mHeaders;
+ void *mSSLContext;
+ void *mSSL;
+
HTTPStream(const HTTPStream &);
HTTPStream &operator=(const HTTPStream &);
};
diff --git a/media/libstagefright/include/NuHTTPDataSource.h b/media/libstagefright/include/NuHTTPDataSource.h
index a99e84a..3918434 100644
--- a/media/libstagefright/include/NuHTTPDataSource.h
+++ b/media/libstagefright/include/NuHTTPDataSource.h
@@ -57,6 +57,7 @@
String8 mHost;
unsigned mPort;
String8 mPath;
+ bool mHTTPS;
String8 mHeaders;
String8 mUri;
@@ -83,6 +84,7 @@
status_t connect(
const char *host, unsigned port, const char *path,
+ bool https,
const String8 &headers,
off64_t offset);
diff --git a/media/libwvm/Android.mk b/media/libwvm/Android.mk
new file mode 100755
index 0000000..7fb759a
--- /dev/null
+++ b/media/libwvm/Android.mk
@@ -0,0 +1,36 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+ifeq ($(TARGET_ARCH),arm)
+
+LOCAL_SRC_FILES:= \
+ WVMLogging.cpp \
+ WVMExtractorImpl.cpp \
+ WVMFileSource.cpp \
+ WVMMediaSource.cpp
+
+LOCAL_C_INCLUDES:= \
+ bionic \
+ bionic/libstdc++ \
+ external/stlport/stlport \
+ vendor/widevine/proprietary/include \
+ frameworks/base/media/libwvm/include
+
+LOCAL_SHARED_LIBRARIES := \
+ libstlport \
+ libstagefright \
+ libWVStreamControlAPI \
+ libdrmframework \
+ libcutils \
+ liblog \
+ libutils \
+ libz
+
+LOCAL_MODULE := libwvm
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_SHARED_LIBRARY)
+
+endif
diff --git a/media/libwvm/WVMExtractorImpl.cpp b/media/libwvm/WVMExtractorImpl.cpp
new file mode 100644
index 0000000..c97d860
--- /dev/null
+++ b/media/libwvm/WVMExtractorImpl.cpp
@@ -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.
+ */
+
+#define LOG_TAG "WVMExtractorImpl"
+#include <utils/Log.h>
+
+#include "WVMExtractorImpl.h"
+#include "WVMMediaSource.h"
+#include "WVMFileSource.h"
+#include "WVMLogging.h"
+#include "WVStreamControlAPI.h"
+#include "media/stagefright/MediaErrors.h"
+#include "media/stagefright/MediaDefs.h"
+#include "drm/DrmManagerClient.h"
+#include "AndroidHooks.h"
+
+#define AES_BLOCK_SIZE 16
+
+
+using namespace android;
+
+static DecryptHandle *sDecryptHandle;
+static DrmManagerClient *sDrmManagerClient;
+
+
+// Android integration callout hooks
+static void HandleEcmCallout(char *ecm, unsigned long size)
+{
+ DrmBuffer buf(ecm, size);
+ if (sDrmManagerClient != NULL) {
+ sDrmManagerClient->initializeDecryptUnit(sDecryptHandle, 0, &buf);
+ }
+}
+
+static int HandleDecryptCallout(char *in, char *out, int length, char *iv)
+{
+ int status = -1;
+
+ if (sDrmManagerClient != NULL) {
+ DrmBuffer encryptedDrmBuffer(in, length);
+ DrmBuffer ivBuffer(iv, length);
+
+ DrmBuffer decryptedDrmBuffer(out, length);
+ DrmBuffer *decryptedDrmBufferPtr = &decryptedDrmBuffer;
+
+ char ivout[AES_BLOCK_SIZE];
+ if (in && length)
+ memcpy(ivout, in + length - AES_BLOCK_SIZE, AES_BLOCK_SIZE);
+
+ status = sDrmManagerClient->decrypt(sDecryptHandle, 0,
+ &encryptedDrmBuffer, &decryptedDrmBufferPtr,
+ &ivBuffer);
+
+ if (iv)
+ memcpy(iv, ivout, AES_BLOCK_SIZE);
+ }
+
+ return status;
+}
+
+
+namespace android {
+
+// DLL entry - construct an extractor and return it
+MediaExtractor *GetInstance(sp<DataSource> dataSource) {
+ return new WVMExtractorImpl(dataSource);
+}
+
+WVMExtractorImpl::WVMExtractorImpl(sp<DataSource> dataSource)
+ : mFileMetaData(new MetaData()),
+ mDataSource(dataSource),
+ mHaveMetaData(false),
+ mSession(NULL),
+ mSetupStatus(OK)
+{
+ dataSource->getDrmInfo(&sDecryptHandle, &sDrmManagerClient);
+
+ // Set up callouts
+ AndroidSetLogCallout(android_printbuf);
+ AndroidSetEcmCallout(HandleEcmCallout);
+ AndroidSetDecryptCallout(HandleDecryptCallout);
+
+ if (sDecryptHandle != NULL) {
+ if (sDecryptHandle->status != RightsStatus::RIGHTS_VALID) {
+ mSetupStatus = ERROR_NO_LICENSE;
+ }
+ } else
+ mSetupStatus = ERROR_NO_LICENSE;
+
+ WVCredentials credentials;
+
+ WVStatus result = WV_Initialize(NULL);
+ if (result != WV_Status_OK) {
+ LOGE("WV_Initialize returned status %d\n", result);
+ mSetupStatus = ERROR_IO;
+ } else {
+ // Enable for debugging HTTP messages
+ // WV_SetLogging(WV_Logging_HTTP);
+
+ if (dataSource->getUri().size() == 0) {
+ // No URI supplied, pull data from the data source
+ mFileSource = new WVMFileSource(dataSource);
+ result = WV_Setup(mSession, mFileSource.get(),
+ "RAW/RAW/RAW;destination=getdata", credentials,
+ WV_OutputFormat_ES, kStreamCacheSize);
+ } else {
+ // Use the URI
+ result = WV_Setup(mSession, dataSource->getUri().string(),
+ "RAW/RAW/RAW;destination=getdata", credentials,
+ WV_OutputFormat_ES, kStreamCacheSize);
+ }
+ if (result != WV_Status_OK) {
+ LOGE("WV_Setup returned status %d in WVMMediaSource::start\n", result);
+ mSetupStatus = ERROR_IO;
+ }
+ }
+
+ WV_SetWarningToErrorMS(5000);
+}
+
+WVMExtractorImpl::~WVMExtractorImpl() {
+}
+
+//
+// Configure metadata for video and audio sources
+//
+status_t WVMExtractorImpl::readMetaData()
+{
+ if (mHaveMetaData)
+ return OK;
+
+ if (mSetupStatus != OK)
+ return mSetupStatus;
+
+ // Get Video Configuration
+ WVVideoType videoType;
+ unsigned short videoStreamID;
+ unsigned short videoProfile;
+ unsigned short level;
+ unsigned short width, height;
+ float aspect, frameRate;
+ unsigned long bitRate;
+
+ WVStatus result = WV_Info_GetVideoConfiguration(mSession, &videoType, &videoStreamID,
+ &videoProfile, &level, &width, &height,
+ &aspect, &frameRate, &bitRate);
+ if (result != WV_Status_OK)
+ return ERROR_MALFORMED;
+
+ // Get Audio Configuration
+ WVAudioType audioType;
+ unsigned short audioStreamID;
+ unsigned short audioProfile;
+ unsigned short numChannels;
+ unsigned long sampleRate;
+
+ result = WV_Info_GetAudioConfiguration(mSession, &audioType, &audioStreamID, &audioProfile,
+ &numChannels, &sampleRate, &bitRate);
+ if (result != WV_Status_OK)
+ return ERROR_MALFORMED;
+
+ std::string durationString = WV_Info_GetDuration(mSession, "sec");
+ if (durationString == "") {
+ return ERROR_MALFORMED;
+ }
+
+ int64_t duration = (int64_t)(strtod(durationString.c_str(), NULL) * 1000000);
+
+ sp<MetaData> audioMetaData = new MetaData();
+ sp<MetaData> videoMetaData = new MetaData();
+
+ audioMetaData->setInt64(kKeyDuration, duration);
+ videoMetaData->setInt64(kKeyDuration, duration);
+
+ switch(videoType) {
+ case WV_VideoType_H264:
+ videoMetaData->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
+ break;
+ default:
+ LOGE("Invalid WV video type %d, expected H264C\n", audioType);
+ break;
+ }
+
+ switch(audioType) {
+ case WV_AudioType_AAC:
+ audioMetaData->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC);
+ break;
+ default:
+ LOGE("Invalid WV audio type %d, expected AAC\n", audioType);
+ break;
+ }
+
+ audioMetaData->setInt32(kKeyTrackID, audioStreamID);
+ videoMetaData->setInt32(kKeyTrackID, videoStreamID);
+
+ audioMetaData->setInt32(kKeyChannelCount, numChannels);
+ audioMetaData->setInt32(kKeySampleRate, sampleRate);
+
+ videoMetaData->setInt32(kKeyWidth, width);
+ videoMetaData->setInt32(kKeyHeight, height);
+
+ status_t status;
+
+ status = readAVCCMetaData(videoMetaData);
+ if (status != OK)
+ return status;
+
+ status = readESDSMetaData(audioMetaData);
+ if (status != OK)
+ return status;
+
+ mAudioSource = new WVMMediaSource(mSession, WV_EsSelector_Audio, audioMetaData);
+ mVideoSource = new WVMMediaSource(mSession, WV_EsSelector_Video, videoMetaData);
+
+ // Since the WVExtractor goes away soon after this, we delegate ownership of some resources
+ // to the constructed media source
+ if (mFileSource.get())
+ mVideoSource->delegateFileSource(mFileSource);
+
+ mVideoSource->delegateDataSource(mDataSource);
+
+ mFileMetaData->setCString(kKeyMIMEType, "video/mp4");
+
+ mHaveMetaData = true;
+
+ return OK;
+}
+
+status_t WVMExtractorImpl::readAVCCMetaData(sp<MetaData> videoMetaData)
+{
+ WVStatus result;
+
+ const unsigned char *config;
+ unsigned long size;
+ int limit = 50;
+ do {
+ size_t bytesRead;
+ bool auStart;
+ unsigned long long dts, pts;
+ unsigned char buf[1];
+ size_t bufSize = 0;
+
+ //
+ // In order to get the codec config data, we need to have the WVMK
+ // pull some video data. But we can't use it yet, so just request 0 bytes.
+ //
+ (void)WV_GetEsData(mSession, WV_EsSelector_Video, buf, bufSize,
+ bytesRead, auStart, dts, pts);
+
+ result = WV_Info_GetCodecConfig(mSession, WV_CodecConfigType_AVCC, config, size);
+ if (result != WV_Status_OK)
+ usleep(100);
+ } while (result == WV_Status_Warning_Not_Available && limit-- > 0);
+
+ if (result != WV_Status_OK) {
+ LOGE("WV_Info_GetCodecConfig AVCC returned error %d\n", result);
+ return ERROR_IO;
+ }
+
+#if 0
+ char *filename = "/data/wvm/avcc";
+ FILE *f = fopen(filename, "w");
+ if (!f)
+ LOGD("Failed to open %s", filename);
+ else {
+ fwrite(config, size, 1, f);
+ fclose(f);
+ }
+#endif
+
+#if 0
+ unsigned char mp4_force[] =
+ { 0x01, 0x42, 0x00, 0x1e, 0xff, 0xe1, 0x00, 0x1b, 0x67, 0x42, 0x80, 0x1e, 0x96, 0x52, 0x01, 0x40,
+ 0x5f, 0xf3, 0x60, 0x2a, 0x10, 0x00, 0x00, 0x03, 0x00, 0x10, 0x00, 0x00, 0x03, 0x03, 0x09, 0xda,
+ 0x14, 0x2a, 0x48, 0x01, 0x00, 0x04, 0x68, 0xcb, 0x8d, 0x48 } ;
+ unsigned char wvm_force[] =
+ { 0x01, 0x42, 0x80, 0x1e, 0xff, 0xe1, 0x00, 0x1c, 0x67, 0x42, 0x80, 0x1e, 0x96, 0x52, 0x01, 0x40,
+ 0x5f, 0xf3, 0x60, 0x2a, 0x10, 0x00, 0x00, 0x03, 0x00, 0x10, 0x00, 0x00, 0x03, 0x03, 0x09, 0xda,
+ 0x14, 0x2a, 0x48, 0x00, 0x01, 0x00, 0x05, 0x68, 0xcb, 0x8d, 0x48, 0x00 } ;
+ unsigned char wvm_force_no_zero[] =
+ { 0x01, 0x42, 0x80, 0x1e, 0xff, 0xe1, 0x00, 0x1b, 0x67, 0x42, 0x80, 0x1e, 0x96, 0x52, 0x01, 0x40,
+ 0x5f, 0xf3, 0x60, 0x2a, 0x10, 0x00, 0x00, 0x03, 0x00, 0x10, 0x00, 0x00, 0x03, 0x03, 0x09, 0xda,
+ 0x14, 0x2a, 0x48, 0x01, 0x00, 0x04, 0x68, 0xcb, 0x8d, 0x48 } ;
+ unsigned char wvm_force_modprof[] =
+ { 0x01, 0x42, 0x00, 0x1e, 0xff, 0xe1, 0x00, 0x1c, 0x67, 0x42, 0x80, 0x1e, 0x96, 0x52, 0x01, 0x40,
+ 0x5f, 0xf3, 0x60, 0x2a, 0x10, 0x00, 0x00, 0x03, 0x00, 0x10, 0x00, 0x00, 0x03, 0x03, 0x09, 0xda,
+ 0x14, 0x2a, 0x48, 0x00, 0x01, 0x00, 0x05, 0x68, 0xcb, 0x8d, 0x48, 0x00 } ;
+
+ videoMetaData->setData(kKeyAVCC, kTypeAVCC, wvm_force_no_zero, sizeof(wvm_force_no_zero));
+#else
+ videoMetaData->setData(kKeyAVCC, kTypeAVCC, config, size);
+#endif
+ return OK;
+}
+
+status_t WVMExtractorImpl::readESDSMetaData(sp<MetaData> audioMetaData)
+{
+ WVStatus result;
+
+ const unsigned char *config;
+ unsigned long size;
+ int limit = 50;
+ do {
+ size_t bytesRead;
+ bool auStart;
+ unsigned long long dts, pts;
+ unsigned char buf[1];
+ size_t bufSize = 0;
+
+ //
+ // In order to get the codec config data, we need to have the WVMK
+ // pull some audio data. But we can't use it yet, so just request 0 bytes.
+ //
+ (void)WV_GetEsData(mSession, WV_EsSelector_Audio, buf, bufSize,
+ bytesRead, auStart, dts, pts);
+
+ result = WV_Info_GetCodecConfig(mSession, WV_CodecConfigType_ESDS, config, size);
+ if (result != WV_Status_OK)
+ usleep(100);
+ } while (result == WV_Status_Warning_Not_Available && limit-- > 0);
+
+ if (result != WV_Status_OK) {
+ LOGE("WV_Info_GetCodecConfig ESDS returned error %d\n", result);
+ return ERROR_IO;
+ }
+
+#if 0
+ char *filename = "/data/wvm/esds";
+ FILE *f = fopen(filename, "w");
+ if (!f)
+ LOGD("Failed to open %s", filename);
+ else {
+ fwrite(config, size, 1, f);
+ fclose(f);
+ }
+#endif
+ audioMetaData->setData(kKeyESDS, kTypeESDS, config, size);
+ return OK;
+}
+
+size_t WVMExtractorImpl::countTracks() {
+ status_t err;
+ if ((err = readMetaData()) != OK) {
+ return 0;
+ }
+
+ return 2; // 1 audio + 1 video
+}
+
+sp<MediaSource> WVMExtractorImpl::getTrack(size_t index)
+{
+ status_t err;
+ if ((err = readMetaData()) != OK) {
+ return NULL;
+ }
+
+ sp<MediaSource> result;
+
+ switch(index) {
+ case 0:
+ result = mVideoSource;
+ break;
+ case 1:
+ result = mAudioSource;
+ break;
+ default:
+ break;
+ }
+ return result;
+}
+
+
+sp<MetaData> WVMExtractorImpl::getTrackMetaData(size_t index, uint32_t flags)
+{
+ status_t err;
+ if ((err = readMetaData()) != OK) {
+ return NULL;
+ }
+
+ sp<MetaData> result;
+ switch(index) {
+ case 0:
+ if (mVideoSource != NULL)
+ result = mVideoSource->getFormat();
+ break;
+ case 1:
+ if (mAudioSource != NULL)
+ result = mAudioSource->getFormat();
+ break;
+ default:
+ break;
+ }
+ return result;
+}
+
+sp<MetaData> WVMExtractorImpl::getMetaData() {
+ status_t err;
+ if ((err = readMetaData()) != OK) {
+ return new MetaData;
+ }
+
+ return mFileMetaData;
+}
+
+} // namespace android
+
diff --git a/media/libwvm/WVMFileSource.cpp b/media/libwvm/WVMFileSource.cpp
new file mode 100644
index 0000000..3da21ff
--- /dev/null
+++ b/media/libwvm/WVMFileSource.cpp
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "WVMFileSource"
+#include <utils/Log.h>
+
+#include "WVMFileSource.h"
+#include "media/stagefright/MediaErrors.h"
+#include "media/stagefright/MediaDefs.h"
+#include "media/stagefright/MediaDebug.h"
+
+namespace android {
+
+
+WVMFileSource::WVMFileSource(sp<DataSource> &dataSource)
+ : mDataSource(dataSource),
+ mOffset(0)
+{
+}
+
+unsigned long long WVMFileSource::GetSize()
+{
+ off64_t size;
+ mDataSource->getSize(&size);
+ return size;
+}
+
+unsigned long long WVMFileSource::GetOffset()
+{
+ return mOffset;
+}
+
+void WVMFileSource::Seek(unsigned long long offset)
+{
+ mOffset = offset;
+}
+
+size_t WVMFileSource::Read(size_t amount, unsigned char *buffer)
+{
+ size_t result = mDataSource->readAt(mOffset, buffer, amount);
+
+#if 0
+ // debug code - log packets to files
+ char filename[32];
+ static int counter = 0;
+ sprintf(filename, "/data/wv/buf%d", counter++);
+ FILE *f = fopen(filename, "w");
+ if (!f)
+ LOGE("WVMFileSource: can't open %s", filename);
+ else {
+ fwrite(buffer, amount, 1, f);
+ fclose(f);
+ }
+ LOGD("WVMFileSource::Read(%d bytes to buf=%p)", amount, buffer);
+#endif
+
+ mOffset += result;
+ return result;
+}
+
+} // namespace android
diff --git a/media/libwvm/WVMLogging.cpp b/media/libwvm/WVMLogging.cpp
new file mode 100644
index 0000000..fd478abc
--- /dev/null
+++ b/media/libwvm/WVMLogging.cpp
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "WVMLogging"
+#include <utils/Log.h>
+#include "WVMLogging.h"
+
+// Connect Widevine debug logging into Android logging
+
+void android_printbuf(const char *buf)
+{
+ LOGD("%s", buf);
+}
diff --git a/media/libwvm/WVMMediaSource.cpp b/media/libwvm/WVMMediaSource.cpp
new file mode 100644
index 0000000..71ea2c0
--- /dev/null
+++ b/media/libwvm/WVMMediaSource.cpp
@@ -0,0 +1,278 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "WVMMediaSource"
+#include <utils/Log.h>
+
+#include "WVMMediaSource.h"
+#include "WVMFileSource.h"
+#include "media/stagefright/MediaErrors.h"
+#include "media/stagefright/MediaDefs.h"
+#include "media/stagefright/MediaDebug.h"
+
+namespace android {
+
+extern DrmManagerClient *gDrmManagerClient;
+
+WVMMediaSource::WVMMediaSource(WVSession *session, WVEsSelector esSelector,
+ const sp<MetaData> &metaData)
+ : mSession(session),
+ mESSelector(esSelector),
+ mTrackMetaData(metaData),
+ mStarted(false),
+ mGroup(NULL),
+ mDts(0),
+ mPts(0)
+{
+}
+
+// Since the WVMExtractor lifetime is short, we delegate ownership of some resources
+// to the media source, which cleans them up after when the media source is destroyed
+
+void WVMMediaSource::delegateFileSource(sp<WVMFileSource> fileSource)
+{
+ mFileSource = fileSource;
+}
+
+void WVMMediaSource::delegateDataSource(sp<DataSource> dataSource)
+{
+ mDataSource = dataSource;
+}
+
+void WVMMediaSource::allocBufferGroup()
+{
+ if (mGroup)
+ delete mGroup;
+
+ mGroup = new MediaBufferGroup;
+
+ size_t size;
+ if (mESSelector == WV_EsSelector_Video)
+ size = 256 * 1024;
+ else
+ size = 64 * 1024;
+
+ mGroup->add_buffer(new MediaBuffer(size));
+}
+
+
+
+status_t WVMMediaSource::start(MetaData *)
+{
+ //LOGD("WVMMediaSource::start()");
+ Mutex::Autolock autoLock(mLock);
+
+ CHECK(!mStarted);
+
+ allocBufferGroup();
+
+ mStarted = true;
+
+ // Let video stream control play/pause
+ if (mESSelector == WV_EsSelector_Video) {
+ float speed;
+ WVStatus result = WV_Play(mSession, 1.0, &speed, "now-");
+ if (result != WV_Status_OK) {
+ LOGE("WV_Play returned status %d in WVMMediaSource::start\n", result);
+ }
+ }
+ return OK;
+}
+
+
+status_t WVMMediaSource::stop()
+{
+ //LOGD("WVMMediaSource::stop()");
+ Mutex::Autolock autoLock(mLock);
+
+ CHECK(mStarted);
+
+ // Let video stream control play/pause
+ if (mESSelector == WV_EsSelector_Video) {
+ WVStatus result = WV_Pause(mSession, "now");
+ if (result != WV_Status_OK) {
+ LOGE("WV_Pause returned status %d in WVMMediaSource::stop\n", result);
+ }
+ }
+
+ delete mGroup;
+ mGroup = NULL;
+
+ mStarted = false;
+
+ return OK;
+}
+
+sp<MetaData> WVMMediaSource::getFormat()
+{
+ Mutex::Autolock autoLock(mLock);
+ return mTrackMetaData;
+}
+
+std::string usecToNPT(int64_t time)
+{
+ unsigned hours = (unsigned)(time / (60LL * 60 * 1000000));
+ time -= (int64_t)hours * 60 * 60 * 1000000;
+ unsigned mins = (unsigned)(time / (60 * 1000000));
+ time -= (int64_t)mins * 60 * 1000000;
+ float secs = (float)time / 1000000;
+ char buf[32];
+ sprintf(buf, "%d:%d:%f", hours, mins, secs);
+ return std::string(buf);
+}
+
+status_t WVMMediaSource::read(MediaBuffer **buffer, const ReadOptions *options)
+{
+ Mutex::Autolock autoLock(mLock);
+
+ CHECK(mStarted);
+
+ *buffer = NULL;
+
+ int64_t seekTimeUs;
+ ReadOptions::SeekMode mode;
+ if (options && options->getSeekTo(&seekTimeUs, &mode)) {
+ // Let video stream control seek
+ if (mESSelector == WV_EsSelector_Video) {
+ float scaleUsed;
+ std::string when = usecToNPT(seekTimeUs) + std::string("-");
+ WVStatus result = WV_Play(mSession, 1.0, &scaleUsed, when );
+ if (result != WV_Status_OK) {
+ LOGE("WV_Play returned status %d in WVMMediaSource::read\n", result);
+ return ERROR_IO;
+ }
+ }
+ }
+
+ MediaBuffer *mediaBuf;
+
+ status_t err = mGroup->acquire_buffer(&mediaBuf);
+
+ if (err != OK) {
+ CHECK_EQ(mediaBuf, NULL);
+ return err;
+ }
+
+ size_t bytesRead;
+ bool auStart;
+ size_t offset = 0;
+
+ // Pull full access units. Since we aren't sure how big they might be,
+ // start with initial buffer size, then allocate a larger buffer if we
+ // get more a number of bytes equal to the full buffer size and go back
+ // for the rest. Only loop in this case, usually it's one pass through.
+
+ while (true) {
+ size_t size = mediaBuf->size() - offset;
+
+ WVStatus result = WV_GetEsData(mSession, mESSelector, (uint8_t *)mediaBuf->data() + offset,
+ size, bytesRead, auStart, mDts, mPts);
+ if (result == WV_Status_End_Of_Media) {
+ mediaBuf->release();
+ return ERROR_END_OF_STREAM;
+ } else if (result != WV_Status_OK) {
+ if (result != WV_Status_Warning_Need_Key &&
+ result != WV_Status_Warning_Download_Stalled)
+ {
+ LOGE("WV_GetEsData returned ERROR %d in WVMMediaSource::read\n", result);
+ mediaBuf->release();
+ return ERROR_IO;
+ } else
+ LOGW("WV_GetEsData returned WARNING %d in WVMMediaSource::read\n", result);
+ }
+
+
+ if (bytesRead == 0) {
+ // Didn't get anything, sleep a bit so we don't hog the CPU then
+ // try again.
+ usleep(10000);
+ continue;
+ }
+
+ if (offset + bytesRead < mediaBuf->size())
+ break;
+
+ //LOGD("Resizing...");
+
+ // This buffer is too small, allocate a larger buffer twice the size
+ // and copy the data from the current buffer into the first part of
+ // the new buffer, then set offset to where the next read should go.
+
+ MediaBuffer *newBuffer = new MediaBuffer(mediaBuf->size() * 2);
+ newBuffer->add_ref();
+
+ memcpy(newBuffer->data(), mediaBuf->data(), mediaBuf->size());
+ offset = mediaBuf->size();
+
+ mGroup->add_buffer(newBuffer);
+
+ mediaBuf->release();
+ mediaBuf = newBuffer;
+ }
+
+#define PCR_HZ 90000
+ int64_t keyTime = (int64_t)mDts * 1000000 / PCR_HZ;
+
+ mediaBuf->meta_data()->clear();
+ mediaBuf->meta_data()->setInt64(kKeyTime, keyTime);
+
+ mediaBuf->set_range(0, bytesRead + offset);
+
+#if 0
+ // debug code - log packets to files
+ char filename[32];
+ static int acounter = 0, vcounter = 0;
+ if (mESSelector == WV_EsSelector_Video)
+ sprintf(filename, "/data/wvm/v%d", vcounter++);
+ else
+ sprintf(filename, "/data/wvm/a%d", acounter++);
+
+ FILE *f = fopen(filename, "w");
+ if (!f)
+ LOGE("WVMFileSource: can't open %s", filename);
+ else {
+ fwrite(mediaBuf->data(), bytesRead + offset, 1, f);
+ fclose(f);
+ }
+ LOGD("WVMMediaSource::read writing (%d bytes to %s)", bytesRead + offset, filename);
+#endif
+
+#if 0
+ LOGD("[%p] %s set range_length=%d, get range_length=%d kKeyTime=%lld\n", mediaBuf,
+ (mESSelector == WV_EsSelector_Video ? "video" : "audio"),
+ bytesRead + offset, mediaBuf->range_length(), keyTime);
+#endif
+
+ *buffer = mediaBuf;
+
+ return OK;
+}
+
+WVMMediaSource::~WVMMediaSource()
+{
+ //LOGD("WVMMediaSource::~WVMMediaSource()");
+
+ if (mStarted) {
+ stop();
+ }
+
+ if (mESSelector == WV_EsSelector_Video) {
+ if (mSession != NULL)
+ WV_Teardown(mSession);
+ }
+}
+
+} // namespace android
diff --git a/media/libwvm/include/WVMExtractorImpl.h b/media/libwvm/include/WVMExtractorImpl.h
new file mode 100644
index 0000000..ba04c1d
--- /dev/null
+++ b/media/libwvm/include/WVMExtractorImpl.h
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ */
+
+#ifndef WVMEXTRACTOR_H_
+
+#define WVMEXTRACTOR_H_
+
+#include "AndroidConfig.h"
+#include "WVStreamControlAPI.h"
+#include <media/stagefright/MediaExtractor.h>
+#include <media/stagefright/DataSource.h>
+#include <utils/RefBase.h>
+
+
+// DLL entry - given a data source, instantiate a WVMExtractor object
+
+namespace android {
+
+MediaExtractor *GetInstance(sp<DataSource> dataSource);
+
+
+class WVMMediaSource;
+class WVMFileSource;
+
+class WVMExtractorImpl : public MediaExtractor {
+public:
+ WVMExtractorImpl(sp<DataSource> dataSource);
+
+ virtual size_t countTracks();
+ virtual sp<MediaSource> getTrack(size_t index);
+ virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
+
+ virtual sp<MetaData> getMetaData();
+
+protected:
+ virtual ~WVMExtractorImpl();
+
+private:
+ status_t readAVCCMetaData(sp<MetaData> videoMetaData);
+ status_t readESDSMetaData(sp<MetaData> audioMetaData);
+
+ sp<WVMMediaSource> mAudioSource;
+ sp<WVMMediaSource> mVideoSource;
+ sp<MetaData> mFileMetaData;
+ sp<WVMFileSource> mFileSource;
+ sp<DataSource> mDataSource;
+
+ bool mHaveMetaData;
+
+ WVSession *mSession;
+
+ status_t mSetupStatus;
+
+ status_t readMetaData();
+
+ const static size_t kStreamCacheSize = 10 * 1024 * 1024;
+
+ WVMExtractorImpl(const WVMExtractorImpl &);
+ WVMExtractorImpl &operator=(const WVMExtractorImpl &);
+};
+
+} // namespace android
+
+#endif // WVMEXTRACTOR_H_
diff --git a/media/libwvm/include/WVMFileSource.h b/media/libwvm/include/WVMFileSource.h
new file mode 100644
index 0000000..da51da2
--- /dev/null
+++ b/media/libwvm/include/WVMFileSource.h
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+#ifndef WVFILE_SOURCE_H_
+#define WVFILE_SOURCE_H_
+
+#include "AndroidConfig.h"
+#include "WVStreamControlAPI.h"
+#include <media/stagefright/DataSource.h>
+#include <utils/RefBase.h>
+
+//
+// Supports reading data from local file descriptor instead of URI-based streaming
+// as we normally do.
+//
+
+namespace android {
+
+class WVMFileSource : public WVFileSource, public RefBase {
+public:
+ WVMFileSource(sp<DataSource> &dataSource);
+ virtual ~WVMFileSource() {}
+
+ virtual unsigned long long GetSize();
+ virtual unsigned long long GetOffset();
+
+ virtual void Seek(unsigned long long offset);
+ virtual size_t Read(size_t amount, unsigned char *buffer);
+
+private:
+ sp<DataSource> mDataSource;
+ unsigned long long mOffset;
+};
+
+};
+
+
+#endif
diff --git a/media/libwvm/include/WVMLogging.h b/media/libwvm/include/WVMLogging.h
new file mode 100644
index 0000000..1e46c7a
--- /dev/null
+++ b/media/libwvm/include/WVMLogging.h
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+
+#ifndef __WVMLOGGING_H__
+#define __WVMLOGGING_H__
+
+void android_printbuf(const char *buf);
+
+#endif
diff --git a/media/libwvm/include/WVMMediaSource.h b/media/libwvm/include/WVMMediaSource.h
new file mode 100644
index 0000000..5f311b4
--- /dev/null
+++ b/media/libwvm/include/WVMMediaSource.h
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ */
+
+#ifndef WVMMEDIA_SOURCE_H_
+#define WVMMEDIA_SOURCE_H_
+
+#include "AndroidConfig.h"
+#include "WVStreamControlAPI.h"
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/MediaBufferGroup.h>
+#include <utils/RefBase.h>
+
+
+namespace android {
+
+class WVMFileSource;
+
+class WVMMediaSource : public MediaSource {
+public:
+ WVMMediaSource(WVSession *session, WVEsSelector esSelector,
+ const sp<MetaData> &metaData);
+
+ void delegateFileSource(sp<WVMFileSource> fileSource);
+ void delegateDataSource(sp<DataSource> dataSource);
+
+ virtual status_t start(MetaData *params = NULL);
+ virtual status_t stop();
+
+ virtual sp<MetaData> getFormat();
+
+ virtual status_t read(MediaBuffer **buffer, const ReadOptions *options = NULL);
+
+protected:
+ virtual ~WVMMediaSource();
+
+private:
+ Mutex mLock;
+
+ WVSession *mSession;
+ WVEsSelector mESSelector; // indicates audio vs. video
+
+ sp<MetaData> mTrackMetaData;
+
+ bool mStarted;
+
+ MediaBufferGroup *mGroup;
+
+ unsigned long long mDts;
+ unsigned long long mPts;
+
+ sp<WVMFileSource> mFileSource;
+ sp<DataSource> mDataSource;
+
+ void allocBufferGroup();
+
+ WVMMediaSource(const WVMMediaSource &);
+ WVMMediaSource &operator=(const WVMMediaSource &);
+};
+
+} // namespace android
+
+#endif // WVMMEDIA_SOURCE_H_
diff --git a/media/libwvm/test/Android.mk b/media/libwvm/test/Android.mk
new file mode 100644
index 0000000..dc237de
--- /dev/null
+++ b/media/libwvm/test/Android.mk
@@ -0,0 +1,27 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ Testlibwvm.cpp
+
+LOCAL_C_INCLUDES+= \
+ bionic \
+ vendor/widevine/proprietary/include \
+ external/stlport/stlport \
+ frameworks/base/media/libstagefright
+
+LOCAL_SHARED_LIBRARIES := \
+ libstlport \
+ libdrmframework \
+ libstagefright \
+ liblog \
+ libutils \
+ libz \
+ libdl
+
+LOCAL_MODULE:=test-libwvm
+
+LOCAL_MODULE_TAGS := tests
+
+include $(BUILD_EXECUTABLE)
+
diff --git a/media/libwvm/test/Testlibwvm.cpp b/media/libwvm/test/Testlibwvm.cpp
new file mode 100644
index 0000000..2cd6040
--- /dev/null
+++ b/media/libwvm/test/Testlibwvm.cpp
@@ -0,0 +1,81 @@
+/*
+ * 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.
+ */
+
+#include <dlfcn.h>
+#include <iostream>
+
+#include "include/WVMExtractor.h"
+#include <media/stagefright/Utils.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/FileSource.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaDebug.h>
+
+using namespace android;
+using namespace std;
+
+class TestLibWVM
+{
+public:
+ TestLibWVM() {}
+ ~TestLibWVM() {}
+
+ // Tests
+ void Load();
+};
+
+DrmManagerClient* gDrmManagerClient;
+
+// This test just confirms that there are no unresolved symbols in libwvm and we
+// can locate the entry point.
+
+void TestLibWVM::Load()
+{
+ cout << "TestLibWVM::Load" << endl;
+
+ const char *path = "/system/lib/libwvm.so";
+ void *handle = dlopen(path, RTLD_NOW);
+ if (handle == NULL) {
+ fprintf(stderr, "Can't open plugin: %s\n", path);
+ exit(-1);
+ }
+
+ typedef MediaExtractor *(*GetInstanceFunc)(sp<DataSource>);
+ GetInstanceFunc getInstanceFunc =
+ (GetInstanceFunc) dlsym(handle,
+ "_ZN7android11GetInstanceENS_2spINS_10DataSourceEEE");
+
+ // Basic test - just see if we can instantiate the object and call a method
+ if (getInstanceFunc) {
+ LOGD("Found GetInstanceFunc");
+ } else {
+ LOGE("Failed to locate GetInstance in libwvm.so");
+ }
+
+// dlclose(handle);
+ printf("Test successful!\n");
+ exit(0);
+}
+
+int main(int argc, char **argv)
+{
+ TestLibWVM test;
+ test.Load();
+}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaPlayerStressTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaPlayerStressTest.java
index e6b1866..b2086d6 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaPlayerStressTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaPlayerStressTest.java
@@ -67,7 +67,7 @@
output.write(" Error: " + CodecTest.mPlaybackError);
output.write(" Unknown Info: " + CodecTest.mMediaInfoUnknownCount);
output.write(" Track Lagging: " + CodecTest.mMediaInfoVideoTrackLaggingCount);
- output.write(" BadInterleaving: " + CodecTest.mMediaInfoBadInterleavingCount);
+ output.write(" Bad Interleaving: " + CodecTest.mMediaInfoBadInterleavingCount);
output.write(" Not Seekable: " + CodecTest.mMediaInfoNotSeekableCount);
output.write(" Info Meta data update: " + CodecTest.mMediaInfoMetdataUpdateCount);
output.write("\n");
@@ -77,9 +77,9 @@
output.write("Total Result:\n");
output.write("Total Complete: " + mTotalComplete + "\n");
output.write("Total Error: " + mTotalPlaybackError + "\n");
- output.write("Total Unknown Info: " + mTotalInfoUnknown);
+ output.write("Total Unknown Info: " + mTotalInfoUnknown + "\n");
output.write("Total Track Lagging: " + mTotalVideoTrackLagging + "\n" );
- output.write("Total BadInterleaving: " + mTotalBadInterleaving + "\n");
+ output.write("Total Bad Interleaving: " + mTotalBadInterleaving + "\n");
output.write("Total Not Seekable: " + mTotalNotSeekable + "\n");
output.write("Total Info Meta data update: " + mTotalMetaDataUpdate + "\n");
output.write("\n");
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_notification_dnd.png b/packages/SystemUI/res/drawable-mdpi/ic_notification_dnd.png
new file mode 100644
index 0000000..6d4da7f
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_notification_dnd.png
Binary files differ
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar_recent_item.xml b/packages/SystemUI/res/layout-xlarge/status_bar_recent_item.xml
index c358e13..d7e1633 100644
--- a/packages/SystemUI/res/layout-xlarge/status_bar_recent_item.xml
+++ b/packages/SystemUI/res/layout-xlarge/status_bar_recent_item.xml
@@ -58,26 +58,33 @@
/>
<TextView android:id="@+id/app_label"
- android:layout_width="113dip"
+ android:layout_width="97dip"
android:layout_height="wrap_content"
android:textSize="18dip"
- android:fadingEdge="none"
- android:fadingEdgeLength="0dp"
+ android:fadingEdge="horizontal"
+ android:fadingEdgeLength="10dip"
android:scrollHorizontally="true"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_marginLeft="16dip"
android:layout_marginTop="32dip"
+ android:singleLine="true"
+ android:ellipsize="marquee"
/>
<TextView android:id="@+id/app_description"
- android:layout_width="wrap_content"
+ android:layout_width="97dip"
android:layout_height="wrap_content"
android:textSize="18dip"
+ android:fadingEdge="horizontal"
+ android:fadingEdgeLength="10dip"
+ android:scrollHorizontally="true"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_marginLeft="16dip"
android:layout_marginTop="61dip"
+ android:singleLine="true"
+ android:ellipsize="marquee"
/>
</RelativeLayout>
diff --git a/packages/SystemUI/res/values-xlarge/strings.xml b/packages/SystemUI/res/values-xlarge/strings.xml
index dfd5851..35be532 100644
--- a/packages/SystemUI/res/values-xlarge/strings.xml
+++ b/packages/SystemUI/res/values-xlarge/strings.xml
@@ -43,4 +43,11 @@
<!-- Notification text: when GPS has found a fix [CHAR LIMIT=50] -->
<string name="gps_notification_found_text">Location set by GPS</string>
+
+ <!-- Title for the pseudo-notification shown when notifications are disabled (do-not-disturb
+ mode) -->
+ <string name="notifications_off_title">Notifications off</string>
+
+ <!-- Content text for do-not-disturb mode notification -->
+ <string name="notifications_off_text">Tap here to turn notifications back on.</string>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index dfe0262..0273a4c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -599,16 +599,6 @@
return (mSignalStrength != null) && !mSignalStrength.isGsm();
}
- private boolean isEvdo() {
- return ( (mServiceState != null)
- && ((mServiceState.getRadioTechnology()
- == ServiceState.RADIO_TECHNOLOGY_EVDO_0)
- || (mServiceState.getRadioTechnology()
- == ServiceState.RADIO_TECHNOLOGY_EVDO_A)
- || (mServiceState.getRadioTechnology()
- == ServiceState.RADIO_TECHNOLOGY_EVDO_B)));
- }
-
private boolean hasService() {
if (mServiceState != null) {
switch (mServiceState.getState()) {
@@ -624,7 +614,6 @@
}
private final void updateSignalStrength() {
- int iconLevel = -1;
int[] iconList;
// Display signal strength while in "emergency calls only" mode
@@ -641,18 +630,6 @@
}
if (!isCdma()) {
- int asu = mSignalStrength.getGsmSignalStrength();
-
- // ASU ranges from 0 to 31 - TS 27.007 Sec 8.5
- // asu = 0 (-113dB or less) is very weak
- // signal, its better to show 0 bars to the user in such cases.
- // asu = 99 is a special case, where the signal strength is unknown.
- if (asu <= 2 || asu == 99) iconLevel = 0;
- else if (asu >= 12) iconLevel = 4;
- else if (asu >= 8) iconLevel = 3;
- else if (asu >= 5) iconLevel = 2;
- else iconLevel = 1;
-
// Though mPhone is a Manager, this call is not an IPC
if (mPhone.isNetworkRoaming()) {
iconList = sSignalImages_r[mInetCondition];
@@ -661,67 +638,11 @@
}
} else {
iconList = sSignalImages[mInetCondition];
-
- // If 3G(EV) and 1x network are available than 3G should be
- // displayed, displayed RSSI should be from the EV side.
- // If a voice call is made then RSSI should switch to 1x.
- if ((mPhoneState == TelephonyManager.CALL_STATE_IDLE) && isEvdo()){
- iconLevel = getEvdoLevel();
- if (false) {
- Slog.d(TAG, "use Evdo level=" + iconLevel + " to replace Cdma Level="
- + getCdmaLevel());
- }
- } else {
- iconLevel = getCdmaLevel();
- }
}
- mPhoneSignalIconId = iconList[iconLevel];
+ mPhoneSignalIconId = iconList[mSignalStrength.getLevel()];
mService.setIcon("phone_signal", mPhoneSignalIconId, 0);
}
- private int getCdmaLevel() {
- final int cdmaDbm = mSignalStrength.getCdmaDbm();
- final int cdmaEcio = mSignalStrength.getCdmaEcio();
- int levelDbm = 0;
- int levelEcio = 0;
-
- if (cdmaDbm >= -75) levelDbm = 4;
- else if (cdmaDbm >= -85) levelDbm = 3;
- else if (cdmaDbm >= -95) levelDbm = 2;
- else if (cdmaDbm >= -100) levelDbm = 1;
- else levelDbm = 0;
-
- // Ec/Io are in dB*10
- if (cdmaEcio >= -90) levelEcio = 4;
- else if (cdmaEcio >= -110) levelEcio = 3;
- else if (cdmaEcio >= -130) levelEcio = 2;
- else if (cdmaEcio >= -150) levelEcio = 1;
- else levelEcio = 0;
-
- return (levelDbm < levelEcio) ? levelDbm : levelEcio;
- }
-
- private int getEvdoLevel() {
- int evdoDbm = mSignalStrength.getEvdoDbm();
- int evdoSnr = mSignalStrength.getEvdoSnr();
- int levelEvdoDbm = 0;
- int levelEvdoSnr = 0;
-
- if (evdoDbm >= -65) levelEvdoDbm = 4;
- else if (evdoDbm >= -75) levelEvdoDbm = 3;
- else if (evdoDbm >= -90) levelEvdoDbm = 2;
- else if (evdoDbm >= -105) levelEvdoDbm = 1;
- else levelEvdoDbm = 0;
-
- if (evdoSnr >= 7) levelEvdoSnr = 4;
- else if (evdoSnr >= 5) levelEvdoSnr = 3;
- else if (evdoSnr >= 3) levelEvdoSnr = 2;
- else if (evdoSnr >= 1) levelEvdoSnr = 1;
- else levelEvdoSnr = 0;
-
- return (levelEvdoDbm < levelEvdoSnr) ? levelEvdoDbm : levelEvdoSnr;
- }
-
private final void updateDataNetType(int net) {
switch (net) {
case TelephonyManager.NETWORK_TYPE_EDGE:
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index 42868db..317490f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -33,6 +33,11 @@
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Binder;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Messenger;
import android.os.RemoteException;
import android.provider.Settings;
import android.provider.Telephony;
@@ -50,6 +55,7 @@
import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.telephony.cdma.EriInfo;
import com.android.server.am.BatteryStatsService;
+import com.android.internal.util.AsyncChannel;
import com.android.systemui.R;
@@ -82,6 +88,7 @@
// wifi
final WifiManager mWifiManager;
+ AsyncChannel mWifiChannel;
boolean mWifiEnabled, mWifiConnected;
int mWifiLevel;
String mWifiSsid;
@@ -140,6 +147,14 @@
// wifi
mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
+ HandlerThread handlerThread = new HandlerThread("WifiServiceThread");
+ handlerThread.start();
+ Handler handler = new WifiHandler(handlerThread.getLooper());
+ mWifiChannel = new AsyncChannel();
+ Messenger wifiMessenger = mWifiManager.getMessenger();
+ if (wifiMessenger != null) {
+ mWifiChannel.connect(mContext, handler, wifiMessenger);
+ }
// broadcasts
IntentFilter filter = new IntentFilter();
@@ -185,6 +200,7 @@
mLabelViews.add(v);
}
+ @Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (action.equals(WifiManager.RSSI_CHANGED_ACTION)
@@ -300,13 +316,6 @@
return (mSignalStrength != null) && !mSignalStrength.isGsm();
}
- private boolean isEvdo() {
- return ((mServiceState != null)
- && ((mServiceState.getRadioTechnology() == ServiceState.RADIO_TECHNOLOGY_EVDO_0)
- || (mServiceState.getRadioTechnology() == ServiceState.RADIO_TECHNOLOGY_EVDO_A)
- || (mServiceState.getRadioTechnology() == ServiceState.RADIO_TECHNOLOGY_EVDO_B)));
- }
-
private boolean hasService() {
if (mServiceState != null) {
switch (mServiceState.getState()) {
@@ -321,51 +330,6 @@
}
}
- private int getCdmaLevel() {
- if (mSignalStrength == null) return 0;
- final int cdmaDbm = mSignalStrength.getCdmaDbm();
- final int cdmaEcio = mSignalStrength.getCdmaEcio();
- int levelDbm = 0;
- int levelEcio = 0;
-
- if (cdmaDbm >= -75) levelDbm = 4;
- else if (cdmaDbm >= -85) levelDbm = 3;
- else if (cdmaDbm >= -95) levelDbm = 2;
- else if (cdmaDbm >= -100) levelDbm = 1;
- else levelDbm = 0;
-
- // Ec/Io are in dB*10
- if (cdmaEcio >= -90) levelEcio = 4;
- else if (cdmaEcio >= -110) levelEcio = 3;
- else if (cdmaEcio >= -130) levelEcio = 2;
- else if (cdmaEcio >= -150) levelEcio = 1;
- else levelEcio = 0;
-
- return (levelDbm < levelEcio) ? levelDbm : levelEcio;
- }
-
- private int getEvdoLevel() {
- if (mSignalStrength == null) return 0;
- int evdoDbm = mSignalStrength.getEvdoDbm();
- int evdoSnr = mSignalStrength.getEvdoSnr();
- int levelEvdoDbm = 0;
- int levelEvdoSnr = 0;
-
- if (evdoDbm >= -65) levelEvdoDbm = 4;
- else if (evdoDbm >= -75) levelEvdoDbm = 3;
- else if (evdoDbm >= -90) levelEvdoDbm = 2;
- else if (evdoDbm >= -105) levelEvdoDbm = 1;
- else levelEvdoDbm = 0;
-
- if (evdoSnr >= 7) levelEvdoSnr = 4;
- else if (evdoSnr >= 5) levelEvdoSnr = 3;
- else if (evdoSnr >= 3) levelEvdoSnr = 2;
- else if (evdoSnr >= 1) levelEvdoSnr = 1;
- else levelEvdoSnr = 0;
-
- return (levelEvdoDbm < levelEvdoSnr) ? levelEvdoDbm : levelEvdoSnr;
- }
-
private final void updateTelephonySignalStrength() {
// Display signal strength while in "emergency calls only" mode
if (mServiceState == null || (!hasService() && !mServiceState.isEmergencyOnly())) {
@@ -382,44 +346,23 @@
if (mSignalStrength == null) {
mPhoneSignalIconId = R.drawable.stat_sys_signal_null;
mDataSignalIconId = R.drawable.stat_sys_signal_0; // note we use 0 instead of null
- } else if (isCdma()) {
- // If 3G(EV) and 1x network are available than 3G should be
- // displayed, displayed RSSI should be from the EV side.
- // If a voice call is made then RSSI should switch to 1x.
- int iconLevel;
- if ((mPhoneState == TelephonyManager.CALL_STATE_IDLE) && isEvdo()){
- iconLevel = getEvdoLevel();
- } else {
- iconLevel = getCdmaLevel();
- }
- int[] iconList;
- if (isCdmaEri()) {
- iconList = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH_ROAMING[mInetCondition];
- } else {
- iconList = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH[mInetCondition];
- }
- mPhoneSignalIconId = iconList[iconLevel];
- mDataSignalIconId = TelephonyIcons.DATA_SIGNAL_STRENGTH[mInetCondition][iconLevel];
} else {
- int asu = mSignalStrength.getGsmSignalStrength();
-
- // ASU ranges from 0 to 31 - TS 27.007 Sec 8.5
- // asu = 0 (-113dB or less) is very weak
- // signal, its better to show 0 bars to the user in such cases.
- // asu = 99 is a special case, where the signal strength is unknown.
int iconLevel;
- if (asu <= 2 || asu == 99) iconLevel = 0;
- else if (asu >= 12) iconLevel = 4;
- else if (asu >= 8) iconLevel = 3;
- else if (asu >= 5) iconLevel = 2;
- else iconLevel = 1;
-
- // Though mPhone is a Manager, this call is not an IPC
int[] iconList;
- if (mPhone.isNetworkRoaming()) {
- iconList = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH_ROAMING[mInetCondition];
+ iconLevel = mSignalStrength.getLevel();
+ if (isCdma()) {
+ if (isCdmaEri()) {
+ iconList = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH_ROAMING[mInetCondition];
+ } else {
+ iconList = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH[mInetCondition];
+ }
} else {
- iconList = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH[mInetCondition];
+ // Though mPhone is a Manager, this call is not an IPC
+ if (mPhone.isNetworkRoaming()) {
+ iconList = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH_ROAMING[mInetCondition];
+ } else {
+ iconList = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH[mInetCondition];
+ }
}
mPhoneSignalIconId = iconList[iconLevel];
mDataSignalIconId = TelephonyIcons.DATA_SIGNAL_STRENGTH[mInetCondition][iconLevel];
@@ -584,6 +527,44 @@
// ===== Wifi ===================================================================
+ class WifiHandler extends Handler {
+
+ WifiHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
+ if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
+ mWifiChannel.sendMessage(Message.obtain(this,
+ AsyncChannel.CMD_CHANNEL_FULL_CONNECTION));
+ } else {
+ Slog.e(TAG, "Failed to connect to wifi");
+ }
+ break;
+ case WifiManager.DATA_ACTIVITY_NOTIFICATION:
+ int dataActivity = msg.arg1;
+ /* TODO: update icons based on data activity */
+ switch (dataActivity) {
+ case WifiManager.DATA_ACTIVITY_IN:
+ break;
+ case WifiManager.DATA_ACTIVITY_OUT:
+ break;
+ case WifiManager.DATA_ACTIVITY_INOUT:
+ break;
+ case WifiManager.DATA_ACTIVITY_NONE:
+ break;
+ }
+ break;
+ default:
+ //Ignore
+ break;
+ }
+ }
+ }
+
private void updateWifiState(Intent intent) {
final String action = intent.getAction();
if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
index 7a13fde..afb73a9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
@@ -28,6 +28,7 @@
import android.app.StatusBarManager;
import android.content.Context;
import android.content.Intent;
+import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.inputmethodservice.InputMethodService;
@@ -67,6 +68,7 @@
import com.android.systemui.statusbar.policy.BluetoothController;
import com.android.systemui.statusbar.policy.LocationController;
import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.statusbar.policy.Prefs;
import com.android.systemui.recent.RecentApplicationsActivity;
public class TabletStatusBar extends StatusBar implements
@@ -105,7 +107,7 @@
IWindowManager mWindowManager;
// tracking all current notifications
- private NotificationData mNotns = new NotificationData();
+ private NotificationData mNotificationData = new NotificationData();
TabletStatusBarView mStatusBarView;
View mNotificationArea;
@@ -113,6 +115,9 @@
NotificationIconArea mNotificationIconArea;
View mNavigationArea;
+ boolean mNotificationDNDMode;
+ NotificationData.Entry mNotificationDNDDummyEntry;
+
ImageView mBackButton;
View mHomeButton;
View mMenuButton;
@@ -489,25 +494,43 @@
switch (m.what) {
case MSG_OPEN_NOTIFICATION_PEEK:
if (DEBUG) Slog.d(TAG, "opening notification peek window; arg=" + m.arg1);
+
if (m.arg1 >= 0) {
- final int N = mNotns.size();
- if (mNotificationPeekIndex >= 0 && mNotificationPeekIndex < N) {
- NotificationData.Entry entry = mNotns.get(N-1-mNotificationPeekIndex);
- entry.icon.setBackgroundColor(0);
- mNotificationPeekIndex = -1;
- mNotificationPeekKey = null;
+ final int N = mNotificationData.size();
+
+ if (!mNotificationDNDMode) {
+ if (mNotificationPeekIndex >= 0 && mNotificationPeekIndex < N) {
+ NotificationData.Entry entry = mNotificationData.get(N-1-mNotificationPeekIndex);
+ entry.icon.setBackgroundColor(0);
+ mNotificationPeekIndex = -1;
+ mNotificationPeekKey = null;
+ }
}
final int peekIndex = m.arg1;
if (peekIndex < N) {
//Slog.d(TAG, "loading peek: " + peekIndex);
- NotificationData.Entry entry = mNotns.get(N-1-peekIndex);
+ NotificationData.Entry entry =
+ mNotificationDNDMode
+ ? mNotificationDNDDummyEntry
+ : mNotificationData.get(N-1-peekIndex);
NotificationData.Entry copy = new NotificationData.Entry(
entry.key,
entry.notification,
entry.icon);
inflateViews(copy, mNotificationPeekRow);
+ if (mNotificationDNDMode) {
+ copy.content.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ SharedPreferences.Editor editor = Prefs.edit(mContext);
+ editor.putBoolean(Prefs.DO_NOT_DISTURB_PREF, false);
+ editor.apply();
+ animateCollapse();
+ }
+ });
+ }
+
entry.icon.setBackgroundColor(0x20FFFFFF);
// mNotificationPeekRow.setLayoutTransition(
@@ -530,9 +553,13 @@
if (DEBUG) Slog.d(TAG, "closing notification peek window");
mNotificationPeekWindow.setVisibility(View.GONE);
mNotificationPeekRow.removeAllViews();
- final int N = mNotns.size();
+
+ final int N = mNotificationData.size();
if (mNotificationPeekIndex >= 0 && mNotificationPeekIndex < N) {
- NotificationData.Entry entry = mNotns.get(N-1-mNotificationPeekIndex);
+ NotificationData.Entry entry =
+ mNotificationDNDMode
+ ? mNotificationDNDDummyEntry
+ : mNotificationData.get(N-1-mNotificationPeekIndex);
entry.icon.setBackgroundColor(0);
}
@@ -643,7 +670,7 @@
public void updateNotification(IBinder key, StatusBarNotification notification) {
if (DEBUG) Slog.d(TAG, "updateNotification(" + key + " -> " + notification + ") // TODO");
- final NotificationData.Entry oldEntry = mNotns.findByKey(key);
+ final NotificationData.Entry oldEntry = mNotificationData.findByKey(key);
if (oldEntry == null) {
Slog.w(TAG, "updateNotification for unknown key: " + key);
return;
@@ -781,14 +808,15 @@
if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
if ((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
Slog.i(TAG, "DISABLE_NOTIFICATION_ICONS: yes");
- // synchronize with current shadow state
- mNotificationIconArea.setVisibility(View.GONE);
mTicker.halt();
} else {
Slog.i(TAG, "DISABLE_NOTIFICATION_ICONS: no");
- // synchronize with current shadow state
- mNotificationIconArea.setVisibility(View.VISIBLE);
}
+ // refresh icons to show either notifications or the DND message
+ mNotificationDNDMode = Prefs.read(mContext)
+ .getBoolean(Prefs.DO_NOT_DISTURB_PREF, Prefs.DO_NOT_DISTURB_DEFAULT);
+ Slog.d(TAG, "DND: " + mNotificationDNDMode);
+ reloadAllNotificationIcons();
} else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
if ((state & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
mTicker.halt();
@@ -947,7 +975,7 @@
}
private void setAreThereNotifications() {
- final boolean hasClearable = mNotns.hasClearableItems();
+ final boolean hasClearable = mNotificationData.hasClearableItems();
}
/**
@@ -1081,7 +1109,7 @@
}
StatusBarNotification removeNotificationViews(IBinder key) {
- NotificationData.Entry entry = mNotns.remove(key);
+ NotificationData.Entry entry = mNotificationData.remove(key);
if (entry == null) {
Slog.w(TAG, "removeNotification for unknown key: " + key);
return null;
@@ -1192,7 +1220,7 @@
}
// Add the icon.
- int pos = mNotns.add(entry);
+ int pos = mNotificationData.add(entry);
if (DEBUG) {
Slog.d(TAG, "addNotificationViews: added at " + pos);
}
@@ -1216,7 +1244,31 @@
final LinearLayout.LayoutParams params
= new LinearLayout.LayoutParams(mIconSize + 2*mIconHPadding, mNaturalBarHeight);
- int N = mNotns.size();
+ // alternate behavior in DND mode
+ if (mNotificationDNDMode && mIconLayout.getChildCount() == 0) {
+ final StatusBarIconView iconView = new StatusBarIconView(mContext, "_dnd");
+ iconView.setImageResource(R.drawable.ic_notification_dnd);
+ iconView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
+ iconView.setPadding(mIconHPadding, 0, mIconHPadding, 0);
+
+ final Notification dndNotification = new Notification.Builder(mContext)
+ .setContentTitle(mContext.getText(R.string.notifications_off_title))
+ .setContentText(mContext.getText(R.string.notifications_off_text))
+ .setSmallIcon(R.drawable.ic_notification_dnd)
+ .setOngoing(true)
+ .getNotification();
+
+ mNotificationDNDDummyEntry = new NotificationData.Entry(
+ null,
+ new StatusBarNotification("", 0, "", 0, 0, dndNotification),
+ iconView);
+
+ mIconLayout.addView(iconView, params);
+
+ return;
+ }
+
+ int N = mNotificationData.size();
if (DEBUG) {
Slog.d(TAG, "refreshing icons: " + N + " notifications, mIconLayout=" + mIconLayout);
@@ -1231,7 +1283,7 @@
MAX_NOTIFICATION_ICONS_IME_BUTTON_VISIBLE : MAX_NOTIFICATION_ICONS;
for (int i=0; i< maxNotificationIconsCount; i++) {
if (i>=N) break;
- toShow.add(mNotns.get(N-i-1).icon);
+ toShow.add(mNotificationData.get(N-i-1).icon);
}
ArrayList<View> toRemove = new ArrayList<View>();
@@ -1258,12 +1310,12 @@
}
private void loadNotificationPanel() {
- int N = mNotns.size();
+ int N = mNotificationData.size();
ArrayList<View> toShow = new ArrayList<View>();
for (int i=0; i<N; i++) {
- View row = mNotns.get(N-i-1).row;
+ View row = mNotificationData.get(N-i-1).row;
toShow.add(row);
}
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
index 7809961..b32a729 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
+++ b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
@@ -89,7 +89,7 @@
* This class is created by the initialization routine of the {@link WindowManagerPolicy},
* and runs on its thread. The keyguard UI is created from that thread in the
* constructor of this class. The apis may be called from other threads, including the
- * {@link com.android.server.InputManager}'s and {@link android.view.WindowManager}'s.
+ * {@link com.android.server.wm.InputManager}'s and {@link android.view.WindowManager}'s.
* Therefore, methods on this class are synchronized, and any action that is pointed
* directly to the keyguard UI is posted to a {@link Handler} to ensure it is taken on the UI
* thread of the keyguard.
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 51e9b00..44b8590 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -26,7 +26,7 @@
import com.android.internal.view.IInputMethodSession;
import com.android.internal.view.InputBindResult;
-import com.android.server.StatusBarManagerService;
+import com.android.server.EventLogTags;
import org.xmlpull.v1.XmlPullParserException;
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index caf6376..d80a2cd 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -40,7 +40,6 @@
import android.os.BatteryManager;
import android.os.BatteryStats;
import android.os.Binder;
-import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
@@ -50,7 +49,6 @@
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.WorkSource;
import android.provider.Settings.SettingNotFoundException;
@@ -69,14 +67,13 @@
import static android.provider.Settings.System.TRANSITION_ANIMATION_SCALE;
import java.io.FileDescriptor;
-import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Observable;
import java.util.Observer;
-class PowerManagerService extends IPowerManager.Stub
+public class PowerManagerService extends IPowerManager.Stub
implements LocalPowerManager, Watchdog.Monitor {
private static final String TAG = "PowerManagerService";
@@ -2689,7 +2686,7 @@
}
}
- void setPolicy(WindowManagerPolicy p) {
+ public void setPolicy(WindowManagerPolicy p) {
synchronized (mLocks) {
mPolicy = p;
mLocks.notifyAll();
diff --git a/services/java/com/android/server/StatusBarManagerService.java b/services/java/com/android/server/StatusBarManagerService.java
index 5ada77b..8df8177 100644
--- a/services/java/com/android/server/StatusBarManagerService.java
+++ b/services/java/com/android/server/StatusBarManagerService.java
@@ -39,6 +39,7 @@
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.statusbar.StatusBarIconList;
import com.android.internal.statusbar.StatusBarNotification;
+import com.android.server.wm.WindowManagerService;
import java.io.FileDescriptor;
import java.io.PrintWriter;
diff --git a/services/java/com/android/server/SystemBackupAgent.java b/services/java/com/android/server/SystemBackupAgent.java
index a1f43b4..80b0174 100644
--- a/services/java/com/android/server/SystemBackupAgent.java
+++ b/services/java/com/android/server/SystemBackupAgent.java
@@ -16,6 +16,7 @@
package com.android.server;
+
import android.app.backup.BackupDataInput;
import android.app.backup.BackupDataOutput;
import android.app.backup.BackupAgentHelper;
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 6a6cc2a..52c47e1 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -17,6 +17,7 @@
package com.android.server;
import com.android.server.am.ActivityManagerService;
+import com.android.server.wm.WindowManagerService;
import com.android.internal.app.ShutdownThread;
import com.android.internal.os.BinderInternal;
import com.android.internal.os.SamplingProfilerIntegration;
diff --git a/services/java/com/android/server/WallpaperManagerService.java b/services/java/com/android/server/WallpaperManagerService.java
index 997e750..b1a6a9a 100644
--- a/services/java/com/android/server/WallpaperManagerService.java
+++ b/services/java/com/android/server/WallpaperManagerService.java
@@ -70,8 +70,6 @@
import com.android.internal.service.wallpaper.ImageWallpaper;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.JournaledFile;
-import com.android.server.DevicePolicyManagerService.ActiveAdmin;
-import com.android.server.DevicePolicyManagerService.MyPackageMonitor;
class WallpaperManagerService extends IWallpaperManager.Stub {
static final String TAG = "WallpaperService";
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index 56fae81..cc25e8d 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -42,14 +42,18 @@
import android.net.DhcpInfo;
import android.net.NetworkInfo;
import android.net.NetworkInfo.State;
+import android.net.NetworkInfo.DetailedState;
+import android.net.TrafficStats;
import android.os.Binder;
import android.os.Handler;
+import android.os.Messenger;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.INetworkManagementService;
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.SystemProperties;
import android.os.WorkSource;
import android.provider.Settings;
import android.text.TextUtils;
@@ -111,6 +115,20 @@
private final IBatteryStats mBatteryStats;
+ private boolean mEnableTrafficStatsPoll = false;
+ private int mTrafficStatsPollToken = 0;
+ private long mTxPkts;
+ private long mRxPkts;
+ /* Tracks last reported data activity */
+ private int mDataActivity;
+ private String mInterfaceName;
+
+ /**
+ * Interval in milliseconds between polling for traffic
+ * statistics
+ */
+ private static final int POLL_TRAFFIC_STATS_INTERVAL_MSECS = 1000;
+
/**
* See {@link Settings.Secure#WIFI_IDLE_MS}. This is the default value if a
* Settings.Secure value is not present. This timeout value is chosen as
@@ -123,6 +141,9 @@
private static final String ACTION_DEVICE_IDLE =
"com.android.server.WifiManager.action.DEVICE_IDLE";
+ private static final int CMD_ENABLE_TRAFFIC_STATS_POLL = 1;
+ private static final int CMD_TRAFFIC_STATS_POLL = 2;
+
private boolean mIsReceiverRegistered = false;
@@ -180,18 +201,20 @@
/**
* Asynchronous channel to WifiStateMachine
*/
- private AsyncChannel mChannel;
+ private AsyncChannel mWifiStateMachineChannel;
/**
- * TODO: Possibly change WifiService into an AsyncService.
+ * Clients receiving asynchronous messages
*/
- private class WifiServiceHandler extends Handler {
- private AsyncChannel mWshChannel;
+ private List<AsyncChannel> mClients = new ArrayList<AsyncChannel>();
- WifiServiceHandler(android.os.Looper looper, Context context) {
+ /**
+ * Handles client connections
+ */
+ private class AsyncServiceHandler extends Handler {
+
+ AsyncServiceHandler(android.os.Looper looper) {
super(looper);
- mWshChannel = new AsyncChannel();
- mWshChannel.connect(context, this, mWifiStateMachine.getHandler());
}
@Override
@@ -199,11 +222,33 @@
switch (msg.what) {
case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: {
if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
- mChannel = mWshChannel;
+ Slog.d(TAG, "New client listening to asynchronous messages");
+ mClients.add((AsyncChannel) msg.obj);
} else {
- Slog.d(TAG, "WifiServicehandler.handleMessage could not connect error=" +
- msg.arg1);
- mChannel = null;
+ Slog.e(TAG, "Client connection failure, error=" + msg.arg1);
+ }
+ break;
+ }
+ case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: {
+ AsyncChannel ac = new AsyncChannel();
+ ac.connect(mContext, this, msg.replyTo);
+ break;
+ }
+ case CMD_ENABLE_TRAFFIC_STATS_POLL: {
+ mEnableTrafficStatsPoll = (msg.arg1 == 1);
+ mTrafficStatsPollToken++;
+ if (mEnableTrafficStatsPoll) {
+ notifyOnDataActivity();
+ sendMessageDelayed(Message.obtain(this, CMD_TRAFFIC_STATS_POLL,
+ mTrafficStatsPollToken, 0), POLL_TRAFFIC_STATS_INTERVAL_MSECS);
+ }
+ break;
+ }
+ case CMD_TRAFFIC_STATS_POLL: {
+ if (msg.arg1 == mTrafficStatsPollToken) {
+ notifyOnDataActivity();
+ sendMessageDelayed(Message.obtain(this, CMD_TRAFFIC_STATS_POLL,
+ mTrafficStatsPollToken, 0), POLL_TRAFFIC_STATS_INTERVAL_MSECS);
}
break;
}
@@ -214,7 +259,40 @@
}
}
}
- WifiServiceHandler mHandler;
+ private AsyncServiceHandler mAsyncServiceHandler;
+
+ /**
+ * Handles interaction with WifiStateMachine
+ */
+ private class WifiStateMachineHandler extends Handler {
+ private AsyncChannel mWsmChannel;
+
+ WifiStateMachineHandler(android.os.Looper looper) {
+ super(looper);
+ mWsmChannel = new AsyncChannel();
+ mWsmChannel.connect(mContext, this, mWifiStateMachine.getHandler());
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: {
+ if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
+ mWifiStateMachineChannel = mWsmChannel;
+ } else {
+ Slog.e(TAG, "WifiStateMachine connection failure, error=" + msg.arg1);
+ mWifiStateMachineChannel = null;
+ }
+ break;
+ }
+ default: {
+ Slog.d(TAG, "WifiStateMachineHandler.handleMessage ignoring msg=" + msg);
+ break;
+ }
+ }
+ }
+ }
+ WifiStateMachineHandler mWifiStateMachineHandler;
/**
* Temporary for computing UIDS that are responsible for starting WIFI.
@@ -224,7 +302,10 @@
WifiService(Context context) {
mContext = context;
- mWifiStateMachine = new WifiStateMachine(mContext);
+
+ mInterfaceName = SystemProperties.get("wifi.interface", "wlan0");
+
+ mWifiStateMachine = new WifiStateMachine(mContext, mInterfaceName);
mWifiStateMachine.enableRssiPolling(true);
mBatteryStats = BatteryStatsService.getService();
@@ -232,10 +313,6 @@
Intent idleIntent = new Intent(ACTION_DEVICE_IDLE, null);
mIdleIntent = PendingIntent.getBroadcast(mContext, IDLE_REQUEST, idleIntent, 0);
- HandlerThread wifiThread = new HandlerThread("WifiService");
- wifiThread.start();
- mHandler = new WifiServiceHandler(wifiThread.getLooper(), context);
-
mContext.registerReceiver(
new BroadcastReceiver() {
@Override
@@ -271,6 +348,7 @@
switch(mNetworkInfo.getDetailedState()) {
case CONNECTED:
case DISCONNECTED:
+ evaluateTrafficStatsPolling();
resetNotification();
break;
}
@@ -281,6 +359,11 @@
}
}, filter);
+ HandlerThread wifiThread = new HandlerThread("WifiService");
+ wifiThread.start();
+ mAsyncServiceHandler = new AsyncServiceHandler(wifiThread.getLooper());
+ mWifiStateMachineHandler = new WifiStateMachineHandler(wifiThread.getLooper());
+
// Setting is in seconds
NOTIFICATION_REPEAT_DELAY_MS = Settings.Secure.getInt(context.getContentResolver(),
Settings.Secure.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, 900) * 1000l;
@@ -337,10 +420,10 @@
*/
public boolean pingSupplicant() {
enforceAccessPermission();
- if (mChannel != null) {
- return mWifiStateMachine.syncPingSupplicant(mChannel);
+ if (mWifiStateMachineChannel != null) {
+ return mWifiStateMachine.syncPingSupplicant(mWifiStateMachineChannel);
} else {
- Slog.e(TAG, "mChannel is not initialized");
+ Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
return false;
}
}
@@ -550,10 +633,10 @@
*/
public int addOrUpdateNetwork(WifiConfiguration config) {
enforceChangePermission();
- if (mChannel != null) {
- return mWifiStateMachine.syncAddOrUpdateNetwork(mChannel, config);
+ if (mWifiStateMachineChannel != null) {
+ return mWifiStateMachine.syncAddOrUpdateNetwork(mWifiStateMachineChannel, config);
} else {
- Slog.e(TAG, "mChannel is not initialized");
+ Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
return -1;
}
}
@@ -566,10 +649,10 @@
*/
public boolean removeNetwork(int netId) {
enforceChangePermission();
- if (mChannel != null) {
- return mWifiStateMachine.syncRemoveNetwork(mChannel, netId);
+ if (mWifiStateMachineChannel != null) {
+ return mWifiStateMachine.syncRemoveNetwork(mWifiStateMachineChannel, netId);
} else {
- Slog.e(TAG, "mChannel is not initialized");
+ Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
return false;
}
}
@@ -583,10 +666,11 @@
*/
public boolean enableNetwork(int netId, boolean disableOthers) {
enforceChangePermission();
- if (mChannel != null) {
- return mWifiStateMachine.syncEnableNetwork(mChannel, netId, disableOthers);
+ if (mWifiStateMachineChannel != null) {
+ return mWifiStateMachine.syncEnableNetwork(mWifiStateMachineChannel, netId,
+ disableOthers);
} else {
- Slog.e(TAG, "mChannel is not initialized");
+ Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
return false;
}
}
@@ -599,10 +683,10 @@
*/
public boolean disableNetwork(int netId) {
enforceChangePermission();
- if (mChannel != null) {
- return mWifiStateMachine.syncDisableNetwork(mChannel, netId);
+ if (mWifiStateMachineChannel != null) {
+ return mWifiStateMachine.syncDisableNetwork(mWifiStateMachineChannel, netId);
} else {
- Slog.e(TAG, "mChannel is not initialized");
+ Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
return false;
}
}
@@ -639,10 +723,10 @@
public boolean saveConfiguration() {
boolean result = true;
enforceChangePermission();
- if (mChannel != null) {
- return mWifiStateMachine.syncSaveConfig(mChannel);
+ if (mWifiStateMachineChannel != null) {
+ return mWifiStateMachine.syncSaveConfig(mWifiStateMachineChannel);
} else {
- Slog.e(TAG, "mChannel is not initialized");
+ Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
return false;
}
}
@@ -776,14 +860,23 @@
public WpsResult startWps(WpsConfiguration config) {
enforceChangePermission();
- if (mChannel != null) {
- return mWifiStateMachine.startWps(mChannel, config);
+ if (mWifiStateMachineChannel != null) {
+ return mWifiStateMachine.startWps(mWifiStateMachineChannel, config);
} else {
- Slog.e(TAG, "mChannel is not initialized");
+ Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
return new WpsResult(WpsResult.Status.FAILURE);
}
}
+ /**
+ * Get a reference to handler. This is used by a client to establish
+ * an AsyncChannel communication with WifiService
+ */
+ public Messenger getMessenger() {
+ enforceAccessPermission();
+ return new Messenger(mAsyncServiceHandler);
+ }
+
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -805,6 +898,7 @@
// Once the screen is on, we are not keeping WIFI running
// because of any locks so clear that tracking immediately.
reportStartWorkSource();
+ evaluateTrafficStatsPolling();
mWifiStateMachine.enableRssiPolling(true);
mWifiStateMachine.enableAllNetworks();
updateWifiState();
@@ -813,6 +907,7 @@
Slog.d(TAG, "ACTION_SCREEN_OFF");
}
mScreenOff = true;
+ evaluateTrafficStatsPolling();
mWifiStateMachine.enableRssiPolling(false);
/*
* Set a timer to put Wi-Fi to sleep, but only if the screen is off
@@ -1416,6 +1511,48 @@
}
}
+ /**
+ * Evaluate if traffic stats polling is needed based on
+ * connection and screen on status
+ */
+ private void evaluateTrafficStatsPolling() {
+ Message msg;
+ if (mNetworkInfo.getDetailedState() == DetailedState.CONNECTED && !mScreenOff) {
+ msg = Message.obtain(mAsyncServiceHandler, CMD_ENABLE_TRAFFIC_STATS_POLL, 1, 0);
+ } else {
+ msg = Message.obtain(mAsyncServiceHandler, CMD_ENABLE_TRAFFIC_STATS_POLL, 0, 0);
+ }
+ msg.sendToTarget();
+ }
+
+ private void notifyOnDataActivity() {
+ long sent, received;
+ long preTxPkts = mTxPkts, preRxPkts = mRxPkts;
+ int dataActivity = WifiManager.DATA_ACTIVITY_NONE;
+
+ mTxPkts = TrafficStats.getTxPackets(mInterfaceName);
+ mRxPkts = TrafficStats.getRxPackets(mInterfaceName);
+
+ if (preTxPkts > 0 || preRxPkts > 0) {
+ sent = mTxPkts - preTxPkts;
+ received = mRxPkts - preRxPkts;
+ if (sent > 0) {
+ dataActivity |= WifiManager.DATA_ACTIVITY_OUT;
+ }
+ if (received > 0) {
+ dataActivity |= WifiManager.DATA_ACTIVITY_IN;
+ }
+
+ if (dataActivity != mDataActivity && !mScreenOff) {
+ mDataActivity = dataActivity;
+ for (AsyncChannel client : mClients) {
+ client.sendMessage(WifiManager.DATA_ACTIVITY_NOTIFICATION, mDataActivity);
+ }
+ }
+ }
+ }
+
+
private void checkAndSetNotification() {
// If we shouldn't place a notification on available networks, then
// don't bother doing any of the following
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index f3ccd38..399c19a 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -24,8 +24,8 @@
import com.android.server.ProcessStats;
import com.android.server.SystemServer;
import com.android.server.Watchdog;
-import com.android.server.WindowManagerService;
import com.android.server.am.ActivityStack.ActivityState;
+import com.android.server.wm.WindowManagerService;
import dalvik.system.Zygote;
diff --git a/services/java/com/android/server/wm/AppWindowToken.java b/services/java/com/android/server/wm/AppWindowToken.java
new file mode 100644
index 0000000..d3d9df4
--- /dev/null
+++ b/services/java/com/android/server/wm/AppWindowToken.java
@@ -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;
+ }
+}
\ No newline at end of file
diff --git a/services/java/com/android/server/wm/DimAnimator.java b/services/java/com/android/server/wm/DimAnimator.java
new file mode 100644
index 0000000..1fcb869
--- /dev/null
+++ b/services/java/com/android/server/wm/DimAnimator.java
@@ -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);
+ }
+}
\ No newline at end of file
diff --git a/services/java/com/android/server/wm/DragState.java b/services/java/com/android/server/wm/DragState.java
new file mode 100644
index 0000000..c8f8ff3
--- /dev/null
+++ b/services/java/com/android/server/wm/DragState.java
@@ -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;
+ }
+}
\ No newline at end of file
diff --git a/services/java/com/android/server/wm/FadeInOutAnimation.java b/services/java/com/android/server/wm/FadeInOutAnimation.java
new file mode 100644
index 0000000..06f7657
--- /dev/null
+++ b/services/java/com/android/server/wm/FadeInOutAnimation.java
@@ -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);
+ }
+}
\ No newline at end of file
diff --git a/services/java/com/android/server/InputApplication.java b/services/java/com/android/server/wm/InputApplication.java
similarity index 96%
rename from services/java/com/android/server/InputApplication.java
rename to services/java/com/android/server/wm/InputApplication.java
index ae09484..e04fd31 100644
--- a/services/java/com/android/server/InputApplication.java
+++ b/services/java/com/android/server/wm/InputApplication.java
@@ -14,7 +14,8 @@
* limitations under the License.
*/
-package com.android.server;
+package com.android.server.wm;
+
/**
* Describes input-related application properties for use by the input dispatcher.
diff --git a/services/java/com/android/server/InputApplicationHandle.java b/services/java/com/android/server/wm/InputApplicationHandle.java
similarity index 87%
rename from services/java/com/android/server/InputApplicationHandle.java
rename to services/java/com/android/server/wm/InputApplicationHandle.java
index d396d11..64c8e7e 100644
--- a/services/java/com/android/server/InputApplicationHandle.java
+++ b/services/java/com/android/server/wm/InputApplicationHandle.java
@@ -14,7 +14,8 @@
* limitations under the License.
*/
-package com.android.server;
+package com.android.server.wm;
+
/**
* Functions as a handle for an application that can receive input.
@@ -29,11 +30,11 @@
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;
}
diff --git a/services/java/com/android/server/InputManager.java b/services/java/com/android/server/wm/InputManager.java
similarity index 99%
rename from services/java/com/android/server/InputManager.java
rename to services/java/com/android/server/wm/InputManager.java
index 8d249ff..80a2a96 100644
--- a/services/java/com/android/server/InputManager.java
+++ b/services/java/com/android/server/wm/InputManager.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server;
+package com.android.server.wm;
import com.android.internal.util.XmlUtils;
diff --git a/services/java/com/android/server/wm/InputMonitor.java b/services/java/com/android/server/wm/InputMonitor.java
new file mode 100644
index 0000000..34f3618
--- /dev/null
+++ b/services/java/com/android/server/wm/InputMonitor.java
@@ -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);
+ }
+}
\ No newline at end of file
diff --git a/services/java/com/android/server/InputWindow.java b/services/java/com/android/server/wm/InputWindow.java
similarity index 98%
rename from services/java/com/android/server/InputWindow.java
rename to services/java/com/android/server/wm/InputWindow.java
index 2c2cdfe..e3eb473 100644
--- a/services/java/com/android/server/InputWindow.java
+++ b/services/java/com/android/server/wm/InputWindow.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server;
+package com.android.server.wm;
import android.graphics.Region;
import android.view.InputChannel;
diff --git a/services/java/com/android/server/InputWindowHandle.java b/services/java/com/android/server/wm/InputWindowHandle.java
similarity index 97%
rename from services/java/com/android/server/InputWindowHandle.java
rename to services/java/com/android/server/wm/InputWindowHandle.java
index 4b92939..cc508c6 100644
--- a/services/java/com/android/server/InputWindowHandle.java
+++ b/services/java/com/android/server/wm/InputWindowHandle.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server;
+package com.android.server.wm;
import android.view.WindowManagerPolicy;
diff --git a/services/java/com/android/server/InputWindowList.java b/services/java/com/android/server/wm/InputWindowList.java
similarity index 98%
rename from services/java/com/android/server/InputWindowList.java
rename to services/java/com/android/server/wm/InputWindowList.java
index 1cbb2cc..6077337 100644
--- a/services/java/com/android/server/InputWindowList.java
+++ b/services/java/com/android/server/wm/InputWindowList.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server;
+package com.android.server.wm;
/**
diff --git a/services/java/com/android/server/ScreenRotationAnimation.java b/services/java/com/android/server/wm/ScreenRotationAnimation.java
similarity index 99%
rename from services/java/com/android/server/ScreenRotationAnimation.java
rename to services/java/com/android/server/wm/ScreenRotationAnimation.java
index ef00b08f..4356ce5 100644
--- a/services/java/com/android/server/ScreenRotationAnimation.java
+++ b/services/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server; // TODO: use com.android.server.wm, once things move there
+package com.android.server.wm;
import android.content.Context;
import android.graphics.Bitmap;
diff --git a/services/java/com/android/server/wm/Session.java b/services/java/com/android/server/wm/Session.java
new file mode 100644
index 0000000..b9db177
--- /dev/null
+++ b/services/java/com/android/server/wm/Session.java
@@ -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;
+ }
+}
\ No newline at end of file
diff --git a/services/java/com/android/server/wm/StartingData.java b/services/java/com/android/server/wm/StartingData.java
new file mode 100644
index 0000000..625fcfe
--- /dev/null
+++ b/services/java/com/android/server/wm/StartingData.java
@@ -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;
+ }
+}
\ No newline at end of file
diff --git a/services/java/com/android/server/StrictModeFlash.java b/services/java/com/android/server/wm/StrictModeFlash.java
similarity index 96%
rename from services/java/com/android/server/StrictModeFlash.java
rename to services/java/com/android/server/wm/StrictModeFlash.java
index 0a6c625..2c62080 100644
--- a/services/java/com/android/server/StrictModeFlash.java
+++ b/services/java/com/android/server/wm/StrictModeFlash.java
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-package com.android.server; // 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;
diff --git a/services/java/com/android/server/ViewServer.java b/services/java/com/android/server/wm/ViewServer.java
similarity index 98%
rename from services/java/com/android/server/ViewServer.java
rename to services/java/com/android/server/wm/ViewServer.java
index 7b5d18a..cebd5e7 100644
--- a/services/java/com/android/server/ViewServer.java
+++ b/services/java/com/android/server/wm/ViewServer.java
@@ -14,7 +14,8 @@
* limitations under the License.
*/
-package com.android.server;
+package com.android.server.wm;
+
import android.util.Slog;
@@ -33,7 +34,7 @@
/**
* The ViewServer is local socket server that can be used to communicate with the
* views of the opened windows. Communication with the views is ensured by the
- * {@link com.android.server.WindowManagerService} and is a cross-process operation.
+ * {@link com.android.server.wm.WindowManagerService} and is a cross-process operation.
*
* {@hide}
*/
diff --git a/services/java/com/android/server/wm/Watermark.java b/services/java/com/android/server/wm/Watermark.java
new file mode 100644
index 0000000..22126f3
--- /dev/null
+++ b/services/java/com/android/server/wm/Watermark.java
@@ -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);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
similarity index 71%
rename from services/java/com/android/server/WindowManagerService.java
rename to services/java/com/android/server/wm/WindowManagerService.java
index b662c55..a598ce9 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server;
+package com.android.server.wm;
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
@@ -22,7 +22,6 @@
import static android.view.WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND;
import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
-import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
@@ -42,6 +41,10 @@
import com.android.internal.view.IInputMethodClient;
import com.android.internal.view.IInputMethodManager;
import com.android.internal.view.WindowManagerPolicyThread;
+import com.android.server.AttributeCache;
+import com.android.server.EventLogTags;
+import com.android.server.PowerManagerService;
+import com.android.server.Watchdog;
import com.android.server.am.BatteryStatsService;
import android.Manifest;
@@ -50,8 +53,6 @@
import android.app.StatusBarManager;
import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
-import android.content.ClipData;
-import android.content.ClipDescription;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -59,17 +60,12 @@
import android.content.pm.PackageManager;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
-import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
-import android.graphics.Paint;
import android.graphics.PixelFormat;
-import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.Region;
-import android.graphics.Typeface;
-import android.graphics.Paint.FontMetricsInt;
import android.os.BatteryStats;
import android.os.Binder;
import android.os.Bundle;
@@ -98,8 +94,6 @@
import android.util.SparseIntArray;
import android.util.TypedValue;
import android.view.Display;
-import android.view.DragEvent;
-import android.view.Gravity;
import android.view.IApplicationToken;
import android.view.IOnKeyguardExitResult;
import android.view.IRotationWatcher;
@@ -116,13 +110,10 @@
import android.view.Surface;
import android.view.SurfaceSession;
import android.view.View;
-import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.view.WindowManagerImpl;
import android.view.WindowManagerPolicy;
-import android.view.Surface.OutOfResourcesException;
import android.view.WindowManager.LayoutParams;
-import android.view.animation.AccelerateInterpolator;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.animation.Transformation;
@@ -219,7 +210,7 @@
private static final int INPUT_DEVICES_READY_FOR_SAFE_MODE_DETECTION_TIMEOUT_MILLIS = 1000;
// Default input dispatching timeout in nanoseconds.
- private static final long DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS = 5000 * 1000000L;
+ static final long DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS = 5000 * 1000000L;
static final int UPDATE_FOCUS_NORMAL = 0;
static final int UPDATE_FOCUS_WILL_ASSIGN_LAYERS = 1;
@@ -507,336 +498,8 @@
boolean mTurnOnScreen;
- /**
- * Drag/drop state
- */
- class DragState {
- 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(IBinder token, Surface surface, int flags, IBinder localWin) {
- 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 (DEBUG_DRAG) Slog.d(TAG, "registering drag input channel");
- if (mClientChannel != null) {
- Slog.e(TAG, "Duplicate register of drag input channel");
- } else {
- InputChannel[] channels = InputChannel.openInputChannelPair("drag");
- mServerChannel = channels[0];
- mClientChannel = channels[1];
- mInputManager.registerInputChannel(mServerChannel, null);
- InputQueue.registerInputChannel(mClientChannel, mDragInputHandler,
- mH.getLooper().getQueue());
- }
- }
-
- void unregister() {
- if (DEBUG_DRAG) Slog.d(TAG, "unregistering drag input channel");
- if (mClientChannel == null) {
- Slog.e(TAG, "Unregister of nonexistent drag input channel");
- } else {
- mInputManager.unregisterInputChannel(mServerChannel);
- InputQueue.unregisterInputChannel(mClientChannel);
- mClientChannel.dispose();
- mServerChannel.dispose();
- mClientChannel = null;
- mServerChannel = null;
- }
- }
-
- int getDragLayerLw() {
- return mPolicy.windowTypeToLayerLw(WindowManager.LayoutParams.TYPE_DRAG)
- * TYPE_LAYER_MULTIPLIER
- + 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 (DEBUG_DRAG) {
- Slog.d(TAG, "broadcasting DRAG_STARTED at (" + touchX + ", " + touchY + ")");
- }
-
- final int N = mWindows.size();
- for (int i = 0; i < N; i++) {
- sendDragStartedLw(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 (DEBUG_DRAG) {
- Slog.d(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(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.
- */
- private 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 (DEBUG_DRAG) {
- Slog.d(TAG, "need to send DRAG_STARTED to new window " + newWin);
- }
- sendDragStartedLw(newWin, mCurrentX, mCurrentY, mDataDescription);
- }
- }
-
- void broadcastDragEndedLw() {
- if (DEBUG_DRAG) {
- Slog.d(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(TAG, "Unable to drag-end window " + ws);
- }
- }
- mNotifiedWindows.clear();
- mDragInProgress = false;
- evt.recycle();
- }
-
- void endDragLw() {
- mDragState.broadcastDragEndedLw();
-
- // stop intercepting input
- mDragState.unregister();
- mInputMonitor.updateInputWindowsLw(true /*force*/);
-
- // free our resources and drop all the object references
- mDragState.reset();
- mDragState = null;
-
- if (DEBUG_ORIENTATION) Slog.d(TAG, "Performing post-drag rotation");
- boolean changed = setRotationUncheckedLocked(
- WindowManagerPolicy.USE_LAST_ROTATION, 0, false);
- if (changed) {
- 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 (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION notifyMoveLw");
- Surface.openTransaction();
- try {
- mSurface.setPosition((int)(x - mThumbOffsetX), (int)(y - mThumbOffsetY));
- if (SHOW_TRANSACTIONS) Slog.i(TAG, " DRAG "
- + mSurface + ": pos=(" +
- (int)(x - mThumbOffsetX) + "," + (int)(y - mThumbOffsetY) + ")");
- } finally {
- Surface.closeTransaction();
- if (SHOW_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION notifyMoveLw");
- }
-
- // Tell the affected window
- WindowState touchedWin = getTouchedWinAtPointLw(x, y);
- if (touchedWin == null) {
- if (DEBUG_DRAG) Slog.d(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 (DEBUG_DRAG) {
- Slog.d(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 && DEBUG_DRAG) {
- Slog.d(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(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 (DEBUG_DRAG) {
- Slog.d(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
- mH.removeMessages(H.DRAG_END_TIMEOUT, token);
- Message msg = mH.obtainMessage(H.DRAG_END_TIMEOUT, token);
- mH.sendMessageDelayed(msg, 5000);
- } catch (RemoteException e) {
- Slog.w(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 = 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;
- }
- }
-
DragState mDragState = null;
- private final InputHandler mDragInputHandler = new BaseInputHandler() {
+ final InputHandler mDragInputHandler = new BaseInputHandler() {
@Override
public void handleMotion(MotionEvent event, InputQueue.FinishedCallback finishedCallback) {
boolean handled = false;
@@ -2300,7 +1963,7 @@
+ attrs.token + ". Aborting.");
return WindowManagerImpl.ADD_BAD_APP_TOKEN;
}
- token = new WindowToken(attrs.token, -1, false);
+ token = new WindowToken(this, attrs.token, -1, false);
addToken = true;
} else if (attrs.type >= FIRST_APPLICATION_WINDOW
&& attrs.type <= LAST_APPLICATION_WINDOW) {
@@ -2334,7 +1997,7 @@
}
}
- win = new WindowState(session, client, token,
+ win = new WindowState(this, session, client, token,
attachedWindow, attrs, viewVisibility);
if (win.mDeathRecipient == null) {
// Client has apparently died, so there is no reason to
@@ -2632,7 +2295,7 @@
mInputMonitor.updateInputWindowsLw(true /*force*/);
}
- private static void logSurface(WindowState w, String msg, RuntimeException where) {
+ static void logSurface(WindowState w, String msg, RuntimeException where) {
String str = " SURFACE " + Integer.toHexString(w.hashCode())
+ ": " + msg + " / " + w.mAttrs.getTitle();
if (where != null) {
@@ -2642,7 +2305,7 @@
}
}
- private void setTransparentRegionWindow(Session session, IWindow client, Region region) {
+ void setTransparentRegionWindow(Session session, IWindow client, Region region) {
long origId = Binder.clearCallingIdentity();
try {
synchronized (mWindowMap) {
@@ -3096,7 +2759,7 @@
return null;
}
- private void applyEnterAnimationLocked(WindowState win) {
+ void applyEnterAnimationLocked(WindowState win) {
int transit = WindowManagerPolicy.TRANSIT_SHOW;
if (win.mEnterAnimationPending) {
win.mEnterAnimationPending = false;
@@ -3106,7 +2769,7 @@
applyAnimationLocked(win, transit, true);
}
- private boolean applyAnimationLocked(WindowState win,
+ boolean applyAnimationLocked(WindowState win,
int transit, boolean isEntrance) {
if (win.mLocalAnimating && win.mAnimationIsEntrance == isEntrance) {
// If we are trying to apply an animation, but already running
@@ -3362,7 +3025,7 @@
Slog.w(TAG, "Attempted to add existing input method token: " + token);
return;
}
- wtoken = new WindowToken(token, type, true);
+ wtoken = new WindowToken(this, token, type, true);
mTokenMap.put(token, wtoken);
if (type == TYPE_WALLPAPER) {
mWallpaperTokens.add(wtoken);
@@ -3450,7 +3113,7 @@
Slog.w(TAG, "Attempted to add existing app token: " + token);
return;
}
- wtoken = new AppWindowToken(token);
+ wtoken = new AppWindowToken(this, token);
wtoken.inputDispatchingTimeoutNanos = inputDispatchingTimeoutNanos;
wtoken.groupId = groupId;
wtoken.appFullscreen = fullscreen;
@@ -5347,8 +5010,8 @@
*
* @return True if the server was successfully started, false otherwise.
*
- * @see com.android.server.ViewServer
- * @see com.android.server.ViewServer#VIEW_SERVER_DEFAULT_PORT
+ * @see com.android.server.wm.ViewServer
+ * @see com.android.server.wm.ViewServer#VIEW_SERVER_DEFAULT_PORT
*/
public boolean startViewServer(int port) {
if (isSystemSecure()) {
@@ -5394,7 +5057,7 @@
* @return True if the server stopped, false if it wasn't started or
* couldn't be stopped.
*
- * @see com.android.server.ViewServer
+ * @see com.android.server.wm.ViewServer
*/
public boolean stopViewServer() {
if (isSystemSecure()) {
@@ -5416,7 +5079,7 @@
*
* @return True if the server is running, false otherwise.
*
- * @see com.android.server.ViewServer
+ * @see com.android.server.wm.ViewServer
*/
public boolean isViewServerRunning() {
if (isSystemSecure()) {
@@ -5578,7 +5241,7 @@
parameters = "";
}
- final WindowManagerService.WindowState window = findWindow(hashCode);
+ final WindowState window = findWindow(hashCode);
if (window == null) {
return false;
}
@@ -5889,7 +5552,7 @@
outSurface.copyFrom(surface);
final IBinder winBinder = window.asBinder();
token = new Binder();
- mDragState = new DragState(token, surface, /*flags*/ 0, winBinder);
+ mDragState = new DragState(this, token, surface, /*flags*/ 0, winBinder);
mDragState.mSurface = surface;
token = mDragState.mToken = new Binder();
@@ -5919,355 +5582,8 @@
// Input Events and Focus Management
// -------------------------------------------------------------
- InputMonitor mInputMonitor = new InputMonitor();
+ final InputMonitor mInputMonitor = new InputMonitor(this);
- /* Tracks the progress of input dispatch and ensures that input dispatch state
- * is kept in sync with changes in window focus, visibility, registration, and
- * other relevant Window Manager state transitions. */
- final class InputMonitor {
- // 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;
-
- /* Notifies the window manager about a broken input channel.
- *
- * Called by the InputManager.
- */
- public void notifyInputChannelBroken(InputWindowHandle inputWindowHandle) {
- if (inputWindowHandle == null) {
- return;
- }
-
- synchronized (mWindowMap) {
- WindowState windowState = (WindowState) inputWindowHandle.windowState;
- Slog.i(TAG, "WINDOW DIED " + windowState);
- 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 (mWindowMap) {
- WindowState windowState = (WindowState) inputWindowHandle.windowState;
- if (windowState != null) {
- Slog.i(TAG, "Input event dispatching timed out sending to "
- + windowState.mAttrs.getTitle());
- appWindowToken = windowState.mAppToken;
- }
- }
- }
-
- if (appWindowToken == null && inputApplicationHandle != null) {
- appWindowToken = inputApplicationHandle.appWindowToken;
- Slog.i(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 = mDragState.mServerChannel;
- inputWindow.name = "drag";
- inputWindow.layoutParamsFlags = 0;
- inputWindow.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG;
- inputWindow.dispatchingTimeoutNanos = DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
- inputWindow.visible = true;
- inputWindow.canReceiveKeys = false;
- inputWindow.hasFocus = true;
- inputWindow.hasWallpaper = false;
- inputWindow.paused = false;
- inputWindow.layer = 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 = mDisplay.getWidth();
- inputWindow.frameBottom = 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 = mWindows;
-
- // If there's a drag in flight, provide a pseudowindow to catch drag input
- final boolean inDrag = (mDragState != null);
- if (inDrag) {
- if (DEBUG_DRAG) {
- Log.d(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 == 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) {
- 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.
- 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() {
- 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) {
- 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 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 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 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 (DEBUG_INPUT) {
- Slog.d(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) {
- mInputManager.setFocusedApplication(null);
- } else {
- mTempInputApplication.inputApplicationHandle = newApp.mInputApplicationHandle;
- mTempInputApplication.name = newApp.toString();
- mTempInputApplication.dispatchingTimeoutNanos =
- newApp.inputDispatchingTimeoutNanos;
-
- mInputManager.setFocusedApplication(mTempInputApplication);
-
- mTempInputApplication.recycle();
- }
- }
-
- public void pauseDispatchingLw(WindowToken window) {
- if (! window.paused) {
- if (DEBUG_INPUT) {
- Slog.v(TAG, "Pausing WindowToken " + window);
- }
-
- window.paused = true;
- updateInputWindowsLw(true /*force*/);
- }
- }
-
- public void resumeDispatchingLw(WindowToken window) {
- if (window.paused) {
- if (DEBUG_INPUT) {
- Slog.v(TAG, "Resuming WindowToken " + window);
- }
-
- window.paused = false;
- updateInputWindowsLw(true /*force*/);
- }
- }
-
- public void freezeInputDispatchingLw() {
- if (! mInputDispatchFrozen) {
- if (DEBUG_INPUT) {
- Slog.v(TAG, "Freezing input dispatching");
- }
-
- mInputDispatchFrozen = true;
- updateInputDispatchModeLw();
- }
- }
-
- public void thawInputDispatchingLw() {
- if (mInputDispatchFrozen) {
- if (DEBUG_INPUT) {
- Slog.v(TAG, "Thawing input dispatching");
- }
-
- mInputDispatchFrozen = false;
- updateInputDispatchModeLw();
- }
- }
-
- public void setEventDispatchingLw(boolean enabled) {
- if (mInputDispatchEnabled != enabled) {
- if (DEBUG_INPUT) {
- Slog.v(TAG, "Setting event dispatching to " + enabled);
- }
-
- mInputDispatchEnabled = enabled;
- updateInputDispatchModeLw();
- }
- }
-
- private void updateInputDispatchModeLw() {
- mInputManager.setInputDispatchMode(mInputDispatchEnabled, mInputDispatchFrozen);
- }
- }
-
public void pauseKeyDispatching(IBinder _token) {
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
"pauseKeyDispatching()")) {
@@ -6496,2409 +5812,6 @@
mPolicy.systemReady();
}
- // -------------------------------------------------------------
- // Client Session State
- // -------------------------------------------------------------
-
- private final class Session extends IWindowSession.Stub
- implements IBinder.DeathRecipient {
- 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(IInputMethodClient client, IInputContext inputContext) {
- 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 (mWindowMap) {
- if (mInputMethodManager == null && mHaveInputMethods) {
- IBinder b = ServiceManager.getService(
- Context.INPUT_METHOD_SERVICE);
- 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 (mInputMethodManager != null) {
- 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 (mInputMethodManager != null) {
- 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(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 (mInputMethodManager != null) {
- mInputMethodManager.removeClient(mClient);
- }
- } catch (RemoteException e) {
- }
- synchronized(mWindowMap) {
- mClient.asBinder().unlinkToDeath(this, 0);
- mClientDead = true;
- killSessionLocked();
- }
- }
-
- public int add(IWindow window, WindowManager.LayoutParams attrs,
- int viewVisibility, Rect outContentInsets, InputChannel outInputChannel) {
- return addWindow(this, window, attrs, viewVisibility, outContentInsets,
- outInputChannel);
- }
-
- public int addWithoutInputChannel(IWindow window, WindowManager.LayoutParams attrs,
- int viewVisibility, Rect outContentInsets) {
- return addWindow(this, window, attrs, viewVisibility, outContentInsets, null);
- }
-
- public void remove(IWindow window) {
- 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 = 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) {
- setTransparentRegionWindow(this, window, region);
- }
-
- public void setInsets(IWindow window, int touchableInsets,
- Rect contentInsets, Rect visibleInsets, Region touchableArea) {
- setInsetsWindow(this, window, touchableInsets, contentInsets,
- visibleInsets, touchableArea);
- }
-
- public void getDisplayFrame(IWindow window, Rect outDisplayFrame) {
- getWindowDisplayFrame(this, window, outDisplayFrame);
- }
-
- public void finishDrawing(IWindow window) {
- if (localLOGV) Slog.v(
- TAG, "IWindow finishDrawing called for " + window);
- finishDrawingWindow(this, window);
- }
-
- public void setInTouchMode(boolean mode) {
- synchronized(mWindowMap) {
- mInTouchMode = mode;
- }
- }
-
- public boolean getInTouchMode() {
- synchronized(mWindowMap) {
- return mInTouchMode;
- }
- }
-
- public boolean performHapticFeedback(IWindow window, int effectId,
- boolean always) {
- synchronized(mWindowMap) {
- long ident = Binder.clearCallingIdentity();
- try {
- return mPolicy.performHapticFeedbackLw(
- 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 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 (DEBUG_DRAG) {
- Slog.d(TAG, "perform drag: win=" + window + " data=" + data);
- }
-
- synchronized (mWindowMap) {
- if (mDragState == null) {
- Slog.w(TAG, "No drag prepared");
- throw new IllegalStateException("performDrag() without prepareDrag()");
- }
-
- if (dragToken != mDragState.mToken) {
- Slog.w(TAG, "Performing mismatched drag");
- throw new IllegalStateException("performDrag() does not match prepareDrag()");
- }
-
- WindowState callingWin = windowForClientLocked(null, window, false);
- if (callingWin == null) {
- Slog.w(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()
-
- 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
-
- mDragState.register();
- mInputMonitor.updateInputWindowsLw(true /*force*/);
- if (!mInputManager.transferTouchFocus(callingWin.mInputChannel,
- mDragState.mServerChannel)) {
- Slog.e(TAG, "Unable to transfer touch focus");
- mDragState.unregister();
- mDragState = null;
- mInputMonitor.updateInputWindowsLw(true /*force*/);
- return false;
- }
-
- mDragState.mData = data;
- mDragState.mCurrentX = touchX;
- mDragState.mCurrentY = touchY;
- mDragState.broadcastDragStartedLw(touchX, touchY);
-
- // remember the thumb offsets for later
- mDragState.mThumbOffsetX = thumbCenterX;
- mDragState.mThumbOffsetY = thumbCenterY;
-
- // Make the surface visible at the proper location
- final Surface surface = mDragState.mSurface;
- if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION performDrag");
- Surface.openTransaction();
- try {
- surface.setPosition((int)(touchX - thumbCenterX),
- (int)(touchY - thumbCenterY));
- surface.setAlpha(.7071f);
- surface.setLayer(mDragState.getDragLayerLw());
- surface.show();
- } finally {
- Surface.closeTransaction();
- if (SHOW_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION performDrag");
- }
- }
-
- return true; // success!
- }
-
- public void reportDropResult(IWindow window, boolean consumed) {
- IBinder token = window.asBinder();
- if (DEBUG_DRAG) {
- Slog.d(TAG, "Drop result=" + consumed + " reported by " + token);
- }
-
- synchronized (mWindowMap) {
- long ident = Binder.clearCallingIdentity();
- try {
- if (mDragState == null || mDragState.mToken != token) {
- Slog.w(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.
- mH.removeMessages(H.DRAG_END_TIMEOUT, window.asBinder());
- WindowState callingWin = windowForClientLocked(null, window, false);
- if (callingWin == null) {
- Slog.w(TAG, "Bad result-reporting window " + window);
- return; // !!! TODO: throw here?
- }
-
- mDragState.mDragResult = consumed;
- mDragState.endDragLw();
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
- }
-
- public void dragRecipientEntered(IWindow window) {
- if (DEBUG_DRAG) {
- Slog.d(TAG, "Drag into new candidate view @ " + window.asBinder());
- }
- }
-
- public void dragRecipientExited(IWindow window) {
- if (DEBUG_DRAG) {
- Slog.d(TAG, "Drag from old candidate view @ " + window.asBinder());
- }
- }
-
- public void setWallpaperPosition(IBinder window, float x, float y, float xStep, float yStep) {
- synchronized(mWindowMap) {
- long ident = Binder.clearCallingIdentity();
- try {
- setWindowWallpaperPositionLocked(
- windowForClientLocked(this, window, true),
- x, y, xStep, yStep);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
- }
-
- public void wallpaperOffsetsComplete(IBinder window) {
- WindowManagerService.this.wallpaperOffsetsComplete(window);
- }
-
- public Bundle sendWallpaperCommand(IBinder window, String action, int x, int y,
- int z, Bundle extras, boolean sync) {
- synchronized(mWindowMap) {
- long ident = Binder.clearCallingIdentity();
- try {
- return sendWindowWallpaperCommandLocked(
- windowForClientLocked(this, window, true),
- action, x, y, z, extras, sync);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
- }
-
- public void wallpaperCommandComplete(IBinder window, Bundle result) {
- WindowManagerService.this.wallpaperCommandComplete(window, result);
- }
-
- void windowAddedLocked() {
- if (mSurfaceSession == null) {
- if (localLOGV) Slog.v(
- TAG, "First window added to " + this + ", creating SurfaceSession");
- mSurfaceSession = new SurfaceSession();
- if (SHOW_TRANSACTIONS) Slog.i(
- TAG, " NEW SURFACE SESSION " + mSurfaceSession);
- mSessions.add(this);
- }
- mNumWindow++;
- }
-
- void windowRemovedLocked() {
- mNumWindow--;
- killSessionLocked();
- }
-
- void killSessionLocked() {
- if (mNumWindow <= 0 && mClientDead) {
- mSessions.remove(this);
- if (mSurfaceSession != null) {
- if (localLOGV) Slog.v(
- TAG, "Last window removed from " + this
- + ", destroying " + mSurfaceSession);
- if (SHOW_TRANSACTIONS) Slog.i(
- TAG, " KILL SURFACE SESSION " + mSurfaceSession);
- try {
- mSurfaceSession.kill();
- } catch (Exception e) {
- Slog.w(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;
- }
- }
-
- // -------------------------------------------------------------
- // Client Window State
- // -------------------------------------------------------------
-
- private final class WindowState implements WindowManagerPolicy.WindowState {
- final Session mSession;
- final IWindow mClient;
- WindowToken mToken;
- WindowToken mRootToken;
- AppWindowToken mAppToken;
- AppWindowToken mTargetAppToken;
- final WindowManager.LayoutParams mAttrs = new WindowManager.LayoutParams();
- final DeathRecipient mDeathRecipient;
- final WindowState mAttachedWindow;
- final ArrayList<WindowState> mChildWindows = new ArrayList<WindowState>();
- final int mBaseLayer;
- final int mSubLayer;
- final boolean mLayoutAttached;
- final boolean mIsImWindow;
- final boolean mIsWallpaper;
- final boolean mIsFloatingLayer;
- int mViewVisibility;
- boolean mPolicyVisibility = true;
- boolean mPolicyVisibilityAfterAnim = true;
- boolean mAppFreezing;
- Surface mSurface;
- boolean mReportDestroySurface;
- boolean mSurfacePendingDestroy;
- boolean mAttachedHidden; // is our parent window hidden?
- boolean mLastHidden; // was this window last hidden?
- boolean mWallpaperVisible; // for wallpaper, what was last vis report?
- int mRequestedWidth;
- int mRequestedHeight;
- int mLastRequestedWidth;
- int mLastRequestedHeight;
- int mLayer;
- int mAnimLayer;
- int mLastLayer;
- boolean mHaveFrame;
- boolean mObscured;
- boolean mTurnOnScreen;
-
- int mLayoutSeq = -1;
-
- Configuration mConfiguration = null;
-
- // Actual frame shown on-screen (may be modified by animation)
- final Rect mShownFrame = new Rect();
- final Rect mLastShownFrame = new Rect();
-
- /**
- * Set when we have changed the size of the surface, to know that
- * we must tell them application to resize (and thus redraw itself).
- */
- boolean mSurfaceResized;
-
- /**
- * Insets that determine the actually visible area
- */
- final Rect mVisibleInsets = new Rect();
- final Rect mLastVisibleInsets = new Rect();
- boolean mVisibleInsetsChanged;
-
- /**
- * Insets that are covered by system windows
- */
- final Rect mContentInsets = new Rect();
- final Rect mLastContentInsets = new Rect();
- boolean mContentInsetsChanged;
-
- /**
- * Set to true if we are waiting for this window to receive its
- * given internal insets before laying out other windows based on it.
- */
- boolean mGivenInsetsPending;
-
- /**
- * These are the content insets that were given during layout for
- * this window, to be applied to windows behind it.
- */
- final Rect mGivenContentInsets = new Rect();
-
- /**
- * These are the visible insets that were given during layout for
- * this window, to be applied to windows behind it.
- */
- final Rect mGivenVisibleInsets = new Rect();
-
- /**
- * This is the given touchable area relative to the window frame, or null if none.
- */
- final Region mGivenTouchableRegion = new Region();
-
- /**
- * Flag indicating whether the touchable region should be adjusted by
- * the visible insets; if false the area outside the visible insets is
- * NOT touchable, so we must use those to adjust the frame during hit
- * tests.
- */
- int mTouchableInsets = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
-
- // Current transformation being applied.
- boolean mHaveMatrix;
- float mDsDx=1, mDtDx=0, mDsDy=0, mDtDy=1;
- float mLastDsDx=1, mLastDtDx=0, mLastDsDy=0, mLastDtDy=1;
- float mHScale=1, mVScale=1;
- float mLastHScale=1, mLastVScale=1;
- final Matrix mTmpMatrix = new Matrix();
-
- // "Real" frame that the application sees.
- final Rect mFrame = new Rect();
- final Rect mLastFrame = new Rect();
-
- final Rect mContainingFrame = new Rect();
- final Rect mDisplayFrame = new Rect();
- final Rect mContentFrame = new Rect();
- final Rect mParentFrame = new Rect();
- final Rect mVisibleFrame = new Rect();
-
- boolean mContentChanged;
-
- float mShownAlpha = 1;
- float mAlpha = 1;
- float mLastAlpha = 1;
-
- // Set to true if, when the window gets displayed, it should perform
- // an enter animation.
- boolean mEnterAnimationPending;
-
- // Currently running animation.
- boolean mAnimating;
- boolean mLocalAnimating;
- Animation mAnimation;
- boolean mAnimationIsEntrance;
- boolean mHasTransformation;
- boolean mHasLocalTransformation;
- final Transformation mTransformation = new Transformation();
-
- // If a window showing a wallpaper: the requested offset for the
- // wallpaper; if a wallpaper window: the currently applied offset.
- float mWallpaperX = -1;
- float mWallpaperY = -1;
-
- // If a window showing a wallpaper: what fraction of the offset
- // range corresponds to a full virtual screen.
- float mWallpaperXStep = -1;
- float mWallpaperYStep = -1;
-
- // Wallpaper windows: pixels offset based on above variables.
- int mXOffset;
- int mYOffset;
-
- // This is set after IWindowSession.relayout() has been called at
- // least once for the window. It allows us to detect the situation
- // where we don't yet have a surface, but should have one soon, so
- // we can give the window focus before waiting for the relayout.
- boolean mRelayoutCalled;
-
- // This is set after the Surface has been created but before the
- // window has been drawn. During this time the surface is hidden.
- boolean mDrawPending;
-
- // This is set after the window has finished drawing for the first
- // time but before its surface is shown. The surface will be
- // displayed when the next layout is run.
- boolean mCommitDrawPending;
-
- // This is set during the time after the window's drawing has been
- // committed, and before its surface is actually shown. It is used
- // to delay showing the surface until all windows in a token are ready
- // to be shown.
- boolean mReadyToShow;
-
- // Set when the window has been shown in the screen the first time.
- boolean mHasDrawn;
-
- // Currently running an exit animation?
- boolean mExiting;
-
- // Currently on the mDestroySurface list?
- boolean mDestroying;
-
- // Completely remove from window manager after exit animation?
- boolean mRemoveOnExit;
-
- // Set when the orientation is changing and this window has not yet
- // been updated for the new orientation.
- boolean mOrientationChanging;
-
- // Is this window now (or just being) removed?
- boolean mRemoved;
-
- // Temp for keeping track of windows that have been removed when
- // rebuilding window list.
- boolean mRebuilding;
-
- // For debugging, this is the last information given to the surface flinger.
- boolean mSurfaceShown;
- int mSurfaceX, mSurfaceY, mSurfaceW, mSurfaceH;
- int mSurfaceLayer;
- float mSurfaceAlpha;
-
- // Input channel and input window handle used by the input dispatcher.
- InputWindowHandle mInputWindowHandle;
- InputChannel mInputChannel;
-
- // Used to improve performance of toString()
- String mStringNameCache;
- CharSequence mLastTitle;
- boolean mWasPaused;
-
- WindowState(Session s, IWindow c, WindowToken token,
- WindowState attachedWindow, WindowManager.LayoutParams a,
- int viewVisibility) {
- mSession = s;
- mClient = c;
- mToken = token;
- mAttrs.copyFrom(a);
- mViewVisibility = viewVisibility;
- DeathRecipient deathRecipient = new DeathRecipient();
- mAlpha = a.alpha;
- if (localLOGV) Slog.v(
- TAG, "Window " + this + " client=" + c.asBinder()
- + " token=" + token + " (" + mAttrs.token + ")");
- try {
- c.asBinder().linkToDeath(deathRecipient, 0);
- } catch (RemoteException e) {
- mDeathRecipient = null;
- mAttachedWindow = null;
- mLayoutAttached = false;
- mIsImWindow = false;
- mIsWallpaper = false;
- mIsFloatingLayer = false;
- mBaseLayer = 0;
- mSubLayer = 0;
- return;
- }
- mDeathRecipient = deathRecipient;
-
- if ((mAttrs.type >= FIRST_SUB_WINDOW &&
- mAttrs.type <= LAST_SUB_WINDOW)) {
- // The multiplier here is to reserve space for multiple
- // windows in the same type layer.
- mBaseLayer = mPolicy.windowTypeToLayerLw(
- attachedWindow.mAttrs.type) * TYPE_LAYER_MULTIPLIER
- + TYPE_LAYER_OFFSET;
- mSubLayer = mPolicy.subWindowTypeToLayerLw(a.type);
- mAttachedWindow = attachedWindow;
- if (DEBUG_ADD_REMOVE) Slog.v(TAG, "Adding " + this + " to " + mAttachedWindow);
- mAttachedWindow.mChildWindows.add(this);
- mLayoutAttached = mAttrs.type !=
- WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
- mIsImWindow = attachedWindow.mAttrs.type == TYPE_INPUT_METHOD
- || attachedWindow.mAttrs.type == TYPE_INPUT_METHOD_DIALOG;
- mIsWallpaper = attachedWindow.mAttrs.type == TYPE_WALLPAPER;
- mIsFloatingLayer = mIsImWindow || mIsWallpaper;
- } else {
- // The multiplier here is to reserve space for multiple
- // windows in the same type layer.
- mBaseLayer = mPolicy.windowTypeToLayerLw(a.type)
- * TYPE_LAYER_MULTIPLIER
- + TYPE_LAYER_OFFSET;
- mSubLayer = 0;
- mAttachedWindow = null;
- mLayoutAttached = false;
- mIsImWindow = mAttrs.type == TYPE_INPUT_METHOD
- || mAttrs.type == TYPE_INPUT_METHOD_DIALOG;
- mIsWallpaper = mAttrs.type == TYPE_WALLPAPER;
- mIsFloatingLayer = mIsImWindow || mIsWallpaper;
- }
-
- WindowState appWin = this;
- while (appWin.mAttachedWindow != null) {
- appWin = mAttachedWindow;
- }
- WindowToken appToken = appWin.mToken;
- while (appToken.appWindowToken == null) {
- WindowToken parent = mTokenMap.get(appToken.token);
- if (parent == null || appToken == parent) {
- break;
- }
- appToken = parent;
- }
- mRootToken = appToken;
- mAppToken = appToken.appWindowToken;
-
- mSurface = null;
- mRequestedWidth = 0;
- mRequestedHeight = 0;
- mLastRequestedWidth = 0;
- mLastRequestedHeight = 0;
- mXOffset = 0;
- mYOffset = 0;
- mLayer = 0;
- mAnimLayer = 0;
- mLastLayer = 0;
- mInputWindowHandle = new InputWindowHandle(
- mAppToken != null ? mAppToken.mInputApplicationHandle : null, this);
- }
-
- void attach() {
- if (localLOGV) Slog.v(
- TAG, "Attaching " + this + " token=" + mToken
- + ", list=" + mToken.windows);
- mSession.windowAddedLocked();
- }
-
- public void computeFrameLw(Rect pf, Rect df, Rect cf, Rect vf) {
- mHaveFrame = true;
-
- final Rect container = mContainingFrame;
- container.set(pf);
-
- final Rect display = mDisplayFrame;
- display.set(df);
-
- if ((mAttrs.flags & FLAG_COMPATIBLE_WINDOW) != 0) {
- container.intersect(mCompatibleScreenFrame);
- if ((mAttrs.flags & FLAG_LAYOUT_NO_LIMITS) == 0) {
- display.intersect(mCompatibleScreenFrame);
- }
- }
-
- final int pw = container.right - container.left;
- final int ph = container.bottom - container.top;
-
- int w,h;
- if ((mAttrs.flags & mAttrs.FLAG_SCALED) != 0) {
- w = mAttrs.width < 0 ? pw : mAttrs.width;
- h = mAttrs.height< 0 ? ph : mAttrs.height;
- } else {
- w = mAttrs.width == mAttrs.MATCH_PARENT ? pw : mRequestedWidth;
- h = mAttrs.height== mAttrs.MATCH_PARENT ? ph : mRequestedHeight;
- }
-
- if (!mParentFrame.equals(pf)) {
- //Slog.i(TAG, "Window " + this + " content frame from " + mParentFrame
- // + " to " + pf);
- mParentFrame.set(pf);
- mContentChanged = true;
- }
-
- final Rect content = mContentFrame;
- content.set(cf);
-
- final Rect visible = mVisibleFrame;
- visible.set(vf);
-
- final Rect frame = mFrame;
- final int fw = frame.width();
- final int fh = frame.height();
-
- //System.out.println("In: w=" + w + " h=" + h + " container=" +
- // container + " x=" + mAttrs.x + " y=" + mAttrs.y);
-
- Gravity.apply(mAttrs.gravity, w, h, container,
- (int) (mAttrs.x + mAttrs.horizontalMargin * pw),
- (int) (mAttrs.y + mAttrs.verticalMargin * ph), frame);
-
- //System.out.println("Out: " + mFrame);
-
- // Now make sure the window fits in the overall display.
- Gravity.applyDisplay(mAttrs.gravity, df, frame);
-
- // Make sure the content and visible frames are inside of the
- // final window frame.
- if (content.left < frame.left) content.left = frame.left;
- if (content.top < frame.top) content.top = frame.top;
- if (content.right > frame.right) content.right = frame.right;
- if (content.bottom > frame.bottom) content.bottom = frame.bottom;
- if (visible.left < frame.left) visible.left = frame.left;
- if (visible.top < frame.top) visible.top = frame.top;
- if (visible.right > frame.right) visible.right = frame.right;
- if (visible.bottom > frame.bottom) visible.bottom = frame.bottom;
-
- final Rect contentInsets = mContentInsets;
- contentInsets.left = content.left-frame.left;
- contentInsets.top = content.top-frame.top;
- contentInsets.right = frame.right-content.right;
- contentInsets.bottom = frame.bottom-content.bottom;
-
- final Rect visibleInsets = mVisibleInsets;
- visibleInsets.left = visible.left-frame.left;
- visibleInsets.top = visible.top-frame.top;
- visibleInsets.right = frame.right-visible.right;
- visibleInsets.bottom = frame.bottom-visible.bottom;
-
- if (mIsWallpaper && (fw != frame.width() || fh != frame.height())) {
- updateWallpaperOffsetLocked(this, mDisplay.getWidth(),
- mDisplay.getHeight(), false);
- }
-
- if (localLOGV) {
- //if ("com.google.android.youtube".equals(mAttrs.packageName)
- // && mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) {
- Slog.v(TAG, "Resolving (mRequestedWidth="
- + mRequestedWidth + ", mRequestedheight="
- + mRequestedHeight + ") to" + " (pw=" + pw + ", ph=" + ph
- + "): frame=" + mFrame.toShortString()
- + " ci=" + contentInsets.toShortString()
- + " vi=" + visibleInsets.toShortString());
- //}
- }
- }
-
- public Rect getFrameLw() {
- return mFrame;
- }
-
- public Rect getShownFrameLw() {
- return mShownFrame;
- }
-
- public Rect getDisplayFrameLw() {
- return mDisplayFrame;
- }
-
- public Rect getContentFrameLw() {
- return mContentFrame;
- }
-
- public Rect getVisibleFrameLw() {
- return mVisibleFrame;
- }
-
- public boolean getGivenInsetsPendingLw() {
- return mGivenInsetsPending;
- }
-
- public Rect getGivenContentInsetsLw() {
- return mGivenContentInsets;
- }
-
- public Rect getGivenVisibleInsetsLw() {
- return mGivenVisibleInsets;
- }
-
- public WindowManager.LayoutParams getAttrs() {
- return mAttrs;
- }
-
- public int getSurfaceLayer() {
- return mLayer;
- }
-
- public IApplicationToken getAppToken() {
- return mAppToken != null ? mAppToken.appToken : null;
- }
-
- public long getInputDispatchingTimeoutNanos() {
- return mAppToken != null
- ? mAppToken.inputDispatchingTimeoutNanos
- : DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
- }
-
- public boolean hasAppShownWindows() {
- return mAppToken != null ? mAppToken.firstWindowDrawn : false;
- }
-
- public void setAnimation(Animation anim) {
- if (localLOGV) Slog.v(
- TAG, "Setting animation in " + this + ": " + anim);
- mAnimating = false;
- mLocalAnimating = false;
- mAnimation = anim;
- mAnimation.restrictDuration(MAX_ANIMATION_DURATION);
- mAnimation.scaleCurrentDuration(mWindowAnimationScale);
- }
-
- public void clearAnimation() {
- if (mAnimation != null) {
- mAnimating = true;
- mLocalAnimating = false;
- mAnimation.cancel();
- mAnimation = null;
- }
- }
-
- Surface createSurfaceLocked() {
- if (mSurface == null) {
- mReportDestroySurface = false;
- mSurfacePendingDestroy = false;
- mDrawPending = true;
- mCommitDrawPending = false;
- mReadyToShow = false;
- if (mAppToken != null) {
- mAppToken.allDrawn = false;
- }
-
- int flags = 0;
-
- if ((mAttrs.flags&WindowManager.LayoutParams.FLAG_SECURE) != 0) {
- flags |= Surface.SECURE;
- }
- if (DEBUG_VISIBILITY) Slog.v(
- TAG, "Creating surface in session "
- + mSession.mSurfaceSession + " window " + this
- + " w=" + mFrame.width()
- + " h=" + mFrame.height() + " format="
- + mAttrs.format + " flags=" + flags);
-
- int w = mFrame.width();
- int h = mFrame.height();
- if ((mAttrs.flags & LayoutParams.FLAG_SCALED) != 0) {
- // for a scaled surface, we always want the requested
- // size.
- w = mRequestedWidth;
- h = mRequestedHeight;
- }
-
- // Something is wrong and SurfaceFlinger will not like this,
- // try to revert to sane values
- if (w <= 0) w = 1;
- if (h <= 0) h = 1;
-
- mSurfaceShown = false;
- mSurfaceLayer = 0;
- mSurfaceAlpha = 1;
- mSurfaceX = 0;
- mSurfaceY = 0;
- mSurfaceW = w;
- mSurfaceH = h;
- try {
- final boolean isHwAccelerated = (mAttrs.flags &
- WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0;
- final int format = isHwAccelerated ? PixelFormat.TRANSLUCENT : mAttrs.format;
- if (isHwAccelerated && mAttrs.format == PixelFormat.OPAQUE) {
- flags |= Surface.OPAQUE;
- }
- mSurface = new Surface(
- mSession.mSurfaceSession, mSession.mPid,
- mAttrs.getTitle().toString(),
- 0, w, h, format, flags);
- if (SHOW_TRANSACTIONS) Slog.i(TAG, " CREATE SURFACE "
- + mSurface + " IN SESSION "
- + mSession.mSurfaceSession
- + ": pid=" + mSession.mPid + " format="
- + mAttrs.format + " flags=0x"
- + Integer.toHexString(flags)
- + " / " + this);
- } catch (Surface.OutOfResourcesException e) {
- Slog.w(TAG, "OutOfResourcesException creating surface");
- reclaimSomeSurfaceMemoryLocked(this, "create");
- return null;
- } catch (Exception e) {
- Slog.e(TAG, "Exception creating surface", e);
- return null;
- }
-
- if (localLOGV) Slog.v(
- TAG, "Got surface: " + mSurface
- + ", set left=" + mFrame.left + " top=" + mFrame.top
- + ", animLayer=" + mAnimLayer);
- if (SHOW_TRANSACTIONS) {
- Slog.i(TAG, ">>> OPEN TRANSACTION createSurfaceLocked");
- logSurface(this, "CREATE pos=(" + mFrame.left + "," + mFrame.top + ") (" +
- mFrame.width() + "x" + mFrame.height() + "), layer=" +
- mAnimLayer + " HIDE", null);
- }
- Surface.openTransaction();
- try {
- try {
- mSurfaceX = mFrame.left + mXOffset;
- mSurfaceY = mFrame.top + mYOffset;
- mSurface.setPosition(mSurfaceX, mSurfaceY);
- mSurfaceLayer = mAnimLayer;
- mSurface.setLayer(mAnimLayer);
- mSurfaceShown = false;
- mSurface.hide();
- if ((mAttrs.flags&WindowManager.LayoutParams.FLAG_DITHER) != 0) {
- if (SHOW_TRANSACTIONS) logSurface(this, "DITHER", null);
- mSurface.setFlags(Surface.SURFACE_DITHER,
- Surface.SURFACE_DITHER);
- }
- } catch (RuntimeException e) {
- Slog.w(TAG, "Error creating surface in " + w, e);
- reclaimSomeSurfaceMemoryLocked(this, "create-init");
- }
- mLastHidden = true;
- } finally {
- Surface.closeTransaction();
- if (SHOW_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION createSurfaceLocked");
- }
- if (localLOGV) Slog.v(
- TAG, "Created surface " + this);
- }
- return mSurface;
- }
-
- void destroySurfaceLocked() {
- if (mAppToken != null && this == mAppToken.startingWindow) {
- mAppToken.startingDisplayed = false;
- }
-
- if (mSurface != null) {
- mDrawPending = false;
- mCommitDrawPending = false;
- mReadyToShow = false;
-
- int i = mChildWindows.size();
- while (i > 0) {
- i--;
- WindowState c = mChildWindows.get(i);
- c.mAttachedHidden = true;
- }
-
- if (mReportDestroySurface) {
- mReportDestroySurface = false;
- mSurfacePendingDestroy = true;
- try {
- mClient.dispatchGetNewSurface();
- // We'll really destroy on the next time around.
- return;
- } catch (RemoteException e) {
- }
- }
-
- try {
- if (DEBUG_VISIBILITY) {
- RuntimeException e = null;
- if (!HIDE_STACK_CRAWLS) {
- e = new RuntimeException();
- e.fillInStackTrace();
- }
- Slog.w(TAG, "Window " + this + " destroying surface "
- + mSurface + ", session " + mSession, e);
- }
- if (SHOW_TRANSACTIONS) {
- RuntimeException e = null;
- if (!HIDE_STACK_CRAWLS) {
- e = new RuntimeException();
- e.fillInStackTrace();
- }
- if (SHOW_TRANSACTIONS) logSurface(this, "DESTROY", e);
- }
- mSurface.destroy();
- } catch (RuntimeException e) {
- Slog.w(TAG, "Exception thrown when destroying Window " + this
- + " surface " + mSurface + " session " + mSession
- + ": " + e.toString());
- }
-
- mSurfaceShown = false;
- mSurface = null;
- }
- }
-
- boolean finishDrawingLocked() {
- if (mDrawPending) {
- if (SHOW_TRANSACTIONS || DEBUG_ORIENTATION) Slog.v(
- TAG, "finishDrawingLocked: " + mSurface);
- mCommitDrawPending = true;
- mDrawPending = false;
- return true;
- }
- return false;
- }
-
- // This must be called while inside a transaction.
- boolean commitFinishDrawingLocked(long currentTime) {
- //Slog.i(TAG, "commitFinishDrawingLocked: " + mSurface);
- if (!mCommitDrawPending) {
- return false;
- }
- mCommitDrawPending = false;
- mReadyToShow = true;
- final boolean starting = mAttrs.type == TYPE_APPLICATION_STARTING;
- final AppWindowToken atoken = mAppToken;
- if (atoken == null || atoken.allDrawn || starting) {
- performShowLocked();
- }
- return true;
- }
-
- // This must be called while inside a transaction.
- boolean performShowLocked() {
- if (DEBUG_VISIBILITY) {
- RuntimeException e = null;
- if (!HIDE_STACK_CRAWLS) {
- e = new RuntimeException();
- e.fillInStackTrace();
- }
- Slog.v(TAG, "performShow on " + this
- + ": readyToShow=" + mReadyToShow + " readyForDisplay=" + isReadyForDisplay()
- + " starting=" + (mAttrs.type == TYPE_APPLICATION_STARTING), e);
- }
- if (mReadyToShow && isReadyForDisplay()) {
- if (SHOW_TRANSACTIONS || DEBUG_ORIENTATION) logSurface(this,
- "SHOW (performShowLocked)", null);
- if (DEBUG_VISIBILITY) Slog.v(TAG, "Showing " + this
- + " during animation: policyVis=" + mPolicyVisibility
- + " attHidden=" + mAttachedHidden
- + " tok.hiddenRequested="
- + (mAppToken != null ? mAppToken.hiddenRequested : false)
- + " tok.hidden="
- + (mAppToken != null ? mAppToken.hidden : false)
- + " animating=" + mAnimating
- + " tok animating="
- + (mAppToken != null ? mAppToken.animating : false));
- if (!showSurfaceRobustlyLocked(this)) {
- return false;
- }
- mLastAlpha = -1;
- mHasDrawn = true;
- mLastHidden = false;
- mReadyToShow = false;
- enableScreenIfNeededLocked();
-
- applyEnterAnimationLocked(this);
-
- int i = mChildWindows.size();
- while (i > 0) {
- i--;
- WindowState c = mChildWindows.get(i);
- if (c.mAttachedHidden) {
- c.mAttachedHidden = false;
- if (c.mSurface != null) {
- c.performShowLocked();
- // It hadn't been shown, which means layout not
- // performed on it, so now we want to make sure to
- // do a layout. If called from within the transaction
- // loop, this will cause it to restart with a new
- // layout.
- mLayoutNeeded = true;
- }
- }
- }
-
- if (mAttrs.type != TYPE_APPLICATION_STARTING
- && mAppToken != null) {
- mAppToken.firstWindowDrawn = true;
-
- if (mAppToken.startingData != null) {
- if (DEBUG_STARTING_WINDOW || DEBUG_ANIM) Slog.v(TAG,
- "Finish starting " + mToken
- + ": first real window is shown, no animation");
- // If this initial window is animating, stop it -- we
- // will do an animation to reveal it from behind the
- // starting window, so there is no need for it to also
- // be doing its own stuff.
- if (mAnimation != null) {
- mAnimation.cancel();
- mAnimation = null;
- // Make sure we clean up the animation.
- mAnimating = true;
- }
- mFinishedStarting.add(mAppToken);
- mH.sendEmptyMessage(H.FINISHED_STARTING);
- }
- mAppToken.updateReportedVisibilityLocked();
- }
- }
- return true;
- }
-
- // This must be called while inside a transaction. Returns true if
- // there is more animation to run.
- boolean stepAnimationLocked(long currentTime, int dw, int dh) {
- if (!mDisplayFrozen && mPolicy.isScreenOn()) {
- // We will run animations as long as the display isn't frozen.
-
- if (!mDrawPending && !mCommitDrawPending && mAnimation != null) {
- mHasTransformation = true;
- mHasLocalTransformation = true;
- if (!mLocalAnimating) {
- if (DEBUG_ANIM) Slog.v(
- TAG, "Starting animation in " + this +
- " @ " + currentTime + ": ww=" + mFrame.width() + " wh=" + mFrame.height() +
- " dw=" + dw + " dh=" + dh + " scale=" + mWindowAnimationScale);
- mAnimation.initialize(mFrame.width(), mFrame.height(), dw, dh);
- mAnimation.setStartTime(currentTime);
- mLocalAnimating = true;
- mAnimating = true;
- }
- mTransformation.clear();
- final boolean more = mAnimation.getTransformation(
- currentTime, mTransformation);
- if (DEBUG_ANIM) Slog.v(
- TAG, "Stepped animation in " + this +
- ": more=" + more + ", xform=" + mTransformation);
- if (more) {
- // we're not done!
- return true;
- }
- if (DEBUG_ANIM) Slog.v(
- TAG, "Finished animation in " + this +
- " @ " + currentTime);
-
- if (mAnimation != null) {
- mAnimation.cancel();
- mAnimation = null;
- }
- //WindowManagerService.this.dump();
- }
- mHasLocalTransformation = false;
- if ((!mLocalAnimating || mAnimationIsEntrance) && mAppToken != null
- && mAppToken.animation != null) {
- // When our app token is animating, we kind-of pretend like
- // we are as well. Note the mLocalAnimating mAnimationIsEntrance
- // part of this check means that we will only do this if
- // our window is not currently exiting, or it is not
- // locally animating itself. The idea being that one that
- // is exiting and doing a local animation should be removed
- // once that animation is done.
- mAnimating = true;
- mHasTransformation = true;
- mTransformation.clear();
- return false;
- } else if (mHasTransformation) {
- // Little trick to get through the path below to act like
- // we have finished an animation.
- mAnimating = true;
- } else if (isAnimating()) {
- mAnimating = true;
- }
- } else if (mAnimation != null) {
- // If the display is frozen, and there is a pending animation,
- // clear it and make sure we run the cleanup code.
- mAnimating = true;
- mLocalAnimating = true;
- mAnimation.cancel();
- mAnimation = null;
- }
-
- if (!mAnimating && !mLocalAnimating) {
- return false;
- }
-
- if (DEBUG_ANIM) Slog.v(
- TAG, "Animation done in " + this + ": exiting=" + mExiting
- + ", reportedVisible="
- + (mAppToken != null ? mAppToken.reportedVisible : false));
-
- mAnimating = false;
- mLocalAnimating = false;
- if (mAnimation != null) {
- mAnimation.cancel();
- mAnimation = null;
- }
- mAnimLayer = mLayer;
- if (mIsImWindow) {
- mAnimLayer += mInputMethodAnimLayerAdjustment;
- } else if (mIsWallpaper) {
- mAnimLayer += mWallpaperAnimLayerAdjustment;
- }
- if (DEBUG_LAYERS) Slog.v(TAG, "Stepping win " + this
- + " anim layer: " + mAnimLayer);
- mHasTransformation = false;
- mHasLocalTransformation = false;
- if (mPolicyVisibility != mPolicyVisibilityAfterAnim) {
- if (DEBUG_VISIBILITY) {
- Slog.v(TAG, "Policy visibility changing after anim in " + this + ": "
- + mPolicyVisibilityAfterAnim);
- }
- mPolicyVisibility = mPolicyVisibilityAfterAnim;
- if (!mPolicyVisibility) {
- if (mCurrentFocus == this) {
- mFocusMayChange = true;
- }
- // Window is no longer visible -- make sure if we were waiting
- // for it to be displayed before enabling the display, that
- // we allow the display to be enabled now.
- enableScreenIfNeededLocked();
- }
- }
- mTransformation.clear();
- if (mHasDrawn
- && mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING
- && mAppToken != null
- && mAppToken.firstWindowDrawn
- && mAppToken.startingData != null) {
- if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Finish starting "
- + mToken + ": first real window done animating");
- mFinishedStarting.add(mAppToken);
- mH.sendEmptyMessage(H.FINISHED_STARTING);
- }
-
- finishExit();
-
- if (mAppToken != null) {
- mAppToken.updateReportedVisibilityLocked();
- }
-
- return false;
- }
-
- void finishExit() {
- if (DEBUG_ANIM) Slog.v(
- TAG, "finishExit in " + this
- + ": exiting=" + mExiting
- + " remove=" + mRemoveOnExit
- + " windowAnimating=" + isWindowAnimating());
-
- final int N = mChildWindows.size();
- for (int i=0; i<N; i++) {
- mChildWindows.get(i).finishExit();
- }
-
- if (!mExiting) {
- return;
- }
-
- if (isWindowAnimating()) {
- return;
- }
-
- if (localLOGV) Slog.v(
- TAG, "Exit animation finished in " + this
- + ": remove=" + mRemoveOnExit);
- if (mSurface != null) {
- mDestroySurface.add(this);
- mDestroying = true;
- if (SHOW_TRANSACTIONS) logSurface(this, "HIDE (finishExit)", null);
- mSurfaceShown = false;
- try {
- mSurface.hide();
- } catch (RuntimeException e) {
- Slog.w(TAG, "Error hiding surface in " + this, e);
- }
- mLastHidden = true;
- }
- mExiting = false;
- if (mRemoveOnExit) {
- mPendingRemove.add(this);
- mRemoveOnExit = false;
- }
- }
-
- boolean isIdentityMatrix(float dsdx, float dtdx, float dsdy, float dtdy) {
- if (dsdx < .99999f || dsdx > 1.00001f) return false;
- if (dtdy < .99999f || dtdy > 1.00001f) return false;
- if (dtdx < -.000001f || dtdx > .000001f) return false;
- if (dsdy < -.000001f || dsdy > .000001f) return false;
- return true;
- }
-
- void computeShownFrameLocked() {
- final boolean selfTransformation = mHasLocalTransformation;
- Transformation attachedTransformation =
- (mAttachedWindow != null && mAttachedWindow.mHasLocalTransformation)
- ? mAttachedWindow.mTransformation : null;
- Transformation appTransformation =
- (mAppToken != null && mAppToken.hasTransformation)
- ? mAppToken.transformation : null;
-
- // Wallpapers are animated based on the "real" window they
- // are currently targeting.
- if (mAttrs.type == TYPE_WALLPAPER && mLowerWallpaperTarget == null
- && mWallpaperTarget != null) {
- if (mWallpaperTarget.mHasLocalTransformation &&
- mWallpaperTarget.mAnimation != null &&
- !mWallpaperTarget.mAnimation.getDetachWallpaper()) {
- attachedTransformation = mWallpaperTarget.mTransformation;
- if (DEBUG_WALLPAPER && attachedTransformation != null) {
- Slog.v(TAG, "WP target attached xform: " + attachedTransformation);
- }
- }
- if (mWallpaperTarget.mAppToken != null &&
- mWallpaperTarget.mAppToken.hasTransformation &&
- mWallpaperTarget.mAppToken.animation != null &&
- !mWallpaperTarget.mAppToken.animation.getDetachWallpaper()) {
- appTransformation = mWallpaperTarget.mAppToken.transformation;
- if (DEBUG_WALLPAPER && appTransformation != null) {
- Slog.v(TAG, "WP target app xform: " + appTransformation);
- }
- }
- }
-
- final boolean screenAnimation = mScreenRotationAnimation != null
- && mScreenRotationAnimation.isAnimating();
- if (selfTransformation || attachedTransformation != null
- || appTransformation != null || screenAnimation) {
- // cache often used attributes locally
- final Rect frame = mFrame;
- final float tmpFloats[] = mTmpFloats;
- final Matrix tmpMatrix = mTmpMatrix;
-
- // Compute the desired transformation.
- tmpMatrix.setTranslate(0, 0);
- if (selfTransformation) {
- tmpMatrix.postConcat(mTransformation.getMatrix());
- }
- tmpMatrix.postTranslate(frame.left + mXOffset, frame.top + mYOffset);
- if (attachedTransformation != null) {
- tmpMatrix.postConcat(attachedTransformation.getMatrix());
- }
- if (appTransformation != null) {
- tmpMatrix.postConcat(appTransformation.getMatrix());
- }
- if (screenAnimation) {
- tmpMatrix.postConcat(
- mScreenRotationAnimation.getEnterTransformation().getMatrix());
- }
-
- // "convert" it into SurfaceFlinger's format
- // (a 2x2 matrix + an offset)
- // Here we must not transform the position of the surface
- // since it is already included in the transformation.
- //Slog.i(TAG, "Transform: " + matrix);
-
- mHaveMatrix = true;
- tmpMatrix.getValues(tmpFloats);
- mDsDx = tmpFloats[Matrix.MSCALE_X];
- mDtDx = tmpFloats[Matrix.MSKEW_Y];
- mDsDy = tmpFloats[Matrix.MSKEW_X];
- mDtDy = tmpFloats[Matrix.MSCALE_Y];
- int x = (int)tmpFloats[Matrix.MTRANS_X];
- int y = (int)tmpFloats[Matrix.MTRANS_Y];
- int w = frame.width();
- int h = frame.height();
- mShownFrame.set(x, y, x+w, y+h);
-
- // Now set the alpha... but because our current hardware
- // can't do alpha transformation on a non-opaque surface,
- // turn it off if we are running an animation that is also
- // transforming since it is more important to have that
- // animation be smooth.
- mShownAlpha = mAlpha;
- if (!mLimitedAlphaCompositing
- || (!PixelFormat.formatHasAlpha(mAttrs.format)
- || (isIdentityMatrix(mDsDx, mDtDx, mDsDy, mDtDy)
- && x == frame.left && y == frame.top))) {
- //Slog.i(TAG, "Applying alpha transform");
- if (selfTransformation) {
- mShownAlpha *= mTransformation.getAlpha();
- }
- if (attachedTransformation != null) {
- mShownAlpha *= attachedTransformation.getAlpha();
- }
- if (appTransformation != null) {
- mShownAlpha *= appTransformation.getAlpha();
- }
- if (screenAnimation) {
- mShownAlpha *=
- mScreenRotationAnimation.getEnterTransformation().getAlpha();
- }
- } else {
- //Slog.i(TAG, "Not applying alpha transform");
- }
-
- if (localLOGV) Slog.v(
- TAG, "Continuing animation in " + this +
- ": " + mShownFrame +
- ", alpha=" + mTransformation.getAlpha());
- return;
- }
-
- mShownFrame.set(mFrame);
- if (mXOffset != 0 || mYOffset != 0) {
- mShownFrame.offset(mXOffset, mYOffset);
- }
- mShownAlpha = mAlpha;
- mHaveMatrix = false;
- mDsDx = 1;
- mDtDx = 0;
- mDsDy = 0;
- mDtDy = 1;
- }
-
- /**
- * Is this window visible? It is not visible if there is no
- * surface, or we are in the process of running an exit animation
- * that will remove the surface, or its app token has been hidden.
- */
- public boolean isVisibleLw() {
- final AppWindowToken atoken = mAppToken;
- return mSurface != null && mPolicyVisibility && !mAttachedHidden
- && (atoken == null || !atoken.hiddenRequested)
- && !mExiting && !mDestroying;
- }
-
- /**
- * Like {@link #isVisibleLw}, but also counts a window that is currently
- * "hidden" behind the keyguard as visible. This allows us to apply
- * things like window flags that impact the keyguard.
- * XXX I am starting to think we need to have ANOTHER visibility flag
- * for this "hidden behind keyguard" state rather than overloading
- * mPolicyVisibility. Ungh.
- */
- public boolean isVisibleOrBehindKeyguardLw() {
- final AppWindowToken atoken = mAppToken;
- return mSurface != null && !mAttachedHidden
- && (atoken == null ? mPolicyVisibility : !atoken.hiddenRequested)
- && !mDrawPending && !mCommitDrawPending
- && !mExiting && !mDestroying;
- }
-
- /**
- * Is this window visible, ignoring its app token? It is not visible
- * if there is no surface, or we are in the process of running an exit animation
- * that will remove the surface.
- */
- public boolean isWinVisibleLw() {
- final AppWindowToken atoken = mAppToken;
- return mSurface != null && mPolicyVisibility && !mAttachedHidden
- && (atoken == null || !atoken.hiddenRequested || atoken.animating)
- && !mExiting && !mDestroying;
- }
-
- /**
- * The same as isVisible(), but follows the current hidden state of
- * the associated app token, not the pending requested hidden state.
- */
- boolean isVisibleNow() {
- return mSurface != null && mPolicyVisibility && !mAttachedHidden
- && !mRootToken.hidden && !mExiting && !mDestroying;
- }
-
- /**
- * Can this window possibly be a drag/drop target? The test here is
- * a combination of the above "visible now" with the check that the
- * Input Manager uses when discarding windows from input consideration.
- */
- boolean isPotentialDragTarget() {
- return isVisibleNow() && (mInputChannel != null) && !mRemoved;
- }
-
- /**
- * Same as isVisible(), but we also count it as visible between the
- * call to IWindowSession.add() and the first relayout().
- */
- boolean isVisibleOrAdding() {
- final AppWindowToken atoken = mAppToken;
- return ((mSurface != null && !mReportDestroySurface)
- || (!mRelayoutCalled && mViewVisibility == View.VISIBLE))
- && mPolicyVisibility && !mAttachedHidden
- && (atoken == null || !atoken.hiddenRequested)
- && !mExiting && !mDestroying;
- }
-
- /**
- * Is this window currently on-screen? It is on-screen either if it
- * is visible or it is currently running an animation before no longer
- * being visible.
- */
- boolean isOnScreen() {
- final AppWindowToken atoken = mAppToken;
- if (atoken != null) {
- return mSurface != null && mPolicyVisibility && !mDestroying
- && ((!mAttachedHidden && !atoken.hiddenRequested)
- || mAnimation != null || atoken.animation != null);
- } else {
- return mSurface != null && mPolicyVisibility && !mDestroying
- && (!mAttachedHidden || mAnimation != null);
- }
- }
-
- /**
- * Like isOnScreen(), but we don't return true if the window is part
- * of a transition that has not yet been started.
- */
- boolean isReadyForDisplay() {
- if (mRootToken.waitingToShow &&
- mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) {
- return false;
- }
- final AppWindowToken atoken = mAppToken;
- final boolean animating = atoken != null
- ? (atoken.animation != null) : false;
- return mSurface != null && mPolicyVisibility && !mDestroying
- && ((!mAttachedHidden && mViewVisibility == View.VISIBLE
- && !mRootToken.hidden)
- || mAnimation != null || animating);
- }
-
- /** Is the window or its container currently animating? */
- boolean isAnimating() {
- final WindowState attached = mAttachedWindow;
- final AppWindowToken atoken = mAppToken;
- return mAnimation != null
- || (attached != null && attached.mAnimation != null)
- || (atoken != null &&
- (atoken.animation != null
- || atoken.inPendingTransaction));
- }
-
- /** Is this window currently animating? */
- boolean isWindowAnimating() {
- return mAnimation != null;
- }
-
- /**
- * Like isOnScreen, but returns false if the surface hasn't yet
- * been drawn.
- */
- public boolean isDisplayedLw() {
- final AppWindowToken atoken = mAppToken;
- return mSurface != null && mPolicyVisibility && !mDestroying
- && !mDrawPending && !mCommitDrawPending
- && ((!mAttachedHidden &&
- (atoken == null || !atoken.hiddenRequested))
- || mAnimating);
- }
-
- /**
- * Returns true if the window has a surface that it has drawn a
- * complete UI in to.
- */
- public boolean isDrawnLw() {
- final AppWindowToken atoken = mAppToken;
- return mSurface != null && !mDestroying
- && !mDrawPending && !mCommitDrawPending;
- }
-
- /**
- * Return true if the window is opaque and fully drawn. This indicates
- * it may obscure windows behind it.
- */
- boolean isOpaqueDrawn() {
- return (mAttrs.format == PixelFormat.OPAQUE
- || mAttrs.type == TYPE_WALLPAPER)
- && mSurface != null && mAnimation == null
- && (mAppToken == null || mAppToken.animation == null)
- && !mDrawPending && !mCommitDrawPending;
- }
-
- /**
- * Return whether this window is wanting to have a translation
- * animation applied to it for an in-progress move. (Only makes
- * sense to call from performLayoutAndPlaceSurfacesLockedInner().)
- */
- boolean shouldAnimateMove() {
- return mContentChanged && !mExiting && !mLastHidden && !mDisplayFrozen
- && (mFrame.top != mLastFrame.top
- || mFrame.left != mLastFrame.left)
- && (mAttachedWindow == null || !mAttachedWindow.shouldAnimateMove())
- && mPolicy.isScreenOn();
- }
-
- boolean needsBackgroundFiller(int screenWidth, int screenHeight) {
- return
- // only if the application is requesting compatible window
- (mAttrs.flags & FLAG_COMPATIBLE_WINDOW) != 0 &&
- // only if it's visible
- mHasDrawn && mViewVisibility == View.VISIBLE &&
- // and only if the application fills the compatible screen
- mFrame.left <= mCompatibleScreenFrame.left &&
- mFrame.top <= mCompatibleScreenFrame.top &&
- mFrame.right >= mCompatibleScreenFrame.right &&
- mFrame.bottom >= mCompatibleScreenFrame.bottom;
- }
-
- boolean isFullscreen(int screenWidth, int screenHeight) {
- return mFrame.left <= 0 && mFrame.top <= 0 &&
- mFrame.right >= screenWidth && mFrame.bottom >= screenHeight;
- }
-
- void removeLocked() {
- disposeInputChannel();
-
- if (mAttachedWindow != null) {
- if (DEBUG_ADD_REMOVE) Slog.v(TAG, "Removing " + this + " from " + mAttachedWindow);
- mAttachedWindow.mChildWindows.remove(this);
- }
- destroySurfaceLocked();
- mSession.windowRemovedLocked();
- try {
- mClient.asBinder().unlinkToDeath(mDeathRecipient, 0);
- } catch (RuntimeException e) {
- // Ignore if it has already been removed (usually because
- // we are doing this as part of processing a death note.)
- }
- }
-
- void disposeInputChannel() {
- if (mInputChannel != null) {
- mInputManager.unregisterInputChannel(mInputChannel);
-
- mInputChannel.dispose();
- mInputChannel = null;
- }
- }
-
- private class DeathRecipient implements IBinder.DeathRecipient {
- public void binderDied() {
- try {
- synchronized(mWindowMap) {
- WindowState win = windowForClientLocked(mSession, mClient, false);
- Slog.i(TAG, "WIN DEATH: " + win);
- if (win != null) {
- removeWindowLocked(mSession, win);
- }
- }
- } catch (IllegalArgumentException ex) {
- // This will happen if the window has already been
- // removed.
- }
- }
- }
-
- /** Returns true if this window desires key events. */
- public final boolean canReceiveKeys() {
- return isVisibleOrAdding()
- && (mViewVisibility == View.VISIBLE)
- && ((mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) == 0);
- }
-
- public boolean hasDrawnLw() {
- return mHasDrawn;
- }
-
- public boolean showLw(boolean doAnimation) {
- return showLw(doAnimation, true);
- }
-
- boolean showLw(boolean doAnimation, boolean requestAnim) {
- if (mPolicyVisibility && mPolicyVisibilityAfterAnim) {
- return false;
- }
- if (DEBUG_VISIBILITY) Slog.v(TAG, "Policy visibility true: " + this);
- if (doAnimation) {
- if (DEBUG_VISIBILITY) Slog.v(TAG, "doAnimation: mPolicyVisibility="
- + mPolicyVisibility + " mAnimation=" + mAnimation);
- if (mDisplayFrozen || !mPolicy.isScreenOn()) {
- doAnimation = false;
- } else if (mPolicyVisibility && mAnimation == null) {
- // Check for the case where we are currently visible and
- // not animating; we do not want to do animation at such a
- // point to become visible when we already are.
- doAnimation = false;
- }
- }
- mPolicyVisibility = true;
- mPolicyVisibilityAfterAnim = true;
- if (doAnimation) {
- applyAnimationLocked(this, WindowManagerPolicy.TRANSIT_ENTER, true);
- }
- if (requestAnim) {
- requestAnimationLocked(0);
- }
- return true;
- }
-
- public boolean hideLw(boolean doAnimation) {
- return hideLw(doAnimation, true);
- }
-
- boolean hideLw(boolean doAnimation, boolean requestAnim) {
- if (doAnimation) {
- if (mDisplayFrozen || !mPolicy.isScreenOn()) {
- doAnimation = false;
- }
- }
- boolean current = doAnimation ? mPolicyVisibilityAfterAnim
- : mPolicyVisibility;
- if (!current) {
- return false;
- }
- if (doAnimation) {
- applyAnimationLocked(this, WindowManagerPolicy.TRANSIT_EXIT, false);
- if (mAnimation == null) {
- doAnimation = false;
- }
- }
- if (doAnimation) {
- mPolicyVisibilityAfterAnim = false;
- } else {
- if (DEBUG_VISIBILITY) Slog.v(TAG, "Policy visibility false: " + this);
- mPolicyVisibilityAfterAnim = false;
- mPolicyVisibility = false;
- // Window is no longer visible -- make sure if we were waiting
- // for it to be displayed before enabling the display, that
- // we allow the display to be enabled now.
- enableScreenIfNeededLocked();
- if (mCurrentFocus == this) {
- mFocusMayChange = true;
- }
- }
- if (requestAnim) {
- requestAnimationLocked(0);
- }
- return true;
- }
-
- public void getTouchableRegion(Region outRegion) {
- final Rect frame = mFrame;
- switch (mTouchableInsets) {
- default:
- case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME:
- outRegion.set(frame);
- break;
- case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT: {
- final Rect inset = mGivenContentInsets;
- outRegion.set(
- frame.left + inset.left, frame.top + inset.top,
- frame.right - inset.right, frame.bottom - inset.bottom);
- break;
- }
- case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE: {
- final Rect inset = mGivenVisibleInsets;
- outRegion.set(
- frame.left + inset.left, frame.top + inset.top,
- frame.right - inset.right, frame.bottom - inset.bottom);
- break;
- }
- case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION: {
- final Region givenTouchableRegion = mGivenTouchableRegion;
- outRegion.set(givenTouchableRegion);
- outRegion.translate(frame.left, frame.top);
- break;
- }
- }
- }
-
- void dump(PrintWriter pw, String prefix) {
- pw.print(prefix); pw.print("mSession="); pw.print(mSession);
- pw.print(" mClient="); pw.println(mClient.asBinder());
- pw.print(prefix); pw.print("mAttrs="); pw.println(mAttrs);
- if (mAttachedWindow != null || mLayoutAttached) {
- pw.print(prefix); pw.print("mAttachedWindow="); pw.print(mAttachedWindow);
- pw.print(" mLayoutAttached="); pw.println(mLayoutAttached);
- }
- if (mIsImWindow || mIsWallpaper || mIsFloatingLayer) {
- pw.print(prefix); pw.print("mIsImWindow="); pw.print(mIsImWindow);
- pw.print(" mIsWallpaper="); pw.print(mIsWallpaper);
- pw.print(" mIsFloatingLayer="); pw.print(mIsFloatingLayer);
- pw.print(" mWallpaperVisible="); pw.println(mWallpaperVisible);
- }
- pw.print(prefix); pw.print("mBaseLayer="); pw.print(mBaseLayer);
- pw.print(" mSubLayer="); pw.print(mSubLayer);
- pw.print(" mAnimLayer="); pw.print(mLayer); pw.print("+");
- pw.print((mTargetAppToken != null ? mTargetAppToken.animLayerAdjustment
- : (mAppToken != null ? mAppToken.animLayerAdjustment : 0)));
- pw.print("="); pw.print(mAnimLayer);
- pw.print(" mLastLayer="); pw.println(mLastLayer);
- if (mSurface != null) {
- pw.print(prefix); pw.print("mSurface="); pw.println(mSurface);
- pw.print(prefix); pw.print("Surface: shown="); pw.print(mSurfaceShown);
- pw.print(" layer="); pw.print(mSurfaceLayer);
- pw.print(" alpha="); pw.print(mSurfaceAlpha);
- pw.print(" rect=("); pw.print(mSurfaceX);
- pw.print(","); pw.print(mSurfaceY);
- pw.print(") "); pw.print(mSurfaceW);
- pw.print(" x "); pw.println(mSurfaceH);
- }
- pw.print(prefix); pw.print("mToken="); pw.println(mToken);
- pw.print(prefix); pw.print("mRootToken="); pw.println(mRootToken);
- if (mAppToken != null) {
- pw.print(prefix); pw.print("mAppToken="); pw.println(mAppToken);
- }
- if (mTargetAppToken != null) {
- pw.print(prefix); pw.print("mTargetAppToken="); pw.println(mTargetAppToken);
- }
- pw.print(prefix); pw.print("mViewVisibility=0x");
- pw.print(Integer.toHexString(mViewVisibility));
- pw.print(" mLastHidden="); pw.print(mLastHidden);
- pw.print(" mHaveFrame="); pw.print(mHaveFrame);
- pw.print(" mObscured="); pw.println(mObscured);
- if (!mPolicyVisibility || !mPolicyVisibilityAfterAnim || mAttachedHidden) {
- pw.print(prefix); pw.print("mPolicyVisibility=");
- pw.print(mPolicyVisibility);
- pw.print(" mPolicyVisibilityAfterAnim=");
- pw.print(mPolicyVisibilityAfterAnim);
- pw.print(" mAttachedHidden="); pw.println(mAttachedHidden);
- }
- if (!mRelayoutCalled) {
- pw.print(prefix); pw.print("mRelayoutCalled="); pw.println(mRelayoutCalled);
- }
- pw.print(prefix); pw.print("Requested w="); pw.print(mRequestedWidth);
- pw.print(" h="); pw.print(mRequestedHeight);
- pw.print(" mLayoutSeq="); pw.println(mLayoutSeq);
- if (mXOffset != 0 || mYOffset != 0) {
- pw.print(prefix); pw.print("Offsets x="); pw.print(mXOffset);
- pw.print(" y="); pw.println(mYOffset);
- }
- pw.print(prefix); pw.print("mGivenContentInsets=");
- mGivenContentInsets.printShortString(pw);
- pw.print(" mGivenVisibleInsets=");
- mGivenVisibleInsets.printShortString(pw);
- pw.println();
- if (mTouchableInsets != 0 || mGivenInsetsPending) {
- pw.print(prefix); pw.print("mTouchableInsets="); pw.print(mTouchableInsets);
- pw.print(" mGivenInsetsPending="); pw.println(mGivenInsetsPending);
- }
- pw.print(prefix); pw.print("mConfiguration="); pw.println(mConfiguration);
- pw.print(prefix); pw.print("mShownFrame=");
- mShownFrame.printShortString(pw);
- pw.print(" last="); mLastShownFrame.printShortString(pw);
- pw.println();
- pw.print(prefix); pw.print("mFrame="); mFrame.printShortString(pw);
- pw.print(" last="); mLastFrame.printShortString(pw);
- pw.println();
- pw.print(prefix); pw.print("mContainingFrame=");
- mContainingFrame.printShortString(pw);
- pw.print(" mParentFrame=");
- mParentFrame.printShortString(pw);
- pw.print(" mDisplayFrame=");
- mDisplayFrame.printShortString(pw);
- pw.println();
- pw.print(prefix); pw.print("mContentFrame="); mContentFrame.printShortString(pw);
- pw.print(" mVisibleFrame="); mVisibleFrame.printShortString(pw);
- pw.println();
- pw.print(prefix); pw.print("mContentInsets="); mContentInsets.printShortString(pw);
- pw.print(" last="); mLastContentInsets.printShortString(pw);
- pw.print(" mVisibleInsets="); mVisibleInsets.printShortString(pw);
- pw.print(" last="); mLastVisibleInsets.printShortString(pw);
- pw.println();
- if (mAnimating || mLocalAnimating || mAnimationIsEntrance
- || mAnimation != null) {
- pw.print(prefix); pw.print("mAnimating="); pw.print(mAnimating);
- pw.print(" mLocalAnimating="); pw.print(mLocalAnimating);
- pw.print(" mAnimationIsEntrance="); pw.print(mAnimationIsEntrance);
- pw.print(" mAnimation="); pw.println(mAnimation);
- }
- if (mHasTransformation || mHasLocalTransformation) {
- pw.print(prefix); pw.print("XForm: has=");
- pw.print(mHasTransformation);
- pw.print(" hasLocal="); pw.print(mHasLocalTransformation);
- pw.print(" "); mTransformation.printShortString(pw);
- pw.println();
- }
- if (mShownAlpha != 1 || mAlpha != 1 || mLastAlpha != 1) {
- pw.print(prefix); pw.print("mShownAlpha="); pw.print(mShownAlpha);
- pw.print(" mAlpha="); pw.print(mAlpha);
- pw.print(" mLastAlpha="); pw.println(mLastAlpha);
- }
- if (mHaveMatrix) {
- pw.print(prefix); pw.print("mDsDx="); pw.print(mDsDx);
- pw.print(" mDtDx="); pw.print(mDtDx);
- pw.print(" mDsDy="); pw.print(mDsDy);
- pw.print(" mDtDy="); pw.println(mDtDy);
- }
- pw.print(prefix); pw.print("mDrawPending="); pw.print(mDrawPending);
- pw.print(" mCommitDrawPending="); pw.print(mCommitDrawPending);
- pw.print(" mReadyToShow="); pw.print(mReadyToShow);
- pw.print(" mHasDrawn="); pw.println(mHasDrawn);
- if (mExiting || mRemoveOnExit || mDestroying || mRemoved) {
- pw.print(prefix); pw.print("mExiting="); pw.print(mExiting);
- pw.print(" mRemoveOnExit="); pw.print(mRemoveOnExit);
- pw.print(" mDestroying="); pw.print(mDestroying);
- pw.print(" mRemoved="); pw.println(mRemoved);
- }
- if (mOrientationChanging || mAppFreezing || mTurnOnScreen) {
- pw.print(prefix); pw.print("mOrientationChanging=");
- pw.print(mOrientationChanging);
- pw.print(" mAppFreezing="); pw.print(mAppFreezing);
- pw.print(" mTurnOnScreen="); pw.println(mTurnOnScreen);
- }
- if (mHScale != 1 || mVScale != 1) {
- pw.print(prefix); pw.print("mHScale="); pw.print(mHScale);
- pw.print(" mVScale="); pw.println(mVScale);
- }
- if (mWallpaperX != -1 || mWallpaperY != -1) {
- pw.print(prefix); pw.print("mWallpaperX="); pw.print(mWallpaperX);
- pw.print(" mWallpaperY="); pw.println(mWallpaperY);
- }
- if (mWallpaperXStep != -1 || mWallpaperYStep != -1) {
- pw.print(prefix); pw.print("mWallpaperXStep="); pw.print(mWallpaperXStep);
- pw.print(" mWallpaperYStep="); pw.println(mWallpaperYStep);
- }
- }
-
- String makeInputChannelName() {
- return Integer.toHexString(System.identityHashCode(this))
- + " " + mAttrs.getTitle();
- }
-
- @Override
- public String toString() {
- if (mStringNameCache == null || mLastTitle != mAttrs.getTitle()
- || mWasPaused != mToken.paused) {
- mLastTitle = mAttrs.getTitle();
- mWasPaused = mToken.paused;
- mStringNameCache = "Window{" + Integer.toHexString(System.identityHashCode(this))
- + " " + mLastTitle + " paused=" + mWasPaused + "}";
- }
- return mStringNameCache;
- }
- }
-
- // -------------------------------------------------------------
- // Window Token State
- // -------------------------------------------------------------
-
- class WindowToken {
- // 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(IBinder _token, int type, boolean _explicit) {
- 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;
- }
- };
-
- 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 = mTransactionSequence-1;
- 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(IApplicationToken _token) {
- super(_token.asBinder(),
- WindowManager.LayoutParams.TYPE_APPLICATION, true);
- appWindowToken = this;
- appToken = _token;
- mInputApplicationHandle = new InputApplicationHandle(this);
- }
-
- public void setAnimation(Animation anim) {
- if (localLOGV) Slog.v(
- TAG, "Setting animation in " + this + ": " + anim);
- animation = anim;
- animating = false;
- anim.restrictDuration(MAX_ANIMATION_DURATION);
- anim.scaleCurrentDuration(mTransitionAnimationScale);
- int zorder = anim.getZAdjustment();
- int adj = 0;
- if (zorder == Animation.ZORDER_TOP) {
- adj = TYPE_LAYER_OFFSET;
- } else if (zorder == Animation.ZORDER_BOTTOM) {
- adj = -TYPE_LAYER_OFFSET;
- }
-
- if (animLayerAdjustment != adj) {
- animLayerAdjustment = adj;
- updateLayers();
- }
- }
-
- public void setDummyAnimation() {
- if (animation == null) {
- if (localLOGV) Slog.v(
- TAG, "Setting dummy animation in " + this);
- animation = 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 (DEBUG_LAYERS) Slog.v(TAG, "Updating layer " + w + ": "
- + w.mAnimLayer);
- if (w == mInputMethodTarget && !mInputMethodTargetWaitingAnim) {
- setInputMethodAnimLayerAdjustment(adj);
- }
- if (w == mWallpaperTarget && mLowerWallpaperTarget == null) {
- 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 (DEBUG_VISIBILITY) Slog.v(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 (DEBUG_VISIBILITY) Slog.v(TAG,
- "performing show on: " + w);
- w.performShowLocked();
- }
- }
-
- // This must be called while inside a transaction.
- boolean stepAnimationLocked(long currentTime, int dw, int dh) {
- if (!mDisplayFrozen && mPolicy.isScreenOn()) {
- // We will run animations as long as the display isn't frozen.
-
- if (animation == 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 (DEBUG_ANIM) Slog.v(
- TAG, "Starting animation in " + this +
- " @ " + currentTime + ": dw=" + dw + " dh=" + dh
- + " scale=" + 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 (DEBUG_ANIM) Slog.v(
- TAG, "Stepped animation in " + this +
- ": more=" + more + ", xform=" + transformation);
- if (more) {
- // we're done!
- hasTransformation = true;
- return true;
- }
- if (DEBUG_ANIM) Slog.v(
- 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 (mInputMethodTarget != null && mInputMethodTarget.mAppToken == this) {
- moveInputMethodWindowsIfNeededLocked(true);
- }
-
- if (DEBUG_ANIM) Slog.v(
- 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 (DEBUG_VISIBILITY) Slog.v(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 (DEBUG_VISIBILITY) {
- Slog.v(TAG, "Win " + win + ": isDrawn="
- + win.isDrawnLw()
- + ", isAnimating=" + win.isAnimating());
- if (!win.isDrawnLw()) {
- Slog.v(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 (DEBUG_VISIBILITY) Slog.v(TAG, "VIS " + this + ": interesting="
- + numInteresting + " visible=" + numVisible);
- if (nowVisible != reportedVisible) {
- if (DEBUG_VISIBILITY) Slog.v(
- TAG, "Visibility changed in " + this
- + ": vis=" + nowVisible);
- reportedVisible = nowVisible;
- Message m = mH.obtainMessage(
- H.REPORT_APPLICATION_TOKEN_WINDOWS,
- nowVisible ? 1 : 0,
- nowGone ? 1 : 0,
- this);
- 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;
- }
- }
-
- // -------------------------------------------------------------
- // DummyAnimation
- // -------------------------------------------------------------
-
// This is an animation that does nothing: it just immediately finishes
// itself every time it is called. It is used as a stub animation in cases
// where we want to synchronize multiple things that may be animating.
@@ -8913,26 +5826,7 @@
// Async Handler
// -------------------------------------------------------------
- static 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;
- }
- }
-
- private final class H extends Handler {
+ final class H extends Handler {
public static final int REPORT_FOCUS_CHANGE = 2;
public static final int REPORT_LOSING_FOCUS = 3;
public static final int ANIMATE = 4;
@@ -9342,7 +6236,7 @@
IInputContext inputContext) {
if (client == null) throw new IllegalArgumentException("null client");
if (inputContext == null) throw new IllegalArgumentException("null inputContext");
- Session session = new Session(client, inputContext);
+ Session session = new Session(this, client, inputContext);
return session;
}
@@ -11476,148 +8370,6 @@
return val;
}
- static 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(TAG, "*********************** WATERMARK");
- for (int i=0; i<tokens.length; i++) {
- Log.i(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(TAG, "Final text: " + mText);
- }
-
- int fontSize = 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 = getPropertyInt(tokens, 2,
- TypedValue.COMPLEX_UNIT_PX, mTextWidth*2, dm);
- mDeltaY = getPropertyInt(tokens, 3,
- TypedValue.COMPLEX_UNIT_PX, mTextHeight*3, dm);
- int shadowColor = getPropertyInt(tokens, 4,
- TypedValue.COMPLEX_UNIT_PX, 0xb0000000, dm);
- int color = getPropertyInt(tokens, 5,
- TypedValue.COMPLEX_UNIT_PX, 0x60ffffff, dm);
- int shadowRadius = getPropertyInt(tokens, 6,
- TypedValue.COMPLEX_UNIT_PX, 7, dm);
- int shadowDx = getPropertyInt(tokens, 8,
- TypedValue.COMPLEX_UNIT_PX, 0, dm);
- int shadowDy = 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(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);
- }
- }
- }
- }
-
void createWatermark() {
if (mWatermark != null) {
return;
@@ -11899,190 +8651,6 @@
synchronized (mKeyguardTokenWatcher) { }
}
- /**
- * DimAnimator class that controls the dim animation. This holds the surface and
- * all state used for dim animation.
- */
- private static 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 (SHOW_TRANSACTIONS) Slog.i(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(TAG, "Exception creating Dim surface", e);
- }
- }
- }
-
- /**
- * Show the dim surface.
- */
- void show(int dw, int dh) {
- if (!mDimShown) {
- if (SHOW_TRANSACTIONS) Slog.i(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(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 (SHOW_TRANSACTIONS) Slog.i(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()
- : 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) / 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 (SHOW_TRANSACTIONS) Slog.i(TAG, " DIM "
- + mDimSurface + ": alpha=" + mDimCurrentAlpha);
- mLastDimAnimTime = currentTime;
- mDimSurface.setAlpha(mDimCurrentAlpha);
- animating = true;
- } else {
- mDimCurrentAlpha = mDimTargetAlpha;
- mLastDimAnimTime = 0;
- if (SHOW_TRANSACTIONS) Slog.i(TAG, " DIM "
- + mDimSurface + ": final alpha=" + mDimCurrentAlpha);
- mDimSurface.setAlpha(mDimCurrentAlpha);
- if (!dimming) {
- if (SHOW_TRANSACTIONS) Slog.i(TAG, " DIM " + mDimSurface
- + ": HIDE");
- try {
- mDimSurface.hide();
- } catch (RuntimeException e) {
- Slog.w(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);
- }
- }
-
- /**
- * 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.
- */
- private static class FadeInOutAnimation extends Animation {
- boolean mFadeIn;
-
- public FadeInOutAnimation(boolean fadeIn) {
- setInterpolator(new AccelerateInterpolator());
- setDuration(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);
- }
- }
-
public interface OnHardKeyboardStatusChangeListener {
public void onHardKeyboardStatusChange(boolean available, boolean enabled);
}
diff --git a/services/java/com/android/server/wm/WindowState.java b/services/java/com/android/server/wm/WindowState.java
new file mode 100644
index 0000000..d0eec89
--- /dev/null
+++ b/services/java/com/android/server/wm/WindowState.java
@@ -0,0 +1,1623 @@
+/*
+ * 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.FIRST_SUB_WINDOW;
+import static android.view.WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
+import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+
+import com.android.server.wm.WindowManagerService.H;
+
+import android.content.res.Configuration;
+import android.graphics.Matrix;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+import android.view.Gravity;
+import android.view.IApplicationToken;
+import android.view.IWindow;
+import android.view.InputChannel;
+import android.view.Surface;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.view.WindowManager;
+import android.view.WindowManagerPolicy;
+import android.view.WindowManager.LayoutParams;
+import android.view.animation.Animation;
+import android.view.animation.Transformation;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+/**
+ * A window in the window manager.
+ */
+final class WindowState implements WindowManagerPolicy.WindowState {
+ final WindowManagerService mService;
+ final Session mSession;
+ final IWindow mClient;
+ WindowToken mToken;
+ WindowToken mRootToken;
+ AppWindowToken mAppToken;
+ AppWindowToken mTargetAppToken;
+ final WindowManager.LayoutParams mAttrs = new WindowManager.LayoutParams();
+ final DeathRecipient mDeathRecipient;
+ final WindowState mAttachedWindow;
+ final ArrayList<WindowState> mChildWindows = new ArrayList<WindowState>();
+ final int mBaseLayer;
+ final int mSubLayer;
+ final boolean mLayoutAttached;
+ final boolean mIsImWindow;
+ final boolean mIsWallpaper;
+ final boolean mIsFloatingLayer;
+ int mViewVisibility;
+ boolean mPolicyVisibility = true;
+ boolean mPolicyVisibilityAfterAnim = true;
+ boolean mAppFreezing;
+ Surface mSurface;
+ boolean mReportDestroySurface;
+ boolean mSurfacePendingDestroy;
+ boolean mAttachedHidden; // is our parent window hidden?
+ boolean mLastHidden; // was this window last hidden?
+ boolean mWallpaperVisible; // for wallpaper, what was last vis report?
+ int mRequestedWidth;
+ int mRequestedHeight;
+ int mLastRequestedWidth;
+ int mLastRequestedHeight;
+ int mLayer;
+ int mAnimLayer;
+ int mLastLayer;
+ boolean mHaveFrame;
+ boolean mObscured;
+ boolean mTurnOnScreen;
+
+ int mLayoutSeq = -1;
+
+ Configuration mConfiguration = null;
+
+ // Actual frame shown on-screen (may be modified by animation)
+ final Rect mShownFrame = new Rect();
+ final Rect mLastShownFrame = new Rect();
+
+ /**
+ * Set when we have changed the size of the surface, to know that
+ * we must tell them application to resize (and thus redraw itself).
+ */
+ boolean mSurfaceResized;
+
+ /**
+ * Insets that determine the actually visible area
+ */
+ final Rect mVisibleInsets = new Rect();
+ final Rect mLastVisibleInsets = new Rect();
+ boolean mVisibleInsetsChanged;
+
+ /**
+ * Insets that are covered by system windows
+ */
+ final Rect mContentInsets = new Rect();
+ final Rect mLastContentInsets = new Rect();
+ boolean mContentInsetsChanged;
+
+ /**
+ * Set to true if we are waiting for this window to receive its
+ * given internal insets before laying out other windows based on it.
+ */
+ boolean mGivenInsetsPending;
+
+ /**
+ * These are the content insets that were given during layout for
+ * this window, to be applied to windows behind it.
+ */
+ final Rect mGivenContentInsets = new Rect();
+
+ /**
+ * These are the visible insets that were given during layout for
+ * this window, to be applied to windows behind it.
+ */
+ final Rect mGivenVisibleInsets = new Rect();
+
+ /**
+ * This is the given touchable area relative to the window frame, or null if none.
+ */
+ final Region mGivenTouchableRegion = new Region();
+
+ /**
+ * Flag indicating whether the touchable region should be adjusted by
+ * the visible insets; if false the area outside the visible insets is
+ * NOT touchable, so we must use those to adjust the frame during hit
+ * tests.
+ */
+ int mTouchableInsets = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
+
+ // Current transformation being applied.
+ boolean mHaveMatrix;
+ float mDsDx=1, mDtDx=0, mDsDy=0, mDtDy=1;
+ float mLastDsDx=1, mLastDtDx=0, mLastDsDy=0, mLastDtDy=1;
+ float mHScale=1, mVScale=1;
+ float mLastHScale=1, mLastVScale=1;
+ final Matrix mTmpMatrix = new Matrix();
+
+ // "Real" frame that the application sees.
+ final Rect mFrame = new Rect();
+ final Rect mLastFrame = new Rect();
+
+ final Rect mContainingFrame = new Rect();
+ final Rect mDisplayFrame = new Rect();
+ final Rect mContentFrame = new Rect();
+ final Rect mParentFrame = new Rect();
+ final Rect mVisibleFrame = new Rect();
+
+ boolean mContentChanged;
+
+ float mShownAlpha = 1;
+ float mAlpha = 1;
+ float mLastAlpha = 1;
+
+ // Set to true if, when the window gets displayed, it should perform
+ // an enter animation.
+ boolean mEnterAnimationPending;
+
+ // Currently running animation.
+ boolean mAnimating;
+ boolean mLocalAnimating;
+ Animation mAnimation;
+ boolean mAnimationIsEntrance;
+ boolean mHasTransformation;
+ boolean mHasLocalTransformation;
+ final Transformation mTransformation = new Transformation();
+
+ // If a window showing a wallpaper: the requested offset for the
+ // wallpaper; if a wallpaper window: the currently applied offset.
+ float mWallpaperX = -1;
+ float mWallpaperY = -1;
+
+ // If a window showing a wallpaper: what fraction of the offset
+ // range corresponds to a full virtual screen.
+ float mWallpaperXStep = -1;
+ float mWallpaperYStep = -1;
+
+ // Wallpaper windows: pixels offset based on above variables.
+ int mXOffset;
+ int mYOffset;
+
+ // This is set after IWindowSession.relayout() has been called at
+ // least once for the window. It allows us to detect the situation
+ // where we don't yet have a surface, but should have one soon, so
+ // we can give the window focus before waiting for the relayout.
+ boolean mRelayoutCalled;
+
+ // This is set after the Surface has been created but before the
+ // window has been drawn. During this time the surface is hidden.
+ boolean mDrawPending;
+
+ // This is set after the window has finished drawing for the first
+ // time but before its surface is shown. The surface will be
+ // displayed when the next layout is run.
+ boolean mCommitDrawPending;
+
+ // This is set during the time after the window's drawing has been
+ // committed, and before its surface is actually shown. It is used
+ // to delay showing the surface until all windows in a token are ready
+ // to be shown.
+ boolean mReadyToShow;
+
+ // Set when the window has been shown in the screen the first time.
+ boolean mHasDrawn;
+
+ // Currently running an exit animation?
+ boolean mExiting;
+
+ // Currently on the mDestroySurface list?
+ boolean mDestroying;
+
+ // Completely remove from window manager after exit animation?
+ boolean mRemoveOnExit;
+
+ // Set when the orientation is changing and this window has not yet
+ // been updated for the new orientation.
+ boolean mOrientationChanging;
+
+ // Is this window now (or just being) removed?
+ boolean mRemoved;
+
+ // Temp for keeping track of windows that have been removed when
+ // rebuilding window list.
+ boolean mRebuilding;
+
+ // For debugging, this is the last information given to the surface flinger.
+ boolean mSurfaceShown;
+ int mSurfaceX, mSurfaceY, mSurfaceW, mSurfaceH;
+ int mSurfaceLayer;
+ float mSurfaceAlpha;
+
+ // Input channel and input window handle used by the input dispatcher.
+ InputWindowHandle mInputWindowHandle;
+ InputChannel mInputChannel;
+
+ // Used to improve performance of toString()
+ String mStringNameCache;
+ CharSequence mLastTitle;
+ boolean mWasPaused;
+
+ WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
+ WindowState attachedWindow, WindowManager.LayoutParams a,
+ int viewVisibility) {
+ mService = service;
+ mSession = s;
+ mClient = c;
+ mToken = token;
+ mAttrs.copyFrom(a);
+ mViewVisibility = viewVisibility;
+ DeathRecipient deathRecipient = new DeathRecipient();
+ mAlpha = a.alpha;
+ if (WindowManagerService.localLOGV) Slog.v(
+ WindowManagerService.TAG, "Window " + this + " client=" + c.asBinder()
+ + " token=" + token + " (" + mAttrs.token + ")");
+ try {
+ c.asBinder().linkToDeath(deathRecipient, 0);
+ } catch (RemoteException e) {
+ mDeathRecipient = null;
+ mAttachedWindow = null;
+ mLayoutAttached = false;
+ mIsImWindow = false;
+ mIsWallpaper = false;
+ mIsFloatingLayer = false;
+ mBaseLayer = 0;
+ mSubLayer = 0;
+ return;
+ }
+ mDeathRecipient = deathRecipient;
+
+ if ((mAttrs.type >= FIRST_SUB_WINDOW &&
+ mAttrs.type <= LAST_SUB_WINDOW)) {
+ // The multiplier here is to reserve space for multiple
+ // windows in the same type layer.
+ mBaseLayer = mService.mPolicy.windowTypeToLayerLw(
+ attachedWindow.mAttrs.type) * WindowManagerService.TYPE_LAYER_MULTIPLIER
+ + WindowManagerService.TYPE_LAYER_OFFSET;
+ mSubLayer = mService.mPolicy.subWindowTypeToLayerLw(a.type);
+ mAttachedWindow = attachedWindow;
+ if (WindowManagerService.DEBUG_ADD_REMOVE) Slog.v(WindowManagerService.TAG, "Adding " + this + " to " + mAttachedWindow);
+ mAttachedWindow.mChildWindows.add(this);
+ mLayoutAttached = mAttrs.type !=
+ WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
+ mIsImWindow = attachedWindow.mAttrs.type == TYPE_INPUT_METHOD
+ || attachedWindow.mAttrs.type == TYPE_INPUT_METHOD_DIALOG;
+ mIsWallpaper = attachedWindow.mAttrs.type == TYPE_WALLPAPER;
+ mIsFloatingLayer = mIsImWindow || mIsWallpaper;
+ } else {
+ // The multiplier here is to reserve space for multiple
+ // windows in the same type layer.
+ mBaseLayer = mService.mPolicy.windowTypeToLayerLw(a.type)
+ * WindowManagerService.TYPE_LAYER_MULTIPLIER
+ + WindowManagerService.TYPE_LAYER_OFFSET;
+ mSubLayer = 0;
+ mAttachedWindow = null;
+ mLayoutAttached = false;
+ mIsImWindow = mAttrs.type == TYPE_INPUT_METHOD
+ || mAttrs.type == TYPE_INPUT_METHOD_DIALOG;
+ mIsWallpaper = mAttrs.type == TYPE_WALLPAPER;
+ mIsFloatingLayer = mIsImWindow || mIsWallpaper;
+ }
+
+ WindowState appWin = this;
+ while (appWin.mAttachedWindow != null) {
+ appWin = mAttachedWindow;
+ }
+ WindowToken appToken = appWin.mToken;
+ while (appToken.appWindowToken == null) {
+ WindowToken parent = mService.mTokenMap.get(appToken.token);
+ if (parent == null || appToken == parent) {
+ break;
+ }
+ appToken = parent;
+ }
+ mRootToken = appToken;
+ mAppToken = appToken.appWindowToken;
+
+ mSurface = null;
+ mRequestedWidth = 0;
+ mRequestedHeight = 0;
+ mLastRequestedWidth = 0;
+ mLastRequestedHeight = 0;
+ mXOffset = 0;
+ mYOffset = 0;
+ mLayer = 0;
+ mAnimLayer = 0;
+ mLastLayer = 0;
+ mInputWindowHandle = new InputWindowHandle(
+ mAppToken != null ? mAppToken.mInputApplicationHandle : null, this);
+ }
+
+ void attach() {
+ if (WindowManagerService.localLOGV) Slog.v(
+ WindowManagerService.TAG, "Attaching " + this + " token=" + mToken
+ + ", list=" + mToken.windows);
+ mSession.windowAddedLocked();
+ }
+
+ public void computeFrameLw(Rect pf, Rect df, Rect cf, Rect vf) {
+ mHaveFrame = true;
+
+ final Rect container = mContainingFrame;
+ container.set(pf);
+
+ final Rect display = mDisplayFrame;
+ display.set(df);
+
+ if ((mAttrs.flags & FLAG_COMPATIBLE_WINDOW) != 0) {
+ container.intersect(mService.mCompatibleScreenFrame);
+ if ((mAttrs.flags & FLAG_LAYOUT_NO_LIMITS) == 0) {
+ display.intersect(mService.mCompatibleScreenFrame);
+ }
+ }
+
+ final int pw = container.right - container.left;
+ final int ph = container.bottom - container.top;
+
+ int w,h;
+ if ((mAttrs.flags & mAttrs.FLAG_SCALED) != 0) {
+ w = mAttrs.width < 0 ? pw : mAttrs.width;
+ h = mAttrs.height< 0 ? ph : mAttrs.height;
+ } else {
+ w = mAttrs.width == mAttrs.MATCH_PARENT ? pw : mRequestedWidth;
+ h = mAttrs.height== mAttrs.MATCH_PARENT ? ph : mRequestedHeight;
+ }
+
+ if (!mParentFrame.equals(pf)) {
+ //Slog.i(TAG, "Window " + this + " content frame from " + mParentFrame
+ // + " to " + pf);
+ mParentFrame.set(pf);
+ mContentChanged = true;
+ }
+
+ final Rect content = mContentFrame;
+ content.set(cf);
+
+ final Rect visible = mVisibleFrame;
+ visible.set(vf);
+
+ final Rect frame = mFrame;
+ final int fw = frame.width();
+ final int fh = frame.height();
+
+ //System.out.println("In: w=" + w + " h=" + h + " container=" +
+ // container + " x=" + mAttrs.x + " y=" + mAttrs.y);
+
+ Gravity.apply(mAttrs.gravity, w, h, container,
+ (int) (mAttrs.x + mAttrs.horizontalMargin * pw),
+ (int) (mAttrs.y + mAttrs.verticalMargin * ph), frame);
+
+ //System.out.println("Out: " + mFrame);
+
+ // Now make sure the window fits in the overall display.
+ Gravity.applyDisplay(mAttrs.gravity, df, frame);
+
+ // Make sure the content and visible frames are inside of the
+ // final window frame.
+ if (content.left < frame.left) content.left = frame.left;
+ if (content.top < frame.top) content.top = frame.top;
+ if (content.right > frame.right) content.right = frame.right;
+ if (content.bottom > frame.bottom) content.bottom = frame.bottom;
+ if (visible.left < frame.left) visible.left = frame.left;
+ if (visible.top < frame.top) visible.top = frame.top;
+ if (visible.right > frame.right) visible.right = frame.right;
+ if (visible.bottom > frame.bottom) visible.bottom = frame.bottom;
+
+ final Rect contentInsets = mContentInsets;
+ contentInsets.left = content.left-frame.left;
+ contentInsets.top = content.top-frame.top;
+ contentInsets.right = frame.right-content.right;
+ contentInsets.bottom = frame.bottom-content.bottom;
+
+ final Rect visibleInsets = mVisibleInsets;
+ visibleInsets.left = visible.left-frame.left;
+ visibleInsets.top = visible.top-frame.top;
+ visibleInsets.right = frame.right-visible.right;
+ visibleInsets.bottom = frame.bottom-visible.bottom;
+
+ if (mIsWallpaper && (fw != frame.width() || fh != frame.height())) {
+ mService.updateWallpaperOffsetLocked(this, mService.mDisplay.getWidth(),
+ mService.mDisplay.getHeight(), false);
+ }
+
+ if (WindowManagerService.localLOGV) {
+ //if ("com.google.android.youtube".equals(mAttrs.packageName)
+ // && mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) {
+ Slog.v(WindowManagerService.TAG, "Resolving (mRequestedWidth="
+ + mRequestedWidth + ", mRequestedheight="
+ + mRequestedHeight + ") to" + " (pw=" + pw + ", ph=" + ph
+ + "): frame=" + mFrame.toShortString()
+ + " ci=" + contentInsets.toShortString()
+ + " vi=" + visibleInsets.toShortString());
+ //}
+ }
+ }
+
+ public Rect getFrameLw() {
+ return mFrame;
+ }
+
+ public Rect getShownFrameLw() {
+ return mShownFrame;
+ }
+
+ public Rect getDisplayFrameLw() {
+ return mDisplayFrame;
+ }
+
+ public Rect getContentFrameLw() {
+ return mContentFrame;
+ }
+
+ public Rect getVisibleFrameLw() {
+ return mVisibleFrame;
+ }
+
+ public boolean getGivenInsetsPendingLw() {
+ return mGivenInsetsPending;
+ }
+
+ public Rect getGivenContentInsetsLw() {
+ return mGivenContentInsets;
+ }
+
+ public Rect getGivenVisibleInsetsLw() {
+ return mGivenVisibleInsets;
+ }
+
+ public WindowManager.LayoutParams getAttrs() {
+ return mAttrs;
+ }
+
+ public int getSurfaceLayer() {
+ return mLayer;
+ }
+
+ public IApplicationToken getAppToken() {
+ return mAppToken != null ? mAppToken.appToken : null;
+ }
+
+ public long getInputDispatchingTimeoutNanos() {
+ return mAppToken != null
+ ? mAppToken.inputDispatchingTimeoutNanos
+ : WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
+ }
+
+ public boolean hasAppShownWindows() {
+ return mAppToken != null ? mAppToken.firstWindowDrawn : false;
+ }
+
+ public void setAnimation(Animation anim) {
+ if (WindowManagerService.localLOGV) Slog.v(
+ WindowManagerService.TAG, "Setting animation in " + this + ": " + anim);
+ mAnimating = false;
+ mLocalAnimating = false;
+ mAnimation = anim;
+ mAnimation.restrictDuration(WindowManagerService.MAX_ANIMATION_DURATION);
+ mAnimation.scaleCurrentDuration(mService.mWindowAnimationScale);
+ }
+
+ public void clearAnimation() {
+ if (mAnimation != null) {
+ mAnimating = true;
+ mLocalAnimating = false;
+ mAnimation.cancel();
+ mAnimation = null;
+ }
+ }
+
+ Surface createSurfaceLocked() {
+ if (mSurface == null) {
+ mReportDestroySurface = false;
+ mSurfacePendingDestroy = false;
+ mDrawPending = true;
+ mCommitDrawPending = false;
+ mReadyToShow = false;
+ if (mAppToken != null) {
+ mAppToken.allDrawn = false;
+ }
+
+ int flags = 0;
+
+ if ((mAttrs.flags&WindowManager.LayoutParams.FLAG_SECURE) != 0) {
+ flags |= Surface.SECURE;
+ }
+ if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(
+ WindowManagerService.TAG, "Creating surface in session "
+ + mSession.mSurfaceSession + " window " + this
+ + " w=" + mFrame.width()
+ + " h=" + mFrame.height() + " format="
+ + mAttrs.format + " flags=" + flags);
+
+ int w = mFrame.width();
+ int h = mFrame.height();
+ if ((mAttrs.flags & LayoutParams.FLAG_SCALED) != 0) {
+ // for a scaled surface, we always want the requested
+ // size.
+ w = mRequestedWidth;
+ h = mRequestedHeight;
+ }
+
+ // Something is wrong and SurfaceFlinger will not like this,
+ // try to revert to sane values
+ if (w <= 0) w = 1;
+ if (h <= 0) h = 1;
+
+ mSurfaceShown = false;
+ mSurfaceLayer = 0;
+ mSurfaceAlpha = 1;
+ mSurfaceX = 0;
+ mSurfaceY = 0;
+ mSurfaceW = w;
+ mSurfaceH = h;
+ try {
+ final boolean isHwAccelerated = (mAttrs.flags &
+ WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0;
+ final int format = isHwAccelerated ? PixelFormat.TRANSLUCENT : mAttrs.format;
+ if (isHwAccelerated && mAttrs.format == PixelFormat.OPAQUE) {
+ flags |= Surface.OPAQUE;
+ }
+ mSurface = new Surface(
+ mSession.mSurfaceSession, mSession.mPid,
+ mAttrs.getTitle().toString(),
+ 0, w, h, format, flags);
+ if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, " CREATE SURFACE "
+ + mSurface + " IN SESSION "
+ + mSession.mSurfaceSession
+ + ": pid=" + mSession.mPid + " format="
+ + mAttrs.format + " flags=0x"
+ + Integer.toHexString(flags)
+ + " / " + this);
+ } catch (Surface.OutOfResourcesException e) {
+ Slog.w(WindowManagerService.TAG, "OutOfResourcesException creating surface");
+ mService.reclaimSomeSurfaceMemoryLocked(this, "create");
+ return null;
+ } catch (Exception e) {
+ Slog.e(WindowManagerService.TAG, "Exception creating surface", e);
+ return null;
+ }
+
+ if (WindowManagerService.localLOGV) Slog.v(
+ WindowManagerService.TAG, "Got surface: " + mSurface
+ + ", set left=" + mFrame.left + " top=" + mFrame.top
+ + ", animLayer=" + mAnimLayer);
+ if (WindowManagerService.SHOW_TRANSACTIONS) {
+ Slog.i(WindowManagerService.TAG, ">>> OPEN TRANSACTION createSurfaceLocked");
+ WindowManagerService.logSurface(this, "CREATE pos=(" + mFrame.left + "," + mFrame.top + ") (" +
+ mFrame.width() + "x" + mFrame.height() + "), layer=" +
+ mAnimLayer + " HIDE", null);
+ }
+ Surface.openTransaction();
+ try {
+ try {
+ mSurfaceX = mFrame.left + mXOffset;
+ mSurfaceY = mFrame.top + mYOffset;
+ mSurface.setPosition(mSurfaceX, mSurfaceY);
+ mSurfaceLayer = mAnimLayer;
+ mSurface.setLayer(mAnimLayer);
+ mSurfaceShown = false;
+ mSurface.hide();
+ if ((mAttrs.flags&WindowManager.LayoutParams.FLAG_DITHER) != 0) {
+ if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(this, "DITHER", null);
+ mSurface.setFlags(Surface.SURFACE_DITHER,
+ Surface.SURFACE_DITHER);
+ }
+ } catch (RuntimeException e) {
+ Slog.w(WindowManagerService.TAG, "Error creating surface in " + w, e);
+ mService.reclaimSomeSurfaceMemoryLocked(this, "create-init");
+ }
+ mLastHidden = true;
+ } finally {
+ Surface.closeTransaction();
+ if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, "<<< CLOSE TRANSACTION createSurfaceLocked");
+ }
+ if (WindowManagerService.localLOGV) Slog.v(
+ WindowManagerService.TAG, "Created surface " + this);
+ }
+ return mSurface;
+ }
+
+ void destroySurfaceLocked() {
+ if (mAppToken != null && this == mAppToken.startingWindow) {
+ mAppToken.startingDisplayed = false;
+ }
+
+ if (mSurface != null) {
+ mDrawPending = false;
+ mCommitDrawPending = false;
+ mReadyToShow = false;
+
+ int i = mChildWindows.size();
+ while (i > 0) {
+ i--;
+ WindowState c = mChildWindows.get(i);
+ c.mAttachedHidden = true;
+ }
+
+ if (mReportDestroySurface) {
+ mReportDestroySurface = false;
+ mSurfacePendingDestroy = true;
+ try {
+ mClient.dispatchGetNewSurface();
+ // We'll really destroy on the next time around.
+ return;
+ } catch (RemoteException e) {
+ }
+ }
+
+ try {
+ if (WindowManagerService.DEBUG_VISIBILITY) {
+ RuntimeException e = null;
+ if (!WindowManagerService.HIDE_STACK_CRAWLS) {
+ e = new RuntimeException();
+ e.fillInStackTrace();
+ }
+ Slog.w(WindowManagerService.TAG, "Window " + this + " destroying surface "
+ + mSurface + ", session " + mSession, e);
+ }
+ if (WindowManagerService.SHOW_TRANSACTIONS) {
+ RuntimeException e = null;
+ if (!WindowManagerService.HIDE_STACK_CRAWLS) {
+ e = new RuntimeException();
+ e.fillInStackTrace();
+ }
+ if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(this, "DESTROY", e);
+ }
+ mSurface.destroy();
+ } catch (RuntimeException e) {
+ Slog.w(WindowManagerService.TAG, "Exception thrown when destroying Window " + this
+ + " surface " + mSurface + " session " + mSession
+ + ": " + e.toString());
+ }
+
+ mSurfaceShown = false;
+ mSurface = null;
+ }
+ }
+
+ boolean finishDrawingLocked() {
+ if (mDrawPending) {
+ if (WindowManagerService.SHOW_TRANSACTIONS || WindowManagerService.DEBUG_ORIENTATION) Slog.v(
+ WindowManagerService.TAG, "finishDrawingLocked: " + mSurface);
+ mCommitDrawPending = true;
+ mDrawPending = false;
+ return true;
+ }
+ return false;
+ }
+
+ // This must be called while inside a transaction.
+ boolean commitFinishDrawingLocked(long currentTime) {
+ //Slog.i(TAG, "commitFinishDrawingLocked: " + mSurface);
+ if (!mCommitDrawPending) {
+ return false;
+ }
+ mCommitDrawPending = false;
+ mReadyToShow = true;
+ final boolean starting = mAttrs.type == TYPE_APPLICATION_STARTING;
+ final AppWindowToken atoken = mAppToken;
+ if (atoken == null || atoken.allDrawn || starting) {
+ performShowLocked();
+ }
+ return true;
+ }
+
+ // This must be called while inside a transaction.
+ boolean performShowLocked() {
+ if (WindowManagerService.DEBUG_VISIBILITY) {
+ RuntimeException e = null;
+ if (!WindowManagerService.HIDE_STACK_CRAWLS) {
+ e = new RuntimeException();
+ e.fillInStackTrace();
+ }
+ Slog.v(WindowManagerService.TAG, "performShow on " + this
+ + ": readyToShow=" + mReadyToShow + " readyForDisplay=" + isReadyForDisplay()
+ + " starting=" + (mAttrs.type == TYPE_APPLICATION_STARTING), e);
+ }
+ if (mReadyToShow && isReadyForDisplay()) {
+ if (WindowManagerService.SHOW_TRANSACTIONS || WindowManagerService.DEBUG_ORIENTATION) WindowManagerService.logSurface(this,
+ "SHOW (performShowLocked)", null);
+ if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(WindowManagerService.TAG, "Showing " + this
+ + " during animation: policyVis=" + mPolicyVisibility
+ + " attHidden=" + mAttachedHidden
+ + " tok.hiddenRequested="
+ + (mAppToken != null ? mAppToken.hiddenRequested : false)
+ + " tok.hidden="
+ + (mAppToken != null ? mAppToken.hidden : false)
+ + " animating=" + mAnimating
+ + " tok animating="
+ + (mAppToken != null ? mAppToken.animating : false));
+ if (!mService.showSurfaceRobustlyLocked(this)) {
+ return false;
+ }
+ mLastAlpha = -1;
+ mHasDrawn = true;
+ mLastHidden = false;
+ mReadyToShow = false;
+ mService.enableScreenIfNeededLocked();
+
+ mService.applyEnterAnimationLocked(this);
+
+ int i = mChildWindows.size();
+ while (i > 0) {
+ i--;
+ WindowState c = mChildWindows.get(i);
+ if (c.mAttachedHidden) {
+ c.mAttachedHidden = false;
+ if (c.mSurface != null) {
+ c.performShowLocked();
+ // It hadn't been shown, which means layout not
+ // performed on it, so now we want to make sure to
+ // do a layout. If called from within the transaction
+ // loop, this will cause it to restart with a new
+ // layout.
+ mService.mLayoutNeeded = true;
+ }
+ }
+ }
+
+ if (mAttrs.type != TYPE_APPLICATION_STARTING
+ && mAppToken != null) {
+ mAppToken.firstWindowDrawn = true;
+
+ if (mAppToken.startingData != null) {
+ if (WindowManagerService.DEBUG_STARTING_WINDOW || WindowManagerService.DEBUG_ANIM) Slog.v(WindowManagerService.TAG,
+ "Finish starting " + mToken
+ + ": first real window is shown, no animation");
+ // If this initial window is animating, stop it -- we
+ // will do an animation to reveal it from behind the
+ // starting window, so there is no need for it to also
+ // be doing its own stuff.
+ if (mAnimation != null) {
+ mAnimation.cancel();
+ mAnimation = null;
+ // Make sure we clean up the animation.
+ mAnimating = true;
+ }
+ mService.mFinishedStarting.add(mAppToken);
+ mService.mH.sendEmptyMessage(H.FINISHED_STARTING);
+ }
+ mAppToken.updateReportedVisibilityLocked();
+ }
+ }
+ return true;
+ }
+
+ // This must be called while inside a transaction. Returns true if
+ // there is more animation to run.
+ boolean stepAnimationLocked(long currentTime, int dw, int dh) {
+ if (!mService.mDisplayFrozen && mService.mPolicy.isScreenOn()) {
+ // We will run animations as long as the display isn't frozen.
+
+ if (!mDrawPending && !mCommitDrawPending && mAnimation != null) {
+ mHasTransformation = true;
+ mHasLocalTransformation = true;
+ if (!mLocalAnimating) {
+ if (WindowManagerService.DEBUG_ANIM) Slog.v(
+ WindowManagerService.TAG, "Starting animation in " + this +
+ " @ " + currentTime + ": ww=" + mFrame.width() + " wh=" + mFrame.height() +
+ " dw=" + dw + " dh=" + dh + " scale=" + mService.mWindowAnimationScale);
+ mAnimation.initialize(mFrame.width(), mFrame.height(), dw, dh);
+ mAnimation.setStartTime(currentTime);
+ mLocalAnimating = true;
+ mAnimating = true;
+ }
+ mTransformation.clear();
+ final boolean more = mAnimation.getTransformation(
+ currentTime, mTransformation);
+ if (WindowManagerService.DEBUG_ANIM) Slog.v(
+ WindowManagerService.TAG, "Stepped animation in " + this +
+ ": more=" + more + ", xform=" + mTransformation);
+ if (more) {
+ // we're not done!
+ return true;
+ }
+ if (WindowManagerService.DEBUG_ANIM) Slog.v(
+ WindowManagerService.TAG, "Finished animation in " + this +
+ " @ " + currentTime);
+
+ if (mAnimation != null) {
+ mAnimation.cancel();
+ mAnimation = null;
+ }
+ //WindowManagerService.this.dump();
+ }
+ mHasLocalTransformation = false;
+ if ((!mLocalAnimating || mAnimationIsEntrance) && mAppToken != null
+ && mAppToken.animation != null) {
+ // When our app token is animating, we kind-of pretend like
+ // we are as well. Note the mLocalAnimating mAnimationIsEntrance
+ // part of this check means that we will only do this if
+ // our window is not currently exiting, or it is not
+ // locally animating itself. The idea being that one that
+ // is exiting and doing a local animation should be removed
+ // once that animation is done.
+ mAnimating = true;
+ mHasTransformation = true;
+ mTransformation.clear();
+ return false;
+ } else if (mHasTransformation) {
+ // Little trick to get through the path below to act like
+ // we have finished an animation.
+ mAnimating = true;
+ } else if (isAnimating()) {
+ mAnimating = true;
+ }
+ } else if (mAnimation != null) {
+ // If the display is frozen, and there is a pending animation,
+ // clear it and make sure we run the cleanup code.
+ mAnimating = true;
+ mLocalAnimating = true;
+ mAnimation.cancel();
+ mAnimation = null;
+ }
+
+ if (!mAnimating && !mLocalAnimating) {
+ return false;
+ }
+
+ if (WindowManagerService.DEBUG_ANIM) Slog.v(
+ WindowManagerService.TAG, "Animation done in " + this + ": exiting=" + mExiting
+ + ", reportedVisible="
+ + (mAppToken != null ? mAppToken.reportedVisible : false));
+
+ mAnimating = false;
+ mLocalAnimating = false;
+ if (mAnimation != null) {
+ mAnimation.cancel();
+ mAnimation = null;
+ }
+ mAnimLayer = mLayer;
+ if (mIsImWindow) {
+ mAnimLayer += mService.mInputMethodAnimLayerAdjustment;
+ } else if (mIsWallpaper) {
+ mAnimLayer += mService.mWallpaperAnimLayerAdjustment;
+ }
+ if (WindowManagerService.DEBUG_LAYERS) Slog.v(WindowManagerService.TAG, "Stepping win " + this
+ + " anim layer: " + mAnimLayer);
+ mHasTransformation = false;
+ mHasLocalTransformation = false;
+ if (mPolicyVisibility != mPolicyVisibilityAfterAnim) {
+ if (WindowManagerService.DEBUG_VISIBILITY) {
+ Slog.v(WindowManagerService.TAG, "Policy visibility changing after anim in " + this + ": "
+ + mPolicyVisibilityAfterAnim);
+ }
+ mPolicyVisibility = mPolicyVisibilityAfterAnim;
+ if (!mPolicyVisibility) {
+ if (mService.mCurrentFocus == this) {
+ mService.mFocusMayChange = true;
+ }
+ // Window is no longer visible -- make sure if we were waiting
+ // for it to be displayed before enabling the display, that
+ // we allow the display to be enabled now.
+ mService.enableScreenIfNeededLocked();
+ }
+ }
+ mTransformation.clear();
+ if (mHasDrawn
+ && mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING
+ && mAppToken != null
+ && mAppToken.firstWindowDrawn
+ && mAppToken.startingData != null) {
+ if (WindowManagerService.DEBUG_STARTING_WINDOW) Slog.v(WindowManagerService.TAG, "Finish starting "
+ + mToken + ": first real window done animating");
+ mService.mFinishedStarting.add(mAppToken);
+ mService.mH.sendEmptyMessage(H.FINISHED_STARTING);
+ }
+
+ finishExit();
+
+ if (mAppToken != null) {
+ mAppToken.updateReportedVisibilityLocked();
+ }
+
+ return false;
+ }
+
+ void finishExit() {
+ if (WindowManagerService.DEBUG_ANIM) Slog.v(
+ WindowManagerService.TAG, "finishExit in " + this
+ + ": exiting=" + mExiting
+ + " remove=" + mRemoveOnExit
+ + " windowAnimating=" + isWindowAnimating());
+
+ final int N = mChildWindows.size();
+ for (int i=0; i<N; i++) {
+ mChildWindows.get(i).finishExit();
+ }
+
+ if (!mExiting) {
+ return;
+ }
+
+ if (isWindowAnimating()) {
+ return;
+ }
+
+ if (WindowManagerService.localLOGV) Slog.v(
+ WindowManagerService.TAG, "Exit animation finished in " + this
+ + ": remove=" + mRemoveOnExit);
+ if (mSurface != null) {
+ mService.mDestroySurface.add(this);
+ mDestroying = true;
+ if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(this, "HIDE (finishExit)", null);
+ mSurfaceShown = false;
+ try {
+ mSurface.hide();
+ } catch (RuntimeException e) {
+ Slog.w(WindowManagerService.TAG, "Error hiding surface in " + this, e);
+ }
+ mLastHidden = true;
+ }
+ mExiting = false;
+ if (mRemoveOnExit) {
+ mService.mPendingRemove.add(this);
+ mRemoveOnExit = false;
+ }
+ }
+
+ boolean isIdentityMatrix(float dsdx, float dtdx, float dsdy, float dtdy) {
+ if (dsdx < .99999f || dsdx > 1.00001f) return false;
+ if (dtdy < .99999f || dtdy > 1.00001f) return false;
+ if (dtdx < -.000001f || dtdx > .000001f) return false;
+ if (dsdy < -.000001f || dsdy > .000001f) return false;
+ return true;
+ }
+
+ void computeShownFrameLocked() {
+ final boolean selfTransformation = mHasLocalTransformation;
+ Transformation attachedTransformation =
+ (mAttachedWindow != null && mAttachedWindow.mHasLocalTransformation)
+ ? mAttachedWindow.mTransformation : null;
+ Transformation appTransformation =
+ (mAppToken != null && mAppToken.hasTransformation)
+ ? mAppToken.transformation : null;
+
+ // Wallpapers are animated based on the "real" window they
+ // are currently targeting.
+ if (mAttrs.type == TYPE_WALLPAPER && mService.mLowerWallpaperTarget == null
+ && mService.mWallpaperTarget != null) {
+ if (mService.mWallpaperTarget.mHasLocalTransformation &&
+ mService.mWallpaperTarget.mAnimation != null &&
+ !mService.mWallpaperTarget.mAnimation.getDetachWallpaper()) {
+ attachedTransformation = mService.mWallpaperTarget.mTransformation;
+ if (WindowManagerService.DEBUG_WALLPAPER && attachedTransformation != null) {
+ Slog.v(WindowManagerService.TAG, "WP target attached xform: " + attachedTransformation);
+ }
+ }
+ if (mService.mWallpaperTarget.mAppToken != null &&
+ mService.mWallpaperTarget.mAppToken.hasTransformation &&
+ mService.mWallpaperTarget.mAppToken.animation != null &&
+ !mService.mWallpaperTarget.mAppToken.animation.getDetachWallpaper()) {
+ appTransformation = mService.mWallpaperTarget.mAppToken.transformation;
+ if (WindowManagerService.DEBUG_WALLPAPER && appTransformation != null) {
+ Slog.v(WindowManagerService.TAG, "WP target app xform: " + appTransformation);
+ }
+ }
+ }
+
+ final boolean screenAnimation = mService.mScreenRotationAnimation != null
+ && mService.mScreenRotationAnimation.isAnimating();
+ if (selfTransformation || attachedTransformation != null
+ || appTransformation != null || screenAnimation) {
+ // cache often used attributes locally
+ final Rect frame = mFrame;
+ final float tmpFloats[] = mService.mTmpFloats;
+ final Matrix tmpMatrix = mTmpMatrix;
+
+ // Compute the desired transformation.
+ tmpMatrix.setTranslate(0, 0);
+ if (selfTransformation) {
+ tmpMatrix.postConcat(mTransformation.getMatrix());
+ }
+ tmpMatrix.postTranslate(frame.left + mXOffset, frame.top + mYOffset);
+ if (attachedTransformation != null) {
+ tmpMatrix.postConcat(attachedTransformation.getMatrix());
+ }
+ if (appTransformation != null) {
+ tmpMatrix.postConcat(appTransformation.getMatrix());
+ }
+ if (screenAnimation) {
+ tmpMatrix.postConcat(
+ mService.mScreenRotationAnimation.getEnterTransformation().getMatrix());
+ }
+
+ // "convert" it into SurfaceFlinger's format
+ // (a 2x2 matrix + an offset)
+ // Here we must not transform the position of the surface
+ // since it is already included in the transformation.
+ //Slog.i(TAG, "Transform: " + matrix);
+
+ mHaveMatrix = true;
+ tmpMatrix.getValues(tmpFloats);
+ mDsDx = tmpFloats[Matrix.MSCALE_X];
+ mDtDx = tmpFloats[Matrix.MSKEW_Y];
+ mDsDy = tmpFloats[Matrix.MSKEW_X];
+ mDtDy = tmpFloats[Matrix.MSCALE_Y];
+ int x = (int)tmpFloats[Matrix.MTRANS_X];
+ int y = (int)tmpFloats[Matrix.MTRANS_Y];
+ int w = frame.width();
+ int h = frame.height();
+ mShownFrame.set(x, y, x+w, y+h);
+
+ // Now set the alpha... but because our current hardware
+ // can't do alpha transformation on a non-opaque surface,
+ // turn it off if we are running an animation that is also
+ // transforming since it is more important to have that
+ // animation be smooth.
+ mShownAlpha = mAlpha;
+ if (!mService.mLimitedAlphaCompositing
+ || (!PixelFormat.formatHasAlpha(mAttrs.format)
+ || (isIdentityMatrix(mDsDx, mDtDx, mDsDy, mDtDy)
+ && x == frame.left && y == frame.top))) {
+ //Slog.i(TAG, "Applying alpha transform");
+ if (selfTransformation) {
+ mShownAlpha *= mTransformation.getAlpha();
+ }
+ if (attachedTransformation != null) {
+ mShownAlpha *= attachedTransformation.getAlpha();
+ }
+ if (appTransformation != null) {
+ mShownAlpha *= appTransformation.getAlpha();
+ }
+ if (screenAnimation) {
+ mShownAlpha *=
+ mService.mScreenRotationAnimation.getEnterTransformation().getAlpha();
+ }
+ } else {
+ //Slog.i(TAG, "Not applying alpha transform");
+ }
+
+ if (WindowManagerService.localLOGV) Slog.v(
+ WindowManagerService.TAG, "Continuing animation in " + this +
+ ": " + mShownFrame +
+ ", alpha=" + mTransformation.getAlpha());
+ return;
+ }
+
+ mShownFrame.set(mFrame);
+ if (mXOffset != 0 || mYOffset != 0) {
+ mShownFrame.offset(mXOffset, mYOffset);
+ }
+ mShownAlpha = mAlpha;
+ mHaveMatrix = false;
+ mDsDx = 1;
+ mDtDx = 0;
+ mDsDy = 0;
+ mDtDy = 1;
+ }
+
+ /**
+ * Is this window visible? It is not visible if there is no
+ * surface, or we are in the process of running an exit animation
+ * that will remove the surface, or its app token has been hidden.
+ */
+ public boolean isVisibleLw() {
+ final AppWindowToken atoken = mAppToken;
+ return mSurface != null && mPolicyVisibility && !mAttachedHidden
+ && (atoken == null || !atoken.hiddenRequested)
+ && !mExiting && !mDestroying;
+ }
+
+ /**
+ * Like {@link #isVisibleLw}, but also counts a window that is currently
+ * "hidden" behind the keyguard as visible. This allows us to apply
+ * things like window flags that impact the keyguard.
+ * XXX I am starting to think we need to have ANOTHER visibility flag
+ * for this "hidden behind keyguard" state rather than overloading
+ * mPolicyVisibility. Ungh.
+ */
+ public boolean isVisibleOrBehindKeyguardLw() {
+ final AppWindowToken atoken = mAppToken;
+ return mSurface != null && !mAttachedHidden
+ && (atoken == null ? mPolicyVisibility : !atoken.hiddenRequested)
+ && !mDrawPending && !mCommitDrawPending
+ && !mExiting && !mDestroying;
+ }
+
+ /**
+ * Is this window visible, ignoring its app token? It is not visible
+ * if there is no surface, or we are in the process of running an exit animation
+ * that will remove the surface.
+ */
+ public boolean isWinVisibleLw() {
+ final AppWindowToken atoken = mAppToken;
+ return mSurface != null && mPolicyVisibility && !mAttachedHidden
+ && (atoken == null || !atoken.hiddenRequested || atoken.animating)
+ && !mExiting && !mDestroying;
+ }
+
+ /**
+ * The same as isVisible(), but follows the current hidden state of
+ * the associated app token, not the pending requested hidden state.
+ */
+ boolean isVisibleNow() {
+ return mSurface != null && mPolicyVisibility && !mAttachedHidden
+ && !mRootToken.hidden && !mExiting && !mDestroying;
+ }
+
+ /**
+ * Can this window possibly be a drag/drop target? The test here is
+ * a combination of the above "visible now" with the check that the
+ * Input Manager uses when discarding windows from input consideration.
+ */
+ boolean isPotentialDragTarget() {
+ return isVisibleNow() && (mInputChannel != null) && !mRemoved;
+ }
+
+ /**
+ * Same as isVisible(), but we also count it as visible between the
+ * call to IWindowSession.add() and the first relayout().
+ */
+ boolean isVisibleOrAdding() {
+ final AppWindowToken atoken = mAppToken;
+ return ((mSurface != null && !mReportDestroySurface)
+ || (!mRelayoutCalled && mViewVisibility == View.VISIBLE))
+ && mPolicyVisibility && !mAttachedHidden
+ && (atoken == null || !atoken.hiddenRequested)
+ && !mExiting && !mDestroying;
+ }
+
+ /**
+ * Is this window currently on-screen? It is on-screen either if it
+ * is visible or it is currently running an animation before no longer
+ * being visible.
+ */
+ boolean isOnScreen() {
+ final AppWindowToken atoken = mAppToken;
+ if (atoken != null) {
+ return mSurface != null && mPolicyVisibility && !mDestroying
+ && ((!mAttachedHidden && !atoken.hiddenRequested)
+ || mAnimation != null || atoken.animation != null);
+ } else {
+ return mSurface != null && mPolicyVisibility && !mDestroying
+ && (!mAttachedHidden || mAnimation != null);
+ }
+ }
+
+ /**
+ * Like isOnScreen(), but we don't return true if the window is part
+ * of a transition that has not yet been started.
+ */
+ boolean isReadyForDisplay() {
+ if (mRootToken.waitingToShow &&
+ mService.mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) {
+ return false;
+ }
+ final AppWindowToken atoken = mAppToken;
+ final boolean animating = atoken != null
+ ? (atoken.animation != null) : false;
+ return mSurface != null && mPolicyVisibility && !mDestroying
+ && ((!mAttachedHidden && mViewVisibility == View.VISIBLE
+ && !mRootToken.hidden)
+ || mAnimation != null || animating);
+ }
+
+ /** Is the window or its container currently animating? */
+ boolean isAnimating() {
+ final WindowState attached = mAttachedWindow;
+ final AppWindowToken atoken = mAppToken;
+ return mAnimation != null
+ || (attached != null && attached.mAnimation != null)
+ || (atoken != null &&
+ (atoken.animation != null
+ || atoken.inPendingTransaction));
+ }
+
+ /** Is this window currently animating? */
+ boolean isWindowAnimating() {
+ return mAnimation != null;
+ }
+
+ /**
+ * Like isOnScreen, but returns false if the surface hasn't yet
+ * been drawn.
+ */
+ public boolean isDisplayedLw() {
+ final AppWindowToken atoken = mAppToken;
+ return mSurface != null && mPolicyVisibility && !mDestroying
+ && !mDrawPending && !mCommitDrawPending
+ && ((!mAttachedHidden &&
+ (atoken == null || !atoken.hiddenRequested))
+ || mAnimating);
+ }
+
+ /**
+ * Returns true if the window has a surface that it has drawn a
+ * complete UI in to.
+ */
+ public boolean isDrawnLw() {
+ final AppWindowToken atoken = mAppToken;
+ return mSurface != null && !mDestroying
+ && !mDrawPending && !mCommitDrawPending;
+ }
+
+ /**
+ * Return true if the window is opaque and fully drawn. This indicates
+ * it may obscure windows behind it.
+ */
+ boolean isOpaqueDrawn() {
+ return (mAttrs.format == PixelFormat.OPAQUE
+ || mAttrs.type == TYPE_WALLPAPER)
+ && mSurface != null && mAnimation == null
+ && (mAppToken == null || mAppToken.animation == null)
+ && !mDrawPending && !mCommitDrawPending;
+ }
+
+ /**
+ * Return whether this window is wanting to have a translation
+ * animation applied to it for an in-progress move. (Only makes
+ * sense to call from performLayoutAndPlaceSurfacesLockedInner().)
+ */
+ boolean shouldAnimateMove() {
+ return mContentChanged && !mExiting && !mLastHidden && !mService.mDisplayFrozen
+ && (mFrame.top != mLastFrame.top
+ || mFrame.left != mLastFrame.left)
+ && (mAttachedWindow == null || !mAttachedWindow.shouldAnimateMove())
+ && mService.mPolicy.isScreenOn();
+ }
+
+ boolean needsBackgroundFiller(int screenWidth, int screenHeight) {
+ return
+ // only if the application is requesting compatible window
+ (mAttrs.flags & FLAG_COMPATIBLE_WINDOW) != 0 &&
+ // only if it's visible
+ mHasDrawn && mViewVisibility == View.VISIBLE &&
+ // and only if the application fills the compatible screen
+ mFrame.left <= mService.mCompatibleScreenFrame.left &&
+ mFrame.top <= mService.mCompatibleScreenFrame.top &&
+ mFrame.right >= mService.mCompatibleScreenFrame.right &&
+ mFrame.bottom >= mService.mCompatibleScreenFrame.bottom;
+ }
+
+ boolean isFullscreen(int screenWidth, int screenHeight) {
+ return mFrame.left <= 0 && mFrame.top <= 0 &&
+ mFrame.right >= screenWidth && mFrame.bottom >= screenHeight;
+ }
+
+ void removeLocked() {
+ disposeInputChannel();
+
+ if (mAttachedWindow != null) {
+ if (WindowManagerService.DEBUG_ADD_REMOVE) Slog.v(WindowManagerService.TAG, "Removing " + this + " from " + mAttachedWindow);
+ mAttachedWindow.mChildWindows.remove(this);
+ }
+ destroySurfaceLocked();
+ mSession.windowRemovedLocked();
+ try {
+ mClient.asBinder().unlinkToDeath(mDeathRecipient, 0);
+ } catch (RuntimeException e) {
+ // Ignore if it has already been removed (usually because
+ // we are doing this as part of processing a death note.)
+ }
+ }
+
+ void disposeInputChannel() {
+ if (mInputChannel != null) {
+ mService.mInputManager.unregisterInputChannel(mInputChannel);
+
+ mInputChannel.dispose();
+ mInputChannel = null;
+ }
+ }
+
+ private class DeathRecipient implements IBinder.DeathRecipient {
+ public void binderDied() {
+ try {
+ synchronized(mService.mWindowMap) {
+ WindowState win = mService.windowForClientLocked(mSession, mClient, false);
+ Slog.i(WindowManagerService.TAG, "WIN DEATH: " + win);
+ if (win != null) {
+ mService.removeWindowLocked(mSession, win);
+ }
+ }
+ } catch (IllegalArgumentException ex) {
+ // This will happen if the window has already been
+ // removed.
+ }
+ }
+ }
+
+ /** Returns true if this window desires key events. */
+ public final boolean canReceiveKeys() {
+ return isVisibleOrAdding()
+ && (mViewVisibility == View.VISIBLE)
+ && ((mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) == 0);
+ }
+
+ public boolean hasDrawnLw() {
+ return mHasDrawn;
+ }
+
+ public boolean showLw(boolean doAnimation) {
+ return showLw(doAnimation, true);
+ }
+
+ boolean showLw(boolean doAnimation, boolean requestAnim) {
+ if (mPolicyVisibility && mPolicyVisibilityAfterAnim) {
+ return false;
+ }
+ if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(WindowManagerService.TAG, "Policy visibility true: " + this);
+ if (doAnimation) {
+ if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(WindowManagerService.TAG, "doAnimation: mPolicyVisibility="
+ + mPolicyVisibility + " mAnimation=" + mAnimation);
+ if (mService.mDisplayFrozen || !mService.mPolicy.isScreenOn()) {
+ doAnimation = false;
+ } else if (mPolicyVisibility && mAnimation == null) {
+ // Check for the case where we are currently visible and
+ // not animating; we do not want to do animation at such a
+ // point to become visible when we already are.
+ doAnimation = false;
+ }
+ }
+ mPolicyVisibility = true;
+ mPolicyVisibilityAfterAnim = true;
+ if (doAnimation) {
+ mService.applyAnimationLocked(this, WindowManagerPolicy.TRANSIT_ENTER, true);
+ }
+ if (requestAnim) {
+ mService.requestAnimationLocked(0);
+ }
+ return true;
+ }
+
+ public boolean hideLw(boolean doAnimation) {
+ return hideLw(doAnimation, true);
+ }
+
+ boolean hideLw(boolean doAnimation, boolean requestAnim) {
+ if (doAnimation) {
+ if (mService.mDisplayFrozen || !mService.mPolicy.isScreenOn()) {
+ doAnimation = false;
+ }
+ }
+ boolean current = doAnimation ? mPolicyVisibilityAfterAnim
+ : mPolicyVisibility;
+ if (!current) {
+ return false;
+ }
+ if (doAnimation) {
+ mService.applyAnimationLocked(this, WindowManagerPolicy.TRANSIT_EXIT, false);
+ if (mAnimation == null) {
+ doAnimation = false;
+ }
+ }
+ if (doAnimation) {
+ mPolicyVisibilityAfterAnim = false;
+ } else {
+ if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(WindowManagerService.TAG, "Policy visibility false: " + this);
+ mPolicyVisibilityAfterAnim = false;
+ mPolicyVisibility = false;
+ // Window is no longer visible -- make sure if we were waiting
+ // for it to be displayed before enabling the display, that
+ // we allow the display to be enabled now.
+ mService.enableScreenIfNeededLocked();
+ if (mService.mCurrentFocus == this) {
+ mService.mFocusMayChange = true;
+ }
+ }
+ if (requestAnim) {
+ mService.requestAnimationLocked(0);
+ }
+ return true;
+ }
+
+ public void getTouchableRegion(Region outRegion) {
+ final Rect frame = mFrame;
+ switch (mTouchableInsets) {
+ default:
+ case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME:
+ outRegion.set(frame);
+ break;
+ case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT: {
+ final Rect inset = mGivenContentInsets;
+ outRegion.set(
+ frame.left + inset.left, frame.top + inset.top,
+ frame.right - inset.right, frame.bottom - inset.bottom);
+ break;
+ }
+ case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE: {
+ final Rect inset = mGivenVisibleInsets;
+ outRegion.set(
+ frame.left + inset.left, frame.top + inset.top,
+ frame.right - inset.right, frame.bottom - inset.bottom);
+ break;
+ }
+ case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION: {
+ final Region givenTouchableRegion = mGivenTouchableRegion;
+ outRegion.set(givenTouchableRegion);
+ outRegion.translate(frame.left, frame.top);
+ break;
+ }
+ }
+ }
+
+ void dump(PrintWriter pw, String prefix) {
+ pw.print(prefix); pw.print("mSession="); pw.print(mSession);
+ pw.print(" mClient="); pw.println(mClient.asBinder());
+ pw.print(prefix); pw.print("mAttrs="); pw.println(mAttrs);
+ if (mAttachedWindow != null || mLayoutAttached) {
+ pw.print(prefix); pw.print("mAttachedWindow="); pw.print(mAttachedWindow);
+ pw.print(" mLayoutAttached="); pw.println(mLayoutAttached);
+ }
+ if (mIsImWindow || mIsWallpaper || mIsFloatingLayer) {
+ pw.print(prefix); pw.print("mIsImWindow="); pw.print(mIsImWindow);
+ pw.print(" mIsWallpaper="); pw.print(mIsWallpaper);
+ pw.print(" mIsFloatingLayer="); pw.print(mIsFloatingLayer);
+ pw.print(" mWallpaperVisible="); pw.println(mWallpaperVisible);
+ }
+ pw.print(prefix); pw.print("mBaseLayer="); pw.print(mBaseLayer);
+ pw.print(" mSubLayer="); pw.print(mSubLayer);
+ pw.print(" mAnimLayer="); pw.print(mLayer); pw.print("+");
+ pw.print((mTargetAppToken != null ? mTargetAppToken.animLayerAdjustment
+ : (mAppToken != null ? mAppToken.animLayerAdjustment : 0)));
+ pw.print("="); pw.print(mAnimLayer);
+ pw.print(" mLastLayer="); pw.println(mLastLayer);
+ if (mSurface != null) {
+ pw.print(prefix); pw.print("mSurface="); pw.println(mSurface);
+ pw.print(prefix); pw.print("Surface: shown="); pw.print(mSurfaceShown);
+ pw.print(" layer="); pw.print(mSurfaceLayer);
+ pw.print(" alpha="); pw.print(mSurfaceAlpha);
+ pw.print(" rect=("); pw.print(mSurfaceX);
+ pw.print(","); pw.print(mSurfaceY);
+ pw.print(") "); pw.print(mSurfaceW);
+ pw.print(" x "); pw.println(mSurfaceH);
+ }
+ pw.print(prefix); pw.print("mToken="); pw.println(mToken);
+ pw.print(prefix); pw.print("mRootToken="); pw.println(mRootToken);
+ if (mAppToken != null) {
+ pw.print(prefix); pw.print("mAppToken="); pw.println(mAppToken);
+ }
+ if (mTargetAppToken != null) {
+ pw.print(prefix); pw.print("mTargetAppToken="); pw.println(mTargetAppToken);
+ }
+ pw.print(prefix); pw.print("mViewVisibility=0x");
+ pw.print(Integer.toHexString(mViewVisibility));
+ pw.print(" mLastHidden="); pw.print(mLastHidden);
+ pw.print(" mHaveFrame="); pw.print(mHaveFrame);
+ pw.print(" mObscured="); pw.println(mObscured);
+ if (!mPolicyVisibility || !mPolicyVisibilityAfterAnim || mAttachedHidden) {
+ pw.print(prefix); pw.print("mPolicyVisibility=");
+ pw.print(mPolicyVisibility);
+ pw.print(" mPolicyVisibilityAfterAnim=");
+ pw.print(mPolicyVisibilityAfterAnim);
+ pw.print(" mAttachedHidden="); pw.println(mAttachedHidden);
+ }
+ if (!mRelayoutCalled) {
+ pw.print(prefix); pw.print("mRelayoutCalled="); pw.println(mRelayoutCalled);
+ }
+ pw.print(prefix); pw.print("Requested w="); pw.print(mRequestedWidth);
+ pw.print(" h="); pw.print(mRequestedHeight);
+ pw.print(" mLayoutSeq="); pw.println(mLayoutSeq);
+ if (mXOffset != 0 || mYOffset != 0) {
+ pw.print(prefix); pw.print("Offsets x="); pw.print(mXOffset);
+ pw.print(" y="); pw.println(mYOffset);
+ }
+ pw.print(prefix); pw.print("mGivenContentInsets=");
+ mGivenContentInsets.printShortString(pw);
+ pw.print(" mGivenVisibleInsets=");
+ mGivenVisibleInsets.printShortString(pw);
+ pw.println();
+ if (mTouchableInsets != 0 || mGivenInsetsPending) {
+ pw.print(prefix); pw.print("mTouchableInsets="); pw.print(mTouchableInsets);
+ pw.print(" mGivenInsetsPending="); pw.println(mGivenInsetsPending);
+ }
+ pw.print(prefix); pw.print("mConfiguration="); pw.println(mConfiguration);
+ pw.print(prefix); pw.print("mShownFrame=");
+ mShownFrame.printShortString(pw);
+ pw.print(" last="); mLastShownFrame.printShortString(pw);
+ pw.println();
+ pw.print(prefix); pw.print("mFrame="); mFrame.printShortString(pw);
+ pw.print(" last="); mLastFrame.printShortString(pw);
+ pw.println();
+ pw.print(prefix); pw.print("mContainingFrame=");
+ mContainingFrame.printShortString(pw);
+ pw.print(" mParentFrame=");
+ mParentFrame.printShortString(pw);
+ pw.print(" mDisplayFrame=");
+ mDisplayFrame.printShortString(pw);
+ pw.println();
+ pw.print(prefix); pw.print("mContentFrame="); mContentFrame.printShortString(pw);
+ pw.print(" mVisibleFrame="); mVisibleFrame.printShortString(pw);
+ pw.println();
+ pw.print(prefix); pw.print("mContentInsets="); mContentInsets.printShortString(pw);
+ pw.print(" last="); mLastContentInsets.printShortString(pw);
+ pw.print(" mVisibleInsets="); mVisibleInsets.printShortString(pw);
+ pw.print(" last="); mLastVisibleInsets.printShortString(pw);
+ pw.println();
+ if (mAnimating || mLocalAnimating || mAnimationIsEntrance
+ || mAnimation != null) {
+ pw.print(prefix); pw.print("mAnimating="); pw.print(mAnimating);
+ pw.print(" mLocalAnimating="); pw.print(mLocalAnimating);
+ pw.print(" mAnimationIsEntrance="); pw.print(mAnimationIsEntrance);
+ pw.print(" mAnimation="); pw.println(mAnimation);
+ }
+ if (mHasTransformation || mHasLocalTransformation) {
+ pw.print(prefix); pw.print("XForm: has=");
+ pw.print(mHasTransformation);
+ pw.print(" hasLocal="); pw.print(mHasLocalTransformation);
+ pw.print(" "); mTransformation.printShortString(pw);
+ pw.println();
+ }
+ if (mShownAlpha != 1 || mAlpha != 1 || mLastAlpha != 1) {
+ pw.print(prefix); pw.print("mShownAlpha="); pw.print(mShownAlpha);
+ pw.print(" mAlpha="); pw.print(mAlpha);
+ pw.print(" mLastAlpha="); pw.println(mLastAlpha);
+ }
+ if (mHaveMatrix) {
+ pw.print(prefix); pw.print("mDsDx="); pw.print(mDsDx);
+ pw.print(" mDtDx="); pw.print(mDtDx);
+ pw.print(" mDsDy="); pw.print(mDsDy);
+ pw.print(" mDtDy="); pw.println(mDtDy);
+ }
+ pw.print(prefix); pw.print("mDrawPending="); pw.print(mDrawPending);
+ pw.print(" mCommitDrawPending="); pw.print(mCommitDrawPending);
+ pw.print(" mReadyToShow="); pw.print(mReadyToShow);
+ pw.print(" mHasDrawn="); pw.println(mHasDrawn);
+ if (mExiting || mRemoveOnExit || mDestroying || mRemoved) {
+ pw.print(prefix); pw.print("mExiting="); pw.print(mExiting);
+ pw.print(" mRemoveOnExit="); pw.print(mRemoveOnExit);
+ pw.print(" mDestroying="); pw.print(mDestroying);
+ pw.print(" mRemoved="); pw.println(mRemoved);
+ }
+ if (mOrientationChanging || mAppFreezing || mTurnOnScreen) {
+ pw.print(prefix); pw.print("mOrientationChanging=");
+ pw.print(mOrientationChanging);
+ pw.print(" mAppFreezing="); pw.print(mAppFreezing);
+ pw.print(" mTurnOnScreen="); pw.println(mTurnOnScreen);
+ }
+ if (mHScale != 1 || mVScale != 1) {
+ pw.print(prefix); pw.print("mHScale="); pw.print(mHScale);
+ pw.print(" mVScale="); pw.println(mVScale);
+ }
+ if (mWallpaperX != -1 || mWallpaperY != -1) {
+ pw.print(prefix); pw.print("mWallpaperX="); pw.print(mWallpaperX);
+ pw.print(" mWallpaperY="); pw.println(mWallpaperY);
+ }
+ if (mWallpaperXStep != -1 || mWallpaperYStep != -1) {
+ pw.print(prefix); pw.print("mWallpaperXStep="); pw.print(mWallpaperXStep);
+ pw.print(" mWallpaperYStep="); pw.println(mWallpaperYStep);
+ }
+ }
+
+ String makeInputChannelName() {
+ return Integer.toHexString(System.identityHashCode(this))
+ + " " + mAttrs.getTitle();
+ }
+
+ @Override
+ public String toString() {
+ if (mStringNameCache == null || mLastTitle != mAttrs.getTitle()
+ || mWasPaused != mToken.paused) {
+ mLastTitle = mAttrs.getTitle();
+ mWasPaused = mToken.paused;
+ mStringNameCache = "Window{" + Integer.toHexString(System.identityHashCode(this))
+ + " " + mLastTitle + " paused=" + mWasPaused + "}";
+ }
+ return mStringNameCache;
+ }
+}
\ No newline at end of file
diff --git a/services/java/com/android/server/wm/WindowToken.java b/services/java/com/android/server/wm/WindowToken.java
new file mode 100644
index 0000000..3cd256e
--- /dev/null
+++ b/services/java/com/android/server/wm/WindowToken.java
@@ -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;
+ }
+}
\ No newline at end of file
diff --git a/services/jni/com_android_server_InputApplication.cpp b/services/jni/com_android_server_InputApplication.cpp
index a46a162..e64ec4e 100644
--- a/services/jni/com_android_server_InputApplication.cpp
+++ b/services/jni/com_android_server_InputApplication.cpp
@@ -77,11 +77,11 @@
LOG_FATAL_IF(! var, "Unable to find field " fieldName);
int register_android_server_InputApplication(JNIEnv* env) {
- FIND_CLASS(gInputApplicationClassInfo.clazz, "com/android/server/InputApplication");
+ FIND_CLASS(gInputApplicationClassInfo.clazz, "com/android/server/wm/InputApplication");
GET_FIELD_ID(gInputApplicationClassInfo.inputApplicationHandle,
gInputApplicationClassInfo.clazz,
- "inputApplicationHandle", "Lcom/android/server/InputApplicationHandle;");
+ "inputApplicationHandle", "Lcom/android/server/wm/InputApplicationHandle;");
GET_FIELD_ID(gInputApplicationClassInfo.name, gInputApplicationClassInfo.clazz,
"name", "Ljava/lang/String;");
diff --git a/services/jni/com_android_server_InputApplicationHandle.cpp b/services/jni/com_android_server_InputApplicationHandle.cpp
index ab82635..3a1214f 100644
--- a/services/jni/com_android_server_InputApplicationHandle.cpp
+++ b/services/jni/com_android_server_InputApplicationHandle.cpp
@@ -106,11 +106,11 @@
LOG_FATAL_IF(! var, "Unable to find field " fieldName);
int register_android_server_InputApplicationHandle(JNIEnv* env) {
- int res = jniRegisterNativeMethods(env, "com/android/server/InputApplicationHandle",
+ int res = jniRegisterNativeMethods(env, "com/android/server/wm/InputApplicationHandle",
gInputApplicationHandleMethods, NELEM(gInputApplicationHandleMethods));
LOG_FATAL_IF(res < 0, "Unable to register native methods.");
- FIND_CLASS(gInputApplicationHandleClassInfo.clazz, "com/android/server/InputApplicationHandle");
+ FIND_CLASS(gInputApplicationHandleClassInfo.clazz, "com/android/server/wm/InputApplicationHandle");
GET_FIELD_ID(gInputApplicationHandleClassInfo.ptr, gInputApplicationHandleClassInfo.clazz,
"ptr", "I");
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index 427af23..0a50ff8 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -1079,7 +1079,7 @@
static JNINativeMethod gInputManagerMethods[] = {
/* name, signature, funcPtr */
- { "nativeInit", "(Lcom/android/server/InputManager$Callbacks;)V",
+ { "nativeInit", "(Lcom/android/server/wm/InputManager$Callbacks;)V",
(void*) android_server_InputManager_nativeInit },
{ "nativeStart", "()V",
(void*) android_server_InputManager_nativeStart },
@@ -1096,15 +1096,15 @@
{ "nativeHasKeys", "(II[I[Z)Z",
(void*) android_server_InputManager_nativeHasKeys },
{ "nativeRegisterInputChannel",
- "(Landroid/view/InputChannel;Lcom/android/server/InputWindowHandle;Z)V",
+ "(Landroid/view/InputChannel;Lcom/android/server/wm/InputWindowHandle;Z)V",
(void*) android_server_InputManager_nativeRegisterInputChannel },
{ "nativeUnregisterInputChannel", "(Landroid/view/InputChannel;)V",
(void*) android_server_InputManager_nativeUnregisterInputChannel },
{ "nativeInjectInputEvent", "(Landroid/view/InputEvent;IIII)I",
(void*) android_server_InputManager_nativeInjectInputEvent },
- { "nativeSetInputWindows", "([Lcom/android/server/InputWindow;)V",
+ { "nativeSetInputWindows", "([Lcom/android/server/wm/InputWindow;)V",
(void*) android_server_InputManager_nativeSetInputWindows },
- { "nativeSetFocusedApplication", "(Lcom/android/server/InputApplication;)V",
+ { "nativeSetFocusedApplication", "(Lcom/android/server/wm/InputApplication;)V",
(void*) android_server_InputManager_nativeSetFocusedApplication },
{ "nativeSetInputDispatchMode", "(ZZ)V",
(void*) android_server_InputManager_nativeSetInputDispatchMode },
@@ -1134,13 +1134,13 @@
LOG_FATAL_IF(! var, "Unable to find field " fieldName);
int register_android_server_InputManager(JNIEnv* env) {
- int res = jniRegisterNativeMethods(env, "com/android/server/InputManager",
+ int res = jniRegisterNativeMethods(env, "com/android/server/wm/InputManager",
gInputManagerMethods, NELEM(gInputManagerMethods));
LOG_FATAL_IF(res < 0, "Unable to register native methods.");
// Callbacks
- FIND_CLASS(gCallbacksClassInfo.clazz, "com/android/server/InputManager$Callbacks");
+ FIND_CLASS(gCallbacksClassInfo.clazz, "com/android/server/wm/InputManager$Callbacks");
GET_METHOD_ID(gCallbacksClassInfo.notifyConfigurationChanged, gCallbacksClassInfo.clazz,
"notifyConfigurationChanged", "(J)V");
@@ -1149,22 +1149,22 @@
"notifyLidSwitchChanged", "(JZ)V");
GET_METHOD_ID(gCallbacksClassInfo.notifyInputChannelBroken, gCallbacksClassInfo.clazz,
- "notifyInputChannelBroken", "(Lcom/android/server/InputWindowHandle;)V");
+ "notifyInputChannelBroken", "(Lcom/android/server/wm/InputWindowHandle;)V");
GET_METHOD_ID(gCallbacksClassInfo.notifyANR, gCallbacksClassInfo.clazz,
"notifyANR",
- "(Lcom/android/server/InputApplicationHandle;Lcom/android/server/InputWindowHandle;)J");
+ "(Lcom/android/server/wm/InputApplicationHandle;Lcom/android/server/wm/InputWindowHandle;)J");
GET_METHOD_ID(gCallbacksClassInfo.interceptKeyBeforeQueueing, gCallbacksClassInfo.clazz,
"interceptKeyBeforeQueueing", "(Landroid/view/KeyEvent;IZ)I");
GET_METHOD_ID(gCallbacksClassInfo.interceptKeyBeforeDispatching, gCallbacksClassInfo.clazz,
"interceptKeyBeforeDispatching",
- "(Lcom/android/server/InputWindowHandle;Landroid/view/KeyEvent;I)Z");
+ "(Lcom/android/server/wm/InputWindowHandle;Landroid/view/KeyEvent;I)Z");
GET_METHOD_ID(gCallbacksClassInfo.dispatchUnhandledKey, gCallbacksClassInfo.clazz,
"dispatchUnhandledKey",
- "(Lcom/android/server/InputWindowHandle;Landroid/view/KeyEvent;I)Landroid/view/KeyEvent;");
+ "(Lcom/android/server/wm/InputWindowHandle;Landroid/view/KeyEvent;I)Landroid/view/KeyEvent;");
GET_METHOD_ID(gCallbacksClassInfo.checkInjectEventsPermission, gCallbacksClassInfo.clazz,
"checkInjectEventsPermission", "(II)Z");
@@ -1188,7 +1188,7 @@
"getPointerLayer", "()I");
GET_METHOD_ID(gCallbacksClassInfo.getPointerIcon, gCallbacksClassInfo.clazz,
- "getPointerIcon", "()Lcom/android/server/InputManager$PointerIcon;");
+ "getPointerIcon", "()Lcom/android/server/wm/InputManager$PointerIcon;");
// KeyEvent
@@ -1235,7 +1235,7 @@
// PointerIcon
- FIND_CLASS(gPointerIconClassInfo.clazz, "com/android/server/InputManager$PointerIcon");
+ FIND_CLASS(gPointerIconClassInfo.clazz, "com/android/server/wm/InputManager$PointerIcon");
GET_FIELD_ID(gPointerIconClassInfo.bitmap, gPointerIconClassInfo.clazz,
"bitmap", "Landroid/graphics/Bitmap;");
diff --git a/services/jni/com_android_server_InputWindow.cpp b/services/jni/com_android_server_InputWindow.cpp
index 75154567c..8548b47 100644
--- a/services/jni/com_android_server_InputWindow.cpp
+++ b/services/jni/com_android_server_InputWindow.cpp
@@ -144,10 +144,10 @@
LOG_FATAL_IF(! var, "Unable to find field " fieldName);
int register_android_server_InputWindow(JNIEnv* env) {
- FIND_CLASS(gInputWindowClassInfo.clazz, "com/android/server/InputWindow");
+ FIND_CLASS(gInputWindowClassInfo.clazz, "com/android/server/wm/InputWindow");
GET_FIELD_ID(gInputWindowClassInfo.inputWindowHandle, gInputWindowClassInfo.clazz,
- "inputWindowHandle", "Lcom/android/server/InputWindowHandle;");
+ "inputWindowHandle", "Lcom/android/server/wm/InputWindowHandle;");
GET_FIELD_ID(gInputWindowClassInfo.inputChannel, gInputWindowClassInfo.clazz,
"inputChannel", "Landroid/view/InputChannel;");
diff --git a/services/jni/com_android_server_InputWindowHandle.cpp b/services/jni/com_android_server_InputWindowHandle.cpp
index 4d66212..5b74e43 100644
--- a/services/jni/com_android_server_InputWindowHandle.cpp
+++ b/services/jni/com_android_server_InputWindowHandle.cpp
@@ -116,18 +116,18 @@
LOG_FATAL_IF(! var, "Unable to find field " fieldName);
int register_android_server_InputWindowHandle(JNIEnv* env) {
- int res = jniRegisterNativeMethods(env, "com/android/server/InputWindowHandle",
+ int res = jniRegisterNativeMethods(env, "com/android/server/wm/InputWindowHandle",
gInputWindowHandleMethods, NELEM(gInputWindowHandleMethods));
LOG_FATAL_IF(res < 0, "Unable to register native methods.");
- FIND_CLASS(gInputWindowHandleClassInfo.clazz, "com/android/server/InputWindowHandle");
+ FIND_CLASS(gInputWindowHandleClassInfo.clazz, "com/android/server/wm/InputWindowHandle");
GET_FIELD_ID(gInputWindowHandleClassInfo.ptr, gInputWindowHandleClassInfo.clazz,
"ptr", "I");
GET_FIELD_ID(gInputWindowHandleClassInfo.inputApplicationHandle,
gInputWindowHandleClassInfo.clazz,
- "inputApplicationHandle", "Lcom/android/server/InputApplicationHandle;");
+ "inputApplicationHandle", "Lcom/android/server/wm/InputApplicationHandle;");
return 0;
}
diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java
index c9e304a..98ab3d1 100644
--- a/telephony/java/android/telephony/SignalStrength.java
+++ b/telephony/java/android/telephony/SignalStrength.java
@@ -27,7 +27,25 @@
*/
public class SignalStrength implements Parcelable {
- static final String LOG_TAG = "PHONE";
+ private static final String LOG_TAG = "SignalStrength";
+ private static final boolean DBG = false;
+
+ /** @hide */
+ public static final int SIGNAL_STRENGTH_NONE_OR_UNKNOWN = 0;
+ /** @hide */
+ public static final int SIGNAL_STRENGTH_POOR = 1;
+ /** @hide */
+ public static final int SIGNAL_STRENGTH_MODERATE = 2;
+ /** @hide */
+ public static final int SIGNAL_STRENGTH_GOOD = 3;
+ /** @hide */
+ public static final int SIGNAL_STRENGTH_GREAT = 4;
+ /** @hide */
+ public static final int NUM_SIGNAL_STRENGTH_BINS = 5;
+ /** @hide */
+ public static final String[] SIGNAL_STRENGTH_NAMES = {
+ "none", "poor", "moderate", "good", "great"
+ };
private int mGsmSignalStrength; // Valid values are (0-31, 99) as defined in TS 27.007 8.5
private int mGsmBitErrorRate; // bit error rate (0-7, 99) as defined in TS 27.007 8.5
@@ -36,6 +54,11 @@
private int mEvdoDbm; // This value is the EVDO RSSI value
private int mEvdoEcio; // This value is the EVDO Ec/Io
private int mEvdoSnr; // Valid values are 0-8. 8 is the highest signal to noise ratio
+ private int mLteSignalStrength;
+ private int mLteRsrp;
+ private int mLteRsrq;
+ private int mLteRssnr;
+ private int mLteCqi;
private boolean isGsm; // This value is set by the ServiceStateTracker onSignalStrengthResult
@@ -70,6 +93,11 @@
mEvdoDbm = -1;
mEvdoEcio = -1;
mEvdoSnr = -1;
+ mLteSignalStrength = -1;
+ mLteRsrp = -1;
+ mLteRsrq = -1;
+ mLteRssnr = -1;
+ mLteCqi = -1;
isGsm = true;
}
@@ -80,7 +108,9 @@
*/
public SignalStrength(int gsmSignalStrength, int gsmBitErrorRate,
int cdmaDbm, int cdmaEcio,
- int evdoDbm, int evdoEcio, int evdoSnr, boolean gsm) {
+ int evdoDbm, int evdoEcio, int evdoSnr,
+ int lteSignalStrength, int lteRsrp, int lteRsrq, int lteRssnr, int lteCqi,
+ boolean gsm) {
mGsmSignalStrength = gsmSignalStrength;
mGsmBitErrorRate = gsmBitErrorRate;
mCdmaDbm = cdmaDbm;
@@ -88,10 +118,28 @@
mEvdoDbm = evdoDbm;
mEvdoEcio = evdoEcio;
mEvdoSnr = evdoSnr;
+ mLteSignalStrength = lteSignalStrength;
+ mLteRsrp = lteRsrp;
+ mLteRsrq = lteRsrq;
+ mLteRssnr = lteRssnr;
+ mLteCqi = lteCqi;
isGsm = gsm;
}
/**
+ * Constructor
+ *
+ * @hide
+ */
+ public SignalStrength(int gsmSignalStrength, int gsmBitErrorRate,
+ int cdmaDbm, int cdmaEcio,
+ int evdoDbm, int evdoEcio, int evdoSnr,
+ boolean gsm) {
+ this(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
+ evdoDbm, evdoEcio, evdoSnr, -1, -1, -1, -1, -1, gsm);
+ }
+
+ /**
* Copy constructors
*
* @param s Source SignalStrength
@@ -113,6 +161,11 @@
mEvdoDbm = s.mEvdoDbm;
mEvdoEcio = s.mEvdoEcio;
mEvdoSnr = s.mEvdoSnr;
+ mLteSignalStrength = s.mLteSignalStrength;
+ mLteRsrp = s.mLteRsrp;
+ mLteRsrq = s.mLteRsrq;
+ mLteRssnr = s.mLteRssnr;
+ mLteCqi = s.mLteCqi;
isGsm = s.isGsm;
}
@@ -129,6 +182,11 @@
mEvdoDbm = in.readInt();
mEvdoEcio = in.readInt();
mEvdoSnr = in.readInt();
+ mLteSignalStrength = in.readInt();
+ mLteRsrp = in.readInt();
+ mLteRsrq = in.readInt();
+ mLteRssnr = in.readInt();
+ mLteCqi = in.readInt();
isGsm = (in.readInt() != 0);
}
@@ -143,6 +201,11 @@
out.writeInt(mEvdoDbm);
out.writeInt(mEvdoEcio);
out.writeInt(mEvdoSnr);
+ out.writeInt(mLteSignalStrength);
+ out.writeInt(mLteRsrp);
+ out.writeInt(mLteRsrq);
+ out.writeInt(mLteRssnr);
+ out.writeInt(mLteCqi);
out.writeInt(isGsm ? 1 : 0);
}
@@ -218,6 +281,312 @@
}
/**
+ * Get signal level as an int from 0..4
+ *
+ * @hide
+ */
+ public int getLevel() {
+ int level;
+
+ if (isGsm) {
+ if ((mLteSignalStrength == -1)
+ && (mLteRsrp == -1)
+ && (mLteRsrq == -1)
+ && (mLteRssnr == -1)
+ && (mLteCqi == -1)) {
+ level = getGsmLevel();
+ } else {
+ level = getLteLevel();
+ }
+ } else {
+ int cdmaLevel = getCdmaLevel();
+ int evdoLevel = getEvdoLevel();
+ if (evdoLevel == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
+ /* We don't know evdo, use cdma */
+ level = getCdmaLevel();
+ } else if (cdmaLevel == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
+ /* We don't know cdma, use evdo */
+ level = getEvdoLevel();
+ } else {
+ /* We know both, use the lowest level */
+ level = cdmaLevel < evdoLevel ? cdmaLevel : evdoLevel;
+ }
+ }
+ if (DBG) log("getLevel=" + level);
+ return level;
+ }
+
+ /**
+ * Get the signal level as an asu value between 0..31, 99 is unknown
+ *
+ * @hide
+ */
+ public int getAsuLevel() {
+ int asuLevel;
+ if (isGsm) {
+ if ((mLteSignalStrength == -1)
+ && (mLteRsrp == -1)
+ && (mLteRsrq == -1)
+ && (mLteRssnr == -1)
+ && (mLteCqi == -1)) {
+ asuLevel = getGsmAsuLevel();
+ } else {
+ asuLevel = getLteAsuLevel();
+ }
+ } else {
+ int cdmaAsuLevel = getCdmaAsuLevel();
+ int evdoAsuLevel = getEvdoAsuLevel();
+ if (evdoAsuLevel == 0) {
+ /* We don't know evdo use, cdma */
+ asuLevel = cdmaAsuLevel;
+ } else if (cdmaAsuLevel == 0) {
+ /* We don't know cdma use, evdo */
+ asuLevel = evdoAsuLevel;
+ } else {
+ /* We know both, use the lowest level */
+ asuLevel = cdmaAsuLevel < evdoAsuLevel ? cdmaAsuLevel : evdoAsuLevel;
+ }
+ }
+ if (DBG) log("getAsuLevel=" + asuLevel);
+ return asuLevel;
+ }
+
+ /**
+ * Get the signal strength as dBm
+ *
+ * @hide
+ */
+ public int getDbm() {
+ int dBm;
+
+ if(isGsm()) {
+ if ((mLteSignalStrength == -1)
+ && (mLteRsrp == -1)
+ && (mLteRsrq == -1)
+ && (mLteRssnr == -1)
+ && (mLteCqi == -1)) {
+ dBm = getGsmDbm();
+ } else {
+ dBm = getLteDbm();
+ }
+ } else {
+ dBm = getCdmaDbm();
+ }
+ if (DBG) log("getDbm=" + dBm);
+ return dBm;
+ }
+
+ /**
+ * Get Gsm signal strength as dBm
+ *
+ * @hide
+ */
+ public int getGsmDbm() {
+ int dBm;
+
+ int gsmSignalStrength = getGsmSignalStrength();
+ int asu = (gsmSignalStrength == 99 ? -1 : gsmSignalStrength);
+ if (asu != -1) {
+ dBm = -113 + (2 * asu);
+ } else {
+ dBm = -1;
+ }
+ if (DBG) log("getGsmDbm=" + dBm);
+ return dBm;
+ }
+
+ /**
+ * Get gsm as level 0..4
+ *
+ * @hide
+ */
+ public int getGsmLevel() {
+ int level;
+
+ // ASU ranges from 0 to 31 - TS 27.007 Sec 8.5
+ // asu = 0 (-113dB or less) is very weak
+ // signal, its better to show 0 bars to the user in such cases.
+ // asu = 99 is a special case, where the signal strength is unknown.
+ int asu = getGsmSignalStrength();
+ if (asu <= 2 || asu == 99) level = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+ else if (asu >= 12) level = SIGNAL_STRENGTH_GREAT;
+ else if (asu >= 8) level = SIGNAL_STRENGTH_GOOD;
+ else if (asu >= 5) level = SIGNAL_STRENGTH_MODERATE;
+ else level = SIGNAL_STRENGTH_POOR;
+ if (DBG) log("getGsmLevel=" + level);
+ return level;
+ }
+
+ /**
+ * Get the gsm signal level as an asu value between 0..31, 99 is unknown
+ *
+ * @hide
+ */
+ public int getGsmAsuLevel() {
+ // ASU ranges from 0 to 31 - TS 27.007 Sec 8.5
+ // asu = 0 (-113dB or less) is very weak
+ // signal, its better to show 0 bars to the user in such cases.
+ // asu = 99 is a special case, where the signal strength is unknown.
+ int level = getGsmSignalStrength();
+ if (DBG) log("getGsmAsuLevel=" + level);
+ return level;
+ }
+
+ /**
+ * Get cdma as level 0..4
+ *
+ * @hide
+ */
+ public int getCdmaLevel() {
+ final int cdmaDbm = getCdmaDbm();
+ final int cdmaEcio = getCdmaEcio();
+ int levelDbm;
+ int levelEcio;
+
+ if (cdmaDbm >= -75) levelDbm = SIGNAL_STRENGTH_GREAT;
+ else if (cdmaDbm >= -85) levelDbm = SIGNAL_STRENGTH_GOOD;
+ else if (cdmaDbm >= -95) levelDbm = SIGNAL_STRENGTH_MODERATE;
+ else if (cdmaDbm >= -100) levelDbm = SIGNAL_STRENGTH_POOR;
+ else levelDbm = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+
+ // Ec/Io are in dB*10
+ if (cdmaEcio >= -90) levelEcio = SIGNAL_STRENGTH_GREAT;
+ else if (cdmaEcio >= -110) levelEcio = SIGNAL_STRENGTH_GOOD;
+ else if (cdmaEcio >= -130) levelEcio = SIGNAL_STRENGTH_MODERATE;
+ else if (cdmaEcio >= -150) levelEcio = SIGNAL_STRENGTH_POOR;
+ else levelEcio = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+
+ int level = (levelDbm < levelEcio) ? levelDbm : levelEcio;
+ if (DBG) log("getCdmaLevel=" + level);
+ return level;
+ }
+
+ /**
+ * Get the cdma signal level as an asu value between 0..31, 99 is unknown
+ *
+ * @hide
+ */
+ public int getCdmaAsuLevel() {
+ final int cdmaDbm = getCdmaDbm();
+ final int cdmaEcio = getCdmaEcio();
+ int cdmaAsuLevel;
+ int ecioAsuLevel;
+
+ if (cdmaDbm >= -75) cdmaAsuLevel = 16;
+ else if (cdmaDbm >= -82) cdmaAsuLevel = 8;
+ else if (cdmaDbm >= -90) cdmaAsuLevel = 4;
+ else if (cdmaDbm >= -95) cdmaAsuLevel = 2;
+ else if (cdmaDbm >= -100) cdmaAsuLevel = 1;
+ else cdmaAsuLevel = 99;
+
+ // Ec/Io are in dB*10
+ if (cdmaEcio >= -90) ecioAsuLevel = 16;
+ else if (cdmaEcio >= -100) ecioAsuLevel = 8;
+ else if (cdmaEcio >= -115) ecioAsuLevel = 4;
+ else if (cdmaEcio >= -130) ecioAsuLevel = 2;
+ else if (cdmaEcio >= -150) ecioAsuLevel = 1;
+ else ecioAsuLevel = 99;
+
+ int level = (cdmaAsuLevel < ecioAsuLevel) ? cdmaAsuLevel : ecioAsuLevel;
+ if (DBG) log("getCdmaAsuLevel=" + level);
+ return level;
+ }
+
+ /**
+ * Get Evdo as level 0..4
+ *
+ * @hide
+ */
+ public int getEvdoLevel() {
+ int evdoDbm = getEvdoDbm();
+ int evdoSnr = getEvdoSnr();
+ int levelEvdoDbm;
+ int levelEvdoSnr;
+
+ if (evdoDbm >= -65) levelEvdoDbm = SIGNAL_STRENGTH_GREAT;
+ else if (evdoDbm >= -75) levelEvdoDbm = SIGNAL_STRENGTH_GOOD;
+ else if (evdoDbm >= -90) levelEvdoDbm = SIGNAL_STRENGTH_MODERATE;
+ else if (evdoDbm >= -105) levelEvdoDbm = SIGNAL_STRENGTH_POOR;
+ else levelEvdoDbm = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+
+ if (evdoSnr >= 7) levelEvdoSnr = SIGNAL_STRENGTH_GREAT;
+ else if (evdoSnr >= 5) levelEvdoSnr = SIGNAL_STRENGTH_GOOD;
+ else if (evdoSnr >= 3) levelEvdoSnr = SIGNAL_STRENGTH_MODERATE;
+ else if (evdoSnr >= 1) levelEvdoSnr = SIGNAL_STRENGTH_POOR;
+ else levelEvdoSnr = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+
+ int level = (levelEvdoDbm < levelEvdoSnr) ? levelEvdoDbm : levelEvdoSnr;
+ if (DBG) log("getEvdoLevel=" + level);
+ return level;
+ }
+
+ /**
+ * Get the evdo signal level as an asu value between 0..31, 99 is unknown
+ *
+ * @hide
+ */
+ public int getEvdoAsuLevel() {
+ int evdoDbm = getEvdoDbm();
+ int evdoSnr = getEvdoSnr();
+ int levelEvdoDbm;
+ int levelEvdoSnr;
+
+ if (evdoDbm >= -65) levelEvdoDbm = 16;
+ else if (evdoDbm >= -75) levelEvdoDbm = 8;
+ else if (evdoDbm >= -85) levelEvdoDbm = 4;
+ else if (evdoDbm >= -95) levelEvdoDbm = 2;
+ else if (evdoDbm >= -105) levelEvdoDbm = 1;
+ else levelEvdoDbm = 99;
+
+ if (evdoSnr >= 7) levelEvdoSnr = 16;
+ else if (evdoSnr >= 6) levelEvdoSnr = 8;
+ else if (evdoSnr >= 5) levelEvdoSnr = 4;
+ else if (evdoSnr >= 3) levelEvdoSnr = 2;
+ else if (evdoSnr >= 1) levelEvdoSnr = 1;
+ else levelEvdoSnr = 99;
+
+ int level = (levelEvdoDbm < levelEvdoSnr) ? levelEvdoDbm : levelEvdoSnr;
+ if (DBG) log("getEvdoAsuLevel=" + level);
+ return level;
+ }
+
+ /**
+ * Get LTE as dBm
+ *
+ * @hide
+ */
+ public int getLteDbm() {
+ log("STOPSHIP teach getLteDbm to compute dBm properly");
+ int level = -1;
+ if (DBG) log("getLteDbm=" + level);
+ return level;
+ }
+
+ /**
+ * Get LTE as level 0..4
+ *
+ * @hide
+ */
+ public int getLteLevel() {
+ log("STOPSHIP teach getLteLevel to compute Level properly");
+ int level = SIGNAL_STRENGTH_MODERATE;
+ if (DBG) log("getLteLevel=" + level);
+ return level;
+ }
+
+ /**
+ * Get the LTE signal level as an asu value between 0..31, 99 is unknown
+ *
+ * @hide
+ */
+ public int getLteAsuLevel() {
+ log("STOPSHIP teach getLteAsuLevel to compute asu Level properly");
+ int level = 4;
+ if (DBG) log("getLteAsuLevel=" + level);
+ return level;
+ }
+
+ /**
* @return true if this is for GSM
*/
public boolean isGsm() {
@@ -229,10 +598,13 @@
*/
@Override
public int hashCode() {
- return ((mGsmSignalStrength * 0x1234)
- + mGsmBitErrorRate
- + mCdmaDbm + mCdmaEcio
- + mEvdoDbm + mEvdoEcio + mEvdoSnr
+ int primeNum = 31;
+ return ((mGsmSignalStrength * primeNum)
+ + (mGsmBitErrorRate * primeNum)
+ + (mCdmaDbm * primeNum) + (mCdmaEcio * primeNum)
+ + (mEvdoDbm * primeNum) + (mEvdoEcio * primeNum) + (mEvdoSnr * primeNum)
+ + (mLteSignalStrength * primeNum) + (mLteRsrp * primeNum)
+ + (mLteRsrq * primeNum) + (mLteRssnr * primeNum) + (mLteCqi * primeNum)
+ (isGsm ? 1 : 0));
}
@@ -260,6 +632,11 @@
&& mEvdoDbm == s.mEvdoDbm
&& mEvdoEcio == s.mEvdoEcio
&& mEvdoSnr == s.mEvdoSnr
+ && mLteSignalStrength == s.mLteSignalStrength
+ && mLteRsrp == s.mLteRsrp
+ && mLteRsrq == s.mLteRsrq
+ && mLteRssnr == s.mLteRssnr
+ && mLteCqi == s.mLteCqi
&& isGsm == s.isGsm);
}
@@ -276,19 +653,12 @@
+ " " + mEvdoDbm
+ " " + mEvdoEcio
+ " " + mEvdoSnr
- + " " + (isGsm ? "gsm" : "cdma"));
- }
-
- /**
- * Test whether two objects hold the same data values or both are null
- *
- * @param a first obj
- * @param b second obj
- * @return true if two objects equal or both are null
- * @hide
- */
- private static boolean equalsHandlesNulls (Object a, Object b) {
- return (a == null) ? (b == null) : a.equals (b);
+ + " " + mLteSignalStrength
+ + " " + mLteRsrp
+ + " " + mLteRsrq
+ + " " + mLteRssnr
+ + " " + mLteCqi
+ + " " + (isGsm ? "gsm|lte" : "cdma"));
}
/**
@@ -305,6 +675,11 @@
mEvdoDbm = m.getInt("EvdoDbm");
mEvdoEcio = m.getInt("EvdoEcio");
mEvdoSnr = m.getInt("EvdoSnr");
+ mLteSignalStrength = m.getInt("LteSignalStrength");
+ mLteRsrp = m.getInt("LteRsrp");
+ mLteRsrq = m.getInt("LteRsrq");
+ mLteRssnr = m.getInt("LteRssnr");
+ mLteCqi = m.getInt("LteCqi");
isGsm = m.getBoolean("isGsm");
}
@@ -322,6 +697,18 @@
m.putInt("EvdoDbm", mEvdoDbm);
m.putInt("EvdoEcio", mEvdoEcio);
m.putInt("EvdoSnr", mEvdoSnr);
+ m.putInt("LteSignalStrength", mLteSignalStrength);
+ m.putInt("LteRsrp", mLteRsrp);
+ m.putInt("LteRsrq", mLteRsrq);
+ m.putInt("LteRssnr", mLteRssnr);
+ m.putInt("LteCqi", mLteCqi);
m.putBoolean("isGsm", Boolean.valueOf(isGsm));
}
+
+ /**
+ * log
+ */
+ private static void log(String s) {
+ Log.w(LOG_TAG, s);
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/BaseCommands.java b/telephony/java/com/android/internal/telephony/BaseCommands.java
index 815fbfb..9b19600 100644
--- a/telephony/java/com/android/internal/telephony/BaseCommands.java
+++ b/telephony/java/com/android/internal/telephony/BaseCommands.java
@@ -47,8 +47,8 @@
protected RegistrantList mRUIMLockedRegistrants = new RegistrantList();
protected RegistrantList mNVReadyRegistrants = new RegistrantList();
protected RegistrantList mCallStateRegistrants = new RegistrantList();
- protected RegistrantList mNetworkStateRegistrants = new RegistrantList();
- protected RegistrantList mDataConnectionRegistrants = new RegistrantList();
+ protected RegistrantList mVoiceNetworkStateRegistrants = new RegistrantList();
+ protected RegistrantList mDataNetworkStateRegistrants = new RegistrantList();
protected RegistrantList mRadioTechnologyChangedRegistrants = new RegistrantList();
protected RegistrantList mIccStatusChangedRegistrants = new RegistrantList();
protected RegistrantList mVoicePrivacyOnRegistrants = new RegistrantList();
@@ -65,6 +65,9 @@
protected RegistrantList mT53AudCntrlInfoRegistrants = new RegistrantList();
protected RegistrantList mRingbackToneRegistrants = new RegistrantList();
protected RegistrantList mResendIncallMuteRegistrants = new RegistrantList();
+ protected RegistrantList mCdmaSubscriptionChangedRegistrants = new RegistrantList();
+ protected RegistrantList mCdmaPrlChangedRegistrants = new RegistrantList();
+ protected RegistrantList mExitEmergencyCallbackModeRegistrants = new RegistrantList();
protected Registrant mSMSRegistrant;
protected Registrant mNITZTimeRegistrant;
@@ -293,24 +296,24 @@
mCallStateRegistrants.remove(h);
}
- public void registerForNetworkStateChanged(Handler h, int what, Object obj) {
+ public void registerForVoiceNetworkStateChanged(Handler h, int what, Object obj) {
Registrant r = new Registrant (h, what, obj);
- mNetworkStateRegistrants.add(r);
+ mVoiceNetworkStateRegistrants.add(r);
}
- public void unregisterForNetworkStateChanged(Handler h) {
- mNetworkStateRegistrants.remove(h);
+ public void unregisterForVoiceNetworkStateChanged(Handler h) {
+ mVoiceNetworkStateRegistrants.remove(h);
}
- public void registerForDataStateChanged(Handler h, int what, Object obj) {
+ public void registerForDataNetworkStateChanged(Handler h, int what, Object obj) {
Registrant r = new Registrant (h, what, obj);
- mDataConnectionRegistrants.add(r);
+ mDataNetworkStateRegistrants.add(r);
}
- public void unregisterForDataStateChanged(Handler h) {
- mDataConnectionRegistrants.remove(h);
+ public void unregisterForDataNetworkStateChanged(Handler h) {
+ mDataNetworkStateRegistrants.remove(h);
}
public void registerForRadioTechnologyChanged(Handler h, int what, Object obj) {
@@ -588,6 +591,39 @@
mResendIncallMuteRegistrants.remove(h);
}
+ @Override
+ public void registerForCdmaSubscriptionChanged(Handler h, int what, Object obj) {
+ Registrant r = new Registrant (h, what, obj);
+ mCdmaSubscriptionChangedRegistrants.add(r);
+ }
+
+ @Override
+ public void unregisterForCdmaSubscriptionChanged(Handler h) {
+ mCdmaSubscriptionChangedRegistrants.remove(h);
+ }
+
+ @Override
+ public void registerForCdmaPrlChanged(Handler h, int what, Object obj) {
+ Registrant r = new Registrant (h, what, obj);
+ mCdmaPrlChangedRegistrants.add(r);
+ }
+
+ @Override
+ public void unregisterForCdmaPrlChanged(Handler h) {
+ mCdmaPrlChangedRegistrants.remove(h);
+ }
+
+ @Override
+ public void registerForExitEmergencyCallbackMode(Handler h, int what, Object obj) {
+ Registrant r = new Registrant (h, what, obj);
+ mExitEmergencyCallbackModeRegistrants.add(r);
+ }
+
+ @Override
+ public void unregisterForExitEmergencyCallbackMode(Handler h) {
+ mExitEmergencyCallbackModeRegistrants.remove(h);
+ }
+
//***** Protected Methods
/**
* Store new RadioState and send notification based on the changes
diff --git a/telephony/java/com/android/internal/telephony/CommandsInterface.java b/telephony/java/com/android/internal/telephony/CommandsInterface.java
index 259acdb..21e9e44 100644
--- a/telephony/java/com/android/internal/telephony/CommandsInterface.java
+++ b/telephony/java/com/android/internal/telephony/CommandsInterface.java
@@ -216,10 +216,10 @@
void registerForCallStateChanged(Handler h, int what, Object obj);
void unregisterForCallStateChanged(Handler h);
- void registerForNetworkStateChanged(Handler h, int what, Object obj);
- void unregisterForNetworkStateChanged(Handler h);
- void registerForDataStateChanged(Handler h, int what, Object obj);
- void unregisterForDataStateChanged(Handler h);
+ void registerForVoiceNetworkStateChanged(Handler h, int what, Object obj);
+ void unregisterForVoiceNetworkStateChanged(Handler h);
+ void registerForDataNetworkStateChanged(Handler h, int what, Object obj);
+ void unregisterForDataNetworkStateChanged(Handler h);
void registerForRadioTechnologyChanged(Handler h, int what, Object obj);
void unregisterForRadioTechnologyChanged(Handler h);
@@ -549,6 +549,39 @@
void registerForResendIncallMute(Handler h, int what, Object obj);
void unregisterForResendIncallMute(Handler h);
+ /**
+ * Registers the handler for when Cdma subscription changed events
+ *
+ * @param h Handler for notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ *
+ */
+ void registerForCdmaSubscriptionChanged(Handler h, int what, Object obj);
+ void unregisterForCdmaSubscriptionChanged(Handler h);
+
+ /**
+ * Registers the handler for when Cdma prl changed events
+ *
+ * @param h Handler for notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ *
+ */
+ void registerForCdmaPrlChanged(Handler h, int what, Object obj);
+ void unregisterForCdmaPrlChanged(Handler h);
+
+ /**
+ * Registers the handler for when Cdma prl changed events
+ *
+ * @param h Handler for notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ *
+ */
+ void registerForExitEmergencyCallbackMode(Handler h, int what, Object obj);
+ void unregisterForExitEmergencyCallbackMode(Handler h);
+
/**
* Supply the ICC PIN to the ICC card
*
@@ -564,7 +597,23 @@
void supplyIccPin(String pin, Message result);
/**
- * Supply the ICC PUK to the ICC card
+ * Supply the PIN for the app with this AID on the ICC card
+ *
+ * AID (Application ID), See ETSI 102.221 8.1 and 101.220 4
+ *
+ * returned message
+ * retMsg.obj = AsyncResult ar
+ * ar.exception carries exception on failure
+ * This exception is CommandException with an error of PASSWORD_INCORRECT
+ * if the password is incorrect
+ *
+ * ar.exception and ar.result are null on success
+ */
+
+ void supplyIccPinForApp(String pin, String aid, Message result);
+
+ /**
+ * Supply the ICC PUK and newPin to the ICC card
*
* returned message
* retMsg.obj = AsyncResult ar
@@ -578,6 +627,22 @@
void supplyIccPuk(String puk, String newPin, Message result);
/**
+ * Supply the PUK, new pin for the app with this AID on the ICC card
+ *
+ * AID (Application ID), See ETSI 102.221 8.1 and 101.220 4
+ *
+ * returned message
+ * retMsg.obj = AsyncResult ar
+ * ar.exception carries exception on failure
+ * This exception is CommandException with an error of PASSWORD_INCORRECT
+ * if the password is incorrect
+ *
+ * ar.exception and ar.result are null on success
+ */
+
+ void supplyIccPukForApp(String puk, String newPin, String aid, Message result);
+
+ /**
* Supply the ICC PIN2 to the ICC card
* Only called following operation where ICC_PIN2 was
* returned as a a failure from a previous operation
@@ -594,6 +659,24 @@
void supplyIccPin2(String pin2, Message result);
/**
+ * Supply the PIN2 for the app with this AID on the ICC card
+ * Only called following operation where ICC_PIN2 was
+ * returned as a a failure from a previous operation
+ *
+ * AID (Application ID), See ETSI 102.221 8.1 and 101.220 4
+ *
+ * returned message
+ * retMsg.obj = AsyncResult ar
+ * ar.exception carries exception on failure
+ * This exception is CommandException with an error of PASSWORD_INCORRECT
+ * if the password is incorrect
+ *
+ * ar.exception and ar.result are null on success
+ */
+
+ void supplyIccPin2ForApp(String pin2, String aid, Message result);
+
+ /**
* Supply the SIM PUK2 to the SIM card
* Only called following operation where SIM_PUK2 was
* returned as a a failure from a previous operation
@@ -609,8 +692,28 @@
void supplyIccPuk2(String puk2, String newPin2, Message result);
+ /**
+ * Supply the PUK2, newPin2 for the app with this AID on the ICC card
+ * Only called following operation where SIM_PUK2 was
+ * returned as a a failure from a previous operation
+ *
+ * AID (Application ID), See ETSI 102.221 8.1 and 101.220 4
+ *
+ * returned message
+ * retMsg.obj = AsyncResult ar
+ * ar.exception carries exception on failure
+ * This exception is CommandException with an error of PASSWORD_INCORRECT
+ * if the password is incorrect
+ *
+ * ar.exception and ar.result are null on success
+ */
+
+ void supplyIccPuk2ForApp(String puk2, String newPin2, String aid, Message result);
+
void changeIccPin(String oldPin, String newPin, Message result);
+ void changeIccPinForApp(String oldPin, String newPin, String aidPtr, Message result);
void changeIccPin2(String oldPin2, String newPin2, Message result);
+ void changeIccPin2ForApp(String oldPin2, String newPin2, String aidPtr, Message result);
void changeBarringPassword(String facility, String oldPwd, String newPwd, Message result);
@@ -853,7 +956,7 @@
* Please note that registration state 4 ("unknown") is treated
* as "out of service" above
*/
- void getRegistrationState (Message response);
+ void getVoiceRegistrationState (Message response);
/**
* response.obj.result is an int[3]
@@ -865,7 +968,7 @@
* Please note that registration state 4 ("unknown") is treated
* as "out of service" above
*/
- void getGPRSRegistrationState (Message response);
+ void getDataRegistrationState (Message response);
/**
* response.obj.result is a String[3]
@@ -1286,7 +1389,7 @@
* @param cdmaSubscriptionType one of CDMA_SUBSCRIPTION_*
* @param response is callback message
*/
- void setCdmaSubscription(int cdmaSubscriptionType, Message response);
+ void setCdmaSubscriptionSource(int cdmaSubscriptionType, Message response);
/**
* Set the TTY mode
diff --git a/telephony/java/com/android/internal/telephony/DataCallState.java b/telephony/java/com/android/internal/telephony/DataCallState.java
index df12153..fda1e47 100644
--- a/telephony/java/com/android/internal/telephony/DataCallState.java
+++ b/telephony/java/com/android/internal/telephony/DataCallState.java
@@ -30,6 +30,7 @@
public String ifname = "";
public String [] addresses = new String[0];
public String [] dnses = new String[0];
+ public String[] gateways = new String[0];
@Override
public String toString() {
@@ -53,6 +54,12 @@
sb.append(",");
}
if (dnses.length > 0) sb.deleteCharAt(sb.length()-1);
+ sb.append("] gateways=[");
+ for (String addr : gateways) {
+ sb.append(addr);
+ sb.append(",");
+ }
+ if (gateways.length > 0) sb.deleteCharAt(sb.length()-1);
sb.append("]}");
return sb.toString();
}
diff --git a/telephony/java/com/android/internal/telephony/DataConnection.java b/telephony/java/com/android/internal/telephony/DataConnection.java
index aa9c4a3..e0b9603 100644
--- a/telephony/java/com/android/internal/telephony/DataConnection.java
+++ b/telephony/java/com/android/internal/telephony/DataConnection.java
@@ -398,22 +398,34 @@
// a failure we'll clear again at the bottom of this code.
LinkProperties linkProperties = new LinkProperties();
if (response.status == FailCause.NONE.getErrorCode()) {
+ String propertyPrefix = "net." + response.ifname + ".";
+
try {
cid = response.cid;
linkProperties.setInterfaceName(response.ifname);
if (response.addresses != null && response.addresses.length > 0) {
for (String addr : response.addresses) {
LinkAddress la;
+ int addrPrefixLen;
+
+ String [] ap = addr.split("/");
+ if (ap.length == 2) {
+ addr = ap[0];
+ addrPrefixLen = Integer.parseInt(ap[1]);
+ } else {
+ addrPrefixLen = 0;
+ }
if (!InetAddress.isNumeric(addr)) {
EventLogTags.writeBadIpAddress(addr);
throw new UnknownHostException("Non-numeric ip addr=" + addr);
}
InetAddress ia = InetAddress.getByName(addr);
- if (ia instanceof Inet4Address) {
- la = new LinkAddress(ia, 32);
- } else {
- la = new LinkAddress(ia, 128);
+ if (addrPrefixLen == 0) {
+ // Assume point to point
+ addrPrefixLen = (ia instanceof Inet4Address) ? 32 : 128;
}
+ if (DBG) log("addr/pl=" + addr + "/" + addrPrefixLen);
+ la = new LinkAddress(ia, addrPrefixLen);
linkProperties.addLinkAddress(la);
}
} else {
@@ -431,11 +443,9 @@
}
result = SetupResult.SUCCESS;
} else {
- String prefix = "net." + response.ifname + ".";
-
String dnsServers[] = new String[2];
- dnsServers[0] = SystemProperties.get(prefix + "dns1");
- dnsServers[1] = SystemProperties.get(prefix + "dns2");
+ dnsServers[0] = SystemProperties.get(propertyPrefix + "dns1");
+ dnsServers[1] = SystemProperties.get(propertyPrefix + "dns2");
if (isDnsOk(dnsServers)) {
for (String dnsAddr : dnsServers) {
if (!InetAddress.isNumeric(dnsAddr)) {
@@ -457,6 +467,23 @@
throw new UnknownHostException("Unacceptable dns addresses=" + sb);
}
}
+ if ((response.gateways == null) || (response.gateways.length == 0)) {
+ String gateways = SystemProperties.get(propertyPrefix + "gw");
+ if (gateways != null) {
+ response.gateways = gateways.split(" ");
+ } else {
+ response.gateways = new String[0];
+ }
+ }
+ for (String addr : response.gateways) {
+ if (!InetAddress.isNumeric(addr)) {
+ EventLogTags.writePdpBadDnsAddress("gateway=" + addr);
+ throw new UnknownHostException("Non-numeric gateway addr=" + addr);
+ }
+ InetAddress ia = InetAddress.getByName(addr);
+ linkProperties.addGateway(ia);
+ }
+ result = SetupResult.SUCCESS;
} catch (UnknownHostException e) {
log("onSetupCompleted: UnknownHostException " + e);
e.printStackTrace();
diff --git a/telephony/java/com/android/internal/telephony/IccCardStatus.java b/telephony/java/com/android/internal/telephony/IccCardStatus.java
index 0e7bad7..7199616 100644
--- a/telephony/java/com/android/internal/telephony/IccCardStatus.java
+++ b/telephony/java/com/android/internal/telephony/IccCardStatus.java
@@ -34,7 +34,7 @@
boolean isCardPresent() {
return this == CARDSTATE_PRESENT;
}
- };
+ }
public enum PinState {
PINSTATE_UNKNOWN,
@@ -43,12 +43,13 @@
PINSTATE_DISABLED,
PINSTATE_ENABLED_BLOCKED,
PINSTATE_ENABLED_PERM_BLOCKED
- };
+ }
private CardState mCardState;
private PinState mUniversalPinState;
private int mGsmUmtsSubscriptionAppIndex;
private int mCdmaSubscriptionAppIndex;
+ private int mImsSubscriptionAppIndex;
private int mNumApplications;
private ArrayList<IccCardApplication> mApplications =
@@ -74,6 +75,10 @@
}
}
+ public PinState getUniversalPinState() {
+ return mUniversalPinState;
+ }
+
public void setUniversalPinState(int state) {
switch(state) {
case 0:
@@ -115,6 +120,14 @@
mCdmaSubscriptionAppIndex = cdmaSubscriptionAppIndex;
}
+ public int getImsSubscriptionAppIndex() {
+ return mImsSubscriptionAppIndex;
+ }
+
+ public void setImsSubscriptionAppIndex(int imsSubscriptionAppIndex) {
+ mImsSubscriptionAppIndex = imsSubscriptionAppIndex;
+ }
+
public int getNumApplications() {
return mNumApplications;
}
@@ -130,4 +143,5 @@
public IccCardApplication getApplication(int index) {
return mApplications.get(index);
}
+
}
diff --git a/telephony/java/com/android/internal/telephony/PhoneBase.java b/telephony/java/com/android/internal/telephony/PhoneBase.java
index cbff130..54341b1 100644
--- a/telephony/java/com/android/internal/telephony/PhoneBase.java
+++ b/telephony/java/com/android/internal/telephony/PhoneBase.java
@@ -679,7 +679,7 @@
* Set the status of the CDMA subscription mode
*/
public void setCdmaSubscription(int cdmaSubscriptionType, Message response) {
- mCM.setCdmaSubscription(cdmaSubscriptionType, response);
+ mCM.setCdmaSubscriptionSource(cdmaSubscriptionType, response);
}
/**
diff --git a/telephony/java/com/android/internal/telephony/PhoneStateIntentReceiver.java b/telephony/java/com/android/internal/telephony/PhoneStateIntentReceiver.java
index b31161c..898e624 100644
--- a/telephony/java/com/android/internal/telephony/PhoneStateIntentReceiver.java
+++ b/telephony/java/com/android/internal/telephony/PhoneStateIntentReceiver.java
@@ -95,25 +95,17 @@
}
/**
- * Returns current signal strength in "asu", ranging from 0-31
- * or -1 if unknown
+ * Returns current signal strength in as an asu 0..31
*
- * For GSM, dBm = -113 + 2*asu
- * 0 means "-113 dBm or less"
- * 31 means "-51 dBm or greater"
- *
- * @return signal strength in asu, -1 if not yet updated
* Throws RuntimeException if client has not called notifySignalStrength()
*/
- public int getSignalStrength() {
+ public int getSignalStrengthLevelAsu() {
// TODO: use new SignalStrength instead of asu
if ((mWants & NOTIF_SIGNAL) == 0) {
throw new RuntimeException
("client must call notifySignalStrength(int)");
}
- int gsmSignalStrength = mSignalStrength.getGsmSignalStrength();
-
- return (gsmSignalStrength == 99 ? -1 : gsmSignalStrength);
+ return mSignalStrength.getAsuLevel();
}
/**
@@ -128,19 +120,7 @@
throw new RuntimeException
("client must call notifySignalStrength(int)");
}
-
- int dBm = -1;
-
- if(!mSignalStrength.isGsm()) {
- dBm = mSignalStrength.getCdmaDbm();
- } else {
- int gsmSignalStrength = mSignalStrength.getGsmSignalStrength();
- int asu = (gsmSignalStrength == 99 ? -1 : gsmSignalStrength);
- if (asu != -1) {
- dBm = -113 + 2*asu;
- }
- }
- return dBm;
+ return mSignalStrength.getDbm();
}
public void notifyPhoneCallState(int eventWhat) {
diff --git a/telephony/java/com/android/internal/telephony/RIL.java b/telephony/java/com/android/internal/telephony/RIL.java
index 3ef1924..76c6229 100644
--- a/telephony/java/com/android/internal/telephony/RIL.java
+++ b/telephony/java/com/android/internal/telephony/RIL.java
@@ -224,7 +224,6 @@
RILSender mSender;
Thread mReceiverThread;
RILReceiver mReceiver;
- private Context mContext;
WakeLock mWakeLock;
int mWakeLockTimeout;
// The number of requests pending to be sent out, it increases before calling
@@ -649,8 +648,6 @@
mRequestMessagesPending = 0;
mRequestMessagesWaiting = 0;
- mContext = context;
-
mSenderThread = new HandlerThread("RILSender");
mSenderThread.start();
@@ -693,90 +690,126 @@
send(rr);
}
- public void
+ @Override public void
supplyIccPin(String pin, Message result) {
+ supplyIccPinForApp(pin, null, result);
+ }
+
+ @Override public void
+ supplyIccPinForApp(String pin, String aid, Message result) {
//Note: This RIL request has not been renamed to ICC,
// but this request is also valid for SIM and RUIM
RILRequest rr = RILRequest.obtain(RIL_REQUEST_ENTER_SIM_PIN, result);
if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
- rr.mp.writeInt(1);
+ rr.mp.writeInt(2);
rr.mp.writeString(pin);
+ rr.mp.writeString(aid);
send(rr);
}
- public void
+ @Override public void
supplyIccPuk(String puk, String newPin, Message result) {
+ supplyIccPukForApp(puk, newPin, null, result);
+ }
+
+ @Override public void
+ supplyIccPukForApp(String puk, String newPin, String aid, Message result) {
//Note: This RIL request has not been renamed to ICC,
// but this request is also valid for SIM and RUIM
RILRequest rr = RILRequest.obtain(RIL_REQUEST_ENTER_SIM_PUK, result);
if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
- rr.mp.writeInt(2);
+ rr.mp.writeInt(3);
rr.mp.writeString(puk);
rr.mp.writeString(newPin);
+ rr.mp.writeString(aid);
send(rr);
}
- public void
+ @Override public void
supplyIccPin2(String pin, Message result) {
+ supplyIccPin2ForApp(pin, null, result);
+ }
+
+ @Override public void
+ supplyIccPin2ForApp(String pin, String aid, Message result) {
//Note: This RIL request has not been renamed to ICC,
// but this request is also valid for SIM and RUIM
RILRequest rr = RILRequest.obtain(RIL_REQUEST_ENTER_SIM_PIN2, result);
if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
- rr.mp.writeInt(1);
+ rr.mp.writeInt(2);
rr.mp.writeString(pin);
+ rr.mp.writeString(aid);
send(rr);
}
- public void
- supplyIccPuk2(String puk, String newPin2, Message result) {
+ @Override public void
+ supplyIccPuk2(String puk2, String newPin2, Message result) {
+ supplyIccPuk2ForApp(puk2, newPin2, null, result);
+ }
+
+ @Override public void
+ supplyIccPuk2ForApp(String puk, String newPin2, String aid, Message result) {
//Note: This RIL request has not been renamed to ICC,
// but this request is also valid for SIM and RUIM
RILRequest rr = RILRequest.obtain(RIL_REQUEST_ENTER_SIM_PUK2, result);
if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
- rr.mp.writeInt(2);
+ rr.mp.writeInt(3);
rr.mp.writeString(puk);
rr.mp.writeString(newPin2);
+ rr.mp.writeString(aid);
send(rr);
}
- public void
+ @Override public void
changeIccPin(String oldPin, String newPin, Message result) {
+ changeIccPinForApp(oldPin, newPin, null, result);
+ }
+
+ @Override public void
+ changeIccPinForApp(String oldPin, String newPin, String aid, Message result) {
//Note: This RIL request has not been renamed to ICC,
// but this request is also valid for SIM and RUIM
RILRequest rr = RILRequest.obtain(RIL_REQUEST_CHANGE_SIM_PIN, result);
if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
- rr.mp.writeInt(2);
+ rr.mp.writeInt(3);
rr.mp.writeString(oldPin);
rr.mp.writeString(newPin);
+ rr.mp.writeString(aid);
send(rr);
}
- public void
+ @Override public void
changeIccPin2(String oldPin2, String newPin2, Message result) {
+ changeIccPin2ForApp(oldPin2, newPin2, null, result);
+ }
+
+ @Override public void
+ changeIccPin2ForApp(String oldPin2, String newPin2, String aid, Message result) {
//Note: This RIL request has not been renamed to ICC,
// but this request is also valid for SIM and RUIM
RILRequest rr = RILRequest.obtain(RIL_REQUEST_CHANGE_SIM_PIN2, result);
if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
- rr.mp.writeInt(2);
+ rr.mp.writeInt(3);
rr.mp.writeString(oldPin2);
rr.mp.writeString(newPin2);
+ rr.mp.writeString(aid);
send(rr);
}
@@ -1072,9 +1105,9 @@
}
public void
- getRegistrationState (Message result) {
+ getVoiceRegistrationState (Message result) {
RILRequest rr
- = RILRequest.obtain(RIL_REQUEST_REGISTRATION_STATE, result);
+ = RILRequest.obtain(RIL_REQUEST_VOICE_REGISTRATION_STATE, result);
if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
@@ -1082,9 +1115,9 @@
}
public void
- getGPRSRegistrationState (Message result) {
+ getDataRegistrationState (Message result) {
RILRequest rr
- = RILRequest.obtain(RIL_REQUEST_GPRS_REGISTRATION_STATE, result);
+ = RILRequest.obtain(RIL_REQUEST_DATA_REGISTRATION_STATE, result);
if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
@@ -1357,7 +1390,7 @@
send(rrPnt);
RILRequest rrCs = RILRequest.obtain(
- RIL_REQUEST_CDMA_SET_SUBSCRIPTION, null);
+ RIL_REQUEST_CDMA_SET_SUBSCRIPTION_SOURCE, null);
rrCs.mp.writeInt(1);
rrCs.mp.writeInt(mCdmaSubscription);
if (RILJ_LOGD) riljLog(rrCs.serialString() + "> "
@@ -2171,8 +2204,8 @@
case RIL_REQUEST_UDUB: ret = responseVoid(p); break;
case RIL_REQUEST_LAST_CALL_FAIL_CAUSE: ret = responseInts(p); break;
case RIL_REQUEST_SIGNAL_STRENGTH: ret = responseSignalStrength(p); break;
- case RIL_REQUEST_REGISTRATION_STATE: ret = responseStrings(p); break;
- case RIL_REQUEST_GPRS_REGISTRATION_STATE: ret = responseStrings(p); break;
+ case RIL_REQUEST_VOICE_REGISTRATION_STATE: ret = responseStrings(p); break;
+ case RIL_REQUEST_DATA_REGISTRATION_STATE: ret = responseStrings(p); break;
case RIL_REQUEST_OPERATOR: ret = responseStrings(p); break;
case RIL_REQUEST_RADIO_POWER: ret = responseVoid(p); break;
case RIL_REQUEST_DTMF: ret = responseVoid(p); break;
@@ -2228,7 +2261,7 @@
case RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE: ret = responseInts(p); break;
case RIL_REQUEST_GET_NEIGHBORING_CELL_IDS: ret = responseCellList(p); break;
case RIL_REQUEST_SET_LOCATION_UPDATES: ret = responseVoid(p); break;
- case RIL_REQUEST_CDMA_SET_SUBSCRIPTION: ret = responseVoid(p); break;
+ case RIL_REQUEST_CDMA_SET_SUBSCRIPTION_SOURCE: ret = responseVoid(p); break;
case RIL_REQUEST_CDMA_SET_ROAMING_PREFERENCE: ret = responseVoid(p); break;
case RIL_REQUEST_CDMA_QUERY_ROAMING_PREFERENCE: ret = responseInts(p); break;
case RIL_REQUEST_SET_TTY_MODE: ret = responseVoid(p); break;
@@ -2255,6 +2288,7 @@
case RIL_REQUEST_EXIT_EMERGENCY_CALLBACK_MODE: ret = responseVoid(p); break;
case RIL_REQUEST_REPORT_SMS_MEMORY_STATUS: ret = responseVoid(p); break;
case RIL_REQUEST_REPORT_STK_SERVICE_IS_RUNNING: ret = responseVoid(p); break;
+ case RIL_REQUEST_CDMA_GET_SUBSCRIPTION_SOURCE: ret = responseInts(p); break;
default:
throw new RuntimeException("Unrecognized solicited response: " + rr.mRequest);
//break;
@@ -2368,7 +2402,7 @@
case RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED: ret = responseVoid(p); break;
case RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED: ret = responseVoid(p); break;
- case RIL_UNSOL_RESPONSE_NETWORK_STATE_CHANGED: ret = responseVoid(p); break;
+ case RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED: ret = responseVoid(p); break;
case RIL_UNSOL_RESPONSE_NEW_SMS: ret = responseString(p); break;
case RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT: ret = responseString(p); break;
case RIL_UNSOL_RESPONSE_NEW_SMS_ON_SIM: ret = responseInts(p); break;
@@ -2396,6 +2430,9 @@
case RIL_UNSOL_OEM_HOOK_RAW: ret = responseRaw(p); break;
case RIL_UNSOL_RINGBACK_TONE: ret = responseInts(p); break;
case RIL_UNSOL_RESEND_INCALL_MUTE: ret = responseVoid(p); break;
+ case RIL_UNSOL_CDMA_SUBSCRIPTION_CHANGED: ret = responseInts(p); break;
+ case RIL_UNSOl_CDMA_PRL_CHANGED: ret = responseInts(p); break;
+ case RIL_UNSOL_EXIT_EMERGENCY_CALLBACK_MODE: ret = responseVoid(p); break;
default:
throw new RuntimeException("Unrecognized unsol response: " + response);
@@ -2420,10 +2457,10 @@
mCallStateRegistrants
.notifyRegistrants(new AsyncResult(null, null, null));
break;
- case RIL_UNSOL_RESPONSE_NETWORK_STATE_CHANGED:
+ case RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED:
if (RILJ_LOGD) unsljLog(response);
- mNetworkStateRegistrants
+ mVoiceNetworkStateRegistrants
.notifyRegistrants(new AsyncResult(null, null, null));
break;
case RIL_UNSOL_RESPONSE_NEW_SMS: {
@@ -2515,7 +2552,7 @@
case RIL_UNSOL_DATA_CALL_LIST_CHANGED:
if (RILJ_LOGD) unsljLogRet(response, ret);
- mDataConnectionRegistrants.notifyRegistrants(new AsyncResult(null, ret, null));
+ mDataNetworkStateRegistrants.notifyRegistrants(new AsyncResult(null, ret, null));
break;
case RIL_UNSOL_SUPP_SVC_NOTIFICATION:
@@ -2698,6 +2735,34 @@
mResendIncallMuteRegistrants.notifyRegistrants(
new AsyncResult (null, ret, null));
}
+ break;
+
+ case RIL_UNSOL_CDMA_SUBSCRIPTION_CHANGED:
+ if (RILJ_LOGD) unsljLogRet(response, ret);
+
+ if (mCdmaSubscriptionChangedRegistrants != null) {
+ mCdmaSubscriptionChangedRegistrants.notifyRegistrants(
+ new AsyncResult (null, ret, null));
+ }
+ break;
+
+ case RIL_UNSOl_CDMA_PRL_CHANGED:
+ if (RILJ_LOGD) unsljLogRet(response, ret);
+
+ if (mCdmaPrlChangedRegistrants != null) {
+ mCdmaPrlChangedRegistrants.notifyRegistrants(
+ new AsyncResult (null, ret, null));
+ }
+ break;
+
+ case RIL_UNSOL_EXIT_EMERGENCY_CALLBACK_MODE:
+ if (RILJ_LOGD) unsljLogRet(response, ret);
+
+ if (mExitEmergencyCallbackModeRegistrants != null) {
+ mExitEmergencyCallbackModeRegistrants.notifyRegistrants(
+ new AsyncResult (null, null, null));
+ }
+ break;
}
}
@@ -2848,6 +2913,7 @@
status.setUniversalPinState(p.readInt());
status.setGsmUmtsSubscriptionAppIndex(p.readInt());
status.setCdmaSubscriptionAppIndex(p.readInt());
+ status.setImsSubscriptionAppIndex(p.readInt());
int numApplications = p.readInt();
// limit to maximum allowed applications
@@ -2905,16 +2971,15 @@
dc.uusInfo.setDcs(p.readInt());
byte[] userData = p.createByteArray();
dc.uusInfo.setUserData(userData);
- Log
- .v(LOG_TAG, String.format("Incoming UUS : type=%d, dcs=%d, length=%d",
+ riljLogv(String.format("Incoming UUS : type=%d, dcs=%d, length=%d",
dc.uusInfo.getType(), dc.uusInfo.getDcs(),
dc.uusInfo.getUserData().length));
- Log.v(LOG_TAG, "Incoming UUS : data (string)="
+ riljLogv("Incoming UUS : data (string)="
+ new String(dc.uusInfo.getUserData()));
- Log.v(LOG_TAG, "Incoming UUS : data (hex): "
+ riljLogv("Incoming UUS : data (hex): "
+ IccUtils.bytesToHexString(dc.uusInfo.getUserData()));
} else {
- Log.v(LOG_TAG, "Incoming UUS : NOT present!");
+ riljLogv("Incoming UUS : NOT present!");
}
// Make sure there's a leading + on addresses with a TOA of 145
@@ -2924,10 +2989,10 @@
if (dc.isVoicePrivacy) {
mVoicePrivacyOnRegistrants.notifyRegistrants();
- Log.d(LOG_TAG, "InCall VoicePrivacy is enabled");
+ riljLog("InCall VoicePrivacy is enabled");
} else {
mVoicePrivacyOffRegistrants.notifyRegistrants();
- Log.d(LOG_TAG, "InCall VoicePrivacy is disabled");
+ riljLog("InCall VoicePrivacy is disabled");
}
}
@@ -2944,7 +3009,6 @@
dataCall.cid = p.readInt();
dataCall.active = p.readInt();
dataCall.type = p.readString();
- p.readString(); // Ignore apn
String addresses = p.readString();
if (TextUtils.isEmpty(addresses)) {
dataCall.addresses = addresses.split(" ");
@@ -2966,6 +3030,10 @@
if (!TextUtils.isEmpty(dnses)) {
dataCall.dnses = dnses.split(" ");
}
+ String gateways = p.readString();
+ if (!TextUtils.isEmpty(gateways)) {
+ dataCall.gateways = gateways.split(" ");
+ }
}
return dataCall;
}
@@ -2976,7 +3044,7 @@
int ver = p.readInt();
int num = p.readInt();
- Log.d(LOG_TAG, "responseDataCallList ver=" + ver + " num=" + num);
+ riljLog("responseDataCallList ver=" + ver + " num=" + num);
response = new ArrayList<DataCallState>(num);
for (int i = 0; i < num; i++) {
@@ -2990,7 +3058,7 @@
responseSetupDataCall(Parcel p) {
int ver = p.readInt();
int num = p.readInt();
- Log.d(LOG_TAG, "responseSetupDataCall ver=" + ver + " num=" + num);
+ if (RILJ_LOGD) riljLog("responseSetupDataCall ver=" + ver + " num=" + num);
DataCallState dataCall;
@@ -3009,11 +3077,18 @@
}
if (num >= 4) {
String dnses = p.readString();
- Log.d(LOG_TAG, "responseSetupDataCall got dnses=" + dnses);
+ if (RILJ_LOGD) riljLog("responseSetupDataCall got dnses=" + dnses);
if (!TextUtils.isEmpty(dnses)) {
dataCall.dnses = dnses.split(" ");
}
}
+ if (num >= 5) {
+ String gateways = p.readString();
+ if (RILJ_LOGD) riljLog("responseSetupDataCall got gateways=" + gateways);
+ if (!TextUtils.isEmpty(gateways)) {
+ dataCall.gateways = gateways.split(" ");
+ }
+ }
} else {
if (num != 1) {
throw new RuntimeException(
@@ -3156,7 +3231,7 @@
private Object
responseSignalStrength(Parcel p) {
- int numInts = 7;
+ int numInts = 12;
int response[];
/* TODO: Add SignalStrength class to match RIL_SignalStrength */
@@ -3200,6 +3275,8 @@
notification.signalType = p.readInt();
notification.alertPitch = p.readInt();
notification.signal = p.readInt();
+ notification.numberType = p.readInt();
+ notification.numberPlan = p.readInt();
return notification;
}
@@ -3291,8 +3368,8 @@
case RIL_REQUEST_UDUB: return "UDUB";
case RIL_REQUEST_LAST_CALL_FAIL_CAUSE: return "LAST_CALL_FAIL_CAUSE";
case RIL_REQUEST_SIGNAL_STRENGTH: return "SIGNAL_STRENGTH";
- case RIL_REQUEST_REGISTRATION_STATE: return "REGISTRATION_STATE";
- case RIL_REQUEST_GPRS_REGISTRATION_STATE: return "GPRS_REGISTRATION_STATE";
+ case RIL_REQUEST_VOICE_REGISTRATION_STATE: return "VOICE_REGISTRATION_STATE";
+ case RIL_REQUEST_DATA_REGISTRATION_STATE: return "DATA_REGISTRATION_STATE";
case RIL_REQUEST_OPERATOR: return "OPERATOR";
case RIL_REQUEST_RADIO_POWER: return "RADIO_POWER";
case RIL_REQUEST_DTMF: return "DTMF";
@@ -3348,7 +3425,7 @@
case RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE: return "REQUEST_GET_PREFERRED_NETWORK_TYPE";
case RIL_REQUEST_GET_NEIGHBORING_CELL_IDS: return "REQUEST_GET_NEIGHBORING_CELL_IDS";
case RIL_REQUEST_SET_LOCATION_UPDATES: return "REQUEST_SET_LOCATION_UPDATES";
- case RIL_REQUEST_CDMA_SET_SUBSCRIPTION: return "RIL_REQUEST_CDMA_SET_SUBSCRIPTION";
+ case RIL_REQUEST_CDMA_SET_SUBSCRIPTION_SOURCE: return "RIL_REQUEST_CDMA_SET_SUBSCRIPTION_SOURCE";
case RIL_REQUEST_CDMA_SET_ROAMING_PREFERENCE: return "RIL_REQUEST_CDMA_SET_ROAMING_PREFERENCE";
case RIL_REQUEST_CDMA_QUERY_ROAMING_PREFERENCE: return "RIL_REQUEST_CDMA_QUERY_ROAMING_PREFERENCE";
case RIL_REQUEST_SET_TTY_MODE: return "RIL_REQUEST_SET_TTY_MODE";
@@ -3375,6 +3452,7 @@
case RIL_REQUEST_EXIT_EMERGENCY_CALLBACK_MODE: return "REQUEST_EXIT_EMERGENCY_CALLBACK_MODE";
case RIL_REQUEST_REPORT_SMS_MEMORY_STATUS: return "RIL_REQUEST_REPORT_SMS_MEMORY_STATUS";
case RIL_REQUEST_REPORT_STK_SERVICE_IS_RUNNING: return "RIL_REQUEST_REPORT_STK_SERVICE_IS_RUNNING";
+ case RIL_REQUEST_CDMA_GET_SUBSCRIPTION_SOURCE: return "RIL_REQUEST_CDMA_GET_SUBSCRIPTION_SOURCE";
default: return "<unknown request>";
}
}
@@ -3390,7 +3468,7 @@
switch(request) {
case RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED: return "UNSOL_RESPONSE_RADIO_STATE_CHANGED";
case RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED: return "UNSOL_RESPONSE_CALL_STATE_CHANGED";
- case RIL_UNSOL_RESPONSE_NETWORK_STATE_CHANGED: return "UNSOL_RESPONSE_NETWORK_STATE_CHANGED";
+ case RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED: return "UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED";
case RIL_UNSOL_RESPONSE_NEW_SMS: return "UNSOL_RESPONSE_NEW_SMS";
case RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT: return "UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT";
case RIL_UNSOL_RESPONSE_NEW_SMS_ON_SIM: return "UNSOL_RESPONSE_NEW_SMS_ON_SIM";
@@ -3419,6 +3497,9 @@
case RIL_UNSOL_OEM_HOOK_RAW: return "UNSOL_OEM_HOOK_RAW";
case RIL_UNSOL_RINGBACK_TONE: return "UNSOL_RINGBACK_TONG";
case RIL_UNSOL_RESEND_INCALL_MUTE: return "UNSOL_RESEND_INCALL_MUTE";
+ case RIL_UNSOL_CDMA_SUBSCRIPTION_CHANGED: return "CDMA_SUBSCRIPTION_CHANGED";
+ case RIL_UNSOl_CDMA_PRL_CHANGED: return "UNSOL_CDMA_PRL_CHANGED";
+ case RIL_UNSOL_EXIT_EMERGENCY_CALLBACK_MODE: return "UNSOL_EXIT_EMERGENCY_CALLBACK_MODE";
default: return "<unknown reponse>";
}
}
@@ -3502,9 +3583,9 @@
/**
* {@inheritDoc}
*/
- public void setCdmaSubscription(int cdmaSubscription , Message response) {
+ public void setCdmaSubscriptionSource(int cdmaSubscription , Message response) {
RILRequest rr = RILRequest.obtain(
- RILConstants.RIL_REQUEST_CDMA_SET_SUBSCRIPTION, response);
+ RILConstants.RIL_REQUEST_CDMA_SET_SUBSCRIPTION_SOURCE, response);
rr.mp.writeInt(1);
rr.mp.writeInt(cdmaSubscription);
@@ -3518,6 +3599,19 @@
/**
* {@inheritDoc}
*/
+ public void getCdmaSubscriptionSource(int cdmaSubscription , Message response) {
+ RILRequest rr = RILRequest.obtain(
+ RILConstants.RIL_REQUEST_CDMA_GET_SUBSCRIPTION_SOURCE, response);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+ + " : " + cdmaSubscription);
+
+ send(rr);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
public void queryTTYMode(Message response) {
RILRequest rr = RILRequest.obtain(
RILConstants.RIL_REQUEST_QUERY_TTY_MODE, response);
@@ -3598,4 +3692,5 @@
send(rr);
}
+
}
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 9b21de8..cdf1977 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -162,8 +162,8 @@
int RIL_REQUEST_UDUB = 17;
int RIL_REQUEST_LAST_CALL_FAIL_CAUSE = 18;
int RIL_REQUEST_SIGNAL_STRENGTH = 19;
- int RIL_REQUEST_REGISTRATION_STATE = 20;
- int RIL_REQUEST_GPRS_REGISTRATION_STATE = 21;
+ int RIL_REQUEST_VOICE_REGISTRATION_STATE = 20;
+ int RIL_REQUEST_DATA_REGISTRATION_STATE = 21;
int RIL_REQUEST_OPERATOR = 22;
int RIL_REQUEST_RADIO_POWER = 23;
int RIL_REQUEST_DTMF = 24;
@@ -219,7 +219,7 @@
int RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE = 74;
int RIL_REQUEST_GET_NEIGHBORING_CELL_IDS = 75;
int RIL_REQUEST_SET_LOCATION_UPDATES = 76;
- int RIL_REQUEST_CDMA_SET_SUBSCRIPTION = 77;
+ int RIL_REQUEST_CDMA_SET_SUBSCRIPTION_SOURCE = 77;
int RIL_REQUEST_CDMA_SET_ROAMING_PREFERENCE = 78;
int RIL_REQUEST_CDMA_QUERY_ROAMING_PREFERENCE = 79;
int RIL_REQUEST_SET_TTY_MODE = 80;
@@ -246,10 +246,11 @@
int RIL_REQUEST_SET_SMSC_ADDRESS = 101;
int RIL_REQUEST_REPORT_SMS_MEMORY_STATUS = 102;
int RIL_REQUEST_REPORT_STK_SERVICE_IS_RUNNING = 103;
+ int RIL_REQUEST_CDMA_GET_SUBSCRIPTION_SOURCE = 104;
int RIL_UNSOL_RESPONSE_BASE = 1000;
int RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED = 1000;
int RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED = 1001;
- int RIL_UNSOL_RESPONSE_NETWORK_STATE_CHANGED = 1002;
+ int RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED = 1002;
int RIL_UNSOL_RESPONSE_NEW_SMS = 1003;
int RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT = 1004;
int RIL_UNSOL_RESPONSE_NEW_SMS_ON_SIM = 1005;
@@ -278,4 +279,7 @@
int RIL_UNSOL_OEM_HOOK_RAW = 1028;
int RIL_UNSOL_RINGBACK_TONE = 1029;
int RIL_UNSOL_RESEND_INCALL_MUTE = 1030;
+ int RIL_UNSOL_CDMA_SUBSCRIPTION_CHANGED = 1031;
+ int RIL_UNSOl_CDMA_PRL_CHANGED = 1032;
+ int RIL_UNSOL_EXIT_EMERGENCY_CALLBACK_MODE = 1033;
}
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaCallWaitingNotification.java b/telephony/java/com/android/internal/telephony/cdma/CdmaCallWaitingNotification.java
index f4119ad..81ff042 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaCallWaitingNotification.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaCallWaitingNotification.java
@@ -26,10 +26,12 @@
*/
public class CdmaCallWaitingNotification {
static final String LOG_TAG = "CDMA";
- public String number =null;
+ public String number = null;
public int numberPresentation = 0;
public String name = null;
public int namePresentation = 0;
+ public int numberType = 0;
+ public int numberPlan = 0;
public int isPresent = 0;
public int signalType = 0;
public int alertPitch = 0;
@@ -42,6 +44,8 @@
+ " numberPresentation: " + numberPresentation
+ " name: " + name
+ " namePresentation: " + namePresentation
+ + " numberType: " + numberType
+ + " numberPlan: " + numberPlan
+ " isPresent: " + isPresent
+ " signalType: " + signalType
+ " alertPitch: " + alertPitch
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
index c324a71..8c36106 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
@@ -101,7 +101,7 @@
p.mCM.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
p.mRuimRecords.registerForRecordsLoaded(this, EVENT_RECORDS_LOADED, null);
p.mCM.registerForNVReady(this, EVENT_NV_READY, null);
- p.mCM.registerForDataStateChanged (this, EVENT_DATA_STATE_CHANGED, null);
+ p.mCM.registerForDataNetworkStateChanged (this, EVENT_DATA_STATE_CHANGED, null);
p.mCT.registerForVoiceCallEnded (this, EVENT_VOICE_CALL_ENDED, null);
p.mCT.registerForVoiceCallStarted (this, EVENT_VOICE_CALL_STARTED, null);
p.mSST.registerForCdmaDataConnectionAttached(this, EVENT_TRY_SETUP_DATA, null);
@@ -125,7 +125,7 @@
mPhone.mCM.unregisterForOffOrNotAvailable(this);
mCdmaPhone.mRuimRecords.unregisterForRecordsLoaded(this);
mPhone.mCM.unregisterForNVReady(this);
- mPhone.mCM.unregisterForDataStateChanged(this);
+ mPhone.mCM.unregisterForDataNetworkStateChanged(this);
mCdmaPhone.mCT.unregisterForVoiceCallEnded(this);
mCdmaPhone.mCT.unregisterForVoiceCallStarted(this);
mCdmaPhone.mSST.unregisterForCdmaDataConnectionAttached(this);
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
index b217f07..0debb42 100755
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
@@ -186,7 +186,7 @@
cm.registerForAvailable(this, EVENT_RADIO_AVAILABLE, null);
cm.registerForRadioStateChanged(this, EVENT_RADIO_STATE_CHANGED, null);
- cm.registerForNetworkStateChanged(this, EVENT_NETWORK_STATE_CHANGED_CDMA, null);
+ cm.registerForVoiceNetworkStateChanged(this, EVENT_NETWORK_STATE_CHANGED_CDMA, null);
cm.setOnNITZTime(this, EVENT_NITZ_TIME, null);
cm.setOnSignalStrengthUpdate(this, EVENT_SIGNAL_STRENGTH_UPDATE, null);
@@ -215,7 +215,7 @@
// Unregister for all events.
cm.unregisterForAvailable(this);
cm.unregisterForRadioStateChanged(this);
- cm.unregisterForNetworkStateChanged(this);
+ cm.unregisterForVoiceNetworkStateChanged(this);
cm.unregisterForRUIMReady(this);
cm.unregisterForNVReady(this);
cm.unregisterForCdmaOtaProvision(this);
@@ -517,7 +517,7 @@
ar = (AsyncResult) msg.obj;
if (ar.exception == null) {
- cm.getRegistrationState(obtainMessage(EVENT_GET_LOC_DONE_CDMA, null));
+ cm.getVoiceRegistrationState(obtainMessage(EVENT_GET_LOC_DONE_CDMA, null));
}
break;
@@ -897,7 +897,8 @@
}
private void setSignalStrengthDefaultValues() {
- mSignalStrength = new SignalStrength(99, -1, -1, -1, -1, -1, -1, false);
+ mSignalStrength = new SignalStrength(99, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, false);
}
/**
@@ -955,8 +956,8 @@
obtainMessage(EVENT_POLL_STATE_OPERATOR_CDMA, pollingContext));
pollingContext[0]++;
- // RIL_REQUEST_REGISTRATION_STATE is necessary for CDMA
- cm.getRegistrationState(
+ // RIL_REQUEST_VOICE_REGISTRATION_STATE is necessary for CDMA
+ cm.getVoiceRegistrationState(
obtainMessage(EVENT_POLL_STATE_REGISTRATION_CDMA, pollingContext));
break;
@@ -1252,7 +1253,7 @@
//log(String.format("onSignalStrengthResult cdmaDbm=%d cdmaEcio=%d evdoRssi=%d evdoEcio=%d evdoSnr=%d",
// cdmaDbm, cdmaEcio, evdoRssi, evdoEcio, evdoSnr));
mSignalStrength = new SignalStrength(99, -1, cdmaDbm, cdmaEcio,
- evdoRssi, evdoEcio, evdoSnr, false);
+ evdoRssi, evdoEcio, evdoSnr, -1, -1, -1, -1, -1, false);
}
try {
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
index f2cdf0c..c57f2f1 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
@@ -141,7 +141,7 @@
p.mCM.registerForAvailable (this, EVENT_RADIO_AVAILABLE, null);
p.mCM.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
p.mSIMRecords.registerForRecordsLoaded(this, EVENT_RECORDS_LOADED, null);
- p.mCM.registerForDataStateChanged (this, EVENT_DATA_STATE_CHANGED, null);
+ p.mCM.registerForDataNetworkStateChanged (this, EVENT_DATA_STATE_CHANGED, null);
p.mCT.registerForVoiceCallEnded (this, EVENT_VOICE_CALL_ENDED, null);
p.mCT.registerForVoiceCallStarted (this, EVENT_VOICE_CALL_STARTED, null);
p.mSST.registerForGprsAttached(this, EVENT_GPRS_ATTACHED, null);
@@ -171,7 +171,7 @@
mPhone.mCM.unregisterForAvailable(this);
mPhone.mCM.unregisterForOffOrNotAvailable(this);
mGsmPhone.mSIMRecords.unregisterForRecordsLoaded(this);
- mPhone.mCM.unregisterForDataStateChanged(this);
+ mPhone.mCM.unregisterForDataNetworkStateChanged(this);
mGsmPhone.mCT.unregisterForVoiceCallEnded(this);
mGsmPhone.mCT.unregisterForVoiceCallStarted(this);
mGsmPhone.mSST.unregisterForGprsAttached(this);
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
index bb99e45..ac83808 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
@@ -79,6 +79,10 @@
private int gprsState = ServiceState.STATE_OUT_OF_SERVICE;
private int newGPRSState = ServiceState.STATE_OUT_OF_SERVICE;
+ private int mMaxDataCalls = 1;
+ private int mNewMaxDataCalls = 1;
+ private int mReasonDataDenied = -1;
+ private int mNewReasonDataDenied = -1;
/**
* Values correspond to ServiceStateTracker.DATA_ACCESS_ definitions.
@@ -212,7 +216,7 @@
cm.registerForAvailable(this, EVENT_RADIO_AVAILABLE, null);
cm.registerForRadioStateChanged(this, EVENT_RADIO_STATE_CHANGED, null);
- cm.registerForNetworkStateChanged(this, EVENT_NETWORK_STATE_CHANGED, null);
+ cm.registerForVoiceNetworkStateChanged(this, EVENT_NETWORK_STATE_CHANGED, null);
cm.setOnNITZTime(this, EVENT_NITZ_TIME, null);
cm.setOnSignalStrengthUpdate(this, EVENT_SIGNAL_STRENGTH_UPDATE, null);
cm.setOnRestrictedStateChanged(this, EVENT_RESTRICTED_STATE_CHANGED, null);
@@ -248,7 +252,7 @@
// Unregister for all events.
cm.unregisterForAvailable(this);
cm.unregisterForRadioStateChanged(this);
- cm.unregisterForNetworkStateChanged(this);
+ cm.unregisterForVoiceNetworkStateChanged(this);
cm.unregisterForSIMReady(this);
phone.mSIMRecords.unregisterForRecordsLoaded(this);
@@ -491,7 +495,7 @@
ar = (AsyncResult) msg.obj;
if (ar.exception == null) {
- cm.getRegistrationState(obtainMessage(EVENT_GET_LOC_DONE, null));
+ cm.getVoiceRegistrationState(obtainMessage(EVENT_GET_LOC_DONE, null));
}
break;
@@ -683,6 +687,7 @@
int lac = -1;
int cid = -1;
int regState = -1;
+ int reasonRegStateDenied = -1;
int psc = -1;
if (states.length > 0) {
try {
@@ -724,6 +729,8 @@
int type = 0;
regState = -1;
+ mNewReasonDataDenied = -1;
+ mNewMaxDataCalls = 1;
if (states.length > 0) {
try {
regState = Integer.parseInt(states[0]);
@@ -732,6 +739,12 @@
if (states.length >= 4 && states[3] != null) {
type = Integer.parseInt(states[3]);
}
+ if ((states.length >= 5 ) && (regState == 3)) {
+ mNewReasonDataDenied = Integer.parseInt(states[4]);
+ }
+ if (states.length >= 6) {
+ mNewMaxDataCalls = Integer.parseInt(states[5]);
+ }
} catch (NumberFormatException ex) {
Log.w(LOG_TAG, "error parsing GprsRegistrationState: " + ex);
}
@@ -785,7 +798,7 @@
}
private void setSignalStrengthDefaultValues() {
- mSignalStrength = new SignalStrength(99, -1, -1, -1, -1, -1, -1, true);
+ mSignalStrength = new SignalStrength(99, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, true);
}
/**
@@ -842,12 +855,12 @@
EVENT_POLL_STATE_OPERATOR, pollingContext));
pollingContext[0]++;
- cm.getGPRSRegistrationState(
+ cm.getDataRegistrationState(
obtainMessage(
EVENT_POLL_STATE_GPRS, pollingContext));
pollingContext[0]++;
- cm.getRegistrationState(
+ cm.getVoiceRegistrationState(
obtainMessage(
EVENT_POLL_STATE_REGISTRATION, pollingContext));
@@ -894,7 +907,11 @@
if (DBG) {
Log.d(LOG_TAG, "Poll ServiceState done: " +
" oldSS=[" + ss + "] newSS=[" + newSS +
- "] oldGprs=" + gprsState + " newGprs=" + newGPRSState +
+ "] oldGprs=" + gprsState + " newData=" + newGPRSState +
+ " oldMaxDataCalls=" + mMaxDataCalls +
+ " mNewMaxDataCalls=" + mNewMaxDataCalls +
+ " oldReasonDataDenied=" + mReasonDataDenied +
+ " mNewReasonDataDenied=" + mNewReasonDataDenied +
" oldType=" + networkTypeToString(networkType) +
" newType=" + networkTypeToString(newNetworkType));
}
@@ -956,6 +973,8 @@
}
gprsState = newGPRSState;
+ mReasonDataDenied = mNewReasonDataDenied;
+ mMaxDataCalls = mNewMaxDataCalls;
networkType = newNetworkType;
// this new state has been applied - forget it until we get a new new state
newNetworkType = 0;
@@ -1158,6 +1177,11 @@
private void onSignalStrengthResult(AsyncResult ar) {
SignalStrength oldSignalStrength = mSignalStrength;
int rssi = 99;
+ int lteSignalStrength = -1;
+ int lteRsrp = -1;
+ int lteRsrq = -1;
+ int lteRssnr = -1;
+ int lteCqi = -1;
if (ar.exception != null) {
// -1 = unknown
@@ -1169,6 +1193,11 @@
// bug 658816 seems to be a case where the result is 0-length
if (ints.length != 0) {
rssi = ints[0];
+ lteSignalStrength = ints[7];
+ lteRsrp = ints[8];
+ lteRsrq = ints[9];
+ lteRssnr = ints[10];
+ lteCqi = ints[11];
} else {
Log.e(LOG_TAG, "Bogus signal strength response");
rssi = 99;
@@ -1176,7 +1205,7 @@
}
mSignalStrength = new SignalStrength(rssi, -1, -1, -1,
- -1, -1, -1, true);
+ -1, -1, -1, lteSignalStrength, lteRsrp, lteRsrq, lteRssnr, lteCqi, true);
if (!mSignalStrength.equals(oldSignalStrength)) {
try { // This takes care of delayed EVENT_POLL_SIGNAL_STRENGTH (scheduled after
diff --git a/telephony/java/com/android/internal/telephony/sip/SipCommandInterface.java b/telephony/java/com/android/internal/telephony/sip/SipCommandInterface.java
index 1939f95..b6c3b67 100644
--- a/telephony/java/com/android/internal/telephony/sip/SipCommandInterface.java
+++ b/telephony/java/com/android/internal/telephony/sip/SipCommandInterface.java
@@ -144,10 +144,10 @@
public void getSignalStrength (Message result) {
}
- public void getRegistrationState (Message result) {
+ public void getVoiceRegistrationState (Message result) {
}
- public void getGPRSRegistrationState (Message result) {
+ public void getDataRegistrationState (Message result) {
}
public void getOperator(Message result) {
@@ -339,7 +339,7 @@
public void setCdmaRoamingPreference(int cdmaRoamingType, Message response) {
}
- public void setCdmaSubscription(int cdmaSubscription , Message response) {
+ public void setCdmaSubscriptionSource(int cdmaSubscription , Message response) {
}
public void queryTTYMode(Message response) {
@@ -362,4 +362,29 @@
public void exitEmergencyCallbackMode(Message response) {
}
+
+ @Override
+ public void supplyIccPinForApp(String pin, String aid, Message response) {
+ }
+
+ @Override
+ public void supplyIccPukForApp(String puk, String newPin, String aid, Message response) {
+ }
+
+ @Override
+ public void supplyIccPin2ForApp(String pin2, String aid, Message response) {
+ }
+
+ @Override
+ public void supplyIccPuk2ForApp(String puk2, String newPin2, String aid, Message response) {
+ }
+
+ @Override
+ public void changeIccPinForApp(String oldPin, String newPin, String aidPtr, Message response) {
+ }
+
+ @Override
+ public void changeIccPin2ForApp(String oldPin2, String newPin2, String aidPtr,
+ Message response) {
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java b/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java
index 8b3a3ad..242d44f 100644
--- a/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java
+++ b/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java
@@ -825,7 +825,7 @@
* Please note that registration state 4 ("unknown") is treated
* as "out of service" above
*/
- public void getRegistrationState (Message result) {
+ public void getVoiceRegistrationState (Message result) {
String ret[] = new String[14];
ret[0] = "5"; // registered roam
@@ -863,7 +863,7 @@
* Please note that registration state 4 ("unknown") is treated
* as "out of service" in the Android telephony system
*/
- public void getGPRSRegistrationState (Message result) {
+ public void getDataRegistrationState (Message result) {
String ret[] = new String[4];
ret[0] = "5"; // registered roam
@@ -1361,7 +1361,7 @@
}
public void
- setCdmaSubscription(int cdmaSubscriptionType, Message response) {
+ setCdmaSubscriptionSource(int cdmaSubscriptionType, Message response) {
Log.w(LOG_TAG, "CDMA not implemented in SimulatedCommands");
unimplemented(response);
}
@@ -1468,4 +1468,35 @@
public void getGsmBroadcastConfig(Message response) {
unimplemented(response);
}
+
+ @Override
+ public void supplyIccPinForApp(String pin, String aid, Message response) {
+ unimplemented(response);
+ }
+
+ @Override
+ public void supplyIccPukForApp(String puk, String newPin, String aid, Message response) {
+ unimplemented(response);
+ }
+
+ @Override
+ public void supplyIccPin2ForApp(String pin2, String aid, Message response) {
+ unimplemented(response);
+ }
+
+ @Override
+ public void supplyIccPuk2ForApp(String puk2, String newPin2, String aid, Message response) {
+ unimplemented(response);
+ }
+
+ @Override
+ public void changeIccPinForApp(String oldPin, String newPin, String aidPtr, Message response) {
+ unimplemented(response);
+ }
+
+ @Override
+ public void changeIccPin2ForApp(String oldPin2, String newPin2, String aidPtr,
+ Message response) {
+ unimplemented(response);
+ }
}
diff --git a/voip/java/android/net/sip/SipProfile.java b/voip/java/android/net/sip/SipProfile.java
index c7e18df..34d91dd 100644
--- a/voip/java/android/net/sip/SipProfile.java
+++ b/voip/java/android/net/sip/SipProfile.java
@@ -150,9 +150,8 @@
/**
* Sets the username used for authentication.
*
- * @param name auth. name of the profile
+ * @param name authentication username of the profile
* @return this builder object
- * @hide // TODO: remove when we make it public
*/
public Builder setAuthUserName(String name) {
mProfile.mAuthUserName = name;
@@ -391,10 +390,10 @@
/**
* Gets the username for authentication. If it is null, then the username
- * should be used in authentication instead.
+ * is used in authentication instead.
*
- * @return the auth. username
- * @hide // TODO: remove when we make it public
+ * @return the authentication username
+ * @see #getUserName
*/
public String getAuthUserName() {
return mAuthUserName;
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 847577f..7a9276d 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -1,16 +1,16 @@
/**
* Copyright (c) 2008, 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
+ * 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
+ * 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
+ * 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.
*/
@@ -23,6 +23,7 @@
import android.net.wifi.ScanResult;
import android.net.DhcpInfo;
+import android.os.Messenger;
import android.os.WorkSource;
/**
@@ -111,5 +112,7 @@
void forgetNetwork(int networkId);
WpsResult startWps(in WpsConfiguration config);
+
+ Messenger getMessenger();
}
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 4ac03a8..0807a24 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -24,6 +24,7 @@
import android.os.Handler;
import android.os.RemoteException;
import android.os.WorkSource;
+import android.os.Messenger;
import java.util.List;
@@ -60,7 +61,7 @@
* Broadcast intent action indicating that Wi-Fi has been enabled, disabled,
* enabling, disabling, or unknown. One extra provides this state as an int.
* Another extra provides the previous state, if available.
- *
+ *
* @see #EXTRA_WIFI_STATE
* @see #EXTRA_PREVIOUS_WIFI_STATE
*/
@@ -71,7 +72,7 @@
* The lookup key for an int that indicates whether Wi-Fi is enabled,
* disabled, enabling, disabling, or unknown. Retrieve it with
* {@link android.content.Intent#getIntExtra(String,int)}.
- *
+ *
* @see #WIFI_STATE_DISABLED
* @see #WIFI_STATE_DISABLING
* @see #WIFI_STATE_ENABLED
@@ -81,22 +82,22 @@
public static final String EXTRA_WIFI_STATE = "wifi_state";
/**
* The previous Wi-Fi state.
- *
+ *
* @see #EXTRA_WIFI_STATE
*/
public static final String EXTRA_PREVIOUS_WIFI_STATE = "previous_wifi_state";
-
+
/**
* Wi-Fi is currently being disabled. The state will change to {@link #WIFI_STATE_DISABLED} if
* it finishes successfully.
- *
+ *
* @see #WIFI_STATE_CHANGED_ACTION
* @see #getWifiState()
*/
public static final int WIFI_STATE_DISABLING = 0;
/**
* Wi-Fi is disabled.
- *
+ *
* @see #WIFI_STATE_CHANGED_ACTION
* @see #getWifiState()
*/
@@ -104,14 +105,14 @@
/**
* Wi-Fi is currently being enabled. The state will change to {@link #WIFI_STATE_ENABLED} if
* it finishes successfully.
- *
+ *
* @see #WIFI_STATE_CHANGED_ACTION
* @see #getWifiState()
*/
public static final int WIFI_STATE_ENABLING = 2;
/**
* Wi-Fi is enabled.
- *
+ *
* @see #WIFI_STATE_CHANGED_ACTION
* @see #getWifiState()
*/
@@ -119,7 +120,7 @@
/**
* Wi-Fi is in an unknown state. This state will occur when an error happens while enabling
* or disabling.
- *
+ *
* @see #WIFI_STATE_CHANGED_ACTION
* @see #getWifiState()
*/
@@ -418,6 +419,22 @@
*/
public static final int WIFI_FREQUENCY_BAND_2GHZ = 2;
+ /** List of asyncronous notifications
+ * @hide
+ */
+ public static final int DATA_ACTIVITY_NOTIFICATION = 1;
+
+ //Lowest bit indicates data reception and the second lowest
+ //bit indicates data transmitted
+ /** @hide */
+ public static final int DATA_ACTIVITY_NONE = 0x00;
+ /** @hide */
+ public static final int DATA_ACTIVITY_IN = 0x01;
+ /** @hide */
+ public static final int DATA_ACTIVITY_OUT = 0x02;
+ /** @hide */
+ public static final int DATA_ACTIVITY_INOUT = 0x03;
+
IWifiManager mService;
Handler mHandler;
@@ -478,7 +495,7 @@
* <p/>
* The new network will be marked DISABLED by default. To enable it,
* called {@link #enableNetwork}.
- *
+ *
* @param config the set of variables that describe the configuration,
* contained in a {@link WifiConfiguration} object.
* @return the ID of the newly created network description. This is used in
@@ -518,7 +535,7 @@
/**
* Internal method for doing the RPC that creates a new network description
* or updates an existing one.
- *
+ *
* @param config The possibly sparse object containing the variables that
* are to set or updated in the network description.
* @return the ID of the network on success, {@code -1} on failure.
@@ -705,7 +722,7 @@
* Note: It is possible for this method to change the network IDs of
* existing networks. You should assume the network IDs can be different
* after calling this method.
- *
+ *
* @return {@code true} if the operation succeeded
*/
public boolean saveConfiguration() {
@@ -816,20 +833,20 @@
return WIFI_STATE_UNKNOWN;
}
}
-
+
/**
- * Return whether Wi-Fi is enabled or disabled.
+ * Return whether Wi-Fi is enabled or disabled.
* @return {@code true} if Wi-Fi is enabled
* @see #getWifiState()
*/
public boolean isWifiEnabled() {
return getWifiState() == WIFI_STATE_ENABLED;
}
-
+
/**
* Calculates the level of the signal. This should be used any time a signal
* is being shown.
- *
+ *
* @param rssi The power of the signal measured in RSSI.
* @param numLevels The number of levels to consider in the calculated
* level.
@@ -847,10 +864,10 @@
return (int)((float)(rssi - MIN_RSSI) * outputRange / inputRange);
}
}
-
+
/**
* Compares two signal strengths.
- *
+ *
* @param rssiA The power of the first signal measured in RSSI.
* @param rssiB The power of the second signal measured in RSSI.
* @return Returns <0 if the first signal is weaker than the second signal,
@@ -1115,9 +1132,24 @@
}
/**
+ * Get a reference to WifiService handler. This is used by a client to establish
+ * an AsyncChannel communication with WifiService
+ *
+ * @return Messenger pointing to the WifiService handler
+ * @hide
+ */
+ public Messenger getMessenger() {
+ try {
+ return mService.getMessenger();
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /**
* Allows an application to keep the Wi-Fi radio awake.
* Normally the Wi-Fi radio may turn off when the user has not used the device in a while.
- * Acquiring a WifiLock will keep the radio on until the lock is released. Multiple
+ * Acquiring a WifiLock will keep the radio on until the lock is released. Multiple
* applications may hold WifiLocks, and the radio will only be allowed to turn off when no
* WifiLocks are held in any application.
*
@@ -1153,7 +1185,7 @@
* Locks the Wi-Fi radio on until {@link #release} is called.
*
* If this WifiLock is reference-counted, each call to {@code acquire} will increment the
- * reference count, and the radio will remain locked as long as the reference count is
+ * reference count, and the radio will remain locked as long as the reference count is
* above zero.
*
* If this WifiLock is not reference-counted, the first call to {@code acquire} will lock
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index 26fae7f..fc42ab8 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -435,10 +435,11 @@
private final IBatteryStats mBatteryStats;
- public WifiStateMachine(Context context) {
+ public WifiStateMachine(Context context, String wlanInterface) {
super(TAG);
mContext = context;
+ mInterfaceName = wlanInterface;
mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, NETWORKTYPE, "");
mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo"));
@@ -449,7 +450,6 @@
mWifiMonitor = new WifiMonitor(this);
mDhcpInfoInternal = new DhcpInfoInternal();
mWifiInfo = new WifiInfo();
- mInterfaceName = SystemProperties.get("wifi.interface", "tiwlan0");
mSupplicantStateTracker = new SupplicantStateTracker(context, this, getHandler());
mWpsStateMachine = new WpsStateMachine(context, this, getHandler());
mLinkProperties = new LinkProperties();