summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/java/android/app/ActivityManager.java18
-rw-r--r--core/java/android/app/ActivityThread.java20
-rw-r--r--core/java/android/app/ContextImpl.java12
-rw-r--r--core/java/android/app/backup/IRestoreSession.aidl19
-rw-r--r--core/java/android/app/backup/RestoreSession.java34
-rw-r--r--core/java/android/bluetooth/BluetoothHealth.java75
-rw-r--r--core/java/android/bluetooth/BluetoothHealthAppConfiguration.java45
-rw-r--r--core/java/android/bluetooth/BluetoothHealthCallback.java42
-rw-r--r--core/java/android/bluetooth/BluetoothServerSocket.java10
-rw-r--r--core/java/android/bluetooth/IBluetooth.aidl4
-rw-r--r--core/java/android/net/NetworkStats.java135
-rw-r--r--core/java/android/net/NetworkUtils.java18
-rw-r--r--core/java/android/os/Looper.java4
-rw-r--r--core/java/android/os/Process.java145
-rw-r--r--core/java/android/os/ServiceManager.java2
-rw-r--r--core/java/android/os/storage/StorageVolume.java24
-rw-r--r--core/java/android/pim/EventRecurrence.java892
-rw-r--r--core/java/android/pim/ICalendar.java660
-rw-r--r--core/java/android/pim/RecurrenceSet.java511
-rw-r--r--core/java/android/provider/CalendarContract.java115
-rw-r--r--core/java/android/provider/ContactsContract.java119
-rw-r--r--core/java/android/provider/Settings.java5
-rw-r--r--core/java/android/server/BluetoothHealthProfileHandler.java49
-rwxr-xr-x[-rw-r--r--]core/java/android/server/BluetoothService.java10
-rw-r--r--core/java/android/speech/tts/AudioPlaybackHandler.java7
-rw-r--r--core/java/android/speech/tts/EventLogTags.logtags6
-rw-r--r--core/java/android/speech/tts/EventLogger.java175
-rw-r--r--core/java/android/speech/tts/PlaybackSynthesisCallback.java33
-rw-r--r--core/java/android/speech/tts/SynthesisMessageParams.java4
-rw-r--r--core/java/android/speech/tts/TextToSpeechService.java13
-rw-r--r--core/java/android/text/style/SuggestionSpan.java16
-rw-r--r--core/java/android/util/NtpTrustedTime.java60
-rw-r--r--core/java/android/view/HardwareRenderer.java88
-rw-r--r--core/java/android/view/View.java9
-rw-r--r--core/java/android/view/ViewAncestor.java8
-rw-r--r--core/java/android/view/ViewGroup.java9
-rw-r--r--core/java/android/webkit/L10nUtils.java14
-rw-r--r--core/java/android/webkit/WebViewCore.java4
-rw-r--r--core/java/android/webkit/ZoomManager.java7
-rw-r--r--core/java/android/widget/TextView.java56
-rwxr-xr-xcore/java/com/android/internal/os/PkgUsageStats.java22
-rw-r--r--core/java/com/android/internal/os/RuntimeInit.java13
-rw-r--r--core/java/com/android/internal/os/WrapperInit.java23
-rw-r--r--core/java/com/android/internal/os/ZygoteConnection.java22
-rw-r--r--core/java/com/android/internal/os/ZygoteInit.java11
-rw-r--r--core/java/com/android/internal/util/Protocol.java5
-rw-r--r--core/java/com/android/internal/view/menu/ActionMenuPresenter.java80
-rw-r--r--core/java/com/android/internal/view/menu/BaseMenuPresenter.java10
-rw-r--r--core/java/com/android/internal/view/menu/IconMenuPresenter.java54
-rw-r--r--core/java/com/android/internal/view/menu/ListMenuPresenter.java27
-rw-r--r--core/java/com/android/internal/view/menu/MenuBuilder.java57
-rw-r--r--core/java/com/android/internal/view/menu/MenuPopupHelper.java15
-rw-r--r--core/java/com/android/internal/view/menu/MenuPresenter.java21
-rw-r--r--core/java/com/android/internal/widget/ActionBarView.java15
-rw-r--r--core/jni/android_net_NetUtils.cpp13
-rw-r--r--core/jni/android_server_BluetoothService.cpp9
-rw-r--r--core/jni/android_util_Process.cpp24
-rw-r--r--core/res/res/drawable-hdpi/dialog_bottom_holo_dark.9.pngbin846 -> 796 bytes
-rw-r--r--core/res/res/drawable-hdpi/dialog_bottom_holo_light.9.pngbin1007 -> 850 bytes
-rw-r--r--core/res/res/drawable-hdpi/dialog_full_holo_dark.9.pngbin1653 -> 1300 bytes
-rw-r--r--core/res/res/drawable-hdpi/dialog_full_holo_light.9.pngbin2039 -> 1216 bytes
-rw-r--r--core/res/res/drawable-hdpi/dialog_middle_holo_dark.9.pngbin361 -> 270 bytes
-rw-r--r--core/res/res/drawable-hdpi/dialog_middle_holo_light.9.pngbin536 -> 280 bytes
-rw-r--r--core/res/res/drawable-hdpi/dialog_top_holo_dark.9.pngbin825 -> 761 bytes
-rw-r--r--core/res/res/drawable-hdpi/dialog_top_holo_light.9.pngbin837 -> 669 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_btn_back.pngbin1952 -> 0 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_btn_next.pngbin2086 -> 0 bytes
-rw-r--r--core/res/res/drawable-hdpi/menu_dropdown_panel_holo_dark.9.pngbin809 -> 921 bytes
-rw-r--r--core/res/res/drawable-hdpi/menu_dropdown_panel_holo_light.9.pngbin815 -> 1056 bytes
-rw-r--r--core/res/res/drawable-hdpi/toast_frame.9.pngbin1923 -> 1319 bytes
-rw-r--r--core/res/res/drawable-hdpi/toast_frame_holo.9.pngbin1923 -> 1319 bytes
-rw-r--r--core/res/res/drawable-mdpi/dialog_bottom_holo_dark.9.pngbin586 -> 550 bytes
-rw-r--r--core/res/res/drawable-mdpi/dialog_bottom_holo_light.9.pngbin691 -> 584 bytes
-rw-r--r--core/res/res/drawable-mdpi/dialog_full_holo_dark.9.pngbin1103 -> 809 bytes
-rw-r--r--core/res/res/drawable-mdpi/dialog_full_holo_light.9.pngbin1294 -> 845 bytes
-rw-r--r--core/res/res/drawable-mdpi/dialog_middle_holo_dark.9.pngbin295 -> 240 bytes
-rw-r--r--core/res/res/drawable-mdpi/dialog_middle_holo_light.9.pngbin370 -> 243 bytes
-rw-r--r--core/res/res/drawable-mdpi/dialog_top_holo_dark.9.pngbin578 -> 507 bytes
-rw-r--r--core/res/res/drawable-mdpi/dialog_top_holo_light.9.pngbin599 -> 504 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_btn_back.pngbin906 -> 0 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/ic_btn_next.pngbin779 -> 0 bytes
-rw-r--r--core/res/res/drawable-mdpi/menu_dropdown_panel_holo_dark.9.pngbin576 -> 651 bytes
-rw-r--r--core/res/res/drawable-mdpi/menu_dropdown_panel_holo_light.9.pngbin555 -> 721 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/toast_frame.9.pngbin1100 -> 839 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/toast_frame_holo.9.pngbin1100 -> 839 bytes
-rw-r--r--core/res/res/drawable-xhdpi/dialog_bottom_holo_dark.9.pngbin1493 -> 1123 bytes
-rw-r--r--core/res/res/drawable-xhdpi/dialog_bottom_holo_light.9.pngbin1502 -> 1194 bytes
-rw-r--r--core/res/res/drawable-xhdpi/dialog_full_holo_dark.9.pngbin2800 -> 1895 bytes
-rw-r--r--core/res/res/drawable-xhdpi/dialog_full_holo_light.9.pngbin2821 -> 1835 bytes
-rw-r--r--core/res/res/drawable-xhdpi/dialog_middle_holo_dark.9.pngbin272 -> 306 bytes
-rw-r--r--core/res/res/drawable-xhdpi/dialog_middle_holo_light.9.pngbin281 -> 308 bytes
-rw-r--r--core/res/res/drawable-xhdpi/dialog_top_holo_dark.9.pngbin1506 -> 1079 bytes
-rw-r--r--core/res/res/drawable-xhdpi/dialog_top_holo_light.9.pngbin1496 -> 908 bytes
-rw-r--r--core/res/res/drawable-xhdpi/menu_dropdown_panel_holo_dark.9.pngbin0 -> 1361 bytes
-rw-r--r--core/res/res/drawable-xhdpi/menu_dropdown_panel_holo_light.9.pngbin0 -> 1553 bytes
-rw-r--r--core/res/res/drawable-xhdpi/toast_frame.9.pngbin2883 -> 1956 bytes
-rw-r--r--core/res/res/drawable-xhdpi/toast_frame_holo.9.pngbin2883 -> 1956 bytes
-rw-r--r--core/res/res/layout-sw600dp/preference_list_content.xml5
-rw-r--r--core/res/res/layout-w600dp/preference_list_content_single.xml5
-rw-r--r--core/res/res/layout-xlarge/activity_list.xml17
-rw-r--r--core/res/res/layout/activity_chooser_list_footer.xml6
-rw-r--r--core/res/res/layout/activity_chooser_list_header.xml4
-rw-r--r--core/res/res/layout/alert_dialog_holo.xml19
-rw-r--r--core/res/res/layout/dialog_custom_title_holo.xml8
-rw-r--r--core/res/res/layout/dialog_title_holo.xml8
-rw-r--r--core/res/res/layout/dialog_title_icons_holo.xml6
-rw-r--r--core/res/res/layout/preference_list_content.xml5
-rw-r--r--core/res/res/layout/preference_list_content_single.xml5
-rw-r--r--core/res/res/layout/preference_list_fragment.xml7
-rwxr-xr-xcore/res/res/values/attrs.xml17
-rw-r--r--core/res/res/values/colors.xml31
-rwxr-xr-xcore/res/res/values/config.xml5
-rw-r--r--core/res/res/values/ids.xml5
-rw-r--r--core/res/res/values/public.xml17
-rwxr-xr-xcore/res/res/values/strings.xml46
-rw-r--r--core/res/res/values/styles.xml4
-rw-r--r--core/res/res/values/themes.xml18
-rw-r--r--core/tests/coretests/AndroidManifest.xml7
-rw-r--r--core/tests/coretests/res/layout/textview_test.xml27
-rw-r--r--core/tests/coretests/res/values/strings.xml2
-rw-r--r--core/tests/coretests/src/android/net/NetworkStatsTest.java6
-rw-r--r--core/tests/coretests/src/android/pim/EventRecurrenceTest.java753
-rw-r--r--core/tests/coretests/src/android/pim/RecurrenceSetTest.java82
-rw-r--r--core/tests/coretests/src/android/widget/TextViewTest.java72
-rw-r--r--core/tests/coretests/src/android/widget/TextViewTestActivity.java30
125 files changed, 1644 insertions, 3471 deletions
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index a6658cc..d207a0a 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -1471,6 +1471,24 @@ public class ActivityManager {
}
/**
+ * Returns the usage statistics of each installed package.
+ *
+ * @hide
+ */
+ public PkgUsageStats[] getAllPackageUsageStats() {
+ try {
+ IUsageStats usageStatsService = IUsageStats.Stub.asInterface(
+ ServiceManager.getService("usagestats"));
+ if (usageStatsService != null) {
+ return usageStatsService.getAllPkgUsageStats();
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "Could not query usage stats", e);
+ }
+ return new PkgUsageStats[0];
+ }
+
+ /**
* @param userid the user's id. Zero indicates the default user
* @hide
*/
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index eee14fb..ee04729 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -4093,11 +4093,6 @@ public final class ActivityThread {
});
}
- private final void detach()
- {
- sThreadLocal.set(null);
- }
-
public static final ActivityThread systemMain() {
HardwareRenderer.disable();
ActivityThread thread = new ActivityThread();
@@ -4105,10 +4100,9 @@ public final class ActivityThread {
return thread;
}
- public final void installSystemProviders(List providers) {
+ public final void installSystemProviders(List<ProviderInfo> providers) {
if (providers != null) {
- installContentProviders(mInitialApplication,
- (List<ProviderInfo>)providers);
+ installContentProviders(mInitialApplication, providers);
}
}
@@ -4147,14 +4141,6 @@ public final class ActivityThread {
Looper.loop();
- if (Process.supportsProcesses()) {
- throw new RuntimeException("Main thread loop unexpectedly exited");
- }
-
- thread.detach();
- String name = (thread.mInitialApplication != null)
- ? thread.mInitialApplication.getPackageName()
- : "<unknown>";
- Slog.i(TAG, "Main thread of " + name + " is now exiting");
+ throw new RuntimeException("Main thread loop unexpectedly exited");
}
}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 8749d3e..d2323e7 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1163,9 +1163,6 @@ class ContextImpl extends Context {
throw new IllegalArgumentException("permission is null");
}
- if (!Process.supportsProcesses()) {
- return PackageManager.PERMISSION_GRANTED;
- }
try {
return ActivityManagerNative.getDefault().checkPermission(
permission, pid, uid);
@@ -1180,9 +1177,6 @@ class ContextImpl extends Context {
throw new IllegalArgumentException("permission is null");
}
- if (!Process.supportsProcesses()) {
- return PackageManager.PERMISSION_GRANTED;
- }
int pid = Binder.getCallingPid();
if (pid != Process.myPid()) {
return checkPermission(permission, pid,
@@ -1263,9 +1257,6 @@ class ContextImpl extends Context {
@Override
public int checkUriPermission(Uri uri, int pid, int uid, int modeFlags) {
- if (!Process.supportsProcesses()) {
- return PackageManager.PERMISSION_GRANTED;
- }
try {
return ActivityManagerNative.getDefault().checkUriPermission(
uri, pid, uid, modeFlags);
@@ -1276,9 +1267,6 @@ class ContextImpl extends Context {
@Override
public int checkCallingUriPermission(Uri uri, int modeFlags) {
- if (!Process.supportsProcesses()) {
- return PackageManager.PERMISSION_GRANTED;
- }
int pid = Binder.getCallingPid();
if (pid != Process.myPid()) {
return checkUriPermission(uri, pid,
diff --git a/core/java/android/app/backup/IRestoreSession.aidl b/core/java/android/app/backup/IRestoreSession.aidl
index 1dddbb0..14731ee 100644
--- a/core/java/android/app/backup/IRestoreSession.aidl
+++ b/core/java/android/app/backup/IRestoreSession.aidl
@@ -52,6 +52,25 @@ interface IRestoreSession {
int restoreAll(long token, IRestoreObserver observer);
/**
+ * Restore select packages from the given set onto the device, replacing the
+ * current data of any app contained in the set with the data previously
+ * backed up.
+ *
+ * <p>Callers must hold the android.permission.BACKUP permission to use this method.
+ *
+ * @return Zero on success, nonzero on error. The observer will only receive
+ * progress callbacks if this method returned zero.
+ * @param token The token from {@link getAvailableRestoreSets()} corresponding to
+ * the restore set that should be used.
+ * @param observer If non-null, this binder points to an object that will receive
+ * progress callbacks during the restore operation.
+ * @param packages The set of packages for which to attempt a restore. Regardless of
+ * the contents of the actual back-end dataset named by {@code token}, only
+ * applications mentioned in this list will have their data restored.
+ */
+ int restoreSome(long token, IRestoreObserver observer, in String[] packages);
+
+ /**
* Restore a single application from backup. The data will be restored from the
* current backup dataset if the given package has stored data there, or from
* the dataset used during the last full device setup operation if the current
diff --git a/core/java/android/app/backup/RestoreSession.java b/core/java/android/app/backup/RestoreSession.java
index 24ddb99..7181c61 100644
--- a/core/java/android/app/backup/RestoreSession.java
+++ b/core/java/android/app/backup/RestoreSession.java
@@ -87,6 +87,40 @@ public class RestoreSession {
}
/**
+ * Restore select packages from the given set onto the device, replacing the
+ * current data of any app contained in the set with the data previously
+ * backed up.
+ *
+ * <p>Callers must hold the android.permission.BACKUP permission to use this method.
+ *
+ * @return Zero on success, nonzero on error. The observer will only receive
+ * progress callbacks if this method returned zero.
+ * @param token The token from {@link getAvailableRestoreSets()} corresponding to
+ * the restore set that should be used.
+ * @param observer If non-null, this binder points to an object that will receive
+ * progress callbacks during the restore operation.
+ * @param packages The set of packages for which to attempt a restore. Regardless of
+ * the contents of the actual back-end dataset named by {@code token}, only
+ * applications mentioned in this list will have their data restored.
+ *
+ * @hide
+ */
+ public int restoreSome(long token, RestoreObserver observer, String[] packages) {
+ int err = -1;
+ if (mObserver != null) {
+ Log.d(TAG, "restoreAll() called during active restore");
+ return -1;
+ }
+ mObserver = new RestoreObserverWrapper(mContext, observer);
+ try {
+ err = mBinder.restoreSome(token, mObserver, packages);
+ } catch (RemoteException e) {
+ Log.d(TAG, "Can't contact server to restore packages");
+ }
+ return err;
+ }
+
+ /**
* Restore a single application from backup. The data will be restored from the
* current backup dataset if the given package has stored data there, or from
* the dataset used during the last full device setup operation if the current
diff --git a/core/java/android/bluetooth/BluetoothHealth.java b/core/java/android/bluetooth/BluetoothHealth.java
index 52efc07..0a01dcf 100644
--- a/core/java/android/bluetooth/BluetoothHealth.java
+++ b/core/java/android/bluetooth/BluetoothHealth.java
@@ -16,7 +16,6 @@
package android.bluetooth;
-import android.annotation.SdkConstant;
import android.content.Context;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
@@ -67,9 +66,6 @@ public final class BluetoothHealth implements BluetoothProfile {
*/
public static final int CHANNEL_TYPE_ANY = 12;
- private final ArrayList<BluetoothHealthAppConfiguration> mAppConfigs =
- new ArrayList<BluetoothHealthAppConfiguration>();
-
/**
* Register an application configuration that acts as a Health SINK.
* This is the configuration that will be used to communicate with health devices
@@ -86,7 +82,7 @@ public final class BluetoothHealth implements BluetoothProfile {
* @return If true, callback will be called.
*/
public boolean registerSinkAppConfiguration(String name, int dataType,
- IBluetoothHealthCallback callback) {
+ BluetoothHealthCallback callback) {
if (!isEnabled() || name == null) return false;
if (DBG) log("registerSinkApplication(" + name + ":" + dataType + ")");
@@ -111,18 +107,18 @@ public final class BluetoothHealth implements BluetoothProfile {
* @hide
*/
public boolean registerAppConfiguration(String name, int dataType, int role,
- int channelType, IBluetoothHealthCallback callback) {
+ int channelType, BluetoothHealthCallback callback) {
boolean result = false;
if (!isEnabled() || !checkAppParam(name, role, channelType, callback)) return result;
if (DBG) log("registerApplication(" + name + ":" + dataType + ")");
+ BluetoothHealthCallbackWrapper wrapper = new BluetoothHealthCallbackWrapper(callback);
BluetoothHealthAppConfiguration config =
- new BluetoothHealthAppConfiguration(name, dataType, role, channelType,
- callback);
+ new BluetoothHealthAppConfiguration(name, dataType, role, channelType);
if (mService != null) {
try {
- result = mService.registerAppConfiguration(config);
+ result = mService.registerAppConfiguration(config, wrapper);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -130,8 +126,6 @@ public final class BluetoothHealth implements BluetoothProfile {
Log.w(TAG, "Proxy not attached to service");
if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
}
-
- if (result) mAppConfigs.add(config);
return result;
}
@@ -147,7 +141,7 @@ public final class BluetoothHealth implements BluetoothProfile {
*/
public boolean unregisterAppConfiguration(BluetoothHealthAppConfiguration config) {
boolean result = false;
- if (mService != null && isEnabled() && isValidAppConfig(config)) {
+ if (mService != null && isEnabled() && config != null) {
try {
result = mService.unregisterAppConfiguration(config);
} catch (RemoteException e) {
@@ -157,26 +151,26 @@ public final class BluetoothHealth implements BluetoothProfile {
Log.w(TAG, "Proxy not attached to service");
if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
}
- if (result) mAppConfigs.remove(config);
+
return result;
}
/**
* Connect to a health device which has the {@link #SOURCE_ROLE}.
- * This is an asynchrnous call. If this function returns true, the callback
+ * This is an asynchronous call. If this function returns true, the callback
* associated with the application configuration will be called.
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
*
* @param device The remote Bluetooth device.
- * @param config The application configuration which has been registed using
- * {@link #registerSinkAppConfiguration(String, int, IBluetoothHealthCallback) }
+ * @param config The application configuration which has been registered using
+ * {@link #registerSinkAppConfiguration(String, int, BluetoothHealthCallback) }
* @return If true, the callback associated with the application config will be called.
*/
public boolean connectChannelToSource(BluetoothDevice device,
BluetoothHealthAppConfiguration config) {
if (mService != null && isEnabled() && isValidDevice(device) &&
- isValidAppConfig(config)) {
+ config != null) {
try {
return mService.connectChannelToSource(device, config);
} catch (RemoteException e) {
@@ -197,15 +191,15 @@ public final class BluetoothHealth implements BluetoothProfile {
*<p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
*
* @param device The remote Bluetooth device.
- * @param config The application configuration which has been registed using
- * {@link #registerSinkAppConfiguration(String, int, IBluetoothHealthCallback) }
+ * @param config The application configuration which has been registered using
+ * {@link #registerSinkAppConfiguration(String, int, BluetoothHealthCallback) }
* @return If true, the callback associated with the application config will be called.
* @hide
*/
public boolean connectChannelToSink(BluetoothDevice device,
BluetoothHealthAppConfiguration config, int channelType) {
if (mService != null && isEnabled() && isValidDevice(device) &&
- isValidAppConfig(config)) {
+ config != null) {
try {
return mService.connectChannelToSink(device, config, channelType);
} catch (RemoteException e) {
@@ -226,8 +220,8 @@ public final class BluetoothHealth implements BluetoothProfile {
*<p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
*
* @param device The remote Bluetooth device.
- * @param config The application configuration which has been registed using
- * {@link #registerSinkAppConfiguration(String, int, IBluetoothHealthCallback) }
+ * @param config The application configuration which has been registered using
+ * {@link #registerSinkAppConfiguration(String, int, BluetoothHealthCallback) }
* @param fd The file descriptor that was associated with the channel.
* @return If true, the callback associated with the application config will be called.
* @hide
@@ -235,7 +229,7 @@ public final class BluetoothHealth implements BluetoothProfile {
public boolean disconnectChannel(BluetoothDevice device,
BluetoothHealthAppConfiguration config, ParcelFileDescriptor fd) {
if (mService != null && isEnabled() && isValidDevice(device) &&
- isValidAppConfig(config)) {
+ config != null) {
try {
return mService.disconnectChannel(device, config, fd);
} catch (RemoteException e) {
@@ -262,7 +256,7 @@ public final class BluetoothHealth implements BluetoothProfile {
public ParcelFileDescriptor getMainChannelFd(BluetoothDevice device,
BluetoothHealthAppConfiguration config) {
if (mService != null && isEnabled() && isValidDevice(device) &&
- isValidAppConfig(config)) {
+ config != null) {
try {
return mService.getMainChannelFd(device, config);
} catch (RemoteException e) {
@@ -290,6 +284,7 @@ public final class BluetoothHealth implements BluetoothProfile {
* {@link #STATE_CONNECTED}, {@link #STATE_CONNECTING},
* {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING}
*/
+ @Override
public int getConnectionState(BluetoothDevice device) {
if (mService != null && isEnabled() && isValidDevice(device)) {
try {
@@ -317,6 +312,7 @@ public final class BluetoothHealth implements BluetoothProfile {
* local adapter.
* @return List of devices. The list will be empty on error.
*/
+ @Override
public List<BluetoothDevice> getConnectedDevices() {
if (mService != null && isEnabled()) {
try {
@@ -348,6 +344,7 @@ public final class BluetoothHealth implements BluetoothProfile {
* {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING},
* @return List of devices. The list will be empty on error.
*/
+ @Override
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (mService != null && isEnabled()) {
try {
@@ -361,6 +358,27 @@ public final class BluetoothHealth implements BluetoothProfile {
return new ArrayList<BluetoothDevice>();
}
+ private static class BluetoothHealthCallbackWrapper extends IBluetoothHealthCallback.Stub {
+ private BluetoothHealthCallback mCallback;
+
+ public BluetoothHealthCallbackWrapper(BluetoothHealthCallback callback) {
+ mCallback = callback;
+ }
+
+ @Override
+ public void onHealthAppConfigurationStatusChange(BluetoothHealthAppConfiguration config,
+ int status) {
+ mCallback.onHealthAppConfigurationStatusChange(config, status);
+ }
+
+ @Override
+ public void onHealthChannelStateChange(BluetoothHealthAppConfiguration config,
+ BluetoothDevice device, int prevState, int newState,
+ ParcelFileDescriptor fd) {
+ mCallback.onHealthChannelStateChange(config, device, prevState, newState, fd);
+ }
+ }
+
/** Health Channel Connection State - Disconnected */
public static final int STATE_CHANNEL_DISCONNECTED = 0;
/** Health Channel Connection State - Connecting */
@@ -379,7 +397,6 @@ public final class BluetoothHealth implements BluetoothProfile {
/** Health App Configuration un-registration failure */
public static final int APPLICATION_UNREGISTRATION_FAILURE = 3;
- private Context mContext;
private ServiceListener mServiceListener;
private IBluetooth mService;
BluetoothAdapter mAdapter;
@@ -420,14 +437,8 @@ public final class BluetoothHealth implements BluetoothProfile {
return false;
}
- private boolean isValidAppConfig(BluetoothHealthAppConfiguration config) {
- if (!mAppConfigs.isEmpty() && mAppConfigs.contains(config)) return true;
- log("Not a valid config: " + config);
- return false;
- }
-
private boolean checkAppParam(String name, int role, int channelType,
- IBluetoothHealthCallback callback) {
+ BluetoothHealthCallback callback) {
if (name == null || (role != SOURCE_ROLE && role != SINK_ROLE) ||
(channelType != CHANNEL_TYPE_RELIABLE &&
channelType != CHANNEL_TYPE_STREAMING &&
diff --git a/core/java/android/bluetooth/BluetoothHealthAppConfiguration.java b/core/java/android/bluetooth/BluetoothHealthAppConfiguration.java
index b87aea5..7020249 100644
--- a/core/java/android/bluetooth/BluetoothHealthAppConfiguration.java
+++ b/core/java/android/bluetooth/BluetoothHealthAppConfiguration.java
@@ -17,7 +17,6 @@
package android.bluetooth;
-import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
@@ -34,21 +33,18 @@ public final class BluetoothHealthAppConfiguration implements Parcelable {
private final int mDataType;
private final int mRole;
private final int mChannelType;
- private final IBluetoothHealthCallback mCallback;
/**
* Constructor to register the SINK role
*
* @param name Friendly name associated with the application configuration
* @param dataType Data Type of the remote Bluetooth Health device
- * @param callback Callback associated with the application configuration.
*/
- BluetoothHealthAppConfiguration(String name, int dataType, IBluetoothHealthCallback callback) {
+ BluetoothHealthAppConfiguration(String name, int dataType) {
mName = name;
mDataType = dataType;
mRole = BluetoothHealth.SINK_ROLE;
mChannelType = BluetoothHealth.CHANNEL_TYPE_ANY;
- mCallback = callback;
}
/**
@@ -56,17 +52,15 @@ public final class BluetoothHealthAppConfiguration implements Parcelable {
*
* @param name Friendly name associated with the application configuration
* @param dataType Data Type of the remote Bluetooth Health device
- * @param role {@link BluetoothHealth.SOURCE_ROLE} or
- * {@link BluetoothHealth.SINK_ROLE}
- * @param callback Callback associated with the application configuration.
+ * @param role {@link BluetoothHealth#SOURCE_ROLE} or
+ * {@link BluetoothHealth#SINK_ROLE}
*/
- BluetoothHealthAppConfiguration(String name, int dataType, int role, int channelType,
- IBluetoothHealthCallback callback) {
+ BluetoothHealthAppConfiguration(String name, int dataType, int role, int
+ channelType) {
mName = name;
mDataType = dataType;
mRole = role;
mChannelType = channelType;
- mCallback = callback;
}
@Override
@@ -77,8 +71,7 @@ public final class BluetoothHealthAppConfiguration implements Parcelable {
return mName.equals(config.getName()) &&
mDataType == config.getDataType() &&
mRole == config.getRole() &&
- mChannelType == config.getChannelType() &&
- mCallback.equals(config.getCallback());
+ mChannelType == config.getChannelType();
}
return false;
}
@@ -90,7 +83,6 @@ public final class BluetoothHealthAppConfiguration implements Parcelable {
result = 31 * result + mDataType;
result = 31 * result + mRole;
result = 31 * result + mChannelType;
- result = 31 * result + (mCallback != null ? mCallback.hashCode() : 0);
return result;
}
@@ -98,9 +90,10 @@ public final class BluetoothHealthAppConfiguration implements Parcelable {
public String toString() {
return "BluetoothHealthAppConfiguration [mName = " + mName +
",mDataType = " + mDataType + ", mRole = " + mRole + ",mChannelType = " +
- mChannelType + ",callback=" + mCallback +"]";
+ mChannelType + "]";
}
+ @Override
public int describeContents() {
return 0;
}
@@ -144,37 +137,31 @@ public final class BluetoothHealthAppConfiguration implements Parcelable {
return mChannelType;
}
- /**
- * Return the callback associated with this application configuration.
- *
- * @return IBluetoothHealthCallback
- */
- public IBluetoothHealthCallback getCallback() {
- return mCallback;
- }
-
public static final Parcelable.Creator<BluetoothHealthAppConfiguration> CREATOR =
new Parcelable.Creator<BluetoothHealthAppConfiguration>() {
+ @Override
public BluetoothHealthAppConfiguration createFromParcel(Parcel in) {
String name = in.readString();
int type = in.readInt();
int role = in.readInt();
int channelType = in.readInt();
- IBluetoothHealthCallback callback =
- IBluetoothHealthCallback.Stub.asInterface(in.readStrongBinder());
- return new BluetoothHealthAppConfiguration(name, type, role, channelType,
- callback);
+ return new BluetoothHealthAppConfiguration(name, type, role,
+ channelType);
}
+
+ @Override
public BluetoothHealthAppConfiguration[] newArray(int size) {
return new BluetoothHealthAppConfiguration[size];
}
};
+ @Override
public void writeToParcel(Parcel out, int flags) {
out.writeString(mName);
out.writeInt(mDataType);
out.writeInt(mRole);
out.writeInt(mChannelType);
- out.writeStrongInterface(mCallback);
}
+
+
}
diff --git a/core/java/android/bluetooth/BluetoothHealthCallback.java b/core/java/android/bluetooth/BluetoothHealthCallback.java
new file mode 100644
index 0000000..0d11bb5
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothHealthCallback.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.bluetooth;
+
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+/**
+ * This class is used for all the {@link BluetoothHealth} callbacks.
+ * @hide
+ */
+public abstract class BluetoothHealthCallback {
+
+ private static final String TAG = "BluetoothHealthCallback";
+
+ public void onHealthAppConfigurationStatusChange(BluetoothHealthAppConfiguration config,
+ int status) {
+ Log.d(TAG, "onHealthAppConfigurationStatusChange: " + config + " Status:" + status);
+ }
+
+ public void onHealthChannelStateChange(BluetoothHealthAppConfiguration config,
+ BluetoothDevice device, int prevState, int newState,
+ ParcelFileDescriptor fd) {
+ Log.d(TAG, "onHealthChannelStateChange: " + config + " Device:" + device +
+ "PrevState:" + prevState + "NewState:" + newState + "FileDescriptor:" + fd);
+ }
+}
diff --git a/core/java/android/bluetooth/BluetoothServerSocket.java b/core/java/android/bluetooth/BluetoothServerSocket.java
index 83e59e2..acce182 100644
--- a/core/java/android/bluetooth/BluetoothServerSocket.java
+++ b/core/java/android/bluetooth/BluetoothServerSocket.java
@@ -62,6 +62,7 @@ public final class BluetoothServerSocket implements Closeable {
/*package*/ final BluetoothSocket mSocket;
private Handler mHandler;
private int mMessage;
+ private final int mChannel;
/**
* Construct a socket for incoming connections.
@@ -74,6 +75,7 @@ public final class BluetoothServerSocket implements Closeable {
*/
/*package*/ BluetoothServerSocket(int type, boolean auth, boolean encrypt, int port)
throws IOException {
+ mChannel = port;
mSocket = new BluetoothSocket(type, -1, auth, encrypt, null, port, null);
}
@@ -125,4 +127,12 @@ public final class BluetoothServerSocket implements Closeable {
mHandler = handler;
mMessage = message;
}
+
+ /**
+ * Returns the channel on which this socket is bound.
+ * @hide
+ */
+ public int getChannel() {
+ return mChannel;
+ }
}
diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl
index 6ca6c2e..183772d 100644
--- a/core/java/android/bluetooth/IBluetooth.aidl
+++ b/core/java/android/bluetooth/IBluetooth.aidl
@@ -17,6 +17,7 @@
package android.bluetooth;
import android.bluetooth.IBluetoothCallback;
+import android.bluetooth.IBluetoothHealthCallback;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHealthAppConfiguration;
import android.os.ParcelUuid;
@@ -102,7 +103,8 @@ interface IBluetooth
boolean disconnectPanDevice(in BluetoothDevice device);
// HDP profile APIs
- boolean registerAppConfiguration(in BluetoothHealthAppConfiguration config);
+ boolean registerAppConfiguration(in BluetoothHealthAppConfiguration config,
+ in IBluetoothHealthCallback callback);
boolean unregisterAppConfiguration(in BluetoothHealthAppConfiguration config);
boolean connectChannelToSource(in BluetoothDevice device, in BluetoothHealthAppConfiguration config);
boolean connectChannelToSink(in BluetoothDevice device, in BluetoothHealthAppConfiguration config,
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index 9d40c42..a4c66e4 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -43,17 +43,41 @@ public class NetworkStats implements Parcelable {
/** {@link #tag} value for without tag. */
public static final int TAG_NONE = 0;
+ // TODO: move public fields to Entry accessors, then undeprecate
+ // TODO: refactor rx/tx to rxBytes/txBytes
+
/**
* {@link SystemClock#elapsedRealtime()} timestamp when this data was
* generated.
*/
+ @Deprecated
public final long elapsedRealtime;
+ @Deprecated
public int size;
+ @Deprecated
public String[] iface;
+ @Deprecated
public int[] uid;
+ @Deprecated
public int[] tag;
+ @Deprecated
public long[] rx;
+ @Deprecated
+ public long[] rxPackets;
+ @Deprecated
public long[] tx;
+ @Deprecated
+ public long[] txPackets;
+
+ public static class Entry {
+ public String iface;
+ public int uid;
+ public int tag;
+ public long rxBytes;
+ public long rxPackets;
+ public long txBytes;
+ public long txPackets;
+ }
public NetworkStats(long elapsedRealtime, int initialSize) {
this.elapsedRealtime = elapsedRealtime;
@@ -62,7 +86,9 @@ public class NetworkStats implements Parcelable {
this.uid = new int[initialSize];
this.tag = new int[initialSize];
this.rx = new long[initialSize];
+ this.rxPackets = new long[initialSize];
this.tx = new long[initialSize];
+ this.txPackets = new long[initialSize];
}
public NetworkStats(Parcel parcel) {
@@ -72,38 +98,82 @@ public class NetworkStats implements Parcelable {
uid = parcel.createIntArray();
tag = parcel.createIntArray();
rx = parcel.createLongArray();
+ rxPackets = parcel.createLongArray();
tx = parcel.createLongArray();
+ txPackets = parcel.createLongArray();
}
/**
* Add new stats entry with given values.
*/
public NetworkStats addEntry(String iface, int uid, int tag, long rx, long tx) {
+ final Entry entry = new Entry();
+ entry.iface = iface;
+ entry.uid = uid;
+ entry.tag = tag;
+ entry.rxBytes = rx;
+ entry.txBytes = tx;
+ return addValues(entry);
+ }
+
+ /**
+ * Add new stats entry, copying from given {@link Entry}. The {@link Entry}
+ * object can be recycled across multiple calls.
+ */
+ public NetworkStats addValues(Entry entry) {
if (size >= this.iface.length) {
- final int newLength = Math.max(this.iface.length, 10) * 3 / 2;
- this.iface = Arrays.copyOf(this.iface, newLength);
- this.uid = Arrays.copyOf(this.uid, newLength);
- this.tag = Arrays.copyOf(this.tag, newLength);
- this.rx = Arrays.copyOf(this.rx, newLength);
- this.tx = Arrays.copyOf(this.tx, newLength);
+ final int newLength = Math.max(iface.length, 10) * 3 / 2;
+ iface = Arrays.copyOf(iface, newLength);
+ uid = Arrays.copyOf(uid, newLength);
+ tag = Arrays.copyOf(tag, newLength);
+ rx = Arrays.copyOf(rx, newLength);
+ rxPackets = Arrays.copyOf(rxPackets, newLength);
+ tx = Arrays.copyOf(tx, newLength);
+ txPackets = Arrays.copyOf(txPackets, newLength);
}
- this.iface[size] = iface;
- this.uid[size] = uid;
- this.tag[size] = tag;
- this.rx[size] = rx;
- this.tx[size] = tx;
+ iface[size] = entry.iface;
+ uid[size] = entry.uid;
+ tag[size] = entry.tag;
+ rx[size] = entry.rxBytes;
+ rxPackets[size] = entry.rxPackets;
+ tx[size] = entry.txBytes;
+ txPackets[size] = entry.txPackets;
size++;
return this;
}
/**
+ * Return specific stats entry.
+ */
+ public Entry getValues(int i, Entry recycle) {
+ final Entry entry = recycle != null ? recycle : new Entry();
+ entry.iface = iface[i];
+ entry.uid = uid[i];
+ entry.tag = tag[i];
+ entry.rxBytes = rx[i];
+ entry.rxPackets = rxPackets[i];
+ entry.txBytes = tx[i];
+ entry.txPackets = txPackets[i];
+ return entry;
+ }
+
+ public long getElapsedRealtime() {
+ return elapsedRealtime;
+ }
+
+ public int size() {
+ return size;
+ }
+
+ /**
* Combine given values with an existing row, or create a new row if
* {@link #findIndex(String, int, int)} is unable to find match. Can also be
* used to subtract values from existing rows.
*/
public NetworkStats combineEntry(String iface, int uid, int tag, long rx, long tx) {
+ // TODO: extent to accept rxPackets/txPackets
final int i = findIndex(iface, uid, tag);
if (i == -1) {
// only create new entry when positive contribution
@@ -199,30 +269,41 @@ public class NetworkStats implements Parcelable {
}
// result will have our rows, and elapsed time between snapshots
+ final Entry entry = new Entry();
final NetworkStats result = new NetworkStats(deltaRealtime, size);
for (int i = 0; i < size; i++) {
- final String iface = this.iface[i];
- final int uid = this.uid[i];
- final int tag = this.tag[i];
+ entry.iface = iface[i];
+ entry.uid = uid[i];
+ entry.tag = tag[i];
// find remote row that matches, and subtract
- final int j = value.findIndex(iface, uid, tag);
+ final int j = value.findIndex(entry.iface, entry.uid, entry.tag);
if (j == -1) {
// newly appearing row, return entire value
- result.addEntry(iface, uid, tag, this.rx[i], this.tx[i]);
+ entry.rxBytes = rx[i];
+ entry.rxPackets = rxPackets[i];
+ entry.txBytes = tx[i];
+ entry.txPackets = txPackets[i];
} else {
// existing row, subtract remote value
- long rx = this.rx[i] - value.rx[j];
- long tx = this.tx[i] - value.tx[j];
- if (enforceMonotonic && (rx < 0 || tx < 0)) {
+ entry.rxBytes = rx[i] - value.rx[j];
+ entry.rxPackets = rxPackets[i] - value.rxPackets[j];
+ entry.txBytes = tx[i] - value.tx[j];
+ entry.txPackets = txPackets[i] - value.txPackets[j];
+ if (enforceMonotonic
+ && (entry.rxBytes < 0 || entry.rxPackets < 0 || entry.txBytes < 0
+ || entry.txPackets < 0)) {
throw new IllegalArgumentException("found non-monotonic values");
}
if (clampNegative) {
- rx = Math.max(0, rx);
- tx = Math.max(0, tx);
+ entry.rxBytes = Math.max(0, entry.rxBytes);
+ entry.rxPackets = Math.max(0, entry.rxPackets);
+ entry.txBytes = Math.max(0, entry.txBytes);
+ entry.txPackets = Math.max(0, entry.txPackets);
}
- result.addEntry(iface, uid, tag, rx, tx);
}
+
+ result.addValues(entry);
}
return result;
@@ -235,13 +316,15 @@ public class NetworkStats implements Parcelable {
public void dump(String prefix, PrintWriter pw) {
pw.print(prefix);
pw.print("NetworkStats: elapsedRealtime="); pw.println(elapsedRealtime);
- for (int i = 0; i < iface.length; i++) {
+ for (int i = 0; i < size; i++) {
pw.print(prefix);
pw.print(" iface="); pw.print(iface[i]);
pw.print(" uid="); pw.print(uid[i]);
pw.print(" tag="); pw.print(tag[i]);
- pw.print(" rx="); pw.print(rx[i]);
- pw.print(" tx="); pw.println(tx[i]);
+ pw.print(" rxBytes="); pw.print(rx[i]);
+ pw.print(" rxPackets="); pw.print(rxPackets[i]);
+ pw.print(" txBytes="); pw.print(tx[i]);
+ pw.print(" txPackets="); pw.println(txPackets[i]);
}
}
@@ -265,7 +348,9 @@ public class NetworkStats implements Parcelable {
dest.writeIntArray(uid);
dest.writeIntArray(tag);
dest.writeLongArray(rx);
+ dest.writeLongArray(rxPackets);
dest.writeLongArray(tx);
+ dest.writeLongArray(txPackets);
}
public static final Creator<NetworkStats> CREATOR = new Creator<NetworkStats>() {
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index 8a678d6..76534ef 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -38,8 +38,22 @@ public class NetworkUtils {
/** Bring the named network interface down. */
public native static int disableInterface(String interfaceName);
- /** Reset any sockets that are connected via the named interface. */
- public native static int resetConnections(String interfaceName);
+ /** Setting bit 0 indicates reseting of IPv4 addresses required */
+ public static final int RESET_IPV4_ADDRESSES = 0x01;
+
+ /** Setting bit 1 indicates reseting of IPv4 addresses required */
+ public static final int RESET_IPV6_ADDRESSES = 0x02;
+
+ /** Reset all addresses */
+ public static final int RESET_ALL_ADDRESSES = RESET_IPV4_ADDRESSES | RESET_IPV6_ADDRESSES;
+
+ /**
+ * Reset IPv6 or IPv4 sockets that are connected via the named interface.
+ *
+ * @param interfaceName is the interface to reset
+ * @param mask {@see #RESET_IPV4_ADDRESSES} and {@see #RESET_IPV6_ADDRESSES}
+ */
+ public native static int resetConnections(String interfaceName, int mask);
/**
* Start the DHCP client daemon, in order to have it request addresses
diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java
index 3edd692..c0be664 100644
--- a/core/java/android/os/Looper.java
+++ b/core/java/android/os/Looper.java
@@ -86,9 +86,7 @@ public class Looper {
public static final void prepareMainLooper() {
prepare();
setMainLooper(myLooper());
- if (Process.supportsProcesses()) {
- myLooper().mQueue.mQuitAllowed = false;
- }
+ myLooper().mQueue.mQuitAllowed = false;
}
private synchronized static void setMainLooper(Looper looper) {
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 673b187..5b1f563 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -266,84 +266,29 @@ public class Process {
* @param uid The user-id under which the process will run.
* @param gid The group-id under which the process will run.
* @param gids Additional group-ids associated with the process.
- * @param enableDebugger True if debugging should be enabled for this process.
+ * @param debugFlags Additional flags.
+ * @param targetSdkVersion The target SDK version for the app.
* @param zygoteArgs Additional arguments to supply to the zygote process.
*
- * @return int If > 0 the pid of the new process; if 0 the process is
- * being emulated by a thread
+ * @return An object that describes the result of the attempt to start the process.
* @throws RuntimeException on fatal start failure
*
* {@hide}
*/
- public static final int start(final String processClass,
+ public static final ProcessStartResult start(final String processClass,
final String niceName,
int uid, int gid, int[] gids,
- int debugFlags,
- String[] zygoteArgs)
- {
- if (supportsProcesses()) {
- try {
- return startViaZygote(processClass, niceName, uid, gid, gids,
- debugFlags, zygoteArgs);
- } catch (ZygoteStartFailedEx ex) {
- Log.e(LOG_TAG,
- "Starting VM process through Zygote failed");
- throw new RuntimeException(
- "Starting VM process through Zygote failed", ex);
- }
- } else {
- // Running in single-process mode
-
- Runnable runnable = new Runnable() {
- public void run() {
- Process.invokeStaticMain(processClass);
- }
- };
-
- // Thread constructors must not be called with null names (see spec).
- if (niceName != null) {
- new Thread(runnable, niceName).start();
- } else {
- new Thread(runnable).start();
- }
-
- return 0;
- }
- }
-
- /**
- * Start a new process. Don't supply a custom nice name.
- * {@hide}
- */
- public static final int start(String processClass, int uid, int gid,
- int[] gids, int debugFlags, String[] zygoteArgs) {
- return start(processClass, "", uid, gid, gids,
- debugFlags, zygoteArgs);
- }
-
- private static void invokeStaticMain(String className) {
- Class cl;
- Object args[] = new Object[1];
-
- args[0] = new String[0]; //this is argv
-
+ int debugFlags, int targetSdkVersion,
+ String[] zygoteArgs) {
try {
- cl = Class.forName(className);
- cl.getMethod("main", new Class[] { String[].class })
- .invoke(null, args);
- } catch (Exception ex) {
- // can be: ClassNotFoundException,
- // NoSuchMethodException, SecurityException,
- // IllegalAccessException, IllegalArgumentException
- // InvocationTargetException
- // or uncaught exception from main()
-
- Log.e(LOG_TAG, "Exception invoking static main on "
- + className, ex);
-
- throw new RuntimeException(ex);
+ return startViaZygote(processClass, niceName, uid, gid, gids,
+ debugFlags, targetSdkVersion, zygoteArgs);
+ } catch (ZygoteStartFailedEx ex) {
+ Log.e(LOG_TAG,
+ "Starting VM process through Zygote failed");
+ throw new RuntimeException(
+ "Starting VM process through Zygote failed", ex);
}
-
}
/** retry interval for opening a zygote socket */
@@ -430,14 +375,11 @@ public class Process {
* and returns the child's pid. Please note: the present implementation
* replaces newlines in the argument list with spaces.
* @param args argument list
- * @return PID of new child process
+ * @return An object that describes the result of the attempt to start the process.
* @throws ZygoteStartFailedEx if process start failed for any reason
*/
- private static int zygoteSendArgsAndGetPid(ArrayList<String> args)
+ private static ProcessStartResult zygoteSendArgsAndGetResult(ArrayList<String> args)
throws ZygoteStartFailedEx {
-
- int pid;
-
openZygoteSocketIfNeeded();
try {
@@ -448,7 +390,8 @@ public class Process {
* b) a number of newline-separated argument strings equal to count
*
* After the zygote process reads these it will write the pid of
- * the child or -1 on failure.
+ * the child or -1 on failure, followed by boolean to
+ * indicate whether a wrapper process was used.
*/
sZygoteWriter.write(Integer.toString(args.size()));
@@ -468,11 +411,13 @@ public class Process {
sZygoteWriter.flush();
// Should there be a timeout on this?
- pid = sZygoteInputStream.readInt();
-
- if (pid < 0) {
+ ProcessStartResult result = new ProcessStartResult();
+ result.pid = sZygoteInputStream.readInt();
+ if (result.pid < 0) {
throw new ZygoteStartFailedEx("fork() failed");
}
+ result.usingWrapper = sZygoteInputStream.readBoolean();
+ return result;
} catch (IOException ex) {
try {
if (sZygoteSocket != null) {
@@ -487,8 +432,6 @@ public class Process {
throw new ZygoteStartFailedEx(ex);
}
-
- return pid;
}
/**
@@ -500,20 +443,19 @@ public class Process {
* @param gid a POSIX gid that the new process shuold setgid() to
* @param gids null-ok; a list of supplementary group IDs that the
* new process should setgroup() to.
- * @param enableDebugger True if debugging should be enabled for this process.
+ * @param debugFlags Additional flags.
+ * @param targetSdkVersion The target SDK version for the app.
* @param extraArgs Additional arguments to supply to the zygote process.
- * @return PID
+ * @return An object that describes the result of the attempt to start the process.
* @throws ZygoteStartFailedEx if process start failed for any reason
*/
- private static int startViaZygote(final String processClass,
+ private static ProcessStartResult startViaZygote(final String processClass,
final String niceName,
final int uid, final int gid,
final int[] gids,
- int debugFlags,
+ int debugFlags, int targetSdkVersion,
String[] extraArgs)
throws ZygoteStartFailedEx {
- int pid;
-
synchronized(Process.class) {
ArrayList<String> argsForZygote = new ArrayList<String>();
@@ -537,6 +479,7 @@ public class Process {
if ((debugFlags & Zygote.DEBUG_ENABLE_ASSERT) != 0) {
argsForZygote.add("--enable-assert");
}
+ argsForZygote.add("--target-sdk-version=" + targetSdkVersion);
//TODO optionally enable debuger
//argsForZygote.add("--enable-debugger");
@@ -568,15 +511,9 @@ public class Process {
argsForZygote.add(arg);
}
}
-
- pid = zygoteSendArgsAndGetPid(argsForZygote);
- }
- if (pid <= 0) {
- throw new ZygoteStartFailedEx("zygote start failed:" + pid);
+ return zygoteSendArgsAndGetResult(argsForZygote);
}
-
- return pid;
}
/**
@@ -736,8 +673,13 @@ public class Process {
*
* @return Returns true if the system can run in multiple processes, else
* false if everything is running in a single process.
+ *
+ * @deprecated This method always returns true. Do not use.
*/
- public static final native boolean supportsProcesses();
+ @Deprecated
+ public static final boolean supportsProcesses() {
+ return true;
+ }
/**
* Set the out-of-memory badness adjustment for a process.
@@ -855,4 +797,21 @@ public class Process {
* @hide
*/
public static final native long getPss(int pid);
+
+ /**
+ * Specifies the outcome of having started a process.
+ * @hide
+ */
+ public static final class ProcessStartResult {
+ /**
+ * The PID of the newly started process.
+ * Always >= 0. (If the start failed, an exception will have been thrown instead.)
+ */
+ public int pid;
+
+ /**
+ * True if the process was started with a wrapper attached.
+ */
+ public boolean usingWrapper;
+ }
}
diff --git a/core/java/android/os/ServiceManager.java b/core/java/android/os/ServiceManager.java
index b721665..1af24f4 100644
--- a/core/java/android/os/ServiceManager.java
+++ b/core/java/android/os/ServiceManager.java
@@ -114,7 +114,7 @@ public final class ServiceManager {
* @hide
*/
public static void initServiceCache(Map<String, IBinder> cache) {
- if (sCache.size() != 0 && Process.supportsProcesses()) {
+ if (sCache.size() != 0) {
throw new IllegalStateException("setServiceCache may only be called once");
}
sCache.putAll(cache);
diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java
index 792e4c1..60900e1 100644
--- a/core/java/android/os/storage/StorageVolume.java
+++ b/core/java/android/os/storage/StorageVolume.java
@@ -34,6 +34,8 @@ public class StorageVolume implements Parcelable {
private final int mMtpReserveSpace;
private final boolean mAllowMassStorage;
private int mStorageId;
+ // maximum file size for the storage, or zero for no limit
+ private final long mMaxFileSize;
// StorageVolume extra for ACTION_MEDIA_REMOVED, ACTION_MEDIA_UNMOUNTED, ACTION_MEDIA_CHECKING,
// ACTION_MEDIA_NOFS, ACTION_MEDIA_MOUNTED, ACTION_MEDIA_SHARED, ACTION_MEDIA_UNSHARED,
@@ -41,18 +43,20 @@ public class StorageVolume implements Parcelable {
public static final String EXTRA_STORAGE_VOLUME = "storage_volume";
public StorageVolume(String path, String description, boolean removable,
- boolean emulated, int mtpReserveSpace, boolean allowMassStorage) {
+ boolean emulated, int mtpReserveSpace, boolean allowMassStorage, long maxFileSize) {
mPath = path;
mDescription = description;
mRemovable = removable;
mEmulated = emulated;
mMtpReserveSpace = mtpReserveSpace;
mAllowMassStorage = allowMassStorage;
+ mMaxFileSize = maxFileSize;
}
// for parcelling only
private StorageVolume(String path, String description, boolean removable,
- boolean emulated, int mtpReserveSpace, int storageId, boolean allowMassStorage) {
+ boolean emulated, int mtpReserveSpace, int storageId,
+ boolean allowMassStorage, long maxFileSize) {
mPath = path;
mDescription = description;
mRemovable = removable;
@@ -60,6 +64,7 @@ public class StorageVolume implements Parcelable {
mMtpReserveSpace = mtpReserveSpace;
mAllowMassStorage = allowMassStorage;
mStorageId = storageId;
+ mMaxFileSize = maxFileSize;
}
/**
@@ -142,6 +147,15 @@ public class StorageVolume implements Parcelable {
return mAllowMassStorage;
}
+ /**
+ * Returns maximum file size for the volume, or zero if it is unbounded.
+ *
+ * @return maximum file size
+ */
+ public long getMaxFileSize() {
+ return mMaxFileSize;
+ }
+
@Override
public boolean equals(Object obj) {
if (obj instanceof StorageVolume && mPath != null) {
@@ -171,9 +185,10 @@ public class StorageVolume implements Parcelable {
int storageId = in.readInt();
int mtpReserveSpace = in.readInt();
int allowMassStorage = in.readInt();
+ long maxFileSize = in.readLong();
return new StorageVolume(path, description,
- removable == 1, emulated == 1,
- mtpReserveSpace, storageId, allowMassStorage == 1);
+ removable == 1, emulated == 1, mtpReserveSpace,
+ storageId, allowMassStorage == 1, maxFileSize);
}
public StorageVolume[] newArray(int size) {
@@ -193,5 +208,6 @@ public class StorageVolume implements Parcelable {
parcel.writeInt(mStorageId);
parcel.writeInt(mMtpReserveSpace);
parcel.writeInt(mAllowMassStorage ? 1 : 0);
+ parcel.writeLong(mMaxFileSize);
}
}
diff --git a/core/java/android/pim/EventRecurrence.java b/core/java/android/pim/EventRecurrence.java
deleted file mode 100644
index 128b697..0000000
--- a/core/java/android/pim/EventRecurrence.java
+++ /dev/null
@@ -1,892 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-package android.pim;
-
-import android.text.TextUtils;
-import android.text.format.Time;
-import android.util.Log;
-import android.util.TimeFormatException;
-
-import java.util.Calendar;
-import java.util.HashMap;
-
-/**
- * Event recurrence utility functions.
- */
-public class EventRecurrence {
- private static String TAG = "EventRecur";
-
- public static final int SECONDLY = 1;
- public static final int MINUTELY = 2;
- public static final int HOURLY = 3;
- public static final int DAILY = 4;
- public static final int WEEKLY = 5;
- public static final int MONTHLY = 6;
- public static final int YEARLY = 7;
-
- public static final int SU = 0x00010000;
- public static final int MO = 0x00020000;
- public static final int TU = 0x00040000;
- public static final int WE = 0x00080000;
- public static final int TH = 0x00100000;
- public static final int FR = 0x00200000;
- public static final int SA = 0x00400000;
-
- public Time startDate; // set by setStartDate(), not parse()
-
- public int freq; // SECONDLY, MINUTELY, etc.
- public String until;
- public int count;
- public int interval;
- public int wkst; // SU, MO, TU, etc.
-
- /* lists with zero entries may be null references */
- public int[] bysecond;
- public int bysecondCount;
- public int[] byminute;
- public int byminuteCount;
- public int[] byhour;
- public int byhourCount;
- public int[] byday;
- public int[] bydayNum;
- public int bydayCount;
- public int[] bymonthday;
- public int bymonthdayCount;
- public int[] byyearday;
- public int byyeardayCount;
- public int[] byweekno;
- public int byweeknoCount;
- public int[] bymonth;
- public int bymonthCount;
- public int[] bysetpos;
- public int bysetposCount;
-
- /** maps a part string to a parser object */
- private static HashMap<String,PartParser> sParsePartMap;
- static {
- sParsePartMap = new HashMap<String,PartParser>();
- sParsePartMap.put("FREQ", new ParseFreq());
- sParsePartMap.put("UNTIL", new ParseUntil());
- sParsePartMap.put("COUNT", new ParseCount());
- sParsePartMap.put("INTERVAL", new ParseInterval());
- sParsePartMap.put("BYSECOND", new ParseBySecond());
- sParsePartMap.put("BYMINUTE", new ParseByMinute());
- sParsePartMap.put("BYHOUR", new ParseByHour());
- sParsePartMap.put("BYDAY", new ParseByDay());
- sParsePartMap.put("BYMONTHDAY", new ParseByMonthDay());
- sParsePartMap.put("BYYEARDAY", new ParseByYearDay());
- sParsePartMap.put("BYWEEKNO", new ParseByWeekNo());
- sParsePartMap.put("BYMONTH", new ParseByMonth());
- sParsePartMap.put("BYSETPOS", new ParseBySetPos());
- sParsePartMap.put("WKST", new ParseWkst());
- }
-
- /* values for bit vector that keeps track of what we have already seen */
- private static final int PARSED_FREQ = 1 << 0;
- private static final int PARSED_UNTIL = 1 << 1;
- private static final int PARSED_COUNT = 1 << 2;
- private static final int PARSED_INTERVAL = 1 << 3;
- private static final int PARSED_BYSECOND = 1 << 4;
- private static final int PARSED_BYMINUTE = 1 << 5;
- private static final int PARSED_BYHOUR = 1 << 6;
- private static final int PARSED_BYDAY = 1 << 7;
- private static final int PARSED_BYMONTHDAY = 1 << 8;
- private static final int PARSED_BYYEARDAY = 1 << 9;
- private static final int PARSED_BYWEEKNO = 1 << 10;
- private static final int PARSED_BYMONTH = 1 << 11;
- private static final int PARSED_BYSETPOS = 1 << 12;
- private static final int PARSED_WKST = 1 << 13;
-
- /** maps a FREQ value to an integer constant */
- private static final HashMap<String,Integer> sParseFreqMap = new HashMap<String,Integer>();
- static {
- sParseFreqMap.put("SECONDLY", SECONDLY);
- sParseFreqMap.put("MINUTELY", MINUTELY);
- sParseFreqMap.put("HOURLY", HOURLY);
- sParseFreqMap.put("DAILY", DAILY);
- sParseFreqMap.put("WEEKLY", WEEKLY);
- sParseFreqMap.put("MONTHLY", MONTHLY);
- sParseFreqMap.put("YEARLY", YEARLY);
- }
-
- /** maps a two-character weekday string to an integer constant */
- private static final HashMap<String,Integer> sParseWeekdayMap = new HashMap<String,Integer>();
- static {
- sParseWeekdayMap.put("SU", SU);
- sParseWeekdayMap.put("MO", MO);
- sParseWeekdayMap.put("TU", TU);
- sParseWeekdayMap.put("WE", WE);
- sParseWeekdayMap.put("TH", TH);
- sParseWeekdayMap.put("FR", FR);
- sParseWeekdayMap.put("SA", SA);
- }
-
- /** If set, allow lower-case recurrence rule strings. Minor performance impact. */
- private static final boolean ALLOW_LOWER_CASE = false;
-
- /** If set, validate the value of UNTIL parts. Minor performance impact. */
- private static final boolean VALIDATE_UNTIL = false;
-
- /** If set, require that only one of {UNTIL,COUNT} is present. Breaks compat w/ old parser. */
- private static final boolean ONLY_ONE_UNTIL_COUNT = false;
-
-
- /**
- * Thrown when a recurrence string provided can not be parsed according
- * to RFC2445.
- */
- public static class InvalidFormatException extends RuntimeException {
- InvalidFormatException(String s) {
- super(s);
- }
- }
-
-
- public void setStartDate(Time date) {
- startDate = date;
- }
-
- /**
- * Converts one of the Calendar.SUNDAY constants to the SU, MO, etc.
- * constants. btw, I think we should switch to those here too, to
- * get rid of this function, if possible.
- */
- public static int calendarDay2Day(int day)
- {
- switch (day)
- {
- case Calendar.SUNDAY:
- return SU;
- case Calendar.MONDAY:
- return MO;
- case Calendar.TUESDAY:
- return TU;
- case Calendar.WEDNESDAY:
- return WE;
- case Calendar.THURSDAY:
- return TH;
- case Calendar.FRIDAY:
- return FR;
- case Calendar.SATURDAY:
- return SA;
- default:
- throw new RuntimeException("bad day of week: " + day);
- }
- }
-
- public static int timeDay2Day(int day)
- {
- switch (day)
- {
- case Time.SUNDAY:
- return SU;
- case Time.MONDAY:
- return MO;
- case Time.TUESDAY:
- return TU;
- case Time.WEDNESDAY:
- return WE;
- case Time.THURSDAY:
- return TH;
- case Time.FRIDAY:
- return FR;
- case Time.SATURDAY:
- return SA;
- default:
- throw new RuntimeException("bad day of week: " + day);
- }
- }
- public static int day2TimeDay(int day)
- {
- switch (day)
- {
- case SU:
- return Time.SUNDAY;
- case MO:
- return Time.MONDAY;
- case TU:
- return Time.TUESDAY;
- case WE:
- return Time.WEDNESDAY;
- case TH:
- return Time.THURSDAY;
- case FR:
- return Time.FRIDAY;
- case SA:
- return Time.SATURDAY;
- default:
- throw new RuntimeException("bad day of week: " + day);
- }
- }
-
- /**
- * Converts one of the SU, MO, etc. constants to the Calendar.SUNDAY
- * constants. btw, I think we should switch to those here too, to
- * get rid of this function, if possible.
- */
- public static int day2CalendarDay(int day)
- {
- switch (day)
- {
- case SU:
- return Calendar.SUNDAY;
- case MO:
- return Calendar.MONDAY;
- case TU:
- return Calendar.TUESDAY;
- case WE:
- return Calendar.WEDNESDAY;
- case TH:
- return Calendar.THURSDAY;
- case FR:
- return Calendar.FRIDAY;
- case SA:
- return Calendar.SATURDAY;
- default:
- throw new RuntimeException("bad day of week: " + day);
- }
- }
-
- /**
- * Converts one of the internal day constants (SU, MO, etc.) to the
- * two-letter string representing that constant.
- *
- * @param day one the internal constants SU, MO, etc.
- * @return the two-letter string for the day ("SU", "MO", etc.)
- *
- * @throws IllegalArgumentException Thrown if the day argument is not one of
- * the defined day constants.
- */
- private static String day2String(int day) {
- switch (day) {
- case SU:
- return "SU";
- case MO:
- return "MO";
- case TU:
- return "TU";
- case WE:
- return "WE";
- case TH:
- return "TH";
- case FR:
- return "FR";
- case SA:
- return "SA";
- default:
- throw new IllegalArgumentException("bad day argument: " + day);
- }
- }
-
- private static void appendNumbers(StringBuilder s, String label,
- int count, int[] values)
- {
- if (count > 0) {
- s.append(label);
- count--;
- for (int i=0; i<count; i++) {
- s.append(values[i]);
- s.append(",");
- }
- s.append(values[count]);
- }
- }
-
- private void appendByDay(StringBuilder s, int i)
- {
- int n = this.bydayNum[i];
- if (n != 0) {
- s.append(n);
- }
-
- String str = day2String(this.byday[i]);
- s.append(str);
- }
-
- @Override
- public String toString()
- {
- StringBuilder s = new StringBuilder();
-
- s.append("FREQ=");
- switch (this.freq)
- {
- case SECONDLY:
- s.append("SECONDLY");
- break;
- case MINUTELY:
- s.append("MINUTELY");
- break;
- case HOURLY:
- s.append("HOURLY");
- break;
- case DAILY:
- s.append("DAILY");
- break;
- case WEEKLY:
- s.append("WEEKLY");
- break;
- case MONTHLY:
- s.append("MONTHLY");
- break;
- case YEARLY:
- s.append("YEARLY");
- break;
- }
-
- if (!TextUtils.isEmpty(this.until)) {
- s.append(";UNTIL=");
- s.append(until);
- }
-
- if (this.count != 0) {
- s.append(";COUNT=");
- s.append(this.count);
- }
-
- if (this.interval != 0) {
- s.append(";INTERVAL=");
- s.append(this.interval);
- }
-
- if (this.wkst != 0) {
- s.append(";WKST=");
- s.append(day2String(this.wkst));
- }
-
- appendNumbers(s, ";BYSECOND=", this.bysecondCount, this.bysecond);
- appendNumbers(s, ";BYMINUTE=", this.byminuteCount, this.byminute);
- appendNumbers(s, ";BYSECOND=", this.byhourCount, this.byhour);
-
- // day
- int count = this.bydayCount;
- if (count > 0) {
- s.append(";BYDAY=");
- count--;
- for (int i=0; i<count; i++) {
- appendByDay(s, i);
- s.append(",");
- }
- appendByDay(s, count);
- }
-
- appendNumbers(s, ";BYMONTHDAY=", this.bymonthdayCount, this.bymonthday);
- appendNumbers(s, ";BYYEARDAY=", this.byyeardayCount, this.byyearday);
- appendNumbers(s, ";BYWEEKNO=", this.byweeknoCount, this.byweekno);
- appendNumbers(s, ";BYMONTH=", this.bymonthCount, this.bymonth);
- appendNumbers(s, ";BYSETPOS=", this.bysetposCount, this.bysetpos);
-
- return s.toString();
- }
-
- public boolean repeatsOnEveryWeekDay() {
- if (this.freq != WEEKLY) {
- return false;
- }
-
- int count = this.bydayCount;
- if (count != 5) {
- return false;
- }
-
- for (int i = 0 ; i < count ; i++) {
- int day = byday[i];
- if (day == SU || day == SA) {
- return false;
- }
- }
-
- return true;
- }
-
- /**
- * Determines whether this rule specifies a simple monthly rule by weekday, such as
- * "FREQ=MONTHLY;BYDAY=3TU" (the 3rd Tuesday of every month).
- * <p>
- * Negative days, e.g. "FREQ=MONTHLY;BYDAY=-1TU" (the last Tuesday of every month),
- * will cause "false" to be returned.
- * <p>
- * Rules that fire every week, such as "FREQ=MONTHLY;BYDAY=TU" (every Tuesday of every
- * month) will cause "false" to be returned. (Note these are usually expressed as
- * WEEKLY rules, and hence are uncommon.)
- *
- * @return true if this rule is of the appropriate form
- */
- public boolean repeatsMonthlyOnDayCount() {
- if (this.freq != MONTHLY) {
- return false;
- }
-
- if (bydayCount != 1 || bymonthdayCount != 0) {
- return false;
- }
-
- if (bydayNum[0] <= 0) {
- return false;
- }
-
- return true;
- }
-
- /**
- * Determines whether two integer arrays contain identical elements.
- * <p>
- * The native implementation over-allocated the arrays (and may have stuff left over from
- * a previous run), so we can't just check the arrays -- the separately-maintained count
- * field also matters. We assume that a null array will have a count of zero, and that the
- * array can hold as many elements as the associated count indicates.
- * <p>
- * TODO: replace this with Arrays.equals() when the old parser goes away.
- */
- private static boolean arraysEqual(int[] array1, int count1, int[] array2, int count2) {
- if (count1 != count2) {
- return false;
- }
-
- for (int i = 0; i < count1; i++) {
- if (array1[i] != array2[i])
- return false;
- }
-
- return true;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- if (!(obj instanceof EventRecurrence)) {
- return false;
- }
-
- EventRecurrence er = (EventRecurrence) obj;
- return (startDate == null ?
- er.startDate == null : Time.compare(startDate, er.startDate) == 0) &&
- freq == er.freq &&
- (until == null ? er.until == null : until.equals(er.until)) &&
- count == er.count &&
- interval == er.interval &&
- wkst == er.wkst &&
- arraysEqual(bysecond, bysecondCount, er.bysecond, er.bysecondCount) &&
- arraysEqual(byminute, byminuteCount, er.byminute, er.byminuteCount) &&
- arraysEqual(byhour, byhourCount, er.byhour, er.byhourCount) &&
- arraysEqual(byday, bydayCount, er.byday, er.bydayCount) &&
- arraysEqual(bydayNum, bydayCount, er.bydayNum, er.bydayCount) &&
- arraysEqual(bymonthday, bymonthdayCount, er.bymonthday, er.bymonthdayCount) &&
- arraysEqual(byyearday, byyeardayCount, er.byyearday, er.byyeardayCount) &&
- arraysEqual(byweekno, byweeknoCount, er.byweekno, er.byweeknoCount) &&
- arraysEqual(bymonth, bymonthCount, er.bymonth, er.bymonthCount) &&
- arraysEqual(bysetpos, bysetposCount, er.bysetpos, er.bysetposCount);
- }
-
- @Override public int hashCode() {
- // We overrode equals, so we must override hashCode(). Nobody seems to need this though.
- throw new UnsupportedOperationException();
- }
-
- /**
- * Resets parser-modified fields to their initial state. Does not alter startDate.
- * <p>
- * The original parser always set all of the "count" fields, "wkst", and "until",
- * essentially allowing the same object to be used multiple times by calling parse().
- * It's unclear whether this behavior was intentional. For now, be paranoid and
- * preserve the existing behavior by resetting the fields.
- * <p>
- * We don't need to touch the integer arrays; they will either be ignored or
- * overwritten. The "startDate" field is not set by the parser, so we ignore it here.
- */
- private void resetFields() {
- until = null;
- freq = count = interval = bysecondCount = byminuteCount = byhourCount =
- bydayCount = bymonthdayCount = byyeardayCount = byweeknoCount = bymonthCount =
- bysetposCount = 0;
- }
-
- /**
- * Parses an rfc2445 recurrence rule string into its component pieces. Attempting to parse
- * malformed input will result in an EventRecurrence.InvalidFormatException.
- *
- * @param recur The recurrence rule to parse (in un-folded form).
- */
- public void parse(String recur) {
- /*
- * From RFC 2445 section 4.3.10:
- *
- * recur = "FREQ"=freq *(
- * ; either UNTIL or COUNT may appear in a 'recur',
- * ; but UNTIL and COUNT MUST NOT occur in the same 'recur'
- *
- * ( ";" "UNTIL" "=" enddate ) /
- * ( ";" "COUNT" "=" 1*DIGIT ) /
- *
- * ; the rest of these keywords are optional,
- * ; but MUST NOT occur more than once
- *
- * ( ";" "INTERVAL" "=" 1*DIGIT ) /
- * ( ";" "BYSECOND" "=" byseclist ) /
- * ( ";" "BYMINUTE" "=" byminlist ) /
- * ( ";" "BYHOUR" "=" byhrlist ) /
- * ( ";" "BYDAY" "=" bywdaylist ) /
- * ( ";" "BYMONTHDAY" "=" bymodaylist ) /
- * ( ";" "BYYEARDAY" "=" byyrdaylist ) /
- * ( ";" "BYWEEKNO" "=" bywknolist ) /
- * ( ";" "BYMONTH" "=" bymolist ) /
- * ( ";" "BYSETPOS" "=" bysplist ) /
- * ( ";" "WKST" "=" weekday ) /
- * ( ";" x-name "=" text )
- * )
- *
- * Examples:
- * FREQ=MONTHLY;INTERVAL=2;COUNT=10;BYDAY=1SU,-1SU
- * FREQ=YEARLY;INTERVAL=4;BYMONTH=11;BYDAY=TU;BYMONTHDAY=2,3,4,5,6,7,8
- *
- * Strategy:
- * (1) Split the string at ';' boundaries to get an array of rule "parts".
- * (2) For each part, find substrings for left/right sides of '=' (name/value).
- * (3) Call a <name>-specific parsing function to parse the <value> into an
- * output field.
- *
- * By keeping track of which names we've seen in a bit vector, we can verify the
- * constraints indicated above (FREQ appears first, none of them appear more than once --
- * though x-[name] would require special treatment), and we have either UNTIL or COUNT
- * but not both.
- *
- * In general, RFC 2445 property names (e.g. "FREQ") and enumerations ("TU") must
- * be handled in a case-insensitive fashion, but case may be significant for other
- * properties. We don't have any case-sensitive values in RRULE, except possibly
- * for the custom "X-" properties, but we ignore those anyway. Thus, we can trivially
- * convert the entire string to upper case and then use simple comparisons.
- *
- * Differences from previous version:
- * - allows lower-case property and enumeration values [optional]
- * - enforces that FREQ appears first
- * - enforces that only one of UNTIL and COUNT may be specified
- * - allows (but ignores) X-* parts
- * - improved validation on various values (e.g. UNTIL timestamps)
- * - error messages are more specific
- */
-
- /* TODO: replace with "if (freq != 0) throw" if nothing requires this */
- resetFields();
-
- int parseFlags = 0;
- String[] parts;
- if (ALLOW_LOWER_CASE) {
- parts = recur.toUpperCase().split(";");
- } else {
- parts = recur.split(";");
- }
- for (String part : parts) {
- int equalIndex = part.indexOf('=');
- if (equalIndex <= 0) {
- /* no '=' or no LHS */
- throw new InvalidFormatException("Missing LHS in " + part);
- }
-
- String lhs = part.substring(0, equalIndex);
- String rhs = part.substring(equalIndex + 1);
- if (rhs.length() == 0) {
- throw new InvalidFormatException("Missing RHS in " + part);
- }
-
- /*
- * In lieu of a "switch" statement that allows string arguments, we use a
- * map from strings to parsing functions.
- */
- PartParser parser = sParsePartMap.get(lhs);
- if (parser == null) {
- if (lhs.startsWith("X-")) {
- //Log.d(TAG, "Ignoring custom part " + lhs);
- continue;
- }
- throw new InvalidFormatException("Couldn't find parser for " + lhs);
- } else {
- int flag = parser.parsePart(rhs, this);
- if ((parseFlags & flag) != 0) {
- throw new InvalidFormatException("Part " + lhs + " was specified twice");
- }
- if (parseFlags == 0 && flag != PARSED_FREQ) {
- throw new InvalidFormatException("FREQ must be specified first");
- }
- parseFlags |= flag;
- }
- }
-
- // If not specified, week starts on Monday.
- if ((parseFlags & PARSED_WKST) == 0) {
- wkst = MO;
- }
-
- // FREQ is mandatory.
- if ((parseFlags & PARSED_FREQ) == 0) {
- throw new InvalidFormatException("Must specify a FREQ value");
- }
-
- // Can't have both UNTIL and COUNT.
- if ((parseFlags & (PARSED_UNTIL | PARSED_COUNT)) == (PARSED_UNTIL | PARSED_COUNT)) {
- if (ONLY_ONE_UNTIL_COUNT) {
- throw new InvalidFormatException("Must not specify both UNTIL and COUNT: " + recur);
- } else {
- Log.w(TAG, "Warning: rrule has both UNTIL and COUNT: " + recur);
- }
- }
- }
-
- /**
- * Base class for the RRULE part parsers.
- */
- abstract static class PartParser {
- /**
- * Parses a single part.
- *
- * @param value The right-hand-side of the part.
- * @param er The EventRecurrence into which the result is stored.
- * @return A bit value indicating which part was parsed.
- */
- public abstract int parsePart(String value, EventRecurrence er);
-
- /**
- * Parses an integer, with range-checking.
- *
- * @param str The string to parse.
- * @param minVal Minimum allowed value.
- * @param maxVal Maximum allowed value.
- * @param allowZero Is 0 allowed?
- * @return The parsed value.
- */
- public static int parseIntRange(String str, int minVal, int maxVal, boolean allowZero) {
- try {
- if (str.charAt(0) == '+') {
- // Integer.parseInt does not allow a leading '+', so skip it manually.
- str = str.substring(1);
- }
- int val = Integer.parseInt(str);
- if (val < minVal || val > maxVal || (val == 0 && !allowZero)) {
- throw new InvalidFormatException("Integer value out of range: " + str);
- }
- return val;
- } catch (NumberFormatException nfe) {
- throw new InvalidFormatException("Invalid integer value: " + str);
- }
- }
-
- /**
- * Parses a comma-separated list of integers, with range-checking.
- *
- * @param listStr The string to parse.
- * @param minVal Minimum allowed value.
- * @param maxVal Maximum allowed value.
- * @param allowZero Is 0 allowed?
- * @return A new array with values, sized to hold the exact number of elements.
- */
- public static int[] parseNumberList(String listStr, int minVal, int maxVal,
- boolean allowZero) {
- int[] values;
-
- if (listStr.indexOf(",") < 0) {
- // Common case: only one entry, skip split() overhead.
- values = new int[1];
- values[0] = parseIntRange(listStr, minVal, maxVal, allowZero);
- } else {
- String[] valueStrs = listStr.split(",");
- int len = valueStrs.length;
- values = new int[len];
- for (int i = 0; i < len; i++) {
- values[i] = parseIntRange(valueStrs[i], minVal, maxVal, allowZero);
- }
- }
- return values;
- }
- }
-
- /** parses FREQ={SECONDLY,MINUTELY,...} */
- private static class ParseFreq extends PartParser {
- @Override public int parsePart(String value, EventRecurrence er) {
- Integer freq = sParseFreqMap.get(value);
- if (freq == null) {
- throw new InvalidFormatException("Invalid FREQ value: " + value);
- }
- er.freq = freq;
- return PARSED_FREQ;
- }
- }
- /** parses UNTIL=enddate, e.g. "19970829T021400" */
- private static class ParseUntil extends PartParser {
- @Override public int parsePart(String value, EventRecurrence er) {
- if (VALIDATE_UNTIL) {
- try {
- // Parse the time to validate it. The result isn't retained.
- Time until = new Time();
- until.parse(value);
- } catch (TimeFormatException tfe) {
- throw new InvalidFormatException("Invalid UNTIL value: " + value);
- }
- }
- er.until = value;
- return PARSED_UNTIL;
- }
- }
- /** parses COUNT=[non-negative-integer] */
- private static class ParseCount extends PartParser {
- @Override public int parsePart(String value, EventRecurrence er) {
- er.count = parseIntRange(value, 0, Integer.MAX_VALUE, true);
- return PARSED_COUNT;
- }
- }
- /** parses INTERVAL=[non-negative-integer] */
- private static class ParseInterval extends PartParser {
- @Override public int parsePart(String value, EventRecurrence er) {
- er.interval = parseIntRange(value, 1, Integer.MAX_VALUE, false);
- return PARSED_INTERVAL;
- }
- }
- /** parses BYSECOND=byseclist */
- private static class ParseBySecond extends PartParser {
- @Override public int parsePart(String value, EventRecurrence er) {
- int[] bysecond = parseNumberList(value, 0, 59, true);
- er.bysecond = bysecond;
- er.bysecondCount = bysecond.length;
- return PARSED_BYSECOND;
- }
- }
- /** parses BYMINUTE=byminlist */
- private static class ParseByMinute extends PartParser {
- @Override public int parsePart(String value, EventRecurrence er) {
- int[] byminute = parseNumberList(value, 0, 59, true);
- er.byminute = byminute;
- er.byminuteCount = byminute.length;
- return PARSED_BYMINUTE;
- }
- }
- /** parses BYHOUR=byhrlist */
- private static class ParseByHour extends PartParser {
- @Override public int parsePart(String value, EventRecurrence er) {
- int[] byhour = parseNumberList(value, 0, 23, true);
- er.byhour = byhour;
- er.byhourCount = byhour.length;
- return PARSED_BYHOUR;
- }
- }
- /** parses BYDAY=bywdaylist, e.g. "1SU,-1SU" */
- private static class ParseByDay extends PartParser {
- @Override public int parsePart(String value, EventRecurrence er) {
- int[] byday;
- int[] bydayNum;
- int bydayCount;
-
- if (value.indexOf(",") < 0) {
- /* only one entry, skip split() overhead */
- bydayCount = 1;
- byday = new int[1];
- bydayNum = new int[1];
- parseWday(value, byday, bydayNum, 0);
- } else {
- String[] wdays = value.split(",");
- int len = wdays.length;
- bydayCount = len;
- byday = new int[len];
- bydayNum = new int[len];
- for (int i = 0; i < len; i++) {
- parseWday(wdays[i], byday, bydayNum, i);
- }
- }
- er.byday = byday;
- er.bydayNum = bydayNum;
- er.bydayCount = bydayCount;
- return PARSED_BYDAY;
- }
-
- /** parses [int]weekday, putting the pieces into parallel array entries */
- private static void parseWday(String str, int[] byday, int[] bydayNum, int index) {
- int wdayStrStart = str.length() - 2;
- String wdayStr;
-
- if (wdayStrStart > 0) {
- /* number is included; parse it out and advance to weekday */
- String numPart = str.substring(0, wdayStrStart);
- int num = parseIntRange(numPart, -53, 53, false);
- bydayNum[index] = num;
- wdayStr = str.substring(wdayStrStart);
- } else {
- /* just the weekday string */
- wdayStr = str;
- }
- Integer wday = sParseWeekdayMap.get(wdayStr);
- if (wday == null) {
- throw new InvalidFormatException("Invalid BYDAY value: " + str);
- }
- byday[index] = wday;
- }
- }
- /** parses BYMONTHDAY=bymodaylist */
- private static class ParseByMonthDay extends PartParser {
- @Override public int parsePart(String value, EventRecurrence er) {
- int[] bymonthday = parseNumberList(value, -31, 31, false);
- er.bymonthday = bymonthday;
- er.bymonthdayCount = bymonthday.length;
- return PARSED_BYMONTHDAY;
- }
- }
- /** parses BYYEARDAY=byyrdaylist */
- private static class ParseByYearDay extends PartParser {
- @Override public int parsePart(String value, EventRecurrence er) {
- int[] byyearday = parseNumberList(value, -366, 366, false);
- er.byyearday = byyearday;
- er.byyeardayCount = byyearday.length;
- return PARSED_BYYEARDAY;
- }
- }
- /** parses BYWEEKNO=bywknolist */
- private static class ParseByWeekNo extends PartParser {
- @Override public int parsePart(String value, EventRecurrence er) {
- int[] byweekno = parseNumberList(value, -53, 53, false);
- er.byweekno = byweekno;
- er.byweeknoCount = byweekno.length;
- return PARSED_BYWEEKNO;
- }
- }
- /** parses BYMONTH=bymolist */
- private static class ParseByMonth extends PartParser {
- @Override public int parsePart(String value, EventRecurrence er) {
- int[] bymonth = parseNumberList(value, 1, 12, false);
- er.bymonth = bymonth;
- er.bymonthCount = bymonth.length;
- return PARSED_BYMONTH;
- }
- }
- /** parses BYSETPOS=bysplist */
- private static class ParseBySetPos extends PartParser {
- @Override public int parsePart(String value, EventRecurrence er) {
- int[] bysetpos = parseNumberList(value, Integer.MIN_VALUE, Integer.MAX_VALUE, true);
- er.bysetpos = bysetpos;
- er.bysetposCount = bysetpos.length;
- return PARSED_BYSETPOS;
- }
- }
- /** parses WKST={SU,MO,...} */
- private static class ParseWkst extends PartParser {
- @Override public int parsePart(String value, EventRecurrence er) {
- Integer wkst = sParseWeekdayMap.get(value);
- if (wkst == null) {
- throw new InvalidFormatException("Invalid WKST value: " + value);
- }
- er.wkst = wkst;
- return PARSED_WKST;
- }
- }
-}
diff --git a/core/java/android/pim/ICalendar.java b/core/java/android/pim/ICalendar.java
deleted file mode 100644
index 58c5c63..0000000
--- a/core/java/android/pim/ICalendar.java
+++ /dev/null
@@ -1,660 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.pim;
-
-import android.util.Log;
-
-import java.util.LinkedHashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Set;
-import java.util.ArrayList;
-
-/**
- * Parses RFC 2445 iCalendar objects.
- */
-public class ICalendar {
-
- private static final String TAG = "Sync";
-
- // TODO: keep track of VEVENT, VTODO, VJOURNAL, VFREEBUSY, VTIMEZONE, VALARM
- // components, by type field or by subclass? subclass would allow us to
- // enforce grammars.
-
- /**
- * Exception thrown when an iCalendar object has invalid syntax.
- */
- public static class FormatException extends Exception {
- public FormatException() {
- super();
- }
-
- public FormatException(String msg) {
- super(msg);
- }
-
- public FormatException(String msg, Throwable cause) {
- super(msg, cause);
- }
- }
-
- /**
- * A component within an iCalendar (VEVENT, VTODO, VJOURNAL, VFEEBUSY,
- * VTIMEZONE, VALARM).
- */
- public static class Component {
-
- // components
- private static final String BEGIN = "BEGIN";
- private static final String END = "END";
- private static final String NEWLINE = "\n";
- public static final String VCALENDAR = "VCALENDAR";
- public static final String VEVENT = "VEVENT";
- public static final String VTODO = "VTODO";
- public static final String VJOURNAL = "VJOURNAL";
- public static final String VFREEBUSY = "VFREEBUSY";
- public static final String VTIMEZONE = "VTIMEZONE";
- public static final String VALARM = "VALARM";
-
- private final String mName;
- private final Component mParent; // see if we can get rid of this
- private LinkedList<Component> mChildren = null;
- private final LinkedHashMap<String, ArrayList<Property>> mPropsMap =
- new LinkedHashMap<String, ArrayList<Property>>();
-
- /**
- * Creates a new component with the provided name.
- * @param name The name of the component.
- */
- public Component(String name, Component parent) {
- mName = name;
- mParent = parent;
- }
-
- /**
- * Returns the name of the component.
- * @return The name of the component.
- */
- public String getName() {
- return mName;
- }
-
- /**
- * Returns the parent of this component.
- * @return The parent of this component.
- */
- public Component getParent() {
- return mParent;
- }
-
- /**
- * Helper that lazily gets/creates the list of children.
- * @return The list of children.
- */
- protected LinkedList<Component> getOrCreateChildren() {
- if (mChildren == null) {
- mChildren = new LinkedList<Component>();
- }
- return mChildren;
- }
-
- /**
- * Adds a child component to this component.
- * @param child The child component.
- */
- public void addChild(Component child) {
- getOrCreateChildren().add(child);
- }
-
- /**
- * Returns a list of the Component children of this component. May be
- * null, if there are no children.
- *
- * @return A list of the children.
- */
- public List<Component> getComponents() {
- return mChildren;
- }
-
- /**
- * Adds a Property to this component.
- * @param prop
- */
- public void addProperty(Property prop) {
- String name= prop.getName();
- ArrayList<Property> props = mPropsMap.get(name);
- if (props == null) {
- props = new ArrayList<Property>();
- mPropsMap.put(name, props);
- }
- props.add(prop);
- }
-
- /**
- * Returns a set of the property names within this component.
- * @return A set of property names within this component.
- */
- public Set<String> getPropertyNames() {
- return mPropsMap.keySet();
- }
-
- /**
- * Returns a list of properties with the specified name. Returns null
- * if there are no such properties.
- * @param name The name of the property that should be returned.
- * @return A list of properties with the requested name.
- */
- public List<Property> getProperties(String name) {
- return mPropsMap.get(name);
- }
-
- /**
- * Returns the first property with the specified name. Returns null
- * if there is no such property.
- * @param name The name of the property that should be returned.
- * @return The first property with the specified name.
- */
- public Property getFirstProperty(String name) {
- List<Property> props = mPropsMap.get(name);
- if (props == null || props.size() == 0) {
- return null;
- }
- return props.get(0);
- }
-
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder();
- toString(sb);
- sb.append(NEWLINE);
- return sb.toString();
- }
-
- /**
- * Helper method that appends this component to a StringBuilder. The
- * caller is responsible for appending a newline at the end of the
- * component.
- */
- public void toString(StringBuilder sb) {
- sb.append(BEGIN);
- sb.append(":");
- sb.append(mName);
- sb.append(NEWLINE);
-
- // append the properties
- for (String propertyName : getPropertyNames()) {
- for (Property property : getProperties(propertyName)) {
- property.toString(sb);
- sb.append(NEWLINE);
- }
- }
-
- // append the sub-components
- if (mChildren != null) {
- for (Component component : mChildren) {
- component.toString(sb);
- sb.append(NEWLINE);
- }
- }
-
- sb.append(END);
- sb.append(":");
- sb.append(mName);
- }
- }
-
- /**
- * A property within an iCalendar component (e.g., DTSTART, DTEND, etc.,
- * within a VEVENT).
- */
- public static class Property {
- // properties
- // TODO: do we want to list these here? the complete list is long.
- public static final String DTSTART = "DTSTART";
- public static final String DTEND = "DTEND";
- public static final String DURATION = "DURATION";
- public static final String RRULE = "RRULE";
- public static final String RDATE = "RDATE";
- public static final String EXRULE = "EXRULE";
- public static final String EXDATE = "EXDATE";
- // ... need to add more.
-
- private final String mName;
- private LinkedHashMap<String, ArrayList<Parameter>> mParamsMap =
- new LinkedHashMap<String, ArrayList<Parameter>>();
- private String mValue; // TODO: make this final?
-
- /**
- * Creates a new property with the provided name.
- * @param name The name of the property.
- */
- public Property(String name) {
- mName = name;
- }
-
- /**
- * Creates a new property with the provided name and value.
- * @param name The name of the property.
- * @param value The value of the property.
- */
- public Property(String name, String value) {
- mName = name;
- mValue = value;
- }
-
- /**
- * Returns the name of the property.
- * @return The name of the property.
- */
- public String getName() {
- return mName;
- }
-
- /**
- * Returns the value of this property.
- * @return The value of this property.
- */
- public String getValue() {
- return mValue;
- }
-
- /**
- * Sets the value of this property.
- * @param value The desired value for this property.
- */
- public void setValue(String value) {
- mValue = value;
- }
-
- /**
- * Adds a {@link Parameter} to this property.
- * @param param The parameter that should be added.
- */
- public void addParameter(Parameter param) {
- ArrayList<Parameter> params = mParamsMap.get(param.name);
- if (params == null) {
- params = new ArrayList<Parameter>();
- mParamsMap.put(param.name, params);
- }
- params.add(param);
- }
-
- /**
- * Returns the set of parameter names for this property.
- * @return The set of parameter names for this property.
- */
- public Set<String> getParameterNames() {
- return mParamsMap.keySet();
- }
-
- /**
- * Returns the list of parameters with the specified name. May return
- * null if there are no such parameters.
- * @param name The name of the parameters that should be returned.
- * @return The list of parameters with the specified name.
- */
- public List<Parameter> getParameters(String name) {
- return mParamsMap.get(name);
- }
-
- /**
- * Returns the first parameter with the specified name. May return
- * nll if there is no such parameter.
- * @param name The name of the parameter that should be returned.
- * @return The first parameter with the specified name.
- */
- public Parameter getFirstParameter(String name) {
- ArrayList<Parameter> params = mParamsMap.get(name);
- if (params == null || params.size() == 0) {
- return null;
- }
- return params.get(0);
- }
-
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder();
- toString(sb);
- return sb.toString();
- }
-
- /**
- * Helper method that appends this property to a StringBuilder. The
- * caller is responsible for appending a newline after this property.
- */
- public void toString(StringBuilder sb) {
- sb.append(mName);
- Set<String> parameterNames = getParameterNames();
- for (String parameterName : parameterNames) {
- for (Parameter param : getParameters(parameterName)) {
- sb.append(";");
- param.toString(sb);
- }
- }
- sb.append(":");
- sb.append(mValue);
- }
- }
-
- /**
- * A parameter defined for an iCalendar property.
- */
- // TODO: make this a proper class rather than a struct?
- public static class Parameter {
- public String name;
- public String value;
-
- /**
- * Creates a new empty parameter.
- */
- public Parameter() {
- }
-
- /**
- * Creates a new parameter with the specified name and value.
- * @param name The name of the parameter.
- * @param value The value of the parameter.
- */
- public Parameter(String name, String value) {
- this.name = name;
- this.value = value;
- }
-
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder();
- toString(sb);
- return sb.toString();
- }
-
- /**
- * Helper method that appends this parameter to a StringBuilder.
- */
- public void toString(StringBuilder sb) {
- sb.append(name);
- sb.append("=");
- sb.append(value);
- }
- }
-
- private static final class ParserState {
- // public int lineNumber = 0;
- public String line; // TODO: just point to original text
- public int index;
- }
-
- // use factory method
- private ICalendar() {
- }
-
- // TODO: get rid of this -- handle all of the parsing in one pass through
- // the text.
- private static String normalizeText(String text) {
- // it's supposed to be \r\n, but not everyone does that
- text = text.replaceAll("\r\n", "\n");
- text = text.replaceAll("\r", "\n");
-
- // we deal with line folding, by replacing all "\n " strings
- // with nothing. The RFC specifies "\r\n " to be folded, but
- // we handle "\n " and "\r " too because we can get those.
- text = text.replaceAll("\n ", "");
-
- return text;
- }
-
- /**
- * Parses text into an iCalendar component. Parses into the provided
- * component, if not null, or parses into a new component. In the latter
- * case, expects a BEGIN as the first line. Returns the provided or newly
- * created top-level component.
- */
- // TODO: use an index into the text, so we can make this a recursive
- // function?
- private static Component parseComponentImpl(Component component,
- String text)
- throws FormatException {
- Component current = component;
- ParserState state = new ParserState();
- state.index = 0;
-
- // split into lines
- String[] lines = text.split("\n");
-
- // each line is of the format:
- // name *(";" param) ":" value
- for (String line : lines) {
- try {
- current = parseLine(line, state, current);
- // if the provided component was null, we will return the root
- // NOTE: in this case, if the first line is not a BEGIN, a
- // FormatException will get thrown.
- if (component == null) {
- component = current;
- }
- } catch (FormatException fe) {
- if (false) {
- Log.v(TAG, "Cannot parse " + line, fe);
- }
- // for now, we ignore the parse error. Google Calendar seems
- // to be emitting some misformatted iCalendar objects.
- }
- continue;
- }
- return component;
- }
-
- /**
- * Parses a line into the provided component. Creates a new component if
- * the line is a BEGIN, adding the newly created component to the provided
- * parent. Returns whatever component is the current one (to which new
- * properties will be added) in the parse.
- */
- private static Component parseLine(String line, ParserState state,
- Component component)
- throws FormatException {
- state.line = line;
- int len = state.line.length();
-
- // grab the name
- char c = 0;
- for (state.index = 0; state.index < len; ++state.index) {
- c = line.charAt(state.index);
- if (c == ';' || c == ':') {
- break;
- }
- }
- String name = line.substring(0, state.index);
-
- if (component == null) {
- if (!Component.BEGIN.equals(name)) {
- throw new FormatException("Expected BEGIN");
- }
- }
-
- Property property;
- if (Component.BEGIN.equals(name)) {
- // start a new component
- String componentName = extractValue(state);
- Component child = new Component(componentName, component);
- if (component != null) {
- component.addChild(child);
- }
- return child;
- } else if (Component.END.equals(name)) {
- // finish the current component
- String componentName = extractValue(state);
- if (component == null ||
- !componentName.equals(component.getName())) {
- throw new FormatException("Unexpected END " + componentName);
- }
- return component.getParent();
- } else {
- property = new Property(name);
- }
-
- if (c == ';') {
- Parameter parameter = null;
- while ((parameter = extractParameter(state)) != null) {
- property.addParameter(parameter);
- }
- }
- String value = extractValue(state);
- property.setValue(value);
- component.addProperty(property);
- return component;
- }
-
- /**
- * Extracts the value ":..." on the current line. The first character must
- * be a ':'.
- */
- private static String extractValue(ParserState state)
- throws FormatException {
- String line = state.line;
- if (state.index >= line.length() || line.charAt(state.index) != ':') {
- throw new FormatException("Expected ':' before end of line in "
- + line);
- }
- String value = line.substring(state.index + 1);
- state.index = line.length() - 1;
- return value;
- }
-
- /**
- * Extracts the next parameter from the line, if any. If there are no more
- * parameters, returns null.
- */
- private static Parameter extractParameter(ParserState state)
- throws FormatException {
- String text = state.line;
- int len = text.length();
- Parameter parameter = null;
- int startIndex = -1;
- int equalIndex = -1;
- while (state.index < len) {
- char c = text.charAt(state.index);
- if (c == ':') {
- if (parameter != null) {
- if (equalIndex == -1) {
- throw new FormatException("Expected '=' within "
- + "parameter in " + text);
- }
- parameter.value = text.substring(equalIndex + 1,
- state.index);
- }
- return parameter; // may be null
- } else if (c == ';') {
- if (parameter != null) {
- if (equalIndex == -1) {
- throw new FormatException("Expected '=' within "
- + "parameter in " + text);
- }
- parameter.value = text.substring(equalIndex + 1,
- state.index);
- return parameter;
- } else {
- parameter = new Parameter();
- startIndex = state.index;
- }
- } else if (c == '=') {
- equalIndex = state.index;
- if ((parameter == null) || (startIndex == -1)) {
- throw new FormatException("Expected ';' before '=' in "
- + text);
- }
- parameter.name = text.substring(startIndex + 1, equalIndex);
- } else if (c == '"') {
- if (parameter == null) {
- throw new FormatException("Expected parameter before '\"' in " + text);
- }
- if (equalIndex == -1) {
- throw new FormatException("Expected '=' within parameter in " + text);
- }
- if (state.index > equalIndex + 1) {
- throw new FormatException("Parameter value cannot contain a '\"' in " + text);
- }
- final int endQuote = text.indexOf('"', state.index + 1);
- if (endQuote < 0) {
- throw new FormatException("Expected closing '\"' in " + text);
- }
- parameter.value = text.substring(state.index + 1, endQuote);
- state.index = endQuote + 1;
- return parameter;
- }
- ++state.index;
- }
- throw new FormatException("Expected ':' before end of line in " + text);
- }
-
- /**
- * Parses the provided text into an iCalendar object. The top-level
- * component must be of type VCALENDAR.
- * @param text The text to be parsed.
- * @return The top-level VCALENDAR component.
- * @throws FormatException Thrown if the text could not be parsed into an
- * iCalendar VCALENDAR object.
- */
- public static Component parseCalendar(String text) throws FormatException {
- Component calendar = parseComponent(null, text);
- if (calendar == null || !Component.VCALENDAR.equals(calendar.getName())) {
- throw new FormatException("Expected " + Component.VCALENDAR);
- }
- return calendar;
- }
-
- /**
- * Parses the provided text into an iCalendar event. The top-level
- * component must be of type VEVENT.
- * @param text The text to be parsed.
- * @return The top-level VEVENT component.
- * @throws FormatException Thrown if the text could not be parsed into an
- * iCalendar VEVENT.
- */
- public static Component parseEvent(String text) throws FormatException {
- Component event = parseComponent(null, text);
- if (event == null || !Component.VEVENT.equals(event.getName())) {
- throw new FormatException("Expected " + Component.VEVENT);
- }
- return event;
- }
-
- /**
- * Parses the provided text into an iCalendar component.
- * @param text The text to be parsed.
- * @return The top-level component.
- * @throws FormatException Thrown if the text could not be parsed into an
- * iCalendar component.
- */
- public static Component parseComponent(String text) throws FormatException {
- return parseComponent(null, text);
- }
-
- /**
- * Parses the provided text, adding to the provided component.
- * @param component The component to which the parsed iCalendar data should
- * be added.
- * @param text The text to be parsed.
- * @return The top-level component.
- * @throws FormatException Thrown if the text could not be parsed as an
- * iCalendar object.
- */
- public static Component parseComponent(Component component, String text)
- throws FormatException {
- text = normalizeText(text);
- return parseComponentImpl(component, text);
- }
-}
diff --git a/core/java/android/pim/RecurrenceSet.java b/core/java/android/pim/RecurrenceSet.java
deleted file mode 100644
index b7fb320..0000000
--- a/core/java/android/pim/RecurrenceSet.java
+++ /dev/null
@@ -1,511 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.pim;
-
-import android.content.ContentValues;
-import android.database.Cursor;
-import android.provider.CalendarContract;
-import android.text.TextUtils;
-import android.text.format.Time;
-import android.util.Log;
-
-import java.util.List;
-import java.util.regex.Pattern;
-
-/**
- * Basic information about a recurrence, following RFC 2445 Section 4.8.5.
- * Contains the RRULEs, RDATE, EXRULEs, and EXDATE properties.
- */
-public class RecurrenceSet {
-
- private final static String TAG = "CalendarProvider";
-
- private final static String RULE_SEPARATOR = "\n";
- private final static String FOLDING_SEPARATOR = "\n ";
-
- // TODO: make these final?
- public EventRecurrence[] rrules = null;
- public long[] rdates = null;
- public EventRecurrence[] exrules = null;
- public long[] exdates = null;
-
- /**
- * Creates a new RecurrenceSet from information stored in the
- * events table in the CalendarProvider.
- * @param values The values retrieved from the Events table.
- */
- public RecurrenceSet(ContentValues values)
- throws EventRecurrence.InvalidFormatException {
- String rruleStr = values.getAsString(CalendarContract.Events.RRULE);
- String rdateStr = values.getAsString(CalendarContract.Events.RDATE);
- String exruleStr = values.getAsString(CalendarContract.Events.EXRULE);
- String exdateStr = values.getAsString(CalendarContract.Events.EXDATE);
- init(rruleStr, rdateStr, exruleStr, exdateStr);
- }
-
- /**
- * Creates a new RecurrenceSet from information stored in a database
- * {@link Cursor} pointing to the events table in the
- * CalendarProvider. The cursor must contain the RRULE, RDATE, EXRULE,
- * and EXDATE columns.
- *
- * @param cursor The cursor containing the RRULE, RDATE, EXRULE, and EXDATE
- * columns.
- */
- public RecurrenceSet(Cursor cursor)
- throws EventRecurrence.InvalidFormatException {
- int rruleColumn = cursor.getColumnIndex(CalendarContract.Events.RRULE);
- int rdateColumn = cursor.getColumnIndex(CalendarContract.Events.RDATE);
- int exruleColumn = cursor.getColumnIndex(CalendarContract.Events.EXRULE);
- int exdateColumn = cursor.getColumnIndex(CalendarContract.Events.EXDATE);
- String rruleStr = cursor.getString(rruleColumn);
- String rdateStr = cursor.getString(rdateColumn);
- String exruleStr = cursor.getString(exruleColumn);
- String exdateStr = cursor.getString(exdateColumn);
- init(rruleStr, rdateStr, exruleStr, exdateStr);
- }
-
- public RecurrenceSet(String rruleStr, String rdateStr,
- String exruleStr, String exdateStr)
- throws EventRecurrence.InvalidFormatException {
- init(rruleStr, rdateStr, exruleStr, exdateStr);
- }
-
- private void init(String rruleStr, String rdateStr,
- String exruleStr, String exdateStr)
- throws EventRecurrence.InvalidFormatException {
- if (!TextUtils.isEmpty(rruleStr) || !TextUtils.isEmpty(rdateStr)) {
-
- if (!TextUtils.isEmpty(rruleStr)) {
- String[] rruleStrs = rruleStr.split(RULE_SEPARATOR);
- rrules = new EventRecurrence[rruleStrs.length];
- for (int i = 0; i < rruleStrs.length; ++i) {
- EventRecurrence rrule = new EventRecurrence();
- rrule.parse(rruleStrs[i]);
- rrules[i] = rrule;
- }
- }
-
- if (!TextUtils.isEmpty(rdateStr)) {
- rdates = parseRecurrenceDates(rdateStr);
- }
-
- if (!TextUtils.isEmpty(exruleStr)) {
- String[] exruleStrs = exruleStr.split(RULE_SEPARATOR);
- exrules = new EventRecurrence[exruleStrs.length];
- for (int i = 0; i < exruleStrs.length; ++i) {
- EventRecurrence exrule = new EventRecurrence();
- exrule.parse(exruleStr);
- exrules[i] = exrule;
- }
- }
-
- if (!TextUtils.isEmpty(exdateStr)) {
- exdates = parseRecurrenceDates(exdateStr);
- }
- }
- }
-
- /**
- * Returns whether or not a recurrence is defined in this RecurrenceSet.
- * @return Whether or not a recurrence is defined in this RecurrenceSet.
- */
- public boolean hasRecurrence() {
- return (rrules != null || rdates != null);
- }
-
- /**
- * Parses the provided RDATE or EXDATE string into an array of longs
- * representing each date/time in the recurrence.
- * @param recurrence The recurrence to be parsed.
- * @return The list of date/times.
- */
- public static long[] parseRecurrenceDates(String recurrence) {
- // TODO: use "local" time as the default. will need to handle times
- // that end in "z" (UTC time) explicitly at that point.
- String tz = Time.TIMEZONE_UTC;
- int tzidx = recurrence.indexOf(";");
- if (tzidx != -1) {
- tz = recurrence.substring(0, tzidx);
- recurrence = recurrence.substring(tzidx + 1);
- }
- Time time = new Time(tz);
- String[] rawDates = recurrence.split(",");
- int n = rawDates.length;
- long[] dates = new long[n];
- for (int i = 0; i<n; ++i) {
- // The timezone is updated to UTC if the time string specified 'Z'.
- time.parse(rawDates[i]);
- dates[i] = time.toMillis(false /* use isDst */);
- time.timezone = tz;
- }
- return dates;
- }
-
- /**
- * Populates the database map of values with the appropriate RRULE, RDATE,
- * EXRULE, and EXDATE values extracted from the parsed iCalendar component.
- * @param component The iCalendar component containing the desired
- * recurrence specification.
- * @param values The db values that should be updated.
- * @return true if the component contained the necessary information
- * to specify a recurrence. The required fields are DTSTART,
- * one of DTEND/DURATION, and one of RRULE/RDATE. Returns false if
- * there was an error, including if the date is out of range.
- */
- public static boolean populateContentValues(ICalendar.Component component,
- ContentValues values) {
- ICalendar.Property dtstartProperty =
- component.getFirstProperty("DTSTART");
- String dtstart = dtstartProperty.getValue();
- ICalendar.Parameter tzidParam =
- dtstartProperty.getFirstParameter("TZID");
- // NOTE: the timezone may be null, if this is a floating time.
- String tzid = tzidParam == null ? null : tzidParam.value;
- Time start = new Time(tzidParam == null ? Time.TIMEZONE_UTC : tzid);
- boolean inUtc = start.parse(dtstart);
- boolean allDay = start.allDay;
-
- // We force TimeZone to UTC for "all day recurring events" as the server is sending no
- // TimeZone in DTSTART for them
- if (inUtc || allDay) {
- tzid = Time.TIMEZONE_UTC;
- }
-
- String duration = computeDuration(start, component);
- String rrule = flattenProperties(component, "RRULE");
- String rdate = extractDates(component.getFirstProperty("RDATE"));
- String exrule = flattenProperties(component, "EXRULE");
- String exdate = extractDates(component.getFirstProperty("EXDATE"));
-
- if ((TextUtils.isEmpty(dtstart))||
- (TextUtils.isEmpty(duration))||
- ((TextUtils.isEmpty(rrule))&&
- (TextUtils.isEmpty(rdate)))) {
- if (false) {
- Log.d(TAG, "Recurrence missing DTSTART, DTEND/DURATION, "
- + "or RRULE/RDATE: "
- + component.toString());
- }
- return false;
- }
-
- if (allDay) {
- start.timezone = Time.TIMEZONE_UTC;
- }
- long millis = start.toMillis(false /* use isDst */);
- values.put(CalendarContract.Events.DTSTART, millis);
- if (millis == -1) {
- if (false) {
- Log.d(TAG, "DTSTART is out of range: " + component.toString());
- }
- return false;
- }
-
- values.put(CalendarContract.Events.RRULE, rrule);
- values.put(CalendarContract.Events.RDATE, rdate);
- values.put(CalendarContract.Events.EXRULE, exrule);
- values.put(CalendarContract.Events.EXDATE, exdate);
- values.put(CalendarContract.Events.EVENT_TIMEZONE, tzid);
- values.put(CalendarContract.Events.DURATION, duration);
- values.put(CalendarContract.Events.ALL_DAY, allDay ? 1 : 0);
- return true;
- }
-
- // This can be removed when the old CalendarSyncAdapter is removed.
- public static boolean populateComponent(Cursor cursor,
- ICalendar.Component component) {
-
- int dtstartColumn = cursor.getColumnIndex(CalendarContract.Events.DTSTART);
- int durationColumn = cursor.getColumnIndex(CalendarContract.Events.DURATION);
- int tzidColumn = cursor.getColumnIndex(CalendarContract.Events.EVENT_TIMEZONE);
- int rruleColumn = cursor.getColumnIndex(CalendarContract.Events.RRULE);
- int rdateColumn = cursor.getColumnIndex(CalendarContract.Events.RDATE);
- int exruleColumn = cursor.getColumnIndex(CalendarContract.Events.EXRULE);
- int exdateColumn = cursor.getColumnIndex(CalendarContract.Events.EXDATE);
- int allDayColumn = cursor.getColumnIndex(CalendarContract.Events.ALL_DAY);
-
-
- long dtstart = -1;
- if (!cursor.isNull(dtstartColumn)) {
- dtstart = cursor.getLong(dtstartColumn);
- }
- String duration = cursor.getString(durationColumn);
- String tzid = cursor.getString(tzidColumn);
- String rruleStr = cursor.getString(rruleColumn);
- String rdateStr = cursor.getString(rdateColumn);
- String exruleStr = cursor.getString(exruleColumn);
- String exdateStr = cursor.getString(exdateColumn);
- boolean allDay = cursor.getInt(allDayColumn) == 1;
-
- if ((dtstart == -1) ||
- (TextUtils.isEmpty(duration))||
- ((TextUtils.isEmpty(rruleStr))&&
- (TextUtils.isEmpty(rdateStr)))) {
- // no recurrence.
- return false;
- }
-
- ICalendar.Property dtstartProp = new ICalendar.Property("DTSTART");
- Time dtstartTime = null;
- if (!TextUtils.isEmpty(tzid)) {
- if (!allDay) {
- dtstartProp.addParameter(new ICalendar.Parameter("TZID", tzid));
- }
- dtstartTime = new Time(tzid);
- } else {
- // use the "floating" timezone
- dtstartTime = new Time(Time.TIMEZONE_UTC);
- }
-
- dtstartTime.set(dtstart);
- // make sure the time is printed just as a date, if all day.
- // TODO: android.pim.Time really should take care of this for us.
- if (allDay) {
- dtstartProp.addParameter(new ICalendar.Parameter("VALUE", "DATE"));
- dtstartTime.allDay = true;
- dtstartTime.hour = 0;
- dtstartTime.minute = 0;
- dtstartTime.second = 0;
- }
-
- dtstartProp.setValue(dtstartTime.format2445());
- component.addProperty(dtstartProp);
- ICalendar.Property durationProp = new ICalendar.Property("DURATION");
- durationProp.setValue(duration);
- component.addProperty(durationProp);
-
- addPropertiesForRuleStr(component, "RRULE", rruleStr);
- addPropertyForDateStr(component, "RDATE", rdateStr);
- addPropertiesForRuleStr(component, "EXRULE", exruleStr);
- addPropertyForDateStr(component, "EXDATE", exdateStr);
- return true;
- }
-
-public static boolean populateComponent(ContentValues values,
- ICalendar.Component component) {
- long dtstart = -1;
- if (values.containsKey(CalendarContract.Events.DTSTART)) {
- dtstart = values.getAsLong(CalendarContract.Events.DTSTART);
- }
- String duration = values.getAsString(CalendarContract.Events.DURATION);
- String tzid = values.getAsString(CalendarContract.Events.EVENT_TIMEZONE);
- String rruleStr = values.getAsString(CalendarContract.Events.RRULE);
- String rdateStr = values.getAsString(CalendarContract.Events.RDATE);
- String exruleStr = values.getAsString(CalendarContract.Events.EXRULE);
- String exdateStr = values.getAsString(CalendarContract.Events.EXDATE);
- Integer allDayInteger = values.getAsInteger(CalendarContract.Events.ALL_DAY);
- boolean allDay = (null != allDayInteger) ? (allDayInteger == 1) : false;
-
- if ((dtstart == -1) ||
- (TextUtils.isEmpty(duration))||
- ((TextUtils.isEmpty(rruleStr))&&
- (TextUtils.isEmpty(rdateStr)))) {
- // no recurrence.
- return false;
- }
-
- ICalendar.Property dtstartProp = new ICalendar.Property("DTSTART");
- Time dtstartTime = null;
- if (!TextUtils.isEmpty(tzid)) {
- if (!allDay) {
- dtstartProp.addParameter(new ICalendar.Parameter("TZID", tzid));
- }
- dtstartTime = new Time(tzid);
- } else {
- // use the "floating" timezone
- dtstartTime = new Time(Time.TIMEZONE_UTC);
- }
-
- dtstartTime.set(dtstart);
- // make sure the time is printed just as a date, if all day.
- // TODO: android.pim.Time really should take care of this for us.
- if (allDay) {
- dtstartProp.addParameter(new ICalendar.Parameter("VALUE", "DATE"));
- dtstartTime.allDay = true;
- dtstartTime.hour = 0;
- dtstartTime.minute = 0;
- dtstartTime.second = 0;
- }
-
- dtstartProp.setValue(dtstartTime.format2445());
- component.addProperty(dtstartProp);
- ICalendar.Property durationProp = new ICalendar.Property("DURATION");
- durationProp.setValue(duration);
- component.addProperty(durationProp);
-
- addPropertiesForRuleStr(component, "RRULE", rruleStr);
- addPropertyForDateStr(component, "RDATE", rdateStr);
- addPropertiesForRuleStr(component, "EXRULE", exruleStr);
- addPropertyForDateStr(component, "EXDATE", exdateStr);
- return true;
- }
-
- private static void addPropertiesForRuleStr(ICalendar.Component component,
- String propertyName,
- String ruleStr) {
- if (TextUtils.isEmpty(ruleStr)) {
- return;
- }
- String[] rrules = getRuleStrings(ruleStr);
- for (String rrule : rrules) {
- ICalendar.Property prop = new ICalendar.Property(propertyName);
- prop.setValue(rrule);
- component.addProperty(prop);
- }
- }
-
- private static String[] getRuleStrings(String ruleStr) {
- if (null == ruleStr) {
- return new String[0];
- }
- String unfoldedRuleStr = unfold(ruleStr);
- String[] split = unfoldedRuleStr.split(RULE_SEPARATOR);
- int count = split.length;
- for (int n = 0; n < count; n++) {
- split[n] = fold(split[n]);
- }
- return split;
- }
-
-
- private static final Pattern IGNORABLE_ICAL_WHITESPACE_RE =
- Pattern.compile("(?:\\r\\n?|\\n)[ \t]");
-
- private static final Pattern FOLD_RE = Pattern.compile(".{75}");
-
- /**
- * fold and unfolds ical content lines as per RFC 2445 section 4.1.
- *
- * <h3>4.1 Content Lines</h3>
- *
- * <p>The iCalendar object is organized into individual lines of text, called
- * content lines. Content lines are delimited by a line break, which is a CRLF
- * sequence (US-ASCII decimal 13, followed by US-ASCII decimal 10).
- *
- * <p>Lines of text SHOULD NOT be longer than 75 octets, excluding the line
- * break. Long content lines SHOULD be split into a multiple line
- * representations using a line "folding" technique. That is, a long line can
- * be split between any two characters by inserting a CRLF immediately
- * followed by a single linear white space character (i.e., SPACE, US-ASCII
- * decimal 32 or HTAB, US-ASCII decimal 9). Any sequence of CRLF followed
- * immediately by a single linear white space character is ignored (i.e.,
- * removed) when processing the content type.
- */
- public static String fold(String unfoldedIcalContent) {
- return FOLD_RE.matcher(unfoldedIcalContent).replaceAll("$0\r\n ");
- }
-
- public static String unfold(String foldedIcalContent) {
- return IGNORABLE_ICAL_WHITESPACE_RE.matcher(
- foldedIcalContent).replaceAll("");
- }
-
- private static void addPropertyForDateStr(ICalendar.Component component,
- String propertyName,
- String dateStr) {
- if (TextUtils.isEmpty(dateStr)) {
- return;
- }
-
- ICalendar.Property prop = new ICalendar.Property(propertyName);
- String tz = null;
- int tzidx = dateStr.indexOf(";");
- if (tzidx != -1) {
- tz = dateStr.substring(0, tzidx);
- dateStr = dateStr.substring(tzidx + 1);
- }
- if (!TextUtils.isEmpty(tz)) {
- prop.addParameter(new ICalendar.Parameter("TZID", tz));
- }
- prop.setValue(dateStr);
- component.addProperty(prop);
- }
-
- private static String computeDuration(Time start,
- ICalendar.Component component) {
- // see if a duration is defined
- ICalendar.Property durationProperty =
- component.getFirstProperty("DURATION");
- if (durationProperty != null) {
- // just return the duration
- return durationProperty.getValue();
- }
-
- // must compute a duration from the DTEND
- ICalendar.Property dtendProperty =
- component.getFirstProperty("DTEND");
- if (dtendProperty == null) {
- // no DURATION, no DTEND: 0 second duration
- return "+P0S";
- }
- ICalendar.Parameter endTzidParameter =
- dtendProperty.getFirstParameter("TZID");
- String endTzid = (endTzidParameter == null)
- ? start.timezone : endTzidParameter.value;
-
- Time end = new Time(endTzid);
- end.parse(dtendProperty.getValue());
- long durationMillis = end.toMillis(false /* use isDst */)
- - start.toMillis(false /* use isDst */);
- long durationSeconds = (durationMillis / 1000);
- if (start.allDay && (durationSeconds % 86400) == 0) {
- return "P" + (durationSeconds / 86400) + "D"; // Server wants this instead of P86400S
- } else {
- return "P" + durationSeconds + "S";
- }
- }
-
- private static String flattenProperties(ICalendar.Component component,
- String name) {
- List<ICalendar.Property> properties = component.getProperties(name);
- if (properties == null || properties.isEmpty()) {
- return null;
- }
-
- if (properties.size() == 1) {
- return properties.get(0).getValue();
- }
-
- StringBuilder sb = new StringBuilder();
-
- boolean first = true;
- for (ICalendar.Property property : component.getProperties(name)) {
- if (first) {
- first = false;
- } else {
- // TODO: use commas. our RECUR parsing should handle that
- // anyway.
- sb.append(RULE_SEPARATOR);
- }
- sb.append(property.getValue());
- }
- return sb.toString();
- }
-
- private static String extractDates(ICalendar.Property recurrence) {
- if (recurrence == null) {
- return null;
- }
- ICalendar.Parameter tzidParam =
- recurrence.getFirstParameter("TZID");
- if (tzidParam != null) {
- return tzidParam.value + ";" + recurrence.getValue();
- }
- return recurrence.getValue();
- }
-}
diff --git a/core/java/android/provider/CalendarContract.java b/core/java/android/provider/CalendarContract.java
index b492615..5b29103 100644
--- a/core/java/android/provider/CalendarContract.java
+++ b/core/java/android/provider/CalendarContract.java
@@ -17,6 +17,8 @@
package android.provider;
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.ContentProviderClient;
@@ -83,7 +85,6 @@ import android.util.Log;
* adapters</li>
* </ul>
*
- * @hide
*/
public final class CalendarContract {
private static final String TAG = "Calendar";
@@ -92,8 +93,8 @@ public final class CalendarContract {
* Broadcast Action: This is the intent that gets fired when an alarm
* notification needs to be posted for a reminder.
*
- * @SdkConstant
*/
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_EVENT_REMINDER = "android.intent.action.EVENT_REMINDER";
/**
@@ -146,6 +147,11 @@ public final class CalendarContract {
public static final String ACCOUNT_TYPE_LOCAL = "LOCAL";
/**
+ * This utility class cannot be instantiated
+ */
+ private CalendarContract() {}
+
+ /**
* Generic columns for use by sync adapters. The specific functions of these
* columns are private to the sync adapter. Other clients of the API should
* not attempt to either read or write this column. These columns are
@@ -384,7 +390,7 @@ public final class CalendarContract {
* Class that represents a Calendar Entity. There is one entry per calendar.
* This is a helper class to make batch operations easier.
*/
- public static class CalendarEntity implements BaseColumns, SyncColumns, CalendarColumns {
+ public static final class CalendarEntity implements BaseColumns, SyncColumns, CalendarColumns {
/**
* The default Uri used when creating a new calendar EntityIterator.
@@ -394,6 +400,11 @@ public final class CalendarContract {
"/calendar_entities");
/**
+ * This utility class cannot be instantiated
+ */
+ private CalendarEntity() {}
+
+ /**
* Creates an entity iterator for the given cursor. It assumes the
* cursor contains a calendars query.
*
@@ -566,7 +577,13 @@ public final class CalendarContract {
* <li>{@link #CAL_SYNC10}</li>
* </ul>
*/
- public static class Calendars implements BaseColumns, SyncColumns, CalendarColumns {
+ public static final class Calendars implements BaseColumns, SyncColumns, CalendarColumns {
+
+ /**
+ * This utility class cannot be instantiated
+ */
+ private Calendars() {}
+
/**
* The content:// style URL for accessing Calendars
*/
@@ -687,7 +704,7 @@ public final class CalendarContract {
/**
* Fields and helpers for interacting with Attendees. Each row of this table
* represents a single attendee or guest of an event. Calling
- * {@link #query(ContentResolver, long)} will return a list of attendees for
+ * {@link #query(ContentResolver, long, String[])} will return a list of attendees for
* the event with the given eventId. Both apps and sync adapters may write
* to this table. There are six writable fields and all of them except
* {@link #ATTENDEE_NAME} must be included when inserting a new attendee.
@@ -708,12 +725,12 @@ public final class CalendarContract {
*/
@SuppressWarnings("hiding")
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/attendees");
+ private static final String ATTENDEES_WHERE = Attendees.EVENT_ID + "=?";
+
/**
- * the projection used by the attendees query
+ * This utility class cannot be instantiated
*/
- public static final String[] PROJECTION = new String[] {
- _ID, ATTENDEE_NAME, ATTENDEE_EMAIL, ATTENDEE_RELATIONSHIP, ATTENDEE_STATUS,};
- private static final String ATTENDEES_WHERE = Attendees.EVENT_ID + "=?";
+ private Attendees() {}
/**
* Queries all attendees associated with the given event. This is a
@@ -721,11 +738,12 @@ public final class CalendarContract {
*
* @param cr The content resolver to use for the query
* @param eventId The id of the event to retrieve attendees for
+ * @param projection the columns to return in the cursor
* @return A Cursor containing all attendees for the event
*/
- public static final Cursor query(ContentResolver cr, long eventId) {
+ public static final Cursor query(ContentResolver cr, long eventId, String[] projection) {
String[] attArgs = {Long.toString(eventId)};
- return cr.query(CONTENT_URI, PROJECTION, ATTENDEES_WHERE, attArgs /* selection args */,
+ return cr.query(CONTENT_URI, projection, ATTENDEES_WHERE, attArgs /* selection args */,
null /* sort order */);
}
}
@@ -1068,6 +1086,11 @@ public final class CalendarContract {
"/event_entities");
/**
+ * This utility class cannot be instantiated
+ */
+ private EventsEntity() {}
+
+ /**
* Creates a new iterator for events
*
* @param cursor An event query
@@ -1411,6 +1434,11 @@ public final class CalendarContract {
Uri.parse("content://" + AUTHORITY + "/exception");
/**
+ * This utility class cannot be instantiated
+ */
+ private Events() {}
+
+ /**
* The default sort order for this table
*/
private static final String DEFAULT_SORT_ORDER = "";
@@ -1484,6 +1512,11 @@ public final class CalendarContract {
};
/**
+ * This utility class cannot be instantiated
+ */
+ private Instances() {}
+
+ /**
* Performs a query to return all visible instances in the given range.
* This is a blocking function and should not be done on the UI thread.
* This will cause an expansion of recurring events to fill this time
@@ -1636,7 +1669,7 @@ public final class CalendarContract {
* time zone for the instances. These settings are stored using a key/value
* scheme. A {@link #KEY} must be specified when updating these values.
*/
- public static class CalendarCache implements CalendarCacheColumns {
+ public static final class CalendarCache implements CalendarCacheColumns {
/**
* The URI to use for retrieving the properties from the Calendar db.
*/
@@ -1644,6 +1677,11 @@ public final class CalendarContract {
Uri.parse("content://" + AUTHORITY + "/properties");
/**
+ * This utility class cannot be instantiated
+ */
+ private CalendarCache() {}
+
+ /**
* They key for updating the use of auto/home time zones in Calendar.
* Valid values are {@link #TIMEZONE_TYPE_AUTO} or
* {@link #TIMEZONE_TYPE_HOME}.
@@ -1724,6 +1762,11 @@ public final class CalendarContract {
* @hide
*/
public static final class CalendarMetaData implements CalendarMetaDataColumns, BaseColumns {
+
+ /**
+ * This utility class cannot be instantiated
+ */
+ private CalendarMetaData() {}
}
protected interface EventDaysColumns {
@@ -1746,14 +1789,12 @@ public final class CalendarContract {
public static final class EventDays implements EventDaysColumns {
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY
+ "/instances/groupbyday");
+ private static final String SELECTION = "selected=1";
/**
- * The projection used by the EventDays query.
+ * This utility class cannot be instantiated
*/
- public static final String[] PROJECTION = {
- STARTDAY, ENDDAY
- };
- private static final String SELECTION = "selected=1";
+ private EventDays() {}
/**
* Retrieves the days with events for the Julian days starting at
@@ -1765,10 +1806,12 @@ public final class CalendarContract {
* @param cr the ContentResolver
* @param startDay the first Julian day in the range
* @param numDays the number of days to load (must be at least 1)
+ * @param projection the columns to return in the cursor
* @return a database cursor containing a list of start and end days for
* events
*/
- public static final Cursor query(ContentResolver cr, int startDay, int numDays) {
+ public static final Cursor query(ContentResolver cr, int startDay, int numDays,
+ String[] projection) {
if (numDays < 1) {
return null;
}
@@ -1776,7 +1819,7 @@ public final class CalendarContract {
Uri.Builder builder = CONTENT_URI.buildUpon();
ContentUris.appendId(builder, startDay);
ContentUris.appendId(builder, endDay);
- return cr.query(builder.build(), PROJECTION, SELECTION,
+ return cr.query(builder.build(), projection, SELECTION,
null /* selection args */, STARTDAY);
}
}
@@ -1821,7 +1864,7 @@ public final class CalendarContract {
/**
* Fields and helpers for accessing reminders for an event. Each row of this
* table represents a single reminder for an event. Calling
- * {@link #query(ContentResolver, long)} will return a list of reminders for
+ * {@link #query(ContentResolver, long, String[])} will return a list of reminders for
* the event with the given eventId. Both apps and sync adapters may write
* to this table. There are three writable fields and all of them must be
* included when inserting a new reminder. They are:
@@ -1833,25 +1876,26 @@ public final class CalendarContract {
*/
public static final class Reminders implements BaseColumns, RemindersColumns, EventsColumns {
private static final String REMINDERS_WHERE = CalendarContract.Reminders.EVENT_ID + "=?";
- /**
- * The projection used by the reminders query.
- */
- public static final String[] PROJECTION = new String[] {
- _ID, MINUTES, METHOD,};
@SuppressWarnings("hiding")
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/reminders");
/**
+ * This utility class cannot be instantiated
+ */
+ private Reminders() {}
+
+ /**
* Queries all reminders associated with the given event. This is a
* blocking call and should not be done on the UI thread.
*
* @param cr The content resolver to use for the query
* @param eventId The id of the event to retrieve reminders for
+ * @param projection the columns to return in the cursor
* @return A Cursor containing all reminders for the event
*/
- public static final Cursor query(ContentResolver cr, long eventId) {
+ public static final Cursor query(ContentResolver cr, long eventId, String[] projection) {
String[] remArgs = {Long.toString(eventId)};
- return cr.query(CONTENT_URI, PROJECTION, REMINDERS_WHERE, remArgs /* selection args */,
+ return cr.query(CONTENT_URI, projection, REMINDERS_WHERE, remArgs /*selection args*/,
null /* sort order */);
}
}
@@ -1964,6 +2008,11 @@ public final class CalendarContract {
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY +
"/calendar_alerts");
+ /**
+ * This utility class cannot be instantiated
+ */
+ private CalendarAlerts() {}
+
private static final String WHERE_ALARM_EXISTS = EVENT_ID + "=?"
+ " AND " + BEGIN + "=?"
+ " AND " + ALARM_TIME + "=?";
@@ -2134,7 +2183,7 @@ public final class CalendarContract {
* given event id, begin time and alarm time. If one is found then this
* alarm already exists and this method returns true. TODO Move to
* provider
- *
+ *
* @param cr the ContentResolver
* @param eventId the event id to match
* @param begin the start time of the event in UTC millis
@@ -2203,6 +2252,11 @@ public final class CalendarContract {
public static final Uri CONTENT_URI =
Uri.parse("content://" + AUTHORITY + "/extendedproperties");
+ /**
+ * This utility class cannot be instantiated
+ */
+ private ExtendedProperties() {}
+
// TODO: fill out this class when we actually start utilizing extendedproperties
// in the calendar application.
}
@@ -2271,5 +2325,10 @@ public final class CalendarContract {
* @hide
*/
public static final class EventsRawTimes implements BaseColumns, EventsRawTimesColumns {
+
+ /**
+ * This utility class cannot be instantiated
+ */
+ private EventsRawTimes() {}
}
}
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 61deea4..ec67683 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -135,14 +135,6 @@ public final class ContactsContract {
public static final String ALLOW_PROFILE = "allow_profile";
/**
- * A query parameter key used to specify the package that is requesting a query.
- * This is used for restricting data based on package name.
- *
- * @hide
- */
- public static final String REQUESTING_PACKAGE_PARAM_KEY = "requesting_package";
-
- /**
* Query parameter that should be used by the client to access a specific
* {@link Directory}. The parameter value should be the _ID of the corresponding
* directory, e.g.
@@ -271,8 +263,6 @@ public final class ContactsContract {
* <li>The URI authority is replaced with the corresponding {@link #DIRECTORY_AUTHORITY}.</li>
* <li>The {@code accountName=} and {@code accountType=} parameters are added or
* replaced using the corresponding {@link #ACCOUNT_TYPE} and {@link #ACCOUNT_NAME} values.</li>
- * <li>If the URI is missing a ContactsContract.REQUESTING_PACKAGE_PARAM_KEY
- * parameter, this parameter is added.</li>
* </ul>
* </p>
* <p>
@@ -1881,13 +1871,16 @@ public final class ContactsContract {
public static final String CONTACT_ID = "contact_id";
/**
- * Flag indicating that this {@link RawContacts} entry and its children have
- * been restricted to specific platform apps.
- * <P>Type: INTEGER (boolean)</P>
+ * The data set within the account that this row belongs to. This allows
+ * multiple sync adapters for the same account type to distinguish between
+ * each others' data.
*
- * @hide until finalized in future platform release
+ * This is empty by default, and is completely optional. It only needs to
+ * be populated if multiple sync adapters are entering distinct data for
+ * the same account type and account name.
+ * <P>Type: TEXT</P>
*/
- public static final String IS_RESTRICTED = "is_restricted";
+ public static final String DATA_SET = "data_set";
/**
* The aggregation mode for this contact.
@@ -2211,8 +2204,8 @@ public final class ContactsContract {
* <td>The name of the account instance to which this row belongs, which when paired with
* {@link #ACCOUNT_TYPE} identifies a specific account.
* For example, this will be the Gmail address if it is a Google account.
- * It should be set at the time
- * the raw contact is inserted and never changed afterwards.</td>
+ * It should be set at the time the raw contact is inserted and never
+ * changed afterwards.</td>
* </tr>
* <tr>
* <td>String</td>
@@ -2222,8 +2215,8 @@ public final class ContactsContract {
* <p>
* The type of account to which this row belongs, which when paired with
* {@link #ACCOUNT_NAME} identifies a specific account.
- * It should be set at the time
- * the raw contact is inserted and never changed afterwards.
+ * It should be set at the time the raw contact is inserted and never
+ * changed afterwards.
* </p>
* <p>
* To ensure uniqueness, new account types should be chosen according to the
@@ -2233,15 +2226,38 @@ public final class ContactsContract {
* </tr>
* <tr>
* <td>String</td>
+ * <td>{@link #DATA_SET}</td>
+ * <td>read/write-once</td>
+ * <td>
+ * <p>
+ * The data set within the account that this row belongs to. This allows
+ * multiple sync adapters for the same account type to distinguish between
+ * each others' data. The combination of {@link #ACCOUNT_TYPE},
+ * {@link #ACCOUNT_NAME}, and {@link #DATA_SET} identifies a set of data
+ * that is associated with a single sync adapter.
+ * </p>
+ * <p>
+ * This is empty by default, and is completely optional. It only needs to
+ * be populated if multiple sync adapters are entering distinct data for
+ * the same account type and account name.
+ * </p>
+ * <p>
+ * It should be set at the time the raw contact is inserted and never
+ * changed afterwards.
+ * </p>
+ * </td>
+ * </tr>
+ * <tr>
+ * <td>String</td>
* <td>{@link #SOURCE_ID}</td>
* <td>read/write</td>
* <td>String that uniquely identifies this row to its source account.
* Typically it is set at the time the raw contact is inserted and never
* changed afterwards. The one notable exception is a new raw contact: it
- * will have an account name and type, but no source id. This
- * indicates to the sync adapter that a new contact needs to be created
- * server-side and its ID stored in the corresponding SOURCE_ID field on
- * the phone.
+ * will have an account name and type (and possibly a data set), but no
+ * source id. This indicates to the sync adapter that a new contact needs
+ * to be created server-side and its ID stored in the corresponding
+ * SOURCE_ID field on the phone.
* </td>
* </tr>
* <tr>
@@ -2537,7 +2553,6 @@ public final class ContactsContract {
DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, DELETED);
DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, CONTACT_ID);
DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, STARRED);
- DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, IS_RESTRICTED);
DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, NAME_VERIFIED);
android.content.Entity contact = new android.content.Entity(cv);
@@ -3814,27 +3829,6 @@ public final class ContactsContract {
/**
* <p>
- * If {@link #FOR_EXPORT_ONLY} is explicitly set to "1", returned Cursor toward
- * Data.CONTENT_URI contains only exportable data.
- * </p>
- * <p>
- * This flag is useful (currently) only for vCard exporter in Contacts app, which
- * needs to exclude "un-exportable" data from available data to export, while
- * Contacts app itself has priviledge to access all data including "un-exportable"
- * ones and providers return all of them regardless of the callers' intention.
- * </p>
- * <p>
- * Type: INTEGER
- * </p>
- *
- * @hide Maybe available only in Eclair and not really ready for public use.
- * TODO: remove, or implement this feature completely. As of now (Eclair),
- * we only use this flag in queryEntities(), not query().
- */
- public static final String FOR_EXPORT_ONLY = "for_export_only";
-
- /**
- * <p>
* Build a {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}
* style {@link Uri} for the parent {@link android.provider.ContactsContract.Contacts}
* entry of the given {@link ContactsContract.Data} entry.
@@ -6213,6 +6207,18 @@ public final class ContactsContract {
*/
protected interface GroupsColumns {
/**
+ * The data set within the account that this group belongs to. This allows
+ * multiple sync adapters for the same account type to distinguish between
+ * each others' group data.
+ *
+ * This is empty by default, and is completely optional. It only needs to
+ * be populated if multiple sync adapters are entering distinct group data
+ * for the same account type and account name.
+ * <P>Type: TEXT</P>
+ */
+ public static final String DATA_SET = "data_set";
+
+ /**
* The display title of this group.
* <p>
* Type: TEXT
@@ -6338,6 +6344,29 @@ public final class ContactsContract {
* In other words, it would be a really bad idea to delete and reinsert a
* group. A sync adapter should always do an update instead.</td>
* </tr>
+ # <tr>
+ * <td>String</td>
+ * <td>{@link #DATA_SET}</td>
+ * <td>read/write-once</td>
+ * <td>
+ * <p>
+ * The data set within the account that this group belongs to. This allows
+ * multiple sync adapters for the same account type to distinguish between
+ * each others' group data. The combination of {@link #ACCOUNT_TYPE},
+ * {@link #ACCOUNT_NAME}, and {@link #DATA_SET} identifies a set of data
+ * that is associated with a single sync adapter.
+ * </p>
+ * <p>
+ * This is empty by default, and is completely optional. It only needs to
+ * be populated if multiple sync adapters are entering distinct data for
+ * the same account type and account name.
+ * </p>
+ * <p>
+ * It should be set at the time the group is inserted and never changed
+ * afterwards.
+ * </p>
+ * </td>
+ * </tr>
* <tr>
* <td>String</td>
* <td>{@link #TITLE}</td>
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 65babc2..23b53ae 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3833,6 +3833,11 @@ public final class Settings {
/** {@hide} */
public static final String NETSTATS_TAG_MAX_HISTORY = "netstats_tag_max_history";
+ /** Preferred NTP server. {@hide} */
+ public static final String NTP_SERVER = "ntp_server";
+ /** Timeout in milliseconds to wait for NTP server. {@hide} */
+ public static final String NTP_TIMEOUT = "ntp_timeout";
+
/**
* @hide
*/
diff --git a/core/java/android/server/BluetoothHealthProfileHandler.java b/core/java/android/server/BluetoothHealthProfileHandler.java
index 7f862e0..105ff33 100644
--- a/core/java/android/server/BluetoothHealthProfileHandler.java
+++ b/core/java/android/server/BluetoothHealthProfileHandler.java
@@ -20,15 +20,12 @@ import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHealth;
import android.bluetooth.BluetoothHealthAppConfiguration;
-import android.bluetooth.BluetoothHealth;
-import android.bluetooth.BluetoothInputDevice;
+import android.bluetooth.IBluetoothHealthCallback;
import android.content.Context;
-import android.content.Intent;
import android.os.Handler;
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
-import android.provider.Settings;
import android.util.Log;
import java.util.ArrayList;
@@ -36,10 +33,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map.Entry;
-import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.IOException;
-
/**
* This handles all the operations on the Bluetooth Health profile.
* All functions are called by BluetoothService, as Bluetooth Service
@@ -58,6 +51,7 @@ final class BluetoothHealthProfileHandler {
private ArrayList<HealthChannel> mHealthChannels;
private HashMap <BluetoothHealthAppConfiguration, String> mHealthAppConfigs;
private HashMap <BluetoothDevice, Integer> mHealthDevices;
+ private HashMap <BluetoothHealthAppConfiguration, IBluetoothHealthCallback> mCallbacks;
private static final int MESSAGE_REGISTER_APPLICATION = 0;
private static final int MESSAGE_UNREGISTER_APPLICATION = 1;
@@ -103,6 +97,7 @@ final class BluetoothHealthProfileHandler {
}
if (path == null) {
+ mCallbacks.remove(registerApp);
callHealthApplicationStatusCallback(registerApp,
BluetoothHealth.APPLICATION_REGISTRATION_FAILURE);
} else {
@@ -118,6 +113,7 @@ final class BluetoothHealthProfileHandler {
boolean result = mBluetoothService.unregisterHealthApplicationNative(
mHealthAppConfigs.get(unregisterApp));
if (result) {
+ mCallbacks.remove(unregisterApp);
callHealthApplicationStatusCallback(unregisterApp,
BluetoothHealth.APPLICATION_UNREGISTRATION_SUCCESS);
} else {
@@ -149,6 +145,7 @@ final class BluetoothHealthProfileHandler {
mHealthAppConfigs = new HashMap<BluetoothHealthAppConfiguration, String>();
mHealthChannels = new ArrayList<HealthChannel>();
mHealthDevices = new HashMap<BluetoothDevice, Integer>();
+ mCallbacks = new HashMap<BluetoothHealthAppConfiguration, IBluetoothHealthCallback>();
}
static synchronized BluetoothHealthProfileHandler getInstance(Context context,
@@ -157,10 +154,12 @@ final class BluetoothHealthProfileHandler {
return sInstance;
}
- boolean registerAppConfiguration(BluetoothHealthAppConfiguration config) {
+ boolean registerAppConfiguration(BluetoothHealthAppConfiguration config,
+ IBluetoothHealthCallback callback) {
Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_APPLICATION);
msg.obj = config;
mHandler.sendMessage(msg);
+ mCallbacks.put(config, callback);
return true;
}
@@ -442,11 +441,11 @@ final class BluetoothHealthProfileHandler {
debugLog("Health Device Callback: " + device + " State Change: "
+ prevState + "->" + state);
- try {
- config.getCallback().onHealthChannelStateChange(config, device, prevState,
- state, fd);
- } catch (RemoteException e) {
- errorLog("Error while making health channel state change callback: " + e);
+ IBluetoothHealthCallback callback = mCallbacks.get(config);
+ if (callback != null) {
+ try {
+ callback.onHealthChannelStateChange(config, device, prevState, state, fd);
+ } catch (RemoteException e) {}
}
}
@@ -454,10 +453,11 @@ final class BluetoothHealthProfileHandler {
BluetoothHealthAppConfiguration config, int status) {
debugLog("Health Device Application: " + config + " State Change: status:"
+ status);
- try {
- config.getCallback().onHealthAppConfigurationStatusChange(config, status);
- } catch (RemoteException e) {
- errorLog("Error while making health app registration state change callback: " + e);
+ IBluetoothHealthCallback callback = mCallbacks.get(config);
+ if (callback != null) {
+ try {
+ callback.onHealthAppConfigurationStatusChange(config, status);
+ } catch (RemoteException e) {}
}
}
@@ -526,19 +526,19 @@ final class BluetoothHealthProfileHandler {
List<HealthChannel> chan;
switch (currDeviceState) {
case BluetoothHealth.STATE_DISCONNECTED:
- updateAndsendIntent(device, currDeviceState, newDeviceState);
+ updateAndSendIntent(device, currDeviceState, newDeviceState);
break;
case BluetoothHealth.STATE_CONNECTING:
// Channel got connected.
if (newDeviceState == BluetoothHealth.STATE_CONNECTED) {
- updateAndsendIntent(device, currDeviceState, newDeviceState);
+ updateAndSendIntent(device, currDeviceState, newDeviceState);
} else {
// Channel got disconnected
chan = findChannelByStates(device, new int [] {
BluetoothHealth.STATE_CHANNEL_CONNECTING,
BluetoothHealth.STATE_CHANNEL_DISCONNECTING});
if (chan.isEmpty()) {
- updateAndsendIntent(device, currDeviceState, newDeviceState);
+ updateAndSendIntent(device, currDeviceState, newDeviceState);
}
}
break;
@@ -548,22 +548,23 @@ final class BluetoothHealthProfileHandler {
BluetoothHealth.STATE_CHANNEL_CONNECTING,
BluetoothHealth.STATE_CHANNEL_CONNECTED});
if (chan.isEmpty()) {
- updateAndsendIntent(device, currDeviceState, newDeviceState);
+ updateAndSendIntent(device, currDeviceState, newDeviceState);
}
+ break;
case BluetoothHealth.STATE_DISCONNECTING:
// Channel got disconnected.
chan = findChannelByStates(device, new int [] {
BluetoothHealth.STATE_CHANNEL_CONNECTING,
BluetoothHealth.STATE_CHANNEL_DISCONNECTING});
if (chan.isEmpty()) {
- updateAndsendIntent(device, currDeviceState, newDeviceState);
+ updateAndSendIntent(device, currDeviceState, newDeviceState);
}
break;
}
}
}
- private void updateAndsendIntent(BluetoothDevice device, int prevDeviceState,
+ private void updateAndSendIntent(BluetoothDevice device, int prevDeviceState,
int newDeviceState) {
mHealthDevices.put(device, newDeviceState);
mBluetoothService.sendConnectionStateChange(device, prevDeviceState, newDeviceState);
diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java
index 9839f76..b23e3ce 100644..100755
--- a/core/java/android/server/BluetoothService.java
+++ b/core/java/android/server/BluetoothService.java
@@ -24,8 +24,6 @@
package android.server;
-import com.android.internal.app.IBatteryStats;
-
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
@@ -40,6 +38,7 @@ import android.bluetooth.BluetoothSocket;
import android.bluetooth.BluetoothUuid;
import android.bluetooth.IBluetooth;
import android.bluetooth.IBluetoothCallback;
+import android.bluetooth.IBluetoothHealthCallback;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
@@ -58,6 +57,8 @@ import android.provider.Settings;
import android.util.Log;
import android.util.Pair;
+import com.android.internal.app.IBatteryStats;
+
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
@@ -2111,11 +2112,12 @@ public class BluetoothService extends IBluetooth.Stub {
/**** Handlers for Health Device Profile ****/
// TODO: All these need to be converted to a state machine.
- public boolean registerAppConfiguration(BluetoothHealthAppConfiguration config) {
+ public boolean registerAppConfiguration(BluetoothHealthAppConfiguration config,
+ IBluetoothHealthCallback callback) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
"Need BLUETOOTH permission");
synchronized (mBluetoothHealthProfileHandler) {
- return mBluetoothHealthProfileHandler.registerAppConfiguration(config);
+ return mBluetoothHealthProfileHandler.registerAppConfiguration(config, callback);
}
}
diff --git a/core/java/android/speech/tts/AudioPlaybackHandler.java b/core/java/android/speech/tts/AudioPlaybackHandler.java
index c7603ee..8ef4295 100644
--- a/core/java/android/speech/tts/AudioPlaybackHandler.java
+++ b/core/java/android/speech/tts/AudioPlaybackHandler.java
@@ -384,11 +384,16 @@ class AudioPlaybackHandler {
}
count += written;
}
+
+ param.mLogger.onPlaybackStart();
}
private void handleSynthesisDone(MessageParams msg) {
final SynthesisMessageParams params = (SynthesisMessageParams) msg;
handleSynthesisDone(params);
+ // This call is delayed more than it should be, but we are
+ // certain at this point that we have all the data we want.
+ params.mLogger.onWriteData();
}
// Flush all remaining data to the audio track, stop it and release
@@ -416,6 +421,8 @@ class AudioPlaybackHandler {
final SynthesisMessageParams params = (SynthesisMessageParams) msg;
if (DBG) Log.d(TAG, "completeAudioAvailable(" + params + ")");
+ params.mLogger.onPlaybackStart();
+
// Channel config and bytes per frame are checked before
// this message is sent.
int channelConfig = AudioPlaybackHandler.getChannelConfig(params.mChannelCount);
diff --git a/core/java/android/speech/tts/EventLogTags.logtags b/core/java/android/speech/tts/EventLogTags.logtags
new file mode 100644
index 0000000..1a9f5fe
--- /dev/null
+++ b/core/java/android/speech/tts/EventLogTags.logtags
@@ -0,0 +1,6 @@
+# See system/core/logcat/event.logtags for a description of the format of this file.
+
+option java_package android.speech.tts;
+
+76001 tts_speak_success (engine|3),(caller|3),(length|1),(locale|3),(rate|1),(pitch|1),(engine_latency|2|3),(engine_total|2|3),(audio_latency|2|3)
+76002 tts_speak_failure (engine|3),(caller|3),(length|1),(locale|3),(rate|1),(pitch|1)
diff --git a/core/java/android/speech/tts/EventLogger.java b/core/java/android/speech/tts/EventLogger.java
new file mode 100644
index 0000000..63b954b
--- /dev/null
+++ b/core/java/android/speech/tts/EventLogger.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.speech.tts;
+
+import android.os.SystemClock;
+import android.text.TextUtils;
+
+/**
+ * Writes data about a given speech synthesis request to the event logs.
+ * The data that is logged includes the calling app, length of the utterance,
+ * speech rate / pitch and the latency and overall time taken.
+ *
+ * Note that {@link EventLogger#onStopped()} and {@link EventLogger#onError()}
+ * might be called from any thread, but on {@link EventLogger#onPlaybackStart()} and
+ * {@link EventLogger#onComplete()} must be called from a single thread
+ * (usually the audio playback thread}
+ */
+class EventLogger {
+ private final SynthesisRequest mRequest;
+ private final String mCallingApp;
+ private final String mServiceApp;
+ private final long mReceivedTime;
+ private long mPlaybackStartTime = -1;
+ private volatile long mRequestProcessingStartTime = -1;
+ private volatile long mEngineStartTime = -1;
+ private volatile long mEngineCompleteTime = -1;
+
+ private volatile boolean mError = false;
+ private volatile boolean mStopped = false;
+ private boolean mLogWritten = false;
+
+ EventLogger(SynthesisRequest request, String callingApp,
+ String serviceApp) {
+ mRequest = request;
+ mCallingApp = callingApp;
+ mServiceApp = serviceApp;
+ mReceivedTime = SystemClock.elapsedRealtime();
+ }
+
+ /**
+ * Notifies the logger that this request has been selected from
+ * the processing queue for processing. Engine latency / total time
+ * is measured from this baseline.
+ */
+ public void onRequestProcessingStart() {
+ mRequestProcessingStartTime = SystemClock.elapsedRealtime();
+ }
+
+ /**
+ * Notifies the logger that a chunk of data has been received from
+ * the engine. Might be called multiple times.
+ */
+ public void onEngineDataReceived() {
+ if (mEngineStartTime == -1) {
+ mEngineStartTime = SystemClock.elapsedRealtime();
+ }
+ }
+
+ /**
+ * Notifies the logger that the engine has finished processing data.
+ * Will be called exactly once.
+ */
+ public void onEngineComplete() {
+ mEngineCompleteTime = SystemClock.elapsedRealtime();
+ }
+
+ /**
+ * Notifies the logger that audio playback has started for some section
+ * of the synthesis. This is normally some amount of time after the engine
+ * has synthesized data and varides depending on utterances and
+ * other audio currently in the queue.
+ */
+ public void onPlaybackStart() {
+ // For now, keep track of only the first chunk of audio
+ // that was played.
+ if (mPlaybackStartTime == -1) {
+ mPlaybackStartTime = SystemClock.elapsedRealtime();
+ }
+ }
+
+ /**
+ * Notifies the logger that the current synthesis was stopped.
+ * Latency numbers are not reported for stopped syntheses.
+ */
+ public void onStopped() {
+ mStopped = false;
+ }
+
+ /**
+ * Notifies the logger that the current synthesis resulted in
+ * an error. This is logged using {@link EventLogTags#writeTtsSpeakFailure}.
+ */
+ public void onError() {
+ mError = true;
+ }
+
+ /**
+ * Notifies the logger that the current synthesis has completed.
+ * All available data is not logged.
+ */
+ public void onWriteData() {
+ if (mLogWritten) {
+ return;
+ } else {
+ mLogWritten = true;
+ }
+
+ long completionTime = SystemClock.elapsedRealtime();
+ // onPlaybackStart() should normally always be called if an
+ // error does not occur.
+ if (mError || mPlaybackStartTime == -1 || mEngineCompleteTime == -1) {
+ EventLogTags.writeTtsSpeakFailure(mServiceApp, mCallingApp,
+ getUtteranceLength(), getLocaleString(),
+ mRequest.getSpeechRate(), mRequest.getPitch());
+ return;
+ }
+
+ // We don't report stopped syntheses because their overall
+ // total time spent will be innacurate (will not correlate with
+ // the length of the utterance).
+ if (mStopped) {
+ return;
+ }
+
+ final long audioLatency = mPlaybackStartTime - mReceivedTime;
+ final long engineLatency = mEngineStartTime - mRequestProcessingStartTime;
+ final long engineTotal = mEngineCompleteTime - mRequestProcessingStartTime;
+ EventLogTags.writeTtsSpeakSuccess(mServiceApp, mCallingApp,
+ getUtteranceLength(), getLocaleString(),
+ mRequest.getSpeechRate(), mRequest.getPitch(),
+ engineLatency, engineTotal, audioLatency);
+ }
+
+ /**
+ * @return the length of the utterance for the given synthesis, 0
+ * if the utterance was {@code null}.
+ */
+ private int getUtteranceLength() {
+ final String utterance = mRequest.getText();
+ return utterance == null ? 0 : utterance.length();
+ }
+
+ /**
+ * Returns a formatted locale string from the synthesis params of the
+ * form lang-country-variant.
+ */
+ private String getLocaleString() {
+ StringBuilder sb = new StringBuilder(mRequest.getLanguage());
+ if (!TextUtils.isEmpty(mRequest.getCountry())) {
+ sb.append('-');
+ sb.append(mRequest.getCountry());
+
+ if (!TextUtils.isEmpty(mRequest.getVariant())) {
+ sb.append('-');
+ sb.append(mRequest.getVariant());
+ }
+ }
+
+ return sb.toString();
+ }
+
+}
diff --git a/core/java/android/speech/tts/PlaybackSynthesisCallback.java b/core/java/android/speech/tts/PlaybackSynthesisCallback.java
index bdaa1b8..38030a6 100644
--- a/core/java/android/speech/tts/PlaybackSynthesisCallback.java
+++ b/core/java/android/speech/tts/PlaybackSynthesisCallback.java
@@ -65,29 +65,42 @@ class PlaybackSynthesisCallback extends AbstractSynthesisCallback {
private final UtteranceCompletedDispatcher mDispatcher;
private final String mCallingApp;
+ private final EventLogger mLogger;
PlaybackSynthesisCallback(int streamType, float volume, float pan,
AudioPlaybackHandler audioTrackHandler, UtteranceCompletedDispatcher dispatcher,
- String callingApp) {
+ String callingApp, EventLogger logger) {
mStreamType = streamType;
mVolume = volume;
mPan = pan;
mAudioTrackHandler = audioTrackHandler;
mDispatcher = dispatcher;
mCallingApp = callingApp;
+ mLogger = logger;
}
@Override
void stop() {
if (DBG) Log.d(TAG, "stop()");
+ // Note that mLogger.mError might be true too at this point.
+ mLogger.onStopped();
+
synchronized (mStateLock) {
- if (mToken == null || mStopped) {
- Log.w(TAG, "stop() called twice, before start(), or after done()");
+ if (mStopped) {
+ Log.w(TAG, "stop() called twice");
return;
}
- mAudioTrackHandler.stop(mToken);
- mToken = null;
+ // mToken will be null if the engine encounters
+ // an error before it called start().
+ if (mToken != null) {
+ mAudioTrackHandler.stop(mToken);
+ mToken = null;
+ } else {
+ // In all other cases, mAudioTrackHandler.stop() will
+ // result in onComplete being called.
+ mLogger.onWriteData();
+ }
mStopped = true;
}
}
@@ -124,7 +137,7 @@ class PlaybackSynthesisCallback extends AbstractSynthesisCallback {
}
SynthesisMessageParams params = new SynthesisMessageParams(
mStreamType, sampleRateInHz, audioFormat, channelCount, mVolume, mPan,
- mDispatcher, mCallingApp);
+ mDispatcher, mCallingApp, mLogger);
mAudioTrackHandler.enqueueSynthesisStart(params);
mToken = params;
@@ -157,6 +170,8 @@ class PlaybackSynthesisCallback extends AbstractSynthesisCallback {
mAudioTrackHandler.enqueueSynthesisDataAvailable(mToken);
}
+ mLogger.onEngineDataReceived();
+
return TextToSpeech.SUCCESS;
}
@@ -177,6 +192,7 @@ class PlaybackSynthesisCallback extends AbstractSynthesisCallback {
}
mAudioTrackHandler.enqueueSynthesisDone(mToken);
+ mLogger.onEngineComplete();
}
return TextToSpeech.SUCCESS;
}
@@ -184,6 +200,9 @@ class PlaybackSynthesisCallback extends AbstractSynthesisCallback {
@Override
public void error() {
if (DBG) Log.d(TAG, "error() [will call stop]");
+ // Currently, this call will not be logged if error( ) is called
+ // before start.
+ mLogger.onError();
stop();
}
@@ -208,7 +227,7 @@ class PlaybackSynthesisCallback extends AbstractSynthesisCallback {
}
SynthesisMessageParams params = new SynthesisMessageParams(
mStreamType, sampleRateInHz, audioFormat, channelCount, mVolume, mPan,
- mDispatcher, mCallingApp);
+ mDispatcher, mCallingApp, mLogger);
params.addBuffer(buffer, offset, length);
mAudioTrackHandler.enqueueSynthesisCompleteDataAvailable(params);
diff --git a/core/java/android/speech/tts/SynthesisMessageParams.java b/core/java/android/speech/tts/SynthesisMessageParams.java
index 51f3d2e..caf02ef 100644
--- a/core/java/android/speech/tts/SynthesisMessageParams.java
+++ b/core/java/android/speech/tts/SynthesisMessageParams.java
@@ -30,6 +30,7 @@ final class SynthesisMessageParams extends MessageParams {
final int mChannelCount;
final float mVolume;
final float mPan;
+ final EventLogger mLogger;
public volatile AudioTrack mAudioTrack;
@@ -38,7 +39,7 @@ final class SynthesisMessageParams extends MessageParams {
SynthesisMessageParams(int streamType, int sampleRate,
int audioFormat, int channelCount,
float volume, float pan, UtteranceCompletedDispatcher dispatcher,
- String callingApp) {
+ String callingApp, EventLogger logger) {
super(dispatcher, callingApp);
mStreamType = streamType;
@@ -47,6 +48,7 @@ final class SynthesisMessageParams extends MessageParams {
mChannelCount = channelCount;
mVolume = volume;
mPan = pan;
+ mLogger = logger;
// initially null.
mAudioTrack = null;
diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java
index 7ea9373..010c155 100644
--- a/core/java/android/speech/tts/TextToSpeechService.java
+++ b/core/java/android/speech/tts/TextToSpeechService.java
@@ -82,8 +82,7 @@ public abstract class TextToSpeechService extends Service {
private AudioPlaybackHandler mAudioPlaybackHandler;
private CallbackMap mCallbacks;
-
- private int mDefaultAvailability = TextToSpeech.LANG_NOT_SUPPORTED;
+ private String mPackageName;
@Override
public void onCreate() {
@@ -99,9 +98,10 @@ public abstract class TextToSpeechService extends Service {
mCallbacks = new CallbackMap();
+ mPackageName = getApplicationInfo().packageName;
+
// Load default language
- mDefaultAvailability = onLoadLanguage(getDefaultLanguage(),
- getDefaultCountry(), getDefaultVariant());
+ onLoadLanguage(getDefaultLanguage(), getDefaultCountry(), getDefaultVariant());
}
@Override
@@ -457,12 +457,14 @@ public abstract class TextToSpeechService extends Service {
// Non null after synthesis has started, and all accesses
// guarded by 'this'.
private AbstractSynthesisCallback mSynthesisCallback;
+ private final EventLogger mEventLogger;
public SynthesisSpeechItem(String callingApp, Bundle params, String text) {
super(callingApp, params);
mText = text;
mSynthesisRequest = new SynthesisRequest(mText, mParams);
setRequestParams(mSynthesisRequest);
+ mEventLogger = new EventLogger(mSynthesisRequest, getCallingApp(), mPackageName);
}
public String getText() {
@@ -485,6 +487,7 @@ public abstract class TextToSpeechService extends Service {
@Override
protected int playImpl() {
AbstractSynthesisCallback synthesisCallback;
+ mEventLogger.onRequestProcessingStart();
synchronized (this) {
mSynthesisCallback = createSynthesisCallback();
synthesisCallback = mSynthesisCallback;
@@ -495,7 +498,7 @@ public abstract class TextToSpeechService extends Service {
protected AbstractSynthesisCallback createSynthesisCallback() {
return new PlaybackSynthesisCallback(getStreamType(), getVolume(), getPan(),
- mAudioPlaybackHandler, this, getCallingApp());
+ mAudioPlaybackHandler, this, getCallingApp(), mEventLogger);
}
private void setRequestParams(SynthesisRequest request) {
diff --git a/core/java/android/text/style/SuggestionSpan.java b/core/java/android/text/style/SuggestionSpan.java
index 240ad9b..555aac5 100644
--- a/core/java/android/text/style/SuggestionSpan.java
+++ b/core/java/android/text/style/SuggestionSpan.java
@@ -22,12 +22,22 @@ import android.os.Parcelable;
import android.os.SystemClock;
import android.text.ParcelableSpan;
import android.text.TextUtils;
+import android.widget.TextView;
import java.util.Arrays;
import java.util.Locale;
/**
- * Holds suggestion candidates of words under this span.
+ * Holds suggestion candidates for the text enclosed in this span.
+ *
+ * When such a span is edited in an EditText, double tapping on the text enclosed in this span will
+ * display a popup dialog listing suggestion replacement for that text. The user can then replace
+ * the original text by one of the suggestions.
+ *
+ * These spans should typically be created by the input method to privide correction and alternates
+ * for the text.
+ *
+ * @see TextView#setSuggestionsEnabled(boolean)
*/
public class SuggestionSpan implements ParcelableSpan {
/**
@@ -115,14 +125,14 @@ public class SuggestionSpan implements ParcelableSpan {
}
/**
- * @return suggestions
+ * @return an array of suggestion texts for this span
*/
public String[] getSuggestions() {
return mSuggestions;
}
/**
- * @return locale of suggestions
+ * @return the locale of the suggestions
*/
public String getLocale() {
return mLocaleString;
diff --git a/core/java/android/util/NtpTrustedTime.java b/core/java/android/util/NtpTrustedTime.java
index 5b19ecd..2179ff3 100644
--- a/core/java/android/util/NtpTrustedTime.java
+++ b/core/java/android/util/NtpTrustedTime.java
@@ -16,41 +16,71 @@
package android.util;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.res.Resources;
import android.net.SntpClient;
import android.os.SystemClock;
+import android.provider.Settings;
/**
- * {@link TrustedTime} that connects with a remote NTP server as its remote
- * trusted time source.
+ * {@link TrustedTime} that connects with a remote NTP server as its trusted
+ * time source.
*
* @hide
*/
public class NtpTrustedTime implements TrustedTime {
- private String mNtpServer;
- private long mNtpTimeout;
+ private static final String TAG = "NtpTrustedTime";
+ private static final boolean LOGD = false;
+
+ private static NtpTrustedTime sSingleton;
+
+ private final String mServer;
+ private final long mTimeout;
private boolean mHasCache;
private long mCachedNtpTime;
private long mCachedNtpElapsedRealtime;
private long mCachedNtpCertainty;
- public NtpTrustedTime() {
+ private NtpTrustedTime(String server, long timeout) {
+ if (LOGD) Log.d(TAG, "creating NtpTrustedTime using " + server);
+ mServer = server;
+ mTimeout = timeout;
}
- public void setNtpServer(String server, long timeout) {
- mNtpServer = server;
- mNtpTimeout = timeout;
+ public static synchronized NtpTrustedTime getInstance(Context context) {
+ if (sSingleton == null) {
+ final Resources res = context.getResources();
+ final ContentResolver resolver = context.getContentResolver();
+
+ final String defaultServer = res.getString(
+ com.android.internal.R.string.config_ntpServer);
+ final long defaultTimeout = res.getInteger(
+ com.android.internal.R.integer.config_ntpTimeout);
+
+ final String secureServer = Settings.Secure.getString(
+ resolver, Settings.Secure.NTP_SERVER);
+ final long timeout = Settings.Secure.getLong(
+ resolver, Settings.Secure.NTP_TIMEOUT, defaultTimeout);
+
+ final String server = secureServer != null ? secureServer : defaultServer;
+ sSingleton = new NtpTrustedTime(server, timeout);
+ }
+
+ return sSingleton;
}
/** {@inheritDoc} */
public boolean forceRefresh() {
- if (mNtpServer == null) {
+ if (mServer == null) {
// missing server, so no trusted time available
return false;
}
+ if (LOGD) Log.d(TAG, "forceRefresh() from cache miss");
final SntpClient client = new SntpClient();
- if (client.requestTime(mNtpServer, (int) mNtpTimeout)) {
+ if (client.requestTime(mServer, (int) mTimeout)) {
mHasCache = true;
mCachedNtpTime = client.getNtpTime();
mCachedNtpElapsedRealtime = client.getNtpTimeReference();
@@ -89,9 +119,19 @@ public class NtpTrustedTime implements TrustedTime {
if (!mHasCache) {
throw new IllegalStateException("Missing authoritative time source");
}
+ if (LOGD) Log.d(TAG, "currentTimeMillis() cache hit");
// current time is age after the last ntp cache; callers who
// want fresh values will hit makeAuthoritative() first.
return mCachedNtpTime + getCacheAge();
}
+
+ public long getCachedNtpTime() {
+ if (LOGD) Log.d(TAG, "getCachedNtpTime() cache hit");
+ return mCachedNtpTime;
+ }
+
+ public long getCachedNtpTimeReference() {
+ return mCachedNtpElapsedRealtime;
+ }
}
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index 188970c..011e44c 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -128,8 +128,23 @@ public abstract class HardwareRenderer {
abstract void updateSurface(SurfaceHolder holder) throws Surface.OutOfResourcesException;
/**
- * Setup the hardware renderer for drawing. This is called for every
- * frame to draw.
+ * This method should be invoked whenever the current hardware renderer
+ * context should be reset.
+ */
+ abstract void invalidate();
+
+ /**
+ * This method should be invoked to ensure the hardware renderer is in
+ * valid state (for instance, to ensure the correct EGL context is bound
+ * to the current thread.)
+ *
+ * @return true if the renderer is now valid, false otherwise
+ */
+ abstract boolean validate();
+
+ /**
+ * Setup the hardware renderer for drawing. This is called whenever the
+ * size of the target surface changes or when the surface is first created.
*
* @param width Width of the drawing surface.
* @param height Height of the drawing surface.
@@ -289,9 +304,9 @@ public abstract class HardwareRenderer {
static abstract class GlRenderer extends HardwareRenderer {
// These values are not exposed in our EGL APIs
static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
- static final int EGL_SURFACE_TYPE = 0x3033;
- static final int EGL_SWAP_BEHAVIOR_PRESERVED_BIT = 0x0400;
static final int EGL_OPENGL_ES2_BIT = 4;
+ static final int EGL_SURFACE_TYPE = 0x3033;
+ static final int EGL_SWAP_BEHAVIOR_PRESERVED_BIT = 0x0400;
private static final int SURFACE_STATE_ERROR = 0;
private static final int SURFACE_STATE_SUCCESS = 1;
@@ -311,8 +326,16 @@ public abstract class HardwareRenderer {
int mFrameCount;
Paint mDebugPaint;
- boolean mDirtyRegions;
- final boolean mDirtyRegionsRequested;
+ static boolean sDirtyRegions;
+ static final boolean sDirtyRegionsRequested;
+ static {
+ String dirtyProperty = SystemProperties.get(RENDER_DIRTY_REGIONS_PROPERTY, "true");
+ //noinspection PointlessBooleanExpression,ConstantConditions
+ sDirtyRegions = RENDER_DIRTY_REGIONS && "true".equalsIgnoreCase(dirtyProperty);
+ sDirtyRegionsRequested = sDirtyRegions;
+ }
+
+ boolean mDirtyRegionsEnabled;
final boolean mVsyncDisabled;
final int mGlVersion;
@@ -326,11 +349,6 @@ public abstract class HardwareRenderer {
mGlVersion = glVersion;
mTranslucent = translucent;
- final String dirtyProperty = SystemProperties.get(RENDER_DIRTY_REGIONS_PROPERTY, "true");
- //noinspection PointlessBooleanExpression,ConstantConditions
- mDirtyRegions = RENDER_DIRTY_REGIONS && "true".equalsIgnoreCase(dirtyProperty);
- mDirtyRegionsRequested = mDirtyRegions;
-
final String vsyncProperty = SystemProperties.get(DISABLE_VSYNC_PROPERTY, "false");
mVsyncDisabled = "true".equalsIgnoreCase(vsyncProperty);
if (mVsyncDisabled) {
@@ -342,7 +360,7 @@ public abstract class HardwareRenderer {
* Indicates whether this renderer instance can track and update dirty regions.
*/
boolean hasDirtyRegions() {
- return mDirtyRegions;
+ return mDirtyRegionsEnabled;
}
/**
@@ -479,8 +497,8 @@ public abstract class HardwareRenderer {
sEglConfig = chooseEglConfig();
if (sEglConfig == null) {
// We tried to use EGL_SWAP_BEHAVIOR_PRESERVED_BIT, try again without
- if (mDirtyRegions) {
- mDirtyRegions = false;
+ if (sDirtyRegions) {
+ sDirtyRegions = false;
sEglConfig = chooseEglConfig();
if (sEglConfig == null) {
throw new RuntimeException("eglConfig not initialized");
@@ -500,7 +518,7 @@ public abstract class HardwareRenderer {
private EGLConfig chooseEglConfig() {
int[] configsCount = new int[1];
EGLConfig[] configs = new EGLConfig[1];
- int[] configSpec = getConfig(mDirtyRegions);
+ int[] configSpec = getConfig(sDirtyRegions);
if (!sEgl.eglChooseConfig(sEglDisplay, configSpec, configs, 1, configsCount)) {
throw new IllegalArgumentException("eglChooseConfig failed " +
getEGLErrorString(sEgl.eglGetError()));
@@ -566,18 +584,18 @@ public abstract class HardwareRenderer {
// If mDirtyRegions is set, this means we have an EGL configuration
// with EGL_SWAP_BEHAVIOR_PRESERVED_BIT set
- if (mDirtyRegions) {
- if (!GLES20Canvas.preserveBackBuffer()) {
+ if (sDirtyRegions) {
+ if (!(mDirtyRegionsEnabled = GLES20Canvas.preserveBackBuffer())) {
Log.w(LOG_TAG, "Backbuffer cannot be preserved");
}
- } else if (mDirtyRegionsRequested) {
+ } else if (sDirtyRegionsRequested) {
// If mDirtyRegions is not set, our EGL configuration does not
// have EGL_SWAP_BEHAVIOR_PRESERVED_BIT; however, the default
// swap behavior might be EGL_BUFFER_PRESERVED, which means we
// want to set mDirtyRegions. We try to do this only if dirty
// regions were initially requested as part of the device
// configuration (see RENDER_DIRTY_REGIONS)
- mDirtyRegions = GLES20Canvas.isBackBufferPreserved();
+ mDirtyRegionsEnabled = GLES20Canvas.isBackBufferPreserved();
}
return sEglContext.getGL();
@@ -620,6 +638,19 @@ public abstract class HardwareRenderer {
}
@Override
+ void invalidate() {
+ // Cancels any existing buffer to ensure we'll get a buffer
+ // of the right size before we call eglSwapBuffers
+ sEgl.eglMakeCurrent(sEglDisplay, EGL10.EGL_NO_SURFACE,
+ EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
+ }
+
+ @Override
+ boolean validate() {
+ return checkCurrent() != SURFACE_STATE_ERROR;
+ }
+
+ @Override
void setup(int width, int height) {
mCanvas.setViewport(width, height);
}
@@ -645,7 +676,7 @@ public abstract class HardwareRenderer {
attachInfo.mDrawingTime = SystemClock.uptimeMillis();
view.mPrivateFlags |= View.DRAWN;
-
+
final int surfaceState = checkCurrent();
if (surfaceState != SURFACE_STATE_ERROR) {
// We had to change the current surface and/or context, redraw everything
@@ -706,10 +737,21 @@ public abstract class HardwareRenderer {
}
}
}
-
+
+ /**
+ * Ensures the currnet EGL context is the one we expect.
+ *
+ * @return {@link #SURFACE_STATE_ERROR} if the correct EGL context cannot be made current,
+ * {@link #SURFACE_STATE_UPDATED} if the EGL context was changed or
+ * {@link #SURFACE_STATE_SUCCESS} if the EGL context was the correct one
+ */
private int checkCurrent() {
- // TODO: Don't check the current context when we have one per UI thread
- // TODO: Use a threadlocal flag to know whether the surface has changed
+ if (sEglThread != Thread.currentThread()) {
+ throw new IllegalStateException("Hardware acceleration can only be used with a " +
+ "single UI thread.\nOriginal thread: " + sEglThread + "\n" +
+ "Current thread: " + Thread.currentThread());
+ }
+
if (!sEglContext.equals(sEgl.eglGetCurrentContext()) ||
!mEglSurface.equals(sEgl.eglGetCurrentSurface(EGL10.EGL_DRAW))) {
if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, sEglContext)) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 1245898..74dc100 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -4474,7 +4474,7 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit
@RemotableViewMethod
public void setLayoutDirection(int layoutDirection) {
if (getLayoutDirection() != layoutDirection) {
- resetLayoutDirectionResolution();
+ resetResolvedLayoutDirection();
// Setting the flag will also request a layout.
setFlags(layoutDirection, LAYOUT_DIRECTION_MASK);
}
@@ -9043,10 +9043,8 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit
mPrivateFlags &= ~AWAKEN_SCROLL_BARS_ON_ATTACH;
}
jumpDrawablesToCurrentState();
- resetLayoutDirectionResolution();
resolveLayoutDirectionIfNeeded();
resolvePadding();
- resetResolvedTextDirection();
resolveTextDirection();
if (isFocused()) {
InputMethodManager imm = InputMethodManager.peekInstance();
@@ -9143,7 +9141,7 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit
*
* @hide
*/
- protected void resetLayoutDirectionResolution() {
+ protected void resetResolvedLayoutDirection() {
// Reset the current View resolution
mPrivateFlags2 &= ~LAYOUT_DIRECTION_RESOLVED;
}
@@ -9190,6 +9188,9 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit
}
mCurrentAnimation = null;
+
+ resetResolvedLayoutDirection();
+ resetResolvedTextDirection();
}
/**
diff --git a/core/java/android/view/ViewAncestor.java b/core/java/android/view/ViewAncestor.java
index d70c798..1dcbc26 100644
--- a/core/java/android/view/ViewAncestor.java
+++ b/core/java/android/view/ViewAncestor.java
@@ -901,6 +901,7 @@ public final class ViewAncestor extends Handler implements ViewParent,
!mAttachInfo.mTurnOffWindowResizeAnim &&
mAttachInfo.mHardwareRenderer != null &&
mAttachInfo.mHardwareRenderer.isEnabled() &&
+ mAttachInfo.mHardwareRenderer.validate() &&
lp != null && !PixelFormat.formatHasAlpha(lp.format)) {
disposeResizeBuffer();
@@ -1315,6 +1316,9 @@ public final class ViewAncestor extends Handler implements ViewParent,
mAttachInfo.mHardwareRenderer != null &&
mAttachInfo.mHardwareRenderer.isEnabled())) {
mAttachInfo.mHardwareRenderer.setup(mWidth, mHeight);
+ if (!hwInitialized) {
+ mAttachInfo.mHardwareRenderer.invalidate();
+ }
}
if (!mStopped) {
@@ -3845,10 +3849,6 @@ public final class ViewAncestor extends Handler implements ViewParent,
}
private static int checkCallingPermission(String permission) {
- if (!Process.supportsProcesses()) {
- return PackageManager.PERMISSION_GRANTED;
- }
-
try {
return ActivityManagerNative.getDefault().checkPermission(
permission, Binder.getCallingPid(), Binder.getCallingUid());
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 41412de..752fd5a 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -46,7 +46,6 @@ import com.android.internal.util.Predicate;
import java.util.ArrayList;
import java.util.HashSet;
-import java.util.Locale;
/**
* <p>
@@ -5000,15 +4999,15 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
}
@Override
- protected void resetLayoutDirectionResolution() {
- super.resetLayoutDirectionResolution();
+ protected void resetResolvedLayoutDirection() {
+ super.resetResolvedLayoutDirection();
// Take care of resetting the children resolution too
final int count = getChildCount();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getLayoutDirection() == LAYOUT_DIRECTION_INHERIT) {
- child.resetLayoutDirectionResolution();
+ child.resetResolvedLayoutDirection();
}
}
}
@@ -5019,7 +5018,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
*/
@Override
protected void resolveTextDirection() {
- int resolvedTextDirection = TEXT_DIRECTION_UNDEFINED;
+ int resolvedTextDirection;
switch(mTextDirection) {
default:
case TEXT_DIRECTION_INHERIT:
diff --git a/core/java/android/webkit/L10nUtils.java b/core/java/android/webkit/L10nUtils.java
index 4c42cde..a1c6a53 100644
--- a/core/java/android/webkit/L10nUtils.java
+++ b/core/java/android/webkit/L10nUtils.java
@@ -74,7 +74,19 @@ public class L10nUtils {
com.android.internal.R.string.autofill_country_code_re, // IDS_AUTOFILL_COUNTRY_CODE_RE
com.android.internal.R.string.autofill_area_code_notext_re, // IDS_AUTOFILL_AREA_CODE_NOTEXT_RE
com.android.internal.R.string.autofill_phone_prefix_separator_re, // IDS_AUTOFILL_PHONE_PREFIX_SEPARATOR_RE
- com.android.internal.R.string.autofill_phone_suffix_separator_re // IDS_AUTOFILL_PHONE_SUFFIX_SEPARATOR_RE
+ com.android.internal.R.string.autofill_phone_suffix_separator_re, // IDS_AUTOFILL_PHONE_SUFFIX_SEPARATOR_RE
+ com.android.internal.R.string.autofill_province, // IDS_AUTOFILL_DIALOG_PROVINCE
+ com.android.internal.R.string.autofill_postal_code, // IDS_AUTOFILL_DIALOG_POSTAL_CODE
+ com.android.internal.R.string.autofill_state, // IDS_AUTOFILL_DIALOG_STATE
+ com.android.internal.R.string.autofill_zip_code, // IDS_AUTOFILL_DIALOG_ZIP_CODE
+ com.android.internal.R.string.autofill_county, // IDS_AUTOFILL_DIALOG_COUNTY
+ com.android.internal.R.string.autofill_island, // IDS_AUTOFILL_DIALOG_ISLAND
+ com.android.internal.R.string.autofill_district, // IDS_AUTOFILL_DIALOG_DISTRICT
+ com.android.internal.R.string.autofill_department, // IDS_AUTOFILL_DIALOG_DEPARTMENT
+ com.android.internal.R.string.autofill_prefecture, // IDS_AUTOFILL_DIALOG_PREFECTURE
+ com.android.internal.R.string.autofill_parish, // IDS_AUTOFILL_DIALOG_PARISH
+ com.android.internal.R.string.autofill_area, // IDS_AUTOFILL_DIALOG_AREA
+ com.android.internal.R.string.autofill_emirate // IDS_AUTOFILL_DIALOG_EMIRATE
};
private static Context mApplicationContext;
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 2145edd..c652e55 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -2240,6 +2240,10 @@ public final class WebViewCore {
}
private void setupViewport(boolean updateViewState) {
+ if (mWebView == null || mSettings == null) {
+ // We've been destroyed or are being destroyed, return early
+ return;
+ }
// set the viewport settings from WebKit
setViewportSettingsFromNative();
diff --git a/core/java/android/webkit/ZoomManager.java b/core/java/android/webkit/ZoomManager.java
index 252fc8f..49ea944 100644
--- a/core/java/android/webkit/ZoomManager.java
+++ b/core/java/android/webkit/ZoomManager.java
@@ -300,7 +300,7 @@ class ZoomManager {
}
public final float getDefaultScale() {
- return mDefaultScale;
+ return mInitialScale > 0 ? mInitialScale : mDefaultScale;
}
public final float getReadingLevelScale() {
@@ -344,6 +344,8 @@ class ZoomManager {
public final void setInitialScaleInPercent(int scaleInPercent) {
mInitialScale = scaleInPercent * 0.01f;
+ mActualScale = mInitialScale > 0 ? mInitialScale : mDefaultScale;
+ mInvActualScale = 1 / mActualScale;
}
public final float computeScaleWithLimits(float scale) {
@@ -1087,6 +1089,7 @@ class ZoomManager {
float scale;
if (mInitialScale > 0) {
scale = mInitialScale;
+ mTextWrapScale = scale;
} else if (viewState.mViewScale > 0) {
mTextWrapScale = viewState.mTextWrapScale;
scale = viewState.mViewScale;
@@ -1105,7 +1108,7 @@ class ZoomManager {
}
boolean reflowText = false;
if (!viewState.mIsRestored) {
- if (settings.getUseFixedViewport()) {
+ if (settings.getUseFixedViewport() && mInitialScale == 0) {
// Override the scale only in case of fixed viewport.
scale = Math.max(scale, overviewScale);
mTextWrapScale = Math.max(mTextWrapScale, overviewScale);
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 1e63e26..766b520 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -5571,8 +5571,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
@Override
- protected void resetLayoutDirectionResolution() {
- super.resetLayoutDirectionResolution();
+ protected void resetResolvedLayoutDirection() {
+ super.resetResolvedLayoutDirection();
if (mLayoutAlignment != null &&
(mTextAlign == TextAlign.VIEW_START ||
@@ -6212,6 +6212,15 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
int scrollx, scrolly;
+ // Convert to left, center, or right alignment.
+ if (a == Layout.Alignment.ALIGN_NORMAL) {
+ a = dir == Layout.DIR_LEFT_TO_RIGHT ? Layout.Alignment.ALIGN_LEFT :
+ Layout.Alignment.ALIGN_RIGHT;
+ } else if (a == Layout.Alignment.ALIGN_OPPOSITE){
+ a = dir == Layout.DIR_LEFT_TO_RIGHT ? Layout.Alignment.ALIGN_RIGHT :
+ Layout.Alignment.ALIGN_LEFT;
+ }
+
if (a == Layout.Alignment.ALIGN_CENTER) {
/*
* Keep centered if possible, or, if it is too wide to fit,
@@ -6230,28 +6239,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
scrollx = left;
}
}
- } else if (a == Layout.Alignment.ALIGN_NORMAL) {
- /*
- * Keep leading edge in view.
- */
-
- if (dir < 0) {
- int right = (int) FloatMath.ceil(mLayout.getLineRight(line));
- scrollx = right - hspace;
- } else {
- scrollx = (int) FloatMath.floor(mLayout.getLineLeft(line));
- }
- } else /* a == Layout.Alignment.ALIGN_OPPOSITE */ {
- /*
- * Keep trailing edge in view.
- */
-
- if (dir < 0) {
- scrollx = (int) FloatMath.floor(mLayout.getLineLeft(line));
- } else {
- int right = (int) FloatMath.ceil(mLayout.getLineRight(line));
- scrollx = right - hspace;
- }
+ } else if (a == Layout.Alignment.ALIGN_LEFT) {
+ scrollx = (int) FloatMath.floor(mLayout.getLineLeft(line));
+ } else { // a == Layout.Alignment.ALIGN_RIGHT
+ int right = (int) FloatMath.ceil(mLayout.getLineRight(line));
+ scrollx = right - hspace;
}
if (ht < vspace) {
@@ -6293,20 +6285,24 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
int grav;
switch (mLayout.getParagraphAlignment(line)) {
- case ALIGN_NORMAL:
+ case ALIGN_LEFT:
grav = 1;
break;
-
- case ALIGN_OPPOSITE:
+ case ALIGN_RIGHT:
grav = -1;
break;
-
+ case ALIGN_NORMAL:
+ grav = mLayout.getParagraphDirection(line);
+ break;
+ case ALIGN_OPPOSITE:
+ grav = -mLayout.getParagraphDirection(line);
+ break;
+ case ALIGN_CENTER:
default:
grav = 0;
+ break;
}
- grav *= mLayout.getParagraphDirection(line);
-
int hspace = mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight();
int vspace = mBottom - mTop - getExtendedPaddingTop() - getExtendedPaddingBottom();
diff --git a/core/java/com/android/internal/os/PkgUsageStats.java b/core/java/com/android/internal/os/PkgUsageStats.java
index 1ac191b..8c2c405 100755
--- a/core/java/com/android/internal/os/PkgUsageStats.java
+++ b/core/java/com/android/internal/os/PkgUsageStats.java
@@ -19,6 +19,9 @@ package com.android.internal.os;
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.HashMap;
+import java.util.Map;
+
/**
* implementation of PkgUsageStats associated with an
* application package.
@@ -28,6 +31,7 @@ public class PkgUsageStats implements Parcelable {
public String packageName;
public int launchCount;
public long usageTime;
+ public Map<String, Long> componentResumeTimes;
public static final Parcelable.Creator<PkgUsageStats> CREATOR
= new Parcelable.Creator<PkgUsageStats>() {
@@ -46,31 +50,45 @@ public class PkgUsageStats implements Parcelable {
+ " " + packageName + "}";
}
- public PkgUsageStats(String pkgName, int count, long time) {
+ public PkgUsageStats(String pkgName, int count, long time, Map<String, Long> lastResumeTimes) {
packageName = pkgName;
launchCount = count;
usageTime = time;
+ componentResumeTimes = new HashMap<String, Long>(lastResumeTimes);
}
public PkgUsageStats(Parcel source) {
packageName = source.readString();
launchCount = source.readInt();
usageTime = source.readLong();
+ final int N = source.readInt();
+ componentResumeTimes = new HashMap<String, Long>(N);
+ for (int i = 0; i < N; i++) {
+ String component = source.readString();
+ long lastResumeTime = source.readLong();
+ componentResumeTimes.put(component, lastResumeTime);
+ }
}
public PkgUsageStats(PkgUsageStats pStats) {
packageName = pStats.packageName;
launchCount = pStats.launchCount;
usageTime = pStats.usageTime;
+ componentResumeTimes = new HashMap<String, Long>(pStats.componentResumeTimes);
}
public int describeContents() {
return 0;
}
- public void writeToParcel(Parcel dest, int parcelableFlags){
+ public void writeToParcel(Parcel dest, int parcelableFlags) {
dest.writeString(packageName);
dest.writeInt(launchCount);
dest.writeLong(usageTime);
+ dest.writeInt(componentResumeTimes.size());
+ for (Map.Entry<String, Long> ent : componentResumeTimes.entrySet()) {
+ dest.writeString(ent.getKey());
+ dest.writeLong(ent.getValue());
+ }
}
}
diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java
index f13e770..53516c0 100644
--- a/core/java/com/android/internal/os/RuntimeInit.java
+++ b/core/java/com/android/internal/os/RuntimeInit.java
@@ -252,9 +252,10 @@ public class RuntimeInit {
* <li> <code> [--] &lt;start class name&gt; &lt;args&gt;
* </ul>
*
+ * @param targetSdkVersion target SDK version
* @param argv arg strings
*/
- public static final void zygoteInit(String[] argv)
+ public static final void zygoteInit(int targetSdkVersion, String[] argv)
throws ZygoteInit.MethodAndArgsCaller {
if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from zygote");
@@ -263,7 +264,7 @@ public class RuntimeInit {
commonInit();
zygoteInitNative();
- applicationInit(argv);
+ applicationInit(targetSdkVersion, argv);
}
/**
@@ -274,20 +275,22 @@ public class RuntimeInit {
* which calls {@link WrapperInit#main} which then calls this method.
* So we don't need to call commonInit() here.
*
+ * @param targetSdkVersion target SDK version
* @param argv arg strings
*/
- public static void wrapperInit(String[] argv)
+ public static void wrapperInit(int targetSdkVersion, String[] argv)
throws ZygoteInit.MethodAndArgsCaller {
if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from wrapper");
- applicationInit(argv);
+ applicationInit(targetSdkVersion, argv);
}
- private static void applicationInit(String[] argv)
+ private static void applicationInit(int targetSdkVersion, String[] argv)
throws ZygoteInit.MethodAndArgsCaller {
// We want to be fairly aggressive about heap utilization, to avoid
// holding on to a lot of memory that isn't needed.
VMRuntime.getRuntime().setTargetHeapUtilization(0.75f);
+ VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion);
final Arguments args;
try {
diff --git a/core/java/com/android/internal/os/WrapperInit.java b/core/java/com/android/internal/os/WrapperInit.java
index 860a08c..c6b3e7c 100644
--- a/core/java/com/android/internal/os/WrapperInit.java
+++ b/core/java/com/android/internal/os/WrapperInit.java
@@ -47,16 +47,22 @@ public class WrapperInit {
* wrapper process instead of by forking Zygote.
*
* The first argument specifies the file descriptor for a pipe that should receive
- * the pid of this process, or 0 if none. The remaining arguments are passed to
- * the runtime.
+ * the pid of this process, or 0 if none.
+ *
+ * The second argument is the target SDK version for the app.
+ *
+ * The remaining arguments are passed to the runtime.
*
* @param args The command-line arguments.
*/
public static void main(String[] args) {
try {
+ // Parse our mandatory arguments.
+ int fdNum = Integer.parseInt(args[0], 10);
+ int targetSdkVersion = Integer.parseInt(args[1], 10);
+
// Tell the Zygote what our actual PID is (since it only knows about the
// wrapper that it directly forked).
- int fdNum = Integer.parseInt(args[0], 10);
if (fdNum != 0) {
try {
FileDescriptor fd = ZygoteInit.createFileDescriptor(fdNum);
@@ -73,9 +79,9 @@ public class WrapperInit {
ZygoteInit.preload();
// Launch the application.
- String[] runtimeArgs = new String[args.length - 1];
- System.arraycopy(args, 1, runtimeArgs, 0, runtimeArgs.length);
- RuntimeInit.wrapperInit(runtimeArgs);
+ String[] runtimeArgs = new String[args.length - 2];
+ System.arraycopy(args, 2, runtimeArgs, 0, runtimeArgs.length);
+ RuntimeInit.wrapperInit(targetSdkVersion, runtimeArgs);
} catch (ZygoteInit.MethodAndArgsCaller caller) {
caller.run();
}
@@ -87,11 +93,12 @@ public class WrapperInit {
*
* @param invokeWith The wrapper command.
* @param niceName The nice name for the application, or null if none.
+ * @param targetSdkVersion The target SDK version for the app.
* @param pipeFd The pipe to which the application's pid should be written, or null if none.
* @param args Arguments for {@link RuntimeInit.main}.
*/
public static void execApplication(String invokeWith, String niceName,
- FileDescriptor pipeFd, String[] args) {
+ int targetSdkVersion, FileDescriptor pipeFd, String[] args) {
StringBuilder command = new StringBuilder(invokeWith);
command.append(" /system/bin/app_process /system/bin --application");
if (niceName != null) {
@@ -99,6 +106,8 @@ public class WrapperInit {
}
command.append(" com.android.internal.os.WrapperInit ");
command.append(pipeFd != null ? pipeFd.getInt$() : 0);
+ command.append(' ');
+ command.append(targetSdkVersion);
Zygote.appendQuotedShellArgs(command, args);
Zygote.execShell(command.toString());
}
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 7cb002c..9af7e96 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -18,6 +18,7 @@ package com.android.internal.os;
import android.net.Credentials;
import android.net.LocalSocket;
+import android.os.Build;
import android.os.Process;
import android.os.SystemProperties;
import android.util.Log;
@@ -333,6 +334,10 @@ class ZygoteConnection {
*/
int debugFlags;
+ /** from --target-sdk-version. */
+ int targetSdkVersion;
+ boolean targetSdkVersionSpecified;
+
/** from --classpath */
String classpath;
@@ -402,6 +407,14 @@ class ZygoteConnection {
gidSpecified = true;
gid = Integer.parseInt(
arg.substring(arg.indexOf('=') + 1));
+ } else if (arg.startsWith("--target-sdk-version=")) {
+ if (targetSdkVersionSpecified) {
+ throw new IllegalArgumentException(
+ "Duplicate target-sdk-version specified");
+ }
+ targetSdkVersionSpecified = true;
+ targetSdkVersion = Integer.parseInt(
+ arg.substring(arg.indexOf('=') + 1));
} else if (arg.equals("--enable-debugger")) {
debugFlags |= Zygote.DEBUG_ENABLE_DEBUGGER;
} else if (arg.equals("--enable-safemode")) {
@@ -821,9 +834,11 @@ class ZygoteConnection {
if (parsedArgs.runtimeInit) {
if (parsedArgs.invokeWith != null) {
WrapperInit.execApplication(parsedArgs.invokeWith,
- parsedArgs.niceName, pipeFd, parsedArgs.remainingArgs);
+ parsedArgs.niceName, parsedArgs.targetSdkVersion,
+ pipeFd, parsedArgs.remainingArgs);
} else {
- RuntimeInit.zygoteInit(parsedArgs.remainingArgs);
+ RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion,
+ parsedArgs.remainingArgs);
}
} else {
String className;
@@ -885,6 +900,7 @@ class ZygoteConnection {
}
}
+ boolean usingWrapper = false;
if (pipeFd != null && pid > 0) {
DataInputStream is = new DataInputStream(new FileInputStream(pipeFd));
int innerPid = -1;
@@ -909,6 +925,7 @@ class ZygoteConnection {
if (parentPid > 0) {
Log.i(TAG, "Wrapped process has pid " + innerPid);
pid = innerPid;
+ usingWrapper = true;
} else {
Log.w(TAG, "Wrapped process reported a pid that is not a child of "
+ "the process that we forked: childPid=" + pid
@@ -919,6 +936,7 @@ class ZygoteConnection {
try {
mSocketOutStream.writeInt(pid);
+ mSocketOutStream.writeBoolean(usingWrapper);
} catch (IOException ex) {
Log.e(TAG, "Error reading from command socket", ex);
return true;
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index b4a7e52..6ec186d 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -48,7 +48,7 @@ import java.util.ArrayList;
* Startup class for the zygote process.
*
* Pre-initializes some classes, and then waits for commands on a UNIX domain
- * socket. Based on these commands, forks of child processes that inherit
+ * socket. Based on these commands, forks off child processes that inherit
* the initial state of the VM.
*
* Please see {@link ZygoteConnection.Arguments} for documentation on the
@@ -453,12 +453,13 @@ public class ZygoteInit {
if (parsedArgs.invokeWith != null) {
WrapperInit.execApplication(parsedArgs.invokeWith,
- parsedArgs.niceName, null, parsedArgs.remainingArgs);
+ parsedArgs.niceName, parsedArgs.targetSdkVersion,
+ null, parsedArgs.remainingArgs);
} else {
/*
* Pass the remaining arguments to SystemServer.
*/
- RuntimeInit.zygoteInit(parsedArgs.remainingArgs);
+ RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs);
}
/* should never reach here */
@@ -491,7 +492,9 @@ public class ZygoteInit {
/* Request to fork the system server process */
pid = Zygote.forkSystemServer(
parsedArgs.uid, parsedArgs.gid,
- parsedArgs.gids, parsedArgs.debugFlags, null,
+ parsedArgs.gids,
+ parsedArgs.debugFlags,
+ null,
parsedArgs.permittedCapabilities,
parsedArgs.effectiveCapabilities);
} catch (IllegalArgumentException ex) {
diff --git a/core/java/com/android/internal/util/Protocol.java b/core/java/com/android/internal/util/Protocol.java
index 2e7ec58..b754d94 100644
--- a/core/java/com/android/internal/util/Protocol.java
+++ b/core/java/com/android/internal/util/Protocol.java
@@ -26,6 +26,9 @@ package com.android.internal.util;
* codes with Message.what starting at Protocol.WIFI + 1 and less than or equal to Protocol.WIFI +
* Protocol.MAX_MESSAGE
*
+ * NOTE: After a value is created and source released a value shouldn't be changed to
+ * maintain backwards compatibility.
+ *
* {@hide}
*/
public class Protocol {
@@ -40,7 +43,7 @@ public class Protocol {
public static final int BASE_DHCP = 0x00030000;
public static final int BASE_DATA_CONNECTION = 0x00040000;
public static final int BASE_DATA_CONNECTION_AC = 0x00041000;
- public static final int BASE_DATA_CONNECTION_TRACKER = 0x00050000;
+ public static final int BASE_DATA_CONNECTION_TRACKER = 0x00042000;
//TODO: define all used protocols
}
diff --git a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java
index b86eb13..9c06d69 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java
@@ -20,7 +20,8 @@ import com.android.internal.view.menu.ActionMenuView.ActionMenuChildView;
import android.content.Context;
import android.content.res.Resources;
-import android.util.Log;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.util.SparseBooleanArray;
import android.view.MenuItem;
import android.view.SoundEffectConstants;
@@ -60,6 +61,9 @@ public class ActionMenuPresenter extends BaseMenuPresenter {
private OpenOverflowRunnable mPostedOpenRunnable;
+ final PopupPresenterCallback mPopupPresenterCallback = new PopupPresenterCallback();
+ int mOpenSubMenuId;
+
public ActionMenuPresenter() {
super(com.android.internal.R.layout.action_menu_layout,
com.android.internal.R.layout.action_menu_item_layout);
@@ -196,8 +200,12 @@ public class ActionMenuPresenter extends BaseMenuPresenter {
topSubMenu = (SubMenuBuilder) topSubMenu.getParentMenu();
}
View anchor = findViewForItem(topSubMenu.getItem());
- if (anchor == null) return false;
+ if (anchor == null) {
+ if (mOverflowButton == null) return false;
+ anchor = mOverflowButton;
+ }
+ mOpenSubMenuId = subMenu.getItem().getItemId();
mActionButtonPopup = new ActionButtonSubmenu(mContext, subMenu);
mActionButtonPopup.setAnchorView(anchor);
mActionButtonPopup.show();
@@ -426,6 +434,57 @@ public class ActionMenuPresenter extends BaseMenuPresenter {
super.onCloseMenu(menu, allMenusAreClosing);
}
+ @Override
+ public Parcelable onSaveInstanceState() {
+ SavedState state = new SavedState();
+ state.openSubMenuId = mOpenSubMenuId;
+ return state;
+ }
+
+ @Override
+ public void onRestoreInstanceState(Parcelable state) {
+ SavedState saved = (SavedState) state;
+ if (saved.openSubMenuId > 0) {
+ MenuItem item = mMenu.findItem(saved.openSubMenuId);
+ if (item != null) {
+ SubMenuBuilder subMenu = (SubMenuBuilder) item.getSubMenu();
+ onSubMenuSelected(subMenu);
+ }
+ }
+ }
+
+ private static class SavedState implements Parcelable {
+ public int openSubMenuId;
+
+ SavedState() {
+ }
+
+ SavedState(Parcel in) {
+ openSubMenuId = in.readInt();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(openSubMenuId);
+ }
+
+ public static final Parcelable.Creator<SavedState> CREATOR
+ = new Parcelable.Creator<SavedState>() {
+ public SavedState createFromParcel(Parcel in) {
+ return new SavedState(in);
+ }
+
+ public SavedState[] newArray(int size) {
+ return new SavedState[size];
+ }
+ };
+ }
+
private class OverflowMenuButton extends ImageButton implements ActionMenuChildView {
public OverflowMenuButton(Context context) {
super(context, null, com.android.internal.R.attr.actionOverflowButtonStyle);
@@ -460,6 +519,7 @@ public class ActionMenuPresenter extends BaseMenuPresenter {
public OverflowPopup(Context context, MenuBuilder menu, View anchorView,
boolean overflowOnly) {
super(context, menu, anchorView, overflowOnly);
+ setCallback(mPopupPresenterCallback);
}
@Override
@@ -482,6 +542,8 @@ public class ActionMenuPresenter extends BaseMenuPresenter {
// Give a reasonable anchor to nested submenus.
setAnchorView(mOverflowButton == null ? (View) mMenuView : mOverflowButton);
}
+
+ setCallback(mPopupPresenterCallback);
}
@Override
@@ -489,6 +551,20 @@ public class ActionMenuPresenter extends BaseMenuPresenter {
super.onDismiss();
mSubMenu.close();
mActionButtonPopup = null;
+ mOpenSubMenuId = 0;
+ }
+ }
+
+ private class PopupPresenterCallback implements MenuPresenter.Callback {
+
+ @Override
+ public boolean onOpenSubMenu(MenuBuilder subMenu) {
+ mOpenSubMenuId = ((SubMenuBuilder) subMenu).getItem().getItemId();
+ return false;
+ }
+
+ @Override
+ public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
}
}
diff --git a/core/java/com/android/internal/view/menu/BaseMenuPresenter.java b/core/java/com/android/internal/view/menu/BaseMenuPresenter.java
index ddbb08c..ed9d34a 100644
--- a/core/java/com/android/internal/view/menu/BaseMenuPresenter.java
+++ b/core/java/com/android/internal/view/menu/BaseMenuPresenter.java
@@ -39,6 +39,8 @@ public abstract class BaseMenuPresenter implements MenuPresenter {
protected MenuView mMenuView;
+ private int mId;
+
/**
* Construct a new BaseMenuPresenter.
*
@@ -200,4 +202,12 @@ public abstract class BaseMenuPresenter implements MenuPresenter {
public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item) {
return false;
}
+
+ public int getId() {
+ return mId;
+ }
+
+ public void setId(int id) {
+ mId = id;
+ }
}
diff --git a/core/java/com/android/internal/view/menu/IconMenuPresenter.java b/core/java/com/android/internal/view/menu/IconMenuPresenter.java
index f717904..56128d4 100644
--- a/core/java/com/android/internal/view/menu/IconMenuPresenter.java
+++ b/core/java/com/android/internal/view/menu/IconMenuPresenter.java
@@ -23,6 +23,7 @@ import android.os.Parcelable;
import android.util.SparseArray;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
+import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
@@ -35,7 +36,12 @@ public class IconMenuPresenter extends BaseMenuPresenter {
private IconMenuItemView mMoreView;
private int mMaxItems = -1;
+ int mOpenSubMenuId;
+ SubMenuPresenterCallback mSubMenuPresenterCallback = new SubMenuPresenterCallback();
+ MenuDialogHelper mOpenSubMenu;
+
private static final String VIEWS_TAG = "android:menu:icon";
+ private static final String OPEN_SUBMENU_KEY = "android:menu:icon:submenu";
public IconMenuPresenter() {
super(com.android.internal.R.layout.icon_menu_layout,
@@ -86,7 +92,11 @@ public class IconMenuPresenter extends BaseMenuPresenter {
if (!subMenu.hasVisibleItems()) return false;
// The window manager will give us a token.
- new MenuDialogHelper(subMenu).show(null);
+ MenuDialogHelper helper = new MenuDialogHelper(subMenu);
+ helper.setPresenterCallback(mSubMenuPresenterCallback);
+ helper.show(null);
+ mOpenSubMenu = helper;
+ mOpenSubMenuId = subMenu.getItem().getItemId();
super.onSubMenuSelected(subMenu);
return true;
}
@@ -137,5 +147,47 @@ public class IconMenuPresenter extends BaseMenuPresenter {
if (viewStates != null) {
((View) mMenuView).restoreHierarchyState(viewStates);
}
+ int subMenuId = inState.getInt(OPEN_SUBMENU_KEY, 0);
+ if (subMenuId > 0 && mMenu != null) {
+ MenuItem item = mMenu.findItem(subMenuId);
+ if (item != null) {
+ onSubMenuSelected((SubMenuBuilder) item.getSubMenu());
+ }
+ }
+ }
+
+ @Override
+ public Parcelable onSaveInstanceState() {
+ if (mMenuView == null) {
+ return null;
+ }
+
+ Bundle state = new Bundle();
+ saveHierarchyState(state);
+ if (mOpenSubMenuId > 0) {
+ state.putInt(OPEN_SUBMENU_KEY, mOpenSubMenuId);
+ }
+ return state;
+ }
+
+ @Override
+ public void onRestoreInstanceState(Parcelable state) {
+ restoreHierarchyState((Bundle) state);
+ }
+
+ class SubMenuPresenterCallback implements MenuPresenter.Callback {
+ @Override
+ public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
+ mOpenSubMenuId = 0;
+ mOpenSubMenu.dismiss();
+ mOpenSubMenu = null;
+ }
+
+ @Override
+ public boolean onOpenSubMenu(MenuBuilder subMenu) {
+ mOpenSubMenuId = ((SubMenuBuilder) subMenu).getItem().getItemId();
+ return false;
+ }
+
}
}
diff --git a/core/java/com/android/internal/view/menu/ListMenuPresenter.java b/core/java/com/android/internal/view/menu/ListMenuPresenter.java
index 27e4191..146c7ac 100644
--- a/core/java/com/android/internal/view/menu/ListMenuPresenter.java
+++ b/core/java/com/android/internal/view/menu/ListMenuPresenter.java
@@ -47,6 +47,8 @@ public class ListMenuPresenter implements MenuPresenter, AdapterView.OnItemClick
private Callback mCallback;
private MenuAdapter mAdapter;
+ private int mId;
+
public static final String VIEWS_TAG = "android:menu:list";
/**
@@ -182,6 +184,31 @@ public class ListMenuPresenter implements MenuPresenter, AdapterView.OnItemClick
}
}
+ public void setId(int id) {
+ mId = id;
+ }
+
+ @Override
+ public int getId() {
+ return mId;
+ }
+
+ @Override
+ public Parcelable onSaveInstanceState() {
+ if (mMenuView == null) {
+ return null;
+ }
+
+ Bundle state = new Bundle();
+ saveHierarchyState(state);
+ return state;
+ }
+
+ @Override
+ public void onRestoreInstanceState(Parcelable state) {
+ restoreHierarchyState((Bundle) state);
+ }
+
private class MenuAdapter extends BaseAdapter {
public int getCount() {
ArrayList<MenuItemImpl> items = mMenu.getNonActionItems();
diff --git a/core/java/com/android/internal/view/menu/MenuBuilder.java b/core/java/com/android/internal/view/menu/MenuBuilder.java
index fdfa954..a4edbc5 100644
--- a/core/java/com/android/internal/view/menu/MenuBuilder.java
+++ b/core/java/com/android/internal/view/menu/MenuBuilder.java
@@ -25,8 +25,8 @@ import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
+import android.os.Bundle;
import android.os.Parcelable;
-import android.util.Log;
import android.util.SparseArray;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.KeyCharacterMap;
@@ -38,7 +38,6 @@ import android.view.View;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
-import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -49,6 +48,8 @@ import java.util.concurrent.CopyOnWriteArrayList;
public class MenuBuilder implements Menu {
private static final String LOGTAG = "MenuBuilder";
+ private static final String PRESENTER_KEY = "android:menu:presenters";
+
private static final int[] sCategoryToOrder = new int[] {
1, /* No category */
4, /* CONTAINER */
@@ -254,6 +255,58 @@ public class MenuBuilder implements Menu {
return result;
}
+ private void dispatchSaveInstanceState(Bundle outState) {
+ if (mPresenters.isEmpty()) return;
+
+ SparseArray<Parcelable> presenterStates = new SparseArray<Parcelable>();
+
+ for (WeakReference<MenuPresenter> ref : mPresenters) {
+ final MenuPresenter presenter = ref.get();
+ if (presenter == null) {
+ mPresenters.remove(ref);
+ } else {
+ final int id = presenter.getId();
+ if (id > 0) {
+ final Parcelable state = presenter.onSaveInstanceState();
+ if (state != null) {
+ presenterStates.put(id, state);
+ }
+ }
+ }
+ }
+
+ outState.putSparseParcelableArray(PRESENTER_KEY, presenterStates);
+ }
+
+ private void dispatchRestoreInstanceState(Bundle state) {
+ SparseArray<Parcelable> presenterStates = state.getSparseParcelableArray(PRESENTER_KEY);
+
+ if (presenterStates == null || mPresenters.isEmpty()) return;
+
+ for (WeakReference<MenuPresenter> ref : mPresenters) {
+ final MenuPresenter presenter = ref.get();
+ if (presenter == null) {
+ mPresenters.remove(ref);
+ } else {
+ final int id = presenter.getId();
+ if (id > 0) {
+ Parcelable parcel = presenterStates.get(id);
+ if (parcel != null) {
+ presenter.onRestoreInstanceState(parcel);
+ }
+ }
+ }
+ }
+ }
+
+ public void savePresenterStates(Bundle outState) {
+ dispatchSaveInstanceState(outState);
+ }
+
+ public void restorePresenterStates(Bundle state) {
+ dispatchRestoreInstanceState(state);
+ }
+
public void setCallback(Callback cb) {
mCallback = cb;
}
diff --git a/core/java/com/android/internal/view/menu/MenuPopupHelper.java b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
index cffbb4e..4ecc828 100644
--- a/core/java/com/android/internal/view/menu/MenuPopupHelper.java
+++ b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
@@ -18,6 +18,7 @@ package com.android.internal.view.menu;
import android.content.Context;
import android.content.res.Resources;
+import android.os.Parcelable;
import android.util.DisplayMetrics;
import android.view.KeyEvent;
import android.view.LayoutInflater;
@@ -296,4 +297,18 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On
return convertView;
}
}
+
+ @Override
+ public int getId() {
+ return 0;
+ }
+
+ @Override
+ public Parcelable onSaveInstanceState() {
+ return null;
+ }
+
+ @Override
+ public void onRestoreInstanceState(Parcelable state) {
+ }
}
diff --git a/core/java/com/android/internal/view/menu/MenuPresenter.java b/core/java/com/android/internal/view/menu/MenuPresenter.java
index bd66448..d913a39 100644
--- a/core/java/com/android/internal/view/menu/MenuPresenter.java
+++ b/core/java/com/android/internal/view/menu/MenuPresenter.java
@@ -17,6 +17,7 @@
package com.android.internal.view.menu;
import android.content.Context;
+import android.os.Parcelable;
import android.view.Menu;
import android.view.ViewGroup;
@@ -125,4 +126,24 @@ public interface MenuPresenter {
* @return true if this presenter collapsed the action view, false otherwise.
*/
public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item);
+
+ /**
+ * Returns an ID for determining how to save/restore instance state.
+ * @return a valid ID value.
+ */
+ public int getId();
+
+ /**
+ * Returns a Parcelable describing the current state of the presenter.
+ * It will be passed to the {@link #onRestoreInstanceState(Parcelable)}
+ * method of the presenter sharing the same ID later.
+ * @return The saved instance state
+ */
+ public Parcelable onSaveInstanceState();
+
+ /**
+ * Supplies the previously saved instance state to be restored.
+ * @param state The previously saved instance state
+ */
+ public void onRestoreInstanceState(Parcelable state);
}
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
index 09bc1fc..595753a 100644
--- a/core/java/com/android/internal/widget/ActionBarView.java
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -337,6 +337,7 @@ public class ActionBarView extends AbsActionBarView {
if (mActionMenuPresenter == null) {
mActionMenuPresenter = new ActionMenuPresenter();
mActionMenuPresenter.setCallback(cb);
+ mActionMenuPresenter.setId(com.android.internal.R.id.action_menu_presenter);
mExpandedMenuPresenter = new ExpandedActionViewMenuPresenter();
}
@@ -1301,5 +1302,19 @@ public class ActionBarView extends AbsActionBarView {
item.setActionViewExpanded(false);
return true;
}
+
+ @Override
+ public int getId() {
+ return 0;
+ }
+
+ @Override
+ public Parcelable onSaveInstanceState() {
+ return null;
+ }
+
+ @Override
+ public void onRestoreInstanceState(Parcelable state) {
+ }
}
}
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index ddae505..d9bd50e 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -26,7 +26,7 @@
extern "C" {
int ifc_enable(const char *ifname);
int ifc_disable(const char *ifname);
-int ifc_reset_connections(const char *ifname);
+int ifc_reset_connections(const char *ifname, int reset_mask);
int dhcp_do_request(const char *ifname,
const char *ipaddr,
@@ -90,12 +90,17 @@ static jint android_net_utils_disableInterface(JNIEnv* env, jobject clazz, jstri
return (jint)result;
}
-static jint android_net_utils_resetConnections(JNIEnv* env, jobject clazz, jstring ifname)
+static jint android_net_utils_resetConnections(JNIEnv* env, jobject clazz,
+ jstring ifname, jint mask)
{
int result;
const char *nameStr = env->GetStringUTFChars(ifname, NULL);
- result = ::ifc_reset_connections(nameStr);
+
+ LOGD("android_net_utils_resetConnections in env=%p clazz=%p iface=%s mask=0x%x\n",
+ env, clazz, nameStr, mask);
+
+ result = ::ifc_reset_connections(nameStr, mask);
env->ReleaseStringUTFChars(ifname, nameStr);
return (jint)result;
}
@@ -206,7 +211,7 @@ static JNINativeMethod gNetworkUtilMethods[] = {
{ "enableInterface", "(Ljava/lang/String;)I", (void *)android_net_utils_enableInterface },
{ "disableInterface", "(Ljava/lang/String;)I", (void *)android_net_utils_disableInterface },
- { "resetConnections", "(Ljava/lang/String;)I", (void *)android_net_utils_resetConnections },
+ { "resetConnections", "(Ljava/lang/String;I)I", (void *)android_net_utils_resetConnections },
{ "runDhcp", "(Ljava/lang/String;Landroid/net/DhcpInfoInternal;)Z", (void *)android_net_utils_runDhcp },
{ "runDhcpRenew", "(Ljava/lang/String;Landroid/net/DhcpInfoInternal;)Z", (void *)android_net_utils_runDhcpRenew },
{ "stopDhcp", "(Ljava/lang/String;)Z", (void *)android_net_utils_stopDhcp },
diff --git a/core/jni/android_server_BluetoothService.cpp b/core/jni/android_server_BluetoothService.cpp
index 1166ae4..86e7cc0 100644
--- a/core/jni/android_server_BluetoothService.cpp
+++ b/core/jni/android_server_BluetoothService.cpp
@@ -1396,7 +1396,6 @@ static jstring registerSinkHealthApplicationNative(JNIEnv *env, jobject object,
LOG_AND_FREE_DBUS_ERROR(&err);
}
} else {
- LOGE("--_Call made getting the patch...");
if (!dbus_message_get_args(reply, &err,
DBUS_TYPE_OBJECT_PATH, &c_path,
DBUS_TYPE_INVALID)) {
@@ -1405,7 +1404,6 @@ static jstring registerSinkHealthApplicationNative(JNIEnv *env, jobject object,
}
} else {
path = env->NewStringUTF(c_path);
- LOGE("----Path is %s", c_path);
}
dbus_message_unref(reply);
}
@@ -1459,7 +1457,6 @@ static jboolean createChannelNative(JNIEnv *env, jobject object,
const char *c_device_path = env->GetStringUTFChars(devicePath, NULL);
const char *c_app_path = env->GetStringUTFChars(appPath, NULL);
const char *c_config = env->GetStringUTFChars(config, NULL);
- LOGE("Params...%s, %s, %s \n", c_device_path, c_app_path, c_config);
DBusMessage *reply = dbus_func_args(env, nat->conn,
c_device_path,
@@ -1531,7 +1528,6 @@ static jstring getMainChannelNative(JNIEnv *env, jobject object, jstring deviceP
DBusError err;
dbus_error_init(&err);
- LOGE("---Args %s", c_device_path);
DBusMessage *reply = dbus_func_args(env, nat->conn,
c_device_path,
DBUS_HEALTH_DEVICE_IFACE, "GetProperties",
@@ -1566,8 +1562,6 @@ static jstring getChannelApplicationNative(JNIEnv *env, jobject object, jstring
DBusError err;
dbus_error_init(&err);
- LOGE("---Args %s", c_channel_path);
-
DBusMessage *reply = dbus_func_args(env, nat->conn,
c_channel_path,
DBUS_HEALTH_CHANNEL_IFACE, "GetProperties",
@@ -1596,7 +1590,6 @@ static jstring getChannelApplicationNative(JNIEnv *env, jobject object, jstring
if (!strcmp(c_name, "Application")) {
path = (jstring) env->GetObjectArrayElement(str_array, i+1);
- LOGE("----Path is %s", env->GetStringUTFChars(path, NULL));
env->ReleaseStringUTFChars(name, c_name);
return path;
}
@@ -1655,13 +1648,11 @@ static jobject getChannelFdNative(JNIEnv *env, jobject object, jstring channelPa
fd = dbus_returns_unixfd(env, reply);
if (fd == -1) return NULL;
- LOGE("---got fd %d\n", fd);
// Create FileDescriptor object
jobject fileDesc = jniCreateFileDescriptor(env, fd);
if (fileDesc == NULL) {
// FileDescriptor constructor has thrown an exception
releaseChannelFdNative(env, object, channelPath);
- LOGE("---File Desc is null");
return NULL;
}
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index 0960b25..d1ba2d1 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -318,17 +318,15 @@ jboolean android_os_Process_setOomAdj(JNIEnv* env, jobject clazz,
jint pid, jint adj)
{
#ifdef HAVE_OOM_ADJ
- if (ProcessState::self()->supportsProcesses()) {
- char text[64];
- sprintf(text, "/proc/%d/oom_adj", pid);
- int fd = open(text, O_WRONLY);
- if (fd >= 0) {
- sprintf(text, "%d", adj);
- write(fd, text, strlen(text));
- close(fd);
- }
- return true;
+ char text[64];
+ sprintf(text, "/proc/%d/oom_adj", pid);
+ int fd = open(text, O_WRONLY);
+ if (fd >= 0) {
+ sprintf(text, "%d", adj);
+ write(fd, text, strlen(text));
+ close(fd);
}
+ return true;
#endif
return false;
}
@@ -370,11 +368,6 @@ jint android_os_Process_setGid(JNIEnv* env, jobject clazz, jint uid)
#endif
}
-jboolean android_os_Process_supportsProcesses(JNIEnv* env, jobject clazz)
-{
- return ProcessState::self()->supportsProcesses();
-}
-
static int pid_compare(const void* v1, const void* v2)
{
//LOGI("Compare %d vs %d\n", *((const jint*)v1), *((const jint*)v2));
@@ -878,7 +871,6 @@ static const JNINativeMethod methods[] = {
{"setGid", "(I)I", (void*)android_os_Process_setGid},
{"sendSignal", "(II)V", (void*)android_os_Process_sendSignal},
{"sendSignalQuiet", "(II)V", (void*)android_os_Process_sendSignalQuiet},
- {"supportsProcesses", "()Z", (void*)android_os_Process_supportsProcesses},
{"getFreeMemory", "()J", (void*)android_os_Process_getFreeMemory},
{"readProcLines", "(Ljava/lang/String;[Ljava/lang/String;[J)V", (void*)android_os_Process_readProcLines},
{"getPids", "(Ljava/lang/String;[I)[I", (void*)android_os_Process_getPids},
diff --git a/core/res/res/drawable-hdpi/dialog_bottom_holo_dark.9.png b/core/res/res/drawable-hdpi/dialog_bottom_holo_dark.9.png
index 5225a81..acbbb38 100644
--- a/core/res/res/drawable-hdpi/dialog_bottom_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/dialog_bottom_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dialog_bottom_holo_light.9.png b/core/res/res/drawable-hdpi/dialog_bottom_holo_light.9.png
index 2e7e973..6009528 100644
--- a/core/res/res/drawable-hdpi/dialog_bottom_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/dialog_bottom_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dialog_full_holo_dark.9.png b/core/res/res/drawable-hdpi/dialog_full_holo_dark.9.png
index 4591627..30727d7 100644
--- a/core/res/res/drawable-hdpi/dialog_full_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/dialog_full_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dialog_full_holo_light.9.png b/core/res/res/drawable-hdpi/dialog_full_holo_light.9.png
index 9cf1826..7cea5e1 100644
--- a/core/res/res/drawable-hdpi/dialog_full_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/dialog_full_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dialog_middle_holo_dark.9.png b/core/res/res/drawable-hdpi/dialog_middle_holo_dark.9.png
index a47ef40..ba0d612 100644
--- a/core/res/res/drawable-hdpi/dialog_middle_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/dialog_middle_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dialog_middle_holo_light.9.png b/core/res/res/drawable-hdpi/dialog_middle_holo_light.9.png
index 9b50c73..e8646b9 100644
--- a/core/res/res/drawable-hdpi/dialog_middle_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/dialog_middle_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dialog_top_holo_dark.9.png b/core/res/res/drawable-hdpi/dialog_top_holo_dark.9.png
index a0d36de..14cb4c9 100644
--- a/core/res/res/drawable-hdpi/dialog_top_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/dialog_top_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dialog_top_holo_light.9.png b/core/res/res/drawable-hdpi/dialog_top_holo_light.9.png
index 805b956..80fd218 100644
--- a/core/res/res/drawable-hdpi/dialog_top_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/dialog_top_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_btn_back.png b/core/res/res/drawable-hdpi/ic_btn_back.png
deleted file mode 100644
index f8b3285..0000000
--- a/core/res/res/drawable-hdpi/ic_btn_back.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_btn_next.png b/core/res/res/drawable-hdpi/ic_btn_next.png
deleted file mode 100644
index b2c6e1b..0000000
--- a/core/res/res/drawable-hdpi/ic_btn_next.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/menu_dropdown_panel_holo_dark.9.png b/core/res/res/drawable-hdpi/menu_dropdown_panel_holo_dark.9.png
index b0fba52..1014d8a 100644
--- a/core/res/res/drawable-hdpi/menu_dropdown_panel_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/menu_dropdown_panel_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/menu_dropdown_panel_holo_light.9.png b/core/res/res/drawable-hdpi/menu_dropdown_panel_holo_light.9.png
index 3ef8935..18cd171 100644
--- a/core/res/res/drawable-hdpi/menu_dropdown_panel_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/menu_dropdown_panel_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/toast_frame.9.png b/core/res/res/drawable-hdpi/toast_frame.9.png
index 9769bbb..ad2cb5a 100644
--- a/core/res/res/drawable-hdpi/toast_frame.9.png
+++ b/core/res/res/drawable-hdpi/toast_frame.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/toast_frame_holo.9.png b/core/res/res/drawable-hdpi/toast_frame_holo.9.png
index 9769bbb..ad2cb5a 100644
--- a/core/res/res/drawable-hdpi/toast_frame_holo.9.png
+++ b/core/res/res/drawable-hdpi/toast_frame_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dialog_bottom_holo_dark.9.png b/core/res/res/drawable-mdpi/dialog_bottom_holo_dark.9.png
index a0bd4e3..4836da1 100644
--- a/core/res/res/drawable-mdpi/dialog_bottom_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/dialog_bottom_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dialog_bottom_holo_light.9.png b/core/res/res/drawable-mdpi/dialog_bottom_holo_light.9.png
index 12abcd2..c299931 100644
--- a/core/res/res/drawable-mdpi/dialog_bottom_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/dialog_bottom_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dialog_full_holo_dark.9.png b/core/res/res/drawable-mdpi/dialog_full_holo_dark.9.png
index adb8104..86edad7 100644
--- a/core/res/res/drawable-mdpi/dialog_full_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/dialog_full_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dialog_full_holo_light.9.png b/core/res/res/drawable-mdpi/dialog_full_holo_light.9.png
index d7c6bbf..53ee68b 100644
--- a/core/res/res/drawable-mdpi/dialog_full_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/dialog_full_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dialog_middle_holo_dark.9.png b/core/res/res/drawable-mdpi/dialog_middle_holo_dark.9.png
index 42cfc52..606adaf 100644
--- a/core/res/res/drawable-mdpi/dialog_middle_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/dialog_middle_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dialog_middle_holo_light.9.png b/core/res/res/drawable-mdpi/dialog_middle_holo_light.9.png
index 9a08e15..14d2e5e 100644
--- a/core/res/res/drawable-mdpi/dialog_middle_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/dialog_middle_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dialog_top_holo_dark.9.png b/core/res/res/drawable-mdpi/dialog_top_holo_dark.9.png
index 5d86b2a..2646332 100644
--- a/core/res/res/drawable-mdpi/dialog_top_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/dialog_top_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dialog_top_holo_light.9.png b/core/res/res/drawable-mdpi/dialog_top_holo_light.9.png
index ad22f5b..48ec0a4 100644
--- a/core/res/res/drawable-mdpi/dialog_top_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/dialog_top_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_btn_back.png b/core/res/res/drawable-mdpi/ic_btn_back.png
deleted file mode 100644
index c9bff4c..0000000
--- a/core/res/res/drawable-mdpi/ic_btn_back.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_btn_next.png b/core/res/res/drawable-mdpi/ic_btn_next.png
deleted file mode 100755
index c6cf436..0000000
--- a/core/res/res/drawable-mdpi/ic_btn_next.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/menu_dropdown_panel_holo_dark.9.png b/core/res/res/drawable-mdpi/menu_dropdown_panel_holo_dark.9.png
index 923f92d..dd5dd39 100644
--- a/core/res/res/drawable-mdpi/menu_dropdown_panel_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/menu_dropdown_panel_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/menu_dropdown_panel_holo_light.9.png b/core/res/res/drawable-mdpi/menu_dropdown_panel_holo_light.9.png
index afada39..12d65be 100644
--- a/core/res/res/drawable-mdpi/menu_dropdown_panel_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/menu_dropdown_panel_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/toast_frame.9.png b/core/res/res/drawable-mdpi/toast_frame.9.png
index 06cfc70..b9105de 100755
--- a/core/res/res/drawable-mdpi/toast_frame.9.png
+++ b/core/res/res/drawable-mdpi/toast_frame.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/toast_frame_holo.9.png b/core/res/res/drawable-mdpi/toast_frame_holo.9.png
index 06cfc70..b9105de 100755
--- a/core/res/res/drawable-mdpi/toast_frame_holo.9.png
+++ b/core/res/res/drawable-mdpi/toast_frame_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/dialog_bottom_holo_dark.9.png b/core/res/res/drawable-xhdpi/dialog_bottom_holo_dark.9.png
index b33e117..077e4d3 100644
--- a/core/res/res/drawable-xhdpi/dialog_bottom_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/dialog_bottom_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/dialog_bottom_holo_light.9.png b/core/res/res/drawable-xhdpi/dialog_bottom_holo_light.9.png
index d84d427..357c17f 100644
--- a/core/res/res/drawable-xhdpi/dialog_bottom_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/dialog_bottom_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/dialog_full_holo_dark.9.png b/core/res/res/drawable-xhdpi/dialog_full_holo_dark.9.png
index 9c0ff47..5b51072 100644
--- a/core/res/res/drawable-xhdpi/dialog_full_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/dialog_full_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/dialog_full_holo_light.9.png b/core/res/res/drawable-xhdpi/dialog_full_holo_light.9.png
index 331a4f2..2705a39 100644
--- a/core/res/res/drawable-xhdpi/dialog_full_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/dialog_full_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/dialog_middle_holo_dark.9.png b/core/res/res/drawable-xhdpi/dialog_middle_holo_dark.9.png
index cdc887d..101876f 100644
--- a/core/res/res/drawable-xhdpi/dialog_middle_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/dialog_middle_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/dialog_middle_holo_light.9.png b/core/res/res/drawable-xhdpi/dialog_middle_holo_light.9.png
index bdb6824..0df1503 100644
--- a/core/res/res/drawable-xhdpi/dialog_middle_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/dialog_middle_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/dialog_top_holo_dark.9.png b/core/res/res/drawable-xhdpi/dialog_top_holo_dark.9.png
index 600efb3..344a4e2 100644
--- a/core/res/res/drawable-xhdpi/dialog_top_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/dialog_top_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/dialog_top_holo_light.9.png b/core/res/res/drawable-xhdpi/dialog_top_holo_light.9.png
index aa8401d..249848f 100644
--- a/core/res/res/drawable-xhdpi/dialog_top_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/dialog_top_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/menu_dropdown_panel_holo_dark.9.png b/core/res/res/drawable-xhdpi/menu_dropdown_panel_holo_dark.9.png
new file mode 100644
index 0000000..92acc47
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/menu_dropdown_panel_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/menu_dropdown_panel_holo_light.9.png b/core/res/res/drawable-xhdpi/menu_dropdown_panel_holo_light.9.png
new file mode 100644
index 0000000..4e54b4b6
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/menu_dropdown_panel_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/toast_frame.9.png b/core/res/res/drawable-xhdpi/toast_frame.9.png
index f7debee..9f39a77 100644
--- a/core/res/res/drawable-xhdpi/toast_frame.9.png
+++ b/core/res/res/drawable-xhdpi/toast_frame.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/toast_frame_holo.9.png b/core/res/res/drawable-xhdpi/toast_frame_holo.9.png
index f7debee..9f39a77 100644
--- a/core/res/res/drawable-xhdpi/toast_frame_holo.9.png
+++ b/core/res/res/drawable-xhdpi/toast_frame_holo.9.png
Binary files differ
diff --git a/core/res/res/layout-sw600dp/preference_list_content.xml b/core/res/res/layout-sw600dp/preference_list_content.xml
index 5a345c6..a5320a7 100644
--- a/core/res/res/layout-sw600dp/preference_list_content.xml
+++ b/core/res/res/layout-sw600dp/preference_list_content.xml
@@ -87,7 +87,6 @@
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_weight="0"
- android:background="@android:drawable/bottom_bar"
android:visibility="gone">
<Button android:id="@+id/back_button"
@@ -95,8 +94,6 @@
android:layout_height="wrap_content"
android:layout_margin="5dip"
android:layout_alignParentLeft="true"
- android:drawableLeft="@drawable/ic_btn_back"
- android:drawablePadding="3dip"
android:text="@string/back_button_label"
/>
<LinearLayout
@@ -117,8 +114,6 @@
android:layout_width="150dip"
android:layout_height="wrap_content"
android:layout_margin="5dip"
- android:drawableRight="@drawable/ic_btn_next"
- android:drawablePadding="3dip"
android:text="@string/next_button_label"
/>
</LinearLayout>
diff --git a/core/res/res/layout-w600dp/preference_list_content_single.xml b/core/res/res/layout-w600dp/preference_list_content_single.xml
index 6725996..bbad296 100644
--- a/core/res/res/layout-w600dp/preference_list_content_single.xml
+++ b/core/res/res/layout-w600dp/preference_list_content_single.xml
@@ -61,7 +61,6 @@
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_weight="0"
- android:background="@android:drawable/bottom_bar"
android:visibility="gone">
<Button android:id="@+id/back_button"
@@ -69,8 +68,6 @@
android:layout_height="wrap_content"
android:layout_margin="5dip"
android:layout_alignParentLeft="true"
- android:drawableLeft="@drawable/ic_btn_back"
- android:drawablePadding="3dip"
android:text="@string/back_button_label"
/>
<LinearLayout
@@ -91,8 +88,6 @@
android:layout_width="150dip"
android:layout_height="wrap_content"
android:layout_margin="5dip"
- android:drawableRight="@drawable/ic_btn_next"
- android:drawablePadding="3dip"
android:text="@string/next_button_label"
/>
</LinearLayout>
diff --git a/core/res/res/layout-xlarge/activity_list.xml b/core/res/res/layout-xlarge/activity_list.xml
index ad485c1..5093a5e 100644
--- a/core/res/res/layout-xlarge/activity_list.xml
+++ b/core/res/res/layout-xlarge/activity_list.xml
@@ -54,21 +54,15 @@
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
- <ImageView android:id="@+id/titleDivider"
+ <View android:id="@+id/titleDivider"
android:layout_width="match_parent"
- android:layout_height="4dip"
- android:layout_marginLeft="16dip"
- android:layout_marginRight="16dip"
- android:scaleType="fitXY"
- android:gravity="fill_horizontal"
- android:src="@android:drawable/divider_strong_holo" />
+ android:layout_height="2dip"
+ android:background="@android:color/holo_blue_light" />
<!-- If the client uses a customTitle, it will be added here. -->
</LinearLayout>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_marginLeft="32dip"
- android:layout_marginRight="32dip"
android:layout_height="0dip"
android:layout_weight="1">
@@ -95,15 +89,12 @@
android:minHeight="54dip"
android:orientation="vertical"
android:divider="?android:attr/dividerHorizontal"
- android:showDividers="beginning"
- android:dividerPadding="16dip">
+ android:showDividers="beginning">
<LinearLayout
style="?android:attr/buttonBarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
- android:paddingLeft="2dip"
- android:paddingRight="2dip"
android:measureWithLargestChild="true">
<LinearLayout android:id="@+id/leftSpacer"
android:layout_weight="0.25"
diff --git a/core/res/res/layout/activity_chooser_list_footer.xml b/core/res/res/layout/activity_chooser_list_footer.xml
index 7603a31..c05ba1a 100644
--- a/core/res/res/layout/activity_chooser_list_footer.xml
+++ b/core/res/res/layout/activity_chooser_list_footer.xml
@@ -23,13 +23,11 @@
android:gravity="center"
android:orientation="vertical">
- <ImageView
+ <View
android:id="@+id/divider"
android:layout_width="match_parent"
android:layout_height="2dip"
- android:scaleType="fitXY"
- android:gravity="fill_horizontal"
- android:src="@drawable/divider_strong_holo" />
+ android:background="@android:color/holo_blue_light" />
<TextView
android:id="@+id/title"
diff --git a/core/res/res/layout/activity_chooser_list_header.xml b/core/res/res/layout/activity_chooser_list_header.xml
index 867014b..0fb256f 100644
--- a/core/res/res/layout/activity_chooser_list_header.xml
+++ b/core/res/res/layout/activity_chooser_list_header.xml
@@ -32,12 +32,10 @@
android:duplicateParentState="true"
android:singleLine="true" />
- <ImageView
+ <View
android:id="@+id/divider"
android:layout_width="match_parent"
android:layout_height="2dip"
- android:scaleType="fitXY"
- android:gravity="fill_horizontal"
android:src="@drawable/divider_strong_holo" />
</LinearLayout>
diff --git a/core/res/res/layout/alert_dialog_holo.xml b/core/res/res/layout/alert_dialog_holo.xml
index 2185467..2b686bd 100644
--- a/core/res/res/layout/alert_dialog_holo.xml
+++ b/core/res/res/layout/alert_dialog_holo.xml
@@ -30,13 +30,11 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
- <ImageView android:id="@+id/titleDividerTop"
+ <View android:id="@+id/titleDividerTop"
android:layout_width="match_parent"
- android:layout_height="1dip"
+ android:layout_height="2dip"
android:visibility="gone"
- android:scaleType="fitXY"
- android:gravity="fill_horizontal"
- android:src="@android:drawable/divider_strong_holo" />
+ android:background="@android:color/holo_blue_light" />
<LinearLayout android:id="@+id/title_template"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -51,20 +49,17 @@
android:paddingRight="8dip"
android:src="@null" />
<com.android.internal.widget.DialogTitle android:id="@+id/alertTitle"
- style="?android:attr/textAppearanceLarge"
- android:textColor="@android:color/holo_blue"
+ style="?android:attr/windowTitleStyle"
android:singleLine="true"
android:ellipsize="end"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
- <ImageView android:id="@+id/titleDivider"
+ <View android:id="@+id/titleDivider"
android:layout_width="match_parent"
- android:layout_height="1dip"
+ android:layout_height="2dip"
android:visibility="gone"
- android:scaleType="fitXY"
- android:gravity="fill_horizontal"
- android:src="@android:drawable/divider_strong_holo" />
+ android:background="@android:color/holo_blue_light" />
<!-- If the client uses a customTitle, it will be added here. -->
</LinearLayout>
diff --git a/core/res/res/layout/dialog_custom_title_holo.xml b/core/res/res/layout/dialog_custom_title_holo.xml
index 5261553..e2335a7 100644
--- a/core/res/res/layout/dialog_custom_title_holo.xml
+++ b/core/res/res/layout/dialog_custom_title_holo.xml
@@ -28,12 +28,10 @@ This is an custom layout for a dialog.
android:gravity="center_vertical|left"
style="?android:attr/windowTitleBackgroundStyle">
</FrameLayout>
- <ImageView android:id="@+id/titleDivider"
+ <View android:id="@+id/titleDivider"
android:layout_width="match_parent"
- android:layout_height="1dip"
- android:scaleType="fitXY"
- android:gravity="fill_horizontal"
- android:src="@android:drawable/divider_strong_holo" />
+ android:layout_height="2dip"
+ android:background="@android:color/holo_blue_light" />
<FrameLayout
android:layout_width="match_parent" android:layout_height="wrap_content"
android:layout_weight="1"
diff --git a/core/res/res/layout/dialog_title_holo.xml b/core/res/res/layout/dialog_title_holo.xml
index 400ef60..50bb0ba 100644
--- a/core/res/res/layout/dialog_title_holo.xml
+++ b/core/res/res/layout/dialog_title_holo.xml
@@ -30,12 +30,10 @@ enabled.
android:paddingLeft="16dip"
android:paddingRight="16dip"
android:gravity="center_vertical|left" />
- <ImageView android:id="@+id/titleDivider"
+ <View android:id="@+id/titleDivider"
android:layout_width="match_parent"
- android:layout_height="1dip"
- android:scaleType="fitXY"
- android:gravity="fill_horizontal"
- android:src="@android:drawable/divider_strong_holo" />
+ android:layout_height="2dip"
+ android:background="@android:color/holo_blue_light" />
<FrameLayout
android:layout_width="match_parent" android:layout_height="wrap_content"
android:layout_weight="1"
diff --git a/core/res/res/layout/dialog_title_icons_holo.xml b/core/res/res/layout/dialog_title_icons_holo.xml
index f780ab0..7d7959f 100644
--- a/core/res/res/layout/dialog_title_icons_holo.xml
+++ b/core/res/res/layout/dialog_title_icons_holo.xml
@@ -48,12 +48,10 @@ enabled.
android:layout_marginLeft="8dip" />
</LinearLayout>
- <ImageView android:id="@+id/titleDivider"
+ <View android:id="@+id/titleDivider"
android:layout_width="match_parent"
android:layout_height="1dip"
- android:scaleType="fitXY"
- android:gravity="fill_horizontal"
- android:src="@android:drawable/divider_strong_holo" />
+ android:background="@android:color/holo_blue_light" />
<FrameLayout
android:layout_width="match_parent" android:layout_height="wrap_content"
diff --git a/core/res/res/layout/preference_list_content.xml b/core/res/res/layout/preference_list_content.xml
index 82b3a4c..fb898ee 100644
--- a/core/res/res/layout/preference_list_content.xml
+++ b/core/res/res/layout/preference_list_content.xml
@@ -85,7 +85,6 @@
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_weight="0"
- android:background="@android:drawable/bottom_bar"
android:visibility="gone">
<Button android:id="@+id/back_button"
@@ -93,8 +92,6 @@
android:layout_height="wrap_content"
android:layout_margin="5dip"
android:layout_alignParentLeft="true"
- android:drawableLeft="@drawable/ic_btn_back"
- android:drawablePadding="3dip"
android:text="@string/back_button_label"
/>
<LinearLayout
@@ -115,8 +112,6 @@
android:layout_width="150dip"
android:layout_height="wrap_content"
android:layout_margin="5dip"
- android:drawableRight="@drawable/ic_btn_next"
- android:drawablePadding="3dip"
android:text="@string/next_button_label"
/>
</LinearLayout>
diff --git a/core/res/res/layout/preference_list_content_single.xml b/core/res/res/layout/preference_list_content_single.xml
index a015761..6902ffd 100644
--- a/core/res/res/layout/preference_list_content_single.xml
+++ b/core/res/res/layout/preference_list_content_single.xml
@@ -56,7 +56,6 @@
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_weight="0"
- android:background="@android:drawable/bottom_bar"
android:visibility="gone">
<Button android:id="@+id/back_button"
@@ -64,8 +63,6 @@
android:layout_height="wrap_content"
android:layout_margin="5dip"
android:layout_alignParentLeft="true"
- android:drawableLeft="@drawable/ic_btn_back"
- android:drawablePadding="3dip"
android:text="@string/back_button_label"
/>
<LinearLayout
@@ -86,8 +83,6 @@
android:layout_width="150dip"
android:layout_height="wrap_content"
android:layout_margin="5dip"
- android:drawableRight="@drawable/ic_btn_next"
- android:drawablePadding="3dip"
android:text="@string/next_button_label"
/>
</LinearLayout>
diff --git a/core/res/res/layout/preference_list_fragment.xml b/core/res/res/layout/preference_list_fragment.xml
index 986536e..315f708 100644
--- a/core/res/res/layout/preference_list_fragment.xml
+++ b/core/res/res/layout/preference_list_fragment.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
-/*
+/*
** Copyright 2010, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
@@ -41,7 +41,6 @@
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_weight="0"
- android:background="@android:drawable/bottom_bar"
android:visibility="gone">
<Button android:id="@+id/back_button"
@@ -49,8 +48,6 @@
android:layout_height="wrap_content"
android:layout_margin="5dip"
android:layout_alignParentLeft="true"
- android:drawableLeft="@drawable/ic_btn_back"
- android:drawablePadding="3dip"
android:text="@string/back_button_label"
/>
<LinearLayout
@@ -71,8 +68,6 @@
android:layout_width="150dip"
android:layout_height="wrap_content"
android:layout_margin="5dip"
- android:drawableRight="@drawable/ic_btn_next"
- android:drawablePadding="3dip"
android:text="@string/next_button_label"
/>
</LinearLayout>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 39de054..9c2133f 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -40,6 +40,21 @@
to use accelerated drawing (thus setting state_accelerated), the
cache hint is ignored and always assumed to be transparent. -->
<attr name="colorBackgroundCacheHint" format="color" />
+
+ <!-- Default highlight color for items that are pressed. -->
+ <attr name="colorPressedHighlight" format="color" />
+ <!-- Default highlight color for items that are long-pressed. -->
+ <attr name="colorLongPressedHighlight" format="color" />
+ <!-- Default highlight color for items that are
+ focused. (Focused meaning cursor-based selection.) -->
+ <attr name="colorFocusedHighlight" format="color" />
+ <!-- Default highlight color for items that are
+ activated. (Activated meaning persistent selection.) -->
+ <attr name="colorActivatedHighlight" format="color" />
+ <!-- Default highlight color for items in multiple selection
+ mode. -->
+ <attr name="colorMultiSelectHighlight" format="color" />
+
<!-- Default disabled alpha for widgets that set enabled/disabled alpha programmatically. -->
<attr name="disabledAlpha" format="float" />
<!-- Default background dim amount when a menu, dialog, or something similar pops up. -->
@@ -5303,6 +5318,8 @@
<attr name="mtpReserve" format="integer" />
<!-- true if the storage can be shared via USB mass storage -->
<attr name="allowMassStorage" format="boolean" />
+ <!-- maximum file size for the volume in megabytes, zero or unspecified if it is unbounded -->
+ <attr name="maxFileSize" format="integer" />
</declare-styleable>
<declare-styleable name="SwitchPreference">
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 2a1ebfc..631d8c6 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -143,19 +143,42 @@
<color name="link_text_holo_light">#0000ee</color>
<!-- Group buttons -->
+ <eat-comment />
<color name="group_button_dialog_pressed_holo_dark">#46c5c1ff</color>
<color name="group_button_dialog_focused_holo_dark">#2699cc00</color>
<color name="group_button_dialog_pressed_holo_light">#ffffffff</color>
<color name="group_button_dialog_focused_holo_light">#4699cc00</color>
+ <!-- Highlight colors for the legacy themes -->
+ <eat-comment />
+ <color name="legacy_pressed_highlight">#fffeaa0c</color>
+ <color name="legacy_selected_highlight">#fff17a0a</color>
+ <color name="legacy_long_pressed_highlight">#ffffffff</color>
+
<!-- General purpose colors for Holo-themed elements -->
<eat-comment />
- <!-- A Holo shade of blue -->
- <color name="holo_blue">#ff6699ff</color>
- <!-- A Holo shade of green -->
- <color name="holo_green">#ff99cc00</color>
+ <!-- A light Holo shade of blue -->
+ <color name="holo_blue_light">#ff33b5e5</color>
+ <!-- A light Holo shade of green -->
+ <color name="holo_green_light">#ff99cc00</color>
+ <!-- A light Holo shade of red -->
+ <color name="holo_red_light">#ffff4444</color>
+ <!-- A dark Holo shade of blue -->
+ <color name="holo_blue_dark">#ff0099cc</color>
+ <!-- A dark Holo shade of green -->
+ <color name="holo_green_dark">#ff669900</color>
+ <!-- A dark Holo shade of red -->
+ <color name="holo_red_dark">#ffcc0000</color>
+ <!-- A Holo shade of purple -->
+ <color name="holo_purple">#ffaa66cc</color>
+ <!-- A light Holo shade of orange -->
+ <color name="holo_orange_light">#ffffbb33</color>
+ <!-- A dark Holo shade of orange -->
+ <color name="holo_orange_dark">#ffff8800</color>
+ <!-- A really bright Holo shade of blue -->
+ <color name="holo_blue_bright">#ff00ddff</color>
</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 9f05cbc..1f2b7fb 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -660,4 +660,9 @@
extremely limited. -->
<bool name="config_allowActionMenuItemTextWithIcon">false</bool>
+ <!-- Remote server that can provide NTP responses. -->
+ <string translatable="false" name="config_ntpServer">pool.ntp.org</string>
+ <!-- Timeout to wait for NTP server response. -->
+ <integer name="config_ntpTimeout">20000</integer>
+
</resources>
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index d05685c..547a192 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -74,4 +74,9 @@
<item type="id" name="rowTypeId" />
<item type="id" name="up" />
<item type="id" name="action_menu_divider" />
+ <item type="id" name="icon_menu_presenter" />
+ <item type="id" name="list_menu_presenter" />
+ <item type="id" name="action_menu_presenter" />
+ <item type="id" name="overflow_menu_presenter" />
+ <item type="id" name="popup_submenu_presenter" />
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 3c23add..f7701f2 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1781,6 +1781,12 @@
<public type="attr" name="textAllCaps" />
+ <public type="attr" name="colorPressedHighlight" />
+ <public type="attr" name="colorLongPressedHighlight" />
+ <public type="attr" name="colorFocusedHighlight" />
+ <public type="attr" name="colorActivatedHighlight" />
+ <public type="attr" name="colorMultiSelectHighlight" />
+
<public type="style" name="TextAppearance.SuggestionHighlight" />
<public type="style" name="Theme.Holo.SplitActionBarWhenNarrow" />
<public type="style" name="Theme.Holo.Light.SplitActionBarWhenNarrow" />
@@ -1809,4 +1815,15 @@
<public type="integer" name="status_bar_notification_info_maxnum" />
<public type="string" name="status_bar_notification_info_overflow" />
+ <public type="color" name="holo_blue_light" />
+ <public type="color" name="holo_blue_dark" />
+ <public type="color" name="holo_green_light" />
+ <public type="color" name="holo_green_dark" />
+ <public type="color" name="holo_red_light" />
+ <public type="color" name="holo_red_dark" />
+ <public type="color" name="holo_orange_light" />
+ <public type="color" name="holo_orange_dark" />
+ <public type="color" name="holo_purple" />
+ <public type="color" name="holo_blue_bright" />
+
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 50f8df7..70c204e 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2038,13 +2038,13 @@
<string name="autofill_phone_re">phone<!-- de-DE -->|telefonnummer<!-- es -->|telefono|teléfono<!-- fr-FR -->|telfixe<!-- ja-JP -->|電話<!-- pt-BR, pt-PT -->|telefone|telemovel<!-- ru -->|телефон<!-- zh-CN -->|电话</string>
<!-- Do not translate. Regex used by AutoFill. -->
- <string name="autofill_area_code_re">area code</string>
+ <string name="autofill_area_code_re">area.*code|acode|area</string>
<!-- Do not translate. Regex used by AutoFill. -->
- <string name="autofill_phone_prefix_re">^-$|\\)$|prefix<!-- fr-FR -->|preselection<!-- pt-BR, pt-PT -->|ddd</string>
+ <string name="autofill_phone_prefix_re">prefix<!-- fr-FR -->|preselection<!-- pt-BR, pt-PT -->|ddd</string>
<!-- Do not translate. Regex used by AutoFill. -->
- <string name="autofill_phone_suffix_re">^-$|suffix</string>
+ <string name="autofill_phone_suffix_re">suffix</string>
<!-- Do not translate. Regex used by AutoFill. -->
<string name="autofill_phone_extension_re">ext<!-- pt-BR, pt-PT -->|ramal</string>
@@ -2077,14 +2077,50 @@
<string name="autofill_country_code_re">country.*code|ccode|_cc</string>
<!-- Do not translate. Regex used by AutoFill. -->
- <string name="autofill_area_code_notext_re">^\($</string>
+ <string name="autofill_area_code_notext_re">^\\($</string>
<!-- Do not translate. Regex used by AutoFill. -->
- <string name="autofill_phone_prefix_separator_re">^-$|^\)$</string>
+ <string name="autofill_phone_prefix_separator_re">^-$|^\\)$</string>
<!-- Do not translate. Regex used by AutoFill. -->
<string name="autofill_phone_suffix_separator_re">^-$</string>
+ <!-- Label in a web form for "Province" [CHAR-LIMIT=NONE] -->
+ <string name="autofill_province">Province</string>
+
+ <!-- Label in a web form for "Postal code" [CHAR-LIMIT=NONE] -->
+ <string name="autofill_postal_code">Postal code</string>
+
+ <!-- Label in a web form for "State" [CHAR-LIMIT=NONE] -->
+ <string name="autofill_state">State</string>
+
+ <!-- Label in a web form for "ZIP code" [CHAR-LIMIT=NONE] -->
+ <string name="autofill_zip_code">ZIP code</string>
+
+ <!-- Label in a web form for "County" [CHAR-LIMIT=NONE] -->
+ <string name="autofill_county">County</string>
+
+ <!-- Label in a web form for "Island" [CHAR-LIMIT=NONE] -->
+ <string name="autofill_island">Island</string>
+
+ <!-- Label in a web form for "District" [CHAR-LIMIT=NONE] -->
+ <string name="autofill_district">District</string>
+
+ <!-- Label in a web form for "Department" [CHAR-LIMIT=NONE] -->
+ <string name="autofill_department">Department</string>
+
+ <!-- Label in a web form for "Prefecture" [CHAR-LIMIT=NONE] -->
+ <string name="autofill_prefecture">Prefecture</string>
+
+ <!-- Label in a web form for "Parish" [CHAR-LIMIT=NONE] -->
+ <string name="autofill_parish">Parish</string>
+
+ <!-- Label in a web form for "Area" [CHAR-LIMIT=NONE] -->
+ <string name="autofill_area">Area</string>
+
+ <!-- Label in a web form for "Emirate" [CHAR-LIMIT=NONE] -->
+ <string name="autofill_emirate">Emirate</string>
+
<!-- Title of an application permission, listed so the user can choose whether
they want to allow the application to do this. -->
<string name="permlab_readHistoryBookmarks">read Browser\'s history and bookmarks</string>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 8a8f81a..a5cd6e3 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -1356,7 +1356,7 @@
<style name="TextAppearance.Holo.DialogWindowTitle">
<item name="android:textSize">22sp</item>
- <item name="android:textColor">@android:color/holo_blue</item>
+ <item name="android:textColor">@android:color/holo_blue_light</item>
</style>
<style name="TextAppearance.Holo.CalendarViewWeekDayView" parent="TextAppearance.Small.CalendarViewWeekDayView">
@@ -1455,7 +1455,7 @@
<style name="TextAppearance.Holo.Light.DialogWindowTitle">
<item name="android:textSize">22sp</item>
- <item name="android:textColor">@android:color/holo_blue</item>
+ <item name="android:textColor">@android:color/holo_blue_light</item>
</style>
<style name="TextAppearance.Holo.Light.CalendarViewWeekDayView" parent="TextAppearance.Small.CalendarViewWeekDayView">
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index 9a52cff..90f3602 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -31,6 +31,13 @@
<item name="colorForegroundInverse">@android:color/bright_foreground_dark_inverse</item>
<item name="colorBackground">@android:color/background_dark</item>
<item name="colorBackgroundCacheHint">?android:attr/colorBackground</item>
+
+ <item name="colorPressedHighlight">@color/legacy_pressed_highlight</item>
+ <item name="colorLongPressedHighlight">@color/legacy_long_pressed_highlight</item>
+ <item name="colorFocusedHighlight">@color/legacy_selected_highlight</item>
+ <item name="colorMultiSelectHighlight">@color/legacy_selected_highlight</item>
+ <item name="colorActivatedHighlight">@color/legacy_selected_highlight</item>
+
<item name="disabledAlpha">0.5</item>
<item name="backgroundDimAmount">0.6</item>
@@ -799,6 +806,12 @@
<item name="disabledAlpha">0.5</item>
<item name="backgroundDimAmount">0.6</item>
+ <item name="colorPressedHighlight">@color/holo_blue_light</item>
+ <item name="colorLongPressedHighlight">@color/holo_blue_bright</item>
+ <item name="colorFocusedHighlight">@color/holo_blue_dark</item>
+ <item name="colorMultiSelectHighlight">@color/holo_green_light</item>
+ <item name="colorActivatedHighlight">@color/holo_blue_dark</item>
+
<!-- Text styles -->
<item name="textAppearance">@android:style/TextAppearance.Holo</item>
<item name="textAppearanceInverse">@android:style/TextAppearance.Holo.Inverse</item>
@@ -1090,6 +1103,11 @@
<item name="disabledAlpha">0.5</item>
<item name="backgroundDimAmount">0.6</item>
+ <item name="colorPressedHighlight">@color/holo_blue_light</item>
+ <item name="colorLongPressedHighlight">@color/holo_blue_bright</item>
+ <item name="colorFocusedHighlight">@color/holo_blue_dark</item>
+ <item name="colorMultiSelectHighlight">@color/holo_green_light</item>
+ <item name="colorActivatedHighlight">@color/holo_blue_dark</item>
<!-- Text styles -->
<item name="textAppearance">@android:style/TextAppearance.Holo.Light</item>
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 059503c..146466f 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -1051,6 +1051,13 @@
</intent-filter>
</activity>
+ <activity android:name="android.widget.TextViewTestActivity" android:label="TextViewTestActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+ </intent-filter>
+ </activity>
+
<!-- Activity-level metadata -->
diff --git a/core/tests/coretests/res/layout/textview_test.xml b/core/tests/coretests/res/layout/textview_test.xml
new file mode 100644
index 0000000..f0c7b9e
--- /dev/null
+++ b/core/tests/coretests/res/layout/textview_test.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/textviewtest_layout"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+
+ <TextView android:id="@+id/textviewtest_textview"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/textview_hebrew_text"/>
+
+</LinearLayout> \ No newline at end of file
diff --git a/core/tests/coretests/res/values/strings.xml b/core/tests/coretests/res/values/strings.xml
index f51b08e..71f3520 100644
--- a/core/tests/coretests/res/values/strings.xml
+++ b/core/tests/coretests/res/values/strings.xml
@@ -129,4 +129,6 @@
<string name="button8">Button8</string>
<string name="button9">Button9</string>
+ <string name="textview_hebrew_text">&#x05DD;&#x05DE;ab?!</string>
+
</resources>
diff --git a/core/tests/coretests/src/android/net/NetworkStatsTest.java b/core/tests/coretests/src/android/net/NetworkStatsTest.java
index 3cb64c7..82345e2 100644
--- a/core/tests/coretests/src/android/net/NetworkStatsTest.java
+++ b/core/tests/coretests/src/android/net/NetworkStatsTest.java
@@ -44,20 +44,20 @@ public class NetworkStatsTest extends TestCase {
public void testAddEntryGrow() throws Exception {
final NetworkStats stats = new NetworkStats(TEST_START, 2);
- assertEquals(0, stats.size);
+ assertEquals(0, stats.size());
assertEquals(2, stats.iface.length);
stats.addEntry(TEST_IFACE, TEST_UID, TAG_NONE, 1L, 2L);
stats.addEntry(TEST_IFACE, TEST_UID, TAG_NONE, 2L, 2L);
- assertEquals(2, stats.size);
+ assertEquals(2, stats.size());
assertEquals(2, stats.iface.length);
stats.addEntry(TEST_IFACE, TEST_UID, TAG_NONE, 3L, 4L);
stats.addEntry(TEST_IFACE, TEST_UID, TAG_NONE, 4L, 4L);
stats.addEntry(TEST_IFACE, TEST_UID, TAG_NONE, 5L, 5L);
- assertEquals(5, stats.size);
+ assertEquals(5, stats.size());
assertTrue(stats.iface.length >= 5);
assertEquals(1L, stats.rx[0]);
diff --git a/core/tests/coretests/src/android/pim/EventRecurrenceTest.java b/core/tests/coretests/src/android/pim/EventRecurrenceTest.java
deleted file mode 100644
index 05000f1..0000000
--- a/core/tests/coretests/src/android/pim/EventRecurrenceTest.java
+++ /dev/null
@@ -1,753 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-package android.pim;
-
-import android.pim.EventRecurrence.InvalidFormatException;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.test.suitebuilder.annotation.Suppress;
-
-import junit.framework.TestCase;
-
-import java.util.Arrays;
-
-/**
- * Test android.pim.EventRecurrence.
- *
- * adb shell am instrument -w -e class android.pim.EventRecurrenceTest \
- * com.android.frameworks.coretests/android.test.InstrumentationTestRunner
- */
-public class EventRecurrenceTest extends TestCase {
-
- @SmallTest
- public void test0() throws Exception {
- verifyRecurType("FREQ=SECONDLY",
- /* int freq */ EventRecurrence.SECONDLY,
- /* String until */ null,
- /* int count */ 0,
- /* int interval */ 0,
- /* int[] bysecond */ null,
- /* int[] byminute */ null,
- /* int[] byhour */ null,
- /* int[] byday */ null,
- /* int[] bydayNum */ null,
- /* int[] bymonthday */ null,
- /* int[] byyearday */ null,
- /* int[] byweekno */ null,
- /* int[] bymonth */ null,
- /* int[] bysetpos */ null,
- /* int wkst */ EventRecurrence.MO
- );
- }
-
- @SmallTest
- public void test1() throws Exception {
- verifyRecurType("FREQ=MINUTELY",
- /* int freq */ EventRecurrence.MINUTELY,
- /* String until */ null,
- /* int count */ 0,
- /* int interval */ 0,
- /* int[] bysecond */ null,
- /* int[] byminute */ null,
- /* int[] byhour */ null,
- /* int[] byday */ null,
- /* int[] bydayNum */ null,
- /* int[] bymonthday */ null,
- /* int[] byyearday */ null,
- /* int[] byweekno */ null,
- /* int[] bymonth */ null,
- /* int[] bysetpos */ null,
- /* int wkst */ EventRecurrence.MO
- );
- }
-
- @SmallTest
- public void test2() throws Exception {
- verifyRecurType("FREQ=HOURLY",
- /* int freq */ EventRecurrence.HOURLY,
- /* String until */ null,
- /* int count */ 0,
- /* int interval */ 0,
- /* int[] bysecond */ null,
- /* int[] byminute */ null,
- /* int[] byhour */ null,
- /* int[] byday */ null,
- /* int[] bydayNum */ null,
- /* int[] bymonthday */ null,
- /* int[] byyearday */ null,
- /* int[] byweekno */ null,
- /* int[] bymonth */ null,
- /* int[] bysetpos */ null,
- /* int wkst */ EventRecurrence.MO
- );
- }
-
- @SmallTest
- public void test3() throws Exception {
- verifyRecurType("FREQ=DAILY",
- /* int freq */ EventRecurrence.DAILY,
- /* String until */ null,
- /* int count */ 0,
- /* int interval */ 0,
- /* int[] bysecond */ null,
- /* int[] byminute */ null,
- /* int[] byhour */ null,
- /* int[] byday */ null,
- /* int[] bydayNum */ null,
- /* int[] bymonthday */ null,
- /* int[] byyearday */ null,
- /* int[] byweekno */ null,
- /* int[] bymonth */ null,
- /* int[] bysetpos */ null,
- /* int wkst */ EventRecurrence.MO
- );
- }
-
- @SmallTest
- public void test4() throws Exception {
- verifyRecurType("FREQ=WEEKLY",
- /* int freq */ EventRecurrence.WEEKLY,
- /* String until */ null,
- /* int count */ 0,
- /* int interval */ 0,
- /* int[] bysecond */ null,
- /* int[] byminute */ null,
- /* int[] byhour */ null,
- /* int[] byday */ null,
- /* int[] bydayNum */ null,
- /* int[] bymonthday */ null,
- /* int[] byyearday */ null,
- /* int[] byweekno */ null,
- /* int[] bymonth */ null,
- /* int[] bysetpos */ null,
- /* int wkst */ EventRecurrence.MO
- );
- }
-
- @SmallTest
- public void test5() throws Exception {
- verifyRecurType("FREQ=MONTHLY",
- /* int freq */ EventRecurrence.MONTHLY,
- /* String until */ null,
- /* int count */ 0,
- /* int interval */ 0,
- /* int[] bysecond */ null,
- /* int[] byminute */ null,
- /* int[] byhour */ null,
- /* int[] byday */ null,
- /* int[] bydayNum */ null,
- /* int[] bymonthday */ null,
- /* int[] byyearday */ null,
- /* int[] byweekno */ null,
- /* int[] bymonth */ null,
- /* int[] bysetpos */ null,
- /* int wkst */ EventRecurrence.MO
- );
- }
-
- @SmallTest
- public void test6() throws Exception {
- verifyRecurType("FREQ=YEARLY",
- /* int freq */ EventRecurrence.YEARLY,
- /* String until */ null,
- /* int count */ 0,
- /* int interval */ 0,
- /* int[] bysecond */ null,
- /* int[] byminute */ null,
- /* int[] byhour */ null,
- /* int[] byday */ null,
- /* int[] bydayNum */ null,
- /* int[] bymonthday */ null,
- /* int[] byyearday */ null,
- /* int[] byweekno */ null,
- /* int[] bymonth */ null,
- /* int[] bysetpos */ null,
- /* int wkst */ EventRecurrence.MO
- );
- }
-
- @SmallTest
- public void test7() throws Exception {
- // with an until
- verifyRecurType("FREQ=DAILY;UNTIL=112233T223344Z",
- /* int freq */ EventRecurrence.DAILY,
- /* String until */ "112233T223344Z",
- /* int count */ 0,
- /* int interval */ 0,
- /* int[] bysecond */ null,
- /* int[] byminute */ null,
- /* int[] byhour */ null,
- /* int[] byday */ null,
- /* int[] bydayNum */ null,
- /* int[] bymonthday */ null,
- /* int[] byyearday */ null,
- /* int[] byweekno */ null,
- /* int[] bymonth */ null,
- /* int[] bysetpos */ null,
- /* int wkst */ EventRecurrence.MO
- );
- }
-
- @SmallTest
- public void test8() throws Exception {
- // with a count
- verifyRecurType("FREQ=DAILY;COUNT=334",
- /* int freq */ EventRecurrence.DAILY,
- /* String until */ null,
- /* int count */ 334,
- /* int interval */ 0,
- /* int[] bysecond */ null,
- /* int[] byminute */ null,
- /* int[] byhour */ null,
- /* int[] byday */ null,
- /* int[] bydayNum */ null,
- /* int[] bymonthday */ null,
- /* int[] byyearday */ null,
- /* int[] byweekno */ null,
- /* int[] bymonth */ null,
- /* int[] bysetpos */ null,
- /* int wkst */ EventRecurrence.MO
- );
- }
-
- @SmallTest
- public void test9() throws Exception {
- // with a count
- verifyRecurType("FREQ=DAILY;INTERVAL=5000",
- /* int freq */ EventRecurrence.DAILY,
- /* String until */ null,
- /* int count */ 0,
- /* int interval */ 5000,
- /* int[] bysecond */ null,
- /* int[] byminute */ null,
- /* int[] byhour */ null,
- /* int[] byday */ null,
- /* int[] bydayNum */ null,
- /* int[] bymonthday */ null,
- /* int[] byyearday */ null,
- /* int[] byweekno */ null,
- /* int[] bymonth */ null,
- /* int[] bysetpos */ null,
- /* int wkst */ EventRecurrence.MO
- );
- }
-
- @SmallTest
- public void test10() throws Exception {
- // verifyRecurType all of the BY* ones with one element
- verifyRecurType("FREQ=DAILY"
- + ";BYSECOND=0"
- + ";BYMINUTE=1"
- + ";BYHOUR=2"
- + ";BYMONTHDAY=30"
- + ";BYYEARDAY=300"
- + ";BYWEEKNO=53"
- + ";BYMONTH=12"
- + ";BYSETPOS=-15"
- + ";WKST=SU",
- /* int freq */ EventRecurrence.DAILY,
- /* String until */ null,
- /* int count */ 0,
- /* int interval */ 0,
- /* int[] bysecond */ new int[]{0},
- /* int[] byminute */ new int[]{1},
- /* int[] byhour */ new int[]{2},
- /* int[] byday */ null,
- /* int[] bydayNum */ null,
- /* int[] bymonthday */ new int[]{30},
- /* int[] byyearday */ new int[]{300},
- /* int[] byweekno */ new int[]{53},
- /* int[] bymonth */ new int[]{12},
- /* int[] bysetpos */ new int[]{-15},
- /* int wkst */ EventRecurrence.SU
- );
- }
-
- @SmallTest
- public void test11() throws Exception {
- // verifyRecurType all of the BY* ones with one element
- verifyRecurType("FREQ=DAILY"
- + ";BYSECOND=0,30,59"
- + ";BYMINUTE=0,41,59"
- + ";BYHOUR=0,4,23"
- + ";BYMONTHDAY=-31,-1,1,31"
- + ";BYYEARDAY=-366,-1,1,366"
- + ";BYWEEKNO=-53,-1,1,53"
- + ";BYMONTH=1,12"
- + ";BYSETPOS=1,2,3,4,500,10000"
- + ";WKST=SU",
- /* int freq */ EventRecurrence.DAILY,
- /* String until */ null,
- /* int count */ 0,
- /* int interval */ 0,
- /* int[] bysecond */ new int[]{0, 30, 59},
- /* int[] byminute */ new int[]{0, 41, 59},
- /* int[] byhour */ new int[]{0, 4, 23},
- /* int[] byday */ null,
- /* int[] bydayNum */ null,
- /* int[] bymonthday */ new int[]{-31, -1, 1, 31},
- /* int[] byyearday */ new int[]{-366, -1, 1, 366},
- /* int[] byweekno */ new int[]{-53, -1, 1, 53},
- /* int[] bymonth */ new int[]{1, 12},
- /* int[] bysetpos */ new int[]{1, 2, 3, 4, 500, 10000},
- /* int wkst */ EventRecurrence.SU
- );
- }
-
- private static class Check {
- Check(String k, int... v) {
- key = k;
- values = v;
- }
-
- String key;
- int[] values;
- }
-
- // this is a negative verifyRecurType case to verifyRecurType the range of the numbers accepted
- @SmallTest
- public void test12() throws Exception {
- Check[] checks = new Check[]{
- new Check("BYSECOND", -100, -1, 60, 100),
- new Check("BYMINUTE", -100, -1, 60, 100),
- new Check("BYHOUR", -100, -1, 24, 100),
- new Check("BYMONTHDAY", -100, -32, 0, 32, 100),
- new Check("BYYEARDAY", -400, -367, 0, 367, 400),
- new Check("BYWEEKNO", -100, -54, 0, 54, 100),
- new Check("BYMONTH", -100, -5, 0, 13, 100)
- };
-
- for (Check ck : checks) {
- for (int n : ck.values) {
- String recur = "FREQ=DAILY;" + ck.key + "=" + n;
- try {
- EventRecurrence er = new EventRecurrence();
- er.parse(recur);
- fail("Negative verifyRecurType failed. "
- + " parse failed to throw an exception for '"
- + recur + "'");
- } catch (EventRecurrence.InvalidFormatException e) {
- // expected
- }
- }
- }
- }
-
- // verifyRecurType BYDAY
- @SmallTest
- public void test13() throws Exception {
- verifyRecurType("FREQ=DAILY;BYDAY=1SU,-2MO,+33TU,WE,TH,FR,SA",
- /* int freq */ EventRecurrence.DAILY,
- /* String until */ null,
- /* int count */ 0,
- /* int interval */ 0,
- /* int[] bysecond */ null,
- /* int[] byminute */ null,
- /* int[] byhour */ null,
- /* int[] byday */ new int[] {
- EventRecurrence.SU,
- EventRecurrence.MO,
- EventRecurrence.TU,
- EventRecurrence.WE,
- EventRecurrence.TH,
- EventRecurrence.FR,
- EventRecurrence.SA
- },
- /* int[] bydayNum */ new int[]{1, -2, 33, 0, 0, 0, 0},
- /* int[] bymonthday */ null,
- /* int[] byyearday */ null,
- /* int[] byweekno */ null,
- /* int[] bymonth */ null,
- /* int[] bysetpos */ null,
- /* int wkst */ EventRecurrence.MO
- );
- }
-
- @Suppress
- // Repro bug #2331761 - this should fail because of the last comma into BYDAY
- public void test14() throws Exception {
- verifyRecurType("FREQ=WEEKLY;WKST=MO;UNTIL=20100129T130000Z;INTERVAL=1;BYDAY=MO,TU,WE,",
- /* int freq */ EventRecurrence.WEEKLY,
- /* String until */ "20100129T130000Z",
- /* int count */ 0,
- /* int interval */ 1,
- /* int[] bysecond */ null,
- /* int[] byminute */ null,
- /* int[] byhour */ null,
- /* int[] byday */ new int[] {
- EventRecurrence.MO,
- EventRecurrence.TU,
- EventRecurrence.WE,
- },
- /* int[] bydayNum */ new int[]{0, 0, 0},
- /* int[] bymonthday */ null,
- /* int[] byyearday */ null,
- /* int[] byweekno */ null,
- /* int[] bymonth */ null,
- /* int[] bysetpos */ null,
- /* int wkst */ EventRecurrence.MO
- );
- }
-
- // This test should pass
- public void test15() throws Exception {
- verifyRecurType("FREQ=WEEKLY;WKST=MO;UNTIL=20100129T130000Z;INTERVAL=1;"
- + "BYDAY=MO,TU,WE,TH,FR,SA,SU",
- /* int freq */ EventRecurrence.WEEKLY,
- /* String until */ "20100129T130000Z",
- /* int count */ 0,
- /* int interval */ 1,
- /* int[] bysecond */ null,
- /* int[] byminute */ null,
- /* int[] byhour */ null,
- /* int[] byday */ new int[] {
- EventRecurrence.MO,
- EventRecurrence.TU,
- EventRecurrence.WE,
- EventRecurrence.TH,
- EventRecurrence.FR,
- EventRecurrence.SA,
- EventRecurrence.SU
- },
- /* int[] bydayNum */ new int[]{0, 0, 0, 0, 0, 0, 0},
- /* int[] bymonthday */ null,
- /* int[] byyearday */ null,
- /* int[] byweekno */ null,
- /* int[] bymonth */ null,
- /* int[] bysetpos */ null,
- /* int wkst */ EventRecurrence.MO
- );
- }
-
- // Sample coming from RFC2445
- public void test16() throws Exception {
- verifyRecurType("FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-1",
- /* int freq */ EventRecurrence.MONTHLY,
- /* String until */ null,
- /* int count */ 0,
- /* int interval */ 0,
- /* int[] bysecond */ null,
- /* int[] byminute */ null,
- /* int[] byhour */ null,
- /* int[] byday */ new int[] {
- EventRecurrence.MO,
- EventRecurrence.TU,
- EventRecurrence.WE,
- EventRecurrence.TH,
- EventRecurrence.FR
- },
- /* int[] bydayNum */ new int[] {0, 0, 0, 0, 0},
- /* int[] bymonthday */ null,
- /* int[] byyearday */ null,
- /* int[] byweekno */ null,
- /* int[] bymonth */ null,
- /* int[] bysetpos */ new int[] { -1 },
- /* int wkst */ EventRecurrence.MO
- );
- }
-
- // Sample coming from RFC2445
- public void test17() throws Exception {
- verifyRecurType("FREQ=DAILY;COUNT=10;INTERVAL=2",
- /* int freq */ EventRecurrence.DAILY,
- /* String until */ null,
- /* int count */ 10,
- /* int interval */ 2,
- /* int[] bysecond */ null,
- /* int[] byminute */ null,
- /* int[] byhour */ null,
- /* int[] byday */ null,
- /* int[] bydayNum */ null,
- /* int[] bymonthday */ null,
- /* int[] byyearday */ null,
- /* int[] byweekno */ null,
- /* int[] bymonth */ null,
- /* int[] bysetpos */ null,
- /* int wkst */ EventRecurrence.MO
- );
- }
-
- // Sample coming from RFC2445
- public void test18() throws Exception {
- verifyRecurType("FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10",
- /* int freq */ EventRecurrence.YEARLY,
- /* String until */ null,
- /* int count */ 0,
- /* int interval */ 0,
- /* int[] bysecond */ null,
- /* int[] byminute */ null,
- /* int[] byhour */ null,
- /* int[] byday */ new int[] {
- EventRecurrence.SU
- },
- /* int[] bydayNum */ new int[] { -1 },
- /* int[] bymonthday */ null,
- /* int[] byyearday */ null,
- /* int[] byweekno */ null,
- /* int[] bymonth */ new int[] { 10 },
- /* int[] bysetpos */ null,
- /* int wkst */ EventRecurrence.MO
- );
- }
-
- // Sample coming from bug #1640517
- public void test19() throws Exception {
- verifyRecurType("FREQ=YEARLY;BYMONTH=3;BYDAY=TH",
- /* int freq */ EventRecurrence.YEARLY,
- /* String until */ null,
- /* int count */ 0,
- /* int interval */ 0,
- /* int[] bysecond */ null,
- /* int[] byminute */ null,
- /* int[] byhour */ null,
- /* int[] byday */ new int[] {
- EventRecurrence.TH
- },
- /* int[] bydayNum */ new int[] { 0 },
- /* int[] bymonthday */ null,
- /* int[] byyearday */ null,
- /* int[] byweekno */ null,
- /* int[] bymonth */ new int[] { 3 },
- /* int[] bysetpos */ null,
- /* int wkst */ EventRecurrence.MO
- );
- }
-
- // for your copying pleasure
- public void fakeTestXX() throws Exception {
- verifyRecurType("FREQ=DAILY;",
- /* int freq */ EventRecurrence.DAILY,
- /* String until */ null,
- /* int count */ 0,
- /* int interval */ 0,
- /* int[] bysecond */ null,
- /* int[] byminute */ null,
- /* int[] byhour */ null,
- /* int[] byday */ null,
- /* int[] bydayNum */ null,
- /* int[] bymonthday */ null,
- /* int[] byyearday */ null,
- /* int[] byweekno */ null,
- /* int[] bymonth */ null,
- /* int[] bysetpos */ null,
- /* int wkst */ EventRecurrence.MO
- );
- }
-
- private static void cmp(int vlen, int[] v, int[] correct, String name) {
- if ((correct == null && v != null)
- || (correct != null && v == null)) {
- throw new RuntimeException("One is null, one isn't for " + name
- + ": correct=" + Arrays.toString(correct)
- + " actual=" + Arrays.toString(v));
- }
- if ((correct == null && vlen != 0)
- || (vlen != (correct == null ? 0 : correct.length))) {
- throw new RuntimeException("Reported length mismatch for " + name
- + ": correct=" + ((correct == null) ? "null" : correct.length)
- + " actual=" + vlen);
- }
- if (correct == null) {
- return;
- }
- if (v.length < correct.length) {
- throw new RuntimeException("Array length mismatch for " + name
- + ": correct=" + Arrays.toString(correct)
- + " actual=" + Arrays.toString(v));
- }
- for (int i = 0; i < correct.length; i++) {
- if (v[i] != correct[i]) {
- throw new RuntimeException("Array value mismatch for " + name
- + ": correct=" + Arrays.toString(correct)
- + " actual=" + Arrays.toString(v));
- }
- }
- }
-
- private static boolean eq(String a, String b) {
- if ((a == null && b != null) || (a != null && b == null)) {
- return false;
- } else {
- return a == b || a.equals(b);
- }
- }
-
- private static void verifyRecurType(String recur,
- int freq, String until, int count, int interval,
- int[] bysecond, int[] byminute, int[] byhour,
- int[] byday, int[] bydayNum, int[] bymonthday,
- int[] byyearday, int[] byweekno, int[] bymonth,
- int[] bysetpos, int wkst) {
- EventRecurrence eventRecurrence = new EventRecurrence();
- eventRecurrence.parse(recur);
- if (eventRecurrence.freq != freq
- || !eq(eventRecurrence.until, until)
- || eventRecurrence.count != count
- || eventRecurrence.interval != interval
- || eventRecurrence.wkst != wkst) {
- System.out.println("Error... got:");
- print(eventRecurrence);
- System.out.println("expected:");
- System.out.println("{");
- System.out.println(" freq=" + freq);
- System.out.println(" until=" + until);
- System.out.println(" count=" + count);
- System.out.println(" interval=" + interval);
- System.out.println(" wkst=" + wkst);
- System.out.println(" bysecond=" + Arrays.toString(bysecond));
- System.out.println(" byminute=" + Arrays.toString(byminute));
- System.out.println(" byhour=" + Arrays.toString(byhour));
- System.out.println(" byday=" + Arrays.toString(byday));
- System.out.println(" bydayNum=" + Arrays.toString(bydayNum));
- System.out.println(" bymonthday=" + Arrays.toString(bymonthday));
- System.out.println(" byyearday=" + Arrays.toString(byyearday));
- System.out.println(" byweekno=" + Arrays.toString(byweekno));
- System.out.println(" bymonth=" + Arrays.toString(bymonth));
- System.out.println(" bysetpos=" + Arrays.toString(bysetpos));
- System.out.println("}");
- throw new RuntimeException("Mismatch in fields");
- }
- cmp(eventRecurrence.bysecondCount, eventRecurrence.bysecond, bysecond, "bysecond");
- cmp(eventRecurrence.byminuteCount, eventRecurrence.byminute, byminute, "byminute");
- cmp(eventRecurrence.byhourCount, eventRecurrence.byhour, byhour, "byhour");
- cmp(eventRecurrence.bydayCount, eventRecurrence.byday, byday, "byday");
- cmp(eventRecurrence.bydayCount, eventRecurrence.bydayNum, bydayNum, "bydayNum");
- cmp(eventRecurrence.bymonthdayCount, eventRecurrence.bymonthday, bymonthday, "bymonthday");
- cmp(eventRecurrence.byyeardayCount, eventRecurrence.byyearday, byyearday, "byyearday");
- cmp(eventRecurrence.byweeknoCount, eventRecurrence.byweekno, byweekno, "byweekno");
- cmp(eventRecurrence.bymonthCount, eventRecurrence.bymonth, bymonth, "bymonth");
- cmp(eventRecurrence.bysetposCount, eventRecurrence.bysetpos, bysetpos, "bysetpos");
- }
-
- private static void print(EventRecurrence er) {
- System.out.println("{");
- System.out.println(" freq=" + er.freq);
- System.out.println(" until=" + er.until);
- System.out.println(" count=" + er.count);
- System.out.println(" interval=" + er.interval);
- System.out.println(" wkst=" + er.wkst);
- System.out.println(" bysecond=" + Arrays.toString(er.bysecond));
- System.out.println(" bysecondCount=" + er.bysecondCount);
- System.out.println(" byminute=" + Arrays.toString(er.byminute));
- System.out.println(" byminuteCount=" + er.byminuteCount);
- System.out.println(" byhour=" + Arrays.toString(er.byhour));
- System.out.println(" byhourCount=" + er.byhourCount);
- System.out.println(" byday=" + Arrays.toString(er.byday));
- System.out.println(" bydayNum=" + Arrays.toString(er.bydayNum));
- System.out.println(" bydayCount=" + er.bydayCount);
- System.out.println(" bymonthday=" + Arrays.toString(er.bymonthday));
- System.out.println(" bymonthdayCount=" + er.bymonthdayCount);
- System.out.println(" byyearday=" + Arrays.toString(er.byyearday));
- System.out.println(" byyeardayCount=" + er.byyeardayCount);
- System.out.println(" byweekno=" + Arrays.toString(er.byweekno));
- System.out.println(" byweeknoCount=" + er.byweeknoCount);
- System.out.println(" bymonth=" + Arrays.toString(er.bymonth));
- System.out.println(" bymonthCount=" + er.bymonthCount);
- System.out.println(" bysetpos=" + Arrays.toString(er.bysetpos));
- System.out.println(" bysetposCount=" + er.bysetposCount);
- System.out.println("}");
- }
-
-
- /** A list of valid rules. The parser must accept these. */
- private static final String[] GOOD_RRULES = {
- /* extracted wholesale from from RFC 2445 section 4.8.5.4 */
- "FREQ=DAILY;COUNT=10",
- "FREQ=DAILY;UNTIL=19971224T000000Z",
- "FREQ=DAILY;INTERVAL=2",
- "FREQ=DAILY;INTERVAL=10;COUNT=5",
- "FREQ=YEARLY;UNTIL=20000131T090000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA",
- "FREQ=DAILY;UNTIL=20000131T090000Z;BYMONTH=1",
- "FREQ=WEEKLY;COUNT=10",
- "FREQ=WEEKLY;UNTIL=19971224T000000Z",
- "FREQ=WEEKLY;INTERVAL=2;WKST=SU",
- "FREQ=WEEKLY;UNTIL=19971007T000000Z;WKST=SU;BYDAY=TU,TH",
- "FREQ=WEEKLY;COUNT=10;WKST=SU;BYDAY=TU,TH",
- "FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;WKST=SU;BYDAY=MO,WE,FR",
- "FREQ=WEEKLY;INTERVAL=2;COUNT=8;WKST=SU;BYDAY=TU,TH",
- "FREQ=MONTHLY;COUNT=10;BYDAY=1FR",
- "FREQ=MONTHLY;UNTIL=19971224T000000Z;BYDAY=1FR",
- "FREQ=MONTHLY;INTERVAL=2;COUNT=10;BYDAY=1SU,-1SU",
- "FREQ=MONTHLY;COUNT=6;BYDAY=-2MO",
- "FREQ=MONTHLY;BYMONTHDAY=-3",
- "FREQ=MONTHLY;COUNT=10;BYMONTHDAY=2,15",
- "FREQ=MONTHLY;COUNT=10;BYMONTHDAY=1,-1",
- "FREQ=MONTHLY;INTERVAL=18;COUNT=10;BYMONTHDAY=10,11,12,13,14,15",
- "FREQ=MONTHLY;INTERVAL=2;BYDAY=TU",
- "FREQ=YEARLY;COUNT=10;BYMONTH=6,7",
- "FREQ=YEARLY;INTERVAL=2;COUNT=10;BYMONTH=1,2,3",
- "FREQ=YEARLY;INTERVAL=3;COUNT=10;BYYEARDAY=1,100,200",
- "FREQ=YEARLY;BYDAY=20MO",
- "FREQ=YEARLY;BYWEEKNO=20;BYDAY=MO",
- "FREQ=YEARLY;BYMONTH=3;BYDAY=TH",
- "FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8",
- "FREQ=MONTHLY;BYDAY=FR;BYMONTHDAY=13",
- "FREQ=MONTHLY;BYDAY=SA;BYMONTHDAY=7,8,9,10,11,12,13",
- "FREQ=YEARLY;INTERVAL=4;BYMONTH=11;BYDAY=TU;BYMONTHDAY=2,3,4,5,6,7,8",
- "FREQ=MONTHLY;COUNT=3;BYDAY=TU,WE,TH;BYSETPOS=3",
- "FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-2",
- "FREQ=HOURLY;INTERVAL=3;UNTIL=19970902T170000Z",
- "FREQ=MINUTELY;INTERVAL=15;COUNT=6",
- "FREQ=MINUTELY;INTERVAL=90;COUNT=4",
- "FREQ=DAILY;BYHOUR=9,10,11,12,13,14,15,16;BYMINUTE=0,20,40",
- "FREQ=MINUTELY;INTERVAL=20;BYHOUR=9,10,11,12,13,14,15,16",
- "FREQ=WEEKLY;INTERVAL=2;COUNT=4;BYDAY=TU,SU;WKST=MO",
- "FREQ=WEEKLY;INTERVAL=2;COUNT=4;BYDAY=TU,SU;WKST=SU",
- /* a few more */
- "FREQ=SECONDLY;BYSECOND=0,15,59",
- "FREQ=MINUTELY;BYMINUTE=0,15,59",
- "FREQ=HOURLY;BYHOUR=+0,+15,+23",
- "FREQ=DAILY;X-WHATEVER=blah", // fails on old parser
- //"freq=daily;wkst=su", // fails on old parser
- };
-
- /** The parser must reject these. */
- private static final String[] BAD_RRULES = {
- "INTERVAL=4;FREQ=YEARLY", // FREQ must come first
- "FREQ=MONTHLY;FREQ=MONTHLY", // can't specify twice
- "FREQ=MONTHLY;COUNT=1;COUNT=1", // can't specify twice
- "FREQ=SECONDLY;BYSECOND=60", // range
- "FREQ=MINUTELY;BYMINUTE=-1", // range
- "FREQ=HOURLY;BYHOUR=24", // range
- "FREQ=YEARLY;BYMONTHDAY=0", // zero not valid
- //"FREQ=YEARLY;COUNT=1;UNTIL=12345", // can't have both COUNT and UNTIL
- //"FREQ=DAILY;UNTIL=19970829T021400e", // invalid date
- };
-
- /**
- * Simple test of good/bad rules.
- */
- @SmallTest
- public void testBasicParse() {
- for (String rule : GOOD_RRULES) {
- EventRecurrence recur = new EventRecurrence();
- recur.parse(rule);
- }
-
- for (String rule : BAD_RRULES) {
- EventRecurrence recur = new EventRecurrence();
- boolean didThrow = false;
-
- try {
- recur.parse(rule);
- } catch (InvalidFormatException ife) {
- didThrow = true;
- }
-
- assertTrue("Expected throw on " + rule, didThrow);
- }
- }
-}
diff --git a/core/tests/coretests/src/android/pim/RecurrenceSetTest.java b/core/tests/coretests/src/android/pim/RecurrenceSetTest.java
deleted file mode 100644
index e5ab179..0000000
--- a/core/tests/coretests/src/android/pim/RecurrenceSetTest.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.pim;
-
-import android.content.ContentValues;
-import android.pim.ICalendar;
-import android.pim.RecurrenceSet;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.util.Log;
-import android.provider.CalendarContract;
-import junit.framework.TestCase;
-
-/**
- * Test some pim.RecurrenceSet functionality.
- */
-public class RecurrenceSetTest extends TestCase {
-
- // Test a recurrence
- @SmallTest
- public void testRecurrenceSet0() throws Exception {
- String recurrence = "DTSTART;TZID=America/New_York:20080221T070000\n"
- + "DTEND;TZID=America/New_York:20080221T190000\n"
- + "RRULE:FREQ=DAILY;UNTIL=20080222T000000Z\n"
- + "EXDATE:20080222T120000Z";
- verifyPopulateContentValues(recurrence, "FREQ=DAILY;UNTIL=20080222T000000Z", null,
- null, "20080222T120000Z", 1203595200000L, "America/New_York", "P43200S", 0);
- }
-
- // Test 1 day all-day event
- @SmallTest
- public void testRecurrenceSet1() throws Exception {
- String recurrence = "DTSTART;VALUE=DATE:20090821\nDTEND;VALUE=DATE:20090822\n"
- + "RRULE:FREQ=YEARLY;WKST=SU";
- verifyPopulateContentValues(recurrence, "FREQ=YEARLY;WKST=SU", null,
- null, null, 1250812800000L, "UTC", "P1D", 1);
- }
-
- // Test 2 day all-day event
- @SmallTest
- public void testRecurrenceSet2() throws Exception {
- String recurrence = "DTSTART;VALUE=DATE:20090821\nDTEND;VALUE=DATE:20090823\n"
- + "RRULE:FREQ=YEARLY;WKST=SU";
- verifyPopulateContentValues(recurrence, "FREQ=YEARLY;WKST=SU", null,
- null, null, 1250812800000L, "UTC", "P2D", 1);
- }
-
- // run populateContentValues and verify the results
- private void verifyPopulateContentValues(String recurrence, String rrule, String rdate,
- String exrule, String exdate, long dtstart, String tzid, String duration, int allDay)
- throws ICalendar.FormatException {
- ICalendar.Component recurrenceComponent =
- new ICalendar.Component("DUMMY", null /* parent */);
- ICalendar.parseComponent(recurrenceComponent, recurrence);
- ContentValues values = new ContentValues();
- RecurrenceSet.populateContentValues(recurrenceComponent, values);
- Log.d("KS", "values " + values);
-
- assertEquals(rrule, values.get(android.provider.CalendarContract.Events.RRULE));
- assertEquals(rdate, values.get(android.provider.CalendarContract.Events.RDATE));
- assertEquals(exrule, values.get(android.provider.CalendarContract.Events.EXRULE));
- assertEquals(exdate, values.get(android.provider.CalendarContract.Events.EXDATE));
- assertEquals(dtstart, (long) values.getAsLong(CalendarContract.Events.DTSTART));
- assertEquals(tzid, values.get(android.provider.CalendarContract.Events.EVENT_TIMEZONE));
- assertEquals(duration, values.get(android.provider.CalendarContract.Events.DURATION));
- assertEquals(allDay,
- (int) values.getAsInteger(android.provider.CalendarContract.Events.ALL_DAY));
- }
-}
diff --git a/core/tests/coretests/src/android/widget/TextViewTest.java b/core/tests/coretests/src/android/widget/TextViewTest.java
index 6db67c0..c54e4a1 100644
--- a/core/tests/coretests/src/android/widget/TextViewTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewTest.java
@@ -16,23 +16,25 @@
package android.widget;
-import com.google.android.collect.Lists;
-import com.google.android.collect.Maps;
+import com.android.frameworks.coretests.R;
-import android.test.AndroidTestCase;
+import android.test.ActivityInstrumentationTestCase2;
import android.test.suitebuilder.annotation.SmallTest;
import android.text.GetChars;
import android.view.View;
-import android.widget.TextView;
/**
* TextViewTest tests {@link TextView}.
*/
-public class TextViewTest extends AndroidTestCase {
+public class TextViewTest extends ActivityInstrumentationTestCase2<TextViewTestActivity> {
+
+ public TextViewTest() {
+ super(TextViewTestActivity.class);
+ }
@SmallTest
public void testArray() throws Exception {
- TextView tv = new TextView(mContext);
+ TextView tv = new TextView(getActivity());
char[] c = new char[] { 'H', 'e', 'l', 'l', 'o', ' ',
'W', 'o', 'r', 'l', 'd', '!' };
@@ -62,13 +64,13 @@ public class TextViewTest extends AndroidTestCase {
@SmallTest
public void testTextDirectionDefault() {
- TextView tv = new TextView(mContext);
+ TextView tv = new TextView(getActivity());
assertEquals(View.TEXT_DIRECTION_INHERIT, tv.getTextDirection());
}
@SmallTest
public void testSetGetTextDirection() {
- TextView tv = new TextView(mContext);
+ TextView tv = new TextView(getActivity());
tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG);
assertEquals(View.TEXT_DIRECTION_FIRST_STRONG, tv.getTextDirection());
@@ -88,7 +90,7 @@ public class TextViewTest extends AndroidTestCase {
@SmallTest
public void testGetResolvedTextDirectionLtr() {
- TextView tv = new TextView(mContext);
+ TextView tv = new TextView(getActivity());
tv.setText("this is a test");
tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG);
@@ -109,10 +111,10 @@ public class TextViewTest extends AndroidTestCase {
@SmallTest
public void testGetResolvedTextDirectionLtrWithInheritance() {
- LinearLayout ll = new LinearLayout(mContext);
+ LinearLayout ll = new LinearLayout(getActivity());
ll.setTextDirection(View.TEXT_DIRECTION_RTL);
- TextView tv = new TextView(mContext);
+ TextView tv = new TextView(getActivity());
tv.setText("this is a test");
ll.addView(tv);
@@ -134,7 +136,7 @@ public class TextViewTest extends AndroidTestCase {
@SmallTest
public void testGetResolvedTextDirectionRtl() {
- TextView tv = new TextView(mContext);
+ TextView tv = new TextView(getActivity());
tv.setText("\u05DD\u05DE"); // hebrew
tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG);
@@ -155,10 +157,9 @@ public class TextViewTest extends AndroidTestCase {
@SmallTest
public void testGetResolvedTextDirectionRtlWithInheritance() {
- LinearLayout ll = new LinearLayout(mContext);
- ll.setTextDirection(View.TEXT_DIRECTION_RTL);
+ LinearLayout ll = new LinearLayout(getActivity());
- TextView tv = new TextView(mContext);
+ TextView tv = new TextView(getActivity());
tv.setText("\u05DD\u05DE"); // hebrew
ll.addView(tv);
@@ -169,6 +170,24 @@ public class TextViewTest extends AndroidTestCase {
assertEquals(View.TEXT_DIRECTION_RTL, tv.getResolvedTextDirection());
tv.setTextDirection(View.TEXT_DIRECTION_INHERIT);
+ assertEquals(View.TEXT_DIRECTION_FIRST_STRONG, tv.getResolvedTextDirection());
+
+ tv.setTextDirection(View.TEXT_DIRECTION_LTR);
+ assertEquals(View.TEXT_DIRECTION_LTR, tv.getResolvedTextDirection());
+
+ tv.setTextDirection(View.TEXT_DIRECTION_RTL);
+ assertEquals(View.TEXT_DIRECTION_RTL, tv.getResolvedTextDirection());
+
+ // Force to RTL text direction on the layout
+ ll.setTextDirection(View.TEXT_DIRECTION_RTL);
+
+ tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG);
+ assertEquals(View.TEXT_DIRECTION_RTL, tv.getResolvedTextDirection());
+
+ tv.setTextDirection(View.TEXT_DIRECTION_ANY_RTL);
+ assertEquals(View.TEXT_DIRECTION_RTL, tv.getResolvedTextDirection());
+
+ tv.setTextDirection(View.TEXT_DIRECTION_INHERIT);
assertEquals(View.TEXT_DIRECTION_RTL, tv.getResolvedTextDirection());
tv.setTextDirection(View.TEXT_DIRECTION_LTR);
@@ -180,10 +199,10 @@ public class TextViewTest extends AndroidTestCase {
@SmallTest
public void testCharCountHeuristic() {
- LinearLayout ll = new LinearLayout(mContext);
+ LinearLayout ll = new LinearLayout(getActivity());
ll.setLayoutDirection(View.LAYOUT_DIRECTION_RTL);
- TextView tv = new TextView(mContext);
+ TextView tv = new TextView(getActivity());
ll.addView(tv);
tv.setTextDirection(View.TEXT_DIRECTION_CHAR_COUNT);
@@ -211,4 +230,23 @@ public class TextViewTest extends AndroidTestCase {
tv.setText("ab \u05DD\u05DE"); // latin + hebrew at 50% each
assertEquals(View.TEXT_DIRECTION_RTL, tv.getResolvedTextDirection());
}
+
+ @SmallTest
+ public void testResetTextDirection() {
+ final TextViewTestActivity activity = getActivity();
+
+ final LinearLayout ll = (LinearLayout) activity.findViewById(R.id.textviewtest_layout);
+ final TextView tv = (TextView) activity.findViewById(R.id.textviewtest_textview);
+
+ getActivity().runOnUiThread(new Runnable() {
+ public void run() {
+ tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG);
+ assertEquals(View.TEXT_DIRECTION_RTL, tv.getResolvedTextDirection());
+ assertEquals(true, tv.isResolvedTextDirection());
+
+ ll.removeView(tv);
+ assertEquals(false, tv.isResolvedTextDirection());
+ }
+ });
+ }
}
diff --git a/core/tests/coretests/src/android/widget/TextViewTestActivity.java b/core/tests/coretests/src/android/widget/TextViewTestActivity.java
new file mode 100644
index 0000000..1bb4d24
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/TextViewTestActivity.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.widget;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+import com.android.frameworks.coretests.R;
+
+public class TextViewTestActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.textview_test);
+ }
+}