summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/java/android/app/FragmentTransaction.java2
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java2
-rw-r--r--core/java/android/bluetooth/BluetoothTetheringDataTracker.java2
-rw-r--r--core/java/android/content/res/Resources.java10
-rw-r--r--core/java/android/hardware/ConsumerIrManager.java2
-rw-r--r--core/java/android/net/EthernetDataTracker.java2
-rw-r--r--core/java/android/net/http/CertificateChainValidator.java28
-rw-r--r--core/java/android/net/http/DelegatingSSLSession.java22
-rw-r--r--core/java/android/net/http/DelegatingSocketWrapper.java127
-rw-r--r--core/java/android/net/http/X509TrustManagerExtensions.java11
-rw-r--r--core/java/android/os/Message.java2
-rw-r--r--core/java/android/text/format/Time.java677
-rw-r--r--core/java/android/text/format/TimeFormatter.java519
-rw-r--r--core/java/android/util/TimeFormatException.java6
-rw-r--r--core/java/android/view/ViewPropertyAnimator.java4
-rw-r--r--core/java/android/widget/NumberPicker.java2
-rw-r--r--core/java/com/android/internal/app/LocalePicker.java130
-rw-r--r--core/java/com/android/internal/backup/LocalTransport.java2
-rw-r--r--core/java/com/android/internal/content/NativeLibraryHelper.java24
-rw-r--r--core/java/com/android/internal/inputmethod/InputMethodUtils.java29
-rw-r--r--core/jni/Android.mk5
-rw-r--r--core/jni/AndroidRuntime.cpp384
-rw-r--r--core/jni/Time.cpp199
-rw-r--r--core/jni/TimeUtils.h89
-rw-r--r--core/jni/android_media_AudioRecord.cpp45
-rw-r--r--core/jni/android_media_AudioTrack.cpp36
-rw-r--r--core/jni/android_text_format_Time.cpp689
-rw-r--r--core/res/res/values-mcc214-mnc07/config.xml40
-rw-r--r--core/res/res/values/strings.xml2
-rw-r--r--core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.mk41
-rw-r--r--core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/AndroidManifest.xml31
-rw-r--r--core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/res/layout/activity_main.xml7
-rw-r--r--core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/src/com/android/framework/multidexlegacyversionedtestapp/ClassForMainDex.java29
-rw-r--r--core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/src/com/android/framework/multidexlegacyversionedtestapp/MainActivity.java34
-rw-r--r--core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/src/com/android/framework/multidexlegacyversionedtestapp/MultiDexUpdateTest.java39
-rw-r--r--core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/src/com/android/framework/multidexlegacyversionedtestapp/Version.java25
-rw-r--r--core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.mk41
-rw-r--r--core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/AndroidManifest.xml31
-rw-r--r--core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/res/layout/activity_main.xml7
-rw-r--r--core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/src/com/android/framework/multidexlegacyversionedtestapp/ClassForMainDex.java29
-rw-r--r--core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/src/com/android/framework/multidexlegacyversionedtestapp/MainActivity.java34
-rw-r--r--core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/src/com/android/framework/multidexlegacyversionedtestapp/MultiDexUpdateTest.java39
-rw-r--r--core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/src/com/android/framework/multidexlegacyversionedtestapp/Version.java25
-rw-r--r--core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.mk41
-rw-r--r--core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/AndroidManifest.xml31
-rw-r--r--core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/res/layout/activity_main.xml7
-rw-r--r--core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/src/com/android/framework/multidexlegacyversionedtestapp/ClassForMainDex.java29
-rw-r--r--core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/src/com/android/framework/multidexlegacyversionedtestapp/MainActivity.java34
-rw-r--r--core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/src/com/android/framework/multidexlegacyversionedtestapp/MultiDexUpdateTest.java39
-rw-r--r--core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/src/com/android/framework/multidexlegacyversionedtestapp/Version.java25
50 files changed, 2143 insertions, 1567 deletions
diff --git a/core/java/android/app/FragmentTransaction.java b/core/java/android/app/FragmentTransaction.java
index 6e99899..652ad23 100644
--- a/core/java/android/app/FragmentTransaction.java
+++ b/core/java/android/app/FragmentTransaction.java
@@ -106,7 +106,7 @@ public abstract class FragmentTransaction {
public abstract FragmentTransaction detach(Fragment fragment);
/**
- * Re-attach a fragment after it had previously been deatched from
+ * Re-attach a fragment after it had previously been detached from
* the UI with {@link #detach(Fragment)}. This
* causes its view hierarchy to be re-created, attached to the UI,
* and displayed.
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index ab82531..49ed27b 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1071,7 +1071,7 @@ public class DevicePolicyManager {
public static final int WIPE_EXTERNAL_STORAGE = 0x0001;
/**
- * Ask the user date be wiped. This will cause the device to reboot,
+ * Ask the user data be wiped. This will cause the device to reboot,
* erasing all user data while next booting up. External storage such
* as SD cards will be also erased if the flag {@link #WIPE_EXTERNAL_STORAGE}
* is set.
diff --git a/core/java/android/bluetooth/BluetoothTetheringDataTracker.java b/core/java/android/bluetooth/BluetoothTetheringDataTracker.java
index a9b7176..f665a00 100644
--- a/core/java/android/bluetooth/BluetoothTetheringDataTracker.java
+++ b/core/java/android/bluetooth/BluetoothTetheringDataTracker.java
@@ -91,7 +91,7 @@ public class BluetoothTetheringDataTracker extends BaseNetworkStateTracker {
return sInstance;
}
- public Object Clone() throws CloneNotSupportedException {
+ public Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 3db9ddb..9a67dc5 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -1680,15 +1680,7 @@ public class Resources {
remainder = languageTag.substring(separator);
}
- if ("id".equals(language)) {
- return "in" + remainder;
- } else if ("yi".equals(language)) {
- return "ji" + remainder;
- } else if ("he".equals(language)) {
- return "iw" + remainder;
- } else {
- return languageTag;
- }
+ return Locale.adjustLanguageCode(language) + remainder;
}
/**
diff --git a/core/java/android/hardware/ConsumerIrManager.java b/core/java/android/hardware/ConsumerIrManager.java
index 77087814..6d29212 100644
--- a/core/java/android/hardware/ConsumerIrManager.java
+++ b/core/java/android/hardware/ConsumerIrManager.java
@@ -65,7 +65,7 @@ public final class ConsumerIrManager {
}
/**
- * Tansmit and infrared pattern
+ * Transmit an infrared pattern
* <p>
* This method is synchronous; when it returns the pattern has
* been transmitted. Only patterns shorter than 2 seconds will
diff --git a/core/java/android/net/EthernetDataTracker.java b/core/java/android/net/EthernetDataTracker.java
index cc8c771..5ed2409 100644
--- a/core/java/android/net/EthernetDataTracker.java
+++ b/core/java/android/net/EthernetDataTracker.java
@@ -186,7 +186,7 @@ public class EthernetDataTracker extends BaseNetworkStateTracker {
return sInstance;
}
- public Object Clone() throws CloneNotSupportedException {
+ public Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
diff --git a/core/java/android/net/http/CertificateChainValidator.java b/core/java/android/net/http/CertificateChainValidator.java
index d06355d..bf3fe02 100644
--- a/core/java/android/net/http/CertificateChainValidator.java
+++ b/core/java/android/net/http/CertificateChainValidator.java
@@ -16,6 +16,9 @@
package android.net.http;
+import com.android.org.conscrypt.SSLParametersImpl;
+import com.android.org.conscrypt.TrustManagerImpl;
+
import android.util.Slog;
import java.io.ByteArrayInputStream;
@@ -37,7 +40,7 @@ import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
-import javax.net.ssl.X509ExtendedTrustManager;
+import javax.net.ssl.X509TrustManager;
/**
* Class responsible for all server certificate validation functionality
@@ -60,7 +63,7 @@ public class CertificateChainValidator {
.getDefaultHostnameVerifier();
}
- private X509ExtendedTrustManager mTrustManager;
+ private X509TrustManager mTrustManager;
/**
* @return The singleton instance of the certificates chain validator
@@ -78,8 +81,8 @@ public class CertificateChainValidator {
TrustManagerFactory tmf = TrustManagerFactory.getInstance("X.509");
tmf.init((KeyStore) null);
for (TrustManager tm : tmf.getTrustManagers()) {
- if (tm instanceof X509ExtendedTrustManager) {
- mTrustManager = (X509ExtendedTrustManager) tm;
+ if (tm instanceof X509TrustManager) {
+ mTrustManager = (X509TrustManager) tm;
}
}
} catch (NoSuchAlgorithmException e) {
@@ -90,7 +93,7 @@ public class CertificateChainValidator {
if (mTrustManager == null) {
throw new RuntimeException(
- "None of the X.509 TrustManagers are X509ExtendedTrustManager");
+ "None of the X.509 TrustManagers are X509TrustManager");
}
}
@@ -225,8 +228,13 @@ public class CertificateChainValidator {
}
try {
- getInstance().getTrustManager().checkServerTrusted(chain, authType,
- new DelegatingSocketWrapper(domain));
+ X509TrustManager x509TrustManager = SSLParametersImpl.getDefaultX509TrustManager();
+ if (x509TrustManager instanceof TrustManagerImpl) {
+ TrustManagerImpl trustManager = (TrustManagerImpl) x509TrustManager;
+ trustManager.checkServerTrusted(chain, authType, domain);
+ } else {
+ x509TrustManager.checkServerTrusted(chain, authType);
+ }
return null; // No errors.
} catch (GeneralSecurityException e) {
if (HttpLog.LOGV) {
@@ -238,9 +246,9 @@ public class CertificateChainValidator {
}
/**
- * Returns the platform default {@link X509ExtendedTrustManager}.
+ * Returns the platform default {@link X509TrustManager}.
*/
- private X509ExtendedTrustManager getTrustManager() {
+ private X509TrustManager getTrustManager() {
return mTrustManager;
}
@@ -268,4 +276,4 @@ public class CertificateChainValidator {
throw new SSLHandshakeException(errorMessage);
}
-} \ No newline at end of file
+}
diff --git a/core/java/android/net/http/DelegatingSSLSession.java b/core/java/android/net/http/DelegatingSSLSession.java
index ff75b24..98fbe21 100644
--- a/core/java/android/net/http/DelegatingSSLSession.java
+++ b/core/java/android/net/http/DelegatingSSLSession.java
@@ -24,12 +24,11 @@ import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSessionContext;
import javax.net.ssl.SSLSocket;
-import javax.net.ssl.X509ExtendedTrustManager;
+import javax.net.ssl.X509TrustManager;
/**
- * This is used when only a {@code hostname} is available but usage of the new API
- * {@link X509ExtendedTrustManager#checkServerTrusted(X509Certificate[], String, Socket)}
- * requires a {@link SSLSocket}.
+ * This is only used when a {@code certificate} is available but usage
+ * requires a {@link SSLSession}.
*
* @hide
*/
@@ -37,19 +36,6 @@ public class DelegatingSSLSession implements SSLSession {
protected DelegatingSSLSession() {
}
- public static class HostnameWrap extends DelegatingSSLSession {
- private final String mHostname;
-
- public HostnameWrap(String hostname) {
- mHostname = hostname;
- }
-
- @Override
- public String getPeerHost() {
- return mHostname;
- }
- }
-
public static class CertificateWrap extends DelegatingSSLSession {
private final Certificate mCertificate;
@@ -169,4 +155,4 @@ public class DelegatingSSLSession implements SSLSession {
public void removeValue(String name) {
throw new UnsupportedOperationException();
}
-} \ No newline at end of file
+}
diff --git a/core/java/android/net/http/DelegatingSocketWrapper.java b/core/java/android/net/http/DelegatingSocketWrapper.java
deleted file mode 100644
index 230d017..0000000
--- a/core/java/android/net/http/DelegatingSocketWrapper.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.http;
-
-import java.io.IOException;
-
-import javax.net.ssl.HandshakeCompletedListener;
-import javax.net.ssl.SSLSession;
-import javax.net.ssl.SSLSocket;
-import javax.net.ssl.X509ExtendedTrustManager;
-
-/**
- * This is used when only a {@code hostname} is available for
- * {@link X509ExtendedTrustManager#checkServerTrusted(java.security.cert.X509Certificate[], String, Socket)}
- * but we want to use the new API that requires a {@link SSLSocket}.
- */
-class DelegatingSocketWrapper extends SSLSocket {
- private String hostname;
-
- public DelegatingSocketWrapper(String hostname) {
- this.hostname = hostname;
- }
-
- @Override
- public String[] getSupportedCipherSuites() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public String[] getEnabledCipherSuites() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void setEnabledCipherSuites(String[] suites) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public String[] getSupportedProtocols() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public String[] getEnabledProtocols() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void setEnabledProtocols(String[] protocols) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public SSLSession getSession() {
- return new DelegatingSSLSession.HostnameWrap(hostname);
- }
-
- @Override
- public void addHandshakeCompletedListener(HandshakeCompletedListener listener) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void removeHandshakeCompletedListener(HandshakeCompletedListener listener) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void startHandshake() throws IOException {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void setUseClientMode(boolean mode) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public boolean getUseClientMode() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void setNeedClientAuth(boolean need) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void setWantClientAuth(boolean want) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public boolean getNeedClientAuth() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public boolean getWantClientAuth() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void setEnableSessionCreation(boolean flag) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public boolean getEnableSessionCreation() {
- throw new UnsupportedOperationException();
- }
-} \ No newline at end of file
diff --git a/core/java/android/net/http/X509TrustManagerExtensions.java b/core/java/android/net/http/X509TrustManagerExtensions.java
index d730a7b..e8ccc2b 100644
--- a/core/java/android/net/http/X509TrustManagerExtensions.java
+++ b/core/java/android/net/http/X509TrustManagerExtensions.java
@@ -24,7 +24,6 @@ import java.util.List;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLSocket;
-import javax.net.ssl.X509ExtendedTrustManager;
import javax.net.ssl.X509TrustManager;
/**
@@ -34,13 +33,6 @@ import javax.net.ssl.X509TrustManager;
* verification of certificate chains after they have been successfully verified
* by the platform.
* </p>
- * <p>
- * If the returned certificate list is not needed, see also
- * {@code X509ExtendedTrustManager#checkServerTrusted(X509Certificate[], String, java.net.Socket)}
- * where an {@link SSLSocket} can be used to verify the given hostname during
- * handshake using
- * {@code SSLParameters#setEndpointIdentificationAlgorithm(String)}.
- * </p>
*/
public class X509TrustManagerExtensions {
@@ -73,7 +65,6 @@ public class X509TrustManagerExtensions {
*/
public List<X509Certificate> checkServerTrusted(X509Certificate[] chain, String authType,
String host) throws CertificateException {
- return mDelegate.checkServerTrusted(chain, authType,
- new DelegatingSSLSession.HostnameWrap(host));
+ return mDelegate.checkServerTrusted(chain, authType, host);
}
}
diff --git a/core/java/android/os/Message.java b/core/java/android/os/Message.java
index 51203a4..5ae03e1 100644
--- a/core/java/android/os/Message.java
+++ b/core/java/android/os/Message.java
@@ -343,7 +343,7 @@ public final class Message implements Parcelable {
}
/**
- * Sets a Bundle of arbitrary data values. Use arg1 and arg1 members
+ * Sets a Bundle of arbitrary data values. Use arg1 and arg2 members
* as a lower cost way to send a few simple integer values, if you can.
* @see #getData()
* @see #peekData()
diff --git a/core/java/android/text/format/Time.java b/core/java/android/text/format/Time.java
index f34e746..aa6ad20 100644
--- a/core/java/android/text/format/Time.java
+++ b/core/java/android/text/format/Time.java
@@ -16,19 +16,38 @@
package android.text.format;
-import android.content.res.Resources;
+import android.util.TimeFormatException;
+import java.io.IOException;
import java.util.Locale;
import java.util.TimeZone;
-import libcore.icu.LocaleData;
+import libcore.util.ZoneInfo;
+import libcore.util.ZoneInfoDB;
/**
* An alternative to the {@link java.util.Calendar} and
* {@link java.util.GregorianCalendar} classes. An instance of the Time class represents
* a moment in time, specified with second precision. It is modelled after
- * struct tm, and in fact, uses struct tm to implement most of the
- * functionality.
+ * struct tm. This class is not thread-safe and does not consider leap seconds.
+ *
+ * <p>This class has a number of issues and it is recommended that
+ * {@link java.util.GregorianCalendar} is used instead.
+ *
+ * <p>Known issues:
+ * <ul>
+ * <li>For historical reasons when performing time calculations all arithmetic currently takes
+ * place using 32-bit integers. This limits the reliable time range representable from 1902
+ * until 2037.See the wikipedia article on the
+ * <a href="http://en.wikipedia.org/wiki/Year_2038_problem">Year 2038 problem</a> for details.
+ * Do not rely on this behavior; it may change in the future.
+ * </li>
+ * <li>Calling {@link #switchTimezone(String)} on a date that cannot exist, such as a wall time
+ * that was skipped due to a DST transition, will result in a date in 1969 (i.e. -1, or 1 second
+ * before 1st Jan 1970 UTC).</li>
+ * <li>Much of the formatting / parsing assumes ASCII text and is therefore not suitable for
+ * use with non-ASCII scripts.</li>
+ * </ul>
*/
public class Time {
private static final String Y_M_D_T_H_M_S_000 = "%Y-%m-%dT%H:%M:%S.000";
@@ -106,7 +125,7 @@ public class Time {
public int isDst;
/**
- * Offset from UTC (in seconds).
+ * Offset in seconds from UTC including any DST offset.
*/
public long gmtoff;
@@ -137,41 +156,20 @@ public class Time {
public static final int FRIDAY = 5;
public static final int SATURDAY = 6;
- /*
- * The Locale for which date formatting strings have been loaded.
- */
- private static Locale sLocale;
- private static String[] sShortMonths;
- private static String[] sLongMonths;
- private static String[] sLongStandaloneMonths;
- private static String[] sShortWeekdays;
- private static String[] sLongWeekdays;
- private static String sTimeOnlyFormat;
- private static String sDateOnlyFormat;
- private static String sDateTimeFormat;
- private static String sAm;
- private static String sPm;
- private static char sZeroDigit;
-
- // Referenced by native code.
- private static String sDateCommand = "%a %b %e %H:%M:%S %Z %Y";
+ // An object that is reused for date calculations.
+ private TimeCalculator calculator;
/**
* Construct a Time object in the timezone named by the string
* argument "timezone". The time is initialized to Jan 1, 1970.
- * @param timezone string containing the timezone to use.
+ * @param timezoneId string containing the timezone to use.
* @see TimeZone
*/
- public Time(String timezone) {
- if (timezone == null) {
- throw new NullPointerException("timezone is null!");
+ public Time(String timezoneId) {
+ if (timezoneId == null) {
+ throw new NullPointerException("timezoneId is null!");
}
- this.timezone = timezone;
- this.year = 1970;
- this.monthDay = 1;
- // Set the daylight-saving indicator to the unknown value -1 so that
- // it will be recomputed.
- this.isDst = -1;
+ initialize(timezoneId);
}
/**
@@ -179,7 +177,7 @@ public class Time {
* Jan 1, 1970.
*/
public Time() {
- this(TimeZone.getDefault().getID());
+ initialize(TimeZone.getDefault().getID());
}
/**
@@ -189,9 +187,23 @@ public class Time {
* @param other
*/
public Time(Time other) {
+ initialize(other.timezone);
set(other);
}
+ /** Initialize the Time to 00:00:00 1/1/1970 in the specified timezone. */
+ private void initialize(String timezoneId) {
+ this.timezone = timezoneId;
+ this.year = 1970;
+ this.monthDay = 1;
+ // Set the daylight-saving indicator to the unknown value -1 so that
+ // it will be recomputed.
+ this.isDst = -1;
+
+ // A reusable object that performs the date/time calculations.
+ calculator = new TimeCalculator(timezoneId);
+ }
+
/**
* Ensures the values in each field are in range. For example if the
* current value of this calendar is March 32, normalize() will convert it
@@ -208,14 +220,26 @@ public class Time {
*
* @return the UTC milliseconds since the epoch
*/
- native public long normalize(boolean ignoreDst);
+ public long normalize(boolean ignoreDst) {
+ calculator.copyFieldsFromTime(this);
+ long timeInMillis = calculator.toMillis(ignoreDst);
+ calculator.copyFieldsToTime(this);
+ return timeInMillis;
+ }
/**
* Convert this time object so the time represented remains the same, but is
* instead located in a different timezone. This method automatically calls
- * normalize() in some cases
+ * normalize() in some cases.
+ *
+ * <p>This method can return incorrect results if the date / time cannot be normalized.
*/
- native public void switchTimezone(String timezone);
+ public void switchTimezone(String timezone) {
+ calculator.copyFieldsFromTime(this);
+ calculator.switchTimeZone(timezone);
+ calculator.copyFieldsToTime(this);
+ this.timezone = timezone;
+ }
private static final int[] DAYS_PER_MONTH = { 31, 28, 31, 30, 31, 30, 31,
31, 30, 31, 30, 31 };
@@ -265,13 +289,13 @@ public class Time {
/**
* Clears all values, setting the timezone to the given timezone. Sets isDst
* to a negative value to mean "unknown".
- * @param timezone the timezone to use.
+ * @param timezoneId the timezone to use.
*/
- public void clear(String timezone) {
- if (timezone == null) {
+ public void clear(String timezoneId) {
+ if (timezoneId == null) {
throw new NullPointerException("timezone is null!");
}
- this.timezone = timezone;
+ this.timezone = timezoneId;
this.allDay = false;
this.second = 0;
this.minute = 0;
@@ -304,12 +328,12 @@ public class Time {
} else if (b == null) {
throw new NullPointerException("b == null");
}
+ a.calculator.copyFieldsFromTime(a);
+ b.calculator.copyFieldsFromTime(b);
- return nativeCompare(a, b);
+ return TimeCalculator.compare(a.calculator, b.calculator);
}
- private static native int nativeCompare(Time a, Time b);
-
/**
* Print the current value given the format string provided. See man
* strftime for what means what. The final string must be less than 256
@@ -318,61 +342,21 @@ public class Time {
* @return a String containing the current time expressed in the current locale.
*/
public String format(String format) {
- synchronized (Time.class) {
- Locale locale = Locale.getDefault();
-
- if (sLocale == null || locale == null || !(locale.equals(sLocale))) {
- LocaleData localeData = LocaleData.get(locale);
-
- sAm = localeData.amPm[0];
- sPm = localeData.amPm[1];
- sZeroDigit = localeData.zeroDigit;
-
- sShortMonths = localeData.shortMonthNames;
- sLongMonths = localeData.longMonthNames;
- sLongStandaloneMonths = localeData.longStandAloneMonthNames;
- sShortWeekdays = localeData.shortWeekdayNames;
- sLongWeekdays = localeData.longWeekdayNames;
-
- Resources r = Resources.getSystem();
- sTimeOnlyFormat = r.getString(com.android.internal.R.string.time_of_day);
- sDateOnlyFormat = r.getString(com.android.internal.R.string.month_day_year);
- sDateTimeFormat = r.getString(com.android.internal.R.string.date_and_time);
-
- sLocale = locale;
- }
-
- String result = format1(format);
- if (sZeroDigit != '0') {
- result = localizeDigits(result);
- }
- return result;
- }
- }
-
- native private String format1(String format);
-
- // TODO: unify this with java.util.Formatter's copy.
- private String localizeDigits(String s) {
- int length = s.length();
- int offsetToLocalizedDigits = sZeroDigit - '0';
- StringBuilder result = new StringBuilder(length);
- for (int i = 0; i < length; ++i) {
- char ch = s.charAt(i);
- if (ch >= '0' && ch <= '9') {
- ch += offsetToLocalizedDigits;
- }
- result.append(ch);
- }
- return result.toString();
+ calculator.copyFieldsFromTime(this);
+ return calculator.format(format);
}
-
/**
* Return the current time in YYYYMMDDTHHMMSS<tz> format
*/
@Override
- native public String toString();
+ public String toString() {
+ // toString() uses its own TimeCalculator rather than the shared one. Otherwise crazy stuff
+ // happens during debugging when the debugger calls toString().
+ TimeCalculator calculator = new TimeCalculator(this.timezone);
+ calculator.copyFieldsFromTime(this);
+ return calculator.toStringInternal();
+ }
/**
* Parses a date-time string in either the RFC 2445 format or an abbreviated
@@ -414,7 +398,7 @@ public class Time {
if (s == null) {
throw new NullPointerException("time string is null");
}
- if (nativeParse(s)) {
+ if (parseInternal(s)) {
timezone = TIMEZONE_UTC;
return true;
}
@@ -424,7 +408,94 @@ public class Time {
/**
* Parse a time in the current zone in YYYYMMDDTHHMMSS format.
*/
- native private boolean nativeParse(String s);
+ private boolean parseInternal(String s) {
+ int len = s.length();
+ if (len < 8) {
+ throw new TimeFormatException("String is too short: \"" + s +
+ "\" Expected at least 8 characters.");
+ }
+
+ boolean inUtc = false;
+
+ // year
+ int n = getChar(s, 0, 1000);
+ n += getChar(s, 1, 100);
+ n += getChar(s, 2, 10);
+ n += getChar(s, 3, 1);
+ year = n;
+
+ // month
+ n = getChar(s, 4, 10);
+ n += getChar(s, 5, 1);
+ n--;
+ month = n;
+
+ // day of month
+ n = getChar(s, 6, 10);
+ n += getChar(s, 7, 1);
+ monthDay = n;
+
+ if (len > 8) {
+ if (len < 15) {
+ throw new TimeFormatException(
+ "String is too short: \"" + s
+ + "\" If there are more than 8 characters there must be at least"
+ + " 15.");
+ }
+ checkChar(s, 8, 'T');
+ allDay = false;
+
+ // hour
+ n = getChar(s, 9, 10);
+ n += getChar(s, 10, 1);
+ hour = n;
+
+ // min
+ n = getChar(s, 11, 10);
+ n += getChar(s, 12, 1);
+ minute = n;
+
+ // sec
+ n = getChar(s, 13, 10);
+ n += getChar(s, 14, 1);
+ second = n;
+
+ if (len > 15) {
+ // Z
+ checkChar(s, 15, 'Z');
+ inUtc = true;
+ }
+ } else {
+ allDay = true;
+ hour = 0;
+ minute = 0;
+ second = 0;
+ }
+
+ weekDay = 0;
+ yearDay = 0;
+ isDst = -1;
+ gmtoff = 0;
+ return inUtc;
+ }
+
+ private void checkChar(String s, int spos, char expected) {
+ char c = s.charAt(spos);
+ if (c != expected) {
+ throw new TimeFormatException(String.format(
+ "Unexpected character 0x%02d at pos=%d. Expected 0x%02d (\'%c\').",
+ (int) c, spos, (int) expected, expected));
+ }
+ }
+
+ private static int getChar(String s, int spos, int mul) {
+ char c = s.charAt(spos);
+ if (Character.isDigit(c)) {
+ return Character.getNumericValue(c) * mul;
+ } else {
+ throw new TimeFormatException("Parse error at pos=" + spos);
+ }
+ }
/**
* Parse a time in RFC 3339 format. This method also parses simple dates
@@ -461,14 +532,140 @@ public class Time {
if (s == null) {
throw new NullPointerException("time string is null");
}
- if (nativeParse3339(s)) {
+ if (parse3339Internal(s)) {
timezone = TIMEZONE_UTC;
return true;
}
return false;
}
- native private boolean nativeParse3339(String s);
+ private boolean parse3339Internal(String s) {
+ int len = s.length();
+ if (len < 10) {
+ throw new TimeFormatException("String too short --- expected at least 10 characters.");
+ }
+ boolean inUtc = false;
+
+ // year
+ int n = getChar(s, 0, 1000);
+ n += getChar(s, 1, 100);
+ n += getChar(s, 2, 10);
+ n += getChar(s, 3, 1);
+ year = n;
+
+ checkChar(s, 4, '-');
+
+ // month
+ n = getChar(s, 5, 10);
+ n += getChar(s, 6, 1);
+ --n;
+ month = n;
+
+ checkChar(s, 7, '-');
+
+ // day
+ n = getChar(s, 8, 10);
+ n += getChar(s, 9, 1);
+ monthDay = n;
+
+ if (len >= 19) {
+ // T
+ checkChar(s, 10, 'T');
+ allDay = false;
+
+ // hour
+ n = getChar(s, 11, 10);
+ n += getChar(s, 12, 1);
+
+ // Note that this.hour is not set here. It is set later.
+ int hour = n;
+
+ checkChar(s, 13, ':');
+
+ // minute
+ n = getChar(s, 14, 10);
+ n += getChar(s, 15, 1);
+ // Note that this.minute is not set here. It is set later.
+ int minute = n;
+
+ checkChar(s, 16, ':');
+
+ // second
+ n = getChar(s, 17, 10);
+ n += getChar(s, 18, 1);
+ second = n;
+
+ // skip the '.XYZ' -- we don't care about subsecond precision.
+
+ int tzIndex = 19;
+ if (tzIndex < len && s.charAt(tzIndex) == '.') {
+ do {
+ tzIndex++;
+ } while (tzIndex < len && Character.isDigit(s.charAt(tzIndex)));
+ }
+
+ int offset = 0;
+ if (len > tzIndex) {
+ char c = s.charAt(tzIndex);
+ // NOTE: the offset is meant to be subtracted to get from local time
+ // to UTC. we therefore use 1 for '-' and -1 for '+'.
+ switch (c) {
+ case 'Z':
+ // Zulu time -- UTC
+ offset = 0;
+ break;
+ case '-':
+ offset = 1;
+ break;
+ case '+':
+ offset = -1;
+ break;
+ default:
+ throw new TimeFormatException(String.format(
+ "Unexpected character 0x%02d at position %d. Expected + or -",
+ (int) c, tzIndex));
+ }
+ inUtc = true;
+
+ if (offset != 0) {
+ if (len < tzIndex + 6) {
+ throw new TimeFormatException(
+ String.format("Unexpected length; should be %d characters",
+ tzIndex + 6));
+ }
+
+ // hour
+ n = getChar(s, tzIndex + 1, 10);
+ n += getChar(s, tzIndex + 2, 1);
+ n *= offset;
+ hour += n;
+
+ // minute
+ n = getChar(s, tzIndex + 4, 10);
+ n += getChar(s, tzIndex + 5, 1);
+ n *= offset;
+ minute += n;
+ }
+ }
+ this.hour = hour;
+ this.minute = minute;
+
+ if (offset != 0) {
+ normalize(false);
+ }
+ } else {
+ allDay = true;
+ this.hour = 0;
+ this.minute = 0;
+ this.second = 0;
+ }
+
+ this.weekDay = 0;
+ this.yearDay = 0;
+ this.isDst = -1;
+ this.gmtoff = 0;
+ return inUtc;
+ }
/**
* Returns the timezone string that is currently set for the device.
@@ -480,7 +677,9 @@ public class Time {
/**
* Sets the time of the given Time object to the current time.
*/
- native public void setToNow();
+ public void setToNow() {
+ set(System.currentTimeMillis());
+ }
/**
* Converts this time to milliseconds. Suitable for interacting with the
@@ -530,7 +729,10 @@ public class Time {
* to read back the same milliseconds that you set with {@link #set(long)}
* or {@link #set(Time)} or after parsing a date string.
*/
- native public long toMillis(boolean ignoreDst);
+ public long toMillis(boolean ignoreDst) {
+ calculator.copyFieldsFromTime(this);
+ return calculator.toMillis(ignoreDst);
+ }
/**
* Sets the fields in this Time object given the UTC milliseconds. After
@@ -539,15 +741,23 @@ public class Time {
*
* @param millis the time in UTC milliseconds since the epoch.
*/
- native public void set(long millis);
+ public void set(long millis) {
+ allDay = false;
+ calculator.timezone = timezone;
+ calculator.setTimeInMillis(millis);
+ calculator.copyFieldsToTime(this);
+ }
/**
- * Format according to RFC 2445 DATETIME type.
+ * Format according to RFC 2445 DATE-TIME type.
*
- * <p>
- * The same as format("%Y%m%dT%H%M%S").
+ * <p>The same as format("%Y%m%dT%H%M%S"), or format("%Y%m%dT%H%M%SZ") for a Time with a
+ * timezone set to "UTC".
*/
- native public String format2445();
+ public String format2445() {
+ calculator.copyFieldsFromTime(this);
+ return calculator.format2445(!allDay);
+ }
/**
* Copy the value of that to this Time object. No normalization happens.
@@ -682,7 +892,6 @@ public class Time {
* Otherwise, if the timezone is UTC, expresses the time as Y-M-D-T-H-M-S UTC</p>
* <p>
* Otherwise the time is expressed the time as Y-M-D-T-H-M-S +- GMT</p>
- * @param allDay
* @return string in the RFC 3339 format.
*/
public String format3339(boolean allDay) {
@@ -693,7 +902,7 @@ public class Time {
} else {
String base = format(Y_M_D_T_H_M_S_000);
String sign = (gmtoff < 0) ? "-" : "+";
- int offset = (int)Math.abs(gmtoff);
+ int offset = (int) Math.abs(gmtoff);
int minutes = (offset % 3600) / 60;
int hours = offset / 3600;
@@ -714,16 +923,18 @@ public class Time {
}
/**
- * Computes the Julian day number, given the UTC milliseconds
- * and the offset (in seconds) from UTC. The Julian day for a given
- * date will be the same for every timezone. For example, the Julian
- * day for July 1, 2008 is 2454649. This is the same value no matter
- * what timezone is being used. The Julian day is useful for testing
- * if two events occur on the same day and for determining the relative
- * time of an event from the present ("yesterday", "3 days ago", etc.).
+ * Computes the Julian day number for a point in time in a particular
+ * timezone. The Julian day for a given date is the same for every
+ * timezone. For example, the Julian day for July 1, 2008 is 2454649.
*
- * <p>
- * Use {@link #toMillis(boolean)} to get the milliseconds.
+ * <p>Callers must pass the time in UTC millisecond (as can be returned
+ * by {@link #toMillis(boolean)} or {@link #normalize(boolean)})
+ * and the offset from UTC of the timezone in seconds (as might be in
+ * {@link #gmtoff}).
+ *
+ * <p>The Julian day is useful for testing if two events occur on the
+ * same calendar date and for determining the relative time of an event
+ * from the present ("yesterday", "3 days ago", etc.).
*
* @param millis the time in UTC milliseconds
* @param gmtoff the offset from UTC in seconds
@@ -810,4 +1021,240 @@ public class Time {
public static int getJulianMondayFromWeeksSinceEpoch(int week) {
return MONDAY_BEFORE_JULIAN_EPOCH + week * 7;
}
+
+ /**
+ * A class that handles date/time calculations.
+ *
+ * This class originated as a port of a native C++ class ("android.Time") to pure Java. It is
+ * separate from the enclosing class because some methods copy the result of calculations back
+ * to the enclosing object, but others do not: thus separate state is retained.
+ */
+ private static class TimeCalculator {
+ public final ZoneInfo.WallTime wallTime;
+ public String timezone;
+
+ // Information about the current timezone.
+ private ZoneInfo zoneInfo;
+
+ public TimeCalculator(String timezoneId) {
+ this.zoneInfo = lookupZoneInfo(timezoneId);
+ this.wallTime = new ZoneInfo.WallTime();
+ }
+
+ public long toMillis(boolean ignoreDst) {
+ if (ignoreDst) {
+ wallTime.setIsDst(-1);
+ }
+
+ int r = wallTime.mktime(zoneInfo);
+ if (r == -1) {
+ return -1;
+ }
+ return r * 1000L;
+ }
+
+ public void setTimeInMillis(long millis) {
+ // Preserve old 32-bit Android behavior.
+ int intSeconds = (int) (millis / 1000);
+
+ updateZoneInfoFromTimeZone();
+ wallTime.localtime(intSeconds, zoneInfo);
+ }
+
+ public String format(String format) {
+ if (format == null) {
+ format = "%c";
+ }
+ TimeFormatter formatter = new TimeFormatter();
+ return formatter.format(format, wallTime, zoneInfo);
+ }
+
+ private void updateZoneInfoFromTimeZone() {
+ if (!zoneInfo.getID().equals(timezone)) {
+ this.zoneInfo = lookupZoneInfo(timezone);
+ }
+ }
+
+ private static ZoneInfo lookupZoneInfo(String timezoneId) {
+ try {
+ ZoneInfo zoneInfo = ZoneInfoDB.getInstance().makeTimeZone(timezoneId);
+ if (zoneInfo == null) {
+ zoneInfo = ZoneInfoDB.getInstance().makeTimeZone("GMT");
+ }
+ if (zoneInfo == null) {
+ throw new AssertionError("GMT not found: \"" + timezoneId + "\"");
+ }
+ return zoneInfo;
+ } catch (IOException e) {
+ // This should not ever be thrown.
+ throw new AssertionError("Error loading timezone: \"" + timezoneId + "\"", e);
+ }
+ }
+
+ public void switchTimeZone(String timezone) {
+ int seconds = wallTime.mktime(zoneInfo);
+ this.timezone = timezone;
+ updateZoneInfoFromTimeZone();
+ wallTime.localtime(seconds, zoneInfo);
+ }
+
+ public String format2445(boolean hasTime) {
+ char[] buf = new char[hasTime ? 16 : 8];
+ int n = wallTime.getYear();
+
+ buf[0] = toChar(n / 1000);
+ n %= 1000;
+ buf[1] = toChar(n / 100);
+ n %= 100;
+ buf[2] = toChar(n / 10);
+ n %= 10;
+ buf[3] = toChar(n);
+
+ n = wallTime.getMonth() + 1;
+ buf[4] = toChar(n / 10);
+ buf[5] = toChar(n % 10);
+
+ n = wallTime.getMonthDay();
+ buf[6] = toChar(n / 10);
+ buf[7] = toChar(n % 10);
+
+ if (!hasTime) {
+ return new String(buf, 0, 8);
+ }
+
+ buf[8] = 'T';
+
+ n = wallTime.getHour();
+ buf[9] = toChar(n / 10);
+ buf[10] = toChar(n % 10);
+
+ n = wallTime.getMinute();
+ buf[11] = toChar(n / 10);
+ buf[12] = toChar(n % 10);
+
+ n = wallTime.getSecond();
+ buf[13] = toChar(n / 10);
+ buf[14] = toChar(n % 10);
+
+ if (TIMEZONE_UTC.equals(timezone)) {
+ // The letter 'Z' is appended to the end.
+ buf[15] = 'Z';
+ return new String(buf, 0, 16);
+ } else {
+ return new String(buf, 0, 15);
+ }
+ }
+
+ private char toChar(int n) {
+ return (n >= 0 && n <= 9) ? (char) (n + '0') : ' ';
+ }
+
+ /**
+ * A method that will return the state of this object in string form. Note: it has side
+ * effects and so has deliberately not been made the default {@link #toString()}.
+ */
+ public String toStringInternal() {
+ // This implementation possibly displays the un-normalized fields because that is
+ // what it has always done.
+ return String.format("%04d%02d%02dT%02d%02d%02d%s(%d,%d,%d,%d,%d)",
+ wallTime.getYear(),
+ wallTime.getMonth() + 1,
+ wallTime.getMonthDay(),
+ wallTime.getHour(),
+ wallTime.getMinute(),
+ wallTime.getSecond(),
+ timezone,
+ wallTime.getWeekDay(),
+ wallTime.getYearDay(),
+ wallTime.getGmtOffset(),
+ wallTime.getIsDst(),
+ toMillis(false /* use isDst */) / 1000
+ );
+
+ }
+
+ public static int compare(TimeCalculator aObject, TimeCalculator bObject) {
+ if (aObject.timezone.equals(bObject.timezone)) {
+ // If the timezones are the same, we can easily compare the two times.
+ int diff = aObject.wallTime.getYear() - bObject.wallTime.getYear();
+ if (diff != 0) {
+ return diff;
+ }
+
+ diff = aObject.wallTime.getMonth() - bObject.wallTime.getMonth();
+ if (diff != 0) {
+ return diff;
+ }
+
+ diff = aObject.wallTime.getMonthDay() - bObject.wallTime.getMonthDay();
+ if (diff != 0) {
+ return diff;
+ }
+
+ diff = aObject.wallTime.getHour() - bObject.wallTime.getHour();
+ if (diff != 0) {
+ return diff;
+ }
+
+ diff = aObject.wallTime.getMinute() - bObject.wallTime.getMinute();
+ if (diff != 0) {
+ return diff;
+ }
+
+ diff = aObject.wallTime.getSecond() - bObject.wallTime.getSecond();
+ if (diff != 0) {
+ return diff;
+ }
+
+ return 0;
+ } else {
+ // Otherwise, convert to milliseconds and compare that. This requires that object be
+ // normalized. Note: For dates that do not exist: toMillis() can return -1, which
+ // can be confused with a valid time.
+ long am = aObject.toMillis(false /* use isDst */);
+ long bm = bObject.toMillis(false /* use isDst */);
+ long diff = am - bm;
+ return (diff < 0) ? -1 : ((diff > 0) ? 1 : 0);
+ }
+
+ }
+
+ public void copyFieldsToTime(Time time) {
+ time.second = wallTime.getSecond();
+ time.minute = wallTime.getMinute();
+ time.hour = wallTime.getHour();
+ time.monthDay = wallTime.getMonthDay();
+ time.month = wallTime.getMonth();
+ time.year = wallTime.getYear();
+
+ // Read-only fields that are derived from other information above.
+ time.weekDay = wallTime.getWeekDay();
+ time.yearDay = wallTime.getYearDay();
+
+ // < 0: DST status unknown, 0: is not in DST, 1: is in DST
+ time.isDst = wallTime.getIsDst();
+ // This is in seconds and includes any DST offset too.
+ time.gmtoff = wallTime.getGmtOffset();
+ }
+
+ public void copyFieldsFromTime(Time time) {
+ wallTime.setSecond(time.second);
+ wallTime.setMinute(time.minute);
+ wallTime.setHour(time.hour);
+ wallTime.setMonthDay(time.monthDay);
+ wallTime.setMonth(time.month);
+ wallTime.setYear(time.year);
+ wallTime.setWeekDay(time.weekDay);
+ wallTime.setYearDay(time.yearDay);
+ wallTime.setIsDst(time.isDst);
+ wallTime.setGmtOffset((int) time.gmtoff);
+
+ if (time.allDay && (time.second != 0 || time.minute != 0 || time.hour != 0)) {
+ throw new IllegalArgumentException("allDay is true but sec, min, hour are not 0.");
+ }
+
+ timezone = time.timezone;
+ updateZoneInfoFromTimeZone();
+ }
+ }
}
diff --git a/core/java/android/text/format/TimeFormatter.java b/core/java/android/text/format/TimeFormatter.java
new file mode 100644
index 0000000..ec79b36
--- /dev/null
+++ b/core/java/android/text/format/TimeFormatter.java
@@ -0,0 +1,519 @@
+/*
+ * Based on the UCB version of strftime.c with the copyright notice appearing below.
+ */
+
+/*
+** Copyright (c) 1989 The Regents of the University of California.
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms are permitted
+** provided that the above copyright notice and this paragraph are
+** duplicated in all such forms and that any documentation,
+** advertising materials, and other materials related to such
+** distribution and use acknowledge that the software was developed
+** by the University of California, Berkeley. The name of the
+** University may not be used to endorse or promote products derived
+** from this software without specific prior written permission.
+** THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
+** IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+*/
+package android.text.format;
+
+import android.content.res.Resources;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.Formatter;
+import java.util.Locale;
+import java.util.TimeZone;
+import libcore.icu.LocaleData;
+import libcore.util.ZoneInfo;
+
+/**
+ * Formatting logic for {@link Time}. Contains a port of Bionic's broken strftime_tz to Java. The
+ * main issue with this implementation is the treatment of characters as ASCII, despite returning
+ * localized (UTF-16) strings from the LocaleData.
+ *
+ * <p>This class is not thread safe.
+ */
+class TimeFormatter {
+ // An arbitrary value outside the range representable by a byte / ASCII character code.
+ private static final int FORCE_LOWER_CASE = 0x100;
+
+ private static final int SECSPERMIN = 60;
+ private static final int MINSPERHOUR = 60;
+ private static final int DAYSPERWEEK = 7;
+ private static final int MONSPERYEAR = 12;
+ private static final int HOURSPERDAY = 24;
+ private static final int DAYSPERLYEAR = 366;
+ private static final int DAYSPERNYEAR = 365;
+
+ /**
+ * The Locale for which the cached LocaleData and formats have been loaded.
+ */
+ private static Locale sLocale;
+ private static LocaleData sLocaleData;
+ private static String sTimeOnlyFormat;
+ private static String sDateOnlyFormat;
+ private static String sDateTimeFormat;
+
+ private final LocaleData localeData;
+ private final String dateTimeFormat;
+ private final String timeOnlyFormat;
+ private final String dateOnlyFormat;
+ private final Locale locale;
+
+ private StringBuilder outputBuilder;
+ private Formatter outputFormatter;
+
+ public TimeFormatter() {
+ synchronized (TimeFormatter.class) {
+ Locale locale = Locale.getDefault();
+
+ if (sLocale == null || !(locale.equals(sLocale))) {
+ sLocale = locale;
+ sLocaleData = LocaleData.get(locale);
+
+ Resources r = Resources.getSystem();
+ sTimeOnlyFormat = r.getString(com.android.internal.R.string.time_of_day);
+ sDateOnlyFormat = r.getString(com.android.internal.R.string.month_day_year);
+ sDateTimeFormat = r.getString(com.android.internal.R.string.date_and_time);
+ }
+
+ this.dateTimeFormat = sDateTimeFormat;
+ this.timeOnlyFormat = sTimeOnlyFormat;
+ this.dateOnlyFormat = sDateOnlyFormat;
+ this.locale = locale;
+ localeData = sLocaleData;
+ }
+ }
+
+ /**
+ * Format the specified {@code wallTime} using {@code pattern}. The output is returned.
+ */
+ public String format(String pattern, ZoneInfo.WallTime wallTime, ZoneInfo zoneInfo) {
+ try {
+ StringBuilder stringBuilder = new StringBuilder();
+
+ outputBuilder = stringBuilder;
+ outputFormatter = new Formatter(stringBuilder, locale);
+
+ formatInternal(pattern, wallTime, zoneInfo);
+ String result = stringBuilder.toString();
+ // This behavior is the source of a bug since some formats are defined as being
+ // in ASCII. Generally localization is very broken.
+ if (localeData.zeroDigit != '0') {
+ result = localizeDigits(result);
+ }
+ return result;
+ } finally {
+ outputBuilder = null;
+ outputFormatter = null;
+ }
+ }
+
+ private String localizeDigits(String s) {
+ int length = s.length();
+ int offsetToLocalizedDigits = localeData.zeroDigit - '0';
+ StringBuilder result = new StringBuilder(length);
+ for (int i = 0; i < length; ++i) {
+ char ch = s.charAt(i);
+ if (ch >= '0' && ch <= '9') {
+ ch += offsetToLocalizedDigits;
+ }
+ result.append(ch);
+ }
+ return result.toString();
+ }
+
+ /**
+ * Format the specified {@code wallTime} using {@code pattern}. The output is written to
+ * {@link #outputBuilder}.
+ */
+ private void formatInternal(String pattern, ZoneInfo.WallTime wallTime, ZoneInfo zoneInfo) {
+ // Convert to ASCII bytes to be compatible with old implementation behavior.
+ byte[] bytes = pattern.getBytes(StandardCharsets.US_ASCII);
+ if (bytes.length == 0) {
+ return;
+ }
+
+ ByteBuffer formatBuffer = ByteBuffer.wrap(bytes);
+ while (formatBuffer.remaining() > 0) {
+ boolean outputCurrentByte = true;
+ char currentByteAsChar = convertToChar(formatBuffer.get(formatBuffer.position()));
+ if (currentByteAsChar == '%') {
+ outputCurrentByte = handleToken(formatBuffer, wallTime, zoneInfo);
+ }
+ if (outputCurrentByte) {
+ currentByteAsChar = convertToChar(formatBuffer.get(formatBuffer.position()));
+ outputBuilder.append(currentByteAsChar);
+ }
+
+ formatBuffer.position(formatBuffer.position() + 1);
+ }
+ }
+
+ private boolean handleToken(ByteBuffer formatBuffer, ZoneInfo.WallTime wallTime,
+ ZoneInfo zoneInfo) {
+
+ // The byte at formatBuffer.position() is expected to be '%' at this point.
+ int modifier = 0;
+ while (formatBuffer.remaining() > 1) {
+ // Increment the position then get the new current byte.
+ formatBuffer.position(formatBuffer.position() + 1);
+ char currentByteAsChar = convertToChar(formatBuffer.get(formatBuffer.position()));
+ switch (currentByteAsChar) {
+ case 'A':
+ modifyAndAppend((wallTime.getWeekDay() < 0
+ || wallTime.getWeekDay() >= DAYSPERWEEK)
+ ? "?" : localeData.longWeekdayNames[wallTime.getWeekDay() + 1],
+ modifier);
+ return false;
+ case 'a':
+ modifyAndAppend((wallTime.getWeekDay() < 0
+ || wallTime.getWeekDay() >= DAYSPERWEEK)
+ ? "?" : localeData.shortWeekdayNames[wallTime.getWeekDay() + 1],
+ modifier);
+ return false;
+ case 'B':
+ if (modifier == '-') {
+ modifyAndAppend((wallTime.getMonth() < 0
+ || wallTime.getMonth() >= MONSPERYEAR)
+ ? "?"
+ : localeData.longStandAloneMonthNames[wallTime.getMonth()],
+ modifier);
+ } else {
+ modifyAndAppend((wallTime.getMonth() < 0
+ || wallTime.getMonth() >= MONSPERYEAR)
+ ? "?" : localeData.longMonthNames[wallTime.getMonth()],
+ modifier);
+ }
+ return false;
+ case 'b':
+ case 'h':
+ modifyAndAppend((wallTime.getMonth() < 0 || wallTime.getMonth() >= MONSPERYEAR)
+ ? "?" : localeData.shortMonthNames[wallTime.getMonth()],
+ modifier);
+ return false;
+ case 'C':
+ outputYear(wallTime.getYear(), true, false, modifier);
+ return false;
+ case 'c':
+ formatInternal(dateTimeFormat, wallTime, zoneInfo);
+ return false;
+ case 'D':
+ formatInternal("%m/%d/%y", wallTime, zoneInfo);
+ return false;
+ case 'd':
+ outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
+ wallTime.getMonthDay());
+ return false;
+ case 'E':
+ case 'O':
+ // C99 locale modifiers are not supported.
+ continue;
+ case '_':
+ case '-':
+ case '0':
+ case '^':
+ case '#':
+ modifier = currentByteAsChar;
+ continue;
+ case 'e':
+ outputFormatter.format(getFormat(modifier, "%2d", "%2d", "%d", "%02d"),
+ wallTime.getMonthDay());
+ return false;
+ case 'F':
+ formatInternal("%Y-%m-%d", wallTime, zoneInfo);
+ return false;
+ case 'H':
+ outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
+ wallTime.getHour());
+ return false;
+ case 'I':
+ int hour = (wallTime.getHour() % 12 != 0) ? (wallTime.getHour() % 12) : 12;
+ outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), hour);
+ return false;
+ case 'j':
+ int yearDay = wallTime.getYearDay() + 1;
+ outputFormatter.format(getFormat(modifier, "%03d", "%3d", "%d", "%03d"),
+ yearDay);
+ return false;
+ case 'k':
+ outputFormatter.format(getFormat(modifier, "%2d", "%2d", "%d", "%02d"),
+ wallTime.getHour());
+ return false;
+ case 'l':
+ int n2 = (wallTime.getHour() % 12 != 0) ? (wallTime.getHour() % 12) : 12;
+ outputFormatter.format(getFormat(modifier, "%2d", "%2d", "%d", "%02d"), n2);
+ return false;
+ case 'M':
+ outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
+ wallTime.getMinute());
+ return false;
+ case 'm':
+ outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
+ wallTime.getMonth() + 1);
+ return false;
+ case 'n':
+ modifyAndAppend("\n", modifier);
+ return false;
+ case 'p':
+ modifyAndAppend((wallTime.getHour() >= (HOURSPERDAY / 2)) ? localeData.amPm[1]
+ : localeData.amPm[0], modifier);
+ return false;
+ case 'P':
+ modifyAndAppend((wallTime.getHour() >= (HOURSPERDAY / 2)) ? localeData.amPm[1]
+ : localeData.amPm[0], FORCE_LOWER_CASE);
+ return false;
+ case 'R':
+ formatInternal("%H:%M", wallTime, zoneInfo);
+ return false;
+ case 'r':
+ formatInternal("%I:%M:%S %p", wallTime, zoneInfo);
+ return false;
+ case 'S':
+ outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
+ wallTime.getSecond());
+ return false;
+ case 's':
+ int timeInSeconds = wallTime.mktime(zoneInfo);
+ modifyAndAppend(Integer.toString(timeInSeconds), modifier);
+ return false;
+ case 'T':
+ formatInternal("%H:%M:%S", wallTime, zoneInfo);
+ return false;
+ case 't':
+ modifyAndAppend("\t", modifier);
+ return false;
+ case 'U':
+ outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
+ (wallTime.getYearDay() + DAYSPERWEEK - wallTime.getWeekDay())
+ / DAYSPERWEEK);
+ return false;
+ case 'u':
+ int day = (wallTime.getWeekDay() == 0) ? DAYSPERWEEK : wallTime.getWeekDay();
+ outputFormatter.format("%d", day);
+ return false;
+ case 'V': /* ISO 8601 week number */
+ case 'G': /* ISO 8601 year (four digits) */
+ case 'g': /* ISO 8601 year (two digits) */
+ {
+ int year = wallTime.getYear();
+ int yday = wallTime.getYearDay();
+ int wday = wallTime.getWeekDay();
+ int w;
+ while (true) {
+ int len = isLeap(year) ? DAYSPERLYEAR : DAYSPERNYEAR;
+ // What yday (-3 ... 3) does the ISO year begin on?
+ int bot = ((yday + 11 - wday) % DAYSPERWEEK) - 3;
+ // What yday does the NEXT ISO year begin on?
+ int top = bot - (len % DAYSPERWEEK);
+ if (top < -3) {
+ top += DAYSPERWEEK;
+ }
+ top += len;
+ if (yday >= top) {
+ ++year;
+ w = 1;
+ break;
+ }
+ if (yday >= bot) {
+ w = 1 + ((yday - bot) / DAYSPERWEEK);
+ break;
+ }
+ --year;
+ yday += isLeap(year) ? DAYSPERLYEAR : DAYSPERNYEAR;
+ }
+ if (currentByteAsChar == 'V') {
+ outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), w);
+ } else if (currentByteAsChar == 'g') {
+ outputYear(year, false, true, modifier);
+ } else {
+ outputYear(year, true, true, modifier);
+ }
+ return false;
+ }
+ case 'v':
+ formatInternal("%e-%b-%Y", wallTime, zoneInfo);
+ return false;
+ case 'W':
+ int n = (wallTime.getYearDay() + DAYSPERWEEK - (
+ wallTime.getWeekDay() != 0 ? (wallTime.getWeekDay() - 1)
+ : (DAYSPERWEEK - 1))) / DAYSPERWEEK;
+ outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), n);
+ return false;
+ case 'w':
+ outputFormatter.format("%d", wallTime.getWeekDay());
+ return false;
+ case 'X':
+ formatInternal(timeOnlyFormat, wallTime, zoneInfo);
+ return false;
+ case 'x':
+ formatInternal(dateOnlyFormat, wallTime, zoneInfo);
+ return false;
+ case 'y':
+ outputYear(wallTime.getYear(), false, true, modifier);
+ return false;
+ case 'Y':
+ outputYear(wallTime.getYear(), true, true, modifier);
+ return false;
+ case 'Z':
+ if (wallTime.getIsDst() < 0) {
+ return false;
+ }
+ boolean isDst = wallTime.getIsDst() != 0;
+ modifyAndAppend(zoneInfo.getDisplayName(isDst, TimeZone.SHORT), modifier);
+ return false;
+ case 'z': {
+ if (wallTime.getIsDst() < 0) {
+ return false;
+ }
+ int diff = wallTime.getGmtOffset();
+ String sign;
+ if (diff < 0) {
+ sign = "-";
+ diff = -diff;
+ } else {
+ sign = "+";
+ }
+ modifyAndAppend(sign, modifier);
+ diff /= SECSPERMIN;
+ diff = (diff / MINSPERHOUR) * 100 + (diff % MINSPERHOUR);
+ outputFormatter.format(getFormat(modifier, "%04d", "%4d", "%d", "%04d"), diff);
+ return false;
+ }
+ case '+':
+ formatInternal("%a %b %e %H:%M:%S %Z %Y", wallTime, zoneInfo);
+ return false;
+ case '%':
+ // If conversion char is undefined, behavior is undefined. Print out the
+ // character itself.
+ default:
+ return true;
+ }
+ }
+ return true;
+ }
+
+ private void modifyAndAppend(CharSequence str, int modifier) {
+ switch (modifier) {
+ case FORCE_LOWER_CASE:
+ for (int i = 0; i < str.length(); i++) {
+ outputBuilder.append(brokenToLower(str.charAt(i)));
+ }
+ break;
+ case '^':
+ for (int i = 0; i < str.length(); i++) {
+ outputBuilder.append(brokenToUpper(str.charAt(i)));
+ }
+ break;
+ case '#':
+ for (int i = 0; i < str.length(); i++) {
+ char c = str.charAt(i);
+ if (brokenIsUpper(c)) {
+ c = brokenToLower(c);
+ } else if (brokenIsLower(c)) {
+ c = brokenToUpper(c);
+ }
+ outputBuilder.append(c);
+ }
+ break;
+ default:
+ outputBuilder.append(str);
+
+ }
+ }
+
+ private void outputYear(int value, boolean outputTop, boolean outputBottom, int modifier) {
+ int lead;
+ int trail;
+
+ final int DIVISOR = 100;
+ trail = value % DIVISOR;
+ lead = value / DIVISOR + trail / DIVISOR;
+ trail %= DIVISOR;
+ if (trail < 0 && lead > 0) {
+ trail += DIVISOR;
+ --lead;
+ } else if (lead < 0 && trail > 0) {
+ trail -= DIVISOR;
+ ++lead;
+ }
+ if (outputTop) {
+ if (lead == 0 && trail < 0) {
+ modifyAndAppend("-0", modifier);
+ } else {
+ outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), lead);
+ }
+ }
+ if (outputBottom) {
+ int n = ((trail < 0) ? -trail : trail);
+ outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), n);
+ }
+ }
+
+ private static String getFormat(int modifier, String normal, String underscore, String dash,
+ String zero) {
+ switch (modifier) {
+ case '_':
+ return underscore;
+ case '-':
+ return dash;
+ case '0':
+ return zero;
+ }
+ return normal;
+ }
+
+ private static boolean isLeap(int year) {
+ return (((year) % 4) == 0 && (((year) % 100) != 0 || ((year) % 400) == 0));
+ }
+
+ /**
+ * A broken implementation of {@link Character#isUpperCase(char)} that assumes ASCII in order to
+ * be compatible with the old native implementation.
+ */
+ private static boolean brokenIsUpper(char toCheck) {
+ return toCheck >= 'A' && toCheck <= 'Z';
+ }
+
+ /**
+ * A broken implementation of {@link Character#isLowerCase(char)} that assumes ASCII in order to
+ * be compatible with the old native implementation.
+ */
+ private static boolean brokenIsLower(char toCheck) {
+ return toCheck >= 'a' && toCheck <= 'z';
+ }
+
+ /**
+ * A broken implementation of {@link Character#toLowerCase(char)} that assumes ASCII in order to
+ * be compatible with the old native implementation.
+ */
+ private static char brokenToLower(char input) {
+ if (input >= 'A' && input <= 'Z') {
+ return (char) (input - 'A' + 'a');
+ }
+ return input;
+ }
+
+ /**
+ * A broken implementation of {@link Character#toUpperCase(char)} that assumes ASCII in order to
+ * be compatible with the old native implementation.
+ */
+ private static char brokenToUpper(char input) {
+ if (input >= 'a' && input <= 'z') {
+ return (char) (input - 'a' + 'A');
+ }
+ return input;
+ }
+
+ /**
+ * Safely convert a byte containing an ASCII character to a char, even for character codes
+ * > 127.
+ */
+ private static char convertToChar(byte b) {
+ return (char) (b & 0xFF);
+ }
+}
diff --git a/core/java/android/util/TimeFormatException.java b/core/java/android/util/TimeFormatException.java
index d7a898b..f520523 100644
--- a/core/java/android/util/TimeFormatException.java
+++ b/core/java/android/util/TimeFormatException.java
@@ -18,7 +18,11 @@ package android.util;
public class TimeFormatException extends RuntimeException
{
- TimeFormatException(String s)
+
+ /**
+ * @hide
+ */
+ public TimeFormatException(String s)
{
super(s);
}
diff --git a/core/java/android/view/ViewPropertyAnimator.java b/core/java/android/view/ViewPropertyAnimator.java
index 67a94be..2a8523f 100644
--- a/core/java/android/view/ViewPropertyAnimator.java
+++ b/core/java/android/view/ViewPropertyAnimator.java
@@ -314,8 +314,8 @@ public class ViewPropertyAnimator {
*/
public ViewPropertyAnimator setStartDelay(long startDelay) {
if (startDelay < 0) {
- throw new IllegalArgumentException("Animators cannot have negative duration: " +
- startDelay);
+ throw new IllegalArgumentException("Animators cannot have negative start " +
+ "delay: " + startDelay);
}
mStartDelaySet = true;
mStartDelay = startDelay;
diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
index 26c5732..7f6823d 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -59,7 +59,7 @@ import java.util.List;
import java.util.Locale;
/**
- * A widget that enables the user to select a number form a predefined range.
+ * A widget that enables the user to select a number from a predefined range.
* There are two flavors of this widget and which one is presented to the user
* depends on the current theme.
* <ul>
diff --git a/core/java/com/android/internal/app/LocalePicker.java b/core/java/com/android/internal/app/LocalePicker.java
index 043964f..229df8f 100644
--- a/core/java/com/android/internal/app/LocalePicker.java
+++ b/core/java/com/android/internal/app/LocalePicker.java
@@ -36,7 +36,8 @@ import android.widget.ListView;
import android.widget.TextView;
import java.text.Collator;
-import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
import java.util.Locale;
import java.util.ArrayList;
@@ -104,93 +105,84 @@ public class LocalePicker extends ListFragment {
return constructAdapter(context, layoutId, fieldId, false /* disable pseudolocales */);
}
- public static ArrayAdapter<LocaleInfo> constructAdapter(Context context,
- final int layoutId, final int fieldId, final boolean isInDeveloperMode) {
+ public static List<LocaleInfo> getAllAssetLocales(Context context, boolean isInDeveloperMode) {
final Resources resources = context.getResources();
- ArrayList<String> localeList = new ArrayList<String>(Arrays.asList(
- Resources.getSystem().getAssets().getLocales()));
+ final String[] locales = Resources.getSystem().getAssets().getLocales();
+ List<String> localeList = new ArrayList<String>(locales.length);
+ Collections.addAll(localeList, locales);
if (isInDeveloperMode) {
if (!localeList.contains("zz_ZZ")) {
localeList.add("zz_ZZ");
}
- /** - TODO: Enable when zz_ZY Pseudolocale is complete
- * if (!localeList.contains("zz_ZY")) {
- * localeList.add("zz_ZY");
- * }
- */
+ /** - TODO: Enable when zz_ZY Pseudolocale is complete
+ * if (!localeList.contains("zz_ZY")) {
+ * localeList.add("zz_ZY");
+ * }
+ */
}
- String[] locales = new String[localeList.size()];
- locales = localeList.toArray(locales);
+ Collections.sort(localeList);
final String[] specialLocaleCodes = resources.getStringArray(R.array.special_locale_codes);
final String[] specialLocaleNames = resources.getStringArray(R.array.special_locale_names);
- Arrays.sort(locales);
- final int origSize = locales.length;
- final LocaleInfo[] preprocess = new LocaleInfo[origSize];
- int finalSize = 0;
- for (int i = 0 ; i < origSize; i++ ) {
- final String s = locales[i];
- final int len = s.length();
- if (len == 5) {
- String language = s.substring(0, 2);
- String country = s.substring(3, 5);
- final Locale l = new Locale(language, country);
-
- if (finalSize == 0) {
+
+ final ArrayList<LocaleInfo> localeInfos = new ArrayList<LocaleInfo>(localeList.size());
+ for (String locale : localeList) {
+ final Locale l = Locale.forLanguageTag(locale.replace('_', '-'));
+ if (l == null || "und".equals(l.getLanguage())
+ || l.getLanguage().isEmpty() || l.getCountry().isEmpty()) {
+ continue;
+ }
+
+ if (localeInfos.isEmpty()) {
+ if (DEBUG) {
+ Log.v(TAG, "adding initial "+ toTitleCase(l.getDisplayLanguage(l)));
+ }
+ localeInfos.add(new LocaleInfo(toTitleCase(l.getDisplayLanguage(l)), l));
+ } else {
+ // check previous entry:
+ // same lang and a country -> upgrade to full name and
+ // insert ours with full name
+ // diff lang -> insert ours with lang-only name
+ final LocaleInfo previous = localeInfos.get(localeInfos.size() - 1);
+ if (previous.locale.getLanguage().equals(l.getLanguage()) &&
+ !previous.locale.getLanguage().equals("zz")) {
+ if (DEBUG) {
+ Log.v(TAG, "backing up and fixing " + previous.label + " to " +
+ getDisplayName(previous.locale, specialLocaleCodes, specialLocaleNames));
+ }
+ previous.label = toTitleCase(getDisplayName(
+ previous.locale, specialLocaleCodes, specialLocaleNames));
if (DEBUG) {
- Log.v(TAG, "adding initial "+ toTitleCase(l.getDisplayLanguage(l)));
+ Log.v(TAG, " and adding "+ toTitleCase(
+ getDisplayName(l, specialLocaleCodes, specialLocaleNames)));
}
- preprocess[finalSize++] =
- new LocaleInfo(toTitleCase(l.getDisplayLanguage(l)), l);
+ localeInfos.add(new LocaleInfo(toTitleCase(
+ getDisplayName(l, specialLocaleCodes, specialLocaleNames)), l));
} else {
- // check previous entry:
- // same lang and a country -> upgrade to full name and
- // insert ours with full name
- // diff lang -> insert ours with lang-only name
- if (preprocess[finalSize-1].locale.getLanguage().equals(
- language) &&
- !preprocess[finalSize-1].locale.getLanguage().equals("zz")) {
- if (DEBUG) {
- Log.v(TAG, "backing up and fixing "+
- preprocess[finalSize-1].label+" to "+
- getDisplayName(preprocess[finalSize-1].locale,
- specialLocaleCodes, specialLocaleNames));
- }
- preprocess[finalSize-1].label = toTitleCase(
- getDisplayName(preprocess[finalSize-1].locale,
- specialLocaleCodes, specialLocaleNames));
- if (DEBUG) {
- Log.v(TAG, " and adding "+ toTitleCase(
- getDisplayName(l, specialLocaleCodes, specialLocaleNames)));
- }
- preprocess[finalSize++] =
- new LocaleInfo(toTitleCase(
- getDisplayName(
- l, specialLocaleCodes, specialLocaleNames)), l);
+ String displayName;
+ if (locale.equals("zz_ZZ")) {
+ displayName = "[Developer] Accented English";
+ } else if (locale.equals("zz_ZY")) {
+ displayName = "[Developer] Fake Bi-Directional";
} else {
- String displayName;
- if (s.equals("zz_ZZ")) {
- displayName = "[Developer] Accented English";
- } else if (s.equals("zz_ZY")) {
- displayName = "[Developer] Fake Bi-Directional";
- } else {
- displayName = toTitleCase(l.getDisplayLanguage(l));
- }
- if (DEBUG) {
- Log.v(TAG, "adding "+displayName);
- }
- preprocess[finalSize++] = new LocaleInfo(displayName, l);
+ displayName = toTitleCase(l.getDisplayLanguage(l));
}
+ if (DEBUG) {
+ Log.v(TAG, "adding "+displayName);
+ }
+ localeInfos.add(new LocaleInfo(displayName, l));
}
}
}
- final LocaleInfo[] localeInfos = new LocaleInfo[finalSize];
- for (int i = 0; i < finalSize; i++) {
- localeInfos[i] = preprocess[i];
- }
- Arrays.sort(localeInfos);
+ Collections.sort(localeInfos);
+ return localeInfos;
+ }
+
+ public static ArrayAdapter<LocaleInfo> constructAdapter(Context context,
+ final int layoutId, final int fieldId, final boolean isInDeveloperMode) {
+ final List<LocaleInfo> localeInfos = getAllAssetLocales(context, isInDeveloperMode);
final LayoutInflater inflater =
(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java
index 494bc78..26433d2 100644
--- a/core/java/com/android/internal/backup/LocalTransport.java
+++ b/core/java/com/android/internal/backup/LocalTransport.java
@@ -47,7 +47,7 @@ import java.util.ArrayList;
public class LocalTransport extends IBackupTransport.Stub {
private static final String TAG = "LocalTransport";
- private static final boolean DEBUG = true;
+ private static final boolean DEBUG = false;
private static final String TRANSPORT_DIR_NAME
= "com.android.internal.backup.LocalTransport";
diff --git a/core/java/com/android/internal/content/NativeLibraryHelper.java b/core/java/com/android/internal/content/NativeLibraryHelper.java
index dab3aff..832829d 100644
--- a/core/java/com/android/internal/content/NativeLibraryHelper.java
+++ b/core/java/com/android/internal/content/NativeLibraryHelper.java
@@ -19,6 +19,7 @@ package com.android.internal.content;
import android.content.pm.PackageManager;
import android.util.Slog;
+import java.io.Closeable;
import java.io.File;
import java.io.IOException;
@@ -39,20 +40,29 @@ public class NativeLibraryHelper {
*
* @hide
*/
- public static class ApkHandle {
+ public static class ApkHandle implements Closeable {
final String apkPath;
final long apkHandle;
- public ApkHandle(String path) {
- apkPath = path;
- apkHandle = nativeOpenApk(apkPath);
+ public static ApkHandle create(String path) throws IOException {
+ final long handle = nativeOpenApk(path);
+ if (handle == 0) {
+ throw new IOException("Unable to open APK: " + path);
+ }
+
+ return new ApkHandle(path, handle);
+ }
+
+ public static ApkHandle create(File path) throws IOException {
+ return create(path.getAbsolutePath());
}
- public ApkHandle(File apkFile) {
- apkPath = apkFile.getPath();
- apkHandle = nativeOpenApk(apkPath);
+ private ApkHandle(String apkPath, long apkHandle) {
+ this.apkPath = apkPath;
+ this.apkHandle = apkHandle;
}
+ @Override
public void close() {
nativeClose(apkHandle);
}
diff --git a/core/java/com/android/internal/inputmethod/InputMethodUtils.java b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
index 63d018f..68260d2 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodUtils.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
@@ -262,6 +262,7 @@ public class InputMethodUtils {
final List<InputMethodSubtype> subtypes = InputMethodUtils.getSubtypes(imi);
final String systemLocale = res.getConfiguration().locale.toString();
if (TextUtils.isEmpty(systemLocale)) return new ArrayList<InputMethodSubtype>();
+ final String systemLanguage = res.getConfiguration().locale.getLanguage();
final HashMap<String, InputMethodSubtype> applicableModeAndSubtypesMap =
new HashMap<String, InputMethodSubtype>();
final int N = subtypes.size();
@@ -282,15 +283,22 @@ public class InputMethodUtils {
final InputMethodSubtype subtype = subtypes.get(i);
final String locale = subtype.getLocale();
final String mode = subtype.getMode();
+ final String language = getLanguageFromLocaleString(locale);
// When system locale starts with subtype's locale, that subtype will be applicable
- // for system locale
+ // for system locale. We need to make sure the languages are the same, to prevent
+ // locales like "fil" (Filipino) being matched by "fi" (Finnish).
+ //
// For instance, it's clearly applicable for cases like system locale = en_US and
// subtype = en, but it is not necessarily considered applicable for cases like system
// locale = en and subtype = en_US.
+ //
// We just call systemLocale.startsWith(locale) in this function because there is no
// need to find applicable subtypes aggressively unlike
// findLastResortApplicableSubtypeLocked.
- if (systemLocale.startsWith(locale)) {
+ //
+ // TODO: This check is broken. It won't take scripts into account and doesn't
+ // account for the mandatory conversions performed by Locale#toString.
+ if (language.equals(systemLanguage) && systemLocale.startsWith(locale)) {
final InputMethodSubtype applicableSubtype = applicableModeAndSubtypesMap.get(mode);
// If more applicable subtypes are contained, skip.
if (applicableSubtype != null) {
@@ -335,6 +343,18 @@ public class InputMethodUtils {
}
/**
+ * Returns the language component of a given locale string.
+ */
+ private static String getLanguageFromLocaleString(String locale) {
+ final int idx = locale.indexOf('_');
+ if (idx < 0) {
+ return locale;
+ } else {
+ return locale.substring(0, idx);
+ }
+ }
+
+ /**
* If there are no selected subtypes, tries finding the most applicable one according to the
* given locale.
* @param subtypes this function will search the most applicable subtype in subtypes
@@ -353,7 +373,7 @@ public class InputMethodUtils {
if (TextUtils.isEmpty(locale)) {
locale = res.getConfiguration().locale.toString();
}
- final String language = locale.substring(0, 2);
+ final String language = getLanguageFromLocaleString(locale);
boolean partialMatchFound = false;
InputMethodSubtype applicableSubtype = null;
InputMethodSubtype firstMatchedModeSubtype = null;
@@ -361,6 +381,7 @@ public class InputMethodUtils {
for (int i = 0; i < N; ++i) {
InputMethodSubtype subtype = subtypes.get(i);
final String subtypeLocale = subtype.getLocale();
+ final String subtypeLanguage = getLanguageFromLocaleString(subtypeLocale);
// An applicable subtype should match "mode". If mode is null, mode will be ignored,
// and all subtypes with all modes can be candidates.
if (mode == null || subtypes.get(i).getMode().equalsIgnoreCase(mode)) {
@@ -371,7 +392,7 @@ public class InputMethodUtils {
// Exact match (e.g. system locale is "en_US" and subtype locale is "en_US")
applicableSubtype = subtype;
break;
- } else if (!partialMatchFound && subtypeLocale.startsWith(language)) {
+ } else if (!partialMatchFound && language.equals(subtypeLanguage)) {
// Partial match (e.g. system locale is "en_US" and subtype locale is "en")
applicableSubtype = subtype;
partialMatchFound = true;
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index bdef428..e4fad09 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -22,7 +22,6 @@ LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
LOCAL_SRC_FILES:= \
AndroidRuntime.cpp \
- Time.cpp \
com_android_internal_content_NativeLibraryHelper.cpp \
com_google_android_gles_jni_EGLImpl.cpp \
com_google_android_gles_jni_GLImpl.cpp.arm \
@@ -76,7 +75,6 @@ LOCAL_SRC_FILES:= \
android_net_TrafficStats.cpp \
android_net_wifi_WifiNative.cpp \
android_nio_utils.cpp \
- android_text_format_Time.cpp \
android_util_AssetManager.cpp \
android_util_Binder.cpp \
android_util_EventLog.cpp \
@@ -162,6 +160,7 @@ LOCAL_C_INCLUDES += \
$(call include-path-for, libhardware_legacy)/hardware_legacy \
$(TOP)/frameworks/av/include \
$(TOP)/system/media/camera/include \
+ external/icu/icu4c/source/common \
external/skia/src/core \
external/skia/src/pdf \
external/skia/src/images \
@@ -171,8 +170,6 @@ LOCAL_C_INCLUDES += \
external/expat/lib \
external/openssl/include \
external/tremor/Tremor \
- external/icu4c/i18n \
- external/icu4c/common \
external/jpeg \
external/harfbuzz_ng/src \
external/zlib \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index a61901b..a65c351 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -132,7 +132,6 @@ extern int register_android_database_SQLiteConnection(JNIEnv* env);
extern int register_android_database_SQLiteGlobal(JNIEnv* env);
extern int register_android_database_SQLiteDebug(JNIEnv* env);
extern int register_android_nio_utils(JNIEnv* env);
-extern int register_android_text_format_Time(JNIEnv* env);
extern int register_android_os_Debug(JNIEnv* env);
extern int register_android_os_MessageQueue(JNIEnv* env);
extern int register_android_os_Parcel(JNIEnv* env);
@@ -391,8 +390,8 @@ static void readLocale(char* language, char* region)
property_get("ro.product.locale.language", propLang, "en");
property_get("ro.product.locale.region", propRegn, "US");
}
- strncat(language, propLang, 2);
- strncat(region, propRegn, 2);
+ strncat(language, propLang, 3);
+ strncat(region, propRegn, 3);
//ALOGD("language=%s region=%s\n", language, region);
}
@@ -436,6 +435,82 @@ void AndroidRuntime::parseExtraOpts(char* extraOptsBuf, const char* quotingArg)
}
/*
+ * Reads a "property" into "buffer" with a default of "defaultArg". If
+ * the property is non-empty, it is treated as a runtime option such
+ * as "-Xmx32m".
+ *
+ * The "runtimeArg" is a prefix for the option such as "-Xms" or "-Xmx".
+ *
+ * If an argument is found, it is added to mOptions.
+ *
+ * If an option is found, it is added to mOptions and true is
+ * returned. Otherwise false is returned.
+ */
+bool AndroidRuntime::parseRuntimeOption(const char* property,
+ char* buffer,
+ const char* runtimeArg,
+ const char* defaultArg)
+{
+ strcpy(buffer, runtimeArg);
+ size_t runtimeArgLen = strlen(runtimeArg);
+ property_get(property, buffer+runtimeArgLen, defaultArg);
+ if (buffer[runtimeArgLen] == '\0') {
+ return false;
+ }
+
+ JavaVMOption opt;
+ memset(&opt, 0, sizeof(opt));
+
+ opt.optionString = buffer;
+ mOptions.add(opt);
+
+ return true;
+}
+
+/*
+ * Reads a "property" into "buffer". If the property is non-empty, it
+ * is treated as a dex2oat compiler runtime option that should be
+ * passed as a quoted option, e.g. "-Ximage-compiler-option
+ * --runtime-arg -Ximage-compiler-option -Xmx32m".
+ *
+ * The "runtimeArg" is a prefix for the option such as "-Xms" or "-Xmx".
+ *
+ * The "quotingArg" should be "-Ximage-compiler-option" or "-Xcompiler-option".
+ *
+ * If an option is found, it is added to mOptions and true is
+ * returned. Otherwise false is returned.
+ */
+bool AndroidRuntime::parseCompilerRuntimeOption(const char* property,
+ char* buffer,
+ const char* runtimeArg,
+ const char* quotingArg)
+{
+ strcpy(buffer, runtimeArg);
+ size_t runtimeArgLen = strlen(runtimeArg);
+ property_get(property, buffer+runtimeArgLen, "");
+ if (buffer[runtimeArgLen] == '\0') {
+ return false;
+ }
+
+ JavaVMOption opt;
+ memset(&opt, 0, sizeof(opt));
+
+ opt.optionString = quotingArg;
+ mOptions.add(opt);
+
+ opt.optionString = "--runtime-arg";
+ mOptions.add(opt);
+
+ opt.optionString = quotingArg;
+ mOptions.add(opt);
+
+ opt.optionString = buffer;
+ mOptions.add(opt);
+
+ return true;
+}
+
+/*
* Start the Dalvik Virtual Machine.
*
* Various arguments, most determined by system properties, are passed in.
@@ -457,7 +532,7 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv)
JavaVMInitArgs initArgs;
JavaVMOption opt;
char propBuf[PROPERTY_VALUE_MAX];
- char stackTraceFileBuf[PROPERTY_VALUE_MAX];
+ char stackTraceFileBuf[sizeof("-Xstacktracefile:")-1 + PROPERTY_VALUE_MAX];
char dexoptFlagsBuf[PROPERTY_VALUE_MAX];
char enableAssertBuf[sizeof("-ea:")-1 + PROPERTY_VALUE_MAX];
char jniOptsBuf[sizeof("-Xjniopts:")-1 + PROPERTY_VALUE_MAX];
@@ -471,31 +546,35 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv)
char heaptargetutilizationOptsBuf[sizeof("-XX:HeapTargetUtilization=")-1 + PROPERTY_VALUE_MAX];
char jitcodecachesizeOptsBuf[sizeof("-Xjitcodecachesize:")-1 + PROPERTY_VALUE_MAX];
char dalvikVmLibBuf[PROPERTY_VALUE_MAX];
+ char dex2oatXmsImageFlagsBuf[sizeof("-Xms")-1 + PROPERTY_VALUE_MAX];
+ char dex2oatXmxImageFlagsBuf[sizeof("-Xmx")-1 + PROPERTY_VALUE_MAX];
+ char dex2oatXmsFlagsBuf[sizeof("-Xms")-1 + PROPERTY_VALUE_MAX];
+ char dex2oatXmxFlagsBuf[sizeof("-Xmx")-1 + PROPERTY_VALUE_MAX];
char dex2oatFlagsBuf[PROPERTY_VALUE_MAX];
char dex2oatImageFlagsBuf[PROPERTY_VALUE_MAX];
char extraOptsBuf[PROPERTY_VALUE_MAX];
- char* stackTraceFile = NULL;
- bool checkJni = false;
- bool checkDexSum = false;
- bool logStdio = false;
enum {
kEMDefault,
kEMIntPortable,
kEMIntFast,
kEMJitCompiler,
} executionMode = kEMDefault;
- char profile_period[sizeof("-Xprofile-period:") + PROPERTY_VALUE_MAX];
- char profile_duration[sizeof("-Xprofile-duration:") + PROPERTY_VALUE_MAX];
- char profile_interval[sizeof("-Xprofile-interval:") + PROPERTY_VALUE_MAX];
- char profile_backoff[sizeof("-Xprofile-backoff:") + PROPERTY_VALUE_MAX];
- char profile_top_k_threshold[sizeof("-Xprofile-top-k-threshold:") + PROPERTY_VALUE_MAX];
- char profile_top_k_change_threshold[sizeof("-Xprofile-top-k-change-threshold:") + PROPERTY_VALUE_MAX];
+ char profilePeriod[sizeof("-Xprofile-period:")-1 + PROPERTY_VALUE_MAX];
+ char profileDuration[sizeof("-Xprofile-duration:")-1 + PROPERTY_VALUE_MAX];
+ char profileInterval[sizeof("-Xprofile-interval:")-1 + PROPERTY_VALUE_MAX];
+ char profileBackoff[sizeof("-Xprofile-backoff:")-1 + PROPERTY_VALUE_MAX];
+ char profileTopKThreshold[sizeof("-Xprofile-top-k-threshold:")-1 + PROPERTY_VALUE_MAX];
+ char profileTopKChangeThreshold[sizeof("-Xprofile-top-k-change-threshold:")-1 +
+ PROPERTY_VALUE_MAX];
+ char profileType[sizeof("-Xprofile-type:")-1 + PROPERTY_VALUE_MAX];
+ char profileMaxStackDepth[sizeof("-Xprofile-max-stack-depth:")-1 + PROPERTY_VALUE_MAX];
char langOption[sizeof("-Duser.language=") + 3];
char regionOption[sizeof("-Duser.region=") + 3];
- char lockProfThresholdBuf[sizeof("-Xlockprofthreshold:") + sizeof(propBuf)];
- char jitOpBuf[sizeof("-Xjitop:") + PROPERTY_VALUE_MAX];
- char jitMethodBuf[sizeof("-Xjitmethod:") + PROPERTY_VALUE_MAX];
+ char lockProfThresholdBuf[sizeof("-Xlockprofthreshold:")-1 + PROPERTY_VALUE_MAX];
+ char jitOpBuf[sizeof("-Xjitop:")-1 + PROPERTY_VALUE_MAX];
+ char jitMethodBuf[sizeof("-Xjitmethod:")-1 + PROPERTY_VALUE_MAX];
+ bool checkJni = false;
property_get("dalvik.vm.checkjni", propBuf, "");
if (strcmp(propBuf, "true") == 0) {
checkJni = true;
@@ -506,6 +585,20 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv)
checkJni = true;
}
}
+ ALOGD("CheckJNI is %s\n", checkJni ? "ON" : "OFF");
+ if (checkJni) {
+ /* extended JNI checking */
+ opt.optionString = "-Xcheck:jni";
+ mOptions.add(opt);
+
+ /* set a cap on JNI global references */
+ opt.optionString = "-Xjnigreflimit:2000";
+ mOptions.add(opt);
+
+ /* with -Xcheck:jni, this provides a JNI function call trace */
+ //opt.optionString = "-verbose:jni";
+ //mOptions.add(opt);
+ }
property_get("dalvik.vm.execution-mode", propBuf, "");
if (strcmp(propBuf, "int:portable") == 0) {
@@ -516,23 +609,39 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv)
executionMode = kEMJitCompiler;
}
- property_get("dalvik.vm.stack-trace-file", stackTraceFileBuf, "");
+ parseRuntimeOption("dalvik.vm.stack-trace-file", stackTraceFileBuf, "-Xstacktracefile:");
property_get("dalvik.vm.check-dex-sum", propBuf, "");
if (strcmp(propBuf, "true") == 0) {
- checkDexSum = true;
+ /* perform additional DEX checksum tests */
+ opt.optionString = "-Xcheckdexsum";
+ mOptions.add(opt);
}
property_get("log.redirect-stdio", propBuf, "");
if (strcmp(propBuf, "true") == 0) {
- logStdio = true;
+ /* convert stdout/stderr to log messages */
+ opt.optionString = "-Xlog-stdio";
+ mOptions.add(opt);
}
strcpy(enableAssertBuf, "-ea:");
- property_get("dalvik.vm.enableassertions", enableAssertBuf+4, "");
+ property_get("dalvik.vm.enableassertions", enableAssertBuf+sizeof("-ea:")-1, "");
+ if (enableAssertBuf[sizeof("-ea:")-1] != '\0') {
+ /* accept "all" to mean "all classes and packages" */
+ if (strcmp(enableAssertBuf+sizeof("-ea:")-1, "all") == 0)
+ enableAssertBuf[3] = '\0'; // truncate to "-ea"
+ ALOGI("Assertions enabled: '%s'\n", enableAssertBuf);
+ opt.optionString = enableAssertBuf;
+ mOptions.add(opt);
+ } else {
+ ALOGV("Assertions disabled\n");
+ }
strcpy(jniOptsBuf, "-Xjniopts:");
- property_get("dalvik.vm.jniopts", jniOptsBuf+10, "");
+ if (parseRuntimeOption("dalvik.vm.jniopts", jniOptsBuf, "-Xjniopts:")) {
+ ALOGI("JNI options: '%s'\n", jniOptsBuf);
+ }
/* route exit() to our handler */
opt.extraInfo = (void*) runtime_exit;
@@ -561,54 +670,24 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv)
* The default starting and maximum size of the heap. Larger
* values should be specified in a product property override.
*/
- strcpy(heapstartsizeOptsBuf, "-Xms");
- property_get("dalvik.vm.heapstartsize", heapstartsizeOptsBuf+4, "4m");
- opt.optionString = heapstartsizeOptsBuf;
- mOptions.add(opt);
- strcpy(heapsizeOptsBuf, "-Xmx");
- property_get("dalvik.vm.heapsize", heapsizeOptsBuf+4, "16m");
- opt.optionString = heapsizeOptsBuf;
- mOptions.add(opt);
+ parseRuntimeOption("dalvik.vm.heapstartsize", heapstartsizeOptsBuf, "-Xms", "4m");
+ parseRuntimeOption("dalvik.vm.heapsize", heapsizeOptsBuf, "-Xmx", "16m");
// Increase the main thread's interpreter stack size for bug 6315322.
opt.optionString = "-XX:mainThreadStackSize=24K";
mOptions.add(opt);
// Set the max jit code cache size. Note: size of 0 will disable the JIT.
- strcpy(jitcodecachesizeOptsBuf, "-Xjitcodecachesize:");
- property_get("dalvik.vm.jit.codecachesize", jitcodecachesizeOptsBuf+19, NULL);
- if (jitcodecachesizeOptsBuf[19] != '\0') {
- opt.optionString = jitcodecachesizeOptsBuf;
- mOptions.add(opt);
- }
+ parseRuntimeOption("dalvik.vm.jit.codecachesize",
+ jitcodecachesizeOptsBuf,
+ "-Xjitcodecachesize:");
- strcpy(heapgrowthlimitOptsBuf, "-XX:HeapGrowthLimit=");
- property_get("dalvik.vm.heapgrowthlimit", heapgrowthlimitOptsBuf+20, "");
- if (heapgrowthlimitOptsBuf[20] != '\0') {
- opt.optionString = heapgrowthlimitOptsBuf;
- mOptions.add(opt);
- }
-
- strcpy(heapminfreeOptsBuf, "-XX:HeapMinFree=");
- property_get("dalvik.vm.heapminfree", heapminfreeOptsBuf+16, "");
- if (heapminfreeOptsBuf[16] != '\0') {
- opt.optionString = heapminfreeOptsBuf;
- mOptions.add(opt);
- }
-
- strcpy(heapmaxfreeOptsBuf, "-XX:HeapMaxFree=");
- property_get("dalvik.vm.heapmaxfree", heapmaxfreeOptsBuf+16, "");
- if (heapmaxfreeOptsBuf[16] != '\0') {
- opt.optionString = heapmaxfreeOptsBuf;
- mOptions.add(opt);
- }
-
- strcpy(heaptargetutilizationOptsBuf, "-XX:HeapTargetUtilization=");
- property_get("dalvik.vm.heaptargetutilization", heaptargetutilizationOptsBuf+26, "");
- if (heaptargetutilizationOptsBuf[26] != '\0') {
- opt.optionString = heaptargetutilizationOptsBuf;
- mOptions.add(opt);
- }
+ parseRuntimeOption("dalvik.vm.heapgrowthlimit", heapgrowthlimitOptsBuf, "-XX:HeapGrowthLimit=");
+ parseRuntimeOption("dalvik.vm.heapminfree", heapminfreeOptsBuf, "-XX:HeapMinFree=");
+ parseRuntimeOption("dalvik.vm.heapmaxfree", heapmaxfreeOptsBuf, "-XX:HeapMaxFree=");
+ parseRuntimeOption("dalvik.vm.heaptargetutilization",
+ heaptargetutilizationOptsBuf,
+ "-XX:HeapTargetUtilization=");
property_get("ro.config.low_ram", propBuf, "");
if (strcmp(propBuf, "true") == 0) {
@@ -616,19 +695,8 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv)
mOptions.add(opt);
}
- strcpy(gctypeOptsBuf, "-Xgc:");
- property_get("dalvik.vm.gctype", gctypeOptsBuf+5, "");
- if (gctypeOptsBuf[5] != '\0') {
- opt.optionString = gctypeOptsBuf;
- mOptions.add(opt);
- }
-
- strcpy(backgroundgcOptsBuf, "-XX:BackgroundGC=");
- property_get("dalvik.vm.backgroundgctype", backgroundgcOptsBuf+sizeof("-XX:BackgroundGC=")-1, "");
- if (backgroundgcOptsBuf[sizeof("-XX:BackgroundGC=")-1] != '\0') {
- opt.optionString = backgroundgcOptsBuf;
- mOptions.add(opt);
- }
+ parseRuntimeOption("dalvik.vm.gctype", gctypeOptsBuf, "-Xgc:");
+ parseRuntimeOption("dalvik.vm.backgroundgctype", backgroundgcOptsBuf, "-XX:BackgroundGC=");
/*
* Enable or disable dexopt features, such as bytecode verification and
@@ -687,46 +755,15 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv)
"-agentlib:jdwp=transport=dt_android_adb,suspend=n,server=y";
mOptions.add(opt);
- ALOGD("CheckJNI is %s\n", checkJni ? "ON" : "OFF");
- if (checkJni) {
- /* extended JNI checking */
- opt.optionString = "-Xcheck:jni";
- mOptions.add(opt);
-
- /* set a cap on JNI global references */
- opt.optionString = "-Xjnigreflimit:2000";
- mOptions.add(opt);
-
- /* with -Xcheck:jni, this provides a JNI function call trace */
- //opt.optionString = "-verbose:jni";
- //mOptions.add(opt);
- }
-
- property_get("dalvik.vm.lockprof.threshold", propBuf, "");
- if (strlen(propBuf) > 0) {
- strcpy(lockProfThresholdBuf, "-Xlockprofthreshold:");
- strcat(lockProfThresholdBuf, propBuf);
- opt.optionString = lockProfThresholdBuf;
- mOptions.add(opt);
- }
+ parseRuntimeOption("dalvik.vm.lockprof.threshold",
+ lockProfThresholdBuf,
+ "-Xlockprofthreshold:");
/* Force interpreter-only mode for selected opcodes. Eg "1-0a,3c,f1-ff" */
- property_get("dalvik.vm.jit.op", propBuf, "");
- if (strlen(propBuf) > 0) {
- strcpy(jitOpBuf, "-Xjitop:");
- strcat(jitOpBuf, propBuf);
- opt.optionString = jitOpBuf;
- mOptions.add(opt);
- }
+ parseRuntimeOption("dalvik.vm.jit.op", jitOpBuf, "-Xjitop:");
/* Force interpreter-only mode for selected methods */
- property_get("dalvik.vm.jit.method", propBuf, "");
- if (strlen(propBuf) > 0) {
- strcpy(jitMethodBuf, "-Xjitmethod:");
- strcat(jitMethodBuf, propBuf);
- opt.optionString = jitMethodBuf;
- mOptions.add(opt);
- }
+ parseRuntimeOption("dalvik.vm.jit.method", jitMethodBuf, "-Xjitmethod:");
if (executionMode == kEMIntPortable) {
opt.optionString = "-Xint:portable";
@@ -739,58 +776,26 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv)
mOptions.add(opt);
}
- if (checkDexSum) {
- /* perform additional DEX checksum tests */
- opt.optionString = "-Xcheckdexsum";
- mOptions.add(opt);
- }
-
- if (logStdio) {
- /* convert stdout/stderr to log messages */
- opt.optionString = "-Xlog-stdio";
- mOptions.add(opt);
- }
-
- if (enableAssertBuf[4] != '\0') {
- /* accept "all" to mean "all classes and packages" */
- if (strcmp(enableAssertBuf+4, "all") == 0)
- enableAssertBuf[3] = '\0';
- ALOGI("Assertions enabled: '%s'\n", enableAssertBuf);
- opt.optionString = enableAssertBuf;
- mOptions.add(opt);
- } else {
- ALOGV("Assertions disabled\n");
- }
-
- if (jniOptsBuf[10] != '\0') {
- ALOGI("JNI options: '%s'\n", jniOptsBuf);
- opt.optionString = jniOptsBuf;
- mOptions.add(opt);
- }
-
- if (stackTraceFileBuf[0] != '\0') {
- static const char* stfOptName = "-Xstacktracefile:";
-
- stackTraceFile = (char*) malloc(strlen(stfOptName) +
- strlen(stackTraceFileBuf) +1);
- strcpy(stackTraceFile, stfOptName);
- strcat(stackTraceFile, stackTraceFileBuf);
- opt.optionString = stackTraceFile;
- mOptions.add(opt);
- }
-
// libart tolerates libdvm flags, but not vice versa, so only pass some options if libart.
property_get("persist.sys.dalvik.vm.lib.2", dalvikVmLibBuf, "libart.so");
bool libart = (strncmp(dalvikVmLibBuf, "libart", 6) == 0);
if (libart) {
- // Extra options for DexClassLoader.
- property_get("dalvik.vm.dex2oat-flags", dex2oatFlagsBuf, "");
- parseExtraOpts(dex2oatFlagsBuf, "-Xcompiler-option");
-
// Extra options for boot.art/boot.oat image generation.
+ parseCompilerRuntimeOption("dalvik.vm.image-dex2oat-Xms", dex2oatXmsImageFlagsBuf,
+ "-Xms", "-Ximage-compiler-option");
+ parseCompilerRuntimeOption("dalvik.vm.image-dex2oat-Xmx", dex2oatXmxImageFlagsBuf,
+ "-Xmx", "-Ximage-compiler-option");
property_get("dalvik.vm.image-dex2oat-flags", dex2oatImageFlagsBuf, "");
parseExtraOpts(dex2oatImageFlagsBuf, "-Ximage-compiler-option");
+
+ // Extra options for DexClassLoader.
+ parseCompilerRuntimeOption("dalvik.vm.dex2oat-Xms", dex2oatXmsFlagsBuf,
+ "-Xms", "-Xcompiler-option");
+ parseCompilerRuntimeOption("dalvik.vm.dex2oat-Xmx", dex2oatXmxFlagsBuf,
+ "-Xmx", "-Xcompiler-option");
+ property_get("dalvik.vm.dex2oat-flags", dex2oatFlagsBuf, "");
+ parseExtraOpts(dex2oatFlagsBuf, "-Xcompiler-option");
}
/* extra options; parse this late so it overrides others */
@@ -829,49 +834,42 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv)
}
// Number of seconds during profile runs.
- strcpy(profile_period, "-Xprofile-period:");
- if (property_get("dalvik.vm.profile.period-secs", profile_period+17, NULL) > 0) {
- opt.optionString = profile_period;
- mOptions.add(opt);
- }
+ parseRuntimeOption("dalvik.vm.profile.period-secs", profilePeriod, "-Xprofile-period:");
// Length of each profile run (seconds).
- strcpy(profile_duration, "-Xprofile-duration:");
- if (property_get("dalvik.vm.profile.duration-secs", profile_duration+19, NULL) > 0) {
- opt.optionString = profile_duration;
- mOptions.add(opt);
- }
+ parseRuntimeOption("dalvik.vm.profile.duration-secs",
+ profileDuration,
+ "-Xprofile-duration:");
// Polling interval during profile run (microseconds).
- strcpy(profile_interval, "-Xprofile-interval:");
- if (property_get("dalvik.vm.profile.interval-us", profile_interval+19, NULL) > 0) {
- opt.optionString = profile_interval;
- mOptions.add(opt);
- }
+ parseRuntimeOption("dalvik.vm.profile.interval-us", profileInterval, "-Xprofile-interval:");
// Coefficient for period backoff. The the period is multiplied
// by this value after each profile run.
- strcpy(profile_backoff, "-Xprofile-backoff:");
- if (property_get("dalvik.vm.profile.backoff-coeff", profile_backoff+18, NULL) > 0) {
- opt.optionString = profile_backoff;
- mOptions.add(opt);
- }
-
- // Top K% of samples that are considered relevant when deciding if the app should be recompiled.
- strcpy(profile_top_k_threshold, "-Xprofile-top-k-threshold:");
- if (property_get("dalvik.vm.profile.top-k-thr", profile_top_k_threshold+26, NULL) > 0) {
- opt.optionString = profile_top_k_threshold;
- mOptions.add(opt);
- }
-
- // The threshold after which a change in the structure of the top K% profiled samples becomes significant
- // and triggers recompilation. A change in profile is considered significant if X% (top-k-change-threshold)
- // of the top K% (top-k-threshold property) samples has changed.
- strcpy(profile_top_k_change_threshold, "-Xprofile-top-k-change-threshold:");
- if (property_get("dalvik.vm.profile.top-k-ch-thr", profile_top_k_change_threshold+33, NULL) > 0) {
- opt.optionString = profile_top_k_change_threshold;
- mOptions.add(opt);
- }
+ parseRuntimeOption("dalvik.vm.profile.backoff-coeff", profileBackoff, "-Xprofile-backoff:");
+
+ // Top K% of samples that are considered relevant when
+ // deciding if the app should be recompiled.
+ parseRuntimeOption("dalvik.vm.profile.top-k-thr",
+ profileTopKThreshold,
+ "-Xprofile-top-k-threshold:");
+
+ // The threshold after which a change in the structure of the
+ // top K% profiled samples becomes significant and triggers
+ // recompilation. A change in profile is considered
+ // significant if X% (top-k-change-threshold) of the top K%
+ // (top-k-threshold property) samples has changed.
+ parseRuntimeOption("dalvik.vm.profile.top-k-ch-thr",
+ profileTopKChangeThreshold,
+ "-Xprofile-top-k-change-threshold:");
+
+ // Type of profile data.
+ parseRuntimeOption("dalvik.vm.profiler.type", profileType, "-Xprofile-type:");
+
+ // Depth of bounded stack data
+ parseRuntimeOption("dalvik.vm.profile.max-stack-depth",
+ profileMaxStackDepth,
+ "-Xprofile-max-stack-depth:");
}
initArgs.version = JNI_VERSION_1_4;
@@ -894,7 +892,6 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv)
result = 0;
bail:
- free(stackTraceFile);
return result;
}
@@ -1218,7 +1215,6 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_util_EventLog),
REG_JNI(register_android_util_Log),
REG_JNI(register_android_util_FloatMath),
- REG_JNI(register_android_text_format_Time),
REG_JNI(register_android_content_AssetManager),
REG_JNI(register_android_content_StringBlock),
REG_JNI(register_android_content_XmlBlock),
diff --git a/core/jni/Time.cpp b/core/jni/Time.cpp
deleted file mode 100644
index f3037f3..0000000
--- a/core/jni/Time.cpp
+++ /dev/null
@@ -1,199 +0,0 @@
-#include "TimeUtils.h"
-#include <stdio.h>
-#include <cutils/tztime.h>
-
-namespace android {
-
-static void
-dump(const Time& t)
-{
- #ifdef HAVE_TM_GMTOFF
- long tm_gmtoff = t.t.tm_gmtoff;
- #else
- long tm_gmtoff = 0;
- #endif
- printf("%04d-%02d-%02d %02d:%02d:%02d (%d,%ld,%d,%d)\n",
- t.t.tm_year+1900, t.t.tm_mon+1, t.t.tm_mday,
- t.t.tm_hour, t.t.tm_min, t.t.tm_sec,
- t.t.tm_isdst, tm_gmtoff, t.t.tm_wday, t.t.tm_yday);
-}
-
-Time::Time()
-{
- t.tm_sec = 0;
- t.tm_min = 0;
- t.tm_hour = 0;
- t.tm_mday = 0;
- t.tm_mon = 0;
- t.tm_year = 0;
- t.tm_wday = 0;
- t.tm_yday = 0;
- t.tm_isdst = -1; // we don't know, so let the C library determine
- #ifdef HAVE_TM_GMTOFF
- t.tm_gmtoff = 0;
- #endif
-}
-
-
-#define COMPARE_FIELD(field) do { \
- int diff = a.t.field - b.t.field; \
- if (diff != 0) return diff; \
- } while(0)
-
-int
-Time::compare(Time& a, Time& b)
-{
- if (0 == strcmp(a.timezone, b.timezone)) {
- // if the timezones are the same, we can easily compare the two
- // times. Otherwise, convert to milliseconds and compare that.
- // This requires that object be normalized.
- COMPARE_FIELD(tm_year);
- COMPARE_FIELD(tm_mon);
- COMPARE_FIELD(tm_mday);
- COMPARE_FIELD(tm_hour);
- COMPARE_FIELD(tm_min);
- COMPARE_FIELD(tm_sec);
- return 0;
- } else {
- int64_t am = a.toMillis(false /* use isDst */);
- int64_t bm = b.toMillis(false /* use isDst */);
- int64_t diff = am-bm;
- return (diff < 0) ? -1 : ((diff > 0) ? 1 : 0);
- }
-}
-
-static const int DAYS_PER_MONTH[] = {
- 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
- };
-
-static inline int days_this_month(int year, int month)
-{
- int n = DAYS_PER_MONTH[month];
- if (n != 28) {
- return n;
- } else {
- int y = year;
- return ((y%4)==0&&((y%100)!=0||(y%400)==0)) ? 29 : 28;
- }
-}
-
-void
-Time::switchTimezone(const char* timezone)
-{
- time_t seconds = mktime_tz(&(this->t), this->timezone);
- localtime_tz(&seconds, &(this->t), timezone);
-}
-
-String8
-Time::format(const char *format, const struct strftime_locale *locale) const
-{
- char buf[257];
- int n = strftime_tz(buf, 257, format, &(this->t), locale);
- if (n > 0) {
- return String8(buf);
- } else {
- return String8();
- }
-}
-
-static inline short
-tochar(int n)
-{
- return (n >= 0 && n <= 9) ? ('0'+n) : ' ';
-}
-
-static inline short
-next_char(int *m, int k)
-{
- int n = *m / k;
- *m = *m % k;
- return tochar(n);
-}
-
-void
-Time::format2445(short* buf, bool hasTime) const
-{
- int n;
-
- n = t.tm_year+1900;
- buf[0] = next_char(&n, 1000);
- buf[1] = next_char(&n, 100);
- buf[2] = next_char(&n, 10);
- buf[3] = tochar(n);
-
- n = t.tm_mon+1;
- buf[4] = next_char(&n, 10);
- buf[5] = tochar(n);
-
- n = t.tm_mday;
- buf[6] = next_char(&n, 10);
- buf[7] = tochar(n);
-
- if (hasTime) {
- buf[8] = 'T';
-
- n = t.tm_hour;
- buf[9] = next_char(&n, 10);
- buf[10] = tochar(n);
-
- n = t.tm_min;
- buf[11] = next_char(&n, 10);
- buf[12] = tochar(n);
-
- n = t.tm_sec;
- buf[13] = next_char(&n, 10);
- buf[14] = tochar(n);
- bool inUtc = strcmp("UTC", timezone) == 0;
- if (inUtc) {
- buf[15] = 'Z';
- }
- }
-}
-
-String8
-Time::toString() const
-{
- String8 str;
- char* s = str.lockBuffer(150);
- #ifdef HAVE_TM_GMTOFF
- long tm_gmtoff = t.tm_gmtoff;
- #else
- long tm_gmtoff = 0;
- #endif
- sprintf(s, "%04d%02d%02dT%02d%02d%02d%s(%d,%d,%ld,%d,%d)",
- t.tm_year+1900, t.tm_mon+1, t.tm_mday, t.tm_hour, t.tm_min,
- t.tm_sec, timezone, t.tm_wday, t.tm_yday, tm_gmtoff, t.tm_isdst,
- (int)(((Time*)this)->toMillis(false /* use isDst */)/1000));
- str.unlockBuffer();
- return str;
-}
-
-void
-Time::setToNow()
-{
- time_t seconds;
- time(&seconds);
- localtime_tz(&seconds, &(this->t), this->timezone);
-}
-
-int64_t
-Time::toMillis(bool ignoreDst)
-{
- if (ignoreDst) {
- this->t.tm_isdst = -1;
- }
- int64_t r = mktime_tz(&(this->t), this->timezone);
- if (r == -1)
- return -1;
- return r * 1000;
-}
-
-void
-Time::set(int64_t millis)
-{
- time_t seconds = millis / 1000;
- localtime_tz(&seconds, &(this->t), this->timezone);
-}
-
-}; // namespace android
-
diff --git a/core/jni/TimeUtils.h b/core/jni/TimeUtils.h
deleted file mode 100644
index b19e021..0000000
--- a/core/jni/TimeUtils.h
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2005 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_TIME_H
-#define ANDROID_TIME_H
-
-#include <time.h>
-#include <cutils/tztime.h>
-#include <stdint.h>
-#include <sys/types.h>
-#include <sys/time.h>
-#include <utils/String8.h>
-#include <utils/String16.h>
-
-namespace android {
-
-/*
- * This class is the core implementation of the android.util.Time java
- * class. It doesn't implement some of the methods that are implemented
- * in Java. They could be done here, but it's not expected that this class
- * will be used. If that assumption is incorrect, feel free to update this
- * file. The reason to do it here is to not mix the implementation of this
- * class and the jni glue code.
- */
-class Time
-{
-public:
- struct tm t;
-
- // this object doesn't own this string
- const char *timezone;
-
- enum {
- SEC = 1,
- MIN = 2,
- HOUR = 3,
- MDAY = 4,
- MON = 5,
- YEAR = 6,
- WDAY = 7,
- YDAY = 8
- };
-
- static int compare(Time& a, Time& b);
-
- Time();
-
- void switchTimezone(const char *timezone);
- String8 format(const char *format, const struct strftime_locale *locale) const;
- void format2445(short* buf, bool hasTime) const;
- String8 toString() const;
- void setToNow();
- int64_t toMillis(bool ignoreDst);
- void set(int64_t millis);
-
- inline void set(int sec, int min, int hour, int mday, int mon, int year,
- int isdst)
- {
- this->t.tm_sec = sec;
- this->t.tm_min = min;
- this->t.tm_hour = hour;
- this->t.tm_mday = mday;
- this->t.tm_mon = mon;
- this->t.tm_year = year;
- this->t.tm_isdst = isdst;
-#ifdef HAVE_TM_GMTOFF
- this->t.tm_gmtoff = 0;
-#endif
- this->t.tm_wday = 0;
- this->t.tm_yday = 0;
- }
-};
-
-}; // namespace android
-
-#endif // ANDROID_TIME_H
diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp
index ab70f25..3f7df50 100644
--- a/core/jni/android_media_AudioRecord.cpp
+++ b/core/jni/android_media_AudioRecord.cpp
@@ -399,13 +399,46 @@ static jint android_media_AudioRecord_readInShortArray(JNIEnv *env, jobject thi
jshortArray javaAudioData,
jint offsetInShorts, jint sizeInShorts) {
- jint read = android_media_AudioRecord_readInByteArray(env, thiz,
- (jbyteArray) javaAudioData,
- offsetInShorts*2, sizeInShorts*2);
- if (read > 0) {
- read /= 2;
+ jshort* recordBuff = NULL;
+ // get the audio recorder from which we'll read new audio samples
+ sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
+ if (lpRecorder == NULL) {
+ ALOGE("Unable to retrieve AudioRecord object, can't record");
+ return 0;
}
- return read;
+
+ if (!javaAudioData) {
+ ALOGE("Invalid Java array to store recorded audio, can't record");
+ return 0;
+ }
+
+ // get the pointer to where we'll record the audio
+ // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such
+ // a way that it becomes much more efficient. When doing so, we will have to prevent the
+ // AudioSystem callback to be called while in critical section (in case of media server
+ // process crash for instance)
+ recordBuff = (jshort *)env->GetShortArrayElements(javaAudioData, NULL);
+
+ if (recordBuff == NULL) {
+ ALOGE("Error retrieving destination for recorded audio data, can't record");
+ return 0;
+ }
+
+ // read the new audio data from the native AudioRecord object
+ const size_t recorderBuffSize = lpRecorder->frameCount()*lpRecorder->frameSize();
+ const size_t sizeInBytes = sizeInShorts * sizeof(short);
+ ssize_t readSize = lpRecorder->read(recordBuff + offsetInShorts * sizeof(short),
+ sizeInBytes > recorderBuffSize ?
+ recorderBuffSize : sizeInBytes);
+
+ env->ReleaseShortArrayElements(javaAudioData, recordBuff, 0);
+
+ if (readSize < 0) {
+ readSize = AUDIORECORD_ERROR_INVALID_OPERATION;
+ } else {
+ readSize /= sizeof(short);
+ }
+ return (jint) readSize;
}
// ----------------------------------------------------------------------------
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index dc8d9d8..1bf42e9 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -595,13 +595,39 @@ static jint android_media_AudioTrack_native_write_short(JNIEnv *env, jobject th
jshortArray javaAudioData,
jint offsetInShorts, jint sizeInShorts,
jint javaAudioFormat) {
- jint written = android_media_AudioTrack_native_write_byte(env, thiz,
- (jbyteArray) javaAudioData,
- offsetInShorts*2, sizeInShorts*2,
- javaAudioFormat);
+ sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
+ if (lpTrack == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "Unable to retrieve AudioTrack pointer for write()");
+ return 0;
+ }
+
+ // get the pointer for the audio data from the java array
+ // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such
+ // a way that it becomes much more efficient. When doing so, we will have to prevent the
+ // AudioSystem callback to be called while in critical section (in case of media server
+ // process crash for instance)
+ jshort* cAudioData = NULL;
+ if (javaAudioData) {
+ cAudioData = (jshort *)env->GetShortArrayElements(javaAudioData, NULL);
+ if (cAudioData == NULL) {
+ ALOGE("Error retrieving source of audio data to play, can't play");
+ return 0; // out of memory or no data to load
+ }
+ } else {
+ ALOGE("NULL java array of audio data to play, can't play");
+ return 0;
+ }
+ jint written = writeToTrack(lpTrack, javaAudioFormat, (jbyte *)cAudioData,
+ offsetInShorts * sizeof(short), sizeInShorts * sizeof(short));
+ env->ReleaseShortArrayElements(javaAudioData, cAudioData, 0);
+
if (written > 0) {
- written /= 2;
+ written /= sizeof(short);
}
+ //ALOGV("write wrote %d (tried %d) shorts in the native AudioTrack with offset %d",
+ // (int)written, (int)(sizeInShorts), (int)offsetInShorts);
+
return written;
}
diff --git a/core/jni/android_text_format_Time.cpp b/core/jni/android_text_format_Time.cpp
deleted file mode 100644
index 28a8a5d..0000000
--- a/core/jni/android_text_format_Time.cpp
+++ /dev/null
@@ -1,689 +0,0 @@
-/*
-** Copyright 2006, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-#define LOG_TAG "Log_println"
-
-#include <utils/Log.h>
-#include <utils/String8.h>
-#include <assert.h>
-
-#include "jni.h"
-#include "utils/misc.h"
-#include "android_runtime/AndroidRuntime.h"
-#include "ScopedStringChars.h"
-#include "TimeUtils.h"
-#include <nativehelper/JNIHelp.h>
-#include <cutils/tztime.h>
-
-namespace android {
-
-static jfieldID g_allDayField = 0;
-static jfieldID g_secField = 0;
-static jfieldID g_minField = 0;
-static jfieldID g_hourField = 0;
-static jfieldID g_mdayField = 0;
-static jfieldID g_monField = 0;
-static jfieldID g_yearField = 0;
-static jfieldID g_wdayField = 0;
-static jfieldID g_ydayField = 0;
-static jfieldID g_isdstField = 0;
-static jfieldID g_gmtoffField = 0;
-static jfieldID g_timezoneField = 0;
-
-static jfieldID g_shortMonthsField = 0;
-static jfieldID g_longMonthsField = 0;
-static jfieldID g_longStandaloneMonthsField = 0;
-static jfieldID g_shortWeekdaysField = 0;
-static jfieldID g_longWeekdaysField = 0;
-static jfieldID g_timeOnlyFormatField = 0;
-static jfieldID g_dateOnlyFormatField = 0;
-static jfieldID g_dateTimeFormatField = 0;
-static jfieldID g_amField = 0;
-static jfieldID g_pmField = 0;
-static jfieldID g_dateCommandField = 0;
-static jfieldID g_localeField = 0;
-
-static jclass g_timeClass = NULL;
-
-static inline bool java2time(JNIEnv* env, Time* t, jobject o)
-{
- t->t.tm_sec = env->GetIntField(o, g_secField);
- t->t.tm_min = env->GetIntField(o, g_minField);
- t->t.tm_hour = env->GetIntField(o, g_hourField);
- t->t.tm_mday = env->GetIntField(o, g_mdayField);
- t->t.tm_mon = env->GetIntField(o, g_monField);
- t->t.tm_year = (env->GetIntField(o, g_yearField))-1900;
- t->t.tm_wday = env->GetIntField(o, g_wdayField);
- t->t.tm_yday = env->GetIntField(o, g_ydayField);
- t->t.tm_isdst = env->GetIntField(o, g_isdstField);
- t->t.tm_gmtoff = env->GetLongField(o, g_gmtoffField);
- bool allDay = env->GetBooleanField(o, g_allDayField);
- if (allDay &&
- ((t->t.tm_sec !=0) || (t->t.tm_min != 0) || (t->t.tm_hour != 0))) {
- jniThrowException(env, "java/lang/IllegalArgumentException",
- "allDay is true but sec, min, hour are not 0.");
- return false;
- }
- return true;
-}
-
-static inline void time2java(JNIEnv* env, jobject o, const Time &t)
-{
- env->SetIntField(o, g_secField, t.t.tm_sec);
- env->SetIntField(o, g_minField, t.t.tm_min);
- env->SetIntField(o, g_hourField, t.t.tm_hour);
- env->SetIntField(o, g_mdayField, t.t.tm_mday);
- env->SetIntField(o, g_monField, t.t.tm_mon);
- env->SetIntField(o, g_yearField, t.t.tm_year+1900);
- env->SetIntField(o, g_wdayField, t.t.tm_wday);
- env->SetIntField(o, g_ydayField, t.t.tm_yday);
- env->SetIntField(o, g_isdstField, t.t.tm_isdst);
- env->SetLongField(o, g_gmtoffField, t.t.tm_gmtoff);
-}
-
-#define ACQUIRE_TIMEZONE(This, t) \
- jstring timezoneString_##This \
- = (jstring) env->GetObjectField(This, g_timezoneField); \
- t.timezone = env->GetStringUTFChars(timezoneString_##This, NULL);
-
-#define RELEASE_TIMEZONE(This, t) \
- env->ReleaseStringUTFChars(timezoneString_##This, t.timezone);
-
-
-// ============================================================================
-
-static jlong android_text_format_Time_normalize(JNIEnv* env, jobject This,
- jboolean ignoreDst)
-{
- Time t;
- if (!java2time(env, &t, This)) return 0L;
- ACQUIRE_TIMEZONE(This, t)
-
- int64_t result = t.toMillis(ignoreDst != 0);
-
- time2java(env, This, t);
- RELEASE_TIMEZONE(This, t)
-
- return static_cast<jlong>(result);
-}
-
-static void android_text_format_Time_switchTimezone(JNIEnv* env, jobject This,
- jstring timezoneObject)
-{
- Time t;
- if (!java2time(env, &t, This)) return;
- ACQUIRE_TIMEZONE(This, t)
-
- const char* timezone = env->GetStringUTFChars(timezoneObject, NULL);
-
- t.switchTimezone(timezone);
-
- time2java(env, This, t);
- env->ReleaseStringUTFChars(timezoneObject, timezone);
- RELEASE_TIMEZONE(This, t)
-
- // we do this here because there's no point in reallocating the string
- env->SetObjectField(This, g_timezoneField, timezoneObject);
-}
-
-static jint android_text_format_Time_compare(JNIEnv* env, jobject clazz,
- jobject aObject, jobject bObject)
-{
- Time a, b;
-
- if (!java2time(env, &a, aObject)) return 0;
- ACQUIRE_TIMEZONE(aObject, a)
-
- if (!java2time(env, &b, bObject)) return 0;
- ACQUIRE_TIMEZONE(bObject, b)
-
- int result = Time::compare(a, b);
-
- RELEASE_TIMEZONE(aObject, a)
- RELEASE_TIMEZONE(bObject, b)
-
- return static_cast<jint>(result);
-}
-
-static jstring android_text_format_Time_format2445(JNIEnv* env, jobject This)
-{
- Time t;
- if (!java2time(env, &t, This)) return env->NewStringUTF("");
- bool allDay = env->GetBooleanField(This, g_allDayField);
-
- if (!allDay) {
- ACQUIRE_TIMEZONE(This, t)
- bool inUtc = strcmp("UTC", t.timezone) == 0;
- short buf[16];
- t.format2445(buf, true);
- RELEASE_TIMEZONE(This, t)
- if (inUtc) {
- // The letter 'Z' is appended to the end so allow for one
- // more character in the buffer.
- return env->NewString((jchar*)buf, 16);
- } else {
- return env->NewString((jchar*)buf, 15);
- }
- } else {
- short buf[8];
- t.format2445(buf, false);
- return env->NewString((jchar*)buf, 8);
- }
-}
-
-static jstring android_text_format_Time_format(JNIEnv* env, jobject This,
- jstring formatObject)
-{
- // We only teardown and setup our 'locale' struct and other state
- // when the Java-side locale changed. This is safe to do here
- // without locking because we're always called from Java code
- // synchronized on the class instance.
- static jobject js_locale_previous = NULL;
- static struct strftime_locale locale;
- static jstring js_mon[12], js_month[12], js_wday[7], js_weekday[7];
- static jstring js_standalone_month[12];
- static jstring js_X_fmt, js_x_fmt, js_c_fmt, js_am, js_pm, js_date_fmt;
-
- Time t;
- if (!java2time(env, &t, This)) return env->NewStringUTF("");
-
- jclass timeClass = g_timeClass;
- jobject js_locale = (jobject) env->GetStaticObjectField(timeClass, g_localeField);
- if (js_locale_previous != js_locale) {
- if (js_locale_previous != NULL) {
- // Free the old one.
- for (int i = 0; i < 12; i++) {
- env->ReleaseStringUTFChars(js_mon[i], locale.mon[i]);
- env->ReleaseStringUTFChars(js_month[i], locale.month[i]);
- env->ReleaseStringUTFChars(js_standalone_month[i], locale.standalone_month[i]);
- env->DeleteGlobalRef(js_mon[i]);
- env->DeleteGlobalRef(js_month[i]);
- env->DeleteGlobalRef(js_standalone_month[i]);
- }
-
- for (int i = 0; i < 7; i++) {
- env->ReleaseStringUTFChars(js_wday[i], locale.wday[i]);
- env->ReleaseStringUTFChars(js_weekday[i], locale.weekday[i]);
- env->DeleteGlobalRef(js_wday[i]);
- env->DeleteGlobalRef(js_weekday[i]);
- }
-
- env->ReleaseStringUTFChars(js_X_fmt, locale.X_fmt);
- env->ReleaseStringUTFChars(js_x_fmt, locale.x_fmt);
- env->ReleaseStringUTFChars(js_c_fmt, locale.c_fmt);
- env->ReleaseStringUTFChars(js_am, locale.am);
- env->ReleaseStringUTFChars(js_pm, locale.pm);
- env->ReleaseStringUTFChars(js_date_fmt, locale.date_fmt);
- env->DeleteGlobalRef(js_X_fmt);
- env->DeleteGlobalRef(js_x_fmt);
- env->DeleteGlobalRef(js_c_fmt);
- env->DeleteGlobalRef(js_am);
- env->DeleteGlobalRef(js_pm);
- env->DeleteGlobalRef(js_date_fmt);
- }
- js_locale_previous = js_locale;
-
- jobjectArray ja;
- ja = (jobjectArray) env->GetStaticObjectField(timeClass, g_shortMonthsField);
- for (int i = 0; i < 12; i++) {
- // Calendar.JANUARY == 0.
- js_mon[i] = (jstring) env->NewGlobalRef(env->GetObjectArrayElement(ja, i));
- locale.mon[i] = env->GetStringUTFChars(js_mon[i], NULL);
- }
-
- ja = (jobjectArray) env->GetStaticObjectField(timeClass, g_longMonthsField);
- for (int i = 0; i < 12; i++) {
- // Calendar.JANUARY == 0.
- js_month[i] = (jstring) env->NewGlobalRef(env->GetObjectArrayElement(ja, i));
- locale.month[i] = env->GetStringUTFChars(js_month[i], NULL);
- }
-
- ja = (jobjectArray) env->GetStaticObjectField(timeClass, g_longStandaloneMonthsField);
- for (int i = 0; i < 12; i++) {
- // Calendar.JANUARY == 0.
- js_standalone_month[i] = (jstring) env->NewGlobalRef(env->GetObjectArrayElement(ja, i));
- locale.standalone_month[i] = env->GetStringUTFChars(js_standalone_month[i], NULL);
- }
-
- ja = (jobjectArray) env->GetStaticObjectField(timeClass, g_shortWeekdaysField);
- for (int i = 0; i < 7; i++) {
- // Calendar.SUNDAY == 1, and there's an empty string in element 0.
- js_wday[i] = (jstring) env->NewGlobalRef(env->GetObjectArrayElement(ja, i + 1));
- locale.wday[i] = env->GetStringUTFChars(js_wday[i], NULL);
- }
-
- ja = (jobjectArray) env->GetStaticObjectField(timeClass, g_longWeekdaysField);
- for (int i = 0; i < 7; i++) {
- // Calendar.SUNDAY == 1, and there's an empty string in element 0.
- js_weekday[i] = (jstring) env->NewGlobalRef(env->GetObjectArrayElement(ja, i + 1));
- locale.weekday[i] = env->GetStringUTFChars(js_weekday[i], NULL);
- }
-
- js_X_fmt = (jstring) env->NewGlobalRef(env->GetStaticObjectField(
- timeClass, g_timeOnlyFormatField));
- locale.X_fmt = env->GetStringUTFChars(js_X_fmt, NULL);
-
- js_x_fmt = (jstring) env->NewGlobalRef(env->GetStaticObjectField(
- timeClass, g_dateOnlyFormatField));
- locale.x_fmt = env->GetStringUTFChars(js_x_fmt, NULL);
-
- js_c_fmt = (jstring) env->NewGlobalRef(env->GetStaticObjectField(
- timeClass, g_dateTimeFormatField));
- locale.c_fmt = env->GetStringUTFChars(js_c_fmt, NULL);
-
- js_am = (jstring) env->NewGlobalRef(env->GetStaticObjectField(
- timeClass, g_amField));
- locale.am = env->GetStringUTFChars(js_am, NULL);
-
- js_pm = (jstring) env->NewGlobalRef(env->GetStaticObjectField(
- timeClass, g_pmField));
- locale.pm = env->GetStringUTFChars(js_pm, NULL);
-
- js_date_fmt = (jstring) env->NewGlobalRef(env->GetStaticObjectField(
- timeClass, g_dateCommandField));
- locale.date_fmt = env->GetStringUTFChars(js_date_fmt, NULL);
- }
-
- ACQUIRE_TIMEZONE(This, t)
-
- const char* format = env->GetStringUTFChars(formatObject, NULL);
-
- String8 r = t.format(format, &locale);
-
- env->ReleaseStringUTFChars(formatObject, format);
- RELEASE_TIMEZONE(This, t)
-
- return env->NewStringUTF(r.string());
-}
-
-
-static jstring android_text_format_Time_toString(JNIEnv* env, jobject This)
-{
- Time t;
- if (!java2time(env, &t, This)) return env->NewStringUTF("");
- ACQUIRE_TIMEZONE(This, t)
-
- String8 r = t.toString();
-
- RELEASE_TIMEZONE(This, t)
-
- return env->NewStringUTF(r.string());
-}
-
-static void android_text_format_Time_setToNow(JNIEnv* env, jobject This)
-{
- env->SetBooleanField(This, g_allDayField, JNI_FALSE);
- Time t;
- ACQUIRE_TIMEZONE(This, t)
-
- t.setToNow();
-
- time2java(env, This, t);
- RELEASE_TIMEZONE(This, t)
-}
-
-static jlong android_text_format_Time_toMillis(JNIEnv* env, jobject This,
- jboolean ignoreDst)
-{
- Time t;
- if (!java2time(env, &t, This)) return 0L;
- ACQUIRE_TIMEZONE(This, t)
-
- int64_t result = t.toMillis(ignoreDst != 0);
-
- RELEASE_TIMEZONE(This, t)
-
- return static_cast<jlong>(result);
-}
-
-static void android_text_format_Time_set(JNIEnv* env, jobject This, jlong millis)
-{
- env->SetBooleanField(This, g_allDayField, JNI_FALSE);
- Time t;
- ACQUIRE_TIMEZONE(This, t)
-
- t.set(millis);
-
- time2java(env, This, t);
- RELEASE_TIMEZONE(This, t)
-}
-
-
-// ============================================================================
-// Just do this here because it's not worth recreating the strings
-
-static int get_char(JNIEnv* env, const ScopedStringChars& s, int spos, int mul,
- bool* thrown)
-{
- jchar c = s[spos];
- if (c >= '0' && c <= '9') {
- return (c - '0') * mul;
- } else {
- if (!*thrown) {
- jniThrowExceptionFmt(env, "android/util/TimeFormatException",
- "Parse error at pos=%d", spos);
- *thrown = true;
- }
- return 0;
- }
-}
-
-static bool check_char(JNIEnv* env, const ScopedStringChars& s, int spos, jchar expected)
-{
- jchar c = s[spos];
- if (c != expected) {
- jniThrowExceptionFmt(env, "android/util/TimeFormatException",
- "Unexpected character 0x%02x at pos=%d. Expected %c.",
- c, spos, expected);
- return false;
- }
- return true;
-}
-
-
-static jboolean android_text_format_Time_parse(JNIEnv* env, jobject This, jstring strObj)
-{
- jsize len = env->GetStringLength(strObj);
- if (len < 8) {
- jniThrowException(env, "android/util/TimeFormatException",
- "String too short -- expected at least 8 characters.");
- return JNI_FALSE;
- }
-
- jboolean inUtc = JNI_FALSE;
-
- ScopedStringChars s(env, strObj);
-
- // year
- int n;
- bool thrown = false;
- n = get_char(env, s, 0, 1000, &thrown);
- n += get_char(env, s, 1, 100, &thrown);
- n += get_char(env, s, 2, 10, &thrown);
- n += get_char(env, s, 3, 1, &thrown);
- if (thrown) return JNI_FALSE;
- env->SetIntField(This, g_yearField, n);
-
- // month
- n = get_char(env, s, 4, 10, &thrown);
- n += get_char(env, s, 5, 1, &thrown);
- n--;
- if (thrown) return JNI_FALSE;
- env->SetIntField(This, g_monField, n);
-
- // day of month
- n = get_char(env, s, 6, 10, &thrown);
- n += get_char(env, s, 7, 1, &thrown);
- if (thrown) return JNI_FALSE;
- env->SetIntField(This, g_mdayField, n);
-
- if (len > 8) {
- // T
- if (!check_char(env, s, 8, 'T')) return JNI_FALSE;
- env->SetBooleanField(This, g_allDayField, JNI_FALSE);
-
- // hour
- n = get_char(env, s, 9, 10, &thrown);
- n += get_char(env, s, 10, 1, &thrown);
- if (thrown) return JNI_FALSE;
- env->SetIntField(This, g_hourField, n);
-
- // min
- n = get_char(env, s, 11, 10, &thrown);
- n += get_char(env, s, 12, 1, &thrown);
- if (thrown) return JNI_FALSE;
- env->SetIntField(This, g_minField, n);
-
- // sec
- n = get_char(env, s, 13, 10, &thrown);
- n += get_char(env, s, 14, 1, &thrown);
- if (thrown) return JNI_FALSE;
- env->SetIntField(This, g_secField, n);
-
- if (len > 15) {
- // Z
- if (!check_char(env, s, 15, 'Z')) return JNI_FALSE;
- inUtc = JNI_TRUE;
- }
- } else {
- env->SetBooleanField(This, g_allDayField, JNI_TRUE);
- env->SetIntField(This, g_hourField, 0);
- env->SetIntField(This, g_minField, 0);
- env->SetIntField(This, g_secField, 0);
- }
-
- env->SetIntField(This, g_wdayField, 0);
- env->SetIntField(This, g_ydayField, 0);
- env->SetIntField(This, g_isdstField, -1);
- env->SetLongField(This, g_gmtoffField, 0);
-
- return inUtc;
-}
-
-static jboolean android_text_format_Time_parse3339(JNIEnv* env,
- jobject This,
- jstring strObj)
-{
- jsize len = env->GetStringLength(strObj);
- if (len < 10) {
- jniThrowException(env, "android/util/TimeFormatException",
- "String too short --- expected at least 10 characters.");
- return JNI_FALSE;
- }
-
- jboolean inUtc = JNI_FALSE;
-
- ScopedStringChars s(env, strObj);
-
- // year
- int n;
- bool thrown = false;
- n = get_char(env, s, 0, 1000, &thrown);
- n += get_char(env, s, 1, 100, &thrown);
- n += get_char(env, s, 2, 10, &thrown);
- n += get_char(env, s, 3, 1, &thrown);
- if (thrown) return JNI_FALSE;
- env->SetIntField(This, g_yearField, n);
-
- // -
- if (!check_char(env, s, 4, '-')) return JNI_FALSE;
-
- // month
- n = get_char(env, s, 5, 10, &thrown);
- n += get_char(env, s, 6, 1, &thrown);
- --n;
- if (thrown) return JNI_FALSE;
- env->SetIntField(This, g_monField, n);
-
- // -
- if (!check_char(env, s, 7, '-')) return JNI_FALSE;
-
- // day
- n = get_char(env, s, 8, 10, &thrown);
- n += get_char(env, s, 9, 1, &thrown);
- if (thrown) return JNI_FALSE;
- env->SetIntField(This, g_mdayField, n);
-
- if (len >= 19) {
- // T
- if (!check_char(env, s, 10, 'T')) return JNI_FALSE;
-
- env->SetBooleanField(This, g_allDayField, JNI_FALSE);
- // hour
- n = get_char(env, s, 11, 10, &thrown);
- n += get_char(env, s, 12, 1, &thrown);
- if (thrown) return JNI_FALSE;
- int hour = n;
- // env->SetIntField(This, g_hourField, n);
-
- // :
- if (!check_char(env, s, 13, ':')) return JNI_FALSE;
-
- // minute
- n = get_char(env, s, 14, 10, &thrown);
- n += get_char(env, s, 15, 1, &thrown);
- if (thrown) return JNI_FALSE;
- int minute = n;
- // env->SetIntField(This, g_minField, n);
-
- // :
- if (!check_char(env, s, 16, ':')) return JNI_FALSE;
-
- // second
- n = get_char(env, s, 17, 10, &thrown);
- n += get_char(env, s, 18, 1, &thrown);
- if (thrown) return JNI_FALSE;
- env->SetIntField(This, g_secField, n);
-
- // skip the '.XYZ' -- we don't care about subsecond precision.
- int tz_index = 19;
- if (tz_index < len && s[tz_index] == '.') {
- do {
- tz_index++;
- } while (tz_index < len
- && s[tz_index] >= '0'
- && s[tz_index] <= '9');
- }
-
- int offset = 0;
- if (len > tz_index) {
- char c = s[tz_index];
-
- // NOTE: the offset is meant to be subtracted to get from local time
- // to UTC. we therefore use 1 for '-' and -1 for '+'.
- switch (c) {
- case 'Z':
- // Zulu time -- UTC
- offset = 0;
- break;
- case '-':
- offset = 1;
- break;
- case '+':
- offset = -1;
- break;
- default:
- jniThrowExceptionFmt(env, "android/util/TimeFormatException",
- "Unexpected character 0x%02x at position %d. Expected + or -",
- c, tz_index);
- return JNI_FALSE;
- }
- inUtc = JNI_TRUE;
-
- if (offset != 0) {
- if (len < tz_index + 6) {
- jniThrowExceptionFmt(env, "android/util/TimeFormatException",
- "Unexpected length; should be %d characters",
- tz_index + 6);
- return JNI_FALSE;
- }
-
- // hour
- n = get_char(env, s, tz_index + 1, 10, &thrown);
- n += get_char(env, s, tz_index + 2, 1, &thrown);
- if (thrown) return JNI_FALSE;
- n *= offset;
- hour += n;
-
- // :
- if (!check_char(env, s, tz_index + 3, ':')) return JNI_FALSE;
-
- // minute
- n = get_char(env, s, tz_index + 4, 10, &thrown);
- n += get_char(env, s, tz_index + 5, 1, &thrown);
- if (thrown) return JNI_FALSE;
- n *= offset;
- minute += n;
- }
- }
- env->SetIntField(This, g_hourField, hour);
- env->SetIntField(This, g_minField, minute);
-
- if (offset != 0) {
- // we need to normalize after applying the hour and minute offsets
- android_text_format_Time_normalize(env, This, false /* use isdst */);
- // The timezone is set to UTC in the calling Java code.
- }
- } else {
- env->SetBooleanField(This, g_allDayField, JNI_TRUE);
- env->SetIntField(This, g_hourField, 0);
- env->SetIntField(This, g_minField, 0);
- env->SetIntField(This, g_secField, 0);
- }
-
- env->SetIntField(This, g_wdayField, 0);
- env->SetIntField(This, g_ydayField, 0);
- env->SetIntField(This, g_isdstField, -1);
- env->SetLongField(This, g_gmtoffField, 0);
-
- return inUtc;
-}
-
-// ============================================================================
-/*
- * JNI registration.
- */
-static JNINativeMethod gMethods[] = {
- /* name, signature, funcPtr */
- { "normalize", "(Z)J", (void*)android_text_format_Time_normalize },
- { "switchTimezone", "(Ljava/lang/String;)V", (void*)android_text_format_Time_switchTimezone },
- { "nativeCompare", "(Landroid/text/format/Time;Landroid/text/format/Time;)I", (void*)android_text_format_Time_compare },
- { "format1", "(Ljava/lang/String;)Ljava/lang/String;", (void*)android_text_format_Time_format },
- { "format2445", "()Ljava/lang/String;", (void*)android_text_format_Time_format2445 },
- { "toString", "()Ljava/lang/String;", (void*)android_text_format_Time_toString },
- { "nativeParse", "(Ljava/lang/String;)Z", (void*)android_text_format_Time_parse },
- { "nativeParse3339", "(Ljava/lang/String;)Z", (void*)android_text_format_Time_parse3339 },
- { "setToNow", "()V", (void*)android_text_format_Time_setToNow },
- { "toMillis", "(Z)J", (void*)android_text_format_Time_toMillis },
- { "set", "(J)V", (void*)android_text_format_Time_set }
-};
-
-int register_android_text_format_Time(JNIEnv* env)
-{
- jclass timeClass = env->FindClass("android/text/format/Time");
-
- g_timeClass = (jclass) env->NewGlobalRef(timeClass);
-
- g_allDayField = env->GetFieldID(timeClass, "allDay", "Z");
- g_secField = env->GetFieldID(timeClass, "second", "I");
- g_minField = env->GetFieldID(timeClass, "minute", "I");
- g_hourField = env->GetFieldID(timeClass, "hour", "I");
- g_mdayField = env->GetFieldID(timeClass, "monthDay", "I");
- g_monField = env->GetFieldID(timeClass, "month", "I");
- g_yearField = env->GetFieldID(timeClass, "year", "I");
- g_wdayField = env->GetFieldID(timeClass, "weekDay", "I");
- g_ydayField = env->GetFieldID(timeClass, "yearDay", "I");
- g_isdstField = env->GetFieldID(timeClass, "isDst", "I");
- g_gmtoffField = env->GetFieldID(timeClass, "gmtoff", "J");
- g_timezoneField = env->GetFieldID(timeClass, "timezone", "Ljava/lang/String;");
-
- g_shortMonthsField = env->GetStaticFieldID(timeClass, "sShortMonths", "[Ljava/lang/String;");
- g_longMonthsField = env->GetStaticFieldID(timeClass, "sLongMonths", "[Ljava/lang/String;");
- g_longStandaloneMonthsField = env->GetStaticFieldID(timeClass, "sLongStandaloneMonths", "[Ljava/lang/String;");
- g_shortWeekdaysField = env->GetStaticFieldID(timeClass, "sShortWeekdays", "[Ljava/lang/String;");
- g_longWeekdaysField = env->GetStaticFieldID(timeClass, "sLongWeekdays", "[Ljava/lang/String;");
- g_timeOnlyFormatField = env->GetStaticFieldID(timeClass, "sTimeOnlyFormat", "Ljava/lang/String;");
- g_dateOnlyFormatField = env->GetStaticFieldID(timeClass, "sDateOnlyFormat", "Ljava/lang/String;");
- g_dateTimeFormatField = env->GetStaticFieldID(timeClass, "sDateTimeFormat", "Ljava/lang/String;");
- g_amField = env->GetStaticFieldID(timeClass, "sAm", "Ljava/lang/String;");
- g_pmField = env->GetStaticFieldID(timeClass, "sPm", "Ljava/lang/String;");
- g_dateCommandField = env->GetStaticFieldID(timeClass, "sDateCommand", "Ljava/lang/String;");
- g_localeField = env->GetStaticFieldID(timeClass, "sLocale", "Ljava/util/Locale;");
-
- return AndroidRuntime::registerNativeMethods(env, "android/text/format/Time", gMethods, NELEM(gMethods));
-}
-
-}; // namespace android
diff --git a/core/res/res/values-mcc214-mnc07/config.xml b/core/res/res/values-mcc214-mnc07/config.xml
new file mode 100644
index 0000000..ce7526c
--- /dev/null
+++ b/core/res/res/values-mcc214-mnc07/config.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You my 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.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Array of ConnectivityManager.TYPE_xxxx values allowable for tethering -->
+ <!-- Common options are [1, 4] for TYPE_WIFI and TYPE_MOBILE_DUN or
+ <!== [0,1,5,7] for TYPE_MOBILE, TYPE_WIFI, TYPE_MOBILE_HIPRI and TYPE_BLUETOOTH -->
+ <integer-array translatable="false" name="config_tether_upstream_types">
+ <item>1</item>
+ <item>4</item>
+ <item>7</item>
+ <item>9</item>
+ </integer-array>
+
+ <!-- String containing the apn value for tethering. May be overriden by secure settings
+ TETHER_DUN_APN. Value is a comma separated series of strings:
+ "name,apn,proxy,port,username,password,server,mmsc,mmsproxy,mmsport,mcc,mnc,auth,type"
+ note that empty fields can be ommitted: "name,apn,,,,,,,,,310,260,,DUN" -->
+ <string translatable="false" name="config_tether_apndata">Conexion Compartida,movistar.es,,,MOVISTAR,MOVISTAR,,,,,214,07,1,DUN</string>
+
+</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 538003f..cc2a298 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1387,7 +1387,7 @@
<string name="permlab_accessLocationExtraCommands">access extra location provider commands</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_accessLocationExtraCommands">Allows the app to access
- extra location provider commands. This may allow the app to to interfere
+ extra location provider commands. This may allow the app to interfere
with the operation of the GPS or other location sources.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.mk
new file mode 100644
index 0000000..9f04228
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.mk
@@ -0,0 +1,41 @@
+# Copyright (C) 2014 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_SDK_VERSION := 9
+
+LOCAL_PACKAGE_NAME := MultiDexLegacyVersionedTestApp_v1
+
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-multidex
+
+mainDexList:= \
+ $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),$(LOCAL_IS_HOST_MODULE),common)/maindex.list
+
+LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(mainDexList) --minimal-main-dex
+
+include $(BUILD_PACKAGE)
+
+$(mainDexList): $(full_classes_proguard_jar) | $(HOST_OUT_EXECUTABLES)/mainDexClasses
+ $(HOST_OUT_EXECUTABLES)/mainDexClasses $< 1>$@
+ echo "com/android/framework/multidexlegacyversionedtestapp/MultiDexUpdateTest.class" >> $@
+
+$(built_dex_intermediate): $(mainDexList)
+
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/AndroidManifest.xml b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/AndroidManifest.xml
new file mode 100644
index 0000000..c7b066d
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.framework.multidexlegacyversionedtestapp"
+ android:versionCode="1"
+ android:versionName="1.0" >
+
+ <uses-sdk
+ android:minSdkVersion="9"
+ android:targetSdkVersion="18" />
+
+ <application
+ android:name="android.support.multidex.MultiDexApplication"
+ android:allowBackup="true"
+ android:label="MultiDexLegacyVersionedTestApp_v1">
+ <activity
+ android:name="com.android.framework.multidexlegacyversionedtestapp.MainActivity"
+ android:label="MultiDexLegacyVersionedTestApp_v1" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="android.test.InstrumentationTestRunner"
+ android:targetPackage="com.android.framework.multidexlegacyversionedtestapp"
+ android:label="Test for MultiDexLegacyVersionedTestApp_v1" />
+
+</manifest>
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/res/layout/activity_main.xml b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/res/layout/activity_main.xml
new file mode 100644
index 0000000..58ae67a
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/res/layout/activity_main.xml
@@ -0,0 +1,7 @@
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context=".MainActivity" >
+
+</RelativeLayout>
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/src/com/android/framework/multidexlegacyversionedtestapp/ClassForMainDex.java b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/src/com/android/framework/multidexlegacyversionedtestapp/ClassForMainDex.java
new file mode 100644
index 0000000..8662562
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/src/com/android/framework/multidexlegacyversionedtestapp/ClassForMainDex.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2014 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.framework.multidexlegacyversionedtestapp;
+
+/**
+ * Class directly referenced from Activity, will be kept in main dex. The class is not referenced
+ * by <clinit> or <init>, its direct references are not kept in main dex.
+ */
+public class ClassForMainDex {
+
+ public static int getVersion() {
+ return Version.getVersion();
+ }
+
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/src/com/android/framework/multidexlegacyversionedtestapp/MainActivity.java b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/src/com/android/framework/multidexlegacyversionedtestapp/MainActivity.java
new file mode 100644
index 0000000..351d860
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/src/com/android/framework/multidexlegacyversionedtestapp/MainActivity.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2014 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.framework.multidexlegacyversionedtestapp;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class MainActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+ }
+
+ public int getVersion() {
+ return ClassForMainDex.getVersion();
+ }
+
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/src/com/android/framework/multidexlegacyversionedtestapp/MultiDexUpdateTest.java b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/src/com/android/framework/multidexlegacyversionedtestapp/MultiDexUpdateTest.java
new file mode 100644
index 0000000..24b4d69
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/src/com/android/framework/multidexlegacyversionedtestapp/MultiDexUpdateTest.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2014 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.framework.multidexlegacyversionedtestapp;
+
+import android.test.ActivityInstrumentationTestCase2;
+
+/**
+ * Run the tests with: <code>adb shell am instrument -w
+ com.android.framework.multidexlegacyversionedtestapp/android.test.InstrumentationTestRunner
+</code>
+ */
+public class MultiDexUpdateTest extends ActivityInstrumentationTestCase2<MainActivity>
+{
+ public MultiDexUpdateTest() {
+ super(MainActivity.class);
+ }
+
+ /**
+ * Tests that all classes of the application can be loaded. Verifies also that we load the
+ * correct version of {@link Version} ie the class is the secondary dex file.
+ */
+ public void testAllClassAvailable()
+ {
+ assertEquals(1, getActivity().getVersion());
+ }
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/src/com/android/framework/multidexlegacyversionedtestapp/Version.java b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/src/com/android/framework/multidexlegacyversionedtestapp/Version.java
new file mode 100644
index 0000000..eb9827a
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/src/com/android/framework/multidexlegacyversionedtestapp/Version.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2014 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.framework.multidexlegacyversionedtestapp;
+
+/* can go in secondary dex */
+public class Version {
+
+ public static int getVersion() {
+ return 1;
+ }
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.mk
new file mode 100644
index 0000000..1b8da41
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.mk
@@ -0,0 +1,41 @@
+# Copyright (C) 2014 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_SDK_VERSION := 9
+
+LOCAL_PACKAGE_NAME := MultiDexLegacyVersionedTestApp_v2
+
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-multidex
+
+mainDexList:= \
+ $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),$(LOCAL_IS_HOST_MODULE),common)/maindex.list
+
+LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(mainDexList) --minimal-main-dex
+
+include $(BUILD_PACKAGE)
+
+$(mainDexList): $(full_classes_proguard_jar) | $(HOST_OUT_EXECUTABLES)/mainDexClasses
+ $(HOST_OUT_EXECUTABLES)/mainDexClasses $< 1>$@
+ echo "com/android/framework/multidexlegacyversionedtestapp/MultiDexUpdateTest.class" >> $@
+
+$(built_dex_intermediate): $(mainDexList)
+
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/AndroidManifest.xml b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/AndroidManifest.xml
new file mode 100644
index 0000000..4d24793
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.framework.multidexlegacyversionedtestapp"
+ android:versionCode="2"
+ android:versionName="2.0" >
+
+ <uses-sdk
+ android:minSdkVersion="9"
+ android:targetSdkVersion="18" />
+
+ <application
+ android:name="android.support.multidex.MultiDexApplication"
+ android:allowBackup="true"
+ android:label="MultiDexLegacyVersionedTestApp_v2">
+ <activity
+ android:name="com.android.framework.multidexlegacyversionedtestapp.MainActivity"
+ android:label="MultiDexLegacyVersionedTestApp_v2" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="android.test.InstrumentationTestRunner"
+ android:targetPackage="com.android.framework.multidexlegacyversionedtestapp"
+ android:label="Test for MultiDexLegacyVersionedTestApp_v2" />
+
+</manifest>
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/res/layout/activity_main.xml b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/res/layout/activity_main.xml
new file mode 100644
index 0000000..58ae67a
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/res/layout/activity_main.xml
@@ -0,0 +1,7 @@
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context=".MainActivity" >
+
+</RelativeLayout>
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/src/com/android/framework/multidexlegacyversionedtestapp/ClassForMainDex.java b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/src/com/android/framework/multidexlegacyversionedtestapp/ClassForMainDex.java
new file mode 100644
index 0000000..8662562
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/src/com/android/framework/multidexlegacyversionedtestapp/ClassForMainDex.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2014 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.framework.multidexlegacyversionedtestapp;
+
+/**
+ * Class directly referenced from Activity, will be kept in main dex. The class is not referenced
+ * by <clinit> or <init>, its direct references are not kept in main dex.
+ */
+public class ClassForMainDex {
+
+ public static int getVersion() {
+ return Version.getVersion();
+ }
+
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/src/com/android/framework/multidexlegacyversionedtestapp/MainActivity.java b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/src/com/android/framework/multidexlegacyversionedtestapp/MainActivity.java
new file mode 100644
index 0000000..351d860
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/src/com/android/framework/multidexlegacyversionedtestapp/MainActivity.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2014 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.framework.multidexlegacyversionedtestapp;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class MainActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+ }
+
+ public int getVersion() {
+ return ClassForMainDex.getVersion();
+ }
+
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/src/com/android/framework/multidexlegacyversionedtestapp/MultiDexUpdateTest.java b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/src/com/android/framework/multidexlegacyversionedtestapp/MultiDexUpdateTest.java
new file mode 100644
index 0000000..f130cb2
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/src/com/android/framework/multidexlegacyversionedtestapp/MultiDexUpdateTest.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2014 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.framework.multidexlegacyversionedtestapp;
+
+import android.test.ActivityInstrumentationTestCase2;
+
+/**
+ * Run the tests with: <code>adb shell am instrument -w
+ com.android.framework.multidexlegacyversionedtestapp/android.test.InstrumentationTestRunner
+</code>
+ */
+public class MultiDexUpdateTest extends ActivityInstrumentationTestCase2<MainActivity>
+{
+ public MultiDexUpdateTest() {
+ super(MainActivity.class);
+ }
+
+ /**
+ * Tests that all classes of the application can be loaded. Verifies also that we load the
+ * correct version of {@link Version} ie the class is the secondary dex file.
+ */
+ public void testAllClassAvailable()
+ {
+ assertEquals(2, getActivity().getVersion());
+ }
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/src/com/android/framework/multidexlegacyversionedtestapp/Version.java b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/src/com/android/framework/multidexlegacyversionedtestapp/Version.java
new file mode 100644
index 0000000..1f2305f
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/src/com/android/framework/multidexlegacyversionedtestapp/Version.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2014 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.framework.multidexlegacyversionedtestapp;
+
+/* can go in secondary dex */
+public class Version {
+
+ public static int getVersion() {
+ return 2;
+ }
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.mk
new file mode 100644
index 0000000..945bfcc
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.mk
@@ -0,0 +1,41 @@
+# Copyright (C) 2014 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_SDK_VERSION := 9
+
+LOCAL_PACKAGE_NAME := MultiDexLegacyVersionedTestApp_v3
+
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-multidex
+
+mainDexList:= \
+ $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),$(LOCAL_IS_HOST_MODULE),common)/maindex.list
+
+LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(mainDexList) --minimal-main-dex
+
+include $(BUILD_PACKAGE)
+
+$(mainDexList): $(full_classes_proguard_jar) | $(HOST_OUT_EXECUTABLES)/mainDexClasses
+ $(HOST_OUT_EXECUTABLES)/mainDexClasses $< 1>$@
+ echo "com/android/framework/multidexlegacyversionedtestapp/MultiDexUpdateTest.class" >> $@
+
+$(built_dex_intermediate): $(mainDexList)
+
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/AndroidManifest.xml b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/AndroidManifest.xml
new file mode 100644
index 0000000..76c92dd
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.framework.multidexlegacyversionedtestapp"
+ android:versionCode="3"
+ android:versionName="3.0" >
+
+ <uses-sdk
+ android:minSdkVersion="9"
+ android:targetSdkVersion="18" />
+
+ <application
+ android:name="android.support.multidex.MultiDexApplication"
+ android:allowBackup="true"
+ android:label="MultiDexLegacyVersionedTestApp_v3">
+ <activity
+ android:name="com.android.framework.multidexlegacyversionedtestapp.MainActivity"
+ android:label="MultiDexLegacyVersionedTestApp_v3" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="android.test.InstrumentationTestRunner"
+ android:targetPackage="com.android.framework.multidexlegacyversionedtestapp"
+ android:label="Test for MultiDexLegacyVersionedTestApp_v3" />
+
+</manifest>
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/res/layout/activity_main.xml b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/res/layout/activity_main.xml
new file mode 100644
index 0000000..58ae67a
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/res/layout/activity_main.xml
@@ -0,0 +1,7 @@
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context=".MainActivity" >
+
+</RelativeLayout>
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/src/com/android/framework/multidexlegacyversionedtestapp/ClassForMainDex.java b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/src/com/android/framework/multidexlegacyversionedtestapp/ClassForMainDex.java
new file mode 100644
index 0000000..8662562
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/src/com/android/framework/multidexlegacyversionedtestapp/ClassForMainDex.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2014 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.framework.multidexlegacyversionedtestapp;
+
+/**
+ * Class directly referenced from Activity, will be kept in main dex. The class is not referenced
+ * by <clinit> or <init>, its direct references are not kept in main dex.
+ */
+public class ClassForMainDex {
+
+ public static int getVersion() {
+ return Version.getVersion();
+ }
+
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/src/com/android/framework/multidexlegacyversionedtestapp/MainActivity.java b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/src/com/android/framework/multidexlegacyversionedtestapp/MainActivity.java
new file mode 100644
index 0000000..351d860
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/src/com/android/framework/multidexlegacyversionedtestapp/MainActivity.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2014 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.framework.multidexlegacyversionedtestapp;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class MainActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+ }
+
+ public int getVersion() {
+ return ClassForMainDex.getVersion();
+ }
+
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/src/com/android/framework/multidexlegacyversionedtestapp/MultiDexUpdateTest.java b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/src/com/android/framework/multidexlegacyversionedtestapp/MultiDexUpdateTest.java
new file mode 100644
index 0000000..67aa478
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/src/com/android/framework/multidexlegacyversionedtestapp/MultiDexUpdateTest.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2014 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.framework.multidexlegacyversionedtestapp;
+
+import android.test.ActivityInstrumentationTestCase2;
+
+/**
+ * Run the tests with: <code>adb shell am instrument -w
+ com.android.framework.multidexlegacyversionedtestapp/android.test.InstrumentationTestRunner
+</code>
+ */
+public class MultiDexUpdateTest extends ActivityInstrumentationTestCase2<MainActivity>
+{
+ public MultiDexUpdateTest() {
+ super(MainActivity.class);
+ }
+
+ /**
+ * Tests that all classes of the application can be loaded. Verifies also that we load the
+ * correct version of {@link Version} ie the class is the secondary dex file.
+ */
+ public void testAllClassAvailable()
+ {
+ assertEquals(3, getActivity().getVersion());
+ }
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/src/com/android/framework/multidexlegacyversionedtestapp/Version.java b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/src/com/android/framework/multidexlegacyversionedtestapp/Version.java
new file mode 100644
index 0000000..1c8ef3b
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/src/com/android/framework/multidexlegacyversionedtestapp/Version.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2014 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.framework.multidexlegacyversionedtestapp;
+
+/* can go in secondary dex */
+public class Version {
+
+ public static int getVersion() {
+ return 3;
+ }
+}