diff options
146 files changed, 4428 insertions, 2557 deletions
diff --git a/api/current.txt b/api/current.txt index 6ded422..7c78c06 100644 --- a/api/current.txt +++ b/api/current.txt @@ -8937,6 +8937,8 @@ package android.hardware { method public java.lang.String flatten(); method public java.lang.String get(java.lang.String); method public java.lang.String getAntibanding(); + method public boolean getAutoExposureLock(); + method public boolean getAutoWhiteBalanceLock(); method public java.lang.String getColorEffect(); method public int getExposureCompensation(); method public float getExposureCompensationStep(); @@ -8982,6 +8984,8 @@ package android.hardware { method public java.lang.String getWhiteBalance(); method public int getZoom(); method public java.util.List<java.lang.Integer> getZoomRatios(); + method public boolean isAutoExposureLockSupported(); + method public boolean isAutoWhiteBalanceLockSupported(); method public boolean isSmoothZoomSupported(); method public boolean isZoomSupported(); method public void remove(java.lang.String); @@ -8989,6 +8993,8 @@ package android.hardware { method public void set(java.lang.String, java.lang.String); method public void set(java.lang.String, int); method public void setAntibanding(java.lang.String); + method public void setAutoExposureLock(boolean); + method public void setAutoWhiteBalanceLock(boolean); method public void setColorEffect(java.lang.String); method public void setExposureCompensation(int); method public void setFlashMode(java.lang.String); @@ -14029,6 +14035,7 @@ package android.os { method public void dispatchMessage(android.os.Message); method public final void dump(android.util.Printer, java.lang.String); method public final android.os.Looper getLooper(); + method public java.lang.String getMessageName(android.os.Message); method public void handleMessage(android.os.Message); method public final boolean hasMessages(int); method public final boolean hasMessages(int, java.lang.Object); @@ -14098,13 +14105,13 @@ package android.os { public class Looper { method public void dump(android.util.Printer, java.lang.String); - method public static final synchronized android.os.Looper getMainLooper(); + method public static synchronized android.os.Looper getMainLooper(); method public java.lang.Thread getThread(); - method public static final void loop(); - method public static final android.os.Looper myLooper(); - method public static final android.os.MessageQueue myQueue(); - method public static final void prepare(); - method public static final void prepareMainLooper(); + method public static void loop(); + method public static android.os.Looper myLooper(); + method public static android.os.MessageQueue myQueue(); + method public static void prepare(); + method public static void prepareMainLooper(); method public void quit(); method public void setMessageLogging(android.util.Printer); } @@ -18018,7 +18025,6 @@ package android.speech.tts { public abstract interface SynthesisCallback { method public abstract int audioAvailable(byte[], int, int); - method public abstract int completeAudioAvailable(int, int, int, byte[], int, int); method public abstract int done(); method public abstract void error(); method public abstract int getMaxBufferSize(); @@ -22074,6 +22080,7 @@ package android.view { method public void buildDrawingCache(); method public void buildDrawingCache(boolean); method public void buildLayer(); + method protected boolean canResolveLayoutDirection(); method public boolean canScrollHorizontally(int); method public boolean canScrollVertically(int); method public void cancelLongPress(); @@ -22636,8 +22643,10 @@ package android.view { ctor public ViewDebug(); method public static void dumpCapturedView(java.lang.String, java.lang.Object); method public static void startHierarchyTracing(java.lang.String, android.view.View); + method public static void startLooperProfiling(java.io.File); method public static void startRecyclerTracing(java.lang.String, android.view.View); method public static void stopHierarchyTracing(); + method public static void stopLooperProfiling(); method public static void stopRecyclerTracing(); method public static void trace(android.view.View, android.view.ViewDebug.RecyclerTraceType, int...); method public static void trace(android.view.View, android.view.ViewDebug.HierarchyTraceType); diff --git a/cmds/ip-up-vpn/ip-up-vpn.c b/cmds/ip-up-vpn/ip-up-vpn.c index bbf6b14..e9ee95d 100644 --- a/cmds/ip-up-vpn/ip-up-vpn.c +++ b/cmds/ip-up-vpn/ip-up-vpn.c @@ -17,19 +17,135 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <errno.h> -#include <cutils/properties.h> +#include <arpa/inet.h> +#include <netinet/in.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <linux/if.h> +#include <linux/route.h> +#define LOG_TAG "ip-up-vpn" +#include <cutils/log.h> + +#define DIR "/data/misc/vpn/" + +static const char *env(const char *name) { + const char *value = getenv(name); + return value ? value : ""; +} + +static int set_address(struct sockaddr *sa, const char *address) { + sa->sa_family = AF_INET; + return inet_pton(AF_INET, address, &((struct sockaddr_in *)sa)->sin_addr); +} + +/* + * The primary goal is to create a file with VPN parameters. Currently they + * are interface, addresses, routes, DNS servers, and search domains. Each + * parameter occupies one line in the file, and it can be an empty string or + * space-separated values. The order and the format must be consistent with + * com.android.server.connectivity.Vpn. Here is an example. + * + * ppp0 + * 192.168.1.100/24 + * 0.0.0.0/0 + * 192.168.1.1 192.168.1.2 + * example.org + * + * The secondary goal is to unify the outcome of VPN. The current baseline + * is to have an interface configured with the given address and netmask + * and maybe add a host route to protect the tunnel. PPP-based VPN already + * does this, but others might not. Routes, DNS servers, and search domains + * are handled by the framework since they can be overridden by the users. + */ int main(int argc, char **argv) { - if (argc > 1 && strlen(argv[1]) > 0) { - char dns[PROPERTY_VALUE_MAX]; - char *dns1 = getenv("DNS1"); - char *dns2 = getenv("DNS2"); - - snprintf(dns, sizeof(dns), "%s %s", dns1 ? dns1 : "", dns2 ? dns2 : ""); - property_set("vpn.dns", dns); - property_set("vpn.via", argv[1]); + FILE *state = fopen(DIR ".tmp", "wb"); + if (!state) { + LOGE("Cannot create state: %s", strerror(errno)); + return 1; + } + + if (argc >= 6) { + /* Invoked by pppd. */ + fprintf(state, "%s\n", argv[1]); + fprintf(state, "%s/32\n", argv[4]); + fprintf(state, "0.0.0.0/0\n"); + fprintf(state, "%s %s\n", env("DNS1"), env("DNS2")); + fprintf(state, "\n"); + } else if (argc == 2) { + /* Invoked by racoon. */ + const char *interface = env("INTERFACE"); + const char *address = env("INTERNAL_ADDR4"); + const char *routes = env("SPLIT_INCLUDE_CIDR"); + + int s = socket(AF_INET, SOCK_DGRAM, 0); + struct rtentry rt; + struct ifreq ifr; + + memset(&rt, 0, sizeof(rt)); + memset(&ifr, 0, sizeof(ifr)); + + /* Remove the old host route. There could be more than one. */ + rt.rt_flags |= RTF_UP | RTF_HOST; + if (set_address(&rt.rt_dst, env("REMOTE_ADDR"))) { + while (!ioctl(s, SIOCDELRT, &rt)); + } + if (errno != ESRCH) { + LOGE("Cannot remove host route: %s", strerror(errno)); + return 1; + } + + /* Create a new host route. */ + rt.rt_flags |= RTF_GATEWAY; + if (!set_address(&rt.rt_gateway, argv[1]) || + (ioctl(s, SIOCADDRT, &rt) && errno != EEXIST)) { + LOGE("Cannot create host route: %s", strerror(errno)); + return 1; + } + + /* Bring up the interface. */ + ifr.ifr_flags = IFF_UP; + strncpy(ifr.ifr_name, interface, IFNAMSIZ); + if (ioctl(s, SIOCSIFFLAGS, &ifr)) { + LOGE("Cannot bring up %s: %s", interface, strerror(errno)); + return 1; + } + + /* Set the address. */ + if (!set_address(&ifr.ifr_addr, address) || + ioctl(s, SIOCSIFADDR, &ifr)) { + LOGE("Cannot set address: %s", strerror(errno)); + return 1; + } + + /* Set the netmask. */ + if (!set_address(&ifr.ifr_netmask, env("INTERNAL_NETMASK4")) || + ioctl(s, SIOCSIFNETMASK, &ifr)) { + LOGE("Cannot set netmask: %s", strerror(errno)); + return 1; + } + + /* TODO: Send few packets to trigger phase 2? */ + + fprintf(state, "%s\n", interface); + fprintf(state, "%s/%s\n", address, env("INTERNAL_CIDR4")); + fprintf(state, "%s\n", routes[0] ? routes : "0.0.0.0/0"); + fprintf(state, "%s\n", env("INTERNAL_DNS4_LIST")); + fprintf(state, "%s\n", env("DEFAULT_DOMAIN")); + } else { + LOGE("Cannot parse parameters"); + return 1; + } + + fclose(state); + if (chmod(DIR ".tmp", 0444) || rename(DIR ".tmp", DIR "state")) { + LOGE("Cannot write state: %s", strerror(errno)); + return 1; } return 0; } diff --git a/core/java/android/animation/LayoutTransition.java b/core/java/android/animation/LayoutTransition.java index d25de97..12a4dbb 100644 --- a/core/java/android/animation/LayoutTransition.java +++ b/core/java/android/animation/LayoutTransition.java @@ -235,7 +235,7 @@ public class LayoutTransition { PropertyValuesHolder pvhBottom = PropertyValuesHolder.ofInt("bottom", 0, 1); PropertyValuesHolder pvhScrollX = PropertyValuesHolder.ofInt("scrollX", 0, 1); PropertyValuesHolder pvhScrollY = PropertyValuesHolder.ofInt("scrollY", 0, 1); - defaultChangeIn = ObjectAnimator.ofPropertyValuesHolder(this, + defaultChangeIn = ObjectAnimator.ofPropertyValuesHolder((Object)null, pvhLeft, pvhTop, pvhRight, pvhBottom, pvhScrollX, pvhScrollY); defaultChangeIn.setDuration(DEFAULT_DURATION); defaultChangeIn.setStartDelay(mChangingAppearingDelay); @@ -244,11 +244,11 @@ public class LayoutTransition { defaultChangeOut.setStartDelay(mChangingDisappearingDelay); defaultChangeOut.setInterpolator(mChangingDisappearingInterpolator); - defaultFadeIn = ObjectAnimator.ofFloat(this, "alpha", 0f, 1f); + defaultFadeIn = ObjectAnimator.ofFloat(null, "alpha", 0f, 1f); defaultFadeIn.setDuration(DEFAULT_DURATION); defaultFadeIn.setStartDelay(mAppearingDelay); defaultFadeIn.setInterpolator(mAppearingInterpolator); - defaultFadeOut = ObjectAnimator.ofFloat(this, "alpha", 1f, 0f); + defaultFadeOut = ObjectAnimator.ofFloat(null, "alpha", 1f, 0f); defaultFadeOut.setDuration(DEFAULT_DURATION); defaultFadeOut.setStartDelay(mDisappearingDelay); defaultFadeOut.setInterpolator(mDisappearingInterpolator); diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java index 8a42693..7d67e11 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -720,8 +720,20 @@ public class Camera { * onAutoFocus will be called immediately with a fake value of * <code>success</code> set to <code>true</code>. * + * The auto-focus routine may lock auto-exposure and auto-white balance + * after it completes. To check for the state of these locks, use the + * {@link android.hardware.Camera.Parameters#getAutoExposureLock()} and + * {@link android.hardware.Camera.Parameters#getAutoWhiteBalanceLock()} + * methods. If such locking is undesirable, use + * {@link android.hardware.Camera.Parameters#setAutoExposureLock(boolean)} + * and + * {@link android.hardware.Camera.Parameters#setAutoWhiteBalanceLock(boolean)} + * to release the locks. + * * @param success true if focus was successful, false if otherwise * @param camera the Camera service object + * @see android.hardware.Camera.Parameters#setAutoExposureLock(boolean) + * @see android.hardware.Camera.Parameters#setAutoWhiteBalanceLock(boolean) */ void onAutoFocus(boolean success, Camera camera); }; @@ -747,8 +759,21 @@ public class Camera { * {@link android.hardware.Camera.Parameters#FLASH_MODE_OFF}, flash may be * fired during auto-focus, depending on the driver and camera hardware.<p> * + * The auto-focus routine may lock auto-exposure and auto-white balance + * after it completes. To check for the state of these locks, use the + * {@link android.hardware.Camera.Parameters#getAutoExposureLock()} and + * {@link android.hardware.Camera.Parameters#getAutoWhiteBalanceLock()} + * methods after the {@link AutoFocusCallback#onAutoFocus(boolean, Camera)} + * callback is invoked. If such locking is undesirable, use + * {@link android.hardware.Camera.Parameters#setAutoExposureLock(boolean)} + * and + * {@link android.hardware.Camera.Parameters#setAutoWhiteBalanceLock(boolean)} + * to release the locks. + * * @param cb the callback to run * @see #cancelAutoFocus() + * @see android.hardware.Camera.Parameters#setAutoExposureLock(boolean) + * @see android.hardware.Camera.Parameters#setAutoWhiteBalanceLock(boolean) */ public final void autoFocus(AutoFocusCallback cb) { @@ -763,7 +788,13 @@ public class Camera { * this function will return the focus position to the default. * If the camera does not support auto-focus, this is a no-op. * + * Canceling auto-focus will return the auto-exposure lock and auto-white + * balance lock to their state before {@link #autoFocus(AutoFocusCallback)} + * was called. + * * @see #autoFocus(Camera.AutoFocusCallback) + * @see android.hardware.Camera.Parameters#setAutoExposureLock(boolean) + * @see android.hardware.Camera.Parameters#setAutoWhiteBalanceLock(boolean) */ public final void cancelAutoFocus() { @@ -2562,8 +2593,6 @@ public class Camera { * routine is free to run normally. * * @see #getAutoExposureLock() - * - * @hide */ public void setAutoExposureLock(boolean toggle) { set(KEY_AUTO_EXPOSURE_LOCK, toggle ? TRUE : FALSE); @@ -2583,7 +2612,6 @@ public class Camera { * * @see #setAutoExposureLock(boolean) * - * @hide */ public boolean getAutoExposureLock() { String str = get(KEY_AUTO_EXPOSURE_LOCK); @@ -2598,7 +2626,6 @@ public class Camera { * @return true if auto-exposure lock is supported. * @see #setAutoExposureLock(boolean) * - * @hide */ public boolean isAutoExposureLockSupported() { String str = get(KEY_AUTO_EXPOSURE_LOCK_SUPPORTED); @@ -2645,8 +2672,6 @@ public class Camera { * auto-white balance routine is free to run normally. * * @see #getAutoWhiteBalanceLock() - * - * @hide */ public void setAutoWhiteBalanceLock(boolean toggle) { set(KEY_AUTO_WHITEBALANCE_LOCK, toggle ? TRUE : FALSE); @@ -2668,7 +2693,6 @@ public class Camera { * * @see #setAutoWhiteBalanceLock(boolean) * - * @hide */ public boolean getAutoWhiteBalanceLock() { String str = get(KEY_AUTO_WHITEBALANCE_LOCK); @@ -2683,7 +2707,6 @@ public class Camera { * @return true if auto-white balance lock is supported. * @see #setAutoWhiteBalanceLock(boolean) * - * @hide */ public boolean isAutoWhiteBalanceLockSupported() { String str = get(KEY_AUTO_WHITEBALANCE_LOCK_SUPPORTED); diff --git a/core/java/android/inputmethodservice/ExtractEditText.java b/core/java/android/inputmethodservice/ExtractEditText.java index 3447e76..44e7e52 100644 --- a/core/java/android/inputmethodservice/ExtractEditText.java +++ b/core/java/android/inputmethodservice/ExtractEditText.java @@ -98,11 +98,8 @@ public class ExtractEditText extends EditText { } @Override public boolean onTextContextMenuItem(int id) { - // Horrible hack: select word option has to be handled by original view to work. - if (mIME != null && id != android.R.id.startSelectingText) { - if (mIME.onExtractTextContextMenuItem(id)) { - return true; - } + if (mIME != null && mIME.onExtractTextContextMenuItem(id)) { + return true; } return super.onTextContextMenuItem(id); } diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl index d6f5643..d95fc8d 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/core/java/android/net/IConnectivityManager.aidl @@ -100,7 +100,7 @@ interface IConnectivityManager void setDataDependency(int networkType, boolean met); - void protectVpn(in ParcelFileDescriptor socket); + boolean protectVpn(in ParcelFileDescriptor socket); boolean prepareVpn(String oldPackage, String newPackage); diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java index f2f0e82..9826bec 100644 --- a/core/java/android/net/LinkProperties.java +++ b/core/java/android/net/LinkProperties.java @@ -57,16 +57,16 @@ public class LinkProperties implements Parcelable { private Collection<RouteInfo> mRoutes = new ArrayList<RouteInfo>(); private ProxyProperties mHttpProxy; - public static class CompareAddressesResult { - public ArrayList<LinkAddress> removed = new ArrayList<LinkAddress>(); - public ArrayList<LinkAddress> added = new ArrayList<LinkAddress>(); + public static class CompareResult<T> { + public ArrayList<T> removed = new ArrayList<T>(); + public ArrayList<T> added = new ArrayList<T>(); @Override public String toString() { - String retVal = "removedAddresses=["; - for (LinkAddress addr : removed) retVal += addr.toString() + ","; - retVal += "] addedAddresses=["; - for (LinkAddress addr : added) retVal += addr.toString() + ","; + String retVal = "removed=["; + for (T addr : removed) retVal += addr.toString() + ","; + retVal += "] added=["; + for (T addr : added) retVal += addr.toString() + ","; retVal += "]"; return retVal; } @@ -263,10 +263,10 @@ public class LinkProperties implements Parcelable { * mLinkAddress which would then result in target and mLinkAddresses * being the same list. * - * @param target is a new list of addresses + * @param target is a LinkProperties with the new list of addresses * @return the removed and added lists. */ - public CompareAddressesResult compareAddresses(LinkProperties target) { + public CompareResult<LinkAddress> compareAddresses(LinkProperties target) { /* * Duplicate the LinkAddresses into removed, we will be removing * address which are common between mLinkAddresses and target @@ -274,17 +274,81 @@ public class LinkProperties implements Parcelable { * are in target but not in mLinkAddresses are placed in the * addedAddresses. */ - CompareAddressesResult result = new CompareAddressesResult(); + CompareResult<LinkAddress> result = new CompareResult<LinkAddress>(); result.removed = new ArrayList<LinkAddress>(mLinkAddresses); result.added.clear(); - for (LinkAddress newAddress : target.getLinkAddresses()) { - if (! result.removed.remove(newAddress)) { - result.added.add(newAddress); + if (target != null) { + for (LinkAddress newAddress : target.getLinkAddresses()) { + if (! result.removed.remove(newAddress)) { + result.added.add(newAddress); + } + } + } + return result; + } + + /** + * Return two lists, a list of dns addresses that would be removed from + * mDnses and a list of addresses that would be added to + * mDnses which would then result in target and mDnses + * being the same list. + * + * @param target is a LinkProperties with the new list of dns addresses + * @return the removed and added lists. + */ + public CompareResult<InetAddress> compareDnses(LinkProperties target) { + /* + * Duplicate the InetAddresses into removed, we will be removing + * dns address which are common between mDnses and target + * leaving the addresses that are different. And dns address which + * are in target but not in mDnses are placed in the + * addedAddresses. + */ + CompareResult<InetAddress> result = new CompareResult<InetAddress>(); + + result.removed = new ArrayList<InetAddress>(mDnses); + result.added.clear(); + if (target != null) { + for (InetAddress newAddress : target.getDnses()) { + if (! result.removed.remove(newAddress)) { + result.added.add(newAddress); + } } } return result; } + /** + * Return two lists, a list of routes that would be removed from + * mRoutes and a list of routes that would be added to + * mRoutes which would then result in target and mRoutes + * being the same list. + * + * @param target is a LinkProperties with the new list of routes + * @return the removed and added lists. + */ + public CompareResult<RouteInfo> compareRoutes(LinkProperties target) { + /* + * Duplicate the RouteInfos into removed, we will be removing + * routes which are common between mDnses and target + * leaving the routes that are different. And route address which + * are in target but not in mRoutes are placed in added. + */ + CompareResult<RouteInfo> result = new CompareResult<RouteInfo>(); + + result.removed = new ArrayList<RouteInfo>(mRoutes); + result.added.clear(); + if (target != null) { + for (RouteInfo r : target.getRoutes()) { + if (! result.removed.remove(r)) { + result.added.add(r); + } + } + } + return result; + } + + @Override /** * generate hashcode based on significant fields diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java index 9381f1d..1ef0d9d 100644 --- a/core/java/android/net/NetworkTemplate.java +++ b/core/java/android/net/NetworkTemplate.java @@ -18,6 +18,7 @@ package android.net; import static android.net.ConnectivityManager.TYPE_WIFI; import static android.net.ConnectivityManager.TYPE_WIMAX; +import static android.net.ConnectivityManager.TYPE_ETHERNET; import static android.net.ConnectivityManager.isNetworkTypeMobile; import static android.telephony.TelephonyManager.NETWORK_CLASS_2_G; import static android.telephony.TelephonyManager.NETWORK_CLASS_3_G; @@ -38,41 +39,69 @@ import com.android.internal.util.Objects; */ public class NetworkTemplate implements Parcelable { + /** {@hide} */ + public static final int MATCH_MOBILE_ALL = 1; + /** {@hide} */ + public static final int MATCH_MOBILE_3G_LOWER = 2; + /** {@hide} */ + public static final int MATCH_MOBILE_4G = 3; + /** {@hide} */ + public static final int MATCH_WIFI = 4; + /** {@hide} */ + public static final int MATCH_ETHERNET = 5; + /** * Template to combine all {@link ConnectivityManager#TYPE_MOBILE} style * networks together. Only uses statistics for requested IMSI. */ - public static final int MATCH_MOBILE_ALL = 1; + public static NetworkTemplate buildTemplateMobileAll(String subscriberId) { + return new NetworkTemplate(MATCH_MOBILE_ALL, subscriberId); + } /** * Template to combine all {@link ConnectivityManager#TYPE_MOBILE} style * networks together that roughly meet a "3G" definition, or lower. Only * uses statistics for requested IMSI. */ - public static final int MATCH_MOBILE_3G_LOWER = 2; + public static NetworkTemplate buildTemplateMobile3gLower(String subscriberId) { + return new NetworkTemplate(MATCH_MOBILE_3G_LOWER, subscriberId); + } /** * Template to combine all {@link ConnectivityManager#TYPE_MOBILE} style * networks together that meet a "4G" definition. Only uses statistics for * requested IMSI. */ - public static final int MATCH_MOBILE_4G = 3; + public static NetworkTemplate buildTemplateMobile4g(String subscriberId) { + return new NetworkTemplate(MATCH_MOBILE_4G, subscriberId); + } /** * Template to combine all {@link ConnectivityManager#TYPE_WIFI} style * networks together. */ - public static final int MATCH_WIFI = 4; + public static NetworkTemplate buildTemplateWifi() { + return new NetworkTemplate(MATCH_WIFI, null); + } - final int mMatchRule; - final String mSubscriberId; + /** + * Template to combine all {@link ConnectivityManager#TYPE_ETHERNET} style + * networks together. + */ + public static NetworkTemplate buildTemplateEthernet() { + return new NetworkTemplate(MATCH_ETHERNET, null); + } + + private final int mMatchRule; + private final String mSubscriberId; + /** {@hide} */ public NetworkTemplate(int matchRule, String subscriberId) { this.mMatchRule = matchRule; this.mSubscriberId = subscriberId; } - public NetworkTemplate(Parcel in) { + private NetworkTemplate(Parcel in) { mMatchRule = in.readInt(); mSubscriberId = in.readString(); } @@ -110,10 +139,12 @@ public class NetworkTemplate implements Parcelable { return false; } + /** {@hide} */ public int getMatchRule() { return mMatchRule; } + /** {@hide} */ public String getSubscriberId() { return mSubscriberId; } @@ -131,6 +162,8 @@ public class NetworkTemplate implements Parcelable { return matchesMobile4g(ident); case MATCH_WIFI: return matchesWifi(ident); + case MATCH_ETHERNET: + return matchesEthernet(ident); default: throw new IllegalArgumentException("unknown network template"); } @@ -190,7 +223,17 @@ public class NetworkTemplate implements Parcelable { return false; } - public static String getMatchRuleName(int matchRule) { + /** + * Check if matches Ethernet network template. + */ + private boolean matchesEthernet(NetworkIdentity ident) { + if (ident.mType == TYPE_ETHERNET) { + return true; + } + return false; + } + + private static String getMatchRuleName(int matchRule) { switch (matchRule) { case MATCH_MOBILE_3G_LOWER: return "MOBILE_3G_LOWER"; @@ -200,6 +243,8 @@ public class NetworkTemplate implements Parcelable { return "MOBILE_ALL"; case MATCH_WIFI: return "WIFI"; + case MATCH_ETHERNET: + return "ETHERNET"; default: return "UNKNOWN"; } diff --git a/core/java/android/net/RouteInfo.java b/core/java/android/net/RouteInfo.java index 8e5ddda..275f32a 100644 --- a/core/java/android/net/RouteInfo.java +++ b/core/java/android/net/RouteInfo.java @@ -43,6 +43,7 @@ public class RouteInfo implements Parcelable { private final InetAddress mGateway; private final boolean mIsDefault; + private final boolean mIsHost; public RouteInfo(LinkAddress destination, InetAddress gateway) { if (destination == null) { @@ -68,6 +69,7 @@ public class RouteInfo implements Parcelable { destination.getNetworkPrefixLength()), destination.getNetworkPrefixLength()); mGateway = gateway; mIsDefault = isDefault(); + mIsHost = isHost(); } public RouteInfo(InetAddress gateway) { @@ -88,6 +90,10 @@ public class RouteInfo implements Parcelable { } } + private boolean isHost() { + return (mGateway.equals(Inet4Address.ANY) || mGateway.equals(Inet6Address.ANY)); + } + private boolean isDefault() { boolean val = false; if (mGateway != null) { @@ -100,6 +106,7 @@ public class RouteInfo implements Parcelable { return val; } + public LinkAddress getDestination() { return mDestination; } @@ -112,6 +119,10 @@ public class RouteInfo implements Parcelable { return mIsDefault; } + public boolean isHostRoute() { + return mIsHost; + } + public String toString() { String val = ""; if (mDestination != null) val = mDestination.toString(); diff --git a/core/java/android/os/Handler.java b/core/java/android/os/Handler.java index 165e438..cd39d5c 100644 --- a/core/java/android/os/Handler.java +++ b/core/java/android/os/Handler.java @@ -169,6 +169,21 @@ public class Handler { } /** + * Returns a string representing the name of the specified message. + * The default implementation will either return the class name of the + * message callback if any, or the hexadecimal representation of the + * message "what" field. + * + * @param message The message whose name is being queried + */ + public String getMessageName(Message message) { + if (message.callback != null) { + return message.callback.getClass().getName(); + } + return "0x" + Integer.toHexString(message.what); + } + + /** * Returns a new {@link android.os.Message Message} from the global message pool. More efficient than * creating and allocating new instances. The retrieved message has its handler set to this instance (Message.target == this). * If you don't want that facility, just call Message.obtain() instead. diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java index c0be664..720e802b 100644 --- a/core/java/android/os/Looper.java +++ b/core/java/android/os/Looper.java @@ -52,7 +52,6 @@ import android.util.PrefixPrinter; */ public class Looper { private static final String TAG = "Looper"; - private static final boolean LOG_V = Log.isLoggable(TAG, Log.VERBOSE); // sThreadLocal.get() will return null unless you've called prepare(). private static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); @@ -70,7 +69,7 @@ public class Looper { * {@link #loop()} after calling this method, and end it by calling * {@link #quit()}. */ - public static final void prepare() { + public static void prepare() { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } @@ -83,7 +82,7 @@ public class Looper { * is created by the Android environment, so you should never need * to call this function yourself. See also: {@link #prepare()} */ - public static final void prepareMainLooper() { + public static void prepareMainLooper() { prepare(); setMainLooper(myLooper()); myLooper().mQueue.mQuitAllowed = false; @@ -95,7 +94,7 @@ public class Looper { /** Returns the application's main looper, which lives in the main thread of the application. */ - public synchronized static final Looper getMainLooper() { + public synchronized static Looper getMainLooper() { return mMainLooper; } @@ -103,7 +102,7 @@ public class Looper { * Run the message queue in this thread. Be sure to call * {@link #quit()} to end the loop. */ - public static final void loop() { + public static void loop() { Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); @@ -122,20 +121,36 @@ public class Looper { // No target is a magic identifier for the quit message. return; } - if (me.mLogging != null) me.mLogging.println( - ">>>>> Dispatching to " + msg.target + " " - + msg.callback + ": " + msg.what - ); + + long wallStart = 0; + long threadStart = 0; + + // This must be in a local variable, in case a UI event sets the logger + Printer logging = me.mLogging; + if (logging != null) { + logging.println(">>>>> Dispatching to " + msg.target + " " + + msg.callback + ": " + msg.what); + wallStart = System.currentTimeMillis(); + threadStart = SystemClock.currentThreadTimeMillis(); + } + msg.target.dispatchMessage(msg); - if (me.mLogging != null) me.mLogging.println( - "<<<<< Finished to " + msg.target + " " - + msg.callback); - + + if (logging != null) { + long wallTime = System.currentTimeMillis() - wallStart; + long threadTime = SystemClock.currentThreadTimeMillis() - threadStart; + + logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); + if (logging instanceof Profiler) { + ((Profiler) logging).profile(msg, wallStart, wallTime, threadTime); + } + } + // Make sure that during the course of dispatching the // identity of the thread wasn't corrupted. final long newIdent = Binder.clearCallingIdentity(); if (ident != newIdent) { - Log.wtf("Looper", "Thread identity changed from 0x" + Log.wtf(TAG, "Thread identity changed from 0x" + Long.toHexString(ident) + " to 0x" + Long.toHexString(newIdent) + " while dispatching to " + msg.target.getClass().getName() + " " @@ -151,7 +166,7 @@ public class Looper { * Return the Looper object associated with the current thread. Returns * null if the calling thread is not associated with a Looper. */ - public static final Looper myLooper() { + public static Looper myLooper() { return sThreadLocal.get(); } @@ -173,7 +188,7 @@ public class Looper { * thread. This must be called from a thread running a Looper, or a * NullPointerException will be thrown. */ - public static final MessageQueue myQueue() { + public static MessageQueue myQueue() { return myLooper().mQueue; } @@ -225,23 +240,13 @@ public class Looper { } public String toString() { - return "Looper{" - + Integer.toHexString(System.identityHashCode(this)) - + "}"; + return "Looper{" + Integer.toHexString(System.identityHashCode(this)) + "}"; } - static class HandlerException extends Exception { - - HandlerException(Message message, Throwable cause) { - super(createMessage(cause), cause); - } - - static String createMessage(Throwable cause) { - String causeMsg = cause.getMessage(); - if (causeMsg == null) { - causeMsg = cause.toString(); - } - return causeMsg; - } + /** + * @hide + */ + public static interface Profiler { + void profile(Message message, long wallStart, long wallTime, long threadTime); } } diff --git a/core/java/android/preference/SeekBarPreference.java b/core/java/android/preference/SeekBarPreference.java index b8919c2..7133d3a 100644 --- a/core/java/android/preference/SeekBarPreference.java +++ b/core/java/android/preference/SeekBarPreference.java @@ -77,6 +77,11 @@ public class SeekBarPreference extends Preference } @Override + protected Object onGetDefaultValue(TypedArray a, int index) { + return a.getInt(index, 0); + } + + @Override public boolean onKey(View v, int keyCode, KeyEvent event) { if (event.getAction() != KeyEvent.ACTION_UP) { if (keyCode == KeyEvent.KEYCODE_PLUS diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java index f799af3..f3bcedb 100644 --- a/core/java/android/provider/MediaStore.java +++ b/core/java/android/provider/MediaStore.java @@ -283,6 +283,13 @@ public final class MediaStore { */ public static final String IS_DRM = "is_drm"; + /** + * Used by the media scanner to suppress files from being processed as media files. + * + * <P>Type: INTEGER (boolean)</P> + * @hide + */ + public static final String NO_MEDIA = "no_media"; } /** diff --git a/core/java/android/speech/tts/AudioPlaybackHandler.java b/core/java/android/speech/tts/AudioPlaybackHandler.java index 1210941..dea708a 100644 --- a/core/java/android/speech/tts/AudioPlaybackHandler.java +++ b/core/java/android/speech/tts/AudioPlaybackHandler.java @@ -31,8 +31,7 @@ class AudioPlaybackHandler { private static final int SYNTHESIS_START = 1; private static final int SYNTHESIS_DATA_AVAILABLE = 2; - private static final int SYNTHESIS_COMPLETE_DATA_AVAILABLE = 3; - private static final int SYNTHESIS_DONE = 4; + private static final int SYNTHESIS_DONE = 3; private static final int PLAY_AUDIO = 5; private static final int PLAY_SILENCE = 6; @@ -120,10 +119,6 @@ class AudioPlaybackHandler { mQueue.add(new ListEntry(SYNTHESIS_DATA_AVAILABLE, token)); } - void enqueueSynthesisCompleteDataAvailable(SynthesisMessageParams token) { - mQueue.add(new ListEntry(SYNTHESIS_COMPLETE_DATA_AVAILABLE, token)); - } - void enqueueSynthesisDone(SynthesisMessageParams token) { mQueue.add(new ListEntry(SYNTHESIS_DONE, token)); } @@ -280,8 +275,6 @@ class AudioPlaybackHandler { handleSynthesisDataAvailable(msg); } else if (entry.mWhat == SYNTHESIS_DONE) { handleSynthesisDone(msg); - } else if (entry.mWhat == SYNTHESIS_COMPLETE_DATA_AVAILABLE) { - handleSynthesisCompleteDataAvailable(msg); } else if (entry.mWhat == PLAY_AUDIO) { handleAudio(msg); } else if (entry.mWhat == PLAY_SILENCE) { @@ -424,54 +417,11 @@ class AudioPlaybackHandler { return; } - final AudioTrack track = params.mAudioTrack; + final AudioTrack audioTrack = params.mAudioTrack; final int bytesPerFrame = getBytesPerFrame(params.mAudioFormat); final int lengthInBytes = params.mBytesWritten; + final int lengthInFrames = lengthInBytes / bytesPerFrame; - blockUntilDone(track, bytesPerFrame, lengthInBytes); - } - - private void handleSynthesisCompleteDataAvailable(MessageParams msg) { - final SynthesisMessageParams params = (SynthesisMessageParams) msg; - if (DBG) Log.d(TAG, "completeAudioAvailable(" + params + ")"); - - params.mLogger.onPlaybackStart(); - - // Channel config and bytes per frame are checked before - // this message is sent. - int channelConfig = AudioPlaybackHandler.getChannelConfig(params.mChannelCount); - int bytesPerFrame = AudioPlaybackHandler.getBytesPerFrame(params.mAudioFormat); - - SynthesisMessageParams.ListEntry entry = params.getNextBuffer(); - - if (entry == null) { - Log.w(TAG, "completeDataAvailable : No buffers available to play."); - return; - } - - final AudioTrack audioTrack = new AudioTrack(params.mStreamType, params.mSampleRateInHz, - channelConfig, params.mAudioFormat, entry.mLength, AudioTrack.MODE_STATIC); - - // So that handleDone can access this correctly. - params.mAudioTrack = audioTrack; - - try { - audioTrack.write(entry.mBytes, entry.mOffset, entry.mLength); - setupVolume(audioTrack, params.mVolume, params.mPan); - audioTrack.play(); - blockUntilDone(audioTrack, bytesPerFrame, entry.mLength); - if (DBG) Log.d(TAG, "Wrote data to audio track successfully : " + entry.mLength); - } catch (IllegalStateException ex) { - Log.e(TAG, "Playback error", ex); - } finally { - handleSynthesisDone(msg); - } - } - - - private static void blockUntilDone(AudioTrack audioTrack, int bytesPerFrame, - int lengthInBytes) { - int lengthInFrames = lengthInBytes / bytesPerFrame; int currentPosition = 0; while ((currentPosition = audioTrack.getPlaybackHeadPosition()) < lengthInFrames) { if (audioTrack.getPlayState() != AudioTrack.PLAYSTATE_PLAYING) { diff --git a/core/java/android/speech/tts/FileSynthesisCallback.java b/core/java/android/speech/tts/FileSynthesisCallback.java index 4f4b3fb..5808919 100644 --- a/core/java/android/speech/tts/FileSynthesisCallback.java +++ b/core/java/android/speech/tts/FileSynthesisCallback.java @@ -187,37 +187,6 @@ class FileSynthesisCallback extends AbstractSynthesisCallback { } } - @Override - public int completeAudioAvailable(int sampleRateInHz, int audioFormat, int channelCount, - byte[] buffer, int offset, int length) { - synchronized (mStateLock) { - if (mStopped) { - if (DBG) Log.d(TAG, "Request has been aborted."); - return TextToSpeech.ERROR; - } - } - FileOutputStream out = null; - try { - out = new FileOutputStream(mFileName); - out.write(makeWavHeader(sampleRateInHz, audioFormat, channelCount, length)); - out.write(buffer, offset, length); - mDone = true; - return TextToSpeech.SUCCESS; - } catch (IOException ex) { - Log.e(TAG, "Failed to write to " + mFileName + ": " + ex); - mFileName.delete(); - return TextToSpeech.ERROR; - } finally { - try { - if (out != null) { - out.close(); - } - } catch (IOException ex) { - Log.e(TAG, "Failed to close " + mFileName + ": " + ex); - } - } - } - private byte[] makeWavHeader(int sampleRateInHz, int audioFormat, int channelCount, int dataLength) { // TODO: is AudioFormat.ENCODING_DEFAULT always the same as ENCODING_PCM_16BIT? diff --git a/core/java/android/speech/tts/PlaybackSynthesisCallback.java b/core/java/android/speech/tts/PlaybackSynthesisCallback.java index 38030a6..04bd745 100644 --- a/core/java/android/speech/tts/PlaybackSynthesisCallback.java +++ b/core/java/android/speech/tts/PlaybackSynthesisCallback.java @@ -53,8 +53,7 @@ class PlaybackSynthesisCallback extends AbstractSynthesisCallback { // Handler associated with a thread that plays back audio requests. private final AudioPlaybackHandler mAudioTrackHandler; - // A request "token", which will be non null after start() or - // completeAudioAvailable() have been called. + // A request "token", which will be non null after start() has been called. private SynthesisMessageParams mToken = null; // Whether this request has been stopped. This is useful for keeping // track whether stop() has been called before start(). In all other cases, @@ -206,35 +205,4 @@ class PlaybackSynthesisCallback extends AbstractSynthesisCallback { stop(); } - @Override - public int completeAudioAvailable(int sampleRateInHz, int audioFormat, int channelCount, - byte[] buffer, int offset, int length) { - int channelConfig = AudioPlaybackHandler.getChannelConfig(channelCount); - if (channelConfig == 0) { - Log.e(TAG, "Unsupported number of channels :" + channelCount); - return TextToSpeech.ERROR; - } - - int bytesPerFrame = AudioPlaybackHandler.getBytesPerFrame(audioFormat); - if (bytesPerFrame < 0) { - Log.e(TAG, "Unsupported audio format :" + audioFormat); - return TextToSpeech.ERROR; - } - - synchronized (mStateLock) { - if (mStopped) { - return TextToSpeech.ERROR; - } - SynthesisMessageParams params = new SynthesisMessageParams( - mStreamType, sampleRateInHz, audioFormat, channelCount, mVolume, mPan, - mDispatcher, mCallingApp, mLogger); - params.addBuffer(buffer, offset, length); - - mAudioTrackHandler.enqueueSynthesisCompleteDataAvailable(params); - mToken = params; - } - - return TextToSpeech.SUCCESS; - } - } diff --git a/core/java/android/speech/tts/SynthesisCallback.java b/core/java/android/speech/tts/SynthesisCallback.java index 1b80e40..d70c371 100644 --- a/core/java/android/speech/tts/SynthesisCallback.java +++ b/core/java/android/speech/tts/SynthesisCallback.java @@ -22,19 +22,16 @@ package android.speech.tts; * {@link #start}, then {@link #audioAvailable} until all audio has been provided, then finally * {@link #done}. * - * Alternatively, the engine can provide all the audio at once, by using - * {@link #completeAudioAvailable}. * * {@link #error} can be called at any stage in the synthesis process to - * indicate that an error has occured, but if the call is made after a call - * to {@link #done} or {@link #completeAudioAvailable} it might be discarded. + * indicate that an error has occurred, but if the call is made after a call + * to {@link #done}, it might be discarded. */ public interface SynthesisCallback { /** * @return the maximum number of bytes that the TTS engine can pass in a single call of - * {@link #audioAvailable}. This does not apply to {@link #completeAudioAvailable}. - * Calls to {@link #audioAvailable} with data lengths larger than this - * value will not succeed. + * {@link #audioAvailable}. Calls to {@link #audioAvailable} with data lengths + * larger than this value will not succeed. */ public int getMaxBufferSize(); @@ -69,23 +66,6 @@ public interface SynthesisCallback { public int audioAvailable(byte[] buffer, int offset, int length); /** - * The service can call this method instead of using {@link #start}, {@link #audioAvailable} - * and {@link #done} if all the audio data is available in a single buffer. - * - * @param sampleRateInHz Sample rate in HZ of the generated audio. - * @param audioFormat Audio format of the generated audio. Must be one of - * the ENCODING_ constants defined in {@link android.media.AudioFormat}. - * @param channelCount The number of channels. Must be {@code 1} or {@code 2}. - * @param buffer The generated audio data. This method will not hold on to {@code buffer}, - * so the caller is free to modify it after this method returns. - * @param offset The offset into {@code buffer} where the audio data starts. - * @param length The number of bytes of audio data in {@code buffer}. - * @return {@link TextToSpeech#SUCCESS} or {@link TextToSpeech#ERROR}. - */ - public int completeAudioAvailable(int sampleRateInHz, int audioFormat, - int channelCount, byte[] buffer, int offset, int length); - - /** * The service should call this method when all the synthesized audio for a request has * been passed to {@link #audioAvailable}. * diff --git a/core/java/android/text/BoringLayout.java b/core/java/android/text/BoringLayout.java index 757a8c3..5a244f1 100644 --- a/core/java/android/text/BoringLayout.java +++ b/core/java/android/text/BoringLayout.java @@ -226,7 +226,17 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback */ public static Metrics isBoring(CharSequence text, TextPaint paint) { - return isBoring(text, paint, null); + return isBoring(text, paint, TextDirectionHeuristics.FIRSTSTRONG_LTR, null); + } + + /** + * Returns null if not boring; the width, ascent, and descent if boring. + * @hide + */ + public static Metrics isBoring(CharSequence text, + TextPaint paint, + TextDirectionHeuristic textDir) { + return isBoring(text, paint, textDir, null); } /** @@ -235,6 +245,17 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback * if boring. */ public static Metrics isBoring(CharSequence text, TextPaint paint, Metrics metrics) { + return isBoring(text, paint, TextDirectionHeuristics.FIRSTSTRONG_LTR, metrics); + } + + /** + * Returns null if not boring; the width, ascent, and descent in the + * provided Metrics object (or a new one if the provided one was null) + * if boring. + * @hide + */ + public static Metrics isBoring(CharSequence text, TextPaint paint, + TextDirectionHeuristic textDir, Metrics metrics) { char[] temp = TextUtils.obtain(500); int length = text.length(); boolean boring = true; @@ -258,6 +279,11 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback break outer; } } + + if (textDir.isRtl(temp, 0, n)) { + boring = false; + break outer; + } } TextUtils.recycle(temp); diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java index f196b34..cb96969 100644 --- a/core/java/android/text/DynamicLayout.java +++ b/core/java/android/text/DynamicLayout.java @@ -75,12 +75,31 @@ extends Layout float spacingmult, float spacingadd, boolean includepad, TextUtils.TruncateAt ellipsize, int ellipsizedWidth) { - super((ellipsize == null) - ? display - : (display instanceof Spanned) - ? new SpannedEllipsizer(display) + this(base, display, paint, width, align, TextDirectionHeuristics.FIRSTSTRONG_LTR, + spacingmult, spacingadd, includepad, ellipsize, ellipsizedWidth); + } + + /** + * Make a layout for the transformed text (password transformation + * being the primary example of a transformation) + * that will be updated as the base text is changed. + * If ellipsize is non-null, the Layout will ellipsize the text + * down to ellipsizedWidth. + * * + * *@hide + */ + public DynamicLayout(CharSequence base, CharSequence display, + TextPaint paint, + int width, Alignment align, TextDirectionHeuristic textDir, + float spacingmult, float spacingadd, + boolean includepad, + TextUtils.TruncateAt ellipsize, int ellipsizedWidth) { + super((ellipsize == null) + ? display + : (display instanceof Spanned) + ? new SpannedEllipsizer(display) : new Ellipsizer(display), - paint, width, align, spacingmult, spacingadd); + paint, width, align, textDir, spacingmult, spacingadd); mBase = base; mDisplay = display; @@ -259,7 +278,7 @@ extends Layout reflowed = new StaticLayout(true); reflowed.generate(text, where, where + after, - getPaint(), getWidth(), getAlignment(), + getPaint(), getWidth(), getAlignment(), getTextDirectionHeuristic(), getSpacingMultiplier(), getSpacingAdd(), false, true, mEllipsizedWidth, mEllipsizeAt); int n = reflowed.getLineCount(); diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java index aae9ccf..eabeef0 100644 --- a/core/java/android/text/Layout.java +++ b/core/java/android/text/Layout.java @@ -16,8 +16,6 @@ package android.text; -import com.android.internal.util.ArrayUtils; - import android.emoji.EmojiFactory; import android.graphics.Canvas; import android.graphics.Paint; @@ -32,6 +30,8 @@ import android.text.style.ParagraphStyle; import android.text.style.ReplacementSpan; import android.text.style.TabStopSpan; +import com.android.internal.util.ArrayUtils; + import java.util.Arrays; /** @@ -113,6 +113,29 @@ public abstract class Layout { protected Layout(CharSequence text, TextPaint paint, int width, Alignment align, float spacingMult, float spacingAdd) { + this(text, paint, width, align, TextDirectionHeuristics.FIRSTSTRONG_LTR, + spacingMult, spacingAdd); + } + + /** + * Subclasses of Layout use this constructor to set the display text, + * width, and other standard properties. + * @param text the text to render + * @param paint the default paint for the layout. Styles can override + * various attributes of the paint. + * @param width the wrapping width for the text. + * @param align whether to left, right, or center the text. Styles can + * override the alignment. + * @param spacingMult factor by which to scale the font size to get the + * default line spacing + * @param spacingAdd amount to add to the default line spacing + * + * @hide + */ + protected Layout(CharSequence text, TextPaint paint, + int width, Alignment align, TextDirectionHeuristic textDir, + float spacingMult, float spacingAdd) { + if (width < 0) throw new IllegalArgumentException("Layout: " + width + " < 0"); @@ -133,6 +156,7 @@ public abstract class Layout { mSpacingMult = spacingMult; mSpacingAdd = spacingAdd; mSpannedText = text instanceof Spanned; + mTextDir = textDir; } /** @@ -531,6 +555,14 @@ public abstract class Layout { } /** + * Return the heuristic used to determine paragraph text direction. + * @hide + */ + public final TextDirectionHeuristic getTextDirectionHeuristic() { + return mTextDir; + } + + /** * Return the number of lines of text in this layout. */ public abstract int getLineCount(); @@ -1419,7 +1451,7 @@ public abstract class Layout { MeasuredText mt = MeasuredText.obtain(); TextLine tl = TextLine.obtain(); try { - mt.setPara(text, start, end, DIR_REQUEST_LTR); + mt.setPara(text, start, end, TextDirectionHeuristics.LTR); Directions directions; int dir; if (mt.mEasy) { @@ -1769,6 +1801,7 @@ public abstract class Layout { private float mSpacingAdd; private static final Rect sTempRect = new Rect(); private boolean mSpannedText; + private TextDirectionHeuristic mTextDir; public static final int DIR_LEFT_TO_RIGHT = 1; public static final int DIR_RIGHT_TO_LEFT = -1; diff --git a/core/java/android/text/MeasuredText.java b/core/java/android/text/MeasuredText.java index a81be09..2920ac5 100644 --- a/core/java/android/text/MeasuredText.java +++ b/core/java/android/text/MeasuredText.java @@ -85,7 +85,7 @@ class MeasuredText { * Analyzes text for bidirectional runs. Allocates working buffers. */ /* package */ - void setPara(CharSequence text, int start, int end, int bidiRequest) { + void setPara(CharSequence text, int start, int end, TextDirectionHeuristic textDir) { mText = text; mTextStart = start; @@ -115,13 +115,29 @@ class MeasuredText { } } - if (TextUtils.doesNotNeedBidi(mChars, 0, len)) { + if ((textDir == TextDirectionHeuristics.LTR || + textDir == TextDirectionHeuristics.FIRSTSTRONG_LTR || + textDir == TextDirectionHeuristics.ANYRTL_LTR) && + TextUtils.doesNotNeedBidi(mChars, 0, len)) { mDir = Layout.DIR_LEFT_TO_RIGHT; mEasy = true; } else { if (mLevels == null || mLevels.length < len) { mLevels = new byte[ArrayUtils.idealByteArraySize(len)]; } + int bidiRequest; + if (textDir == TextDirectionHeuristics.LTR) { + bidiRequest = Layout.DIR_REQUEST_LTR; + } else if (textDir == TextDirectionHeuristics.RTL) { + bidiRequest = Layout.DIR_REQUEST_RTL; + } else if (textDir == TextDirectionHeuristics.FIRSTSTRONG_LTR) { + bidiRequest = Layout.DIR_REQUEST_DEFAULT_LTR; + } else if (textDir == TextDirectionHeuristics.FIRSTSTRONG_RTL) { + bidiRequest = Layout.DIR_REQUEST_DEFAULT_RTL; + } else { + boolean isRtl = textDir.isRtl(mChars, 0, len); + bidiRequest = isRtl ? Layout.DIR_REQUEST_RTL : Layout.DIR_REQUEST_LTR; + } mDir = AndroidBidi.bidi(bidiRequest, mChars, mLevels, len, false); mEasy = false; } diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java index 9e48eff..f7b9502 100644 --- a/core/java/android/text/StaticLayout.java +++ b/core/java/android/text/StaticLayout.java @@ -16,8 +16,6 @@ package android.text; -import com.android.internal.util.ArrayUtils; - import android.graphics.Bitmap; import android.graphics.Paint; import android.text.style.LeadingMarginSpan; @@ -26,6 +24,8 @@ import android.text.style.LineHeightSpan; import android.text.style.MetricAffectingSpan; import android.text.style.TabStopSpan; +import com.android.internal.util.ArrayUtils; + /** * StaticLayout is a Layout for text that will not be edited after it * is laid out. Use {@link DynamicLayout} for text that may change. @@ -46,6 +46,17 @@ public class StaticLayout extends Layout { spacingmult, spacingadd, includepad); } + /** + * @hide + */ + public StaticLayout(CharSequence source, TextPaint paint, + int width, Alignment align, TextDirectionHeuristic textDir, + float spacingmult, float spacingadd, + boolean includepad) { + this(source, 0, source.length(), paint, width, align, textDir, + spacingmult, spacingadd, includepad); + } + public StaticLayout(CharSequence source, int bufstart, int bufend, TextPaint paint, int outerwidth, Alignment align, @@ -55,9 +66,35 @@ public class StaticLayout extends Layout { spacingmult, spacingadd, includepad, null, 0); } + /** + * @hide + */ + public StaticLayout(CharSequence source, int bufstart, int bufend, + TextPaint paint, int outerwidth, + Alignment align, TextDirectionHeuristic textDir, + float spacingmult, float spacingadd, + boolean includepad) { + this(source, bufstart, bufend, paint, outerwidth, align, textDir, + spacingmult, spacingadd, includepad, null, 0); +} + + public StaticLayout(CharSequence source, int bufstart, int bufend, + TextPaint paint, int outerwidth, + Alignment align, + float spacingmult, float spacingadd, + boolean includepad, + TextUtils.TruncateAt ellipsize, int ellipsizedWidth) { + this(source, bufstart, bufend, paint, outerwidth, align, + TextDirectionHeuristics.FIRSTSTRONG_LTR, + spacingmult, spacingadd, includepad, ellipsize, ellipsizedWidth); + } + + /** + * @hide + */ public StaticLayout(CharSequence source, int bufstart, int bufend, TextPaint paint, int outerwidth, - Alignment align, + Alignment align, TextDirectionHeuristic textDir, float spacingmult, float spacingadd, boolean includepad, TextUtils.TruncateAt ellipsize, int ellipsizedWidth) { @@ -66,7 +103,7 @@ public class StaticLayout extends Layout { : (source instanceof Spanned) ? new SpannedEllipsizer(source) : new Ellipsizer(source), - paint, outerwidth, align, spacingmult, spacingadd); + paint, outerwidth, align, textDir, spacingmult, spacingadd); /* * This is annoying, but we can't refer to the layout until @@ -96,7 +133,7 @@ public class StaticLayout extends Layout { mMeasured = MeasuredText.obtain(); - generate(source, bufstart, bufend, paint, outerwidth, align, + generate(source, bufstart, bufend, paint, outerwidth, align, textDir, spacingmult, spacingadd, includepad, includepad, ellipsizedWidth, ellipsize); @@ -116,7 +153,7 @@ public class StaticLayout extends Layout { /* package */ void generate(CharSequence source, int bufStart, int bufEnd, TextPaint paint, int outerWidth, - Alignment align, + Alignment align, TextDirectionHeuristic textDir, float spacingmult, float spacingadd, boolean includepad, boolean trackpad, float ellipsizedWidth, TextUtils.TruncateAt ellipsize) { @@ -157,7 +194,7 @@ public class StaticLayout extends Layout { LeadingMarginSpan lms = sp[i]; firstWidth -= sp[i].getLeadingMargin(true); restWidth -= sp[i].getLeadingMargin(false); - + // LeadingMarginSpan2 is odd. The count affects all // leading margin spans, not just this particular one, // and start from the top of the span, not the top of the @@ -195,7 +232,7 @@ public class StaticLayout extends Layout { } } - measured.setPara(source, paraStart, paraEnd, DIR_REQUEST_DEFAULT_LTR); + measured.setPara(source, paraStart, paraEnd, textDir); char[] chs = measured.mChars; float[] widths = measured.mWidths; byte[] chdirs = measured.mLevels; diff --git a/core/java/android/text/TextDirectionHeuristic.java b/core/java/android/text/TextDirectionHeuristic.java new file mode 100644 index 0000000..130f879 --- /dev/null +++ b/core/java/android/text/TextDirectionHeuristic.java @@ -0,0 +1,13 @@ +// Copyright 2011 Google Inc. All Rights Reserved. + +package android.text; + +/** + * Interface for objects that guess at the paragraph direction by examining text. + * + * @hide + */ +public interface TextDirectionHeuristic { + /** @hide */ boolean isRtl(CharSequence text, int start, int end); + /** @hide */ boolean isRtl(char[] text, int start, int count); +} diff --git a/core/java/android/text/TextDirectionHeuristics.java b/core/java/android/text/TextDirectionHeuristics.java new file mode 100644 index 0000000..5f9ffc5 --- /dev/null +++ b/core/java/android/text/TextDirectionHeuristics.java @@ -0,0 +1,310 @@ +// Copyright 2011 Google Inc. All Rights Reserved. + +package android.text; + + +/** + * Some objects that implement TextDirectionHeuristic. + * @hide + */ +public class TextDirectionHeuristics { + + /** Always decides that the direction is left to right. */ + public static final TextDirectionHeuristic LTR = + new TextDirectionHeuristicInternal(null /* no algorithm */, false); + + /** Always decides that the direction is right to left. */ + public static final TextDirectionHeuristic RTL = + new TextDirectionHeuristicInternal(null /* no algorithm */, true); + + /** + * Determines the direction based on the first strong directional character, + * including bidi format chars, falling back to left to right if it + * finds none. This is the default behavior of the Unicode Bidirectional + * Algorithm. + */ + public static final TextDirectionHeuristic FIRSTSTRONG_LTR = + new TextDirectionHeuristicInternal(FirstStrong.INSTANCE, false); + + /** + * Determines the direction based on the first strong directional character, + * including bidi format chars, falling back to right to left if it + * finds none. This is similar to the default behavior of the Unicode + * Bidirectional Algorithm, just with different fallback behavior. + */ + public static final TextDirectionHeuristic FIRSTSTRONG_RTL = + new TextDirectionHeuristicInternal(FirstStrong.INSTANCE, true); + + /** + * If the text contains any strong right to left non-format character, determines + * that the direction is right to left, falling back to left to right if it + * finds none. + */ + public static final TextDirectionHeuristic ANYRTL_LTR = + new TextDirectionHeuristicInternal(AnyStrong.INSTANCE_RTL, false); + + /** + * If the text contains any strong left to right non-format character, determines + * that the direction is left to right, falling back to right to left if it + * finds none. + */ + public static final TextDirectionHeuristic ANYLTR_RTL = + new TextDirectionHeuristicInternal(AnyStrong.INSTANCE_LTR, true); + + /** + * Examines only the strong directional non-format characters, and if either + * left to right or right to left characters are 60% or more of this total, + * determines that the direction follows the majority of characters. Falls + * back to left to right if neither direction meets this threshold. + */ + public static final TextDirectionHeuristic CHARCOUNT_LTR = + new TextDirectionHeuristicInternal(CharCount.INSTANCE_DEFAULT, false); + + /** + * Examines only the strong directional non-format characters, and if either + * left to right or right to left characters are 60% or more of this total, + * determines that the direction follows the majority of characters. Falls + * back to right to left if neither direction meets this threshold. + */ + public static final TextDirectionHeuristic CHARCOUNT_RTL = + new TextDirectionHeuristicInternal(CharCount.INSTANCE_DEFAULT, true); + + private static enum TriState { + TRUE, FALSE, UNKNOWN; + } + + /** + * Computes the text direction based on an algorithm. Subclasses implement + * {@link #defaultIsRtl} to handle cases where the algorithm cannot determine the + * direction from the text alone. + * @hide + */ + public static abstract class TextDirectionHeuristicImpl implements TextDirectionHeuristic { + private final TextDirectionAlgorithm mAlgorithm; + + public TextDirectionHeuristicImpl(TextDirectionAlgorithm algorithm) { + mAlgorithm = algorithm; + } + + /** + * Return true if the default text direction is rtl. + */ + abstract protected boolean defaultIsRtl(); + + @Override + public boolean isRtl(CharSequence text, int start, int end) { + if (text == null || start < 0 || end < start || text.length() < end) { + throw new IllegalArgumentException(); + } + if (mAlgorithm == null) { + return defaultIsRtl(); + } + text = text.subSequence(start, end); + char[] chars = text.toString().toCharArray(); + return doCheck(chars, 0, chars.length); + } + + @Override + public boolean isRtl(char[] chars, int start, int count) { + if (chars == null || start < 0 || count < 0 || chars.length - count < start) { + throw new IllegalArgumentException(); + } + if (mAlgorithm == null) { + return defaultIsRtl(); + } + return doCheck(chars, start, count); + } + + private boolean doCheck(char[] chars, int start, int count) { + switch(mAlgorithm.checkRtl(chars, start, count)) { + case TRUE: + return true; + case FALSE: + return false; + default: + return defaultIsRtl(); + } + } + } + + private static class TextDirectionHeuristicInternal extends TextDirectionHeuristicImpl { + private final boolean mDefaultIsRtl; + + private TextDirectionHeuristicInternal(TextDirectionAlgorithm algorithm, + boolean defaultIsRtl) { + super(algorithm); + mDefaultIsRtl = defaultIsRtl; + } + + @Override + protected boolean defaultIsRtl() { + return mDefaultIsRtl; + } + } + + private static TriState isRtlText(int directionality) { + switch (directionality) { + case Character.DIRECTIONALITY_LEFT_TO_RIGHT: + return TriState.FALSE; + case Character.DIRECTIONALITY_RIGHT_TO_LEFT: + case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC: + return TriState.TRUE; + default: + return TriState.UNKNOWN; + } + } + + private static TriState isRtlTextOrFormat(int directionality) { + switch (directionality) { + case Character.DIRECTIONALITY_LEFT_TO_RIGHT: + case Character.DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING: + case Character.DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE: + return TriState.FALSE; + case Character.DIRECTIONALITY_RIGHT_TO_LEFT: + case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC: + case Character.DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING: + case Character.DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE: + return TriState.TRUE; + default: + return TriState.UNKNOWN; + } + } + + /** + * Interface for an algorithm to guess the direction of a paragraph of text. + * + * @hide + */ + public static interface TextDirectionAlgorithm { + /** + * Returns whether the range of text is RTL according to the algorithm. + * + * @hide + */ + TriState checkRtl(char[] text, int start, int count); + } + + /** + * Algorithm that uses the first strong directional character to determine + * the paragraph direction. This is the standard Unicode Bidirectional + * algorithm. + * + * @hide + */ + public static class FirstStrong implements TextDirectionAlgorithm { + @Override + public TriState checkRtl(char[] text, int start, int count) { + TriState result = TriState.UNKNOWN; + for (int i = start, e = start + count; i < e && result == TriState.UNKNOWN; ++i) { + result = isRtlTextOrFormat(Character.getDirectionality(text[i])); + } + return result; + } + + private FirstStrong() { + } + + public static final FirstStrong INSTANCE = new FirstStrong(); + } + + /** + * Algorithm that uses the presence of any strong directional non-format + * character (e.g. excludes LRE, LRO, RLE, RLO) to determine the + * direction of text. + * + * @hide + */ + public static class AnyStrong implements TextDirectionAlgorithm { + private final boolean mLookForRtl; + + @Override + public TriState checkRtl(char[] text, int start, int count) { + boolean haveUnlookedFor = false; + for (int i = start, e = start + count; i < e; ++i) { + switch (isRtlText(Character.getDirectionality(text[i]))) { + case TRUE: + if (mLookForRtl) { + return TriState.TRUE; + } + haveUnlookedFor = true; + break; + case FALSE: + if (!mLookForRtl) { + return TriState.FALSE; + } + haveUnlookedFor = true; + break; + default: + break; + } + } + if (haveUnlookedFor) { + return mLookForRtl ? TriState.FALSE : TriState.TRUE; + } + return TriState.UNKNOWN; + } + + private AnyStrong(boolean lookForRtl) { + this.mLookForRtl = lookForRtl; + } + + public static final AnyStrong INSTANCE_RTL = new AnyStrong(true); + public static final AnyStrong INSTANCE_LTR = new AnyStrong(false); + } + + /** + * Algorithm that uses the relative proportion of strong directional + * characters (excluding LRE, LRO, RLE, RLO) to determine the direction + * of the paragraph, if the proportion exceeds a given threshold. + * + * @hide + */ + public static class CharCount implements TextDirectionAlgorithm { + private final float mThreshold; + + @Override + public TriState checkRtl(char[] text, int start, int count) { + int countLtr = 0; + int countRtl = 0; + for(int i = start, e = start + count; i < e; ++i) { + switch (isRtlText(Character.getDirectionality(text[i]))) { + case TRUE: + ++countLtr; + break; + case FALSE: + ++countRtl; + break; + default: + break; + } + } + int limit = (int)((countLtr + countRtl) * mThreshold); + if (limit > 0) { + if (countLtr > limit) { + return TriState.FALSE; + } + if (countRtl > limit) { + return TriState.TRUE; + } + } + return TriState.UNKNOWN; + } + + private CharCount(float threshold) { + mThreshold = threshold; + } + + public static CharCount withThreshold(float threshold) { + if (threshold < 0 || threshold > 1) { + throw new IllegalArgumentException(); + } + if (threshold == DEFAULT_THRESHOLD) { + return INSTANCE_DEFAULT; + } + return new CharCount(threshold); + } + + public static final float DEFAULT_THRESHOLD = 0.6f; + public static final CharCount INSTANCE_DEFAULT = new CharCount(DEFAULT_THRESHOLD); + } +} diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java index 6741059..29c9853 100644 --- a/core/java/android/text/TextUtils.java +++ b/core/java/android/text/TextUtils.java @@ -16,9 +16,6 @@ package android.text; -import com.android.internal.R; -import com.android.internal.util.ArrayUtils; - import android.content.res.Resources; import android.os.Parcel; import android.os.Parcelable; @@ -45,6 +42,9 @@ import android.text.style.URLSpan; import android.text.style.UnderlineSpan; import android.util.Printer; +import com.android.internal.R; +import com.android.internal.util.ArrayUtils; + import java.lang.reflect.Array; import java.util.Iterator; import java.util.regex.Pattern; @@ -1001,13 +1001,37 @@ public class TextUtils { * will be padded with zero-width spaces to preserve the original * length and offsets instead of truncating. * If <code>callback</code> is non-null, it will be called to - * report the start and end of the ellipsized range. + * report the start and end of the ellipsized range. TextDirection + * is determined by the first strong directional character. */ public static CharSequence ellipsize(CharSequence text, TextPaint paint, float avail, TruncateAt where, boolean preserveLength, EllipsizeCallback callback) { + return ellipsize(text, paint, avail, where, preserveLength, callback, + TextDirectionHeuristics.FIRSTSTRONG_LTR); + } + + /** + * Returns the original text if it fits in the specified width + * given the properties of the specified Paint, + * or, if it does not fit, a copy with ellipsis character added + * at the specified edge or center. + * If <code>preserveLength</code> is specified, the returned copy + * will be padded with zero-width spaces to preserve the original + * length and offsets instead of truncating. + * If <code>callback</code> is non-null, it will be called to + * report the start and end of the ellipsized range. + * + * @hide + */ + public static CharSequence ellipsize(CharSequence text, + TextPaint paint, + float avail, TruncateAt where, + boolean preserveLength, + EllipsizeCallback callback, + TextDirectionHeuristic textDir) { if (sEllipsis == null) { Resources r = Resources.getSystem(); sEllipsis = r.getString(R.string.ellipsis); @@ -1017,8 +1041,7 @@ public class TextUtils { MeasuredText mt = MeasuredText.obtain(); try { - float width = setPara(mt, paint, text, 0, text.length(), - Layout.DIR_REQUEST_DEFAULT_LTR); + float width = setPara(mt, paint, text, 0, text.length(), textDir); if (width <= avail) { if (callback != null) { @@ -1108,11 +1131,20 @@ public class TextUtils { TextPaint p, float avail, String oneMore, String more) { + return commaEllipsize(text, p, avail, oneMore, more, + TextDirectionHeuristics.FIRSTSTRONG_LTR); + } + + /** + * @hide + */ + public static CharSequence commaEllipsize(CharSequence text, TextPaint p, + float avail, String oneMore, String more, TextDirectionHeuristic textDir) { MeasuredText mt = MeasuredText.obtain(); try { int len = text.length(); - float width = setPara(mt, p, text, 0, len, Layout.DIR_REQUEST_DEFAULT_LTR); + float width = setPara(mt, p, text, 0, len, textDir); if (width <= avail) { return text; } @@ -1135,9 +1167,6 @@ public class TextUtils { int count = 0; float[] widths = mt.mWidths; - int request = mt.mDir == 1 ? Layout.DIR_REQUEST_LTR : - Layout.DIR_REQUEST_RTL; - MeasuredText tempMt = MeasuredText.obtain(); for (int i = 0; i < len; i++) { w += widths[i]; @@ -1155,7 +1184,7 @@ public class TextUtils { } // XXX this is probably ok, but need to look at it more - tempMt.setPara(format, 0, format.length(), request); + tempMt.setPara(format, 0, format.length(), textDir); float moreWid = tempMt.addStyleRun(p, tempMt.mLen, null); if (w + moreWid <= avail) { @@ -1175,9 +1204,9 @@ public class TextUtils { } private static float setPara(MeasuredText mt, TextPaint paint, - CharSequence text, int start, int end, int bidiRequest) { + CharSequence text, int start, int end, TextDirectionHeuristic textDir) { - mt.setPara(text, start, end, bidiRequest); + mt.setPara(text, start, end, textDir); float width; Spanned sp = text instanceof Spanned ? (Spanned) text : null; diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 74dc100..c8ff37a 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -16,13 +16,6 @@ package android.view; -import android.util.FloatProperty; -import android.util.LocaleUtil; -import android.util.Property; -import com.android.internal.R; -import com.android.internal.util.Predicate; -import com.android.internal.view.menu.MenuBuilder; - import android.content.ClipData; import android.content.Context; import android.content.res.Configuration; @@ -53,11 +46,14 @@ import android.os.Parcelable; import android.os.RemoteException; import android.os.SystemClock; import android.util.AttributeSet; +import android.util.FloatProperty; +import android.util.LocaleUtil; import android.util.Log; import android.util.Pool; import android.util.Poolable; import android.util.PoolableManager; import android.util.Pools; +import android.util.Property; import android.util.SparseArray; import android.util.TypedValue; import android.view.ContextMenu.ContextMenuInfo; @@ -72,6 +68,10 @@ import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; import android.widget.ScrollBarDrawable; +import com.android.internal.R; +import com.android.internal.util.Predicate; +import com.android.internal.view.menu.MenuBuilder; + import java.lang.ref.WeakReference; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -2493,12 +2493,6 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit private boolean mSendingHoverAccessibilityEvents; /** - * Undefined text direction (used by resolution algorithm). - * @hide - */ - public static final int TEXT_DIRECTION_UNDEFINED = -1; - - /** * Text direction is inherited thru {@link ViewGroup} * @hide */ @@ -2507,7 +2501,7 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit /** * Text direction is using "first strong algorithm". The first strong directional character * determines the paragraph direction. If there is no strong directional character, the - * paragraph direction is the view’s resolved ayout direction. + * paragraph direction is the view's resolved ayout direction. * * @hide */ @@ -2516,7 +2510,7 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit /** * Text direction is using "any-RTL" algorithm. The paragraph direction is RTL if it contains * any strong RTL character, otherwise it is LTR if it contains any strong LTR characters. - * If there are neither, the paragraph direction is the view’s resolved layout direction. + * If there are neither, the paragraph direction is the view's resolved layout direction. * * @hide */ @@ -2560,7 +2554,6 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit * {@hide} */ @ViewDebug.ExportedProperty(category = "text", mapping = { - @ViewDebug.IntToString(from = TEXT_DIRECTION_UNDEFINED, to = "UNDEFINED"), @ViewDebug.IntToString(from = TEXT_DIRECTION_INHERIT, to = "INHERIT"), @ViewDebug.IntToString(from = TEXT_DIRECTION_FIRST_STRONG, to = "FIRST_STRONG"), @ViewDebug.IntToString(from = TEXT_DIRECTION_ANY_RTL, to = "ANY_RTL"), @@ -2568,21 +2561,25 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit @ViewDebug.IntToString(from = TEXT_DIRECTION_LTR, to = "LTR"), @ViewDebug.IntToString(from = TEXT_DIRECTION_RTL, to = "RTL") }) - protected int mTextDirection = DEFAULT_TEXT_DIRECTION; + private int mTextDirection = DEFAULT_TEXT_DIRECTION; /** - * The resolved text direction. If resolution has not yet been done or has been reset, it will - * be equal to {@link #TEXT_DIRECTION_UNDEFINED}. Otherwise it will be either {@link #TEXT_DIRECTION_LTR} - * or {@link #TEXT_DIRECTION_RTL}. + * The resolved text direction. This needs resolution if the value is + * TEXT_DIRECTION_INHERIT. The resolution matches mTextDirection if that is + * not TEXT_DIRECTION_INHERIT, otherwise resolution proceeds up the parent + * chain of the view. * * {@hide} */ @ViewDebug.ExportedProperty(category = "text", mapping = { - @ViewDebug.IntToString(from = TEXT_DIRECTION_UNDEFINED, to = "UNDEFINED"), + @ViewDebug.IntToString(from = TEXT_DIRECTION_INHERIT, to = "INHERIT"), + @ViewDebug.IntToString(from = TEXT_DIRECTION_FIRST_STRONG, to = "FIRST_STRONG"), + @ViewDebug.IntToString(from = TEXT_DIRECTION_ANY_RTL, to = "ANY_RTL"), + @ViewDebug.IntToString(from = TEXT_DIRECTION_CHAR_COUNT, to = "CHAR_COUNT"), @ViewDebug.IntToString(from = TEXT_DIRECTION_LTR, to = "LTR"), @ViewDebug.IntToString(from = TEXT_DIRECTION_RTL, to = "RTL") }) - protected int mResolvedTextDirection = TEXT_DIRECTION_UNDEFINED; + private int mResolvedTextDirection = TEXT_DIRECTION_INHERIT; /** * Consistency verifier for debugging purposes. @@ -9066,11 +9063,20 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit // Set resolved depending on layout direction switch (getLayoutDirection()) { case LAYOUT_DIRECTION_INHERIT: + // We cannot do the resolution if there is no parent + if (mParent == null) return; + // If this is root view, no need to look at parent's layout dir. - if (mParent != null && - mParent instanceof ViewGroup && - ((ViewGroup) mParent).getResolvedLayoutDirection() == LAYOUT_DIRECTION_RTL) { - mPrivateFlags2 |= LAYOUT_DIRECTION_RESOLVED_RTL; + if (mParent instanceof ViewGroup) { + ViewGroup viewGroup = ((ViewGroup) mParent); + + // Check if the parent view group can resolve + if (! viewGroup.canResolveLayoutDirection()) { + return; + } + if (viewGroup.getResolvedLayoutDirection() == LAYOUT_DIRECTION_RTL) { + mPrivateFlags2 |= LAYOUT_DIRECTION_RESOLVED_RTL; + } } break; case LAYOUT_DIRECTION_RTL: @@ -9132,6 +9138,15 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit recomputePadding(); } + protected boolean canResolveLayoutDirection() { + switch (getLayoutDirection()) { + case LAYOUT_DIRECTION_INHERIT: + return (mParent != null); + default: + return true; + } + } + /** * Reset the resolved layout direction. * @@ -13048,43 +13063,41 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit * * @return the resolved text direction. Return one of: * + * {@link #TEXT_DIRECTION_FIRST_STRONG} + * {@link #TEXT_DIRECTION_ANY_RTL}, + * {@link #TEXT_DIRECTION_CHAR_COUNT}, * {@link #TEXT_DIRECTION_LTR}, * {@link #TEXT_DIRECTION_RTL}, * * @hide */ public int getResolvedTextDirection() { - if (!isResolvedTextDirection()) { + if (mResolvedTextDirection == TEXT_DIRECTION_INHERIT) { resolveTextDirection(); } return mResolvedTextDirection; } /** - * Resolve the text direction. Classes that extend View and want to do a specific text direction - * resolution will need to implement this method and set the mResolvedTextDirection to - * either TEXT_DIRECTION_LTR if direction is LTR or TEXT_DIRECTION_RTL if - * direction is RTL. + * Resolve the text direction. */ protected void resolveTextDirection() { + if (mTextDirection != TEXT_DIRECTION_INHERIT) { + mResolvedTextDirection = mTextDirection; + return; + } + if (mParent != null && mParent instanceof ViewGroup) { + mResolvedTextDirection = ((ViewGroup) mParent).getResolvedTextDirection(); + return; + } + mResolvedTextDirection = TEXT_DIRECTION_FIRST_STRONG; } /** - * Return if the text direction has been resolved or not. - * - * @return true, if resolved and false if not resolved - * - * @hide - */ - public boolean isResolvedTextDirection() { - return (mResolvedTextDirection != TEXT_DIRECTION_UNDEFINED); - } - - /** - * Reset resolved text direction. Will be resolved during a call to getResolvedLayoutDirection(). + * Reset resolved text direction. Will be resolved during a call to getResolvedTextDirection(). */ protected void resetResolvedTextDirection() { - mResolvedTextDirection = TEXT_DIRECTION_UNDEFINED; + mResolvedTextDirection = TEXT_DIRECTION_INHERIT; } // diff --git a/core/java/android/view/ViewAncestor.java b/core/java/android/view/ViewAncestor.java index 1dcbc26..ac73611 100644 --- a/core/java/android/view/ViewAncestor.java +++ b/core/java/android/view/ViewAncestor.java @@ -2209,6 +2209,62 @@ public final class ViewAncestor extends Handler implements ViewParent, public final static int DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT = 1023; @Override + public String getMessageName(Message message) { + switch (message.what) { + case DO_TRAVERSAL: + return "DO_TRAVERSAL"; + case DIE: + return "DIE"; + case RESIZED: + return "RESIZED"; + case RESIZED_REPORT: + return "RESIZED_REPORT"; + case WINDOW_FOCUS_CHANGED: + return "WINDOW_FOCUS_CHANGED"; + case DISPATCH_KEY: + return "DISPATCH_KEY"; + case DISPATCH_POINTER: + return "DISPATCH_POINTER"; + case DISPATCH_TRACKBALL: + return "DISPATCH_TRACKBALL"; + case DISPATCH_APP_VISIBILITY: + return "DISPATCH_APP_VISIBILITY"; + case DISPATCH_GET_NEW_SURFACE: + return "DISPATCH_GET_NEW_SURFACE"; + case FINISHED_EVENT: + return "FINISHED_EVENT"; + case DISPATCH_KEY_FROM_IME: + return "DISPATCH_KEY_FROM_IME"; + case FINISH_INPUT_CONNECTION: + return "FINISH_INPUT_CONNECTION"; + case CHECK_FOCUS: + return "CHECK_FOCUS"; + case CLOSE_SYSTEM_DIALOGS: + return "CLOSE_SYSTEM_DIALOGS"; + case DISPATCH_DRAG_EVENT: + return "DISPATCH_DRAG_EVENT"; + case DISPATCH_DRAG_LOCATION_EVENT: + return "DISPATCH_DRAG_LOCATION_EVENT"; + case DISPATCH_SYSTEM_UI_VISIBILITY: + return "DISPATCH_SYSTEM_UI_VISIBILITY"; + case DISPATCH_GENERIC_MOTION: + return "DISPATCH_GENERIC_MOTION"; + case UPDATE_CONFIGURATION: + return "UPDATE_CONFIGURATION"; + case DO_PERFORM_ACCESSIBILITY_ACTION: + return "DO_PERFORM_ACCESSIBILITY_ACTION"; + case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID: + return "DO_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID"; + case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID: + return "DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID"; + case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT: + return "DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT"; + + } + return super.getMessageName(message); + } + + @Override public void handleMessage(Message msg) { switch (msg.what) { case View.AttachInfo.INVALIDATE_MSG: @@ -2634,8 +2690,9 @@ public final class ViewAncestor extends Handler implements ViewParent, mInputEventDeliverTimeNanos = System.nanoTime(); } + final boolean isTouchEvent = event.isTouchEvent(); if (mInputEventConsistencyVerifier != null) { - if (event.isTouchEvent()) { + if (isTouchEvent) { mInputEventConsistencyVerifier.onTouchEvent(event, 0); } else { mInputEventConsistencyVerifier.onGenericMotionEvent(event, 0); @@ -2653,9 +2710,9 @@ public final class ViewAncestor extends Handler implements ViewParent, mTranslator.translateEventInScreenToAppWindow(event); } - // Enter touch mode on the down. - boolean isDown = event.getAction() == MotionEvent.ACTION_DOWN; - if (isDown) { + // Enter touch mode on down or scroll. + final int action = event.getAction(); + if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_SCROLL) { ensureTouchMode(true); } @@ -2668,8 +2725,10 @@ public final class ViewAncestor extends Handler implements ViewParent, } // Remember the touch position for possible drag-initiation. - mLastTouchPoint.x = event.getRawX(); - mLastTouchPoint.y = event.getRawY(); + if (isTouchEvent) { + mLastTouchPoint.x = event.getRawX(); + mLastTouchPoint.y = event.getRawY(); + } // Dispatch touch to view hierarchy. boolean handled = mView.dispatchPointerEvent(event); @@ -2681,51 +2740,6 @@ public final class ViewAncestor extends Handler implements ViewParent, return; } - // Apply edge slop and try again, if appropriate. - final int edgeFlags = event.getEdgeFlags(); - if (edgeFlags != 0 && mView instanceof ViewGroup) { - final int edgeSlop = mViewConfiguration.getScaledEdgeSlop(); - int direction = View.FOCUS_UP; - int x = (int)event.getX(); - int y = (int)event.getY(); - final int[] deltas = new int[2]; - - if ((edgeFlags & MotionEvent.EDGE_TOP) != 0) { - direction = View.FOCUS_DOWN; - if ((edgeFlags & MotionEvent.EDGE_LEFT) != 0) { - deltas[0] = edgeSlop; - x += edgeSlop; - } else if ((edgeFlags & MotionEvent.EDGE_RIGHT) != 0) { - deltas[0] = -edgeSlop; - x -= edgeSlop; - } - } else if ((edgeFlags & MotionEvent.EDGE_BOTTOM) != 0) { - direction = View.FOCUS_UP; - if ((edgeFlags & MotionEvent.EDGE_LEFT) != 0) { - deltas[0] = edgeSlop; - x += edgeSlop; - } else if ((edgeFlags & MotionEvent.EDGE_RIGHT) != 0) { - deltas[0] = -edgeSlop; - x -= edgeSlop; - } - } else if ((edgeFlags & MotionEvent.EDGE_LEFT) != 0) { - direction = View.FOCUS_RIGHT; - } else if ((edgeFlags & MotionEvent.EDGE_RIGHT) != 0) { - direction = View.FOCUS_LEFT; - } - - View nearest = FocusFinder.getInstance().findNearestTouchable( - ((ViewGroup) mView), x, y, direction, deltas); - if (nearest != null) { - event.offsetLocation(deltas[0], deltas[1]); - event.setEdgeFlags(0); - if (mView.dispatchPointerEvent(event)) { - finishMotionEvent(event, sendDone, true); - return; - } - } - } - // Pointer event was unhandled. finishMotionEvent(event, sendDone, false); } diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java index f014070..f7f5a21 100644 --- a/core/java/android/view/ViewDebug.java +++ b/core/java/android/view/ViewDebug.java @@ -16,41 +16,45 @@ package android.view; -import android.util.Log; -import android.util.DisplayMetrics; -import android.content.res.Resources; import android.content.Context; +import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Rect; -import android.os.Environment; import android.os.Debug; +import android.os.Environment; +import android.os.Looper; +import android.os.Message; import android.os.RemoteException; +import android.util.DisplayMetrics; +import android.util.Log; +import android.util.Printer; +import java.io.BufferedOutputStream; +import java.io.BufferedWriter; import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; import java.io.File; -import java.io.BufferedWriter; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; -import java.io.FileOutputStream; -import java.io.DataOutputStream; -import java.io.OutputStreamWriter; -import java.io.BufferedOutputStream; import java.io.OutputStream; -import java.util.List; -import java.util.LinkedList; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.lang.annotation.Target; +import java.io.OutputStreamWriter; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.reflect.AccessibleObject; import java.lang.reflect.Field; -import java.lang.reflect.Method; import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; /** * Various debugging/tracing tools related to {@link View} and the view hierarchy. @@ -106,13 +110,6 @@ public class ViewDebug { public static final boolean DEBUG_PROFILE_LAYOUT = false; /** - * Profiles real fps (times between draws) and displays the result. - * - * @hide - */ - public static final boolean DEBUG_SHOW_FPS = false; - - /** * Enables detailed logging of drag/drop operations. * @hide */ @@ -396,6 +393,9 @@ public class ViewDebug { private static List<RecyclerTrace> sRecyclerTraces; private static String sRecyclerTracePrefix; + private static final ThreadLocal<LooperProfiler> sLooperProfilerStorage = + new ThreadLocal<LooperProfiler>(); + /** * Returns the number of instanciated Views. * @@ -419,6 +419,124 @@ public class ViewDebug { } /** + * Starts profiling the looper associated with the current thread. + * You must call {@link #stopLooperProfiling} to end profiling + * and obtain the traces. Both methods must be invoked on the + * same thread. + * + * @param traceFile The path where to write the looper traces + * + * @see #stopLooperProfiling() + */ + public static void startLooperProfiling(File traceFile) { + if (sLooperProfilerStorage.get() == null) { + LooperProfiler profiler = new LooperProfiler(traceFile); + sLooperProfilerStorage.set(profiler); + Looper.myLooper().setMessageLogging(profiler); + } + } + + /** + * Stops profiling the looper associated with the current thread. + * + * @see #startLooperProfiling(java.io.File) + */ + public static void stopLooperProfiling() { + LooperProfiler profiler = sLooperProfilerStorage.get(); + if (profiler != null) { + sLooperProfilerStorage.remove(); + Looper.myLooper().setMessageLogging(null); + profiler.save(); + } + } + + private static class LooperProfiler implements Looper.Profiler, Printer { + private static final int LOOPER_PROFILER_VERSION = 1; + + private static final String LOG_TAG = "LooperProfiler"; + + private final ArrayList<Entry> mTraces = new ArrayList<Entry>(512); + private final File mTraceFile; + + public LooperProfiler(File traceFile) { + mTraceFile = traceFile; + } + + @Override + public void println(String x) { + // Ignore messages + } + + @Override + public void profile(Message message, long wallStart, long wallTime, long threadTime) { + Entry entry = new Entry(); + entry.messageId = message.what; + entry.name = message.getTarget().getMessageName(message); + entry.wallStart = wallStart; + entry.wallTime = wallTime; + entry.threadTime = threadTime; + + mTraces.add(entry); + } + + void save() { + // Don't block the UI thread + new Thread(new Runnable() { + @Override + public void run() { + saveTraces(); + } + }, "LooperProfiler[" + mTraceFile + "]").start(); + } + + private void saveTraces() { + FileOutputStream fos; + try { + fos = new FileOutputStream(mTraceFile); + } catch (FileNotFoundException e) { + Log.e(LOG_TAG, "Could not open trace file: " + mTraceFile); + return; + } + + DataOutputStream out = new DataOutputStream(new BufferedOutputStream(fos)); + + try { + out.writeInt(LOOPER_PROFILER_VERSION); + out.writeInt(mTraces.size()); + for (Entry entry : mTraces) { + saveTrace(entry, out); + } + + Log.d(LOG_TAG, "Looper traces ready: " + mTraceFile); + } catch (IOException e) { + Log.e(LOG_TAG, "Could not write trace file: ", e); + } finally { + try { + out.close(); + } catch (IOException e) { + // Ignore + } + } + } + + private void saveTrace(Entry entry, DataOutputStream out) throws IOException { + out.writeInt(entry.messageId); + out.writeUTF(entry.name); + out.writeLong(entry.wallStart); + out.writeLong(entry.wallTime); + out.writeLong(entry.threadTime); + } + + static class Entry { + int messageId; + String name; + long wallStart; + long wallTime; + long threadTime; + } + } + + /** * Outputs a trace to the currently opened recycler traces. The trace records the type of * recycler action performed on the supplied view as well as a number of parameters. * diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 752fd5a..cb3e9c6 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -41,6 +41,7 @@ import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.view.animation.LayoutAnimationController; import android.view.animation.Transformation; + import com.android.internal.R; import com.android.internal.util.Predicate; @@ -5012,37 +5013,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } } - /** - * This method will be called during text direction resolution (text direction resolution - * inheritance) - */ - @Override - protected void resolveTextDirection() { - int resolvedTextDirection; - switch(mTextDirection) { - default: - case TEXT_DIRECTION_INHERIT: - // Try to the text direction from the parent layout - if (mParent != null && mParent instanceof ViewGroup) { - resolvedTextDirection = ((ViewGroup) mParent).getResolvedTextDirection(); - } else { - // We reached the top of the View hierarchy, so set the text direction - // heuristic to "first strong" - resolvedTextDirection = TEXT_DIRECTION_FIRST_STRONG; - } - break; - // Pass down the hierarchy the following text direction values - case TEXT_DIRECTION_FIRST_STRONG: - case TEXT_DIRECTION_ANY_RTL: - case TEXT_DIRECTION_CHAR_COUNT: - case TEXT_DIRECTION_LTR: - case TEXT_DIRECTION_RTL: - resolvedTextDirection = mTextDirection; - break; - } - mResolvedTextDirection = resolvedTextDirection; - } - @Override protected void resetResolvedTextDirection() { super.resetResolvedTextDirection(); diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java index 761007f..d584acd 100644 --- a/core/java/android/webkit/WebSettings.java +++ b/core/java/android/webkit/WebSettings.java @@ -1783,6 +1783,20 @@ public class WebSettings { } /** + * @hide + */ + public void setProperty(String key, String value) { + mWebView.nativeSetProperty(key, value); + } + + /** + * @hide + */ + public String getProperty(String key) { + return mWebView.nativeGetProperty(key); + } + + /** * Transfer messages from the queue to the new WebCoreThread. Called from * WebCore thread. */ diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 3ae10fe..b22c57b 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -9289,4 +9289,6 @@ public class WebView extends AbsoluteLayout */ private native boolean nativeScrollLayer(int layer, int newX, int newY); private native int nativeGetBackgroundColor(); + native void nativeSetProperty(String key, String value); + native String nativeGetProperty(String key); } diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index 1449b18..8f8c1d0 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -271,12 +271,6 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te Drawable mSelector; /** - * Set to true if we would like to have the selector showing itself. - * We still need to draw and position it even if this is false. - */ - boolean mSelectorShowing; - - /** * The current position of the selector in the list. */ int mSelectorPosition = INVALID_POSITION; @@ -1669,7 +1663,6 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te setSelectedPositionInt(INVALID_POSITION); setNextSelectedPositionInt(INVALID_POSITION); mSelectedTop = 0; - mSelectorShowing = false; mSelectorPosition = INVALID_POSITION; mSelectorRect.setEmpty(); invalidate(); @@ -2025,7 +2018,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te final boolean isChildViewEnabled = mIsChildViewEnabled; if (sel.isEnabled() != isChildViewEnabled) { mIsChildViewEnabled = !isChildViewEnabled; - if (mSelectorShowing) { + if (getSelectedItemPosition() != INVALID_POSITION) { refreshDrawableState(); } } @@ -2769,6 +2762,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te // touch mode). Force an initial layout to get rid of the selection. layoutChildren(); } + updateSelectorState(); } else { int touchMode = mTouchMode; if (touchMode == TOUCH_MODE_OVERSCROLL || touchMode == TOUCH_MODE_OVERFLING) { @@ -2847,14 +2841,6 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout()); } else { - if (ev.getEdgeFlags() != 0 && motionPosition < 0) { - // If we couldn't find a view to click on, but the down event - // was touching the edge, we will bail out and try again. - // This allows the edge correcting code in ViewAncestor to try to - // find a nearby view to select - return false; - } - if (mTouchMode == TOUCH_MODE_FLING) { // Stopped a fling. It is a scroll. createScrollingCache(); @@ -2888,7 +2874,11 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } case MotionEvent.ACTION_MOVE: { - final int pointerIndex = ev.findPointerIndex(mActivePointerId); + int pointerIndex = ev.findPointerIndex(mActivePointerId); + if (pointerIndex == -1) { + pointerIndex = 0; + mActivePointerId = ev.getPointerId(pointerIndex); + } final int y = (int) ev.getY(pointerIndex); deltaY = y - mMotionY; switch (mTouchMode) { @@ -3464,7 +3454,11 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te case MotionEvent.ACTION_MOVE: { switch (mTouchMode) { case TOUCH_MODE_DOWN: - final int pointerIndex = ev.findPointerIndex(mActivePointerId); + int pointerIndex = ev.findPointerIndex(mActivePointerId); + if (pointerIndex == -1) { + pointerIndex = 0; + mActivePointerId = ev.getPointerId(pointerIndex); + } final int y = (int) ev.getY(pointerIndex); if (startScrollIfNeeded(y - mMotionY)) { return true; @@ -4521,7 +4515,6 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te setSelectedPositionInt(INVALID_POSITION); setNextSelectedPositionInt(INVALID_POSITION); mSelectedTop = 0; - mSelectorShowing = false; } } @@ -4645,6 +4638,9 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te childrenTop += getVerticalFadingEdgeLength(); } } + // Don't ever focus a disabled item. + if (!mAdapter.isEnabled(i)) continue; + if (top >= childrenTop) { // Found a view whose top is fully visisble selectedPos = firstPosition + i; diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java index 7c9be1e..b428301 100644 --- a/core/java/android/widget/HorizontalScrollView.java +++ b/core/java/android/widget/HorizontalScrollView.java @@ -498,13 +498,6 @@ public class HorizontalScrollView extends FrameLayout { @Override public boolean onTouchEvent(MotionEvent ev) { - - if (ev.getAction() == MotionEvent.ACTION_DOWN && ev.getEdgeFlags() != 0) { - // Don't handle edge touches immediately -- they may actually belong to one of our - // descendants. - return false; - } - if (mVelocityTracker == null) { mVelocityTracker = VelocityTracker.obtain(); } diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java index e7a9e41..1f29b16 100644 --- a/core/java/android/widget/ListView.java +++ b/core/java/android/widget/ListView.java @@ -3588,17 +3588,6 @@ public class ListView extends AbsListView { return null; } - @Override - public boolean onTouchEvent(MotionEvent ev) { - //noinspection SimplifiableIfStatement - if (mItemsCanFocus && ev.getAction() == MotionEvent.ACTION_DOWN && ev.getEdgeFlags() != 0) { - // Don't handle edge touches immediately -- they may actually belong to one of our - // descendants. - return false; - } - return super.onTouchEvent(ev); - } - /** * Returns the set of checked items ids. The result is only valid if the * choice mode has not been set to {@link #CHOICE_MODE_NONE}. diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java index 4c47d37..867ebb4 100644 --- a/core/java/android/widget/RemoteViewsAdapter.java +++ b/core/java/android/widget/RemoteViewsAdapter.java @@ -53,6 +53,10 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback // This ensures that we don't stay continually bound to the service and that it can be destroyed // if we need the memory elsewhere in the system. private static final int sUnbindServiceDelay = 5000; + + // Default height for the default loading view, in case we cannot get inflate the first view + private static final int sDefaultLoadingViewHeight = 50; + // Type defs for controlling different messages across the main and worker message queues private static final int sDefaultMessageType = 0; private static final int sUnbindServiceMessageType = 1; @@ -386,21 +390,39 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback // Create a new loading view synchronized (mCache) { + boolean customLoadingViewAvailable = false; + if (mUserLoadingView != null) { - // A user-specified loading view - View loadingView = mUserLoadingView.apply(parent.getContext(), parent); - loadingView.setTagInternal(com.android.internal.R.id.rowTypeId, new Integer(0)); - layout.addView(loadingView); - } else { + // Try to inflate user-specified loading view + try { + View loadingView = mUserLoadingView.apply(parent.getContext(), parent); + loadingView.setTagInternal(com.android.internal.R.id.rowTypeId, + new Integer(0)); + layout.addView(loadingView); + customLoadingViewAvailable = true; + } catch (Exception e) { + Log.w(TAG, "Error inflating custom loading view, using default loading" + + "view instead", e); + } + } + if (!customLoadingViewAvailable) { // A default loading view // Use the size of the first row as a guide for the size of the loading view if (mFirstViewHeight < 0) { - View firstView = mFirstView.apply(parent.getContext(), parent); - firstView.measure( - MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), - MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); - mFirstViewHeight = firstView.getMeasuredHeight(); - mFirstView = null; + try { + View firstView = mFirstView.apply(parent.getContext(), parent); + firstView.measure( + MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), + MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); + mFirstViewHeight = firstView.getMeasuredHeight(); + mFirstView = null; + } catch (Exception e) { + float density = mContext.getResources().getDisplayMetrics().density; + mFirstViewHeight = (int) + Math.round(sDefaultLoadingViewHeight * density); + mFirstView = null; + Log.w(TAG, "Error inflating first RemoteViews" + e); + } } // Compose the loading view text @@ -937,24 +959,40 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback indexMetaData.isRequested = true; int typeId = indexMetaData.typeId; - // Reuse the convert view where possible - if (layout != null) { - if (convertViewTypeId == typeId) { - rv.reapply(context, convertViewChild); - return layout; + try { + // Reuse the convert view where possible + if (layout != null) { + if (convertViewTypeId == typeId) { + rv.reapply(context, convertViewChild); + return layout; + } + layout.removeAllViews(); + } else { + layout = new RemoteViewsFrameLayout(context); } - layout.removeAllViews(); - } else { - layout = new RemoteViewsFrameLayout(context); - } - // Otherwise, create a new view to be returned - View newView = rv.apply(context, parent); - newView.setTagInternal(com.android.internal.R.id.rowTypeId, new Integer(typeId)); - layout.addView(newView); - if (hasNewItems) loadNextIndexInBackground(); - - return layout; + // Otherwise, create a new view to be returned + View newView = rv.apply(context, parent); + newView.setTagInternal(com.android.internal.R.id.rowTypeId, + new Integer(typeId)); + layout.addView(newView); + return layout; + + } catch (Exception e){ + // We have to make sure that we successfully inflated the RemoteViews, if not + // we return the loading view instead. + Log.w(TAG, "Error inflating RemoteViews at position: " + position + ", using" + + "loading view instead" + e); + + RemoteViewsFrameLayout loadingView = null; + final RemoteViewsMetaData metaData = mCache.getMetaData(); + synchronized (metaData) { + loadingView = metaData.createLoadingView(position, convertView, parent); + } + return loadingView; + } finally { + if (hasNewItems) loadNextIndexInBackground(); + } } else { // If the cache does not have the RemoteViews at this position, then create a // loading view and queue the actual position to be loaded in the background diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java index 12775a4..e59f731 100644 --- a/core/java/android/widget/ScrollView.java +++ b/core/java/android/widget/ScrollView.java @@ -65,7 +65,6 @@ public class ScrollView extends FrameLayout { static final float MAX_SCROLL_FACTOR = 0.5f; - private long mLastScroll; private final Rect mTempRect = new Rect(); @@ -506,13 +505,6 @@ public class ScrollView extends FrameLayout { @Override public boolean onTouchEvent(MotionEvent ev) { - - if (ev.getAction() == MotionEvent.ACTION_DOWN && ev.getEdgeFlags() != 0) { - // Don't handle edge touches immediately -- they may actually belong to one of our - // descendants. - return false; - } - if (mVelocityTracker == null) { mVelocityTracker = VelocityTracker.obtain(); } @@ -1438,17 +1430,6 @@ public class ScrollView extends FrameLayout { final boolean movingDown = velocityY > 0; - View currentFocused = findFocus(); - View newFocused = - findFocusableViewInMyBounds(movingDown, mScroller.getFinalY(), currentFocused); - if (newFocused == null) { - newFocused = this; - } - - if (newFocused != currentFocused) { - newFocused.requestFocus(movingDown ? View.FOCUS_DOWN : View.FOCUS_UP); - } - if (mFlingStrictSpan == null) { mFlingStrictSpan = StrictMode.enterCriticalSpan("ScrollView-fling"); } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 51eee20..5a5fae4 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -16,6 +16,11 @@ package android.widget; +import com.android.internal.util.FastMath; +import com.android.internal.widget.EditableInputConnection; + +import org.xmlpull.v1.XmlPullParserException; + import android.R; import android.content.ClipData; import android.content.ClipData.Item; @@ -59,6 +64,13 @@ import android.text.SpannableStringBuilder; import android.text.Spanned; import android.text.SpannedString; import android.text.StaticLayout; +import android.text.TextDirectionHeuristic; +import android.text.TextDirectionHeuristics; +import android.text.TextDirectionHeuristics.AnyStrong; +import android.text.TextDirectionHeuristics.CharCount; +import android.text.TextDirectionHeuristics.FirstStrong; +import android.text.TextDirectionHeuristics.TextDirectionAlgorithm; +import android.text.TextDirectionHeuristics.TextDirectionHeuristicImpl; import android.text.TextPaint; import android.text.TextUtils; import android.text.TextWatcher; @@ -127,11 +139,6 @@ import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; import android.widget.RemoteViews.RemoteView; -import com.android.internal.util.FastMath; -import com.android.internal.widget.EditableInputConnection; - -import org.xmlpull.v1.XmlPullParserException; - import java.io.IOException; import java.lang.ref.WeakReference; import java.text.BreakIterator; @@ -5832,6 +5839,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (curs >= 0) { mHighlightPathBogus = true; makeBlink(); + bringPointIntoView(curs); } checkForResize(); @@ -5992,14 +6000,17 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener Layout.Alignment alignment = getLayoutAlignment(); boolean shouldEllipsize = mEllipsize != null && mInput == null; + if (mTextDir == null) { + resolveTextDirection(); + } if (mText instanceof Spannable) { mLayout = new DynamicLayout(mText, mTransformed, mTextPaint, w, - alignment, mSpacingMult, + alignment, mTextDir, mSpacingMult, mSpacingAdd, mIncludePad, mInput == null ? mEllipsize : null, ellipsisWidth); } else { if (boring == UNKNOWN_BORING) { - boring = BoringLayout.isBoring(mTransformed, mTextPaint, mBoring); + boring = BoringLayout.isBoring(mTransformed, mTextPaint, mTextDir, mBoring); if (boring != null) { mBoring = boring; } @@ -6036,23 +6047,23 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } else if (shouldEllipsize) { mLayout = new StaticLayout(mTransformed, 0, mTransformed.length(), - mTextPaint, w, alignment, mSpacingMult, + mTextPaint, w, alignment, mTextDir, mSpacingMult, mSpacingAdd, mIncludePad, mEllipsize, ellipsisWidth); } else { mLayout = new StaticLayout(mTransformed, mTextPaint, - w, alignment, mSpacingMult, mSpacingAdd, + w, alignment, mTextDir, mSpacingMult, mSpacingAdd, mIncludePad); } } else if (shouldEllipsize) { mLayout = new StaticLayout(mTransformed, 0, mTransformed.length(), - mTextPaint, w, alignment, mSpacingMult, + mTextPaint, w, alignment, mTextDir, mSpacingMult, mSpacingAdd, mIncludePad, mEllipsize, ellipsisWidth); } else { mLayout = new StaticLayout(mTransformed, mTextPaint, - w, alignment, mSpacingMult, mSpacingAdd, + w, alignment, mTextDir, mSpacingMult, mSpacingAdd, mIncludePad); } } @@ -6064,7 +6075,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (shouldEllipsize) hintWidth = w; if (hintBoring == UNKNOWN_BORING) { - hintBoring = BoringLayout.isBoring(mHint, mTextPaint, + hintBoring = BoringLayout.isBoring(mHint, mTextPaint, mTextDir, mHintBoring); if (hintBoring != null) { mHintBoring = hintBoring; @@ -6102,23 +6113,23 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } else if (shouldEllipsize) { mHintLayout = new StaticLayout(mHint, 0, mHint.length(), - mTextPaint, hintWidth, alignment, mSpacingMult, + mTextPaint, hintWidth, alignment, mTextDir, mSpacingMult, mSpacingAdd, mIncludePad, mEllipsize, ellipsisWidth); } else { mHintLayout = new StaticLayout(mHint, mTextPaint, - hintWidth, alignment, mSpacingMult, mSpacingAdd, + hintWidth, alignment, mTextDir, mSpacingMult, mSpacingAdd, mIncludePad); } } else if (shouldEllipsize) { mHintLayout = new StaticLayout(mHint, 0, mHint.length(), - mTextPaint, hintWidth, alignment, mSpacingMult, + mTextPaint, hintWidth, alignment, mTextDir, mSpacingMult, mSpacingAdd, mIncludePad, mEllipsize, ellipsisWidth); } else { mHintLayout = new StaticLayout(mHint, mTextPaint, - hintWidth, alignment, mSpacingMult, mSpacingAdd, + hintWidth, alignment, mTextDir, mSpacingMult, mSpacingAdd, mIncludePad); } } @@ -6219,6 +6230,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener BoringLayout.Metrics boring = UNKNOWN_BORING; BoringLayout.Metrics hintBoring = UNKNOWN_BORING; + if (mTextDir == null) { + resolveTextDirection(); + } + int des = -1; boolean fromexisting = false; @@ -6231,7 +6246,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } if (des < 0) { - boring = BoringLayout.isBoring(mTransformed, mTextPaint, mBoring); + boring = BoringLayout.isBoring(mTransformed, mTextPaint, mTextDir, mBoring); if (boring != null) { mBoring = boring; } @@ -9441,7 +9456,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener @Override public void onClick(View v) { if (canPaste()) { - paste(getSelectionStart(), getSelectionEnd()); + onTextContextMenuItem(ID_PASTE); } hide(); } @@ -10439,11 +10454,21 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return mInBatchEditControllers; } + private class TextViewDirectionHeuristic extends TextDirectionHeuristicImpl { + private TextViewDirectionHeuristic(TextDirectionAlgorithm algorithm) { + super(algorithm); + } + @Override + protected boolean defaultIsRtl() { + return getResolvedLayoutDirection() == LAYOUT_DIRECTION_RTL; + } + } + /** * Resolve the text direction. * * Text direction of paragraphs in a TextView is determined using a heuristic. If the correct - * text direction cannot be determined by the heuristic, the view’s resolved layout direction + * text direction cannot be determined by the heuristic, the view's resolved layout direction * determines the direction. * * This heuristic and result is applied individually to each paragraph in a TextView, based on @@ -10452,157 +10477,27 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener */ @Override protected void resolveTextDirection() { - int resolvedTextDirection = TEXT_DIRECTION_UNDEFINED; - switch(mTextDirection) { + super.resolveTextDirection(); + + int textDir = getResolvedTextDirection(); + switch (textDir) { default: - case TEXT_DIRECTION_INHERIT: - // Try to the text direction from the parent layout. If not possible, then we will - // use the default layout direction to decide later - if (mParent != null && mParent instanceof ViewGroup) { - resolvedTextDirection = ((ViewGroup) mParent).getResolvedTextDirection(); - } - break; case TEXT_DIRECTION_FIRST_STRONG: - resolvedTextDirection = getTextDirectionFromFirstStrong(mText); + mTextDir = new TextViewDirectionHeuristic(FirstStrong.INSTANCE); break; case TEXT_DIRECTION_ANY_RTL: - resolvedTextDirection = getTextDirectionFromAnyRtl(mText); + mTextDir = new TextViewDirectionHeuristic(AnyStrong.INSTANCE_RTL); break; case TEXT_DIRECTION_CHAR_COUNT: - resolvedTextDirection = getTextDirectionFromCharCount(mText); + mTextDir = new TextViewDirectionHeuristic(CharCount.INSTANCE_DEFAULT); break; case TEXT_DIRECTION_LTR: - resolvedTextDirection = TEXT_DIRECTION_LTR; + mTextDir = TextDirectionHeuristics.LTR; break; case TEXT_DIRECTION_RTL: - resolvedTextDirection = TEXT_DIRECTION_RTL; + mTextDir = TextDirectionHeuristics.RTL; break; } - // if we have been so far unable to get the text direction from the heuristics, then we are - // falling back using the layout direction - if (resolvedTextDirection == TEXT_DIRECTION_UNDEFINED) { - switch(getResolvedLayoutDirection()) { - default: - case LAYOUT_DIRECTION_LTR: - resolvedTextDirection = TEXT_DIRECTION_LTR; - break; - case LAYOUT_DIRECTION_RTL: - resolvedTextDirection = TEXT_DIRECTION_RTL; - break; - } - } - mResolvedTextDirection = resolvedTextDirection; - } - - /** - * Get text direction following the "first strong" heuristic. - * - * @param cs the CharSequence used to get the text direction. - * - * @return {@link #TEXT_DIRECTION_RTL} if direction it RTL, {@link #TEXT_DIRECTION_LTR} if - * direction it LTR or {@link #TEXT_DIRECTION_UNDEFINED} if direction cannot be found. - */ - private static int getTextDirectionFromFirstStrong(final CharSequence cs) { - final int length = cs.length(); - if (length == 0) { - return TEXT_DIRECTION_UNDEFINED; - } - for(int i = 0; i < length; i++) { - final char c = cs.charAt(i); - final byte dir = Character.getDirectionality(c); - if (isStrongLtrChar(dir)) { - return TEXT_DIRECTION_LTR; - } else if (isStrongRtlChar(dir)) { - return TEXT_DIRECTION_RTL; - } - } - return TEXT_DIRECTION_UNDEFINED; - } - - /** - * Get text direction following the "any RTL" heuristic. - * - * @param cs the CharSequence used to get the text direction. - * - * @return {@link #TEXT_DIRECTION_RTL} if direction it RTL, {@link #TEXT_DIRECTION_LTR} if - * direction it LTR or {@link #TEXT_DIRECTION_UNDEFINED} if direction cannot be found. - */ - private static int getTextDirectionFromAnyRtl(final CharSequence cs) { - final int length = cs.length(); - if (length == 0) { - return TEXT_DIRECTION_UNDEFINED; - } - boolean foundStrongLtr = false; - boolean foundStrongRtl = false; - for(int i = 0; i < length; i++) { - final char c = cs.charAt(i); - final byte dir = Character.getDirectionality(c); - if (isStrongLtrChar(dir)) { - foundStrongLtr = true; - } else if (isStrongRtlChar(dir)) { - foundStrongRtl = true; - break; - } - } - if (foundStrongRtl) { - return TEXT_DIRECTION_RTL; - } - if (foundStrongLtr) { - return TEXT_DIRECTION_LTR; - } - return TEXT_DIRECTION_UNDEFINED; - } - - /** - * Get text direction following the "char count" heuristic. - * - * @param cs the CharSequence used to get the text direction. - * - * @return {@link #TEXT_DIRECTION_RTL} if direction it RTL, {@link #TEXT_DIRECTION_LTR} if - * direction it LTR or {@link #TEXT_DIRECTION_UNDEFINED} if direction cannot be found. - */ - private int getTextDirectionFromCharCount(CharSequence cs) { - final int length = cs.length(); - if (length == 0) { - return TEXT_DIRECTION_UNDEFINED; - } - int countLtr = 0; - int countRtl = 0; - for(int i = 0; i < length; i++) { - final char c = cs.charAt(i); - final byte dir = Character.getDirectionality(c); - if (isStrongLtrChar(dir)) { - countLtr++; - } else if (isStrongRtlChar(dir)) { - countRtl++; - } - } - final float percentLtr = ((float) countLtr) / (countLtr + countRtl); - if (percentLtr > DEFAULT_TEXT_DIRECTION_CHAR_COUNT_THRESHOLD) { - return TEXT_DIRECTION_LTR; - } - final float percentRtl = ((float) countRtl) / (countLtr + countRtl); - if (percentRtl > DEFAULT_TEXT_DIRECTION_CHAR_COUNT_THRESHOLD) { - return TEXT_DIRECTION_RTL; - } - return TEXT_DIRECTION_UNDEFINED; - } - - /** - * Return true if the char direction is corresponding to a "strong RTL char" following the - * Unicode Bidirectional Algorithm (UBA). - */ - private static boolean isStrongRtlChar(final byte dir) { - return (dir == Character.DIRECTIONALITY_RIGHT_TO_LEFT || - dir == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC); - } - - /** - * Return true if the char direction is corresponding to a "strong LTR char" following the - * Unicode Bidirectional Algorithm (UBA). - */ - private static boolean isStrongLtrChar(final byte dir) { - return (dir == Character.DIRECTIONALITY_LEFT_TO_RIGHT); } /** @@ -10768,6 +10663,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private BoringLayout mSavedLayout, mSavedHintLayout; + private TextDirectionHeuristic mTextDir = null; + private static final InputFilter[] NO_FILTERS = new InputFilter[0]; private InputFilter[] mFilters = NO_FILTERS; private static final Spanned EMPTY_SPANNED = new SpannedString(""); diff --git a/core/java/com/android/internal/app/ActionBarImpl.java b/core/java/com/android/internal/app/ActionBarImpl.java index 243c605..cf5666c 100644 --- a/core/java/com/android/internal/app/ActionBarImpl.java +++ b/core/java/com/android/internal/app/ActionBarImpl.java @@ -500,7 +500,7 @@ public class ActionBarImpl extends ActionBar { @Override public int getHeight() { - return mActionView.getHeight(); + return mContainerView.getHeight(); } @Override diff --git a/services/java/com/android/server/ProcessStats.java b/core/java/com/android/internal/os/ProcessStats.java index f693ed1..ea5ce09 100644 --- a/services/java/com/android/server/ProcessStats.java +++ b/core/java/com/android/internal/os/ProcessStats.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server; +package com.android.internal.os; import static android.os.Process.*; @@ -182,7 +182,7 @@ public class ProcessStats { public String baseName; public String name; - int nameWidth; + public int nameWidth; public long base_uptime; public long rel_uptime; diff --git a/core/java/com/android/internal/util/Protocol.java b/core/java/com/android/internal/util/Protocol.java index b754d94..69b80d9 100644 --- a/core/java/com/android/internal/util/Protocol.java +++ b/core/java/com/android/internal/util/Protocol.java @@ -40,6 +40,7 @@ public class Protocol { /** Non system protocols */ public static final int BASE_WIFI = 0x00020000; + public static final int BASE_WIFI_WATCHDOG = 0x00021000; public static final int BASE_DHCP = 0x00030000; public static final int BASE_DATA_CONNECTION = 0x00040000; public static final int BASE_DATA_CONNECTION_AC = 0x00041000; diff --git a/core/jni/Android.mk b/core/jni/Android.mk index 58e7c8d..06dc083 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -145,7 +145,6 @@ LOCAL_SRC_FILES:= \ android_server_Watchdog.cpp \ android_ddm_DdmHandleNativeHeap.cpp \ com_android_internal_os_ZygoteInit.cpp \ - com_android_internal_graphics_NativeUtils.cpp \ android_backup_BackupDataInput.cpp \ android_backup_BackupDataOutput.cpp \ android_backup_FileBackupHelperBase.cpp \ diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 23c6da7..08431cd 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -115,7 +115,6 @@ extern int register_android_graphics_Region(JNIEnv* env); extern int register_android_graphics_SurfaceTexture(JNIEnv* env); extern int register_android_graphics_Xfermode(JNIEnv* env); extern int register_android_graphics_PixelFormat(JNIEnv* env); -extern int register_com_android_internal_graphics_NativeUtils(JNIEnv *env); extern int register_android_view_Display(JNIEnv* env); extern int register_android_view_GLES20Canvas(JNIEnv* env); extern int register_android_view_Surface(JNIEnv* env); @@ -1147,7 +1146,6 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_graphics_Typeface), REG_JNI(register_android_graphics_Xfermode), REG_JNI(register_android_graphics_YuvImage), - REG_JNI(register_com_android_internal_graphics_NativeUtils), REG_JNI(register_android_database_CursorWindow), REG_JNI(register_android_database_SQLiteCompiledSql), diff --git a/core/jni/com_android_internal_graphics_NativeUtils.cpp b/core/jni/com_android_internal_graphics_NativeUtils.cpp deleted file mode 100644 index 9cc43606..0000000 --- a/core/jni/com_android_internal_graphics_NativeUtils.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "AWT" - -#include "jni.h" -#include "JNIHelp.h" -#include "GraphicsJNI.h" -#include <android_runtime/AndroidRuntime.h> - -#include "SkCanvas.h" -#include "SkDevice.h" -#include "SkPicture.h" -#include "SkTemplates.h" - -namespace android -{ - -static jboolean scrollRect(JNIEnv* env, jobject graphics2D, jobject canvas, jobject rect, int dx, int dy) { - if (canvas == NULL) { - jniThrowNullPointerException(env, NULL); - return false; - } - - SkIRect src, *srcPtr = NULL; - if (NULL != rect) { - GraphicsJNI::jrect_to_irect(env, rect, &src); - srcPtr = &src; - } - SkCanvas* c = GraphicsJNI::getNativeCanvas(env, canvas); - const SkBitmap& bitmap = c->getDevice()->accessBitmap(true); - return bitmap.scrollRect(srcPtr, dx, dy, NULL); -} - -static JNINativeMethod method_table[] = { - { "nativeScrollRect", - "(Landroid/graphics/Canvas;Landroid/graphics/Rect;II)Z", - (void*)scrollRect} -}; - -int register_com_android_internal_graphics_NativeUtils(JNIEnv *env) { - return AndroidRuntime::registerNativeMethods( - env, "com/android/internal/graphics/NativeUtils", - method_table, NELEM(method_table)); -} - -} diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 49eaf19..0397dfa 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1489,9 +1489,6 @@ android:excludeFromRecents="true"> </activity> - <service android:name="com.android.server.LoadAverageService" - android:exported="true" /> - <service android:name="com.android.internal.service.wallpaper.ImageWallpaper" android:permission="android.permission.BIND_WALLPAPER"> </service> diff --git a/core/res/res/layout/list_menu_item_icon.xml b/core/res/res/layout/list_menu_item_icon.xml index a885211..27dd9b8 100644 --- a/core/res/res/layout/list_menu_item_icon.xml +++ b/core/res/res/layout/list_menu_item_icon.xml @@ -21,6 +21,8 @@ android:layout_gravity="center_vertical" android:layout_marginLeft="8dip" android:layout_marginRight="-8dip" - android:scaleType="center" + android:layout_marginTop="8dip" + android:layout_marginBottom="8dip" + android:scaleType="centerInside" android:duplicateParentState="true" /> diff --git a/core/res/res/layout/preference_list_content.xml b/core/res/res/layout/preference_list_content.xml index fb898ee..4e7981a 100644 --- a/core/res/res/layout/preference_list_content.xml +++ b/core/res/res/layout/preference_list_content.xml @@ -44,8 +44,8 @@ android:layout_width="match_parent" android:layout_height="0px" android:layout_weight="1" - android:paddingTop="16dp" - android:paddingBottom="16dp" + android:paddingTop="@dimen/preference_screen_header_vertical_padding" + android:paddingBottom="@dimen/preference_screen_header_vertical_padding" android:drawSelectorOnTop="false" android:cacheColorHint="@android:color/transparent" android:listPreferredItemHeight="48dp" diff --git a/core/res/res/values-sw600dp/dimens.xml b/core/res/res/values-sw600dp/dimens.xml index 9ffe6b1..17bf561 100644 --- a/core/res/res/values-sw600dp/dimens.xml +++ b/core/res/res/values-sw600dp/dimens.xml @@ -44,5 +44,7 @@ <!-- Size of status line font in LockScreen. --> <dimen name="keyguard_pattern_unlock_status_line_font_size">14sp</dimen> + <!-- Preference activity, vertical padding for the header list --> + <dimen name="reference_screen_header_vertical_padding">16dp</dimen> </resources> diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml index 0f04a67..bcf1991 100644 --- a/core/res/res/values/arrays.xml +++ b/core/res/res/values/arrays.xml @@ -22,6 +22,15 @@ <!-- Do not translate. These are all of the drawable resources that should be preloaded by the zygote process before it starts forking application processes. --> <array name="preloaded_drawables"> + <item>@drawable/spinner_black_16</item> + <item>@drawable/spinner_black_20</item> + <item>@drawable/spinner_black_48</item> + <item>@drawable/spinner_black_76</item> + <item>@drawable/spinner_white_16</item> + <item>@drawable/spinner_white_48</item> + <item>@drawable/spinner_white_76</item> + <item>@drawable/toast_frame</item> + <item>@drawable/toast_frame_holo</item> <item>@drawable/btn_check_on_selected</item> <item>@drawable/btn_check_on_pressed_holo_light</item> <item>@drawable/btn_check_on_pressed_holo_dark</item> @@ -109,6 +118,11 @@ <item>@drawable/btn_default_disabled_holo_dark</item> <item>@drawable/btn_default_disabled_focused_holo_light</item> <item>@drawable/btn_default_disabled_focused_holo_dark</item> + <item>@drawable/btn_dropdown_disabled</item> + <item>@drawable/btn_dropdown_disabled_focused</item> + <item>@drawable/btn_dropdown_normal</item> + <item>@drawable/btn_dropdown_pressed</item> + <item>@drawable/btn_dropdown_selected</item> <item>@drawable/btn_toggle_on_pressed_holo_light</item> <item>@drawable/btn_toggle_on_pressed_holo_dark</item> <item>@drawable/btn_toggle_on_normal_holo_light</item> @@ -141,6 +155,10 @@ <item>@drawable/edit_text_holo_light</item> <item>@drawable/edit_text_holo_dark</item> <item>@drawable/edit_text</item> + <item>@drawable/expander_close_holo_dark</item> + <item>@drawable/expander_close_holo_light</item> + <item>@drawable/expander_ic_maximized</item> + <item>@drawable/expander_ic_minimized</item> <item>@drawable/expander_group</item> <item>@drawable/expander_group_holo_dark</item> <item>@drawable/expander_group_holo_light</item> @@ -157,6 +175,8 @@ <item>@drawable/menu_background_fill_parent_width</item> <item>@drawable/menu_submenu_background</item> <item>@drawable/menu_selector</item> + <item>@drawable/overscroll_edge</item> + <item>@drawable/overscroll_glow</item> <item>@drawable/panel_background</item> <item>@drawable/popup_bottom_bright</item> <item>@drawable/popup_bottom_dark</item> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index 0f6e5cf..abc56ec 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -96,6 +96,8 @@ is along the major axis (that is the screen is landscape). This may be either a fraction or a dimension. --> <item type="dimen" name="dialog_min_width_major">65%</item> + <!-- Preference activity, vertical padding for the header list --> + <dimen name="preference_screen_header_vertical_padding">0dp</dimen> <!-- The platform's desired minimum size for a dialog's width when it is along the minor axis (that is the screen is portrait). This may diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml index a5cd6e3..6b75146 100644 --- a/core/res/res/values/styles.xml +++ b/core/res/res/values/styles.xml @@ -1672,6 +1672,7 @@ <style name="Widget.Holo.ProgressBar.Small" parent="Widget.ProgressBar.Small"> <item name="android:indeterminateDrawable">@android:drawable/progress_small_holo</item> + <item name="android:animationResolution">33</item> </style> <style name="Widget.Holo.ProgressBar.Small.Title"> @@ -1679,6 +1680,7 @@ <style name="Widget.Holo.ProgressBar.Large" parent="Widget.ProgressBar.Large"> <item name="android:indeterminateDrawable">@android:drawable/progress_large_holo</item> + <item name="android:animationResolution">33</item> </style> <style name="Widget.Holo.ProgressBar.Inverse"> diff --git a/core/tests/coretests/src/android/widget/TextViewTest.java b/core/tests/coretests/src/android/widget/TextViewTest.java index c54e4a1..7dc95db 100644 --- a/core/tests/coretests/src/android/widget/TextViewTest.java +++ b/core/tests/coretests/src/android/widget/TextViewTest.java @@ -16,13 +16,13 @@ package android.widget; -import com.android.frameworks.coretests.R; - import android.test.ActivityInstrumentationTestCase2; import android.test.suitebuilder.annotation.SmallTest; import android.text.GetChars; import android.view.View; +import com.android.frameworks.coretests.R; + /** * TextViewTest tests {@link TextView}. */ @@ -240,12 +240,12 @@ public class TextViewTest extends ActivityInstrumentationTestCase2<TextViewTestA getActivity().runOnUiThread(new Runnable() { public void run() { - tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG); + ll.setTextDirection(View.TEXT_DIRECTION_RTL); + tv.setTextDirection(View.TEXT_DIRECTION_INHERIT); assertEquals(View.TEXT_DIRECTION_RTL, tv.getResolvedTextDirection()); - assertEquals(true, tv.isResolvedTextDirection()); ll.removeView(tv); - assertEquals(false, tv.isResolvedTextDirection()); + assertEquals(View.TEXT_DIRECTION_FIRST_STRONG, tv.getResolvedTextDirection()); } }); } diff --git a/data/fonts/fallback_fonts.xml b/data/fonts/fallback_fonts.xml new file mode 100644 index 0000000..c0d9153 --- /dev/null +++ b/data/fonts/fallback_fonts.xml @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Fallback Fonts + + This file specifies the fonts, and the priority order, that will be searched for any + glyphs not handled by the default fonts specified in /system/etc/system_fonts.xml. + Each entry consists of a family tag and a list of files (file names) which support that + family. The fonts for each family are listed in the order of the styles that they + handle (the order is: regular, bold, italic, and bold-italic). The order in which the + families are listed in this file represents the order in which these fallback fonts + will be searched for glyphs that are not supported by the default system fonts (which are + found in /system/etc/system_fonts.xml). + + Note that there is not nameset for fallback fonts, unlike the fonts specified in + system_fonts.xml. The ability to support specific names in fallback fonts may be supported + in the future. For now, the lack of files entries here is an indicator to the system that + these are fallback fonts, instead of default named system fonts. + + There is another optional file in /vendor/etc/fallback_fonts.xml. That file can be used to + provide references to other font families that should be used in addition to the default + fallback fonts. That file can also specify the order in which the fallback fonts should be + searched, to ensure that a vendor-provided font will be used before another fallback font + which happens to handle the same glyph. +--> +<familyset> + <family> + <fileset> + <file>DroidSansArabic.ttf</file> + </fileset> + </family> + <family> + <fileset> + <file>DroidSansHebrew-Regular.ttf</file> + <file>DroidSansHebrew-Bold.ttf</file> + </fileset> + </family> + <family> + <fileset> + <file>DroidSansThai.ttf</file> + </fileset> + </family> + <family> + <fileset> + <file>DroidSansFallback.ttf</file> + </fileset> + </family> +</familyset> diff --git a/data/fonts/fonts.mk b/data/fonts/fonts.mk index d222c0b..57a1bab 100644 --- a/data/fonts/fonts.mk +++ b/data/fonts/fonts.mk @@ -30,4 +30,6 @@ PRODUCT_COPY_FILES := \ frameworks/base/data/fonts/DroidSansFallback.ttf:system/fonts/DroidSansFallback.ttf \ frameworks/base/data/fonts/AndroidClock.ttf:system/fonts/AndroidClock.ttf \ frameworks/base/data/fonts/AndroidClock_Highlight.ttf:system/fonts/AndroidClock_Highlight.ttf \ - frameworks/base/data/fonts/AndroidClock_Solid.ttf:system/fonts/AndroidClock_Solid.ttf + frameworks/base/data/fonts/AndroidClock_Solid.ttf:system/fonts/AndroidClock_Solid.ttf \ + frameworks/base/data/fonts/system_fonts.xml:system/etc/system_fonts.xml \ + frameworks/base/data/fonts/fallback_fonts.xml:system/etc/fallback_fonts.xml diff --git a/data/fonts/system_fonts.xml b/data/fonts/system_fonts.xml new file mode 100644 index 0000000..8d8d020 --- /dev/null +++ b/data/fonts/system_fonts.xml @@ -0,0 +1,68 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + System Fonts + + This file lists the font families that will be used by default for all supported glyphs. + Each entry consists of a family, various names that are supported by that family, and + up to four font files. The font files are listed in the order of the styles which they + support: regular, bold, italic and bold-italic. If less than four styles are listed, then + the styles with no associated font file will be supported by the other font files listed. + + The first family is also the default font, which handles font request that have not specified + specific font names. + + Any glyph that is not handled by the system fonts will cause a search of the fallback fonts. + The default fallback fonts are specified in the file /system/etc/fallback_fonts.xml, and there + is an optional file which may be supplied by vendors to specify other fallback fonts to use + in /vendor/etc/fallback_fonts.xml. +--> +<familyset> + + <family> + <nameset> + <name>sans-serif</name> + <name>arial</name> + <name>helvetica</name> + <name>tahoma</name> + <name>verdana</name> + </nameset> + <fileset> + <file>DroidSans.ttf</file> + <file>DroidSans-Bold.ttf</file> + </fileset> + </family> + + <family> + <nameset> + <name>serif</name> + <name>times</name> + <name>times new roman</name> + <name>palatino</name> + <name>georgia</name> + <name>baskerville</name> + <name>goudy</name> + <name>fantasy</name> + <name>cursive</name> + <name>ITC Stone Serif</name> + </nameset> + <fileset> + <file>DroidSerif-Regular.ttf</file> + <file>DroidSerif-Bold.ttf</file> + <file>DroidSerif-Italic.ttf</file> + <file>DroidSerif-BoldItalic.ttf</file> + </fileset> + </family> + + <family> + <nameset> + <name>monospace</name> + <name>courier</name> + <name>courier new</name> + <name>monaco</name> + </nameset> + <fileset> + <file>DroidSansMono.ttf</file> + </fileset> + </family> + +</familyset> diff --git a/data/fonts/vendor_fonts.xml b/data/fonts/vendor_fonts.xml new file mode 100644 index 0000000..fe51fd2 --- /dev/null +++ b/data/fonts/vendor_fonts.xml @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Vendor-provided fallback fonts + + This file can be edited to add references to fonts that are not installed or referenced in the + default system. The file should then be placed in /vendor/etc/fallback_fonts.xml. + + For example, vendors might want to build configurations for locales that are + better served by fonts which either handle glyphs not supported in the default fonts or which + handle these glyphs differently than the default fallback fonts. + Each entry in this list is a "family", which consists of a list of "files" + (the filenames for that family). The files objects are + provided in the order of the styles supported for that family: regular, bold, italic, and + bold-italic. Only providing one font means that all styles will be rendered with that font. + Providing two means that these two fonts will render regular and bold fonts (italics will + be mapped to these two fonts). + + There is also an optional "order" attribute on the Family tag. This specifies the index at + which that family of fonts should be inserted in the fallback font list, where the + default fallback fonts on the system (in /system/etc/fallback_fonts.xml) start at index 0. + If no 'order' attribute is supplied, that family will be inserted either at the end of the + current fallback list (if no order was supplied for any previous family in this file) or + after the previous family (if there was an order specified previously). Typically, vendors + may want to supply an order for the first family that puts this set of fonts at the appropriate + place in the overall fallback fonts. The order of this list determines which fallback font + will be used to support any glyphs that are not handled by the default system fonts. + + The sample configuration below is an example of how one might provide two families of fonts + that get inserted at the first and second (0 and 1) position in the overall fallback fonts. + + See /system/etc/system_fonts.xml and /system/etc/fallback_fonts.xml for more information + and to understand the order in which the default system fonts are loaded and structured for + lookup. +--> + +<!-- Sample fallback font additions to the default fallback list. These fonts will be added + to the top two positions of the fallback list, since the first has an order of 0. --> +<!-- +<familyset> + <family order="0"> + <fileset> + <file>MyFont.ttf</file> + </fileset> + </family> + <family> + <fileset> + <file>MyOtherFont.ttf</file> + </fileset> + </family> +</familyset> +-->
\ No newline at end of file diff --git a/data/sounds/effects/ogg/Dock.ogg b/data/sounds/effects/ogg/Dock.ogg Binary files differindex 626d695..a1c1f2c 100644..100755 --- a/data/sounds/effects/ogg/Dock.ogg +++ b/data/sounds/effects/ogg/Dock.ogg diff --git a/data/sounds/effects/ogg/KeypressDelete.ogg b/data/sounds/effects/ogg/KeypressDelete.ogg Binary files differindex 5e724f4..38c3244 100644..100755 --- a/data/sounds/effects/ogg/KeypressDelete.ogg +++ b/data/sounds/effects/ogg/KeypressDelete.ogg diff --git a/data/sounds/effects/ogg/KeypressReturn.ogg b/data/sounds/effects/ogg/KeypressReturn.ogg Binary files differindex a1200b2..1bd5b73 100644..100755 --- a/data/sounds/effects/ogg/KeypressReturn.ogg +++ b/data/sounds/effects/ogg/KeypressReturn.ogg diff --git a/data/sounds/effects/ogg/KeypressSpacebar.ogg b/data/sounds/effects/ogg/KeypressSpacebar.ogg Binary files differindex 0d0fbf1..3a83594 100644..100755 --- a/data/sounds/effects/ogg/KeypressSpacebar.ogg +++ b/data/sounds/effects/ogg/KeypressSpacebar.ogg diff --git a/data/sounds/effects/ogg/KeypressStandard.ogg b/data/sounds/effects/ogg/KeypressStandard.ogg Binary files differindex 5878135..4b8d128 100644..100755 --- a/data/sounds/effects/ogg/KeypressStandard.ogg +++ b/data/sounds/effects/ogg/KeypressStandard.ogg diff --git a/data/sounds/effects/ogg/Lock.ogg b/data/sounds/effects/ogg/Lock.ogg Binary files differindex a25513f..deeba68 100644..100755 --- a/data/sounds/effects/ogg/Lock.ogg +++ b/data/sounds/effects/ogg/Lock.ogg diff --git a/data/sounds/effects/ogg/LowBattery.ogg b/data/sounds/effects/ogg/LowBattery.ogg Binary files differindex c21218c..370c86c 100644..100755 --- a/data/sounds/effects/ogg/LowBattery.ogg +++ b/data/sounds/effects/ogg/LowBattery.ogg diff --git a/data/sounds/effects/ogg/Undock.ogg b/data/sounds/effects/ogg/Undock.ogg Binary files differindex dd132c4..91e410e 100644..100755 --- a/data/sounds/effects/ogg/Undock.ogg +++ b/data/sounds/effects/ogg/Undock.ogg diff --git a/data/sounds/effects/ogg/Unlock.ogg b/data/sounds/effects/ogg/Unlock.ogg Binary files differindex 490f98e..ac50288 100644..100755 --- a/data/sounds/effects/ogg/Unlock.ogg +++ b/data/sounds/effects/ogg/Unlock.ogg diff --git a/data/sounds/effects/ogg/camera_click.ogg b/data/sounds/effects/ogg/camera_click.ogg Binary files differindex eee590f..bfb2a68 100644..100755 --- a/data/sounds/effects/ogg/camera_click.ogg +++ b/data/sounds/effects/ogg/camera_click.ogg diff --git a/data/sounds/notifications/ogg/Argon.ogg b/data/sounds/notifications/ogg/Argon.ogg Binary files differindex f160562..e58b3b6 100644..100755 --- a/data/sounds/notifications/ogg/Argon.ogg +++ b/data/sounds/notifications/ogg/Argon.ogg diff --git a/data/sounds/notifications/ogg/Beryllium.ogg b/data/sounds/notifications/ogg/Beryllium.ogg Binary files differindex 5f7bd3c..2c5b4fe 100644..100755 --- a/data/sounds/notifications/ogg/Beryllium.ogg +++ b/data/sounds/notifications/ogg/Beryllium.ogg diff --git a/data/sounds/notifications/ogg/Cobalt.ogg b/data/sounds/notifications/ogg/Cobalt.ogg Binary files differindex a9adeb8..b6e253a 100644..100755 --- a/data/sounds/notifications/ogg/Cobalt.ogg +++ b/data/sounds/notifications/ogg/Cobalt.ogg diff --git a/data/sounds/notifications/ogg/Fluorine.ogg b/data/sounds/notifications/ogg/Fluorine.ogg Binary files differindex 6340cf3..fd884f5 100644..100755 --- a/data/sounds/notifications/ogg/Fluorine.ogg +++ b/data/sounds/notifications/ogg/Fluorine.ogg diff --git a/data/sounds/notifications/ogg/Gallium.ogg b/data/sounds/notifications/ogg/Gallium.ogg Binary files differindex b0446b2..3c7e115 100644..100755 --- a/data/sounds/notifications/ogg/Gallium.ogg +++ b/data/sounds/notifications/ogg/Gallium.ogg diff --git a/data/sounds/notifications/ogg/Radon.ogg b/data/sounds/notifications/ogg/Radon.ogg Binary files differindex 1d70c11..550cddd 100644..100755 --- a/data/sounds/notifications/ogg/Radon.ogg +++ b/data/sounds/notifications/ogg/Radon.ogg diff --git a/data/sounds/notifications/ogg/Selenium.ogg b/data/sounds/notifications/ogg/Selenium.ogg Binary files differindex 7624eff..9d60917 100644..100755 --- a/data/sounds/notifications/ogg/Selenium.ogg +++ b/data/sounds/notifications/ogg/Selenium.ogg diff --git a/data/sounds/notifications/ogg/Zirconium.ogg b/data/sounds/notifications/ogg/Zirconium.ogg Binary files differindex 31e40c1..d84b59e 100644..100755 --- a/data/sounds/notifications/ogg/Zirconium.ogg +++ b/data/sounds/notifications/ogg/Zirconium.ogg diff --git a/graphics/java/com/android/internal/graphics/NativeUtils.java b/graphics/java/com/android/internal/graphics/NativeUtils.java deleted file mode 100644 index c91b7d9..0000000 --- a/graphics/java/com/android/internal/graphics/NativeUtils.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.graphics; - -import android.graphics.Canvas; -import android.graphics.Rect; - -public final class NativeUtils { - /** - * This class is uninstantiable. - */ - private NativeUtils() { - // This space intentionally left blank. - } - - /** - * Scroll a rectangular portion of a canvas. - * - * @param canvas the canvas to manipulate - * @param src the source rectangle - * @param dx horizontal offset - * @param dy vertical offset - */ - public static native boolean nativeScrollRect(Canvas canvas, Rect src, - int dx, int dy); -} diff --git a/include/cpustats/CentralTendencyStatistics.h b/include/cpustats/CentralTendencyStatistics.h new file mode 100644 index 0000000..21b6981 --- /dev/null +++ b/include/cpustats/CentralTendencyStatistics.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _CENTRAL_TENDENCY_STATISTICS_H +#define _CENTRAL_TENDENCY_STATISTICS_H + +#include <math.h> + +// Not multithread safe +class CentralTendencyStatistics { + +public: + + CentralTendencyStatistics() : + mMean(NAN), mMedian(NAN), mMinimum(INFINITY), mMaximum(-INFINITY), mN(0), mM2(0), + mVariance(NAN), mVarianceKnownForN(0), mStddev(NAN), mStddevKnownForN(0) { } + + ~CentralTendencyStatistics() { } + + // add x to the set of samples + void sample(double x); + + // return the arithmetic mean of all samples so far + double mean() const { return mMean; } + + // return the minimum of all samples so far + double minimum() const { return mMinimum; } + + // return the maximum of all samples so far + double maximum() const { return mMaximum; } + + // return the variance of all samples so far + double variance() const; + + // return the standard deviation of all samples so far + double stddev() const; + + // return the number of samples added so far + unsigned n() const { return mN; } + + // reset the set of samples to be empty + void reset(); + +private: + double mMean; + double mMedian; + double mMinimum; + double mMaximum; + unsigned mN; // number of samples so far + double mM2; + + // cached variance, and n at time of caching + mutable double mVariance; + mutable unsigned mVarianceKnownForN; + + // cached standard deviation, and n at time of caching + mutable double mStddev; + mutable unsigned mStddevKnownForN; + +}; + +#endif // _CENTRAL_TENDENCY_STATISTICS_H diff --git a/include/cpustats/README.txt b/include/cpustats/README.txt new file mode 100644 index 0000000..14439f0 --- /dev/null +++ b/include/cpustats/README.txt @@ -0,0 +1,6 @@ +This is a static library of CPU usage statistics, originally written +for audio but most are not actually specific to audio. + +Requirements to be here: + * should be related to CPU usage statistics + * should be portable to host; avoid Android OS dependencies without a conditional diff --git a/include/cpustats/ThreadCpuUsage.h b/include/cpustats/ThreadCpuUsage.h new file mode 100644 index 0000000..24012a4 --- /dev/null +++ b/include/cpustats/ThreadCpuUsage.h @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _THREAD_CPU_USAGE_H +#define _THREAD_CPU_USAGE_H + +#include <cpustats/CentralTendencyStatistics.h> + +// Track CPU usage for the current thread, and maintain statistics on +// the CPU usage. Units are in per-thread CPU ns, as reported by +// clock_gettime(CLOCK_THREAD_CPUTIME_ID). Simple usage: for cyclic +// threads where you want to measure the execution time of the whole +// cycle, just call sampleAndEnable() at the start of each cycle. +// Then call statistics() to get the results, and resetStatistics() +// to start a new set of measurements. +// For acyclic threads, or for cyclic threads where you want to measure +// only part of each cycle, call enable(), disable(), and/or setEnabled() +// to demarcate the region(s) of interest, and then call sample() periodically. +// This class is not thread-safe for concurrent calls from multiple threads; +// the methods of this class may only be called by the current thread +// which constructed the object. + +class ThreadCpuUsage +{ + +public: + ThreadCpuUsage() : + mIsEnabled(false), + mWasEverEnabled(false), + mAccumulator(0), + // mPreviousTs + // mMonotonicTs + mMonotonicKnown(false) + // mStatistics + { } + + ~ThreadCpuUsage() { } + + // Return whether currently tracking CPU usage by current thread + bool isEnabled() { return mIsEnabled; } + + // Enable tracking of CPU usage by current thread; + // any CPU used from this point forward will be tracked. + // Returns the previous enabled status. + bool enable() { return setEnabled(true); } + + // Disable tracking of CPU usage by current thread; + // any CPU used from this point forward will be ignored. + // Returns the previous enabled status. + bool disable() { return setEnabled(false); } + + // Set the enabled status and return the previous enabled status. + // This method is intended to be used for safe nested enable/disabling. + bool setEnabled(bool isEnabled); + + // Add a sample point for central tendency statistics, and also + // enable tracking if needed. If tracking has never been enabled, then + // enables tracking but does not add a sample (it is not possible to add + // a sample the first time because no previous). Otherwise if tracking is + // enabled, then adds a sample for tracked CPU ns since the previous + // sample, or since the first call to sampleAndEnable(), enable(), or + // setEnabled(true). If there was a previous sample but tracking is + // now disabled, then adds a sample for the tracked CPU ns accumulated + // up until the most recent disable(), resets this accumulator, and then + // enables tracking. Calling this method rather than enable() followed + // by sample() avoids a race condition for the first sample. + void sampleAndEnable(); + + // Add a sample point for central tendency statistics, but do not + // change the tracking enabled status. If tracking has either never been + // enabled, or has never been enabled since the last sample, then log a warning + // and don't add sample. Otherwise, adds a sample for tracked CPU ns since + // the previous sample or since the first call to sampleAndEnable(), + // enable(), or setEnabled(true) if no previous sample. + void sample(); + + // Return the elapsed delta wall clock ns since initial enable or statistics reset, + // as reported by clock_gettime(CLOCK_MONOTONIC). + long long elapsed() const; + + // Reset statistics and elapsed. Has no effect on tracking or accumulator. + void resetStatistics(); + + // Return a const reference to the central tendency statistics. + // Note that only the const methods can be called on this object. + const CentralTendencyStatistics& statistics() const { + return mStatistics; + } + +private: + bool mIsEnabled; // whether tracking is currently enabled + bool mWasEverEnabled; // whether tracking was ever enabled + long long mAccumulator; // accumulated thread CPU time since last sample, in ns + struct timespec mPreviousTs; // most recent thread CPU time, valid only if mIsEnabled is true + struct timespec mMonotonicTs; // most recent monotonic time + bool mMonotonicKnown; // whether mMonotonicTs has been set + CentralTendencyStatistics mStatistics; +}; + +#endif // _THREAD_CPU_USAGE_H diff --git a/include/gui/ISurfaceTexture.h b/include/gui/ISurfaceTexture.h index e705c6f..5b5b731 100644 --- a/include/gui/ISurfaceTexture.h +++ b/include/gui/ISurfaceTexture.h @@ -104,6 +104,24 @@ protected: // queued buffers will be retired in order. // The default mode is asynchronous. virtual status_t setSynchronousMode(bool enabled) = 0; + + // connect attempts to connect a client API to the SurfaceTexture. This + // must be called before any other ISurfaceTexture methods are called except + // for getAllocator. + // + // This method will fail if the connect was previously called on the + // SurfaceTexture and no corresponding disconnect call was made. + virtual status_t connect(int api) = 0; + + // disconnect attempts to disconnect a client API from the SurfaceTexture. + // Calling this method will cause any subsequent calls to other + // ISurfaceTexture methods to fail except for getAllocator and connect. + // Successfully calling connect after this will allow the other methods to + // succeed again. + // + // This method will fail if the the SurfaceTexture is not currently + // connected to the specified client API. + virtual status_t disconnect(int api) = 0; }; // ---------------------------------------------------------------------------- diff --git a/include/gui/SurfaceTexture.h b/include/gui/SurfaceTexture.h index e36360c..4080f27 100644 --- a/include/gui/SurfaceTexture.h +++ b/include/gui/SurfaceTexture.h @@ -44,6 +44,7 @@ public: MIN_SYNC_BUFFER_SLOTS = MIN_UNDEQUEUED_BUFFERS }; enum { NUM_BUFFER_SLOTS = 32 }; + enum { NO_CONNECTED_API = 0 }; struct FrameAvailableListener : public virtual RefBase { // onFrameAvailable() is called from queueBuffer() each time an @@ -97,6 +98,24 @@ public: // The default mode is asynchronous. virtual status_t setSynchronousMode(bool enabled); + // connect attempts to connect a client API to the SurfaceTexture. This + // must be called before any other ISurfaceTexture methods are called except + // for getAllocator. + // + // This method will fail if the connect was previously called on the + // SurfaceTexture and no corresponding disconnect call was made. + virtual status_t connect(int api); + + // disconnect attempts to disconnect a client API from the SurfaceTexture. + // Calling this method will cause any subsequent calls to other + // ISurfaceTexture methods to fail except for getAllocator and connect. + // Successfully calling connect after this will allow the other methods to + // succeed again. + // + // This method will fail if the the SurfaceTexture is not currently + // connected to the specified client API. + virtual status_t disconnect(int api); + // updateTexImage sets the image contents of the target texture to that of // the most recently queued buffer. // @@ -362,6 +381,11 @@ private: // mAllowSynchronousMode whether we allow synchronous mode or not const bool mAllowSynchronousMode; + // mConnectedApi indicates the API that is currently connected to this + // SurfaceTexture. It defaults to NO_CONNECTED_API (= 0), and gets updated + // by the connect and disconnect methods. + int mConnectedApi; + // mDequeueCondition condition used for dequeueBuffer in synchronous mode mutable Condition mDequeueCondition; diff --git a/include/gui/SurfaceTextureClient.h b/include/gui/SurfaceTextureClient.h index 9db7364..5ec469e 100644 --- a/include/gui/SurfaceTextureClient.h +++ b/include/gui/SurfaceTextureClient.h @@ -129,9 +129,6 @@ private: // a timestamp is auto-generated when queueBuffer is called. int64_t mTimestamp; - // mConnectedApi holds the currently connected API to this surface - int mConnectedApi; - // mQueryWidth is the width returned by query(). It is set to width // of the last dequeued buffer or to mReqWidth if no buffer was dequeued. uint32_t mQueryWidth; diff --git a/keystore/java/android/security/IKeyChainService.aidl b/keystore/java/android/security/IKeyChainService.aidl index 23ffd59..f38f6ce 100644 --- a/keystore/java/android/security/IKeyChainService.aidl +++ b/keystore/java/android/security/IKeyChainService.aidl @@ -23,8 +23,8 @@ package android.security; */ interface IKeyChainService { // APIs used by KeyChain - byte[] getPrivateKey(String alias, String authToken); - byte[] getCertificate(String alias, String authToken); + byte[] getPrivateKey(String alias); + byte[] getCertificate(String alias); // APIs used by CertInstaller void installCaCertificate(in byte[] caCertificate); @@ -32,4 +32,8 @@ interface IKeyChainService { // APIs used by Settings boolean deleteCaCertificate(String alias); boolean reset(); + + // APIs used by KeyChainActivity + void setGrant(int uid, String alias, boolean value); + boolean hasGrant(int uid, String alias); } diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java index 6229331..db6388a 100644 --- a/keystore/java/android/security/KeyChain.java +++ b/keystore/java/android/security/KeyChain.java @@ -15,36 +15,25 @@ */ package android.security; -import android.accounts.Account; -import android.accounts.AccountManager; -import android.accounts.AccountManagerCallback; -import android.accounts.AccountManagerFuture; -import android.accounts.AuthenticatorException; -import android.accounts.OperationCanceledException; import android.app.Activity; import android.app.PendingIntent; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; -import android.os.Bundle; import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; import java.io.ByteArrayInputStream; import java.io.Closeable; import java.io.IOException; -import java.security.KeyFactory; import java.security.KeyPair; -import java.security.NoSuchAlgorithmException; import java.security.Principal; import java.security.PrivateKey; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; -import java.security.spec.InvalidKeySpecException; -import java.security.spec.PKCS8EncodedKeySpec; import java.util.ArrayList; import java.util.List; import java.util.concurrent.BlockingQueue; @@ -266,7 +255,7 @@ public final class KeyChain { throw new NullPointerException("response == null"); } Intent intent = new Intent(ACTION_CHOOSER); - intent.putExtra(EXTRA_RESPONSE, new AliasResponse(activity, response)); + intent.putExtra(EXTRA_RESPONSE, new AliasResponse(response)); intent.putExtra(EXTRA_HOST, host); intent.putExtra(EXTRA_PORT, port); intent.putExtra(EXTRA_ALIAS, alias); @@ -276,56 +265,12 @@ public final class KeyChain { } private static class AliasResponse extends IKeyChainAliasCallback.Stub { - private final Activity activity; private final KeyChainAliasCallback keyChainAliasResponse; - private AliasResponse(Activity activity, KeyChainAliasCallback keyChainAliasResponse) { - this.activity = activity; + private AliasResponse(KeyChainAliasCallback keyChainAliasResponse) { this.keyChainAliasResponse = keyChainAliasResponse; } @Override public void alias(String alias) { - if (alias == null) { - keyChainAliasResponse.alias(null); - return; - } - AccountManager accountManager = AccountManager.get(activity); - accountManager.getAuthToken(getAccount(activity), - alias, - null, - activity, - new AliasAccountManagerCallback(keyChainAliasResponse, - alias), - null); - } - } - - private static class AliasAccountManagerCallback implements AccountManagerCallback<Bundle> { - private final KeyChainAliasCallback keyChainAliasResponse; - private final String alias; - private AliasAccountManagerCallback(KeyChainAliasCallback keyChainAliasResponse, - String alias) { - this.keyChainAliasResponse = keyChainAliasResponse; - this.alias = alias; - } - @Override public void run(AccountManagerFuture<Bundle> future) { - Bundle bundle; - try { - bundle = future.getResult(); - } catch (OperationCanceledException e) { - keyChainAliasResponse.alias(null); - return; - } catch (IOException e) { - keyChainAliasResponse.alias(null); - return; - } catch (AuthenticatorException e) { - keyChainAliasResponse.alias(null); - return; - } - String authToken = bundle.getString(AccountManager.KEY_AUTHTOKEN); - if (authToken != null) { - keyChainAliasResponse.alias(alias); - } else { - keyChainAliasResponse.alias(null); - } + keyChainAliasResponse.alias(alias); } } @@ -347,12 +292,8 @@ public final class KeyChain { } KeyChainConnection keyChainConnection = bind(context); try { - String authToken = authToken(context, alias); - if (authToken == null) { - return null; - } IKeyChainService keyChainService = keyChainConnection.getService(); - byte[] privateKeyBytes = keyChainService.getPrivateKey(alias, authToken); + byte[] privateKeyBytes = keyChainService.getPrivateKey(alias); return toPrivateKey(privateKeyBytes); } catch (RemoteException e) { throw new KeyChainException(e); @@ -382,12 +323,8 @@ public final class KeyChain { } KeyChainConnection keyChainConnection = bind(context); try { - String authToken = authToken(context, alias); - if (authToken == null) { - return null; - } IKeyChainService keyChainService = keyChainConnection.getService(); - byte[] certificateBytes = keyChainService.getCertificate(alias, authToken); + byte[] certificateBytes = keyChainService.getCertificate(alias); List<X509Certificate> chain = new ArrayList<X509Certificate>(); chain.add(toCertificate(certificateBytes)); TrustedCertificateStore store = new TrustedCertificateStore(); @@ -438,50 +375,6 @@ public final class KeyChain { } } - private static String authToken(Context context, String alias) { - AccountManager accountManager = AccountManager.get(context); - AccountManagerFuture<Bundle> future = accountManager.getAuthToken(getAccount(context), - alias, - false, - null, - null); - Bundle bundle; - try { - bundle = future.getResult(); - } catch (OperationCanceledException e) { - throw new AssertionError(e); - } catch (IOException e) { - // KeyChainAccountAuthenticator doesn't do I/O - throw new AssertionError(e); - } catch (AuthenticatorException e) { - throw new AssertionError(e); - } - Intent intent = bundle.getParcelable(AccountManager.KEY_INTENT); - if (intent != null) { - return null; - } - String authToken = bundle.getString(AccountManager.KEY_AUTHTOKEN); - if (authToken == null) { - throw new AssertionError("Invalid authtoken"); - } - return authToken; - } - - private static Account getAccount(Context context) { - AccountManager accountManager = AccountManager.get(context); - Account[] accounts = accountManager.getAccountsByType(ACCOUNT_TYPE); - if (accounts.length == 0) { - try { - // Account is created if necessary during binding of the IKeyChainService - bind(context).close(); - } catch (InterruptedException e) { - throw new AssertionError(e); - } - accounts = accountManager.getAccountsByType(ACCOUNT_TYPE); - } - return accounts[0]; - } - /** * @hide for reuse by CertInstaller and Settings. * @see KeyChain#bind @@ -517,11 +410,15 @@ public final class KeyChain { ensureNotOnMainThread(context); final BlockingQueue<IKeyChainService> q = new LinkedBlockingQueue<IKeyChainService>(1); ServiceConnection keyChainServiceConnection = new ServiceConnection() { + volatile boolean mConnectedAtLeastOnce = false; @Override public void onServiceConnected(ComponentName name, IBinder service) { - try { - q.put(IKeyChainService.Stub.asInterface(service)); - } catch (InterruptedException e) { - throw new AssertionError(e); + if (!mConnectedAtLeastOnce) { + mConnectedAtLeastOnce = true; + try { + q.put(IKeyChainService.Stub.asInterface(service)); + } catch (InterruptedException e) { + // will never happen, since the queue starts with one available slot + } } } @Override public void onServiceDisconnected(ComponentName name) {} diff --git a/libs/cpustats/Android.mk b/libs/cpustats/Android.mk new file mode 100644 index 0000000..5c1074d --- /dev/null +++ b/libs/cpustats/Android.mk @@ -0,0 +1,21 @@ +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + CentralTendencyStatistics.cpp \ + ThreadCpuUsage.cpp + +LOCAL_MODULE := libcpustats + +include $(BUILD_STATIC_LIBRARY) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + CentralTendencyStatistics.cpp \ + ThreadCpuUsage.cpp + +LOCAL_MODULE := libcpustats + +include $(BUILD_HOST_STATIC_LIBRARY) diff --git a/libs/cpustats/CentralTendencyStatistics.cpp b/libs/cpustats/CentralTendencyStatistics.cpp new file mode 100644 index 0000000..42ab62b --- /dev/null +++ b/libs/cpustats/CentralTendencyStatistics.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdlib.h> + +#include <cpustats/CentralTendencyStatistics.h> + +void CentralTendencyStatistics::sample(double x) +{ + // update min and max + if (x < mMinimum) + mMinimum = x; + if (x > mMaximum) + mMaximum = x; + // Knuth + if (mN == 0) { + mMean = 0; + } + ++mN; + double delta = x - mMean; + mMean += delta / mN; + mM2 += delta * (x - mMean); +} + +void CentralTendencyStatistics::reset() +{ + mMean = NAN; + mMedian = NAN; + mMinimum = INFINITY; + mMaximum = -INFINITY; + mN = 0; + mM2 = 0; + mVariance = NAN; + mVarianceKnownForN = 0; + mStddev = NAN; + mStddevKnownForN = 0; +} + +double CentralTendencyStatistics::variance() const +{ + double variance; + if (mVarianceKnownForN != mN) { + if (mN > 1) { + // double variance_n = M2/n; + variance = mM2 / (mN - 1); + } else { + variance = NAN; + } + mVariance = variance; + mVarianceKnownForN = mN; + } else { + variance = mVariance; + } + return variance; +} + +double CentralTendencyStatistics::stddev() const +{ + double stddev; + if (mStddevKnownForN != mN) { + stddev = sqrt(variance()); + mStddev = stddev; + mStddevKnownForN = mN; + } else { + stddev = mStddev; + } + return stddev; +} diff --git a/libs/cpustats/ThreadCpuUsage.cpp b/libs/cpustats/ThreadCpuUsage.cpp new file mode 100644 index 0000000..4bfbdf3 --- /dev/null +++ b/libs/cpustats/ThreadCpuUsage.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <errno.h> +#include <time.h> + +#include <utils/Log.h> + +#include <cpustats/ThreadCpuUsage.h> + +bool ThreadCpuUsage::setEnabled(bool isEnabled) +{ + bool wasEnabled = mIsEnabled; + // only do something if there is a change + if (isEnabled != wasEnabled) { + int rc; + // enabling + if (isEnabled) { + rc = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &mPreviousTs); + if (rc) { + LOGE("clock_gettime(CLOCK_THREAD_CPUTIME_ID) errno=%d", errno); + isEnabled = false; + } else { + mWasEverEnabled = true; + // record wall clock time at first enable + if (!mMonotonicKnown) { + rc = clock_gettime(CLOCK_MONOTONIC, &mMonotonicTs); + if (rc) { + LOGE("clock_gettime(CLOCK_MONOTONIC) errno=%d", errno); + } else { + mMonotonicKnown = true; + } + } + } + // disabling + } else { + struct timespec ts; + rc = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts); + if (rc) { + LOGE("clock_gettime(CLOCK_THREAD_CPUTIME_ID) errno=%d", errno); + } else { + long long delta = (ts.tv_sec - mPreviousTs.tv_sec) * 1000000000LL + + (ts.tv_nsec - mPreviousTs.tv_nsec); + mAccumulator += delta; +#if 0 + mPreviousTs = ts; +#endif + } + } + mIsEnabled = isEnabled; + } + return wasEnabled; +} + +void ThreadCpuUsage::sampleAndEnable() +{ + bool wasEverEnabled = mWasEverEnabled; + if (enable()) { + // already enabled, so add a new sample relative to previous + sample(); + } else if (wasEverEnabled) { + // was disabled, but add sample for accumulated time while enabled + mStatistics.sample((double) mAccumulator); + mAccumulator = 0; + } +} + +void ThreadCpuUsage::sample() +{ + if (mWasEverEnabled) { + if (mIsEnabled) { + struct timespec ts; + int rc; + rc = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts); + if (rc) { + LOGE("clock_gettime(CLOCK_THREAD_CPUTIME_ID) errno=%d", errno); + } else { + long long delta = (ts.tv_sec - mPreviousTs.tv_sec) * 1000000000LL + + (ts.tv_nsec - mPreviousTs.tv_nsec); + mAccumulator += delta; + mPreviousTs = ts; + } + } else { + mWasEverEnabled = false; + } + mStatistics.sample((double) mAccumulator); + mAccumulator = 0; + } else { + LOGW("Can't add sample because measurements have never been enabled"); + } +} + +long long ThreadCpuUsage::elapsed() const +{ + long long elapsed; + if (mMonotonicKnown) { + struct timespec ts; + int rc; + rc = clock_gettime(CLOCK_MONOTONIC, &ts); + if (rc) { + LOGE("clock_gettime(CLOCK_MONOTONIC) errno=%d", errno); + elapsed = 0; + } else { + // mMonotonicTs is updated only at first enable and resetStatistics + elapsed = (ts.tv_sec - mMonotonicTs.tv_sec) * 1000000000LL + + (ts.tv_nsec - mMonotonicTs.tv_nsec); + } + } else { + LOGW("Can't compute elapsed time because measurements have never been enabled"); + elapsed = 0; + } + return elapsed; +} + +void ThreadCpuUsage::resetStatistics() +{ + mStatistics.reset(); + if (mMonotonicKnown) { + int rc; + rc = clock_gettime(CLOCK_MONOTONIC, &mMonotonicTs); + if (rc) { + LOGE("clock_gettime(CLOCK_MONOTONIC) errno=%d", errno); + mMonotonicKnown = false; + } + } +} diff --git a/libs/gui/ISurfaceTexture.cpp b/libs/gui/ISurfaceTexture.cpp index 16e3780..41434a4 100644 --- a/libs/gui/ISurfaceTexture.cpp +++ b/libs/gui/ISurfaceTexture.cpp @@ -41,6 +41,8 @@ enum { GET_ALLOCATOR, QUERY, SET_SYNCHRONOUS_MODE, + CONNECT, + DISCONNECT, }; @@ -154,7 +156,23 @@ public: return result; } + virtual status_t connect(int api) { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor()); + data.writeInt32(api); + remote()->transact(CONNECT, data, &reply); + status_t result = reply.readInt32(); + return result; + } + virtual status_t disconnect(int api) { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor()); + data.writeInt32(api); + remote()->transact(DISCONNECT, data, &reply); + status_t result = reply.readInt32(); + return result; + } }; IMPLEMENT_META_INTERFACE(SurfaceTexture, "android.gui.SurfaceTexture"); @@ -248,6 +266,20 @@ status_t BnSurfaceTexture::onTransact( reply->writeInt32(res); return NO_ERROR; } break; + case CONNECT: { + CHECK_INTERFACE(ISurfaceTexture, data, reply); + int api = data.readInt32(); + status_t res = connect(api); + reply->writeInt32(res); + return NO_ERROR; + } break; + case DISCONNECT: { + CHECK_INTERFACE(ISurfaceTexture, data, reply); + int api = data.readInt32(); + status_t res = disconnect(api); + reply->writeInt32(res); + return NO_ERROR; + } break; } return BBinder::onTransact(code, data, reply, flags); } diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp index 886a3fb..1410481 100644 --- a/libs/gui/SurfaceTexture.cpp +++ b/libs/gui/SurfaceTexture.cpp @@ -92,7 +92,8 @@ SurfaceTexture::SurfaceTexture(GLuint tex, bool allowSynchronousMode) : mNextTransform(0), mTexName(tex), mSynchronousMode(false), - mAllowSynchronousMode(allowSynchronousMode) { + mAllowSynchronousMode(allowSynchronousMode), + mConnectedApi(NO_CONNECTED_API) { LOGV("SurfaceTexture::SurfaceTexture"); sp<ISurfaceComposer> composer(ComposerService::getComposerService()); mGraphicBufferAlloc = composer->createGraphicBufferAlloc(); @@ -493,6 +494,50 @@ status_t SurfaceTexture::setTransform(uint32_t transform) { return OK; } +status_t SurfaceTexture::connect(int api) { + LOGV("SurfaceTexture::connect"); + Mutex::Autolock lock(mMutex); + int err = NO_ERROR; + switch (api) { + case NATIVE_WINDOW_API_EGL: + case NATIVE_WINDOW_API_CPU: + case NATIVE_WINDOW_API_MEDIA: + case NATIVE_WINDOW_API_CAMERA: + if (mConnectedApi != NO_CONNECTED_API) { + err = -EINVAL; + } else { + mConnectedApi = api; + } + break; + default: + err = -EINVAL; + break; + } + return err; +} + +status_t SurfaceTexture::disconnect(int api) { + LOGV("SurfaceTexture::disconnect"); + Mutex::Autolock lock(mMutex); + int err = NO_ERROR; + switch (api) { + case NATIVE_WINDOW_API_EGL: + case NATIVE_WINDOW_API_CPU: + case NATIVE_WINDOW_API_MEDIA: + case NATIVE_WINDOW_API_CAMERA: + if (mConnectedApi == api) { + mConnectedApi = NO_CONNECTED_API; + } else { + err = -EINVAL; + } + break; + default: + err = -EINVAL; + break; + } + return err; +} + status_t SurfaceTexture::updateTexImage() { LOGV("SurfaceTexture::updateTexImage"); Mutex::Autolock lock(mMutex); diff --git a/libs/gui/SurfaceTextureClient.cpp b/libs/gui/SurfaceTextureClient.cpp index e203035..f39cabf 100644 --- a/libs/gui/SurfaceTextureClient.cpp +++ b/libs/gui/SurfaceTextureClient.cpp @@ -27,7 +27,7 @@ SurfaceTextureClient::SurfaceTextureClient( const sp<ISurfaceTexture>& surfaceTexture): mSurfaceTexture(surfaceTexture), mAllocator(0), mReqWidth(0), mReqHeight(0), mReqFormat(0), mReqUsage(0), - mTimestamp(NATIVE_WINDOW_TIMESTAMP_AUTO), mConnectedApi(0), + mTimestamp(NATIVE_WINDOW_TIMESTAMP_AUTO), mQueryWidth(0), mQueryHeight(0), mQueryFormat(0), mMutex() { // Initialize the ANativeWindow function pointers. @@ -327,45 +327,22 @@ int SurfaceTextureClient::dispatchSetBuffersTimestamp(va_list args) { int SurfaceTextureClient::connect(int api) { LOGV("SurfaceTextureClient::connect"); Mutex::Autolock lock(mMutex); - int err = NO_ERROR; - switch (api) { - case NATIVE_WINDOW_API_EGL: - if (mConnectedApi) { - err = -EINVAL; - } else { - mConnectedApi = api; - } - break; - default: - err = -EINVAL; - break; - } - return err; + return mSurfaceTexture->connect(api); } int SurfaceTextureClient::disconnect(int api) { LOGV("SurfaceTextureClient::disconnect"); Mutex::Autolock lock(mMutex); - int err = NO_ERROR; - switch (api) { - case NATIVE_WINDOW_API_EGL: - if (mConnectedApi == api) { - mConnectedApi = 0; - } else { - err = -EINVAL; - } - break; - default: - err = -EINVAL; - break; - } - return err; + return mSurfaceTexture->disconnect(api); } int SurfaceTextureClient::getConnectedApi() const { + // XXX: This method will be going away shortly, and is currently bogus. It + // always returns "nothing is connected". It will go away once Surface gets + // updated to actually connect as the 'CPU' API when locking a buffer. Mutex::Autolock lock(mMutex); - return mConnectedApi; + return 0; } diff --git a/libs/gui/tests/SurfaceTexture_test.cpp b/libs/gui/tests/SurfaceTexture_test.cpp index 88433fb..9abe89d 100644 --- a/libs/gui/tests/SurfaceTexture_test.cpp +++ b/libs/gui/tests/SurfaceTexture_test.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#define LOG_TAG "SurfaceTexture_test" //#define LOG_NDEBUG 0 #include <gtest/gtest.h> @@ -379,6 +380,13 @@ protected: ASSERT_NE(-1, mTexMatrixHandle); } + virtual void TearDown() { + mANW.clear(); + mSTC.clear(); + mST.clear(); + GLTest::TearDown(); + } + // drawTexture draws the SurfaceTexture over the entire GL viewport. void drawTexture() { const GLfloat triangleVertices[] = { @@ -1089,13 +1097,21 @@ protected: // synchronously from SurfaceTexture::queueBuffer. class FrameCondition : public SurfaceTexture::FrameAvailableListener { public: + FrameCondition(): + mFrameAvailable(false), + mFrameFinished(false) { + } + // waitForFrame waits for the next frame to arrive. This should be // called from the consumer thread once for every frame expected by the // test. void waitForFrame() { - LOGV("+waitForFrame"); Mutex::Autolock lock(mMutex); - status_t result = mFrameAvailableCondition.wait(mMutex); + LOGV("+waitForFrame"); + while (!mFrameAvailable) { + mFrameAvailableCondition.wait(mMutex); + } + mFrameAvailable = false; LOGV("-waitForFrame"); } @@ -1103,22 +1119,30 @@ protected: // on to produce the next frame. This should be called by the consumer // thread once for every frame expected by the test. void finishFrame() { - LOGV("+finishFrame"); Mutex::Autolock lock(mMutex); + LOGV("+finishFrame"); + mFrameFinished = true; mFrameFinishCondition.signal(); LOGV("-finishFrame"); } // This should be called by SurfaceTexture on the producer thread. virtual void onFrameAvailable() { - LOGV("+onFrameAvailable"); Mutex::Autolock lock(mMutex); + LOGV("+onFrameAvailable"); + mFrameAvailable = true; mFrameAvailableCondition.signal(); - mFrameFinishCondition.wait(mMutex); + while (!mFrameFinished) { + mFrameFinishCondition.wait(mMutex); + } + mFrameFinished = false; LOGV("-onFrameAvailable"); } protected: + bool mFrameAvailable; + bool mFrameFinished; + Mutex mMutex; Condition mFrameAvailableCondition; Condition mFrameFinishCondition; @@ -1164,6 +1188,7 @@ protected: } mProducerThread.clear(); mFC.clear(); + SurfaceTextureGLTest::TearDown(); } void runProducerThread(const sp<ProducerThread> producerThread) { diff --git a/libs/rs/rsContext.cpp b/libs/rs/rsContext.cpp index 8798612..447a7ff 100644 --- a/libs/rs/rsContext.cpp +++ b/libs/rs/rsContext.cpp @@ -340,10 +340,6 @@ Context::Context() { Context * Context::createContext(Device *dev, const RsSurfaceConfig *sc) { Context * rsc = new Context(); - // Temporary to avoid breaking the tools - if (!dev) { - return rsc; - } if (!rsc->initContext(dev, sc)) { delete rsc; return NULL; diff --git a/libs/rs/rsLocklessFifo.cpp b/libs/rs/rsLocklessFifo.cpp index 70b7278..7023a1f 100644 --- a/libs/rs/rsLocklessFifo.cpp +++ b/libs/rs/rsLocklessFifo.cpp @@ -21,14 +21,16 @@ using namespace android; using namespace android::renderscript; -LocklessCommandFifo::LocklessCommandFifo() { +LocklessCommandFifo::LocklessCommandFifo() : mBuffer(0) { } LocklessCommandFifo::~LocklessCommandFifo() { if (!mInShutdown) { shutdown(); } - free(mBuffer); + if (mBuffer) { + free(mBuffer); + } } void LocklessCommandFifo::shutdown() { diff --git a/libs/rs/rsThreadIO.cpp b/libs/rs/rsThreadIO.cpp index ab164c3..1c8b89c 100644 --- a/libs/rs/rsThreadIO.cpp +++ b/libs/rs/rsThreadIO.cpp @@ -21,8 +21,7 @@ using namespace android; using namespace android::renderscript; -ThreadIO::ThreadIO() { - mToCore.init(16 * 1024); +ThreadIO::ThreadIO() : mUsingSocket(false) { } ThreadIO::~ThreadIO() { @@ -30,6 +29,7 @@ ThreadIO::~ThreadIO() { void ThreadIO::init(bool useSocket) { mUsingSocket = useSocket; + mToCore.init(16 * 1024); if (mUsingSocket) { mToClientSocket.init(); diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp index 50312e7..d18c0a2 100644 --- a/libs/utils/Threads.cpp +++ b/libs/utils/Threads.cpp @@ -161,6 +161,7 @@ int androidCreateRawThreadEtc(android_thread_func_t entryFunction, pthread_t thread; int result = pthread_create(&thread, &attr, (android_pthread_entry)entryFunction, userData); + pthread_attr_destroy(&attr); if (result != 0) { LOGE("androidCreateRawThreadEtc failed (entry=%p, res=%d, errno=%d)\n" "(android threadPriority=%d)", diff --git a/libs/utils/VectorImpl.cpp b/libs/utils/VectorImpl.cpp index 87ae3d5..bfb37a6 100644 --- a/libs/utils/VectorImpl.cpp +++ b/libs/utils/VectorImpl.cpp @@ -252,13 +252,15 @@ ssize_t VectorImpl::replaceAt(const void* prototype, size_t index) "[%p] replace: index=%d, size=%d", this, (int)index, (int)size()); void* item = editItemLocation(index); - if (item == 0) - return NO_MEMORY; - _do_destroy(item, 1); - if (prototype == 0) { - _do_construct(item, 1); - } else { - _do_copy(item, prototype, 1); + if (item != prototype) { + if (item == 0) + return NO_MEMORY; + _do_destroy(item, 1); + if (prototype == 0) { + _do_construct(item, 1); + } else { + _do_copy(item, prototype, 1); + } } return ssize_t(index); } @@ -347,9 +349,10 @@ void* VectorImpl::_grow(size_t where, size_t amount) // LOGV("_grow(this=%p, where=%d, amount=%d) count=%d, capacity=%d", // this, (int)where, (int)amount, (int)mCount, (int)capacity()); - if (where > mCount) - where = mCount; - + LOG_ASSERT(where <= mCount, + "[%p] _grow: where=%d, amount=%d, count=%d", + this, (int)where, (int)amount, (int)mCount); // caller already checked + const size_t new_size = mCount + amount; if (capacity() < new_size) { const size_t new_capacity = max(kMinVectorCapacity, ((new_size*3)+1)/2); @@ -366,10 +369,10 @@ void* VectorImpl::_grow(size_t where, size_t amount) SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize); if (sb) { void* array = sb->data(); - if (where>0) { + if (where != 0) { _do_copy(array, mStorage, where); } - if (mCount>where) { + if (where != mCount) { const void* from = reinterpret_cast<const uint8_t *>(mStorage) + where*mItemSize; void* dest = reinterpret_cast<uint8_t *>(array) + (where+amount)*mItemSize; _do_copy(dest, from, mCount-where); @@ -379,15 +382,14 @@ void* VectorImpl::_grow(size_t where, size_t amount) } } } else { - ssize_t s = mCount-where; - if (s>0) { - void* array = editArrayImpl(); - void* to = reinterpret_cast<uint8_t *>(array) + (where+amount)*mItemSize; + if (where != mCount) { + void* array = editArrayImpl(); const void* from = reinterpret_cast<const uint8_t *>(array) + where*mItemSize; - _do_move_forward(to, from, s); + void* to = reinterpret_cast<uint8_t *>(array) + (where+amount)*mItemSize; + _do_move_forward(to, from, mCount - where); } } - mCount += amount; + mCount = new_size; void* free_space = const_cast<void*>(itemLocation(where)); return free_space; } @@ -400,14 +402,15 @@ void VectorImpl::_shrink(size_t where, size_t amount) // LOGV("_shrink(this=%p, where=%d, amount=%d) count=%d, capacity=%d", // this, (int)where, (int)amount, (int)mCount, (int)capacity()); - if (where >= mCount) - where = mCount - amount; + LOG_ASSERT(where + amount <= mCount, + "[%p] _shrink: where=%d, amount=%d, count=%d", + this, (int)where, (int)amount, (int)mCount); // caller already checked const size_t new_size = mCount - amount; if (new_size*3 < capacity()) { const size_t new_capacity = max(kMinVectorCapacity, new_size*2); // LOGV("shrink vector %p, new_capacity=%d", this, (int)new_capacity); - if ((where == mCount-amount) && + if ((where == new_size) && (mFlags & HAS_TRIVIAL_COPY) && (mFlags & HAS_TRIVIAL_DTOR)) { @@ -418,31 +421,28 @@ void VectorImpl::_shrink(size_t where, size_t amount) SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize); if (sb) { void* array = sb->data(); - if (where>0) { + if (where != 0) { _do_copy(array, mStorage, where); } - if (mCount > where+amount) { + if (where != new_size) { const void* from = reinterpret_cast<const uint8_t *>(mStorage) + (where+amount)*mItemSize; void* dest = reinterpret_cast<uint8_t *>(array) + where*mItemSize; - _do_copy(dest, from, mCount-(where+amount)); + _do_copy(dest, from, new_size - where); } release_storage(); mStorage = const_cast<void*>(array); } } } else { - void* array = editArrayImpl(); + void* array = editArrayImpl(); void* to = reinterpret_cast<uint8_t *>(array) + where*mItemSize; _do_destroy(to, amount); - ssize_t s = mCount-(where+amount); - if (s>0) { + if (where != new_size) { const void* from = reinterpret_cast<uint8_t *>(array) + (where+amount)*mItemSize; - _do_move_backward(to, from, s); + _do_move_backward(to, from, new_size - where); } } - - // adjust the number of items... - mCount -= amount; + mCount = new_size; } size_t VectorImpl::itemSize() const { diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java index e89be08..8c8569a 100644 --- a/media/java/android/media/MediaScanner.java +++ b/media/java/android/media/MediaScanner.java @@ -707,7 +707,9 @@ public class MediaScanner map.put(MediaStore.MediaColumns.MIME_TYPE, mMimeType); map.put(MediaStore.MediaColumns.IS_DRM, mIsDrm); - if (!mNoMedia) { + if (mNoMedia) { + map.put(MediaStore.MediaColumns.NO_MEDIA, true); + } else { if (MediaFile.isVideoFileType(mFileType)) { map.put(Video.Media.ARTIST, (mArtist != null && mArtist.length() > 0 ? mArtist : MediaStore.UNKNOWN_STRING)); diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp index 788464e..0098537 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -453,7 +453,6 @@ status_t AwesomePlayer::setDataSource_l(const sp<MediaExtractor> &extractor) { } void AwesomePlayer::reset() { - LOGI("reset"); Mutex::Autolock autoLock(mLock); reset_l(); } @@ -467,10 +466,8 @@ void AwesomePlayer::reset_l() { Playback::STOP, 0); mDecryptHandle = NULL; mDrmManagerClient = NULL; - LOGI("DRM manager client stopped"); } - if (mFlags & PLAYING) { uint32_t params = IMediaPlayerService::kBatteryDataTrackDecoder; if ((mAudioSource != NULL) && (mAudioSource != mAudioTrack)) { @@ -503,7 +500,6 @@ void AwesomePlayer::reset_l() { mPreparedCondition.wait(mLock); } - LOGI("cancel player events"); cancelPlayerEvents(); mWVMExtractor.clear(); diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index b7b0dc0..5cab60e 100755 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -3539,7 +3539,7 @@ status_t OMXCodec::start(MetaData *meta) { } status_t OMXCodec::stop() { - CODEC_LOGI("stop mState=%d", mState); + CODEC_LOGV("stop mState=%d", mState); Mutex::Autolock autoLock(mLock); @@ -3601,7 +3601,6 @@ status_t OMXCodec::stop() { mLeftOverBuffer = NULL; } - CODEC_LOGI("stopping video source"); mSource->stop(); CODEC_LOGI("stopped in state %d", mState); diff --git a/media/libstagefright/XINGSeeker.cpp b/media/libstagefright/XINGSeeker.cpp index 0d0d6c2..2091381 100644 --- a/media/libstagefright/XINGSeeker.cpp +++ b/media/libstagefright/XINGSeeker.cpp @@ -24,8 +24,8 @@ namespace android { static bool parse_xing_header( const sp<DataSource> &source, off64_t first_frame_pos, int32_t *frame_number = NULL, int32_t *byte_number = NULL, - char *table_of_contents = NULL, int32_t *quality_indicator = NULL, - int64_t *duration = NULL); + unsigned char *table_of_contents = NULL, + int32_t *quality_indicator = NULL, int64_t *duration = NULL); // static sp<XINGSeeker> XINGSeeker::CreateFromSource( @@ -94,7 +94,7 @@ bool XINGSeeker::getOffsetForTime(int64_t *timeUs, off64_t *pos) { static bool parse_xing_header( const sp<DataSource> &source, off64_t first_frame_pos, int32_t *frame_number, int32_t *byte_number, - char *table_of_contents, int32_t *quality_indicator, + unsigned char *table_of_contents, int32_t *quality_indicator, int64_t *duration) { if (frame_number) { *frame_number = 0; diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp index 8ecc17c..ca61b3d 100644 --- a/media/libstagefright/httplive/LiveSession.cpp +++ b/media/libstagefright/httplive/LiveSession.cpp @@ -36,11 +36,10 @@ #include <ctype.h> #include <openssl/aes.h> +#include <openssl/md5.h> namespace android { -const int64_t LiveSession::kMaxPlaylistAgeUs = 15000000ll; - LiveSession::LiveSession(uint32_t flags, bool uidValid, uid_t uid) : mFlags(flags), mUIDValid(uidValid), @@ -59,7 +58,8 @@ LiveSession::LiveSession(uint32_t flags, bool uidValid, uid_t uid) mDurationUs(-1), mSeekDone(false), mDisconnectPending(false), - mMonitorQueueGeneration(0) { + mMonitorQueueGeneration(0), + mRefreshState(INITIAL_MINIMUM_RELOAD_DELAY) { if (mUIDValid) { mHTTPDataSource->setUID(mUID); } @@ -175,7 +175,8 @@ void LiveSession::onConnect(const sp<AMessage> &msg) { mMasterURL = url; - sp<M3UParser> playlist = fetchPlaylist(url.c_str()); + bool dummy; + sp<M3UParser> playlist = fetchPlaylist(url.c_str(), &dummy); if (playlist == NULL) { LOGE("unable to fetch master playlist '%s'.", url.c_str()); @@ -289,7 +290,9 @@ status_t LiveSession::fetchFile(const char *url, sp<ABuffer> *out) { return OK; } -sp<M3UParser> LiveSession::fetchPlaylist(const char *url) { +sp<M3UParser> LiveSession::fetchPlaylist(const char *url, bool *unchanged) { + *unchanged = false; + sp<ABuffer> buffer; status_t err = fetchFile(url, &buffer); @@ -297,6 +300,38 @@ sp<M3UParser> LiveSession::fetchPlaylist(const char *url) { return NULL; } + // MD5 functionality is not available on the simulator, treat all + // playlists as changed. + +#if defined(HAVE_ANDROID_OS) + uint8_t hash[16]; + + MD5_CTX m; + MD5_Init(&m); + MD5_Update(&m, buffer->data(), buffer->size()); + + MD5_Final(hash, &m); + + if (mPlaylist != NULL && !memcmp(hash, mPlaylistHash, 16)) { + // playlist unchanged + + if (mRefreshState != THIRD_UNCHANGED_RELOAD_ATTEMPT) { + mRefreshState = (RefreshState)(mRefreshState + 1); + } + + *unchanged = true; + + LOGV("Playlist unchanged, refresh state is now %d", + (int)mRefreshState); + + return NULL; + } + + memcpy(mPlaylistHash, hash, sizeof(hash)); + + mRefreshState = INITIAL_MINIMUM_RELOAD_DELAY; +#endif + sp<M3UParser> playlist = new M3UParser(url, buffer->data(), buffer->size()); @@ -384,6 +419,63 @@ size_t LiveSession::getBandwidthIndex() { return index; } +bool LiveSession::timeToRefreshPlaylist(int64_t nowUs) const { + if (mPlaylist == NULL) { + CHECK_EQ((int)mRefreshState, (int)INITIAL_MINIMUM_RELOAD_DELAY); + return true; + } + + int32_t targetDurationSecs; + CHECK(mPlaylist->meta()->findInt32("target-duration", &targetDurationSecs)); + + int64_t targetDurationUs = targetDurationSecs * 1000000ll; + + int64_t minPlaylistAgeUs; + + switch (mRefreshState) { + case INITIAL_MINIMUM_RELOAD_DELAY: + { + size_t n = mPlaylist->size(); + if (n > 0) { + sp<AMessage> itemMeta; + CHECK(mPlaylist->itemAt(n - 1, NULL /* uri */, &itemMeta)); + + int64_t itemDurationUs; + CHECK(itemMeta->findInt64("durationUs", &itemDurationUs)); + + minPlaylistAgeUs = itemDurationUs; + break; + } + + // fall through + } + + case FIRST_UNCHANGED_RELOAD_ATTEMPT: + { + minPlaylistAgeUs = targetDurationUs / 2; + break; + } + + case SECOND_UNCHANGED_RELOAD_ATTEMPT: + { + minPlaylistAgeUs = (targetDurationUs * 3) / 2; + break; + } + + case THIRD_UNCHANGED_RELOAD_ATTEMPT: + { + minPlaylistAgeUs = targetDurationUs * 3; + break; + } + + default: + TRESPASS(); + break; + } + + return mLastPlaylistFetchTimeUs + minPlaylistAgeUs <= nowUs; +} + void LiveSession::onDownloadNext() { size_t bandwidthIndex = getBandwidthIndex(); @@ -392,8 +484,7 @@ rinse_repeat: if (mLastPlaylistFetchTimeUs < 0 || (ssize_t)bandwidthIndex != mPrevBandwidthIndex - || (!mPlaylist->isComplete() - && mLastPlaylistFetchTimeUs + kMaxPlaylistAgeUs <= nowUs)) { + || (!mPlaylist->isComplete() && timeToRefreshPlaylist(nowUs))) { AString url; if (mBandwidthItems.size() > 0) { url = mBandwidthItems.editItemAt(bandwidthIndex).mURI; @@ -403,11 +494,19 @@ rinse_repeat: bool firstTime = (mPlaylist == NULL); - mPlaylist = fetchPlaylist(url.c_str()); - if (mPlaylist == NULL) { - LOGE("failed to load playlist at url '%s'", url.c_str()); - mDataSource->queueEOS(ERROR_IO); - return; + bool unchanged; + sp<M3UParser> playlist = fetchPlaylist(url.c_str(), &unchanged); + if (playlist == NULL) { + if (unchanged) { + // We succeeded in fetching the playlist, but it was + // unchanged from the last time we tried. + } else { + LOGE("failed to load playlist at url '%s'", url.c_str()); + mDataSource->queueEOS(ERROR_IO); + return; + } + } else { + mPlaylist = playlist; } if (firstTime) { diff --git a/media/libstagefright/include/LiveSession.h b/media/libstagefright/include/LiveSession.h index 188ef5e..116ed0e 100644 --- a/media/libstagefright/include/LiveSession.h +++ b/media/libstagefright/include/LiveSession.h @@ -62,8 +62,6 @@ private: kMaxNumRetries = 5, }; - static const int64_t kMaxPlaylistAgeUs; - enum { kWhatConnect = 'conn', kWhatDisconnect = 'disc', @@ -106,6 +104,16 @@ private: int32_t mMonitorQueueGeneration; + enum RefreshState { + INITIAL_MINIMUM_RELOAD_DELAY, + FIRST_UNCHANGED_RELOAD_ATTEMPT, + SECOND_UNCHANGED_RELOAD_ATTEMPT, + THIRD_UNCHANGED_RELOAD_ATTEMPT + }; + RefreshState mRefreshState; + + uint8_t mPlaylistHash[16]; + void onConnect(const sp<AMessage> &msg); void onDisconnect(); void onDownloadNext(); @@ -113,7 +121,7 @@ private: void onSeek(const sp<AMessage> &msg); status_t fetchFile(const char *url, sp<ABuffer> *out); - sp<M3UParser> fetchPlaylist(const char *url); + sp<M3UParser> fetchPlaylist(const char *url, bool *unchanged); size_t getBandwidthIndex(); status_t decryptBuffer( @@ -121,6 +129,8 @@ private: void postMonitorQueue(int64_t delayUs = 0); + bool timeToRefreshPlaylist(int64_t nowUs) const; + static int SortByBandwidth(const BandwidthItem *, const BandwidthItem *); DISALLOW_EVIL_CONSTRUCTORS(LiveSession); diff --git a/media/libstagefright/include/XINGSeeker.h b/media/libstagefright/include/XINGSeeker.h index d5a484e..ec5bd9b 100644 --- a/media/libstagefright/include/XINGSeeker.h +++ b/media/libstagefright/include/XINGSeeker.h @@ -37,7 +37,7 @@ private: int32_t mSizeBytes; // TOC entries in XING header. Skip the first one since it's always 0. - char mTableOfContents[99]; + unsigned char mTableOfContents[99]; XINGSeeker(); diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp index 25f30d6..bc24dbb 100644 --- a/media/libstagefright/omx/OMX.cpp +++ b/media/libstagefright/omx/OMX.cpp @@ -96,9 +96,14 @@ OMX::CallbackDispatcher::~CallbackDispatcher() { mQueueChanged.signal(); } - // Don't call join on myself + // A join on self can happen if the last ref to CallbackDispatcher + // is released within the CallbackDispatcherThread loop status_t status = mThread->join(); - CHECK(status == NO_ERROR); + if (status != WOULD_BLOCK) { + // Other than join to self, the only other error return codes are + // whatever readyToRun() returns, and we don't override that + CHECK_EQ(status, NO_ERROR); + } } void OMX::CallbackDispatcher::post(const omx_message &msg) { diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPropertiesTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPropertiesTest.java index 59783e5..4d517db 100755 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPropertiesTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPropertiesTest.java @@ -91,8 +91,12 @@ public class MediaPropertiesTest extends assertEquals("AudioType Mismatch ", audioCodecType, mvi.getAudioType()); assertEquals("Audio Sampling " + mvi.getAudioSamplingFrequency(), audioSamplingFrequency, mvi.getAudioSamplingFrequency()); - assertEquals("Audio Channels " + mvi.getAudioChannels(), audioChannel, - mvi.getAudioChannels()); + // PV SW AAC codec always returns number of channels as Stereo. + // So we do not assert for number of audio channels for AAC_LC + if ( audioCodecType != MediaProperties.ACODEC_AAC_LC ) { + assertEquals("Audio Channels " + mvi.getAudioChannels(), audioChannel, + mvi.getAudioChannels()); + } } protected void validateAudioProperties(int audioCodecType, int duration, @@ -103,8 +107,12 @@ public class MediaPropertiesTest extends duration, aT.getDuration(), 10)); assertEquals("Audio Sampling " + aT.getAudioSamplingFrequency(), audioSamplingFrequency, aT.getAudioSamplingFrequency()); - assertEquals("Audio Channels " + aT.getAudioChannels(), audioChannel, - aT.getAudioChannels()); + // PV SW AAC codec always returns number of channels as Stereo. + // So we do not assert for number of audio channels for AAC_LC + if ( audioCodecType != MediaProperties.ACODEC_AAC_LC ) { + assertEquals("Audio Channels " + aT.getAudioChannels(), audioChannel, + aT.getAudioChannels()); + } } protected void validateImageProperties(int aspectRatio, int fileType, diff --git a/nfc-extras/java/com/android/nfc_extras/NfcAdapterExtras.java b/nfc-extras/java/com/android/nfc_extras/NfcAdapterExtras.java index e0c38b1..99cbb86 100644 --- a/nfc-extras/java/com/android/nfc_extras/NfcAdapterExtras.java +++ b/nfc-extras/java/com/android/nfc_extras/NfcAdapterExtras.java @@ -67,7 +67,11 @@ public final class NfcAdapterExtras { /** get service handles */ private static void initService() { - sService = sAdapter.getNfcAdapterExtrasInterface(); + final INfcAdapterExtras service = sAdapter.getNfcAdapterExtrasInterface(); + if (service != null) { + // Leave stale rather than receive a null value. + sService = service; + } } /** @@ -84,18 +88,19 @@ public final class NfcAdapterExtras { if (sSingleton == null) { try { sAdapter = adapter; - sRouteOff = new CardEmulationRoute(CardEmulationRoute.ROUTE_OFF, null); sSingleton = new NfcAdapterExtras(); sEmbeddedEe = new NfcExecutionEnvironment(sSingleton); + sRouteOff = new CardEmulationRoute(CardEmulationRoute.ROUTE_OFF, null); sRouteOnWhenScreenOn = new CardEmulationRoute( CardEmulationRoute.ROUTE_ON_WHEN_SCREEN_ON, sEmbeddedEe); initService(); } finally { - if (sSingleton == null) { - sService = null; - sEmbeddedEe = null; - sRouteOff = null; + if (sService == null) { sRouteOnWhenScreenOn = null; + sRouteOff = null; + sEmbeddedEe = null; + sSingleton = null; + sAdapter = null; } } } diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 26ea225..d32df6e 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -30,6 +30,15 @@ <service android:name=".screenshot.TakeScreenshotService" android:exported="false" /> + <service android:name=".LoadAverageService" + android:exported="true" /> + + <receiver android:name=".BootReceiver" > + <intent-filter> + <action android:name="android.intent.action.BOOT_COMPLETED" /> + </intent-filter> + </receiver> + <activity android:name=".usb.UsbStorageActivity" android:excludeFromRecents="true"> </activity> diff --git a/packages/SystemUI/src/com/android/systemui/BootReceiver.java b/packages/SystemUI/src/com/android/systemui/BootReceiver.java new file mode 100644 index 0000000..de005aa --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/BootReceiver.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui; + +import android.content.BroadcastReceiver; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.provider.Settings; +import android.util.Slog; + +/** + * Performs a number of miscellaneous, non-system-critical actions + * after the system has finished booting. + */ +public class BootReceiver extends BroadcastReceiver { + private static final String TAG = "SystemUIBootReceiver"; + + @Override + public void onReceive(final Context context, Intent intent) { + try { + // Start the load average overlay, if activated + ContentResolver res = context.getContentResolver(); + if (Settings.System.getInt(res, Settings.System.SHOW_PROCESSES, 0) != 0) { + Intent loadavg = new Intent(context, com.android.systemui.LoadAverageService.class); + context.startService(loadavg); + } + } catch (Exception e) { + Slog.e(TAG, "Can't start load average service", e); + } + } +} diff --git a/services/java/com/android/server/LoadAverageService.java b/packages/SystemUI/src/com/android/systemui/LoadAverageService.java index e05b570..67dc3cd 100644 --- a/services/java/com/android/server/LoadAverageService.java +++ b/packages/SystemUI/src/com/android/systemui/LoadAverageService.java @@ -14,7 +14,9 @@ * limitations under the License. */ -package com.android.server; +package com.android.systemui; + +import com.android.internal.os.ProcessStats; import android.app.Service; import android.content.Context; diff --git a/packages/VpnDialogs/AndroidManifest.xml b/packages/VpnDialogs/AndroidManifest.xml index c0b0a08..8554b77 100644 --- a/packages/VpnDialogs/AndroidManifest.xml +++ b/packages/VpnDialogs/AndroidManifest.xml @@ -4,6 +4,7 @@ <application android:label="VpnDialogs"> <activity android:name=".ConfirmDialog" + android:permission="android.permission.VPN" android:theme="@style/transparent"> <intent-filter> <action android:name="android.intent.action.MAIN"/> diff --git a/packages/VpnDialogs/res/layout/confirm.xml b/packages/VpnDialogs/res/layout/confirm.xml index 5ab6ee2..11a247a 100644 --- a/packages/VpnDialogs/res/layout/confirm.xml +++ b/packages/VpnDialogs/res/layout/confirm.xml @@ -18,7 +18,7 @@ <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:padding="5mm"> + android:padding="3mm"> <ImageView android:id="@+id/icon" android:layout_width="@android:dimen/app_icon_size" diff --git a/packages/VpnDialogs/res/layout/manage.xml b/packages/VpnDialogs/res/layout/manage.xml index 330b8e3..3dcbb46 100644 --- a/packages/VpnDialogs/res/layout/manage.xml +++ b/packages/VpnDialogs/res/layout/manage.xml @@ -18,6 +18,7 @@ <TableLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:padding="3mm" android:stretchColumns="0,1"> <TableRow> diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java index 21e916b..40c0a02 100644 --- a/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java +++ b/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java @@ -58,6 +58,13 @@ public class ManageDialog extends Activity implements Handler.Callback, @Override protected void onResume() { super.onResume(); + + if (getCallingPackage() != null) { + Log.e(TAG, getCallingPackage() + " cannot start this activity"); + finish(); + return; + } + try { mConfig = getIntent().getParcelableExtra("config"); @@ -83,7 +90,6 @@ public class ManageDialog extends Activity implements Handler.Callback, } else { PackageManager pm = getPackageManager(); ApplicationInfo app = pm.getApplicationInfo(mConfig.packagz, 0); - mDialog = new AlertDialog.Builder(this) .setIcon(app.loadIcon(pm)) .setTitle(app.loadLabel(pm)) diff --git a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java index 8ba235b..935f4ad 100644 --- a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java +++ b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java @@ -689,13 +689,11 @@ public class KeyguardViewMediator implements KeyguardViewCallback, switch (simState) { case ABSENT: - case PERM_DISABLED: // only force lock screen in case of missing sim if user hasn't // gone through setup wizard if (!mUpdateMonitor.isDeviceProvisioned()) { if (!isShowing()) { - if (DEBUG) Log.d(TAG, "INTENT_VALUE_ICC_ABSENT " - + "or PERM_DISABLED and keygaurd isn't showing," + if (DEBUG) Log.d(TAG, "ICC_ABSENT isn't showing," + " we need to show the keyguard since the " + "device isn't provisioned yet."); doKeyguard(); @@ -713,7 +711,17 @@ public class KeyguardViewMediator implements KeyguardViewCallback, } else { resetStateLocked(); } - + break; + case PERM_DISABLED: + if (!isShowing()) { + if (DEBUG) Log.d(TAG, "PERM_DISABLED and " + + "keygaurd isn't showing."); + doKeyguard(); + } else { + if (DEBUG) Log.d(TAG, "PERM_DISABLED, resetStateLocked to" + + "show permanently disabled message in lockscreen."); + resetStateLocked(); + } break; case READY: if (isShowing()) { diff --git a/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java b/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java index 75e799c..e177565 100644 --- a/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java +++ b/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java @@ -75,6 +75,7 @@ public class PasswordUnlockScreen extends LinearLayout implements KeyguardScreen private StatusView mStatusView; private final boolean mUseSystemIME = true; // TODO: Make configurable + private boolean mResuming; // used to prevent poking the wakelock during onResume() // To avoid accidental lockout due to events while the device in in the pocket, ignore // any passwords with length less than or equal to this length. @@ -185,7 +186,9 @@ public class PasswordUnlockScreen extends LinearLayout implements KeyguardScreen } public void afterTextChanged(Editable s) { - mCallback.pokeWakelock(); + if (!mResuming) { + mCallback.pokeWakelock(); + } } }); } @@ -208,6 +211,7 @@ public class PasswordUnlockScreen extends LinearLayout implements KeyguardScreen /** {@inheritDoc} */ public void onResume() { + mResuming = true; // reset status mStatusView.resetStatusInfo(mUpdateMonitor, mLockPatternUtils); @@ -222,6 +226,7 @@ public class PasswordUnlockScreen extends LinearLayout implements KeyguardScreen if (deadline != 0) { handleAttemptLockout(deadline); } + mResuming = false; } /** {@inheritDoc} */ diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java index dff0556..4be00c5 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java @@ -2240,7 +2240,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false); - if (st != null && st.menu != null) { + if (st != null && st.menu != null && mFeatureId < 0) { st.menu.close(); } } diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk index 6bb1f56..a0407b9 100644 --- a/services/audioflinger/Android.mk +++ b/services/audioflinger/Android.mk @@ -24,6 +24,7 @@ LOCAL_SHARED_LIBRARIES := \ libdl LOCAL_STATIC_LIBRARIES := \ + libcpustats \ libmedia_helper LOCAL_MODULE:= libaudioflinger diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index daf94f2..86d4cc3 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -52,6 +52,9 @@ #include <media/EffectsFactoryApi.h> #include <audio_effects/effect_visualizer.h> +#include <cpustats/ThreadCpuUsage.h> +// #define DEBUG_CPU_USAGE 10 // log statistics every n wall clock seconds + // ---------------------------------------------------------------------------- @@ -1529,9 +1532,40 @@ bool AudioFlinger::MixerThread::threadLoop() uint32_t idleSleepTime = idleSleepTimeUs(); uint32_t sleepTime = idleSleepTime; Vector< sp<EffectChain> > effectChains; +#ifdef DEBUG_CPU_USAGE + ThreadCpuUsage cpu; + const CentralTendencyStatistics& stats = cpu.statistics(); +#endif while (!exitPending()) { +#ifdef DEBUG_CPU_USAGE + cpu.sampleAndEnable(); + unsigned n = stats.n(); + // cpu.elapsed() is expensive, so don't call it every loop + if ((n & 127) == 1) { + long long elapsed = cpu.elapsed(); + if (elapsed >= DEBUG_CPU_USAGE * 1000000000LL) { + double perLoop = elapsed / (double) n; + double perLoop100 = perLoop * 0.01; + double mean = stats.mean(); + double stddev = stats.stddev(); + double minimum = stats.minimum(); + double maximum = stats.maximum(); + cpu.resetStatistics(); + LOGI("CPU usage over past %.1f secs (%u mixer loops at %.1f mean ms per loop):\n us per mix loop: mean=%.0f stddev=%.0f min=%.0f max=%.0f\n %% of wall: mean=%.1f stddev=%.1f min=%.1f max=%.1f", + elapsed * .000000001, n, perLoop * .000001, + mean * .001, + stddev * .001, + minimum * .001, + maximum * .001, + mean / perLoop100, + stddev / perLoop100, + minimum / perLoop100, + maximum / perLoop100); + } + } +#endif processConfigEvents(); mixerStatus = MIXER_IDLE; diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp index 49cb864..b2fbcb1 100644 --- a/services/input/InputReader.cpp +++ b/services/input/InputReader.cpp @@ -181,25 +181,6 @@ static bool isPointerDown(int32_t buttonState) { | AMOTION_EVENT_BUTTON_TERTIARY); } -static int32_t calculateEdgeFlagsUsingPointerBounds( - const sp<PointerControllerInterface>& pointerController, float x, float y) { - int32_t edgeFlags = 0; - float minX, minY, maxX, maxY; - if (pointerController->getBounds(&minX, &minY, &maxX, &maxY)) { - if (x <= minX) { - edgeFlags |= AMOTION_EVENT_EDGE_FLAG_LEFT; - } else if (x >= maxX) { - edgeFlags |= AMOTION_EVENT_EDGE_FLAG_RIGHT; - } - if (y <= minY) { - edgeFlags |= AMOTION_EVENT_EDGE_FLAG_TOP; - } else if (y >= maxY) { - edgeFlags |= AMOTION_EVENT_EDGE_FLAG_BOTTOM; - } - } - return edgeFlags; -} - static float calculateCommonVector(float a, float b) { if (a > 0 && b > 0) { return a < b ? a : b; @@ -1619,7 +1600,6 @@ void CursorInputMapper::sync(nsecs_t when) { } int32_t motionEventAction; - int32_t motionEventEdgeFlags; int32_t lastButtonState, currentButtonState; PointerProperties pointerProperties; PointerCoords pointerCoords; @@ -1697,8 +1677,6 @@ void CursorInputMapper::sync(nsecs_t when) { } } - motionEventEdgeFlags = AMOTION_EVENT_EDGE_FLAG_NONE; - pointerProperties.clear(); pointerProperties.id = 0; pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_MOUSE; @@ -1742,11 +1720,6 @@ void CursorInputMapper::sync(nsecs_t when) { mPointerController->getPosition(&x, &y); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y); - - if (motionEventAction == AMOTION_EVENT_ACTION_DOWN) { - motionEventEdgeFlags = calculateEdgeFlagsUsingPointerBounds( - mPointerController, x, y); - } } else { pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, deltaX); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, deltaY); @@ -1771,7 +1744,7 @@ void CursorInputMapper::sync(nsecs_t when) { // Send motion event. int32_t metaState = mContext->getGlobalMetaState(); getDispatcher()->notifyMotion(when, getDeviceId(), mSource, policyFlags, - motionEventAction, 0, metaState, currentButtonState, motionEventEdgeFlags, + motionEventAction, 0, metaState, currentButtonState, 0, 1, &pointerProperties, &pointerCoords, mXPrecision, mYPrecision, downTime); // Send hover move after UP to tell the application that the mouse is hovering now. @@ -3168,9 +3141,8 @@ void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) { } // Update current touch coordinates. - int32_t edgeFlags; float xPrecision, yPrecision; - prepareTouches(&edgeFlags, &xPrecision, &yPrecision); + prepareTouches(&xPrecision, &yPrecision); // Dispatch motions. BitSet32 currentIdBits = mCurrentTouch.idBits; @@ -3239,13 +3211,10 @@ void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) { if (dispatchedIdBits.count() == 1) { // First pointer is going down. Set down time. mDownTime = when; - } else { - // Only send edge flags with first pointer down. - edgeFlags = AMOTION_EVENT_EDGE_FLAG_NONE; } dispatchMotion(when, policyFlags, mTouchSource, - AMOTION_EVENT_ACTION_POINTER_DOWN, 0, metaState, buttonState, edgeFlags, + AMOTION_EVENT_ACTION_POINTER_DOWN, 0, metaState, buttonState, 0, mCurrentTouchProperties, mCurrentTouchCoords, mCurrentTouch.idToIndex, dispatchedIdBits, downId, xPrecision, yPrecision, mDownTime); @@ -3259,8 +3228,7 @@ void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) { } } -void TouchInputMapper::prepareTouches(int32_t* outEdgeFlags, - float* outXPrecision, float* outYPrecision) { +void TouchInputMapper::prepareTouches(float* outXPrecision, float* outYPrecision) { uint32_t currentPointerCount = mCurrentTouch.pointerCount; uint32_t lastPointerCount = mLastTouch.pointerCount; @@ -3471,28 +3439,6 @@ void TouchInputMapper::prepareTouches(int32_t* outEdgeFlags, properties.toolType = getTouchToolType(mCurrentTouch.pointers[i].isStylus); } - // Check edge flags by looking only at the first pointer since the flags are - // global to the event. - *outEdgeFlags = AMOTION_EVENT_EDGE_FLAG_NONE; - if (lastPointerCount == 0 && currentPointerCount > 0) { - const PointerData& in = mCurrentTouch.pointers[0]; - - if (in.x <= mRawAxes.x.minValue) { - *outEdgeFlags |= rotateEdgeFlag(AMOTION_EVENT_EDGE_FLAG_LEFT, - mLocked.surfaceOrientation); - } else if (in.x >= mRawAxes.x.maxValue) { - *outEdgeFlags |= rotateEdgeFlag(AMOTION_EVENT_EDGE_FLAG_RIGHT, - mLocked.surfaceOrientation); - } - if (in.y <= mRawAxes.y.minValue) { - *outEdgeFlags |= rotateEdgeFlag(AMOTION_EVENT_EDGE_FLAG_TOP, - mLocked.surfaceOrientation); - } else if (in.y >= mRawAxes.y.maxValue) { - *outEdgeFlags |= rotateEdgeFlag(AMOTION_EVENT_EDGE_FLAG_BOTTOM, - mLocked.surfaceOrientation); - } - } - *outXPrecision = mLocked.orientedXPrecision; *outYPrecision = mLocked.orientedYPrecision; } @@ -3640,19 +3586,12 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlag downGestureIdBits.clearBit(id); dispatchedGestureIdBits.markBit(id); - int32_t edgeFlags = AMOTION_EVENT_EDGE_FLAG_NONE; if (dispatchedGestureIdBits.count() == 1) { - // First pointer is going down. Calculate edge flags and set down time. - uint32_t index = mPointerGesture.currentGestureIdToIndex[id]; - const PointerCoords& downCoords = mPointerGesture.currentGestureCoords[index]; - edgeFlags = calculateEdgeFlagsUsingPointerBounds(mPointerController, - downCoords.getAxisValue(AMOTION_EVENT_AXIS_X), - downCoords.getAxisValue(AMOTION_EVENT_AXIS_Y)); mPointerGesture.downTime = when; } dispatchMotion(when, policyFlags, mPointerSource, - AMOTION_EVENT_ACTION_POINTER_DOWN, 0, metaState, buttonState, edgeFlags, + AMOTION_EVENT_ACTION_POINTER_DOWN, 0, metaState, buttonState, 0, mPointerGesture.currentGestureProperties, mPointerGesture.currentGestureCoords, mPointerGesture.currentGestureIdToIndex, dispatchedGestureIdBits, id, diff --git a/services/input/InputReader.h b/services/input/InputReader.h index 69fa6b4..b1fdcf2 100644 --- a/services/input/InputReader.h +++ b/services/input/InputReader.h @@ -1176,7 +1176,7 @@ private: TouchResult consumeOffScreenTouches(nsecs_t when, uint32_t policyFlags); void dispatchTouches(nsecs_t when, uint32_t policyFlags); - void prepareTouches(int32_t* outEdgeFlags, float* outXPrecision, float* outYPrecision); + void prepareTouches(float* outXPrecision, float* outYPrecision); void dispatchPointerGestures(nsecs_t when, uint32_t policyFlags, bool isTimeout); bool preparePointerGestures(nsecs_t when, bool* outCancelPreviousGesture, bool* outFinishPreviousGesture, bool isTimeout); diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java index 168b894..e9e66cb 100644 --- a/services/java/com/android/server/BackupManagerService.java +++ b/services/java/com/android/server/BackupManagerService.java @@ -87,8 +87,10 @@ import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.io.RandomAccessFile; +import java.io.UnsupportedEncodingException; import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.HashSet; @@ -109,6 +111,8 @@ class BackupManagerService extends IBackupManager.Stub { // Name and current contents version of the full-backup manifest file static final String BACKUP_MANIFEST_FILENAME = "_manifest"; static final int BACKUP_MANIFEST_VERSION = 1; + static final String BACKUP_FILE_HEADER_MAGIC = "ANDROID BACKUP\n"; + static final int BACKUP_FILE_VERSION = 1; // How often we perform a backup pass. Privileged external callers can // trigger an immediate pass. @@ -1791,16 +1795,42 @@ class BackupManagerService extends IBackupManager.Stub { } } - // Set up the compression stage FileOutputStream ofstream = new FileOutputStream(mOutputFile.getFileDescriptor()); + + // Set up the compression stage Deflater deflater = new Deflater(Deflater.BEST_COMPRESSION); DeflaterOutputStream out = new DeflaterOutputStream(ofstream, deflater, true); - // !!! TODO: if using encryption, set up the encryption stage - // and emit the tar header stating the password salt. - PackageInfo pkg = null; try { + + // !!! TODO: if using encryption, set up the encryption stage + // and emit the tar header stating the password salt. + + // Write the global file header. All strings are UTF-8 encoded; lines end + // with a '\n' byte. Actual backup data begins immediately following the + // final '\n'. + // + // line 1: "ANDROID BACKUP" + // line 2: backup file format version, currently "1" + // line 3: compressed? "0" if not compressed, "1" if compressed. + // line 4: encryption salt? "-" if not encrypted, otherwise this + // line contains the encryption salt with which the user- + // supplied password is to be expanded, in hexadecimal. + StringBuffer headerbuf = new StringBuffer(256); + // !!! TODO: programmatically build the compressed / encryption salt fields + headerbuf.append(BACKUP_FILE_HEADER_MAGIC); + headerbuf.append("1\n1\n-\n"); + + try { + byte[] header = headerbuf.toString().getBytes("UTF-8"); + ofstream.write(header); + } catch (Exception e) { + // Should never happen! + Slog.e(TAG, "Unable to emit archive header", e); + return; + } + // Now back up the app data via the agent mechanism int N = packagesToBackup.size(); for (int i = 0; i < N; i++) { @@ -2176,7 +2206,46 @@ class BackupManagerService extends IBackupManager.Stub { mBytes = 0; byte[] buffer = new byte[32 * 1024]; FileInputStream rawInStream = new FileInputStream(mInputFile.getFileDescriptor()); - InflaterInputStream in = new InflaterInputStream(rawInStream); + + // First, parse out the unencrypted/uncompressed header + boolean compressed = false; + boolean encrypted = false; + final InputStream in; + + boolean okay = false; + final int headerLen = BACKUP_FILE_HEADER_MAGIC.length(); + byte[] streamHeader = new byte[headerLen]; + try { + int got; + if ((got = rawInStream.read(streamHeader, 0, headerLen)) == headerLen) { + byte[] magicBytes = BACKUP_FILE_HEADER_MAGIC.getBytes("UTF-8"); + if (Arrays.equals(magicBytes, streamHeader)) { + // okay, header looks good. now parse out the rest of the fields. + String s = readHeaderLine(rawInStream); + if (Integer.parseInt(s) == BACKUP_FILE_VERSION) { + // okay, it's a version we recognize + s = readHeaderLine(rawInStream); + compressed = (Integer.parseInt(s) != 0); + s = readHeaderLine(rawInStream); + if (!s.startsWith("-")) { + encrypted = true; + // TODO: parse out the salt here and process with the user pw + } + okay = true; + } else Slog.e(TAG, "Wrong header version: " + s); + } else Slog.e(TAG, "Didn't read the right header magic"); + } else Slog.e(TAG, "Only read " + got + " bytes of header"); + } catch (NumberFormatException e) { + Slog.e(TAG, "Can't parse restore data header"); + } + + if (!okay) { + Slog.e(TAG, "Invalid restore data; aborting."); + return; + } + + // okay, use the right stream layer based on compression + in = (compressed) ? new InflaterInputStream(rawInStream) : rawInStream; boolean didRestore; do { @@ -2184,6 +2253,8 @@ class BackupManagerService extends IBackupManager.Stub { } while (didRestore); if (DEBUG) Slog.v(TAG, "Done consuming input tarfile, total bytes=" + mBytes); + } catch (IOException e) { + Slog.e(TAG, "Unable to read restore input"); } finally { tearDownPipes(); tearDownAgent(mTargetApp); @@ -2207,6 +2278,16 @@ class BackupManagerService extends IBackupManager.Stub { } } + String readHeaderLine(InputStream in) throws IOException { + int c; + StringBuffer buffer = new StringBuffer(80); + while ((c = in.read()) >= 0) { + if (c == '\n') break; // consume and discard the newlines + buffer.append((char)c); + } + return buffer.toString(); + } + boolean restoreOneFile(InputStream instream, byte[] buffer) { FileMetadata info; try { diff --git a/services/java/com/android/server/BootReceiver.java b/services/java/com/android/server/BootReceiver.java index b9ff8d0..6665614 100644 --- a/services/java/com/android/server/BootReceiver.java +++ b/services/java/com/android/server/BootReceiver.java @@ -17,7 +17,6 @@ package com.android.server; import android.content.BroadcastReceiver; -import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; @@ -28,7 +27,6 @@ import android.os.FileObserver; import android.os.FileUtils; import android.os.RecoverySystem; import android.os.SystemProperties; -import android.provider.Settings; import android.util.Slog; import java.io.File; @@ -59,17 +57,6 @@ public class BootReceiver extends BroadcastReceiver { @Override public void onReceive(final Context context, Intent intent) { - try { - // Start the load average overlay, if activated - ContentResolver res = context.getContentResolver(); - if (Settings.System.getInt(res, Settings.System.SHOW_PROCESSES, 0) != 0) { - Intent loadavg = new Intent(context, com.android.server.LoadAverageService.class); - context.startService(loadavg); - } - } catch (Exception e) { - Slog.e(TAG, "Can't start load average service", e); - } - // Log boot events in the background to avoid blocking the main thread with I/O new Thread() { @Override diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java index 85891a2..55c92e8 100644 --- a/services/java/com/android/server/ConnectivityService.java +++ b/services/java/com/android/server/ConnectivityService.java @@ -35,7 +35,7 @@ import android.net.INetworkPolicyListener; import android.net.INetworkPolicyManager; import android.net.LinkAddress; import android.net.LinkProperties; -import android.net.LinkProperties.CompareAddressesResult; +import android.net.LinkProperties.CompareResult; import android.net.MobileDataStateTracker; import android.net.NetworkConfig; import android.net.NetworkInfo; @@ -260,6 +260,10 @@ public class ConnectivityService extends IConnectivityManager.Stub { private InetAddress mDefaultDns; + // this collection is used to refcount the added routes - if there are none left + // it's time to remove the route from the route table + private Collection<RouteInfo> mAddedRoutes = new ArrayList<RouteInfo>(); + // used in DBG mode to track inet condition reports private static final int INET_CONDITION_LOG_MAX_SIZE = 15; private ArrayList mInetLog; @@ -479,7 +483,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { mNetConfigs[netType].radio); continue; } - mCurrentLinkProperties[netType] = mNetTrackers[netType].getLinkProperties(); + mCurrentLinkProperties[netType] = null; } IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); @@ -1053,62 +1057,71 @@ public class ConnectivityService extends IConnectivityManager.Stub { } try { InetAddress addr = InetAddress.getByAddress(hostAddress); - return addHostRoute(tracker, addr, 0); + LinkProperties lp = tracker.getLinkProperties(); + return addRoute(lp, RouteInfo.makeHostRoute(addr)); } catch (UnknownHostException e) {} return false; } - /** - * Ensure that a network route exists to deliver traffic to the specified - * host via the mobile data network. - * @param hostAddress the IP address of the host to which the route is desired, - * in network byte order. - * TODO - deprecate - * @return {@code true} on success, {@code false} on failure - */ - private boolean addHostRoute(NetworkStateTracker nt, InetAddress hostAddress, int cycleCount) { - LinkProperties lp = nt.getLinkProperties(); - if ((lp == null) || (hostAddress == null)) return false; + private boolean addRoute(LinkProperties p, RouteInfo r) { + return modifyRoute(p.getInterfaceName(), p, r, 0, true); + } - String interfaceName = lp.getInterfaceName(); - if (DBG) { - log("Requested host route to " + hostAddress + "(" + interfaceName + "), cycleCount=" + - cycleCount); - } - if (interfaceName == null) { - if (DBG) loge("addHostRoute failed due to null interface name"); + private boolean removeRoute(LinkProperties p, RouteInfo r) { + return modifyRoute(p.getInterfaceName(), p, r, 0, false); + } + + private boolean modifyRoute(String ifaceName, LinkProperties lp, RouteInfo r, int cycleCount, + boolean doAdd) { + if ((ifaceName == null) || (lp == null) || (r == null)) return false; + + if (cycleCount > MAX_HOSTROUTE_CYCLE_COUNT) { + loge("Error adding route - too much recursion"); return false; } - RouteInfo bestRoute = RouteInfo.selectBestRoute(lp.getRoutes(), hostAddress); - InetAddress gatewayAddress = null; - if (bestRoute != null) { - gatewayAddress = bestRoute.getGateway(); - // if the best route is ourself, don't relf-reference, just add the host route - if (hostAddress.equals(gatewayAddress)) gatewayAddress = null; + if (r.isHostRoute() == false) { + RouteInfo bestRoute = RouteInfo.selectBestRoute(lp.getRoutes(), r.getGateway()); + if (bestRoute != null) { + if (bestRoute.getGateway().equals(r.getGateway())) { + // if there is no better route, add the implied hostroute for our gateway + bestRoute = RouteInfo.makeHostRoute(r.getGateway()); + } else { + // if we will connect to our gateway through another route, add a direct + // route to it's gateway + bestRoute = RouteInfo.makeHostRoute(r.getGateway(), bestRoute.getGateway()); + } + modifyRoute(ifaceName, lp, bestRoute, cycleCount+1, doAdd); + } } - if (gatewayAddress != null) { - if (cycleCount > MAX_HOSTROUTE_CYCLE_COUNT) { - loge("Error adding hostroute - too much recursion"); + if (doAdd) { + if (DBG) log("Adding " + r + " for interface " + ifaceName); + mAddedRoutes.add(r); + try { + mNetd.addRoute(ifaceName, r); + } catch (Exception e) { + // never crash - catch them all + loge("Exception trying to add a route: " + e); return false; } - if (!addHostRoute(nt, gatewayAddress, cycleCount+1)) return false; - } - - RouteInfo route = RouteInfo.makeHostRoute(hostAddress, gatewayAddress); - - try { - mNetd.addRoute(interfaceName, route); - return true; - } catch (Exception ex) { - return false; + } else { + // if we remove this one and there are no more like it, then refcount==0 and + // we can remove it from the table + mAddedRoutes.remove(r); + if (mAddedRoutes.contains(r) == false) { + if (DBG) log("Removing " + r + " for interface " + ifaceName); + try { + mNetd.removeRoute(ifaceName, r); + } catch (Exception e) { + // never crash - catch them all + loge("Exception trying to remove a route: " + e); + return false; + } + } else { + if (DBG) log("not removing " + r + " as it's still in use"); + } } - } - - // TODO support the removal of single host routes. Keep a ref count of them so we - // aren't over-zealous - private boolean removeHostRoute(NetworkStateTracker nt, InetAddress hostAddress) { - return false; + return true; } /** @@ -1157,9 +1170,6 @@ public class ConnectivityService extends IConnectivityManager.Stub { public void setDataDependency(int networkType, boolean met) { enforceConnectivityInternalPermission(); - if (DBG) { - log("setDataDependency(" + networkType + ", " + met + ")"); - } mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_DEPENDENCY_MET, (met ? ENABLED : DISABLED), networkType)); } @@ -1583,10 +1593,11 @@ public class ConnectivityService extends IConnectivityManager.Stub { */ handleDnsConfigurationChange(netType); + LinkProperties curLp = mCurrentLinkProperties[netType]; + LinkProperties newLp = null; + if (mNetTrackers[netType].getNetworkInfo().isConnected()) { - LinkProperties newLp = mNetTrackers[netType].getLinkProperties(); - LinkProperties curLp = mCurrentLinkProperties[netType]; - mCurrentLinkProperties[netType] = newLp; + newLp = mNetTrackers[netType].getLinkProperties(); if (VDBG) { log("handleConnectivityChange: changed linkProperty[" + netType + "]:" + " doReset=" + doReset + " resetMask=" + resetMask + @@ -1594,61 +1605,50 @@ public class ConnectivityService extends IConnectivityManager.Stub { "\n newLp=" + newLp); } - if (curLp.isIdenticalInterfaceName(newLp)) { - CompareAddressesResult car = curLp.compareAddresses(newLp); - if ((car.removed.size() != 0) || (car.added.size() != 0)) { - for (LinkAddress linkAddr : car.removed) { - if (linkAddr.getAddress() instanceof Inet4Address) { - resetMask |= NetworkUtils.RESET_IPV4_ADDRESSES; + if (curLp != null) { + if (curLp.isIdenticalInterfaceName(newLp)) { + CompareResult<LinkAddress> car = curLp.compareAddresses(newLp); + if ((car.removed.size() != 0) || (car.added.size() != 0)) { + for (LinkAddress linkAddr : car.removed) { + if (linkAddr.getAddress() instanceof Inet4Address) { + resetMask |= NetworkUtils.RESET_IPV4_ADDRESSES; + } + if (linkAddr.getAddress() instanceof Inet6Address) { + resetMask |= NetworkUtils.RESET_IPV6_ADDRESSES; + } + } + if (DBG) { + log("handleConnectivityChange: addresses changed" + + " linkProperty[" + netType + "]:" + " resetMask=" + resetMask + + "\n car=" + car); } - if (linkAddr.getAddress() instanceof Inet6Address) { - resetMask |= NetworkUtils.RESET_IPV6_ADDRESSES; + } else { + if (DBG) { + log("handleConnectivityChange: address are the same reset per doReset" + + " linkProperty[" + netType + "]:" + + " resetMask=" + resetMask); } } - if (DBG) { - log("handleConnectivityChange: addresses changed" + - " linkProperty[" + netType + "]:" + " resetMask=" + resetMask + - "\n car=" + car); - } } else { - if (DBG) { - log("handleConnectivityChange: address are the same reset per doReset" + - " linkProperty[" + netType + "]:" + - " resetMask=" + resetMask); - } + resetMask = NetworkUtils.RESET_ALL_ADDRESSES; + log("handleConnectivityChange: interface not not equivalent reset both" + + " linkProperty[" + netType + "]:" + + " resetMask=" + resetMask); } - } else { - resetMask = NetworkUtils.RESET_ALL_ADDRESSES; - log("handleConnectivityChange: interface not not equivalent reset both" + - " linkProperty[" + netType + "]:" + - " resetMask=" + resetMask); } if (mNetConfigs[netType].isDefault()) { handleApplyDefaultProxy(netType); - addDefaultRoute(mNetTrackers[netType]); - } else { - // many radios add a default route even when we don't want one. - // remove the default route unless we need it for our active network - if (mActiveDefaultNetwork != -1) { - LinkProperties defaultLinkProperties = - mNetTrackers[mActiveDefaultNetwork].getLinkProperties(); - LinkProperties newLinkProperties = - mNetTrackers[netType].getLinkProperties(); - String defaultIface = defaultLinkProperties.getInterfaceName(); - if (defaultIface != null && - !defaultIface.equals(newLinkProperties.getInterfaceName())) { - removeDefaultRoute(mNetTrackers[netType]); - } - } - addPrivateDnsRoutes(mNetTrackers[netType]); } } else { - if (mNetConfigs[netType].isDefault()) { - removeDefaultRoute(mNetTrackers[netType]); - } else { - removePrivateDnsRoutes(mNetTrackers[netType]); + if (VDBG) { + log("handleConnectivityChange: changed linkProperty[" + netType + "]:" + + " doReset=" + doReset + " resetMask=" + resetMask + + "\n curLp=" + curLp + + "\n newLp= null"); } } + mCurrentLinkProperties[netType] = newLp; + updateRoutes(newLp, curLp, mNetConfigs[netType].isDefault()); if (doReset || resetMask != 0) { LinkProperties linkProperties = mNetTrackers[netType].getLinkProperties(); @@ -1672,108 +1672,64 @@ public class ConnectivityService extends IConnectivityManager.Stub { } } - private void addPrivateDnsRoutes(NetworkStateTracker nt) { - boolean privateDnsRouteSet = nt.isPrivateDnsRouteSet(); - LinkProperties p = nt.getLinkProperties(); - if (p == null) return; - String interfaceName = p.getInterfaceName(); - - if (DBG) { - log("addPrivateDnsRoutes for " + nt + - "(" + interfaceName + ") - mPrivateDnsRouteSet = " + privateDnsRouteSet); - } - if (interfaceName != null && !privateDnsRouteSet) { - Collection<InetAddress> dnsList = p.getDnses(); - for (InetAddress dns : dnsList) { - addHostRoute(nt, dns, 0); + /** + * Add and remove routes using the old properties (null if not previously connected), + * new properties (null if becoming disconnected). May even be double null, which + * is a noop. + * Uses isLinkDefault to determine if default routes should be set or conversely if + * host routes should be set to the dns servers + */ + private void updateRoutes(LinkProperties newLp, LinkProperties curLp, boolean isLinkDefault) { + Collection<RouteInfo> routesToAdd = null; + CompareResult<InetAddress> dnsDiff = null; + + if (curLp != null) { + // check for the delta between the current set and the new + CompareResult<RouteInfo> routeDiff = curLp.compareRoutes(newLp); + dnsDiff = curLp.compareDnses(newLp); + + for (RouteInfo r : routeDiff.removed) { + if (isLinkDefault || ! r.isDefaultRoute()) { + removeRoute(curLp, r); + } } - nt.privateDnsRouteSet(true); + routesToAdd = routeDiff.added; } - } - private void removePrivateDnsRoutes(NetworkStateTracker nt) { - LinkProperties p = nt.getLinkProperties(); - if (p == null) return; - String interfaceName = p.getInterfaceName(); - boolean privateDnsRouteSet = nt.isPrivateDnsRouteSet(); - if (interfaceName != null && privateDnsRouteSet) { - if (DBG) { - log("removePrivateDnsRoutes for " + nt.getNetworkInfo().getTypeName() + - " (" + interfaceName + ")"); + if (newLp != null) { + // if we didn't get a diff from cur -> new, then just use the new + if (routesToAdd == null) { + routesToAdd = newLp.getRoutes(); } - Collection<InetAddress> dnsList = p.getDnses(); - for (InetAddress dns : dnsList) { - if (DBG) log(" removing " + dns); - RouteInfo route = RouteInfo.makeHostRoute(dns); - try { - mNetd.removeRoute(interfaceName, route); - } catch (Exception ex) { - loge("error (" + ex + ") removing dns route " + route); + for (RouteInfo r : routesToAdd) { + if (isLinkDefault || ! r.isDefaultRoute()) { + addRoute(newLp, r); } } - nt.privateDnsRouteSet(false); } - } - - - private void addDefaultRoute(NetworkStateTracker nt) { - LinkProperties p = nt.getLinkProperties(); - if (p == null) return; - String interfaceName = p.getInterfaceName(); - if (TextUtils.isEmpty(interfaceName)) return; - for (RouteInfo route : p.getRoutes()) { - //TODO - handle non-default routes - if (route.isDefaultRoute()) { - if (DBG) log("adding default route " + route); - InetAddress gateway = route.getGateway(); - if (addHostRoute(nt, gateway, 0)) { - try { - mNetd.addRoute(interfaceName, route); - } catch (Exception e) { - loge("error adding default route " + route); - continue; - } - if (DBG) { - NetworkInfo networkInfo = nt.getNetworkInfo(); - log("addDefaultRoute for " + networkInfo.getTypeName() + - " (" + interfaceName + "), GatewayAddr=" + - gateway.getHostAddress()); - } - } else { - loge("error adding host route for default route " + route); + if (!isLinkDefault) { + // handle DNS routes + Collection<InetAddress> dnsToAdd = null; + if (dnsDiff != null) { + dnsToAdd = dnsDiff.added; + for (InetAddress dnsAddress : dnsDiff.removed) { + removeRoute(curLp, RouteInfo.makeHostRoute(dnsAddress)); } } - } - } - - - public void removeDefaultRoute(NetworkStateTracker nt) { - LinkProperties p = nt.getLinkProperties(); - if (p == null) return; - String interfaceName = p.getInterfaceName(); - - if (interfaceName == null) return; - - for (RouteInfo route : p.getRoutes()) { - //TODO - handle non-default routes - if (route.isDefaultRoute()) { - try { - mNetd.removeRoute(interfaceName, route); - } catch (Exception ex) { - loge("error (" + ex + ") removing default route " + route); - continue; + if (newLp != null) { + if (dnsToAdd == null) { + dnsToAdd = newLp.getDnses(); } - if (DBG) { - NetworkInfo networkInfo = nt.getNetworkInfo(); - log("removeDefaultRoute for " + networkInfo.getTypeName() + " (" + - interfaceName + ")"); + for(InetAddress dnsAddress : dnsToAdd) { + addRoute(newLp, RouteInfo.makeHostRoute(dnsAddress)); } } } } + /** * Reads the network specific TCP buffer sizes from SystemProperties * net.tcp.buffersize.[default|wifi|umts|edge|gprs] and set them for system @@ -2528,8 +2484,23 @@ public class ConnectivityService extends IConnectivityManager.Stub { * @hide */ @Override - public void protectVpn(ParcelFileDescriptor socket) { - mVpn.protect(socket, getDefaultInterface()); + public boolean protectVpn(ParcelFileDescriptor socket) { + try { + int type = mActiveDefaultNetwork; + if (ConnectivityManager.isNetworkTypeValid(type)) { + mVpn.protect(socket, mNetTrackers[type].getLinkProperties().getInterfaceName()); + return true; + } + } catch (Exception e) { + // ignore + } finally { + try { + socket.close(); + } catch (Exception e) { + // ignore + } + } + return false; } /** @@ -2577,19 +2548,6 @@ public class ConnectivityService extends IConnectivityManager.Stub { return mVpn.getLegacyVpnInfo(); } - private String getDefaultInterface() { - if (ConnectivityManager.isNetworkTypeValid(mActiveDefaultNetwork)) { - NetworkStateTracker tracker = mNetTrackers[mActiveDefaultNetwork]; - if (tracker != null) { - LinkProperties properties = tracker.getLinkProperties(); - if (properties != null) { - return properties.getInterfaceName(); - } - } - } - throw new IllegalStateException("No default interface"); - } - /** * Callback for VPN subsystem. Currently VPN is not adapted to the service * through NetworkStateTracker since it works differently. For example, it diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java index 5f0922e..7112553 100644 --- a/services/java/com/android/server/WifiService.java +++ b/services/java/com/android/server/WifiService.java @@ -35,8 +35,8 @@ import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.net.wifi.WifiStateMachine; import android.net.wifi.WifiConfiguration; +import android.net.wifi.WifiWatchdogStateMachine; import android.net.wifi.WifiConfiguration.KeyMgmt; -import android.net.wifi.WifiWatchdogService; import android.net.wifi.WpsConfiguration; import android.net.wifi.WpsResult; import android.net.ConnectivityManager; @@ -343,7 +343,7 @@ public class WifiService extends IWifiManager.Stub { * Protected by mWifiStateTracker lock. */ private final WorkSource mTmpWorkSource = new WorkSource(); - private WifiWatchdogService mWifiWatchdogService; + private WifiWatchdogStateMachine mWifiWatchdogStateMachine; WifiService(Context context) { mContext = context; @@ -434,8 +434,9 @@ public class WifiService extends IWifiManager.Stub { (wifiEnabled ? "enabled" : "disabled")); setWifiEnabled(wifiEnabled); - //TODO: as part of WWS refactor, create only when needed - mWifiWatchdogService = new WifiWatchdogService(mContext); + mWifiWatchdogStateMachine = WifiWatchdogStateMachine. + makeWifiWatchdogStateMachine(mContext); + } private boolean testAndClearWifiSavedState() { @@ -1162,8 +1163,8 @@ public class WifiService extends IWifiManager.Stub { mLocks.dump(pw); pw.println(); - pw.println("WifiWatchdogService dump"); - mWifiWatchdogService.dump(pw); + pw.println("WifiWatchdogStateMachine dump"); + mWifiWatchdogStateMachine.dump(pw); } private class WifiLock extends DeathRecipient { diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index fd93bcf..f546cf1 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -18,10 +18,10 @@ package com.android.server.am; import com.android.internal.R; import com.android.internal.os.BatteryStatsImpl; +import com.android.internal.os.ProcessStats; import com.android.server.AttributeCache; import com.android.server.IntentResolver; import com.android.server.ProcessMap; -import com.android.server.ProcessStats; import com.android.server.SystemServer; import com.android.server.Watchdog; import com.android.server.am.ActivityStack.ActivityState; diff --git a/services/java/com/android/server/connectivity/Vpn.java b/services/java/com/android/server/connectivity/Vpn.java index c185012..05e95a7 100644 --- a/services/java/com/android/server/connectivity/Vpn.java +++ b/services/java/com/android/server/connectivity/Vpn.java @@ -41,6 +41,9 @@ import com.android.internal.net.LegacyVpnInfo; import com.android.internal.net.VpnConfig; import com.android.server.ConnectivityService.VpnCallback; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; import java.io.OutputStream; import java.nio.charset.Charsets; import java.util.Arrays; @@ -67,22 +70,14 @@ public class Vpn extends INetworkManagementEventObserver.Stub { /** * Protect a socket from routing changes by binding it to the given - * interface. The socket IS closed by this method. + * interface. The socket is NOT closed by this method. * * @param socket The socket to be bound. * @param name The name of the interface. */ public void protect(ParcelFileDescriptor socket, String interfaze) { - try { - mContext.enforceCallingPermission(VPN, "protect"); - jniProtect(socket.getFd(), interfaze); - } finally { - try { - socket.close(); - } catch (Exception e) { - // ignore - } - } + mContext.enforceCallingPermission(VPN, "protect"); + jniProtect(socket.getFd(), interfaze); } /** @@ -192,10 +187,15 @@ public class Vpn extends INetworkManagementEventObserver.Stub { } // Configure the interface. Abort if any of these steps fails. - ParcelFileDescriptor tun = ParcelFileDescriptor.adoptFd( - jniConfigure(config.mtu, config.addresses, config.routes)); + ParcelFileDescriptor tun = ParcelFileDescriptor.adoptFd(jniCreate(config.mtu)); try { String interfaze = jniGetName(tun.getFd()); + if (jniSetAddresses(interfaze, config.addresses) < 1) { + throw new IllegalArgumentException("At least one address must be specified"); + } + if (config.routes != null) { + jniSetRoutes(interfaze, config.routes); + } if (mInterface != null && !mInterface.equals(interfaze)) { jniReset(mInterface); } @@ -222,15 +222,21 @@ public class Vpn extends INetworkManagementEventObserver.Stub { } // INetworkManagementEventObserver.Stub - public void interfaceStatusChanged(String interfaze, boolean up) { + public void interfaceAdded(String interfaze) { } // INetworkManagementEventObserver.Stub - public void interfaceLinkStateChanged(String interfaze, boolean up) { + public synchronized void interfaceStatusChanged(String interfaze, boolean up) { + if (!up && mLegacyVpnRunner != null) { + mLegacyVpnRunner.check(interfaze); + } } // INetworkManagementEventObserver.Stub - public void interfaceAdded(String interfaze) { + public synchronized void interfaceLinkStateChanged(String interfaze, boolean up) { + if (!up && mLegacyVpnRunner != null) { + mLegacyVpnRunner.check(interfaze); + } } // INetworkManagementEventObserver.Stub @@ -279,8 +285,10 @@ public class Vpn extends INetworkManagementEventObserver.Stub { } } - private native int jniConfigure(int mtu, String addresses, String routes); + private native int jniCreate(int mtu); private native String jniGetName(int tun); + private native int jniSetAddresses(String interfaze, String addresses); + private native int jniSetRoutes(String interfaze, String routes); private native void jniReset(String interfaze); private native int jniCheck(String interfaze); private native void jniProtect(int socket, String interfaze); @@ -323,11 +331,11 @@ public class Vpn extends INetworkManagementEventObserver.Stub { */ private class LegacyVpnRunner extends Thread { private static final String TAG = "LegacyVpnRunner"; - private static final String NONE = "--"; private final VpnConfig mConfig; private final String[] mDaemons; private final String[][] mArguments; + private final String mOuterInterface; private final LegacyVpnInfo mInfo; private long mTimer = -1; @@ -339,17 +347,27 @@ public class Vpn extends INetworkManagementEventObserver.Stub { mArguments = new String[][] {racoon, mtpd}; mInfo = new LegacyVpnInfo(); + // This is the interface which VPN is running on. + mOuterInterface = mConfig.interfaze; + // Legacy VPN is not a real package, so we use it to carry the key. mInfo.key = mConfig.packagz; mConfig.packagz = VpnConfig.LEGACY_VPN; } + public void check(String interfaze) { + if (interfaze.equals(mOuterInterface)) { + Log.i(TAG, "Legacy VPN is going down with " + interfaze); + exit(); + } + } + public void exit() { // We assume that everything is reset after the daemons die. + interrupt(); for (String daemon : mDaemons) { SystemProperties.set("ctl.stop", daemon); } - interrupt(); } public LegacyVpnInfo getInfo() { @@ -380,7 +398,7 @@ public class Vpn extends INetworkManagementEventObserver.Stub { Thread.sleep(yield ? 200 : 1); } else { mInfo.state = LegacyVpnInfo.STATE_TIMEOUT; - throw new IllegalStateException("time is up"); + throw new IllegalStateException("Time is up"); } } @@ -404,12 +422,11 @@ public class Vpn extends INetworkManagementEventObserver.Stub { } } - // Reset the properties. - SystemProperties.set("vpn.dns", NONE); - SystemProperties.set("vpn.via", NONE); - while (!NONE.equals(SystemProperties.get("vpn.dns")) || - !NONE.equals(SystemProperties.get("vpn.via"))) { - checkpoint(true); + // Clear the previous state. + File state = new File("/data/misc/vpn/state"); + state.delete(); + if (state.exists()) { + throw new IllegalStateException("Cannot delete the state"); } // Check if we need to restart any of the daemons. @@ -461,29 +478,34 @@ public class Vpn extends INetworkManagementEventObserver.Stub { OutputStream out = socket.getOutputStream(); for (String argument : arguments) { byte[] bytes = argument.getBytes(Charsets.UTF_8); - if (bytes.length >= 0xFFFF) { - throw new IllegalArgumentException("argument is too large"); + if (bytes.length > 0xFFFF) { + throw new IllegalArgumentException("Argument is too large"); } out.write(bytes.length >> 8); out.write(bytes.length); out.write(bytes); checkpoint(false); } - - // Send End-Of-Arguments. - out.write(0xFF); - out.write(0xFF); out.flush(); + socket.shutdownOutput(); + + // Wait for End-of-File. + InputStream in = socket.getInputStream(); + while (true) { + try { + if (in.read() == -1) { + break; + } + } catch (Exception e) { + // ignore + } + checkpoint(true); + } socket.close(); } - // Now here is the beast from the old days. We check few - // properties to figure out the current status. Ideally we - // can read things back from the sockets and get rid of the - // properties, but we have no time... - while (NONE.equals(SystemProperties.get("vpn.dns")) || - NONE.equals(SystemProperties.get("vpn.via"))) { - + // Wait for the daemons to create the new state. + while (!state.exists()) { // Check if a running daemon is dead. for (int i = 0; i < mDaemons.length; ++i) { String daemon = mDaemons[i]; @@ -495,20 +517,45 @@ public class Vpn extends INetworkManagementEventObserver.Stub { checkpoint(true); } - // Now we are connected. Get the interface. - mConfig.interfaze = SystemProperties.get("vpn.via"); + // Now we are connected. Read and parse the new state. + byte[] buffer = new byte[(int) state.length()]; + if (new FileInputStream(state).read(buffer) != buffer.length) { + throw new IllegalStateException("Cannot read the state"); + } + String[] parameters = new String(buffer, Charsets.UTF_8).split("\n", -1); + if (parameters.length != 6) { + throw new IllegalStateException("Cannot parse the state"); + } - // Get the DNS servers if they are not set in config. + // Set the interface and the addresses in the config. + mConfig.interfaze = parameters[0].trim(); + mConfig.addresses = parameters[1].trim(); + + // Set the routes if they are not set in the config. + if (mConfig.routes == null || mConfig.routes.isEmpty()) { + mConfig.routes = parameters[2].trim(); + } + + // Set the DNS servers if they are not set in the config. if (mConfig.dnsServers == null || mConfig.dnsServers.size() == 0) { - String dnsServers = SystemProperties.get("vpn.dns").trim(); + String dnsServers = parameters[3].trim(); if (!dnsServers.isEmpty()) { mConfig.dnsServers = Arrays.asList(dnsServers.split(" ")); } } - // TODO: support search domains from ISAKMP mode config. + // Set the search domains if they are not set in the config. + if (mConfig.searchDomains == null || mConfig.searchDomains.size() == 0) { + String searchDomains = parameters[4].trim(); + if (!searchDomains.isEmpty()) { + mConfig.searchDomains = Arrays.asList(searchDomains.split(" ")); + } + } + + // Set the routes. + jniSetRoutes(mConfig.interfaze, mConfig.routes); - // The final step must be synchronized. + // Here is the last step and it must be done synchronously. synchronized (Vpn.this) { // Check if the thread is interrupted while we are waiting. checkpoint(false); diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java index d30b66b..0c78fe7 100644 --- a/services/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java @@ -24,8 +24,8 @@ import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY; import static android.Manifest.permission.READ_PHONE_STATE; import static android.content.Intent.ACTION_UID_REMOVED; import static android.content.Intent.EXTRA_UID; +import static android.net.ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; -import static android.net.ConnectivityManager.*; import static android.net.ConnectivityManager.TYPE_MOBILE; import static android.net.NetworkPolicy.LIMIT_DISABLED; import static android.net.NetworkPolicy.WARNING_DISABLED; @@ -42,7 +42,7 @@ import static android.net.NetworkPolicyManager.dumpRules; import static android.net.NetworkPolicyManager.isUidValidForPolicy; import static android.net.NetworkTemplate.MATCH_MOBILE_3G_LOWER; import static android.net.NetworkTemplate.MATCH_MOBILE_4G; -import static android.net.NetworkTemplate.MATCH_MOBILE_ALL; +import static android.net.NetworkTemplate.buildTemplateMobileAll; import static android.text.format.DateUtils.DAY_IN_MILLIS; import static com.android.internal.util.Preconditions.checkNotNull; import static com.android.server.net.NetworkStatsService.ACTION_NETWORK_STATS_UPDATED; @@ -678,7 +678,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { time.setToNow(); final int cycleDay = time.monthDay; - final NetworkTemplate template = new NetworkTemplate(MATCH_MOBILE_ALL, subscriberId); + final NetworkTemplate template = buildTemplateMobileAll(subscriberId); mNetworkPolicy.add( new NetworkPolicy(template, cycleDay, 4 * GB_IN_BYTES, LIMIT_DISABLED)); writePolicyLocked(); diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java index 54e94db..7ec6b81 100644 --- a/services/java/com/android/server/net/NetworkStatsService.java +++ b/services/java/com/android/server/net/NetworkStatsService.java @@ -73,11 +73,12 @@ import com.android.internal.os.AtomicFile; import com.google.android.collect.Maps; import com.google.android.collect.Sets; +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileDescriptor; -import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; @@ -719,10 +720,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub { // clear any existing stats and read from disk mNetworkStats.clear(); - FileInputStream fis = null; + DataInputStream in = null; try { - fis = mNetworkFile.openRead(); - final DataInputStream in = new DataInputStream(fis); + in = new DataInputStream(new BufferedInputStream(mNetworkFile.openRead())); // verify file magic header intact final int magic = in.readInt(); @@ -751,7 +751,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } catch (IOException e) { Slog.e(TAG, "problem reading network stats", e); } finally { - IoUtils.closeQuietly(fis); + IoUtils.closeQuietly(in); } } @@ -768,10 +768,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub { // clear any existing stats and read from disk mUidStats.clear(); - FileInputStream fis = null; + DataInputStream in = null; try { - fis = mUidFile.openRead(); - final DataInputStream in = new DataInputStream(fis); + in = new DataInputStream(new BufferedInputStream(mUidFile.openRead())); // verify file magic header intact final int magic = in.readInt(); @@ -826,7 +825,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } catch (IOException e) { Slog.e(TAG, "problem reading uid stats", e); } finally { - IoUtils.closeQuietly(fis); + IoUtils.closeQuietly(in); } } @@ -838,7 +837,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { FileOutputStream fos = null; try { fos = mNetworkFile.startWrite(); - final DataOutputStream out = new DataOutputStream(fos); + final DataOutputStream out = new DataOutputStream(new BufferedOutputStream(fos)); out.writeInt(FILE_MAGIC); out.writeInt(VERSION_NETWORK_INIT); @@ -850,6 +849,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { history.writeToStream(out); } + out.flush(); mNetworkFile.finishWrite(fos); } catch (IOException e) { if (fos != null) { @@ -871,7 +871,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { FileOutputStream fos = null; try { fos = mUidFile.startWrite(); - final DataOutputStream out = new DataOutputStream(fos); + final DataOutputStream out = new DataOutputStream(new BufferedOutputStream(fos)); out.writeInt(FILE_MAGIC); out.writeInt(VERSION_UID_WITH_TAG); @@ -895,6 +895,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } } + out.flush(); mUidFile.finishWrite(fos); } catch (IOException e) { if (fos != null) { diff --git a/services/jni/com_android_server_connectivity_Vpn.cpp b/services/jni/com_android_server_connectivity_Vpn.cpp index 5f920f1..d28a6b4 100644 --- a/services/jni/com_android_server_connectivity_Vpn.cpp +++ b/services/jni/com_android_server_connectivity_Vpn.cpp @@ -18,7 +18,6 @@ #define LOG_TAG "VpnJni" #include <cutils/log.h> -#include <cutils/properties.h> #include <stdio.h> #include <string.h> @@ -54,7 +53,7 @@ static inline in_addr_t *as_in_addr(sockaddr *sa) { #define SYSTEM_ERROR -1 #define BAD_ARGUMENT -2 -static int create_interface(char *name, int *index, int mtu) +static int create_interface(int mtu) { int tun = open("/dev/tun", O_RDWR | O_NONBLOCK); @@ -82,14 +81,6 @@ static int create_interface(char *name, int *index, int mtu) goto error; } - // Get interface index. - if (ioctl(inet4, SIOGIFINDEX, &ifr4)) { - LOGE("Cannot get index of %s: %s", ifr4.ifr_name, strerror(errno)); - goto error; - } - - strncpy(name, ifr4.ifr_name, IFNAMSIZ); - *index = ifr4.ifr_ifindex; return tun; error: @@ -97,12 +88,40 @@ error: return SYSTEM_ERROR; } -static int set_addresses(const char *name, int index, const char *addresses) +static int get_interface_name(char *name, int tun) +{ + ifreq ifr4; + if (ioctl(tun, TUNGETIFF, &ifr4)) { + LOGE("Cannot get interface name: %s", strerror(errno)); + return SYSTEM_ERROR; + } + strncpy(name, ifr4.ifr_name, IFNAMSIZ); + return 0; +} + +static int get_interface_index(const char *name) { ifreq ifr4; + strncpy(ifr4.ifr_name, name, IFNAMSIZ); + if (ioctl(inet4, SIOGIFINDEX, &ifr4)) { + LOGE("Cannot get index of %s: %s", name, strerror(errno)); + return SYSTEM_ERROR; + } + return ifr4.ifr_ifindex; +} + +static int set_addresses(const char *name, const char *addresses) +{ + int index = get_interface_index(name); + if (index < 0) { + return index; + } + + ifreq ifr4; memset(&ifr4, 0, sizeof(ifr4)); strncpy(ifr4.ifr_name, name, IFNAMSIZ); ifr4.ifr_addr.sa_family = AF_INET; + ifr4.ifr_netmask.sa_family = AF_INET; in6_ifreq ifr6; memset(&ifr6, 0, sizeof(ifr6)); @@ -146,7 +165,7 @@ static int set_addresses(const char *name, int index, const char *addresses) } in_addr_t mask = prefix ? (~0 << (32 - prefix)) : 0; - *as_in_addr(&ifr4.ifr_addr) = htonl(mask); + *as_in_addr(&ifr4.ifr_netmask) = htonl(mask); if (ioctl(inet4, SIOCSIFNETMASK, &ifr4)) { count = (errno == EINVAL) ? BAD_ARGUMENT : SYSTEM_ERROR; break; @@ -168,8 +187,13 @@ static int set_addresses(const char *name, int index, const char *addresses) return count; } -static int set_routes(const char *name, int index, const char *routes) +static int set_routes(const char *name, const char *routes) { + int index = get_interface_index(name); + if (index < 0) { + return index; + } + rtentry rt4; memset(&rt4, 0, sizeof(rt4)); rt4.rt_dev = (char *)name; @@ -253,17 +277,6 @@ static int set_routes(const char *name, int index, const char *routes) return count; } -static int get_interface_name(char *name, int tun) -{ - ifreq ifr4; - if (ioctl(tun, TUNGETIFF, &ifr4)) { - LOGE("Cannot get interface name: %s", strerror(errno)); - return SYSTEM_ERROR; - } - strncpy(name, ifr4.ifr_name, IFNAMSIZ); - return 0; -} - static int reset_interface(const char *name) { ifreq ifr4; @@ -309,63 +322,90 @@ static void throwException(JNIEnv *env, int error, const char *message) } } -static jint configure(JNIEnv *env, jobject thiz, - jint mtu, jstring jAddresses, jstring jRoutes) +static jint create(JNIEnv *env, jobject thiz, jint mtu) { - char name[IFNAMSIZ]; - int index; - int tun = create_interface(name, &index, mtu); + int tun = create_interface(mtu); if (tun < 0) { throwException(env, tun, "Cannot create interface"); return -1; } + return tun; +} +static jstring getName(JNIEnv *env, jobject thiz, jint tun) +{ + char name[IFNAMSIZ]; + if (get_interface_name(name, tun) < 0) { + throwException(env, SYSTEM_ERROR, "Cannot get interface name"); + return NULL; + } + return env->NewStringUTF(name); +} + +static jint setAddresses(JNIEnv *env, jobject thiz, jstring jName, + jstring jAddresses) +{ + const char *name = NULL; const char *addresses = NULL; - const char *routes = NULL; - int count; + int count = -1; - // At least one address must be set. + name = jName ? env->GetStringUTFChars(jName, NULL) : NULL; + if (!name) { + jniThrowNullPointerException(env, "name"); + goto error; + } addresses = jAddresses ? env->GetStringUTFChars(jAddresses, NULL) : NULL; if (!addresses) { - jniThrowNullPointerException(env, "address"); + jniThrowNullPointerException(env, "addresses"); goto error; } - count = set_addresses(name, index, addresses); - env->ReleaseStringUTFChars(jAddresses, addresses); - if (count <= 0) { + count = set_addresses(name, addresses); + if (count < 0) { throwException(env, count, "Cannot set address"); - goto error; + count = -1; } - LOGD("Configured %d address(es) on %s", count, name); - - // On the contrary, routes are optional. - routes = jRoutes ? env->GetStringUTFChars(jRoutes, NULL) : NULL; - if (routes) { - count = set_routes(name, index, routes); - env->ReleaseStringUTFChars(jRoutes, routes); - if (count < 0) { - throwException(env, count, "Cannot set route"); - goto error; - } - LOGD("Configured %d route(s) on %s", count, name); - } - - return tun; error: - close(tun); - LOGD("%s is destroyed", name); - return -1; + if (name) { + env->ReleaseStringUTFChars(jName, name); + } + if (addresses) { + env->ReleaseStringUTFChars(jAddresses, addresses); + } + return count; } -static jstring getName(JNIEnv *env, jobject thiz, jint tun) +static jint setRoutes(JNIEnv *env, jobject thiz, jstring jName, + jstring jRoutes) { - char name[IFNAMSIZ]; - if (get_interface_name(name, tun) < 0) { - throwException(env, SYSTEM_ERROR, "Cannot get interface name"); - return NULL; + const char *name = NULL; + const char *routes = NULL; + int count = -1; + + name = jName ? env->GetStringUTFChars(jName, NULL) : NULL; + if (!name) { + jniThrowNullPointerException(env, "name"); + goto error; } - return env->NewStringUTF(name); + routes = jRoutes ? env->GetStringUTFChars(jRoutes, NULL) : NULL; + if (!routes) { + jniThrowNullPointerException(env, "routes"); + goto error; + } + count = set_routes(name, routes); + if (count < 0) { + throwException(env, count, "Cannot set route"); + count = -1; + } + +error: + if (name) { + env->ReleaseStringUTFChars(jName, name); + } + if (routes) { + env->ReleaseStringUTFChars(jRoutes, routes); + } + return count; } static void reset(JNIEnv *env, jobject thiz, jstring jName) @@ -409,8 +449,10 @@ static void protect(JNIEnv *env, jobject thiz, jint socket, jstring jName) //------------------------------------------------------------------------------ static JNINativeMethod gMethods[] = { - {"jniConfigure", "(ILjava/lang/String;Ljava/lang/String;)I", (void *)configure}, + {"jniCreate", "(I)I", (void *)create}, {"jniGetName", "(I)Ljava/lang/String;", (void *)getName}, + {"jniSetAddresses", "(Ljava/lang/String;Ljava/lang/String;)I", (void *)setAddresses}, + {"jniSetRoutes", "(Ljava/lang/String;Ljava/lang/String;)I", (void *)setRoutes}, {"jniReset", "(Ljava/lang/String;)V", (void *)reset}, {"jniCheck", "(Ljava/lang/String;)I", (void *)check}, {"jniProtect", "(ILjava/lang/String;)V", (void *)protect}, diff --git a/services/sensorservice/SensorFusion.cpp b/services/sensorservice/SensorFusion.cpp index 4ec0c8c..518a1bb 100644 --- a/services/sensorservice/SensorFusion.cpp +++ b/services/sensorservice/SensorFusion.cpp @@ -28,23 +28,25 @@ SensorFusion::SensorFusion() mEnabled(false), mGyroTime(0) { sensor_t const* list; - size_t count = mSensorDevice.getSensorList(&list); - for (size_t i=0 ; i<count ; i++) { - if (list[i].type == SENSOR_TYPE_ACCELEROMETER) { - mAcc = Sensor(list + i); - } - if (list[i].type == SENSOR_TYPE_MAGNETIC_FIELD) { - mMag = Sensor(list + i); - } - if (list[i].type == SENSOR_TYPE_GYROSCOPE) { - mGyro = Sensor(list + i); - // 200 Hz for gyro events is a good compromise between precision - // and power/cpu usage. - mGyroRate = 200; - mTargetDelayNs = 1000000000LL/mGyroRate; + ssize_t count = mSensorDevice.getSensorList(&list); + if (count > 0) { + for (size_t i=0 ; i<size_t(count) ; i++) { + if (list[i].type == SENSOR_TYPE_ACCELEROMETER) { + mAcc = Sensor(list + i); + } + if (list[i].type == SENSOR_TYPE_MAGNETIC_FIELD) { + mMag = Sensor(list + i); + } + if (list[i].type == SENSOR_TYPE_GYROSCOPE) { + mGyro = Sensor(list + i); + // 200 Hz for gyro events is a good compromise between precision + // and power/cpu usage. + mGyroRate = 200; + mTargetDelayNs = 1000000000LL/mGyroRate; + } } + mFusion.init(); } - mFusion.init(); } void SensorFusion::process(const sensors_event_t& event) { diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp index 64d214b..e0dce1f 100644 --- a/services/sensorservice/SensorService.cpp +++ b/services/sensorservice/SensorService.cpp @@ -70,73 +70,76 @@ void SensorService::onFirstRef() SensorDevice& dev(SensorDevice::getInstance()); if (dev.initCheck() == NO_ERROR) { - ssize_t orientationIndex = -1; - bool hasGyro = false; - uint32_t virtualSensorsNeeds = - (1<<SENSOR_TYPE_GRAVITY) | - (1<<SENSOR_TYPE_LINEAR_ACCELERATION) | - (1<<SENSOR_TYPE_ROTATION_VECTOR); sensor_t const* list; - int count = dev.getSensorList(&list); - mLastEventSeen.setCapacity(count); - for (int i=0 ; i<count ; i++) { - registerSensor( new HardwareSensor(list[i]) ); - switch (list[i].type) { - case SENSOR_TYPE_ORIENTATION: - orientationIndex = i; - break; - case SENSOR_TYPE_GYROSCOPE: - hasGyro = true; - break; - case SENSOR_TYPE_GRAVITY: - case SENSOR_TYPE_LINEAR_ACCELERATION: - case SENSOR_TYPE_ROTATION_VECTOR: - virtualSensorsNeeds &= ~(1<<list[i].type); - break; + ssize_t count = dev.getSensorList(&list); + if (count > 0) { + ssize_t orientationIndex = -1; + bool hasGyro = false; + uint32_t virtualSensorsNeeds = + (1<<SENSOR_TYPE_GRAVITY) | + (1<<SENSOR_TYPE_LINEAR_ACCELERATION) | + (1<<SENSOR_TYPE_ROTATION_VECTOR); + + mLastEventSeen.setCapacity(count); + for (ssize_t i=0 ; i<count ; i++) { + registerSensor( new HardwareSensor(list[i]) ); + switch (list[i].type) { + case SENSOR_TYPE_ORIENTATION: + orientationIndex = i; + break; + case SENSOR_TYPE_GYROSCOPE: + hasGyro = true; + break; + case SENSOR_TYPE_GRAVITY: + case SENSOR_TYPE_LINEAR_ACCELERATION: + case SENSOR_TYPE_ROTATION_VECTOR: + virtualSensorsNeeds &= ~(1<<list[i].type); + break; + } } - } - // it's safe to instantiate the SensorFusion object here - // (it wants to be instantiated after h/w sensors have been - // registered) - const SensorFusion& fusion(SensorFusion::getInstance()); - - if (hasGyro) { - // Always instantiate Android's virtual sensors. Since they are - // instantiated behind sensors from the HAL, they won't - // interfere with applications, unless they looks specifically - // for them (by name). - - registerVirtualSensor( new RotationVectorSensor() ); - registerVirtualSensor( new GravitySensor(list, count) ); - registerVirtualSensor( new LinearAccelerationSensor(list, count) ); - - // these are optional - registerVirtualSensor( new OrientationSensor() ); - registerVirtualSensor( new CorrectedGyroSensor(list, count) ); - - // virtual debugging sensors... - char value[PROPERTY_VALUE_MAX]; - property_get("debug.sensors", value, "0"); - if (atoi(value)) { - registerVirtualSensor( new GyroDriftSensor() ); + // it's safe to instantiate the SensorFusion object here + // (it wants to be instantiated after h/w sensors have been + // registered) + const SensorFusion& fusion(SensorFusion::getInstance()); + + if (hasGyro) { + // Always instantiate Android's virtual sensors. Since they are + // instantiated behind sensors from the HAL, they won't + // interfere with applications, unless they looks specifically + // for them (by name). + + registerVirtualSensor( new RotationVectorSensor() ); + registerVirtualSensor( new GravitySensor(list, count) ); + registerVirtualSensor( new LinearAccelerationSensor(list, count) ); + + // these are optional + registerVirtualSensor( new OrientationSensor() ); + registerVirtualSensor( new CorrectedGyroSensor(list, count) ); + + // virtual debugging sensors... + char value[PROPERTY_VALUE_MAX]; + property_get("debug.sensors", value, "0"); + if (atoi(value)) { + registerVirtualSensor( new GyroDriftSensor() ); + } } - } - // build the sensor list returned to users - mUserSensorList = mSensorList; - if (hasGyro && - (virtualSensorsNeeds & (1<<SENSOR_TYPE_ROTATION_VECTOR))) { - // if we have the fancy sensor fusion, and it's not provided by the - // HAL, use our own (fused) orientation sensor by removing the - // HAL supplied one form the user list. - if (orientationIndex >= 0) { - mUserSensorList.removeItemsAt(orientationIndex); + // build the sensor list returned to users + mUserSensorList = mSensorList; + if (hasGyro && + (virtualSensorsNeeds & (1<<SENSOR_TYPE_ROTATION_VECTOR))) { + // if we have the fancy sensor fusion, and it's not provided by the + // HAL, use our own (fused) orientation sensor by removing the + // HAL supplied one form the user list. + if (orientationIndex >= 0) { + mUserSensorList.removeItemsAt(orientationIndex); + } } - } - run("SensorService", PRIORITY_URGENT_DISPLAY); - mInitCheck = NO_ERROR; + run("SensorService", PRIORITY_URGENT_DISPLAY); + mInitCheck = NO_ERROR; + } } } diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index b0881a4..680814c 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -1551,8 +1551,18 @@ status_t SurfaceFlinger::dump(int fd, const Vector<String16>& args) * Dump SurfaceFlinger global state */ - snprintf(buffer, SIZE, "SurfaceFlinger global state\n"); + snprintf(buffer, SIZE, "SurfaceFlinger global state:\n"); result.append(buffer); + + const GLExtensions& extensions(GLExtensions::getInstance()); + snprintf(buffer, SIZE, "GLES: %s, %s, %s\n", + extensions.getVendor(), + extensions.getRenderer(), + extensions.getVersion()); + result.append(buffer); + snprintf(buffer, SIZE, "EXTS: %s\n", extensions.getExtension()); + result.append(buffer); + mWormholeRegion.dump(result, "WormholeRegion"); const DisplayHardware& hw(graphicPlane(0).displayHardware()); snprintf(buffer, SIZE, diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java index 33fd355..504ba42 100644 --- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java @@ -27,7 +27,6 @@ import static android.net.NetworkPolicyManager.RULE_REJECT_METERED; import static android.net.NetworkPolicyManager.computeLastCycleBoundary; import static android.net.NetworkStats.TAG_NONE; import static android.net.NetworkStats.UID_ALL; -import static android.net.NetworkTemplate.MATCH_WIFI; import static org.easymock.EasyMock.anyInt; import static org.easymock.EasyMock.aryEq; import static org.easymock.EasyMock.capture; @@ -88,7 +87,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { private static final long TEST_START = 1194220800000L; private static final String TEST_IFACE = "test0"; - private static NetworkTemplate sTemplateWifi = new NetworkTemplate(MATCH_WIFI, null); + private static NetworkTemplate sTemplateWifi = NetworkTemplate.buildTemplateWifi(); private BroadcastInterceptingContext mServiceContext; private File mPolicyDir; diff --git a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java index ac74063..bd80af9 100644 --- a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java @@ -25,8 +25,8 @@ import static android.net.ConnectivityManager.TYPE_WIMAX; import static android.net.NetworkStats.IFACE_ALL; import static android.net.NetworkStats.TAG_NONE; import static android.net.NetworkStats.UID_ALL; -import static android.net.NetworkTemplate.MATCH_MOBILE_ALL; -import static android.net.NetworkTemplate.MATCH_WIFI; +import static android.net.NetworkTemplate.buildTemplateMobileAll; +import static android.net.NetworkTemplate.buildTemplateWifi; import static android.net.TrafficStats.UID_REMOVED; import static android.text.format.DateUtils.DAY_IN_MILLIS; import static android.text.format.DateUtils.HOUR_IN_MILLIS; @@ -81,9 +81,9 @@ public class NetworkStatsServiceTest extends AndroidTestCase { private static final String IMSI_1 = "310004"; private static final String IMSI_2 = "310260"; - private static NetworkTemplate sTemplateWifi = new NetworkTemplate(MATCH_WIFI, null); - private static NetworkTemplate sTemplateImsi1 = new NetworkTemplate(MATCH_MOBILE_ALL, IMSI_1); - private static NetworkTemplate sTemplateImsi2 = new NetworkTemplate(MATCH_MOBILE_ALL, IMSI_2); + private static NetworkTemplate sTemplateWifi = buildTemplateWifi(); + private static NetworkTemplate sTemplateImsi1 = buildTemplateMobileAll(IMSI_1); + private static NetworkTemplate sTemplateImsi2 = buildTemplateMobileAll(IMSI_2); private static final int UID_RED = 1001; private static final int UID_BLUE = 1002; @@ -290,7 +290,7 @@ public class NetworkStatsServiceTest extends AndroidTestCase { mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL)); // verify service recorded history - history = mService.getHistoryForNetwork(new NetworkTemplate(MATCH_WIFI, null)); + history = mService.getHistoryForNetwork(sTemplateWifi); assertValues(history, Long.MIN_VALUE, Long.MAX_VALUE, 512L, 512L); assertEquals(HOUR_IN_MILLIS, history.getBucketDuration()); assertEquals(2, history.size()); @@ -307,7 +307,7 @@ public class NetworkStatsServiceTest extends AndroidTestCase { mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL)); // verify identical stats, but spread across 4 buckets now - history = mService.getHistoryForNetwork(new NetworkTemplate(MATCH_WIFI, null)); + history = mService.getHistoryForNetwork(sTemplateWifi); assertValues(history, Long.MIN_VALUE, Long.MAX_VALUE, 512L, 512L); assertEquals(30 * MINUTE_IN_MILLIS, history.getBucketDuration()); assertEquals(4, history.size()); diff --git a/telephony/java/com/android/internal/telephony/DataConnection.java b/telephony/java/com/android/internal/telephony/DataConnection.java index 1bba8e3..8978f1d 100644 --- a/telephony/java/com/android/internal/telephony/DataConnection.java +++ b/telephony/java/com/android/internal/telephony/DataConnection.java @@ -26,7 +26,6 @@ import com.android.internal.util.StateMachine; import android.app.PendingIntent; import android.net.LinkCapabilities; import android.net.LinkProperties; -import android.net.LinkProperties.CompareAddressesResult; import android.net.ProxyProperties; import android.os.AsyncResult; import android.os.Message; diff --git a/telephony/java/com/android/internal/telephony/DataConnectionAc.java b/telephony/java/com/android/internal/telephony/DataConnectionAc.java index 9e185e5..a9f2cd1 100644 --- a/telephony/java/com/android/internal/telephony/DataConnectionAc.java +++ b/telephony/java/com/android/internal/telephony/DataConnectionAc.java @@ -23,7 +23,6 @@ import com.android.internal.util.Protocol; import android.app.PendingIntent; import android.net.LinkCapabilities; import android.net.LinkProperties; -import android.net.LinkProperties.CompareAddressesResult; import android.net.ProxyProperties; import android.os.Message; diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java index bf964b7..ccdb0bf 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java +++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java @@ -27,7 +27,7 @@ import android.database.ContentObserver; import android.database.Cursor; import android.net.ConnectivityManager; import android.net.LinkAddress; -import android.net.LinkProperties.CompareAddressesResult; +import android.net.LinkProperties.CompareResult; import android.net.NetworkUtils; import android.net.ProxyProperties; import android.net.TrafficStats; @@ -1152,7 +1152,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { ! result.oldLp.isIdenticalHttpProxy(result.newLp) || ! result.oldLp.isIdenticalAddresses(result.newLp)) { // If the same address type was removed and added we need to cleanup - CompareAddressesResult car = + CompareResult<LinkAddress> car = result.oldLp.compareAddresses(result.newLp); boolean needToClean = false; for (LinkAddress added : car.added) { diff --git a/tests/BiDiTests/res/layout/textview_direction_ltr.xml b/tests/BiDiTests/res/layout/textview_direction_ltr.xml index f7b7b8e..2c790ec 100644 --- a/tests/BiDiTests/res/layout/textview_direction_ltr.xml +++ b/tests/BiDiTests/res/layout/textview_direction_ltr.xml @@ -18,95 +18,232 @@ android:id="@+id/textview_direction_ltr" android:layout_width="fill_parent" android:layout_height="fill_parent" - android:layoutDirection="ltr"> + android:layoutDirection="ltr" + android:textDirection="ltr"> - <LinearLayout android:orientation="vertical" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:textDirection="ltr"> - - <LinearLayout android:orientation="vertical" + <TableLayout android:orientation="vertical" android:layout_width="wrap_content" android:layout_height="wrap_content"> - <TextView android:layout_height="wrap_content" - android:layout_width="wrap_content" - android:textSize="24dip" - android:text="@string/textview_text" - /> - <TextView android:layout_height="wrap_content" - android:layout_width="wrap_content" - android:textSize="24dip" - android:text="@string/textview_text" - android:textDirection="inherit" - /> - <TextView android:layout_height="wrap_content" - android:layout_width="wrap_content" - android:textSize="24dip" - android:text="@string/textview_text" - android:textDirection="firstStrong" - /> - <TextView android:layout_height="wrap_content" - android:layout_width="wrap_content" - android:textSize="24dip" - android:text="@string/textview_text" - android:textDirection="anyRtl" - /> - <TextView android:layout_height="wrap_content" - android:layout_width="wrap_content" - android:textSize="24dip" - android:text="@string/textview_text" - android:textDirection="ltr" - /> - <TextView android:layout_height="wrap_content" - android:layout_width="wrap_content" - android:textSize="24dip" - android:text="@string/textview_text" - android:textDirection="rtl" - /> - </LinearLayout> + <TableRow> + <TextView android:text="(unspecified)" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:typeface="serif" + android:layout_marginLeft="7dip" + android:layout_marginRight="7dip" + /> + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_hebrew_text" + android:layout_marginLeft="7dip" + android:layout_marginRight="7dip" + android:background="#444444" + /> + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_latin_text" + android:layout_marginLeft="7dip" + android:layout_marginRight="7dip" + android:background="#444444" + /> + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_multiline_text" + android:layout_marginLeft="7dip" + android:layout_marginRight="7dip" + android:background="#444444" + /> + </TableRow> - <LinearLayout android:orientation="vertical" - android:layout_width="wrap_content" - android:layout_height="wrap_content"> + <TableRow> + <TextView android:text="inherit" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:typeface="serif" + android:layout_marginLeft="7dip" + android:layout_marginRight="7dip" + /> + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_hebrew_text" + android:textDirection="inherit" + android:layout_marginLeft="7dip" + android:layout_marginRight="7dip" + android:background="#444444" + /> + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_latin_text" + android:textDirection="inherit" + android:layout_marginLeft="7dip" + android:layout_marginRight="7dip" + android:background="#444444" + /> + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_multiline_text" + android:textDirection="inherit" + android:layout_marginLeft="7dip" + android:layout_marginRight="7dip" + android:background="#444444" + /> + </TableRow> - <TextView android:layout_height="wrap_content" - android:layout_width="wrap_content" - android:textSize="24dip" - android:text="@string/textview_hebrew_text" - /> - <TextView android:layout_height="wrap_content" - android:layout_width="wrap_content" - android:textSize="24dip" - android:text="@string/textview_hebrew_text" - android:textDirection="inherit" - /> - <TextView android:layout_height="wrap_content" - android:layout_width="wrap_content" - android:textSize="24dip" - android:text="@string/textview_hebrew_text" - android:textDirection="firstStrong" - /> - <TextView android:layout_height="wrap_content" - android:layout_width="wrap_content" - android:textSize="24dip" - android:text="@string/textview_hebrew_text" - android:textDirection="anyRtl" - /> - <TextView android:layout_height="wrap_content" - android:layout_width="wrap_content" - android:textSize="24dip" - android:text="@string/textview_hebrew_text" - android:textDirection="ltr" - /> - <TextView android:layout_height="wrap_content" - android:layout_width="wrap_content" - android:textSize="24dip" - android:text="@string/textview_hebrew_text" - android:textDirection="rtl" - /> - </LinearLayout> + <TableRow> + <TextView android:text="firstStrong" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:typeface="serif" + android:layout_marginLeft="7dip" + android:layout_marginRight="7dip" + /> + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_hebrew_text" + android:textDirection="firstStrong" + android:layout_marginLeft="7dip" + android:layout_marginRight="7dip" + android:background="#444444" + /> + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_latin_text" + android:textDirection="firstStrong" + android:layout_marginLeft="7dip" + android:layout_marginRight="7dip" + android:background="#444444" + /> + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_multiline_text" + android:textDirection="firstStrong" + android:layout_marginLeft="7dip" + android:layout_marginRight="7dip" + android:background="#444444" + /> + </TableRow> + + <TableRow> + <TextView android:text="anyRtl" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:typeface="serif" + android:layout_marginLeft="7dip" + android:layout_marginRight="7dip" + /> + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_hebrew_text" + android:textDirection="anyRtl" + android:layout_marginLeft="7dip" + android:layout_marginRight="7dip" + android:background="#444444" + /> + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_latin_text" + android:textDirection="anyRtl" + android:layout_marginLeft="7dip" + android:layout_marginRight="7dip" + android:background="#444444" + /> + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_multiline_text" + android:textDirection="anyRtl" + android:layout_marginLeft="7dip" + android:layout_marginRight="7dip" + android:background="#444444" + /> + </TableRow> + + <TableRow> + <TextView android:text="ltr" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:typeface="serif" + android:layout_marginLeft="7dip" + android:layout_marginRight="7dip" + /> + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_hebrew_text" + android:textDirection="ltr" + android:layout_marginLeft="7dip" + android:layout_marginRight="7dip" + android:background="#444444" + /> + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_latin_text" + android:textDirection="ltr" + android:layout_marginLeft="7dip" + android:layout_marginRight="7dip" + android:background="#444444" + /> + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_multiline_text" + android:textDirection="ltr" + android:layout_marginLeft="7dip" + android:layout_marginRight="7dip" + android:background="#444444" + /> + </TableRow> + + <TableRow> + <TextView android:text="rtl" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:typeface="serif" + android:layout_marginRight="7dip" + android:layout_marginLeft="7dip" + /> + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_hebrew_text" + android:textDirection="rtl" + android:layout_marginLeft="7dip" + android:layout_marginRight="7dip" + android:background="#444444" + /> + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_latin_text" + android:textDirection="rtl" + android:layout_marginLeft="7dip" + android:layout_marginRight="7dip" + android:background="#444444" + /> + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_multiline_text" + android:textDirection="rtl" + android:layout_marginLeft="7dip" + android:layout_marginRight="7dip" + android:background="#444444" + /> + </TableRow> - </LinearLayout> + </TableLayout> -</FrameLayout>
\ No newline at end of file +</FrameLayout> diff --git a/tests/BiDiTests/res/layout/textview_direction_rtl.xml b/tests/BiDiTests/res/layout/textview_direction_rtl.xml index 81c5411..1df100d 100644 --- a/tests/BiDiTests/res/layout/textview_direction_rtl.xml +++ b/tests/BiDiTests/res/layout/textview_direction_rtl.xml @@ -18,95 +18,232 @@ android:id="@+id/textview_direction_rtl" android:layout_width="fill_parent" android:layout_height="fill_parent" - android:layoutDirection="rtl"> + android:layoutDirection="rtl" + android:textDirection="rtl"> - <LinearLayout android:orientation="vertical" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:textDirection="rtl"> - - <LinearLayout android:orientation="vertical" + <TableLayout android:orientation="vertical" android:layout_width="wrap_content" android:layout_height="wrap_content"> - <TextView android:layout_height="wrap_content" - android:layout_width="wrap_content" - android:textSize="24dip" - android:text="@string/textview_text" - /> - <TextView android:layout_height="wrap_content" - android:layout_width="wrap_content" - android:textSize="24dip" - android:text="@string/textview_text" - android:textDirection="inherit" - /> - <TextView android:layout_height="wrap_content" - android:layout_width="wrap_content" - android:textSize="24dip" - android:text="@string/textview_text" - android:textDirection="firstStrong" - /> - <TextView android:layout_height="wrap_content" - android:layout_width="wrap_content" - android:textSize="24dip" - android:text="@string/textview_text" - android:textDirection="anyRtl" - /> - <TextView android:layout_height="wrap_content" - android:layout_width="wrap_content" - android:textSize="24dip" - android:text="@string/textview_text" - android:textDirection="ltr" - /> - <TextView android:layout_height="wrap_content" - android:layout_width="wrap_content" - android:textSize="24dip" - android:text="@string/textview_text" - android:textDirection="rtl" - /> - </LinearLayout> + <TableRow> + <TextView android:text="(unspecified)" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:typeface="serif" + android:layout_marginLeft="7dip" + android:layout_marginRight="7dip" + /> + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_hebrew_text" + android:layout_marginLeft="7dip" + android:layout_marginRight="7dip" + android:background="#444444" + /> + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_latin_text" + android:layout_marginLeft="7dip" + android:layout_marginRight="7dip" + android:background="#444444" + /> + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_multiline_text" + android:layout_marginLeft="7dip" + android:layout_marginRight="7dip" + android:background="#444444" + /> + </TableRow> - <LinearLayout android:orientation="vertical" - android:layout_width="wrap_content" - android:layout_height="wrap_content"> + <TableRow> + <TextView android:text="inherit" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:typeface="serif" + android:layout_marginLeft="7dip" + android:layout_marginRight="7dip" + /> + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_hebrew_text" + android:textDirection="inherit" + android:layout_marginLeft="7dip" + android:layout_marginRight="7dip" + android:background="#444444" + /> + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_latin_text" + android:textDirection="inherit" + android:layout_marginLeft="7dip" + android:layout_marginRight="7dip" + android:background="#444444" + /> + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_multiline_text" + android:textDirection="inherit" + android:layout_marginLeft="7dip" + android:layout_marginRight="7dip" + android:background="#444444" + /> + </TableRow> - <TextView android:layout_height="wrap_content" - android:layout_width="wrap_content" - android:textSize="24dip" - android:text="@string/textview_hebrew_text" - /> - <TextView android:layout_height="wrap_content" - android:layout_width="wrap_content" - android:textSize="24dip" - android:text="@string/textview_hebrew_text" - android:textDirection="inherit" - /> - <TextView android:layout_height="wrap_content" - android:layout_width="wrap_content" - android:textSize="24dip" - android:text="@string/textview_hebrew_text" - android:textDirection="firstStrong" - /> - <TextView android:layout_height="wrap_content" - android:layout_width="wrap_content" - android:textSize="24dip" - android:text="@string/textview_hebrew_text" - android:textDirection="anyRtl" - /> - <TextView android:layout_height="wrap_content" - android:layout_width="wrap_content" - android:textSize="24dip" - android:text="@string/textview_hebrew_text" - android:textDirection="ltr" - /> - <TextView android:layout_height="wrap_content" - android:layout_width="wrap_content" - android:textSize="24dip" - android:text="@string/textview_hebrew_text" - android:textDirection="rtl" - /> - </LinearLayout> + <TableRow> + <TextView android:text="firstStrong" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:typeface="serif" + android:layout_marginLeft="7dip" + android:layout_marginRight="7dip" + /> + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_hebrew_text" + android:textDirection="firstStrong" + android:layout_marginLeft="7dip" + android:layout_marginRight="7dip" + android:background="#444444" + /> + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_latin_text" + android:textDirection="firstStrong" + android:layout_marginLeft="7dip" + android:layout_marginRight="7dip" + android:background="#444444" + /> + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_multiline_text" + android:textDirection="firstStrong" + android:layout_marginLeft="7dip" + android:layout_marginRight="7dip" + android:background="#444444" + /> + </TableRow> + + <TableRow> + <TextView android:text="anyRtl" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:typeface="serif" + android:layout_marginLeft="7dip" + android:layout_marginRight="7dip" + /> + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_hebrew_text" + android:textDirection="anyRtl" + android:layout_marginLeft="7dip" + android:layout_marginRight="7dip" + android:background="#444444" + /> + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_latin_text" + android:textDirection="anyRtl" + android:layout_marginLeft="7dip" + android:layout_marginRight="7dip" + android:background="#444444" + /> + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_multiline_text" + android:textDirection="anyRtl" + android:layout_marginLeft="7dip" + android:layout_marginRight="7dip" + android:background="#444444" + /> + </TableRow> + + <TableRow> + <TextView android:text="ltr" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:typeface="serif" + android:layout_marginLeft="7dip" + android:layout_marginRight="7dip" + /> + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_hebrew_text" + android:textDirection="ltr" + android:layout_marginLeft="7dip" + android:layout_marginRight="7dip" + android:background="#444444" + /> + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_latin_text" + android:textDirection="ltr" + android:layout_marginLeft="7dip" + android:layout_marginRight="7dip" + android:background="#444444" + /> + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_multiline_text" + android:textDirection="ltr" + android:layout_marginLeft="7dip" + android:layout_marginRight="7dip" + android:background="#444444" + /> + </TableRow> + + <TableRow> + <TextView android:text="rtl" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:typeface="serif" + android:layout_marginRight="7dip" + android:layout_marginLeft="7dip" + /> + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_hebrew_text" + android:textDirection="rtl" + android:layout_marginLeft="7dip" + android:layout_marginRight="7dip" + android:background="#444444" + /> + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_latin_text" + android:textDirection="rtl" + android:layout_marginLeft="7dip" + android:layout_marginRight="7dip" + android:background="#444444" + /> + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_multiline_text" + android:textDirection="rtl" + android:layout_marginLeft="7dip" + android:layout_marginRight="7dip" + android:background="#444444" + /> + </TableRow> - </LinearLayout> + </TableLayout> -</FrameLayout>
\ No newline at end of file +</FrameLayout> diff --git a/tests/BiDiTests/res/values/strings.xml b/tests/BiDiTests/res/values/strings.xml index c0bbe94..9a486c1 100644 --- a/tests/BiDiTests/res/values/strings.xml +++ b/tests/BiDiTests/res/values/strings.xml @@ -40,6 +40,6 @@ <string name="menu_delete">Delete</string> <string name="textview_hebrew_text">םמab?!</string> <string name="textview_latin_text">abםמ?!</string> - <string name="textview_multiline_text">םמ?!\nab?!</string> + <string name="textview_multiline_text">םמ?!\nab?!\n?!</string> </resources> diff --git a/tests/HwAccelerationTest/res/layout/list_activity.xml b/tests/HwAccelerationTest/res/layout/list_activity.xml index 6bba370..1a5d3d9 100644 --- a/tests/HwAccelerationTest/res/layout/list_activity.xml +++ b/tests/HwAccelerationTest/res/layout/list_activity.xml @@ -30,8 +30,10 @@ android:layout_height="wrap_content" android:layout_marginLeft="10dip" android:layout_marginRight="3dip" + + android:onClick="startProfiling" - android:text="Add" /> + android:text="Start" /> <Button android:layout_width="0dip" @@ -39,8 +41,10 @@ android:layout_height="wrap_content" android:layout_marginLeft="3dip" android:layout_marginRight="10dip" + + android:onClick="stopProfiling" - android:text="Remove" /> + android:text="Stop" /> </LinearLayout> diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ListActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ListActivity.java index 8fd4f6b..1493ab9 100644 --- a/tests/HwAccelerationTest/src/com/android/test/hwui/ListActivity.java +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ListActivity.java @@ -20,15 +20,19 @@ import android.app.Activity; import android.content.Context; import android.content.res.Resources; import android.os.Bundle; +import android.os.Environment; import android.util.DisplayMetrics; import android.view.ContextMenu; import android.view.View; +import android.view.ViewDebug; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.ListAdapter; import android.widget.ListView; import android.widget.TextView; +import java.io.File; + @SuppressWarnings({"UnusedDeclaration"}) public class ListActivity extends Activity { private static final String[] DATA_LIST = { @@ -87,6 +91,15 @@ public class ListActivity extends Activity { registerForContextMenu(list); } + + public void startProfiling(View v) { + ViewDebug.startLooperProfiling(new File(Environment.getExternalStorageDirectory(), + "looper.trace")); + } + + public void stopProfiling(View v) { + ViewDebug.stopLooperProfiling(); + } @Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { diff --git a/tools/aidl/Type.cpp b/tools/aidl/Type.cpp index a44072d..6b69864 100755 --- a/tools/aidl/Type.cpp +++ b/tools/aidl/Type.cpp @@ -198,7 +198,7 @@ Type::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int fl } void -Type::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel) +Type::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**) { fprintf(stderr, "aidl:internal error %s:%d qualifiedName=%s\n", __FILE__, __LINE__, m_qualifiedName.c_str()); @@ -207,7 +207,7 @@ Type::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel) } void -Type::ReadFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel) +Type::ReadFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**) { fprintf(stderr, "aidl:internal error %s:%d qualifiedName=%s\n", __FILE__, __LINE__, m_qualifiedName.c_str()); @@ -226,7 +226,7 @@ Type::WriteArrayToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, i void Type::CreateArrayFromParcel(StatementBlock* addTo, Variable* v, - Variable* parcel) + Variable* parcel, Variable**) { fprintf(stderr, "aidl:internal error %s:%d qualifiedName=%s\n", __FILE__, __LINE__, m_qualifiedName.c_str()); @@ -235,7 +235,7 @@ Type::CreateArrayFromParcel(StatementBlock* addTo, Variable* v, } void -Type::ReadArrayFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel) +Type::ReadArrayFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**) { fprintf(stderr, "aidl:internal error %s:%d qualifiedName=%s\n", __FILE__, __LINE__, m_qualifiedName.c_str()); @@ -284,7 +284,7 @@ BasicType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, i } void -BasicType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel) +BasicType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**) { addTo->Add(new Assignment(v, new MethodCall(parcel, m_unmarshallMethod))); } @@ -303,13 +303,13 @@ BasicType::WriteArrayToParcel(StatementBlock* addTo, Variable* v, Variable* parc void BasicType::CreateArrayFromParcel(StatementBlock* addTo, Variable* v, - Variable* parcel) + Variable* parcel, Variable**) { addTo->Add(new Assignment(v, new MethodCall(parcel, m_createArrayMethod))); } void -BasicType::ReadArrayFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel) +BasicType::ReadArrayFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**) { addTo->Add(new MethodCall(parcel, m_readArrayMethod, 1, v)); } @@ -331,7 +331,7 @@ BooleanType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, } void -BooleanType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel) +BooleanType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**) { addTo->Add(new Assignment(v, new Comparison(new LiteralExpression("0"), "!=", new MethodCall(parcel, "readInt")))); @@ -351,13 +351,13 @@ BooleanType::WriteArrayToParcel(StatementBlock* addTo, Variable* v, Variable* pa void BooleanType::CreateArrayFromParcel(StatementBlock* addTo, Variable* v, - Variable* parcel) + Variable* parcel, Variable**) { addTo->Add(new Assignment(v, new MethodCall(parcel, "createBooleanArray"))); } void -BooleanType::ReadArrayFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel) +BooleanType::ReadArrayFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**) { addTo->Add(new MethodCall(parcel, "readBooleanArray", 1, v)); } @@ -378,7 +378,7 @@ CharType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, in } void -CharType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel) +CharType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**) { addTo->Add(new Assignment(v, new MethodCall(parcel, "readInt"), this)); } @@ -397,13 +397,13 @@ CharType::WriteArrayToParcel(StatementBlock* addTo, Variable* v, Variable* parce void CharType::CreateArrayFromParcel(StatementBlock* addTo, Variable* v, - Variable* parcel) + Variable* parcel, Variable**) { addTo->Add(new Assignment(v, new MethodCall(parcel, "createCharArray"))); } void -CharType::ReadArrayFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel) +CharType::ReadArrayFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**) { addTo->Add(new MethodCall(parcel, "readCharArray", 1, v)); } @@ -428,7 +428,7 @@ StringType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, } void -StringType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel) +StringType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**) { addTo->Add(new Assignment(v, new MethodCall(parcel, "readString"))); } @@ -447,13 +447,13 @@ StringType::WriteArrayToParcel(StatementBlock* addTo, Variable* v, Variable* par void StringType::CreateArrayFromParcel(StatementBlock* addTo, Variable* v, - Variable* parcel) + Variable* parcel, Variable**) { addTo->Add(new Assignment(v, new MethodCall(parcel, "createStringArray"))); } void -StringType::ReadArrayFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel) +StringType::ReadArrayFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**) { addTo->Add(new MethodCall(parcel, "readStringArray", 1, v)); } @@ -496,7 +496,7 @@ CharSequenceType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* pa void CharSequenceType::CreateFromParcel(StatementBlock* addTo, Variable* v, - Variable* parcel) + Variable* parcel, Variable**) { // if (0 != parcel.readInt()) { // v = TextUtils.createFromParcel(parcel) @@ -532,7 +532,7 @@ RemoteExceptionType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* } void -RemoteExceptionType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel) +RemoteExceptionType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**) { fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__); } @@ -551,7 +551,7 @@ RuntimeExceptionType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable } void -RuntimeExceptionType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel) +RuntimeExceptionType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**) { fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__); } @@ -571,7 +571,7 @@ IBinderType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, } void -IBinderType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel) +IBinderType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**) { addTo->Add(new Assignment(v, new MethodCall(parcel, "readStrongBinder"))); } @@ -584,13 +584,13 @@ IBinderType::WriteArrayToParcel(StatementBlock* addTo, Variable* v, Variable* pa void IBinderType::CreateArrayFromParcel(StatementBlock* addTo, Variable* v, - Variable* parcel) + Variable* parcel, Variable**) { addTo->Add(new Assignment(v, new MethodCall(parcel, "createBinderArray"))); } void -IBinderType::ReadArrayFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel) +IBinderType::ReadArrayFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**) { addTo->Add(new MethodCall(parcel, "readBinderArray", 1, v)); } @@ -610,7 +610,7 @@ IInterfaceType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parc } void -IInterfaceType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel) +IInterfaceType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**) { fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__); } @@ -631,7 +631,7 @@ BinderType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, void BinderType::CreateFromParcel(StatementBlock* addTo, Variable* v, - Variable* parcel) + Variable* parcel, Variable**) { fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__); } @@ -652,7 +652,7 @@ BinderProxyType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* par void BinderProxyType::CreateFromParcel(StatementBlock* addTo, Variable* v, - Variable* parcel) + Variable* parcel, Variable**) { fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__); } @@ -672,7 +672,7 @@ ParcelType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, } void -ParcelType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel) +ParcelType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**) { fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__); } @@ -691,7 +691,7 @@ ParcelableInterfaceType::WriteToParcel(StatementBlock* addTo, Variable* v, Varia } void -ParcelableInterfaceType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel) +ParcelableInterfaceType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**) { fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__); } @@ -709,25 +709,31 @@ MapType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int addTo->Add(new MethodCall(parcel, "writeMap", 1, v)); } +static void EnsureClassLoader(StatementBlock* addTo, Variable** cl) +{ + // We don't want to look up the class loader once for every + // collection argument, so ensure we do it at most once per method. + if (*cl == NULL) { + *cl = new Variable(CLASSLOADER_TYPE, "cl"); + addTo->Add(new VariableDeclaration(*cl, + new LiteralExpression("this.getClass().getClassLoader()"), + CLASSLOADER_TYPE)); + } +} + void -MapType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel) +MapType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable** cl) { - Variable *cl = new Variable(CLASSLOADER_TYPE, "cl"); - addTo->Add(new VariableDeclaration(cl, - new LiteralExpression("this.getClass().getClassLoader()"), - CLASSLOADER_TYPE)); - addTo->Add(new Assignment(v, new MethodCall(parcel, "readHashMap", 1, cl))); + EnsureClassLoader(addTo, cl); + addTo->Add(new Assignment(v, new MethodCall(parcel, "readHashMap", 1, *cl))); } void MapType::ReadFromParcel(StatementBlock* addTo, Variable* v, - Variable* parcel) + Variable* parcel, Variable** cl) { - Variable *cl = new Variable(CLASSLOADER_TYPE, "cl"); - addTo->Add(new VariableDeclaration(cl, - new LiteralExpression("this.getClass().getClassLoader()"), - CLASSLOADER_TYPE)); - addTo->Add(new MethodCall(parcel, "readMap", 2, v, cl)); + EnsureClassLoader(addTo, cl); + addTo->Add(new MethodCall(parcel, "readMap", 2, v, *cl)); } @@ -751,24 +757,18 @@ ListType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, in } void -ListType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel) +ListType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable** cl) { - Variable *cl = new Variable(CLASSLOADER_TYPE, "cl"); - addTo->Add(new VariableDeclaration(cl, - new LiteralExpression("this.getClass().getClassLoader()"), - CLASSLOADER_TYPE)); - addTo->Add(new Assignment(v, new MethodCall(parcel, "readArrayList", 1, cl))); + EnsureClassLoader(addTo, cl); + addTo->Add(new Assignment(v, new MethodCall(parcel, "readArrayList", 1, *cl))); } void ListType::ReadFromParcel(StatementBlock* addTo, Variable* v, - Variable* parcel) + Variable* parcel, Variable** cl) { - Variable *cl = new Variable(CLASSLOADER_TYPE, "cl"); - addTo->Add(new VariableDeclaration(cl, - new LiteralExpression("this.getClass().getClassLoader()"), - CLASSLOADER_TYPE)); - addTo->Add(new MethodCall(parcel, "readList", 2, v, cl)); + EnsureClassLoader(addTo, cl); + addTo->Add(new MethodCall(parcel, "readList", 2, v, *cl)); } @@ -811,7 +811,7 @@ ParcelableType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parc } void -ParcelableType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel) +ParcelableType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**) { // if (0 != parcel.readInt()) { // v = CLASS.CREATOR.createFromParcel(parcel) @@ -833,7 +833,7 @@ ParcelableType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* p void ParcelableType::ReadFromParcel(StatementBlock* addTo, Variable* v, - Variable* parcel) + Variable* parcel, Variable**) { // TODO: really, we don't need to have this extra check, but we // don't have two separate marshalling code paths @@ -862,7 +862,7 @@ ParcelableType::WriteArrayToParcel(StatementBlock* addTo, Variable* v, Variable* void ParcelableType::CreateArrayFromParcel(StatementBlock* addTo, Variable* v, - Variable* parcel) + Variable* parcel, Variable**) { string creator = v->type->QualifiedName() + ".CREATOR"; addTo->Add(new Assignment(v, new MethodCall(parcel, @@ -870,7 +870,7 @@ ParcelableType::CreateArrayFromParcel(StatementBlock* addTo, Variable* v, } void -ParcelableType::ReadArrayFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel) +ParcelableType::ReadArrayFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**) { string creator = v->type->QualifiedName() + ".CREATOR"; addTo->Add(new MethodCall(parcel, "readTypedArray", 2, @@ -907,7 +907,7 @@ InterfaceType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parce } void -InterfaceType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel) +InterfaceType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**) { // v = Interface.asInterface(parcel.readStrongBinder()); string type = v->type->QualifiedName(); @@ -961,14 +961,14 @@ GenericType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, } void -GenericType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel) +GenericType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**) { fprintf(stderr, "implement GenericType::CreateFromParcel\n"); } void GenericType::ReadFromParcel(StatementBlock* addTo, Variable* v, - Variable* parcel) + Variable* parcel, Variable**) { fprintf(stderr, "implement GenericType::ReadFromParcel\n"); } @@ -1009,7 +1009,7 @@ GenericListType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* par } void -GenericListType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel) +GenericListType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**) { if (m_creator == STRING_TYPE->CreatorName()) { addTo->Add(new Assignment(v, @@ -1027,7 +1027,7 @@ GenericListType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* void GenericListType::ReadFromParcel(StatementBlock* addTo, Variable* v, - Variable* parcel) + Variable* parcel, Variable**) { if (m_creator == STRING_TYPE->CreatorName()) { addTo->Add(new MethodCall(parcel, "readStringList", 1, v)); diff --git a/tools/aidl/Type.h b/tools/aidl/Type.h index 2ea3ac9..662e3a2 100755 --- a/tools/aidl/Type.h +++ b/tools/aidl/Type.h @@ -46,18 +46,18 @@ public: virtual void WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags); virtual void CreateFromParcel(StatementBlock* addTo, Variable* v, - Variable* parcel); + Variable* parcel, Variable** cl); virtual void ReadFromParcel(StatementBlock* addTo, Variable* v, - Variable* parcel); + Variable* parcel, Variable** cl); virtual bool CanBeArray() const; virtual void WriteArrayToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags); virtual void CreateArrayFromParcel(StatementBlock* addTo, Variable* v, - Variable* parcel); + Variable* parcel, Variable** cl); virtual void ReadArrayFromParcel(StatementBlock* addTo, Variable* v, - Variable* parcel); + Variable* parcel, Variable** cl); protected: void SetQualifiedName(const string& qualified); @@ -89,16 +89,16 @@ public: virtual void WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags); virtual void CreateFromParcel(StatementBlock* addTo, Variable* v, - Variable* parcel); + Variable* parcel, Variable** cl); virtual bool CanBeArray() const; virtual void WriteArrayToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags); virtual void CreateArrayFromParcel(StatementBlock* addTo, Variable* v, - Variable* parcel); + Variable* parcel, Variable** cl); virtual void ReadArrayFromParcel(StatementBlock* addTo, Variable* v, - Variable* parcel); + Variable* parcel, Variable** cl); private: string m_marshallMethod; @@ -116,16 +116,16 @@ public: virtual void WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags); virtual void CreateFromParcel(StatementBlock* addTo, Variable* v, - Variable* parcel); + Variable* parcel, Variable** cl); virtual bool CanBeArray() const; virtual void WriteArrayToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags); virtual void CreateArrayFromParcel(StatementBlock* addTo, Variable* v, - Variable* parcel); + Variable* parcel, Variable** cl); virtual void ReadArrayFromParcel(StatementBlock* addTo, Variable* v, - Variable* parcel); + Variable* parcel, Variable** cl); }; class CharType : public Type @@ -136,16 +136,16 @@ public: virtual void WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags); virtual void CreateFromParcel(StatementBlock* addTo, Variable* v, - Variable* parcel); + Variable* parcel, Variable** cl); virtual bool CanBeArray() const; virtual void WriteArrayToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags); virtual void CreateArrayFromParcel(StatementBlock* addTo, Variable* v, - Variable* parcel); + Variable* parcel, Variable** cl); virtual void ReadArrayFromParcel(StatementBlock* addTo, Variable* v, - Variable* parcel); + Variable* parcel, Variable** cl); }; @@ -159,16 +159,16 @@ public: virtual void WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags); virtual void CreateFromParcel(StatementBlock* addTo, Variable* v, - Variable* parcel); + Variable* parcel, Variable** cl); virtual bool CanBeArray() const; virtual void WriteArrayToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags); virtual void CreateArrayFromParcel(StatementBlock* addTo, Variable* v, - Variable* parcel); + Variable* parcel, Variable** cl); virtual void ReadArrayFromParcel(StatementBlock* addTo, Variable* v, - Variable* parcel); + Variable* parcel, Variable** cl); }; class CharSequenceType : public Type @@ -181,7 +181,7 @@ public: virtual void WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags); virtual void CreateFromParcel(StatementBlock* addTo, Variable* v, - Variable* parcel); + Variable* parcel, Variable** cl); }; class RemoteExceptionType : public Type @@ -192,7 +192,7 @@ public: virtual void WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags); virtual void CreateFromParcel(StatementBlock* addTo, Variable* v, - Variable* parcel); + Variable* parcel, Variable** cl); }; class RuntimeExceptionType : public Type @@ -203,7 +203,7 @@ public: virtual void WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags); virtual void CreateFromParcel(StatementBlock* addTo, Variable* v, - Variable* parcel); + Variable* parcel, Variable** cl); }; class IBinderType : public Type @@ -214,14 +214,14 @@ public: virtual void WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags); virtual void CreateFromParcel(StatementBlock* addTo, Variable* v, - Variable* parcel); + Variable* parcel, Variable** cl); virtual void WriteArrayToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags); virtual void CreateArrayFromParcel(StatementBlock* addTo, Variable* v, - Variable* parcel); + Variable* parcel, Variable** cl); virtual void ReadArrayFromParcel(StatementBlock* addTo, Variable* v, - Variable* parcel); + Variable* parcel, Variable** cl); }; class IInterfaceType : public Type @@ -232,7 +232,7 @@ public: virtual void WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags); virtual void CreateFromParcel(StatementBlock* addTo, Variable* v, - Variable* parcel); + Variable* parcel, Variable** cl); }; class BinderType : public Type @@ -243,7 +243,7 @@ public: virtual void WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags); virtual void CreateFromParcel(StatementBlock* addTo, Variable* v, - Variable* parcel); + Variable* parcel, Variable** cl); }; class BinderProxyType : public Type @@ -254,7 +254,7 @@ public: virtual void WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags); virtual void CreateFromParcel(StatementBlock* addTo, Variable* v, - Variable* parcel); + Variable* parcel, Variable** cl); }; class ParcelType : public Type @@ -265,7 +265,7 @@ public: virtual void WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags); virtual void CreateFromParcel(StatementBlock* addTo, Variable* v, - Variable* parcel); + Variable* parcel, Variable** cl); }; class ParcelableInterfaceType : public Type @@ -276,7 +276,7 @@ public: virtual void WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags); virtual void CreateFromParcel(StatementBlock* addTo, Variable* v, - Variable* parcel); + Variable* parcel, Variable** cl); }; class MapType : public Type @@ -287,9 +287,9 @@ public: virtual void WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags); virtual void CreateFromParcel(StatementBlock* addTo, Variable* v, - Variable* parcel); + Variable* parcel, Variable** cl); virtual void ReadFromParcel(StatementBlock* addTo, Variable* v, - Variable* parcel); + Variable* parcel, Variable** cl); }; class ListType : public Type @@ -302,9 +302,9 @@ public: virtual void WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags); virtual void CreateFromParcel(StatementBlock* addTo, Variable* v, - Variable* parcel); + Variable* parcel, Variable** cl); virtual void ReadFromParcel(StatementBlock* addTo, Variable* v, - Variable* parcel); + Variable* parcel, Variable** cl); }; class ParcelableType : public Type @@ -318,18 +318,18 @@ public: virtual void WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags); virtual void CreateFromParcel(StatementBlock* addTo, Variable* v, - Variable* parcel); + Variable* parcel, Variable** cl); virtual void ReadFromParcel(StatementBlock* addTo, Variable* v, - Variable* parcel); + Variable* parcel, Variable** cl); virtual bool CanBeArray() const; virtual void WriteArrayToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags); virtual void CreateArrayFromParcel(StatementBlock* addTo, Variable* v, - Variable* parcel); + Variable* parcel, Variable** cl); virtual void ReadArrayFromParcel(StatementBlock* addTo, Variable* v, - Variable* parcel); + Variable* parcel, Variable** cl); }; class InterfaceType : public Type @@ -344,7 +344,7 @@ public: virtual void WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags); virtual void CreateFromParcel(StatementBlock* addTo, Variable* v, - Variable* parcel); + Variable* parcel, Variable** cl); private: bool m_oneway; @@ -364,9 +364,9 @@ public: virtual void WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags); virtual void CreateFromParcel(StatementBlock* addTo, Variable* v, - Variable* parcel); + Variable* parcel, Variable** cl); virtual void ReadFromParcel(StatementBlock* addTo, Variable* v, - Variable* parcel); + Variable* parcel, Variable** cl); private: string m_genericArguments; @@ -387,9 +387,9 @@ public: virtual void WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags); virtual void CreateFromParcel(StatementBlock* addTo, Variable* v, - Variable* parcel); + Variable* parcel, Variable** cl); virtual void ReadFromParcel(StatementBlock* addTo, Variable* v, - Variable* parcel); + Variable* parcel, Variable** cl); private: string m_creator; diff --git a/tools/aidl/aidl.cpp b/tools/aidl/aidl.cpp index f17f66b..92f5b64 100644 --- a/tools/aidl/aidl.cpp +++ b/tools/aidl/aidl.cpp @@ -948,8 +948,6 @@ preprocess_aidl(const Options& options) int main(int argc, const char **argv) { - int err = 0; - Options options; int result = parse_options(argc, argv, &options); if (result) { diff --git a/tools/aidl/generate_java.cpp b/tools/aidl/generate_java.cpp index 0f18132..83e3bbc 100644 --- a/tools/aidl/generate_java.cpp +++ b/tools/aidl/generate_java.cpp @@ -286,25 +286,25 @@ generate_write_to_parcel(Type* t, StatementBlock* addTo, Variable* v, static void generate_create_from_parcel(Type* t, StatementBlock* addTo, Variable* v, - Variable* parcel) + Variable* parcel, Variable** cl) { if (v->dimension == 0) { - t->CreateFromParcel(addTo, v, parcel); + t->CreateFromParcel(addTo, v, parcel, cl); } if (v->dimension == 1) { - t->CreateArrayFromParcel(addTo, v, parcel); + t->CreateArrayFromParcel(addTo, v, parcel, cl); } } static void generate_read_from_parcel(Type* t, StatementBlock* addTo, Variable* v, - Variable* parcel) + Variable* parcel, Variable** cl) { if (v->dimension == 0) { - t->ReadFromParcel(addTo, v, parcel); + t->ReadFromParcel(addTo, v, parcel, cl); } if (v->dimension == 1) { - t->ReadArrayFromParcel(addTo, v, parcel); + t->ReadArrayFromParcel(addTo, v, parcel, cl); } } @@ -362,6 +362,7 @@ generate_method(const method_type* method, Class* interface, "enforceInterface", 1, new LiteralExpression("DESCRIPTOR"))); // args + Variable* cl = NULL; VariableFactory stubArgs("_arg"); arg = method->args; while (arg != NULL) { @@ -373,7 +374,7 @@ generate_method(const method_type* method, Class* interface, if (convert_direction(arg->direction.data) & IN_PARAMETER) { generate_create_from_parcel(t, c->statements, v, - stubClass->transact_data); + stubClass->transact_data, &cl); } else { if (arg->type.dimension == 0) { c->statements->Add(new Assignment( @@ -531,7 +532,7 @@ generate_method(const method_type* method, Class* interface, if (_reply != NULL) { if (_result != NULL) { generate_create_from_parcel(proxy->returnType, - tryStatement->statements, _result, _reply); + tryStatement->statements, _result, _reply, &cl); } // the out/inout parameters @@ -541,7 +542,7 @@ generate_method(const method_type* method, Class* interface, Variable* v = new Variable(t, arg->name.data, arg->type.dimension); if (convert_direction(arg->direction.data) & OUT_PARAMETER) { generate_read_from_parcel(t, tryStatement->statements, - v, _reply); + v, _reply, &cl); } arg = arg->next; } diff --git a/wifi/java/android/net/wifi/WifiWatchdogService.java b/wifi/java/android/net/wifi/WifiWatchdogService.java deleted file mode 100644 index bce4b3a..0000000 --- a/wifi/java/android/net/wifi/WifiWatchdogService.java +++ /dev/null @@ -1,765 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * 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 android.content.BroadcastReceiver; -import android.content.ContentResolver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.database.ContentObserver; -import android.net.ConnectivityManager; -import android.net.DnsPinger; -import android.net.NetworkInfo; -import android.net.Uri; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.Looper; -import android.os.Message; -import android.os.SystemClock; -import android.provider.Settings; -import android.text.TextUtils; -import android.util.Slog; - -import java.io.BufferedInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.PrintWriter; -import java.net.HttpURLConnection; -import java.net.URL; -import java.util.HashSet; -import java.util.List; -import java.util.Scanner; - -/** - * {@link WifiWatchdogService} monitors the initial connection to a Wi-Fi - * network with multiple access points. After the framework successfully - * connects to an access point, the watchdog verifies connectivity by 'pinging' - * the configured DNS server using {@link DnsPinger}. - * <p> - * On DNS check failure, the BSSID is blacklisted if it is reasonably likely - * that another AP might have internet access; otherwise the SSID is disabled. - * <p> - * On DNS success, the WatchdogService initiates a walled garden check via an - * http get. A browser windows is activated if a walled garden is detected. - * - * @hide - */ -public class WifiWatchdogService { - - private static final String WWS_TAG = "WifiWatchdogService"; - - private static final boolean VDBG = true; - private static final boolean DBG = true; - - // Used for verbose logging - private String mDNSCheckLogStr; - - private Context mContext; - private ContentResolver mContentResolver; - private WifiManager mWifiManager; - - private WifiWatchdogHandler mHandler; - - private DnsPinger mDnsPinger; - - private IntentFilter mIntentFilter; - private BroadcastReceiver mBroadcastReceiver; - private boolean mBroadcastsEnabled; - - private static final int WIFI_SIGNAL_LEVELS = 4; - - /** - * Low signal is defined as less than or equal to cut off - */ - private static final int LOW_SIGNAL_CUTOFF = 0; - - private static final long MIN_LOW_SIGNAL_CHECK_INTERVAL = 2 * 60 * 1000; - private static final long MIN_SINGLE_DNS_CHECK_INTERVAL = 10 * 60 * 1000; - private static final long MIN_WALLED_GARDEN_INTERVAL = 15 * 60 * 1000; - - private static final int MAX_CHECKS_PER_SSID = 9; - private static final int NUM_DNS_PINGS = 7; - private static double MIN_RESPONSE_RATE = 0.50; - - // TODO : Adjust multiple DNS downward to 250 on repeated failure - // private static final int MULTI_DNS_PING_TIMEOUT_MS = 250; - - private static final int DNS_PING_TIMEOUT_MS = 800; - private static final long DNS_PING_INTERVAL = 250; - - private static final long BLACKLIST_FOLLOWUP_INTERVAL = 15 * 1000; - - private Status mStatus = new Status(); - - private static class Status { - String bssid = ""; - String ssid = ""; - - HashSet<String> allBssids = new HashSet<String>(); - int numFullDNSchecks = 0; - - long lastSingleCheckTime = -24 * 60 * 60 * 1000; - long lastWalledGardenCheckTime = -24 * 60 * 60 * 1000; - - WatchdogState state = WatchdogState.INACTIVE; - - // Info for dns check - int dnsCheckTries = 0; - int dnsCheckSuccesses = 0; - - public int signal = -200; - - } - - private enum WatchdogState { - /** - * Full DNS check in progress - */ - DNS_FULL_CHECK, - - /** - * Walled Garden detected, will pop up browser next round. - */ - WALLED_GARDEN_DETECTED, - - /** - * DNS failed, will blacklist/disable AP next round - */ - DNS_CHECK_FAILURE, - - /** - * Online or displaying walled garden auth page - */ - CHECKS_COMPLETE, - - /** - * Watchdog idle, network has been blacklisted or received disconnect - * msg - */ - INACTIVE, - - BLACKLISTED_AP - } - - public WifiWatchdogService(Context context) { - mContext = context; - mContentResolver = context.getContentResolver(); - mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); - mDnsPinger = new DnsPinger("WifiWatchdogServer.DnsPinger", context, - ConnectivityManager.TYPE_WIFI); - - HandlerThread handlerThread = new HandlerThread("WifiWatchdogServiceThread"); - handlerThread.start(); - mHandler = new WifiWatchdogHandler(handlerThread.getLooper()); - - setupNetworkReceiver(); - - // The content observer to listen needs a handler, which createThread - // creates - registerForSettingsChanges(); - - // Start things off - if (isWatchdogEnabled()) { - mHandler.sendEmptyMessage(WifiWatchdogHandler.MESSAGE_CONTEXT_EVENT); - } - } - - /** - * - */ - private void setupNetworkReceiver() { - mBroadcastReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) { - mHandler.sendMessage(mHandler.obtainMessage( - WifiWatchdogHandler.MESSAGE_NETWORK_EVENT, - intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO) - )); - } else if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) { - mHandler.sendEmptyMessage(WifiWatchdogHandler.RSSI_CHANGE_EVENT); - } else if (action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) { - mHandler.sendEmptyMessage(WifiWatchdogHandler.SCAN_RESULTS_AVAILABLE); - } else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) { - mHandler.sendMessage(mHandler.obtainMessage( - WifiWatchdogHandler.WIFI_STATE_CHANGE, - intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, 4))); - } - } - }; - - mIntentFilter = new IntentFilter(); - mIntentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); - mIntentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); - mIntentFilter.addAction(WifiManager.RSSI_CHANGED_ACTION); - mIntentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION); - } - - /** - * Observes the watchdog on/off setting, and takes action when changed. - */ - private void registerForSettingsChanges() { - ContentObserver contentObserver = new ContentObserver(mHandler) { - @Override - public void onChange(boolean selfChange) { - mHandler.sendEmptyMessage((WifiWatchdogHandler.MESSAGE_CONTEXT_EVENT)); - } - }; - - mContext.getContentResolver().registerContentObserver( - Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_ON), - false, contentObserver); - } - - private void handleNewConnection() { - WifiInfo wifiInfo = mWifiManager.getConnectionInfo(); - String newSsid = wifiInfo.getSSID(); - String newBssid = wifiInfo.getBSSID(); - - if (VDBG) { - Slog.v(WWS_TAG, String.format("handleConnected:: old (%s, %s) ==> new (%s, %s)", - mStatus.ssid, mStatus.bssid, newSsid, newBssid)); - } - - if (TextUtils.isEmpty(newSsid) || TextUtils.isEmpty(newBssid)) { - return; - } - - if (!TextUtils.equals(mStatus.ssid, newSsid)) { - mStatus = new Status(); - mStatus.ssid = newSsid; - } - - mStatus.bssid = newBssid; - mStatus.allBssids.add(newBssid); - mStatus.signal = WifiManager.calculateSignalLevel(wifiInfo.getRssi(), WIFI_SIGNAL_LEVELS); - - initDnsFullCheck(); - } - - public void updateRssi() { - WifiInfo wifiInfo = mWifiManager.getConnectionInfo(); - if (!TextUtils.equals(mStatus.ssid, wifiInfo.getSSID()) || - !TextUtils.equals(mStatus.bssid, wifiInfo.getBSSID())) { - return; - } - - mStatus.signal = WifiManager.calculateSignalLevel(wifiInfo.getRssi(), WIFI_SIGNAL_LEVELS); - } - - /** - * Single step in state machine - */ - private void handleStateStep() { - // Slog.v(WWS_TAG, "handleStateStep:: " + mStatus.state); - - switch (mStatus.state) { - case DNS_FULL_CHECK: - if (VDBG) { - Slog.v(WWS_TAG, "DNS_FULL_CHECK: " + mDNSCheckLogStr); - } - - long pingResponseTime = mDnsPinger.pingDns(mDnsPinger.getDns(), - DNS_PING_TIMEOUT_MS); - - mStatus.dnsCheckTries++; - if (pingResponseTime >= 0) - mStatus.dnsCheckSuccesses++; - - if (DBG) { - if (pingResponseTime >= 0) { - mDNSCheckLogStr += " | " + pingResponseTime; - } else { - mDNSCheckLogStr += " | " + "x"; - } - } - - switch (currentDnsCheckStatus()) { - case SUCCESS: - if (DBG) { - Slog.d(WWS_TAG, mDNSCheckLogStr + " -- Success"); - } - doWalledGardenCheck(); - break; - case FAILURE: - if (DBG) { - Slog.d(WWS_TAG, mDNSCheckLogStr + " -- Failure"); - } - mStatus.state = WatchdogState.DNS_CHECK_FAILURE; - break; - case INCOMPLETE: - // Taking no action - break; - } - break; - case DNS_CHECK_FAILURE: - WifiInfo wifiInfo = mWifiManager.getConnectionInfo(); - if (!mStatus.ssid.equals(wifiInfo.getSSID()) || - !mStatus.bssid.equals(wifiInfo.getBSSID())) { - Slog.i(WWS_TAG, "handleState DNS_CHECK_FAILURE:: network has changed!"); - mStatus.state = WatchdogState.INACTIVE; - break; - } - - if (mStatus.numFullDNSchecks >= mStatus.allBssids.size() || - mStatus.numFullDNSchecks >= MAX_CHECKS_PER_SSID) { - disableAP(wifiInfo); - } else { - blacklistAP(); - } - break; - case WALLED_GARDEN_DETECTED: - popUpBrowser(); - mStatus.state = WatchdogState.CHECKS_COMPLETE; - break; - case BLACKLISTED_AP: - WifiInfo wifiInfo2 = mWifiManager.getConnectionInfo(); - if (wifiInfo2.getSupplicantState() != SupplicantState.COMPLETED) { - Slog.d(WWS_TAG, - "handleState::BlacklistedAP - offline, but didn't get disconnect!"); - mStatus.state = WatchdogState.INACTIVE; - break; - } - if (mStatus.bssid.equals(wifiInfo2.getBSSID())) { - Slog.d(WWS_TAG, "handleState::BlacklistedAP - connected to same bssid"); - if (!handleSingleDnsCheck()) { - disableAP(wifiInfo2); - break; - } - } - - Slog.d(WWS_TAG, "handleState::BlacklistedAP - Simiulating a new connection"); - handleNewConnection(); - break; - } - } - - private void doWalledGardenCheck() { - if (!isWalledGardenTestEnabled()) { - if (VDBG) - Slog.v(WWS_TAG, "Skipping walled garden check - disabled"); - mStatus.state = WatchdogState.CHECKS_COMPLETE; - return; - } - long waitTime = waitTime(MIN_WALLED_GARDEN_INTERVAL, - mStatus.lastWalledGardenCheckTime); - if (waitTime > 0) { - if (VDBG) { - Slog.v(WWS_TAG, "Skipping walled garden check - wait " + - waitTime + " ms."); - } - mStatus.state = WatchdogState.CHECKS_COMPLETE; - return; - } - - mStatus.lastWalledGardenCheckTime = SystemClock.elapsedRealtime(); - if (isWalledGardenConnection()) { - if (DBG) - Slog.d(WWS_TAG, - "Walled garden test complete - walled garden detected"); - mStatus.state = WatchdogState.WALLED_GARDEN_DETECTED; - } else { - if (DBG) - Slog.d(WWS_TAG, "Walled garden test complete - online"); - mStatus.state = WatchdogState.CHECKS_COMPLETE; - } - } - - private boolean handleSingleDnsCheck() { - mStatus.lastSingleCheckTime = SystemClock.elapsedRealtime(); - long responseTime = mDnsPinger.pingDns(mDnsPinger.getDns(), - DNS_PING_TIMEOUT_MS); - if (DBG) { - Slog.d(WWS_TAG, "Ran a single DNS ping. Response time: " + responseTime); - } - if (responseTime < 0) { - return false; - } - return true; - - } - - /** - * @return Delay in MS before next single DNS check can proceed. - */ - private long timeToNextScheduledDNSCheck() { - if (mStatus.signal > LOW_SIGNAL_CUTOFF) { - return waitTime(MIN_SINGLE_DNS_CHECK_INTERVAL, mStatus.lastSingleCheckTime); - } else { - return waitTime(MIN_LOW_SIGNAL_CHECK_INTERVAL, mStatus.lastSingleCheckTime); - } - } - - /** - * Helper to return wait time left given a min interval and last run - * - * @param interval minimum wait interval - * @param lastTime last time action was performed in - * SystemClock.elapsedRealtime() - * @return non negative time to wait - */ - private static long waitTime(long interval, long lastTime) { - long wait = interval + lastTime - SystemClock.elapsedRealtime(); - return wait > 0 ? wait : 0; - } - - private void popUpBrowser() { - Uri uri = Uri.parse("http://www.google.com"); - Intent intent = new Intent(Intent.ACTION_VIEW, uri); - intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | - Intent.FLAG_ACTIVITY_NEW_TASK); - mContext.startActivity(intent); - } - - private void disableAP(WifiInfo info) { - // TODO : Unban networks if they had low signal ? - Slog.i(WWS_TAG, String.format("Disabling current SSID, %s [bssid %s]. " + - "numChecks %d, numAPs %d", mStatus.ssid, mStatus.bssid, - mStatus.numFullDNSchecks, mStatus.allBssids.size())); - mWifiManager.disableNetwork(info.getNetworkId()); - mStatus.state = WatchdogState.INACTIVE; - } - - private void blacklistAP() { - Slog.i(WWS_TAG, String.format("Blacklisting current BSSID %s [ssid %s]. " + - "numChecks %d, numAPs %d", mStatus.bssid, mStatus.ssid, - mStatus.numFullDNSchecks, mStatus.allBssids.size())); - - mWifiManager.addToBlacklist(mStatus.bssid); - mWifiManager.reassociate(); - mStatus.state = WatchdogState.BLACKLISTED_AP; - } - - /** - * Checks the scan for new BBIDs using current mSsid - */ - private void updateBssids() { - String curSsid = mStatus.ssid; - HashSet<String> bssids = mStatus.allBssids; - List<ScanResult> results = mWifiManager.getScanResults(); - int oldNumBssids = bssids.size(); - - if (results == null) { - if (VDBG) { - Slog.v(WWS_TAG, "updateBssids: Got null scan results!"); - } - return; - } - - for (ScanResult result : results) { - if (result != null && curSsid.equals(result.SSID)) - bssids.add(result.BSSID); - } - - // if (VDBG && bssids.size() - oldNumBssids > 0) { - // Slog.v(WWS_TAG, - // String.format("updateBssids:: Found %d new APs (total %d) on SSID %s", - // bssids.size() - oldNumBssids, bssids.size(), curSsid)); - // } - } - - enum DnsCheckStatus { - SUCCESS, - FAILURE, - INCOMPLETE - } - - /** - * Computes the current results of the dns check, ends early if outcome is - * assured. - */ - private DnsCheckStatus currentDnsCheckStatus() { - /** - * After a full ping count, if we have more responses than this cutoff, - * the outcome is success; else it is 'failure'. - */ - double pingResponseCutoff = MIN_RESPONSE_RATE * NUM_DNS_PINGS; - int remainingChecks = NUM_DNS_PINGS - mStatus.dnsCheckTries; - - /** - * Our final success count will be at least this big, so we're - * guaranteed to succeed. - */ - if (mStatus.dnsCheckSuccesses >= pingResponseCutoff) { - return DnsCheckStatus.SUCCESS; - } - - /** - * Our final count will be at most the current count plus the remaining - * pings - we're guaranteed to fail. - */ - if (remainingChecks + mStatus.dnsCheckSuccesses < pingResponseCutoff) { - return DnsCheckStatus.FAILURE; - } - - return DnsCheckStatus.INCOMPLETE; - } - - private void initDnsFullCheck() { - if (DBG) { - Slog.d(WWS_TAG, "Starting DNS pings at " + SystemClock.elapsedRealtime()); - } - mStatus.numFullDNSchecks++; - mStatus.dnsCheckSuccesses = 0; - mStatus.dnsCheckTries = 0; - mStatus.state = WatchdogState.DNS_FULL_CHECK; - - if (DBG) { - mDNSCheckLogStr = String.format("Dns Check %d. Pinging %s on ssid [%s]: ", - mStatus.numFullDNSchecks, mDnsPinger.getDns(), - mStatus.ssid); - } - } - - /** - * DNS based detection techniques do not work at all hotspots. The one sure - * way to check a walled garden is to see if a URL fetch on a known address - * fetches the data we expect - */ - private boolean isWalledGardenConnection() { - InputStream in = null; - HttpURLConnection urlConnection = null; - try { - URL url = new URL(getWalledGardenUrl()); - urlConnection = (HttpURLConnection) url.openConnection(); - in = new BufferedInputStream(urlConnection.getInputStream()); - Scanner scanner = new Scanner(in); - if (scanner.findInLine(getWalledGardenPattern()) != null) { - return false; - } else { - return true; - } - } catch (IOException e) { - return false; - } finally { - if (in != null) { - try { - in.close(); - } catch (IOException e) { - } - } - if (urlConnection != null) - urlConnection.disconnect(); - } - } - - /** - * There is little logic inside this class, instead methods of the form - * "handle___" are called in the main {@link WifiWatchdogService}. - */ - private class WifiWatchdogHandler extends Handler { - /** - * Major network event, object is NetworkInfo - */ - static final int MESSAGE_NETWORK_EVENT = 1; - /** - * Change in settings, no object - */ - static final int MESSAGE_CONTEXT_EVENT = 2; - - /** - * Change in signal strength - */ - static final int RSSI_CHANGE_EVENT = 3; - static final int SCAN_RESULTS_AVAILABLE = 4; - - static final int WIFI_STATE_CHANGE = 5; - - /** - * Single step of state machine. One DNS check, or one WalledGarden - * check, or one external action. We separate out external actions to - * increase chance of detecting that a check failure is caused by change - * in network status. Messages should have an arg1 which to sync status - * messages. - */ - static final int CHECK_SEQUENCE_STEP = 10; - static final int SINGLE_DNS_CHECK = 11; - - /** - * @param looper - */ - public WifiWatchdogHandler(Looper looper) { - super(looper); - } - - boolean singleCheckQueued = false; - long queuedSingleDnsCheckArrival; - - /** - * Sends a singleDnsCheck message with shortest time - guards against - * multiple. - */ - private boolean queueSingleDnsCheck() { - long delay = timeToNextScheduledDNSCheck(); - long newArrival = delay + SystemClock.elapsedRealtime(); - if (singleCheckQueued && queuedSingleDnsCheckArrival <= newArrival) - return true; - queuedSingleDnsCheckArrival = newArrival; - singleCheckQueued = true; - removeMessages(SINGLE_DNS_CHECK); - return sendMessageDelayed(obtainMessage(SINGLE_DNS_CHECK), delay); - } - - boolean checkSequenceQueued = false; - long queuedCheckSequenceArrival; - - /** - * Sends a state_machine_step message if the delay requested is lower - * than the current delay. - */ - private boolean sendCheckSequenceStep(long delay) { - long newArrival = delay + SystemClock.elapsedRealtime(); - if (checkSequenceQueued && queuedCheckSequenceArrival <= newArrival) - return true; - queuedCheckSequenceArrival = newArrival; - checkSequenceQueued = true; - removeMessages(CHECK_SEQUENCE_STEP); - return sendMessageDelayed(obtainMessage(CHECK_SEQUENCE_STEP), delay); - } - - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case CHECK_SEQUENCE_STEP: - checkSequenceQueued = false; - handleStateStep(); - if (mStatus.state == WatchdogState.CHECKS_COMPLETE) { - queueSingleDnsCheck(); - } else if (mStatus.state == WatchdogState.DNS_FULL_CHECK) { - sendCheckSequenceStep(DNS_PING_INTERVAL); - } else if (mStatus.state == WatchdogState.BLACKLISTED_AP) { - sendCheckSequenceStep(BLACKLIST_FOLLOWUP_INTERVAL); - } else if (mStatus.state != WatchdogState.INACTIVE) { - sendCheckSequenceStep(0); - } - return; - case MESSAGE_NETWORK_EVENT: - if (!mBroadcastsEnabled) { - Slog.e(WWS_TAG, - "MessageNetworkEvent - WatchdogService not enabled... returning"); - return; - } - NetworkInfo info = (NetworkInfo) msg.obj; - switch (info.getState()) { - case DISCONNECTED: - mStatus.state = WatchdogState.INACTIVE; - return; - case CONNECTED: - handleNewConnection(); - sendCheckSequenceStep(0); - } - return; - case SINGLE_DNS_CHECK: - singleCheckQueued = false; - if (mStatus.state != WatchdogState.CHECKS_COMPLETE) { - Slog.d(WWS_TAG, "Single check returning, curState: " + mStatus.state); - break; - } - - if (!handleSingleDnsCheck()) { - initDnsFullCheck(); - sendCheckSequenceStep(0); - } else { - queueSingleDnsCheck(); - } - - break; - case RSSI_CHANGE_EVENT: - updateRssi(); - if (mStatus.state == WatchdogState.CHECKS_COMPLETE) - queueSingleDnsCheck(); - break; - case SCAN_RESULTS_AVAILABLE: - updateBssids(); - break; - case WIFI_STATE_CHANGE: - if ((Integer) msg.obj == WifiManager.WIFI_STATE_DISABLING) { - Slog.i(WWS_TAG, "WifiStateDisabling -- Resetting WatchdogState"); - mStatus = new Status(); - } - break; - case MESSAGE_CONTEXT_EVENT: - if (isWatchdogEnabled() && !mBroadcastsEnabled) { - mContext.registerReceiver(mBroadcastReceiver, mIntentFilter); - mBroadcastsEnabled = true; - Slog.i(WWS_TAG, "WifiWatchdogService enabled"); - } else if (!isWatchdogEnabled() && mBroadcastsEnabled) { - mContext.unregisterReceiver(mBroadcastReceiver); - removeMessages(SINGLE_DNS_CHECK); - removeMessages(CHECK_SEQUENCE_STEP); - mBroadcastsEnabled = false; - Slog.i(WWS_TAG, "WifiWatchdogService disabled"); - } - break; - } - } - } - - public void dump(PrintWriter pw) { - pw.print("WatchdogStatus: "); - pw.print("State " + mStatus.state); - pw.println(", network [" + mStatus.ssid + ", " + mStatus.bssid + "]"); - pw.print("checkCount " + mStatus.numFullDNSchecks); - pw.println(", bssids: " + mStatus.allBssids); - pw.print(", hasCheckMessages? " + - mHandler.hasMessages(WifiWatchdogHandler.CHECK_SEQUENCE_STEP)); - pw.println(" hasSingleCheckMessages? " + - mHandler.hasMessages(WifiWatchdogHandler.SINGLE_DNS_CHECK)); - pw.println("DNS check log str: " + mDNSCheckLogStr); - pw.println("lastSingleCheck: " + mStatus.lastSingleCheckTime); - } - - /** - * @see android.provider.Settings.Secure#WIFI_WATCHDOG_WALLED_GARDEN_TEST_ENABLED - */ - private Boolean isWalledGardenTestEnabled() { - return Settings.Secure.getInt(mContentResolver, - Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_TEST_ENABLED, 1) == 1; - } - - /** - * @see android.provider.Settings.Secure#WIFI_WATCHDOG_WALLED_GARDEN_URL - */ - private String getWalledGardenUrl() { - String url = Settings.Secure.getString(mContentResolver, - Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_URL); - if (TextUtils.isEmpty(url)) - return "http://www.google.com/"; - return url; - } - - /** - * @see android.provider.Settings.Secure#WIFI_WATCHDOG_WALLED_GARDEN_PATTERN - */ - private String getWalledGardenPattern() { - String pattern = Settings.Secure.getString(mContentResolver, - Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_PATTERN); - if (TextUtils.isEmpty(pattern)) - return "<title>.*Google.*</title>"; - return pattern; - } - - /** - * @see android.provider.Settings.Secure#WIFI_WATCHDOG_ON - */ - private boolean isWatchdogEnabled() { - return Settings.Secure.getInt(mContentResolver, - Settings.Secure.WIFI_WATCHDOG_ON, 1) == 1; - } -} diff --git a/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java b/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java new file mode 100644 index 0000000..0eb73b7 --- /dev/null +++ b/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java @@ -0,0 +1,825 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.wifi; + +import android.content.BroadcastReceiver; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.database.ContentObserver; +import android.net.ConnectivityManager; +import android.net.DnsPinger; +import android.net.NetworkInfo; +import android.net.Uri; +import android.os.Message; +import android.os.SystemClock; +import android.provider.Settings; +import android.text.TextUtils; +import android.util.Slog; + +import com.android.internal.util.Protocol; +import com.android.internal.util.State; +import com.android.internal.util.StateMachine; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintWriter; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.HashSet; +import java.util.List; +import java.util.Scanner; + +/** + * {@link WifiWatchdogStateMachine} monitors the initial connection to a Wi-Fi + * network with multiple access points. After the framework successfully + * connects to an access point, the watchdog verifies connectivity by 'pinging' + * the configured DNS server using {@link DnsPinger}. + * <p> + * On DNS check failure, the BSSID is blacklisted if it is reasonably likely + * that another AP might have internet access; otherwise the SSID is disabled. + * <p> + * On DNS success, the WatchdogService initiates a walled garden check via an + * http get. A browser window is activated if a walled garden is detected. + * + * @hide + */ +public class WifiWatchdogStateMachine extends StateMachine { + + private static final boolean VDBG = false; + private static final boolean DBG = true; + private static final String WWSM_TAG = "WifiWatchdogStateMachine"; + + private static final int WIFI_SIGNAL_LEVELS = 4; + /** + * Low signal is defined as less than or equal to cut off + */ + private static final int LOW_SIGNAL_CUTOFF = 1; + + private static final long MIN_LOW_SIGNAL_CHECK_INTERVAL_MS = 2 * 60 * 1000; + private static final long MIN_SINGLE_DNS_CHECK_INTERVAL_MS = 10 * 60 * 1000; + private static final long MIN_WALLED_GARDEN_INTERVAL_MS = 30 * 60 * 1000; + + private static final int MAX_CHECKS_PER_SSID = 7; + private static final int NUM_DNS_PINGS = 5; + private static final double MIN_DNS_RESPONSE_RATE = 0.50; + + private static final int DNS_PING_TIMEOUT_MS = 800; + private static final long DNS_PING_INTERVAL_MS = 100; + + private static final long BLACKLIST_FOLLOWUP_INTERVAL_MS = 15 * 1000; + + private static final int BASE = Protocol.BASE_WIFI_WATCHDOG; + + /** + * Indicates the enable setting of WWS may have changed + */ + private static final int EVENT_WATCHDOG_TOGGLED = BASE + 1; + + /** + * Indicates the wifi network state has changed. Passed w/ original intent + * which has a non-null networkInfo object + */ + private static final int EVENT_NETWORK_STATE_CHANGE = BASE + 2; + /** + * Indicates the signal has changed. Passed with arg1 + * {@link #mNetEventCounter} and arg2 [raw signal strength] + */ + private static final int EVENT_RSSI_CHANGE = BASE + 3; + private static final int EVENT_SCAN_RESULTS_AVAILABLE = BASE + 4; + private static final int EVENT_WIFI_RADIO_STATE_CHANGE = BASE + 5; + + private static final int MESSAGE_CHECK_STEP = BASE + 100; + private static final int MESSAGE_HANDLE_WALLED_GARDEN = BASE + 101; + private static final int MESSAGE_HANDLE_BAD_AP = BASE + 102; + /** + * arg1 == mOnlineWatchState.checkCount + */ + private static final int MESSAGE_SINGLE_DNS_CHECK = BASE + 103; + private static final int MESSAGE_NETWORK_FOLLOWUP = BASE + 104; + + private Context mContext; + private ContentResolver mContentResolver; + private WifiManager mWifiManager; + private DnsPinger mDnsPinger; + private IntentFilter mIntentFilter; + private BroadcastReceiver mBroadcastReceiver; + + private DefaultState mDefaultState = new DefaultState(); + private WatchdogDisabledState mWatchdogDisabledState = new WatchdogDisabledState(); + private WatchdogEnabledState mWatchdogEnabledState = new WatchdogEnabledState(); + private NotConnectedState mNotConnectedState = new NotConnectedState(); + private ConnectedState mConnectedState = new ConnectedState(); + private DnsCheckingState mDnsCheckingState = new DnsCheckingState(); + private OnlineWatchState mOnlineWatchState = new OnlineWatchState(); + private DnsCheckFailureState mDnsCheckFailureState = new DnsCheckFailureState(); + private WalledGardenState mWalledGardenState = new WalledGardenState(); + private BlacklistedApState mBlacklistedApState = new BlacklistedApState(); + + /** + * The {@link WifiInfo} object passed to WWSM on network broadcasts + */ + private WifiInfo mInitialConnInfo; + private int mNetEventCounter = 0; + + /** + * Currently maintained but not used, TODO + */ + private HashSet<String> mBssids = new HashSet<String>(); + private int mNumFullDNSchecks = 0; + + private Long mLastWalledGardenCheckTime = null; + + /** + * This is set by the blacklisted state and reset when connected to a new AP. + * It triggers a disableNetwork call if a DNS check fails. + */ + public boolean mDisableAPNextFailure = false; + + /** + * STATE MAP + * Default + * / \ + * Disabled Enabled + * / \ + * Disconnected Connected + * /---------\ + * (all other states) + */ + private WifiWatchdogStateMachine(Context context) { + super(WWSM_TAG); + mContext = context; + mContentResolver = context.getContentResolver(); + mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); + mDnsPinger = new DnsPinger("WifiWatchdogServer.DnsPinger", context, + ConnectivityManager.TYPE_WIFI); + + setupNetworkReceiver(); + + // The content observer to listen needs a handler + registerForSettingsChanges(); + addState(mDefaultState); + addState(mWatchdogDisabledState, mDefaultState); + addState(mWatchdogEnabledState, mDefaultState); + addState(mNotConnectedState, mWatchdogEnabledState); + addState(mConnectedState, mWatchdogEnabledState); + addState(mDnsCheckingState, mConnectedState); + addState(mDnsCheckFailureState, mConnectedState); + addState(mWalledGardenState, mConnectedState); + addState(mBlacklistedApState, mConnectedState); + addState(mOnlineWatchState, mConnectedState); + + setInitialState(mWatchdogDisabledState); + } + + public static WifiWatchdogStateMachine makeWifiWatchdogStateMachine(Context context) { + WifiWatchdogStateMachine wwsm = new WifiWatchdogStateMachine(context); + wwsm.start(); + wwsm.sendMessage(EVENT_WATCHDOG_TOGGLED); + return wwsm; + } + + /** + * + */ + private void setupNetworkReceiver() { + mBroadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) { + sendMessage(EVENT_NETWORK_STATE_CHANGE, intent); + } else if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) { + obtainMessage(EVENT_RSSI_CHANGE, mNetEventCounter, + intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200)).sendToTarget(); + } else if (action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) { + sendMessage(EVENT_SCAN_RESULTS_AVAILABLE); + } else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) { + sendMessage(EVENT_WIFI_RADIO_STATE_CHANGE, + intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, + WifiManager.WIFI_STATE_UNKNOWN)); + } + } + }; + + mIntentFilter = new IntentFilter(); + mIntentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); + mIntentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); + mIntentFilter.addAction(WifiManager.RSSI_CHANGED_ACTION); + mIntentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION); + } + + /** + * Observes the watchdog on/off setting, and takes action when changed. + */ + private void registerForSettingsChanges() { + ContentObserver contentObserver = new ContentObserver(this.getHandler()) { + @Override + public void onChange(boolean selfChange) { + sendMessage(EVENT_WATCHDOG_TOGGLED); + } + }; + + mContext.getContentResolver().registerContentObserver( + Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_ON), + false, contentObserver); + } + + /** + * DNS based detection techniques do not work at all hotspots. The one sure + * way to check a walled garden is to see if a URL fetch on a known address + * fetches the data we expect + */ + private boolean isWalledGardenConnection() { + InputStream in = null; + HttpURLConnection urlConnection = null; + try { + URL url = new URL(getWalledGardenUrl()); + urlConnection = (HttpURLConnection) url.openConnection(); + in = new BufferedInputStream(urlConnection.getInputStream()); + Scanner scanner = new Scanner(in); + if (scanner.findInLine(getWalledGardenPattern()) != null) { + return false; + } else { + return true; + } + } catch (IOException e) { + return false; + } finally { + if (in != null) { + try { + in.close(); + } catch (IOException e) { + } + } + if (urlConnection != null) + urlConnection.disconnect(); + } + } + + private boolean rssiStrengthAboveCutoff(int rssi) { + return WifiManager.calculateSignalLevel(rssi, WIFI_SIGNAL_LEVELS) > LOW_SIGNAL_CUTOFF; + } + + public void dump(PrintWriter pw) { + pw.print("WatchdogStatus: "); + pw.print("State " + getCurrentState()); + pw.println(", network [" + mInitialConnInfo + "]"); + pw.print("checkCount " + mNumFullDNSchecks); + pw.println(", bssids: " + mBssids); + pw.println("lastSingleCheck: " + mOnlineWatchState.lastCheckTime); + } + + /** + * @see android.provider.Settings.Secure#WIFI_WATCHDOG_WALLED_GARDEN_TEST_ENABLED + */ + private Boolean isWalledGardenTestEnabled() { + return Settings.Secure.getInt(mContentResolver, + Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_TEST_ENABLED, 1) == 1; + } + + /** + * @see android.provider.Settings.Secure#WIFI_WATCHDOG_WALLED_GARDEN_URL + */ + private String getWalledGardenUrl() { + String url = Settings.Secure.getString(mContentResolver, + Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_URL); + if (TextUtils.isEmpty(url)) + return "http://www.google.com/"; + return url; + } + + /** + * @see android.provider.Settings.Secure#WIFI_WATCHDOG_WALLED_GARDEN_PATTERN + */ + private String getWalledGardenPattern() { + String pattern = Settings.Secure.getString(mContentResolver, + Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_PATTERN); + if (TextUtils.isEmpty(pattern)) + return "<title>.*Google.*</title>"; + return pattern; + } + + /** + * @see android.provider.Settings.Secure#WIFI_WATCHDOG_ON + */ + private boolean isWatchdogEnabled() { + return Settings.Secure.getInt(mContentResolver, + Settings.Secure.WIFI_WATCHDOG_ON, 1) == 1; + } + + + /** + * Helper to return wait time left given a min interval and last run + * + * @param interval minimum wait interval + * @param lastTime last time action was performed in + * SystemClock.elapsedRealtime(). Null if never. + * @return non negative time to wait + */ + private static long waitTime(long interval, Long lastTime) { + if (lastTime == null) + return 0; + long wait = interval + lastTime - SystemClock.elapsedRealtime(); + return wait > 0 ? wait : 0; + } + + private static String wifiInfoToStr(WifiInfo wifiInfo) { + if (wifiInfo == null) + return "null"; + return "(" + wifiInfo.getSSID() + ", " + wifiInfo.getBSSID() + ")"; + } + + /** + * + */ + private void resetWatchdogState() { + mInitialConnInfo = null; + mDisableAPNextFailure = false; + mLastWalledGardenCheckTime = null; + mNumFullDNSchecks = 0; + mBssids.clear(); + } + + private void popUpBrowser() { + Uri uri = Uri.parse("http://www.google.com"); + Intent intent = new Intent(Intent.ACTION_VIEW, uri); + intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | + Intent.FLAG_ACTIVITY_NEW_TASK); + mContext.startActivity(intent); + } + + private void sendCheckStepMessage(long delay) { + sendMessageDelayed(obtainMessage(MESSAGE_CHECK_STEP, mNetEventCounter, 0), delay); + } + + class DefaultState extends State { + @Override + public boolean processMessage(Message msg) { + if (VDBG) { + Slog.v(WWSM_TAG, "Caught message " + msg.what + " in state " + + getCurrentState().getName()); + } + return HANDLED; + } + } + + class WatchdogDisabledState extends State { + @Override + public boolean processMessage(Message msg) { + switch (msg.what) { + case EVENT_WATCHDOG_TOGGLED: + if (isWatchdogEnabled()) + transitionTo(mNotConnectedState); + return HANDLED; + } + return NOT_HANDLED; + } + } + + class WatchdogEnabledState extends State { + @Override + public void enter() { + resetWatchdogState(); + mContext.registerReceiver(mBroadcastReceiver, mIntentFilter); + Slog.i(WWSM_TAG, "WifiWatchdogService enabled"); + } + + @Override + public boolean processMessage(Message msg) { + switch (msg.what) { + case EVENT_WATCHDOG_TOGGLED: + if (!isWatchdogEnabled()) + transitionTo(mWatchdogDisabledState); + return HANDLED; + case EVENT_NETWORK_STATE_CHANGE: + Intent stateChangeIntent = (Intent) msg.obj; + NetworkInfo networkInfo = (NetworkInfo) + stateChangeIntent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO); + + switch (networkInfo.getState()) { + case CONNECTED: + // WifiInfo wifiInfo = (WifiInfo) + // stateChangeIntent + // .getParcelableExtra(WifiManager.EXTRA_WIFI_INFO); + // TODO : Replace with above code when API is changed + WifiInfo wifiInfo = mWifiManager.getConnectionInfo(); + if (wifiInfo == null) { + Slog.e(WWSM_TAG, "Connected --> WifiInfo object null!"); + return HANDLED; + } + + if (wifiInfo.getSSID() == null || wifiInfo.getBSSID() == null) { + Slog.e(WWSM_TAG, "Received wifiInfo object with null elts: " + + wifiInfoToStr(wifiInfo)); + return HANDLED; + } + + initConnection(wifiInfo); + transitionTo(mDnsCheckingState); + mNetEventCounter++; + return HANDLED; + case DISCONNECTED: + case DISCONNECTING: + mNetEventCounter++; + transitionTo(mNotConnectedState); + return HANDLED; + } + return HANDLED; + case EVENT_WIFI_RADIO_STATE_CHANGE: + if ((Integer) msg.obj == WifiManager.WIFI_STATE_DISABLING) { + Slog.i(WWSM_TAG, "WifiStateDisabling -- Resetting WatchdogState"); + resetWatchdogState(); + mNetEventCounter++; + transitionTo(mNotConnectedState); + } + return HANDLED; + } + + return NOT_HANDLED; + } + + /** + * @param wifiInfo Info object with non-null ssid and bssid + */ + private void initConnection(WifiInfo wifiInfo) { + if (VDBG) { + Slog.v(WWSM_TAG, "Connected:: old " + wifiInfoToStr(mInitialConnInfo) + + " ==> new " + wifiInfoToStr(wifiInfo)); + } + + if (mInitialConnInfo == null || !wifiInfo.getSSID().equals(mInitialConnInfo.getSSID())) { + resetWatchdogState(); + } else if (!wifiInfo.getBSSID().equals(mInitialConnInfo.getBSSID())) { + mDisableAPNextFailure = false; + } + mInitialConnInfo = wifiInfo; + } + + @Override + public void exit() { + mContext.unregisterReceiver(mBroadcastReceiver); + Slog.i(WWSM_TAG, "WifiWatchdogService disabled"); + } + } + + class NotConnectedState extends State { + } + + class ConnectedState extends State { + @Override + public boolean processMessage(Message msg) { + switch (msg.what) { + case EVENT_SCAN_RESULTS_AVAILABLE: + String curSsid = mInitialConnInfo.getSSID(); + List<ScanResult> results = mWifiManager.getScanResults(); + int oldNumBssids = mBssids.size(); + + if (results == null) { + if (DBG) { + Slog.d(WWSM_TAG, "updateBssids: Got null scan results!"); + } + return HANDLED; + } + + for (ScanResult result : results) { + if (result == null || result.SSID == null) { + if (VDBG) { + Slog.v(WWSM_TAG, "Received invalid scan result: " + result); + } + continue; + } + if (curSsid.equals(result.SSID)) + mBssids.add(result.BSSID); + } + return HANDLED; + } + return NOT_HANDLED; + } + + } + + class DnsCheckingState extends State { + int dnsCheckTries = 0; + int dnsCheckSuccesses = 0; + String dnsCheckLogStr = ""; + + @Override + public void enter() { + mNumFullDNSchecks++; + dnsCheckSuccesses = 0; + dnsCheckTries = 0; + if (DBG) { + Slog.d(WWSM_TAG, "Starting DNS pings at " + SystemClock.elapsedRealtime()); + dnsCheckLogStr = String.format("Dns Check %d. Pinging %s on ssid [%s]: ", + mNumFullDNSchecks, mDnsPinger.getDns(), mInitialConnInfo.getSSID()); + } + + sendCheckStepMessage(0); + } + + @Override + public boolean processMessage(Message msg) { + if (msg.what != MESSAGE_CHECK_STEP) { + return NOT_HANDLED; + } + if (msg.arg1 != mNetEventCounter) { + Slog.d(WWSM_TAG, "Check step out of sync, ignoring..."); + return HANDLED; + } + + long pingResponseTime = mDnsPinger.pingDns(mDnsPinger.getDns(), + DNS_PING_TIMEOUT_MS); + + dnsCheckTries++; + if (pingResponseTime >= 0) + dnsCheckSuccesses++; + + if (DBG) { + if (pingResponseTime >= 0) { + dnsCheckLogStr += "|" + pingResponseTime; + } else { + dnsCheckLogStr += "|x"; + } + } + + if (VDBG) { + Slog.v(WWSM_TAG, dnsCheckLogStr); + } + + /** + * After a full ping count, if we have more responses than this + * cutoff, the outcome is success; else it is 'failure'. + */ + double pingResponseCutoff = MIN_DNS_RESPONSE_RATE * NUM_DNS_PINGS; + int remainingChecks = NUM_DNS_PINGS - dnsCheckTries; + + /** + * Our final success count will be at least this big, so we're + * guaranteed to succeed. + */ + if (dnsCheckSuccesses >= pingResponseCutoff) { + // DNS CHECKS OK, NOW WALLED GARDEN + if (DBG) { + Slog.d(WWSM_TAG, dnsCheckLogStr + "| SUCCESS"); + } + + if (!shouldCheckWalledGarden()) { + transitionTo(mOnlineWatchState); + return HANDLED; + } + + mLastWalledGardenCheckTime = SystemClock.elapsedRealtime(); + if (isWalledGardenConnection()) { + if (DBG) + Slog.d(WWSM_TAG, + "Walled garden test complete - walled garden detected"); + transitionTo(mWalledGardenState); + } else { + if (DBG) + Slog.d(WWSM_TAG, "Walled garden test complete - online"); + transitionTo(mOnlineWatchState); + } + return HANDLED; + } + + /** + * Our final count will be at most the current count plus the + * remaining pings - we're guaranteed to fail. + */ + if (remainingChecks + dnsCheckSuccesses < pingResponseCutoff) { + if (DBG) { + Slog.d(WWSM_TAG, dnsCheckLogStr + "| FAILURE"); + } + transitionTo(mDnsCheckFailureState); + return HANDLED; + } + + // Still in dns check step + sendCheckStepMessage(DNS_PING_INTERVAL_MS); + return HANDLED; + } + + private boolean shouldCheckWalledGarden() { + if (!isWalledGardenTestEnabled()) { + if (VDBG) + Slog.v(WWSM_TAG, "Skipping walled garden check - disabled"); + return false; + } + long waitTime = waitTime(MIN_WALLED_GARDEN_INTERVAL_MS, + mLastWalledGardenCheckTime); + if (waitTime > 0) { + if (DBG) { + Slog.d(WWSM_TAG, "Skipping walled garden check - wait " + + waitTime + " ms."); + } + return false; + } + return true; + } + + } + + class OnlineWatchState extends State { + /** + * Signals a short-wait message is enqueued for the current 'guard' counter + */ + boolean unstableSignalChecks = false; + + /** + * The signal is unstable. We should enqueue a short-wait check, if one is enqueued + * already + */ + boolean signalUnstable = false; + + /** + * A monotonic counter to ensure that at most one check message will be processed from any + * set of check messages currently enqueued. Avoids duplicate checks when a low-signal + * event is observed. + */ + int checkGuard = 0; + Long lastCheckTime = null; + + @Override + public void enter() { + lastCheckTime = SystemClock.elapsedRealtime(); + signalUnstable = false; + checkGuard++; + unstableSignalChecks = false; + triggerSingleDnsCheck(); + } + + @Override + public boolean processMessage(Message msg) { + switch (msg.what) { + case EVENT_RSSI_CHANGE: + if (msg.arg1 != mNetEventCounter) { + if (DBG) { + Slog.d(WWSM_TAG, "Rssi change message out of sync, ignoring"); + } + return HANDLED; + } + int newRssi = msg.arg2; + signalUnstable = !rssiStrengthAboveCutoff(newRssi); + if (VDBG) { + Slog.v(WWSM_TAG, "OnlineWatchState:: new rssi " + newRssi + " --> level " + + WifiManager.calculateSignalLevel(newRssi, WIFI_SIGNAL_LEVELS)); + } + + if (signalUnstable && !unstableSignalChecks) { + if (VDBG) { + Slog.v(WWSM_TAG, "Sending triggered check msg"); + } + triggerSingleDnsCheck(); + } + return HANDLED; + case MESSAGE_SINGLE_DNS_CHECK: + if (msg.arg1 != checkGuard) { + if (VDBG) { + Slog.v(WWSM_TAG, "Single check msg out of sync, ignoring."); + } + return HANDLED; + } + lastCheckTime = SystemClock.elapsedRealtime(); + long responseTime = mDnsPinger.pingDns(mDnsPinger.getDns(), + DNS_PING_TIMEOUT_MS); + if (responseTime >= 0) { + if (VDBG) { + Slog.v(WWSM_TAG, "Ran a single DNS ping. Response time: " + + responseTime); + } + + checkGuard++; + unstableSignalChecks = false; + triggerSingleDnsCheck(); + } else { + if (DBG) { + Slog.d(WWSM_TAG, "Single dns ping failure. Starting full checks."); + } + transitionTo(mDnsCheckingState); + } + return HANDLED; + } + return NOT_HANDLED; + } + + /** + * Times a dns check with an interval based on {@link #curSignalStable} + */ + private void triggerSingleDnsCheck() { + long waitInterval; + if (signalUnstable) { + waitInterval = MIN_LOW_SIGNAL_CHECK_INTERVAL_MS; + unstableSignalChecks = true; + } else { + waitInterval = MIN_SINGLE_DNS_CHECK_INTERVAL_MS; + } + sendMessageDelayed(obtainMessage(MESSAGE_SINGLE_DNS_CHECK, checkGuard, 0), + waitTime(waitInterval, lastCheckTime)); + } + } + + class DnsCheckFailureState extends State { + @Override + public void enter() { + obtainMessage(MESSAGE_HANDLE_BAD_AP, mNetEventCounter, 0).sendToTarget(); + } + + @Override + public boolean processMessage(Message msg) { + if (msg.what != MESSAGE_HANDLE_BAD_AP) { + return NOT_HANDLED; + } + + if (msg.arg1 != mNetEventCounter) { + if (VDBG) { + Slog.v(WWSM_TAG, "Msg out of sync, ignoring..."); + } + return HANDLED; + } + + if (mDisableAPNextFailure || mNumFullDNSchecks >= MAX_CHECKS_PER_SSID) { + // TODO : Unban networks if they had low signal ? + Slog.i(WWSM_TAG, "Disabling current SSID " + wifiInfoToStr(mInitialConnInfo) + + ". " + + "numChecks " + mNumFullDNSchecks + ", numAPs " + mBssids.size()); + mWifiManager.disableNetwork(mInitialConnInfo.getNetworkId()); + transitionTo(mNotConnectedState); + } else { + Slog.i(WWSM_TAG, "Blacklisting current BSSID. " + wifiInfoToStr(mInitialConnInfo) + + "numChecks " + mNumFullDNSchecks + ", numAPs " + mBssids.size()); + + mWifiManager.addToBlacklist(mInitialConnInfo.getBSSID()); + mWifiManager.reassociate(); + transitionTo(mBlacklistedApState); + } + return HANDLED; + } + } + + class WalledGardenState extends State { + @Override + public void enter() { + obtainMessage(MESSAGE_HANDLE_WALLED_GARDEN, mNetEventCounter, 0).sendToTarget(); + } + + @Override + public boolean processMessage(Message msg) { + if (msg.what != MESSAGE_HANDLE_WALLED_GARDEN) { + return NOT_HANDLED; + } + + if (msg.arg1 != mNetEventCounter) { + if (VDBG) { + Slog.v(WWSM_TAG, "WalledGardenState::Msg out of sync, ignoring..."); + } + return HANDLED; + } + popUpBrowser(); + transitionTo(mOnlineWatchState); + return HANDLED; + } + } + + class BlacklistedApState extends State { + @Override + public void enter() { + mDisableAPNextFailure = true; + sendMessageDelayed(obtainMessage(MESSAGE_NETWORK_FOLLOWUP, mNetEventCounter, 0), + BLACKLIST_FOLLOWUP_INTERVAL_MS); + } + + @Override + public boolean processMessage(Message msg) { + if (msg.what != MESSAGE_NETWORK_FOLLOWUP) { + return NOT_HANDLED; + } + + if (msg.arg1 != mNetEventCounter) { + if (VDBG) { + Slog.v(WWSM_TAG, "BlacklistedApState::Msg out of sync, ignoring..."); + } + return HANDLED; + } + + transitionTo(mDnsCheckingState); + return HANDLED; + } + } +} |