diff options
73 files changed, 1930 insertions, 647 deletions
diff --git a/api/current.xml b/api/current.xml index 3c58ded..673d053 100644 --- a/api/current.xml +++ b/api/current.xml @@ -139488,6 +139488,19 @@ visibility="public" > </method> +<method name="buildDrawingCache" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="autoScale" type="boolean"> +</parameter> +</method> <method name="cancelLongPress" return="void" abstract="false" @@ -140064,6 +140077,19 @@ visibility="public" > </method> +<method name="getDrawingCache" + return="android.graphics.Bitmap" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="autoScale" type="boolean"> +</parameter> +</method> <method name="getDrawingCacheBackgroundColor" return="int" abstract="false" diff --git a/camera/libcameraservice/CameraService.cpp b/camera/libcameraservice/CameraService.cpp index 105d4d2..022fe5a 100644 --- a/camera/libcameraservice/CameraService.cpp +++ b/camera/libcameraservice/CameraService.cpp @@ -410,11 +410,14 @@ void CameraService::Client::disconnect() // pass the buffered ISurface to the camera service status_t CameraService::Client::setPreviewDisplay(const sp<ISurface>& surface) { - LOGD("setPreviewDisplay(%p) (pid %d)", surface.get(), getCallingPid()); + LOGD("setPreviewDisplay(%p) (pid %d)", + ((surface == NULL) ? NULL : surface.get()), getCallingPid()); Mutex::Autolock lock(mLock); status_t result = checkPid(); if (result != NO_ERROR) return result; + Mutex::Autolock surfaceLock(mSurfaceLock); + result = NO_ERROR; // asBinder() is safe on NULL (returns NULL) if (surface->asBinder() != mSurface->asBinder()) { if (mSurface != 0 && !mUseOverlay) { @@ -422,8 +425,17 @@ status_t CameraService::Client::setPreviewDisplay(const sp<ISurface>& surface) mSurface->unregisterBuffers(); } mSurface = surface; + // If preview has been already started, set overlay or register preview + // buffers now. + if (mHardware->previewEnabled()) { + if (mUseOverlay) { + result = setOverlay(); + } else if (mSurface != 0) { + result = registerPreviewBuffers(); + } + } } - return NO_ERROR; + return result; } // set the preview callback flag to affect how the received frames from @@ -436,7 +448,7 @@ void CameraService::Client::setPreviewCallbackFlag(int callback_flag) mPreviewCallbackFlag = callback_flag; } -// start preview mode, must call setPreviewDisplay first +// start preview mode status_t CameraService::Client::startCameraMode(camera_mode mode) { int callingPid = getCallingPid(); @@ -456,16 +468,18 @@ status_t CameraService::Client::startCameraMode(camera_mode mode) return INVALID_OPERATION; } - if (mSurface == 0) { - LOGE("setPreviewDisplay must be called before startCameraMode!"); - return INVALID_OPERATION; - } - switch(mode) { case CAMERA_RECORDING_MODE: + if (mSurface == 0) { + LOGE("setPreviewDisplay must be called before startRecordingMode."); + return INVALID_OPERATION; + } return startRecordingMode(); default: // CAMERA_PREVIEW_MODE + if (mSurface == 0) { + LOGD("mSurface is not set yet."); + } return startPreviewMode(); } } @@ -498,6 +512,62 @@ status_t CameraService::Client::startRecordingMode() return ret; } +status_t CameraService::Client::setOverlay() +{ + LOGD("setOverlay"); + int w, h; + CameraParameters params(mHardware->getParameters()); + params.getPreviewSize(&w, &h); + + const char *format = params.getPreviewFormat(); + int fmt; + if (!strcmp(format, "yuv422i")) + fmt = OVERLAY_FORMAT_YCbCr_422_I; + else if (!strcmp(format, "rgb565")) + fmt = OVERLAY_FORMAT_RGB_565; + else { + LOGE("Invalid preview format for overlays"); + return -EINVAL; + } + + status_t ret = NO_ERROR; + if (mSurface != 0) { + sp<OverlayRef> ref = mSurface->createOverlay(w, h, fmt); + ret = mHardware->setOverlay(new Overlay(ref)); + } else { + ret = mHardware->setOverlay(NULL); + } + if (ret != NO_ERROR) { + LOGE("mHardware->setOverlay() failed with status %d\n", ret); + } + return ret; +} + +status_t CameraService::Client::registerPreviewBuffers() +{ + int w, h; + CameraParameters params(mHardware->getParameters()); + params.getPreviewSize(&w, &h); + + uint32_t transform = 0; + if (params.getOrientation() == + CameraParameters::CAMERA_ORIENTATION_PORTRAIT) { + LOGV("portrait mode"); + transform = ISurface::BufferHeap::ROT_90; + } + ISurface::BufferHeap buffers(w, h, w, h, + PIXEL_FORMAT_YCbCr_420_SP, + transform, + 0, + mHardware->getPreviewHeap()); + + status_t ret = mSurface->registerBuffers(buffers); + if (ret != NO_ERROR) { + LOGE("registerBuffers failed with status %d", ret); + } + return ret; +} + status_t CameraService::Client::startPreviewMode() { LOGD("startPreviewMode (pid %d)", getCallingPid()); @@ -511,55 +581,24 @@ status_t CameraService::Client::startPreviewMode() #if DEBUG_DUMP_PREVIEW_FRAME_TO_FILE debug_frame_cnt = 0; #endif - status_t ret = UNKNOWN_ERROR; - int w, h; - CameraParameters params(mHardware->getParameters()); - params.getPreviewSize(&w, &h); + status_t ret = NO_ERROR; if (mUseOverlay) { - const char *format = params.getPreviewFormat(); - int fmt; - LOGD("Use Overlays"); - if (!strcmp(format, "yuv422i")) - fmt = OVERLAY_FORMAT_YCbCr_422_I; - else if (!strcmp(format, "rgb565")) - fmt = OVERLAY_FORMAT_RGB_565; - else { - LOGE("Invalid preview format for overlays"); - return -EINVAL; - } - sp<OverlayRef> ref = mSurface->createOverlay(w, h, fmt); - ret = mHardware->setOverlay(new Overlay(ref)); - if (ret != NO_ERROR) { - LOGE("mHardware->setOverlay() failed with status %d\n", ret); - return ret; + // If preview display has been set, set overlay now. + if (mSurface != 0) { + ret = setOverlay(); } + if (ret != NO_ERROR) return ret; ret = mHardware->startPreview(NULL, mCameraService.get()); - if (ret != NO_ERROR) - LOGE("mHardware->startPreview() failed with status %d\n", ret); - } else { ret = mHardware->startPreview(previewCallback, mCameraService.get()); - if (ret == NO_ERROR) { - - mSurface->unregisterBuffers(); - - uint32_t transform = 0; - if (params.getOrientation() == - CameraParameters::CAMERA_ORIENTATION_PORTRAIT) { - LOGV("portrait mode"); - transform = ISurface::BufferHeap::ROT_90; - } - ISurface::BufferHeap buffers(w, h, w, h, - PIXEL_FORMAT_YCbCr_420_SP, - transform, - 0, - mHardware->getPreviewHeap()); - - mSurface->registerBuffers(buffers); - } else { - LOGE("mHardware->startPreview() failed with status %d", ret); + if (ret != NO_ERROR) return ret; + // If preview display has been set, register preview buffers now. + if (mSurface != 0) { + // Unregister here because the surface registered with raw heap. + mSurface->unregisterBuffers(); + ret = registerPreviewBuffers(); } } return ret; diff --git a/camera/libcameraservice/CameraService.h b/camera/libcameraservice/CameraService.h index 8b8b54c..0f07673 100644 --- a/camera/libcameraservice/CameraService.h +++ b/camera/libcameraservice/CameraService.h @@ -157,6 +157,8 @@ private: status_t startCameraMode(camera_mode mode); status_t startPreviewMode(); status_t startRecordingMode(); + status_t setOverlay(); + status_t registerPreviewBuffers(); // Ensures atomicity among the public methods mutable Mutex mLock; diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java index c299bff..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(); @@ -124,11 +169,14 @@ public final class Bmgr { private void doTransport() { try { - int which = Integer.parseInt(nextArg()); - int old = mBmgr.selectBackupTransport(which); - System.out.println("Selected transport " + which + " (formerly " + old + ")"); - } catch (NumberFormatException e) { - showUsage(); + String which = nextArg(); + String old = mBmgr.selectBackupTransport(which); + if (old == null) { + System.out.println("Unknown transport '" + which + + "' specified; no changes made."); + } else { + System.out.println("Selected transport " + which + " (formerly " + old + ")"); + } } catch (RemoteException e) { System.err.println(e.toString()); System.err.println(BMGR_NOT_RUNNING_ERR); @@ -144,7 +192,7 @@ public final class Bmgr { // The rest of the 'list' options work with a restore session on the current transport try { - int curTransport = mBmgr.getCurrentTransport(); + String curTransport = mBmgr.getCurrentTransport(); mRestore = mBmgr.beginRestoreSession(curTransport); if (mRestore == null) { System.err.println(BMGR_NOT_RUNNING_ERR); @@ -153,6 +201,8 @@ public final class Bmgr { if ("sets".equals(arg)) { doListRestoreSets(); + } else if ("transports".equals(arg)) { + doListTransports(); } mRestore.endRestoreSession(); @@ -163,6 +213,22 @@ public final class Bmgr { } private void doListTransports() { + try { + String current = mBmgr.getCurrentTransport(); + String[] transports = mBmgr.listAllTransports(); + if (transports == null || transports.length == 0) { + System.out.println("No transports available."); + return; + } + + for (String t : transports) { + String pad = (t.equals(current)) ? " * " : " "; + System.out.println(pad + t); + } + } catch (RemoteException e) { + System.err.println(e.toString()); + System.err.println(BMGR_NOT_RUNNING_ERR); + } } private void doListRestoreSets() { @@ -217,7 +283,7 @@ public final class Bmgr { try { boolean didRestore = false; - int curTransport = mBmgr.getCurrentTransport(); + String curTransport = mBmgr.getCurrentTransport(); mRestore = mBmgr.beginRestoreSession(curTransport); if (mRestore == null) { System.err.println(BMGR_NOT_RUNNING_ERR); @@ -269,11 +335,43 @@ public final class Bmgr { private static void showUsage() { System.err.println("usage: bmgr [backup|restore|list|transport|run]"); - System.err.println(" bmgr backup [-f] package"); + 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 list transports"); - System.err.println(" #bmgr transport which#"); - System.err.println(" bmgr restore token#"); + System.err.println(" bmgr transport WHICH"); + System.err.println(" bmgr restore TOKEN"); System.err.println(" bmgr run"); + System.err.println(""); + System.err.println("The 'backup' command schedules a backup pass for the named package."); + 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"); + System.err.println("with a '*' character."); + System.err.println(""); + System.err.println("The 'list sets' command reports the token and name of each restore set"); + System.err.println("available to the device via the current transport."); + System.err.println(""); + System.err.println("The 'transport' command designates the named transport as the currently"); + System.err.println("active one. This setting is persistent across reboots."); + System.err.println(""); + System.err.println("The 'restore' command initiates a restore operation, using the restore set"); + System.err.println("from the current transport whose token matches the argument."); + System.err.println(""); + System.err.println("The 'run' command causes any scheduled backup operation to be initiated"); + System.err.println("immediately, without the usual waiting period for batching together"); + System.err.println("data changes."); } } diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java index 6ddf50f..6fe4896 100644 --- a/core/java/android/app/SearchDialog.java +++ b/core/java/android/app/SearchDialog.java @@ -33,7 +33,6 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Configuration; import android.content.res.Resources; import android.database.Cursor; -import android.graphics.drawable.AnimationDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; @@ -107,7 +106,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS private Button mGoButton; private ImageButton mVoiceButton; private View mSearchPlate; - private AnimationDrawable mWorkingSpinner; + private Drawable mWorkingSpinner; // interaction with searchable application private SearchableInfo mSearchable; @@ -188,7 +187,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS mGoButton = (Button) findViewById(com.android.internal.R.id.search_go_btn); mVoiceButton = (ImageButton) findViewById(com.android.internal.R.id.search_voice_btn); mSearchPlate = findViewById(com.android.internal.R.id.search_plate); - mWorkingSpinner = (AnimationDrawable) getContext().getResources(). + mWorkingSpinner = getContext().getResources(). getDrawable(com.android.internal.R.drawable.search_spinner); // attach listeners @@ -423,11 +422,11 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS if (working) { mSearchAutoComplete.setCompoundDrawablesWithIntrinsicBounds( null, null, mWorkingSpinner, null); - mWorkingSpinner.start(); +// mWorkingSpinner.start(); } else { mSearchAutoComplete.setCompoundDrawablesWithIntrinsicBounds( null, null, null, null); - mWorkingSpinner.stop(); +// mWorkingSpinner.stop(); } } @@ -601,7 +600,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS mSearchPlate.getPaddingBottom()); } else { PackageManager pm = getContext().getPackageManager(); - Drawable icon = null; + Drawable icon; try { ActivityInfo info = pm.getActivityInfo(mLaunchComponent, 0); icon = pm.getApplicationIcon(info.applicationInfo); diff --git a/core/java/android/backup/BackupManager.java b/core/java/android/backup/BackupManager.java index 79e2c03..5b4ac0d 100644 --- a/core/java/android/backup/BackupManager.java +++ b/core/java/android/backup/BackupManager.java @@ -83,11 +83,11 @@ public class BackupManager { * * {@hide} */ - public IRestoreSession beginRestoreSession(int transportID) { + public IRestoreSession beginRestoreSession(String transport) { IRestoreSession binder = null; if (mService != null) { try { - binder = mService.beginRestoreSession(transportID); + binder = mService.beginRestoreSession(transport); } catch (RemoteException e) { } } diff --git a/core/java/android/backup/IBackupManager.aidl b/core/java/android/backup/IBackupManager.aidl index d6283d0..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 @@ -63,23 +81,32 @@ interface IBackupManager { * Identify the currently selected transport. Callers must hold the * android.permission.BACKUP permission to use this method. */ - int getCurrentTransport(); + String getCurrentTransport(); + + /** + * Request a list of all available backup transports' names. Callers must + * hold the android.permission.BACKUP permission to use this method. + */ + String[] listAllTransports(); /** - * Specify a default backup transport. Callers must hold the + * Specify the current backup transport. Callers must hold the * android.permission.BACKUP permission to use this method. * - * @param transportID The ID of the transport to select. This should be one + * @param transport The name of the transport to select. This should be one * of {@link BackupManager.TRANSPORT_GOOGLE} or {@link BackupManager.TRANSPORT_ADB}. - * @return The ID of the previously selected transport. + * @return The name of the previously selected transport. If the given transport + * name is not one of the currently available transports, no change is made to + * the current transport setting and the method returns null. */ - int selectBackupTransport(int transportID); + String selectBackupTransport(String transport); /** * Begin a restore session with the given transport (which may differ from the * currently-active backup transport). * + * @param transport The name of the transport to use for the restore operation. * @return An interface to the restore session, or null on error. */ - IRestoreSession beginRestoreSession(int transportID); + IRestoreSession beginRestoreSession(String transportID); } diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java index 09fbc97..3ce951f 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -155,7 +155,11 @@ public class Camera { * @throws IOException if the method fails. */ public final void setPreviewDisplay(SurfaceHolder holder) throws IOException { - setPreviewDisplay(holder.getSurface()); + if (holder != null) { + setPreviewDisplay(holder.getSurface()); + } else { + setPreviewDisplay((Surface)null); + } } private native final void setPreviewDisplay(Surface surface); diff --git a/core/java/android/net/http/RequestHandle.java b/core/java/android/net/http/RequestHandle.java index c4ee5b0..6a97951 100644 --- a/core/java/android/net/http/RequestHandle.java +++ b/core/java/android/net/http/RequestHandle.java @@ -159,11 +159,11 @@ public class RequestHandle { e.printStackTrace(); } - // update the "cookie" header based on the redirected url - mHeaders.remove("cookie"); + // update the "Cookie" header based on the redirected url + mHeaders.remove("Cookie"); String cookie = CookieManager.getInstance().getCookie(mUri); if (cookie != null && cookie.length() > 0) { - mHeaders.put("cookie", cookie); + mHeaders.put("Cookie", cookie); } if ((statusCode == 302 || statusCode == 303) && mMethod.equals("POST")) { diff --git a/core/java/android/preference/PreferenceScreen.java b/core/java/android/preference/PreferenceScreen.java index 6ea2528..95e5432 100644 --- a/core/java/android/preference/PreferenceScreen.java +++ b/core/java/android/preference/PreferenceScreen.java @@ -150,7 +150,7 @@ public final class PreferenceScreen extends PreferenceGroup implements AdapterVi // Set the title bar if title is available, else no title bar final CharSequence title = getTitle(); - Dialog dialog = mDialog = new Dialog(context, !TextUtils.isEmpty(title) + Dialog dialog = mDialog = new Dialog(context, TextUtils.isEmpty(title) ? com.android.internal.R.style.Theme_NoTitleBar : com.android.internal.R.style.Theme); dialog.setContentView(listView); diff --git a/core/java/android/provider/Browser.java b/core/java/android/provider/Browser.java index 789fdff..1ba5e25 100644 --- a/core/java/android/provider/Browser.java +++ b/core/java/android/provider/Browser.java @@ -91,6 +91,17 @@ public class Browser { */ public static final String EXTRA_APPEND_LOCATION = "com.android.browser.append_location"; + /** + * The name of the extra data in the VIEW intent. The data is in the format of + * a byte array. + * <p> + * Any value sent here will be passed in the http request to the provided url as post data. + * <p> + * pending api approval + * @hide + */ + public static final String EXTRA_POST_DATA = "com.android.browser.post_data"; + /* if you change column order you must also change indices below */ public static final String[] HISTORY_PROJECTION = new String[] { diff --git a/core/java/android/speech/tts/ITts.aidl b/core/java/android/speech/tts/ITts.aidl index 47976e5..15f3876 100755 --- a/core/java/android/speech/tts/ITts.aidl +++ b/core/java/android/speech/tts/ITts.aidl @@ -43,6 +43,10 @@ interface ITts { void addSpeechFile(in String text, in String filename);
+ String[] getLanguage();
+
+ int isLanguageAvailable(in String language, in String country, in String variant);
+
void setLanguage(in String language, in String country, in String variant);
boolean synthesizeToFile(in String text, in String[] params, in String outputDirectory); diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java index 41b25ec..b245713 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,10 +378,37 @@ 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) { - //TODO: Implement speakIpa + synchronized (mStartLock) { + Log.i("TTS received: ", ipaText); + if (!mStarted) { + 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; + initTts(); + } catch (NullPointerException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } catch (IllegalStateException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } + return TTS_ERROR; + } } @@ -382,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; @@ -405,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; @@ -430,6 +484,7 @@ public class TextToSpeech { mStarted = false; initTts(); } + return TTS_ERROR; } } @@ -466,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; @@ -487,6 +545,7 @@ public class TextToSpeech { mStarted = false; initTts(); } + return TTS_ERROR; } } @@ -503,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; } } @@ -536,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; } } @@ -564,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(); @@ -576,31 +645,72 @@ 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; + } + } + + + /** + * Returns a Locale instance describing the language currently being used by the TTS engine. + * @return language, country (if any) and variant (if any) used by the engine stored in a Locale + * instance, or null is the TTS engine has failed. + */ + public Locale getLanguage() { + synchronized (mStartLock) { + if (!mStarted) { + return null; + } + try { + String[] locStrings = mITts.getLanguage(); + if (locStrings.length == 3) { + return new Locale(locStrings[0], locStrings[1], locStrings[2]); + } else { + return null; + } } catch (RemoteException e) { // TTS died; restart it. mStarted = false; initTts(); } + return null; } } /** - * Checks if the specified language as represented by the locale is available. + * Checks if the specified language as represented by the Locale is available. * * @param loc - * The locale describing the language to be used. + * The Locale describing the language to be used. + * * @return one of TTS_LANG_NOT_SUPPORTED, TTS_LANG_MISSING_DATA, TTS_LANG_AVAILABLE, - TTS_LANG_COUNTRY_AVAILABLE, TTS_LANG_COUNTRY_VAR_AVAILABLE. + * TTS_LANG_COUNTRY_AVAILABLE, TTS_LANG_COUNTRY_VAR_AVAILABLE. */ public int isLanguageAvailable(Locale loc) { - //TODO: Implement isLanguageAvailable - return TTS_LANG_NOT_SUPPORTED; + synchronized (mStartLock) { + if (!mStarted) { + return TTS_LANG_NOT_SUPPORTED; + } + try { + return mITts.isLanguageAvailable(loc.getISO3Language(), loc.getISO3Country(), + loc.getVariant()); + } catch (RemoteException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } + return TTS_LANG_NOT_SUPPORTED; + } } - /** - * Speaks the given text using the specified queueing mode and parameters. + * Synthesizes the given text to a file using the specified parameters. * * @param text * The String of text that should be synthesized @@ -609,17 +719,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; @@ -633,9 +746,52 @@ public class TextToSpeech { mStarted = false; initTts(); } - return false; + return TTS_ERROR; } } + /** + * Synthesizes the given IPA text to a file using the specified parameters. + * + * @param text + * The String of text that should be synthesized + * @param params + * A hashmap of parameters. + * @param filename + * The string that gives the full output filename; it should be + * something like "/sdcard/myappsounds/mysound.wav". + * + * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS. + * + * {@hide} + */ + public int synthesizeIpaToFile(String ipaText, + HashMap<String,String> params, String filename) { + synchronized (mStartLock) { + if (!mStarted) { + return TTS_ERROR; + } + try { + // TODO support extra parameters, passing null for the moment + if (mITts.synthesizeIpaToFile(ipaText, null, filename)){ + return TTS_SUCCESS; + } + } catch (RemoteException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } catch (NullPointerException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } catch (IllegalStateException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } + return TTS_ERROR; + } + } + } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 3bfdde8..b3180ca 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -1690,6 +1690,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility private int[] mDrawableState = null; private SoftReference<Bitmap> mDrawingCache; + private SoftReference<Bitmap> mUnscaledDrawingCache; /** * When this view has focus and the next focus is {@link #FOCUS_LEFT}, @@ -5783,28 +5784,52 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } /** + * <p>Calling this method is equivalent to calling <code>getDrawingCache(false)</code>.</p> + * + * @return A non-scaled bitmap representing this view or null if cache is disabled. + * + * @see #getDrawingCache(boolean) + */ + public Bitmap getDrawingCache() { + return getDrawingCache(false); + } + + /** * <p>Returns the bitmap in which this view drawing is cached. The returned bitmap * is null when caching is disabled. If caching is enabled and the cache is not ready, * this method will create it. Calling {@link #draw(android.graphics.Canvas)} will not * draw from the cache when the cache is enabled. To benefit from the cache, you must * request the drawing cache by calling this method and draw it on screen if the * returned bitmap is not null.</p> - * - * @return a bitmap representing this view or null if cache is disabled - * + * + * <p>Note about auto scaling in compatibility mode: When auto scaling is not enabled, + * this method will create a bitmap of the same size as this view. Because this bitmap + * will be drawn scaled by the parent ViewGroup, the result on screen might show + * scaling artifacts. To avoid such artifacts, you should call this method by setting + * the auto scaling to true. Doing so, however, will generate a bitmap of a different + * size than the view. This implies that your application must be able to handle this + * size.</p> + * + * @param autoScale Indicates whether the generated bitmap should be scaled based on + * the current density of the screen when the application is in compatibility + * mode. + * + * @return A bitmap representing this view or null if cache is disabled. + * * @see #setDrawingCacheEnabled(boolean) * @see #isDrawingCacheEnabled() - * @see #buildDrawingCache() + * @see #buildDrawingCache(boolean) * @see #destroyDrawingCache() */ - public Bitmap getDrawingCache() { + public Bitmap getDrawingCache(boolean autoScale) { if ((mViewFlags & WILL_NOT_CACHE_DRAWING) == WILL_NOT_CACHE_DRAWING) { return null; } if ((mViewFlags & DRAWING_CACHE_ENABLED) == DRAWING_CACHE_ENABLED) { - buildDrawingCache(); + buildDrawingCache(autoScale); } - return mDrawingCache == null ? null : mDrawingCache.get(); + return autoScale ? (mDrawingCache == null ? null : mDrawingCache.get()) : + (mUnscaledDrawingCache == null ? null : mUnscaledDrawingCache.get()); } /** @@ -5823,6 +5848,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility if (bitmap != null) bitmap.recycle(); mDrawingCache = null; } + if (mUnscaledDrawingCache != null) { + final Bitmap bitmap = mUnscaledDrawingCache.get(); + if (bitmap != null) bitmap.recycle(); + mUnscaledDrawingCache = null; + } } /** @@ -5850,18 +5880,36 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } /** + * <p>Calling this method is equivalent to calling <code>buildDrawingCache(false)</code>.</p> + * + * @see #buildDrawingCache(boolean) + */ + public void buildDrawingCache() { + buildDrawingCache(false); + } + + /** * <p>Forces the drawing cache to be built if the drawing cache is invalid.</p> * * <p>If you call {@link #buildDrawingCache()} manually without calling * {@link #setDrawingCacheEnabled(boolean) setDrawingCacheEnabled(true)}, you * should cleanup the cache by calling {@link #destroyDrawingCache()} afterwards.</p> + * + * <p>Note about auto scaling in compatibility mode: When auto scaling is not enabled, + * this method will create a bitmap of the same size as this view. Because this bitmap + * will be drawn scaled by the parent ViewGroup, the result on screen might show + * scaling artifacts. To avoid such artifacts, you should call this method by setting + * the auto scaling to true. Doing so, however, will generate a bitmap of a different + * size than the view. This implies that your application must be able to handle this + * size.</p> * * @see #getDrawingCache() * @see #destroyDrawingCache() */ - public void buildDrawingCache() { - if ((mPrivateFlags & DRAWING_CACHE_VALID) == 0 || mDrawingCache == null || - mDrawingCache.get() == null) { + public void buildDrawingCache(boolean autoScale) { + if ((mPrivateFlags & DRAWING_CACHE_VALID) == 0 || (autoScale ? + (mDrawingCache == null || mDrawingCache.get() == null) : + (mUnscaledDrawingCache == null || mUnscaledDrawingCache.get() == null))) { if (ViewDebug.TRACE_HIERARCHY) { ViewDebug.trace(this, ViewDebug.HierarchyTraceType.BUILD_CACHE); @@ -5874,12 +5922,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility int height = mBottom - mTop; final AttachInfo attachInfo = mAttachInfo; - if (attachInfo != null) { - final boolean scalingRequired = attachInfo.mScalingRequired; - if (scalingRequired) { - width = (int) ((width * attachInfo.mApplicationScale) + 0.5f); - height = (int) ((height * attachInfo.mApplicationScale) + 0.5f); - } + final boolean scalingRequired = attachInfo != null && attachInfo.mScalingRequired; + + if (autoScale && scalingRequired) { + width = (int) ((width * attachInfo.mApplicationScale) + 0.5f); + height = (int) ((height * attachInfo.mApplicationScale) + 0.5f); } final int drawingCacheBackgroundColor = mDrawingCacheBackgroundColor; @@ -5894,7 +5941,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } boolean clear = true; - Bitmap bitmap = mDrawingCache == null ? null : mDrawingCache.get(); + Bitmap bitmap = autoScale ? (mDrawingCache == null ? null : mDrawingCache.get()) : + (mUnscaledDrawingCache == null ? null : mUnscaledDrawingCache.get()); if (bitmap == null || bitmap.getWidth() != width || bitmap.getHeight() != height) { @@ -5923,12 +5971,20 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility try { bitmap = Bitmap.createBitmap(width, height, quality); - mDrawingCache = new SoftReference<Bitmap>(bitmap); + if (autoScale) { + mDrawingCache = new SoftReference<Bitmap>(bitmap); + } else { + mUnscaledDrawingCache = new SoftReference<Bitmap>(bitmap); + } } catch (OutOfMemoryError e) { // If there is not enough memory to create the bitmap cache, just // ignore the issue as bitmap caches are not required to draw the // view hierarchy - mDrawingCache = null; + if (autoScale) { + mDrawingCache = null; + } else { + mUnscaledDrawingCache = null; + } return; } @@ -5940,13 +5996,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility canvas = attachInfo.mCanvas; if (canvas == null) { canvas = new Canvas(); - - // NOTE: This should have to happen only once since compatibility - // mode should not change at runtime - if (attachInfo.mScalingRequired) { - final float scale = attachInfo.mApplicationScale; - canvas.scale(scale, scale); - } } canvas.setBitmap(bitmap); // Temporarily clobber the cached Canvas in case one of our children @@ -5965,6 +6014,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility computeScroll(); final int restoreCount = canvas.save(); + + if (autoScale && scalingRequired) { + final float scale = attachInfo.mApplicationScale; + canvas.scale(scale, scale); + } + canvas.translate(-mScrollX, -mScrollY); mPrivateFlags |= DRAWN; diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index f803b5a..f7b7f02 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -1166,7 +1166,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager final View child = children[i]; if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) { child.setDrawingCacheEnabled(true); - child.buildDrawingCache(); + child.buildDrawingCache(true); } } @@ -1208,7 +1208,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager bindLayoutAnimation(child); if (cache) { child.setDrawingCacheEnabled(true); - child.buildDrawingCache(); + child.buildDrawingCache(true); } } } @@ -1448,7 +1448,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager Bitmap cache = null; if ((flags & FLAG_CHILDREN_DRAWN_WITH_CACHE) == FLAG_CHILDREN_DRAWN_WITH_CACHE || (flags & FLAG_ALWAYS_DRAWN_WITH_CACHE) == FLAG_ALWAYS_DRAWN_WITH_CACHE) { - cache = child.getDrawingCache(); + cache = child.getDrawingCache(true); if (mAttachInfo != null) scalingRequired = mAttachInfo.mScalingRequired; } diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java index 9a0f467..7393737 100644 --- a/core/java/android/view/inputmethod/BaseInputConnection.java +++ b/core/java/android/view/inputmethod/BaseInputConnection.java @@ -340,10 +340,19 @@ public class BaseInputConnection implements InputConnection { } /** - * The default implementation does nothing. + * The default implementation turns this into the enter key. */ public boolean performEditorAction(int actionCode) { - return false; + long eventTime = SystemClock.uptimeMillis(); + sendKeyEvent(new KeyEvent(eventTime, eventTime, + KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER, 0, 0, 0, 0, + KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE + | KeyEvent.FLAG_EDITOR_ACTION)); + sendKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), eventTime, + KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER, 0, 0, 0, 0, + KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE + | KeyEvent.FLAG_EDITOR_ACTION)); + return true; } /** diff --git a/core/java/android/webkit/FrameLoader.java b/core/java/android/webkit/FrameLoader.java index 6f1b160..66ab021 100644 --- a/core/java/android/webkit/FrameLoader.java +++ b/core/java/android/webkit/FrameLoader.java @@ -364,7 +364,7 @@ class FrameLoader { String cookie = CookieManager.getInstance().getCookie( mListener.getWebAddress()); if (cookie != null && cookie.length() > 0) { - mHeaders.put("cookie", cookie); + mHeaders.put("Cookie", cookie); } } } diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java index 70749d1..e84e5b0 100644 --- a/core/java/android/widget/AutoCompleteTextView.java +++ b/core/java/android/widget/AutoCompleteTextView.java @@ -1202,7 +1202,11 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe int position, long id) { if (position != -1) { - mDropDownList.mListSelectionHidden = false; + DropDownListView dropDownList = mDropDownList; + + if (dropDownList != null) { + dropDownList.mListSelectionHidden = false; + } } } 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/java/com/android/internal/util/BitwiseInputStream.java b/core/java/com/android/internal/util/BitwiseInputStream.java index 031abdd..86f74f3 100644 --- a/core/java/com/android/internal/util/BitwiseInputStream.java +++ b/core/java/com/android/internal/util/BitwiseInputStream.java @@ -65,8 +65,10 @@ public class BitwiseInputStream { /** * Read some data and increment the current position. * - * @param bits the amount of data to read (gte 0, lte 8) + * The 8-bit limit on access to bitwise streams is intentional to + * avoid endianness issues. * + * @param bits the amount of data to read (gte 0, lte 8) * @return byte of read data (possibly partially filled, from lsb) */ public int read(int bits) throws AccessException { @@ -88,7 +90,6 @@ public class BitwiseInputStream { * Read data in bulk into a byte array and increment the current position. * * @param bits the amount of data to read - * * @return newly allocated byte array of read data */ public byte[] readByteArray(int bits) throws AccessException { diff --git a/core/java/com/android/internal/util/BitwiseOutputStream.java b/core/java/com/android/internal/util/BitwiseOutputStream.java index ab8a7f3..70c0be8 100644 --- a/core/java/com/android/internal/util/BitwiseOutputStream.java +++ b/core/java/com/android/internal/util/BitwiseOutputStream.java @@ -82,6 +82,9 @@ public class BitwiseOutputStream { /** * Write some data and increment the current position. * + * The 8-bit limit on access to bitwise streams is intentional to + * avoid endianness issues. + * * @param bits the amount of data to write (gte 0, lte 8) * @param data to write, will be masked to expose only bits param from lsb */ diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp index 8e48b38..3550716 100644 --- a/core/jni/android_hardware_Camera.cpp +++ b/core/jni/android_hardware_Camera.cpp @@ -262,7 +262,10 @@ static void android_hardware_Camera_setPreviewDisplay(JNIEnv *env, jobject thiz, sp<Camera> camera = get_native_camera(env, thiz, NULL); if (camera == 0) return; - sp<Surface> surface = reinterpret_cast<Surface*>(env->GetIntField(jSurface, fields.surface)); + sp<Surface> surface = NULL; + if (jSurface != NULL) { + surface = reinterpret_cast<Surface*>(env->GetIntField(jSurface, fields.surface)); + } if (camera->setPreviewDisplay(surface) != NO_ERROR) { jniThrowException(env, "java/io/IOException", "setPreviewDisplay failed"); } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 7f24dcc..599360f 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -973,7 +973,7 @@ <permission android:name="android.permission.BACKUP" android:label="@string/permlab_backup" android:description="@string/permdesc_backup" - android:protectionLevel="signature" /> + android:protectionLevel="signatureOrSystem" /> <!-- Allows an application to tell the AppWidget service which application can access AppWidget's data. The normal user flow is that a user diff --git a/core/res/res/drawable/progress.xml b/core/res/res/drawable/progress.xml deleted file mode 100644 index d270520..0000000 --- a/core/res/res/drawable/progress.xml +++ /dev/null @@ -1,40 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* //device/apps/common/res/drawable/progress.xml -** -** Copyright 2007, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> - -<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:drawable="@android:drawable/progress_circular_background" /> - <item> - <shape android:shape="ring" - android:innerRadiusRatio="3.4" - android:thicknessRatio="6.0"> - <gradient - android:useLevel="true" - android:type="sweep" - android:startColor="#ff000000" - android:endColor="#ffffffff" /> - </shape> - </item> - <item> - <rotate - android:pivotX="50%" android:pivotY="50%" - android:fromDegrees="0" android:toDegrees="360" - android:drawable="@android:drawable/progress_particle" /> - </item> -</layer-list> diff --git a/core/res/res/drawable/progress_circular_background.png b/core/res/res/drawable/progress_circular_background.png Binary files differdeleted file mode 100644 index 7c637fd..0000000 --- a/core/res/res/drawable/progress_circular_background.png +++ /dev/null diff --git a/core/res/res/drawable/progress_circular_background_small.png b/core/res/res/drawable/progress_circular_background_small.png Binary files differdeleted file mode 100644 index 6b8ba9b..0000000 --- a/core/res/res/drawable/progress_circular_background_small.png +++ /dev/null diff --git a/core/res/res/drawable/progress_circular_indeterminate.png b/core/res/res/drawable/progress_circular_indeterminate.png Binary files differdeleted file mode 100644 index 125a264..0000000 --- a/core/res/res/drawable/progress_circular_indeterminate.png +++ /dev/null diff --git a/core/res/res/drawable/progress_indeterminate.xml b/core/res/res/drawable/progress_indeterminate.xml deleted file mode 100644 index 1bf715e..0000000 --- a/core/res/res/drawable/progress_indeterminate.xml +++ /dev/null @@ -1,32 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* //device/apps/common/res/drawable/progress.xml -** -** Copyright 2007, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> - -<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> - <item - android:drawable="@android:drawable/progress_circular_background" /> - - <item><rotate - android:pivotX="50%" - android:pivotY="50%" - android:fromDegrees="0" - android:toDegrees="360" - android:drawable="@android:drawable/progress_circular_indeterminate" /> - </item> -</layer-list> diff --git a/core/res/res/drawable/progress_particle.png b/core/res/res/drawable/progress_particle.png Binary files differdeleted file mode 100644 index 9160108..0000000 --- a/core/res/res/drawable/progress_particle.png +++ /dev/null diff --git a/core/res/res/drawable/search_spinner.xml b/core/res/res/drawable/search_spinner.xml index 34c163d..31a77c3 100644 --- a/core/res/res/drawable/search_spinner.xml +++ b/core/res/res/drawable/search_spinner.xml @@ -2,7 +2,7 @@ <!-- /* ** -** Copyright 2008, The Android Open Source Project +** Copyright 2009, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. @@ -17,20 +17,9 @@ ** limitations under the License. */ --> -<animation-list - xmlns:android="http://schemas.android.com/apk/res/android" - android:oneshot="false"> - <item android:drawable="@drawable/search_spinner_anim1" android:duration="150" /> - <item android:drawable="@drawable/search_spinner_anim2" android:duration="150" /> - <item android:drawable="@drawable/search_spinner_anim3" android:duration="150" /> - <item android:drawable="@drawable/search_spinner_anim4" android:duration="150" /> - <item android:drawable="@drawable/search_spinner_anim5" android:duration="150" /> - <item android:drawable="@drawable/search_spinner_anim6" android:duration="150" /> - <item android:drawable="@drawable/search_spinner_anim7" android:duration="150" /> - <item android:drawable="@drawable/search_spinner_anim8" android:duration="150" /> - <item android:drawable="@drawable/search_spinner_anim9" android:duration="150" /> - <item android:drawable="@drawable/search_spinner_anim10" android:duration="150" /> - <item android:drawable="@drawable/search_spinner_anim11" android:duration="150" /> - <item android:drawable="@drawable/search_spinner_anim12" android:duration="150" /> -</animation-list> - +<animated-rotate xmlns:android="http://schemas.android.com/apk/res/android" + android:drawable="@drawable/spinner_black_20" + android:pivotX="50%" + android:pivotY="50%" + android:framesCount="12" + android:frameDuration="100" /> diff --git a/core/res/res/drawable/search_spinner_anim10.png b/core/res/res/drawable/search_spinner_anim10.png Binary files differdeleted file mode 100755 index 9611d97..0000000 --- a/core/res/res/drawable/search_spinner_anim10.png +++ /dev/null diff --git a/core/res/res/drawable/search_spinner_anim11.png b/core/res/res/drawable/search_spinner_anim11.png Binary files differdeleted file mode 100755 index 4261704..0000000 --- a/core/res/res/drawable/search_spinner_anim11.png +++ /dev/null diff --git a/core/res/res/drawable/search_spinner_anim12.png b/core/res/res/drawable/search_spinner_anim12.png Binary files differdeleted file mode 100755 index 0602314..0000000 --- a/core/res/res/drawable/search_spinner_anim12.png +++ /dev/null diff --git a/core/res/res/drawable/search_spinner_anim2.png b/core/res/res/drawable/search_spinner_anim2.png Binary files differdeleted file mode 100755 index 05d58e0..0000000 --- a/core/res/res/drawable/search_spinner_anim2.png +++ /dev/null diff --git a/core/res/res/drawable/search_spinner_anim3.png b/core/res/res/drawable/search_spinner_anim3.png Binary files differdeleted file mode 100755 index 69fa9c1..0000000 --- a/core/res/res/drawable/search_spinner_anim3.png +++ /dev/null diff --git a/core/res/res/drawable/search_spinner_anim4.png b/core/res/res/drawable/search_spinner_anim4.png Binary files differdeleted file mode 100755 index 9201bac..0000000 --- a/core/res/res/drawable/search_spinner_anim4.png +++ /dev/null diff --git a/core/res/res/drawable/search_spinner_anim5.png b/core/res/res/drawable/search_spinner_anim5.png Binary files differdeleted file mode 100755 index f0c7101..0000000 --- a/core/res/res/drawable/search_spinner_anim5.png +++ /dev/null diff --git a/core/res/res/drawable/search_spinner_anim6.png b/core/res/res/drawable/search_spinner_anim6.png Binary files differdeleted file mode 100755 index 99d1d4e..0000000 --- a/core/res/res/drawable/search_spinner_anim6.png +++ /dev/null diff --git a/core/res/res/drawable/search_spinner_anim7.png b/core/res/res/drawable/search_spinner_anim7.png Binary files differdeleted file mode 100755 index 8ca3358..0000000 --- a/core/res/res/drawable/search_spinner_anim7.png +++ /dev/null diff --git a/core/res/res/drawable/search_spinner_anim8.png b/core/res/res/drawable/search_spinner_anim8.png Binary files differdeleted file mode 100755 index 408d723..0000000 --- a/core/res/res/drawable/search_spinner_anim8.png +++ /dev/null diff --git a/core/res/res/drawable/search_spinner_anim9.png b/core/res/res/drawable/search_spinner_anim9.png Binary files differdeleted file mode 100755 index 42a2c65..0000000 --- a/core/res/res/drawable/search_spinner_anim9.png +++ /dev/null diff --git a/core/res/res/drawable/search_spinner_anim1.png b/core/res/res/drawable/spinner_black_20.png Binary files differindex e55b60d..e55b60d 100755 --- a/core/res/res/drawable/search_spinner_anim1.png +++ b/core/res/res/drawable/spinner_black_20.png 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/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index e978ef5..d070107 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -2327,6 +2327,15 @@ <attr name="drawable" /> </declare-styleable> + <declare-styleable name="AnimatedRotateDrawable"> + <attr name="visible" /> + <attr name="frameDuration" format="integer" /> + <attr name="framesCount" format="integer" /> + <attr name="pivotX" /> + <attr name="pivotY" /> + <attr name="drawable" /> + </declare-styleable> + <declare-styleable name="InsetDrawable"> <attr name="visible" /> <attr name="drawable" /> diff --git a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java new file mode 100644 index 0000000..08d295d --- /dev/null +++ b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java @@ -0,0 +1,315 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.graphics.drawable; + +import android.graphics.Canvas; +import android.graphics.Rect; +import android.graphics.ColorFilter; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.util.AttributeSet; +import android.util.TypedValue; +import android.util.Log; +import android.os.SystemClock; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; + +import com.android.internal.R; + +/** + * @hide + */ +public class AnimatedRotateDrawable extends Drawable implements Drawable.Callback, Runnable { + private AnimatedRotateState mState; + private boolean mMutated; + private float mCurrentDegrees; + private float mIncrement; + + public AnimatedRotateDrawable() { + this(null); + } + + private AnimatedRotateDrawable(AnimatedRotateState rotateState) { + mState = new AnimatedRotateState(rotateState, this); + init(); + } + + private void init() { + final AnimatedRotateState state = mState; + mIncrement = 360.0f / (float) state.mFramesCount; + final Drawable drawable = state.mDrawable; + if (drawable != null) { + drawable.setFilterBitmap(true); + if (drawable instanceof BitmapDrawable) { + ((BitmapDrawable) drawable).setAntiAlias(true); + } + } + } + + public void draw(Canvas canvas) { + int saveCount = canvas.save(); + + final AnimatedRotateState st = mState; + final Drawable drawable = st.mDrawable; + final Rect bounds = drawable.getBounds(); + + int w = bounds.right - bounds.left; + int h = bounds.bottom - bounds.top; + + float px = st.mPivotXRel ? (w * st.mPivotX) : st.mPivotX; + float py = st.mPivotYRel ? (h * st.mPivotY) : st.mPivotY; + + canvas.rotate(mCurrentDegrees, px, py); + + drawable.draw(canvas); + + canvas.restoreToCount(saveCount); + + nextFrame(); + } + + private void nextFrame() { + unscheduleSelf(this); + scheduleSelf(this, SystemClock.uptimeMillis() + mState.mFrameDuration); + } + + public void run() { + // TODO: This should be computed in draw(Canvas), based on the amount + // of time since the last frame drawn + mCurrentDegrees += mIncrement; + if (mCurrentDegrees > (360.0f - mIncrement)) { + mCurrentDegrees = 0.0f; + } + nextFrame(); + invalidateSelf(); + } + + @Override + public boolean setVisible(boolean visible, boolean restart) { + mState.mDrawable.setVisible(visible, restart); + boolean changed = super.setVisible(visible, restart); + if (visible) { + if (changed || restart) { + mCurrentDegrees = 0.0f; + nextFrame(); + } + } else { + unscheduleSelf(this); + } + return changed; + } + + /** + * Returns the drawable rotated by this RotateDrawable. + */ + public Drawable getDrawable() { + return mState.mDrawable; + } + + @Override + public int getChangingConfigurations() { + return super.getChangingConfigurations() + | mState.mChangingConfigurations + | mState.mDrawable.getChangingConfigurations(); + } + + public void setAlpha(int alpha) { + mState.mDrawable.setAlpha(alpha); + } + + public void setColorFilter(ColorFilter cf) { + mState.mDrawable.setColorFilter(cf); + } + + public int getOpacity() { + return mState.mDrawable.getOpacity(); + } + + public void invalidateDrawable(Drawable who) { + if (mCallback != null) { + mCallback.invalidateDrawable(this); + } + } + + public void scheduleDrawable(Drawable who, Runnable what, long when) { + if (mCallback != null) { + mCallback.scheduleDrawable(this, what, when); + } + } + + public void unscheduleDrawable(Drawable who, Runnable what) { + if (mCallback != null) { + mCallback.unscheduleDrawable(this, what); + } + } + + @Override + public boolean getPadding(Rect padding) { + return mState.mDrawable.getPadding(padding); + } + + @Override + public boolean isStateful() { + return mState.mDrawable.isStateful(); + } + + @Override + protected void onBoundsChange(Rect bounds) { + mState.mDrawable.setBounds(bounds.left, bounds.top, bounds.right, bounds.bottom); + } + + @Override + public int getIntrinsicWidth() { + return mState.mDrawable.getIntrinsicWidth(); + } + + @Override + public int getIntrinsicHeight() { + return mState.mDrawable.getIntrinsicHeight(); + } + + @Override + public ConstantState getConstantState() { + if (mState.canConstantState()) { + mState.mChangingConfigurations = super.getChangingConfigurations(); + return mState; + } + return null; + } + + @Override + public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs) + throws XmlPullParserException, IOException { + + final TypedArray a = r.obtainAttributes(attrs, R.styleable.AnimatedRotateDrawable); + + super.inflateWithAttributes(r, parser, a, R.styleable.AnimatedRotateDrawable_visible); + + TypedValue tv = a.peekValue(R.styleable.AnimatedRotateDrawable_pivotX); + final boolean pivotXRel = tv.type == TypedValue.TYPE_FRACTION; + final float pivotX = pivotXRel ? tv.getFraction(1.0f, 1.0f) : tv.getFloat(); + + tv = a.peekValue(R.styleable.AnimatedRotateDrawable_pivotY); + final boolean pivotYRel = tv.type == TypedValue.TYPE_FRACTION; + final float pivotY = pivotYRel ? tv.getFraction(1.0f, 1.0f) : tv.getFloat(); + + final int framesCount = a.getInt(R.styleable.AnimatedRotateDrawable_framesCount, 12); + final int frameDuration = a.getInt(R.styleable.AnimatedRotateDrawable_frameDuration, 150); + + final int res = a.getResourceId(R.styleable.AnimatedRotateDrawable_drawable, 0); + Drawable drawable = null; + if (res > 0) { + drawable = r.getDrawable(res); + } + + a.recycle(); + + int outerDepth = parser.getDepth(); + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && + (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { + + if (type != XmlPullParser.START_TAG) { + continue; + } + + if ((drawable = Drawable.createFromXmlInner(r, parser, attrs)) == null) { + Log.w("drawable", "Bad element under <animated-rotate>: " + + parser .getName()); + } + } + + if (drawable == null) { + Log.w("drawable", "No drawable specified for <animated-rotate>"); + } + + final AnimatedRotateState rotateState = mState; + rotateState.mDrawable = drawable; + rotateState.mPivotXRel = pivotXRel; + rotateState.mPivotX = pivotX; + rotateState.mPivotYRel = pivotYRel; + rotateState.mPivotY = pivotY; + rotateState.mFramesCount = framesCount; + rotateState.mFrameDuration = frameDuration; + + init(); + + if (drawable != null) { + drawable.setCallback(this); + } + } + + @Override + public Drawable mutate() { + if (!mMutated && super.mutate() == this) { + mState.mDrawable.mutate(); + mMutated = true; + } + return this; + } + + final static class AnimatedRotateState extends Drawable.ConstantState { + Drawable mDrawable; + + int mChangingConfigurations; + + boolean mPivotXRel; + float mPivotX; + boolean mPivotYRel; + float mPivotY; + int mFrameDuration; + int mFramesCount; + + private boolean mCanConstantState; + private boolean mCheckedConstantState; + + public AnimatedRotateState(AnimatedRotateState source, AnimatedRotateDrawable owner) { + if (source != null) { + mDrawable = source.mDrawable.getConstantState().newDrawable(); + mDrawable.setCallback(owner); + mPivotXRel = source.mPivotXRel; + mPivotX = source.mPivotX; + mPivotYRel = source.mPivotYRel; + mPivotY = source.mPivotY; + mFramesCount = source.mFramesCount; + mFrameDuration = source.mFrameDuration; + mCanConstantState = mCheckedConstantState = true; + } + } + + @Override + public Drawable newDrawable() { + return new AnimatedRotateDrawable(this); + } + + @Override + public int getChangingConfigurations() { + return mChangingConfigurations; + } + + public boolean canConstantState() { + if (!mCheckedConstantState) { + mCanConstantState = mDrawable.getConstantState() != null; + mCheckedConstantState = true; + } + + return mCanConstantState; + } + } +} diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java index f0d49f5..910e111 100644 --- a/graphics/java/android/graphics/drawable/Drawable.java +++ b/graphics/java/android/graphics/drawable/Drawable.java @@ -745,6 +745,8 @@ public abstract class Drawable { drawable = new ClipDrawable(); } else if (name.equals("rotate")) { drawable = new RotateDrawable(); + } else if (name.equals("animated-rotate")) { + drawable = new AnimatedRotateDrawable(); } else if (name.equals("animation-list")) { drawable = new AnimationDrawable(); } else if (name.equals("inset")) { diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java index f8b88d0..376b1df 100644 --- a/graphics/java/android/graphics/drawable/DrawableContainer.java +++ b/graphics/java/android/graphics/drawable/DrawableContainer.java @@ -234,8 +234,10 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { @Override public Drawable mutate() { if (!mMutated && super.mutate() == this) { - for (Drawable child : mDrawableContainerState.mDrawables) { - child.mutate(); + final int N = mDrawableContainerState.getChildCount(); + final Drawable[] drawables = mDrawableContainerState.getChildren(); + for (int i = 0; i < N; i++) { + drawables[i].mutate(); } mMutated = true; } diff --git a/include/tts/TtsEngine.h b/include/tts/TtsEngine.h index ca50a5e..21cb73b 100644 --- a/include/tts/TtsEngine.h +++ b/include/tts/TtsEngine.h @@ -133,16 +133,26 @@ public: // @return TTS_SUCCESS, or TTS_FAILURE virtual tts_result setLanguage(const char *lang, const char *country, const char *variant); - // Retrieve the currently set language, or an empty "value" if no language - // has been set. - // @param[out] value pointer to the retrieved language value - // @param[inout] iosize in: stores the size available to store the language - // value in *value - // out: stores the size required to hold the language - // value if getLanguage() returned - // TTS_PROPERTY_SIZE_TOO_SMALL, unchanged otherwise. - // @return TTS_SUCCESS, or TTS_PROPERTY_SIZE_TOO_SMALL, or TTS_FAILURE - virtual tts_result getLanguage(char *value, size_t *iosize); + // Retrieve the currently set language, country and variant, or empty strings if none of + // parameters have been set. Language and country are represented by their 3-letter ISO code + // @param[out] pointer to the retrieved 3-letter code language value + // @param[out] pointer to the retrieved 3-letter code country value + // @param[out] pointer to the retrieved variant value + // @return TTS_SUCCESS, or TTS_FAILURE + virtual tts_result getLanguage(char *language, char *country, char *variant); + + // Notifies the engine what audio parameters should be used for the synthesis. + // This is meant to be used as a hint, the engine implementation will set the output values + // to those of the synthesis format, based on a given hint. + // @param[inout] encoding in: the desired audio sample format + // out: the format used by the TTS engine + // @param[inout] rate in: the desired audio sample rate + // out: the sample rate used by the TTS engine + // @param[inout] channels in: the desired number of audio channels + // out: the number of channels used by the TTS engine + // @return TTS_SUCCESS, or TTS_FAILURE + virtual tts_result setAudioFormat(AudioSystem::audio_format& encoding, uint32_t& rate, + int& channels); // Set a property for the the TTS engine // "size" is the maximum size of "value" for properties "property" diff --git a/libs/surfaceflinger/SurfaceFlinger.cpp b/libs/surfaceflinger/SurfaceFlinger.cpp index 0e998bf..ef4a8ea 100644 --- a/libs/surfaceflinger/SurfaceFlinger.cpp +++ b/libs/surfaceflinger/SurfaceFlinger.cpp @@ -771,10 +771,11 @@ 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/libs/ui/Camera.cpp b/libs/ui/Camera.cpp index bb22dab..a481ce7 100644 --- a/libs/ui/Camera.cpp +++ b/libs/ui/Camera.cpp @@ -149,21 +149,21 @@ status_t Camera::unlock() status_t Camera::setPreviewDisplay(const sp<Surface>& surface) { LOGV("setPreviewDisplay"); - if (surface == 0) { - LOGE("app passed NULL surface"); - return NO_INIT; - } sp <ICamera> c = mCamera; if (c == 0) return NO_INIT; - return c->setPreviewDisplay(surface->getISurface()); + if (surface != 0) { + return c->setPreviewDisplay(surface->getISurface()); + } else { + LOGD("app passed NULL surface"); + return c->setPreviewDisplay(0); + } } status_t Camera::setPreviewDisplay(const sp<ISurface>& surface) { LOGV("setPreviewDisplay"); if (surface == 0) { - LOGE("app passed NULL surface"); - return NO_INIT; + LOGD("app passed NULL surface"); } sp <ICamera> c = mCamera; if (c == 0) return NO_INIT; @@ -171,7 +171,7 @@ status_t Camera::setPreviewDisplay(const sp<ISurface>& surface) } -// start preview mode, must call setPreviewDisplay first +// start preview mode status_t Camera::startPreview() { LOGV("startPreview"); diff --git a/media/libmediaplayerservice/VorbisPlayer.cpp b/media/libmediaplayerservice/VorbisPlayer.cpp index 14fd6ce..7f0ef21 100644 --- a/media/libmediaplayerservice/VorbisPlayer.cpp +++ b/media/libmediaplayerservice/VorbisPlayer.cpp @@ -345,9 +345,6 @@ status_t VorbisPlayer::reset() { LOGV("reset\n"); Mutex::Autolock l(mMutex); - if (mState != STATE_OPEN) { - return NO_ERROR; - } return reset_nosync(); } @@ -355,10 +352,13 @@ status_t VorbisPlayer::reset() status_t VorbisPlayer::reset_nosync() { // close file - ov_clear(&mVorbisFile); // this also closes the FILE if (mFile != NULL) { - LOGV("OOPS! Vorbis didn't close the file"); - fclose(mFile); + ov_clear(&mVorbisFile); // this also closes the FILE + if (mFile != NULL) { + LOGV("OOPS! Vorbis didn't close the file"); + fclose(mFile); + mFile = NULL; + } } mState = STATE_ERROR; diff --git a/packages/TtsService/AndroidManifest.xml b/packages/TtsService/AndroidManifest.xml index 1dc25c6..fab2534 100755 --- a/packages/TtsService/AndroidManifest.xml +++ b/packages/TtsService/AndroidManifest.xml @@ -12,4 +12,5 @@ </service> </application> <uses-permission android:name="android.permission.INTERNET"/> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> </manifest> diff --git a/packages/TtsService/jni/android_tts_SynthProxy.cpp b/packages/TtsService/jni/android_tts_SynthProxy.cpp index 8537cae..a55b704 100644 --- a/packages/TtsService/jni/android_tts_SynthProxy.cpp +++ b/packages/TtsService/jni/android_tts_SynthProxy.cpp @@ -199,6 +199,7 @@ static tts_callback_status ttsSynthDoneCB(void *& userdata, uint32_t rate, if (wav == NULL) { delete pForAfter; LOGV("Null: speech has completed"); + return TTS_CALLBACK_HALT; } if (bufferSize > 0){ fwrite(wav, 1, bufferSize, pForAfter->outputFile); @@ -213,8 +214,12 @@ static tts_callback_status ttsSynthDoneCB(void *& userdata, uint32_t rate, // this struct was allocated in the original android_tts_SynthProxy_speak call, // all processing matching this call is now done. LOGV("Speech synthesis done."); - delete pForAfter; - pForAfter = NULL; + if (pForAfter->usageMode == USAGEMODE_PLAY_IMMEDIATELY) { + // only delete for direct playback. When writing to a file, we still have work to do + // in android_tts_SynthProxy_synthesizeToFile. The struct will be deleted there. + delete pForAfter; + pForAfter = NULL; + } return TTS_CALLBACK_HALT; } @@ -278,6 +283,33 @@ 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) @@ -370,7 +402,6 @@ android_tts_SynthProxy_setPitch(JNIEnv *env, jobject thiz, jint jniData, } -// TODO: Refactor this to get rid of any assumptions about sample rate, etc. static void android_tts_SynthProxy_synthesizeToFile(JNIEnv *env, jobject thiz, jint jniData, jstring textJavaString, jstring filenameJavaString) @@ -381,6 +412,21 @@ android_tts_SynthProxy_synthesizeToFile(JNIEnv *env, jobject thiz, jint jniData, } SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData; + if (!pSynthData->mNativeSynthInterface) { + LOGE("android_tts_SynthProxy_synthesizeToFile(): invalid engine handle"); + return; + } + + // Retrieve audio parameters before writing the file header + AudioSystem::audio_format encoding = DEFAULT_TTS_FORMAT; + uint32_t rate = DEFAULT_TTS_RATE; + int channels = DEFAULT_TTS_NB_CHANNELS; + pSynthData->mNativeSynthInterface->setAudioFormat(encoding, rate, channels); + + if ((encoding != AudioSystem::PCM_16_BIT) && (encoding != AudioSystem::PCM_8_BIT)) { + LOGE("android_tts_SynthProxy_synthesizeToFile(): engine uses invalid format"); + return; + } const char *filenameNativeString = env->GetStringUTFChars(filenameJavaString, 0); @@ -392,6 +438,12 @@ android_tts_SynthProxy_synthesizeToFile(JNIEnv *env, jobject thiz, jint jniData, pForAfter->outputFile = fopen(filenameNativeString, "wb"); + if (pForAfter->outputFile == NULL) { + LOGE("android_tts_SynthProxy_synthesizeToFile(): error creating output file"); + delete pForAfter; + return; + } + // Write 44 blank bytes for WAV header, then come back and fill them in // after we've written the audio data char header[44]; @@ -400,10 +452,8 @@ android_tts_SynthProxy_synthesizeToFile(JNIEnv *env, jobject thiz, jint jniData, unsigned int unique_identifier; // TODO check return codes - if (pSynthData->mNativeSynthInterface) { - pSynthData->mNativeSynthInterface->synthesizeText(textNativeString, pSynthData->mBuffer, pSynthData->mBufferSize, - (void *)pForAfter); - } + pSynthData->mNativeSynthInterface->synthesizeText(textNativeString, pSynthData->mBuffer, + pSynthData->mBufferSize, (void *)pForAfter); long filelen = ftell(pForAfter->outputFile); @@ -425,12 +475,14 @@ android_tts_SynthProxy_synthesizeToFile(JNIEnv *env, jobject thiz, jint jniData, ((uint32_t *)(&header[16]))[0] = 16; // size of fmt + int sampleSizeInByte = (encoding == AudioSystem::PCM_16_BIT ? 2 : 1); + ((unsigned short *)(&header[20]))[0] = 1; // format - ((unsigned short *)(&header[22]))[0] = 1; // channels - ((uint32_t *)(&header[24]))[0] = 22050; // samplerate - ((uint32_t *)(&header[28]))[0] = 44100; // byterate - ((unsigned short *)(&header[32]))[0] = 2; // block align - ((unsigned short *)(&header[34]))[0] = 16; // bits per sample + ((unsigned short *)(&header[22]))[0] = channels; // channels + ((uint32_t *)(&header[24]))[0] = rate; // samplerate + ((uint32_t *)(&header[28]))[0] = rate * sampleSizeInByte * channels;// byterate + ((unsigned short *)(&header[32]))[0] = sampleSizeInByte * channels; // block align + ((unsigned short *)(&header[34]))[0] = sampleSizeInByte * 8; // bits per sample header[36] = 'd'; header[37] = 'a'; @@ -446,6 +498,9 @@ android_tts_SynthProxy_synthesizeToFile(JNIEnv *env, jobject thiz, jint jniData, fflush(pForAfter->outputFile); fclose(pForAfter->outputFile); + delete pForAfter; + pForAfter = NULL; + env->ReleaseStringUTFChars(textJavaString, textNativeString); env->ReleaseStringUTFChars(filenameJavaString, filenameNativeString); } @@ -473,8 +528,8 @@ android_tts_SynthProxy_speak(JNIEnv *env, jobject thiz, jint jniData, if (pSynthData->mNativeSynthInterface) { const char *textNativeString = env->GetStringUTFChars(textJavaString, 0); - pSynthData->mNativeSynthInterface->synthesizeText(textNativeString, pSynthData->mBuffer, pSynthData->mBufferSize, - (void *)pForAfter); + pSynthData->mNativeSynthInterface->synthesizeText(textNativeString, pSynthData->mBuffer, + pSynthData->mBufferSize, (void *)pForAfter); env->ReleaseStringUTFChars(textJavaString, textNativeString); } } @@ -533,23 +588,34 @@ 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) { - pSynthData->mNativeSynthInterface->getLanguage(buf, &bufSize); + size_t bufSize = 100; + char lang[bufSize]; + char country[bufSize]; + char variant[bufSize]; + memset(lang, 0, bufSize); + memset(country, 0, bufSize); + memset(variant, 0, bufSize); + jobjectArray retLocale = (jobjectArray)env->NewObjectArray(3, + env->FindClass("java/lang/String"), env->NewStringUTF("")); + pSynthData->mNativeSynthInterface->getLanguage(lang, country, variant); + env->SetObjectArrayElement(retLocale, 0, env->NewStringUTF(lang)); + env->SetObjectArrayElement(retLocale, 1, env->NewStringUTF(country)); + env->SetObjectArrayElement(retLocale, 2, env->NewStringUTF(variant)); + return retLocale; + } else { + return NULL; } - return env->NewStringUTF(buf); } @@ -587,6 +653,10 @@ 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 @@ -608,7 +678,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..b1e6425 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"; @@ -147,8 +148,7 @@ public class TtsService extends Service implements OnCompletionListener { private void setDefaultSettings() { - // TODO handle default language - setLanguage("eng", "USA", ""); + setLanguage(this.getDefaultLanguage(), getDefaultCountry(), getDefaultLocVariant()); // speech rate setSpeechRate(getDefaultRate()); @@ -217,6 +217,17 @@ public class TtsService extends Service implements OnCompletionListener { } + private int isLanguageAvailable(String lang, String country, String variant) { + Log.v("TTS", "TtsService.isLanguageAvailable(" + lang + ", " + country + ", " +variant+")"); + return nativeSynth.isLanguageAvailable(lang, country, variant); + } + + + private String[] getLanguage() { + return nativeSynth.getLanguage(); + } + + private void setLanguage(String lang, String country, String variant) { Log.v("TTS", "TtsService.setLanguage(" + lang + ", " + country + ", " + variant + ")"); if (isDefaultEnforced()) { @@ -414,6 +425,26 @@ 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 +645,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 +690,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); @@ -874,6 +903,30 @@ public class TtsService extends Service implements OnCompletionListener { } /** + * Returns the level of support for the specified language. + * + * @param lang the three letter ISO language code. + * @param country the three letter ISO country code. + * @param variant the variant code associated with the country and language pair. + * @return one of TTS_LANG_NOT_SUPPORTED, TTS_LANG_MISSING_DATA, TTS_LANG_AVAILABLE, + * TTS_LANG_COUNTRY_AVAILABLE, TTS_LANG_COUNTRY_VAR_AVAILABLE as defined in + * android.speech.tts.TextToSpeech. + */ + public int isLanguageAvailable(String lang, String country, String variant) { + return mSelf.isLanguageAvailable(lang, country, variant); + } + + /** + * Returns the currently set language / country / variant strings representing the + * language used by the TTS engine. + * @return null is no language is set, or an array of 3 string containing respectively + * the language, country and variant. + */ + public String[] getLanguage() { + return mSelf.getLanguage(); + } + + /** * Sets the speech rate for the TTS, which affects the synthesized voice. * * @param lang the three letter ISO language code. diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java index 8297aa2..c67f0b5 100644 --- a/services/java/com/android/server/BackupManagerService.java +++ b/services/java/com/android/server/BackupManagerService.java @@ -42,13 +42,13 @@ import android.os.Message; import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.RemoteException; +import android.os.SystemProperties; import android.util.Log; import android.util.SparseArray; import android.backup.IBackupManager; import android.backup.IRestoreObserver; import android.backup.IRestoreSession; -import android.backup.BackupManager; import android.backup.RestoreSet; import com.android.internal.backup.LocalTransport; @@ -68,28 +68,33 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; 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; private static final int MSG_RUN_BACKUP = 1; private static final int MSG_RUN_FULL_BACKUP = 2; private static final int MSG_RUN_RESTORE = 3; - private static final String RESTORE_OBSERVER_KEY = "_resOb"; // Timeout interval for deciding that a bind or clear-data has taken too long static final long TIMEOUT_INTERVAL = 10 * 1000; 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 { @@ -127,8 +132,10 @@ class BackupManagerService extends IBackupManager.Stub { private final Object mClearDataLock = new Object(); private volatile boolean mClearingData; - // Current active transport & restore session - private int mTransportId; + // Transport bookkeeping + private final HashMap<String,IBackupTransport> mTransports + = new HashMap<String,IBackupTransport>(); + private String mCurrentTransport; private IBackupTransport mLocalTransport, mGoogleTransport; private RestoreSession mActiveRestoreSession; @@ -157,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(); @@ -175,11 +185,19 @@ class BackupManagerService extends IBackupManager.Stub { // Set up our transport options and initialize the default transport // TODO: Have transports register themselves somehow? // TODO: Don't create transports that we don't need to? - mTransportId = BackupManager.TRANSPORT_GOOGLE; mLocalTransport = new LocalTransport(context); // This is actually pretty cheap - mGoogleTransport = null; + ComponentName localName = new ComponentName(context, LocalTransport.class); + registerTransport(localName.flattenToShortString(), mLocalTransport); - // Attach to the Google backup transport. + mGoogleTransport = null; + // !!! TODO: set up the default transport name "the right way" + mCurrentTransport = SystemProperties.get(BACKUP_TRANSPORT_PROPERTY, + "com.google.android.backup/.BackupTransportService"); + if (DEBUG) Log.v(TAG, "Starting with transport " + mCurrentTransport); + + // Attach to the Google backup transport. When this comes up, it will set + // itself as the current transport because we explicitly reset mCurrentTransport + // to null. Intent intent = new Intent().setComponent(new ComponentName( "com.google.android.backup", "com.google.android.backup.BackupTransportService")); @@ -238,6 +256,13 @@ class BackupManagerService extends IBackupManager.Stub { } } + // Add a transport to our set of available backends + private void registerTransport(String name, IBackupTransport transport) { + synchronized (mTransports) { + mTransports.put(name, transport); + } + } + // ----- Track installation/removal of packages ----- BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { @@ -283,11 +308,13 @@ class BackupManagerService extends IBackupManager.Stub { public void onServiceConnected(ComponentName name, IBinder service) { if (DEBUG) Log.v(TAG, "Connected to Google transport"); mGoogleTransport = IBackupTransport.Stub.asInterface(service); + registerTransport(name.flattenToShortString(), mGoogleTransport); } public void onServiceDisconnected(ComponentName name) { if (DEBUG) Log.v(TAG, "Disconnected from Google transport"); mGoogleTransport = null; + registerTransport(name.flattenToShortString(), null); } }; @@ -299,7 +326,7 @@ class BackupManagerService extends IBackupManager.Stub { switch (msg.what) { case MSG_RUN_BACKUP: { - IBackupTransport transport = getTransport(mTransportId); + IBackupTransport transport = getTransport(mCurrentTransport); if (transport == null) { Log.v(TAG, "Backup requested but no transport available"); break; @@ -469,24 +496,25 @@ 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 - private IBackupTransport getTransport(int transportID) { - switch (transportID) { - case BackupManager.TRANSPORT_LOCAL: - Log.v(TAG, "Supplying local transport"); - return mLocalTransport; - - case BackupManager.TRANSPORT_GOOGLE: - Log.v(TAG, "Supplying Google transport"); - return mGoogleTransport; - - default: - Log.e(TAG, "Asked for unknown transport " + transportID); - return null; + private IBackupTransport getTransport(String transportName) { + synchronized (mTransports) { + IBackupTransport transport = mTransports.get(transportName); + if (transport == null) { + Log.w(TAG, "Requested unavailable transport: " + transportName); + } + return transport; } } @@ -529,6 +557,19 @@ class BackupManagerService extends IBackupManager.Stub { // clear an application's data, blocking until the operation completes or times out void clearApplicationDataSynchronous(String packageName) { + // Don't wipe packages marked allowClearUserData=false + try { + PackageInfo info = mPackageManager.getPackageInfo(packageName, 0); + if ((info.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA) == 0) { + if (DEBUG) Log.i(TAG, "allowClearUserData=false so not wiping " + + packageName); + return; + } + } catch (NameNotFoundException e) { + Log.w(TAG, "Tried to clear data for " + packageName + " but not found"); + return; + } + ClearDataObserver observer = new ClearDataObserver(); synchronized(mClearDataLock) { @@ -1060,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); } @@ -1090,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) { @@ -1098,21 +1139,78 @@ class BackupManagerService extends IBackupManager.Stub { } } - // Report the currently active transport - public int getCurrentTransport() { - mContext.enforceCallingPermission("android.permission.BACKUP", "selectBackupTransport"); - Log.v(TAG, "getCurrentTransport() returning " + mTransportId); - return mTransportId; + // 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", "getCurrentTransport"); + Log.v(TAG, "getCurrentTransport() returning " + mCurrentTransport); + return mCurrentTransport; } - // Select which transport to use for the next backup operation - public int selectBackupTransport(int transportId) { + // Report all known, available backup transports + public String[] listAllTransports() { + mContext.enforceCallingPermission("android.permission.BACKUP", "listAllTransports"); + + String[] list = null; + ArrayList<String> known = new ArrayList<String>(); + for (Map.Entry<String, IBackupTransport> entry : mTransports.entrySet()) { + if (entry.getValue() != null) { + known.add(entry.getKey()); + } + } + + if (known.size() > 0) { + list = new String[known.size()]; + known.toArray(list); + } + return list; + } + + // Select which transport to use for the next backup operation. If the given + // name is not one of the available transports, no action is taken and the method + // returns null. + public String selectBackupTransport(String transport) { mContext.enforceCallingPermission("android.permission.BACKUP", "selectBackupTransport"); - int prevTransport = mTransportId; - mTransportId = transportId; - Log.v(TAG, "selectBackupTransport() set " + mTransportId + " returning " + prevTransport); - return prevTransport; + synchronized (mTransports) { + String prevTransport = null; + if (mTransports.get(transport) != null) { + prevTransport = mCurrentTransport; + mCurrentTransport = transport; + SystemProperties.set(BACKUP_TRANSPORT_PROPERTY, transport); + Log.v(TAG, "selectBackupTransport() set " + mCurrentTransport + + " returning " + prevTransport); + } else { + Log.w(TAG, "Attempt to select unavailable transport " + transport); + } + return prevTransport; + } } // Callback: a requested backup agent has been instantiated. This should only @@ -1150,7 +1248,7 @@ class BackupManagerService extends IBackupManager.Stub { } // Hand off a restore session - public IRestoreSession beginRestoreSession(int transportID) { + public IRestoreSession beginRestoreSession(String transport) { mContext.enforceCallingPermission("android.permission.BACKUP", "beginRestoreSession"); synchronized(this) { @@ -1158,7 +1256,7 @@ class BackupManagerService extends IBackupManager.Stub { Log.d(TAG, "Restore session requested but one already active"); return null; } - mActiveRestoreSession = new RestoreSession(transportID); + mActiveRestoreSession = new RestoreSession(transport); } return mActiveRestoreSession; } @@ -1171,8 +1269,8 @@ class BackupManagerService extends IBackupManager.Stub { private IBackupTransport mRestoreTransport = null; RestoreSet[] mRestoreSets = null; - RestoreSession(int transportID) { - mRestoreTransport = getTransport(transportID); + RestoreSession(String transport) { + mRestoreTransport = getTransport(transport); } // --- Binder interface --- @@ -1233,6 +1331,11 @@ class BackupManagerService extends IBackupManager.Stub { @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { synchronized (mQueueLock) { + pw.println("Available transports:"); + for (String t : listAllTransports()) { + String pad = (t.equals(mCurrentTransport)) ? " * " : " "; + pw.println(pad + t); + } int N = mBackupParticipants.size(); pw.println("Participants:"); for (int i=0; i<N; i++) { @@ -1241,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/PackageManagerBackupAgent.java b/services/java/com/android/server/PackageManagerBackupAgent.java index 66fb86d..16f14e8 100644 --- a/services/java/com/android/server/PackageManagerBackupAgent.java +++ b/services/java/com/android/server/PackageManagerBackupAgent.java @@ -120,7 +120,10 @@ public class PackageManagerBackupAgent extends BackupAgent { // write its signature block to the output, keyed on the package name. for (PackageInfo pkg : mAllPackages) { String packName = pkg.packageName; - if (!existing.contains(packName)) { + if (packName.equals(GLOBAL_METADATA_KEY)) { + // We've already handled the metadata key; skip it here + continue; + } else if (!existing.contains(packName)) { // We haven't stored this app's signatures yet, so we do that now try { PackageInfo info = mPackageManager.getPackageInfo(packName, diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java index 5ea7504..9bad153 100644 --- a/services/java/com/android/server/WindowManagerService.java +++ b/services/java/com/android/server/WindowManagerService.java @@ -3834,7 +3834,7 @@ 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/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java index d3942fc..dbe8431 100644 --- a/telephony/java/android/telephony/PhoneNumberUtils.java +++ b/telephony/java/android/telephony/PhoneNumberUtils.java @@ -701,17 +701,6 @@ public class PhoneNumberUtils } /** - * Note: calls extractNetworkPortion(), so do not use for - * SIM EF[ADN] style records - * - * Exceptions thrown if extractNetworkPortion(s).length() == 0 - */ - public static byte[] - networkPortionToCalledPartyBCD(String s) { - return numberToCalledPartyBCD(extractNetworkPortion(s)); - } - - /** * Return true iff the network portion of <code>address</code> is, * as far as we can tell on the device, suitable for use as an SMS * destination address. @@ -744,12 +733,25 @@ public class PhoneNumberUtils } /** + * Note: calls extractNetworkPortion(), so do not use for + * SIM EF[ADN] style records + * + * Returns null if network portion is empty. + */ + public static byte[] + networkPortionToCalledPartyBCD(String s) { + String networkPortion = extractNetworkPortion(s); + return numberToCalledPartyBCDHelper(networkPortion, false); + } + + /** * Same as {@link #networkPortionToCalledPartyBCD}, but includes a * one-byte length prefix. */ public static byte[] networkPortionToCalledPartyBCDWithLength(String s) { - return numberToCalledPartyBCDWithLength(extractNetworkPortion(s)); + String networkPortion = extractNetworkPortion(s); + return numberToCalledPartyBCDHelper(networkPortion, true); } /** @@ -761,61 +763,46 @@ public class PhoneNumberUtils */ public static byte[] numberToCalledPartyBCD(String number) { - // The extra byte required for '+' is taken into consideration while calculating - // length of ret. - int size = (hasPlus(number) ? number.length() - 1 : number.length()); - byte[] ret = new byte[(size + 1) / 2 + 1]; - - return numberToCalledPartyBCDHelper(ret, 0, number); + return numberToCalledPartyBCDHelper(number, false); } /** - * Same as {@link #numberToCalledPartyBCD}, but includes a - * one-byte length prefix. + * If includeLength is true, prepend a one-byte length value to + * the return array. */ private static byte[] - numberToCalledPartyBCDWithLength(String number) { - // The extra byte required for '+' is taken into consideration while calculating - // length of ret. - int size = (hasPlus(number) ? number.length() - 1 : number.length()); - int length = (size + 1) / 2 + 1; - byte[] ret = new byte[length + 1]; - - ret[0] = (byte) (length & 0xff); - return numberToCalledPartyBCDHelper(ret, 1, number); - } - - private static boolean - hasPlus(String s) { - return s.indexOf('+') >= 0; - } - - private static byte[] - numberToCalledPartyBCDHelper(byte[] ret, int offset, String number) { - if (hasPlus(number)) { - number = number.replaceAll("\\+", ""); - ret[offset] = (byte) TOA_International; - } else { - ret[offset] = (byte) TOA_Unknown; + numberToCalledPartyBCDHelper(String number, boolean includeLength) { + int numberLenReal = number.length(); + int numberLenEffective = numberLenReal; + boolean hasPlus = number.indexOf('+') != -1; + if (hasPlus) numberLenEffective--; + + if (numberLenEffective == 0) return null; + + int resultLen = (numberLenEffective + 1) / 2; // Encoded numbers require only 4 bits each. + int extraBytes = 1; // Prepended TOA byte. + if (includeLength) extraBytes++; // Optional prepended length byte. + resultLen += extraBytes; + + byte[] result = new byte[resultLen]; + + int digitCount = 0; + for (int i = 0; i < numberLenReal; i++) { + char c = number.charAt(i); + if (c == '+') continue; + int shift = ((digitCount & 0x01) == 1) ? 4 : 0; + result[extraBytes + (digitCount >> 1)] |= (byte)((charToBCD(c) & 0x0F) << shift); + digitCount++; } - int size = number.length(); - int curChar = 0; - int countFullBytes = ret.length - offset - 1 - ((size - curChar) & 1); - for (int i = 1; i < 1 + countFullBytes; i++) { - ret[offset + i] - = (byte) ((charToBCD(number.charAt(curChar++))) - | (charToBCD(number.charAt(curChar++))) << 4); - } + // 1-fill any trailing odd nibble/quartet. + if ((digitCount & 0x01) == 1) result[extraBytes + (digitCount >> 1)] |= 0xF0; - // The left-over octet for odd-length phone numbers should be - // filled with 0xf. - if (countFullBytes + offset < ret.length - 1) { - ret[ret.length - 1] - = (byte) (charToBCD(number.charAt(curChar)) - | (0xf << 4)); - } - return ret; + int offset = 0; + if (includeLength) result[offset++] = (byte)(resultLen - 1); + result[offset] = (byte)(hasPlus ? TOA_International : TOA_Unknown); + + return result; } /** all of 'a' up to len must match non-US trunk prefix ('0') */ diff --git a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java index af79404..c074cb8 100644 --- a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java +++ b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java @@ -98,8 +98,8 @@ public abstract class DataConnectionTracker extends Handler { //***** Constants protected static final int RECONNECT_DELAY_INITIAL_MILLIS = 5 * 1000; - /** Cap out with 1 hour retry interval. */ - protected static final int RECONNECT_DELAY_MAX_MILLIS = 60 * 60 * 1000; + /** Cap out with 30 min retry interval. */ + protected static final int RECONNECT_DELAY_MAX_MILLIS = 30 * 60 * 1000; /** Slow poll when attempting connection recovery. */ protected static final int POLL_NETSTAT_SLOW_MILLIS = 5000; diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java index 2a65de3..9d29272 100644 --- a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java @@ -26,22 +26,18 @@ import android.content.SharedPreferences; import android.net.NetworkInfo; import android.net.wifi.WifiManager; import android.os.AsyncResult; -import android.os.Handler; import android.os.INetStatService; import android.os.Message; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; -import android.os.SystemProperties; import android.preference.PreferenceManager; import android.provider.Checkin; -import android.provider.Settings; -import android.provider.Settings.SettingNotFoundException; import android.telephony.ServiceState; import android.telephony.TelephonyManager; import android.telephony.cdma.CdmaCellLocation; -import android.util.EventLog; import android.text.TextUtils; +import android.util.EventLog; import android.util.Log; import com.android.internal.telephony.CommandsInterface; @@ -50,7 +46,6 @@ import com.android.internal.telephony.DataConnection; import com.android.internal.telephony.DataConnection.FailCause; import com.android.internal.telephony.DataConnectionTracker; import com.android.internal.telephony.Phone; -import com.android.internal.telephony.PhoneProxy; import com.android.internal.telephony.TelephonyEventLog; import java.util.ArrayList; @@ -67,6 +62,7 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker { // Indicates baseband will not auto-attach private boolean noAutoAttach = false; long nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS; + private boolean mReregisterOnReconnectFailure = false; private boolean mIsScreenOn = true; //useful for debugging @@ -464,6 +460,7 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker { startNetStatPoll(); // reset reconnect timer nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS; + mReregisterOnReconnectFailure = false; } private void resetPollStats() { @@ -619,6 +616,21 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker { private void reconnectAfterFail(FailCause lastFailCauseCode, String reason) { if (state == State.FAILED) { + if (nextReconnectDelay > RECONNECT_DELAY_MAX_MILLIS) { + if (mReregisterOnReconnectFailure) { + // We have already tried to re-register to the network. + // This might be a problem with the data network. + nextReconnectDelay = RECONNECT_DELAY_MAX_MILLIS; + } else { + // Try to Re-register to the network. + Log.d(LOG_TAG, "PDP activate failed, Reregistering to the network"); + mReregisterOnReconnectFailure = true; + mCdmaPhone.mSST.reRegisterNetwork(null); + nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS; + return; + } + } + Log.d(LOG_TAG, "Data Connection activate failed. Scheduling next attempt for " + (nextReconnectDelay / 1000) + "s"); @@ -634,9 +646,6 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker { // double it for next time nextReconnectDelay *= 2; - if (nextReconnectDelay > RECONNECT_DELAY_MAX_MILLIS) { - nextReconnectDelay = RECONNECT_DELAY_MAX_MILLIS; - } if (!shouldPostNotification(lastFailCauseCode)) { Log.d(LOG_TAG,"NOT Posting Data Connection Unavailable notification " @@ -716,6 +725,7 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker { // Make sure our reconnect delay starts at the initial value // next time the radio comes on nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS; + mReregisterOnReconnectFailure = false; if (phone.getSimulatedRadioControl() != null) { // Assume data is connected on the simulator @@ -793,6 +803,7 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker { } else { // reset reconnect timer nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS; + mReregisterOnReconnectFailure = false; // in case data setup was attempted when we were on a voice call trySetupData(Phone.REASON_VOICE_CALL_ENDED); } diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java index 03bdbda..eaeda64 100644 --- a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java +++ b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java @@ -83,7 +83,7 @@ public final class BearerData { public static final int MESSAGE_TYPE_DELIVER_REPORT = 0x07; public static final int MESSAGE_TYPE_SUBMIT_REPORT = 0x08; - public byte messageType; + public int messageType; /** * 16-bit value indicating the message ID, which increments modulo 65536. @@ -102,7 +102,7 @@ public final class BearerData { public static final int PRIORITY_EMERGENCY = 0x3; public boolean priorityIndicatorSet = false; - public byte priority = PRIORITY_NORMAL; + public int priority = PRIORITY_NORMAL; /** * Supported privacy modes for CDMA SMS messages @@ -114,7 +114,7 @@ public final class BearerData { public static final int PRIVACY_SECRET = 0x3; public boolean privacyIndicatorSet = false; - public byte privacy = PRIVACY_NOT_RESTRICTED; + public int privacy = PRIVACY_NOT_RESTRICTED; /** * Supported alert priority modes for CDMA SMS messages @@ -139,7 +139,7 @@ public final class BearerData { public static final int DISPLAY_MODE_USER = 0x2; public boolean displayModeSet = false; - public byte displayMode = DISPLAY_MODE_DEFAULT; + public int displayMode = DISPLAY_MODE_DEFAULT; /** * Language Indicator values. NOTE: the spec (3GPP2 C.S0015-B, @@ -789,7 +789,7 @@ public final class BearerData { if (inStream.read(8) != 3) { throw new CodingException("MESSAGE_IDENTIFIER subparam size incorrect"); } - bData.messageType = (byte)inStream.read(4); + bData.messageType = inStream.read(4); bData.messageId = inStream.read(8) << 8; bData.messageId |= inStream.read(8); bData.hasUserDataHeader = (inStream.read(1) == 1); @@ -919,6 +919,122 @@ public final class BearerData { } } + /** + * IS-91 Voice Mail message decoding + * (See 3GPP2 C.S0015-A, Table 4.3.1.4.1-1) + * (For character encodings, see TIA/EIA/IS-91, Annex B) + * + * Protocol Summary: The user data payload may contain 3-14 + * characters. The first two characters are parsed as a number + * and indicate the number of voicemails. The third character is + * either a SPACE or '!' to indicate normal or urgent priority, + * respectively. Any following characters are treated as normal + * text user data payload. + * + * Note that the characters encoding is 6-bit packed. + */ + private static void decodeIs91VoicemailStatus(BearerData bData) + throws BitwiseInputStream.AccessException, CodingException + { + BitwiseInputStream inStream = new BitwiseInputStream(bData.userData.payload); + int dataLen = inStream.available() / 6; // 6-bit packed character encoding. + int numFields = bData.userData.numFields; + if ((dataLen > 14) || (dataLen < 3) || (dataLen < numFields)) { + throw new CodingException("IS-91 voicemail status decoding failed"); + } + try { + StringBuffer strbuf = new StringBuffer(dataLen); + while (inStream.available() >= 6) { + strbuf.append(UserData.IA5_MAP[inStream.read(6)]); + } + String data = strbuf.toString(); + bData.numberOfMessages = Integer.parseInt(data.substring(0, 2)); + char prioCode = data.charAt(2); + if (prioCode == ' ') { + bData.priority = PRIORITY_NORMAL; + } else if (prioCode == '!') { + bData.priority = PRIORITY_URGENT; + } else { + throw new CodingException("IS-91 voicemail status decoding failed: " + + "illegal priority setting (" + prioCode + ")"); + } + bData.priorityIndicatorSet = true; + bData.userData.payloadStr = data.substring(3, numFields - 3); + } catch (java.lang.NumberFormatException ex) { + throw new CodingException("IS-91 voicemail status decoding failed: " + ex); + } catch (java.lang.IndexOutOfBoundsException ex) { + throw new CodingException("IS-91 voicemail status decoding failed: " + ex); + } + } + + /** + * IS-91 Short Message decoding + * (See 3GPP2 C.S0015-A, Table 4.3.1.4.1-1) + * (For character encodings, see TIA/EIA/IS-91, Annex B) + * + * Protocol Summary: The user data payload may contain 1-14 + * characters, which are treated as normal text user data payload. + * Note that the characters encoding is 6-bit packed. + */ + private static void decodeIs91ShortMessage(BearerData bData) + throws BitwiseInputStream.AccessException, CodingException + { + BitwiseInputStream inStream = new BitwiseInputStream(bData.userData.payload); + int dataLen = inStream.available() / 6; // 6-bit packed character encoding. + int numFields = bData.userData.numFields; + if ((dataLen > 14) || (dataLen < numFields)) { + throw new CodingException("IS-91 voicemail status decoding failed"); + } + StringBuffer strbuf = new StringBuffer(dataLen); + for (int i = 0; i < numFields; i++) { + strbuf.append(UserData.IA5_MAP[inStream.read(6)]); + } + bData.userData.payloadStr = strbuf.toString(); + } + + /** + * IS-91 CLI message (callback number) decoding + * (See 3GPP2 C.S0015-A, Table 4.3.1.4.1-1) + * + * Protocol Summary: The data payload may contain 1-32 digits, + * encoded using standard 4-bit DTMF, which are treated as a + * callback number. + */ + private static void decodeIs91Cli(BearerData bData) throws CodingException { + BitwiseInputStream inStream = new BitwiseInputStream(bData.userData.payload); + int dataLen = inStream.available() / 4; // 4-bit packed DTMF digit encoding. + int numFields = bData.userData.numFields; + if ((dataLen > 14) || (dataLen < 3) || (dataLen < numFields)) { + throw new CodingException("IS-91 voicemail status decoding failed"); + } + CdmaSmsAddress addr = new CdmaSmsAddress(); + addr.digitMode = CdmaSmsAddress.DIGIT_MODE_4BIT_DTMF; + addr.origBytes = bData.userData.payload; + addr.numberOfDigits = (byte)numFields; + decodeSmsAddress(addr); + bData.callbackNumber = addr; + } + + private static void decodeIs91(BearerData bData) + throws BitwiseInputStream.AccessException, CodingException + { + switch (bData.userData.msgType) { + case UserData.IS91_MSG_TYPE_VOICEMAIL_STATUS: + decodeIs91VoicemailStatus(bData); + break; + case UserData.IS91_MSG_TYPE_CLI: + decodeIs91Cli(bData); + break; + case UserData.IS91_MSG_TYPE_SHORT_MESSAGE_FULL: + case UserData.IS91_MSG_TYPE_SHORT_MESSAGE: + decodeIs91ShortMessage(bData); + break; + default: + throw new CodingException("unsupported IS-91 message type (" + + bData.userData.msgType + ")"); + } + } + private static void decodeReplyOption(BearerData bData, BitwiseInputStream inStream) throws BitwiseInputStream.AccessException, CodingException { @@ -987,16 +1103,16 @@ public final class BearerData { { int paramBytes = inStream.read(8); CdmaSmsAddress addr = new CdmaSmsAddress(); - addr.digitMode = (byte)inStream.read(1); + addr.digitMode = inStream.read(1); byte fieldBits = 4; byte consumedBits = 1; if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR) { addr.ton = inStream.read(3); - addr.numberPlan = (byte)inStream.read(4); + addr.numberPlan = inStream.read(4); fieldBits = 8; consumedBits += 7; } - addr.numberOfDigits = (byte)inStream.read(8); + addr.numberOfDigits = inStream.read(8); consumedBits += 8; int remainingBits = (paramBytes * 8) - consumedBits; int dataBits = addr.numberOfDigits * fieldBits; @@ -1076,7 +1192,7 @@ public final class BearerData { if (inStream.read(8) != 1) { throw new CodingException("PRIVACY_INDICATOR subparam size incorrect"); } - bData.privacy = (byte)inStream.read(2); + bData.privacy = inStream.read(2); inStream.skip(6); bData.privacyIndicatorSet = true; } @@ -1097,7 +1213,7 @@ public final class BearerData { if (inStream.read(8) != 1) { throw new CodingException("DISPLAY_MODE subparam size incorrect"); } - bData.displayMode = (byte)inStream.read(2); + bData.displayMode = inStream.read(2); inStream.skip(6); bData.displayModeSet = true; } @@ -1108,7 +1224,7 @@ public final class BearerData { if (inStream.read(8) != 1) { throw new CodingException("PRIORITY_INDICATOR subparam size incorrect"); } - bData.priority = (byte)inStream.read(2); + bData.priority = inStream.read(2); inStream.skip(6); bData.priorityIndicatorSet = true; } @@ -1219,7 +1335,18 @@ public final class BearerData { throw new CodingException("missing MESSAGE_IDENTIFIER subparam"); } if (bData.userData != null) { - decodeUserDataPayload(bData.userData, bData.hasUserDataHeader); + if (bData.userData.msgEncoding == UserData.ENCODING_IS91_EXTENDED_PROTOCOL) { + if ((foundSubparamMask ^ + (1 << SUBPARAM_MESSAGE_IDENTIFIER) ^ + (1 << SUBPARAM_USER_DATA)) + != 0) { + Log.e(LOG_TAG, "IS-91 must occur without extra subparams (" + + foundSubparamMask + ")"); + } + decodeIs91(bData); + } else { + decodeUserDataPayload(bData.userData, bData.hasUserDataHeader); + } } return bData; } catch (BitwiseInputStream.AccessException ex) { diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java b/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java index 917ec9d..4d79966 100644 --- a/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java +++ b/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java @@ -29,7 +29,7 @@ public class CdmaSmsAddress extends SmsAddress { static public final int DIGIT_MODE_4BIT_DTMF = 0x00; static public final int DIGIT_MODE_8BIT_CHAR = 0x01; - public byte digitMode; + public int digitMode; /** * Number Mode Indicator is 1-bit value that indicates whether the @@ -39,7 +39,7 @@ public class CdmaSmsAddress extends SmsAddress { static public final int NUMBER_MODE_NOT_DATA_NETWORK = 0x00; static public final int NUMBER_MODE_DATA_NETWORK = 0x01; - public byte numberMode; + public int numberMode; /** * Number Types for data networks. @@ -65,7 +65,7 @@ public class CdmaSmsAddress extends SmsAddress { * This field shall be set to the number of address digits * (See 3GPP2 C.S0015-B, v2, 3.4.3.3) */ - public byte numberOfDigits; + public int numberOfDigits; /** * Numbering Plan identification is a 0 or 4-bit value that @@ -78,7 +78,7 @@ public class CdmaSmsAddress extends SmsAddress { //static protected final int NUMBERING_PLAN_TELEX = 0x4; //static protected final int NUMBERING_PLAN_PRIVATE = 0x9; - public byte numberPlan; + public int numberPlan; /** * NOTE: the parsed string address and the raw byte array values diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java b/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java index d8a48cc..34cbbfa 100644 --- a/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java +++ b/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java @@ -40,12 +40,26 @@ public class UserData { public static final int ENCODING_GSM_DCS = 0x0A; /** + * IS-91 message types. + * (See TIA/EIS/IS-91-A-ENGL 1999, table 3.7.1.1-3) + */ + public static final int IS91_MSG_TYPE_VOICEMAIL_STATUS = 0x82; + public static final int IS91_MSG_TYPE_SHORT_MESSAGE_FULL = 0x83; + public static final int IS91_MSG_TYPE_CLI = 0x84; + public static final int IS91_MSG_TYPE_SHORT_MESSAGE = 0x85; + + /** * IA5 data encoding character mappings. * (See CCITT Rec. T.50 Tables 1 and 3) * * Note this mapping is the the same as for printable ASCII * characters, with a 0x20 offset, meaning that the ASCII SPACE * character occurs with code 0x20. + * + * Note this mapping is also equivalent to that used by the IS-91 + * protocol, except for the latter using only 6 bits, and hence + * mapping only entries up to the '_' character. + * */ public static final char[] IA5_MAP = { ' ', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', @@ -96,7 +110,6 @@ public class UserData { public int msgEncoding; public boolean msgEncodingSet = false; - // XXX needed when encoding is IS91 or DCS (not supported yet): public int msgType; /** diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java index 035c690..e00ee83 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java +++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java @@ -65,6 +65,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { private static final String LOG_TAG = "GSM"; private static final boolean DBG = true; + private GSMPhone mGsmPhone; /** * Handles changes to the APN db. */ @@ -85,6 +86,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { // Indicates baseband will not auto-attach private boolean noAutoAttach = false; long nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS; + private boolean mReregisterOnReconnectFailure = false; private ContentResolver mResolver; private boolean mPingTestActive = false; @@ -204,6 +206,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { GsmDataConnectionTracker(GSMPhone p) { super(p); + mGsmPhone = p; p.mCM.registerForAvailable (this, EVENT_RADIO_AVAILABLE, null); p.mCM.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null); @@ -250,16 +253,16 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { //Unregister for all events phone.mCM.unregisterForAvailable(this); phone.mCM.unregisterForOffOrNotAvailable(this); - ((GSMPhone) phone).mSIMRecords.unregisterForRecordsLoaded(this); + mGsmPhone.mSIMRecords.unregisterForRecordsLoaded(this); phone.mCM.unregisterForDataStateChanged(this); - ((GSMPhone) phone).mCT.unregisterForVoiceCallEnded(this); - ((GSMPhone) phone).mCT.unregisterForVoiceCallStarted(this); - ((GSMPhone) phone).mSST.unregisterForGprsAttached(this); - ((GSMPhone) phone).mSST.unregisterForGprsDetached(this); - ((GSMPhone) phone).mSST.unregisterForRoamingOn(this); - ((GSMPhone) phone).mSST.unregisterForRoamingOff(this); - ((GSMPhone) phone).mSST.unregisterForPsRestrictedEnabled(this); - ((GSMPhone) phone).mSST.unregisterForPsRestrictedDisabled(this); + mGsmPhone.mCT.unregisterForVoiceCallEnded(this); + mGsmPhone.mCT.unregisterForVoiceCallStarted(this); + mGsmPhone.mSST.unregisterForGprsAttached(this); + mGsmPhone.mSST.unregisterForGprsDetached(this); + mGsmPhone.mSST.unregisterForRoamingOn(this); + mGsmPhone.mSST.unregisterForRoamingOff(this); + mGsmPhone.mSST.unregisterForPsRestrictedEnabled(this); + mGsmPhone.mSST.unregisterForPsRestrictedDisabled(this); phone.getContext().unregisterReceiver(this.mIntentReceiver); phone.getContext().getContentResolver().unregisterContentObserver(this.apnObserver); @@ -374,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 { @@ -407,8 +414,8 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { public boolean isDataConnectionAsDesired() { boolean roaming = phone.getServiceState().getRoaming(); - if (((GSMPhone) phone).mSIMRecords.getRecordsLoaded() && - ((GSMPhone) phone).mSST.getCurrentGprsState() == ServiceState.STATE_IN_SERVICE && + if (mGsmPhone.mSIMRecords.getRecordsLoaded() && + mGsmPhone.mSST.getCurrentGprsState() == ServiceState.STATE_IN_SERVICE && (!roaming || getDataOnRoamingEnabled()) && !mIsWifiConnected && !mIsPsRestricted ) { @@ -572,13 +579,13 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { return true; } - int gprsState = ((GSMPhone) phone).mSST.getCurrentGprsState(); + int gprsState = mGsmPhone.mSST.getCurrentGprsState(); boolean roaming = phone.getServiceState().getRoaming(); - boolean desiredPowerState = ((GSMPhone) phone).mSST.getDesiredPowerState(); + boolean desiredPowerState = mGsmPhone.mSST.getDesiredPowerState(); if ((state == State.IDLE || state == State.SCANNING) && (gprsState == ServiceState.STATE_IN_SERVICE || noAutoAttach) - && ((GSMPhone) phone).mSIMRecords.getRecordsLoaded() + && mGsmPhone.mSIMRecords.getRecordsLoaded() && phone.getState() == Phone.State.IDLE && isDataAllowed() && !mIsPsRestricted @@ -604,8 +611,8 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { log("trySetupData: Not ready for data: " + " dataState=" + state + " gprsState=" + gprsState + - " sim=" + ((GSMPhone) phone).mSIMRecords.getRecordsLoaded() + - " UMTS=" + ((GSMPhone) phone).mSST.isConcurrentVoiceAndData() + + " sim=" + mGsmPhone.mSIMRecords.getRecordsLoaded() + + " UMTS=" + mGsmPhone.mSST.isConcurrentVoiceAndData() + " phoneState=" + phone.getState() + " dataEnabled=" + getAnyDataEnabled() + " roaming=" + roaming + @@ -792,7 +799,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { isConnected = (state != State.IDLE && state != State.FAILED); // The "current" may no longer be valid. MMS depends on this to send properly. - ((GSMPhone) phone).updateCurrentCarrierInProvider(); + mGsmPhone.updateCurrentCarrierInProvider(); // TODO: It'd be nice to only do this if the changed entrie(s) // match the current operator. @@ -802,6 +809,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { if (!isConnected) { // reset reconnect timer nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS; + mReregisterOnReconnectFailure = false; trySetupData(Phone.REASON_APN_CHANGED); } } @@ -882,6 +890,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { startNetStatPoll(); // reset reconnect timer nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS; + mReregisterOnReconnectFailure = false; } private void setupDnsProperties() { @@ -952,7 +961,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { } else { mPdpResetCount = 0; EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_REREGISTER_NETWORK, sentSinceLastRecv); - ((GSMPhone) phone).mSST.reRegisterNetwork(null); + mGsmPhone.mSST.reRegisterNetwork(null); } // TODO: Add increasingly drastic recovery steps, eg, // reset the radio, reset the device. @@ -1166,6 +1175,20 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { private void reconnectAfterFail(FailCause lastFailCauseCode, String reason) { if (state == State.FAILED) { + if (nextReconnectDelay > RECONNECT_DELAY_MAX_MILLIS) { + if (mReregisterOnReconnectFailure) { + // We have already tried to re-register to the network. + // This might be a problem with the data network. + nextReconnectDelay = RECONNECT_DELAY_MAX_MILLIS; + } else { + // Try to Re-register to the network. + Log.d(LOG_TAG, "PDP activate failed, Reregistering to the network"); + mReregisterOnReconnectFailure = true; + mGsmPhone.mSST.reRegisterNetwork(null); + nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS; + return; + } + } Log.d(LOG_TAG, "PDP activate failed. Scheduling next attempt for " + (nextReconnectDelay / 1000) + "s"); @@ -1181,9 +1204,6 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { // double it for next time nextReconnectDelay *= 2; - if (nextReconnectDelay > RECONNECT_DELAY_MAX_MILLIS) { - nextReconnectDelay = RECONNECT_DELAY_MAX_MILLIS; - } if (!shouldPostNotification(lastFailCauseCode)) { Log.d(LOG_TAG,"NOT Posting GPRS Unavailable notification " @@ -1219,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; } } @@ -1258,6 +1277,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { // Make sure our reconnect delay starts at the initial value // next time the radio comes on nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS; + mReregisterOnReconnectFailure = false; if (phone.getSimulatedRadioControl() != null) { // Assume data is connected on the simulator @@ -1370,7 +1390,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { } protected void onVoiceCallStarted() { - if (state == State.CONNECTED && !((GSMPhone) phone).mSST.isConcurrentVoiceAndData()) { + if (state == State.CONNECTED && ! mGsmPhone.mSST.isConcurrentVoiceAndData()) { stopNetStatPoll(); phone.notifyDataConnection(Phone.REASON_VOICE_CALL_STARTED); } @@ -1378,7 +1398,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { protected void onVoiceCallEnded() { if (state == State.CONNECTED) { - if (!((GSMPhone) phone).mSST.isConcurrentVoiceAndData()) { + if (mGsmPhone.mSST.isConcurrentVoiceAndData()) { startNetStatPoll(); phone.notifyDataConnection(Phone.REASON_VOICE_CALL_ENDED); } else { @@ -1388,6 +1408,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { } else { // reset reconnect timer nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS; + mReregisterOnReconnectFailure = false; // in case data setup was attempted when we were on a voice call trySetupData(Phone.REASON_VOICE_CALL_ENDED); } @@ -1417,7 +1438,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { */ private void createAllApnList() { allApns = new ArrayList<ApnSetting>(); - String operator = ((GSMPhone) phone).mSIMRecords.getSIMOperatorNumeric(); + String operator = mGsmPhone.mSIMRecords.getSIMOperatorNumeric(); if (operator != null) { String selection = "numeric = '" + operator + "'"; @@ -1459,7 +1480,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { DataConnection pdp; for (int i = 0; i < PDP_CONNECTION_POOL_SIZE; i++) { - pdp = new PdpConnection((GSMPhone) phone); + pdp = new PdpConnection(mGsmPhone); pdpList.add(pdp); } } @@ -1478,7 +1499,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { */ private ArrayList<ApnSetting> buildWaitingApns() { ArrayList<ApnSetting> apnList = new ArrayList<ApnSetting>(); - String operator = ((GSMPhone )phone).mSIMRecords.getSIMOperatorNumeric(); + String operator = mGsmPhone.mSIMRecords.getSIMOperatorNumeric(); if (mRequestedApnType.equals(Phone.APN_TYPE_DEFAULT)) { if (canSetPreferApn && preferredApn != null) { @@ -1664,6 +1685,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { if (state == State.FAILED) { cleanUpConnection(false, Phone.REASON_PS_RESTRICT_ENABLED); nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS; + mReregisterOnReconnectFailure = false; } trySetupData(Phone.REASON_PS_RESTRICT_ENABLED); } diff --git a/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java b/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java index 9188e04..2ff0a6a 100644 --- a/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java +++ b/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java @@ -30,6 +30,8 @@ import android.test.suitebuilder.annotation.SmallTest; import java.util.Iterator; +import java.lang.Integer; + import android.util.Log; public class CdmaSmsTest extends AndroidTestCase { @@ -679,4 +681,15 @@ public class CdmaSmsTest extends AndroidTestCase { assertEquals(revBearerData.displayModeSet, true); assertEquals(revBearerData.displayMode, bearerData.displayMode); } + + @SmallTest + public void testIs91() throws Exception { + String pdu1 = "000320001001070c2039acc13880"; + BearerData bd1 = BearerData.decode(HexDump.hexStringToByteArray(pdu1)); + assertEquals(bd1.callbackNumber.address, "3598271"); + String pdu4 = "000320001001080c283c314724b34e"; + BearerData bd4 = BearerData.decode(HexDump.hexStringToByteArray(pdu4)); + assertEquals(bd4.userData.payloadStr, "ABCDEFG"); + } + } diff --git a/tests/CoreTests/com/android/internal/telephony/PhoneNumberUtilsTest.java b/tests/CoreTests/com/android/internal/telephony/PhoneNumberUtilsTest.java index e8bd239..577d384 100644 --- a/tests/CoreTests/com/android/internal/telephony/PhoneNumberUtilsTest.java +++ b/tests/CoreTests/com/android/internal/telephony/PhoneNumberUtilsTest.java @@ -93,6 +93,13 @@ public class PhoneNumberUtilsTest extends TestCase { assertEquals(b[i], bRet[i]); } + bRet = PhoneNumberUtils.networkPortionToCalledPartyBCDWithLength("+17005550020"); + assertEquals(8, bRet.length); + assertEquals(bRet[0], 7); + for (int i = 1; i < 8; i++) { + assertEquals(b[i - 1], bRet[i]); + } + bRet = PhoneNumberUtils.networkPortionToCalledPartyBCD("7005550020"); assertEquals("7005550020", PhoneNumberUtils.calledPartyBCDToString(bRet, 0, bRet.length)); diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/FileList.java b/tests/DumpRenderTree/src/com/android/dumprendertree/FileList.java index 0218317..e741177 100644 --- a/tests/DumpRenderTree/src/com/android/dumprendertree/FileList.java +++ b/tests/DumpRenderTree/src/com/android/dumprendertree/FileList.java @@ -23,7 +23,9 @@ import java.util.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"; } |