Merge "Fixed MediaPlayer unit test issues"
diff --git a/api/current.xml b/api/current.xml
index 9422552..3ccb4e9 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -58662,6 +58662,21 @@
<parameter name="flags" type="int">
</parameter>
</method>
+<method name="setInstallerPackageName"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="targetPackage" type="java.lang.String">
+</parameter>
+<parameter name="installerPackageName" type="java.lang.String">
+</parameter>
+</method>
<field name="COMPONENT_ENABLED_STATE_DEFAULT"
type="int"
transient="false"
@@ -175729,6 +175744,21 @@
<parameter name="flags" type="int">
</parameter>
</method>
+<method name="setInstallerPackageName"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="targetPackage" type="java.lang.String">
+</parameter>
+<parameter name="installerPackageName" type="java.lang.String">
+</parameter>
+</method>
<method name="setPackageObbPath"
return="void"
abstract="false"
@@ -194129,53 +194159,6 @@
visibility="public"
>
</method>
-<method name="obtain"
- return="android.view.DragEvent"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="action" type="int">
-</parameter>
-<parameter name="x" type="float">
-</parameter>
-<parameter name="y" type="float">
-</parameter>
-<parameter name="description" type="android.content.ClipDescription">
-</parameter>
-<parameter name="data" type="android.content.ClipData">
-</parameter>
-<parameter name="result" type="boolean">
-</parameter>
-</method>
-<method name="obtain"
- return="android.view.DragEvent"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="source" type="android.view.DragEvent">
-</parameter>
-</method>
-<method name="recycle"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
<method name="writeToParcel"
return="void"
abstract="false"
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 3cead1174..c0714e3 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -42,6 +42,9 @@
import android.database.sqlite.SQLiteDebug.DbStats;
import android.graphics.Bitmap;
import android.graphics.Canvas;
+import android.net.IConnectivityManager;
+import android.net.Proxy;
+import android.net.ProxyProperties;
import android.os.Build;
import android.os.Bundle;
import android.os.Debug;
@@ -272,7 +275,7 @@
super(resultCode, resultData, resultExtras, TYPE_COMPONENT, ordered, sticky, token);
this.intent = intent;
}
-
+
Intent intent;
ActivityInfo info;
public String toString() {
@@ -592,6 +595,10 @@
InetAddress.clearDnsCache();
}
+ public void setHttpProxy(String host, String port, String exclList) {
+ Proxy.setHttpProxySystemProperty(host, port, exclList);
+ }
+
public void processInBackground() {
mH.removeMessages(H.GC_WHEN_IDLE);
mH.sendMessage(mH.obtainMessage(H.GC_WHEN_IDLE));
@@ -3253,6 +3260,16 @@
}
}
+ /**
+ * Initialize the default http proxy in this process for the reasons we set the time zone.
+ */
+ IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
+ IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
+ try {
+ ProxyProperties proxyProperties = service.getProxy();
+ Proxy.setHttpProxySystemProperty(proxyProperties);
+ } catch (RemoteException e) {}
+
if (data.instrumentationName != null) {
ContextImpl appContext = new ContextImpl();
appContext.init(data.info, null, this);
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index ce9501a..abb26e3 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -914,6 +914,16 @@
}
@Override
+ public void setInstallerPackageName(String targetPackage,
+ String installerPackageName) {
+ try {
+ mPM.setInstallerPackageName(targetPackage, installerPackageName);
+ } catch (RemoteException e) {
+ // Should never happen!
+ }
+ }
+
+ @Override
public void movePackage(String packageName, IPackageMoveObserver observer, int flags) {
try {
mPM.movePackage(packageName, observer, flags);
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index b19fb59..801c3f9 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -276,7 +276,7 @@
requestThumbnail(b);
return true;
}
-
+
case SCHEDULE_CONFIGURATION_CHANGED_TRANSACTION:
{
data.enforceInterface(IApplicationThread.descriptor);
@@ -297,12 +297,21 @@
return true;
}
+ case SET_HTTP_PROXY_TRANSACTION: {
+ data.enforceInterface(IApplicationThread.descriptor);
+ final String proxy = data.readString();
+ final String port = data.readString();
+ final String exclList = data.readString();
+ setHttpProxy(proxy, port, exclList);
+ return true;
+ }
+
case PROCESS_IN_BACKGROUND_TRANSACTION: {
data.enforceInterface(IApplicationThread.descriptor);
processInBackground();
return true;
}
-
+
case DUMP_SERVICE_TRANSACTION: {
data.enforceInterface(IApplicationThread.descriptor);
ParcelFileDescriptor fd = data.readFileDescriptor();
@@ -758,6 +767,16 @@
data.recycle();
}
+ public void setHttpProxy(String proxy, String port, String exclList) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken(IApplicationThread.descriptor);
+ data.writeString(proxy);
+ data.writeString(port);
+ data.writeString(exclList);
+ mRemote.transact(SET_HTTP_PROXY_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
+ data.recycle();
+ }
+
public void processInBackground() throws RemoteException {
Parcel data = Parcel.obtain();
data.writeInterfaceToken(IApplicationThread.descriptor);
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index dda3107..82b3f75 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -575,7 +575,8 @@
}
Map map = null;
- if (prefsFile.exists() && prefsFile.canRead()) {
+ FileStatus stat = new FileStatus();
+ if (FileUtils.getFileStatus(prefsFile.getPath(), stat) && prefsFile.canRead()) {
try {
FileInputStream str = new FileInputStream(prefsFile);
map = XmlUtils.readMapXml(str);
@@ -588,7 +589,7 @@
Log.w(TAG, "getSharedPreferences", e);
}
}
- sp.replace(map);
+ sp.replace(map, stat);
}
return sp;
}
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index 830c702..eca84ef 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -88,6 +88,7 @@
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 processInBackground() throws RemoteException;
void dumpService(FileDescriptor fd, IBinder servicetoken, String[] args)
throws RemoteException;
@@ -148,4 +149,5 @@
int DUMP_HEAP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+35;
int DUMP_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+36;
int CLEAR_DNS_CACHE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+37;
+ int SET_HTTP_PROXY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+38;
}
diff --git a/core/java/android/app/SharedPreferencesImpl.java b/core/java/android/app/SharedPreferencesImpl.java
index 2096a78..a807d3b 100644
--- a/core/java/android/app/SharedPreferencesImpl.java
+++ b/core/java/android/app/SharedPreferencesImpl.java
@@ -103,12 +103,16 @@
}
}
- public void replace(Map newContents) {
+ /*package*/ void replace(Map newContents, FileStatus stat) {
synchronized (this) {
mLoaded = true;
if (newContents != null) {
mMap = newContents;
}
+ if (stat != null) {
+ mStatTimestamp = stat.mtime;
+ mStatSize = stat.size;
+ }
}
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index a18fdac..f169cd7 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -691,8 +691,6 @@
*
* <p>To disable password expiration, a value of 0 may be used for timeout.
*
- * <p>Timeout must be at least 1 day or IllegalArgumentException will be thrown.
- *
* <p>The calling device admin must have requested
* {@link DeviceAdminInfo#USES_POLICY_EXPIRE_PASSWORD} to be able to call this
* method; if it has not, a security exception will be thrown.
diff --git a/core/java/android/content/ClipboardManager.java b/core/java/android/content/ClipboardManager.java
index 85a6765..3e2b763 100644
--- a/core/java/android/content/ClipboardManager.java
+++ b/core/java/android/content/ClipboardManager.java
@@ -189,7 +189,7 @@
*/
public boolean hasText() {
try {
- return getService().hasPrimaryClip();
+ return getService().hasClipboardText();
} catch (RemoteException e) {
return false;
}
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 4cff3bb..d01a68a 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -158,6 +158,8 @@
void finishPackageInstall(int token);
+ void setInstallerPackageName(in String targetPackage, in String installerPackageName);
+
/**
* Delete a package.
*
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index b5d1653..ac7a95a 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1918,6 +1918,24 @@
String installerPackageName);
/**
+ * Change the installer associated with a given package. There are limitations
+ * on how the installer package can be changed; in particular:
+ * <ul>
+ * <li> A SecurityException will be thrown if <var>installerPackageName</var>
+ * is not signed with the same certificate as the calling application.
+ * <li> A SecurityException will be thrown if <var>targetPackage</var> already
+ * has an installer package, and that installer package is not signed with
+ * the same certificate as the calling application.
+ * </ul>
+ *
+ * @param targetPackage The installed package whose installer will be changed.
+ * @param installerPackageName The package name of the new installer. May be
+ * null to clear the association.
+ */
+ public abstract void setInstallerPackageName(String targetPackage,
+ String installerPackageName);
+
+ /**
* Attempts to delete a package. Since this may take a little while, the result will
* be posted back to the given observer. A deletion will fail if the calling context
* lacks the {@link android.Manifest.permission#DELETE_PACKAGES} permission, if the
diff --git a/core/java/android/content/res/StringBlock.java b/core/java/android/content/res/StringBlock.java
index 5e90b91..23a6f97 100644
--- a/core/java/android/content/res/StringBlock.java
+++ b/core/java/android/content/res/StringBlock.java
@@ -87,21 +87,48 @@
if (style != null) {
if (mStyleIDs == null) {
mStyleIDs = new StyleIDs();
- mStyleIDs.boldId = nativeIndexOfString(mNative, "b");
- mStyleIDs.italicId = nativeIndexOfString(mNative, "i");
- mStyleIDs.underlineId = nativeIndexOfString(mNative, "u");
- mStyleIDs.ttId = nativeIndexOfString(mNative, "tt");
- mStyleIDs.bigId = nativeIndexOfString(mNative, "big");
- mStyleIDs.smallId = nativeIndexOfString(mNative, "small");
- mStyleIDs.supId = nativeIndexOfString(mNative, "sup");
- mStyleIDs.subId = nativeIndexOfString(mNative, "sub");
- mStyleIDs.strikeId = nativeIndexOfString(mNative, "strike");
- mStyleIDs.listItemId = nativeIndexOfString(mNative, "li");
- mStyleIDs.marqueeId = nativeIndexOfString(mNative, "marquee");
+ }
- if (localLOGV) Log.v(TAG, "BoldId=" + mStyleIDs.boldId
- + ", ItalicId=" + mStyleIDs.italicId
- + ", UnderlineId=" + mStyleIDs.underlineId);
+ // the style array is a flat array of <type, start, end> hence
+ // the magic constant 3.
+ for (int styleIndex = 0; styleIndex < style.length; styleIndex += 3) {
+ int styleId = style[styleIndex];
+
+ if (styleId == mStyleIDs.boldId || styleId == mStyleIDs.italicId
+ || styleId == mStyleIDs.underlineId || styleId == mStyleIDs.ttId
+ || styleId == mStyleIDs.bigId || styleId == mStyleIDs.smallId
+ || styleId == mStyleIDs.subId || styleId == mStyleIDs.supId
+ || styleId == mStyleIDs.strikeId || styleId == mStyleIDs.listItemId
+ || styleId == mStyleIDs.marqueeId) {
+ // id already found skip to next style
+ continue;
+ }
+
+ String styleTag = nativeGetString(mNative, styleId);
+
+ if (styleTag.equals("b")) {
+ mStyleIDs.boldId = styleId;
+ } else if (styleTag.equals("i")) {
+ mStyleIDs.italicId = styleId;
+ } else if (styleTag.equals("u")) {
+ mStyleIDs.underlineId = styleId;
+ } else if (styleTag.equals("tt")) {
+ mStyleIDs.ttId = styleId;
+ } else if (styleTag.equals("big")) {
+ mStyleIDs.bigId = styleId;
+ } else if (styleTag.equals("small")) {
+ mStyleIDs.smallId = styleId;
+ } else if (styleTag.equals("sup")) {
+ mStyleIDs.supId = styleId;
+ } else if (styleTag.equals("sub")) {
+ mStyleIDs.subId = styleId;
+ } else if (styleTag.equals("strike")) {
+ mStyleIDs.strikeId = styleId;
+ } else if (styleTag.equals("li")) {
+ mStyleIDs.listItemId = styleId;
+ } else if (styleTag.equals("marquee")) {
+ mStyleIDs.marqueeId = styleId;
+ }
}
res = applyStyles(str, style, mStyleIDs);
@@ -119,17 +146,17 @@
}
static final class StyleIDs {
- private int boldId;
- private int italicId;
- private int underlineId;
- private int ttId;
- private int bigId;
- private int smallId;
- private int subId;
- private int supId;
- private int strikeId;
- private int listItemId;
- private int marqueeId;
+ private int boldId = -1;
+ private int italicId = -1;
+ private int underlineId = -1;
+ private int ttId = -1;
+ private int bigId = -1;
+ private int smallId = -1;
+ private int subId = -1;
+ private int supId = -1;
+ private int strikeId = -1;
+ private int listItemId = -1;
+ private int marqueeId = -1;
}
private CharSequence applyStyles(String str, int[] style, StyleIDs ids) {
@@ -403,6 +430,5 @@
private static final native int nativeGetSize(int obj);
private static final native String nativeGetString(int obj, int idx);
private static final native int[] nativeGetStyle(int obj, int idx);
- private static final native int nativeIndexOfString(int obj, String str);
private static final native void nativeDestroy(int obj);
}
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index 3f6e4ce..c178aee 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -1158,7 +1158,7 @@
*
* <p>
* <center><img src="../../../images/axis_globe.png"
- * alt="Sensors coordinate-system diagram." border="0" /></center>
+ * alt="World coordinate-system diagram." border="0" /></center>
* </p>
*
* <p>
@@ -1521,9 +1521,19 @@
* <li>values[1]: <i>pitch</i>, rotation around the X axis.</li>
* <li>values[2]: <i>roll</i>, rotation around the Y axis.</li>
* </ul>
+ * <p>The reference coordinate-system used is different from the world
+ * coordinate-system defined for the rotation matrix:</p>
+ * <ul>
+ * <li>X is defined as the vector product <b>Y.Z</b> (It is tangential to
+ * the ground at the device's current location and roughly points West).</li>
+ * <li>Y is tangential to the ground at the device's current location and
+ * points towards the magnetic North Pole.</li>
+ * <li>Z points towards the center of the Earth and is perpendicular to the ground.</li>
+ * </ul>
+ *
* <p>
- * <center><img src="../../../images/axis_device.png"
- * alt="Sensors coordinate-system diagram." border="0" /></center>
+ * <center><img src="../../../images/axis_device_inverted.png"
+ * alt="Inverted world coordinate-system diagram." border="0" /></center>
* </p>
* <p>
* All three angles above are in <b>radians</b> and <b>positive</b> in the
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index dd9c8f0..ecfa2c1 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -624,4 +624,39 @@
} catch (RemoteException e) {
}
}
+
+ /**
+ * @param proxyProperties The definition for the new global http proxy
+ * {@hide}
+ */
+ public void setGlobalProxy(ProxyProperties p) {
+ try {
+ mService.setGlobalProxy(p);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * @return proxyProperties for the current global proxy
+ * {@hide}
+ */
+ public ProxyProperties getGlobalProxy() {
+ try {
+ return mService.getGlobalProxy();
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /**
+ * @return proxyProperties for the current proxy (global if set, network specific if not)
+ * {@hide}
+ */
+ public ProxyProperties getProxy() {
+ try {
+ return mService.getProxy();
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
}
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 35054d6..70ab4f1 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -18,6 +18,7 @@
import android.net.LinkProperties;
import android.net.NetworkInfo;
+import android.net.ProxyProperties;
import android.os.IBinder;
/**
@@ -85,4 +86,10 @@
void requestNetworkTransitionWakelock(in String forWhom);
void reportInetCondition(int networkType, int percentage);
+
+ ProxyProperties getGlobalProxy();
+
+ void setGlobalProxy(in ProxyProperties p);
+
+ ProxyProperties getProxy();
}
diff --git a/core/java/android/net/Proxy.java b/core/java/android/net/Proxy.java
index 21c485e..3b9b9fe 100644
--- a/core/java/android/net/Proxy.java
+++ b/core/java/android/net/Proxy.java
@@ -16,9 +16,12 @@
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;
@@ -55,17 +58,22 @@
// Set to true to enable extra debugging.
private static final boolean DEBUG = false;
+ private static final String TAG = "Proxy";
- // Used to notify an app that's caching the default connection proxy
- // that either the default connection or its proxy has changed
- public static final String PROXY_CHANGE_ACTION =
- "android.intent.action.PROXY_CHANGE";
-
- private static ReadWriteLock sProxyInfoLock = new ReentrantReadWriteLock();
-
- private static SettingsObserver sGlobalProxyChangedObserver = null;
-
- private static ProxySpec sGlobalProxySpec = null;
+ /**
+ * Used to notify an app that's caching the default connection proxy
+ * that either the default connection or its proxy has changed.
+ * The intent will have the following extra value:</p>
+ * <ul>
+ * <li><em>EXTRA_PROXY_INFO</em> - The ProxyProperties for the proxy
+ * </ul>
+ *
+ * <p class="note">This is a protected intent that can only be sent by the system
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String PROXY_CHANGE_ACTION = "android.intent.action.PROXY_CHANGE";
+ /** {@hide} **/
+ public static final String EXTRA_PROXY_INFO = "proxy";
private static ConnectivityManager sConnectivityManager = null;
@@ -88,62 +96,6 @@
EXCLLIST_PATTERN = Pattern.compile(EXCLLIST_REGEXP);
}
- // useful because it holds the processed exclusion list - don't want to reparse it each time
- private static class ProxySpec {
- String[] exclusionList;
- InetSocketAddress address = null;
- public ProxySpec() {
- exclusionList = new String[0];
- };
- }
-
- private static boolean isURLInExclusionList(String url, String[] exclusionList) {
- if (url == null) {
- return false;
- }
- Uri u = Uri.parse(url);
- String urlDomain = u.getHost();
- // If the domain is defined as ".android.com" or "android.com", we wish to match
- // http://android.com as well as http://xxx.android.com , but not
- // http://myandroid.com . This code works out the logic.
- for (String excludedDomain : exclusionList) {
- String dotDomain = "." + excludedDomain;
- if (urlDomain.equals(excludedDomain)) {
- return true;
- }
- if (urlDomain.endsWith(dotDomain)) {
- return true;
- }
- }
- // No match
- return false;
- }
-
- private static String parseHost(String proxySpec) {
- int i = proxySpec.indexOf(':');
- if (i == -1) {
- if (DEBUG) {
- Assert.assertTrue(proxySpec.length() == 0);
- }
- return null;
- }
- return proxySpec.substring(0, i);
- }
-
- private static int parsePort(String proxySpec) {
- int i = proxySpec.indexOf(':');
- if (i == -1) {
- if (DEBUG) {
- Assert.assertTrue(proxySpec.length() == 0);
- }
- return -1;
- }
- if (DEBUG) {
- Assert.assertTrue(i < proxySpec.length());
- }
- return Integer.parseInt(proxySpec.substring(i+1));
- }
-
/**
* Return the proxy object to be used for the URL given as parameter.
* @param ctx A Context used to get the settings for the proxy host.
@@ -154,34 +106,31 @@
* {@hide}
*/
public static final java.net.Proxy getProxy(Context ctx, String url) {
- sProxyInfoLock.readLock().lock();
- java.net.Proxy retval;
- try {
- if (sGlobalProxyChangedObserver == null) {
- registerContentObserversReadLocked(ctx);
- parseGlobalProxyInfoReadLocked(ctx);
+ String host = "";
+ if (url != null) {
+ URI uri = URI.create(url);
+ host = uri.getHost();
+ }
+
+ if (!isLocalHost(host)) {
+ if (sConnectivityManager == null) {
+ sConnectivityManager = (ConnectivityManager)ctx.getSystemService(
+ Context.CONNECTIVITY_SERVICE);
}
- if (sGlobalProxySpec != null) {
- // Proxy defined - Apply exclusion rules
- if (isURLInExclusionList(url, sGlobalProxySpec.exclusionList)) {
- // Return no proxy
- retval = java.net.Proxy.NO_PROXY;
- } else {
- retval =
- new java.net.Proxy(java.net.Proxy.Type.HTTP, sGlobalProxySpec.address);
+ if (sConnectivityManager == null) return java.net.Proxy.NO_PROXY;
+
+ ProxyProperties proxyProperties = sConnectivityManager.getProxy();
+
+ if (proxyProperties != null) {
+ if (!proxyProperties.isExcluded(host)) {
+ return proxyProperties.makeProxy();
}
- } else {
- retval = getDefaultProxy(ctx, url);
}
- } finally {
- sProxyInfoLock.readLock().unlock();
}
- if ((retval != java.net.Proxy.NO_PROXY) && (isLocalHost(url))) {
- retval = java.net.Proxy.NO_PROXY;
- }
- return retval;
+ return java.net.Proxy.NO_PROXY;
}
+
// TODO: deprecate this function
/**
* Return the proxy host set by the user.
@@ -236,35 +185,6 @@
return -1;
}
- // TODO - cache the details for each network so we don't have to fetch and parse
- // on each request
- private static final java.net.Proxy getDefaultProxy(Context context, String url) {
- if (sConnectivityManager == null) {
- sConnectivityManager = (ConnectivityManager)context.getSystemService(
- Context.CONNECTIVITY_SERVICE);
- }
- if (sConnectivityManager == null) return java.net.Proxy.NO_PROXY;
-
- LinkProperties linkProperties = sConnectivityManager.getActiveLinkProperties();
-
- if (linkProperties != null) {
- ProxyProperties proxyProperties = linkProperties.getHttpProxy();
-
- if (proxyProperties != null) {
- String exclusionList = proxyProperties.getExclusionList();
- SocketAddress socketAddr = proxyProperties.getSocketAddress();
- if (socketAddr != null) {
- String[] parsedExclusionArray =
- parsedExclusionArray = parseExclusionList(exclusionList);
- if (!isURLInExclusionList(url, parsedExclusionArray)) {
- return new java.net.Proxy(java.net.Proxy.Type.HTTP, socketAddr);
- }
- }
- }
- }
- return java.net.Proxy.NO_PROXY;
- }
-
// TODO: remove this function / deprecate
/**
* Returns the preferred proxy to be used by clients. This is a wrapper
@@ -291,13 +211,11 @@
}
}
- private static final boolean isLocalHost(String url) {
- if (url == null) {
+ private static final boolean isLocalHost(String host) {
+ if (host == null) {
return false;
}
try {
- final URI uri = URI.create(url);
- final String host = uri.getHost();
if (host != null) {
if (host.equalsIgnoreCase("localhost")) {
return true;
@@ -317,92 +235,6 @@
return false;
}
- private static class SettingsObserver extends ContentObserver {
-
- private Context mContext;
-
- SettingsObserver(Context ctx) {
- super(new Handler(ctx.getMainLooper()));
- mContext = ctx;
- }
-
- @Override
- public void onChange(boolean selfChange) {
- sProxyInfoLock.readLock().lock();
- parseGlobalProxyInfoReadLocked(mContext);
- sProxyInfoLock.readLock().unlock();
- }
- }
-
- private static final void registerContentObserversReadLocked(Context ctx) {
- Uri uriGlobalProxy = Settings.Secure.getUriFor(Settings.Secure.HTTP_PROXY);
- Uri uriGlobalExclList =
- Settings.Secure.getUriFor(Settings.Secure.HTTP_PROXY_EXCLUSION_LIST);
-
- // No lock upgrading (from read to write) allowed
- sProxyInfoLock.readLock().unlock();
- sProxyInfoLock.writeLock().lock();
- try {
- sGlobalProxyChangedObserver = new SettingsObserver(ctx);
- } finally {
- // Downgrading locks (from write to read) is allowed
- sProxyInfoLock.readLock().lock();
- sProxyInfoLock.writeLock().unlock();
- }
- ctx.getContentResolver().registerContentObserver(uriGlobalProxy, false,
- sGlobalProxyChangedObserver);
- ctx.getContentResolver().registerContentObserver(uriGlobalExclList, false,
- sGlobalProxyChangedObserver);
- }
-
- private static final void parseGlobalProxyInfoReadLocked(Context ctx) {
- ContentResolver contentResolver = ctx.getContentResolver();
- String proxyHost = Settings.Secure.getString(
- contentResolver,
- Settings.Secure.HTTP_PROXY);
- if (TextUtils.isEmpty(proxyHost)) {
- // Clear signal
- sProxyInfoLock.readLock().unlock();
- sProxyInfoLock.writeLock().lock();
- sGlobalProxySpec = null;
- sProxyInfoLock.readLock().lock();
- sProxyInfoLock.writeLock().unlock();
- return;
- }
- String exclusionListSpec = Settings.Secure.getString(
- contentResolver,
- Settings.Secure.HTTP_PROXY_EXCLUSION_LIST);
- String host = parseHost(proxyHost);
- int port = parsePort(proxyHost);
- ProxySpec tmpProxySpec = null;
- if (proxyHost != null) {
- tmpProxySpec = new ProxySpec();
- tmpProxySpec.address = new InetSocketAddress(host, port);
- tmpProxySpec.exclusionList = parseExclusionList(exclusionListSpec);
- }
- sProxyInfoLock.readLock().unlock();
- sProxyInfoLock.writeLock().lock();
- sGlobalProxySpec = tmpProxySpec;
- sProxyInfoLock.readLock().lock();
- sProxyInfoLock.writeLock().unlock();
- }
-
- private static String[] parseExclusionList(String exclusionList) {
- String[] processedArray = new String[0];
- if (!TextUtils.isEmpty(exclusionList)) {
- String[] exclusionListArray = exclusionList.toLowerCase().split(",");
- processedArray = new String[exclusionListArray.length];
- for (int i = 0; i < exclusionListArray.length; i++) {
- String entry = exclusionListArray[i].trim();
- if (entry.startsWith(".")) {
- entry = entry.substring(1);
- }
- processedArray[i] = entry;
- }
- }
- return processedArray;
- }
-
/**
* Validate syntax of hostname, port and exclusion list entries
* {@hide}
@@ -480,4 +312,44 @@
new SchemeRegistry(), ProxySelector.getDefault(), context);
return ret;
}
+
+ /** @hide */
+ public static final void setHttpProxySystemProperty(ProxyProperties p) {
+ String host = null;
+ String port = null;
+ String exclList = null;
+ if (p != null) {
+ host = p.getHost();
+ port = Integer.toString(p.getPort());
+ exclList = p.getExclusionList();
+ }
+ setHttpProxySystemProperty(host, port, exclList);
+ }
+
+ /** @hide */
+ public static final void setHttpProxySystemProperty(String host, String port, String exclList) {
+ if (exclList != null) exclList = exclList.replace(",", "|");
+ if (false) Log.d(TAG, "setHttpProxySystemProperty :"+host+":"+port+" - "+exclList);
+ if (host != null) {
+ System.setProperty("http.proxyHost", host);
+ System.setProperty("https.proxyHost", host);
+ } else {
+ System.clearProperty("http.proxyHost");
+ System.clearProperty("https.proxyHost");
+ }
+ if (port != null) {
+ System.setProperty("http.proxyPort", port);
+ System.setProperty("https.proxyPort", port);
+ } else {
+ System.clearProperty("http.proxyPort");
+ System.clearProperty("https.proxyPort");
+ }
+ if (exclList != null) {
+ System.setProperty("http.nonProxyHosts", exclList);
+ System.setProperty("https.nonProxyHosts", exclList);
+ } else {
+ System.clearProperty("http.nonProxyHosts");
+ System.clearProperty("https.nonProxyHosts");
+ }
+ }
}
diff --git a/core/java/android/net/ProxyProperties.aidl b/core/java/android/net/ProxyProperties.aidl
new file mode 100644
index 0000000..02ea15d
--- /dev/null
+++ b/core/java/android/net/ProxyProperties.aidl
@@ -0,0 +1,21 @@
+/*
+**
+** Copyright (C) 2010 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.net;
+
+parcelable ProxyProperties;
+
diff --git a/core/java/android/net/ProxyProperties.java b/core/java/android/net/ProxyProperties.java
index 5fd0d89..cbe4445 100644
--- a/core/java/android/net/ProxyProperties.java
+++ b/core/java/android/net/ProxyProperties.java
@@ -19,6 +19,8 @@
import android.os.Parcel;
import android.os.Parcelable;
+import android.text.TextUtils;
+import android.util.Log;
import java.net.InetAddress;
import java.net.InetSocketAddress;
@@ -30,44 +32,108 @@
*/
public class ProxyProperties implements Parcelable {
- private InetSocketAddress mProxy;
+ private String mHost;
+ private int mPort;
private String mExclusionList;
+ private String[] mParsedExclusionList;
- public ProxyProperties() {
+ public ProxyProperties(String host, int port, String exclList) {
+ mHost = host;
+ mPort = port;
+ setExclusionList(exclList);
+ }
+
+ private ProxyProperties(String host, int port, String exclList, String[] parsedExclList) {
+ mHost = host;
+ mPort = port;
+ mExclusionList = exclList;
+ mParsedExclusionList = parsedExclList;
}
// copy constructor instead of clone
public ProxyProperties(ProxyProperties source) {
if (source != null) {
- mProxy = source.getSocketAddress();
- String exclusionList = source.getExclusionList();
- if (exclusionList != null) {
- mExclusionList = new String(exclusionList);
- }
+ mHost = source.getHost();
+ mPort = source.getPort();
+ mExclusionList = source.getExclusionList();
+ mParsedExclusionList = source.mParsedExclusionList;
}
}
public InetSocketAddress getSocketAddress() {
- return mProxy;
+ InetSocketAddress inetSocketAddress = null;
+ try {
+ inetSocketAddress = new InetSocketAddress(mHost, mPort);
+ } catch (IllegalArgumentException e) { }
+ return inetSocketAddress;
}
- public void setSocketAddress(InetSocketAddress proxy) {
- mProxy = proxy;
+ public String getHost() {
+ return mHost;
}
+ public int getPort() {
+ return mPort;
+ }
+
+ // comma separated
public String getExclusionList() {
return mExclusionList;
}
- public void setExclusionList(String exclusionList) {
+ // comma separated
+ private void setExclusionList(String exclusionList) {
mExclusionList = exclusionList;
+ if (mExclusionList == null) {
+ mParsedExclusionList = new String[0];
+ } else {
+ String splitExclusionList[] = exclusionList.toLowerCase().split(",");
+ mParsedExclusionList = new String[splitExclusionList.length * 2];
+ for (int i = 0; i < splitExclusionList.length; i++) {
+ String s = splitExclusionList[i].trim();
+ if (s.startsWith(".")) s = s.substring(1);
+ mParsedExclusionList[i*2] = s;
+ mParsedExclusionList[(i*2)+1] = "." + s;
+ }
+ }
+ }
+
+ public boolean isExcluded(String url) {
+ if (TextUtils.isEmpty(url) || mParsedExclusionList == null ||
+ mParsedExclusionList.length == 0) return false;
+
+ Uri u = Uri.parse(url);
+ String urlDomain = u.getHost();
+ if (urlDomain == null) return false;
+ for (int i = 0; i< mParsedExclusionList.length; i+=2) {
+ if (urlDomain.equals(mParsedExclusionList[i]) ||
+ urlDomain.endsWith(mParsedExclusionList[i+1])) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public java.net.Proxy makeProxy() {
+ java.net.Proxy proxy = java.net.Proxy.NO_PROXY;
+ if (mHost != null) {
+ try {
+ InetSocketAddress inetSocketAddress = new InetSocketAddress(mHost, mPort);
+ proxy = new java.net.Proxy(java.net.Proxy.Type.HTTP, inetSocketAddress);
+ } catch (IllegalArgumentException e) {
+ }
+ }
+ return proxy;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
- if (mProxy != null) {
- sb.append(mProxy.toString());
+ if (mHost != null) {
+ sb.append("[");
+ sb.append(mHost);
+ sb.append("] ");
+ sb.append(Integer.toString(mPort));
if (mExclusionList != null) {
sb.append(" xl=").append(mExclusionList);
}
@@ -75,6 +141,20 @@
return sb.toString();
}
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof ProxyProperties)) return false;
+ ProxyProperties p = (ProxyProperties)o;
+ if (mExclusionList != null && !mExclusionList.equals(p.getExclusionList())) return false;
+ if (mHost != null && p.getHost() != null && mHost.equals(p.getHost()) == false) {
+ return false;
+ }
+ if (mHost != null && p.mHost == null) return false;
+ if (mHost == null && p.mHost != null) return false;
+ if (mPort != p.mPort) return false;
+ return true;
+ }
+
/**
* Implement the Parcelable interface
* @hide
@@ -88,27 +168,15 @@
* @hide
*/
public void writeToParcel(Parcel dest, int flags) {
- String host = null;
- if (mProxy != null) {
- try {
- InetAddress addr = mProxy.getAddress();
- if (addr != null) {
- host = addr.getHostAddress();
- } else {
- /* Does not resolve when addr is null */
- host = mProxy.getHostName();
- }
- } catch (Exception e) { }
- }
-
- if (host != null) {
+ if (mHost != null) {
dest.writeByte((byte)1);
- dest.writeString(host);
- dest.writeInt(mProxy.getPort());
+ dest.writeString(mHost);
+ dest.writeInt(mPort);
} else {
dest.writeByte((byte)0);
}
dest.writeString(mExclusionList);
+ dest.writeStringArray(mParsedExclusionList);
}
/**
@@ -118,16 +186,16 @@
public static final Creator<ProxyProperties> CREATOR =
new Creator<ProxyProperties>() {
public ProxyProperties createFromParcel(Parcel in) {
- ProxyProperties proxyProperties = new ProxyProperties();
+ String host = null;
+ int port = 0;
if (in.readByte() == 1) {
- try {
- String host = in.readString();
- int port = in.readInt();
- proxyProperties.setSocketAddress(InetSocketAddress.createUnresolved(
- host, port));
- } catch (IllegalArgumentException e) { }
+ host = in.readString();
+ port = in.readInt();
}
- proxyProperties.setExclusionList(in.readString());
+ String exclList = in.readString();
+ String[] parsedExclList = in.readStringArray();
+ ProxyProperties proxyProperties =
+ new ProxyProperties(host, port, exclList, parsedExclList);
return proxyProperties;
}
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index 93542c6e..c836e56 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -104,7 +104,7 @@
*/
public final class StrictMode {
private static final String TAG = "StrictMode";
- private static final boolean LOG_V = false;
+ private static final boolean LOG_V = Log.isLoggable(TAG, Log.VERBOSE);
private static final boolean IS_USER_BUILD = "user".equals(Build.TYPE);
diff --git a/core/java/android/provider/Mtp.java b/core/java/android/provider/Mtp.java
index de161e7..78110ef 100644
--- a/core/java/android/provider/Mtp.java
+++ b/core/java/android/provider/Mtp.java
@@ -113,6 +113,25 @@
}
/**
+ * Used for copying files from device to host.
+ * Constructs a Uri based on the ID of the device and object for the source file,
+ * and the path for the destination file.
+ * When passed to the ContentProvider.insert() method, the file will be transferred
+ * to the specified destination directory and insert() will return a content Uri
+ * for the new file in the MediaProvider.
+ * ContentProvider.insert() will throw IllegalArgumentException if the destination
+ * path is not in the external storage or internal media directory.
+ */
+ public static Uri getContentUriForImport(int deviceID, long objectID, String destPath) {
+ if (destPath.length() == 0 || destPath.charAt(0) != '/') {
+ throw new IllegalArgumentException(
+ "destPath must be a full path in getContentUriForImport");
+ }
+ return Uri.parse(CONTENT_AUTHORITY_DEVICE_SLASH + deviceID
+ + "/import/" + objectID + "?" + destPath);
+ }
+
+ /**
* The following columns correspond to the fields in the ObjectInfo dataset
* as described in the MTP specification.
*/
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 1fe2c5a..7075774 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2424,17 +2424,32 @@
public static final String DISABLED_SYSTEM_INPUT_METHODS = "disabled_system_input_methods";
/**
- * Host name and port for global proxy.
+ * Host name and port for global http proxy. Uses ':' seperator for between host and port
+ * TODO - deprecate in favor of global_http_proxy_host, etc
*/
public static final String HTTP_PROXY = "http_proxy";
/**
+ * Host name for global http proxy. Set via ConnectivityManager.
+ * @hide
+ */
+ public static final String GLOBAL_HTTP_PROXY_HOST = "global_http_proxy_host";
+
+ /**
+ * Integer host port for global http proxy. Set via ConnectivityManager.
+ * @hide
+ */
+ public static final String GLOBAL_HTTP_PROXY_PORT = "global_http_proxy_port";
+
+ /**
* Exclusion list for global proxy. This string contains a list of comma-separated
* domains where the global proxy does not apply. Domains should be listed in a comma-
* separated list. Example of acceptable formats: ".domain1.com,my.domain2.com"
+ * Use ConnectivityManager to set/get.
* @hide
*/
- public static final String HTTP_PROXY_EXCLUSION_LIST = "http_proxy_exclusion_list";
+ public static final String GLOBAL_HTTP_PROXY_EXCLUSION_LIST =
+ "global_http_proxy_exclusion_list";
/**
* Enables the UI setting to allow the user to specify the global HTTP proxy
diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java
index 56ab6bf..4f56281 100644
--- a/core/java/android/server/BluetoothEventLoop.java
+++ b/core/java/android/server/BluetoothEventLoop.java
@@ -287,6 +287,8 @@
mBluetoothService.setIsDiscovering(true);
intent = new Intent(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
} else {
+ // Stop the discovery.
+ mBluetoothService.cancelDiscovery();
mBluetoothService.setIsDiscovering(false);
intent = new Intent(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
}
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index cc969cb..3dd1ecd 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -21,10 +21,10 @@
import android.graphics.Bitmap;
import android.graphics.Paint;
import android.text.style.LeadingMarginSpan;
+import android.text.style.LeadingMarginSpan.LeadingMarginSpan2;
import android.text.style.LineHeightSpan;
import android.text.style.MetricAffectingSpan;
import android.text.style.TabStopSpan;
-import android.text.style.LeadingMarginSpan.LeadingMarginSpan2;
/**
* StaticLayout is a Layout for text that will not be edited after it
@@ -36,9 +36,7 @@
* float, float, android.graphics.Paint)
* Canvas.drawText()} directly.</p>
*/
-public class
-StaticLayout
-extends Layout
+public class StaticLayout extends Layout
{
public StaticLayout(CharSequence source, TextPaint paint,
int width,
@@ -260,7 +258,7 @@
float before = w;
if (c == '\n') {
- ;
+ // intentionally left empty
} else if (c == '\t') {
if (hasTab == false) {
hasTab = true;
@@ -837,6 +835,7 @@
// rather than relying on member functions.
// The logic mirrors that of Layout.getLineForVertical
// FIXME: It may be faster to do a linear search for layouts without many lines.
+ @Override
public int getLineForVertical(int vertical) {
int high = mLineCount;
int low = -1;
@@ -857,38 +856,47 @@
}
}
+ @Override
public int getLineCount() {
return mLineCount;
}
+ @Override
public int getLineTop(int line) {
return mLines[mColumns * line + TOP];
}
+ @Override
public int getLineDescent(int line) {
return mLines[mColumns * line + DESCENT];
}
+ @Override
public int getLineStart(int line) {
return mLines[mColumns * line + START] & START_MASK;
}
+ @Override
public int getParagraphDirection(int line) {
return mLines[mColumns * line + DIR] >> DIR_SHIFT;
}
+ @Override
public boolean getLineContainsTab(int line) {
return (mLines[mColumns * line + TAB] & TAB_MASK) != 0;
}
+ @Override
public final Directions getLineDirections(int line) {
return mLineDirections[line];
}
+ @Override
public int getTopPadding() {
return mTopPadding;
}
+ @Override
public int getBottomPadding() {
return mBottomPadding;
}
@@ -935,7 +943,6 @@
private Directions[] mLineDirections;
private static final int START_MASK = 0x1FFFFFFF;
- private static final int DIR_MASK = 0xC0000000;
private static final int DIR_SHIFT = 30;
private static final int TAB_MASK = 0x20000000;
diff --git a/core/java/android/view/DragEvent.java b/core/java/android/view/DragEvent.java
index bbac14c..07e87d6 100644
--- a/core/java/android/view/DragEvent.java
+++ b/core/java/android/view/DragEvent.java
@@ -41,16 +41,90 @@
private static DragEvent gRecyclerTop = null;
/**
- * action constants for DragEvent dispatch
+ * Action constant returned by {@link #getAction()}. Delivery of a DragEvent whose
+ * action is ACTION_DRAG_STARTED means that a drag operation has been initiated. The
+ * view receiving this DragEvent should inspect the metadata of the dragged content,
+ * available via {@link #getClipDescription()}, and return {@code true} from
+ * {@link View#onDragEvent(DragEvent)} if the view is prepared to accept a drop of
+ * that clip data. If the view chooses to present a visual indication that it is
+ * a valid target of the ongoing drag, then it should draw that indication in response
+ * to this event.
+ * <p>
+ * A view will only receive ACTION_DRAG_ENTERED, ACTION_DRAG_LOCATION, ACTION_DRAG_EXITED,
+ * and ACTION_DRAG_LOCATION events if it returns {@code true} in response to the
+ * ACTION_DRAG_STARTED event.
*/
public static final int ACTION_DRAG_STARTED = 1;
- public static final int ACTION_DRAG_LOCATION = 2;
- public static final int ACTION_DROP = 3;
- public static final int ACTION_DRAG_ENDED = 4;
- public static final int ACTION_DRAG_ENTERED = 5;
- public static final int ACTION_DRAG_EXITED = 6;
- /* hide the constructor behind package scope */
+ /**
+ * Action constant returned by {@link #getAction()}. Delivery of a DragEvent whose
+ * action is ACTION_DRAG_LOCATION means that the drag operation is currently hovering
+ * over the view. The {@link #getX()} and {@link #getY()} methods supply the location
+ * of the drag point within the view's coordinate system.
+ * <p>
+ * A view will receive an ACTION_DRAG_ENTERED event before receiving any
+ * ACTION_DRAG_LOCATION events. If the drag point leaves the view, then an
+ * ACTION_DRAG_EXITED event is delivered to the view, after which no more
+ * ACTION_DRAG_LOCATION events will be sent (unless the drag re-enters the view,
+ * of course).
+ */
+ public static final int ACTION_DRAG_LOCATION = 2;
+
+ /**
+ * Action constant returned by {@link #getAction()}. Delivery of a DragEvent whose
+ * action is ACTION_DROP means that the dragged content has been dropped on this view.
+ * The view should retrieve the content via {@link #getClipData()} and act on it
+ * appropriately. The {@link #getX()} and {@link #getY()} methods supply the location
+ * of the drop point within the view's coordinate system.
+ * <p>
+ * The view should return {@code true} from its {@link View#onDragEvent(DragEvent)}
+ * method in response to this event if it accepted the content, and {@code false}
+ * if it ignored the drop.
+ */
+ public static final int ACTION_DROP = 3;
+
+ /**
+ * Action constant returned by {@link #getAction()}. Delivery of a DragEvent whose
+ * action is ACTION_DRAG_ENDED means that the drag operation has concluded. A view
+ * that is drawing a visual indication of drag acceptance should return to its usual
+ * drawing state in response to this event.
+ * <p>
+ * All views that received an ACTION_DRAG_STARTED event will receive the
+ * ACTION_DRAG_ENDED event even if they are not currently visible when the drag
+ * ends.
+ */
+ public static final int ACTION_DRAG_ENDED = 4;
+
+ /**
+ * Action constant returned by {@link #getAction()}. Delivery of a DragEvent whose
+ * action is ACTION_DRAG_ENTERED means that the drag point has entered the view's
+ * bounds. If the view changed its visual state in response to the ACTION_DRAG_ENTERED
+ * event, it should return to its normal drag-in-progress visual state in response to
+ * this event.
+ * <p>
+ * A view will receive an ACTION_DRAG_ENTERED event before receiving any
+ * ACTION_DRAG_LOCATION events. If the drag point leaves the view, then an
+ * ACTION_DRAG_EXITED event is delivered to the view, after which no more
+ * ACTION_DRAG_LOCATION events will be sent (unless the drag re-enters the view,
+ * of course).
+ */
+ public static final int ACTION_DRAG_ENTERED = 5;
+
+ /**
+ * Action constant returned by {@link #getAction()}. Delivery of a DragEvent whose
+ * action is ACTION_DRAG_ENTERED means that the drag point has entered the view's
+ * bounds. If the view chooses to present a visual indication that it will receive
+ * the drop if it occurs now, then it should draw that indication in response to
+ * this event.
+ * <p>
+ * A view will receive an ACTION_DRAG_ENTERED event before receiving any
+ * ACTION_DRAG_LOCATION events. If the drag point leaves the view, then an
+ * ACTION_DRAG_EXITED event is delivered to the view, after which no more
+ * ACTION_DRAG_LOCATION events will be sent (unless the drag re-enters the view,
+ * of course).
+ */
+public static final int ACTION_DRAG_EXITED = 6;
+
private DragEvent() {
}
@@ -68,6 +142,7 @@
return DragEvent.obtain(0, 0f, 0f, null, null, false);
}
+ /** @hide */
public static DragEvent obtain(int action, float x, float y,
ClipDescription description, ClipData data, boolean result) {
final DragEvent ev;
@@ -90,31 +165,64 @@
return ev;
}
+ /** @hide */
public static DragEvent obtain(DragEvent source) {
return obtain(source.mAction, source.mX, source.mY,
source.mClipDescription, source.mClipData, source.mDragResult);
}
+ /**
+ * Inspect the action value of this event.
+ * @return One of {@link #ACTION_DRAG_STARTED}, {@link #ACTION_DRAG_ENDED},
+ * {@link #ACTION_DROP}, {@link #ACTION_DRAG_ENTERED}, {@link #ACTION_DRAG_EXITED},
+ * or {@link #ACTION_DRAG_LOCATION}.
+ */
public int getAction() {
return mAction;
}
+ /**
+ * For ACTION_DRAG_LOCATION and ACTION_DROP events, returns the x coordinate of the
+ * drag point.
+ * @return The current drag point's x coordinate, when relevant.
+ */
public float getX() {
return mX;
}
+ /**
+ * For ACTION_DRAG_LOCATION and ACTION_DROP events, returns the y coordinate of the
+ * drag point.
+ * @return The current drag point's y coordinate, when relevant.
+ */
public float getY() {
return mY;
}
+ /**
+ * Provides the data payload of the drag operation. This payload is only available
+ * for events whose action value is ACTION_DROP.
+ * @return The ClipData containing the data being dropped on the view.
+ */
public ClipData getClipData() {
return mClipData;
}
+ /**
+ * Provides a description of the drag operation's data payload. This payload is
+ * available for all DragEvents other than ACTION_DROP.
+ * @return A ClipDescription describing the contents of the data being dragged.
+ */
public ClipDescription getClipDescription() {
return mClipDescription;
}
+ /**
+ * Provides an indication of whether the drag operation concluded successfully.
+ * This method is only available on ACTION_DRAG_ENDED events.
+ * @return {@code true} if the drag operation ended with an accepted drop; {@code false}
+ * otherwise.
+ */
public boolean getResult() {
return mDragResult;
}
@@ -122,6 +230,8 @@
/**
* Recycle the DragEvent, to be re-used by a later caller. After calling
* this function you must never touch the event again.
+ *
+ * @hide
*/
public final void recycle() {
// Ensure recycle is only called once!
diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java
index c6fae9d..13d0ec1 100644
--- a/core/java/android/view/animation/Animation.java
+++ b/core/java/android/view/animation/Animation.java
@@ -18,9 +18,11 @@
import android.content.Context;
import android.content.res.TypedArray;
+import android.graphics.RectF;
+import android.os.SystemProperties;
import android.util.AttributeSet;
import android.util.TypedValue;
-import android.graphics.RectF;
+import dalvik.system.CloseGuard;
/**
* Abstraction for an Animation that can be applied to Views, Surfaces, or
@@ -86,7 +88,10 @@
* content for the duration of the animation.
*/
public static final int ZORDER_BOTTOM = -1;
-
+
+ private static final boolean USE_CLOSEGUARD
+ = SystemProperties.getBoolean("log.closeguard.Animation", false);
+
/**
* Set by {@link #getTransformation(long, Transformation)} when the animation ends.
*/
@@ -194,6 +199,8 @@
Transformation mTransformation = new Transformation();
Transformation mPreviousTransformation = new Transformation();
+ private final CloseGuard guard = CloseGuard.get();
+
/**
* Creates a new animation with a duration of 0ms, the default interpolator, with
* fillBefore set to true and fillAfter set to false
@@ -276,6 +283,7 @@
if (mStarted && !mEnded) {
if (mListener != null) mListener.onAnimationEnd(this);
mEnded = true;
+ guard.close();
}
// Make sure we move the animation to the end
mStartTime = Long.MIN_VALUE;
@@ -288,6 +296,7 @@
public void detach() {
if (mStarted && !mEnded) {
mEnded = true;
+ guard.close();
if (mListener != null) mListener.onAnimationEnd(this);
}
}
@@ -781,6 +790,9 @@
mListener.onAnimationStart(this);
}
mStarted = true;
+ if (USE_CLOSEGUARD) {
+ guard.open("cancel or detach or getTransformation");
+ }
}
if (mFillEnabled) normalizedTime = Math.max(Math.min(normalizedTime, 1.0f), 0.0f);
@@ -797,6 +809,7 @@
if (mRepeatCount == mRepeated) {
if (!mEnded) {
mEnded = true;
+ guard.close();
if (mListener != null) {
mListener.onAnimationEnd(this);
}
@@ -953,6 +966,16 @@
}
}
+ protected void finalize() throws Throwable {
+ try {
+ if (guard != null) {
+ guard.warnIfOpen();
+ }
+ } finally {
+ super.finalize();
+ }
+ }
+
/**
* Utility class to parse a string description of a size.
*/
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index 4d582ef..f4caa74 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -483,11 +483,11 @@
// default to "en"
buffer.append("en");
}
+ buffer.append("; ");
// add the model for the release build
if ("REL".equals(Build.VERSION.CODENAME)) {
final String model = Build.MODEL;
if (model.length() > 0) {
- buffer.append("; ");
buffer.append(model);
}
}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 383e977..5bf2ad4 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -2187,14 +2187,6 @@
private View mTitleBar;
/**
- * Since we draw the title bar ourselves, we removed the shadow from the
- * browser's activity. We do want a shadow at the bottom of the title bar,
- * or at the top of the screen if the title bar is not visible. This
- * drawable serves that purpose.
- */
- private Drawable mTitleShadow;
-
- /**
* Add or remove a title bar to be embedded into the WebView, and scroll
* along with it vertically, while remaining in view horizontally. Pass
* null to remove the title bar from the WebView, and return to drawing
@@ -2220,10 +2212,6 @@
addView(v, new AbsoluteLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT, 0, 0));
- if (mTitleShadow == null) {
- mTitleShadow = (Drawable) mContext.getResources().getDrawable(
- com.android.internal.R.drawable.title_bar_shadow);
- }
}
mTitleBar = v;
}
@@ -3470,15 +3458,6 @@
drawContent(canvas);
canvas.restoreToCount(saveCount);
- // Now draw the shadow.
- int titleH = getVisibleTitleHeight();
- if (mTitleBar != null && titleH == 0) {
- int height = (int) (5f * getContext().getResources()
- .getDisplayMetrics().density);
- mTitleShadow.setBounds(mScrollX, mScrollY, mScrollX + getWidth(),
- mScrollY + height);
- mTitleShadow.draw(canvas);
- }
if (AUTO_REDRAW_HACK && mAutoRedraw) {
invalidate();
}
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index d388985..a3e9bc9 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -3018,6 +3018,11 @@
reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
clearScrollingCache();
mScroller.abortAnimation();
+
+ if (mFlingStrictSpan != null) {
+ mFlingStrictSpan.finish();
+ mFlingStrictSpan = null;
+ }
}
void flywheelTouch() {
@@ -3086,11 +3091,6 @@
Debug.stopMethodTracing();
mFlingProfilingStarted = false;
}
-
- if (mFlingStrictSpan != null) {
- mFlingStrictSpan.finish();
- mFlingStrictSpan = null;
- }
}
}
}
diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java
index a371290..595b487 100644
--- a/core/java/android/widget/DatePicker.java
+++ b/core/java/android/widget/DatePicker.java
@@ -22,6 +22,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.text.format.DateFormat;
+import android.text.format.DateUtils;
import android.util.AttributeSet;
import android.util.SparseArray;
import android.view.LayoutInflater;
@@ -33,6 +34,7 @@
import java.text.DateFormatSymbols;
import java.text.SimpleDateFormat;
import java.util.Calendar;
+import java.util.Locale;
/**
* A view for selecting a month / year / day based on a calendar like layout.
@@ -47,7 +49,10 @@
private static final int DEFAULT_START_YEAR = 1900;
private static final int DEFAULT_END_YEAR = 2100;
-
+
+ // This ignores Undecimber, but we only support real Gregorian calendars.
+ private static final int NUMBER_OF_MONTHS = 12;
+
/* UI Components */
private final NumberPicker mDayPicker;
private final NumberPicker mMonthPicker;
@@ -62,6 +67,10 @@
private int mMonth;
private int mYear;
+ private Object mMonthUpdateLock = new Object();
+ private volatile Locale mMonthLocale;
+ private String[] mShortMonths;
+
/**
* The callback used to indicate the user changes the date.
*/
@@ -102,8 +111,7 @@
});
mMonthPicker = (NumberPicker) findViewById(R.id.month);
mMonthPicker.setFormatter(NumberPicker.TWO_DIGIT_FORMATTER);
- DateFormatSymbols dfs = new DateFormatSymbols();
- String[] months = dfs.getShortMonths();
+ final String[] months = getShortMonths();
/*
* If the user is in a locale where the month names are numeric,
@@ -114,9 +122,9 @@
for (int i = 0; i < months.length; i++) {
months[i] = String.valueOf(i + 1);
}
- mMonthPicker.setRange(1, 12);
+ mMonthPicker.setRange(1, NUMBER_OF_MONTHS);
} else {
- mMonthPicker.setRange(1, 12, months);
+ mMonthPicker.setRange(1, NUMBER_OF_MONTHS, months);
}
mMonthPicker.setSpeed(200);
@@ -246,11 +254,30 @@
mMonth = monthOfYear;
mDay = dayOfMonth;
updateSpinners();
- reorderPickers(new DateFormatSymbols().getShortMonths());
+ reorderPickers(getShortMonths());
notifyDateChanged();
}
}
+ private String[] getShortMonths() {
+ final Locale currentLocale = Locale.getDefault();
+ if (currentLocale.equals(mMonthLocale) && mShortMonths != null) {
+ return mShortMonths;
+ } else {
+ synchronized (mMonthUpdateLock) {
+ if (!currentLocale.equals(mMonthLocale)) {
+ mShortMonths = new String[NUMBER_OF_MONTHS];
+ for (int i = 0; i < NUMBER_OF_MONTHS; i++) {
+ mShortMonths[i] = DateUtils.getMonthString(Calendar.JANUARY + i,
+ DateUtils.LENGTH_MEDIUM);
+ }
+ mMonthLocale = currentLocale;
+ }
+ }
+ return mShortMonths;
+ }
+ }
+
private static class SavedState extends BaseSavedState {
private final int mYear;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index e6503a9..80a6a2f 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -7117,7 +7117,10 @@
csr = new CommitSelectionReceiver(oldSelStart, oldSelEnd);
}
- handled |= imm.showSoftInput(this, 0, csr) && (csr != null);
+ if (!mTextIsSelectable) {
+ // Selection in read-only text should not bring up the IME.
+ handled |= imm.showSoftInput(this, 0, csr) && (csr != null);
+ }
// Cannot be done by CommitSelectionReceiver, which might not always be called,
// for instance when dealing with an ExtractEditText.
diff --git a/core/jni/android_util_StringBlock.cpp b/core/jni/android_util_StringBlock.cpp
index 641fbce..a021efd 100644
--- a/core/jni/android_util_StringBlock.cpp
+++ b/core/jni/android_util_StringBlock.cpp
@@ -147,25 +147,6 @@
return array;
}
-static jint android_content_StringBlock_nativeIndexOfString(JNIEnv* env, jobject clazz,
- jint token, jstring str)
-{
- ResStringPool* osb = (ResStringPool*)token;
- if (osb == NULL || str == NULL) {
- doThrow(env, "java/lang/NullPointerException");
- return 0;
- }
-
- const char16_t* str16 = env->GetStringChars(str, NULL);
- jsize strLen = env->GetStringLength(str);
-
- ssize_t idx = osb->indexOfString(str16, strLen);
-
- env->ReleaseStringChars(str, str16);
-
- return idx;
-}
-
static void android_content_StringBlock_nativeDestroy(JNIEnv* env, jobject clazz,
jint token)
{
@@ -193,8 +174,6 @@
(void*) android_content_StringBlock_nativeGetString },
{ "nativeGetStyle", "(II)[I",
(void*) android_content_StringBlock_nativeGetStyle },
- { "nativeIndexOfString","(ILjava/lang/String;)I",
- (void*) android_content_StringBlock_nativeIndexOfString },
{ "nativeDestroy", "(I)V",
(void*) android_content_StringBlock_nativeDestroy },
};
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index d02ca64..ddc63dd 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -92,6 +92,7 @@
<protected-broadcast android:name="android.nfc.action.LLCP_LINK_STATE_CHANGED" />
<protected-broadcast android:name="android.nfc.action.TRANSACTION_DETECTED" />
<protected-broadcast android:name="android.intent.action.CLEAR_DNS_CACHE" />
+ <protected-broadcast android:name="android.intent.action.PROXY_CHANGE" />
<!-- ====================================== -->
<!-- Permissions for things that cost money -->
diff --git a/core/res/res/drawable-hdpi/overscroll_edge.png b/core/res/res/drawable-hdpi/overscroll_edge.png
index 58b4f19..6d3c26d 100644
--- a/core/res/res/drawable-hdpi/overscroll_edge.png
+++ b/core/res/res/drawable-hdpi/overscroll_edge.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/overscroll_glow.png b/core/res/res/drawable-hdpi/overscroll_glow.png
index fa58f1e..0b0b936 100644
--- a/core/res/res/drawable-hdpi/overscroll_glow.png
+++ b/core/res/res/drawable-hdpi/overscroll_glow.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/title_bar_shadow.9.png b/core/res/res/drawable-hdpi/title_bar_shadow.9.png
deleted file mode 100644
index e6dab63..0000000
--- a/core/res/res/drawable-hdpi/title_bar_shadow.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-ldpi/title_bar_shadow.9.png b/core/res/res/drawable-ldpi/title_bar_shadow.9.png
deleted file mode 100644
index fc45ee8..0000000
--- a/core/res/res/drawable-ldpi/title_bar_shadow.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/overscroll_edge.png b/core/res/res/drawable-mdpi/overscroll_edge.png
index 22f4ef8..6d3c26d 100644
--- a/core/res/res/drawable-mdpi/overscroll_edge.png
+++ b/core/res/res/drawable-mdpi/overscroll_edge.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/overscroll_glow.png b/core/res/res/drawable-mdpi/overscroll_glow.png
index 761fb74..0b0b936 100644
--- a/core/res/res/drawable-mdpi/overscroll_glow.png
+++ b/core/res/res/drawable-mdpi/overscroll_glow.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/title_bar_shadow.9.png b/core/res/res/drawable-mdpi/title_bar_shadow.9.png
deleted file mode 100644
index dbcefee..0000000
--- a/core/res/res/drawable-mdpi/title_bar_shadow.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/layout/grant_credentials_permission.xml b/core/res/res/layout/grant_credentials_permission.xml
index 4133ea9..0ffe8de 100644
--- a/core/res/res/layout/grant_credentials_permission.xml
+++ b/core/res/res/layout/grant_credentials_permission.xml
@@ -47,8 +47,7 @@
android:layout_height="wrap_content"
android:fillViewport="true"
android:layout_weight="1"
- android:gravity="top|center_horizontal"
- android:foreground="@drawable/title_bar_shadow">
+ android:gravity="top|center_horizontal">
<LinearLayout
android:layout_width="match_parent"
diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml
index 3e546a1..04c6538 100644
--- a/core/res/res/values/arrays.xml
+++ b/core/res/res/values/arrays.xml
@@ -67,7 +67,6 @@
<item>@drawable/text_select_handle_middle</item>
<item>@drawable/text_select_handle_right</item>
<item>@drawable/title_bar</item>
- <item>@drawable/title_bar_shadow</item>
<!-- Visual lock screen -->
<item>@drawable/indicator_code_lock_drag_direction_green_up</item>
<item>@drawable/indicator_code_lock_drag_direction_red_up</item>
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index f5dca47..9215bf2 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -130,7 +130,7 @@
<item name="windowNoTitle">false</item>
<item name="windowFullscreen">false</item>
<item name="windowIsFloating">false</item>
- <item name="windowContentOverlay">@android:drawable/title_bar_shadow</item>
+ <item name="windowContentOverlay">@null</item>
<item name="windowShowWallpaper">false</item>
<item name="windowTitleStyle">@android:style/WindowTitle</item>
<item name="windowTitleSize">25dip</item>
diff --git a/docs/html/guide/appendix/media-formats.jd b/docs/html/guide/appendix/media-formats.jd
index 94a6471..8709994 100644
--- a/docs/html/guide/appendix/media-formats.jd
+++ b/docs/html/guide/appendix/media-formats.jd
@@ -22,7 +22,7 @@
<tr>
<td rowspan="9">Audio</td>
<td>AAC LC/LTP</td>
-<td> </td>
+<td style="text-align: center;">X</td>
<td style="text-align: center;">X</td>
<td rowspan="3">Mono/Stereo content in any combination of standard bit rates up to 160 kbps and sampling rates from 8 to 48kHz</td>
<td rowspan="3">3GPP (.3gp) and MPEG-4 (.mp4, .m4a). No support for raw AAC (.aac)</td>
@@ -51,7 +51,7 @@
<tr>
<td>AMR-WB</td>
-<td> </td>
+<td style="text-align: center;">X</td>
<td style="text-align: center;">X</td>
<td>9 rates from 6.60 kbit/s to 23.85 kbit/s sampled @ 16kHz</td>
<td>3GPP (.3gp)</td>
@@ -109,7 +109,7 @@
<tr>
<td>PNG</td>
-<td> </td>
+<td style="text-align: center;">X</td>
<td style="text-align: center;">X</td>
<td> </td>
<td>PNG (.png)</td>
diff --git a/docs/html/guide/guide_toc.cs b/docs/html/guide/guide_toc.cs
index 545807e..3cc1806 100644
--- a/docs/html/guide/guide_toc.cs
+++ b/docs/html/guide/guide_toc.cs
@@ -196,6 +196,9 @@
</li>
</ul>
<ul>
+ <li><a href="<?cs var:toroot ?>guide/topics/fragments/index.html">
+ <span class="en">Fragments</span>
+ </a> <span class="new">new!</span></li>
<li class="toggle-list">
<div><a href="<?cs var:toroot ?>guide/topics/graphics/index.html">
<span class="en">Graphics</span>
diff --git a/docs/html/guide/practices/screens_support.jd b/docs/html/guide/practices/screens_support.jd
index 13b5e3a..7811d90 100644
--- a/docs/html/guide/practices/screens_support.jd
+++ b/docs/html/guide/practices/screens_support.jd
@@ -35,7 +35,9 @@
<ol>
<li><code><a href="{@docRoot}guide/topics/manifest/supports-screens-element.html"><supports-screens></a></code></li>
<li><code><a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html"><uses-sdk></a></code></li>
- <li><a href="{@docRoot}guide/topics/resources/providing-resources.html#AlternativeResources">Alternative Resources</a></li>
+ <li><a
+href="{@docRoot}guide/topics/resources/providing-resources.html#AlternativeResources">
+Providing Alternative Resources</a></li>
<li><a href="{@docRoot}guide/developing/tools/avd.html">Android Virtual Devices</a></li>
</ol>
@@ -53,7 +55,7 @@
<p>This document explains the screens-support features provided by the platform
and how you use them in your application. By following the practices described
here, you can easily create an application that displays properly on all
-supported device screens and that you can deploy to any device as a single .apk.
+supported device screens and that you can deploy to any device as a single {@code .apk}.
</p>
<p>If you have already developed and published an application for Android 1.5 or
@@ -63,10 +65,16 @@
are needed, however you should make sure to <a href="#testing">test your
application</a> on all supported screens. </p>
+<p>Starting in Android 2.2, the platform includes support for extra high density screens
+(<em>xhdpi</em>), and starting in Android 2.3, the platform includes support for extra large screens
+(<em>xlarge</em>). If you've already followed the guidance in this document to support all other
+screen types, you should consider providing additional support for <em>xhdpi</em> and
+<em>xlarge</em> screens.</p>
+
<p>In particular, if you have an existing application that you would like to
-make available for users of devices with small screens (such as QVGA), please
-see <a href="#strategies">Strategies for Legacy Applications</a> for more
-information about how to do that. </p>
+make available on small screens (such as QVGA) or for which you would like to provide better support
+for extra large screens, please see <a href="#strategies">Strategies for Legacy Applications</a> for
+more information about how to do that. </p>
<h2 id="overview">Overview of Screens Support</h2>
@@ -82,11 +90,11 @@
<dl>
<dt><em>Screen size</em></dt>
- <dd>Actual physical size, measured as the screen's diagonal.
+ <dd>Actual physical size, measured as the screen's diagonal.
- <p>For simplicity, Android collapses all actual screen sizes into three
-generalized sizes: large, normal, and small. Applications can provide custom
-layouts for each of these three sizes — the platform transparently handles
+ <p>For simplicity, Android collapses all actual screen sizes into four
+generalized sizes: small, normal, large, and extra large. Applications can provide custom
+layouts for each of these four sizes — the platform transparently handles
the rendering of the layouts at the actual screen size.</p></dd>
<dt><em>Aspect ratio</em></dt>
@@ -110,22 +118,22 @@
density of a screen is important because, other things being equal, a UI element
(such as a button) whose height and width are defined in terms of screen pixels
will appear larger on the lower density screen and smaller on the higher density
-screen. </p>
+screen.</p>
- <p>For simplicity, Android collapses all actual screen densities into three
-generalized densities: high, medium, and low. Applications can provide custom
-resources for each of these three densities — the platform handles the
-scaling of the resources up or down to meet the actual screen density. </p></dd>
-<dt><em>Density-independent pixel (dip)</em></dt>
+ <p>For simplicity, Android collapses all actual screen densities into four
+generalized densities: low, medium, large, and extra large. Applications can provide custom
+resources for each of these densities — the platform handles any necessary
+scaling of the resources up or down to meet the specific screen density. </p></dd>
+<dt><em>Density-independent pixel (dp)</em></dt>
<dd>A virtual pixel unit that applications can use in defining their UI, to
-express layout dimensions or position in a density-independent way.
+express layout dimensions or position in a density-independent way.
<p>The density-independent pixel is equivalent to one physical pixel on a 160
dpi screen, the baseline density assumed by the platform (as described later in
this document). At run time, the platform transparently handles any scaling of
-the dip units needed, based on the actual density of the screen in use. The
-conversion of dip units to screen pixels is simple: <code>pixels = dips *
-(density / 160)</code>. For example, on 240 dpi screen, 1 dip would equal 1.5
-physical pixels. Using dip units to define your application's UI is highly
+the dp units needed, based on the actual density of the screen in use. The
+conversion of dp units to screen pixels is simple: <nobr><code>pixels = dps *
+(density / 160)</code></nobr>. For example, on 240 dpi screen, 1 dp would equal 1.5
+physical pixels. Using dp units to define your application's UI is highly
recommended, as a way of ensuring proper display of your UI on different
screens. </p></dd>
</dl>
@@ -146,13 +154,19 @@
and resolutions into:</p>
<ul>
-<li>A set of three generalized sizes: <em>large</em>, <em>normal</em>, and <em>small</em>, and </li>
-<li>A set of three generalized densities: <em>hdpi</em> (high), <em>mdpi</em> (medium), and <em>ldpi</em> (low)
+<li>A set of four generalized sizes: <em>small</em>, <em>normal</em>, <em>large</em>,
+and <em>xlarge</em></em>
+<li>A set of four generalized densities: <em>ldpi</em> (low), <em>mdpi</em> (medium),
+<em>hdpi</em> (high), and <em>xhdpi</em> (extra high)
</ul>
+<p class="note"><strong>Note:</strong> The <code>xhdpi</code> density category was added in
+Android 2.2 (API Level 8). The <em>xlarge</em> size category was added in Android 2.3 (API Level
+9).</p>
+
<p>Applications can provide custom resources (primarily layouts) for any of the
-three generalized sizes and can provide resources (primarily drawables such as
-images) for any of the three generalized densities. Applications do not need to
+four generalized sizes and can provide resources (primarily drawables such as
+images) for any of the four generalized densities. Applications do not need to
work with the actual physical size or density of the device screen. At run time,
the platform handles the loading of the correct size or density resources, based
on the generalized size or density of the current device screen, and adapts them
@@ -177,8 +191,8 @@
<img src="{@docRoot}images/screens_support/screens-ranges.png" />
-<p class="img-caption"><strong>Figure 1.</strong>
-Illustration of how the Android platform maps actual screen densities and sizes
+<p class="img-caption"><strong>Figure 1.</strong>
+Illustration of how the Android platform maps actual screen densities and sizes
to generalized density and size configurations. </p>
<p>Although the platform lets your application provide layouts and resources for
@@ -213,6 +227,9 @@
<td style="background-color:#f3f3f3">
<nobr>High density (240), <em>hdpi</em><nobr>
</td>
+ <td style="background-color:#f3f3f3">
+ <nobr>Extra high density (320), <em>xhdpi</em><nobr>
+ </td>
</tr>
<tr>
<td style="background-color:#f3f3f3">
@@ -222,6 +239,7 @@
</td>
<td></td>
<td></td>
+ <td></td>
</tr>
<tr>
<td style="background-color:#f3f3f3">
@@ -230,6 +248,7 @@
<td style="font-size:.9em;">WQVGA400 (240x400)<br>WQVGA432 (240x432)</td>
<td style="font-size:.9em;">HVGA (320x480)</td>
<td style="font-size:.9em;">WVGA800 (480x800)<br>WVGA854 (480x854)</td>
+ <td style="font-size:.9em;"></td>
</tr>
<tr>
<td style="background-color:#f3f3f3">
@@ -238,16 +257,27 @@
<td></td>
<td style="font-size:.9em;">WVGA800* (480x800)<br>WVGA854* (480x854)</td>
<td></td>
+ <td></td>
</tr>
<tr>
- <td colspan="4" style="border:none;font-size:90%;">* To emulate this
- configuration, specify a custom density of 160 when
+ <td style="background-color:#f3f3f3">
+ <em>Extra Large</em> screen
+ </td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ </tr>
+ <tr>
+ <td colspan="4" style="border:none;font-size:90%;">* To emulate this
+ configuration, specify a custom density of 160 when
creating an AVD that uses a WVGA800 or WVGA854 skin.
</td>
+ </tr>
</table>
-<p>For an overview of the relative numbers of high (hdpi), medium (mdpi), and
-low (ldpi) density screens in Android-powered devices available now, see the <a
+<p>For an overview of the relative numbers of high (hdpi), medium (mdpi), and
+low (ldpi) density screens in Android-powered devices available now, see the <a
href="{@docRoot}resources/dashboard/screens.html">Screen Sizes and Densities</a> dashboard.</p>
@@ -262,9 +292,9 @@
<p> If you want to use size- or density-specific layouts or drawables in your
application and you are not familiar with resource qualifiers or how the
-platform uses them, please read
+platform uses them, please read
<a href="{@docRoot}guide/topics/resources/providing-resources.html#AlternativeResources">
-Alternative Resources</a>.
+Providing Alternative Resources</a>.
</div>
</div>
@@ -277,19 +307,20 @@
<ul>
<li>The platform supports a set of resource qualifiers that let you provide
-size- and density-specific resources, if needed. The qualifiers for
-size-specific resources are <code>large</code>, <code>normal</code>, and
-<code>small</code>, and those for density-specific resources are
-<code>hdpi</code> (high), <code>mdpi</code> (medium), and <code>ldpi</code>
-(low). The qualifiers correspond to the generalized densities described in
+size- and density-specific resources, if needed. The qualifiers for
+size-specific resources are <code>small</code>, <code>normal</code>, <code>large</code>, and
+<code>xlarge</code>. Those for density-specific resources are <code>ldpi</code>
+(low), <code>mdpi</code> (medium), <code>hdpi</code> (high), and <code>xhdpi</code> (extra high).
+The qualifiers correspond to the generalized densities described in
<a href="#range">Range of screens supported</a>, above.</li>
- <li>The platform also provides a
+ <li>The platform also provides a
<a href="{@docRoot}guide/topics/manifest/supports-screens-element.html">
<code><supports-screens></code></a>
manifest element, whose attributes
-<code>android:largeScreens</code>, <code>android:normalScreens</code>, and
-<code>android:smallScreens</code> let you specify what generalized screen sizes
-your application supports. A fourth attribute, <code>android:anyDensity</code>,
+<code>android:smallScreens</code>, <code>android:normalScreens</code>,
+<code>android:largeScreens</code>, and <code>android:xlargeScreens</code> let you specify what
+generalized screen sizes
+your application supports. Another attribute, <code>android:anyDensity</code>,
lets you indicate whether or not your application includes built-in support for
multiple densities.</li>
</ul>
@@ -299,7 +330,7 @@
screen:</p>
<ol>
-<li><em>Pre-scaling of resources (such as image assets)</em>
+<li><em>Pre-scaling of resources (such as image assets)</em>
<p>Based on the density of the current screen, the platform automatically
loads any size- or density-specific resources from your application and displays
@@ -344,18 +375,18 @@
<p>For more information, see the <code>android:anyDensity</code> attribute in
<a href="#attrs">Manifest attributes for screens support</a>.</p></li>
-<div class="sidebox-wrapper" xstyle="margin-bottom:2em;margin-top:.5em;width:90%;">
- <img id="rule" src="{@docRoot}assets/images/grad-rule-qv.png">
- <div id="qv-sub-rule">
- <img src="{@docRoot}assets/images/icon_market.jpg" style="float:left;margin:0;padding:0;">
- <p style="color:#669999;">Publishing to Small Screen Devices</p>
+<div class="sidebox-wrapper" xstyle="margin-bottom:2em;margin-top:.5em;width:90%;">
+ <img id="rule" src="{@docRoot}assets/images/grad-rule-qv.png">
+ <div id="qv-sub-rule">
+ <img src="{@docRoot}assets/images/icon_market.jpg" style="float:left;margin:0;padding:0;">
+ <p style="color:#669999;">Publishing to Small Screen Devices</p>
<p>To ensure the best experience for users on small-screen devices, Android
Market only shows applications that explicitly declare support for small
screens. If you developed an application on Android 1.5 or earlier and published
it on Android Market, you need to <a href="#testing">test your application</a>
-on small screens and then upload an updated version that explicitly
+on small screens and then upload an updated version that explicitly
<a href="#attrs">indicates support for small screens</a>. </p>
- </div>
+ </div>
</div>
<li><em>Compatibility-mode display on larger screen-sizes</em>
@@ -374,7 +405,7 @@
placed as a "postage stamp" in the larger 480x800 screen.</p>
<p>For more information, see the <code>android:anyDensity</code> attribute in
-<a href="#attrs">Manifest elements for screens support</a> and the
+<a href="#attrs">Manifest elements for screens support</a> and the
<a href="#compatibility-examples">Screen-Compatibility Examples</a>
section.</p></li>
</ol>
@@ -415,7 +446,7 @@
<ul>
<li>Through pre-scaling of drawable resources (scaled at resource loading
time)</li>
-<li>Through auto-scaling of density-independent pixel (dip) values used in
+<li>Through auto-scaling of density-independent pixel (dp) values used in
layouts</li>
<li>Through auto-scaling of absolute pixel values used in the application (only
needed if the application has set <code>android:anyDensity="false"</code> in its
@@ -437,25 +468,36 @@
<p>In most cases, you can take advantage of density independence in your
application simply by making sure that your layouts specify all dimension values
-in density-independent pixels (<code>dip</code> or <code>dp</code>) or
+in density-independent pixels (<code>dp</code> or <code>dp</code>) or
scale-independent pixels (<code>sip</code> or <code>sp</code>, for text only).
If you are using absolute pixel values in the application and manifest includes
<a href="#attrs"><code>android:anyDensity="true"</code></a>, you will also need
-to scale the pixel values. See <a href="#dips-pels">Converting from dips to
-pixels</a> for more information. </p>
+to scale the pixel values. See <a href="#dips-pels">Converting dp units to
+pixel units</a> for more information. </p>
<h3 id="attrs">Manifest attributes for screens support</h3>
-<p> Android 1.6 introduced a new manifest element,
+<p> Android 1.6 introduced a new manifest element,
<a href="{@docRoot}guide/topics/manifest/supports-screens-element.html"><code><supports-screens></code></a>,
whose attributes you can use to control the
display of your application on different classes of device screens, as listed
-below. The <code>smallScreens</code>, <code>normalScreens</code>, and
-<code>largeScreens</code> attributes correspond to the generalized screen sizes
+in table 2. The <code>smallScreens</code>, <code>normalScreens</code>, <code>largeScreens</code> and
+<code>xlargeScreens</code> attributes correspond to the generalized screen sizes
described in <a href="#range">Range of screens supported</a>, earlier in this
-document.</p>
+document. Notice that the default values for each attribute vary, depending
+on your minimum and targeted platform, as indicated in the <a
+href="{@docRoot}guide/topics/manifest/uses-sdk-element.html">{@code
+android:minSdkVersion}</a> and <a
+href="{@docRoot}guide/topics/manifest/uses-sdk-element.html">{@code
+android:targetSdkVersion}</a> attributes of your <a
+href="{@docRoot}guide/topics/manifest/uses-sdk-element.html">{@code <uses-sdk>}</a>
+manifest element.</p>
+<p class="table-caption" id="table2"><strong>Table 2.</strong> Summary of attributes for the <a
+href="{@docRoot}guide/topics/manifest/supports-screens-element.html">{@code
+<supports-screens>}</a> manifest element, including default values based on platform
+version.</p>
<table id="vrr8">
<tr>
<th>
@@ -465,10 +507,12 @@
Description
</th>
<th>
- Default value,<br><nobr>Android 1.5 and Lower</nobr>
+ Default value, when<br><nobr><code>minSdkVersion</code> or</nobr>
+<code>targetSdkVersion</code> is 4 or lower
</th>
<th>
- Default value,<br><nobr>Android 1.6 and Higher</nobr>
+ Default value, when<br><nobr><code>minSdkVersion</code> or</nobr>
+<code>targetSdkVersion</code> is 5 or higher
</th>
</tr>
<tr>
@@ -540,11 +584,46 @@
<td>"<code>false</code>"</td>
<td>"<code>true</code>"</td>
</tr>
+ <tr>
+ <td colspan="4"><strong>Note:</strong> Android 2.3 (API Level 9) introduced a new
+attribute for the <code><supports-screens></code> element: <code>xlargeScreens</code>, shown
+below. It works the same as the other screen attributes above, but, if neither your
+<code>minSdkVersion</code> or <code>targetSdkVersion</code> are set to "9", the default value is
+"false" when your application is installed on a device running Android 2.3.</td>
+ </tr>
+ <tr>
+ <th>
+ Attribute
+ </th>
+ <th >
+ Description
+ </th>
+ <th>
+ Default value, when<br><nobr><code>minSdkVersion</code> or</nobr>
+<code>targetSdkVersion</code> is 8 or lower
+ </th>
+ <th>
+ Default value, when<br><nobr><code>minSdkVersion</code> or</nobr>
+<code>targetSdkVersion</code> is 9 or higher
+ </th>
+ </tr>
+ <tr>
+ <td>
+ <code>android:xlargeScreens</code>
+ </td>
+ <td>
+ Whether or not the application UI is designed for use on
+<em>xlarge</em> screens — "<code>true</code>" if it is, and
+"<code>false</code>" if not.
+ </td>
+<td>"<code>false</code>"</td>
+<td>"<code>true</code>"</td>
+ </tr>
</table>
<p>In general, when you declare a screen-size attribute
-(<code>smallScreens</code>, <code>normalScreens</code>, or
-<code>largeScreens</code>) as "<code>true</code>", you are signaling to the
+(<code>smallScreens</code>, <code>normalScreens</code>, <code>largeScreens</code>, or
+<code>xlargeScreens</code>) as "<code>true</code>", you are signaling to the
platform that your application is designed to render properly on that screen
size. As a result, the platform does not apply any size-compatibility features
(such as a virtual HVGA display area). If you declare a screen-size attribute as
@@ -577,16 +656,20 @@
<ul>
<li>Assume that you declare <code>smallScreens="false" normalScreens="true"
-largeScreens="false" </code> in your application's manifest. <p>Although the
-application is not designed for display on large screens, the platform can still
-run it successfully in <a href="#compatibility-examples">size-compatibility
-mode</a>. Android Market does not filter the application from devices
-<em>normal</em> and <em>large</em> size screens, but does filter it from
-<em>small</em> size screens, since the application provides no screen support at
-<em>small</em> size (and there is no smaller size).</p></li>
+largeScreens="false" xlargeScreens="false"</code> in your application's manifest. <p>Although the
+application is not designed for display on large or extra large screens, the platform can still
+run it successfully in <a href="#compatibility-examples">screen-compatibility
+mode</a>. Android Market shows the application to devices with
+<em>normal</em>, <em>large</em>, and <em>xlarge</em> size screens, but does filter it from
+<em>small</em> size screens, because the application provides no screen support at
+<em>small</em> size. Android's <a href="#compatibility-examples">screen-compatibility
+mode</a> mode does not provide support for screens that are smaller than those the
+application supports—it only provides support for screens that are larger. Thus,
+although the application declares "false" for <em>large</em> and <em>xlarge</em> screens,
+the application still functions, but runs in compatibility mode.</p></li>
<li>Assume that you declare <code>smallScreens="false" normalScreens="false"
-largeScreens="true"</code> in your application's manifest. <p>Android Market
+largeScreens="true" xlargeScreens="true"</code> in your application's manifest. <p>Android Market
filters the application from users of devices with <em>small</em> and
<em>normal</em> size screens. In effect, this prevents such users from
installing the application.</p></li>
@@ -599,23 +682,24 @@
density-independent pixels (<code>dp</code>) and scales any absolute pixel
values (<code>px</code>) or math by the scaling factor available from {@link
android.util.DisplayMetrics#density android.util.DisplayMetrics.density}. See <a
-href="#dips-pels">Converting from dips to pixels</a> for an example.</p>
+href="#dips-pels">Converting dp units to pixel units</a> for an example.</p>
<p>Note that the setting of the <code>android:anyDensity</code> attribute does
not affect the platform's pre-scaling of drawable resources, such as bitmaps and
nine-patch images, which always takes place by default. </p>
-<p>The following example shows a manifest that declares support for large,
-normal, and small screens in any densities.</p>
+<p>The following example shows a manifest that declares support for small, normal, large, and
+ xlarge screens in any density.</p>
-<pre><manifest xmlns:android="http://schemas.android.com/apk/res/android">
- ...
- <supports-screens
- android:largeScreens="true"
- android:normalScreens="true"
- android:smallScreens="true"
- android:anyDensity="true" />
- ...
+<pre>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+ ...
+ <supports-screens
+ android:smallScreens="true"
+ android:normalScreens="true"
+ android:largeScreens="true"
+ android:xlargeScreens="true"
+ android:anyDensity="true" />
</manifest>
</pre>
<!-- android:resizeable="true" -->
@@ -624,36 +708,25 @@
</h4>
<p>The default values for the <code><supports-screens></code> attributes
-differ, depending on the the value of the
+differ, depending on the the value of the
<a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html"><code>android:minSdkVersion</code></a>
attribute in the application's manifest, as well as on
-the value of <code>android:targetSdkVersion</code>, if declared:</p>
+the value of <a
+href="{@docRoot}guide/topics/manifest/uses-sdk-element.html">{@code
+android:targetSdkVersion}</a>, if declared.</p>
-<div>
- <ul>
- <li>
- If <code>android:minSdkVersion</code> or
-<code>android:targetSdkVersion</code> is "4" (Android 1.6) or higher, the
-default value for everything is "<code>true</code>". If your application uses
-APIs introduced in Android 1.6 or higher, but does not support specific screen
-densities and/or screen sizes, you need to explicitly set the appropriate
-attributes to "<code>false</code>".
- </li>
- <li>
- If <code>android:minSdkVersion</code> is declared with a value of "3"
-(Android 1.5) or lower <em>and</em> a <code>android:targetSdkVersion</code>
-attribute is <em>not</em> declared with a value of "4" or higher, the default
-value for all attributes except <code>android:normalScreens</code> is
-"<code>false</code>". If you are primarily targeting pre-Android 1.6 platforms
-but also want to support other densities/screen sizes, you need to explicitly
-set the appropriate attributes to "<code>true</code>".
- </li>
- <li>
- Note that <code>android:normalScreens</code> always defaults to
-<code>true</code>.
- </li>
- </ul>
-</div>
+<p>Above, <a href="#table2">table 2</a> indicates the default values for each attribute, based on
+the values you provide for the <a
+href="{@docRoot}guide/topics/manifest/uses-sdk-element.html">{@code
+android:minSdkVersion}</a> and <a
+href="{@docRoot}guide/topics/manifest/uses-sdk-element.html">{@code
+android:targetSdkVersion}</a>, in the <a
+href="{@docRoot}guide/topics/manifest/uses-sdk-element.html">{@code <uses-sdk>}</a>
+element.</p>
+
+<p class="note"><strong>Note:</strong> If your application uses APIs introduced in Android 1.6 or
+higher, but does not support specific screen densities and/or screen sizes, you need to explicitly
+set the appropriate attributes to "<code>false</code>" (because most are "true", by default).</p>
<h3 id="qualifiers">Resource directory qualifiers for screen size and density</h3>
@@ -673,7 +746,7 @@
</tr>
<tr>
- <td rowspan="3">Size</td>
+ <td rowspan="4">Size</td>
<td><code>small</code></td>
<td>Resources designed for <em>small</em> size screens.</td>
</tr>
@@ -683,11 +756,15 @@
</tr>
<tr>
<td><code>large</code></td>
-<td>Resources for <em>large</em> size screens.</td>
+<td>Resources designed for <em>large</em> size screens.</td>
+</tr>
+<tr>
+<td><code>xlarge</code></td>
+<td>Resources designed for <em>extra large</em> size screens.</td>
</tr>
<tr>
-<td rowspan="4">Density</td>
+<td rowspan="5">Density</td>
<td><code>ldpi</code></td>
<td>Resources designed for low-density (<em>ldpi</em>) screens.</td>
</tr>
@@ -700,6 +777,10 @@
<td>Resources designed for high-density (<em>hdpi</em>) screens.</td>
</tr>
<tr>
+<td><code>xhdpi</code></td>
+<td>Resources designed for extra high-density (<em>xhdpi</em>) screens.</td>
+</tr>
+<tr>
<td><code>nodpi</code></td>
<td>Density-independent resources. The platform does not auto-scale resources
tagged with this qualifier, regardless of the current screen's density.</td>
@@ -732,31 +813,34 @@
Note that the density and the screen size are independent parameters and are
interpreted by the system individually. For example, WVGA high density is
considered a normal screen because its physical size is about the same as one of
-T-Mobile G1. On the other hand, a WVGA medium density screen is considered a
+T-Mobile G1. On the other hand, a WVGA medium density screen is considered a
<i>large</i> screen — it offers the same resolution but at lower pixel
density, meaning that it is both physically larger than the baseline screen and
can display significantly more information than a normal screen size.
</p>
<p>Here is an example of the resource directory structure of an application that
-supports low and high density, and employs different layout schemes.</p>
+employs different layout schemes for different screen sizes and supports low and high density
+screens.</p>
-<pre>res/layout/my_layout.xml // layout for normal screen size
+<pre>
+res/layout/my_layout.xml // layout for normal screen size
res/layout-small/my_layout.xml // layout for small screen size
res/layout-large/my_layout.xml // layout for large screen size
res/layout-large-land/my_layout.xml // layout for large screen size in landscape mode
+res/layout-xlarge/my_layout.xml // layout for extra large screen size
-res/drawable-ldpi/my_icon.png // icon image for low density
-res/drawable-mdpi/dpi/my_icon.png // icon for medium density
-res/drawable-hdpi/my_icon.png // icon image for high density
+res/drawable-lhdpi/my_icon.png // image for low density
+res/drawable-mdpi/dpi/my_icon.png // image for medium density
+res/drawable-hdpi/my_icon.png // image for high density
res/drawable-nodpi/composite.xml // density independent resource
</pre>
<p>For more information about how to use resource qualifiers or how the platform
-selects them, please read
+selects them, please read
<a href="{@docRoot}guide/topics/resources/providing-resources.html#AlternativeResources">
-Alternative Resources</a>.</p>
+Providing Alternative Resources</a>.</p>
<h2 id="screen-independence">Best practices for Screen Independence</h2>
@@ -771,10 +855,11 @@
<ol>
<li>
- Prefer wrap_content, fill_parent and the dip unit to px in XML layout files
+ Use {@code wrap_content}, {@code fill_parent}, or the {@code dp} unit (instead of {@code px}),
+when specifying dimensions in an XML layout file
</li>
<li>
- Avoid AbsoluteLayout
+ Do not use {@code AbsoluteLayout}
</li>
<li>
Do not use hard coded pixel values in your code
@@ -784,19 +869,19 @@
</li>
</ol>
-<h3 id="use-relative">1. Prefer wrap_content, fill_parent and the dip unit to
+<h3 id="use-relative">1. Use wrap_content, fill_parent, or the dp unit, instead of
absolute pixels<br> </h3>
<p>When defining the <code>layout_width</code> and <code>layout_height</code> of
views in an XML layout file, using <code>wrap_content</code>,
-<code>fill_parent</code> or the <code>dip</code> will guarantee that the view is
+<code>fill_parent</code> or the <code>dp</code> will guarantee that the view is
given an appropriate size on the current device screen. For instance, a view
-with a <code>layout_width="100dip"</code> will measure 100 pixels wide on an
+with a <code>layout_width="100dp"</code> will measure 100 pixels wide on an
HVGA@160 density display and 150 pixels on a WVGA@240 density display, but the
view will occupy approximately the same physical space. </p>
<p>Similarly, you should prefer the <code>sp</code> (scale-independent pixel,
-the scale factor depends on a user setting) or <code>dip</code> (if you don't
+the scale factor depends on a user setting) or <code>dp</code> (if you don't
want to allow the user to scale the text) to define font sizes.</p>
<h3 id="avoid-absolute">2. Avoid AbsoluteLayout </h3>
@@ -808,7 +893,7 @@
different displays. Because of this, <code>AbsoluteLayout</code> was deprecated
in Android 1.5 (API Level 3). </p>
-<p>You can achieve much the same layout by using a
+<p>You can achieve much the same layout by using a
{@link android.widget.FrameLayout FrameLayout} instead, and setting
<code>layout_margin</code> attributes of the children. This approach is more
flexible and will yield better results on different screens.</p>
@@ -822,9 +907,9 @@
view is 10 pixels wide. In some cases, you may need to scale the pixel values
that you use in your code. The sections below provide more information. </p>
-<h4 id="dips-pels">Converting from dips to pixels</h4>
+<h4 id="dips-pels">Converting dp units to pixel units</h4>
-<p>In some cases, you will need to express dimensions in <code>dip</code> and
+<p>In some cases, you will need to express dimensions in <code>dp</code> and
then convert them to pixels. Imagine an application in which a scroll gesture is
recognized after the user's finger has moved by at least 16 pixels. On a
baseline screen, the user will have to move his finger by 16 pixels / 160
@@ -832,26 +917,26 @@
device with a high (240) density display, the user will move his finger by only
16 pixels / 240 dpi = 1/15th of an inch (or 1.7 mm.) The distance is much
shorter and the application thus appears more sensitive to the user. To fix this
-issue, the gesture threshold must be expressed in the code in <code>dip</code>
+issue, the gesture threshold must be expressed in the code in <code>dp</code>
and then converted to actual pixels.</p>
-<pre>// The gesture threshold expressed in dip
-private static final float GESTURE_THRESHOLD_DIP = 16.0f;
+<pre>// The gesture threshold expressed in dp
+private static final float GESTURE_THRESHOLD_DP = 16.0f;
-// Convert the dips to pixels
+// Convert the dps to pixels
final float scale = getContext().getResources().getDisplayMetrics().density;
-mGestureThreshold = (int) (GESTURE_THRESHOLD_DIP * scale + 0.5f);</span>
+mGestureThreshold = (int) (GESTURE_THRESHOLD_DP * scale + 0.5f);</span>
// Use mGestureThreshold as a distance in pixels
</pre>
-<p>The {@link android.util.DisplayMetrics#density android.util.DisplayMetrics.density}
+<p>The {@link android.util.DisplayMetrics#density android.util.DisplayMetrics.density}
field specifies the the scale factor you must use to
-convert dips to pixels according to the current screen density. You can access
+convert dps to pixels according to the current screen density. You can access
the current screen's metrics through a <code>Context</code> or
<code>Activity</code>. On a medium (160) density screen,
<code>DisplayMetrics.density</code> equals "1.0", whereas on a high (240)
-density screen it equals "1.5". You can refer to the documentation of the
+density screen it equals "1.5". You can refer to the documentation of the
{@link android.util.DisplayMetrics DisplayMetrics}
class for details.</p>
@@ -901,7 +986,7 @@
baseline medium density. It is not recommended that you put density-specific
resources such as images in the default directory.</p>
-<p>For more information about valid resource qualifiers, see
+<p>For more information about valid resource qualifiers, see
<a href="#qualifiers">Resource directory qualifiers</a>, earlier in this
document.</p>
@@ -945,7 +1030,7 @@
<p style="margin-left:2em;"><code>res/drawable-nodpi/icon.png</code></p>
-<p>You can also take complete control of the scaling mechanism by using the
+<p>You can also take complete control of the scaling mechanism by using the
{@link android.graphics.BitmapFactory.Options BitmapFactory.Options} class,
which lets you define whether you want the bitmap to be pre-scaled and what the
density of the bitmap should be. For instance, if you are loading a bitmap from
@@ -953,12 +1038,12 @@
When pre-scaling is disabled, the resulting bitmap is in auto-scaling mode. The
bitmap is associated with a density (that you may or may not have specified
through the <code>BitmapFactory.Options</code>) which will be used to scale the
-bitmap on screen <em>at drawing time</em>.
+bitmap on screen <em>at drawing time</em>.
<p>Using auto-scaling instead of pre-scaling is more CPU expensive than
-pre-scaling but uses less memory. You can refer to the documentation of
-{@link android.graphics.BitmapFactory BitmapFactory},
-{@link android.graphics.Bitmap Bitmap}, and
+pre-scaling but uses less memory. You can refer to the documentation of
+{@link android.graphics.BitmapFactory BitmapFactory},
+{@link android.graphics.Bitmap Bitmap}, and
{@link android.graphics.Canvas Canvas} for more
information on auto-scaling.</p>
@@ -973,7 +1058,7 @@
<p>If you have already developed and published an Android application based on
Android 1.5 or earlier platform version, you need to consider how you will adapt
-your application so that it is deployable to </p>
+your application so that it is deployable to:</p>
<ul>
<li>Existing devices, which may be running Android 1.5 (or lower) platform
@@ -982,16 +1067,21 @@
screen sizes and resolutions</li>
</ul>
+<p class="note"><strong>Note:</strong> Even if your application targets Android 1.6 already, you
+should follow the same strategies below in order to support <em>xhdpi</em> and <em>xlarge</em>
+screens on Android 2.3 (API Level 9), while maintaining compatibility with older versions of
+the platform.</p>
+
<p>To support the newer devices and the different screens they use, you might
need to make some changes in your app, but at the same time your app may be very
stable and so you want to minimize the changes. There are a variety of ways that
you can extend your existing application to support new devices with multiple
screens <em>and</em> existing devices running older platform versions. You
should be able to make these changes to your application such that you can
-distribute a single .apk to any and all devices.</p>
+distribute a single {@code .apk} to all devices.</p>
<p>The recommended strategy is to develop against the most recent version of the
-platform you are targeting, and test on the minimum one you want to run on.
+platform you are targeting, and test on the minimum platform version you want to run on.
Here's how to do that:</p>
<ol>
@@ -999,39 +1089,41 @@
<code>android:minSdkVersion</code> attribute as it is. You <em>do not</em> need
to increment the value of the attribute to support new devices and multiple
screens. </li>
- <li>Extend compatibility for Android 1.6 (and higher) devices by adding
+ <li>Extend compatibility for Android 1.6 (and higher) devices by adding
a new attribute — <code>android:targetSdkVersion</code> — to the
<code>uses-sdk</code> element. Set the value of the attribute to
-"<code>4</code>". This allows your application to "inherit" the platform's
+<code>"4"</code>. [To support <em>xhdpi</em> and <em>xlarge</em> screens, set the value to
+<code>"9"</code>.] This allows your application to "inherit" the platform's
multiple screens support, even though it is technically using an earlier version
of the API. </li>
<li>Add an empty <code><supports-screens></code> element as a child of
<code><manifest></code>. If you need to enable size or density attributes
later, this is where you will add them.</li>
<li>Change your application's build properties, such that it compiles against
-the Android 1.6 (API Level 4) library, rather than against the Android 1.5 (or
+the Android 1.6 (API Level 4) library [or against Android 2.3 (API Level 9) to support
+<em>xhdpi</em> and <em>xlarge</em> screens], rather than against the Android 1.5 (or
earlier) library. You will not be able to compile your application against the
older platform because of the new manifest attribute. </li>
- <li>Set up AVDs for testing your application on Android 1.6 and higher
+ <li>Set up AVDs for testing your application on Android 1.6 [or Android 2.3] and higher
releases. Create AVDs that use the screen sizes and densities that you want to
-support. When you create the AVDs, make sure to select the Android 1.6 or higher
+support. When you create the AVDs, make sure to select the Android 1.6 [or Android 2.3] or higher
platform as the system image to run. For more information, see <a
href="#testing">How to Test Your Application on Multiple Screens</a>,
below.</li>
- <li>Set up AVDs for testing your application on Android 1.5 (or earlier
-platform). You need AVDs running the older platforms you are targeting, so that
+ <li>Set up AVDs for testing your application on older versions of the platform, as low as the
+version declared by your <code>android:minSdkVersion</code>. You need AVDs running the older
+platforms you are targeting, so that
you can test for compatibility and ensure that there are no functional
regressions. </li>
- <li>Compile your application against the Android 1.6 library and run it on the
+ <li>Compile your application against the Android 1.6 [or Android 2.3] library and run it on the
AVDs you created. Observe the way your application looks and runs, and test all
of the user interactions. </li>
<li>Debug any display or functional issues. For issues that you resolve in
your application code, <span style="color:red">make certain not to use any APIs
-introduced in API Level 4 or later</span>. If you are in doubt, refer to SDK
-reference documentation and look for the API Level specifier for the API you
-want to use. Using an API introduced in API Level 4 or later will mean that your
-application will no longer be compatible with devices running Android 1.5 or
-earlier.</li>
+introduced later than the version declared by your <code>android:minSdkVersion</code></span>. If you
+are in doubt, refer to SDK reference documentation and look for the API Level specifier for the API
+you want to use. Using newer APIs not supported by your minimum version will mean that your
+application will no longer be compatible with devices running on that version.</li>
<li>For resource-related issues, you can try resolving them by:
<ul>
<li>Adding a <code>anyDensity="false"</code> attribute to
@@ -1039,22 +1131,22 @@
scaling.</li>
<li>Creating any size- or density-specific resources you need and placing
them in directories tagged with the <a href="#qualifiers">correct
-qualifiers</a>. Qualifiers must be arranged in a proscribed order. See
+qualifiers</a>. Qualifiers must be arranged in a proscribed order. See
<a href="{@docRoot}guide/topics/resources/providing-resources.html#AlternativeResources">
-Alternative Resources</a> for more information. </li>
+Providing Alternative Resources</a> for more information. </li>
<li>Note that if you add size- or density-specific resource directories
tagged with any of the resource qualifiers listed in this document, you should
make sure to also tag those directories with the <code>v<api-level></code>
-qualifier (for example, <code>-v4</code>). This ensures that those resources
+qualifier (for example, <code>-v4</code> to target API Level 4). This ensures that those resources
will be ignored when the application is run on Android 1.5 or lower platform
versions.</p></li>
</ul>
</li>
<li>If your application does not offer support (such as custom layouts) for
large screens and you want the platform to display your application in
-screen-compatibility mode on larger screens, add a
-<code>largeScreens="false"</code> attribute to the
-<code><supports-screens></code> element in the manifest. See
+screen-compatibility mode on larger screens, add the
+<code>largeScreens="false"</code> and <code>xlargeScreens="false"</code> attributes to the
+<code><supports-screens></code> element in the manifest. See
<a href="#compatibility-examples">Screen-Compatibility Examples</a> for
illustrations of how the platform displays your application in this case.</li>
<li>If your application does not offer support (such as custom layouts) for
@@ -1077,6 +1169,16 @@
density mean that you may need to make tradeoffs in design, content, and
function on those devices. </p>
+<p>Also give extra attention to testing your application on an AVD that emulates an <em>xlarge</em>
+screen. Devices with extra large screens
+are tablet-sized or larger, so you should pay close attention to how usable your application is on
+such screens. You might want to design new layouts specifically for extra large screens, to address
+usability aspects such as the location and size of buttons in your UI. To test your application on
+an extra large screen, create an AVD targeted to Android 2.3 with a high resolution, such as 1280 x
+800, and the default density of 160dpi. This AVD will use any resources you've provided with the
+<code>xlarge</code> <a href="#qualifiers">resouce qualifier</a>.</p>
+
+
<h2 id="testing">How to Test Your Application on Multiple Screens</h2>
<p>Before publishing an application that supports multiple screens, you should
@@ -1091,22 +1193,22 @@
various screen sizes, you should make sure to add the corresponding size
attribute(s) to your application's manifest. -->
-<div id="f9.5" style="float:right;margin:0;padding:0;">
- <img src="{@docRoot}images/screens_support/avds-config.png" style="padding:0;margin:0;">
- <p class="caption" style="margin:0 0 1.5em 1em;padding:0 0 0 1em;"><strong>Figure 4.</strong>
+<div id="f9.5" class="figure" style="width:530px">
+ <img src="{@docRoot}images/screens_support/avds-config.png" />
+ <p class="img-caption"><strong>Figure 4.</strong>
A typical set of AVDs for testing screens support.</p>
</div>
<p>As a test environment for your applications, set up a series of AVDs that
emulate the screen sizes and densities you want to support. The Android SDK
-includes six emulator skins to get you started. You can use the Android AVD
+includes several emulator skins to get you started. You can use the Android AVD
Manager or the <code>android</code> tool to create AVDs that use the various
emulator skins and you can also set up custom AVDs to test densities other than
-the defaults. For general information about working with AVDs, see
+the defaults. For general information about working with AVDs, see
<a href="{@docRoot}guide/developing/tools/avd.html">Android Virtual
Devices</a>.</p>
-<p>The Android SDK provides a set of default emulator skins that you can use for
+<p>The Android SDK provides a set of default emulator skins that you can use for
testing. The skins are included as part of each Android platform that you can
install in your SDK. The Android 1.6 platform offers these default skins:</p>
@@ -1125,7 +1227,7 @@
</li>
</ul>
-<p>The Android 2.0 platform offers all of the Android 1.6 default skins,
+<p>The Android 2.0 platform offers all of the Android 1.6 default skins,
above, plus:</p>
<ul>
@@ -1161,15 +1263,15 @@
monitor. The default emulator skins included in the Android SDK are listed
in <a href="#screens-table">Table 1</a>, earlier in this document.</p>
-<div style="float: right;background-color:#fff;margin: 0;padding: 20px 0 20px 20px;width:520px;">
- <img src="{@docRoot}images/screens_support/avd-density.png" style="padding:0;margin:0;">
- <p class="caption" style="margin:0 0 1.5em 1em;padding:0 0 0 1em; width:280px;"><strong>Figure 5.</strong>
+<div class="figure" style="width:324px">
+ <img src="{@docRoot}images/screens_support/avd-density.png" >
+ <p class="img-caption"><strong>Figure 5.</strong>
Resolution and density options that you can use, when creating an AVD using the AVD Manager.</p>
</div>
<p>You should also make sure to test your application on different physical
-screen sizes within a single size-density configuration. For example, to
-display this screen configuration on a 30" monitor you will need to adjust
+screen sizes within a single size-density configuration. For example, to
+display this screen configuration on a 30" monitor you will need to adjust
the value passed to <code>-scale</code> to 96*2.8/3.3 = 81dpi. You can also
pass a float value to <code>-scale</code> to specify your own scaling factor:</p>
@@ -1201,7 +1303,7 @@
<li>Set any other hardware options and complete the AVD creation.</li>
</ol>
-<p>In the example above (WVGA medium density), the new AVD will emulate a 5.8"
+<p>In the example above (WVGA medium density), the new AVD will emulate a 5.8"
WVGA screen.</p>
<p>As an alternative to adjusting the emulator skin configuration, you can use
@@ -1216,21 +1318,21 @@
<p>This section provides examples of how the Android platform displays an
application written for the baseline screen configuration — HVGA (320x480)
resolution on a 3.2" screen — with all of the platform's size- and
-density-compatibility features enabled. That is, the examples show how
-the platform displays an application that doesn't provide built-in support
+density-compatibility features enabled. That is, the examples show how
+the platform displays an application that doesn't provide built-in support
for the screen on which it is being rendered, but which instead relies completely
on the platform.</p>
-<p>The platform's screen-compatibility features are designed to provide such
-an application with a virtual baseline screen environment against which to run,
-while at the same time ensuring for the user a physical display that is
+<p>The platform's screen-compatibility features are designed to provide such
+an application with a virtual baseline screen environment against which to run,
+while at the same time ensuring for the user a physical display that is
approximately the same as the baseline screen size and density. </p>
<p>Legacy applications that have not been modified to support multiple
-screens would be typical examples of such applications. In most cases,
+screens would be typical examples of such applications. In most cases,
you would want to add multiple-screens support to a legacy application and
publish an updated version, as described in <a href="#strategies">Strategies
-for Legacy Applications</a>. However, if you did not do so, the
+for Legacy Applications</a>. However, if you did not do so, the
platform still performs best-effort rendering of your application, as
illustrated below.</p>
@@ -1248,9 +1350,9 @@
</li>
<li>
If the device's screen size is <em>small</em>, there are few options
-options for making Android 1.5 applications work well on such a screen, so
+options for making Android 1.5 applications work well on such a screen, so
Android Market will filter applications that are not known to support these
-screens from the device.
+screens from the device.
</li>
<li>
If the device's screen size is <em>large</em>, it limits the application's
diff --git a/docs/html/guide/topics/fragments/index.jd b/docs/html/guide/topics/fragments/index.jd
new file mode 100644
index 0000000..ce10ef7
--- /dev/null
+++ b/docs/html/guide/topics/fragments/index.jd
@@ -0,0 +1,648 @@
+page.title=Fragments
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+
+ <h2>Quickview</h2>
+ <ul>
+ <li>Decompose application functionality and UI into reusable modules</li>
+ <li>Add multiple fragments to a screen to avoid switching activities</li>
+ <li>Fragments have their own lifecycle, state, and back stack</li>
+ <li>Fragments require API Level HONEYCOMB or greater</li>
+ </ul>
+
+ <h2>In this document</h2>
+ <ol>
+ <li><a href="#Creating">Creating fragments</a></li>
+ <li><a href="#Adding">Adding a fragment to an activity</a></li>
+ <li><a href="#Managing">Managing fragments</a></li>
+ <li><a href="#Lifecycle">Handling the lifecycle</a></li>
+ <li><a href="#Integrating">Integrating with the activity</a></li>
+ <li><a href="#Menus">Adding menus</a></li>
+ </ol>
+
+ <h2>Key classes</h2>
+ <ol>
+ <li>{@link android.app.Fragment}</li>
+ <li>{@link android.app.FragmentManager}</li>
+ <li>{@link android.app.FragmentTransaction}</li>
+ </ol>
+
+ <!--
+ <h2>Related samples</h2>
+ <ol>
+ <li><a
+href="{@docRoot}resources/samples/NotePad/index.html">NotePad</a></li>
+ </ol>
+ -->
+</div>
+</div>
+
+
+<p>An {@link android.app.Activity} is always the window in which users interact with your
+application, but a {@link android.app.Fragment} can be responsible for distinct operations and UI
+that's embedded in an activity. So, when using fragments, your activity becomes more like a
+container for fragments that define the activity's behavior and UI.</p>
+
+<p>Fragments have their own
+set of lifecylce callback methods and recieve their own user input events. A fragment must always be
+embedded in an activity and the fragment's lifecycle is directly affected by the activity's
+lifecycle. For example, when the activity is stopped, so are all fragments in it, and when
+the activity is destroyed, so are all fragments. However, while an activity
+is active (in the "resumed" lifecycle stage), you can manipulate the lifecycle of each fragment
+independently. For example, you can add and remove fragments while the activity is active and you
+can add each fragment to a back stack within the activity—each back stack entry in the
+activity is actually a record of a "transaction" that occurred with the activity's fragments, so
+that the user can reverse the transaction with the BACK key (this is discussed more later).</p>
+
+<div class="figure" style="width:314px">
+<img src="{@docRoot}images/fragment_lifecycle.png" alt="" />
+<p class="img-caption"><strong>Figure 1.</strong> The lifecycle of a fragment (while its
+activity is running).</p>
+</div>
+
+<p>Android introduced fragments in Android X.X (API Level HONEYCOMB), with the primary intention to
+support more dynamic and flexible UI designs on large screen devices, such as tablets. Because a
+tablet has a much larger screen than a mobile phone, there's more room to interchange UI
+elements. Fragments allow that without the need for you to start a new activity or manage complex
+changes to the view hierarchy. By dividing the layout of an activity into fragments, the code
+that defines your activity becomes more modular and interchangable, allowing you to modify the
+activity's appearance at runtime and for different types of screens.</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 handle
+their own user input events. Thus, instead 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>
+
+<!-- ** TODO: Save this for later or move it down in the doc so the intro isn't overwhelming **
+
+<p>A fragment can be a modular and reusable component in your application. That is, because
+the fragment defines its own behavior using its own set of lifecycle callbacks, you can
+include one fragment in multiple activities. This also enables you to create one version of your
+application for multiple screen sizes. For instance, on an extra large screen (<em>xlarge</em>
+screen configuration), you can embed two or more fragments in one activity, but on a normal-sized
+screen (<em>normal</em> screen configuration), you can embed just one fragment in an activity and
+then start other activities in order to display the other fragments.</p>
+-->
+
+<p>When you use a fragment as a part of your layout, it technically lives within a {@link
+android.view.View} of the activity's layout and defines its own layout of views. You can insert a
+fragment into your activity layout by declaring the fragment in the activity's XML layout file, as
+a {@code <fragment>} element, or from your application code by adding it to an existing {@link
+android.view.View}. However, a fragment is not required to be a part of the activity
+layout—you might use a fragment as an invisible worker for the activity (more about that
+later).</p>
+
+<p>The rest of this document describes how to build your application to use fragments, including
+how fragments can contribute to the activity options menu and action bar, create context menus,
+maintain their state when added to the activity's back stack, and more.</p>
+
+
+
+<h2 id="Creating">Creating a Fragment</h2>
+
+<p>An implementation of the {@link android.app.Fragment} class contains code that looks a lot like
+the code in an {@link android.app.Activity}. In fact, if you're
+converting an existing Android application to use fragments, you'll move code
+from your {@link android.app.Activity} implementation into your {@link android.app.Fragment} class
+implementation, and into some of the same callback methods. A fragment contains callback methods
+similar to an activity, such as {@link android.app.Fragment#onCreate onCreate()}, {@link
+android.app.Fragment#onStart onStart()}, {@link android.app.Fragment#onPause onPause()}, and {@link
+android.app.Fragment#onStop onStop()}.</p>
+
+<p>If you're creating a fragment to be a modular piece of an activity UI, then your
+implementation of {@link android.app.Fragment} should include most of the same lifecycle
+callback methods traditionally implemented by the activity to initialize elements of the UI and
+save and restore state information. Usually, you'll want to implement the following methods:</p>
+
+<dl>
+ <dt>{@link android.app.Fragment#onCreate onCreate()}</dt>
+ <dd>The system calls this when creating the fragment. Within your implementation, you should
+initialize the essential components of the fragment that should be retained when the fragment is
+paused or stopped.</dd>
+ <dt>{@link android.app.Fragment#onCreateView onCreateView()}</dt>
+ <dd>The system calls this when it's time for the fragment to draw its user interface for the
+first time. To draw a UI for your fragment, you must return a {@link android.view.View} from this
+method that is the root of your fragment's layout. You can return null if the fragment does not
+provide a UI.</dd>
+ <dt>{@link android.app.Activity#onPause onPause()}</dt>
+ <dd>The system calls this method as the first indication that the user is leaving the
+fragment (though it does not always mean the fragment is being destroyed). This is usually where you
+should commit any changes that should be persisted beyond the current user session (because
+the user might not come back).</dd>
+</dl>
+
+<p>Most applications should implement at least these three methods for each fragment, but there are
+several other lifecycle callback methods that you should also use in order to provide the best
+user experience when switching fragments and when the activity is paused or stopped. All of the
+lifecycle callback methods are discussed more later, in
+the section about <a href="#Lifecycle">Handling the Lifecycle</a>.</p>
+
+
+<p>There are also a few different subclasses of {@link android.app.Fragment} that you might want
+to use:</p>
+
+<dl>
+ <dt>{@link android.app.DialogFragment}</dt>
+ <dd>Displays a floating dialog. Using this class to create a dialog is a good alternative to using
+the dialog helper methods in the {@link android.app.Activity} class, because the dialog can be
+incorporated into the fragment back stack managed by the activity.</dd>
+
+ <dt>{@link android.app.ListFragment}</dt>
+ <dd>Displays a list of items that are managed by an adapter (such as a {@link
+android.widget.SimpleCursorAdapter}), similar to {@link android.app.ListActivity}. Provides methods
+for managing a list, such as the {@link
+android.app.ListFragment#onListItemClick(ListView,View,int,long) onListItemClick()} callback to
+handle click events on list items.</dd>
+
+ <dt>{@link android.preference.PreferenceFragment}</dt>
+ <dd>Displays a hierarchy of {@link android.preference.Preference} objects as a list, similar to
+{@link android.preference.PreferenceActivity}. </dd>
+</dl>
+
+<p>However, subclassing the standard {@link android.app.Fragment} class is most common, if
+you're not creating a dialog, a list, or displaying preferences.</p>
+
+
+<h3 id="UI">Providing a user interface</h3>
+
+<p>To provide a UI layout for a fragment, you must implement
+the {@link android.app.Fragment#onCreateView onCreateView()}
+callback method in your {@link android.app.Fragment} (unless your fragment is a subclass of
+{@link android.app.ListFragment}, which returns a {@link android.widget.ListView} from this method
+by default). The Android system calls {@link android.app.Fragment#onCreateView onCreateView()} when
+it's time for the fragment to draw its layout. Your implementation of this method must return a
+{@link android.view.View} that is the root of your fragment's layout.</p>
+
+<p>The easiest way to provide your layout is to inflate it from a <a
+href="{@docRoot}guide/topics/resources/layout-resource.html">layout resource</a>. To help you
+inflate a layout, the {@link android.app.Fragment#onCreateView onCreateView()} method passes a
+{@link android.view.LayoutInflater} that you can use to get your layout. For example, here's a
+simple subclass of {@link android.app.Fragment} that contains an implementation of {@link
+android.app.Fragment#onCreateView onCreateView()} that loads the fragment's layout from a
+resource:</p>
+
+<pre>
+public static class SimpleFragment extends Fragment {
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ // Inflate the layout for this fragment
+ return inflater.inflate(R.layout.simple_fragment, container, false);
+ }
+}
+</pre>
+
+<p>The {@link android.view.LayoutInflater#inflate(int,ViewGroup,boolean) inflate()} method takes
+three arguments:</p>
+<ul>
+ <li>The resource ID of the layout you want to inflate</li>
+ <li>The {@link android.view.ViewGroup} to be the parent of the
+inflated layout (supplying this is important in order to apply layout parameters from the parent
+view)</li>
+ <li>And a boolean indicating whether the inflated layout should be attached to the {@link
+android.view.ViewGroup} (from the second parameter) during inflation (in this case, this
+is false because the system is already going to insert the layout into the appropriate parent
+view—doing otherwise would create a redundant view group in the final layout)</li>
+</ul>
+
+<p>The {@code container} parameter passed to {@link android.app.Fragment#onCreateView
+onCreateView()} provides the parent {@link android.view.ViewGroup} in which your fragment layout
+will be inserted, which you can use to generate layout parameters for your
+fragment layout. The {@code savedInstanceState} parameter is a {@link android.os.Bundle} that
+provides data about the previous instance of the fragment, if the fragment is being resumed
+(restoring state is discussed more in the section about <a href="#Lifecycle">Handling the
+Lifecycle</a>.</p>
+
+
+<h3 id="Adding">Adding a Fragment to an Activity</h3>
+
+<p>Each fragment is embedded into the layout of its container activity as a part of the overall view
+hierarchy, whether or not it actually provides a UI. If a fragment is not embedded into the activity
+layout, then it is never created (it does not receive any lifecycle callbacks). There are two ways
+you can add a fragment to the activity layout:</p>
+
+<ul>
+ <li><b>Declare the fragment inside the activity's layout XML file.</b>
+<p>In this case, you can
+specify layout properties for the fragment as if it were a view itself and the fragment's layout
+fills that space. For example:</p>
+<pre>
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <fragment android:name="com.example.news.ArticleListFragment"
+ android:id="@+id/list"
+ android:layout_weight="1"
+ android:layout_width="0dp"
+ android:layout_height="match_parent" />
+ <fragment android:name="com.example.news.ArticleReaderFragment"
+ android:id="@+id/viewer"
+ android:layout_weight="2"
+ android:layout_width="0dp"
+ android:layout_height="match_parent" />
+</LinearLayout>
+</pre>
+ <p>The {@code <fragment>} element uses the {@code android:name} attribute to specify the
+{@link android.app.Fragment} class to instantiate and insert into the layout. When the activity
+layout is created, the system instantiates each fragment in the layout and calls its {@link
+android.app.Fragment#onCreateView onCreateView()} method in order to retrieve the fragment's
+layout. The {@link android.view.View} object returned by {@link
+android.app.Fragment#onCreateView onCreateView()} is then
+placed directly in the activity layout in place of the {@code <fragment>} element.</p>
+
+<div class="note">
+ <p><strong>Note:</strong> Each fragment requires a unique identifier that
+the system can use to restore the fragment if the activity is restarted (and which you can use to
+perform fragment transactions). There are three ways to identify a fragment:</p>
+ <ul>
+ <li>Supply the {@code android:id} attribute with a unique ID, in the {@code
+<fragment>}</li>
+ <li>Supply the {@code android:tag} attribute with a unique string ID, in the {@code
+ <fragment>}</li>
+ <li>If neither of the previous two are provided, the system uses the ID of the container
+ view.</li>
+ </ul>
+</div>
+ </li>
+
+ <li><b>Or, programmatically add the fragment to an existing {@link android.view.ViewGroup}.</b>
+<p>At any time while your activity is running (in the "resumed" state), you can add (and remove)
+fragments to your activity layout. You simply need to specify a {@link android.view.ViewGroup} in
+which to place the fragment.</p>
+ <p>To make any fragment transactions in your activity (such as add, remove, or replace a
+fragment), you must use APIs from {@link android.app.FragmentTransaction}. You can get an instance
+of {@link android.app.FragmentTransaction} from your {@link android.app.Activity} using {@link
+android.app.Activity#openFragmentTransaction()}. You can then add a fragment using the {@link
+android.app.FragmentTransaction#add add()} method, specifying the fragment to add and the view in
+which to insert it. For example:</p>
+<pre>
+MyFragment fragment = new MyFragment();
+openFragmentTransaction().add(R.id.fragment_container, fragment).commit();
+</pre>
+ <p>The first argument passed to {@link android.app.FragmentTransaction#add add()}
+is the {@link android.view.ViewGroup} in which the fragment should be placed, specified by
+resource ID, and the second parameter is the fragment object.</p>
+ <p>Once you've made your changes using
+{@link android.app.FragmentTransaction}, you must
+call {@link android.app.FragmentTransaction#commit} in order for the changes to take effect.</p>
+ </li>
+</ul>
+
+
+<h3 id="Example1">Example: simple fragments</h3>
+
+<p>In the last couple sections, you saw how to declare layout for a fragment and add it to an
+activity. What follows is some code that brings it all together, like a "Hello World" for
+fragments.</p>
+
+<p>First, here's a layout file for a fragment:</p>
+<pre>
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+<TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/hello" />
+<TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/hello" />
+</LinearLayout>
+</pre>
+
+<p>With that file saved at {@code res/layout/simple_fragment.xml}, the following {@link
+android.app.Fragment} uses it for its layout:</p>
+
+<pre>
+public static class SimpleFragment extends Fragment {
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ // Inflate the layout for this fragment
+ return inflater.inflate(R.layout.simple_fragment, null);
+ }
+}
+</pre>
+
+<p>And the following layout for an activity applies the fragment twice, side by side:</p>
+
+<pre>
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <fragment android:name="com.example.SimpleFragment"
+ android:id="@+id/list"
+ android:layout_weight="1"
+ android:layout_width="0dp"
+ android:layout_height="match_parent" />
+ <fragment android:name="com.example.SimpleFragment"
+ android:id="@+id/viewer"
+ android:layout_weight="1"
+ android:layout_width="0dp"
+ android:layout_height="match_parent" />
+</LinearLayout>
+</pre>
+
+<p>That's it. When an activity applies the previous layout as its content, the {@code
+SimpleFragment} class is instantiated for each occurence in the layout, applying the fragment
+layout when it receives the call to {@link android.app.Fragment#onCreateView onCreateView()}.</p>
+
+<p>Although the fragment in this example implements only the {@link
+android.app.Fragment#onCreateView onCreateView()} callback, there are several other lifecycle
+callback methods that you should implement in your application. For example, {@link
+android.app.Fragment#onCreate onCreate()}, {@link android.app.Fragment#onPause onPause()}, {@link
+android.app.Fragment#onStop onStop()} and others that coincide with the fragment's lifecycle.</p>
+
+
+
+<h2 id="Managing">Managing Fragments</h2>
+
+<p>A useful feature of fragments is the ability to add, remove, replace, and perform other
+operations on a fragment as the user interacts with the activity, alowing for more rich user
+experiences without changing activities. In order to perform these operations, you must use {@link
+android.app.FragmentTransaction} to perform fragment "transactions." You can acquire {@link
+android.app.FragmentTransaction} from your activity with {@link
+android.app.Activity#openFragmentTransaction}.</p>
+
+<p>Common transactions you can perform with fragments include:</p>
+
+<dl>
+ <dt>{@link android.app.FragmentTransaction#add add()}</dt>
+ <dd>Add a {@link android.app.Fragment} to the {@link android.app.Activity} layout.</dd>
+ <dt>{@link android.app.FragmentTransaction#remove remove()}</dt>
+ <dd>Remove a {@link android.app.Fragment} from the {@link android.app.Activity} layout.</dd>
+ <dt>{@link android.app.FragmentTransaction#replace replace()}</dt>
+ <dd>Replace an existing {@link android.app.Fragment} with another one.</dd>
+</dl>
+
+<p>For every transaction (or set of transactions) you perform, you must call {@link
+android.app.FragmentTransaction#commit} in order for the transactions made with {@link
+android.app.FragmentTransaction} to be applied. Before you do, however, you can call {@link
+android.app.FragmentTransaction#addToBackStack} to add the current fragment state to the
+activity's back stack, so that the user can return to the previous fragment state with the BACK key.
+For example, here's how a new fragment can replace another one, but keep the previous fragment
+in the back stack:</p>
+
+<pre>
+// Create new fragment
+Fragment newFragment = new MyFragment();
+FragmentTransaction ft = openFragmentTransaction();
+// Replace and add to back stack
+ft.replace(newFragment, R.id.myfragment);
+ft.addToBackStack(null);
+// Apply changes
+ft.commit();
+</pre>
+
+<p>In this example, {@code newFragment} replaces whatever fragment is currently in the
+layout container identified by the {@code R.id.myfragment} ID. By calling {@link
+android.app.FragmentTransaction#addToBackStack addToBackStack()}, this transaction (the replace) is
+saved to the activity's back stack so that the user can reverse this change and bring back the
+previous fragment by pressing the BACK key.</p>
+
+<p>If you perform multiple transactions and call {@link
+android.app.FragmentTransaction#addToBackStack addToBackStack()}, then all transactions performed
+before {@link android.app.FragmentTransaction#commit} are added to the activity's back stack as a
+single event and the BACK key will reverse them all together.</p>
+
+
+
+<h2 id="Lifecycle">Handling the Lifecycle</h2>
+
+<p>A fragment has a lifecycle that corresponds to the lifecycle of the activity in which it
+resides. For example, a fragment has callback methods {@link
+android.app.Fragment#onCreate onCreate()}, {@link android.app.Fragment#onStart onStart()}, {@link
+android.app.Fragment#onPause onPause()}, {@link android.app.Fragment#onStop onStop()}, and more.</p>
+
+<p>The lifecycle of the activity directly affects the lifecycle of the fragment, such that each
+lifecycle callback for the activity results in a similar callback for each fragment (for
+example, when the activity receives {@link android.app.Activity#onPause}, each fragment receives
+{@link android.app.Fragment#onPause}). However, the
+fragment's lifecycle can also change independently—but only while the activity is
+resumed (while it is in the foreground)—because you can dynamically
+add, remove, and replace fragments without any change to the lifecycle of the activity.</p>
+
+<p>To accomodate backward navigation with the
+BACK key, you can optionally maintain a back stack of fragment transactions, as described in the
+previous section. So, if you
+replace one fragment with another, the user can press the BACK key and view the previous
+fragment. Additionally, each fragment can maintain its own state, such that
+when the user navigates back to a previous fragment, the state of that fragment can be restored in
+the same manner as the state of an activity is restored when it is stopped and restarted.</p>
+
+<p>Managing the lifecycle of a fragment is a lot like managing the lifecycle of an activity. A
+fragment and an activity both have an "resumed," "paused," and "stopped" state, and they can both
+retain their state using a {@link android.os.Bundle}. The only significant difference is that an
+activity is placed into a the task's back stack by default (so that the user can navigate to
+the previous activity with the BACK key), but a fragment is placed into the activity's back stack
+only when you explicitly call {@link android.app.FragmentTransaction#addToBackStack(String)
+addToBackStack()} before you {@link android.app.FragmentTransaction#commit()} a fragment
+transaction.</p>
+
+<p>The order in which you perform transactions with {@link android.app.FragmentTransaction} doesn't
+matter, except:</p>
+<ul>
+ <li>You must call {@link android.app.FragmentTransaction#commit()} last</li>
+ <li>If you're adding multiple fragments to the same container, then the order in which
+you add them determines the order they appear</li>
+</ul>
+<p>If you do not call {@link android.app.FragmentTransaction#addToBackStack(String)
+addToBackStack()} when you perform a transaction that removes a fragment, then that fragment is
+destroyed when the transaction is committed.</p>
+
+
+<h3 id="CoordinatingWithTheActivity">Coordinating with the activity lifecycle</h3>
+
+<p>The lifecycle of an activity directly affects the lifecycle of a {@link android.app.Fragment}
+embedded in that activity. The {@link android.app.Fragment} class has lifecycle callback
+methods that match those in the {@link android.app.Activity} class.</p>
+
+<p>Fragments have a few extra lifecycle callbacks, however, that handle unique interaction with the
+activity in order to perform actions such as build and destroy the fragment's UI. These additional
+callback methods are:</p>
+
+<dl>
+ <dt>{@link android.app.Fragment#onAttach onAttach()}</dt>
+ <dd>Called when the fragment has been associated with the activity (the {@link
+android.app.Activity} is passed in here).</dd>
+ <dt>{@link android.app.Fragment#onCreateView onCreateView()}</dt>
+ <dd>Called to create the view hierarchy associated with the fragment.</dd>
+ <dt>{@link android.app.Fragment#onActivityCreated onActivityCreated()}</dt>
+ <dd>Called when the activity's own {@link android.app.Activity#onCreate
+onCreate()} has finished.</dd>
+ <dt>{@link android.app.Fragment#onDestroyView onDestroyView()}</dt>
+ <dd>Called when the view hierarchy associated with the fragment is being removed.</dd>
+ <dt>{@link android.app.Fragment#onDetach onDetach()}</dt>
+ <dd>Called when the fragment is being disassociated from the activity.</dd>
+</dl>
+
+<p>The flow of a fragment's lifecycle, as it is affected by its container activity, is illustrated
+by figure 3. In this figure, you can see how each successive state of the activity determines which
+callback methods the fragment may receive. For example, when the activity has received
+its {@link android.app.Activity#onCreate onCreate()} callback, the fragment receives no more
+than the {@link android.app.Fragment#onActivityCreated onActivityCreated()} callback. However,
+once the activity reaches the resumed state, you can freely add and remove fragments to the
+activity, so the fragment lifecycle is no longer inhibitted by the state of the activity. Yet,
+when the activity leaves the resumed state, the fragment again is pushed through its lifecycle by
+the activity (unless you explicitly destroy the fragment sooner).</p>
+
+
+<img src="{@docRoot}images/activity_fragment_lifecycle.png" alt=""/>
+<p class="img-caption"><strong>Figure 3.</strong> The activity lifecycle's affect on the lifecycle
+of a fragment.</p>
+
+
+<h3 id="Integrating">Integrating with the Activity</h3>
+
+<p>Although a {@link android.app.Fragment} is implemented separate from an {@link
+android.app.Activity} and can be used inside multiple activities, a fragment is directly tied to its
+container activity and can access the Activity instance with {@link
+android.app.Fragment#getActivity()}. So, a fragment can
+easily perform tasks such as find a view in the activity:</p>
+
+<pre>
+View listView = {@link android.app.Fragment#getActivity()}.{@link android.app.Activity#findViewById findViewById}(R.id.list);
+</pre>
+
+<p>This makes it easy for your fragment to call public methods in the activity.</p>
+
+<p>Likewise, your activity can call public methods in the fragment when you have a reference to the
+{@link android.app.Fragment}. You can acquire a reference to the fragment with {@link
+android.app.Activity#findFragmentById findFragmentById()} and cast it to your implementation of
+{@link android.app.Fragment}. For example:</p>
+
+<pre>
+MyFragment fragment = (MyFragment) findFragmentById(R.id.myfragment);
+fragment.refreshList();
+</pre>
+
+
+<h4 id="Callbacks">Creating event callbacks to the activity</h4>
+
+<p>In some cases, you might need a fragment to share events with the activity. A good way to do that
+is to define a callback interface inside the fragment and require that the host activity implement
+it. When the activity receives a callback, it can share the information with other fragments in the layout as
+necessary.</p>
+
+<p>For example, if a news application has two fragments in an activity—one to show a list of
+articles (fragment A) and another to display an article (fragment B)—then fragment A must tell
+the activity when a list item is selected so that it can tell fragment B to display the article. In
+this case, the following interface is defined inside fragment A:</p>
+
+<pre>
+public static class FragmentA extends ListFragment {
+ ...
+ // Container Activity must implement this interface
+ public interface SelectedCallback {
+ public void onArticleSelected(Uri articleUri);
+ }
+ ...
+}
+</pre>
+
+<p>Then the activity that hosts the fragment implements the {@code SelectedCallback} interface and
+overrides {@code onArticleSelected()} to notify fragment B of the event from fragment A. To ensure
+that the host activity implements this interface, fragment A's {@link
+android.app.Fragment#onAttach onAttach()} callback method (called when
+the fragment is added to the activity) instantiates an instance of {@code SelectedCallback} by
+casting the {@link android.app.Activity} that is passed into {@link android.app.Fragment#onAttach
+onAttach()}:</p>
+
+<pre>
+public static class FragmentA extends ListFragment {
+ ...
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ try {
+ mCallback = (SelectedCallback) activity;
+ } catch (ClassCastException e) {
+ activity.finish();
+ throw new ClassCastException(activity.toString() + " must implement SelectedCallback");
+ }
+ }
+ ...
+}
+</pre>
+
+<p>If the activity has not implemented the interface, then a {@link java.lang.ClassCastException} is
+thrown and the activity is shut down. On success, the {@code mCallback} member holds a reference to
+the {@link android.app.Activity}, so that fragment A can share events with the activity by calling
+methods defined by the {@code SelectedCallback} interface. For example, if fragment A is an
+extension of {@link android.app.ListFragment}, each time
+the user clicks a list item, the system calls {@link android.app.ListFragment#onListItemClick
+onListItemClick()} in the fragment, which then calls {@code onArticleSelected()} to share
+the event with the activity:</p>
+
+<pre>
+public static class FragmentA extends ListFragment {
+ ...
+ @Override
+ public void onListItemClick(ListView l, View v, int position, long id) {
+ // Append the clicked item's row ID with the content provider Uri
+ Uri noteUri = ContentUris.{@link android.content.ContentUris#withAppendedId withAppendedId}(ArticleColumns.CONTENT_URI, id);
+ // Send the event and Uri to the host activity
+ mCallback.onArticleSelected(noteUri);
+ }
+ ...
+}
+</pre>
+
+<p>The {@code id} parameter passed to {@link
+android.app.ListFragment#onListItemClick onListItemClick()} is the row ID of the clicked item,
+which the activity (or other fragment) uses to fetch the article from the application's {@link
+android.content.ContentProvider}.</p>
+
+<p><!--To see a complete implementation of this kind of callback interface, see the <a
+href="{@docRoot}resources/samples/NotePad/index.html">NotePad sample</a>. -->More information about
+using a content provider is available in the <a
+href="{@docRoot}guide/topics/providers/content-providers.html">Content Providers</a> document.</p>
+
+
+
+
+
+
+<h2 id="Menus">Adding Action Items to the Activity</h2>
+
+<p>Your fragments can contribute action items to the activity's <a
+href="{@docRoot}guide/topics/ui/actionbar.html">action bar</a> (and menu items to the options menu)
+using the callback methods
+{@link android.app.Fragment#onCreateOptionsMenu(Menu,MenuInflater) onCreateOptionsMenu()}. In order
+for this method to receive calls, however, you must call {@link
+android.app.Fragment#setHasOptionsMenu(boolean) setHasOptionsMenu()} during the {@link
+android.app.Fragment#onCreate(Bundle) onCreate()} callback in order to indicate that the fragment
+would like to receive a call to {@link android.app.Fragment#onCreateOptionsMenu(Menu,MenuInflater)
+onCreateOptionsMenu()}. Any action or menu items that you add from the fragment are appended to the
+existing
+items for the options menu (including those added by other fragments in the activity). The
+fragment also receives item-selected events with the {@link
+android.app.Fragment#onOptionsItemSelected(MenuItem) onOptionsItemSelected()} callback method.</p>
+
+<p>The {@link android.app.Fragment} class also contains methods to handle context menus. You can
+register a view to provide a context menu with {@link
+android.app.Fragment#registerForContextMenu(View) registerForContextMenu()}. When the user opens
+the context menu, the fragment receives a call to {@link
+android.app.Fragment#onCreateContextMenu(ContextMenu,View,ContextMenu.ContextMenuInfo)
+onCreateContextMenu()}. When the user selects an item, the fragment receives a call to {@link
+android.app.Fragment#onContextItemSelected(MenuItem) onContextItemSelected()}.</p>
+
+<p>For more information, see <a href="{@docRoot}guide/topics/ui/menus.html">Creating
+Menus</a> and <a href="{@docRoot}guide/topics/ui/actionbar.html">Using the Action Bar</a>.</p>
+
+
+
+
+
+
diff --git a/docs/html/guide/topics/manifest/activity-element.jd b/docs/html/guide/topics/manifest/activity-element.jd
index e030a4c..2648cb7 100644
--- a/docs/html/guide/topics/manifest/activity-element.jd
+++ b/docs/html/guide/topics/manifest/activity-element.jd
@@ -290,7 +290,8 @@
<dd>An icon representing the activity. The icon is displayed to users when
a representation of the activity is required on-screen. For example, icons
for activities that initiate tasks are displayed in the launcher window.
-The icon is often accompanied by a label (see the {@code label} attribute).
+The icon is often accompanied by a label (see the <a href="#label">{@code
+android:label}</a> attribute).
</p>
<p>
diff --git a/docs/html/guide/topics/manifest/application-element.jd b/docs/html/guide/topics/manifest/application-element.jd
index 9ac07fd..1fadc6e 100644
--- a/docs/html/guide/topics/manifest/application-element.jd
+++ b/docs/html/guide/topics/manifest/application-element.jd
@@ -12,6 +12,7 @@
android:<a href="#icon">icon</a>="<i>drawable resource</i>"
android:<a href="#killrst">killAfterRestore</a>=["true" | "false"]
android:<a href="#label">label</a>="<i>string resource</i>"
+ android:<a href="#logo">logo</a>="<i>drawable resource</i>"
android:<a href="#space">manageSpaceActivity</a>="<i>string</i>"
android:<a href="#nm">name</a>="<i>string</i>"
android:<a href="#prmsn">permission</a>="<i>string</i>"
@@ -121,7 +122,7 @@
<p>
This attribute must be set as a reference to a drawable resource containing
-the image definition. There is no default icon.
+the image (for example {@code "@drawable/icon"}). There is no default icon.
</p></dd>
<dt><a name="killrst"></a>{@code android:killAfterRestore}</dt>
@@ -154,6 +155,11 @@
it can also be set as a raw string.
</p></dd>
+<dt><a name="logo"></a>{@code android:logo}</dt>
+<dd>A logo for the application as whole, and the default logo for activities.
+<p>This attribute must be set as a reference to a drawable resource containing
+the image (for example {@code "@drawable/logo"}). There is no default logo.</p></dd>
+
<dt><a name="space"></a>{@code android:manageSpaceActivity}</dt>
<dd>The fully qualified name of an Activity subclass that the system
can launch to let users manage the memory occupied by the application
diff --git a/docs/html/guide/topics/resources/providing-resources.jd b/docs/html/guide/topics/resources/providing-resources.jd
index d868599..4f3b0da 100644
--- a/docs/html/guide/topics/resources/providing-resources.jd
+++ b/docs/html/guide/topics/resources/providing-resources.jd
@@ -329,7 +329,8 @@
<td>
<code>small</code><br/>
<code>normal</code><br/>
- <code>large</code>
+ <code>large</code><br/>
+ <code>xlarge</code>
</td>
<td>
<ul class="nolist">
@@ -347,6 +348,10 @@
medium-density VGA screen. Such a screen has significantly more
available space in both width and height than an HVGA display.
Examples are VGA and WVGA medium density screens.</li>
+ <li>{@code xlarge}: Screens that are considerably larger than the traditional
+ medium-density HVGA screen. In most cases, devices with extra large screens would be too
+large to carry in a pocket and would most likely be tablet-style devices. <em>Added in API Level
+9.</em></li>
</ul>
<p><em>Added in API Level 4.</em></p>
<p>See <a href="{@docRoot}guide/practices/screens_support.html">Supporting Multiple
@@ -437,6 +442,7 @@
<code>ldpi</code><br/>
<code>mdpi</code><br/>
<code>hdpi</code><br/>
+ <code>xhdpi</code><br/>
<code>nodpi</code>
</td>
<td>
@@ -445,6 +451,8 @@
<li>{@code mdpi}: Medium-density (on traditional HVGA) screens; approximately
160dpi.</li>
<li>{@code hdpi}: High-density screens; approximately 240dpi.</li>
+ <li>{@code xhdpi}: Extra high-density screens; approximately 320dpi. <em>Added in API
+Level 8</em></li>
<li>{@code nodpi}: This can be used for bitmap resources that you do not want to be scaled
to match the device density.</li>
</ul>
diff --git a/docs/html/images/activity_fragment_lifecycle.png b/docs/html/images/activity_fragment_lifecycle.png
new file mode 100644
index 0000000..156aa40
--- /dev/null
+++ b/docs/html/images/activity_fragment_lifecycle.png
Binary files differ
diff --git a/docs/html/images/axis_globe_inverted.png b/docs/html/images/axis_globe_inverted.png
new file mode 100644
index 0000000..bd78c7f
--- /dev/null
+++ b/docs/html/images/axis_globe_inverted.png
Binary files differ
diff --git a/docs/html/images/fragment_lifecycle.png b/docs/html/images/fragment_lifecycle.png
new file mode 100644
index 0000000..ce9d395
--- /dev/null
+++ b/docs/html/images/fragment_lifecycle.png
Binary files differ
diff --git a/docs/html/images/screens_support/screens-ranges.png b/docs/html/images/screens_support/screens-ranges.png
index 034ac34..dce6264 100644
--- a/docs/html/images/screens_support/screens-ranges.png
+++ b/docs/html/images/screens_support/screens-ranges.png
Binary files differ
diff --git a/docs/html/resources/resources-data.js b/docs/html/resources/resources-data.js
index d06b695..221406c 100644
--- a/docs/html/resources/resources-data.js
+++ b/docs/html/resources/resources-data.js
@@ -465,7 +465,7 @@
}
},
{
- tags: ['sample', 'ui', 'search', 'new'],
+ tags: ['sample', 'ui', 'search'],
path: 'samples/SearchableDictionary/index.html',
title: {
en: 'Searchable Dictionary v2'
@@ -485,7 +485,7 @@
}
},
{
- tags: ['sample', 'testing', 'new'],
+ tags: ['sample', 'testing'],
path: 'samples/Spinner/index.html',
title: {
en: 'Spinner'
@@ -495,7 +495,7 @@
}
},
{
- tags: ['sample', 'testing', 'new'],
+ tags: ['sample', 'testing'],
path: 'samples/SpinnerTest/index.html',
title: {
en: 'SpinnerTest'
@@ -505,7 +505,7 @@
}
},
{
- tags: ['sample', 'newfeature', 'new'],
+ tags: ['sample', 'newfeature'],
path: 'samples/TicTacToeLib/index.html',
title: {
en: 'TicTacToeLib'
@@ -515,7 +515,7 @@
}
},
{
- tags: ['sample', 'newfeature', 'new'],
+ tags: ['sample', 'newfeature',],
path: 'samples/TicTacToeMain/index.html',
title: {
en: 'TicTacToeMain'
@@ -610,7 +610,7 @@
}
},
{
- tags: ['tutorial', 'testing', 'new'],
+ tags: ['tutorial', 'testing'],
path: 'tutorials/testing/helloandroid_test.html',
title: {
en: 'Hello Testing'
@@ -620,7 +620,7 @@
}
},
{
- tags: ['tutorial', 'testing', 'new'],
+ tags: ['tutorial', 'testing'],
path: 'tutorials/testing/activity_test.html',
title: {
en: 'Activity Testing'
diff --git a/include/media/AudioEffect.h b/include/media/AudioEffect.h
index c967efb..cda2be0 100644
--- a/include/media/AudioEffect.h
+++ b/include/media/AudioEffect.h
@@ -403,7 +403,7 @@
static status_t guidToString(const effect_uuid_t *guid, char *str, size_t maxLen);
protected:
- volatile int32_t mEnabled; // enable state
+ bool mEnabled; // enable state
int32_t mSessionId; // audio session ID
int32_t mPriority; // priority for effect control
status_t mStatus; // effect status
@@ -412,6 +412,7 @@
void* mUserData; // client context for callback function
effect_descriptor_t mDescriptor; // effect descriptor
int32_t mId; // system wide unique effect engine instance ID
+ Mutex mLock; // Mutex for mEnabled access
private:
diff --git a/include/media/AudioRecord.h b/include/media/AudioRecord.h
index 38e3d44..5f7cd90 100644
--- a/include/media/AudioRecord.h
+++ b/include/media/AudioRecord.h
@@ -356,7 +356,7 @@
sp<IAudioRecord> mAudioRecord;
sp<IMemory> mCblkMemory;
sp<ClientRecordThread> mClientRecordThread;
- Mutex mRecordThreadLock;
+ Mutex mLock;
uint32_t mFrameCount;
diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h
index 4475d4a..813a905 100644
--- a/include/media/AudioTrack.h
+++ b/include/media/AudioTrack.h
@@ -480,6 +480,7 @@
uint32_t mFlags;
int mSessionId;
int mAuxEffectId;
+ Mutex mLock;
};
diff --git a/include/media/stagefright/AMRWriter.h b/include/media/stagefright/AMRWriter.h
index d8410a9..62d57b4 100644
--- a/include/media/stagefright/AMRWriter.h
+++ b/include/media/stagefright/AMRWriter.h
@@ -44,7 +44,6 @@
virtual ~AMRWriter();
private:
- FILE *mFile;
int mFd;
status_t mInitCheck;
sp<MediaSource> mSource;
diff --git a/include/media/stagefright/FileSource.h b/include/media/stagefright/FileSource.h
index a7f7181..72a0403 100644
--- a/include/media/stagefright/FileSource.h
+++ b/include/media/stagefright/FileSource.h
@@ -46,7 +46,6 @@
virtual ~FileSource();
private:
- FILE *mFile;
int mFd;
int64_t mOffset;
int64_t mLength;
diff --git a/include/media/stagefright/MPEG4Writer.h b/include/media/stagefright/MPEG4Writer.h
index e6d8a26..f7618e9 100644
--- a/include/media/stagefright/MPEG4Writer.h
+++ b/include/media/stagefright/MPEG4Writer.h
@@ -61,8 +61,8 @@
private:
class Track;
- FILE *mFile;
int mFd;
+ status_t mInitCheck;
bool mUse4ByteNalLength;
bool mUse32BitOffset;
bool mIsFileSizeLimitExplicitlyRequested;
@@ -149,7 +149,7 @@
off64_t addSample_l(MediaBuffer *buffer);
off64_t addLengthPrefixedSample_l(MediaBuffer *buffer);
- inline size_t write(const void *ptr, size_t size, size_t nmemb, FILE* stream);
+ inline size_t write(const void *ptr, size_t size, size_t nmemb);
bool exceedsFileSizeLimit();
bool use32BitFileOffset() const;
bool exceedsFileDurationLimit();
diff --git a/include/utils/Tokenizer.h b/include/utils/Tokenizer.h
index bfe8923..21e58e6 100644
--- a/include/utils/Tokenizer.h
+++ b/include/utils/Tokenizer.h
@@ -19,6 +19,7 @@
#include <assert.h>
#include <utils/Errors.h>
+#include <utils/FileMap.h>
#include <utils/String8.h>
namespace android {
@@ -27,7 +28,7 @@
* A simple tokenizer for loading and parsing ASCII text files line by line.
*/
class Tokenizer {
- Tokenizer(const String8& filename, const char* buffer, size_t length);
+ Tokenizer(const String8& filename, FileMap* fileMap, const char* buffer, size_t length);
public:
~Tokenizer();
@@ -108,6 +109,7 @@
Tokenizer(const Tokenizer& other); // not copyable
String8 mFilename;
+ FileMap* mFileMap;
const char* mBuffer;
size_t mLength;
diff --git a/libs/utils/Tokenizer.cpp b/libs/utils/Tokenizer.cpp
index 19dadf0..9251973 100644
--- a/libs/utils/Tokenizer.cpp
+++ b/libs/utils/Tokenizer.cpp
@@ -22,7 +22,6 @@
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
-#include <sys/mman.h>
#include <utils/Log.h>
#include <utils/Tokenizer.h>
@@ -37,13 +36,16 @@
}
-Tokenizer::Tokenizer(const String8& filename, const char* buffer, size_t length) :
- mFilename(filename), mBuffer(buffer), mLength(length),
+Tokenizer::Tokenizer(const String8& filename, FileMap* fileMap,
+ const char* buffer, size_t length) :
+ mFilename(filename), mFileMap(fileMap), mBuffer(buffer), mLength(length),
mCurrent(buffer), mLineNumber(1) {
}
Tokenizer::~Tokenizer() {
- munmap((void*)mBuffer, mLength);
+ if (mFileMap) {
+ mFileMap->release();
+ }
}
status_t Tokenizer::open(const String8& filename, Tokenizer** outTokenizer) {
@@ -55,29 +57,29 @@
result = -errno;
LOGE("Error opening file '%s', %s.", filename.string(), strerror(errno));
} else {
- struct stat64 stat;
- if (fstat64(fd, &stat)) {
+ struct stat stat;
+ if (fstat(fd, &stat)) {
result = -errno;
LOGE("Error getting size of file '%s', %s.", filename.string(), strerror(errno));
} else {
size_t length = size_t(stat.st_size);
- void* buffer = mmap(NULL, length, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0);
- if (buffer == MAP_FAILED) {
- result = -errno;
+ FileMap* fileMap = new FileMap();
+ if (!fileMap->create(NULL, fd, 0, length, true)) {
+ result = NO_MEMORY;
LOGE("Error mapping file '%s', %s.", filename.string(), strerror(errno));
} else {
- if (madvise(buffer, length, MADV_SEQUENTIAL)) {
- LOGW("Error calling madvise for mmapped file '%s', %s.", filename.string(),
- strerror(errno));
- }
+ fileMap->advise(FileMap::SEQUENTIAL);
- *outTokenizer = new Tokenizer(filename, static_cast<const char*>(buffer), length);
+ *outTokenizer = new Tokenizer(filename, fileMap,
+ static_cast<const char*>(fileMap->getDataPtr()), length);
if (!*outTokenizer) {
result = NO_MEMORY;
LOGE("Error allocating tokenizer for file=%s.", filename.string());
- munmap(buffer, length);
}
}
+ if (result) {
+ fileMap->release();
+ }
}
close(fd);
}
diff --git a/media/java/android/media/MtpClient.java b/media/java/android/media/MtpClient.java
index 98da1f6..dcb1983 100644
--- a/media/java/android/media/MtpClient.java
+++ b/media/java/android/media/MtpClient.java
@@ -16,7 +16,6 @@
package android.media;
-import android.os.ParcelFileDescriptor;
import android.util.Log;
/**
@@ -69,9 +68,10 @@
return native_get_storage_id(deviceID, objectID);
}
- // create a file descriptor for reading the contents of an object over MTP
- public ParcelFileDescriptor openFile(int deviceID, long objectID) {
- return native_open_file(deviceID, objectID);
+ // Reads a file from device to host to the specified destination.
+ // Returns true if the transfer succeeds.
+ public boolean importFile(int deviceID, long objectID, String destPath) {
+ return native_import_file(deviceID, objectID, destPath);
}
public interface Listener {
@@ -104,5 +104,5 @@
private native boolean native_delete_object(int deviceID, long objectID);
private native long native_get_parent(int deviceID, long objectID);
private native long native_get_storage_id(int deviceID, long objectID);
- private native ParcelFileDescriptor native_open_file(int deviceID, long objectID);
+ private native boolean native_import_file(int deviceID, long objectID, String destPath);
}
diff --git a/media/java/android/media/MtpCursor.java b/media/java/android/media/MtpCursor.java
index 9b5ab95..daa3f4d 100644
--- a/media/java/android/media/MtpCursor.java
+++ b/media/java/android/media/MtpCursor.java
@@ -40,6 +40,7 @@
public static final int OBJECT_ID = 6;
public static final int STORAGE_CHILDREN = 7;
public static final int OBJECT_CHILDREN = 8;
+ public static final int OBJECT_IMPORT = 9;
/** The names of the columns in the projection */
private String[] mColumns;
diff --git a/media/jni/android_media_MtpClient.cpp b/media/jni/android_media_MtpClient.cpp
index d23185b..144dfc8 100644
--- a/media/jni/android_media_MtpClient.cpp
+++ b/media/jni/android_media_MtpClient.cpp
@@ -26,6 +26,7 @@
#include "jni.h"
#include "JNIHelp.h"
#include "android_runtime/AndroidRuntime.h"
+#include "private/android_filesystem_config.h"
#include "MtpClient.h"
#include "MtpDevice.h"
@@ -39,19 +40,6 @@
static jmethodID method_deviceRemoved;
static jfieldID field_context;
-static struct file_descriptor_offsets_t
-{
- jclass mClass;
- jmethodID mConstructor;
- jfieldID mDescriptor;
-} gFileDescriptorOffsets;
-
-static struct parcel_file_descriptor_offsets_t
-{
- jclass mClass;
- jmethodID mConstructor;
-} gParcelFileDescriptorOffsets;
-
#ifdef HAVE_ANDROID_OS
static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
@@ -201,34 +189,19 @@
return -1;
}
-static jobject
-android_media_MtpClient_open_file(JNIEnv *env, jobject thiz,
- jint device_id, jlong object_id)
+static jboolean
+android_media_MtpClient_import_file(JNIEnv *env, jobject thiz,
+ jint device_id, jlong object_id, jstring dest_path)
{
#ifdef HAVE_ANDROID_OS
MyClient *client = (MyClient *)env->GetIntField(thiz, field_context);
MtpDevice* device = client->getDevice(device_id);
- if (!device)
- return NULL;
-
- MtpObjectInfo* info = device->getObjectInfo(object_id);
- if (!info)
- return NULL;
- int object_size = info->mCompressedSize;
- delete info;
- int fd = device->readObject(object_id, object_size);
- if (fd < 0)
- return NULL;
-
- jobject fileDescriptor = env->NewObject(gFileDescriptorOffsets.mClass,
- gFileDescriptorOffsets.mConstructor);
- if (fileDescriptor != NULL) {
- env->SetIntField(fileDescriptor, gFileDescriptorOffsets.mDescriptor, fd);
- } else {
- return NULL;
+ if (device) {
+ const char *destPathStr = env->GetStringUTFChars(dest_path, NULL);
+ bool result = device->readObject(object_id, destPathStr, AID_SDCARD_RW, 0664);
+ env->ReleaseStringUTFChars(dest_path, destPathStr);
+ return result;
}
- return env->NewObject(gParcelFileDescriptorOffsets.mClass,
- gParcelFileDescriptorOffsets.mConstructor, fileDescriptor);
#endif
return NULL;
}
@@ -243,8 +216,8 @@
{"native_delete_object", "(IJ)Z", (void *)android_media_MtpClient_delete_object},
{"native_get_parent", "(IJ)J", (void *)android_media_MtpClient_get_parent},
{"native_get_storage_id", "(IJ)J", (void *)android_media_MtpClient_get_storage_id},
- {"native_open_file", "(IJ)Landroid/os/ParcelFileDescriptor;",
- (void *)android_media_MtpClient_open_file},
+ {"native_import_file", "(IJLjava/lang/String;)Z",
+ (void *)android_media_MtpClient_import_file},
};
static const char* const kClassPathName = "android/media/MtpClient";
@@ -276,21 +249,6 @@
return -1;
}
- clazz = env->FindClass("java/io/FileDescriptor");
- LOG_FATAL_IF(clazz == NULL, "Unable to find class java.io.FileDescriptor");
- gFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
- gFileDescriptorOffsets.mConstructor = env->GetMethodID(clazz, "<init>", "()V");
- gFileDescriptorOffsets.mDescriptor = env->GetFieldID(clazz, "descriptor", "I");
- LOG_FATAL_IF(gFileDescriptorOffsets.mDescriptor == NULL,
- "Unable to find descriptor field in java.io.FileDescriptor");
-
- clazz = env->FindClass("android/os/ParcelFileDescriptor");
- LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.ParcelFileDescriptor");
- gParcelFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
- gParcelFileDescriptorOffsets.mConstructor = env->GetMethodID(clazz, "<init>", "(Ljava/io/FileDescriptor;)V");
- LOG_FATAL_IF(gParcelFileDescriptorOffsets.mConstructor == NULL,
- "Unable to find constructor for android.os.ParcelFileDescriptor");
-
return AndroidRuntime::registerNativeMethods(env,
"android/media/MtpClient", gMethods, NELEM(gMethods));
}
diff --git a/media/jni/soundpool/SoundPool.h b/media/jni/soundpool/SoundPool.h
index 42037af..1b0fd38 100644
--- a/media/jni/soundpool/SoundPool.h
+++ b/media/jni/soundpool/SoundPool.h
@@ -22,7 +22,6 @@
#include <utils/Vector.h>
#include <utils/KeyedVector.h>
#include <media/AudioTrack.h>
-#include <cutils/atomic.h>
namespace android {
diff --git a/media/libmedia/AudioEffect.cpp b/media/libmedia/AudioEffect.cpp
index 88b8c86..aadeba5 100644
--- a/media/libmedia/AudioEffect.cpp
+++ b/media/libmedia/AudioEffect.cpp
@@ -27,7 +27,6 @@
#include <media/AudioEffect.h>
#include <utils/Log.h>
-#include <cutils/atomic.h>
#include <binder/IPCThreadState.h>
@@ -207,18 +206,22 @@
return INVALID_OPERATION;
}
- if (enabled) {
- LOGV("enable %p", this);
- if (android_atomic_or(1, &mEnabled) == 0) {
- return mIEffect->enable();
+ status_t status = NO_ERROR;
+
+ AutoMutex lock(mLock);
+ if (enabled != mEnabled) {
+ if (enabled) {
+ LOGV("enable %p", this);
+ status = mIEffect->enable();
+ } else {
+ LOGV("disable %p", this);
+ status = mIEffect->disable();
}
- } else {
- LOGV("disable %p", this);
- if (android_atomic_and(~1, &mEnabled) == 1) {
- return mIEffect->disable();
+ if (status == NO_ERROR) {
+ mEnabled = enabled;
}
}
- return NO_ERROR;
+ return status;
}
status_t AudioEffect::command(uint32_t cmdCode,
@@ -232,26 +235,26 @@
return INVALID_OPERATION;
}
- if ((cmdCode == EFFECT_CMD_ENABLE || cmdCode == EFFECT_CMD_DISABLE) &&
- (replySize == NULL || *replySize != sizeof(status_t) || replyData == NULL)) {
- return BAD_VALUE;
+ if (cmdCode == EFFECT_CMD_ENABLE || cmdCode == EFFECT_CMD_DISABLE) {
+ if (mEnabled == (cmdCode == EFFECT_CMD_ENABLE)) {
+ return NO_ERROR;
+ }
+ if (replySize == NULL || *replySize != sizeof(status_t) || replyData == NULL) {
+ return BAD_VALUE;
+ }
+ mLock.lock();
}
status_t status = mIEffect->command(cmdCode, cmdSize, cmdData, replySize, replyData);
- if (status != NO_ERROR) {
- return status;
- }
if (cmdCode == EFFECT_CMD_ENABLE || cmdCode == EFFECT_CMD_DISABLE) {
- status = *(status_t *)replyData;
- if (status != NO_ERROR) {
- return status;
+ if (status == NO_ERROR) {
+ status = *(status_t *)replyData;
}
- if (cmdCode == EFFECT_CMD_ENABLE) {
- android_atomic_or(1, &mEnabled);
- } else {
- android_atomic_and(~1, &mEnabled);
+ if (status == NO_ERROR) {
+ mEnabled = (cmdCode == EFFECT_CMD_ENABLE);
}
+ mLock.unlock();
}
return status;
@@ -370,11 +373,7 @@
{
LOGV("enableStatusChanged %p enabled %d mCbf %p", this, enabled, mCbf);
if (mStatus == ALREADY_EXISTS) {
- if (enabled) {
- android_atomic_or(1, &mEnabled);
- } else {
- android_atomic_and(~1, &mEnabled);
- }
+ mEnabled = enabled;
if (mCbf) {
mCbf(EVENT_ENABLE_STATUS_CHANGED, mUserData, &enabled);
}
diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp
index a6c515c..1d6ffa0 100644
--- a/media/libmedia/AudioRecord.cpp
+++ b/media/libmedia/AudioRecord.cpp
@@ -35,7 +35,6 @@
#include <binder/Parcel.h>
#include <binder/IPCThreadState.h>
#include <utils/Timers.h>
-#include <cutils/atomic.h>
#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true ))
#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false ))
@@ -282,7 +281,9 @@
t->mLock.lock();
}
- if (android_atomic_or(1, &mActive) == 0) {
+ AutoMutex lock(mLock);
+ if (mActive == 0) {
+ mActive = 1;
ret = mAudioRecord->start();
if (ret == DEAD_OBJECT) {
LOGV("start() dead IAudioRecord: creating a new one");
@@ -302,8 +303,7 @@
setpriority(PRIO_PROCESS, 0, THREAD_PRIORITY_AUDIO_CLIENT);
}
} else {
- LOGV("start() failed");
- android_atomic_and(~1, &mActive);
+ mActive = 0;
}
}
@@ -322,9 +322,11 @@
if (t != 0) {
t->mLock.lock();
- }
+ }
- if (android_atomic_and(~1, &mActive) == 1) {
+ AutoMutex lock(mLock);
+ if (mActive == 1) {
+ mActive = 0;
mCblk->cv.signal();
mAudioRecord->stop();
// the record head position will reset to 0, so if a marker is set, we need
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index 587c8ff..c1bed59 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -35,7 +35,6 @@
#include <binder/Parcel.h>
#include <binder/IPCThreadState.h>
#include <utils/Timers.h>
-#include <cutils/atomic.h>
#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true ))
#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false ))
@@ -312,7 +311,9 @@
t->mLock.lock();
}
- if (android_atomic_or(1, &mActive) == 0) {
+ AutoMutex lock(mLock);
+ if (mActive == 0) {
+ mActive = 1;
mNewPosition = mCblk->server + mUpdatePeriod;
mCblk->bufferTimeoutMs = MAX_STARTUP_TIMEOUT_MS;
mCblk->waitTimeMs = 0;
@@ -344,7 +345,7 @@
}
if (status != NO_ERROR) {
LOGV("start() failed");
- android_atomic_and(~1, &mActive);
+ mActive = 0;
if (t != 0) {
t->requestExit();
} else {
@@ -367,7 +368,9 @@
t->mLock.lock();
}
- if (android_atomic_and(~1, &mActive) == 1) {
+ AutoMutex lock(mLock);
+ if (mActive == 1) {
+ mActive = 0;
mCblk->cv.signal();
mAudioTrack->stop();
// Cancel loops (If we are in the middle of a loop, playback
@@ -407,7 +410,6 @@
mMarkerReached = false;
mUpdatePeriod = 0;
-
if (!mActive) {
mAudioTrack->flush();
// Release AudioTrack callback thread in case it was waiting for new buffers
@@ -419,7 +421,9 @@
void AudioTrack::pause()
{
LOGV("pause");
- if (android_atomic_and(~1, &mActive) == 1) {
+ AutoMutex lock(mLock);
+ if (mActive == 1) {
+ mActive = 0;
mAudioTrack->pause();
}
}
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index dadd1db..ec3b5a2 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -864,7 +864,7 @@
return UNKNOWN_ERROR;
}
- mWriter = new AMRWriter(dup(mOutputFd));
+ mWriter = new AMRWriter(mOutputFd);
mWriter->addSource(audioEncoder);
if (mMaxFileDurationUs != 0) {
@@ -912,7 +912,7 @@
}
}
- mWriter = new ARTPWriter(dup(mOutputFd));
+ mWriter = new ARTPWriter(mOutputFd);
mWriter->addSource(source);
mWriter->setListener(mListener);
@@ -922,7 +922,7 @@
status_t StagefrightRecorder::startMPEG2TSRecording() {
CHECK_EQ(mOutputFormat, OUTPUT_FORMAT_MPEG2TS);
- sp<MediaWriter> writer = new MPEG2TSWriter(dup(mOutputFd));
+ sp<MediaWriter> writer = new MPEG2TSWriter(mOutputFd);
if (mAudioSource != AUDIO_SOURCE_LIST_END) {
if (mAudioEncoder != AUDIO_ENCODER_AAC) {
@@ -1204,7 +1204,7 @@
mediaWriter->clear();
*totalBitRate = 0;
status_t err = OK;
- sp<MediaWriter> writer = new MPEG4Writer(dup(outputFd));
+ sp<MediaWriter> writer = new MPEG4Writer(outputFd);
// Add audio source first if it exists
if (!mCaptureTimeLapse && (mAudioSource != AUDIO_SOURCE_LIST_END)) {
diff --git a/media/libstagefright/AMRWriter.cpp b/media/libstagefright/AMRWriter.cpp
index ecbd96c..0db3d1d 100644
--- a/media/libstagefright/AMRWriter.cpp
+++ b/media/libstagefright/AMRWriter.cpp
@@ -24,22 +24,28 @@
#include <media/mediarecorder.h>
#include <sys/prctl.h>
#include <sys/resource.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
namespace android {
AMRWriter::AMRWriter(const char *filename)
- : mFile(fopen(filename, "wb")),
- mFd(mFile == NULL? -1: fileno(mFile)),
- mInitCheck(mFile != NULL ? OK : NO_INIT),
+ : mFd(-1),
+ mInitCheck(NO_INIT),
mStarted(false),
mPaused(false),
mResumed(false) {
+
+ mFd = open(filename, O_CREAT | O_LARGEFILE | O_TRUNC);
+ if (mFd >= 0) {
+ mInitCheck = OK;
+ }
}
AMRWriter::AMRWriter(int fd)
- : mFile(fdopen(fd, "wb")),
- mFd(mFile == NULL? -1: fileno(mFile)),
- mInitCheck(mFile != NULL ? OK : NO_INIT),
+ : mFd(dup(fd)),
+ mInitCheck(mFd < 0? NO_INIT: OK),
mStarted(false),
mPaused(false),
mResumed(false) {
@@ -50,9 +56,9 @@
stop();
}
- if (mFile != NULL) {
- fclose(mFile);
- mFile = NULL;
+ if (mFd != -1) {
+ close(mFd);
+ mFd = -1;
}
}
@@ -92,7 +98,7 @@
mSource = source;
const char *kHeader = isWide ? "#!AMR-WB\n" : "#!AMR\n";
- size_t n = strlen(kHeader);
+ ssize_t n = strlen(kHeader);
if (write(mFd, kHeader, n) != n) {
return ERROR_IO;
}
@@ -266,9 +272,8 @@
notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_COMPLETION_STATUS, UNKNOWN_ERROR);
}
- fflush(mFile);
- fclose(mFile);
- mFile = NULL;
+ close(mFd);
+ mFd = -1;
mReachedEOS = true;
if (err == ERROR_END_OF_STREAM) {
return OK;
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 8fe1d4d..4ad1eb4 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -49,7 +49,6 @@
WVMExtractor.cpp \
XINGSeeker.cpp \
avc_utils.cpp \
- string.cpp
LOCAL_C_INCLUDES:= \
$(JNI_H_INCLUDE) \
diff --git a/media/libstagefright/FileSource.cpp b/media/libstagefright/FileSource.cpp
index c9f68e9..98d5b50 100644
--- a/media/libstagefright/FileSource.cpp
+++ b/media/libstagefright/FileSource.cpp
@@ -18,12 +18,14 @@
#include <media/stagefright/MediaDebug.h>
#include <sys/types.h>
#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
namespace android {
FileSource::FileSource(const char *filename)
- : mFile(fopen(filename, "rb")),
- mFd(mFile == NULL ? -1 : fileno(mFile)),
+ : mFd(-1),
mOffset(0),
mLength(-1),
mDecryptHandle(NULL),
@@ -31,11 +33,12 @@
mDrmBufOffset(0),
mDrmBufSize(0),
mDrmBuf(NULL){
+
+ mFd = open(filename, O_LARGEFILE | O_RDONLY);
}
FileSource::FileSource(int fd, int64_t offset, int64_t length)
- : mFile(fdopen(fd, "rb")),
- mFd(fd),
+ : mFd(fd),
mOffset(offset),
mLength(length),
mDecryptHandle(NULL),
@@ -48,9 +51,9 @@
}
FileSource::~FileSource() {
- if (mFile != NULL) {
- fclose(mFile);
- mFile = NULL;
+ if (mFd >= 0) {
+ close(mFd);
+ mFd = -1;
}
if (mDrmBuf != NULL) {
@@ -60,11 +63,11 @@
}
status_t FileSource::initCheck() const {
- return mFile != NULL ? OK : NO_INIT;
+ return mFd >= 0 ? OK : NO_INIT;
}
ssize_t FileSource::readAt(off64_t offset, void *data, size_t size) {
- if (mFile == NULL) {
+ if (mFd < 0) {
return NO_INIT;
}
@@ -95,7 +98,7 @@
}
status_t FileSource::getSize(off64_t *size) {
- if (mFile == NULL) {
+ if (mFd < 0) {
return NO_INIT;
}
diff --git a/media/libstagefright/HTTPStream.cpp b/media/libstagefright/HTTPStream.cpp
index ccc6a34..e7f00aa 100644
--- a/media/libstagefright/HTTPStream.cpp
+++ b/media/libstagefright/HTTPStream.cpp
@@ -36,7 +36,7 @@
namespace android {
// static
-const char *HTTPStream::kStatusKey = ":status:";
+const char *HTTPStream::kStatusKey = ":status:"; // MUST be lowercase.
HTTPStream::HTTPStream()
: mState(READY),
@@ -220,7 +220,7 @@
return err;
}
- mHeaders.add(string(kStatusKey), string(line));
+ mHeaders.add(AString(kStatusKey), AString(line));
char *spacePos = strchr(line, ' ');
if (spacePos == NULL) {
@@ -264,7 +264,10 @@
char *colonPos = strchr(line, ':');
if (colonPos == NULL) {
- mHeaders.add(string(line), string());
+ AString key = line;
+ key.tolower();
+
+ mHeaders.add(key, AString());
} else {
char *end_of_key = colonPos;
while (end_of_key > line && isspace(end_of_key[-1])) {
@@ -278,7 +281,10 @@
*end_of_key = '\0';
- mHeaders.add(string(line), string(start_of_value));
+ AString key = line;
+ key.tolower();
+
+ mHeaders.add(key, AString(start_of_value));
}
}
@@ -314,8 +320,11 @@
return (ssize_t)total;
}
-bool HTTPStream::find_header_value(const string &key, string *value) const {
- ssize_t index = mHeaders.indexOfKey(key);
+bool HTTPStream::find_header_value(const AString &key, AString *value) const {
+ AString key_lower = key;
+ key_lower.tolower();
+
+ ssize_t index = mHeaders.indexOfKey(key_lower);
if (index < 0) {
value->clear();
return false;
diff --git a/media/libstagefright/MPEG2TSWriter.cpp b/media/libstagefright/MPEG2TSWriter.cpp
index 81a2b0d..4d8165e 100644
--- a/media/libstagefright/MPEG2TSWriter.cpp
+++ b/media/libstagefright/MPEG2TSWriter.cpp
@@ -410,7 +410,7 @@
////////////////////////////////////////////////////////////////////////////////
MPEG2TSWriter::MPEG2TSWriter(int fd)
- : mFile(fdopen(fd, "wb")),
+ : mFile(fdopen(dup(fd), "wb")),
mStarted(false),
mNumSourcesDone(0),
mNumTSPacketsWritten(0),
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 19e3eae..6760707 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -33,6 +33,10 @@
#include <media/stagefright/MediaSource.h>
#include <media/stagefright/Utils.h>
#include <media/mediarecorder.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
#include "include/ESDS.h"
@@ -214,8 +218,8 @@
};
MPEG4Writer::MPEG4Writer(const char *filename)
- : mFile(fopen(filename, "wb")),
- mFd(mFile == NULL? -1: fileno(mFile)),
+ : mFd(-1),
+ mInitCheck(NO_INIT),
mUse4ByteNalLength(true),
mUse32BitOffset(true),
mIsFileSizeLimitExplicitlyRequested(false),
@@ -225,12 +229,16 @@
mMdatOffset(0),
mEstimatedMoovBoxSize(0),
mInterleaveDurationUs(1000000) {
- CHECK(mFile != NULL);
+
+ mFd = open(filename, O_CREAT | O_LARGEFILE | O_TRUNC);
+ if (mFd >= 0) {
+ mInitCheck = OK;
+ }
}
MPEG4Writer::MPEG4Writer(int fd)
- : mFile(fdopen(fd, "wb")),
- mFd(mFile == NULL? -1: fileno(mFile)),
+ : mFd(dup(fd)),
+ mInitCheck(mFd < 0? NO_INIT: OK),
mUse4ByteNalLength(true),
mUse32BitOffset(true),
mIsFileSizeLimitExplicitlyRequested(false),
@@ -240,7 +248,6 @@
mMdatOffset(0),
mEstimatedMoovBoxSize(0),
mInterleaveDurationUs(1000000) {
- CHECK(mFile != NULL);
}
MPEG4Writer::~MPEG4Writer() {
@@ -370,7 +377,7 @@
}
status_t MPEG4Writer::start(MetaData *param) {
- if (mFile == NULL) {
+ if (mInitCheck != OK) {
return UNKNOWN_ERROR;
}
@@ -493,7 +500,7 @@
}
status_t MPEG4Writer::pause() {
- if (mFile == NULL) {
+ if (mInitCheck != OK) {
return OK;
}
mPaused = true;
@@ -577,7 +584,7 @@
status_t MPEG4Writer::stop() {
- if (mFile == NULL) {
+ if (mInitCheck != OK) {
return OK;
}
@@ -600,9 +607,9 @@
// Do not write out movie header on error.
if (err != OK) {
- fflush(mFile);
- fclose(mFile);
- mFile = NULL;
+ close(mFd);
+ mFd = -1;
+ mInitCheck = NO_INIT;
mStarted = false;
return err;
}
@@ -665,7 +672,7 @@
// Moov box
lseek64(mFd, mFreeBoxOffset, SEEK_SET);
mOffset = mFreeBoxOffset;
- write(mMoovBoxBuffer, 1, mMoovBoxBufferOffset, mFile);
+ write(mMoovBoxBuffer, 1, mMoovBoxBufferOffset);
// Free box
lseek64(mFd, mOffset, SEEK_SET);
@@ -682,9 +689,9 @@
CHECK(mBoxes.empty());
- fflush(mFile);
- fclose(mFile);
- mFile = NULL;
+ close(mFd);
+ mFd = -1;
+ mInitCheck = NO_INIT;
mStarted = false;
return err;
}
@@ -763,20 +770,21 @@
}
size_t MPEG4Writer::write(
- const void *ptr, size_t size, size_t nmemb, FILE *stream) {
+ const void *ptr, size_t size, size_t nmemb) {
const size_t bytes = size * nmemb;
- int fd = fileno(stream);
if (mWriteMoovBoxToMemory) {
+ // This happens only when we write the moov box at the end of
+ // recording, not for each output video/audio frame we receive.
off64_t moovBoxSize = 8 + mMoovBoxBufferOffset + bytes;
if (moovBoxSize > mEstimatedMoovBoxSize) {
for (List<off64_t>::iterator it = mBoxes.begin();
it != mBoxes.end(); ++it) {
(*it) += mOffset;
}
- lseek64(fd, mOffset, SEEK_SET);
- ::write(fd, mMoovBoxBuffer, mMoovBoxBufferOffset);
- ::write(fd, ptr, size * nmemb);
+ lseek64(mFd, mOffset, SEEK_SET);
+ ::write(mFd, mMoovBoxBuffer, mMoovBoxBufferOffset);
+ ::write(mFd, ptr, size * nmemb);
mOffset += (bytes + mMoovBoxBufferOffset);
free(mMoovBoxBuffer);
mMoovBoxBuffer = NULL;
@@ -788,7 +796,7 @@
mMoovBoxBufferOffset += bytes;
}
} else {
- ::write(fd, ptr, size * nmemb);
+ ::write(mFd, ptr, size * nmemb);
mOffset += bytes;
}
return bytes;
@@ -822,36 +830,36 @@
}
void MPEG4Writer::writeInt8(int8_t x) {
- write(&x, 1, 1, mFile);
+ write(&x, 1, 1);
}
void MPEG4Writer::writeInt16(int16_t x) {
x = htons(x);
- write(&x, 1, 2, mFile);
+ write(&x, 1, 2);
}
void MPEG4Writer::writeInt32(int32_t x) {
x = htonl(x);
- write(&x, 1, 4, mFile);
+ write(&x, 1, 4);
}
void MPEG4Writer::writeInt64(int64_t x) {
x = hton64(x);
- write(&x, 1, 8, mFile);
+ write(&x, 1, 8);
}
void MPEG4Writer::writeCString(const char *s) {
size_t n = strlen(s);
- write(s, 1, n + 1, mFile);
+ write(s, 1, n + 1);
}
void MPEG4Writer::writeFourcc(const char *s) {
CHECK_EQ(strlen(s), 4);
- write(s, 1, 4, mFile);
+ write(s, 1, 4);
}
void MPEG4Writer::write(const void *data, size_t size) {
- write(data, 1, size, mFile);
+ write(data, 1, size);
}
bool MPEG4Writer::isFileStreamable() const {
diff --git a/media/libstagefright/NuCachedSource2.cpp b/media/libstagefright/NuCachedSource2.cpp
index 7f765ca..829ab20 100644
--- a/media/libstagefright/NuCachedSource2.cpp
+++ b/media/libstagefright/NuCachedSource2.cpp
@@ -465,7 +465,7 @@
return OK;
}
- LOGI("new range: offset= %ld", offset);
+ LOGI("new range: offset= %lld", offset);
mCacheOffset = offset;
diff --git a/media/libstagefright/NuHTTPDataSource.cpp b/media/libstagefright/NuHTTPDataSource.cpp
index 15c9ac6..269b233 100644
--- a/media/libstagefright/NuHTTPDataSource.cpp
+++ b/media/libstagefright/NuHTTPDataSource.cpp
@@ -178,7 +178,7 @@
}
if (IsRedirectStatusCode(httpStatus)) {
- string value;
+ AString value;
CHECK(mHTTP.find_header_value("Location", &value));
mState = DISCONNECTED;
@@ -198,9 +198,8 @@
mHasChunkedTransferEncoding = false;
{
- string value;
- if (mHTTP.find_header_value("Transfer-Encoding", &value)
- || mHTTP.find_header_value("Transfer-encoding", &value)) {
+ AString value;
+ if (mHTTP.find_header_value("Transfer-Encoding", &value)) {
// We don't currently support any transfer encodings but
// chunked.
@@ -222,9 +221,9 @@
applyTimeoutResponse();
if (offset == 0) {
- string value;
+ AString value;
unsigned long x;
- if (mHTTP.find_header_value(string("Content-Length"), &value)
+ if (mHTTP.find_header_value(AString("Content-Length"), &value)
&& ParseSingleUnsignedLong(value.c_str(), &x)) {
mContentLength = (off64_t)x;
mContentLengthValid = true;
@@ -239,9 +238,9 @@
return ERROR_UNSUPPORTED;
}
- string value;
+ AString value;
unsigned long x;
- if (mHTTP.find_header_value(string("Content-Range"), &value)) {
+ if (mHTTP.find_header_value(AString("Content-Range"), &value)) {
const char *slashPos = strchr(value.c_str(), '/');
if (slashPos != NULL
&& ParseSingleUnsignedLong(slashPos + 1, &x)) {
@@ -439,7 +438,7 @@
}
void NuHTTPDataSource::applyTimeoutResponse() {
- string timeout;
+ AString timeout;
if (mHTTP.find_header_value("X-SocketTimeout", &timeout)) {
const char *s = timeout.c_str();
char *end;
diff --git a/media/libstagefright/ShoutcastSource.cpp b/media/libstagefright/ShoutcastSource.cpp
index 23b7681..783f2d0 100644
--- a/media/libstagefright/ShoutcastSource.cpp
+++ b/media/libstagefright/ShoutcastSource.cpp
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-#include "include/stagefright_string.h"
#include "include/HTTPStream.h"
#include <stdlib.h>
@@ -34,7 +33,7 @@
mBytesUntilMetaData(0),
mGroup(NULL),
mStarted(false) {
- string metaint;
+ AString metaint;
if (mHttp->find_header_value("icy-metaint", &metaint)) {
char *end;
const char *start = metaint.c_str();
diff --git a/media/libstagefright/StagefrightMediaScanner.cpp b/media/libstagefright/StagefrightMediaScanner.cpp
index 0f6af28..86e0e73 100644
--- a/media/libstagefright/StagefrightMediaScanner.cpp
+++ b/media/libstagefright/StagefrightMediaScanner.cpp
@@ -127,10 +127,11 @@
|| !strcasecmp(extension, ".rtttl")
|| !strcasecmp(extension, ".rtx")
|| !strcasecmp(extension, ".ota")) {
- return HandleMIDI(path, &client);
- }
-
- if (mRetriever->setDataSource(path) == OK
+ status_t status = HandleMIDI(path, &client);
+ if (status != OK) {
+ return status;
+ }
+ } else if (mRetriever->setDataSource(path) == OK
&& mRetriever->setMode(
METADATA_MODE_METADATA_RETRIEVAL_ONLY) == OK) {
const char *value;
diff --git a/media/libstagefright/include/HTTPStream.h b/media/libstagefright/include/HTTPStream.h
index 793798f..545cd0c 100644
--- a/media/libstagefright/include/HTTPStream.h
+++ b/media/libstagefright/include/HTTPStream.h
@@ -18,10 +18,9 @@
#define HTTP_STREAM_H_
-#include "stagefright_string.h"
-
#include <sys/types.h>
+#include <media/stagefright/foundation/AString.h>
#include <media/stagefright/MediaErrors.h>
#include <utils/KeyedVector.h>
#include <utils/threads.h>
@@ -50,7 +49,7 @@
static const char *kStatusKey;
bool find_header_value(
- const string &key, string *value) const;
+ const AString &key, AString *value) const;
// Pass a negative value to disable the timeout.
void setReceiveTimeout(int seconds);
@@ -70,7 +69,7 @@
Mutex mLock;
int mSocket;
- KeyedVector<string, string> mHeaders;
+ KeyedVector<AString, AString> mHeaders;
HTTPStream(const HTTPStream &);
HTTPStream &operator=(const HTTPStream &);
diff --git a/media/libstagefright/include/stagefright_string.h b/media/libstagefright/include/stagefright_string.h
deleted file mode 100644
index 5dc7116..0000000
--- a/media/libstagefright/include/stagefright_string.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2009 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 STRING_H_
-
-#define STRING_H_
-
-#include <utils/String8.h>
-
-namespace android {
-
-class string {
-public:
- typedef size_t size_type;
- static size_type npos;
-
- string();
- string(const char *s);
- string(const char *s, size_t length);
- string(const string &from, size_type start, size_type length = npos);
-
- const char *c_str() const;
- size_type size() const;
-
- void clear();
- void erase(size_type from, size_type length);
-
- size_type find(char c) const;
-
- bool operator<(const string &other) const;
- bool operator==(const string &other) const;
-
- string &operator+=(char c);
-
-private:
- String8 mString;
-};
-
-} // namespace android
-
-#endif // STRING_H_
diff --git a/media/libstagefright/rtsp/ARTPWriter.cpp b/media/libstagefright/rtsp/ARTPWriter.cpp
index 155fd96..5a033e1 100644
--- a/media/libstagefright/rtsp/ARTPWriter.cpp
+++ b/media/libstagefright/rtsp/ARTPWriter.cpp
@@ -46,7 +46,7 @@
ARTPWriter::ARTPWriter(int fd)
: mFlags(0),
- mFd(fd),
+ mFd(dup(fd)),
mLooper(new ALooper),
mReflector(new AHandlerReflector<ARTPWriter>(this)) {
CHECK_GE(fd, 0);
diff --git a/media/libstagefright/string.cpp b/media/libstagefright/string.cpp
deleted file mode 100644
index 8b2c36c..0000000
--- a/media/libstagefright/string.cpp
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2009 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 "include/stagefright_string.h"
-
-#include <media/stagefright/MediaDebug.h>
-
-namespace android {
-
-// static
-string::size_type string::npos = (string::size_type)-1;
-
-string::string() {
-}
-
-string::string(const char *s, size_t length)
- : mString(s, length) {
-}
-
-string::string(const string &from, size_type start, size_type length) {
- CHECK(start <= from.size());
- if (length == npos) {
- length = from.size() - start;
- } else {
- CHECK(start + length <= from.size());
- }
-
- mString.setTo(from.c_str() + start, length);
-}
-
-string::string(const char *s)
- : mString(s) {
-}
-
-const char *string::c_str() const {
- return mString.string();
-}
-
-string::size_type string::size() const {
- return mString.length();
-}
-
-void string::clear() {
- mString = String8();
-}
-
-string::size_type string::find(char c) const {
- char s[2];
- s[0] = c;
- s[1] = '\0';
-
- ssize_t index = mString.find(s);
-
- return index < 0 ? npos : (size_type)index;
-}
-
-bool string::operator<(const string &other) const {
- return mString < other.mString;
-}
-
-bool string::operator==(const string &other) const {
- return mString == other.mString;
-}
-
-string &string::operator+=(char c) {
- mString.append(&c, 1);
-
- return *this;
-}
-
-void string::erase(size_t from, size_t length) {
- String8 s(mString.string(), from);
- s.append(mString.string() + from + length);
-
- mString = s;
-}
-
-} // namespace android
-
diff --git a/media/mtp/MtpDataPacket.cpp b/media/mtp/MtpDataPacket.cpp
index 20dd94d..e1d1a92 100644
--- a/media/mtp/MtpDataPacket.cpp
+++ b/media/mtp/MtpDataPacket.cpp
@@ -413,20 +413,32 @@
}
int MtpDataPacket::readData(struct usb_endpoint *ep, void* buffer, int length) {
- int packetSize = usb_endpoint_max_packet(ep);
int read = 0;
while (read < length) {
- int ret = transfer(ep, (char *)buffer + read, packetSize);
+ int ret = transfer(ep, (char *)buffer + read, length - read);
if (ret < 0) {
-printf("MtpDataPacket::readData returning %d\n", ret);
return ret;
}
read += ret;
}
-printf("MtpDataPacket::readData returning %d\n", read);
return read;
}
+// Queue a read request. Call readDataWait to wait for result
+int MtpDataPacket::readDataAsync(struct usb_endpoint *ep, void* buffer, int length) {
+ if (usb_endpoint_queue(ep, buffer, length)) {
+ LOGE("usb_endpoint_queue failed, errno: %d", errno);
+ return -1;
+ }
+ return 0;
+}
+
+// Wait for result of readDataAsync
+int MtpDataPacket::readDataWait(struct usb_endpoint *ep) {
+ int ep_num;
+ return usb_endpoint_wait(usb_endpoint_get_device(ep), &ep_num);
+}
+
int MtpDataPacket::readDataHeader(struct usb_endpoint *ep) {
int length = transfer(ep, mBuffer, usb_endpoint_max_packet(ep));
if (length >= 0)
@@ -454,15 +466,7 @@
}
int MtpDataPacket::write(struct usb_endpoint *ep, void* buffer, uint32_t length) {
- int ret = 0;
- int packetSize = usb_endpoint_max_packet(ep);
- while (length > 0) {
- int write = (length > packetSize ? packetSize : length);
- int ret = transfer(ep, buffer, write);
- if (ret < 0)
- break;
- length -= ret;
- }
+ int ret = transfer(ep, buffer, length);
return (ret < 0 ? ret : 0);
}
diff --git a/media/mtp/MtpDataPacket.h b/media/mtp/MtpDataPacket.h
index fab6a07..3ae6226 100644
--- a/media/mtp/MtpDataPacket.h
+++ b/media/mtp/MtpDataPacket.h
@@ -102,6 +102,8 @@
#ifdef MTP_HOST
int read(struct usb_endpoint *ep);
int readData(struct usb_endpoint *ep, void* buffer, int length);
+ int readDataAsync(struct usb_endpoint *ep, void* buffer, int length);
+ int readDataWait(struct usb_endpoint *ep);
int readDataHeader(struct usb_endpoint *ep);
int writeDataHeader(struct usb_endpoint *ep, uint32_t length);
@@ -110,6 +112,7 @@
#endif
inline bool hasData() const { return mPacketSize > MTP_CONTAINER_HEADER_SIZE; }
+ inline uint32_t getContainerLength() const { return MtpPacket::getUInt32(MTP_CONTAINER_LENGTH_OFFSET); }
void* getData(int& outLength) const;
};
diff --git a/media/mtp/MtpDevice.cpp b/media/mtp/MtpDevice.cpp
index fca0142..416ebfe 100644
--- a/media/mtp/MtpDevice.cpp
+++ b/media/mtp/MtpDevice.cpp
@@ -348,97 +348,96 @@
return NULL;
}
-class ReadObjectThread : public Thread {
-private:
- MtpDevice* mDevice;
- MtpObjectHandle mHandle;
- int mObjectSize;
- void* mInitialData;
- int mInitialDataLength;
- int mFD;
-
-public:
- ReadObjectThread(MtpDevice* device, MtpObjectHandle handle, int objectSize)
- : mDevice(device),
- mHandle(handle),
- mObjectSize(objectSize),
- mInitialData(NULL),
- mInitialDataLength(0)
- {
- }
-
- virtual ~ReadObjectThread() {
- if (mFD >= 0)
- close(mFD);
- free(mInitialData);
- }
-
- // returns file descriptor
- int init() {
- mDevice->mRequest.reset();
- mDevice->mRequest.setParameter(1, mHandle);
- if (mDevice->sendRequest(MTP_OPERATION_GET_OBJECT)
- && mDevice->mData.readDataHeader(mDevice->mEndpointIn)) {
-
- // mData will contain header and possibly the beginning of the object data
- mInitialData = mDevice->mData.getData(mInitialDataLength);
-
- // create a pipe for the client to read from
- int pipefd[2];
- if (pipe(pipefd) < 0) {
- LOGE("pipe failed (%s)", strerror(errno));
- return -1;
- }
-
- mFD = pipefd[1];
- return pipefd[0];
- } else {
- return -1;
- }
- }
-
- virtual bool threadLoop() {
- int remaining = mObjectSize;
- if (mInitialData) {
- write(mFD, mInitialData, mInitialDataLength);
- remaining -= mInitialDataLength;
- free(mInitialData);
- mInitialData = NULL;
- }
-
- char buffer[16384];
- while (remaining > 0) {
- int readSize = (remaining > sizeof(buffer) ? sizeof(buffer) : remaining);
- int count = mDevice->mData.readData(mDevice->mEndpointIn, buffer, readSize);
- int written;
- if (count >= 0) {
- int written = write(mFD, buffer, count);
- // FIXME check error
- remaining -= count;
- } else {
- break;
- }
- }
-
- MtpResponseCode ret = mDevice->readResponse();
- mDevice->mMutex.unlock();
+// reads the object's data and writes it to the specified file path
+bool MtpDevice::readObject(MtpObjectHandle handle, const char* destPath, int group, int perm) {
+ LOGD("readObject: %s", destPath);
+ int fd = ::open(destPath, O_RDWR | O_CREAT | O_TRUNC);
+ if (fd < 0) {
+ LOGE("open failed for %s", destPath);
return false;
}
-};
- // returns the file descriptor for a pipe to read the object's data
-int MtpDevice::readObject(MtpObjectHandle handle, int objectSize) {
- mMutex.lock();
+ fchown(fd, getuid(), group);
+ // set permissions
+ int mask = umask(0);
+ fchmod(fd, perm);
+ umask(mask);
- ReadObjectThread* thread = new ReadObjectThread(this, handle, objectSize);
- int fd = thread->init();
- if (fd < 0) {
- delete thread;
- mMutex.unlock();
- } else {
- thread->run("ReadObjectThread");
+ Mutex::Autolock autoLock(mMutex);
+ bool result = false;
+
+ mRequest.reset();
+ mRequest.setParameter(1, handle);
+ if (sendRequest(MTP_OPERATION_GET_OBJECT)
+ && mData.readDataHeader(mEndpointIn)) {
+ uint32_t length = mData.getContainerLength();
+ if (length < MTP_CONTAINER_HEADER_SIZE)
+ goto fail;
+ length -= MTP_CONTAINER_HEADER_SIZE;
+ uint32_t remaining = length;
+
+ int initialDataLength = 0;
+ void* initialData = mData.getData(initialDataLength);
+ if (initialData) {
+ if (initialDataLength > 0) {
+ if (write(fd, initialData, initialDataLength) != initialDataLength)
+ goto fail;
+ remaining -= initialDataLength;
+ }
+ free(initialData);
+ }
+
+ // USB reads greater than 16K don't work
+ char buffer1[16384], buffer2[16384];
+ char* readBuffer = buffer1;
+ char* writeBuffer = NULL;
+ int writeLength = 0;
+
+ while (remaining > 0 || writeBuffer) {
+ if (remaining > 0) {
+ // queue up a read request
+ int readSize = (remaining > sizeof(buffer1) ? sizeof(buffer1) : remaining);
+ if (mData.readDataAsync(mEndpointIn, readBuffer, readSize)) {
+ LOGE("readDataAsync failed");
+ goto fail;
+ }
+ } else {
+ readBuffer = NULL;
+ }
+
+ if (writeBuffer) {
+ // write previous buffer
+ if (write(fd, writeBuffer, writeLength) != writeLength) {
+ LOGE("write failed");
+ // wait for pending read before failing
+ if (readBuffer)
+ mData.readDataWait(mEndpointIn);
+ goto fail;
+ }
+ writeBuffer = NULL;
+ }
+
+ // wait for read to complete
+ if (readBuffer) {
+ int read = mData.readDataWait(mEndpointIn);
+ if (read < 0)
+ goto fail;
+
+ writeBuffer = readBuffer;
+ writeLength = read;
+ remaining -= read;
+ readBuffer = (readBuffer == buffer1 ? buffer2 : buffer1);
+ }
+ }
+
+ MtpResponseCode response = readResponse();
+ if (response == MTP_RESPONSE_OK)
+ result = true;
}
- return fd;
+
+fail:
+ ::close(fd);
+ return result;
}
bool MtpDevice::sendRequest(MtpOperationCode operation) {
diff --git a/media/mtp/MtpDevice.h b/media/mtp/MtpDevice.h
index 57f492f..21c85d5 100644
--- a/media/mtp/MtpDevice.h
+++ b/media/mtp/MtpDevice.h
@@ -75,7 +75,8 @@
MtpDeviceInfo* getDeviceInfo();
MtpStorageIDList* getStorageIDs();
MtpStorageInfo* getStorageInfo(MtpStorageID storageID);
- MtpObjectHandleList* getObjectHandles(MtpStorageID storageID, MtpObjectFormat format, MtpObjectHandle parent);
+ MtpObjectHandleList* getObjectHandles(MtpStorageID storageID, MtpObjectFormat format,
+ MtpObjectHandle parent);
MtpObjectInfo* getObjectInfo(MtpObjectHandle handle);
void* getThumbnail(MtpObjectHandle handle, int& outLength);
MtpObjectHandle sendObjectInfo(MtpObjectInfo* info);
@@ -86,12 +87,10 @@
MtpProperty* getDevicePropDesc(MtpDeviceProperty code);
- // returns the file descriptor for a pipe to read the object's data
- int readObject(MtpObjectHandle handle, int objectSize);
+ bool readObject(MtpObjectHandle handle, const char* destPath, int group,
+ int perm);
private:
- friend class ReadObjectThread;
-
bool sendRequest(MtpOperationCode operation);
bool sendData();
bool readData();
diff --git a/media/tests/CameraBrowser/Android.mk b/media/tests/CameraBrowser/Android.mk
index 1d81129..295b3e6 100644
--- a/media/tests/CameraBrowser/Android.mk
+++ b/media/tests/CameraBrowser/Android.mk
@@ -1,7 +1,7 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := tests
+LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/media/tests/CameraBrowser/AndroidManifest.xml b/media/tests/CameraBrowser/AndroidManifest.xml
index eae0b01..db4a336 100644
--- a/media/tests/CameraBrowser/AndroidManifest.xml
+++ b/media/tests/CameraBrowser/AndroidManifest.xml
@@ -1,8 +1,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.camerabrowser">
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
-
<application android:label="@string/app_label">
<activity android:name="CameraBrowser" android:label="Camera Browser">
<intent-filter>
diff --git a/media/tests/CameraBrowser/res/layout/object_info.xml b/media/tests/CameraBrowser/res/layout/object_info.xml
index ac210b9..a0499f2 100644
--- a/media/tests/CameraBrowser/res/layout/object_info.xml
+++ b/media/tests/CameraBrowser/res/layout/object_info.xml
@@ -153,5 +153,17 @@
<TableRow>
<ImageView android:id="@+id/thumbnail" />
</TableRow>
+ <TableRow>
+ <Button android:id="@+id/import_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/import_label">
+ </Button>
+ <Button android:id="@+id/delete_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/delete_label">
+ </Button>
+ </TableRow>
</TableLayout>
diff --git a/media/tests/CameraBrowser/res/menu/object_menu.xml b/media/tests/CameraBrowser/res/menu/object_menu.xml
deleted file mode 100644
index a0865f0..0000000
--- a/media/tests/CameraBrowser/res/menu/object_menu.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2010 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.
--->
-
-<menu xmlns:android="http://schemas.android.com/apk/res/android">
-
- <item android:id="@+id/save"
- android:title="@string/save_item" />
- <item android:id="@+id/delete"
- android:title="@string/delete_item" />
-</menu>
diff --git a/media/tests/CameraBrowser/res/values/strings.xml b/media/tests/CameraBrowser/res/values/strings.xml
index cd477f1..7955773 100644
--- a/media/tests/CameraBrowser/res/values/strings.xml
+++ b/media/tests/CameraBrowser/res/values/strings.xml
@@ -32,9 +32,9 @@
<string name="modified_label">Modified: </string>
<string name="keywords_label">Keywords: </string>
- <!-- menu items -->
- <string name="save_item">Save</string>
- <string name="delete_item">Delete</string>
+ <!-- button labels -->
+ <string name="import_label">Import</string>
+ <string name="delete_label">Delete</string>
<!-- toasts -->
<string name="object_saved_message">Object saved</string>
diff --git a/media/tests/CameraBrowser/src/com/android/camerabrowser/DeviceDisconnectedReceiver.java b/media/tests/CameraBrowser/src/com/android/camerabrowser/DeviceDisconnectedReceiver.java
new file mode 100644
index 0000000..fb004c4
--- /dev/null
+++ b/media/tests/CameraBrowser/src/com/android/camerabrowser/DeviceDisconnectedReceiver.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2010 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.camerabrowser;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.hardware.Usb;
+import android.net.Uri;
+
+public class DeviceDisconnectedReceiver extends BroadcastReceiver {
+
+ private final Activity mActivity;
+ private final int mDeviceID;
+
+ public DeviceDisconnectedReceiver(Activity activity, int deviceID) {
+ mActivity = activity;
+ mDeviceID = deviceID;
+
+ IntentFilter filter = new IntentFilter(Usb.ACTION_USB_CAMERA_DETACHED);
+ filter.addDataScheme("content");
+ activity.registerReceiver(this, filter);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // close our activity if the device it is displaying is disconnected
+ Uri uri = intent.getData();
+ int id = Integer.parseInt(uri.getPathSegments().get(1));
+ if (id == mDeviceID) {
+ mActivity.finish();
+ }
+ }
+}
\ No newline at end of file
diff --git a/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectBrowser.java b/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectBrowser.java
index 6d34fd4..2060657 100644
--- a/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectBrowser.java
+++ b/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectBrowser.java
@@ -46,6 +46,7 @@
private int mDeviceID;
private long mStorageID;
private long mObjectID;
+ private DeviceDisconnectedReceiver mDisconnectedReceiver;
private static final String[] OBJECT_COLUMNS =
new String[] { Mtp.Object._ID, Mtp.Object.NAME, Mtp.Object.FORMAT, Mtp.Object.THUMB };
@@ -58,15 +59,17 @@
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+
+ mDeviceID = getIntent().getIntExtra("device", 0);
+ mStorageID = getIntent().getLongExtra("storage", 0);
+ mObjectID = getIntent().getLongExtra("object", 0);
+ mDisconnectedReceiver = new DeviceDisconnectedReceiver(this, mDeviceID);
}
@Override
protected void onResume() {
super.onResume();
- mDeviceID = getIntent().getIntExtra("device", 0);
- mStorageID = getIntent().getLongExtra("storage", 0);
- mObjectID = getIntent().getLongExtra("object", 0);
if (mDeviceID != 0 && mStorageID != 0) {
Cursor c;
Uri uri;
@@ -87,6 +90,12 @@
}
@Override
+ protected void onDestroy() {
+ unregisterReceiver(mDisconnectedReceiver);
+ super.onDestroy();
+ }
+
+ @Override
protected void onListItemClick(ListView l, View v, int position, long id) {
long rowID = mAdapter.getItemId(position);
Cursor c = getContentResolver().query(
diff --git a/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectViewer.java b/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectViewer.java
index 9f2f98e..4e63128 100644
--- a/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectViewer.java
+++ b/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectViewer.java
@@ -16,6 +16,7 @@
package com.android.camerabrowser;
import android.app.Activity;
+import android.content.ContentValues;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
@@ -23,36 +24,31 @@
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
-import android.os.FileUtils;
-import android.os.ParcelFileDescriptor;
-import android.os.Process;
import android.provider.Mtp;
import android.util.Log;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
import android.view.View;
+import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import java.io.File;
-import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.util.Calendar;
import java.util.Date;
/**
* A view to display the properties of an object.
*/
-public class ObjectViewer extends Activity {
+public class ObjectViewer extends Activity implements View.OnClickListener {
private static final String TAG = "ObjectViewer";
private int mDeviceID;
private long mStorageID;
private long mObjectID;
+ private String mFileName;
+ private Button mImportButton;
+ private Button mDeleteButton;
+ private DeviceDisconnectedReceiver mDisconnectedReceiver;
private static final String[] OBJECT_COLUMNS =
new String[] { Mtp.Object._ID,
@@ -77,23 +73,30 @@
super.onCreate(savedInstanceState);
setContentView(R.layout.object_info);
+
+ mImportButton = (Button)findViewById(R.id.import_button);
+ mImportButton.setOnClickListener(this);
+ mDeleteButton = (Button)findViewById(R.id.delete_button);
+ mDeleteButton.setOnClickListener(this);
+
+ mDeviceID = getIntent().getIntExtra("device", 0);
+ mStorageID = getIntent().getLongExtra("storage", 0);
+ mObjectID = getIntent().getLongExtra("object", 0);
+ mDisconnectedReceiver = new DeviceDisconnectedReceiver(this, mDeviceID);
}
@Override
protected void onResume() {
super.onResume();
- mDeviceID = getIntent().getIntExtra("device", 0);
- mStorageID = getIntent().getLongExtra("storage", 0);
- mObjectID = getIntent().getLongExtra("object", 0);
-
if (mDeviceID != 0 && mObjectID != 0) {
Cursor c = getContentResolver().query(
Mtp.Object.getContentUri(mDeviceID, mObjectID),
OBJECT_COLUMNS, null, null, null);
c.moveToFirst();
TextView view = (TextView)findViewById(R.id.name);
- view.setText(c.getString(1));
+ mFileName = c.getString(1);
+ view.setText(mFileName);
view = (TextView)findViewById(R.id.size);
view.setText(Long.toString(c.getLong(2)));
view = (TextView)findViewById(R.id.thumb_width);
@@ -132,113 +135,33 @@
}
@Override
- public boolean onCreateOptionsMenu(Menu menu) {
- MenuInflater inflater = getMenuInflater();
- inflater.inflate(R.menu.object_menu, menu);
- return true;
+ protected void onDestroy() {
+ unregisterReceiver(mDisconnectedReceiver);
+ super.onDestroy();
}
- @Override
- public boolean onPrepareOptionsMenu(Menu menu) {
- MenuItem item = menu.findItem(R.id.save);
- item.setEnabled(true);
- item = menu.findItem(R.id.delete);
- item.setEnabled(true);
- return true;
- }
+ private void importObject() {
+ // copy file to /mnt/sdcard/imported/<filename>
+ File dest = Environment.getExternalStorageDirectory();
+ dest = new File(dest, "imported");
+ dest.mkdirs();
+ dest = new File(dest, mFileName);
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.save:
- save();
- return true;
- case R.id.delete:
- delete();
- return true;
- }
- return false;
- }
+ Uri requestUri = Mtp.Object.getContentUriForImport(mDeviceID, mObjectID,
+ dest.getAbsolutePath());
+ Uri resultUri = getContentResolver().insert(requestUri, new ContentValues());
+ Log.d(TAG, "save returned " + resultUri);
- private static String getTimestamp() {
- Calendar c = Calendar.getInstance();
- c.setTimeInMillis(System.currentTimeMillis());
- return String.format("%tY-%tm-%td-%tH-%tM-%tS", c, c, c, c, c, c);
- }
-
- private void save() {
- boolean success = false;
- Uri uri = Mtp.Object.getContentUri(mDeviceID, mObjectID);
- File destFile = null;
- ParcelFileDescriptor pfd = null;
- FileInputStream fis = null;
- FileOutputStream fos = null;
-
- try {
- pfd = getContentResolver().openFileDescriptor(uri, "r");
- Log.d(TAG, "save got pfd " + pfd);
- if (pfd != null) {
- fis = new FileInputStream(pfd.getFileDescriptor());
- Log.d(TAG, "save got fis " + fis);
- File destDir = Environment.getExternalStoragePublicDirectory(
- Environment.DIRECTORY_DCIM);
- destDir.mkdirs();
- destFile = new File(destDir, "CameraBrowser-" + getTimestamp() + ".jpeg");
-
-
- Log.d(TAG, "save got destFile " + destFile);
-
- if (destFile.exists()) {
- destFile.delete();
- }
- fos = new FileOutputStream(destFile);
-
- byte[] buffer = new byte[65536];
- int bytesRead;
- while ((bytesRead = fis.read(buffer)) >= 0) {
- fos.write(buffer, 0, bytesRead);
- }
-
- // temporary workaround until we straighten out permissions in /data/media
- FileUtils.setPermissions(destDir.getPath(), 0775, Process.myUid(), Process.SDCARD_RW_GID);
- FileUtils.setPermissions(destFile.getPath(), 0664, Process.myUid(), Process.SDCARD_RW_GID);
-
- success = true;
- }
- } catch (Exception e) {
- Log.e(TAG, "Exception in ObjectView.save", e);
- } finally {
- if (fis != null) {
- try {
- fis.close();
- } catch (Exception e) {
- }
- }
- if (fos != null) {
- try {
- fos.close();
- } catch (Exception e) {
- }
- }
- if (pfd != null) {
- try {
- pfd.close();
- } catch (Exception e) {
- }
- }
- }
-
- if (success) {
- Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
- intent.setData(Uri.fromFile(destFile));
- sendBroadcast(intent);
+ if (resultUri != null) {
Toast.makeText(this, R.string.object_saved_message, Toast.LENGTH_SHORT).show();
+ Intent intent = new Intent(Intent.ACTION_VIEW, resultUri);
+ startActivity(intent);
} else {
Toast.makeText(this, R.string.save_failed_message, Toast.LENGTH_SHORT).show();
}
}
- private void delete() {
+ private void deleteObject() {
Uri uri = Mtp.Object.getContentUri(mDeviceID, mObjectID);
Log.d(TAG, "deleting " + uri);
@@ -251,4 +174,12 @@
Toast.makeText(this, R.string.delete_failed_message, Toast.LENGTH_SHORT).show();
}
}
+
+ public void onClick(View v) {
+ if (v == mImportButton) {
+ importObject();
+ } else if (v == mDeleteButton) {
+ deleteObject();
+ }
+ }
}
diff --git a/media/tests/CameraBrowser/src/com/android/camerabrowser/StorageBrowser.java b/media/tests/CameraBrowser/src/com/android/camerabrowser/StorageBrowser.java
index 63e036e..4da88d6 100644
--- a/media/tests/CameraBrowser/src/com/android/camerabrowser/StorageBrowser.java
+++ b/media/tests/CameraBrowser/src/com/android/camerabrowser/StorageBrowser.java
@@ -37,6 +37,7 @@
private ListAdapter mAdapter;
private int mDeviceID;
+ private DeviceDisconnectedReceiver mDisconnectedReceiver;
private static final String[] STORAGE_COLUMNS =
new String[] { Mtp.Storage._ID, Mtp.Storage.DESCRIPTION };
@@ -44,13 +45,14 @@
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ mDeviceID = getIntent().getIntExtra("device", 0);
+ mDisconnectedReceiver = new DeviceDisconnectedReceiver(this, mDeviceID);
}
@Override
protected void onResume() {
super.onResume();
- mDeviceID = getIntent().getIntExtra("device", 0);
if (mDeviceID != 0) {
Cursor c = getContentResolver().query(Mtp.Storage.getContentUri(mDeviceID),
STORAGE_COLUMNS, null, null, null);
@@ -67,6 +69,12 @@
}
@Override
+ protected void onDestroy() {
+ unregisterReceiver(mDisconnectedReceiver);
+ super.onDestroy();
+ }
+
+ @Override
protected void onListItemClick(ListView l, View v, int position, long id) {
Intent intent = new Intent(this, ObjectBrowser.class);
intent.putExtra("device", mDeviceID);
diff --git a/media/tests/mtp/Android.mk b/media/tests/mtp/Android.mk
deleted file mode 100644
index a9074ed..0000000
--- a/media/tests/mtp/Android.mk
+++ /dev/null
@@ -1,61 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-ifneq ($(TARGET_SIMULATOR),true)
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
- mtp.cpp \
- MtpFile.cpp \
-
-LOCAL_C_INCLUDES += \
- frameworks/base/media/mtp \
-
-LOCAL_CFLAGS := -DMTP_HOST
-
-LOCAL_MODULE := mtp
-
-LOCAL_STATIC_LIBRARIES := libmtp libusbhost libutils libcutils
-
-include $(BUILD_EXECUTABLE)
-
-endif
-
-ifeq ($(HOST_OS),linux)
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
- mtp.cpp \
- MtpFile.cpp \
- ../../../libs/utils/RefBase.cpp \
- ../../../libs/utils/SharedBuffer.cpp \
- ../../../libs/utils/Threads.cpp \
- ../../../libs/utils/VectorImpl.cpp \
-
-LOCAL_C_INCLUDES += \
- frameworks/base/media/mtp \
-
-LOCAL_CFLAGS := -DMTP_HOST -g -O0
-
-have_readline := $(wildcard /usr/include/readline/readline.h)
-have_history := $(wildcard /usr/lib/libhistory*)
-ifneq ($(strip $(have_readline)),)
-LOCAL_CFLAGS += -DHAVE_READLINE=1
-endif
-
-LOCAL_LDLIBS += -lpthread
-ifneq ($(strip $(have_readline)),)
-LOCAL_LDLIBS += -lreadline -lncurses
-endif
-ifneq ($(strip $(have_history)),)
-LOCAL_LDLIBS += -lhistory
-endif
-
-LOCAL_MODULE := mtp
-
-LOCAL_STATIC_LIBRARIES := libmtp libusbhost libcutils
-
-include $(BUILD_HOST_EXECUTABLE)
-
-endif
diff --git a/media/tests/mtp/MtpFile.cpp b/media/tests/mtp/MtpFile.cpp
deleted file mode 100644
index 00d328e..0000000
--- a/media/tests/mtp/MtpFile.cpp
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * Copyright (C) 2010 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 "MtpClient.h"
-#include "MtpDevice.h"
-#include "MtpDeviceInfo.h"
-#include "MtpObjectInfo.h"
-#include "MtpStorage.h"
-#include "MtpUtils.h"
-
-#include "MtpFile.h"
-
-namespace android {
-
-MtpClient* MtpFile::sClient = NULL;
-
-MtpFile::MtpFile(MtpDevice* device)
- : mDevice(device),
- mStorage(0),
- mHandle(0)
-{
-}
-
-MtpFile::MtpFile(MtpDevice* device, MtpStorageID storage)
- : mDevice(device),
- mStorage(storage),
- mHandle(0)
-{
-}
-
-MtpFile::MtpFile(MtpDevice* device, MtpStorageID storage, MtpObjectHandle handle)
- : mDevice(device),
- mStorage(storage),
- mHandle(handle)
-{
-}
-
-MtpFile::MtpFile(MtpFile* file)
- : mDevice(file->mDevice),
- mStorage(file->mStorage),
- mHandle(file->mHandle)
-{
-}
-
-MtpFile::~MtpFile() {
-}
-
-void MtpFile::print() {
- if (mHandle) {
-
- } else if (mStorage) {
- printf("%x\n", mStorage);
- } else {
- int id = mDevice->getID();
- MtpDeviceInfo* info = mDevice->getDeviceInfo();
- if (info)
- printf("%d\t%s %s %s\n", id, info->mManufacturer, info->mModel, info->mSerial);
- else
- printf("%d\t(no device info available)\n", id);
- delete info;
- }
-}
-
-MtpObjectInfo* MtpFile::getObjectInfo() {
- return mDevice->getObjectInfo(mHandle);
-}
-
-void MtpFile::list() {
- if (mStorage) {
- MtpObjectHandleList* handles = mDevice->getObjectHandles(mStorage, 0,
- (mHandle ? mHandle : -1));
- if (handles) {
- for (int i = 0; i < handles->size(); i++) {
- MtpObjectHandle handle = (*handles)[i];
- MtpObjectInfo* info = mDevice->getObjectInfo(handle);
- if (info) {
- char modified[100];
- struct tm tm;
-
- gmtime_r(&info->mDateModified, &tm);
- strftime(modified, sizeof(modified), "%a %b %e %H:%M:%S GMT %Y", &tm);
- printf("%s Handle: %d Format: %04X Size: %d Modified: %s\n",
- info->mName, handle, info->mFormat, info->mCompressedSize, modified);
- delete info;
- }
- }
- delete handles;
- }
- } else {
- // list storage units for device
- MtpStorageIDList* storageList = mDevice->getStorageIDs();
- for (int i = 0; i < storageList->size(); i++) {
- MtpStorageID storageID = (*storageList)[i];
- printf("%x\n", storageID);
- }
- }
-}
-
-void MtpFile::init(MtpClient* client) {
- sClient = client;
-}
-
-MtpFile* MtpFile::parsePath(MtpFile* base, char* path) {
- MtpDevice* device = NULL;
- MtpStorageID storage = 0;
- MtpObjectHandle handle = 0;
-
- if (path[0] != '/' && base) {
- device = base->mDevice;
- storage = base->mStorage;
- handle = base->mHandle;
- }
-
- // parse an absolute path
- if (path[0] == '/')
- path++;
- char* tok = strtok(path, "/");
- while (tok) {
- if (storage) {
- // find child of current handle
- MtpObjectHandleList* handles = device->getObjectHandles(storage, 0,
- (handle ? handle : -1));
- MtpObjectHandle childHandle = 0;
-
- if (handles) {
- for (int i = 0; i < handles->size() && !childHandle; i++) {
- MtpObjectHandle handle = (*handles)[i];
- MtpObjectInfo* info = device->getObjectInfo(handle);
- if (info && !strcmp(tok, info->mName))
- childHandle = handle;
- delete info;
- }
- delete handles;
- }
- if (childHandle)
- handle = childHandle;
- else
- return NULL;
- } else if (device) {
- unsigned int id;
- // find storage for the device
- if (sscanf(tok, "%x", &id) == 1) {
- MtpStorageIDList* storageList = device->getStorageIDs();
- bool found = false;
- for (int i = 0; i < storageList->size(); i++) {
- if ((*storageList)[i] == id) {
- found = true;
- break;
- }
- }
- if (found)
- storage = id;
- else
- return NULL;
- }
- } else {
- // find device
- unsigned int id;
- if (sscanf(tok, "%d", &id) == 1)
- device = sClient->getDevice(id);
- if (!device)
- return NULL;
- }
-
- tok = strtok(NULL, "/");
- }
-
- if (device)
- return new MtpFile(device, storage, handle);
- else
- return NULL;
-}
-
-}
diff --git a/media/tests/mtp/MtpFile.h b/media/tests/mtp/MtpFile.h
deleted file mode 100644
index ab8762b..0000000
--- a/media/tests/mtp/MtpFile.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2010 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 _MTP_FILE_H
-#define _MTP_FILE_H
-
-#include "MtpTypes.h"
-
-namespace android {
-
-class MtpClient;
-class MtpDevice;
-class MtpObjectInfo;
-
-// File-like abstraction for the interactive shell.
-// This can be used to represent an MTP device, storage unit or object
-// (either file or association).
-class MtpFile {
-private:
- MtpDevice* mDevice;
- MtpStorageID mStorage;
- MtpObjectHandle mHandle;
- static MtpClient* sClient;
-
-public:
- MtpFile(MtpDevice* device);
- MtpFile(MtpDevice* device, MtpStorageID storage);
- MtpFile(MtpDevice* device, MtpStorageID storage, MtpObjectHandle handle);
- MtpFile(MtpFile* file);
- virtual ~MtpFile();
-
- MtpObjectInfo* getObjectInfo();
- void print();
- void list();
-
- inline MtpDevice* getDevice() const { return mDevice; }
-
- static void init(MtpClient* client);
- static MtpFile* parsePath(MtpFile* base, char* path);
-};
-
-}
-
-#endif // _MTP_DIRECTORY_H
diff --git a/media/tests/mtp/mtp.cpp b/media/tests/mtp/mtp.cpp
deleted file mode 100644
index 3202cae..0000000
--- a/media/tests/mtp/mtp.cpp
+++ /dev/null
@@ -1,370 +0,0 @@
-/*
- * Copyright (C) 2010 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 <unistd.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-
-#if HAVE_READLINE
-#include <readline/readline.h>
-#include <readline/history.h>
-#endif
-
-#include "MtpClient.h"
-#include "MtpDevice.h"
-#include "MtpObjectInfo.h"
-
-#include "MtpFile.h"
-
-#define PROMPT "mtp> "
-
-using namespace android;
-
-static MtpClient* sClient = NULL;
-
-// current working directory information for interactive shell
-static MtpFile* sCurrentDirectory = NULL;
-
-static MtpFile* parse_path(char* path) {
- return MtpFile::parsePath(sCurrentDirectory, path);
-}
-
-class MyClient : public MtpClient {
-private:
- virtual void deviceAdded(MtpDevice *device) {
- }
-
- virtual void deviceRemoved(MtpDevice *device) {
- }
-
-public:
-};
-
-static void init() {
- sClient = new MyClient;
- sClient->start();
- MtpFile::init(sClient);
-}
-
-static int set_cwd(int argc, char* argv[]) {
- if (argc != 1) {
- fprintf(stderr, "cd should have one argument\n");
- return -1;
- }
- if (!strcmp(argv[0], "/")) {
- delete sCurrentDirectory;
- sCurrentDirectory = NULL;
- }
- else {
- MtpFile* file = parse_path(argv[0]);
- if (file) {
- delete sCurrentDirectory;
- sCurrentDirectory = file;
- } else {
- fprintf(stderr, "could not find %s\n", argv[0]);
- return -1;
- }
- }
- return 0;
-}
-
-static void list_devices() {
- // TODO - need to make sure the list will not change while iterating
- MtpDeviceList& devices = sClient->getDeviceList();
- for (int i = 0; i < devices.size(); i++) {
- MtpDevice* device = devices[i];
- MtpFile* file = new MtpFile(device);
- file->print();
- delete file;
- }
-}
-
-static int list(int argc, char* argv[]) {
- if (argc == 0) {
- // list cwd
- if (sCurrentDirectory) {
- sCurrentDirectory->list();
- } else {
- list_devices();
- }
- }
-
- for (int i = 0; i < argc; i++) {
- char* path = argv[i];
- if (!strcmp(path, "/")) {
- list_devices();
- } else {
- MtpFile* file = parse_path(path);
- if (!file) {
- fprintf(stderr, "could not find %s\n", path);
- return -1;
- }
- file->list();
- }
- }
-
- return 0;
-}
-
-static int get_file(int argc, char* argv[]) {
- int ret = -1;
- int srcFD = -1;
- int destFD = -1;
- MtpFile* srcFile = NULL;
- MtpObjectInfo* info = NULL;
- char* dest;
-
- if (argc < 1) {
- fprintf(stderr, "not enough arguments\n");
- return -1;
- } else if (argc > 2) {
- fprintf(stderr, "too many arguments\n");
- return -1;
- }
-
- // find source object
- char* src = argv[0];
- srcFile = parse_path(src);
- if (!srcFile) {
- fprintf(stderr, "could not find %s\n", src);
- return -1;
- }
- info = srcFile->getObjectInfo();
- if (!info) {
- fprintf(stderr, "could not find object info for %s\n", src);
- goto fail;
- }
- if (info->mFormat == MTP_FORMAT_ASSOCIATION) {
- fprintf(stderr, "copying directories not implemented yet\n");
- goto fail;
- }
-
- dest = (argc > 1 ? argv[1] : info->mName);
- destFD = open(dest, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
- if (destFD < 0) {
- fprintf(stderr, "could not create %s\n", dest);
- goto fail;
- }
- srcFD = srcFile->getDevice()->readObject(info->mHandle, info->mCompressedSize);
- if (srcFD < 0)
- goto fail;
-
- char buffer[65536];
- while (1) {
- int count = read(srcFD, buffer, sizeof(buffer));
- if (count <= 0)
- break;
- write(destFD, buffer, count);
- }
- // FIXME - error checking and reporting
- ret = 0;
-
-fail:
- delete srcFile;
- delete info;
- if (srcFD >= 0)
- close(srcFD);
- if (destFD >= 0)
- close(destFD);
- return ret;
-}
-
-static int put_file(int argc, char* argv[]) {
- int ret = -1;
- int srcFD = -1;
- MtpFile* destFile = NULL;
- MtpObjectInfo* srcInfo = NULL;
- MtpObjectInfo* destInfo = NULL;
- MtpObjectHandle handle;
- struct stat statbuf;
- const char* lastSlash;
-
- if (argc < 1) {
- fprintf(stderr, "not enough arguments\n");
- return -1;
- } else if (argc > 2) {
- fprintf(stderr, "too many arguments\n");
- return -1;
- }
- const char* src = argv[0];
- srcFD = open(src, O_RDONLY);
- if (srcFD < 0) {
- fprintf(stderr, "could not open %s\n", src);
- goto fail;
- }
- if (argc == 2) {
- char* dest = argv[1];
- destFile = parse_path(dest);
- if (!destFile) {
- fprintf(stderr, "could not find %s\n", dest);
- goto fail;
- }
- } else {
- if (!sCurrentDirectory) {
- fprintf(stderr, "current working directory not set\n");
- goto fail;
- }
- destFile = new MtpFile(sCurrentDirectory);
- }
-
- destInfo = destFile->getObjectInfo();
- if (!destInfo) {
- fprintf(stderr, "could not find object info destination directory\n");
- goto fail;
- }
- if (destInfo->mFormat != MTP_FORMAT_ASSOCIATION) {
- fprintf(stderr, "destination not a directory\n");
- goto fail;
- }
-
- if (fstat(srcFD, &statbuf))
- goto fail;
-
- srcInfo = new MtpObjectInfo(0);
- srcInfo->mStorageID = destInfo->mStorageID;
- srcInfo->mFormat = MTP_FORMAT_EXIF_JPEG; // FIXME
- srcInfo->mCompressedSize = statbuf.st_size;
- srcInfo->mParent = destInfo->mHandle;
- lastSlash = strrchr(src, '/');
- srcInfo->mName = strdup(lastSlash ? lastSlash + 1 : src);
- srcInfo->mDateModified = statbuf.st_mtime;
- handle = destFile->getDevice()->sendObjectInfo(srcInfo);
- if (handle <= 0) {
- printf("sendObjectInfo returned %04X\n", handle);
- goto fail;
- }
- if (destFile->getDevice()->sendObject(srcInfo, srcFD))
- ret = 0;
-
-fail:
- delete destFile;
- delete srcInfo;
- delete destInfo;
- if (srcFD >= 0)
- close(srcFD);
- printf("returning %d\n", ret);
- return ret;
-}
-
-typedef int (* command_func)(int argc, char* argv[]);
-
-struct command_table_entry {
- const char* name;
- command_func func;
-};
-
-const command_table_entry command_list[] = {
- { "cd", set_cwd },
- { "ls", list },
- { "get", get_file },
- { "put", put_file },
- { NULL, NULL },
-};
-
-
-static int do_command(int argc, char* argv[]) {
- const command_table_entry* command = command_list;
- const char* name = *argv++;
- argc--;
-
- while (command->name) {
- if (!strcmp(command->name, name))
- return command->func(argc, argv);
- else
- command++;
- }
- fprintf(stderr, "unknown command %s\n", name);
- return -1;
-}
-
-static int shell() {
- int argc;
- int result = 0;
-#define MAX_ARGS 100
- char* argv[MAX_ARGS];
-
-#if HAVE_READLINE
- using_history();
-#endif
-
- while (1) {
-#if HAVE_READLINE
- char* line = readline(PROMPT);
- if (!line) {
- printf("\n");
- exit(0);
- }
-#else
- char buffer[1000];
- printf("%s", PROMPT);
- char* line = NULL;
- size_t length = 0;
-
- buffer[0] = 0;
- fgets(buffer, sizeof(buffer), stdin);
- int count = strlen(buffer);
- if (count > 0 && buffer[0] == (char)EOF) {
- printf("\n");
- exit(0);
- }
- if (count > 0 && line[count - 1] == '\n')
- line[count - 1] == 0;
-#endif
- char* tok = strtok(line, " \t\n\r");
- if (!tok)
- continue;
- if (!strcmp(tok, "quit") || !strcmp(tok, "exit")) {
- exit(0);
- }
-#if HAVE_READLINE
- add_history(line);
-#endif
- argc = 0;
- while (tok) {
- if (argc + 1 == MAX_ARGS) {
- fprintf(stderr, "too many arguments\n");
- result = -1;
- goto bottom_of_loop;
- }
-
- argv[argc++] = strdup(tok);
- tok = strtok(NULL, " \t\n\r");
- }
-
- result = do_command(argc, argv);
-
-bottom_of_loop:
- for (int i = 0; i < argc; i++)
- free(argv[i]);
- free(line);
- }
-
- return result;
-}
-
-int main(int argc, char* argv[]) {
- init();
-
- if (argc == 1)
- return shell();
- else
- return do_command(argc - 1, argv + 1);
-}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 6c84afd..d11a18e 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -61,7 +61,7 @@
// database gets upgraded properly. At a minimum, please confirm that 'upgradeVersion'
// is properly propagated through your change. Not doing so will result in a loss of user
// settings.
- private static final int DATABASE_VERSION = 61;
+ private static final int DATABASE_VERSION = 62;
private Context mContext;
@@ -793,22 +793,15 @@
}
if (upgradeVersion == 60) {
- // Increase screen timeout for tablet
- db.beginTransaction();
- SQLiteStatement stmt = null;
- try {
- stmt = db.compileStatement("INSERT OR REPLACE INTO system(name,value)"
- + " VALUES(?,?);");
- loadIntegerSetting(stmt, Settings.System.SCREEN_OFF_TIMEOUT,
- R.integer.def_screen_off_timeout);
- db.setTransactionSuccessful();
- } finally {
- db.endTransaction();
- if (stmt != null) stmt.close();
- }
+ upgradeScreenTimeout(db);
upgradeVersion = 61;
}
+ if (upgradeVersion == 61) {
+ upgradeScreenTimeout(db);
+ upgradeVersion = 62;
+ }
+
// *** Remember to update DATABASE_VERSION above!
if (upgradeVersion != currentVersion) {
@@ -914,6 +907,23 @@
}
}
+ private void upgradeScreenTimeout(SQLiteDatabase db) {
+ // Change screen timeout to current default
+ db.beginTransaction();
+ SQLiteStatement stmt = null;
+ try {
+ stmt = db.compileStatement("INSERT OR REPLACE INTO system(name,value)"
+ + " VALUES(?,?);");
+ loadIntegerSetting(stmt, Settings.System.SCREEN_OFF_TIMEOUT,
+ R.integer.def_screen_off_timeout);
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
+ if (stmt != null)
+ stmt.close();
+ }
+ }
+
/**
* Loads the default set of bookmarked shortcuts from an xml file.
*
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar.xml b/packages/SystemUI/res/layout-xlarge/status_bar.xml
index d4a6136..d11e6da 100644
--- a/packages/SystemUI/res/layout-xlarge/status_bar.xml
+++ b/packages/SystemUI/res/layout-xlarge/status_bar.xml
@@ -25,7 +25,6 @@
android:id="@+id/bar_contents"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:animateLayoutChanges="false"
>
<!-- notification icons & panel access -->
@@ -105,7 +104,6 @@
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:orientation="horizontal"
- android:animateLayoutChanges="false"
>
<com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/back"
@@ -178,62 +176,68 @@
</LinearLayout>
<!-- lights out mode: "shadow" views -->
- <ImageView
- android:id="@+id/notification_shadow"
- android:layout_width="176dip"
+ <RelativeLayout
+ android:id="@+id/shadows"
+ android:layout_width="match_parent"
android:layout_height="match_parent"
- android:paddingRight="48dip"
- android:layout_alignParentRight="true"
- android:layout_alignParentBottom="true"
- android:src="@drawable/ic_sysbar_shadow"
- android:visibility="gone"
- android:scaleType="fitXY"
- />
+ >
+ <ImageView
+ android:id="@+id/notification_shadow"
+ android:layout_width="176dip"
+ android:layout_height="match_parent"
+ android:paddingRight="48dip"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentBottom="true"
+ android:src="@drawable/ic_sysbar_shadow"
+ android:visibility="gone"
+ android:scaleType="fitXY"
+ />
- <ImageView
- android:id="@+id/back_shadow"
- android:layout_width="96dip"
- android:layout_height="match_parent"
- android:paddingLeft="18dip"
- android:paddingRight="18dip"
- android:layout_alignParentLeft="true"
- android:layout_alignParentBottom="true"
- android:src="@drawable/ic_sysbar_shadow"
- android:visibility="gone"
- />
- <ImageView
- android:id="@+id/home_shadow"
- android:layout_width="96dip"
- android:layout_height="match_parent"
- android:paddingLeft="18dip"
- android:paddingRight="18dip"
- android:layout_toRightOf="@id/back_shadow"
- android:layout_alignParentBottom="true"
- android:src="@drawable/ic_sysbar_shadow"
- android:visibility="gone"
- />
- <ImageView
- android:id="@+id/recent_shadow"
- android:layout_width="96dip"
- android:layout_height="match_parent"
- android:paddingLeft="18dip"
- android:paddingRight="18dip"
- android:layout_toRightOf="@id/home_shadow"
- android:layout_alignParentBottom="true"
- android:src="@drawable/ic_sysbar_shadow"
- android:visibility="gone"
- />
- <ImageView
- android:id="@+id/menu_shadow"
- android:layout_width="96dip"
- android:layout_height="match_parent"
- android:paddingLeft="18dip"
- android:paddingRight="18dip"
- android:layout_toRightOf="@id/recent_shadow"
- android:layout_alignParentBottom="true"
- android:src="@drawable/ic_sysbar_shadow"
- android:visibility="gone"
- />
+ <ImageView
+ android:id="@+id/back_shadow"
+ android:layout_width="96dip"
+ android:layout_height="match_parent"
+ android:paddingLeft="18dip"
+ android:paddingRight="18dip"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentBottom="true"
+ android:src="@drawable/ic_sysbar_shadow"
+ android:visibility="gone"
+ />
+ <ImageView
+ android:id="@+id/home_shadow"
+ android:layout_width="96dip"
+ android:layout_height="match_parent"
+ android:paddingLeft="18dip"
+ android:paddingRight="18dip"
+ android:layout_toRightOf="@id/back_shadow"
+ android:layout_alignParentBottom="true"
+ android:src="@drawable/ic_sysbar_shadow"
+ android:visibility="gone"
+ />
+ <ImageView
+ android:id="@+id/recent_shadow"
+ android:layout_width="96dip"
+ android:layout_height="match_parent"
+ android:paddingLeft="18dip"
+ android:paddingRight="18dip"
+ android:layout_toRightOf="@id/home_shadow"
+ android:layout_alignParentBottom="true"
+ android:src="@drawable/ic_sysbar_shadow"
+ android:visibility="gone"
+ />
+ <ImageView
+ android:id="@+id/menu_shadow"
+ android:layout_width="96dip"
+ android:layout_height="match_parent"
+ android:paddingLeft="18dip"
+ android:paddingRight="18dip"
+ android:layout_toRightOf="@id/recent_shadow"
+ android:layout_alignParentBottom="true"
+ android:src="@drawable/ic_sysbar_shadow"
+ android:visibility="gone"
+ />
+ </RelativeLayout>
<!-- ticker: transient incoming notification information -->
<FrameLayout
@@ -244,7 +248,6 @@
android:layout_toRightOf="@+id/systemInfo"
android:paddingLeft="6dip"
android:gravity="center_vertical"
- android:animateLayoutChanges="true"
/>
</RelativeLayout>
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 7c97ac7..233ac45 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
@@ -19,9 +19,12 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Map;
+import java.util.IdentityHashMap;
import android.animation.LayoutTransition;
import android.animation.ObjectAnimator;
+import android.animation.AnimatorSet;
import android.app.ActivityManagerNative;
import android.app.PendingIntent;
import android.app.Notification;
@@ -257,21 +260,6 @@
mBarContents = sb.findViewById(R.id.bar_contents);
- // "shadows" of the status bar features, for lights-out mode
- mBackShadow = sb.findViewById(R.id.back_shadow);
- mHomeShadow = sb.findViewById(R.id.home_shadow);
- mRecentShadow = sb.findViewById(R.id.recent_shadow);
- mMenuShadow = sb.findViewById(R.id.menu_shadow);
- mNotificationShadow = sb.findViewById(R.id.notification_shadow);
-
- mShadowController = new ShadowController(false);
-
- mBackShadow.setOnTouchListener(mShadowController.makeTouchListener());
- mHomeShadow.setOnTouchListener(mShadowController.makeTouchListener());
- mRecentShadow.setOnTouchListener(mShadowController.makeTouchListener());
- mMenuShadow.setOnTouchListener(mShadowController.makeTouchListener());
- mNotificationShadow.setOnTouchListener(mShadowController.makeTouchListener());
-
// the whole right-hand side of the bar
mNotificationArea = sb.findViewById(R.id.notificationArea);
@@ -310,6 +298,20 @@
// The bar contents buttons
mInputMethodButton = (InputMethodButton) sb.findViewById(R.id.imeButton);
+ // "shadows" of the status bar features, for lights-out mode
+ mBackShadow = sb.findViewById(R.id.back_shadow);
+ mHomeShadow = sb.findViewById(R.id.home_shadow);
+ mRecentShadow = sb.findViewById(R.id.recent_shadow);
+ mMenuShadow = sb.findViewById(R.id.menu_shadow);
+ mNotificationShadow = sb.findViewById(R.id.notification_shadow);
+
+ mShadowController = new ShadowController(false);
+ mShadowController.add(mBackButton, mBackShadow);
+ mShadowController.add(mHomeButton, mHomeShadow);
+ mShadowController.add(mRecentButton, mRecentShadow);
+ mShadowController.add(mMenuButton, mMenuShadow);
+ mShadowController.add(mNotificationArea, mNotificationShadow);
+
// set the initial view visibility
setAreThereNotifications();
refreshNotificationTrigger();
@@ -386,8 +388,8 @@
mNotificationPanel.setVisibility(View.VISIBLE);
- // XXX: need to synchronize with shadows here
- mNotificationArea.setVisibility(View.GONE);
+ // synchronize with current shadow state
+ mShadowController.hideElement(mNotificationArea);
}
break;
case MSG_CLOSE_NOTIFICATION_PANEL:
@@ -395,8 +397,8 @@
if (mNotificationPanel.getVisibility() == View.VISIBLE) {
mNotificationPanel.setVisibility(View.GONE);
- // XXX: need to synchronize with shadows here
- mNotificationArea.setVisibility(View.VISIBLE);
+ // synchronize with current shadow state
+ mShadowController.showElement(mNotificationArea);
}
break;
case MSG_OPEN_RECENTS_PANEL:
@@ -576,11 +578,13 @@
if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
if ((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes");
- mNotificationIconArea.setVisibility(View.GONE);
+ // synchronize with current shadow state
+ mShadowController.hideElement(mNotificationArea);
mTicker.halt();
} else {
Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: no");
- mNotificationIconArea.setVisibility(View.VISIBLE);
+ // synchronize with current shadow state
+ mShadowController.showElement(mNotificationArea);
}
} else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
if ((state & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
@@ -1047,11 +1051,57 @@
public class ShadowController {
boolean mShowShadows;
+ Map<View, View> mShadowsForElements = new IdentityHashMap<View, View>(7);
+ Map<View, View> mElementsForShadows = new IdentityHashMap<View, View>(7);
+ LayoutTransition mElementTransition, mShadowTransition;
+
View mTouchTarget;
ShadowController(boolean showShadows) {
mShowShadows = showShadows;
mTouchTarget = null;
+
+ mElementTransition = new LayoutTransition();
+// AnimatorSet s = new AnimatorSet();
+// s.play(ObjectAnimator.ofInt(null, "top", 48, 0))
+// .with(ObjectAnimator.ofFloat(null, "scaleY", 0.5f, 1f))
+// .with(ObjectAnimator.ofFloat(null, "alpha", 0.5f, 1f))
+// ;
+ mElementTransition.setAnimator(LayoutTransition.APPEARING, //s);
+ ObjectAnimator.ofInt(null, "top", 48, 0));
+ mElementTransition.setDuration(LayoutTransition.APPEARING, 100);
+ mElementTransition.setStartDelay(LayoutTransition.APPEARING, 0);
+
+// s = new AnimatorSet();
+// s.play(ObjectAnimator.ofInt(null, "top", 0, 48))
+// .with(ObjectAnimator.ofFloat(null, "scaleY", 1f, 0.5f))
+// .with(ObjectAnimator.ofFloat(null, "alpha", 1f, 0.5f))
+// ;
+ mElementTransition.setAnimator(LayoutTransition.DISAPPEARING, //s);
+ ObjectAnimator.ofInt(null, "top", 0, 48));
+ mElementTransition.setDuration(LayoutTransition.DISAPPEARING, 400);
+
+ mShadowTransition = new LayoutTransition();
+ mShadowTransition.setAnimator(LayoutTransition.APPEARING,
+ ObjectAnimator.ofFloat(null, "alpha", 0f, 1f));
+ mShadowTransition.setDuration(LayoutTransition.APPEARING, 200);
+ mShadowTransition.setStartDelay(LayoutTransition.APPEARING, 100);
+ mShadowTransition.setAnimator(LayoutTransition.DISAPPEARING,
+ ObjectAnimator.ofFloat(null, "alpha", 1f, 0f));
+ mShadowTransition.setDuration(LayoutTransition.DISAPPEARING, 100);
+
+ ViewGroup bar = (ViewGroup) TabletStatusBar.this.mBarContents;
+ bar.setLayoutTransition(mElementTransition);
+ ViewGroup nav = (ViewGroup) TabletStatusBar.this.mNavigationArea;
+ nav.setLayoutTransition(mElementTransition);
+ ViewGroup shadowGroup = (ViewGroup) bar.findViewById(R.id.shadows);
+ shadowGroup.setLayoutTransition(mShadowTransition);
+ }
+
+ public void add(View element, View shadow) {
+ shadow.setOnTouchListener(makeTouchListener());
+ mShadowsForElements.put(element, shadow);
+ mElementsForShadows.put(shadow, element);
}
public boolean getShadowState() {
@@ -1067,17 +1117,7 @@
// currently redirecting events?
if (mTouchTarget == null) {
- if (v == mBackShadow) {
- mTouchTarget = mBackButton;
- } else if (v == mHomeShadow) {
- mTouchTarget = mHomeButton;
- } else if (v == mMenuShadow) {
- mTouchTarget = mMenuButton;
- } else if (v == mRecentShadow) {
- mTouchTarget = mRecentButton;
- } else if (v == mNotificationShadow) {
- mTouchTarget = mNotificationArea;
- }
+ mTouchTarget = mElementsForShadows.get(v);
}
if (mTouchTarget != null && mTouchTarget.getVisibility() != View.GONE) {
@@ -1094,7 +1134,7 @@
break;
case MotionEvent.ACTION_DOWN:
mHandler.removeMessages(MSG_RESTORE_SHADOWS);
- setShadowForButton(mTouchTarget, false);
+ setElementShadow(mTouchTarget, false);
break;
}
mTouchTarget.dispatchTouchEvent(ev);
@@ -1108,11 +1148,9 @@
}
public void refresh() {
- setShadowForButton(mBackButton, mShowShadows);
- setShadowForButton(mHomeButton, mShowShadows);
- setShadowForButton(mRecentButton, mShowShadows);
- setShadowForButton(mMenuButton, mShowShadows);
- setShadowForButton(mNotificationArea, mShowShadows);
+ for (View element : mShadowsForElements.keySet()) {
+ setElementShadow(element, mShowShadows);
+ }
}
public void showAllShadows() {
@@ -1127,19 +1165,8 @@
// Use View.INVISIBLE for things hidden due to shadowing, and View.GONE for things that are
// disabled (and should not be shadowed or re-shown)
- public void setShadowForButton(View button, boolean shade) {
- View shadow = null;
- if (button == mBackButton) {
- shadow = mBackShadow;
- } else if (button == mHomeButton) {
- shadow = mHomeShadow;
- } else if (button == mMenuButton) {
- shadow = mMenuShadow;
- } else if (button == mRecentButton) {
- shadow = mRecentShadow;
- } else if (button == mNotificationArea) {
- shadow = mNotificationShadow;
- }
+ public void setElementShadow(View button, boolean shade) {
+ View shadow = mShadowsForElements.get(button);
if (shadow != null) {
if (button.getVisibility() != View.GONE) {
shadow.setVisibility(shade ? View.VISIBLE : View.INVISIBLE);
@@ -1147,6 +1174,26 @@
}
}
}
+
+ // Hide both element and shadow, using default layout animations.
+ public void hideElement(View button) {
+ Slog.d(TAG, "hiding: " + button);
+ View shadow = mShadowsForElements.get(button);
+ if (shadow != null) {
+ shadow.setVisibility(View.GONE);
+ }
+ button.setVisibility(View.GONE);
+ }
+
+ // Honoring the current shadow state.
+ public void showElement(View button) {
+ Slog.d(TAG, "showing: " + button);
+ View shadow = mShadowsForElements.get(button);
+ if (shadow != null) {
+ shadow.setVisibility(mShowShadows ? View.VISIBLE : View.INVISIBLE);
+ }
+ button.setVisibility(mShowShadows ? View.INVISIBLE : View.VISIBLE);
+ }
}
public class TouchOutsideListener implements View.OnTouchListener {
@@ -1171,15 +1218,6 @@
}
}
- private void setViewVisibility(View v, int vis, int anim) {
- if (v.getVisibility() != vis) {
- //Slog.d(TAG, "setViewVisibility vis=" + (vis == View.VISIBLE) + " v=" + v);
- v.setAnimation(AnimationUtils.loadAnimation(mContext, anim));
- v.setVisibility(vis);
- }
- }
-
-
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.print("mDisabled=0x");
pw.println(Integer.toHexString(mDisabled));
diff --git a/packages/TtsService/src/android/tts/TtsService.java b/packages/TtsService/src/android/tts/TtsService.java
index c977ba3..08bbfb2 100755
--- a/packages/TtsService/src/android/tts/TtsService.java
+++ b/packages/TtsService/src/android/tts/TtsService.java
@@ -496,7 +496,7 @@
* engines.
*/
private int speak(String callingApp, String text, int queueMode, ArrayList<String> params) {
- Log.v(SERVICE_TAG, "TTS service received " + text);
+ // Log.v(SERVICE_TAG, "TTS service received " + text);
if (queueMode == TextToSpeech.QUEUE_FLUSH) {
stop(callingApp);
} else if (queueMode == 2) {
@@ -705,27 +705,27 @@
}
}
- public void onCompletion(MediaPlayer arg0) {
- // mCurrentSpeechItem may become null if it is stopped at the same
- // time it completes.
- SpeechItem currentSpeechItemCopy = mCurrentSpeechItem;
- if (currentSpeechItemCopy != null) {
- String callingApp = currentSpeechItemCopy.mCallingApp;
- ArrayList<String> params = currentSpeechItemCopy.mParams;
- String utteranceId = "";
- if (params != null) {
- for (int i = 0; i < params.size() - 1; i = i + 2) {
- String param = params.get(i);
- if (param.equals(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID)) {
- utteranceId = params.get(i + 1);
- }
- }
- }
- if (utteranceId.length() > 0) {
- dispatchUtteranceCompletedCallback(utteranceId, callingApp);
- }
- }
- processSpeechQueue();
+ public void onCompletion(MediaPlayer arg0) {
+ // mCurrentSpeechItem may become null if it is stopped at the same
+ // time it completes.
+ SpeechItem currentSpeechItemCopy = mCurrentSpeechItem;
+ if (currentSpeechItemCopy != null) {
+ String callingApp = currentSpeechItemCopy.mCallingApp;
+ ArrayList<String> params = currentSpeechItemCopy.mParams;
+ String utteranceId = "";
+ if (params != null) {
+ for (int i = 0; i < params.size() - 1; i = i + 2) {
+ String param = params.get(i);
+ if (param.equals(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID)) {
+ utteranceId = params.get(i + 1);
+ }
+ }
+ }
+ if (utteranceId.length() > 0) {
+ dispatchUtteranceCompletedCallback(utteranceId, callingApp);
+ }
+ }
+ processSpeechQueue();
}
private int playSilence(String callingApp, long duration, int queueMode,
@@ -1064,7 +1064,7 @@
SoundResource sr = getSoundResource(mCurrentSpeechItem);
// Synth speech as needed - synthesizer should call
// processSpeechQueue to continue running the queue
- Log.v(SERVICE_TAG, "TTS processing: " + mCurrentSpeechItem.mText);
+ // Log.v(SERVICE_TAG, "TTS processing: " + mCurrentSpeechItem.mText);
if (sr == null) {
if (mCurrentSpeechItem.mType == SpeechItem.TEXT) {
mCurrentSpeechItem = splitCurrentTextIfNeeded(mCurrentSpeechItem);
@@ -1462,8 +1462,8 @@
*
* @return SUCCESS or ERROR as defined in android.speech.tts.TextToSpeech.
*/
- public int setEngineByPackageName(String packageName) {
- return mSelf.setEngine(packageName);
+ public int setEngineByPackageName(String packageName) {
+ return mSelf.setEngine(packageName);
}
/**
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardViewBase.java b/policy/src/com/android/internal/policy/impl/KeyguardViewBase.java
index 34dbace..82753b2 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardViewBase.java
+++ b/policy/src/com/android/internal/policy/impl/KeyguardViewBase.java
@@ -43,13 +43,6 @@
public KeyguardViewBase(Context context) {
super(context);
-
- // drop shadow below status bar in keyguard too
- mForegroundInPadding = false;
- setForegroundGravity(Gravity.FILL_HORIZONTAL | Gravity.TOP);
- setForeground(
- context.getResources().getDrawable(
- com.android.internal.R.drawable.title_bar_shadow));
}
// used to inject callback
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 84dd022..51b5947 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -343,7 +343,7 @@
lSessionId = *sessionId;
} else {
// if no audio session id is provided, create one here
- lSessionId = nextUniqueId();
+ lSessionId = nextUniqueId_l();
if (sessionId != NULL) {
*sessionId = lSessionId;
}
@@ -3699,7 +3699,7 @@
if (sessionId != NULL && *sessionId != AudioSystem::SESSION_OUTPUT_MIX) {
lSessionId = *sessionId;
} else {
- lSessionId = nextUniqueId();
+ lSessionId = nextUniqueId_l();
if (sessionId != NULL) {
*sessionId = lSessionId;
}
@@ -4300,7 +4300,7 @@
mHardwareStatus = AUDIO_HW_IDLE;
if (output != 0) {
- int id = nextUniqueId();
+ int id = nextUniqueId_l();
if ((flags & AudioSystem::OUTPUT_FLAG_DIRECT) ||
(format != AudioSystem::PCM_16_BIT) ||
(channels != AudioSystem::CHANNEL_OUT_STEREO)) {
@@ -4348,7 +4348,7 @@
return 0;
}
- int id = nextUniqueId();
+ int id = nextUniqueId_l();
DuplicatingThread *thread = new DuplicatingThread(this, thread1, id);
thread->addOutputTrack(thread2);
mPlaybackThreads.add(id, thread);
@@ -4473,7 +4473,7 @@
}
if (input != 0) {
- int id = nextUniqueId();
+ int id = nextUniqueId_l();
// Start record thread
thread = new RecordThread(this, input, reqSamplingRate, reqChannels, id);
mRecordThreads.add(id, thread);
@@ -4543,7 +4543,8 @@
int AudioFlinger::newAudioSessionId()
{
- return nextUniqueId();
+ AutoMutex _l(mLock);
+ return nextUniqueId_l();
}
// checkPlaybackThread_l() must be called with AudioFlinger::mLock held
@@ -4578,9 +4579,10 @@
return thread;
}
-int AudioFlinger::nextUniqueId()
+// nextUniqueId_l() must be called with AudioFlinger::mLock held
+int AudioFlinger::nextUniqueId_l()
{
- return android_atomic_inc(&mNextUniqueId);
+ return mNextUniqueId++;
}
// ----------------------------------------------------------------------------
@@ -4967,7 +4969,7 @@
LOGV("createEffect_l() got effect %p on chain %p", effect == 0 ? 0 : effect.get(), chain.get());
if (effect == 0) {
- int id = mAudioFlinger->nextUniqueId();
+ int id = mAudioFlinger->nextUniqueId_l();
// Check CPU and memory usage
lStatus = AudioSystem::registerEffect(desc, mId, chain->strategy(), sessionId, id);
if (lStatus != NO_ERROR) {
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 5917632..f0ef867 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -785,7 +785,7 @@
float streamVolumeInternal(int stream) const { return mStreamTypes[stream].volume; }
void audioConfigChanged_l(int event, int ioHandle, void *param2);
- int nextUniqueId();
+ int nextUniqueId_l();
status_t moveEffectChain_l(int session,
AudioFlinger::PlaybackThread *srcThread,
AudioFlinger::PlaybackThread *dstThread,
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index c18262e..5c67da7 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -22,6 +22,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.database.ContentObserver;
import android.net.ConnectivityManager;
import android.net.IConnectivityManager;
import android.net.MobileDataStateTracker;
@@ -29,8 +30,9 @@
import android.net.LinkProperties;
import android.net.NetworkStateTracker;
import android.net.NetworkUtils;
+import android.net.Proxy;
+import android.net.ProxyProperties;
import android.net.wifi.WifiStateTracker;
-import android.net.NetworkUtils;
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
@@ -55,13 +57,12 @@
import java.io.IOException;
import java.io.PrintWriter;
import java.net.InetAddress;
+import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.GregorianCalendar;
import java.util.List;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
/**
* @hide
@@ -179,6 +180,12 @@
private static final int EVENT_CLEAR_NET_TRANSITION_WAKELOCK =
MAX_NETWORK_STATE_TRACKER_EVENT + 8;
+ /**
+ * used internally to reload global proxy settings
+ */
+ private static final int EVENT_APPLY_GLOBAL_HTTP_PROXY =
+ MAX_NETWORK_STATE_TRACKER_EVENT + 9;
+
private Handler mHandler;
// list of DeathRecipients used to make sure features are turned off when
@@ -199,6 +206,14 @@
private static final int INET_CONDITION_LOG_MAX_SIZE = 15;
private ArrayList mInetLog;
+ // track the current default http proxy - tell the world if we get a new one (real change)
+ private ProxyProperties mDefaultProxy = null;
+ // track the global proxy.
+ private ProxyProperties mGlobalProxy = null;
+ private final Object mGlobalProxyLock = new Object();
+
+ private SettingsObserver mSettingsObserver;
+
private static class NetworkAttributes {
/**
* Class for holding settings read from resources.
@@ -412,6 +427,9 @@
if (DBG) {
mInetLog = new ArrayList();
}
+
+ mSettingsObserver = new SettingsObserver(mHandler, EVENT_APPLY_GLOBAL_HTTP_PROXY);
+ mSettingsObserver.observe(mContext);
}
@@ -1303,6 +1321,8 @@
mInitialBroadcast = null;
}
}
+ // load the global proxy at startup
+ mHandler.sendMessage(mHandler.obtainMessage(EVENT_APPLY_GLOBAL_HTTP_PROXY));
}
private void handleConnect(NetworkInfo info) {
@@ -1380,6 +1400,7 @@
if (mNetTrackers[netType].getNetworkInfo().isConnected()) {
if (mNetAttributes[netType].isDefault()) {
+ handleApplyDefaultProxy(netType);
addDefaultRoute(mNetTrackers[netType]);
} else {
addPrivateDnsRoutes(mNetTrackers[netType]);
@@ -1783,10 +1804,9 @@
}
break;
case NetworkStateTracker.EVENT_CONFIGURATION_CHANGED:
- // TODO - make this handle ip/proxy/gateway/dns changes
info = (NetworkInfo) msg.obj;
type = info.getType();
- handleDnsConfigurationChange(type);
+ handleConnectivityChange(type);
break;
case EVENT_CLEAR_NET_TRANSITION_WAKELOCK:
String causedBy = null;
@@ -1838,6 +1858,10 @@
handleSetMobileData(enabled);
break;
}
+ case EVENT_APPLY_GLOBAL_HTTP_PROXY:
+ {
+ handleDeprecatedGlobalHttpProxy();
+ }
}
}
}
@@ -2037,4 +2061,113 @@
sendInetConditionBroadcast(networkInfo);
return;
}
+
+ public synchronized ProxyProperties getProxy() {
+ if (mGlobalProxy != null) return mGlobalProxy;
+ if (mDefaultProxy != null) return mDefaultProxy;
+ return null;
+ }
+
+ public void setGlobalProxy(ProxyProperties proxyProperties) {
+ enforceChangePermission();
+ synchronized (mGlobalProxyLock) {
+ if (proxyProperties == mGlobalProxy) return;
+ if (proxyProperties != null && proxyProperties.equals(mGlobalProxy)) return;
+ if (mGlobalProxy != null && mGlobalProxy.equals(proxyProperties)) return;
+
+ String host = "";
+ int port = 0;
+ String exclList = "";
+ if (proxyProperties != null && !TextUtils.isEmpty(proxyProperties.getHost())) {
+ mGlobalProxy = new ProxyProperties(proxyProperties);
+ host = mGlobalProxy.getHost();
+ port = mGlobalProxy.getPort();
+ exclList = mGlobalProxy.getExclusionList();
+ } else {
+ mGlobalProxy = null;
+ }
+ ContentResolver res = mContext.getContentResolver();
+ Settings.Secure.putString(res, Settings.Secure.GLOBAL_HTTP_PROXY_HOST, host);
+ Settings.Secure.putInt(res, Settings.Secure.GLOBAL_HTTP_PROXY_PORT, port);
+ Settings.Secure.putString(res,Settings.Secure.GLOBAL_HTTP_PROXY_EXCLUSION_LIST,
+ exclList);
+ }
+
+ if (mGlobalProxy == null) {
+ proxyProperties = mDefaultProxy;
+ }
+ sendProxyBroadcast(proxyProperties);
+ }
+
+ public ProxyProperties getGlobalProxy() {
+ synchronized (mGlobalProxyLock) {
+ return mGlobalProxy;
+ }
+ }
+
+ private void handleApplyDefaultProxy(int type) {
+ // check if new default - push it out to all VM if so
+ ProxyProperties proxy = mNetTrackers[type].getLinkProperties().getHttpProxy();
+ synchronized (this) {
+ if (mDefaultProxy != null && mDefaultProxy.equals(proxy)) return;
+ if (mDefaultProxy == proxy) return;
+ if (!TextUtils.isEmpty(proxy.getHost())) {
+ mDefaultProxy = proxy;
+ } else {
+ mDefaultProxy = null;
+ }
+ }
+ if (DBG) Slog.d(TAG, "changing default proxy to " + proxy);
+ if ((proxy == null && mGlobalProxy == null) || proxy.equals(mGlobalProxy)) return;
+ if (mGlobalProxy != null) return;
+ sendProxyBroadcast(proxy);
+ }
+
+ private void handleDeprecatedGlobalHttpProxy() {
+ String proxy = Settings.Secure.getString(mContext.getContentResolver(),
+ Settings.Secure.HTTP_PROXY);
+ if (!TextUtils.isEmpty(proxy)) {
+ String data[] = proxy.split(":");
+ String proxyHost = data[0];
+ int proxyPort = 8080;
+ if (data.length > 1) {
+ try {
+ proxyPort = Integer.parseInt(data[1]);
+ } catch (NumberFormatException e) {
+ return;
+ }
+ }
+ ProxyProperties p = new ProxyProperties(data[0], proxyPort, "");
+ setGlobalProxy(p);
+ }
+ }
+
+ private void sendProxyBroadcast(ProxyProperties proxy) {
+ Slog.d(TAG, "sending Proxy Broadcast for " + proxy);
+ Intent intent = new Intent(Proxy.PROXY_CHANGE_ACTION);
+ intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+ intent.putExtra(Proxy.EXTRA_PROXY_INFO, proxy);
+ mContext.sendBroadcast(intent);
+ }
+
+ private static class SettingsObserver extends ContentObserver {
+ private int mWhat;
+ private Handler mHandler;
+ SettingsObserver(Handler handler, int what) {
+ super(handler);
+ mHandler = handler;
+ mWhat = what;
+ }
+
+ void observe(Context context) {
+ ContentResolver resolver = context.getContentResolver();
+ resolver.registerContentObserver(Settings.Secure.getUriFor(
+ Settings.Secure.HTTP_PROXY), false, this);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ mHandler.obtainMessage(mWhat).sendToTarget();
+ }
+ }
}
diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java
index 3dcad38..53a19f5 100644
--- a/services/java/com/android/server/DevicePolicyManagerService.java
+++ b/services/java/com/android/server/DevicePolicyManagerService.java
@@ -41,9 +41,8 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
import android.content.pm.PackageManager.NameNotFoundException;
-import android.net.ConnectivityManager;
+import android.content.pm.ResolveInfo;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
@@ -55,9 +54,9 @@
import android.os.ServiceManager;
import android.os.SystemClock;
import android.provider.Settings;
-import android.util.Slog;
import android.util.PrintWriterPrinter;
import android.util.Printer;
+import android.util.Slog;
import android.util.Xml;
import android.view.WindowManagerPolicy;
@@ -89,9 +88,6 @@
= "com.android.server.ACTION_EXPIRED_PASSWORD_NOTIFICATION";
private static final long MS_PER_DAY = 86400 * 1000;
- private static final long MS_PER_HOUR = 3600 * 1000;
- private static final long MS_PER_MINUTE = 60 * 1000;
- private static final long MIN_TIMEOUT = 86400 * 1000; // minimum expiration timeout is 1 day
final Context mContext;
final MyPackageMonitor mMonitor;
@@ -364,6 +360,7 @@
}
class MyPackageMonitor extends PackageMonitor {
+ @Override
public void onSomePackagesChanged() {
synchronized (DevicePolicyManagerService.this) {
boolean removed = false;
@@ -410,13 +407,6 @@
context.registerReceiver(mReceiver, filter);
}
- static String countdownString(long time) {
- long days = time / MS_PER_DAY;
- long hours = (time / MS_PER_HOUR) % 24;
- long minutes = (time / MS_PER_MINUTE) % 60;
- return days + "d" + hours + "h" + minutes + "m";
- }
-
protected void setExpirationAlarmCheckLocked(Context context) {
final long expiration = getPasswordExpirationLocked(null);
final long now = System.currentTimeMillis();
@@ -430,12 +420,17 @@
alarmTime = now + MS_PER_DAY;
}
- AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
- PendingIntent pi = PendingIntent.getBroadcast(context, REQUEST_EXPIRE_PASSWORD,
- new Intent(ACTION_EXPIRED_PASSWORD_NOTIFICATION),
- PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT);
- am.cancel(pi);
- am.set(AlarmManager.RTC, alarmTime, pi);
+ long token = Binder.clearCallingIdentity();
+ try {
+ AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+ PendingIntent pi = PendingIntent.getBroadcast(context, REQUEST_EXPIRE_PASSWORD,
+ new Intent(ACTION_EXPIRED_PASSWORD_NOTIFICATION),
+ PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT);
+ am.cancel(pi);
+ am.set(AlarmManager.RTC, alarmTime, pi);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
private IPowerManager getIPowerManager() {
@@ -993,8 +988,8 @@
if (who == null) {
throw new NullPointerException("ComponentName is null");
}
- if (timeout != 0L && timeout < MIN_TIMEOUT) {
- throw new IllegalArgumentException("Timeout must be > " + MIN_TIMEOUT + "ms");
+ if (timeout < 0) {
+ throw new IllegalArgumentException("Timeout must be >= 0 ms");
}
ActiveAdmin ap = getActiveAdminForCallerLocked(who,
DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD);
@@ -1757,10 +1752,19 @@
}
// Remove white spaces
proxySpec = proxySpec.trim();
+ String data[] = proxySpec.split(":");
+ int proxyPort = 8080;
+ if (data.length > 1) {
+ try {
+ proxyPort = Integer.parseInt(data[1]);
+ } catch (NumberFormatException e) {}
+ }
exclusionList = exclusionList.trim();
ContentResolver res = mContext.getContentResolver();
- Settings.Secure.putString(res, Settings.Secure.HTTP_PROXY, proxySpec);
- Settings.Secure.putString(res, Settings.Secure.HTTP_PROXY_EXCLUSION_LIST, exclusionList);
+ Settings.Secure.putString(res, Settings.Secure.GLOBAL_HTTP_PROXY_HOST, data[0]);
+ Settings.Secure.putInt(res, Settings.Secure.GLOBAL_HTTP_PROXY_PORT, proxyPort);
+ Settings.Secure.putString(res, Settings.Secure.GLOBAL_HTTP_PROXY_EXCLUSION_LIST,
+ exclusionList);
}
@Override
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index a0a1974..c121808 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -4578,6 +4578,80 @@
mHandler.sendMessage(msg);
}
+ public void setInstallerPackageName(String targetPackage,
+ String installerPackageName) {
+ PackageSetting pkgSetting;
+ final int uid = Binder.getCallingUid();
+ final int permission = mContext.checkCallingPermission(
+ android.Manifest.permission.INSTALL_PACKAGES);
+ final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED);
+ synchronized (mPackages) {
+ PackageSetting targetPackageSetting = mSettings.mPackages.get(targetPackage);
+ if (targetPackageSetting == null) {
+ throw new IllegalArgumentException("Unknown target package: " + targetPackage);
+ }
+
+ PackageSetting installerPackageSetting;
+ if (installerPackageName != null) {
+ installerPackageSetting = mSettings.mPackages.get(installerPackageName);
+ if (installerPackageSetting == null) {
+ throw new IllegalArgumentException("Unknown installer package: "
+ + installerPackageName);
+ }
+ } else {
+ installerPackageSetting = null;
+ }
+
+ Signature[] callerSignature;
+ Object obj = mSettings.getUserIdLP(uid);
+ if (obj != null) {
+ if (obj instanceof SharedUserSetting) {
+ callerSignature = ((SharedUserSetting)obj).signatures.mSignatures;
+ } else if (obj instanceof PackageSetting) {
+ callerSignature = ((PackageSetting)obj).signatures.mSignatures;
+ } else {
+ throw new SecurityException("Bad object " + obj + " for uid " + uid);
+ }
+ } else {
+ throw new SecurityException("Unknown calling uid " + uid);
+ }
+
+ // Verify: can't set installerPackageName to a package that is
+ // not signed with the same cert as the caller.
+ if (installerPackageSetting != null) {
+ if (checkSignaturesLP(callerSignature,
+ installerPackageSetting.signatures.mSignatures)
+ != PackageManager.SIGNATURE_MATCH) {
+ throw new SecurityException(
+ "Caller does not have same cert as new installer package "
+ + installerPackageName);
+ }
+ }
+
+ // Verify: if target already has an installer package, it must
+ // be signed with the same cert as the caller.
+ if (targetPackageSetting.installerPackageName != null) {
+ PackageSetting setting = mSettings.mPackages.get(
+ targetPackageSetting.installerPackageName);
+ // If the currently set package isn't valid, then it's always
+ // okay to change it.
+ if (setting != null) {
+ if (checkSignaturesLP(callerSignature,
+ setting.signatures.mSignatures)
+ != PackageManager.SIGNATURE_MATCH) {
+ throw new SecurityException(
+ "Caller does not have same cert as old installer package "
+ + targetPackageSetting.installerPackageName);
+ }
+ }
+ }
+
+ // Okay!
+ targetPackageSetting.installerPackageName = installerPackageName;
+ scheduleWriteSettingsLocked();
+ }
+ }
+
public void setPackageObbPath(String packageName, String path) {
if (DEBUG_OBB)
Log.v(TAG, "Setting .obb path for " + packageName + " to: " + path);
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 1ec8a22..e815524 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -76,6 +76,8 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Configuration;
import android.graphics.Bitmap;
+import android.net.Proxy;
+import android.net.ProxyProperties;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
@@ -127,6 +129,7 @@
import java.io.PrintWriter;
import java.lang.IllegalStateException;
import java.lang.ref.WeakReference;
+import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -960,6 +963,7 @@
static final int SHOW_STRICT_MODE_VIOLATION_MSG = 26;
static final int CHECK_EXCESSIVE_WAKE_LOCKS_MSG = 27;
static final int CLEAR_DNS_CACHE = 28;
+ static final int UPDATE_HTTP_PROXY = 29;
AlertDialog mUidAlert;
@@ -1125,6 +1129,30 @@
}
}
} break;
+ case UPDATE_HTTP_PROXY: {
+ ProxyProperties proxy = (ProxyProperties)msg.obj;
+ String host = "";
+ String port = "";
+ String exclList = "";
+ if (proxy != null) {
+ host = proxy.getHost();
+ port = Integer.toString(proxy.getPort());
+ exclList = proxy.getExclusionList();
+ }
+ synchronized (ActivityManagerService.this) {
+ for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
+ ProcessRecord r = mLruProcesses.get(i);
+ if (r.thread != null) {
+ try {
+ r.thread.setHttpProxy(host, port, exclList);
+ } catch (RemoteException ex) {
+ Slog.w(TAG, "Failed to update http proxy for: " +
+ r.info.processName);
+ }
+ }
+ }
+ }
+ } break;
case SHOW_UID_ERROR_MSG: {
// XXX This is a temporary dialog, no need to localize.
AlertDialog d = new BaseErrorDialog(mContext);
@@ -10402,6 +10430,11 @@
mHandler.sendEmptyMessage(CLEAR_DNS_CACHE);
}
+ if (Proxy.PROXY_CHANGE_ACTION.equals(intent.getAction())) {
+ ProxyProperties proxy = intent.getParcelableExtra("proxy");
+ mHandler.sendMessage(mHandler.obtainMessage(UPDATE_HTTP_PROXY, proxy));
+ }
+
/*
* Prevent non-system code (defined here to be non-persistent
* processes) from sending protected broadcasts.
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
index 70328f7..06c7c1b 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
@@ -1007,14 +1007,9 @@
ApnSetting apn = mPendingDataConnection.getApn();
if (apn.proxy != null && apn.proxy.length() != 0) {
try {
- ProxyProperties proxy = new ProxyProperties();
- proxy.setSocketAddress(new InetSocketAddress(InetAddress.getByName(apn.proxy),
- Integer.parseInt(apn.port)));
+ ProxyProperties proxy = new ProxyProperties(apn.proxy,
+ Integer.parseInt(apn.port), null);
mLinkProperties.setHttpProxy(proxy);
- } catch (UnknownHostException e) {
- loge("UnknownHostException making ProxyProperties: " + e);
- } catch (SecurityException e) {
- loge("SecurityException making ProxyProperties: " + e);
} catch (NumberFormatException e) {
loge("NumberFormatException making ProxyProperties (" + apn.port +
"): " + e);
diff --git a/test-runner/src/android/test/IsolatedContext.java b/test-runner/src/android/test/IsolatedContext.java
index b483b82..bc00f68 100644
--- a/test-runner/src/android/test/IsolatedContext.java
+++ b/test-runner/src/android/test/IsolatedContext.java
@@ -87,6 +87,11 @@
}
@Override
+ public void unregisterReceiver(BroadcastReceiver receiver) {
+ // Ignore
+ }
+
+ @Override
public void sendBroadcast(Intent intent) {
mBroadcastIntents.add(intent);
}
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index f0cbaa0..615870b1 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -341,6 +341,12 @@
throw new UnsupportedOperationException();
}
+ @Override
+ public void setInstallerPackageName(String targetPackage,
+ String installerPackageName) {
+ throw new UnsupportedOperationException();
+ }
+
/**
* @hide - to match hiding in superclass
*/
diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
index a5f3456..cea07af 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
@@ -18,6 +18,7 @@
import com.android.layoutlib.api.ILayoutLog;
import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.layoutlib.bridge.impl.Stack;
import android.graphics.Paint_Delegate.FontInfo;
import android.text.TextUtils;
@@ -32,7 +33,6 @@
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.util.List;
-import java.util.Stack;
/**
diff --git a/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java
index 77de32d..6e80268 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java
@@ -813,6 +813,10 @@
return mask;
}
+ private Matrix_Delegate() {
+ reset();
+ }
+
/**
* Adds the given transformation to the current Matrix
* <p/>This in effect does this = this*matrix
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index 02db2cf..2ddabdf 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -22,6 +22,7 @@
import com.android.layoutlib.api.IStyleResourceValue;
import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.BridgeConstants;
+import com.android.layoutlib.bridge.impl.Stack;
import com.android.layoutlib.bridge.impl.TempResourceValue;
import android.app.Activity;
@@ -99,6 +100,8 @@
private final ILayoutLog mLogger;
private BridgeContentResolver mContentResolver;
+ private final Stack<BridgeXmlBlockParser> mParserStack = new Stack<BridgeXmlBlockParser>();
+
/**
* @param projectKey An Object identifying the project. This is used for the cache mechanism.
* @param metrics the {@link DisplayMetrics}.
@@ -188,6 +191,40 @@
return mDefaultPropMaps.get(key);
}
+ /**
+ * Adds a parser to the stack.
+ * @param parser the parser to add.
+ */
+ public void pushParser(BridgeXmlBlockParser parser) {
+ mParserStack.push(parser);
+ }
+
+ /**
+ * Removes the parser at the top of the stack
+ */
+ public void popParser() {
+ mParserStack.pop();
+ }
+
+ /**
+ * Returns the current parser at the top the of the stack.
+ * @return a parser or null.
+ */
+ public BridgeXmlBlockParser getCurrentParser() {
+ return mParserStack.peek();
+ }
+
+ /**
+ * Returns the previous parser.
+ * @return a parser or null if there isn't any previous parser
+ */
+ public BridgeXmlBlockParser getPreviousParser() {
+ if (mParserStack.size() < 2) {
+ return null;
+ }
+ return mParserStack.get(mParserStack.size() - 2);
+ }
+
// ------------- Activity Methods
@Override
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeInflater.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeInflater.java
index b4a28a6..d9e26e2 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeInflater.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeInflater.java
@@ -35,17 +35,17 @@
import java.io.FileReader;
/**
- * Custom implementation of {@link LayoutInflater} to handle custom views.
+ * Custom implementation of {@link LayoutInflater} to handle custom views.
*/
public final class BridgeInflater extends LayoutInflater {
-
+
private final IProjectCallback mProjectCallback;
/**
* List of class prefixes which are tried first by default.
* <p/>
* This should match the list in com.android.internal.policy.impl.PhoneLayoutInflater.
- */
+ */
private static final String[] sClassPrefixList = {
"android.widget.",
"android.webkit."
@@ -55,10 +55,10 @@
super(original, newContext);
mProjectCallback = null;
}
-
+
/**
* Instantiate a new BridgeInflater with an {@link IProjectCallback} object.
- *
+ *
* @param context The Android application context.
* @param projectCallback the {@link IProjectCallback} object.
*/
@@ -84,7 +84,7 @@
// Ignore. We'll try again using the base class below.
}
}
-
+
// Next try using the parent loader. This will most likely only work for
// fully-qualified class names.
try {
@@ -94,7 +94,7 @@
} catch (ClassNotFoundException e) {
// Ignore. We'll try again using the custom view loader below.
}
-
+
// Finally try again using the custom view loader
try {
if (view == null) {
@@ -111,12 +111,12 @@
ClassNotFoundException exception = new ClassNotFoundException("onCreateView", e);
throw exception;
}
-
+
setupViewInContext(view, attrs);
-
+
return view;
}
-
+
@Override
public View createViewFromTag(View parent, String name, AttributeSet attrs) {
View view = null;
@@ -130,7 +130,7 @@
// Wrap the real exception in an InflateException so that the calling
// method can deal with it.
InflateException exception = new InflateException();
- if (e2.getClass().equals(ClassNotFoundException.class) == false) {
+ if (e2.getClass().equals(ClassNotFoundException.class) == false) {
exception.initCause(e2);
} else {
exception.initCause(e);
@@ -138,18 +138,18 @@
throw exception;
}
}
-
+
setupViewInContext(view, attrs);
-
+
return view;
}
-
+
@Override
public View inflate(int resource, ViewGroup root) {
Context context = getContext();
if (context instanceof BridgeContext) {
BridgeContext bridgeContext = (BridgeContext)context;
-
+
IResourceValue value = null;
String[] layoutInfo = Bridge.resolveResourceValue(resource);
@@ -158,7 +158,7 @@
layoutInfo[0]);
} else {
layoutInfo = mProjectCallback.resolveResourceValue(resource);
-
+
if (layoutInfo != null) {
value = bridgeContext.getProjectResource(BridgeConstants.RES_LAYOUT,
layoutInfo[0]);
@@ -172,10 +172,10 @@
KXmlParser parser = new KXmlParser();
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
parser.setInput(new FileReader(f));
-
+
BridgeXmlBlockParser bridgeParser = new BridgeXmlBlockParser(
parser, bridgeContext, false);
-
+
return inflate(bridgeParser, root);
} catch (Exception e) {
bridgeContext.getLogger().error(e);
@@ -186,7 +186,7 @@
}
return null;
}
-
+
private View loadCustomView(String name, AttributeSet attrs) throws ClassNotFoundException,
Exception{
if (mProjectCallback != null) {
@@ -194,12 +194,12 @@
if (name.equals("view")) {
name = attrs.getAttributeValue(null, "class");
}
-
+
mConstructorArgs[1] = attrs;
Object customView = mProjectCallback.loadView(name, mConstructorSignature,
mConstructorArgs);
-
+
if (customView instanceof View) {
return (View)customView;
}
@@ -207,14 +207,27 @@
return null;
}
-
-
-
+
+
+
private void setupViewInContext(View view, AttributeSet attrs) {
if (getContext() instanceof BridgeContext) {
BridgeContext bc = (BridgeContext) getContext();
if (attrs instanceof BridgeXmlBlockParser) {
- Object viewKey = ((BridgeXmlBlockParser) attrs).getViewKey();
+ BridgeXmlBlockParser parser = (BridgeXmlBlockParser) attrs;
+
+ // get the view key
+ Object viewKey = parser.getViewKey();
+
+ // if there's no view key and the depth is 1 (ie this is the first tag),
+ // look for a previous parser in the context, and check if this one has a viewkey.
+ if (viewKey == null && parser.getDepth() == 1) {
+ BridgeXmlBlockParser previousParser = bc.getPreviousParser();
+ if (previousParser != null) {
+ viewKey = previousParser.getViewKey();
+ }
+ }
+
if (viewKey != null) {
bc.addViewKey(view, viewKey);
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java
index bfbb01a8..1011173 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java
@@ -220,24 +220,37 @@
IResourceValue value = getResourceValue(id, mPlatformResourceFlag);
if (value != null) {
- File xml = new File(value.getValue());
- if (xml.isFile()) {
- // we need to create a pull parser around the layout XML file, and then
- // give that to our XmlBlockParser
- try {
- KXmlParser parser = new KXmlParser();
- parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
- parser.setInput(new FileReader(xml));
+ XmlPullParser parser = null;
- return new BridgeXmlBlockParser(parser, mContext, mPlatformResourceFlag[0]);
- } catch (XmlPullParserException e) {
- mContext.getLogger().error(e);
-
- // we'll return null below.
- } catch (FileNotFoundException e) {
- // this shouldn't happen since we check above.
+ try {
+ // check if the current parser can provide us with a custom parser.
+ BridgeXmlBlockParser currentParser = mContext.getCurrentParser();
+ if (currentParser != null) {
+ parser = currentParser.getParser(value.getName());
}
+
+ // create a new one manually if needed.
+ if (parser == null) {
+ File xml = new File(value.getValue());
+ if (xml.isFile()) {
+ // we need to create a pull parser around the layout XML file, and then
+ // give that to our XmlBlockParser
+ parser = new KXmlParser();
+ parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
+ parser.setInput(new FileReader(xml));
+ }
+ }
+
+ if (parser != null) {
+ return new BridgeXmlBlockParser(parser, mContext, mPlatformResourceFlag[0]);
+ }
+ } catch (XmlPullParserException e) {
+ mContext.getLogger().error(e);
+ // we'll return null below.
+ } catch (FileNotFoundException e) {
+ // this shouldn't happen since we check above.
}
+
}
// id was not found or not resolved. Throw a NotFoundException.
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParser.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParser.java
index 24f61c8..c3d0b14a 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParser.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParser.java
@@ -40,10 +40,9 @@
private XmlPullAttributes mAttrib;
private boolean mStarted = false;
- private boolean mDecNextDepth = false;
- private int mDepth = 0;
private int mEventType = START_DOCUMENT;
private final boolean mPlatformFile;
+ private final BridgeContext mContext;
/**
* Builds a {@link BridgeXmlBlockParser}.
@@ -53,14 +52,27 @@
*/
public BridgeXmlBlockParser(XmlPullParser parser, BridgeContext context, boolean platformFile) {
mParser = parser;
+ mContext = context;
mPlatformFile = platformFile;
mAttrib = new BridgeXmlPullAttributes(parser, context, mPlatformFile);
+
+ if (mContext != null) {
+ mContext.pushParser(this);
+ }
}
-
+
public boolean isPlatformFile() {
return mPlatformFile;
}
+ public IXmlPullParser getParser(String layoutName) {
+ if (mParser instanceof IXmlPullParser) {
+ return ((IXmlPullParser)mParser).getParser(layoutName);
+ }
+
+ return null;
+ }
+
public Object getViewKey() {
if (mParser instanceof IXmlPullParser) {
return ((IXmlPullParser)mParser).getViewKey();
@@ -68,10 +80,9 @@
return null;
}
-
-
+
// ------- XmlResourceParser implementation
-
+
public void setFeature(String name, boolean state)
throws XmlPullParserException {
if (FEATURE_PROCESS_NAMESPACES.equals(name) && state) {
@@ -145,7 +156,7 @@
}
public int getDepth() {
- return mDepth;
+ return mParser.getDepth();
}
public String getText() {
@@ -236,17 +247,10 @@
return START_DOCUMENT;
}
int ev = mParser.next();
- if (mDecNextDepth) {
- mDepth--;
- mDecNextDepth = false;
- }
- switch (ev) {
- case START_TAG:
- mDepth++;
- break;
- case END_TAG:
- mDecNextDepth = true;
- break;
+
+ if (ev == END_TAG && mParser.getDepth() == 1 && mContext != null) {
+ // done with parser remove it from the context stack.
+ mContext.popParser();
}
mEventType = ev;
return ev;
@@ -301,7 +305,7 @@
// AttributeSet implementation
-
+
public void close() {
// pass
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutSceneImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutSceneImpl.java
index b0316a3..2e3f9a8 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutSceneImpl.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutSceneImpl.java
@@ -25,6 +25,7 @@
import com.android.layoutlib.api.SceneResult;
import com.android.layoutlib.api.ViewInfo;
import com.android.layoutlib.api.IDensityBasedResourceValue.Density;
+import com.android.layoutlib.api.SceneParams.RenderingMode;
import com.android.layoutlib.bridge.BridgeConstants;
import com.android.layoutlib.bridge.android.BridgeContext;
import com.android.layoutlib.bridge.android.BridgeInflater;
@@ -259,22 +260,32 @@
int renderScreenWidth = mParams.getScreenWidth();
int renderScreenHeight = mParams.getScreenHeight();
- if (mParams.getRenderFullSize()) {
+ RenderingMode renderingMode = mParams.getRenderingMode();
+
+ if (renderingMode != RenderingMode.NORMAL) {
// measure the full size needed by the layout.
w_spec = MeasureSpec.makeMeasureSpec(renderScreenWidth,
- MeasureSpec.UNSPECIFIED); // this lets us know the actual needed size
+ renderingMode.isHorizExpand() ?
+ MeasureSpec.UNSPECIFIED // this lets us know the actual needed size
+ : MeasureSpec.EXACTLY);
h_spec = MeasureSpec.makeMeasureSpec(renderScreenHeight - mScreenOffset,
- MeasureSpec.UNSPECIFIED); // this lets us know the actual needed size
+ renderingMode.isVertExpand() ?
+ MeasureSpec.UNSPECIFIED // this lets us know the actual needed size
+ : MeasureSpec.EXACTLY);
mViewRoot.measure(w_spec, h_spec);
- int neededWidth = mViewRoot.getChildAt(0).getMeasuredWidth();
- if (neededWidth > renderScreenWidth) {
- renderScreenWidth = neededWidth;
+ if (renderingMode.isHorizExpand()) {
+ int neededWidth = mViewRoot.getChildAt(0).getMeasuredWidth();
+ if (neededWidth > renderScreenWidth) {
+ renderScreenWidth = neededWidth;
+ }
}
- int neededHeight = mViewRoot.getChildAt(0).getMeasuredHeight();
- if (neededHeight > renderScreenHeight - mScreenOffset) {
- renderScreenHeight = neededHeight + mScreenOffset;
+ if (renderingMode.isVertExpand()) {
+ int neededHeight = mViewRoot.getChildAt(0).getMeasuredHeight();
+ if (neededHeight > renderScreenHeight - mScreenOffset) {
+ renderScreenHeight = neededHeight + mScreenOffset;
+ }
}
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/Stack.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/Stack.java
new file mode 100644
index 0000000..9bd0015
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/Stack.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2010 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.layoutlib.bridge.impl;
+
+import java.util.ArrayList;
+
+/**
+ * Custom Stack implementation on top of an {@link ArrayList} instead of
+ * using {@link java.util.Stack} which is on top of a vector.
+ *
+ * @param <T>
+ */
+public class Stack<T> extends ArrayList<T> {
+
+ private static final long serialVersionUID = 1L;
+
+ public Stack() {
+ super();
+ }
+
+ public Stack(int size) {
+ super(size);
+ }
+
+ /**
+ * Pushes the given object to the stack
+ * @param object the object to push
+ */
+ public void push(T object) {
+ add(object);
+ }
+
+ /**
+ * Remove the object at the top of the stack and returns it.
+ * @return the removed object or null if the stack was empty.
+ */
+ public T pop() {
+ if (size() > 0) {
+ return remove(size() - 1);
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the object at the top of the stack.
+ * @return the object at the top or null if the stack is empty.
+ */
+ public T peek() {
+ if (size() > 0) {
+ return get(size() - 1);
+ }
+
+ return null;
+ }
+}
diff --git a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestClassReplacement.java b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestClassReplacement.java
index adb693d..a068ae2 100644
--- a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestClassReplacement.java
+++ b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestClassReplacement.java
@@ -25,11 +25,11 @@
public class TestClassReplacement extends TestCase {
public void testClassReplacements() {
- // TODO: we want to test all the classes. For now only Paint passes the tests.
+ // TODO: we want to test all the classes. For now only, no classes pass the test.
// final String[] classes = CreateInfo.RENAMED_CLASSES;
final String[] classes = new String[] {
- "android.graphics.Paint", "android.graphics._Original_Paint",
- "android.graphics.Canvas", "android.graphics._Original_Canvas",
+// "android.graphics.Paint", "android.graphics._Original_Paint",
+// "android.graphics.Canvas", "android.graphics._Original_Canvas",
};
final int count = classes.length;
for (int i = 0 ; i < count ; i += 2) {
diff --git a/wifi/java/android/net/wifi/SupplicantStateTracker.java b/wifi/java/android/net/wifi/SupplicantStateTracker.java
new file mode 100644
index 0000000..a83a0ad
--- /dev/null
+++ b/wifi/java/android/net/wifi/SupplicantStateTracker.java
@@ -0,0 +1,333 @@
+/*
+ * Copyright (C) 2010 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.net.wifi;
+
+import com.android.internal.util.HierarchicalState;
+import com.android.internal.util.HierarchicalStateMachine;
+
+import android.net.wifi.WifiStateMachine.StateChangeResult;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Parcelable;
+import android.util.Log;
+
+/**
+ * Tracks the state changes in supplicant and provides functionality
+ * that is based on these state changes:
+ * - detect a failed WPA handshake that loops indefinitely
+ * - password failure handling
+ * - Enable networks after a WPS success/failure
+ */
+class SupplicantStateTracker extends HierarchicalStateMachine {
+
+ private static final String TAG = "SupplicantStateTracker";
+ private static final boolean DBG = false;
+
+ private WifiStateMachine mWifiStateMachine;
+ private int mPasswordFailuresCount = 0;
+ /* Indicates authentication failure in supplicant broadcast.
+ * TODO: enhance auth failure reporting to include notification
+ * for all type of failures: EAP, WPS & WPA networks */
+ private boolean mAuthFailureInSupplicantBroadcast = false;
+
+ /* Maximum retries on a password failure notification */
+ private static final int MAX_RETRIES_ON_PASSWORD_FAILURE = 2;
+
+ /* Track if WPS was started since we need to re-enable networks
+ * and load configuration afterwards */
+ private boolean mWpsStarted = false;
+
+ private Context mContext;
+
+ private HierarchicalState mUninitializedState = new UninitializedState();
+ private HierarchicalState mDefaultState = new DefaultState();
+ private HierarchicalState mInactiveState = new InactiveState();
+ private HierarchicalState mDisconnectState = new DisconnectedState();
+ private HierarchicalState mScanState = new ScanState();
+ private HierarchicalState mHandshakeState = new HandshakeState();
+ private HierarchicalState mCompletedState = new CompletedState();
+ private HierarchicalState mDormantState = new DormantState();
+
+ public SupplicantStateTracker(Context context, WifiStateMachine wsm, Handler target) {
+ super(TAG, target.getLooper());
+
+ mContext = context;
+ mWifiStateMachine = wsm;
+ addState(mDefaultState);
+ addState(mUninitializedState, mDefaultState);
+ addState(mInactiveState, mDefaultState);
+ addState(mDisconnectState, mDefaultState);
+ addState(mScanState, mDefaultState);
+ addState(mHandshakeState, mDefaultState);
+ addState(mCompletedState, mDefaultState);
+ addState(mDormantState, mDefaultState);
+
+ setInitialState(mUninitializedState);
+
+ //start the state machine
+ start();
+ }
+
+ public void resetSupplicantState() {
+ transitionTo(mUninitializedState);
+ }
+
+
+ private void transitionOnSupplicantStateChange(StateChangeResult stateChangeResult) {
+ SupplicantState supState = (SupplicantState) stateChangeResult.state;
+
+ if (DBG) Log.d(TAG, "Supplicant state: " + supState.toString() + "\n");
+
+ switch (supState) {
+ case DISCONNECTED:
+ transitionTo(mDisconnectState);
+ break;
+ case SCANNING:
+ transitionTo(mScanState);
+ break;
+ case ASSOCIATING:
+ case ASSOCIATED:
+ case FOUR_WAY_HANDSHAKE:
+ case GROUP_HANDSHAKE:
+ transitionTo(mHandshakeState);
+ break;
+ case COMPLETED:
+ transitionTo(mCompletedState);
+ break;
+ case DORMANT:
+ transitionTo(mDormantState);
+ break;
+ case INACTIVE:
+ transitionTo(mInactiveState);
+ break;
+ case UNINITIALIZED:
+ case INVALID:
+ transitionTo(mUninitializedState);
+ break;
+ default:
+ Log.e(TAG, "Unknown supplicant state " + supState);
+ break;
+ }
+ }
+
+ private void sendSupplicantStateChangedBroadcast(StateChangeResult sc, boolean failedAuth) {
+ Intent intent = new Intent(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
+ | Intent.FLAG_RECEIVER_REPLACE_PENDING);
+ intent.putExtra(WifiManager.EXTRA_NEW_STATE, (Parcelable)sc.state);
+ if (failedAuth) {
+ intent.putExtra(
+ WifiManager.EXTRA_SUPPLICANT_ERROR,
+ WifiManager.ERROR_AUTHENTICATING);
+ }
+ mContext.sendStickyBroadcast(intent);
+ }
+
+ /********************************************************
+ * HSM states
+ *******************************************************/
+
+ class DefaultState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ switch (message.what) {
+ case WifiStateMachine.PASSWORD_MAY_BE_INCORRECT_EVENT:
+ mPasswordFailuresCount++;
+ mAuthFailureInSupplicantBroadcast = true;
+ break;
+ case WifiStateMachine.CMD_START_WPS_PBC:
+ case WifiStateMachine.CMD_START_WPS_PIN_FROM_AP:
+ case WifiStateMachine.CMD_START_WPS_PIN_FROM_DEVICE:
+ mWpsStarted = true;
+ break;
+ case WifiStateMachine.SUPPLICANT_STATE_CHANGE_EVENT:
+ StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
+ sendSupplicantStateChangedBroadcast(stateChangeResult,
+ mAuthFailureInSupplicantBroadcast);
+ mAuthFailureInSupplicantBroadcast = false;
+ transitionOnSupplicantStateChange(stateChangeResult);
+ break;
+ default:
+ Log.e(TAG, "Ignoring " + message);
+ break;
+ }
+ return HANDLED;
+ }
+ }
+
+ class UninitializedState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ mWifiStateMachine.setNetworkAvailable(false);
+ }
+ @Override
+ public void exit() {
+ mWifiStateMachine.setNetworkAvailable(true);
+ }
+ }
+
+ class InactiveState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ /* A failed WPS connection */
+ if (mWpsStarted) {
+ Log.e(TAG, "WPS set up failed, enabling other networks");
+ WifiConfigStore.enableAllNetworks();
+ mWpsStarted = false;
+ }
+ mWifiStateMachine.setNetworkAvailable(false);
+ }
+ @Override
+ public void exit() {
+ mWifiStateMachine.setNetworkAvailable(true);
+ }
+ }
+
+
+ class DisconnectedState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ /* If a disconnect event happens after password key failure
+ * exceeds maximum retries, disable the network
+ */
+
+ Message message = getCurrentMessage();
+ StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
+
+ if (mPasswordFailuresCount >= MAX_RETRIES_ON_PASSWORD_FAILURE) {
+ Log.d(TAG, "Failed to authenticate, disabling network " +
+ stateChangeResult.networkId);
+ WifiConfigStore.disableNetwork(stateChangeResult.networkId);
+ mPasswordFailuresCount = 0;
+ }
+ }
+ }
+
+ class ScanState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ }
+ }
+
+ class HandshakeState extends HierarchicalState {
+ /**
+ * The max number of the WPA supplicant loop iterations before we
+ * decide that the loop should be terminated:
+ */
+ private static final int MAX_SUPPLICANT_LOOP_ITERATIONS = 4;
+ private int mLoopDetectIndex;
+ private int mLoopDetectCount;
+
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ mLoopDetectIndex = 0;
+ mLoopDetectCount = 0;
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ switch (message.what) {
+ case WifiStateMachine.SUPPLICANT_STATE_CHANGE_EVENT:
+ StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
+ SupplicantState state = (SupplicantState) stateChangeResult.state;
+ if (state == SupplicantState.ASSOCIATING ||
+ state == SupplicantState.ASSOCIATED ||
+ state == SupplicantState.FOUR_WAY_HANDSHAKE ||
+ state == SupplicantState.GROUP_HANDSHAKE) {
+ if (mLoopDetectIndex > state.ordinal()) {
+ mLoopDetectCount++;
+ }
+ if (mLoopDetectCount > MAX_SUPPLICANT_LOOP_ITERATIONS) {
+ Log.d(TAG, "Supplicant loop detected, disabling network " +
+ stateChangeResult.networkId);
+ WifiConfigStore.disableNetwork(stateChangeResult.networkId);
+ }
+ mLoopDetectIndex = state.ordinal();
+ sendSupplicantStateChangedBroadcast(stateChangeResult,
+ mAuthFailureInSupplicantBroadcast);
+ } else {
+ //Have the DefaultState handle the transition
+ return NOT_HANDLED;
+ }
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return HANDLED;
+ }
+ }
+
+ class CompletedState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ /* Reset password failure count */
+ mPasswordFailuresCount = 0;
+
+ /* A successful WPS connection */
+ if (mWpsStarted) {
+ WifiConfigStore.enableAllNetworks();
+ WifiConfigStore.loadConfiguredNetworks();
+ mWpsStarted = false;
+ }
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ switch(message.what) {
+ case WifiStateMachine.SUPPLICANT_STATE_CHANGE_EVENT:
+ StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
+ SupplicantState state = (SupplicantState) stateChangeResult.state;
+ sendSupplicantStateChangedBroadcast(stateChangeResult,
+ mAuthFailureInSupplicantBroadcast);
+ /* Ignore a re-auth in completed state */
+ if (state == SupplicantState.ASSOCIATING ||
+ state == SupplicantState.ASSOCIATED ||
+ state == SupplicantState.FOUR_WAY_HANDSHAKE ||
+ state == SupplicantState.GROUP_HANDSHAKE ||
+ state == SupplicantState.COMPLETED) {
+ break;
+ }
+ transitionOnSupplicantStateChange(stateChangeResult);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return HANDLED;
+ }
+ }
+
+ //TODO: remove after getting rid of the state in supplicant
+ class DormantState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ }
+ }
+}
\ No newline at end of file
diff --git a/wifi/java/android/net/wifi/WifiConfigStore.java b/wifi/java/android/net/wifi/WifiConfigStore.java
index 04b3891..24f6f31 100644
--- a/wifi/java/android/net/wifi/WifiConfigStore.java
+++ b/wifi/java/android/net/wifi/WifiConfigStore.java
@@ -595,9 +595,9 @@
out.writeUTF(PROXY_SETTINGS_KEY);
out.writeUTF(config.proxySettings.toString());
out.writeUTF(PROXY_HOST_KEY);
- out.writeUTF(proxyProperties.getSocketAddress().getHostName());
+ out.writeUTF(proxyProperties.getHost());
out.writeUTF(PROXY_PORT_KEY);
- out.writeInt(proxyProperties.getSocketAddress().getPort());
+ out.writeInt(proxyProperties.getPort());
out.writeUTF(EXCLUSION_LIST_KEY);
out.writeUTF(exclusionList);
writeToFile = true;
@@ -716,10 +716,8 @@
switch (proxySettings) {
case STATIC:
config.proxySettings = proxySettings;
- ProxyProperties proxyProperties = new ProxyProperties();
- proxyProperties.setSocketAddress(
- new InetSocketAddress(proxyHost, proxyPort));
- proxyProperties.setExclusionList(exclusionList);
+ ProxyProperties proxyProperties =
+ new ProxyProperties(proxyHost, proxyPort, exclusionList);
linkProperties.setHttpProxy(proxyProperties);
break;
case NONE:
@@ -1012,42 +1010,13 @@
switch (newConfig.proxySettings) {
case STATIC:
- InetSocketAddress newSockAddr = null;
- String newExclusionList = null;
- InetSocketAddress currentSockAddr = null;
- String currentExclusionList = null;
-
ProxyProperties newHttpProxy = newConfig.linkProperties.getHttpProxy();
- if (newHttpProxy != null) {
- newSockAddr = newHttpProxy.getSocketAddress();
- newExclusionList = newHttpProxy.getExclusionList();
- }
-
ProxyProperties currentHttpProxy = currentConfig.linkProperties.getHttpProxy();
- if (currentHttpProxy != null) {
- currentSockAddr = currentHttpProxy.getSocketAddress();
- currentExclusionList = currentHttpProxy.getExclusionList();
- }
- boolean socketAddressDiffers = false;
- boolean exclusionListDiffers = false;
-
- if (newSockAddr != null && currentSockAddr != null ) {
- socketAddressDiffers = !currentSockAddr.equals(newSockAddr);
- } else if (newSockAddr != null || currentSockAddr != null) {
- socketAddressDiffers = true;
- }
-
- if (newExclusionList != null && currentExclusionList != null) {
- exclusionListDiffers = !currentExclusionList.equals(newExclusionList);
- } else if (newExclusionList != null || currentExclusionList != null) {
- exclusionListDiffers = true;
- }
-
- if ((currentConfig.proxySettings != newConfig.proxySettings) ||
- socketAddressDiffers ||
- exclusionListDiffers) {
- proxyChanged = true;
+ if (newHttpProxy != null) {
+ proxyChanged = !newHttpProxy.equals(currentHttpProxy);
+ } else {
+ proxyChanged = (currentHttpProxy != null);
}
break;
case NONE:
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index 95e2df3..90abd02 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -49,8 +49,6 @@
import android.net.LinkProperties;
import android.os.Binder;
import android.os.Message;
-import android.os.Parcelable;
-import android.os.Handler;
import android.os.IBinder;
import android.os.INetworkManagementService;
import android.os.PowerManager;
@@ -93,8 +91,6 @@
*
* @hide
*/
-//TODO: we still need frequent scanning for the case when
-// we issue disconnect but need scan results for open network notification
public class WifiStateMachine extends HierarchicalStateMachine {
private static final String TAG = "WifiStateMachine";
@@ -120,11 +116,17 @@
private String mLastBssid;
private int mLastNetworkId;
private boolean mEnableRssiPolling = false;
- private boolean mPasswordKeyMayBeIncorrect = false;
+ private int mRssiPollToken = 0;
private int mReconnectCount = 0;
private boolean mIsScanMode = false;
/**
+ * Interval in milliseconds between polling for RSSI
+ * and linkspeed information
+ */
+ private static final int POLL_RSSI_INTERVAL_MSECS = 3000;
+
+ /**
* Instance of the bluetooth headset helper. This needs to be created
* early because there is a delay before it actually 'connects', as
* noted by its javadoc. If we check before it is connected, it will be
@@ -148,9 +150,6 @@
/* Connection to a specific network involves disabling all networks,
* this flag tracks if networks need to be re-enabled */
private boolean mEnableAllNetworks = false;
- /* Track if WPS was started since we need to re-enable networks
- * and load configuration afterwards */
- private boolean mWpsStarted = false;
private AlarmManager mAlarmManager;
private PendingIntent mScanIntent;
@@ -166,95 +165,95 @@
private static final int EVENTLOG_SUPPLICANT_STATE_CHANGED = 50023;
/* Load the driver */
- private static final int CMD_LOAD_DRIVER = 1;
+ static final int CMD_LOAD_DRIVER = 1;
/* Unload the driver */
- private static final int CMD_UNLOAD_DRIVER = 2;
+ static final int CMD_UNLOAD_DRIVER = 2;
/* Indicates driver load succeeded */
- private static final int CMD_LOAD_DRIVER_SUCCESS = 3;
+ static final int CMD_LOAD_DRIVER_SUCCESS = 3;
/* Indicates driver load failed */
- private static final int CMD_LOAD_DRIVER_FAILURE = 4;
+ static final int CMD_LOAD_DRIVER_FAILURE = 4;
/* Indicates driver unload succeeded */
- private static final int CMD_UNLOAD_DRIVER_SUCCESS = 5;
+ static final int CMD_UNLOAD_DRIVER_SUCCESS = 5;
/* Indicates driver unload failed */
- private static final int CMD_UNLOAD_DRIVER_FAILURE = 6;
+ static final int CMD_UNLOAD_DRIVER_FAILURE = 6;
/* Set bluetooth headset proxy */
- private static final int CMD_SET_BLUETOOTH_HEADSET_PROXY = 7;
+ static final int CMD_SET_BLUETOOTH_HEADSET_PROXY = 7;
/* Set bluetooth A2dp proxy */
- private static final int CMD_SET_BLUETOOTH_A2DP_PROXY = 8;
+ static final int CMD_SET_BLUETOOTH_A2DP_PROXY = 8;
/* Start the supplicant */
- private static final int CMD_START_SUPPLICANT = 11;
+ static final int CMD_START_SUPPLICANT = 11;
/* Stop the supplicant */
- private static final int CMD_STOP_SUPPLICANT = 12;
+ static final int CMD_STOP_SUPPLICANT = 12;
/* Start the driver */
- private static final int CMD_START_DRIVER = 13;
+ static final int CMD_START_DRIVER = 13;
/* Start the driver */
- private static final int CMD_STOP_DRIVER = 14;
+ static final int CMD_STOP_DRIVER = 14;
/* Indicates DHCP succeded */
- private static final int CMD_IP_CONFIG_SUCCESS = 15;
+ static final int CMD_IP_CONFIG_SUCCESS = 15;
/* Indicates DHCP failed */
- private static final int CMD_IP_CONFIG_FAILURE = 16;
+ static final int CMD_IP_CONFIG_FAILURE = 16;
/* Re-configure interface */
- private static final int CMD_RECONFIGURE_IP = 17;
+ static final int CMD_RECONFIGURE_IP = 17;
/* Start the soft access point */
- private static final int CMD_START_AP = 21;
+ static final int CMD_START_AP = 21;
/* Stop the soft access point */
- private static final int CMD_STOP_AP = 22;
+ static final int CMD_STOP_AP = 22;
/* Supplicant events */
/* Connection to supplicant established */
- private static final int SUP_CONNECTION_EVENT = 31;
+ static final int SUP_CONNECTION_EVENT = 31;
/* Connection to supplicant lost */
- private static final int SUP_DISCONNECTION_EVENT = 32;
+ static final int SUP_DISCONNECTION_EVENT = 32;
/* Driver start completed */
- private static final int DRIVER_START_EVENT = 33;
+ static final int DRIVER_START_EVENT = 33;
/* Driver stop completed */
- private static final int DRIVER_STOP_EVENT = 34;
+ static final int DRIVER_STOP_EVENT = 34;
/* Network connection completed */
- private static final int NETWORK_CONNECTION_EVENT = 36;
+ static final int NETWORK_CONNECTION_EVENT = 36;
/* Network disconnection completed */
- private static final int NETWORK_DISCONNECTION_EVENT = 37;
+ static final int NETWORK_DISCONNECTION_EVENT = 37;
/* Scan results are available */
- private static final int SCAN_RESULTS_EVENT = 38;
+ static final int SCAN_RESULTS_EVENT = 38;
/* Supplicate state changed */
- private static final int SUPPLICANT_STATE_CHANGE_EVENT = 39;
+ static final int SUPPLICANT_STATE_CHANGE_EVENT = 39;
/* Password may be incorrect */
- private static final int PASSWORD_MAY_BE_INCORRECT_EVENT = 40;
+ static final int PASSWORD_MAY_BE_INCORRECT_EVENT = 40;
/* Supplicant commands */
/* Is supplicant alive ? */
- private static final int CMD_PING_SUPPLICANT = 51;
+ static final int CMD_PING_SUPPLICANT = 51;
/* Add/update a network configuration */
- private static final int CMD_ADD_OR_UPDATE_NETWORK = 52;
+ static final int CMD_ADD_OR_UPDATE_NETWORK = 52;
/* Delete a network */
- private static final int CMD_REMOVE_NETWORK = 53;
+ static final int CMD_REMOVE_NETWORK = 53;
/* Enable a network. The device will attempt a connection to the given network. */
- private static final int CMD_ENABLE_NETWORK = 54;
+ static final int CMD_ENABLE_NETWORK = 54;
/* Disable a network. The device does not attempt a connection to the given network. */
- private static final int CMD_DISABLE_NETWORK = 55;
+ static final int CMD_DISABLE_NETWORK = 55;
/* Blacklist network. De-prioritizes the given BSSID for connection. */
- private static final int CMD_BLACKLIST_NETWORK = 56;
+ static final int CMD_BLACKLIST_NETWORK = 56;
/* Clear the blacklist network list */
- private static final int CMD_CLEAR_BLACKLIST = 57;
+ static final int CMD_CLEAR_BLACKLIST = 57;
/* Save configuration */
- private static final int CMD_SAVE_CONFIG = 58;
+ static final int CMD_SAVE_CONFIG = 58;
/* Supplicant commands after driver start*/
/* Initiate a scan */
- private static final int CMD_START_SCAN = 71;
+ static final int CMD_START_SCAN = 71;
/* Set scan mode. CONNECT_MODE or SCAN_ONLY_MODE */
- private static final int CMD_SET_SCAN_MODE = 72;
+ static final int CMD_SET_SCAN_MODE = 72;
/* Set scan type. SCAN_ACTIVE or SCAN_PASSIVE */
- private static final int CMD_SET_SCAN_TYPE = 73;
+ static final int CMD_SET_SCAN_TYPE = 73;
/* Disconnect from a network */
- private static final int CMD_DISCONNECT = 74;
+ static final int CMD_DISCONNECT = 74;
/* Reconnect to a network */
- private static final int CMD_RECONNECT = 75;
+ static final int CMD_RECONNECT = 75;
/* Reassociate to a network */
- private static final int CMD_REASSOCIATE = 76;
+ static final int CMD_REASSOCIATE = 76;
/* Controls power mode and suspend mode optimizations
*
* When high perf mode is enabled, power mode is set to
@@ -268,30 +267,30 @@
* - turn off roaming
* - DTIM wake up settings
*/
- private static final int CMD_SET_HIGH_PERF_MODE = 77;
+ static final int CMD_SET_HIGH_PERF_MODE = 77;
/* Set bluetooth co-existence
* BLUETOOTH_COEXISTENCE_MODE_ENABLED
* BLUETOOTH_COEXISTENCE_MODE_DISABLED
* BLUETOOTH_COEXISTENCE_MODE_SENSE
*/
- private static final int CMD_SET_BLUETOOTH_COEXISTENCE = 78;
+ static final int CMD_SET_BLUETOOTH_COEXISTENCE = 78;
/* Enable/disable bluetooth scan mode
* true(1)
* false(0)
*/
- private static final int CMD_SET_BLUETOOTH_SCAN_MODE = 79;
+ static final int CMD_SET_BLUETOOTH_SCAN_MODE = 79;
/* Set the country code */
- private static final int CMD_SET_COUNTRY_CODE = 80;
+ static final int CMD_SET_COUNTRY_CODE = 80;
/* Request connectivity manager wake lock before driver stop */
- private static final int CMD_REQUEST_CM_WAKELOCK = 81;
+ static final int CMD_REQUEST_CM_WAKELOCK = 81;
/* Enables RSSI poll */
- private static final int CMD_ENABLE_RSSI_POLL = 82;
+ static final int CMD_ENABLE_RSSI_POLL = 82;
/* RSSI poll */
- private static final int CMD_RSSI_POLL = 83;
+ static final int CMD_RSSI_POLL = 83;
/* Set up packet filtering */
- private static final int CMD_START_PACKET_FILTERING = 84;
+ static final int CMD_START_PACKET_FILTERING = 84;
/* Clear packet filter */
- private static final int CMD_STOP_PACKET_FILTERING = 85;
+ static final int CMD_STOP_PACKET_FILTERING = 85;
/* Connect to a specified network (network id
* or WifiConfiguration) This involves increasing
* the priority of the network, enabling the network
@@ -300,32 +299,29 @@
* an existing network. All the networks get enabled
* upon a successful connection or a failure.
*/
- private static final int CMD_CONNECT_NETWORK = 86;
+ static final int CMD_CONNECT_NETWORK = 86;
/* Save the specified network. This involves adding
* an enabled network (if new) and updating the
* config and issuing a save on supplicant config.
*/
- private static final int CMD_SAVE_NETWORK = 87;
+ static final int CMD_SAVE_NETWORK = 87;
/* Delete the specified network. This involves
* removing the network and issuing a save on
* supplicant config.
*/
- private static final int CMD_FORGET_NETWORK = 88;
+ static final int CMD_FORGET_NETWORK = 88;
/* Start Wi-Fi protected setup push button configuration */
- private static final int CMD_START_WPS_PBC = 89;
+ static final int CMD_START_WPS_PBC = 89;
/* Start Wi-Fi protected setup pin method configuration with pin obtained from AP */
- private static final int CMD_START_WPS_PIN_FROM_AP = 90;
+ static final int CMD_START_WPS_PIN_FROM_AP = 90;
/* Start Wi-Fi protected setup pin method configuration with pin obtained from device */
- private static final int CMD_START_WPS_PIN_FROM_DEVICE = 91;
+ static final int CMD_START_WPS_PIN_FROM_DEVICE = 91;
/* Set the frequency band */
- private static final int CMD_SET_FREQUENCY_BAND = 92;
+ static final int CMD_SET_FREQUENCY_BAND = 92;
- /**
- * Interval in milliseconds between polling for connection
- * status items that are not sent via asynchronous events.
- * An example is RSSI (signal strength).
- */
- private static final int POLL_RSSI_INTERVAL_MSECS = 3000;
+ /* Commands from the SupplicantStateTracker */
+ /* Indicates whether a wifi network is available for connection */
+ static final int CMD_SET_NETWORK_AVAILABLE = 111;
private static final int CONNECT_MODE = 1;
private static final int SCAN_ONLY_MODE = 2;
@@ -463,7 +459,7 @@
mDhcpInfo = new DhcpInfo();
mWifiInfo = new WifiInfo();
mInterfaceName = SystemProperties.get("wifi.interface", "tiwlan0");
- mSupplicantStateTracker = new SupplicantStateTracker(context, getHandler());
+ mSupplicantStateTracker = new SupplicantStateTracker(context, this, getHandler());
mLinkProperties = new LinkProperties();
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
@@ -985,8 +981,6 @@
sb.append("mLastBssid ").append(mLastBssid).append(LS);
sb.append("mLastNetworkId ").append(mLastNetworkId).append(LS);
sb.append("mEnableAllNetworks ").append(mEnableAllNetworks).append(LS);
- sb.append("mEnableRssiPolling ").append(mEnableRssiPolling).append(LS);
- sb.append("mPasswordKeyMayBeIncorrect ").append(mPasswordKeyMayBeIncorrect).append(LS);
sb.append("mReconnectCount ").append(mReconnectCount).append(LS);
sb.append("mIsScanMode ").append(mIsScanMode).append(LS);
sb.append("Supplicant status").append(LS)
@@ -1215,6 +1209,44 @@
return null;
}
+ /*
+ * Fetch RSSI and linkspeed on current connection
+ */
+ private void fetchRssiAndLinkSpeedNative() {
+ int newRssi = WifiNative.getRssiCommand();
+ if (newRssi != -1 && -200 < newRssi && newRssi < 256) { // screen out invalid values
+ /* some implementations avoid negative values by adding 256
+ * so we need to adjust for that here.
+ */
+ if (newRssi > 0) newRssi -= 256;
+ mWifiInfo.setRssi(newRssi);
+ /*
+ * Rather then sending the raw RSSI out every time it
+ * changes, we precalculate the signal level that would
+ * be displayed in the status bar, and only send the
+ * broadcast if that much more coarse-grained number
+ * changes. This cuts down greatly on the number of
+ * broadcasts, at the cost of not mWifiInforming others
+ * interested in RSSI of all the changes in signal
+ * level.
+ */
+ // TODO: The second arg to the call below needs to be a symbol somewhere, but
+ // it's actually the size of an array of icons that's private
+ // to StatusBar Policy.
+ int newSignalLevel = WifiManager.calculateSignalLevel(newRssi, 4);
+ if (newSignalLevel != mLastSignalLevel) {
+ sendRssiChangeBroadcast(newRssi);
+ }
+ mLastSignalLevel = newSignalLevel;
+ } else {
+ mWifiInfo.setRssi(-200);
+ }
+ int newLinkSpeed = WifiNative.getLinkSpeedCommand();
+ if (newLinkSpeed != -1) {
+ mWifiInfo.setLinkSpeed(newLinkSpeed);
+ }
+ }
+
private void setHighPerfModeEnabledNative(boolean enable) {
if(!WifiNative.setSuspendOptimizationsCommand(!enable)) {
Log.e(TAG, "set suspend optimizations failed!");
@@ -1336,19 +1368,6 @@
mContext.sendBroadcast(intent);
}
- private void sendSupplicantStateChangedBroadcast(StateChangeResult sc, boolean failedAuth) {
- Intent intent = new Intent(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
- | Intent.FLAG_RECEIVER_REPLACE_PENDING);
- intent.putExtra(WifiManager.EXTRA_NEW_STATE, (Parcelable)sc.state);
- if (failedAuth) {
- intent.putExtra(
- WifiManager.EXTRA_SUPPLICANT_ERROR,
- WifiManager.ERROR_AUTHENTICATING);
- }
- mContext.sendStickyBroadcast(intent);
- }
-
private void sendSupplicantConnectionChangedBroadcast(boolean connected) {
if (!ActivityManagerNative.isSystemReady()) return;
@@ -1370,45 +1389,6 @@
}
/**
- * Poll for info not reported via events
- * RSSI & Linkspeed
- */
- private void requestPolledInfo() {
- int newRssi = WifiNative.getRssiCommand();
- if (newRssi != -1 && -200 < newRssi && newRssi < 256) { // screen out invalid values
- /* some implementations avoid negative values by adding 256
- * so we need to adjust for that here.
- */
- if (newRssi > 0) newRssi -= 256;
- mWifiInfo.setRssi(newRssi);
- /*
- * Rather then sending the raw RSSI out every time it
- * changes, we precalculate the signal level that would
- * be displayed in the status bar, and only send the
- * broadcast if that much more coarse-grained number
- * changes. This cuts down greatly on the number of
- * broadcasts, at the cost of not mWifiInforming others
- * interested in RSSI of all the changes in signal
- * level.
- */
- // TODO: The second arg to the call below needs to be a symbol somewhere, but
- // it's actually the size of an array of icons that's private
- // to StatusBar Policy.
- int newSignalLevel = WifiManager.calculateSignalLevel(newRssi, 4);
- if (newSignalLevel != mLastSignalLevel) {
- sendRssiChangeBroadcast(newRssi);
- }
- mLastSignalLevel = newSignalLevel;
- } else {
- mWifiInfo.setRssi(-200);
- }
- int newLinkSpeed = WifiNative.getLinkSpeedCommand();
- if (newLinkSpeed != -1) {
- mWifiInfo.setLinkSpeed(newLinkSpeed);
- }
- }
-
- /**
* Resets the Wi-Fi Connections by clearing any state, resetting any sockets
* using the interface, stopping DHCP & disabling interface
*/
@@ -1456,7 +1436,7 @@
* WifiMonitor
* thread.
*/
- private static class StateChangeResult {
+ static class StateChangeResult {
StateChangeResult(int networkId, String BSSID, Object state) {
this.state = state;
this.BSSID = BSSID;
@@ -1548,6 +1528,9 @@
setWifiEnabled(true);
}
+ void setNetworkAvailable(boolean available) {
+ sendMessage(CMD_SET_NETWORK_AVAILABLE, available ? 1 : 0);
+ }
/********************************************************
* HSM states
@@ -1576,7 +1559,9 @@
break;
case CMD_ENABLE_RSSI_POLL:
mEnableRssiPolling = (message.arg1 == 1);
- mSupplicantStateTracker.sendMessage(CMD_ENABLE_RSSI_POLL);
+ break;
+ case CMD_SET_NETWORK_AVAILABLE:
+ mNetworkInfo.setIsAvailable(message.arg1 == 1);
break;
/* Discard */
case CMD_LOAD_DRIVER:
@@ -1616,6 +1601,7 @@
case CMD_FORGET_NETWORK:
case CMD_START_WPS_PBC:
case CMD_START_WPS_PIN_FROM_AP:
+ case CMD_RSSI_POLL:
break;
default:
Log.e(TAG, "Error! unhandled message" + message);
@@ -2136,6 +2122,9 @@
WifiNative.setScanModeCommand(false);
}
break;
+ case CMD_START_SCAN:
+ WifiNative.scanCommand(message.arg1 == SCAN_ACTIVE);
+ break;
case CMD_SET_HIGH_PERF_MODE:
setHighPerfModeEnabledNative(message.arg1 == 1);
break;
@@ -2278,9 +2267,6 @@
transitionTo(mDisconnectedState);
}
break;
- case CMD_START_SCAN:
- WifiNative.scanCommand(message.arg1 == SCAN_ACTIVE);
- break;
/* Ignore */
case CMD_DISCONNECT:
case CMD_RECONNECT:
@@ -2309,14 +2295,23 @@
StateChangeResult stateChangeResult;
switch(message.what) {
case PASSWORD_MAY_BE_INCORRECT_EVENT:
- mPasswordKeyMayBeIncorrect = true;
+ mSupplicantStateTracker.sendMessage(PASSWORD_MAY_BE_INCORRECT_EVENT);
break;
case SUPPLICANT_STATE_CHANGE_EVENT:
stateChangeResult = (StateChangeResult) message.obj;
- mSupplicantStateTracker.handleEvent(stateChangeResult);
- break;
- case CMD_START_SCAN:
- /* We need to set scan type in completed state */
+ SupplicantState state = (SupplicantState) stateChangeResult.state;
+ // Supplicant state change
+ // [31-13] Reserved for future use
+ // [8 - 0] Supplicant state (as defined in SupplicantState.java)
+ // 50023 supplicant_state_changed (custom|1|5)
+ EventLog.writeEvent(EVENTLOG_SUPPLICANT_STATE_CHANGED, state.ordinal());
+ mWifiInfo.setSupplicantState(state);
+ mWifiInfo.setNetworkId(stateChangeResult.networkId);
+ if (state == SupplicantState.ASSOCIATING) {
+ /* BSSID is valid only in ASSOCIATING state */
+ mWifiInfo.setBSSID(stateChangeResult.BSSID);
+ }
+
Message newMsg = obtainMessage();
newMsg.copyFrom(message);
mSupplicantStateTracker.sendMessage(newMsg);
@@ -2375,7 +2370,7 @@
* Upon success, the configuration list needs to be reloaded
*/
if (success) {
- mWpsStarted = true;
+ mSupplicantStateTracker.sendMessage(message.what);
/* Expect a disconnection from the old connection */
transitionTo(mDisconnectingState);
}
@@ -2388,7 +2383,7 @@
success = WifiConfigStore.startWpsWithPinFromAccessPoint(bssid, apPin);
if (success) {
- mWpsStarted = true;
+ mSupplicantStateTracker.sendMessage(message.what);
/* Expect a disconnection from the old connection */
transitionTo(mDisconnectingState);
}
@@ -2400,7 +2395,7 @@
mReplyChannel.replyToMessage(message, CMD_START_WPS_PIN_FROM_DEVICE, pin);
if (success) {
- mWpsStarted = true;
+ mSupplicantStateTracker.sendMessage(message.what);
/* Expect a disconnection from the old connection */
transitionTo(mDisconnectingState);
}
@@ -2583,6 +2578,10 @@
deferMessage(message);
}
break;
+ /* Defer scan when IP is being fetched */
+ case CMD_START_SCAN:
+ deferMessage(message);
+ break;
case CMD_RECONFIGURE_IP:
deferMessage(message);
break;
@@ -2619,13 +2618,11 @@
@Override
public void enter() {
if (DBG) Log.d(TAG, getName() + "\n");
- /* A successful WPS connection */
- if (mWpsStarted) {
- WifiConfigStore.enableAllNetworks();
- WifiConfigStore.loadConfiguredNetworks();
- mWpsStarted = false;
- }
EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ mRssiPollToken++;
+ if (mEnableRssiPolling) {
+ sendMessage(obtainMessage(WifiStateMachine.CMD_RSSI_POLL, mRssiPollToken, 0));
+ }
}
@Override
public boolean processMessage(Message message) {
@@ -2650,6 +2647,15 @@
deferMessage(message);
}
break;
+ case CMD_START_SCAN:
+ /* When the network is connected, re-scanning can trigger
+ * a reconnection. Put it in scan-only mode during scan.
+ * When scan results are received, the mode is switched
+ * back to CONNECT_MODE.
+ */
+ WifiNative.setScanResultHandlingCommand(SCAN_ONLY_MODE);
+ WifiNative.scanCommand(message.arg1 == SCAN_ACTIVE);
+ break;
/* Ignore connection to same network */
case CMD_CONNECT_NETWORK:
int netId = message.arg1;
@@ -2660,6 +2666,26 @@
/* Ignore */
case NETWORK_CONNECTION_EVENT:
break;
+ case CMD_RSSI_POLL:
+ if (message.arg1 == mRssiPollToken) {
+ // Get Info and continue polling
+ fetchRssiAndLinkSpeedNative();
+ sendMessageDelayed(obtainMessage(WifiStateMachine.CMD_RSSI_POLL,
+ mRssiPollToken, 0), POLL_RSSI_INTERVAL_MSECS);
+ } else {
+ // Polling has completed
+ }
+ break;
+ case CMD_ENABLE_RSSI_POLL:
+ mEnableRssiPolling = (message.arg1 == 1);
+ mRssiPollToken++;
+ if (mEnableRssiPolling) {
+ // first poll
+ fetchRssiAndLinkSpeedNative();
+ sendMessageDelayed(obtainMessage(WifiStateMachine.CMD_RSSI_POLL,
+ mRssiPollToken, 0), POLL_RSSI_INTERVAL_MSECS);
+ }
+ break;
default:
return NOT_HANDLED;
}
@@ -2686,6 +2712,10 @@
deferMessage(message);
}
break;
+ /* Handle in DisconnectedState */
+ case SUPPLICANT_STATE_CHANGE_EVENT:
+ deferMessage(message);
+ break;
default:
return NOT_HANDLED;
}
@@ -2734,6 +2764,12 @@
/* Ignore network disconnect */
case NETWORK_DISCONNECTION_EVENT:
break;
+ case SUPPLICANT_STATE_CHANGE_EVENT:
+ StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
+ SupplicantState state = (SupplicantState) stateChangeResult.state;
+ setDetailedState(WifiInfo.getDetailedStateOf(state));
+ /* DriverStartedState does the rest of the handling */
+ return NOT_HANDLED;
default:
return NOT_HANDLED;
}
@@ -2795,375 +2831,4 @@
return HANDLED;
}
}
-
-
- class SupplicantStateTracker extends HierarchicalStateMachine {
-
- private int mRssiPollToken = 0;
-
- /**
- * The max number of the WPA supplicant loop iterations before we
- * decide that the loop should be terminated:
- */
- private static final int MAX_SUPPLICANT_LOOP_ITERATIONS = 4;
- private int mLoopDetectIndex = 0;
- private int mLoopDetectCount = 0;
-
- /**
- * Supplicant state change commands follow
- * the ordinal values defined in SupplicantState.java
- */
- private static final int DISCONNECTED = 0;
- private static final int INACTIVE = 1;
- private static final int SCANNING = 2;
- private static final int ASSOCIATING = 3;
- private static final int ASSOCIATED = 4;
- private static final int FOUR_WAY_HANDSHAKE = 5;
- private static final int GROUP_HANDSHAKE = 6;
- private static final int COMPLETED = 7;
- private static final int DORMANT = 8;
- private static final int UNINITIALIZED = 9;
- private static final int INVALID = 10;
-
- private HierarchicalState mUninitializedState = new UninitializedState();
- private HierarchicalState mInitializedState = new InitializedState();;
- private HierarchicalState mInactiveState = new InactiveState();
- private HierarchicalState mDisconnectState = new DisconnectedState();
- private HierarchicalState mScanState = new ScanState();
- private HierarchicalState mConnectState = new ConnectState();
- private HierarchicalState mHandshakeState = new HandshakeState();
- private HierarchicalState mCompletedState = new CompletedState();
- private HierarchicalState mDormantState = new DormantState();
-
- public SupplicantStateTracker(Context context, Handler target) {
- super(TAG, target.getLooper());
-
- addState(mUninitializedState);
- addState(mInitializedState);
- addState(mInactiveState, mInitializedState);
- addState(mDisconnectState, mInitializedState);
- addState(mScanState, mInitializedState);
- addState(mConnectState, mInitializedState);
- addState(mHandshakeState, mConnectState);
- addState(mCompletedState, mConnectState);
- addState(mDormantState, mInitializedState);
-
- setInitialState(mUninitializedState);
-
- //start the state machine
- start();
- }
-
- public void handleEvent(StateChangeResult stateChangeResult) {
- SupplicantState newState = (SupplicantState) stateChangeResult.state;
-
- // Supplicant state change
- // [31-13] Reserved for future use
- // [8 - 0] Supplicant state (as defined in SupplicantState.java)
- // 50023 supplicant_state_changed (custom|1|5)
- EventLog.writeEvent(EVENTLOG_SUPPLICANT_STATE_CHANGED, newState.ordinal());
-
- sendMessage(obtainMessage(newState.ordinal(), stateChangeResult));
- }
-
- public void resetSupplicantState() {
- transitionTo(mUninitializedState);
- }
-
- private void resetLoopDetection() {
- mLoopDetectCount = 0;
- mLoopDetectIndex = 0;
- }
-
- private boolean handleTransition(Message msg) {
- if (DBG) Log.d(TAG, getName() + msg.toString() + "\n");
- switch (msg.what) {
- case DISCONNECTED:
- transitionTo(mDisconnectState);
- break;
- case SCANNING:
- transitionTo(mScanState);
- break;
- case ASSOCIATING:
- StateChangeResult stateChangeResult = (StateChangeResult) msg.obj;
- /* BSSID is valid only in ASSOCIATING state */
- mWifiInfo.setBSSID(stateChangeResult.BSSID);
- //$FALL-THROUGH$
- case ASSOCIATED:
- case FOUR_WAY_HANDSHAKE:
- case GROUP_HANDSHAKE:
- transitionTo(mHandshakeState);
- break;
- case COMPLETED:
- transitionTo(mCompletedState);
- break;
- case DORMANT:
- transitionTo(mDormantState);
- break;
- case INACTIVE:
- transitionTo(mInactiveState);
- break;
- case UNINITIALIZED:
- case INVALID:
- transitionTo(mUninitializedState);
- break;
- default:
- return NOT_HANDLED;
- }
- StateChangeResult stateChangeResult = (StateChangeResult) msg.obj;
- SupplicantState supState = (SupplicantState) stateChangeResult.state;
- setDetailedState(WifiInfo.getDetailedStateOf(supState));
- mWifiInfo.setSupplicantState(supState);
- mWifiInfo.setNetworkId(stateChangeResult.networkId);
- return HANDLED;
- }
-
- /********************************************************
- * HSM states
- *******************************************************/
-
- class InitializedState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- }
- @Override
- public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- switch (message.what) {
- case CMD_START_SCAN:
- WifiNative.scanCommand(message.arg1 == SCAN_ACTIVE);
- break;
- default:
- if (DBG) Log.w(TAG, "Ignoring " + message);
- break;
- }
- return HANDLED;
- }
- }
-
- class UninitializedState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- mNetworkInfo.setIsAvailable(false);
- resetLoopDetection();
- mPasswordKeyMayBeIncorrect = false;
- }
- @Override
- public boolean processMessage(Message message) {
- switch(message.what) {
- default:
- if (!handleTransition(message)) {
- if (DBG) Log.w(TAG, "Ignoring " + message);
- }
- break;
- }
- return HANDLED;
- }
- @Override
- public void exit() {
- mNetworkInfo.setIsAvailable(true);
- }
- }
-
- class InactiveState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
-
- /* A failed WPS connection */
- if (mWpsStarted) {
- Log.e(TAG, "WPS set up failed, enabling other networks");
- WifiConfigStore.enableAllNetworks();
- mWpsStarted = false;
- }
-
- Message message = getCurrentMessage();
- StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
-
- mNetworkInfo.setIsAvailable(false);
- resetLoopDetection();
- mPasswordKeyMayBeIncorrect = false;
-
- sendSupplicantStateChangedBroadcast(stateChangeResult, false);
- }
- @Override
- public boolean processMessage(Message message) {
- return handleTransition(message);
- }
- @Override
- public void exit() {
- mNetworkInfo.setIsAvailable(true);
- }
- }
-
-
- class DisconnectedState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- Message message = getCurrentMessage();
- StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
-
- resetLoopDetection();
-
- /* If a disconnect event happens after a password key failure
- * event, disable the network
- */
- if (mPasswordKeyMayBeIncorrect) {
- Log.d(TAG, "Failed to authenticate, disabling network " +
- mWifiInfo.getNetworkId());
- WifiConfigStore.disableNetwork(mWifiInfo.getNetworkId());
- mPasswordKeyMayBeIncorrect = false;
- sendSupplicantStateChangedBroadcast(stateChangeResult, true);
- }
- else {
- sendSupplicantStateChangedBroadcast(stateChangeResult, false);
- }
- }
- @Override
- public boolean processMessage(Message message) {
- return handleTransition(message);
- }
- }
-
- class ScanState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- Message message = getCurrentMessage();
- StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
-
- mPasswordKeyMayBeIncorrect = false;
- resetLoopDetection();
- sendSupplicantStateChangedBroadcast(stateChangeResult, false);
- }
- @Override
- public boolean processMessage(Message message) {
- return handleTransition(message);
- }
- }
-
- class ConnectState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- }
- @Override
- public boolean processMessage(Message message) {
- switch (message.what) {
- case CMD_START_SCAN:
- WifiNative.setScanResultHandlingCommand(SCAN_ONLY_MODE);
- WifiNative.scanCommand(message.arg1 == SCAN_ACTIVE);
- break;
- default:
- return NOT_HANDLED;
- }
- return HANDLED;
- }
- }
-
- class HandshakeState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- final Message message = getCurrentMessage();
- StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
-
- if (mLoopDetectIndex > message.what) {
- mLoopDetectCount++;
- }
- if (mLoopDetectCount > MAX_SUPPLICANT_LOOP_ITERATIONS) {
- WifiConfigStore.disableNetwork(stateChangeResult.networkId);
- mLoopDetectCount = 0;
- }
-
- mLoopDetectIndex = message.what;
-
- mPasswordKeyMayBeIncorrect = false;
- sendSupplicantStateChangedBroadcast(stateChangeResult, false);
- }
- @Override
- public boolean processMessage(Message message) {
- return handleTransition(message);
- }
- }
-
- class CompletedState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- Message message = getCurrentMessage();
- StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
-
- mRssiPollToken++;
- if (mEnableRssiPolling) {
- sendMessageDelayed(obtainMessage(CMD_RSSI_POLL, mRssiPollToken, 0),
- POLL_RSSI_INTERVAL_MSECS);
- }
-
- resetLoopDetection();
-
- mPasswordKeyMayBeIncorrect = false;
- sendSupplicantStateChangedBroadcast(stateChangeResult, false);
- }
- @Override
- public boolean processMessage(Message message) {
- switch(message.what) {
- case ASSOCIATING:
- case ASSOCIATED:
- case FOUR_WAY_HANDSHAKE:
- case GROUP_HANDSHAKE:
- case COMPLETED:
- break;
- case CMD_RSSI_POLL:
- if (message.arg1 == mRssiPollToken) {
- // Get Info and continue polling
- requestPolledInfo();
- sendMessageDelayed(obtainMessage(CMD_RSSI_POLL, mRssiPollToken, 0),
- POLL_RSSI_INTERVAL_MSECS);
- } else {
- // Polling has completed
- }
- break;
- case CMD_ENABLE_RSSI_POLL:
- mRssiPollToken++;
- if (mEnableRssiPolling) {
- // first poll
- requestPolledInfo();
- sendMessageDelayed(obtainMessage(CMD_RSSI_POLL, mRssiPollToken, 0),
- POLL_RSSI_INTERVAL_MSECS);
- }
- break;
- default:
- return handleTransition(message);
- }
- return HANDLED;
- }
- }
-
- class DormantState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- Message message = getCurrentMessage();
- StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
-
- resetLoopDetection();
- mPasswordKeyMayBeIncorrect = false;
-
- sendSupplicantStateChangedBroadcast(stateChangeResult, false);
-
- /* TODO: reconnect is now being handled at DHCP failure handling
- * If we run into issues with staying in Dormant state, might
- * need a reconnect here
- */
- }
- @Override
- public boolean processMessage(Message message) {
- return handleTransition(message);
- }
- }
- }
}