summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/java/android/app/ActivityThread.java4
-rw-r--r--core/java/android/app/ApplicationThreadNative.java7
-rw-r--r--core/java/android/app/IApplicationThread.java3
-rw-r--r--core/java/android/hardware/Sensor.java40
-rw-r--r--core/java/android/net/PacProxySelector.java80
-rw-r--r--core/java/android/net/Proxy.java48
-rw-r--r--core/java/android/net/ProxyProperties.java45
-rw-r--r--core/java/android/provider/Settings.java15
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java3
-rw-r--r--core/java/android/widget/AbsListView.java1
-rw-r--r--core/java/android/widget/FastScroller.java105
-rw-r--r--core/java/com/android/internal/view/IInputMethodManager.aidl4
-rw-r--r--core/tests/coretests/AndroidManifest.xml6
-rw-r--r--core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java508
14 files changed, 799 insertions, 70 deletions
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index d9f9d61..5300eca 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -793,8 +793,8 @@ public final class ActivityThread {
InetAddress.clearDnsCache();
}
- public void setHttpProxy(String host, String port, String exclList) {
- Proxy.setHttpProxySystemProperty(host, port, exclList);
+ public void setHttpProxy(String host, String port, String exclList, String pacFileUrl) {
+ Proxy.setHttpProxySystemProperty(host, port, exclList, pacFileUrl);
}
public void processInBackground() {
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index 6f18e84..1465de2 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -338,7 +338,8 @@ public abstract class ApplicationThreadNative extends Binder
final String proxy = data.readString();
final String port = data.readString();
final String exclList = data.readString();
- setHttpProxy(proxy, port, exclList);
+ final String pacFileUrl = data.readString();
+ setHttpProxy(proxy, port, exclList, pacFileUrl);
return true;
}
@@ -1001,12 +1002,14 @@ class ApplicationThreadProxy implements IApplicationThread {
data.recycle();
}
- public void setHttpProxy(String proxy, String port, String exclList) throws RemoteException {
+ public void setHttpProxy(String proxy, String port, String exclList,
+ String pacFileUrl) throws RemoteException {
Parcel data = Parcel.obtain();
data.writeInterfaceToken(IApplicationThread.descriptor);
data.writeString(proxy);
data.writeString(port);
data.writeString(exclList);
+ data.writeString(pacFileUrl);
mRemote.transact(SET_HTTP_PROXY_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
data.recycle();
}
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index 8e882df..61c499a 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -101,7 +101,8 @@ public interface IApplicationThread extends IInterface {
void scheduleConfigurationChanged(Configuration config) throws RemoteException;
void updateTimeZone() throws RemoteException;
void clearDnsCache() throws RemoteException;
- void setHttpProxy(String proxy, String port, String exclList) throws RemoteException;
+ void setHttpProxy(String proxy, String port, String exclList,
+ String pacFileUrl) throws RemoteException;
void processInBackground() throws RemoteException;
void dumpService(FileDescriptor fd, IBinder servicetoken, String[] args)
throws RemoteException;
diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index c3e9cb7..d708791 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -129,7 +129,7 @@ public final class Sensor {
* due to distortions that arise from magnetized iron, steel or permanent magnets on the
* device) is not considered in the given sensor values. However, such hard iron bias values
* are returned to you separately in the result {@link android.hardware.SensorEvent#values}
- * so you may use them for custom calibrations.
+ * so you may use them for custom calibrations.
* <p>Also, no periodic calibration is performed
* (i.e. there are no discontinuities in the data stream while using this sensor) and
* assumptions that the magnetic field is due to the Earth's poles is avoided, but
@@ -174,7 +174,7 @@ public final class Sensor {
public static final int TYPE_GYROSCOPE_UNCALIBRATED = 16;
/**
- * A constant describing the significant motion trigger sensor.
+ * A constant describing a significant motion trigger sensor.
* <p>
* It triggers when an event occurs and then automatically disables
* itself. The sensor continues to operate while the device is asleep
@@ -186,6 +186,42 @@ public final class Sensor {
public static final int TYPE_SIGNIFICANT_MOTION = 17;
/**
+ * A constant describing a step detector sensor.
+ * <p>
+ * A sensor of this type triggers an event each time a step is taken by the user. The only
+ * allowed value to return is 1.0 and an event is generated for each step. Like with any other
+ * event, the timestamp indicates when the event (here the step) occurred, this corresponds to
+ * when the foot hit the ground, generating a high variation in acceleration.
+ * <p>
+ * See {@link android.hardware.SensorEvent#values SensorEvent.values} for more details.
+ */
+ public static final int TYPE_STEP_DETECTOR = 18;
+
+ /**
+ * A constant describing a step counter sensor.
+ * <p>
+ * A sensor of this type returns the number of steps taken by the user since the last reboot
+ * while activated. The value is returned as a float (with the fractional part set to zero) and
+ * is reset to zero only on a system reboot. The timestamp of the event is set to the time when
+ * the first step for that event was taken. This sensor is implemented in hardware and is
+ * expected to be low power.
+ * <p>
+ * See {@link android.hardware.SensorEvent#values SensorEvent.values} for more details.
+ */
+ public static final int TYPE_STEP_COUNTER = 19;
+
+ /**
+ * A constant describing the geo-magnetic rotation vector.
+ * <p>
+ * Similar to {@link #SENSOR_TYPE_ROTATION_VECTOR}, but using a magnetometer instead of using a
+ * gyroscope. This sensor uses lower power than the other rotation vectors, because it doesn't
+ * use the gyroscope. However, it is more noisy and will work best outdoors.
+ * <p>
+ * See {@link android.hardware.SensorEvent#values SensorEvent.values} for more details.
+ */
+ public static final int TYPE_GEOMAGNETIC_ROTATION_VECTOR = 20;
+
+ /**
* A constant describing all sensor types.
*/
public static final int TYPE_ALL = -1;
diff --git a/core/java/android/net/PacProxySelector.java b/core/java/android/net/PacProxySelector.java
new file mode 100644
index 0000000..be3a31d
--- /dev/null
+++ b/core/java/android/net/PacProxySelector.java
@@ -0,0 +1,80 @@
+
+package android.net;
+
+import android.os.RemoteException;
+import android.os.ServiceManager;
+
+import com.android.net.IProxyService;
+import com.google.android.collect.Lists;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.MalformedURLException;
+import java.net.Proxy;
+import java.net.Proxy.Type;
+import java.net.ProxySelector;
+import java.net.SocketAddress;
+import java.net.URI;
+import java.util.List;
+
+/**
+ * @hide
+ */
+public class PacProxySelector extends ProxySelector {
+ public static final String PROXY_SERVICE = "com.android.net.IProxyService";
+ private IProxyService mProxyService;
+
+ public PacProxySelector() {
+ mProxyService = IProxyService.Stub.asInterface(
+ ServiceManager.getService(PROXY_SERVICE));
+ }
+
+ @Override
+ public List<Proxy> select(URI uri) {
+ String response = null;
+ String urlString;
+ try {
+ urlString = uri.toURL().toString();
+ } catch (MalformedURLException e) {
+ urlString = uri.getHost();
+ }
+ try {
+ response = mProxyService.resolvePacFile(uri.getHost(), urlString);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+
+ return parseResponse(response);
+ }
+
+ private static List<Proxy> parseResponse(String response) {
+ String[] split = response.split(";");
+ List<Proxy> ret = Lists.newArrayList();
+ for (String s : split) {
+ String trimmed = s.trim();
+ if (trimmed.equals("DIRECT")) {
+ ret.add(java.net.Proxy.NO_PROXY);
+ } else if (trimmed.startsWith("PROXY ")) {
+ String[] hostPort = trimmed.substring(6).split(":");
+ String host = hostPort[0];
+ int port;
+ try {
+ port = Integer.parseInt(hostPort[1]);
+ } catch (Exception e) {
+ port = 8080;
+ }
+ ret.add(new Proxy(Type.HTTP, new InetSocketAddress(host, port)));
+ }
+ }
+ if (ret.size() == 0) {
+ ret.add(java.net.Proxy.NO_PROXY);
+ }
+ return ret;
+ }
+
+ @Override
+ public void connectFailed(URI uri, SocketAddress address, IOException failure) {
+
+ }
+
+}
diff --git a/core/java/android/net/Proxy.java b/core/java/android/net/Proxy.java
index a408ea0..5b38f57 100644
--- a/core/java/android/net/Proxy.java
+++ b/core/java/android/net/Proxy.java
@@ -18,38 +18,25 @@ package android.net;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
-import android.content.ContentResolver;
import android.content.Context;
-import android.database.ContentObserver;
-import android.net.ProxyProperties;
-import android.os.Handler;
-import android.os.SystemProperties;
import android.text.TextUtils;
-import android.provider.Settings;
import android.util.Log;
-import java.net.InetAddress;
+
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.conn.routing.HttpRoutePlanner;
+import org.apache.http.conn.scheme.SchemeRegistry;
+import org.apache.http.protocol.HttpContext;
+
import java.net.InetSocketAddress;
import java.net.ProxySelector;
-import java.net.SocketAddress;
import java.net.URI;
-import java.net.UnknownHostException;
-import java.util.concurrent.locks.ReadWriteLock;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import junit.framework.Assert;
-
-import org.apache.http.conn.routing.HttpRoute;
-import org.apache.http.conn.routing.HttpRoutePlanner;
-import org.apache.http.conn.scheme.SchemeRegistry;
-import org.apache.http.HttpHost;
-import org.apache.http.HttpRequest;
-import org.apache.http.impl.conn.ProxySelectorRoutePlanner;
-import org.apache.http.protocol.HttpContext;
-
/**
* A convenience class for accessing the user and default proxy
* settings.
@@ -60,6 +47,9 @@ public final class Proxy {
private static final boolean DEBUG = false;
private static final String TAG = "Proxy";
+ private static final ProxySelector sDefaultProxySelector;
+ private static PacProxySelector sPacProxySelector;
+
/**
* Used to notify an app that's caching the default connection proxy
* that either the default connection or its proxy has changed.
@@ -96,6 +86,7 @@ public final class Proxy {
static {
HOSTNAME_PATTERN = Pattern.compile(HOSTNAME_REGEXP);
EXCLLIST_PATTERN = Pattern.compile(EXCLLIST_REGEXP);
+ sDefaultProxySelector = ProxySelector.getDefault();
}
/**
@@ -325,16 +316,19 @@ public final class Proxy {
String host = null;
String port = null;
String exclList = null;
+ String pacFileUrl = null;
if (p != null) {
host = p.getHost();
port = Integer.toString(p.getPort());
exclList = p.getExclusionList();
+ pacFileUrl = p.getPacFileUrl();
}
- setHttpProxySystemProperty(host, port, exclList);
+ setHttpProxySystemProperty(host, port, exclList, pacFileUrl);
}
/** @hide */
- public static final void setHttpProxySystemProperty(String host, String port, String exclList) {
+ public static final void setHttpProxySystemProperty(String host, String port, String exclList,
+ String pacFileUrl) {
if (exclList != null) exclList = exclList.replace(",", "|");
if (false) Log.d(TAG, "setHttpProxySystemProperty :"+host+":"+port+" - "+exclList);
if (host != null) {
@@ -358,5 +352,13 @@ public final class Proxy {
System.clearProperty("http.nonProxyHosts");
System.clearProperty("https.nonProxyHosts");
}
+ if ((pacFileUrl != null) && !TextUtils.isEmpty(pacFileUrl)) {
+ if (sPacProxySelector == null) {
+ sPacProxySelector = new PacProxySelector();
+ }
+ ProxySelector.setDefault(sPacProxySelector);
+ } else {
+ ProxySelector.setDefault(sDefaultProxySelector);
+ }
}
}
diff --git a/core/java/android/net/ProxyProperties.java b/core/java/android/net/ProxyProperties.java
index 9c4772b..76aea9f 100644
--- a/core/java/android/net/ProxyProperties.java
+++ b/core/java/android/net/ProxyProperties.java
@@ -20,9 +20,7 @@ package android.net;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
-import android.util.Log;
-import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.Locale;
@@ -38,17 +36,30 @@ public class ProxyProperties implements Parcelable {
private String mExclusionList;
private String[] mParsedExclusionList;
+ private String mPacFileUrl;
+ public static final String LOCAL_EXCL_LIST = "";
+ public static final int LOCAL_PORT = 8182;
+ public static final String LOCAL_HOST = "localhost";
+
public ProxyProperties(String host, int port, String exclList) {
mHost = host;
mPort = port;
setExclusionList(exclList);
}
+ public ProxyProperties(String pacFileUrl) {
+ mHost = LOCAL_HOST;
+ mPort = LOCAL_PORT;
+ setExclusionList(LOCAL_EXCL_LIST);
+ mPacFileUrl = pacFileUrl;
+ }
+
private ProxyProperties(String host, int port, String exclList, String[] parsedExclList) {
mHost = host;
mPort = port;
mExclusionList = exclList;
mParsedExclusionList = parsedExclList;
+ mPacFileUrl = null;
}
// copy constructor instead of clone
@@ -56,6 +67,7 @@ public class ProxyProperties implements Parcelable {
if (source != null) {
mHost = source.getHost();
mPort = source.getPort();
+ mPacFileUrl = source.getPacFileUrl();
mExclusionList = source.getExclusionList();
mParsedExclusionList = source.mParsedExclusionList;
}
@@ -69,6 +81,10 @@ public class ProxyProperties implements Parcelable {
return inetSocketAddress;
}
+ public String getPacFileUrl() {
+ return mPacFileUrl;
+ }
+
public String getHost() {
return mHost;
}
@@ -130,7 +146,10 @@ public class ProxyProperties implements Parcelable {
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
- if (mHost != null) {
+ if (mPacFileUrl != null) {
+ sb.append("PAC Script: ");
+ sb.append(mPacFileUrl);
+ } else if (mHost != null) {
sb.append("[");
sb.append(mHost);
sb.append("] ");
@@ -148,6 +167,14 @@ public class ProxyProperties implements Parcelable {
public boolean equals(Object o) {
if (!(o instanceof ProxyProperties)) return false;
ProxyProperties p = (ProxyProperties)o;
+ // If PAC URL is present in either then they must be equal.
+ // Other parameters will only be for fall back.
+ if (!TextUtils.isEmpty(mPacFileUrl)) {
+ return mPacFileUrl.equals(p.getPacFileUrl());
+ }
+ if (!TextUtils.isEmpty(p.getPacFileUrl())) {
+ return false;
+ }
if (mExclusionList != null && !mExclusionList.equals(p.getExclusionList())) return false;
if (mHost != null && p.getHost() != null && mHost.equals(p.getHost()) == false) {
return false;
@@ -181,6 +208,13 @@ public class ProxyProperties implements Parcelable {
* @hide
*/
public void writeToParcel(Parcel dest, int flags) {
+ if (mPacFileUrl != null) {
+ dest.writeByte((byte)1);
+ dest.writeString(mPacFileUrl);
+ return;
+ } else {
+ dest.writeByte((byte)0);
+ }
if (mHost != null) {
dest.writeByte((byte)1);
dest.writeString(mHost);
@@ -201,7 +235,10 @@ public class ProxyProperties implements Parcelable {
public ProxyProperties createFromParcel(Parcel in) {
String host = null;
int port = 0;
- if (in.readByte() == 1) {
+ if (in.readByte() != 0) {
+ return new ProxyProperties(in.readString());
+ }
+ if (in.readByte() != 0) {
host = in.readString();
port = in.readInt();
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index b252641..130123f 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5292,6 +5292,14 @@ public final class Settings {
public static final String CONNECTIVITY_CHANGE_DELAY = "connectivity_change_delay";
/**
+ * The series of successively longer delays used in retrying to download PAC file.
+ * Last delay is used between successful PAC downloads.
+ *
+ * @hide
+ */
+ public static final String PAC_CHANGE_DELAY = "pac_change_delay";
+
+ /**
* Setting to turn off captive portal detection. Feature is enabled by
* default and the setting needs to be set to 0 to disable it.
*
@@ -5386,6 +5394,13 @@ public final class Settings {
GLOBAL_HTTP_PROXY_EXCLUSION_LIST = "global_http_proxy_exclusion_list";
/**
+ * The location PAC File for the proxy.
+ * @hide
+ */
+ public static final String
+ GLOBAL_HTTP_PROXY_PAC = "global_proxy_pac_url";
+
+ /**
* Enables the UI setting to allow the user to specify the global HTTP
* proxy and associated exclusion list.
*
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 4df4734..c8ce6fa 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -613,7 +613,8 @@ public final class InputMethodManager {
public List<InputMethodSubtype> getEnabledInputMethodSubtypeList(InputMethodInfo imi,
boolean allowsImplicitlySelectedSubtypes) {
try {
- return mService.getEnabledInputMethodSubtypeList(imi, allowsImplicitlySelectedSubtypes);
+ return mService.getEnabledInputMethodSubtypeList(
+ imi == null ? null : imi.getId(), allowsImplicitlySelectedSubtypes);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index e7227e3..3f391ad 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -1241,6 +1241,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
mFastScroller.setEnabled(enabled);
} else if (enabled) {
mFastScroller = new FastScroller(this);
+ mFastScroller.setEnabled(true);
}
}
diff --git a/core/java/android/widget/FastScroller.java b/core/java/android/widget/FastScroller.java
index b3b00b1..393720f 100644
--- a/core/java/android/widget/FastScroller.java
+++ b/core/java/android/widget/FastScroller.java
@@ -106,6 +106,7 @@ class FastScroller {
private final Rect mTempBounds = new Rect();
private final Rect mTempMargins = new Rect();
+ private final Rect mContainerRect = new Rect();
private final AbsListView mList;
private final ViewGroupOverlay mOverlay;
@@ -159,6 +160,9 @@ class FastScroller {
private Object[] mSections;
+ /** Whether this view is currently performing layout. */
+ private boolean mUpdatingLayout;
+
/**
* Current decoration state, one of:
* <ul>
@@ -434,6 +438,16 @@ class FastScroller {
* Measures and layouts the scrollbar and decorations.
*/
private void updateLayout() {
+ // Prevent re-entry when RTL properties change as a side-effect of
+ // resolving padding.
+ if (mUpdatingLayout) {
+ return;
+ }
+
+ mUpdatingLayout = true;
+
+ updateContainerRect();
+
layoutThumb();
layoutTrack();
@@ -451,6 +465,8 @@ class FastScroller {
bounds.bottom += mPreviewImage.getPaddingBottom();
applyLayout(mPreviewImage, bounds);
}
+
+ mUpdatingLayout = false;
}
/**
@@ -513,14 +529,15 @@ class FastScroller {
marginRight = margins.right;
}
- final int listWidth = mList.getWidth();
+ final Rect container = mContainerRect;
+ final int containerWidth = container.width();
final int maxWidth;
if (adjacent == null) {
- maxWidth = listWidth;
+ maxWidth = containerWidth;
} else if (mLayoutFromRight) {
maxWidth = adjacent.getLeft();
} else {
- maxWidth = listWidth - adjacent.getRight();
+ maxWidth = containerWidth - adjacent.getRight();
}
final int adjMaxWidth = maxWidth - marginLeft - marginRight;
@@ -533,10 +550,10 @@ class FastScroller {
final int left;
final int right;
if (mLayoutFromRight) {
- right = (adjacent == null ? listWidth : adjacent.getLeft()) - marginRight;
+ right = (adjacent == null ? container.right : adjacent.getLeft()) - marginRight;
left = right - width;
} else {
- left = (adjacent == null ? 0 : adjacent.getRight()) + marginLeft;
+ left = (adjacent == null ? container.left : adjacent.getRight()) + marginLeft;
right = left + width;
}
@@ -560,22 +577,41 @@ class FastScroller {
marginRight = margins.right;
}
- final View list = mList;
- final int listWidth = list.getWidth();
- final int adjMaxWidth = listWidth - marginLeft - marginRight;
+ final Rect container = mContainerRect;
+ final int containerWidth = container.width();
+ final int adjMaxWidth = containerWidth - marginLeft - marginRight;
final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(adjMaxWidth, MeasureSpec.AT_MOST);
final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
preview.measure(widthMeasureSpec, heightMeasureSpec);
// Align at the vertical center, 10% from the top.
+ final int containerHeight = container.height();
final int width = preview.getMeasuredWidth();
- final int top = list.getHeight() / 10 + marginTop;
+ final int top = containerHeight / 10 + marginTop + container.top;
final int bottom = top + preview.getMeasuredHeight();
- final int left = (listWidth - width) / 2;
+ final int left = (containerWidth - width) / 2 + container.left;
final int right = left + width;
out.set(left, top, right, bottom);
}
+ private void updateContainerRect() {
+ final AbsListView list = mList;
+ final Rect container = mContainerRect;
+ container.left = 0;
+ container.top = 0;
+ container.right = list.getWidth();
+ container.bottom = list.getHeight();
+
+ final int scrollbarStyle = list.getScrollBarStyle();
+ if (scrollbarStyle == View.SCROLLBARS_INSIDE_INSET
+ || scrollbarStyle == View.SCROLLBARS_INSIDE_OVERLAY) {
+ container.left += list.getPaddingLeft();
+ container.top += list.getPaddingTop();
+ container.right -= list.getPaddingRight();
+ container.bottom -= list.getPaddingBottom();
+ }
+ }
+
/**
* Lays out the thumb according to the current scrollbar position.
*/
@@ -586,25 +622,24 @@ class FastScroller {
}
/**
- * Lays out the track centered on the thumb, if available, or against the
- * edge if no thumb is available. Must be called after {@link #layoutThumb}.
+ * Lays out the track centered on the thumb. Must be called after
+ * {@link #layoutThumb}.
*/
private void layoutTrack() {
final View track = mTrackImage;
final View thumb = mThumbImage;
- final View list = mList;
- final int listWidth = list.getWidth();
- final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(listWidth, MeasureSpec.AT_MOST);
+ final Rect container = mContainerRect;
+ final int containerWidth = container.width();
+ final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(containerWidth, MeasureSpec.AT_MOST);
final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
track.measure(widthMeasureSpec, heightMeasureSpec);
final int trackWidth = track.getMeasuredWidth();
final int thumbHalfHeight = thumb == null ? 0 : thumb.getHeight() / 2;
- final int left = thumb == null ? listWidth - trackWidth :
- thumb.getLeft() + (thumb.getWidth() - trackWidth) / 2;
+ final int left = thumb.getLeft() + (thumb.getWidth() - trackWidth) / 2;
final int right = left + trackWidth;
- final int top = thumbHalfHeight;
- final int bottom = list.getHeight() - thumbHalfHeight;
+ final int top = container.top + thumbHalfHeight;
+ final int bottom = container.bottom - thumbHalfHeight;
track.layout(left, top, right, bottom);
}
@@ -990,36 +1025,40 @@ class FastScroller {
* to place the thumb.
*/
private void setThumbPos(float position) {
- final int top = 0;
- final int bottom = mList.getHeight();
-
- final float thumbHalfHeight = mThumbImage.getHeight() / 2f;
- final float min = top + thumbHalfHeight;
- final float max = bottom - thumbHalfHeight;
+ final Rect container = mContainerRect;
+ final int top = container.top;
+ final int bottom = container.bottom;
+
+ final ImageView trackImage = mTrackImage;
+ final ImageView thumbImage = mThumbImage;
+ final float min = trackImage.getTop();
+ final float max = trackImage.getBottom();
final float offset = min;
final float range = max - min;
final float thumbMiddle = position * range + offset;
- mThumbImage.setTranslationY(thumbMiddle - thumbHalfHeight);
+ thumbImage.setTranslationY(thumbMiddle - thumbImage.getHeight() / 2);
// Center the preview on the thumb, constrained to the list bounds.
- final float previewHalfHeight = mPreviewImage.getHeight() / 2f;
+ final ImageView previewImage = mPreviewImage;
+ final float previewHalfHeight = previewImage.getHeight() / 2f;
final float minP = top + previewHalfHeight;
final float maxP = bottom - previewHalfHeight;
final float previewMiddle = MathUtils.constrain(thumbMiddle, minP, maxP);
final float previewTop = previewMiddle - previewHalfHeight;
+ previewImage.setTranslationY(previewTop);
- mPreviewImage.setTranslationY(previewTop);
mPrimaryText.setTranslationY(previewTop);
mSecondaryText.setTranslationY(previewTop);
}
private float getPosFromMotionEvent(float y) {
- final int top = 0;
- final int bottom = mList.getHeight();
+ final Rect container = mContainerRect;
+ final int top = container.top;
+ final int bottom = container.bottom;
- final float thumbHalfHeight = mThumbImage.getHeight() / 2f;
- final float min = top + thumbHalfHeight;
- final float max = bottom - thumbHalfHeight;
+ final ImageView trackImage = mTrackImage;
+ final float min = trackImage.getTop();
+ final float max = trackImage.getBottom();
final float offset = min;
final float range = max - min;
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 82b2654..ebd3e1c 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -28,11 +28,13 @@ import com.android.internal.view.IInputMethodClient;
/**
* Public interface to the global input method manager, used by all client
* applications.
+ * You need to update BridgeIInputMethodManager.java as well when changing
+ * this file.
*/
interface IInputMethodManager {
List<InputMethodInfo> getInputMethodList();
List<InputMethodInfo> getEnabledInputMethodList();
- List<InputMethodSubtype> getEnabledInputMethodSubtypeList(in InputMethodInfo imi,
+ List<InputMethodSubtype> getEnabledInputMethodSubtypeList(in String imiId,
boolean allowsImplicitlySelectedSubtypes);
InputMethodSubtype getLastInputMethodSubtype();
// TODO: We should change the return type from List to List<Parcelable>
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index f8b26bc..a2cc40c 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -94,7 +94,7 @@
<uses-permission android:name="android.permission.MOVE_PACKAGE" />
<uses-permission android:name="android.permission.PACKAGE_VERIFICATION_AGENT" />
- <!--os storage test permissions -->
+ <!-- os storage test permissions -->
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
<uses-permission android:name="android.permission.ASEC_ACCESS" />
<uses-permission android:name="android.permission.ASEC_CREATE" />
@@ -103,6 +103,10 @@
<uses-permission android:name="android.permission.ASEC_RENAME" />
<uses-permission android:name="android.permission.SHUTDOWN" />
+ <!-- virtual display test permissions -->
+ <uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT" />
+ <uses-permission android:name="android.permission.CAPTURE_SECURE_VIDEO_OUTPUT" />
+
<!-- accessibility test permissions -->
<uses-permission android:name="android.permission.RETRIEVE_WINDOW_CONTENT" />
diff --git a/core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java b/core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java
new file mode 100644
index 0000000..ebecf2e
--- /dev/null
+++ b/core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java
@@ -0,0 +1,508 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.display;
+
+import android.app.Presentation;
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.graphics.Point;
+import android.graphics.drawable.ColorDrawable;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.VirtualDisplay;
+import android.media.Image;
+import android.media.ImageReader;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.SystemClock;
+import android.test.AndroidTestCase;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.Display;
+import android.view.Surface;
+import android.view.ViewGroup.LayoutParams;
+import android.view.WindowManager;
+import android.widget.ImageView;
+
+import java.nio.ByteBuffer;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * Tests that applications can create virtual displays and present content on them.
+ *
+ * Contains additional tests that cannot be included in CTS because they require
+ * system permissions. See also the CTS version of VirtualDisplayTest.
+ */
+public class VirtualDisplayTest extends AndroidTestCase {
+ private static final String TAG = "VirtualDisplayTest";
+
+ private static final String NAME = TAG;
+ private static final int WIDTH = 720;
+ private static final int HEIGHT = 480;
+ private static final int DENSITY = DisplayMetrics.DENSITY_MEDIUM;
+ private static final int TIMEOUT = 10000;
+
+ // Colors that we use as a signal to determine whether some desired content was
+ // drawn. The colors themselves doesn't matter but we choose them to have with distinct
+ // values for each color channel so as to detect possible RGBA vs. BGRA buffer format issues.
+ // We should only observe RGBA buffers but some graphics drivers might incorrectly
+ // deliver BGRA buffers to virtual displays instead.
+ private static final int BLUEISH = 0xff1122ee;
+ private static final int GREENISH = 0xff33dd44;
+
+ private DisplayManager mDisplayManager;
+ private Handler mHandler;
+ private final Lock mImageReaderLock = new ReentrantLock(true /*fair*/);
+ private ImageReader mImageReader;
+ private Surface mSurface;
+ private ImageListener mImageListener;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ mDisplayManager = (DisplayManager)mContext.getSystemService(Context.DISPLAY_SERVICE);
+ mHandler = new Handler(Looper.getMainLooper());
+ mImageListener = new ImageListener();
+
+ mImageReaderLock.lock();
+ try {
+ mImageReader = new ImageReader(WIDTH, HEIGHT, PixelFormat.RGBA_8888, 2);
+ mImageReader.setImageAvailableListener(mImageListener, mHandler);
+ mSurface = mImageReader.getSurface();
+ } finally {
+ mImageReaderLock.unlock();
+ }
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+
+ mImageReaderLock.lock();
+ try {
+ mImageReader.close();
+ mImageReader = null;
+ mSurface = null;
+ } finally {
+ mImageReaderLock.unlock();
+ }
+ }
+
+ /**
+ * Ensures that an application can create a private virtual display and show
+ * its own windows on it.
+ */
+ public void testPrivateVirtualDisplay() throws Exception {
+ VirtualDisplay virtualDisplay = mDisplayManager.createVirtualDisplay(NAME,
+ WIDTH, HEIGHT, DENSITY, mSurface, 0);
+ assertNotNull("virtual display must not be null", virtualDisplay);
+
+ Display display = virtualDisplay.getDisplay();
+ try {
+ assertDisplayRegistered(display, Display.FLAG_PRIVATE);
+
+ // Show a private presentation on the display.
+ assertDisplayCanShowPresentation("private presentation window",
+ display, BLUEISH,
+ WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION, 0);
+ } finally {
+ virtualDisplay.release();
+ }
+ assertDisplayUnregistered(display);
+ }
+
+ /**
+ * Ensures that an application can create a private presentation virtual display and show
+ * its own windows on it.
+ */
+ public void testPrivatePresentationVirtualDisplay() throws Exception {
+ VirtualDisplay virtualDisplay = mDisplayManager.createVirtualDisplay(NAME,
+ WIDTH, HEIGHT, DENSITY, mSurface,
+ DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION);
+ assertNotNull("virtual display must not be null", virtualDisplay);
+
+ Display display = virtualDisplay.getDisplay();
+ try {
+ assertDisplayRegistered(display, Display.FLAG_PRIVATE | Display.FLAG_PRESENTATION);
+
+ // Show a private presentation on the display.
+ assertDisplayCanShowPresentation("private presentation window",
+ display, BLUEISH,
+ WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION, 0);
+ } finally {
+ virtualDisplay.release();
+ }
+ assertDisplayUnregistered(display);
+ }
+
+ /**
+ * Ensures that an application can create a public virtual display and show
+ * its own windows on it. This test requires the CAPTURE_VIDEO_OUTPUT permission.
+ *
+ * Because this test does not have an activity token, we use the TOAST window
+ * type to create the window. Another choice might be SYSTEM_ALERT_WINDOW but
+ * that requires a permission.
+ */
+ public void testPublicPresentationVirtualDisplay() throws Exception {
+ VirtualDisplay virtualDisplay = mDisplayManager.createVirtualDisplay(NAME,
+ WIDTH, HEIGHT, DENSITY, mSurface,
+ DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC
+ | DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION);
+ assertNotNull("virtual display must not be null", virtualDisplay);
+
+ Display display = virtualDisplay.getDisplay();
+ try {
+ assertDisplayRegistered(display, Display.FLAG_PRESENTATION);
+
+ // Mirroring case.
+ // Show a window on the default display. It should be mirrored to the
+ // virtual display automatically.
+ Display defaultDisplay = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
+ assertDisplayCanShowPresentation("mirrored window",
+ defaultDisplay, GREENISH,
+ WindowManager.LayoutParams.TYPE_TOAST, 0);
+
+ // Mirroring case with secure window (but display is not secure).
+ // Show a window on the default display. It should be replaced with black on
+ // the virtual display.
+ assertDisplayCanShowPresentation("mirrored secure window on non-secure display",
+ defaultDisplay, Color.BLACK,
+ WindowManager.LayoutParams.TYPE_TOAST,
+ WindowManager.LayoutParams.FLAG_SECURE);
+
+ // Presentation case.
+ // Show a normal presentation on the display.
+ assertDisplayCanShowPresentation("presentation window",
+ display, BLUEISH,
+ WindowManager.LayoutParams.TYPE_TOAST, 0);
+
+ // Presentation case with secure window (but display is not secure).
+ // Show a normal presentation on the display. It should be replaced with black.
+ assertDisplayCanShowPresentation("secure presentation window on non-secure display",
+ display, Color.BLACK,
+ WindowManager.LayoutParams.TYPE_TOAST,
+ WindowManager.LayoutParams.FLAG_SECURE);
+ } finally {
+ virtualDisplay.release();
+ }
+ assertDisplayUnregistered(display);
+ }
+
+ /**
+ * Ensures that an application can create a secure public virtual display and show
+ * its own windows on it. This test requires the CAPTURE_SECURE_VIDEO_OUTPUT permission.
+ *
+ * Because this test does not have an activity token, we use the TOAST window
+ * type to create the window. Another choice might be SYSTEM_ALERT_WINDOW but
+ * that requires a permission.
+ */
+ public void testSecurePublicPresentationVirtualDisplay() throws Exception {
+ VirtualDisplay virtualDisplay = mDisplayManager.createVirtualDisplay(NAME,
+ WIDTH, HEIGHT, DENSITY, mSurface,
+ DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE
+ | DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC
+ | DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION);
+ assertNotNull("virtual display must not be null", virtualDisplay);
+
+ Display display = virtualDisplay.getDisplay();
+ try {
+ assertDisplayRegistered(display, Display.FLAG_PRESENTATION | Display.FLAG_SECURE);
+
+ // Mirroring case with secure window (and display is secure).
+ // Show a window on the default display. It should be mirrored to the
+ // virtual display automatically.
+ Display defaultDisplay = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
+ assertDisplayCanShowPresentation("mirrored secure window on secure display",
+ defaultDisplay, GREENISH,
+ WindowManager.LayoutParams.TYPE_TOAST,
+ WindowManager.LayoutParams.FLAG_SECURE);
+
+ // Presentation case with secure window (and display is secure).
+ // Show a normal presentation on the display.
+ assertDisplayCanShowPresentation("secure presentation window on secure display",
+ display, BLUEISH,
+ WindowManager.LayoutParams.TYPE_TOAST,
+ WindowManager.LayoutParams.FLAG_SECURE);
+ } finally {
+ virtualDisplay.release();
+ }
+ assertDisplayUnregistered(display);
+ }
+
+ private void assertDisplayRegistered(Display display, int flags) {
+ assertNotNull("display object must not be null", display);
+ assertTrue("display must be valid", display.isValid());
+ assertTrue("display id must be unique",
+ display.getDisplayId() != Display.DEFAULT_DISPLAY);
+ assertEquals("display must have correct flags", flags, display.getFlags());
+ assertEquals("display name must match supplied name", NAME, display.getName());
+ Point size = new Point();
+ display.getSize(size);
+ assertEquals("display width must match supplied width", WIDTH, size.x);
+ assertEquals("display height must match supplied height", HEIGHT, size.y);
+ assertEquals("display rotation must be 0",
+ Surface.ROTATION_0, display.getRotation());
+ assertNotNull("display must be registered",
+ findDisplay(mDisplayManager.getDisplays(), NAME));
+
+ if ((flags & Display.FLAG_PRESENTATION) != 0) {
+ assertNotNull("display must be registered as a presentation display",
+ findDisplay(mDisplayManager.getDisplays(
+ DisplayManager.DISPLAY_CATEGORY_PRESENTATION), NAME));
+ } else {
+ assertNull("display must not be registered as a presentation display",
+ findDisplay(mDisplayManager.getDisplays(
+ DisplayManager.DISPLAY_CATEGORY_PRESENTATION), NAME));
+ }
+ }
+
+ private void assertDisplayUnregistered(Display display) {
+ assertNull("display must no longer be registered after being removed",
+ findDisplay(mDisplayManager.getDisplays(), NAME));
+ assertFalse("display must no longer be valid", display.isValid());
+ }
+
+ private void assertDisplayCanShowPresentation(String message, final Display display,
+ final int color, final int windowType, final int windowFlags) {
+ // At this point, we should not have seen any blue.
+ assertTrue(message + ": display should not show content before window is shown",
+ mImageListener.getColor() != color);
+
+ final TestPresentation[] presentation = new TestPresentation[1];
+ try {
+ // Show the presentation.
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ presentation[0] = new TestPresentation(getContext(), display,
+ color, windowType, windowFlags);
+ presentation[0].show();
+ }
+ });
+
+ // Wait for the blue to be seen.
+ assertTrue(message + ": display should show content after window is shown",
+ mImageListener.waitForColor(color, TIMEOUT));
+ } finally {
+ if (presentation[0] != null) {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ presentation[0].dismiss();
+ }
+ });
+ }
+ }
+ }
+
+ private void runOnUiThread(Runnable runnable) {
+ Runnable waiter = new Runnable() {
+ @Override
+ public void run() {
+ synchronized (this) {
+ notifyAll();
+ }
+ }
+ };
+ synchronized (waiter) {
+ mHandler.post(runnable);
+ mHandler.post(waiter);
+ try {
+ waiter.wait(TIMEOUT);
+ } catch (InterruptedException ex) {
+ }
+ }
+ }
+
+ private Display findDisplay(Display[] displays, String name) {
+ for (int i = 0; i < displays.length; i++) {
+ if (displays[i].getName().equals(name)) {
+ return displays[i];
+ }
+ }
+ return null;
+ }
+
+ private final class TestPresentation extends Presentation {
+ private final int mColor;
+ private final int mWindowType;
+ private final int mWindowFlags;
+
+ public TestPresentation(Context context, Display display,
+ int color, int windowType, int windowFlags) {
+ super(context, display);
+ mColor = color;
+ mWindowType = windowType;
+ mWindowFlags = windowFlags;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setTitle(TAG);
+ getWindow().setType(mWindowType);
+ getWindow().addFlags(mWindowFlags);
+
+ // Create a solid color image to use as the content of the presentation.
+ ImageView view = new ImageView(getContext());
+ view.setImageDrawable(new ColorDrawable(mColor));
+ view.setLayoutParams(new LayoutParams(
+ LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
+ setContentView(view);
+ }
+ }
+
+ /**
+ * Watches for an image with a large amount of some particular solid color to be shown.
+ */
+ private final class ImageListener
+ implements ImageReader.OnImageAvailableListener {
+ private int mColor = -1;
+
+ public int getColor() {
+ synchronized (this) {
+ return mColor;
+ }
+ }
+
+ public boolean waitForColor(int color, long timeoutMillis) {
+ long timeoutTime = SystemClock.uptimeMillis() + timeoutMillis;
+ synchronized (this) {
+ while (mColor != color) {
+ long now = SystemClock.uptimeMillis();
+ if (now >= timeoutTime) {
+ return false;
+ }
+ try {
+ wait(timeoutTime - now);
+ } catch (InterruptedException ex) {
+ }
+ }
+ return true;
+ }
+ }
+
+ @Override
+ public void onImageAvailable(ImageReader reader) {
+ mImageReaderLock.lock();
+ try {
+ if (reader != mImageReader) {
+ return;
+ }
+
+ Log.d(TAG, "New image available from virtual display.");
+ Image image = reader.getNextImage();
+ if (image != null) {
+ try {
+ // Get the latest buffer.
+ for (;;) {
+ Image nextImage = reader.getNextImage();
+ if (nextImage == null) {
+ break;
+ }
+ reader.releaseImage(image);
+ image = nextImage;
+ }
+
+ // Scan for colors.
+ int color = scanImage(image);
+ synchronized (this) {
+ if (mColor != color) {
+ mColor = color;
+ notifyAll();
+ }
+ }
+ } finally {
+ reader.releaseImage(image);
+ }
+ }
+ } finally {
+ mImageReaderLock.unlock();
+ }
+ }
+
+ private int scanImage(Image image) {
+ final Image.Plane plane = image.getPlanes()[0];
+ final ByteBuffer buffer = plane.getBuffer();
+ final int width = image.getWidth();
+ final int height = image.getHeight();
+ final int pixelStride = plane.getPixelStride();
+ final int rowStride = plane.getRowStride();
+ final int rowPadding = rowStride - pixelStride * width;
+
+ Log.d(TAG, "- Scanning image: width=" + width + ", height=" + height
+ + ", pixelStride=" + pixelStride + ", rowStride=" + rowStride);
+
+ int offset = 0;
+ int blackPixels = 0;
+ int bluePixels = 0;
+ int greenPixels = 0;
+ int otherPixels = 0;
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ int pixel = 0;
+ pixel |= (buffer.get(offset) & 0xff) << 16; // R
+ pixel |= (buffer.get(offset + 1) & 0xff) << 8; // G
+ pixel |= (buffer.get(offset + 2) & 0xff); // B
+ pixel |= (buffer.get(offset + 3) & 0xff) << 24; // A
+ if (pixel == Color.BLACK || pixel == 0) {
+ blackPixels += 1;
+ } else if (pixel == BLUEISH) {
+ bluePixels += 1;
+ } else if (pixel == GREENISH) {
+ greenPixels += 1;
+ } else {
+ otherPixels += 1;
+ if (otherPixels < 10) {
+ Log.d(TAG, "- Found unexpected color: " + Integer.toHexString(pixel));
+ }
+ }
+ offset += pixelStride;
+ }
+ offset += rowPadding;
+ }
+
+ // Return a color if it represents more than one quarter of the pixels.
+ // We use this threshold in case the display is being letterboxed when
+ // mirroring so there might be large black bars on the sides, which is normal.
+ Log.d(TAG, "- Pixels: " + blackPixels + " black, "
+ + bluePixels + " blue, "
+ + greenPixels + " green, "
+ + otherPixels + " other");
+ final int threshold = width * height / 4;
+ if (bluePixels > threshold) {
+ Log.d(TAG, "- Reporting blue.");
+ return BLUEISH;
+ }
+ if (greenPixels > threshold) {
+ Log.d(TAG, "- Reporting green.");
+ return GREENISH;
+ }
+ if (blackPixels > threshold) {
+ Log.d(TAG, "- Reporting black.");
+ return Color.BLACK;
+ }
+ Log.d(TAG, "- Reporting other.");
+ return -1;
+ }
+ }
+}
+