summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.xml32
-rw-r--r--core/java/android/app/ActivityThread.java19
-rw-r--r--core/java/android/app/ApplicationPackageManager.java10
-rw-r--r--core/java/android/app/ApplicationThreadNative.java23
-rw-r--r--core/java/android/app/IApplicationThread.java2
-rw-r--r--core/java/android/content/pm/IPackageManager.aidl2
-rw-r--r--core/java/android/content/pm/PackageManager.java18
-rw-r--r--core/java/android/content/res/StringBlock.java80
-rw-r--r--core/java/android/net/ConnectivityManager.java35
-rw-r--r--core/java/android/net/IConnectivityManager.aidl7
-rw-r--r--core/java/android/net/Proxy.java286
-rw-r--r--core/java/android/net/ProxyProperties.aidl21
-rw-r--r--core/java/android/net/ProxyProperties.java142
-rw-r--r--core/java/android/os/StrictMode.java2
-rw-r--r--core/java/android/provider/Settings.java19
-rw-r--r--core/java/android/text/StaticLayout.java19
-rw-r--r--core/java/android/widget/AbsListView.java10
-rw-r--r--core/java/android/widget/DatePicker.java39
-rw-r--r--core/java/android/widget/TextView.java5
-rw-r--r--core/jni/android_util_StringBlock.cpp21
-rw-r--r--core/res/AndroidManifest.xml1
-rw-r--r--docs/html/guide/appendix/media-formats.jd6
-rw-r--r--docs/html/guide/guide_toc.cs3
-rw-r--r--docs/html/guide/topics/fragments/index.jd648
-rw-r--r--docs/html/guide/topics/manifest/activity-element.jd3
-rw-r--r--docs/html/guide/topics/manifest/application-element.jd8
-rw-r--r--docs/html/images/activity_fragment_lifecycle.pngbin0 -> 50826 bytes
-rw-r--r--docs/html/images/fragment_lifecycle.pngbin0 -> 69715 bytes
-rw-r--r--docs/html/resources/resources-data.js14
-rw-r--r--include/utils/Tokenizer.h4
-rw-r--r--libs/utils/Tokenizer.cpp32
-rw-r--r--media/libstagefright/Android.mk1
-rw-r--r--media/libstagefright/HTTPStream.cpp21
-rw-r--r--media/libstagefright/NuCachedSource2.cpp2
-rw-r--r--media/libstagefright/NuHTTPDataSource.cpp17
-rw-r--r--media/libstagefright/ShoutcastSource.cpp3
-rw-r--r--media/libstagefright/include/HTTPStream.h7
-rw-r--r--media/libstagefright/include/stagefright_string.h54
-rw-r--r--media/libstagefright/string.cpp92
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java38
-rw-r--r--packages/SystemUI/res/layout-xlarge/status_bar.xml117
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java158
-rwxr-xr-xpackages/TtsService/src/android/tts/TtsService.java50
-rw-r--r--services/java/com/android/server/ConnectivityService.java143
-rw-r--r--services/java/com/android/server/DevicePolicyManagerService.java13
-rw-r--r--services/java/com/android/server/PackageManagerService.java74
-rw-r--r--services/java/com/android/server/am/ActivityManagerService.java33
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java9
-rw-r--r--test-runner/src/android/test/mock/MockPackageManager.java6
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java4
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java18
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeInflater.java63
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParser.java32
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutSceneImpl.java29
-rw-r--r--tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestClassReplacement.java6
-rw-r--r--wifi/java/android/net/wifi/SupplicantStateTracker.java333
-rw-r--r--wifi/java/android/net/wifi/WifiConfigStore.java47
-rw-r--r--wifi/java/android/net/wifi/WifiStateMachine.java695
58 files changed, 2282 insertions, 1294 deletions
diff --git a/api/current.xml b/api/current.xml
index 94225529169b..2964fb0af178 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"
@@ -248151,7 +248181,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="arg0" type="T">
+<parameter name="t" type="T">
</parameter>
</method>
</interface>
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 3cead1174da6..c0714e3bba25 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -42,6 +42,9 @@ import android.database.sqlite.SQLiteDebug;
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 @@ public final class ActivityThread {
super(resultCode, resultData, resultExtras, TYPE_COMPONENT, ordered, sticky, token);
this.intent = intent;
}
-
+
Intent intent;
ActivityInfo info;
public String toString() {
@@ -592,6 +595,10 @@ public final class ActivityThread {
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 @@ public final class ActivityThread {
}
}
+ /**
+ * 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 ce9501a6b156..abb26e3398f8 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -914,6 +914,16 @@ final class ApplicationPackageManager extends PackageManager {
}
@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 b19fb5979e1c..801c3f94bfb2 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -276,7 +276,7 @@ public abstract class ApplicationThreadNative extends Binder
requestThumbnail(b);
return true;
}
-
+
case SCHEDULE_CONFIGURATION_CHANGED_TRANSACTION:
{
data.enforceInterface(IApplicationThread.descriptor);
@@ -297,12 +297,21 @@ public abstract class ApplicationThreadNative extends Binder
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 @@ class ApplicationThreadProxy implements IApplicationThread {
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/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index 830c7024b5d2..eca84efec33f 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -88,6 +88,7 @@ public interface IApplicationThread extends IInterface {
void scheduleConfigurationChanged(Configuration config) throws RemoteException;
void updateTimeZone() throws RemoteException;
void clearDnsCache() throws RemoteException;
+ void setHttpProxy(String proxy, String port, String exclList) throws RemoteException;
void processInBackground() throws RemoteException;
void dumpService(FileDescriptor fd, IBinder servicetoken, String[] args)
throws RemoteException;
@@ -148,4 +149,5 @@ public interface IApplicationThread extends IInterface {
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/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 4cff3bb21538..d01a68a83add 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -158,6 +158,8 @@ interface IPackageManager {
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 b5d165383097..ac7a95ae7e2c 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1918,6 +1918,24 @@ public abstract class PackageManager {
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 5e90b9110e71..23a6f97c37db 100644
--- a/core/java/android/content/res/StringBlock.java
+++ b/core/java/android/content/res/StringBlock.java
@@ -87,21 +87,48 @@ final class StringBlock {
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 @@ final class StringBlock {
}
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 @@ final class StringBlock {
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/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index dd9c8f09bd16..ecfa2c1c5989 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -624,4 +624,39 @@ public class ConnectivityManager
} 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 35054d6c4094..70ab4f153ef7 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -18,6 +18,7 @@ package android.net;
import android.net.LinkProperties;
import android.net.NetworkInfo;
+import android.net.ProxyProperties;
import android.os.IBinder;
/**
@@ -85,4 +86,10 @@ interface IConnectivityManager
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 21c485e70e18..3b9b9fe8c6e8 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 @@ public final class Proxy {
// 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 @@ public final class Proxy {
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 @@ public final class Proxy {
* {@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 @@ public final class Proxy {
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 @@ public final class Proxy {
}
}
- 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 @@ public final class Proxy {
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 @@ public final class Proxy {
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 000000000000..02ea15d769f9
--- /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 5fd0d89c72eb..cbe4445a3a2d 100644
--- a/core/java/android/net/ProxyProperties.java
+++ b/core/java/android/net/ProxyProperties.java
@@ -19,6 +19,8 @@ package android.net;
import android.os.Parcel;
import android.os.Parcelable;
+import android.text.TextUtils;
+import android.util.Log;
import java.net.InetAddress;
import java.net.InetSocketAddress;
@@ -30,44 +32,108 @@ import java.net.UnknownHostException;
*/
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 @@ public class ProxyProperties implements Parcelable {
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 @@ public class ProxyProperties implements Parcelable {
* @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 class ProxyProperties implements Parcelable {
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 93542c6ee355..c836e56d2026 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -104,7 +104,7 @@ import java.util.HashMap;
*/
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/Settings.java b/core/java/android/provider/Settings.java
index 1fe2c5a94094..70757740655f 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2424,17 +2424,32 @@ public final class Settings {
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/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index cc969cb9f50c..3dd1ecd34ad8 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -21,10 +21,10 @@ import com.android.internal.util.ArrayUtils;
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 @@ import android.text.style.LeadingMarginSpan.LeadingMarginSpan2;
* 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 @@ extends Layout
float before = w;
if (c == '\n') {
- ;
+ // intentionally left empty
} else if (c == '\t') {
if (hasTab == false) {
hasTab = true;
@@ -837,6 +835,7 @@ extends Layout
// 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 @@ extends Layout
}
}
+ @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 @@ extends Layout
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/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index d38898587bf5..a3e9bc9f3398 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -3018,6 +3018,11 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
clearScrollingCache();
mScroller.abortAnimation();
+
+ if (mFlingStrictSpan != null) {
+ mFlingStrictSpan.finish();
+ mFlingStrictSpan = null;
+ }
}
void flywheelTouch() {
@@ -3086,11 +3091,6 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
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 a3712902b85e..595b4871c9e6 100644
--- a/core/java/android/widget/DatePicker.java
+++ b/core/java/android/widget/DatePicker.java
@@ -22,6 +22,7 @@ import android.content.res.TypedArray;
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 com.android.internal.R;
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 @@ public class DatePicker extends FrameLayout {
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 @@ public class DatePicker extends FrameLayout {
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 @@ public class DatePicker extends FrameLayout {
});
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 @@ public class DatePicker extends FrameLayout {
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 @@ public class DatePicker extends FrameLayout {
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 e6503a9b2c9b..80a6a2f385a0 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -7117,7 +7117,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
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 641fbce5c2a6..a021efda763a 100644
--- a/core/jni/android_util_StringBlock.cpp
+++ b/core/jni/android_util_StringBlock.cpp
@@ -147,25 +147,6 @@ static jintArray android_content_StringBlock_nativeGetStyle(JNIEnv* env, jobject
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 @@ static JNINativeMethod gStringBlockMethods[] = {
(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 d02ca64fdcf0..ddc63ddb8681 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/docs/html/guide/appendix/media-formats.jd b/docs/html/guide/appendix/media-formats.jd
index 94a6471c9b87..87099943c6e6 100644
--- a/docs/html/guide/appendix/media-formats.jd
+++ b/docs/html/guide/appendix/media-formats.jd
@@ -22,7 +22,7 @@ page.title=Android Supported Media Formats
<tr>
<td rowspan="9">Audio</td>
<td>AAC LC/LTP</td>
-<td>&nbsp;</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 @@ page.title=Android Supported Media Formats
<tr>
<td>AMR-WB</td>
-<td>&nbsp;</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 @@ page.title=Android Supported Media Formats
<tr>
<td>PNG</td>
-<td>&nbsp;</td>
+<td style="text-align: center;">X</td>
<td style="text-align: center;">X</td>
<td>&nbsp;</td>
<td>PNG (.png)</td>
diff --git a/docs/html/guide/guide_toc.cs b/docs/html/guide/guide_toc.cs
index 545807eb1894..3cc18060841f 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/topics/fragments/index.jd b/docs/html/guide/topics/fragments/index.jd
new file mode 100644
index 000000000000..ce10ef7d50e1
--- /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&mdash;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&mdash;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 &lt;fragment&gt;} 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&mdash;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 {
+ &#64;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&mdash;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>
+&lt;?xml version="1.0" encoding="utf-8"?&gt;
+&lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"&gt;
+ &lt;fragment android:name="com.example.news.ArticleListFragment"
+ android:id="@+id/list"
+ android:layout_weight="1"
+ android:layout_width="0dp"
+ android:layout_height="match_parent" /&gt;
+ &lt;fragment android:name="com.example.news.ArticleReaderFragment"
+ android:id="@+id/viewer"
+ android:layout_weight="2"
+ android:layout_width="0dp"
+ android:layout_height="match_parent" /&gt;
+&lt;/LinearLayout&gt;
+</pre>
+ <p>The {@code &lt;fragment&gt;} 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 &lt;fragment&gt;} 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
+&lt;fragment&gt;}</li>
+ <li>Supply the {@code android:tag} attribute with a unique string ID, in the {@code
+ &lt;fragment&gt;}</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>
+&lt;?xml version="1.0" encoding="utf-8"?&gt;
+&lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" &gt;
+&lt;TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/hello" /&gt;
+&lt;TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/hello" /&gt;
+&lt;/LinearLayout&gt;
+</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 {
+ &#64;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>
+&lt;?xml version="1.0" encoding="utf-8"?&gt;
+&lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"&gt;
+ &lt;fragment android:name="com.example.SimpleFragment"
+ android:id="@+id/list"
+ android:layout_weight="1"
+ android:layout_width="0dp"
+ android:layout_height="match_parent" /&gt;
+ &lt;fragment android:name="com.example.SimpleFragment"
+ android:id="@+id/viewer"
+ android:layout_weight="1"
+ android:layout_width="0dp"
+ android:layout_height="match_parent" /&gt;
+&lt;/LinearLayout&gt;
+</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&mdash;but only while the activity is
+resumed (while it is in the foreground)&mdash;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&mdash;one to show a list of
+articles (fragment A) and another to display an article (fragment B)&mdash;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 {
+ ...
+ &#64;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 {
+ ...
+ &#64;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 e030a4c48e5e..2648cb7a4737 100644
--- a/docs/html/guide/topics/manifest/activity-element.jd
+++ b/docs/html/guide/topics/manifest/activity-element.jd
@@ -290,7 +290,8 @@ activity is ignored. The activity is not re-parented, but destroyed.
<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 9ac07fd884e5..1fadc6e32c85 100644
--- a/docs/html/guide/topics/manifest/application-element.jd
+++ b/docs/html/guide/topics/manifest/application-element.jd
@@ -12,6 +12,7 @@ page.title=&lt;application&gt;
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 @@ each of the application's components. See the individual
<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 @@ However, as a convenience while you're developing the application,
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/images/activity_fragment_lifecycle.png b/docs/html/images/activity_fragment_lifecycle.png
new file mode 100644
index 000000000000..156aa40edfe4
--- /dev/null
+++ b/docs/html/images/activity_fragment_lifecycle.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 000000000000..ce9d395bfdec
--- /dev/null
+++ b/docs/html/images/fragment_lifecycle.png
Binary files differ
diff --git a/docs/html/resources/resources-data.js b/docs/html/resources/resources-data.js
index d06b695351fe..221406c459f3 100644
--- a/docs/html/resources/resources-data.js
+++ b/docs/html/resources/resources-data.js
@@ -465,7 +465,7 @@ var ANDROID_RESOURCES = [
}
},
{
- tags: ['sample', 'ui', 'search', 'new'],
+ tags: ['sample', 'ui', 'search'],
path: 'samples/SearchableDictionary/index.html',
title: {
en: 'Searchable Dictionary v2'
@@ -485,7 +485,7 @@ var ANDROID_RESOURCES = [
}
},
{
- tags: ['sample', 'testing', 'new'],
+ tags: ['sample', 'testing'],
path: 'samples/Spinner/index.html',
title: {
en: 'Spinner'
@@ -495,7 +495,7 @@ var ANDROID_RESOURCES = [
}
},
{
- tags: ['sample', 'testing', 'new'],
+ tags: ['sample', 'testing'],
path: 'samples/SpinnerTest/index.html',
title: {
en: 'SpinnerTest'
@@ -505,7 +505,7 @@ var ANDROID_RESOURCES = [
}
},
{
- tags: ['sample', 'newfeature', 'new'],
+ tags: ['sample', 'newfeature'],
path: 'samples/TicTacToeLib/index.html',
title: {
en: 'TicTacToeLib'
@@ -515,7 +515,7 @@ var ANDROID_RESOURCES = [
}
},
{
- tags: ['sample', 'newfeature', 'new'],
+ tags: ['sample', 'newfeature',],
path: 'samples/TicTacToeMain/index.html',
title: {
en: 'TicTacToeMain'
@@ -610,7 +610,7 @@ var ANDROID_RESOURCES = [
}
},
{
- tags: ['tutorial', 'testing', 'new'],
+ tags: ['tutorial', 'testing'],
path: 'tutorials/testing/helloandroid_test.html',
title: {
en: 'Hello Testing'
@@ -620,7 +620,7 @@ var ANDROID_RESOURCES = [
}
},
{
- tags: ['tutorial', 'testing', 'new'],
+ tags: ['tutorial', 'testing'],
path: 'tutorials/testing/activity_test.html',
title: {
en: 'Activity Testing'
diff --git a/include/utils/Tokenizer.h b/include/utils/Tokenizer.h
index bfe89232dd1a..21e58e62ce1d 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 @@ namespace android {
* 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 @@ private:
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 19dadf08d557..9251973ca1f0 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 @@ static inline bool isDelimiter(char ch, const char* delimiters) {
}
-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 @@ status_t Tokenizer::open(const String8& filename, Tokenizer** outTokenizer) {
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/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 8fe1d4d6009d..4ad1eb42ed68 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -49,7 +49,6 @@ LOCAL_SRC_FILES:= \
WVMExtractor.cpp \
XINGSeeker.cpp \
avc_utils.cpp \
- string.cpp
LOCAL_C_INCLUDES:= \
$(JNI_H_INCLUDE) \
diff --git a/media/libstagefright/HTTPStream.cpp b/media/libstagefright/HTTPStream.cpp
index ccc6a340fca6..e7f00aa7fd2e 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 @@ status_t HTTPStream::receive_header(int *http_status) {
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 @@ status_t HTTPStream::receive_header(int *http_status) {
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 @@ status_t HTTPStream::receive_header(int *http_status) {
*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 @@ ssize_t HTTPStream::receive(void *data, size_t size) {
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/NuCachedSource2.cpp b/media/libstagefright/NuCachedSource2.cpp
index 7f765ca0e818..829ab2016fc6 100644
--- a/media/libstagefright/NuCachedSource2.cpp
+++ b/media/libstagefright/NuCachedSource2.cpp
@@ -465,7 +465,7 @@ status_t NuCachedSource2::seekInternal_l(off64_t offset) {
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 15c9ac6098b9..269b23345a75 100644
--- a/media/libstagefright/NuHTTPDataSource.cpp
+++ b/media/libstagefright/NuHTTPDataSource.cpp
@@ -178,7 +178,7 @@ status_t NuHTTPDataSource::connect(
}
if (IsRedirectStatusCode(httpStatus)) {
- string value;
+ AString value;
CHECK(mHTTP.find_header_value("Location", &value));
mState = DISCONNECTED;
@@ -198,9 +198,8 @@ status_t NuHTTPDataSource::connect(
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 @@ status_t NuHTTPDataSource::connect(
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 @@ status_t NuHTTPDataSource::connect(
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::MakeFullHeaders(
}
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 23b768133463..783f2d0382f8 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 @@ ShoutcastSource::ShoutcastSource(HTTPStream *http)
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/include/HTTPStream.h b/media/libstagefright/include/HTTPStream.h
index 793798f0942a..545cd0c431ed 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 @@ public:
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 @@ private:
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 5dc711653f04..000000000000
--- 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/string.cpp b/media/libstagefright/string.cpp
deleted file mode 100644
index 8b2c36c2d9d6..000000000000
--- 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/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 6c84afd65e70..d11a18e6d5fe 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 @@ public class DatabaseHelper extends SQLiteOpenHelper {
// 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 @@ public class DatabaseHelper extends SQLiteOpenHelper {
}
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 @@ public class DatabaseHelper extends SQLiteOpenHelper {
}
}
+ 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 d4a61362fe77..d11e6da009e2 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 7c97ac7671ca..233ac454d04c 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 @@ package com.android.systemui.statusbar.tablet;
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 @@ public class TabletStatusBar extends StatusBar {
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 @@ public class TabletStatusBar extends StatusBar {
// 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 @@ public class TabletStatusBar extends StatusBar {
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 @@ public class TabletStatusBar extends StatusBar {
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 @@ public class TabletStatusBar extends StatusBar {
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 TabletStatusBar extends StatusBar {
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 @@ public class TabletStatusBar extends StatusBar {
// 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 @@ public class TabletStatusBar extends StatusBar {
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 class TabletStatusBar extends StatusBar {
}
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 @@ public class TabletStatusBar extends StatusBar {
// 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 @@ public class TabletStatusBar extends StatusBar {
}
}
}
+
+ // 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 @@ public class TabletStatusBar extends StatusBar {
}
}
- 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 c977ba3dac51..08bbfb28c29f 100755
--- a/packages/TtsService/src/android/tts/TtsService.java
+++ b/packages/TtsService/src/android/tts/TtsService.java
@@ -496,7 +496,7 @@ public class TtsService extends Service implements OnCompletionListener {
* 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 class TtsService extends Service implements OnCompletionListener {
}
}
- 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 @@ public class TtsService extends Service implements OnCompletionListener {
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 @@ public class TtsService extends Service implements OnCompletionListener {
*
* @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/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index c18262e1d388..5c67da7f4df4 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -22,6 +22,7 @@ import android.content.ContentResolver;
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.NetworkInfo;
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.FileWriter;
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 @@ public class ConnectivityService extends IConnectivityManager.Stub {
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 @@ public class ConnectivityService extends IConnectivityManager.Stub {
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 @@ public class ConnectivityService extends IConnectivityManager.Stub {
if (DBG) {
mInetLog = new ArrayList();
}
+
+ mSettingsObserver = new SettingsObserver(mHandler, EVENT_APPLY_GLOBAL_HTTP_PROXY);
+ mSettingsObserver.observe(mContext);
}
@@ -1303,6 +1321,8 @@ public class ConnectivityService extends IConnectivityManager.Stub {
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 @@ public class ConnectivityService extends IConnectivityManager.Stub {
if (mNetTrackers[netType].getNetworkInfo().isConnected()) {
if (mNetAttributes[netType].isDefault()) {
+ handleApplyDefaultProxy(netType);
addDefaultRoute(mNetTrackers[netType]);
} else {
addPrivateDnsRoutes(mNetTrackers[netType]);
@@ -1783,10 +1804,9 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
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 @@ public class ConnectivityService extends IConnectivityManager.Stub {
handleSetMobileData(enabled);
break;
}
+ case EVENT_APPLY_GLOBAL_HTTP_PROXY:
+ {
+ handleDeprecatedGlobalHttpProxy();
+ }
}
}
}
@@ -2037,4 +2061,113 @@ public class ConnectivityService extends IConnectivityManager.Stub {
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 3dcad3855879..2b43b0131cd8 100644
--- a/services/java/com/android/server/DevicePolicyManagerService.java
+++ b/services/java/com/android/server/DevicePolicyManagerService.java
@@ -1757,10 +1757,19 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
// 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 a0a19748f9eb..c1218080873f 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -4578,6 +4578,80 @@ class PackageManagerService extends IPackageManager.Stub {
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 1ec8a22d20b5..e81552433a60 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.ServiceInfo;
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.InputStreamReader;
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 @@ public final class ActivityManagerService extends ActivityManagerNative
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 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
} 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 @@ public final class ActivityManagerService extends ActivityManagerNative
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 70328f721e4b..06c7c1baf6fa 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 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
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/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index f0cbaa0a69ee..615870b11c93 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -341,6 +341,12 @@ public class MockPackageManager extends PackageManager {
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/Matrix_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java
index 77de32daeb21..6e802680958d 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 @@ public final class Matrix_Delegate {
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 02db2cfbac13..ac7fada6df3a 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
@@ -65,6 +65,7 @@ import java.io.InputStream;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Map;
+import java.util.Stack;
import java.util.TreeMap;
import java.util.Map.Entry;
@@ -99,6 +100,8 @@ public final class BridgeContext extends Activity {
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,21 @@ public final class BridgeContext extends Activity {
return mDefaultPropMaps.get(key);
}
+ public void pushParser(BridgeXmlBlockParser parser) {
+ mParserStack.push(parser);
+ }
+
+ public void popParser() {
+ mParserStack.pop();
+ }
+
+ 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 b4a28a6d9790..d9e26e258ba8 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.File;
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 @@ public final class BridgeInflater extends LayoutInflater {
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 @@ public final class BridgeInflater extends LayoutInflater {
// 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 @@ public final class BridgeInflater extends LayoutInflater {
} 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 @@ public final class BridgeInflater extends LayoutInflater {
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 @@ public final class BridgeInflater extends LayoutInflater {
// 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 @@ public final class BridgeInflater extends LayoutInflater {
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 @@ public final class BridgeInflater extends LayoutInflater {
layoutInfo[0]);
} else {
layoutInfo = mProjectCallback.resolveResourceValue(resource);
-
+
if (layoutInfo != null) {
value = bridgeContext.getProjectResource(BridgeConstants.RES_LAYOUT,
layoutInfo[0]);
@@ -172,10 +172,10 @@ public final class BridgeInflater extends LayoutInflater {
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 @@ public final class BridgeInflater extends LayoutInflater {
}
return null;
}
-
+
private View loadCustomView(String name, AttributeSet attrs) throws ClassNotFoundException,
Exception{
if (mProjectCallback != null) {
@@ -194,12 +194,12 @@ public final class BridgeInflater extends LayoutInflater {
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 @@ public final class BridgeInflater extends LayoutInflater {
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/BridgeXmlBlockParser.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParser.java
index 24f61c818dff..073a0191a808 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 @@ public class BridgeXmlBlockParser implements XmlResourceParser {
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,10 +52,13 @@ public class BridgeXmlBlockParser implements XmlResourceParser {
*/
public BridgeXmlBlockParser(XmlPullParser parser, BridgeContext context, boolean platformFile) {
mParser = parser;
+ mContext = context;
mPlatformFile = platformFile;
mAttrib = new BridgeXmlPullAttributes(parser, context, mPlatformFile);
+
+ mContext.pushParser(this);
}
-
+
public boolean isPlatformFile() {
return mPlatformFile;
}
@@ -68,10 +70,9 @@ public class BridgeXmlBlockParser implements XmlResourceParser {
return null;
}
-
-
+
// ------- XmlResourceParser implementation
-
+
public void setFeature(String name, boolean state)
throws XmlPullParserException {
if (FEATURE_PROCESS_NAMESPACES.equals(name) && state) {
@@ -145,7 +146,7 @@ public class BridgeXmlBlockParser implements XmlResourceParser {
}
public int getDepth() {
- return mDepth;
+ return mParser.getDepth();
}
public String getText() {
@@ -236,17 +237,10 @@ public class BridgeXmlBlockParser implements XmlResourceParser {
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) {
+ // done with parser remove it from the context stack.
+ mContext.popParser();
}
mEventType = ev;
return ev;
@@ -301,7 +295,7 @@ public class BridgeXmlBlockParser implements XmlResourceParser {
// 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 b0316a3567c6..2e3f9a8ee3c5 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.SceneParams;
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 @@ public class LayoutSceneImpl {
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/tests/com/android/layoutlib/bridge/TestClassReplacement.java b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestClassReplacement.java
index adb693de475c..a068ae273651 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 @@ import junit.framework.TestCase;
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 000000000000..a83a0ad44ce2
--- /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 04b3891ef240..24f6f319f84a 100644
--- a/wifi/java/android/net/wifi/WifiConfigStore.java
+++ b/wifi/java/android/net/wifi/WifiConfigStore.java
@@ -595,9 +595,9 @@ class WifiConfigStore {
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 @@ class WifiConfigStore {
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 @@ class WifiConfigStore {
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 95e2df33456c..90abd029442a 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -49,8 +49,6 @@ import android.net.NetworkInfo.DetailedState;
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 @@ import java.util.regex.Pattern;
*
* @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 @@ public class WifiStateMachine extends HierarchicalStateMachine {
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 @@ public class WifiStateMachine extends HierarchicalStateMachine {
/* 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 @@ public class WifiStateMachine extends HierarchicalStateMachine {
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 @@ public class WifiStateMachine extends HierarchicalStateMachine {
* - 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 @@ public class WifiStateMachine extends HierarchicalStateMachine {
* 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 @@ public class WifiStateMachine extends HierarchicalStateMachine {
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 @@ public class WifiStateMachine extends HierarchicalStateMachine {
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 @@ public class WifiStateMachine extends HierarchicalStateMachine {
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 @@ public class WifiStateMachine extends HierarchicalStateMachine {
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 @@ public class WifiStateMachine extends HierarchicalStateMachine {
}
/**
- * 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 @@ public class WifiStateMachine extends HierarchicalStateMachine {
* 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 @@ public class WifiStateMachine extends HierarchicalStateMachine {
setWifiEnabled(true);
}
+ void setNetworkAvailable(boolean available) {
+ sendMessage(CMD_SET_NETWORK_AVAILABLE, available ? 1 : 0);
+ }
/********************************************************
* HSM states
@@ -1576,7 +1559,9 @@ public class WifiStateMachine extends HierarchicalStateMachine {
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 @@ public class WifiStateMachine extends HierarchicalStateMachine {
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 @@ public class WifiStateMachine extends HierarchicalStateMachine {
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 @@ public class WifiStateMachine extends HierarchicalStateMachine {
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 @@ public class WifiStateMachine extends HierarchicalStateMachine {
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 @@ public class WifiStateMachine extends HierarchicalStateMachine {
* 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 @@ public class WifiStateMachine extends HierarchicalStateMachine {
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 @@ public class WifiStateMachine extends HierarchicalStateMachine {
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 @@ public class WifiStateMachine extends HierarchicalStateMachine {
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 @@ public class WifiStateMachine extends HierarchicalStateMachine {
@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 @@ public class WifiStateMachine extends HierarchicalStateMachine {
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 @@ public class WifiStateMachine extends HierarchicalStateMachine {
/* 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 @@ public class WifiStateMachine extends HierarchicalStateMachine {
deferMessage(message);
}
break;
+ /* Handle in DisconnectedState */
+ case SUPPLICANT_STATE_CHANGE_EVENT:
+ deferMessage(message);
+ break;
default:
return NOT_HANDLED;
}
@@ -2734,6 +2764,12 @@ public class WifiStateMachine extends HierarchicalStateMachine {
/* 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 @@ public class WifiStateMachine extends HierarchicalStateMachine {
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);
- }
- }
- }
}