Merge "Updated some effects and notifications"
diff --git a/api/current.txt b/api/current.txt
index 5418072..3503fb3 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -18024,7 +18024,6 @@
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();
@@ -22080,6 +22079,7 @@
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();
diff --git a/cmds/ip-up-vpn/ip-up-vpn.c b/cmds/ip-up-vpn/ip-up-vpn.c
index bbf6b14e..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");
+ FILE *state = fopen(DIR ".tmp", "wb");
+ if (!state) {
+ LOGE("Cannot create state: %s", strerror(errno));
+ return 1;
+ }
- snprintf(dns, sizeof(dns), "%s %s", dns1 ? dns1 : "", dns2 ? dns2 : "");
- property_set("vpn.dns", dns);
- property_set("vpn.via", argv[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/inputmethodservice/ExtractEditText.java b/core/java/android/inputmethodservice/ExtractEditText.java
index 3447e76c..44e7e52 100644
--- a/core/java/android/inputmethodservice/ExtractEditText.java
+++ b/core/java/android/inputmethodservice/ExtractEditText.java
@@ -98,11 +98,8 @@
}
@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 @@
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 @@
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 @@
* 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 @@
* 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 @@
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 @@
*/
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 @@
return false;
}
+ /** {@hide} */
public int getMatchRule() {
return mMatchRule;
}
+ /** {@hide} */
public String getSubscriberId() {
return mSubscriberId;
}
@@ -131,6 +162,8 @@
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 @@
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 @@
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 @@
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 @@
destination.getNetworkPrefixLength()), destination.getNetworkPrefixLength());
mGateway = gateway;
mIsDefault = isDefault();
+ mIsHost = isHost();
}
public RouteInfo(InetAddress gateway) {
@@ -88,6 +90,10 @@
}
}
+ 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 @@
return val;
}
+
public LinkAddress getDestination() {
return mDestination;
}
@@ -112,6 +119,10 @@
return mIsDefault;
}
+ public boolean isHostRoute() {
+ return mIsHost;
+ }
+
public String toString() {
String val = "";
if (mDestination != null) val = mDestination.toString();
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 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 @@
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 @@
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 @@
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 @@
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 @@
}
}
- @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 @@
// 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 @@
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..d70c371d 100644
--- a/core/java/android/speech/tts/SynthesisCallback.java
+++ b/core/java/android/speech/tts/SynthesisCallback.java
@@ -22,19 +22,16 @@
* {@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 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 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 @@
* 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 @@
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 @@
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 @@
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.ReplacementSpan;
import android.text.style.TabStopSpan;
+import com.android.internal.util.ArrayUtils;
+
import java.util.Arrays;
/**
@@ -113,6 +113,29 @@
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 @@
mSpacingMult = spacingMult;
mSpacingAdd = spacingAdd;
mSpannedText = text instanceof Spanned;
+ mTextDir = textDir;
}
/**
@@ -531,6 +555,14 @@
}
/**
+ * 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 @@
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 @@
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 @@
* 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 @@
}
}
- 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.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 @@
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 @@
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 @@
: (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 @@
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 @@
/* 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 @@
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 @@
}
}
- 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.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 @@
* 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 @@
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 @@
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 @@
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 @@
}
// 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 @@
}
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.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.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 @@
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 @@
/**
* 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 @@
/**
* 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 @@
* {@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 @@
@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 @@
// 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 @@
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 @@
*
* @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..d539a03 100644
--- a/core/java/android/view/ViewAncestor.java
+++ b/core/java/android/view/ViewAncestor.java
@@ -2634,8 +2634,9 @@
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 +2654,9 @@
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 +2669,10 @@
}
// 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 +2684,6 @@
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/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.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 @@
}
}
- /**
- * 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/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 @@
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 @@
setSelectedPositionInt(INVALID_POSITION);
setNextSelectedPositionInt(INVALID_POSITION);
mSelectedTop = 0;
- mSelectorShowing = false;
mSelectorPosition = INVALID_POSITION;
mSelectorRect.setEmpty();
invalidate();
@@ -2025,7 +2018,7 @@
final boolean isChildViewEnabled = mIsChildViewEnabled;
if (sel.isEnabled() != isChildViewEnabled) {
mIsChildViewEnabled = !isChildViewEnabled;
- if (mSelectorShowing) {
+ if (getSelectedItemPosition() != INVALID_POSITION) {
refreshDrawableState();
}
}
@@ -2769,6 +2762,7 @@
// 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 @@
}
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 @@
}
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 @@
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 @@
setSelectedPositionInt(INVALID_POSITION);
setNextSelectedPositionInt(INVALID_POSITION);
mSelectedTop = 0;
- mSelectorShowing = false;
}
}
@@ -4645,6 +4638,9 @@
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 @@
@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 @@
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 @@
// 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 @@
// 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 @@
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);
+ 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();
}
-
- // 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;
} 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 @@
static final float MAX_SCROLL_FACTOR = 0.5f;
-
private long mLastScroll;
private final Rect mTempRect = new Rect();
@@ -506,13 +505,6 @@
@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 @@
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 6b4e454..ecbd997 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.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.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 @@
if (curs >= 0) {
mHighlightPathBogus = true;
makeBlink();
+ bringPointIntoView(curs);
}
checkForResize();
@@ -5992,14 +6000,17 @@
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 @@
} 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 @@
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 @@
} 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 @@
BoringLayout.Metrics boring = UNKNOWN_BORING;
BoringLayout.Metrics hintBoring = UNKNOWN_BORING;
+ if (mTextDir == null) {
+ resolveTextDirection();
+ }
+
int des = -1;
boolean fromexisting = false;
@@ -6231,7 +6246,7 @@
}
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 @@
@Override
public void onClick(View v) {
if (canPaste()) {
- paste(getSelectionStart(), getSelectionEnd());
+ onTextContextMenuItem(ID_PASTE);
}
hide();
}
@@ -10439,11 +10454,21 @@
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 @@
*/
@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 @@
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 @@
@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
similarity index 99%
rename from services/java/com/android/server/ProcessStats.java
rename to 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 String baseName;
public String name;
- int nameWidth;
+ public int nameWidth;
public long base_uptime;
public long rel_uptime;
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 @@
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/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 @@
// 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 @@
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 @@
// 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 @@
// 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 @@
// 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 @@
*/
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 @@
// 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 @@
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 @@
}
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 @@
}
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 @@
}
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 @@
}
}
- 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 @@
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/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 @@
GET_ALLOCATOR,
QUERY,
SET_SYNCHRONOUS_MODE,
+ CONNECT,
+ DISCONNECT,
};
@@ -154,7 +156,23 @@
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 @@
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 @@
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 @@
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 @@
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::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/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::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 @@
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 @@
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 @@
"[%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 @@
// 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 @@
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 @@
}
}
} 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 @@
// 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 @@
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 @@
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 @@
}
void AwesomePlayer::reset() {
- LOGI("reset");
Mutex::Autolock autoLock(mLock);
reset_l();
}
@@ -467,10 +466,8 @@
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 @@
mPreparedCondition.wait(mLock);
}
- LOGI("cancel player events");
cancelPlayerEvents();
mWVMExtractor.clear();
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index b7b0dc0f..5cab60e 100755
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -3539,7 +3539,7 @@
}
status_t OMXCodec::stop() {
- CODEC_LOGI("stop mState=%d", mState);
+ CODEC_LOGV("stop mState=%d", mState);
Mutex::Autolock autoLock(mLock);
@@ -3601,7 +3601,6 @@
mLeftOverBuffer = NULL;
}
- CODEC_LOGI("stopping video source");
mSource->stop();
CODEC_LOGI("stopped in state %d", mState);
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 @@
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/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
similarity index 98%
rename from services/java/com/android/server/LoadAverageService.java
rename to 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/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 @@
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 void afterTextChanged(Editable s) {
- mCallback.pokeWakelock();
+ if (!mResuming) {
+ mCallback.pokeWakelock();
+ }
}
});
}
@@ -208,6 +211,7 @@
/** {@inheritDoc} */
public void onResume() {
+ mResuming = true;
// reset status
mStatusView.resetStatusInfo(mUpdateMonitor, mLockPatternUtils);
@@ -222,6 +226,7 @@
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 @@
}
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/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 @@
| 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 @@
}
int32_t motionEventAction;
- int32_t motionEventEdgeFlags;
int32_t lastButtonState, currentButtonState;
PointerProperties pointerProperties;
PointerCoords pointerCoords;
@@ -1697,8 +1677,6 @@
}
}
- motionEventEdgeFlags = AMOTION_EVENT_EDGE_FLAG_NONE;
-
pointerProperties.clear();
pointerProperties.id = 0;
pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_MOUSE;
@@ -1742,11 +1720,6 @@
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 @@
// 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 @@
}
// 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 @@
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::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 @@
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 @@
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 @@
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/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.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 @@
@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.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 @@
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 @@
mNetConfigs[netType].radio);
continue;
}
- mCurrentLinkProperties[netType] = mNetTrackers[netType].getLinkProperties();
+ mCurrentLinkProperties[netType] = null;
}
IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
@@ -1053,62 +1057,71 @@
}
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;
-
- 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");
- 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 (gatewayAddress != null) {
- if (cycleCount > MAX_HOSTROUTE_CYCLE_COUNT) {
- loge("Error adding hostroute - too much recursion");
- 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;
- }
+ private boolean addRoute(LinkProperties p, RouteInfo r) {
+ return modifyRoute(p.getInterfaceName(), p, r, 0, true);
}
- // 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;
+ 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;
+ }
+
+ 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 (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;
+ }
+ } 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");
+ }
+ }
+ return true;
}
/**
@@ -1157,9 +1170,6 @@
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 @@
*/
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 @@
"\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 (linkAddr.getAddress() instanceof Inet6Address) {
- resetMask |= NetworkUtils.RESET_IPV6_ADDRESSES;
+ if (DBG) {
+ log("handleConnectivityChange: addresses changed" +
+ " linkProperty[" + netType + "]:" + " resetMask=" + resetMask +
+ "\n car=" + car);
}
- }
- 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);
+ }
}
} 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 @@
}
}
- private void addPrivateDnsRoutes(NetworkStateTracker nt) {
- boolean privateDnsRouteSet = nt.isPrivateDnsRouteSet();
- LinkProperties p = nt.getLinkProperties();
- if (p == null) return;
- String interfaceName = p.getInterfaceName();
+ /**
+ * 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 (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);
- }
- nt.privateDnsRouteSet(true);
- }
- }
+ if (curLp != null) {
+ // check for the delta between the current set and the new
+ CompareResult<RouteInfo> routeDiff = curLp.compareRoutes(newLp);
+ dnsDiff = curLp.compareDnses(newLp);
- 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 + ")");
- }
-
- 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 : routeDiff.removed) {
+ if (isLinkDefault || ! r.isDefaultRoute()) {
+ removeRoute(curLp, r);
}
}
- nt.privateDnsRouteSet(false);
+ routesToAdd = routeDiff.added;
}
- }
+ if (newLp != null) {
+ // if we didn't get a diff from cur -> new, then just use the new
+ if (routesToAdd == null) {
+ routesToAdd = newLp.getRoutes();
+ }
- private void addDefaultRoute(NetworkStateTracker nt) {
- LinkProperties p = nt.getLinkProperties();
- if (p == null) return;
- String interfaceName = p.getInterfaceName();
- if (TextUtils.isEmpty(interfaceName)) return;
+ for (RouteInfo r : routesToAdd) {
+ if (isLinkDefault || ! r.isDefaultRoute()) {
+ addRoute(newLp, r);
+ }
+ }
+ }
- 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));
+ }
+ }
+ if (newLp != null) {
+ if (dnsToAdd == null) {
+ dnsToAdd = newLp.getDnses();
+ }
+ for(InetAddress dnsAddress : dnsToAdd) {
+ addRoute(newLp, 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 (DBG) {
- NetworkInfo networkInfo = nt.getNetworkInfo();
- log("removeDefaultRoute for " + networkInfo.getTypeName() + " (" +
- interfaceName + ")");
- }
- }
- }
- }
-
/**
* 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 @@
* @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 @@
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/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 @@
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.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 @@
/**
* 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 @@
}
// 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,18 +222,24 @@
}
// INetworkManagementEventObserver.Stub
- public void interfaceStatusChanged(String interfaze, boolean up) {
- }
-
- // INetworkManagementEventObserver.Stub
- public void interfaceLinkStateChanged(String interfaze, boolean up) {
- }
-
- // INetworkManagementEventObserver.Stub
public void interfaceAdded(String interfaze) {
}
// INetworkManagementEventObserver.Stub
+ public synchronized void interfaceStatusChanged(String interfaze, boolean up) {
+ if (!up && mLegacyVpnRunner != null) {
+ mLegacyVpnRunner.check(interfaze);
+ }
+ }
+
+ // INetworkManagementEventObserver.Stub
+ public synchronized void interfaceLinkStateChanged(String interfaze, boolean up) {
+ if (!up && mLegacyVpnRunner != null) {
+ mLegacyVpnRunner.check(interfaze);
+ }
+ }
+
+ // INetworkManagementEventObserver.Stub
public synchronized void interfaceRemoved(String interfaze) {
if (interfaze.equals(mInterface) && jniCheck(interfaze) == 0) {
mCallback.restore();
@@ -279,8 +285,10 @@
}
}
- 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 @@
*/
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 @@
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 @@
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 @@
}
}
- // 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 @@
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 @@
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(" "));
+ }
+ }
- // The final step must be synchronized.
+ // Set the routes.
+ jniSetRoutes(mConfig.interfaze, mConfig.routes);
+
+ // 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_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.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 @@
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.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 @@
// 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 @@
} catch (IOException e) {
Slog.e(TAG, "problem reading network stats", e);
} finally {
- IoUtils.closeQuietly(fis);
+ IoUtils.closeQuietly(in);
}
}
@@ -768,10 +768,9 @@
// 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 @@
} catch (IOException e) {
Slog.e(TAG, "problem reading uid stats", e);
} finally {
- IoUtils.closeQuietly(fis);
+ IoUtils.closeQuietly(in);
}
}
@@ -838,7 +837,7 @@
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 @@
history.writeToStream(out);
}
+ out.flush();
mNetworkFile.finishWrite(fos);
} catch (IOException e) {
if (fos != null) {
@@ -871,7 +871,7 @@
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 @@
}
}
+ 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 @@
#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 @@
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 @@
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 @@
}
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 @@
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 @@
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,53 +322,14 @@
}
}
-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;
}
-
- const char *addresses = NULL;
- const char *routes = NULL;
- int count;
-
- // At least one address must be set.
- addresses = jAddresses ? env->GetStringUTFChars(jAddresses, NULL) : NULL;
- if (!addresses) {
- jniThrowNullPointerException(env, "address");
- goto error;
- }
- count = set_addresses(name, index, addresses);
- env->ReleaseStringUTFChars(jAddresses, addresses);
- if (count <= 0) {
- throwException(env, count, "Cannot set address");
- goto error;
- }
- 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;
}
static jstring getName(JNIEnv *env, jobject thiz, jint tun)
@@ -368,6 +342,72 @@
return env->NewStringUTF(name);
}
+static jint setAddresses(JNIEnv *env, jobject thiz, jstring jName,
+ jstring jAddresses)
+{
+ const char *name = NULL;
+ const char *addresses = NULL;
+ int count = -1;
+
+ 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, "addresses");
+ goto error;
+ }
+ count = set_addresses(name, addresses);
+ if (count < 0) {
+ throwException(env, count, "Cannot set address");
+ count = -1;
+ }
+
+error:
+ if (name) {
+ env->ReleaseStringUTFChars(jName, name);
+ }
+ if (addresses) {
+ env->ReleaseStringUTFChars(jAddresses, addresses);
+ }
+ return count;
+}
+
+static jint setRoutes(JNIEnv *env, jobject thiz, jstring jName,
+ jstring jRoutes)
+{
+ 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;
+ }
+ 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)
{
const char *name = jName ? env->GetStringUTFChars(jName, NULL) : NULL;
@@ -409,8 +449,10 @@
//------------------------------------------------------------------------------
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..518a1bb7 100644
--- a/services/sensorservice/SensorFusion.cpp
+++ b/services/sensorservice/SensorFusion.cpp
@@ -28,23 +28,25 @@
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);
+ 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;
+ }
}
- 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 @@
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());
+ // 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).
+ 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) );
+ registerVirtualSensor( new RotationVectorSensor() );
+ registerVirtualSensor( new GravitySensor(list, count) );
+ registerVirtualSensor( new LinearAccelerationSensor(list, count) );
- // these are optional
- registerVirtualSensor( new OrientationSensor() );
- registerVirtualSensor( new CorrectedGyroSensor(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() );
+ // 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/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.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 @@
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.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 @@
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 @@
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 @@
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 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 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.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 @@
! 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>
- </LinearLayout>
+ <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>
-</FrameLayout>
\ No newline at end of file
+ <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>
+
+ </TableLayout>
+
+</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>
- </LinearLayout>
+ <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>
-</FrameLayout>
\ No newline at end of file
+ <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>
+
+ </TableLayout>
+
+</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>