Merge change 5732 into donut
* changes:
Fix assertContentsInOrder (bug 1790350).
diff --git a/camera/libcameraservice/CameraService.cpp b/camera/libcameraservice/CameraService.cpp
index 105d4d2..022fe5a 100644
--- a/camera/libcameraservice/CameraService.cpp
+++ b/camera/libcameraservice/CameraService.cpp
@@ -410,11 +410,14 @@
// pass the buffered ISurface to the camera service
status_t CameraService::Client::setPreviewDisplay(const sp<ISurface>& surface)
{
- LOGD("setPreviewDisplay(%p) (pid %d)", surface.get(), getCallingPid());
+ LOGD("setPreviewDisplay(%p) (pid %d)",
+ ((surface == NULL) ? NULL : surface.get()), getCallingPid());
Mutex::Autolock lock(mLock);
status_t result = checkPid();
if (result != NO_ERROR) return result;
+
Mutex::Autolock surfaceLock(mSurfaceLock);
+ result = NO_ERROR;
// asBinder() is safe on NULL (returns NULL)
if (surface->asBinder() != mSurface->asBinder()) {
if (mSurface != 0 && !mUseOverlay) {
@@ -422,8 +425,17 @@
mSurface->unregisterBuffers();
}
mSurface = surface;
+ // If preview has been already started, set overlay or register preview
+ // buffers now.
+ if (mHardware->previewEnabled()) {
+ if (mUseOverlay) {
+ result = setOverlay();
+ } else if (mSurface != 0) {
+ result = registerPreviewBuffers();
+ }
+ }
}
- return NO_ERROR;
+ return result;
}
// set the preview callback flag to affect how the received frames from
@@ -436,7 +448,7 @@
mPreviewCallbackFlag = callback_flag;
}
-// start preview mode, must call setPreviewDisplay first
+// start preview mode
status_t CameraService::Client::startCameraMode(camera_mode mode)
{
int callingPid = getCallingPid();
@@ -456,16 +468,18 @@
return INVALID_OPERATION;
}
- if (mSurface == 0) {
- LOGE("setPreviewDisplay must be called before startCameraMode!");
- return INVALID_OPERATION;
- }
-
switch(mode) {
case CAMERA_RECORDING_MODE:
+ if (mSurface == 0) {
+ LOGE("setPreviewDisplay must be called before startRecordingMode.");
+ return INVALID_OPERATION;
+ }
return startRecordingMode();
default: // CAMERA_PREVIEW_MODE
+ if (mSurface == 0) {
+ LOGD("mSurface is not set yet.");
+ }
return startPreviewMode();
}
}
@@ -498,6 +512,62 @@
return ret;
}
+status_t CameraService::Client::setOverlay()
+{
+ LOGD("setOverlay");
+ int w, h;
+ CameraParameters params(mHardware->getParameters());
+ params.getPreviewSize(&w, &h);
+
+ const char *format = params.getPreviewFormat();
+ int fmt;
+ if (!strcmp(format, "yuv422i"))
+ fmt = OVERLAY_FORMAT_YCbCr_422_I;
+ else if (!strcmp(format, "rgb565"))
+ fmt = OVERLAY_FORMAT_RGB_565;
+ else {
+ LOGE("Invalid preview format for overlays");
+ return -EINVAL;
+ }
+
+ status_t ret = NO_ERROR;
+ if (mSurface != 0) {
+ sp<OverlayRef> ref = mSurface->createOverlay(w, h, fmt);
+ ret = mHardware->setOverlay(new Overlay(ref));
+ } else {
+ ret = mHardware->setOverlay(NULL);
+ }
+ if (ret != NO_ERROR) {
+ LOGE("mHardware->setOverlay() failed with status %d\n", ret);
+ }
+ return ret;
+}
+
+status_t CameraService::Client::registerPreviewBuffers()
+{
+ int w, h;
+ CameraParameters params(mHardware->getParameters());
+ params.getPreviewSize(&w, &h);
+
+ uint32_t transform = 0;
+ if (params.getOrientation() ==
+ CameraParameters::CAMERA_ORIENTATION_PORTRAIT) {
+ LOGV("portrait mode");
+ transform = ISurface::BufferHeap::ROT_90;
+ }
+ ISurface::BufferHeap buffers(w, h, w, h,
+ PIXEL_FORMAT_YCbCr_420_SP,
+ transform,
+ 0,
+ mHardware->getPreviewHeap());
+
+ status_t ret = mSurface->registerBuffers(buffers);
+ if (ret != NO_ERROR) {
+ LOGE("registerBuffers failed with status %d", ret);
+ }
+ return ret;
+}
+
status_t CameraService::Client::startPreviewMode()
{
LOGD("startPreviewMode (pid %d)", getCallingPid());
@@ -511,55 +581,24 @@
#if DEBUG_DUMP_PREVIEW_FRAME_TO_FILE
debug_frame_cnt = 0;
#endif
- status_t ret = UNKNOWN_ERROR;
- int w, h;
- CameraParameters params(mHardware->getParameters());
- params.getPreviewSize(&w, &h);
+ status_t ret = NO_ERROR;
if (mUseOverlay) {
- const char *format = params.getPreviewFormat();
- int fmt;
- LOGD("Use Overlays");
- if (!strcmp(format, "yuv422i"))
- fmt = OVERLAY_FORMAT_YCbCr_422_I;
- else if (!strcmp(format, "rgb565"))
- fmt = OVERLAY_FORMAT_RGB_565;
- else {
- LOGE("Invalid preview format for overlays");
- return -EINVAL;
+ // If preview display has been set, set overlay now.
+ if (mSurface != 0) {
+ ret = setOverlay();
}
- sp<OverlayRef> ref = mSurface->createOverlay(w, h, fmt);
- ret = mHardware->setOverlay(new Overlay(ref));
- if (ret != NO_ERROR) {
- LOGE("mHardware->setOverlay() failed with status %d\n", ret);
- return ret;
- }
+ if (ret != NO_ERROR) return ret;
ret = mHardware->startPreview(NULL, mCameraService.get());
- if (ret != NO_ERROR)
- LOGE("mHardware->startPreview() failed with status %d\n", ret);
-
} else {
ret = mHardware->startPreview(previewCallback,
mCameraService.get());
- if (ret == NO_ERROR) {
-
- mSurface->unregisterBuffers();
-
- uint32_t transform = 0;
- if (params.getOrientation() ==
- CameraParameters::CAMERA_ORIENTATION_PORTRAIT) {
- LOGV("portrait mode");
- transform = ISurface::BufferHeap::ROT_90;
- }
- ISurface::BufferHeap buffers(w, h, w, h,
- PIXEL_FORMAT_YCbCr_420_SP,
- transform,
- 0,
- mHardware->getPreviewHeap());
-
- mSurface->registerBuffers(buffers);
- } else {
- LOGE("mHardware->startPreview() failed with status %d", ret);
+ if (ret != NO_ERROR) return ret;
+ // If preview display has been set, register preview buffers now.
+ if (mSurface != 0) {
+ // Unregister here because the surface registered with raw heap.
+ mSurface->unregisterBuffers();
+ ret = registerPreviewBuffers();
}
}
return ret;
diff --git a/camera/libcameraservice/CameraService.h b/camera/libcameraservice/CameraService.h
index 8b8b54c..0f07673 100644
--- a/camera/libcameraservice/CameraService.h
+++ b/camera/libcameraservice/CameraService.h
@@ -157,6 +157,8 @@
status_t startCameraMode(camera_mode mode);
status_t startPreviewMode();
status_t startRecordingMode();
+ status_t setOverlay();
+ status_t registerPreviewBuffers();
// Ensures atomicity among the public methods
mutable Mutex mLock;
diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
index c3ddd20..c90b862 100644
--- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
+++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
@@ -62,6 +62,16 @@
String op = args[0];
mNextArg = 1;
+ if ("enabled".equals(op)) {
+ doEnabled();
+ return;
+ }
+
+ if ("enable".equals(op)) {
+ doEnable();
+ return;
+ }
+
if ("run".equals(op)) {
doRun();
return;
@@ -91,6 +101,41 @@
showUsage();
}
+ private String enableToString(boolean enabled) {
+ return enabled ? "enabled" : "disabled";
+ }
+
+ private void doEnabled() {
+ try {
+ boolean isEnabled = mBmgr.isBackupEnabled();
+ System.out.println("Backup Manager currently "
+ + enableToString(isEnabled));
+ } catch (RemoteException e) {
+ System.err.println(e.toString());
+ System.err.println(BMGR_NOT_RUNNING_ERR);
+ }
+ }
+
+ private void doEnable() {
+ String arg = nextArg();
+ if (arg == null) {
+ showUsage();
+ return;
+ }
+
+ try {
+ boolean enable = Boolean.parseBoolean(arg);
+ mBmgr.setBackupEnabled(enable);
+ System.out.println("Backup Manager now " + enableToString(enable));
+ } catch (NumberFormatException e) {
+ showUsage();
+ return;
+ } catch (RemoteException e) {
+ System.err.println(e.toString());
+ System.err.println(BMGR_NOT_RUNNING_ERR);
+ }
+ }
+
private void doRun() {
try {
mBmgr.backupNow();
@@ -291,6 +336,8 @@
private static void showUsage() {
System.err.println("usage: bmgr [backup|restore|list|transport|run]");
System.err.println(" bmgr backup PACKAGE");
+ System.err.println(" bmgr enable BOOL");
+ System.err.println(" bmgr enabled");
System.err.println(" bmgr list transports");
System.err.println(" bmgr list sets");
System.err.println(" bmgr transport WHICH");
@@ -301,6 +348,14 @@
System.err.println("Note that the backup pass will effectively be a no-op if the package");
System.err.println("does not actually have changed data to store.");
System.err.println("");
+ System.err.println("The 'enable' command enables or disables the entire backup mechanism.");
+ System.err.println("If the argument is 'true' it will be enabled, otherwise it will be");
+ System.err.println("disabled. When disabled, neither backup or restore operations will");
+ System.err.println("be performed.");
+ System.err.println("");
+ System.err.println("The 'enabled' command reports the current enabled/disabled state of");
+ System.err.println("the backup mechanism.");
+ System.err.println("");
System.err.println("The 'list transports' command reports the names of the backup transports");
System.err.println("currently available on the device. These names can be passed as arguments");
System.err.println("to the 'transport' command. The currently selected transport is indicated");
diff --git a/core/java/android/backup/IBackupManager.aidl b/core/java/android/backup/IBackupManager.aidl
index 39e160b..1f11762 100644
--- a/core/java/android/backup/IBackupManager.aidl
+++ b/core/java/android/backup/IBackupManager.aidl
@@ -48,6 +48,24 @@
void agentDisconnected(String packageName);
/**
+ * Enable/disable the backup service entirely. When disabled, no backup
+ * or restore operations will take place. Data-changed notifications will
+ * still be observed and collected, however, so that changes made while the
+ * mechanism was disabled will still be backed up properly if it is enabled
+ * at some point in the future.
+ *
+ * <p>Callers must hold the android.permission.BACKUP permission to use this method.
+ */
+ void setBackupEnabled(boolean isEnabled);
+
+ /**
+ * Report whether the backup mechanism is currently enabled.
+ *
+ * <p>Callers must hold the android.permission.BACKUP permission to use this method.
+ */
+ boolean isBackupEnabled();
+
+ /**
* Schedule an immediate backup attempt for all pending updates. This is
* primarily intended for transports to use when they detect a suitable
* opportunity for doing a backup pass. If there are no pending updates to
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 09fbc97..3ce951f 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -155,7 +155,11 @@
* @throws IOException if the method fails.
*/
public final void setPreviewDisplay(SurfaceHolder holder) throws IOException {
- setPreviewDisplay(holder.getSurface());
+ if (holder != null) {
+ setPreviewDisplay(holder.getSurface());
+ } else {
+ setPreviewDisplay((Surface)null);
+ }
}
private native final void setPreviewDisplay(Surface surface);
diff --git a/core/java/android/net/http/RequestHandle.java b/core/java/android/net/http/RequestHandle.java
index c4ee5b0..6a97951 100644
--- a/core/java/android/net/http/RequestHandle.java
+++ b/core/java/android/net/http/RequestHandle.java
@@ -159,11 +159,11 @@
e.printStackTrace();
}
- // update the "cookie" header based on the redirected url
- mHeaders.remove("cookie");
+ // update the "Cookie" header based on the redirected url
+ mHeaders.remove("Cookie");
String cookie = CookieManager.getInstance().getCookie(mUri);
if (cookie != null && cookie.length() > 0) {
- mHeaders.put("cookie", cookie);
+ mHeaders.put("Cookie", cookie);
}
if ((statusCode == 302 || statusCode == 303) && mMethod.equals("POST")) {
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 51d1951..bc7b5be 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -344,7 +344,10 @@
// Check if file exists with a FileInputStream
FileInputStream stream = new FileInputStream(imagePath);
try {
- return insertImage(cr, BitmapFactory.decodeFile(imagePath), name, description);
+ Bitmap bm = BitmapFactory.decodeFile(imagePath);
+ String ret = insertImage(cr, bm, name, description);
+ bm.recycle();
+ return ret;
} finally {
try {
stream.close();
diff --git a/core/java/android/speech/tts/ITts.aidl b/core/java/android/speech/tts/ITts.aidl
index 47976e5..15f3876 100755
--- a/core/java/android/speech/tts/ITts.aidl
+++ b/core/java/android/speech/tts/ITts.aidl
@@ -43,6 +43,10 @@
void addSpeechFile(in String text, in String filename);
+ String[] getLanguage();
+
+ int isLanguageAvailable(in String language, in String country, in String variant);
+
void setLanguage(in String language, in String country, in String variant);
boolean synthesizeToFile(in String text, in String[] params, in String outputDirectory);
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
index 8fa06fa..b245713 100755
--- a/core/java/android/speech/tts/TextToSpeech.java
+++ b/core/java/android/speech/tts/TextToSpeech.java
@@ -251,14 +251,17 @@
*
* @param resourceId
* Example: <b><code>R.raw.south_south_east</code></b>
+ *
+ * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
*/
- public void addSpeech(String text, String packagename, int resourceId) {
+ public int addSpeech(String text, String packagename, int resourceId) {
synchronized(mStartLock) {
if (!mStarted) {
- return;
+ return TTS_ERROR;
}
try {
mITts.addSpeech(text, packagename, resourceId);
+ return TTS_SUCCESS;
} catch (RemoteException e) {
// TTS died; restart it.
mStarted = false;
@@ -272,6 +275,7 @@
mStarted = false;
initTts();
}
+ return TTS_ERROR;
}
}
@@ -285,14 +289,17 @@
* @param filename
* The full path to the sound file (for example:
* "/sdcard/mysounds/hello.wav")
+ *
+ * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
*/
- public void addSpeech(String text, String filename) {
+ public int addSpeech(String text, String filename) {
synchronized (mStartLock) {
if (!mStarted) {
- return;
+ return TTS_ERROR;
}
try {
mITts.addSpeechFile(text, filename);
+ return TTS_SUCCESS;
} catch (RemoteException e) {
// TTS died; restart it.
mStarted = false;
@@ -306,6 +313,7 @@
mStarted = false;
initTts();
}
+ return TTS_ERROR;
}
}
@@ -324,17 +332,20 @@
* See TTS_QUEUE_ADD and TTS_QUEUE_FLUSH.
* @param params
* The hashmap of speech parameters to be used.
+ *
+ * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
*/
- public void speak(String text, int queueMode, HashMap<String,String> params)
+ public int speak(String text, int queueMode, HashMap<String,String> params)
{
synchronized (mStartLock) {
Log.i("TTS received: ", text);
if (!mStarted) {
- return;
+ return TTS_ERROR;
}
try {
// TODO support extra parameters, passing cache of current parameters for the moment
mITts.speak(text, queueMode, mCachedParams);
+ return TTS_SUCCESS;
} catch (RemoteException e) {
// TTS died; restart it.
mStarted = false;
@@ -348,6 +359,7 @@
mStarted = false;
initTts();
}
+ return TTS_ERROR;
}
}
@@ -366,17 +378,22 @@
* See TTS_QUEUE_ADD and TTS_QUEUE_FLUSH.
* @param params
* The hashmap of speech parameters to be used.
+ *
+ * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
+ *
+ * {@hide}
*/
- public void speakIpa(String ipaText, int queueMode, HashMap<String,String> params)
+ public int speakIpa(String ipaText, int queueMode, HashMap<String,String> params)
{
synchronized (mStartLock) {
Log.i("TTS received: ", ipaText);
if (!mStarted) {
- return;
+ return TTS_ERROR;
}
try {
// TODO support extra parameters, passing cache of current parameters for the moment
mITts.speakIpa(ipaText, queueMode, mCachedParams);
+ return TTS_SUCCESS;
} catch (RemoteException e) {
// TTS died; restart it.
mStarted = false;
@@ -390,6 +407,7 @@
mStarted = false;
initTts();
}
+ return TTS_ERROR;
}
}
@@ -403,16 +421,19 @@
* See TTS_QUEUE_ADD and TTS_QUEUE_FLUSH.
* @param params
* The hashmap of parameters to be used.
+ *
+ * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
*/
- public void playEarcon(String earcon, int queueMode,
+ public int playEarcon(String earcon, int queueMode,
HashMap<String,String> params) {
synchronized (mStartLock) {
if (!mStarted) {
- return;
+ return TTS_ERROR;
}
try {
// TODO support extra parameters, passing null for the moment
mITts.playEarcon(earcon, queueMode, null);
+ return TTS_SUCCESS;
} catch (RemoteException e) {
// TTS died; restart it.
mStarted = false;
@@ -426,18 +447,30 @@
mStarted = false;
initTts();
}
+ return TTS_ERROR;
}
}
-
- public void playSilence(long durationInMs, int queueMode) {
+ /**
+ * Plays silence for the specified amount of time using the specified
+ * queue mode.
+ *
+ * @param durationInMs
+ * A long that indicates how long the silence should last.
+ * @param queueMode
+ * See TTS_QUEUE_ADD and TTS_QUEUE_FLUSH.
+ *
+ * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
+ */
+ public int playSilence(long durationInMs, int queueMode) {
synchronized (mStartLock) {
if (!mStarted) {
- return;
+ return TTS_ERROR;
}
try {
// TODO support extra parameters, passing cache of current parameters for the moment
mITts.playSilence(durationInMs, queueMode, mCachedParams);
+ return TTS_SUCCESS;
} catch (RemoteException e) {
// TTS died; restart it.
mStarted = false;
@@ -451,6 +484,7 @@
mStarted = false;
initTts();
}
+ return TTS_ERROR;
}
}
@@ -487,14 +521,17 @@
/**
* Stops speech from the TTS.
+ *
+ * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
*/
- public void stop() {
+ public int stop() {
synchronized (mStartLock) {
if (!mStarted) {
- return;
+ return TTS_ERROR;
}
try {
mITts.stop();
+ return TTS_SUCCESS;
} catch (RemoteException e) {
// TTS died; restart it.
mStarted = false;
@@ -508,6 +545,7 @@
mStarted = false;
initTts();
}
+ return TTS_ERROR;
}
}
@@ -524,23 +562,27 @@
* The speech rate for the TTS engine. 1 is the normal speed,
* lower values slow down the speech (0.5 is half the normal speech rate),
* greater values accelerate it (2 is twice the normal speech rate).
+ *
+ * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
*/
- public void setSpeechRate(float speechRate) {
+ public int setSpeechRate(float speechRate) {
synchronized (mStartLock) {
if (!mStarted) {
- return;
+ return TTS_SUCCESS;
}
try {
if (speechRate > 0) {
mCachedRate = (int)(speechRate*100);
updateCachedParamArray();
mITts.setSpeechRate(mCachedRate);
+ return TTS_SUCCESS;
}
} catch (RemoteException e) {
// TTS died; restart it.
mStarted = false;
initTts();
}
+ return TTS_ERROR;
}
}
@@ -557,21 +599,25 @@
* The pitch for the TTS engine. 1 is the normal pitch,
* lower values lower the tone of the synthesized voice,
* greater values increase it.
+ *
+ * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
*/
- public void setPitch(float pitch) {
+ public int setPitch(float pitch) {
synchronized (mStartLock) {
if (!mStarted) {
- return;
+ return TTS_ERROR;
}
try {
if (pitch > 0) {
mITts.setPitch((int)(pitch*100));
+ return TTS_SUCCESS;
}
} catch (RemoteException e) {
// TTS died; restart it.
mStarted = false;
initTts();
}
+ return TTS_ERROR;
}
}
@@ -585,11 +631,13 @@
*
* @param loc
* The locale describing the language to be used.
+ *
+ * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
*/
- public void setLanguage(Locale loc) {
+ public int setLanguage(Locale loc) {
synchronized (mStartLock) {
if (!mStarted) {
- return;
+ return TTS_ERROR;
}
try {
mCachedLang = loc.getISO3Language();
@@ -597,29 +645,70 @@
mCachedVariant = loc.getVariant();
updateCachedParamArray();
mITts.setLanguage(mCachedLang, mCachedCountry, mCachedVariant);
+ return TTS_SUCCESS;
} catch (RemoteException e) {
// TTS died; restart it.
mStarted = false;
initTts();
}
+ return TTS_ERROR;
+ }
+ }
+
+
+ /**
+ * Returns a Locale instance describing the language currently being used by the TTS engine.
+ * @return language, country (if any) and variant (if any) used by the engine stored in a Locale
+ * instance, or null is the TTS engine has failed.
+ */
+ public Locale getLanguage() {
+ synchronized (mStartLock) {
+ if (!mStarted) {
+ return null;
+ }
+ try {
+ String[] locStrings = mITts.getLanguage();
+ if (locStrings.length == 3) {
+ return new Locale(locStrings[0], locStrings[1], locStrings[2]);
+ } else {
+ return null;
+ }
+ } catch (RemoteException e) {
+ // TTS died; restart it.
+ mStarted = false;
+ initTts();
+ }
+ return null;
}
}
/**
- * Checks if the specified language as represented by the locale is available.
+ * Checks if the specified language as represented by the Locale is available.
*
* @param loc
- * The locale describing the language to be used.
+ * The Locale describing the language to be used.
+ *
* @return one of TTS_LANG_NOT_SUPPORTED, TTS_LANG_MISSING_DATA, TTS_LANG_AVAILABLE,
- TTS_LANG_COUNTRY_AVAILABLE, TTS_LANG_COUNTRY_VAR_AVAILABLE.
+ * TTS_LANG_COUNTRY_AVAILABLE, TTS_LANG_COUNTRY_VAR_AVAILABLE.
*/
public int isLanguageAvailable(Locale loc) {
- //TODO: Implement isLanguageAvailable
- return TTS_LANG_NOT_SUPPORTED;
+ synchronized (mStartLock) {
+ if (!mStarted) {
+ return TTS_LANG_NOT_SUPPORTED;
+ }
+ try {
+ return mITts.isLanguageAvailable(loc.getISO3Language(), loc.getISO3Country(),
+ loc.getVariant());
+ } catch (RemoteException e) {
+ // TTS died; restart it.
+ mStarted = false;
+ initTts();
+ }
+ return TTS_LANG_NOT_SUPPORTED;
+ }
}
-
/**
* Synthesizes the given text to a file using the specified parameters.
*
@@ -630,17 +719,20 @@
* @param filename
* The string that gives the full output filename; it should be
* something like "/sdcard/myappsounds/mysound.wav".
- * @return A boolean that indicates if the synthesis succeeded
+ *
+ * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
*/
- public boolean synthesizeToFile(String text, HashMap<String,String> params,
+ public int synthesizeToFile(String text, HashMap<String,String> params,
String filename) {
synchronized (mStartLock) {
if (!mStarted) {
- return false;
+ return TTS_ERROR;
}
try {
// TODO support extra parameters, passing null for the moment
- return mITts.synthesizeToFile(text, null, filename);
+ if (mITts.synthesizeToFile(text, null, filename)){
+ return TTS_SUCCESS;
+ }
} catch (RemoteException e) {
// TTS died; restart it.
mStarted = false;
@@ -654,10 +746,11 @@
mStarted = false;
initTts();
}
- return false;
+ return TTS_ERROR;
}
}
+
/**
* Synthesizes the given IPA text to a file using the specified parameters.
*
@@ -668,17 +761,22 @@
* @param filename
* The string that gives the full output filename; it should be
* something like "/sdcard/myappsounds/mysound.wav".
- * @return A boolean that indicates if the synthesis succeeded
+ *
+ * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
+ *
+ * {@hide}
*/
- public boolean synthesizeIpaToFile(String ipaText,
+ public int synthesizeIpaToFile(String ipaText,
HashMap<String,String> params, String filename) {
synchronized (mStartLock) {
if (!mStarted) {
- return false;
+ return TTS_ERROR;
}
try {
// TODO support extra parameters, passing null for the moment
- return mITts.synthesizeIpaToFile(ipaText, null, filename);
+ if (mITts.synthesizeIpaToFile(ipaText, null, filename)){
+ return TTS_SUCCESS;
+ }
} catch (RemoteException e) {
// TTS died; restart it.
mStarted = false;
@@ -692,7 +790,7 @@
mStarted = false;
initTts();
}
- return false;
+ return TTS_ERROR;
}
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index c9a785c..b3180ca 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -5922,8 +5922,9 @@
int height = mBottom - mTop;
final AttachInfo attachInfo = mAttachInfo;
+ final boolean scalingRequired = attachInfo != null && attachInfo.mScalingRequired;
- if (autoScale && attachInfo != null && attachInfo.mScalingRequired) {
+ if (autoScale && scalingRequired) {
width = (int) ((width * attachInfo.mApplicationScale) + 0.5f);
height = (int) ((height * attachInfo.mApplicationScale) + 0.5f);
}
@@ -6014,7 +6015,7 @@
computeScroll();
final int restoreCount = canvas.save();
- if (autoScale && attachInfo.mScalingRequired) {
+ if (autoScale && scalingRequired) {
final float scale = attachInfo.mApplicationScale;
canvas.scale(scale, scale);
}
diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java
index 9a0f467..7393737 100644
--- a/core/java/android/view/inputmethod/BaseInputConnection.java
+++ b/core/java/android/view/inputmethod/BaseInputConnection.java
@@ -340,10 +340,19 @@
}
/**
- * The default implementation does nothing.
+ * The default implementation turns this into the enter key.
*/
public boolean performEditorAction(int actionCode) {
- return false;
+ long eventTime = SystemClock.uptimeMillis();
+ sendKeyEvent(new KeyEvent(eventTime, eventTime,
+ KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER, 0, 0, 0, 0,
+ KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE
+ | KeyEvent.FLAG_EDITOR_ACTION));
+ sendKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), eventTime,
+ KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER, 0, 0, 0, 0,
+ KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE
+ | KeyEvent.FLAG_EDITOR_ACTION));
+ return true;
}
/**
diff --git a/core/java/android/webkit/FrameLoader.java b/core/java/android/webkit/FrameLoader.java
index 6f1b160..66ab021 100644
--- a/core/java/android/webkit/FrameLoader.java
+++ b/core/java/android/webkit/FrameLoader.java
@@ -364,7 +364,7 @@
String cookie = CookieManager.getInstance().getCookie(
mListener.getWebAddress());
if (cookie != null && cookie.length() > 0) {
- mHeaders.put("cookie", cookie);
+ mHeaders.put("Cookie", cookie);
}
}
}
diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java
index 12bb01c..e62dda5 100644
--- a/core/java/android/widget/RelativeLayout.java
+++ b/core/java/android/widget/RelativeLayout.java
@@ -558,9 +558,9 @@
myWidth);
int childHeightMeasureSpec;
if (params.width == LayoutParams.FILL_PARENT) {
- childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.EXACTLY, myHeight);
+ childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(myHeight, MeasureSpec.EXACTLY);
} else {
- childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+ childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(myHeight, MeasureSpec.AT_MOST);
}
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
@@ -1403,7 +1403,9 @@
/*
* START POOL IMPLEMENTATION
*/
- private static final int POOL_LIMIT = 12;
+ // The pool is static, so all nodes instances are shared across
+ // activities, that's why we give it a rather high limit
+ private static final int POOL_LIMIT = 100;
private static final Pool<Node> sPool = Pools.synchronizedPool(
Pools.finitePool(new PoolableManager<Node>() {
public Node newInstance() {
diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp
index 8e48b38..3550716 100644
--- a/core/jni/android_hardware_Camera.cpp
+++ b/core/jni/android_hardware_Camera.cpp
@@ -262,7 +262,10 @@
sp<Camera> camera = get_native_camera(env, thiz, NULL);
if (camera == 0) return;
- sp<Surface> surface = reinterpret_cast<Surface*>(env->GetIntField(jSurface, fields.surface));
+ sp<Surface> surface = NULL;
+ if (jSurface != NULL) {
+ surface = reinterpret_cast<Surface*>(env->GetIntField(jSurface, fields.surface));
+ }
if (camera->setPreviewDisplay(surface) != NO_ERROR) {
jniThrowException(env, "java/io/IOException", "setPreviewDisplay failed");
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 7f24dcc..599360f 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -973,7 +973,7 @@
<permission android:name="android.permission.BACKUP"
android:label="@string/permlab_backup"
android:description="@string/permdesc_backup"
- android:protectionLevel="signature" />
+ android:protectionLevel="signatureOrSystem" />
<!-- Allows an application to tell the AppWidget service which application
can access AppWidget's data. The normal user flow is that a user
diff --git a/core/res/res/layout/character_picker.xml b/core/res/res/layout/character_picker.xml
index bb4955a..0344849 100644
--- a/core/res/res/layout/character_picker.xml
+++ b/core/res/res/layout/character_picker.xml
@@ -23,8 +23,8 @@
android:id="@+id/characterPicker"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:padding="12dp"
- android:verticalSpacing="8dp"
+ android:padding="4dp"
+ android:verticalSpacing="4dp"
android:horizontalSpacing="8dp"
android:stretchMode="spacingWidth"
android:gravity="left"
diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java
index f8b88d0..376b1df 100644
--- a/graphics/java/android/graphics/drawable/DrawableContainer.java
+++ b/graphics/java/android/graphics/drawable/DrawableContainer.java
@@ -234,8 +234,10 @@
@Override
public Drawable mutate() {
if (!mMutated && super.mutate() == this) {
- for (Drawable child : mDrawableContainerState.mDrawables) {
- child.mutate();
+ final int N = mDrawableContainerState.getChildCount();
+ final Drawable[] drawables = mDrawableContainerState.getChildren();
+ for (int i = 0; i < N; i++) {
+ drawables[i].mutate();
}
mMutated = true;
}
diff --git a/include/tts/TtsEngine.h b/include/tts/TtsEngine.h
index ca50a5e..21cb73b 100644
--- a/include/tts/TtsEngine.h
+++ b/include/tts/TtsEngine.h
@@ -133,16 +133,26 @@
// @return TTS_SUCCESS, or TTS_FAILURE
virtual tts_result setLanguage(const char *lang, const char *country, const char *variant);
- // Retrieve the currently set language, or an empty "value" if no language
- // has been set.
- // @param[out] value pointer to the retrieved language value
- // @param[inout] iosize in: stores the size available to store the language
- // value in *value
- // out: stores the size required to hold the language
- // value if getLanguage() returned
- // TTS_PROPERTY_SIZE_TOO_SMALL, unchanged otherwise.
- // @return TTS_SUCCESS, or TTS_PROPERTY_SIZE_TOO_SMALL, or TTS_FAILURE
- virtual tts_result getLanguage(char *value, size_t *iosize);
+ // Retrieve the currently set language, country and variant, or empty strings if none of
+ // parameters have been set. Language and country are represented by their 3-letter ISO code
+ // @param[out] pointer to the retrieved 3-letter code language value
+ // @param[out] pointer to the retrieved 3-letter code country value
+ // @param[out] pointer to the retrieved variant value
+ // @return TTS_SUCCESS, or TTS_FAILURE
+ virtual tts_result getLanguage(char *language, char *country, char *variant);
+
+ // Notifies the engine what audio parameters should be used for the synthesis.
+ // This is meant to be used as a hint, the engine implementation will set the output values
+ // to those of the synthesis format, based on a given hint.
+ // @param[inout] encoding in: the desired audio sample format
+ // out: the format used by the TTS engine
+ // @param[inout] rate in: the desired audio sample rate
+ // out: the sample rate used by the TTS engine
+ // @param[inout] channels in: the desired number of audio channels
+ // out: the number of channels used by the TTS engine
+ // @return TTS_SUCCESS, or TTS_FAILURE
+ virtual tts_result setAudioFormat(AudioSystem::audio_format& encoding, uint32_t& rate,
+ int& channels);
// Set a property for the the TTS engine
// "size" is the maximum size of "value" for properties "property"
diff --git a/libs/surfaceflinger/SurfaceFlinger.cpp b/libs/surfaceflinger/SurfaceFlinger.cpp
index 0e998bf..ef4a8ea 100644
--- a/libs/surfaceflinger/SurfaceFlinger.cpp
+++ b/libs/surfaceflinger/SurfaceFlinger.cpp
@@ -771,10 +771,11 @@
dirty.orSelf(layer->visibleRegionScreen);
layer->contentDirty = false;
} else {
- // compute the exposed region
- // dirty = what's visible now - what's wasn't covered before
- // = what's visible now & what's was covered before
- dirty = visibleRegion.intersect(layer->coveredRegionScreen);
+ /* compute the exposed region:
+ * exposed = what's VISIBLE and NOT COVERED now
+ * but was COVERED before
+ */
+ dirty = (visibleRegion - coveredRegion) & layer->coveredRegionScreen;
}
dirty.subtractSelf(aboveOpaqueLayers);
@@ -783,7 +784,7 @@
// updade aboveOpaqueLayers/aboveCoveredLayers for next (lower) layer
aboveOpaqueLayers.orSelf(opaqueRegion);
- aboveCoveredLayers.orSelf(bounds);
+ aboveCoveredLayers.orSelf(visibleRegion);
// Store the visible region is screen space
layer->setVisibleRegion(visibleRegion);
diff --git a/libs/ui/Camera.cpp b/libs/ui/Camera.cpp
index bb22dab..a481ce7 100644
--- a/libs/ui/Camera.cpp
+++ b/libs/ui/Camera.cpp
@@ -149,21 +149,21 @@
status_t Camera::setPreviewDisplay(const sp<Surface>& surface)
{
LOGV("setPreviewDisplay");
- if (surface == 0) {
- LOGE("app passed NULL surface");
- return NO_INIT;
- }
sp <ICamera> c = mCamera;
if (c == 0) return NO_INIT;
- return c->setPreviewDisplay(surface->getISurface());
+ if (surface != 0) {
+ return c->setPreviewDisplay(surface->getISurface());
+ } else {
+ LOGD("app passed NULL surface");
+ return c->setPreviewDisplay(0);
+ }
}
status_t Camera::setPreviewDisplay(const sp<ISurface>& surface)
{
LOGV("setPreviewDisplay");
if (surface == 0) {
- LOGE("app passed NULL surface");
- return NO_INIT;
+ LOGD("app passed NULL surface");
}
sp <ICamera> c = mCamera;
if (c == 0) return NO_INIT;
@@ -171,7 +171,7 @@
}
-// start preview mode, must call setPreviewDisplay first
+// start preview mode
status_t Camera::startPreview()
{
LOGV("startPreview");
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java
index fd3a4ba..e76967d 100755
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java
@@ -501,4 +501,6 @@
"http://sridharg.googlejunta.com/yslau/stress_media/mp3_regular.mp3";
public static final String STREAM_MPEG4_QVGA_128k =
"http://sridharg.googlejunta.com/yslau/stress_media/mpeg4_qvga_24fps.3gp";
+ public static final int STREAM_H264_480_360_1411k_DURATION = 46000;
+ public static final int VIDEO_H263_AAC_DURATION = 501000;
}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaRecorderStressTestRunner.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaRecorderStressTestRunner.java
index 12eacd3..ae9e102 100755
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaRecorderStressTestRunner.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaRecorderStressTestRunner.java
@@ -19,6 +19,7 @@
import android.test.InstrumentationTestRunner;
import android.test.InstrumentationTestSuite;
import com.android.mediaframeworktest.stress.MediaRecorderStressTest;
+import com.android.mediaframeworktest.stress.MediaPlayerStressTest;
import junit.framework.TestSuite;
@@ -28,6 +29,7 @@
public TestSuite getAllTests() {
TestSuite suite = new InstrumentationTestSuite(this);
suite.addTestSuite(MediaRecorderStressTest.class);
+ suite.addTestSuite(MediaPlayerStressTest.class);
return suite;
}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaPlayerStressTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaPlayerStressTest.java
new file mode 100644
index 0000000..5e213d7
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaPlayerStressTest.java
@@ -0,0 +1,136 @@
+/*
+ * 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 com.android.mediaframeworktest.stress;
+
+import com.android.mediaframeworktest.MediaFrameworkTest;
+
+import android.hardware.Camera;
+import android.media.MediaPlayer;
+import android.media.MediaRecorder;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.util.Log;
+import android.view.SurfaceHolder;
+
+import com.android.mediaframeworktest.MediaNames;
+
+import java.util.Random;
+
+/**
+ * Junit / Instrumentation test case for the media player
+ */
+public class MediaPlayerStressTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> {
+ private String TAG = "MediaPlayerStressTest";
+ private MediaRecorder mRecorder;
+ private Camera mCamera;
+
+ private static final int NUMBER_OF_RANDOM_REPOSITION_AND_PLAY = 10;
+ private static final int NUMBER_OF_RANDOM_REPOSITION_AND_PLAY_SHORT = 5;
+ private static final int NUMBER_OF_STRESS_LOOPS = 1000;
+ private static final int PLAYBACK_END_TOLERANCE = 5000;
+ private static final int WAIT_UNTIL_PLAYBACK_FINISH = 515000 ;
+
+ public MediaPlayerStressTest() {
+ super("com.android.mediaframeworktest", MediaFrameworkTest.class);
+ }
+
+ protected void setUp() throws Exception {
+ getActivity();
+ super.setUp();
+ }
+
+ @LargeTest
+ public void testStressHWDecoderRelease() throws Exception {
+ SurfaceHolder mSurfaceHolder;
+ long randomseed = System.currentTimeMillis();
+ Random generator = new Random(randomseed);
+ Log.v(TAG, "Random seed: " + randomseed);
+ int video_duration = MediaNames.STREAM_H264_480_360_1411k_DURATION;
+ int random_play_time;
+
+ mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder();
+ try {
+ for (int i = 0; i < NUMBER_OF_STRESS_LOOPS; i++) {
+ MediaPlayer mp = new MediaPlayer();
+ mp.setDataSource(MediaNames.STREAM_H264_480_360_1411k);
+ mp.setDisplay(MediaFrameworkTest.mSurfaceView.getHolder());
+ mp.prepare();
+ mp.start();
+ // seek and play
+ for (int j = 0; j < generator.nextInt(10); j++) {
+ random_play_time =
+ generator.nextInt(MediaNames.STREAM_H264_480_360_1411k_DURATION / 2);
+ Log.v(TAG, "Play time = " + random_play_time);
+ Thread.sleep(random_play_time);
+ int seek_time = MediaNames.STREAM_H264_480_360_1411k_DURATION / 2;
+ Log.v(TAG, "Seek time = " + seek_time);
+ mp.seekTo(seek_time);
+ }
+ mp.release();
+ }
+
+ } catch (Exception e) {
+ Log.v(TAG, e.toString());
+ }
+ }
+
+ @LargeTest
+ public void testStressGetCurrentPosition() throws Exception {
+ SurfaceHolder mSurfaceHolder;
+ long randomseed = System.currentTimeMillis();
+ Random generator = new Random(randomseed);
+ Log.v(TAG, "Random seed: " + randomseed);
+ int video_duration = MediaNames.VIDEO_H263_AAC_DURATION;
+ int random_play_time = 0;
+ int random_seek_time = 0;
+
+ mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder();
+ try {
+ for (int i = 0; i < NUMBER_OF_STRESS_LOOPS; i++) {
+ MediaPlayer mp = new MediaPlayer();
+ mp.setDataSource(MediaNames.VIDEO_H263_AMR);
+ mp.setDisplay(MediaFrameworkTest.mSurfaceView.getHolder());
+ mp.prepare();
+ mp.start();
+ // Random seek and play
+ for (int j = 0; j < generator.nextInt(10); j++) {
+ random_play_time =
+ generator.nextInt(video_duration / 2);
+ Log.v(TAG, "Play time = " + random_play_time);
+ Thread.sleep(random_play_time);
+ random_seek_time =
+ generator.nextInt(video_duration / 2);
+ Log.v(TAG, "Seek time = " + random_seek_time);
+ mp.seekTo(random_seek_time);
+ }
+ //wait until the movie finish and check the current position
+ //Make sure the wait time is long enough
+ long wait_until_playback_finish = video_duration - random_seek_time + PLAYBACK_END_TOLERANCE * 2;
+ Thread.sleep(wait_until_playback_finish);
+ Log.v(TAG, "CurrentPosition = " + mp.getCurrentPosition());
+ if ( mp.isPlaying() || mp.getCurrentPosition() > (video_duration + PLAYBACK_END_TOLERANCE)){
+ assertTrue("Current PlayTime greater than duration", false);
+ }
+ mp.release();
+ }
+
+ } catch (Exception e) {
+ Log.v(TAG, e.toString());
+ }
+ }
+}
+
diff --git a/packages/TtsService/AndroidManifest.xml b/packages/TtsService/AndroidManifest.xml
index 1dc25c6..fab2534 100755
--- a/packages/TtsService/AndroidManifest.xml
+++ b/packages/TtsService/AndroidManifest.xml
@@ -12,4 +12,5 @@
</service>
</application>
<uses-permission android:name="android.permission.INTERNET"/>
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
</manifest>
diff --git a/packages/TtsService/jni/android_tts_SynthProxy.cpp b/packages/TtsService/jni/android_tts_SynthProxy.cpp
index 8537cae..a55b704 100644
--- a/packages/TtsService/jni/android_tts_SynthProxy.cpp
+++ b/packages/TtsService/jni/android_tts_SynthProxy.cpp
@@ -199,6 +199,7 @@
if (wav == NULL) {
delete pForAfter;
LOGV("Null: speech has completed");
+ return TTS_CALLBACK_HALT;
}
if (bufferSize > 0){
fwrite(wav, 1, bufferSize, pForAfter->outputFile);
@@ -213,8 +214,12 @@
// this struct was allocated in the original android_tts_SynthProxy_speak call,
// all processing matching this call is now done.
LOGV("Speech synthesis done.");
- delete pForAfter;
- pForAfter = NULL;
+ if (pForAfter->usageMode == USAGEMODE_PLAY_IMMEDIATELY) {
+ // only delete for direct playback. When writing to a file, we still have work to do
+ // in android_tts_SynthProxy_synthesizeToFile. The struct will be deleted there.
+ delete pForAfter;
+ pForAfter = NULL;
+ }
return TTS_CALLBACK_HALT;
}
@@ -278,6 +283,33 @@
}
+static int
+android_tts_SynthProxy_isLanguageAvailable(JNIEnv *env, jobject thiz, jint jniData,
+ jstring language, jstring country, jstring variant)
+{
+ int result = TTS_LANG_NOT_SUPPORTED;
+
+ if (jniData == 0) {
+ LOGE("android_tts_SynthProxy_isLanguageAvailable(): invalid JNI data");
+ return result;
+ }
+
+ SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
+ const char *langNativeString = env->GetStringUTFChars(language, 0);
+ const char *countryNativeString = env->GetStringUTFChars(country, 0);
+ const char *variantNativeString = env->GetStringUTFChars(variant, 0);
+ // TODO check return codes
+ if (pSynthData->mNativeSynthInterface) {
+ result = pSynthData->mNativeSynthInterface->isLanguageAvailable(langNativeString,
+ countryNativeString, variantNativeString);
+ }
+ env->ReleaseStringUTFChars(language, langNativeString);
+ env->ReleaseStringUTFChars(country, countryNativeString);
+ env->ReleaseStringUTFChars(variant, variantNativeString);
+ return result;
+}
+
+
static void
android_tts_SynthProxy_setLanguage(JNIEnv *env, jobject thiz, jint jniData,
jstring language, jstring country, jstring variant)
@@ -370,7 +402,6 @@
}
-// TODO: Refactor this to get rid of any assumptions about sample rate, etc.
static void
android_tts_SynthProxy_synthesizeToFile(JNIEnv *env, jobject thiz, jint jniData,
jstring textJavaString, jstring filenameJavaString)
@@ -381,6 +412,21 @@
}
SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
+ if (!pSynthData->mNativeSynthInterface) {
+ LOGE("android_tts_SynthProxy_synthesizeToFile(): invalid engine handle");
+ return;
+ }
+
+ // Retrieve audio parameters before writing the file header
+ AudioSystem::audio_format encoding = DEFAULT_TTS_FORMAT;
+ uint32_t rate = DEFAULT_TTS_RATE;
+ int channels = DEFAULT_TTS_NB_CHANNELS;
+ pSynthData->mNativeSynthInterface->setAudioFormat(encoding, rate, channels);
+
+ if ((encoding != AudioSystem::PCM_16_BIT) && (encoding != AudioSystem::PCM_8_BIT)) {
+ LOGE("android_tts_SynthProxy_synthesizeToFile(): engine uses invalid format");
+ return;
+ }
const char *filenameNativeString =
env->GetStringUTFChars(filenameJavaString, 0);
@@ -392,6 +438,12 @@
pForAfter->outputFile = fopen(filenameNativeString, "wb");
+ if (pForAfter->outputFile == NULL) {
+ LOGE("android_tts_SynthProxy_synthesizeToFile(): error creating output file");
+ delete pForAfter;
+ return;
+ }
+
// Write 44 blank bytes for WAV header, then come back and fill them in
// after we've written the audio data
char header[44];
@@ -400,10 +452,8 @@
unsigned int unique_identifier;
// TODO check return codes
- if (pSynthData->mNativeSynthInterface) {
- pSynthData->mNativeSynthInterface->synthesizeText(textNativeString, pSynthData->mBuffer, pSynthData->mBufferSize,
- (void *)pForAfter);
- }
+ pSynthData->mNativeSynthInterface->synthesizeText(textNativeString, pSynthData->mBuffer,
+ pSynthData->mBufferSize, (void *)pForAfter);
long filelen = ftell(pForAfter->outputFile);
@@ -425,12 +475,14 @@
((uint32_t *)(&header[16]))[0] = 16; // size of fmt
+ int sampleSizeInByte = (encoding == AudioSystem::PCM_16_BIT ? 2 : 1);
+
((unsigned short *)(&header[20]))[0] = 1; // format
- ((unsigned short *)(&header[22]))[0] = 1; // channels
- ((uint32_t *)(&header[24]))[0] = 22050; // samplerate
- ((uint32_t *)(&header[28]))[0] = 44100; // byterate
- ((unsigned short *)(&header[32]))[0] = 2; // block align
- ((unsigned short *)(&header[34]))[0] = 16; // bits per sample
+ ((unsigned short *)(&header[22]))[0] = channels; // channels
+ ((uint32_t *)(&header[24]))[0] = rate; // samplerate
+ ((uint32_t *)(&header[28]))[0] = rate * sampleSizeInByte * channels;// byterate
+ ((unsigned short *)(&header[32]))[0] = sampleSizeInByte * channels; // block align
+ ((unsigned short *)(&header[34]))[0] = sampleSizeInByte * 8; // bits per sample
header[36] = 'd';
header[37] = 'a';
@@ -446,6 +498,9 @@
fflush(pForAfter->outputFile);
fclose(pForAfter->outputFile);
+ delete pForAfter;
+ pForAfter = NULL;
+
env->ReleaseStringUTFChars(textJavaString, textNativeString);
env->ReleaseStringUTFChars(filenameJavaString, filenameNativeString);
}
@@ -473,8 +528,8 @@
if (pSynthData->mNativeSynthInterface) {
const char *textNativeString = env->GetStringUTFChars(textJavaString, 0);
- pSynthData->mNativeSynthInterface->synthesizeText(textNativeString, pSynthData->mBuffer, pSynthData->mBufferSize,
- (void *)pForAfter);
+ pSynthData->mNativeSynthInterface->synthesizeText(textNativeString, pSynthData->mBuffer,
+ pSynthData->mBufferSize, (void *)pForAfter);
env->ReleaseStringUTFChars(textJavaString, textNativeString);
}
}
@@ -533,23 +588,34 @@
}
-JNIEXPORT jstring JNICALL
+static jobjectArray
android_tts_SynthProxy_getLanguage(JNIEnv *env, jobject thiz, jint jniData)
{
if (jniData == 0) {
LOGE("android_tts_SynthProxy_getLanguage(): invalid JNI data");
- return env->NewStringUTF("");
+ return NULL;
}
SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
- size_t bufSize = 100;
- char buf[bufSize];
- memset(buf, 0, bufSize);
- // TODO check return codes
+
if (pSynthData->mNativeSynthInterface) {
- pSynthData->mNativeSynthInterface->getLanguage(buf, &bufSize);
+ size_t bufSize = 100;
+ char lang[bufSize];
+ char country[bufSize];
+ char variant[bufSize];
+ memset(lang, 0, bufSize);
+ memset(country, 0, bufSize);
+ memset(variant, 0, bufSize);
+ jobjectArray retLocale = (jobjectArray)env->NewObjectArray(3,
+ env->FindClass("java/lang/String"), env->NewStringUTF(""));
+ pSynthData->mNativeSynthInterface->getLanguage(lang, country, variant);
+ env->SetObjectArrayElement(retLocale, 0, env->NewStringUTF(lang));
+ env->SetObjectArrayElement(retLocale, 1, env->NewStringUTF(country));
+ env->SetObjectArrayElement(retLocale, 2, env->NewStringUTF(variant));
+ return retLocale;
+ } else {
+ return NULL;
}
- return env->NewStringUTF(buf);
}
@@ -587,6 +653,10 @@
"(ILjava/lang/String;Ljava/lang/String;)V",
(void*)android_tts_SynthProxy_synthesizeToFile
},
+ { "native_isLanguageAvailable",
+ "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
+ (void*)android_tts_SynthProxy_isLanguageAvailable
+ },
{ "native_setLanguage",
"(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V",
(void*)android_tts_SynthProxy_setLanguage
@@ -608,7 +678,7 @@
(void*)android_tts_SynthProxy_playAudioBuffer
},
{ "native_getLanguage",
- "(I)Ljava/lang/String;",
+ "(I)[Ljava/lang/String;",
(void*)android_tts_SynthProxy_getLanguage
},
{ "native_getRate",
diff --git a/packages/TtsService/src/android/tts/SynthProxy.java b/packages/TtsService/src/android/tts/SynthProxy.java
index a8eaaa43..91fe3b7 100755
--- a/packages/TtsService/src/android/tts/SynthProxy.java
+++ b/packages/TtsService/src/android/tts/SynthProxy.java
@@ -68,12 +68,20 @@
// TODO add IPA methods
/**
- * Sets the language
+ * Queries for language support.
+ * Return codes are defined in android.speech.tts.TextToSpeech
+ */
+ public int isLanguageAvailable(String language, String country, String variant) {
+ return native_isLanguageAvailable(mJniData, language, country, variant);
+ }
+
+ /**
+ * Sets the language.
*/
public void setLanguage(String language, String country, String variant) {
native_setLanguage(mJniData, language, country, variant);
}
-
+
/**
* Loads the language: it's not set, but prepared for use later.
*/
@@ -82,42 +90,42 @@
}
/**
- * Sets the speech rate
+ * Sets the speech rate.
*/
public final void setSpeechRate(int speechRate) {
native_setSpeechRate(mJniData, speechRate);
}
/**
- * Sets the pitch of the synthesized voice
+ * Sets the pitch of the synthesized voice.
*/
public final void setPitch(int pitch) {
native_setPitch(mJniData, pitch);
}
/**
- * Plays the given audio buffer
+ * Plays the given audio buffer.
*/
public void playAudioBuffer(int bufferPointer, int bufferSize) {
native_playAudioBuffer(mJniData, bufferPointer, bufferSize);
}
/**
- * Gets the currently set language
+ * Returns the currently set language, country and variant information.
*/
- public String getLanguage() {
+ public String[] getLanguage() {
return native_getLanguage(mJniData);
}
/**
- * Gets the currently set rate
+ * Gets the currently set rate.
*/
public int getRate() {
return native_getRate(mJniData);
}
/**
- * Shuts down the native synthesizer
+ * Shuts down the native synthesizer.
*/
public void shutdown() {
native_shutdown(mJniData);
@@ -154,20 +162,23 @@
private native final void native_synthesizeToFile(int jniData, String text, String filename);
+ private native final int native_isLanguageAvailable(int jniData, String language,
+ String country, String variant);
+
private native final void native_setLanguage(int jniData, String language, String country,
String variant);
-
+
private native final void native_loadLanguage(int jniData, String language, String country,
String variant);
private native final void native_setSpeechRate(int jniData, int speechRate);
-
+
private native final void native_setPitch(int jniData, int speechRate);
// TODO add buffer format
private native final void native_playAudioBuffer(int jniData, int bufferPointer, int bufferSize);
- private native final String native_getLanguage(int jniData);
+ private native final String[] native_getLanguage(int jniData);
private native final int native_getRate(int jniData);
diff --git a/packages/TtsService/src/android/tts/TtsService.java b/packages/TtsService/src/android/tts/TtsService.java
index 421b2ca..b1e6425 100755
--- a/packages/TtsService/src/android/tts/TtsService.java
+++ b/packages/TtsService/src/android/tts/TtsService.java
@@ -91,6 +91,7 @@
}
private static final int MAX_SPEECH_ITEM_CHAR_LENGTH = 4000;
+ private static final int MAX_FILENAME_LENGTH = 250;
private static final String ACTION = "android.intent.action.USE_TTS";
private static final String CATEGORY = "android.intent.category.TTS";
@@ -147,8 +148,7 @@
private void setDefaultSettings() {
- // TODO handle default language
- setLanguage("eng", "USA", "");
+ setLanguage(this.getDefaultLanguage(), getDefaultCountry(), getDefaultLocVariant());
// speech rate
setSpeechRate(getDefaultRate());
@@ -217,6 +217,17 @@
}
+ private int isLanguageAvailable(String lang, String country, String variant) {
+ Log.v("TTS", "TtsService.isLanguageAvailable(" + lang + ", " + country + ", " +variant+")");
+ return nativeSynth.isLanguageAvailable(lang, country, variant);
+ }
+
+
+ private String[] getLanguage() {
+ return nativeSynth.getLanguage();
+ }
+
+
private void setLanguage(String lang, String country, String variant) {
Log.v("TTS", "TtsService.setLanguage(" + lang + ", " + country + ", " + variant + ")");
if (isDefaultEnforced()) {
@@ -414,6 +425,26 @@
synth.start();
return;
}
+ if (params != null){
+ String language = "";
+ String country = "";
+ String variant = "";
+ for (int i = 0; i < params.size() - 1; i = i + 2){
+ String param = params.get(i);
+ if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_RATE)){
+ setSpeechRate(Integer.parseInt(params.get(i+1)));
+ } else if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_LANGUAGE)){
+ language = params.get(i+1);
+ } else if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_COUNTRY)){
+ country = params.get(i+1);
+ } else if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_VARIANT)){
+ variant = params.get(i+1);
+ }
+ }
+ if (language.length() > 0){
+ setLanguage(language, country, variant);
+ }
+ }
nativeSynth.speak(text);
} catch (InterruptedException e) {
e.printStackTrace();
@@ -614,8 +645,7 @@
return false;
}
// Don't allow a filename that is too long
- // TODO use platform constant
- if (filename.length() > 250) {
+ if (filename.length() > MAX_FILENAME_LENGTH) {
return false;
}
nativeSynth.synthesizeToFile(text, filename);
@@ -660,8 +690,7 @@
return false;
}
// Don't allow a filename that is too long
- // TODO use platform constant
- if (filename.length() > 250) {
+ if (filename.length() > MAX_FILENAME_LENGTH) {
return false;
}
// TODO: Add nativeSynth.synthesizeIpaToFile(text, filename);
@@ -874,6 +903,30 @@
}
/**
+ * Returns the level of support for the specified language.
+ *
+ * @param lang the three letter ISO language code.
+ * @param country the three letter ISO country code.
+ * @param variant the variant code associated with the country and language pair.
+ * @return one of TTS_LANG_NOT_SUPPORTED, TTS_LANG_MISSING_DATA, TTS_LANG_AVAILABLE,
+ * TTS_LANG_COUNTRY_AVAILABLE, TTS_LANG_COUNTRY_VAR_AVAILABLE as defined in
+ * android.speech.tts.TextToSpeech.
+ */
+ public int isLanguageAvailable(String lang, String country, String variant) {
+ return mSelf.isLanguageAvailable(lang, country, variant);
+ }
+
+ /**
+ * Returns the currently set language / country / variant strings representing the
+ * language used by the TTS engine.
+ * @return null is no language is set, or an array of 3 string containing respectively
+ * the language, country and variant.
+ */
+ public String[] getLanguage() {
+ return mSelf.getLanguage();
+ }
+
+ /**
* Sets the speech rate for the TTS, which affects the synthesized voice.
*
* @param lang the three letter ISO language code.
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 2b9ac4d..c67f0b5 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -74,6 +74,10 @@
private static final String TAG = "BackupManagerService";
private static final boolean DEBUG = true;
+ // Persistent properties
+ private static final String BACKUP_TRANSPORT_PROPERTY = "persist.service.bkup.trans";
+ private static final String BACKUP_ENABLED_PROPERTY = "persist.service.bkup.enabled";
+
// Default time to wait after data changes before we back up the data
private static final long COLLECTION_INTERVAL = 3 * 60 * 1000;
@@ -86,10 +90,11 @@
private Context mContext;
private PackageManager mPackageManager;
- private final IActivityManager mActivityManager;
+ private IActivityManager mActivityManager;
+ private boolean mEnabled; // access to this is synchronized on 'this'
private final BackupHandler mBackupHandler = new BackupHandler();
// map UIDs to the set of backup client services within that UID's app set
- private SparseArray<HashSet<ApplicationInfo>> mBackupParticipants
+ private final SparseArray<HashSet<ApplicationInfo>> mBackupParticipants
= new SparseArray<HashSet<ApplicationInfo>>();
// set of backup services that have pending changes
private class BackupRequest {
@@ -128,7 +133,6 @@
private volatile boolean mClearingData;
// Transport bookkeeping
- static private final String BACKUP_TRANSPORT_PROPERTY = "persist.service.bkup.trans";
private final HashMap<String,IBackupTransport> mTransports
= new HashMap<String,IBackupTransport>();
private String mCurrentTransport;
@@ -160,6 +164,9 @@
mActivityManager = ActivityManagerNative.getDefault();
// Set up our bookkeeping
+ // !!! STOPSHIP: make this disabled by default so that we then gate on
+ // setupwizard or other opt-out UI
+ mEnabled = SystemProperties.getBoolean(BACKUP_ENABLED_PROPERTY, true);
mBaseStateDir = new File(Environment.getDataDirectory(), "backup");
mDataDir = Environment.getDownloadCacheDirectory();
@@ -489,8 +496,15 @@
// The queue lock should be held when scheduling a backup pass
private void scheduleBackupPassLocked(long timeFromNowMillis) {
- mBackupHandler.removeMessages(MSG_RUN_BACKUP);
- mBackupHandler.sendEmptyMessageDelayed(MSG_RUN_BACKUP, timeFromNowMillis);
+ // We only schedule backups when we're actually enabled
+ synchronized (this) {
+ if (mEnabled) {
+ mBackupHandler.removeMessages(MSG_RUN_BACKUP);
+ mBackupHandler.sendEmptyMessageDelayed(MSG_RUN_BACKUP, timeFromNowMillis);
+ } else if (DEBUG) {
+ Log.v(TAG, "Disabled, so not scheduling backup pass");
+ }
+ }
}
// Return the given transport
@@ -1087,7 +1101,7 @@
if (DEBUG) {
int numKeys = mPendingBackups.size();
- Log.d(TAG, "Scheduling backup for " + numKeys + " participants:");
+ Log.d(TAG, "Now awaiting backup for " + numKeys + " participants:");
for (BackupRequest b : mPendingBackups.values()) {
Log.d(TAG, " + " + b + " agent=" + b.appInfo.backupAgentName);
}
@@ -1117,7 +1131,7 @@
// Run a backup pass immediately for any applications that have declared
// that they have pending updates.
public void backupNow() throws RemoteException {
- mContext.enforceCallingPermission("android.permission.BACKUP", "tryBackupNow");
+ mContext.enforceCallingPermission("android.permission.BACKUP", "backupNow");
if (DEBUG) Log.v(TAG, "Scheduling immediate backup pass");
synchronized (mQueueLock) {
@@ -1125,16 +1139,43 @@
}
}
+ // Enable/disable the backup transport
+ public void setBackupEnabled(boolean enable) {
+ mContext.enforceCallingPermission("android.permission.BACKUP", "setBackupEnabled");
+
+ boolean wasEnabled = mEnabled;
+ synchronized (this) {
+ SystemProperties.set(BACKUP_ENABLED_PROPERTY, enable ? "true" : "false");
+ mEnabled = enable;
+ }
+
+ if (enable && !wasEnabled) {
+ synchronized (mQueueLock) {
+ if (mPendingBackups.size() > 0) {
+ // !!! TODO: better policy around timing of the first backup pass
+ if (DEBUG) Log.v(TAG, "Backup enabled with pending data changes, scheduling");
+ this.scheduleBackupPassLocked(COLLECTION_INTERVAL);
+ }
+ }
+ }
+}
+
+ // Report whether the backup mechanism is currently enabled
+ public boolean isBackupEnabled() {
+ mContext.enforceCallingPermission("android.permission.BACKUP", "isBackupEnabled");
+ return mEnabled; // no need to synchronize just to read it
+ }
+
// Report the name of the currently active transport
public String getCurrentTransport() {
- mContext.enforceCallingPermission("android.permission.BACKUP", "selectBackupTransport");
+ mContext.enforceCallingPermission("android.permission.BACKUP", "getCurrentTransport");
Log.v(TAG, "getCurrentTransport() returning " + mCurrentTransport);
return mCurrentTransport;
}
// Report all known, available backup transports
public String[] listAllTransports() {
- mContext.enforceCallingPermission("android.permission.BACKUP", "selectBackupTransport");
+ mContext.enforceCallingPermission("android.permission.BACKUP", "listAllTransports");
String[] list = null;
ArrayList<String> known = new ArrayList<String>();
@@ -1292,7 +1333,8 @@
synchronized (mQueueLock) {
pw.println("Available transports:");
for (String t : listAllTransports()) {
- pw.println(" " + t);
+ String pad = (t.equals(mCurrentTransport)) ? " * " : " ";
+ pw.println(pad + t);
}
int N = mBackupParticipants.size();
pw.println("Participants:");
@@ -1302,14 +1344,12 @@
pw.println(uid);
HashSet<ApplicationInfo> participants = mBackupParticipants.valueAt(i);
for (ApplicationInfo app: participants) {
- pw.print(" ");
- pw.println(app.toString());
+ pw.println(" " + app.toString());
}
}
pw.println("Pending: " + mPendingBackups.size());
for (BackupRequest req : mPendingBackups.values()) {
- pw.print(" ");
- pw.println(req);
+ pw.println(" " + req);
}
}
}
diff --git a/services/java/com/android/server/PackageManagerBackupAgent.java b/services/java/com/android/server/PackageManagerBackupAgent.java
index 66fb86d..16f14e8 100644
--- a/services/java/com/android/server/PackageManagerBackupAgent.java
+++ b/services/java/com/android/server/PackageManagerBackupAgent.java
@@ -120,7 +120,10 @@
// write its signature block to the output, keyed on the package name.
for (PackageInfo pkg : mAllPackages) {
String packName = pkg.packageName;
- if (!existing.contains(packName)) {
+ if (packName.equals(GLOBAL_METADATA_KEY)) {
+ // We've already handled the metadata key; skip it here
+ continue;
+ } else if (!existing.contains(packName)) {
// We haven't stored this app's signatures yet, so we do that now
try {
PackageInfo info = mPackageManager.getPackageInfo(packName,
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 5ea7504..9bad153 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -3834,7 +3834,7 @@
"dispatchPointer " + ev);
Object targetObj = mKeyWaiter.waitForNextEventTarget(null, qev,
- ev, true, false);
+ ev, true, false, pid, uid);
int action = ev.getAction();
@@ -4032,7 +4032,7 @@
TAG, "dispatchTrackball [" + ev.getAction() +"] <" + ev.getX() + ", " + ev.getY() + ">");
Object focusObj = mKeyWaiter.waitForNextEventTarget(null, qev,
- ev, false, false);
+ ev, false, false, pid, uid);
if (focusObj == null) {
Log.w(TAG, "No focus window, dropping trackball: " + ev);
if (qev != null) {
@@ -4103,7 +4103,7 @@
if (DEBUG_INPUT) Log.v(TAG, "Dispatch key: " + event);
Object focusObj = mKeyWaiter.waitForNextEventTarget(event, null,
- null, false, false);
+ null, false, false, pid, uid);
if (focusObj == null) {
Log.w(TAG, "No focus window, dropping: " + event);
return INJECT_FAILED;
@@ -4220,10 +4220,14 @@
KeyEvent newEvent = new KeyEvent(downTime, eventTime, action, code, repeatCount, metaState,
deviceId, scancode, KeyEvent.FLAG_FROM_SYSTEM);
- int result = dispatchKey(newEvent, Binder.getCallingPid(), Binder.getCallingUid());
+ final int pid = Binder.getCallingPid();
+ final int uid = Binder.getCallingUid();
+ final long ident = Binder.clearCallingIdentity();
+ final int result = dispatchKey(newEvent, pid, uid);
if (sync) {
- mKeyWaiter.waitForNextEventTarget(null, null, null, false, true);
+ mKeyWaiter.waitForNextEventTarget(null, null, null, false, true, pid, uid);
}
+ Binder.restoreCallingIdentity(ident);
switch (result) {
case INJECT_NO_PERMISSION:
throw new SecurityException(
@@ -4244,10 +4248,14 @@
* @return Returns true if event was dispatched, false if it was dropped for any reason
*/
public boolean injectPointerEvent(MotionEvent ev, boolean sync) {
- int result = dispatchPointer(null, ev, Binder.getCallingPid(), Binder.getCallingUid());
+ final int pid = Binder.getCallingPid();
+ final int uid = Binder.getCallingUid();
+ final long ident = Binder.clearCallingIdentity();
+ final int result = dispatchPointer(null, ev, pid, uid);
if (sync) {
- mKeyWaiter.waitForNextEventTarget(null, null, null, false, true);
+ mKeyWaiter.waitForNextEventTarget(null, null, null, false, true, pid, uid);
}
+ Binder.restoreCallingIdentity(ident);
switch (result) {
case INJECT_NO_PERMISSION:
throw new SecurityException(
@@ -4268,10 +4276,14 @@
* @return Returns true if event was dispatched, false if it was dropped for any reason
*/
public boolean injectTrackballEvent(MotionEvent ev, boolean sync) {
- int result = dispatchTrackball(null, ev, Binder.getCallingPid(), Binder.getCallingUid());
+ final int pid = Binder.getCallingPid();
+ final int uid = Binder.getCallingUid();
+ final long ident = Binder.clearCallingIdentity();
+ final int result = dispatchTrackball(null, ev, pid, uid);
if (sync) {
- mKeyWaiter.waitForNextEventTarget(null, null, null, false, true);
+ mKeyWaiter.waitForNextEventTarget(null, null, null, false, true, pid, uid);
}
+ Binder.restoreCallingIdentity(ident);
switch (result) {
case INJECT_NO_PERMISSION:
throw new SecurityException(
@@ -4380,7 +4392,7 @@
*/
Object waitForNextEventTarget(KeyEvent nextKey, QueuedEvent qev,
MotionEvent nextMotion, boolean isPointerEvent,
- boolean failIfTimeout) {
+ boolean failIfTimeout, int callingPid, int callingUid) {
long startTime = SystemClock.uptimeMillis();
long keyDispatchingTimeout = 5 * 1000;
long waitedFor = 0;
@@ -4398,7 +4410,7 @@
", mLastWin=" + mLastWin);
if (targetIsNew) {
Object target = findTargetWindow(nextKey, qev, nextMotion,
- isPointerEvent);
+ isPointerEvent, callingPid, callingUid);
if (target == SKIP_TARGET_TOKEN) {
// The user has pressed a special key, and we are
// dropping all pending events before it.
@@ -4574,7 +4586,8 @@
}
Object findTargetWindow(KeyEvent nextKey, QueuedEvent qev,
- MotionEvent nextMotion, boolean isPointerEvent) {
+ MotionEvent nextMotion, boolean isPointerEvent,
+ int callingPid, int callingUid) {
mOutsideTouchTargets = null;
if (nextKey != null) {
@@ -4583,9 +4596,16 @@
final int repeatCount = nextKey.getRepeatCount();
final boolean down = nextKey.getAction() != KeyEvent.ACTION_UP;
boolean dispatch = mKeyWaiter.checkShouldDispatchKey(keycode);
+
if (!dispatch) {
- mPolicy.interceptKeyTi(null, keycode,
- nextKey.getMetaState(), down, repeatCount);
+ if (callingUid == 0 ||
+ mContext.checkPermission(
+ android.Manifest.permission.INJECT_EVENTS,
+ callingPid, callingUid)
+ == PackageManager.PERMISSION_GRANTED) {
+ mPolicy.interceptKeyTi(null, keycode,
+ nextKey.getMetaState(), down, repeatCount);
+ }
Log.w(TAG, "Event timeout during app switch: dropping "
+ nextKey);
return SKIP_TARGET_TOKEN;
@@ -4600,9 +4620,16 @@
wakeupIfNeeded(focus, LocalPowerManager.BUTTON_EVENT);
- if (mPolicy.interceptKeyTi(focus,
- keycode, nextKey.getMetaState(), down, repeatCount)) {
- return CONSUMED_EVENT_TOKEN;
+ if (callingUid == 0 ||
+ (focus != null && callingUid == focus.mSession.mUid) ||
+ mContext.checkPermission(
+ android.Manifest.permission.INJECT_EVENTS,
+ callingPid, callingUid)
+ == PackageManager.PERMISSION_GRANTED) {
+ if (mPolicy.interceptKeyTi(focus,
+ keycode, nextKey.getMetaState(), down, repeatCount)) {
+ return CONSUMED_EVENT_TOKEN;
+ }
}
return focus;
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index d3942fc..dbe8431 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -701,17 +701,6 @@
}
/**
- * Note: calls extractNetworkPortion(), so do not use for
- * SIM EF[ADN] style records
- *
- * Exceptions thrown if extractNetworkPortion(s).length() == 0
- */
- public static byte[]
- networkPortionToCalledPartyBCD(String s) {
- return numberToCalledPartyBCD(extractNetworkPortion(s));
- }
-
- /**
* Return true iff the network portion of <code>address</code> is,
* as far as we can tell on the device, suitable for use as an SMS
* destination address.
@@ -744,12 +733,25 @@
}
/**
+ * Note: calls extractNetworkPortion(), so do not use for
+ * SIM EF[ADN] style records
+ *
+ * Returns null if network portion is empty.
+ */
+ public static byte[]
+ networkPortionToCalledPartyBCD(String s) {
+ String networkPortion = extractNetworkPortion(s);
+ return numberToCalledPartyBCDHelper(networkPortion, false);
+ }
+
+ /**
* Same as {@link #networkPortionToCalledPartyBCD}, but includes a
* one-byte length prefix.
*/
public static byte[]
networkPortionToCalledPartyBCDWithLength(String s) {
- return numberToCalledPartyBCDWithLength(extractNetworkPortion(s));
+ String networkPortion = extractNetworkPortion(s);
+ return numberToCalledPartyBCDHelper(networkPortion, true);
}
/**
@@ -761,61 +763,46 @@
*/
public static byte[]
numberToCalledPartyBCD(String number) {
- // The extra byte required for '+' is taken into consideration while calculating
- // length of ret.
- int size = (hasPlus(number) ? number.length() - 1 : number.length());
- byte[] ret = new byte[(size + 1) / 2 + 1];
-
- return numberToCalledPartyBCDHelper(ret, 0, number);
+ return numberToCalledPartyBCDHelper(number, false);
}
/**
- * Same as {@link #numberToCalledPartyBCD}, but includes a
- * one-byte length prefix.
+ * If includeLength is true, prepend a one-byte length value to
+ * the return array.
*/
private static byte[]
- numberToCalledPartyBCDWithLength(String number) {
- // The extra byte required for '+' is taken into consideration while calculating
- // length of ret.
- int size = (hasPlus(number) ? number.length() - 1 : number.length());
- int length = (size + 1) / 2 + 1;
- byte[] ret = new byte[length + 1];
+ numberToCalledPartyBCDHelper(String number, boolean includeLength) {
+ int numberLenReal = number.length();
+ int numberLenEffective = numberLenReal;
+ boolean hasPlus = number.indexOf('+') != -1;
+ if (hasPlus) numberLenEffective--;
- ret[0] = (byte) (length & 0xff);
- return numberToCalledPartyBCDHelper(ret, 1, number);
- }
+ if (numberLenEffective == 0) return null;
- private static boolean
- hasPlus(String s) {
- return s.indexOf('+') >= 0;
- }
+ int resultLen = (numberLenEffective + 1) / 2; // Encoded numbers require only 4 bits each.
+ int extraBytes = 1; // Prepended TOA byte.
+ if (includeLength) extraBytes++; // Optional prepended length byte.
+ resultLen += extraBytes;
- private static byte[]
- numberToCalledPartyBCDHelper(byte[] ret, int offset, String number) {
- if (hasPlus(number)) {
- number = number.replaceAll("\\+", "");
- ret[offset] = (byte) TOA_International;
- } else {
- ret[offset] = (byte) TOA_Unknown;
+ byte[] result = new byte[resultLen];
+
+ int digitCount = 0;
+ for (int i = 0; i < numberLenReal; i++) {
+ char c = number.charAt(i);
+ if (c == '+') continue;
+ int shift = ((digitCount & 0x01) == 1) ? 4 : 0;
+ result[extraBytes + (digitCount >> 1)] |= (byte)((charToBCD(c) & 0x0F) << shift);
+ digitCount++;
}
- int size = number.length();
- int curChar = 0;
- int countFullBytes = ret.length - offset - 1 - ((size - curChar) & 1);
- for (int i = 1; i < 1 + countFullBytes; i++) {
- ret[offset + i]
- = (byte) ((charToBCD(number.charAt(curChar++)))
- | (charToBCD(number.charAt(curChar++))) << 4);
- }
+ // 1-fill any trailing odd nibble/quartet.
+ if ((digitCount & 0x01) == 1) result[extraBytes + (digitCount >> 1)] |= 0xF0;
- // The left-over octet for odd-length phone numbers should be
- // filled with 0xf.
- if (countFullBytes + offset < ret.length - 1) {
- ret[ret.length - 1]
- = (byte) (charToBCD(number.charAt(curChar))
- | (0xf << 4));
- }
- return ret;
+ int offset = 0;
+ if (includeLength) result[offset++] = (byte)(resultLen - 1);
+ result[offset] = (byte)(hasPlus ? TOA_International : TOA_Unknown);
+
+ return result;
}
/** all of 'a' up to len must match non-US trunk prefix ('0') */
diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
index 03bdbda..eaeda64 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
@@ -83,7 +83,7 @@
public static final int MESSAGE_TYPE_DELIVER_REPORT = 0x07;
public static final int MESSAGE_TYPE_SUBMIT_REPORT = 0x08;
- public byte messageType;
+ public int messageType;
/**
* 16-bit value indicating the message ID, which increments modulo 65536.
@@ -102,7 +102,7 @@
public static final int PRIORITY_EMERGENCY = 0x3;
public boolean priorityIndicatorSet = false;
- public byte priority = PRIORITY_NORMAL;
+ public int priority = PRIORITY_NORMAL;
/**
* Supported privacy modes for CDMA SMS messages
@@ -114,7 +114,7 @@
public static final int PRIVACY_SECRET = 0x3;
public boolean privacyIndicatorSet = false;
- public byte privacy = PRIVACY_NOT_RESTRICTED;
+ public int privacy = PRIVACY_NOT_RESTRICTED;
/**
* Supported alert priority modes for CDMA SMS messages
@@ -139,7 +139,7 @@
public static final int DISPLAY_MODE_USER = 0x2;
public boolean displayModeSet = false;
- public byte displayMode = DISPLAY_MODE_DEFAULT;
+ public int displayMode = DISPLAY_MODE_DEFAULT;
/**
* Language Indicator values. NOTE: the spec (3GPP2 C.S0015-B,
@@ -789,7 +789,7 @@
if (inStream.read(8) != 3) {
throw new CodingException("MESSAGE_IDENTIFIER subparam size incorrect");
}
- bData.messageType = (byte)inStream.read(4);
+ bData.messageType = inStream.read(4);
bData.messageId = inStream.read(8) << 8;
bData.messageId |= inStream.read(8);
bData.hasUserDataHeader = (inStream.read(1) == 1);
@@ -919,6 +919,122 @@
}
}
+ /**
+ * IS-91 Voice Mail message decoding
+ * (See 3GPP2 C.S0015-A, Table 4.3.1.4.1-1)
+ * (For character encodings, see TIA/EIA/IS-91, Annex B)
+ *
+ * Protocol Summary: The user data payload may contain 3-14
+ * characters. The first two characters are parsed as a number
+ * and indicate the number of voicemails. The third character is
+ * either a SPACE or '!' to indicate normal or urgent priority,
+ * respectively. Any following characters are treated as normal
+ * text user data payload.
+ *
+ * Note that the characters encoding is 6-bit packed.
+ */
+ private static void decodeIs91VoicemailStatus(BearerData bData)
+ throws BitwiseInputStream.AccessException, CodingException
+ {
+ BitwiseInputStream inStream = new BitwiseInputStream(bData.userData.payload);
+ int dataLen = inStream.available() / 6; // 6-bit packed character encoding.
+ int numFields = bData.userData.numFields;
+ if ((dataLen > 14) || (dataLen < 3) || (dataLen < numFields)) {
+ throw new CodingException("IS-91 voicemail status decoding failed");
+ }
+ try {
+ StringBuffer strbuf = new StringBuffer(dataLen);
+ while (inStream.available() >= 6) {
+ strbuf.append(UserData.IA5_MAP[inStream.read(6)]);
+ }
+ String data = strbuf.toString();
+ bData.numberOfMessages = Integer.parseInt(data.substring(0, 2));
+ char prioCode = data.charAt(2);
+ if (prioCode == ' ') {
+ bData.priority = PRIORITY_NORMAL;
+ } else if (prioCode == '!') {
+ bData.priority = PRIORITY_URGENT;
+ } else {
+ throw new CodingException("IS-91 voicemail status decoding failed: " +
+ "illegal priority setting (" + prioCode + ")");
+ }
+ bData.priorityIndicatorSet = true;
+ bData.userData.payloadStr = data.substring(3, numFields - 3);
+ } catch (java.lang.NumberFormatException ex) {
+ throw new CodingException("IS-91 voicemail status decoding failed: " + ex);
+ } catch (java.lang.IndexOutOfBoundsException ex) {
+ throw new CodingException("IS-91 voicemail status decoding failed: " + ex);
+ }
+ }
+
+ /**
+ * IS-91 Short Message decoding
+ * (See 3GPP2 C.S0015-A, Table 4.3.1.4.1-1)
+ * (For character encodings, see TIA/EIA/IS-91, Annex B)
+ *
+ * Protocol Summary: The user data payload may contain 1-14
+ * characters, which are treated as normal text user data payload.
+ * Note that the characters encoding is 6-bit packed.
+ */
+ private static void decodeIs91ShortMessage(BearerData bData)
+ throws BitwiseInputStream.AccessException, CodingException
+ {
+ BitwiseInputStream inStream = new BitwiseInputStream(bData.userData.payload);
+ int dataLen = inStream.available() / 6; // 6-bit packed character encoding.
+ int numFields = bData.userData.numFields;
+ if ((dataLen > 14) || (dataLen < numFields)) {
+ throw new CodingException("IS-91 voicemail status decoding failed");
+ }
+ StringBuffer strbuf = new StringBuffer(dataLen);
+ for (int i = 0; i < numFields; i++) {
+ strbuf.append(UserData.IA5_MAP[inStream.read(6)]);
+ }
+ bData.userData.payloadStr = strbuf.toString();
+ }
+
+ /**
+ * IS-91 CLI message (callback number) decoding
+ * (See 3GPP2 C.S0015-A, Table 4.3.1.4.1-1)
+ *
+ * Protocol Summary: The data payload may contain 1-32 digits,
+ * encoded using standard 4-bit DTMF, which are treated as a
+ * callback number.
+ */
+ private static void decodeIs91Cli(BearerData bData) throws CodingException {
+ BitwiseInputStream inStream = new BitwiseInputStream(bData.userData.payload);
+ int dataLen = inStream.available() / 4; // 4-bit packed DTMF digit encoding.
+ int numFields = bData.userData.numFields;
+ if ((dataLen > 14) || (dataLen < 3) || (dataLen < numFields)) {
+ throw new CodingException("IS-91 voicemail status decoding failed");
+ }
+ CdmaSmsAddress addr = new CdmaSmsAddress();
+ addr.digitMode = CdmaSmsAddress.DIGIT_MODE_4BIT_DTMF;
+ addr.origBytes = bData.userData.payload;
+ addr.numberOfDigits = (byte)numFields;
+ decodeSmsAddress(addr);
+ bData.callbackNumber = addr;
+ }
+
+ private static void decodeIs91(BearerData bData)
+ throws BitwiseInputStream.AccessException, CodingException
+ {
+ switch (bData.userData.msgType) {
+ case UserData.IS91_MSG_TYPE_VOICEMAIL_STATUS:
+ decodeIs91VoicemailStatus(bData);
+ break;
+ case UserData.IS91_MSG_TYPE_CLI:
+ decodeIs91Cli(bData);
+ break;
+ case UserData.IS91_MSG_TYPE_SHORT_MESSAGE_FULL:
+ case UserData.IS91_MSG_TYPE_SHORT_MESSAGE:
+ decodeIs91ShortMessage(bData);
+ break;
+ default:
+ throw new CodingException("unsupported IS-91 message type (" +
+ bData.userData.msgType + ")");
+ }
+ }
+
private static void decodeReplyOption(BearerData bData, BitwiseInputStream inStream)
throws BitwiseInputStream.AccessException, CodingException
{
@@ -987,16 +1103,16 @@
{
int paramBytes = inStream.read(8);
CdmaSmsAddress addr = new CdmaSmsAddress();
- addr.digitMode = (byte)inStream.read(1);
+ addr.digitMode = inStream.read(1);
byte fieldBits = 4;
byte consumedBits = 1;
if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR) {
addr.ton = inStream.read(3);
- addr.numberPlan = (byte)inStream.read(4);
+ addr.numberPlan = inStream.read(4);
fieldBits = 8;
consumedBits += 7;
}
- addr.numberOfDigits = (byte)inStream.read(8);
+ addr.numberOfDigits = inStream.read(8);
consumedBits += 8;
int remainingBits = (paramBytes * 8) - consumedBits;
int dataBits = addr.numberOfDigits * fieldBits;
@@ -1076,7 +1192,7 @@
if (inStream.read(8) != 1) {
throw new CodingException("PRIVACY_INDICATOR subparam size incorrect");
}
- bData.privacy = (byte)inStream.read(2);
+ bData.privacy = inStream.read(2);
inStream.skip(6);
bData.privacyIndicatorSet = true;
}
@@ -1097,7 +1213,7 @@
if (inStream.read(8) != 1) {
throw new CodingException("DISPLAY_MODE subparam size incorrect");
}
- bData.displayMode = (byte)inStream.read(2);
+ bData.displayMode = inStream.read(2);
inStream.skip(6);
bData.displayModeSet = true;
}
@@ -1108,7 +1224,7 @@
if (inStream.read(8) != 1) {
throw new CodingException("PRIORITY_INDICATOR subparam size incorrect");
}
- bData.priority = (byte)inStream.read(2);
+ bData.priority = inStream.read(2);
inStream.skip(6);
bData.priorityIndicatorSet = true;
}
@@ -1219,7 +1335,18 @@
throw new CodingException("missing MESSAGE_IDENTIFIER subparam");
}
if (bData.userData != null) {
- decodeUserDataPayload(bData.userData, bData.hasUserDataHeader);
+ if (bData.userData.msgEncoding == UserData.ENCODING_IS91_EXTENDED_PROTOCOL) {
+ if ((foundSubparamMask ^
+ (1 << SUBPARAM_MESSAGE_IDENTIFIER) ^
+ (1 << SUBPARAM_USER_DATA))
+ != 0) {
+ Log.e(LOG_TAG, "IS-91 must occur without extra subparams (" +
+ foundSubparamMask + ")");
+ }
+ decodeIs91(bData);
+ } else {
+ decodeUserDataPayload(bData.userData, bData.hasUserDataHeader);
+ }
}
return bData;
} catch (BitwiseInputStream.AccessException ex) {
diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java b/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java
index 917ec9d..4d799665 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java
@@ -29,7 +29,7 @@
static public final int DIGIT_MODE_4BIT_DTMF = 0x00;
static public final int DIGIT_MODE_8BIT_CHAR = 0x01;
- public byte digitMode;
+ public int digitMode;
/**
* Number Mode Indicator is 1-bit value that indicates whether the
@@ -39,7 +39,7 @@
static public final int NUMBER_MODE_NOT_DATA_NETWORK = 0x00;
static public final int NUMBER_MODE_DATA_NETWORK = 0x01;
- public byte numberMode;
+ public int numberMode;
/**
* Number Types for data networks.
@@ -65,7 +65,7 @@
* This field shall be set to the number of address digits
* (See 3GPP2 C.S0015-B, v2, 3.4.3.3)
*/
- public byte numberOfDigits;
+ public int numberOfDigits;
/**
* Numbering Plan identification is a 0 or 4-bit value that
@@ -78,7 +78,7 @@
//static protected final int NUMBERING_PLAN_TELEX = 0x4;
//static protected final int NUMBERING_PLAN_PRIVATE = 0x9;
- public byte numberPlan;
+ public int numberPlan;
/**
* NOTE: the parsed string address and the raw byte array values
diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java b/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java
index d8a48cc..34cbbfa 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java
@@ -40,12 +40,26 @@
public static final int ENCODING_GSM_DCS = 0x0A;
/**
+ * IS-91 message types.
+ * (See TIA/EIS/IS-91-A-ENGL 1999, table 3.7.1.1-3)
+ */
+ public static final int IS91_MSG_TYPE_VOICEMAIL_STATUS = 0x82;
+ public static final int IS91_MSG_TYPE_SHORT_MESSAGE_FULL = 0x83;
+ public static final int IS91_MSG_TYPE_CLI = 0x84;
+ public static final int IS91_MSG_TYPE_SHORT_MESSAGE = 0x85;
+
+ /**
* IA5 data encoding character mappings.
* (See CCITT Rec. T.50 Tables 1 and 3)
*
* Note this mapping is the the same as for printable ASCII
* characters, with a 0x20 offset, meaning that the ASCII SPACE
* character occurs with code 0x20.
+ *
+ * Note this mapping is also equivalent to that used by the IS-91
+ * protocol, except for the latter using only 6 bits, and hence
+ * mapping only entries up to the '_' character.
+ *
*/
public static final char[] IA5_MAP = {
' ', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/',
@@ -96,7 +110,6 @@
public int msgEncoding;
public boolean msgEncodingSet = false;
- // XXX needed when encoding is IS91 or DCS (not supported yet):
public int msgType;
/**
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
index 346944a..c33d4b6 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
@@ -377,10 +377,14 @@
removeMessages(EVENT_RESTORE_DEFAULT_APN);
setEnabled(type, false);
if (isApnTypeActive(Phone.APN_TYPE_DEFAULT)) {
+ mRequestedApnType = Phone.APN_TYPE_DEFAULT;
if (dataEnabled[APN_DEFAULT_ID]) {
return Phone.APN_ALREADY_ACTIVE;
} else {
- cleanUpConnection(true, Phone.REASON_DATA_DISABLED);
+ Message msg = obtainMessage(EVENT_CLEAN_UP_CONNECTION);
+ msg.arg1 = 1; // tearDown is true;
+ msg.obj = Phone.REASON_DATA_DISABLED;
+ sendMessage(msg);
return Phone.APN_REQUEST_STARTED;
}
} else {
@@ -1235,10 +1239,9 @@
protected void onRestoreDefaultApn() {
if (DBG) Log.d(LOG_TAG, "Restore default APN");
setEnabled(Phone.APN_TYPE_MMS, false);
-
+ mRequestedApnType = Phone.APN_TYPE_DEFAULT;
if (!isApnTypeActive(Phone.APN_TYPE_DEFAULT)) {
cleanUpConnection(true, Phone.REASON_RESTORE_DEFAULT_APN);
- mRequestedApnType = Phone.APN_TYPE_DEFAULT;
}
}
@@ -1395,7 +1398,7 @@
protected void onVoiceCallEnded() {
if (state == State.CONNECTED) {
- if (mGsmPhone.mSST.isConcurrentVoiceAndData()) {
+ if (!mGsmPhone.mSST.isConcurrentVoiceAndData()) {
startNetStatPoll();
phone.notifyDataConnection(Phone.REASON_VOICE_CALL_ENDED);
} else {
diff --git a/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java b/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java
index 9188e04b..2ff0a6a 100644
--- a/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java
@@ -30,6 +30,8 @@
import java.util.Iterator;
+import java.lang.Integer;
+
import android.util.Log;
public class CdmaSmsTest extends AndroidTestCase {
@@ -679,4 +681,15 @@
assertEquals(revBearerData.displayModeSet, true);
assertEquals(revBearerData.displayMode, bearerData.displayMode);
}
+
+ @SmallTest
+ public void testIs91() throws Exception {
+ String pdu1 = "000320001001070c2039acc13880";
+ BearerData bd1 = BearerData.decode(HexDump.hexStringToByteArray(pdu1));
+ assertEquals(bd1.callbackNumber.address, "3598271");
+ String pdu4 = "000320001001080c283c314724b34e";
+ BearerData bd4 = BearerData.decode(HexDump.hexStringToByteArray(pdu4));
+ assertEquals(bd4.userData.payloadStr, "ABCDEFG");
+ }
+
}
diff --git a/tests/CoreTests/com/android/internal/telephony/PhoneNumberUtilsTest.java b/tests/CoreTests/com/android/internal/telephony/PhoneNumberUtilsTest.java
index e8bd239..577d384 100644
--- a/tests/CoreTests/com/android/internal/telephony/PhoneNumberUtilsTest.java
+++ b/tests/CoreTests/com/android/internal/telephony/PhoneNumberUtilsTest.java
@@ -93,6 +93,13 @@
assertEquals(b[i], bRet[i]);
}
+ bRet = PhoneNumberUtils.networkPortionToCalledPartyBCDWithLength("+17005550020");
+ assertEquals(8, bRet.length);
+ assertEquals(bRet[0], 7);
+ for (int i = 1; i < 8; i++) {
+ assertEquals(b[i - 1], bRet[i]);
+ }
+
bRet = PhoneNumberUtils.networkPortionToCalledPartyBCD("7005550020");
assertEquals("7005550020",
PhoneNumberUtils.calledPartyBCDToString(bRet, 0, bRet.length));
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/FileList.java b/tests/DumpRenderTree/src/com/android/dumprendertree/FileList.java
index 0218317..e741177 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/FileList.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/FileList.java
@@ -23,7 +23,9 @@
import java.util.Map;
import java.io.File;
+import android.app.AlertDialog;
import android.app.ListActivity;
+import android.content.DialogInterface;
import android.view.KeyEvent;
import android.view.View;
import android.widget.ListView;
@@ -31,7 +33,7 @@
import android.os.Bundle;
-public abstract class FileList extends ListActivity
+public abstract class FileList extends ListActivity
{
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (keyCode)
@@ -39,7 +41,7 @@
case KeyEvent.KEYCODE_DPAD_LEFT:
if (mPath.length() > mBaseLength) {
File f = new File(mPath);
- mFocusFile = f.getName();
+ mFocusFile = f.getName();
mFocusIndex = 0;
f = f.getParentFile();
mPath = f.getPath();
@@ -47,7 +49,7 @@
return true;
}
break;
-
+
case KeyEvent.KEYCODE_DPAD_RIGHT:
{
Map map = (Map) getListView().getItemAtPosition(getListView().getSelectedItemPosition());
@@ -61,24 +63,24 @@
}
return true;
}
-
+
default:
break;
}
return super.onKeyDown(keyCode, event);
}
- public void onCreate(Bundle icicle)
+ public void onCreate(Bundle icicle)
{
super.onCreate(icicle);
setupPath();
updateList();
}
-
+
protected List getData()
{
List myData = new ArrayList<HashMap>();
-
+
File f = new File(mPath);
if (!f.exists()) {
addItem(myData, "!LayoutTests path missing!", "");
@@ -103,10 +105,10 @@
addItem(myData, files[i], path);
}
}
-
+
return myData;
}
-
+
protected void addItem(List<Map> data, String name, String path)
{
HashMap temp = new HashMap();
@@ -114,34 +116,58 @@
temp.put("path", path);
data.add(temp);
}
-
+
protected void onListItemClick(ListView l, View v, int position, long id)
{
- Map map = (Map) l.getItemAtPosition(position);
- String path = (String)map.get("path");
+ Map map = (Map) l.getItemAtPosition(position);
+ final String path = (String)map.get("path");
if ((new File(path)).isDirectory()) {
- mPath = path;
- mFocusFile = null;
- updateList();
+ final CharSequence[] items = {"Open", "Run"};
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle("Select an Action");
+ builder.setSingleChoiceItems(items, -1,
+ new DialogInterface.OnClickListener(){
+ public void onClick(DialogInterface dialog, int which) {
+ switch (which) {
+ case OPEN_DIRECTORY:
+ dialog.dismiss();
+ mPath = path;
+ mFocusFile = null;
+ updateList();
+ break;
+ case RUN_TESTS:
+ dialog.dismiss();
+ processDirectory(path, false);
+ break;
+ }
+ }
+ });
+ builder.create().show();
} else {
processFile(path, false);
}
}
-
+
+ /*
+ * This function is called when the user has selected a directory in the
+ * list and wants to perform an action on it instead of navigating into
+ * the directory.
+ */
+ abstract void processDirectory(String path, boolean selection);
/*
* This function is called when the user has selected a file in the
* file list. The selected file could be a file or a directory.
* The flag indicates if this was from a selection or not.
*/
abstract void processFile(String filename, boolean selection);
-
+
/*
* This function is called when the file list is being built. Return
* true if the file is to be added to the file list.
*/
abstract boolean fileFilter(File f);
-
+
protected void updateList() {
setListAdapter(new SimpleAdapter(this,
getData(),
@@ -152,16 +178,19 @@
setTitle(title);
getListView().setSelection(mFocusIndex);
}
-
- protected void setupPath()
+
+ protected void setupPath()
{
mPath = "/sdcard/android/layout_tests";
mBaseLength = mPath.length();
}
-
+
protected String mPath;
protected int mBaseLength;
protected String mFocusFile;
protected int mFocusIndex;
-
+
+ private final static int OPEN_DIRECTORY = 0;
+ private final static int RUN_TESTS = 1;
+
}
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/FsUtils.java b/tests/DumpRenderTree/src/com/android/dumprendertree/FsUtils.java
new file mode 100644
index 0000000..cc2f1f5
--- /dev/null
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/FsUtils.java
@@ -0,0 +1,80 @@
+package com.android.dumprendertree;
+
+import android.util.Log;
+
+import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+
+public class FsUtils {
+
+ private static final String LOGTAG = "FsUtils";
+ private FsUtils() {
+ //no creation of instances
+ }
+
+ public static void findLayoutTestsRecursively(BufferedOutputStream bos,
+ String dir) throws IOException {
+ Log.v(LOGTAG, "Searching tests under " + dir);
+
+ File d = new File(dir);
+ if (!d.isDirectory()) {
+ throw new AssertionError("A directory expected, but got " + dir);
+ }
+
+ String[] files = d.list();
+ for (int i = 0; i < files.length; i++) {
+ String s = dir + "/" + files[i];
+ if (FileFilter.ignoreTest(s)) {
+ Log.v(LOGTAG, " Ignoring: " + s);
+ continue;
+ }
+ if (s.toLowerCase().endsWith(".html")
+ || s.toLowerCase().endsWith(".xml")) {
+ bos.write(s.getBytes());
+ bos.write('\n');
+ continue;
+ }
+
+ File f = new File(s);
+ if (f.isDirectory()) {
+ findLayoutTestsRecursively(bos, s);
+ continue;
+ }
+
+ Log.v(LOGTAG, "Skipping " + s);
+ }
+ }
+
+ public static void updateTestStatus(String statusFile, String s) {
+ try {
+ BufferedOutputStream bos = new BufferedOutputStream(
+ new FileOutputStream(statusFile));
+ bos.write(s.getBytes());
+ bos.close();
+ } catch (Exception e) {
+ Log.e(LOGTAG, "Cannot update file " + statusFile);
+ }
+ }
+
+ public static String readTestStatus(String statusFile) {
+ // read out the test name it stopped last time.
+ String status = null;
+ File testStatusFile = new File(statusFile);
+ if(testStatusFile.exists()) {
+ try {
+ BufferedReader inReader = new BufferedReader(
+ new FileReader(testStatusFile));
+ status = inReader.readLine();
+ inReader.close();
+ } catch (IOException e) {
+ Log.e(LOGTAG, "Error reading test status.", e);
+ }
+ }
+ return status;
+ }
+
+}
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java
index f169a26..a03490d 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java
@@ -178,15 +178,13 @@
private void resumeTestList() {
// read out the test name it stoped last time.
try {
- BufferedReader inReader = new BufferedReader(new FileReader(TEST_STATUS_FILE));
- String line = inReader.readLine();
+ String line = FsUtils.readTestStatus(TEST_STATUS_FILE);
for (int i = 0; i < mTestList.size(); i++) {
if (mTestList.elementAt(i).equals(line)) {
mTestList = new Vector<String>(mTestList.subList(i+1, mTestList.size()));
break;
}
}
- inReader.close();
} catch (Exception e) {
Log.e(LOGTAG, "Error reading " + TEST_STATUS_FILE);
}
@@ -204,18 +202,7 @@
Log.e(LOGTAG, "Fail to delete " + TEST_STATUS_FILE + " : " + e.getMessage());
}
}
-
- private void updateTestStatus(String s) {
- // Write TEST_STATUS_FILE
- try {
- BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(TEST_STATUS_FILE));
- bos.write(s.getBytes());
- bos.close();
- } catch (Exception e) {
- Log.e(LOGTAG, "Cannot update file " + TEST_STATUS_FILE);
- }
- }
-
+
private String getResultFile(String test) {
String shortName = test.substring(0, test.lastIndexOf('.'));
// Write actual results to result directory.
@@ -392,12 +379,12 @@
// Run tests.
for (int i = 0; i < mTestList.size(); i++) {
String s = mTestList.elementAt(i);
- updateTestStatus(s);
+ FsUtils.updateTestStatus(TEST_STATUS_FILE, s);
// Run tests
runTestAndWaitUntilDone(activity, s, runner.mTimeoutInMillis);
}
- updateTestStatus("#DONE");
+ FsUtils.updateTestStatus(TEST_STATUS_FILE, "#DONE");
activity.finish();
}
@@ -424,7 +411,7 @@
try {
File tests_list = new File(LAYOUT_TESTS_LIST_FILE);
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(tests_list, false));
- findTestsRecursively(bos, getTestPath());
+ FsUtils.findLayoutTestsRecursively(bos, getTestPath());
bos.flush();
bos.close();
} catch (Exception e) {
@@ -432,38 +419,6 @@
}
}
- private void findTestsRecursively(BufferedOutputStream bos, String dir) throws IOException {
- Log.v(LOGTAG, "Searching tests under " + dir);
-
- File d = new File(dir);
- if (!d.isDirectory()) {
- throw new AssertionError("A directory expected, but got " + dir);
- }
-
- String[] files = d.list();
- for (int i = 0; i < files.length; i++) {
- String s = dir + "/" + files[i];
- if (FileFilter.ignoreTest(s)) {
- Log.v(LOGTAG, " Ignoring: " + s);
- continue;
- }
- if (s.toLowerCase().endsWith(".html")
- || s.toLowerCase().endsWith(".xml")) {
- bos.write(s.getBytes());
- bos.write('\n');
- continue;
- }
-
- File f = new File(s);
- if (f.isDirectory()) {
- findTestsRecursively(bos, s);
- continue;
- }
-
- Log.v(LOGTAG, "Skipping " + s);
- }
- }
-
// Running all the layout tests at once sometimes
// causes the dumprendertree to run out of memory.
// So, additional tests are added to run the tests
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/Menu.java b/tests/DumpRenderTree/src/com/android/dumprendertree/Menu.java
index 00e0f89..e15ab65 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/Menu.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/Menu.java
@@ -17,19 +17,23 @@
package com.android.dumprendertree;
import android.content.Intent;
-import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
+import java.io.BufferedOutputStream;
import java.io.File;
+import java.io.FileOutputStream;
public class Menu extends FileList {
-
- public void onCreate(Bundle icicle)
- {
+
+ private static final int MENU_START = 0x01;
+ private static String LOGTAG = "MenuActivity";
+ static final String LAYOUT_TESTS_LIST_FILE = "/sdcard/android/layout_tests_list.txt";
+
+ public void onCreate(Bundle icicle) {
super.onCreate(icicle);
}
-
+
boolean fileFilter(File f) {
if (f.getName().startsWith("."))
return false;
@@ -41,14 +45,36 @@
return true;
return false;
}
-
- void processFile(String filename, boolean selection)
- {
+
+ void processFile(String filename, boolean selection) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setClass(this, TestShellActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.putExtra(TestShellActivity.TEST_URL, "file://" + filename);
startActivity(intent);
}
+
+ @Override
+ void processDirectory(String path, boolean selection) {
+ generateTestList(path);
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setClass(this, TestShellActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
+ intent.putExtra(TestShellActivity.UI_AUTO_TEST, LAYOUT_TESTS_LIST_FILE);
+ startActivity(intent);
+ }
+
+ private void generateTestList(String path) {
+ try {
+ File tests_list = new File(LAYOUT_TESTS_LIST_FILE);
+ BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(tests_list, false));
+ FsUtils.findLayoutTestsRecursively(bos, path);
+ bos.flush();
+ bos.close();
+ } catch (Exception e) {
+ Log.e(LOGTAG, "Error when creating test list: " + e.getMessage());
+ }
+ }
+
}
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/ReliabilityTest.java b/tests/DumpRenderTree/src/com/android/dumprendertree/ReliabilityTest.java
index 16973be..de39800 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/ReliabilityTest.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/ReliabilityTest.java
@@ -45,7 +45,7 @@
//always try to resume first, hence cleaning up status will be the
//responsibility of driver scripts
- String lastUrl = readTestStatus();
+ String lastUrl = FsUtils.readTestStatus(TEST_STATUS_FILE);
if(lastUrl != null && !TEST_DONE.equals(lastUrl))
fastForward(listReader, lastUrl);
@@ -62,7 +62,7 @@
continue;
start = System.currentTimeMillis();
Log.v(LOGTAG, "Testing URL: " + url);
- updateTestStatus(url);
+ FsUtils.updateTestStatus(TEST_STATUS_FILE, url);
activity.reset();
//use message to send new URL to avoid interacting with
//WebView in non-UI thread
@@ -92,7 +92,7 @@
System.gc();
System.gc();
}
- updateTestStatus(TEST_DONE);
+ FsUtils.updateTestStatus(TEST_STATUS_FILE, TEST_DONE);
activity.finish();
listReader.close();
}
@@ -122,35 +122,6 @@
}
}
- private void updateTestStatus(String s) {
- // write last tested url into status file
- try {
- BufferedOutputStream bos = new BufferedOutputStream(
- new FileOutputStream(TEST_STATUS_FILE));
- bos.write(s.getBytes());
- bos.close();
- } catch (IOException e) {
- Log.e(LOGTAG, "Cannot update file " + TEST_STATUS_FILE, e);
- }
- }
-
- private String readTestStatus() {
- // read out the test name it stopped last time.
- String status = null;
- File testStatusFile = new File(TEST_STATUS_FILE);
- if(testStatusFile.exists()) {
- try {
- BufferedReader inReader = new BufferedReader(
- new FileReader(testStatusFile));
- status = inReader.readLine();
- inReader.close();
- } catch (IOException e) {
- Log.e(LOGTAG, "Error reading test status.", e);
- }
- }
- return status;
- }
-
private void fastForward(BufferedReader testListReader, String lastUrl) {
//fastforward the BufferedReader to the position right after last url
if(lastUrl == null)
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
index 1ba291c..0d22eca 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
@@ -17,7 +17,10 @@
package com.android.dumprendertree;
import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
import android.content.Intent;
+import android.content.DialogInterface.OnClickListener;
import android.graphics.Bitmap;
import android.net.http.SslError;
import android.os.Bundle;
@@ -35,21 +38,24 @@
import android.webkit.WebViewClient;
import android.widget.LinearLayout;
+import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
+import java.io.FileReader;
import java.io.IOException;
import java.util.Vector;
public class TestShellActivity extends Activity implements LayoutTestController {
-
+
static enum DumpDataType {DUMP_AS_TEXT, EXT_REPR, NO_OP}
-
+
public class AsyncHandler extends Handler {
@Override
public void handleMessage(Message msg) {
if (msg.what == MSG_TIMEOUT) {
mTimedOut = true;
- mCallback.timedOut(mWebView.getUrl());
+ if(mCallback != null)
+ mCallback.timedOut(mWebView.getUrl());
requestWebKitData();
return;
} else if (msg.what == MSG_WEBKIT_DATA) {
@@ -63,10 +69,10 @@
public void requestWebKitData() {
Message callback = mHandler.obtainMessage(MSG_WEBKIT_DATA);
-
+
if (mRequestedWebKitData)
throw new AssertionError("Requested webkit data twice: " + mWebView.getUrl());
-
+
mRequestedWebKitData = true;
switch (mDumpDataType) {
case DUMP_AS_TEXT:
@@ -79,12 +85,12 @@
finished();
break;
}
- }
+ }
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
-
+
LinearLayout contentView = new LinearLayout(this);
contentView.setOrientation(LinearLayout.VERTICAL);
setContentView(contentView);
@@ -133,59 +139,122 @@
mWebView.addJavascriptInterface(mCallbackProxy, "layoutTestController");
mWebView.addJavascriptInterface(mCallbackProxy, "eventSender");
contentView.addView(mWebView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT, 0.0f));
-
+
mWebView.getSettings().setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL);
-
+
mHandler = new AsyncHandler();
-
+
Intent intent = getIntent();
if (intent != null) {
executeIntent(intent);
}
}
-
+
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
executeIntent(intent);
}
-
+
private void executeIntent(Intent intent) {
resetTestStatus();
if (!Intent.ACTION_VIEW.equals(intent.getAction())) {
return;
}
-
+
mTestUrl = intent.getStringExtra(TEST_URL);
- if (mTestUrl == null)
+ if (mTestUrl == null) {
+ mUiAutoTestPath = intent.getStringExtra(UI_AUTO_TEST);
+ if(mUiAutoTestPath != null) {
+ beginUiAutoTest();
+ }
return;
-
+ }
+
mResultFile = intent.getStringExtra(RESULT_FILE);
mTimeoutInMillis = intent.getIntExtra(TIMEOUT_IN_MILLIS, 0);
Log.v(LOGTAG, " Loading " + mTestUrl);
mWebView.loadUrl(mTestUrl);
-
+
if (mTimeoutInMillis > 0) {
// Create a timeout timer
Message m = mHandler.obtainMessage(MSG_TIMEOUT);
mHandler.sendMessageDelayed(m, mTimeoutInMillis);
}
}
-
+
+ private void beginUiAutoTest() {
+ try {
+ mTestListReader = new BufferedReader(
+ new FileReader(mUiAutoTestPath));
+ } catch (IOException ioe) {
+ Log.e(LOGTAG, "Failed to open test list for read.", ioe);
+ finishUiAutoTest();
+ return;
+ }
+ moveToNextTest();
+ }
+
+ private void finishUiAutoTest() {
+ try {
+ if(mTestListReader != null)
+ mTestListReader.close();
+ } catch (IOException ioe) {
+ Log.w(LOGTAG, "Failed to close test list file.", ioe);
+ }
+ finished();
+ }
+
+ private void moveToNextTest() {
+ String url = null;
+ try {
+ url = mTestListReader.readLine();
+ } catch (IOException ioe) {
+ Log.e(LOGTAG, "Failed to read next test.", ioe);
+ finishUiAutoTest();
+ return;
+ }
+ if (url == null) {
+ mUiAutoTestPath = null;
+ finishUiAutoTest();
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setMessage("All tests finished. Exit?")
+ .setCancelable(false)
+ .setPositiveButton("Yes", new OnClickListener(){
+ public void onClick(DialogInterface dialog, int which) {
+ TestShellActivity.this.finish();
+ }
+ })
+ .setNegativeButton("No", new OnClickListener(){
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.cancel();
+ }
+ });
+ builder.create().show();
+ return;
+ }
+ url = "file://" + url;
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
+ intent.putExtra(TestShellActivity.TEST_URL, url);
+ intent.putExtra(TIMEOUT_IN_MILLIS, 10000);
+ executeIntent(intent);
+ }
+
@Override
protected void onStop() {
super.onStop();
mWebView.stopLoading();
}
-
+
@Override
protected void onDestroy() {
super.onDestroy();
mWebView.destroy();
mWebView = null;
}
-
+
@Override
public void onLowMemory() {
super.onLowMemory();
@@ -199,13 +268,13 @@
finished();
return;
}
-
+
try {
File parentDir = new File(mResultFile).getParentFile();
if (!parentDir.exists()) {
parentDir.mkdirs();
}
-
+
FileOutputStream os = new FileOutputStream(mResultFile);
if (timeout) {
Log.w("Layout test: Timeout", mResultFile);
@@ -222,22 +291,27 @@
os.flush();
os.close();
} catch (IOException ex) {
- Log.e(LOGTAG, "Cannot write to " + mResultFile + ", " + ex.getMessage());
+ Log.e(LOGTAG, "Cannot write to " + mResultFile + ", " + ex.getMessage());
}
finished();
}
-
+
public void setCallback(TestShellCallback callback) {
mCallback = callback;
}
-
+
public void finished() {
- if (mCallback != null) {
- mCallback.finished();
+ if (mUiAutoTestPath != null) {
+ //don't really finish here
+ moveToNextTest();
+ } else {
+ if (mCallback != null) {
+ mCallback.finished();
+ }
}
}
-
+
public void setDefaultDumpDataType(DumpDataType defaultDumpDataType) {
mDefaultDumpDataType = defaultDumpDataType;
}
@@ -257,7 +331,7 @@
String url = mWebView.getUrl();
Log.v(LOGTAG, "waitUntilDone called: " + url);
}
-
+
public void notifyDone() {
String url = mWebView.getUrl();
Log.v(LOGTAG, "notifyDone called: " + url);
@@ -266,7 +340,7 @@
mChromeClient.onProgressChanged(mWebView, 100);
}
}
-
+
public void display() {
mWebView.invalidate();
}
@@ -332,7 +406,7 @@
}
public void queueScript(String scriptToRunInCurrentContext) {
- mWebView.loadUrl("javascript:"+scriptToRunInCurrentContext);
+ mWebView.loadUrl("javascript:"+scriptToRunInCurrentContext);
}
public void repaintSweepHorizontally() {
@@ -359,7 +433,7 @@
public void testRepaint() {
mWebView.invalidate();
}
-
+
private final WebChromeClient mChromeClient = new WebChromeClient() {
@Override
public void onProgressChanged(WebView view, int newProgress) {
@@ -406,7 +480,7 @@
result.confirm();
return true;
}
-
+
@Override
public boolean onJsConfirm(WebView view, String url, String message,
JsResult result) {
@@ -419,7 +493,7 @@
result.confirm();
return true;
}
-
+
@Override
public boolean onJsPrompt(WebView view, String url, String message,
String defaultValue, JsPromptResult result) {
@@ -435,7 +509,7 @@
return true;
}
};
-
+
private void resetTestStatus() {
mWaitUntilDone = false;
mDumpDataType = mDefaultDumpDataType;
@@ -444,17 +518,19 @@
mRequestedWebKitData = false;
mEventSender.resetMouse();
}
-
+
private WebView mWebView;
private WebViewEventSender mEventSender;
private AsyncHandler mHandler;
private TestShellCallback mCallback;
private CallbackProxy mCallbackProxy;
-
+
private String mTestUrl;
private String mResultFile;
private int mTimeoutInMillis;
+ private String mUiAutoTestPath;
+ private BufferedReader mTestListReader;
// States
private boolean mTimedOut;
@@ -472,13 +548,14 @@
private Vector mWebHistory;
static final String TIMEOUT_STR = "**Test timeout";
-
+
static final int MSG_TIMEOUT = 0;
static final int MSG_WEBKIT_DATA = 1;
static final String LOGTAG="TestShell";
-
+
static final String TEST_URL = "TestUrl";
static final String RESULT_FILE = "ResultFile";
static final String TIMEOUT_IN_MILLIS = "TimeoutInMillis";
+ static final String UI_AUTO_TEST = "UiAutoTest";
}