Merge change 5699 into donut
* changes:
Adding status return codes for TTS calls.
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/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/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 @@
}
+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 @@
}
-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 @@
"(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 @@
(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/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/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 @@
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;
}
}
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";
}