diff options
20 files changed, 672 insertions, 256 deletions
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 @@ public final class Bmgr { 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 @@ public final class Bmgr { 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 @@ public final class Bmgr { 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 @@ public final class Bmgr { 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 @@ interface IBackupManager { 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/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java index 8fa06fa..f3b8963 100755 --- a/core/java/android/speech/tts/TextToSpeech.java +++ b/core/java/android/speech/tts/TextToSpeech.java @@ -251,14 +251,17 @@ public class TextToSpeech { * * @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 @@ public class TextToSpeech { mStarted = false; initTts(); } + return TTS_ERROR; } } @@ -285,14 +289,17 @@ public class TextToSpeech { * @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 @@ public class TextToSpeech { mStarted = false; initTts(); } + return TTS_ERROR; } } @@ -324,17 +332,20 @@ public class TextToSpeech { * 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 @@ public class TextToSpeech { mStarted = false; initTts(); } + return TTS_ERROR; } } @@ -366,17 +378,22 @@ public class TextToSpeech { * 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 @@ public class TextToSpeech { mStarted = false; initTts(); } + return TTS_ERROR; } } @@ -403,16 +421,19 @@ public class TextToSpeech { * 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 @@ public class TextToSpeech { 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 @@ public class TextToSpeech { mStarted = false; initTts(); } + return TTS_ERROR; } } @@ -487,14 +521,17 @@ public class TextToSpeech { /** * 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 @@ public class TextToSpeech { mStarted = false; initTts(); } + return TTS_ERROR; } } @@ -524,23 +562,27 @@ public class TextToSpeech { * 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 @@ public class TextToSpeech { * 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 @@ public class TextToSpeech { * * @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,21 +645,25 @@ public class TextToSpeech { 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; } } + /** * Checks if the specified language as represented by the locale is available. * * @param loc * 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 @@ -619,7 +671,6 @@ public class TextToSpeech { } - /** * Synthesizes the given text to a file using the specified parameters. * @@ -630,17 +681,20 @@ public class TextToSpeech { * @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 +708,11 @@ public class TextToSpeech { mStarted = false; initTts(); } - return false; + return TTS_ERROR; } } + /** * Synthesizes the given IPA text to a file using the specified parameters. * @@ -668,17 +723,22 @@ public class TextToSpeech { * @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 +752,7 @@ public class TextToSpeech { 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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility 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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility 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/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 @@ public class RelativeLayout extends ViewGroup { 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 @@ public class RelativeLayout extends ViewGroup { /* * 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/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/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 @@ void SurfaceFlinger::computeVisibleRegions( 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 @@ void SurfaceFlinger::computeVisibleRegions( // 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/packages/TtsService/jni/android_tts_SynthProxy.cpp b/packages/TtsService/jni/android_tts_SynthProxy.cpp index 27c0021..69ba062 100644 --- a/packages/TtsService/jni/android_tts_SynthProxy.cpp +++ b/packages/TtsService/jni/android_tts_SynthProxy.cpp @@ -278,6 +278,33 @@ android_tts_SynthProxy_native_finalize(JNIEnv *env, jobject thiz, jint jniData) } +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) @@ -533,24 +560,34 @@ LOGI("android_tts_SynthProxy_playAudioBuffer"); } -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) { - // TODO use the correct getLanguage() - //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); } @@ -588,6 +625,10 @@ static JNINativeMethod gMethods[] = { "(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 @@ -609,7 +650,7 @@ static JNINativeMethod gMethods[] = { (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 a8eaaa4..91fe3b7 100755 --- a/packages/TtsService/src/android/tts/SynthProxy.java +++ b/packages/TtsService/src/android/tts/SynthProxy.java @@ -68,12 +68,20 @@ public class SynthProxy { // 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 @@ public class SynthProxy { } /** - * 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 @@ public class SynthProxy { 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..c6b5bee 100755 --- a/packages/TtsService/src/android/tts/TtsService.java +++ b/packages/TtsService/src/android/tts/TtsService.java @@ -91,6 +91,7 @@ public class TtsService extends Service implements OnCompletionListener { } 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"; @@ -414,6 +415,26 @@ public class TtsService extends Service implements OnCompletionListener { 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 +635,7 @@ public class TtsService extends Service implements OnCompletionListener { 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 +680,7 @@ public class TtsService extends Service implements OnCompletionListener { 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); 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 @@ class BackupManagerService extends IBackupManager.Stub { 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 @@ class BackupManagerService extends IBackupManager.Stub { 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 @@ class BackupManagerService extends IBackupManager.Stub { 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 @@ class BackupManagerService extends IBackupManager.Stub { 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 @@ class BackupManagerService extends IBackupManager.Stub { // 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 @@ class BackupManagerService extends IBackupManager.Stub { 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 @@ class BackupManagerService extends IBackupManager.Stub { // 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 @@ class BackupManagerService extends IBackupManager.Stub { } } + // 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 @@ class BackupManagerService extends IBackupManager.Stub { 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 @@ class BackupManagerService extends IBackupManager.Stub { 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/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 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo "dispatchPointer " + ev); Object targetObj = mKeyWaiter.waitForNextEventTarget(null, qev, - ev, true, false); + ev, true, false, pid, uid); int action = ev.getAction(); @@ -4032,7 +4032,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo 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 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo 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 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo 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 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo * @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 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo * @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 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo */ 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 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo ", 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 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } 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 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo 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 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo 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/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java index 346944a..e00ee83 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 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { 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 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { 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; } } 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.List; 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.widget.SimpleAdapter; 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 @@ public abstract class FileList extends ListActivity 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 @@ public abstract class FileList extends ListActivity return true; } break; - + case KeyEvent.KEYCODE_DPAD_RIGHT: { Map map = (Map) getListView().getItemAtPosition(getListView().getSelectedItemPosition()); @@ -61,24 +63,24 @@ public abstract class FileList extends ListActivity } 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 @@ public abstract class FileList extends ListActivity 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 @@ public abstract class FileList extends ListActivity 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 @@ public abstract class FileList extends ListActivity 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 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh 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 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh 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 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh // 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 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh 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 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh } } - 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 @@ public class Menu extends FileList { 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 @@ public class ReliabilityTest extends ActivityInstrumentationTestCase2<Reliabilit //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 @@ public class ReliabilityTest extends ActivityInstrumentationTestCase2<Reliabilit 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 @@ public class ReliabilityTest extends ActivityInstrumentationTestCase2<Reliabilit System.gc(); System.gc(); } - updateTestStatus(TEST_DONE); + FsUtils.updateTestStatus(TEST_STATUS_FILE, TEST_DONE); activity.finish(); listReader.close(); } @@ -122,35 +122,6 @@ public class ReliabilityTest extends ActivityInstrumentationTestCase2<Reliabilit } } - 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.WebView; 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 class TestShellActivity extends Activity implements LayoutTestController 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 @@ public class TestShellActivity extends Activity implements LayoutTestController 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 @@ public class TestShellActivity extends Activity implements LayoutTestController 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 @@ public class TestShellActivity extends Activity implements LayoutTestController 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 @@ public class TestShellActivity extends Activity implements LayoutTestController 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 @@ public class TestShellActivity extends Activity implements LayoutTestController 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 @@ public class TestShellActivity extends Activity implements LayoutTestController mChromeClient.onProgressChanged(mWebView, 100); } } - + public void display() { mWebView.invalidate(); } @@ -332,7 +406,7 @@ public class TestShellActivity extends Activity implements LayoutTestController } public void queueScript(String scriptToRunInCurrentContext) { - mWebView.loadUrl("javascript:"+scriptToRunInCurrentContext); + mWebView.loadUrl("javascript:"+scriptToRunInCurrentContext); } public void repaintSweepHorizontally() { @@ -359,7 +433,7 @@ public class TestShellActivity extends Activity implements LayoutTestController public void testRepaint() { mWebView.invalidate(); } - + private final WebChromeClient mChromeClient = new WebChromeClient() { @Override public void onProgressChanged(WebView view, int newProgress) { @@ -406,7 +480,7 @@ public class TestShellActivity extends Activity implements LayoutTestController result.confirm(); return true; } - + @Override public boolean onJsConfirm(WebView view, String url, String message, JsResult result) { @@ -419,7 +493,7 @@ public class TestShellActivity extends Activity implements LayoutTestController result.confirm(); return true; } - + @Override public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) { @@ -435,7 +509,7 @@ public class TestShellActivity extends Activity implements LayoutTestController return true; } }; - + private void resetTestStatus() { mWaitUntilDone = false; mDumpDataType = mDefaultDumpDataType; @@ -444,17 +518,19 @@ public class TestShellActivity extends Activity implements LayoutTestController 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 @@ public class TestShellActivity extends Activity implements LayoutTestController 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"; } |
