Merge change 5420 into donut
* changes:
Added two test cases to trace the failure in closing the hw decoder and the current playtime is greater than the duration.
diff --git a/Android.mk b/Android.mk
index 0e8793d..f32129e 100644
--- a/Android.mk
+++ b/Android.mk
@@ -82,6 +82,7 @@
core/java/android/app/IWallpaperService.aidl \
core/java/android/app/IWallpaperServiceCallback.aidl \
core/java/android/backup/IBackupManager.aidl \
+ core/java/android/backup/IRestoreObserver.aidl \
core/java/android/backup/IRestoreSession.aidl \
core/java/android/bluetooth/IBluetoothA2dp.aidl \
core/java/android/bluetooth/IBluetoothDevice.aidl \
diff --git a/api/current.xml b/api/current.xml
index 658a87c..673d053 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -3452,39 +3452,6 @@
visibility="public"
>
</field>
-<field name="donut_resource_pad26"
- type="int"
- transient="false"
- volatile="false"
- value="16843398"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="donut_resource_pad27"
- type="int"
- transient="false"
- volatile="false"
- value="16843397"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="donut_resource_pad28"
- type="int"
- transient="false"
- volatile="false"
- value="16843396"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
<field name="donut_resource_pad3"
type="int"
transient="false"
@@ -5311,6 +5278,17 @@
visibility="public"
>
</field>
+<field name="largeScreens"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843398"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="launchMode"
type="int"
transient="false"
@@ -6191,6 +6169,17 @@
visibility="public"
>
</field>
+<field name="normalScreens"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843397"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="numColumns"
type="int"
transient="false"
@@ -7577,6 +7566,17 @@
visibility="public"
>
</field>
+<field name="smallScreens"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843396"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="smoothScrollbar"
type="int"
transient="false"
@@ -22088,6 +22088,19 @@
<parameter name="position" type="int">
</parameter>
</method>
+<method name="itemForPosition"
+ return="android.app.LauncherActivity.ListItem"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+<parameter name="position" type="int">
+</parameter>
+</method>
<method name="makeListItems"
return="java.util.List<android.app.LauncherActivity.ListItem>"
abstract="false"
@@ -22196,6 +22209,16 @@
visibility="public"
>
</field>
+<field name="resolveInfo"
+ type="android.content.pm.ResolveInfo"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
</class>
<class name="ListActivity"
extends="android.app.Activity"
@@ -34798,6 +34821,17 @@
visibility="public"
>
</field>
+<field name="CONFIG_SCREEN_LAYOUT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="256"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="CONFIG_TOUCHSCREEN"
type="int"
transient="false"
@@ -35266,6 +35300,28 @@
type="int"
transient="false"
volatile="false"
+ value="2048"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="FLAG_SUPPORTS_NORMAL_SCREENS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1024"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="FLAG_SUPPORTS_SMALL_SCREENS"
+ type="int"
+ transient="false"
+ volatile="false"
value="512"
static="true"
final="true"
@@ -39465,6 +39521,50 @@
visibility="public"
>
</field>
+<field name="SCREENLAYOUT_LARGE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SCREENLAYOUT_NORMAL"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SCREENLAYOUT_SMALL"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SCREENLAYOUT_UNDEFINED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="TOUCHSCREEN_FINGER"
type="int"
transient="false"
@@ -39599,6 +39699,16 @@
visibility="public"
>
</field>
+<field name="screenLayout"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="touchscreen"
type="int"
transient="false"
@@ -139378,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"
@@ -139954,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"
@@ -180577,7 +180713,7 @@
<method name="startMethodTracing"
return="void"
abstract="false"
- native="true"
+ native="false"
synchronized="false"
static="true"
final="false"
diff --git a/camera/libcameraservice/CameraService.cpp b/camera/libcameraservice/CameraService.cpp
index e945056..022fe5a 100644
--- a/camera/libcameraservice/CameraService.cpp
+++ b/camera/libcameraservice/CameraService.cpp
@@ -98,7 +98,7 @@
LOGD("CameraService::connect E (pid %d, client %p)", callingPid,
cameraClient->asBinder().get());
- Mutex::Autolock lock(mLock);
+ Mutex::Autolock lock(mServiceLock);
sp<Client> client;
if (mClient != 0) {
sp<Client> currentClient = mClient.promote();
@@ -125,13 +125,14 @@
LOGD("New client (pid %d) connecting, old reference was dangling...",
callingPid);
mClient.clear();
- if (mUsers > 0) {
- LOGD("Still have client, rejected");
- return client;
- }
}
}
+ if (mUsers > 0) {
+ LOGD("Still have client, rejected");
+ return client;
+ }
+
// create a new Client object
client = new Client(this, cameraClient, callingPid);
mClient = client;
@@ -152,7 +153,7 @@
// destructor won't be called with the lock held.
sp<Client> client;
- Mutex::Autolock lock(mLock);
+ Mutex::Autolock lock(mServiceLock);
if (mClient == 0) {
// This happens when we have already disconnected.
@@ -390,8 +391,6 @@
// from the user directly, or called by the destructor.
if (mHardware == 0) return;
- mCameraService->removeClient(mCameraClient);
-
LOGD("hardware teardown");
// Before destroying mHardware, we must make sure it's in the
// idle state.
@@ -402,6 +401,7 @@
mHardware->release();
mHardware.clear();
+ mCameraService->removeClient(mCameraClient);
mCameraService->decUsers();
LOGD("Client::disconnect() X (pid %d)", callingPid);
@@ -410,11 +410,14 @@
// pass the buffered ISurface to the camera service
status_t CameraService::Client::setPreviewDisplay(const sp<ISurface>& surface)
{
- LOGD("setPreviewDisplay(%p) (pid %d)", surface.get(), getCallingPid());
+ LOGD("setPreviewDisplay(%p) (pid %d)",
+ ((surface == NULL) ? NULL : surface.get()), getCallingPid());
Mutex::Autolock lock(mLock);
status_t result = checkPid();
if (result != NO_ERROR) return result;
+
Mutex::Autolock surfaceLock(mSurfaceLock);
+ result = NO_ERROR;
// asBinder() is safe on NULL (returns NULL)
if (surface->asBinder() != mSurface->asBinder()) {
if (mSurface != 0 && !mUseOverlay) {
@@ -422,8 +425,17 @@
mSurface->unregisterBuffers();
}
mSurface = surface;
+ // If preview has been already started, set overlay or register preview
+ // buffers now.
+ if (mHardware->previewEnabled()) {
+ if (mUseOverlay) {
+ result = setOverlay();
+ } else if (mSurface != 0) {
+ result = registerPreviewBuffers();
+ }
+ }
}
- return NO_ERROR;
+ return result;
}
// set the preview callback flag to affect how the received frames from
@@ -436,7 +448,7 @@
mPreviewCallbackFlag = callback_flag;
}
-// start preview mode, must call setPreviewDisplay first
+// start preview mode
status_t CameraService::Client::startCameraMode(camera_mode mode)
{
int callingPid = getCallingPid();
@@ -456,16 +468,18 @@
return INVALID_OPERATION;
}
- if (mSurface == 0) {
- LOGE("setPreviewDisplay must be called before startCameraMode!");
- return INVALID_OPERATION;
- }
-
switch(mode) {
case CAMERA_RECORDING_MODE:
+ if (mSurface == 0) {
+ LOGE("setPreviewDisplay must be called before startRecordingMode.");
+ return INVALID_OPERATION;
+ }
return startRecordingMode();
default: // CAMERA_PREVIEW_MODE
+ if (mSurface == 0) {
+ LOGD("mSurface is not set yet.");
+ }
return startPreviewMode();
}
}
@@ -498,6 +512,62 @@
return ret;
}
+status_t CameraService::Client::setOverlay()
+{
+ LOGD("setOverlay");
+ int w, h;
+ CameraParameters params(mHardware->getParameters());
+ params.getPreviewSize(&w, &h);
+
+ const char *format = params.getPreviewFormat();
+ int fmt;
+ if (!strcmp(format, "yuv422i"))
+ fmt = OVERLAY_FORMAT_YCbCr_422_I;
+ else if (!strcmp(format, "rgb565"))
+ fmt = OVERLAY_FORMAT_RGB_565;
+ else {
+ LOGE("Invalid preview format for overlays");
+ return -EINVAL;
+ }
+
+ status_t ret = NO_ERROR;
+ if (mSurface != 0) {
+ sp<OverlayRef> ref = mSurface->createOverlay(w, h, fmt);
+ ret = mHardware->setOverlay(new Overlay(ref));
+ } else {
+ ret = mHardware->setOverlay(NULL);
+ }
+ if (ret != NO_ERROR) {
+ LOGE("mHardware->setOverlay() failed with status %d\n", ret);
+ }
+ return ret;
+}
+
+status_t CameraService::Client::registerPreviewBuffers()
+{
+ int w, h;
+ CameraParameters params(mHardware->getParameters());
+ params.getPreviewSize(&w, &h);
+
+ uint32_t transform = 0;
+ if (params.getOrientation() ==
+ CameraParameters::CAMERA_ORIENTATION_PORTRAIT) {
+ LOGV("portrait mode");
+ transform = ISurface::BufferHeap::ROT_90;
+ }
+ ISurface::BufferHeap buffers(w, h, w, h,
+ PIXEL_FORMAT_YCbCr_420_SP,
+ transform,
+ 0,
+ mHardware->getPreviewHeap());
+
+ status_t ret = mSurface->registerBuffers(buffers);
+ if (ret != NO_ERROR) {
+ LOGE("registerBuffers failed with status %d", ret);
+ }
+ return ret;
+}
+
status_t CameraService::Client::startPreviewMode()
{
LOGD("startPreviewMode (pid %d)", getCallingPid());
@@ -511,55 +581,24 @@
#if DEBUG_DUMP_PREVIEW_FRAME_TO_FILE
debug_frame_cnt = 0;
#endif
- status_t ret = UNKNOWN_ERROR;
- int w, h;
- CameraParameters params(mHardware->getParameters());
- params.getPreviewSize(&w, &h);
+ status_t ret = NO_ERROR;
if (mUseOverlay) {
- const char *format = params.getPreviewFormat();
- int fmt;
- LOGD("Use Overlays");
- if (!strcmp(format, "yuv422i"))
- fmt = OVERLAY_FORMAT_YCbCr_422_I;
- else if (!strcmp(format, "rgb565"))
- fmt = OVERLAY_FORMAT_RGB_565;
- else {
- LOGE("Invalid preview format for overlays");
- return -EINVAL;
+ // If preview display has been set, set overlay now.
+ if (mSurface != 0) {
+ ret = setOverlay();
}
- sp<OverlayRef> ref = mSurface->createOverlay(w, h, fmt);
- ret = mHardware->setOverlay(new Overlay(ref));
- if (ret != NO_ERROR) {
- LOGE("mHardware->setOverlay() failed with status %d\n", ret);
- return ret;
- }
+ if (ret != NO_ERROR) return ret;
ret = mHardware->startPreview(NULL, mCameraService.get());
- if (ret != NO_ERROR)
- LOGE("mHardware->startPreview() failed with status %d\n", ret);
-
} else {
ret = mHardware->startPreview(previewCallback,
mCameraService.get());
- if (ret == NO_ERROR) {
-
- mSurface->unregisterBuffers();
-
- uint32_t transform = 0;
- if (params.getOrientation() ==
- CameraParameters::CAMERA_ORIENTATION_PORTRAIT) {
- LOGV("portrait mode");
- transform = ISurface::BufferHeap::ROT_90;
- }
- ISurface::BufferHeap buffers(w, h, w, h,
- PIXEL_FORMAT_YCbCr_420_SP,
- transform,
- 0,
- mHardware->getPreviewHeap());
-
- mSurface->registerBuffers(buffers);
- } else {
- LOGE("mHardware->startPreview() failed with status %d", ret);
+ if (ret != NO_ERROR) return ret;
+ // If preview display has been set, register preview buffers now.
+ if (mSurface != 0) {
+ // Unregister here because the surface registered with raw heap.
+ mSurface->unregisterBuffers();
+ ret = registerPreviewBuffers();
}
}
return ret;
@@ -661,7 +700,7 @@
sp<Client> client = 0;
CameraService *service = static_cast<CameraService*>(user);
if (service != NULL) {
- Mutex::Autolock ourLock(service->mLock);
+ Mutex::Autolock ourLock(service->mServiceLock);
if (service->mClient != 0) {
client = service->mClient.promote();
if (client == 0) {
@@ -1104,7 +1143,7 @@
result.append(buffer);
write(fd, result.string(), result.size());
} else {
- AutoMutex lock(&mLock);
+ AutoMutex lock(&mServiceLock);
if (mClient != 0) {
sp<Client> currentClient = mClient.promote();
sprintf(buffer, "Client (%p) PID: %d\n",
diff --git a/camera/libcameraservice/CameraService.h b/camera/libcameraservice/CameraService.h
index 729e539..0f07673 100644
--- a/camera/libcameraservice/CameraService.h
+++ b/camera/libcameraservice/CameraService.h
@@ -157,6 +157,8 @@
status_t startCameraMode(camera_mode mode);
status_t startPreviewMode();
status_t startRecordingMode();
+ status_t setOverlay();
+ status_t registerPreviewBuffers();
// Ensures atomicity among the public methods
mutable Mutex mLock;
@@ -199,7 +201,7 @@
virtual void incUsers();
virtual void decUsers();
- mutable Mutex mLock;
+ mutable Mutex mServiceLock;
wp<Client> mClient;
#if DEBUG_HEAP_LEAKS
diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
index 841e3df..c90b862 100644
--- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
+++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
@@ -17,6 +17,7 @@
package com.android.commands.bmgr;
import android.backup.IBackupManager;
+import android.backup.IRestoreObserver;
import android.backup.IRestoreSession;
import android.backup.RestoreSet;
import android.os.RemoteException;
@@ -61,6 +62,16 @@
String op = args[0];
mNextArg = 1;
+ if ("enabled".equals(op)) {
+ doEnabled();
+ return;
+ }
+
+ if ("enable".equals(op)) {
+ doEnable();
+ return;
+ }
+
if ("run".equals(op)) {
doRun();
return;
@@ -90,6 +101,41 @@
showUsage();
}
+ private String enableToString(boolean enabled) {
+ return enabled ? "enabled" : "disabled";
+ }
+
+ private void doEnabled() {
+ try {
+ boolean isEnabled = mBmgr.isBackupEnabled();
+ System.out.println("Backup Manager currently "
+ + enableToString(isEnabled));
+ } catch (RemoteException e) {
+ System.err.println(e.toString());
+ System.err.println(BMGR_NOT_RUNNING_ERR);
+ }
+ }
+
+ private void doEnable() {
+ String arg = nextArg();
+ if (arg == null) {
+ showUsage();
+ return;
+ }
+
+ try {
+ boolean enable = Boolean.parseBoolean(arg);
+ mBmgr.setBackupEnabled(enable);
+ System.out.println("Backup Manager now " + enableToString(enable));
+ } catch (NumberFormatException e) {
+ showUsage();
+ return;
+ } catch (RemoteException e) {
+ System.err.println(e.toString());
+ System.err.println(BMGR_NOT_RUNNING_ERR);
+ }
+ }
+
private void doRun() {
try {
mBmgr.backupNow();
@@ -123,11 +169,14 @@
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);
@@ -143,7 +192,7 @@
// 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);
@@ -152,6 +201,8 @@
if ("sets".equals(arg)) {
doListRestoreSets();
+ } else if ("transports".equals(arg)) {
+ doListTransports();
}
mRestore.endRestoreSession();
@@ -162,6 +213,22 @@
}
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() {
@@ -170,9 +237,7 @@
if (sets == null || sets.length == 0) {
System.out.println("No restore sets available");
} else {
- for (RestoreSet s : sets) {
- System.out.println(" " + s.token + " : " + s.name);
- }
+ printRestoreSets(sets);
}
} catch (RemoteException e) {
System.err.println(e.toString());
@@ -180,17 +245,45 @@
}
}
+ private void printRestoreSets(RestoreSet[] sets) {
+ for (RestoreSet s : sets) {
+ System.out.println(" " + s.token + " : " + s.name);
+ }
+ }
+
+ class RestoreObserver extends IRestoreObserver.Stub {
+ boolean done;
+ public void restoreStarting(int numPackages) {
+ System.out.println("restoreStarting: " + numPackages + " packages");
+ }
+
+ public void onUpdate(int nowBeingRestored) {
+ System.out.println("onUpdate: " + nowBeingRestored);
+ }
+
+ public void restoreFinished(int error) {
+ System.out.println("restoreFinished: " + error);
+ synchronized (this) {
+ done = true;
+ this.notify();
+ }
+ }
+ }
+
private void doRestore() {
- int token;
+ long token;
try {
- token = Integer.parseInt(nextArg());
+ token = Long.parseLong(nextArg());
} catch (NumberFormatException e) {
showUsage();
return;
}
+ RestoreObserver observer = new RestoreObserver();
+
try {
- int curTransport = mBmgr.getCurrentTransport();
+ boolean didRestore = false;
+ String curTransport = mBmgr.getCurrentTransport();
mRestore = mBmgr.beginRestoreSession(curTransport);
if (mRestore == null) {
System.err.println(BMGR_NOT_RUNNING_ERR);
@@ -200,15 +293,35 @@
for (RestoreSet s : sets) {
if (s.token == token) {
System.out.println("Scheduling restore: " + s.name);
- mRestore.performRestore(token);
+ mRestore.performRestore(token, observer);
+ didRestore = true;
break;
}
}
+ if (!didRestore) {
+ if (sets == null || sets.length == 0) {
+ System.out.println("No available restore sets; no restore performed");
+ } else {
+ System.out.println("No matching restore set token. Available sets:");
+ printRestoreSets(sets);
+ }
+ }
mRestore.endRestoreSession();
} catch (RemoteException e) {
System.err.println(e.toString());
System.err.println(BMGR_NOT_RUNNING_ERR);
}
+
+ // now wait for it to be done
+ synchronized (observer) {
+ while (!observer.done) {
+ try {
+ observer.wait();
+ } catch (InterruptedException ex) {
+ }
+ }
+ }
+ System.out.println("done");
}
private String nextArg() {
@@ -222,11 +335,43 @@
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.");
}
-}
\ No newline at end of file
+}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 79588ea..62dc651 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -3697,6 +3697,13 @@
*/
Locale.setDefault(data.config.locale);
+ /*
+ * Update the system configuration since its preloaded and might not
+ * reflect configuration changes. The configuration object passed
+ * in AppBindData can be safely assumed to be up to date
+ */
+ Resources.getSystem().updateConfiguration(mConfiguration, null);
+
data.info = getPackageInfoNoCheck(data.appInfo);
if (data.debugMode != IApplicationThread.DEBUG_OFF) {
diff --git a/core/java/android/app/BackupAgent.java b/core/java/android/app/BackupAgent.java
index e810775..0ac8a1e 100644
--- a/core/java/android/app/BackupAgent.java
+++ b/core/java/android/app/BackupAgent.java
@@ -67,7 +67,7 @@
* here after writing the requested data to dataFd.
*/
public abstract void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
- ParcelFileDescriptor newState);
+ ParcelFileDescriptor newState) throws IOException;
/**
* The application is being restored from backup, and should replace any
@@ -120,6 +120,9 @@
BackupDataOutput output = new BackupDataOutput(data.getFileDescriptor());
try {
BackupAgent.this.onBackup(oldState, output, newState);
+ } catch (IOException ex) {
+ Log.d(TAG, "onBackup (" + BackupAgent.this.getClass().getName() + ") threw", ex);
+ throw new RuntimeException(ex);
} catch (RuntimeException ex) {
Log.d(TAG, "onBackup (" + BackupAgent.this.getClass().getName() + ") threw", ex);
throw ex;
diff --git a/core/java/android/app/LauncherActivity.java b/core/java/android/app/LauncherActivity.java
index 8d249da..accdda9 100644
--- a/core/java/android/app/LauncherActivity.java
+++ b/core/java/android/app/LauncherActivity.java
@@ -60,26 +60,20 @@
* An item in the list
*/
public static class ListItem {
+ public ResolveInfo resolveInfo;
public CharSequence label;
- //public CharSequence description;
public Drawable icon;
public String packageName;
public String className;
public Bundle extras;
ListItem(PackageManager pm, ResolveInfo resolveInfo, IconResizer resizer) {
+ this.resolveInfo = resolveInfo;
label = resolveInfo.loadLabel(pm);
if (label == null && resolveInfo.activityInfo != null) {
label = resolveInfo.activityInfo.name;
}
- /*
- if (resolveInfo.activityInfo != null &&
- resolveInfo.activityInfo.applicationInfo != null) {
- description = resolveInfo.activityInfo.applicationInfo.loadDescription(pm);
- }
- */
-
icon = resizer.createIconThumbnail(resolveInfo.loadIcon(pm));
packageName = resolveInfo.activityInfo.applicationInfo.packageName;
className = resolveInfo.activityInfo.name;
@@ -122,6 +116,14 @@
return intent;
}
+ public ListItem itemForPosition(int position) {
+ if (mActivitiesList == null) {
+ return null;
+ }
+
+ return mActivitiesList.get(position);
+ }
+
public int getCount() {
return mActivitiesList != null ? mActivitiesList.size() : 0;
}
@@ -354,6 +356,16 @@
}
/**
+ * Return the {@link ListItem} for a specific position in our
+ * {@link android.widget.ListView}.
+ * @param position The item to return
+ */
+ protected ListItem itemForPosition(int position) {
+ ActivityAdapter adapter = (ActivityAdapter) mAdapter;
+ return adapter.itemForPosition(position);
+ }
+
+ /**
* Get the base intent to use when running
* {@link PackageManager#queryIntentActivities(Intent, int)}.
*/
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.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 @@
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 @@
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 @@
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 @@
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/BackupHelperAgent.java b/core/java/android/backup/BackupHelperAgent.java
index 3720d50..5d0c4a2 100644
--- a/core/java/android/backup/BackupHelperAgent.java
+++ b/core/java/android/backup/BackupHelperAgent.java
@@ -34,7 +34,7 @@
@Override
public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
- ParcelFileDescriptor newState) {
+ ParcelFileDescriptor newState) throws IOException {
mDispatcher.performBackup(oldState, data, newState);
}
diff --git a/core/java/android/backup/BackupHelperDispatcher.java b/core/java/android/backup/BackupHelperDispatcher.java
index b25c3e3..6ccb83e 100644
--- a/core/java/android/backup/BackupHelperDispatcher.java
+++ b/core/java/android/backup/BackupHelperDispatcher.java
@@ -19,7 +19,10 @@
import android.os.ParcelFileDescriptor;
import android.util.Log;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
import java.io.IOException;
+import java.io.FileDescriptor;
import java.util.TreeMap;
import java.util.Map;
@@ -27,6 +30,11 @@
public class BackupHelperDispatcher {
private static final String TAG = "BackupHelperDispatcher";
+ private static class Header {
+ int chunkSize; // not including the header
+ String keyPrefix;
+ }
+
TreeMap<String,BackupHelper> mHelpers = new TreeMap<String,BackupHelper>();
public BackupHelperDispatcher() {
@@ -36,13 +44,63 @@
mHelpers.put(keyPrefix, helper);
}
- /** TODO: Make this save and restore the key prefix. */
public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
- ParcelFileDescriptor newState) {
- // Write out the state files -- mHelpers is a TreeMap, so the order is well defined.
- for (Map.Entry<String,BackupHelper> entry: mHelpers.entrySet()) {
- data.setKeyPrefix(entry.getKey());
- entry.getValue().performBackup(oldState, data, newState);
+ ParcelFileDescriptor newState) throws IOException {
+ // First, do the helpers that we've already done, since they're already in the state
+ // file.
+ int err;
+ Header header = new Header();
+ TreeMap<String,BackupHelper> helpers = (TreeMap<String,BackupHelper>)mHelpers.clone();
+ FileDescriptor oldStateFD = null;
+ FileDescriptor newStateFD = newState.getFileDescriptor();
+
+ if (oldState != null) {
+ oldStateFD = oldState.getFileDescriptor();
+ while ((err = readHeader_native(header, oldStateFD)) >= 0) {
+ if (err == 0) {
+ BackupHelper helper = helpers.get(header.keyPrefix);
+ Log.d(TAG, "handling existing helper '" + header.keyPrefix + "' " + helper);
+ if (helper != null) {
+ doOneBackup(oldState, data, newState, header, helper);
+ helpers.remove(header.keyPrefix);
+ } else {
+ skipChunk_native(oldStateFD, header.chunkSize);
+ }
+ }
+ }
+ }
+
+ // Then go through and do the rest that we haven't done.
+ for (Map.Entry<String,BackupHelper> entry: helpers.entrySet()) {
+ header.keyPrefix = entry.getKey();
+ Log.d(TAG, "handling new helper '" + header.keyPrefix + "'");
+ BackupHelper helper = entry.getValue();
+ doOneBackup(oldState, data, newState, header, helper);
+ }
+ }
+
+ private void doOneBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
+ ParcelFileDescriptor newState, Header header, BackupHelper helper)
+ throws IOException {
+ int err;
+ FileDescriptor newStateFD = newState.getFileDescriptor();
+
+ // allocate space for the header in the file
+ int pos = allocateHeader_native(header, newStateFD);
+ if (pos < 0) {
+ throw new IOException("allocateHeader_native failed (error " + pos + ")");
+ }
+
+ data.setKeyPrefix(header.keyPrefix);
+
+ // do the backup
+ helper.performBackup(oldState, data, newState);
+
+ // fill in the header (seeking back to pos). The file pointer will be returned to
+ // where it was at the end of performBackup. Header.chunkSize will not be filled in.
+ err = writeHeader_native(header, newStateFD, pos);
+ if (err != 0) {
+ throw new IOException("writeHeader_native failed (error " + err + ")");
}
}
@@ -83,5 +141,11 @@
helper.writeRestoreSnapshot(newState);
}
}
+
+ private static native int readHeader_native(Header h, FileDescriptor fd);
+ private static native int skipChunk_native(FileDescriptor fd, int bytesToSkip);
+
+ private static native int allocateHeader_native(Header h, FileDescriptor fd);
+ private static native int writeHeader_native(Header h, FileDescriptor fd, int pos);
}
diff --git a/core/java/android/backup/BackupManager.java b/core/java/android/backup/BackupManager.java
index 8df7eae..5b4ac0d 100644
--- a/core/java/android/backup/BackupManager.java
+++ b/core/java/android/backup/BackupManager.java
@@ -68,9 +68,11 @@
* {@link android.app.BackupAgent} subclass will be scheduled when you call this method.
*/
public void dataChanged() {
- try {
- mService.dataChanged(mContext.getPackageName());
- } catch (RemoteException e) {
+ if (mService != null) {
+ try {
+ mService.dataChanged(mContext.getPackageName());
+ } catch (RemoteException e) {
+ }
}
}
@@ -81,11 +83,13 @@
*
* {@hide}
*/
- public IRestoreSession beginRestoreSession(int transportID) {
+ public IRestoreSession beginRestoreSession(String transport) {
IRestoreSession binder = null;
- try {
- binder = mService.beginRestoreSession(transportID);
- } catch (RemoteException e) {
+ if (mService != null) {
+ try {
+ binder = mService.beginRestoreSession(transport);
+ } catch (RemoteException e) {
+ }
}
return binder;
}
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 @@
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 @@
* Identify the currently selected transport. Callers must hold the
* android.permission.BACKUP permission to use this method.
*/
- int getCurrentTransport();
+ String getCurrentTransport();
/**
- * Specify a default backup transport. Callers must hold the
+ * Request a list of all available backup transports' names. Callers must
+ * hold the android.permission.BACKUP permission to use this method.
+ */
+ String[] listAllTransports();
+
+ /**
+ * 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/backup/IRestoreObserver.aidl b/core/java/android/backup/IRestoreObserver.aidl
new file mode 100644
index 0000000..59e59fc
--- /dev/null
+++ b/core/java/android/backup/IRestoreObserver.aidl
@@ -0,0 +1,50 @@
+/*
+ * 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.backup;
+
+/**
+ * Callback class for receiving progress reports during a restore operation.
+ *
+ * @hide
+ */
+interface IRestoreObserver {
+ /**
+ * The restore operation has begun.
+ *
+ * @param numPackages The total number of packages being processed in
+ * this restore operation.
+ */
+ void restoreStarting(int numPackages);
+
+ /**
+ * An indication of which package is being restored currently, out of the
+ * total number provided in the restoreStarting() callback. This method
+ * is not guaranteed to be called.
+ *
+ * @param nowBeingRestored The index, between 1 and the numPackages parameter
+ * to the restoreStarting() callback, of the package now being restored.
+ */
+ void onUpdate(int nowBeingRestored);
+
+ /**
+ * The restore operation has completed.
+ *
+ * @param error Zero on success; a nonzero error code if the restore operation
+ * as a whole failed.
+ */
+ void restoreFinished(int error);
+}
diff --git a/core/java/android/backup/IRestoreSession.aidl b/core/java/android/backup/IRestoreSession.aidl
index 6bca865..2a1fbc1 100644
--- a/core/java/android/backup/IRestoreSession.aidl
+++ b/core/java/android/backup/IRestoreSession.aidl
@@ -17,6 +17,7 @@
package android.backup;
import android.backup.RestoreSet;
+import android.backup.IRestoreObserver;
/**
* Binder interface used by clients who wish to manage a restore operation. Every
@@ -41,8 +42,10 @@
*
* @param token The token from {@link getAvailableRestoreSets()} corresponding to
* the restore set that should be used.
+ * @param observer If non-null, this binder points to an object that will receive
+ * progress callbacks during the restore operation.
*/
- int performRestore(int token);
+ int performRestore(long token, IRestoreObserver observer);
/**
* End this restore session. After this method is called, the IRestoreSession binder
diff --git a/core/java/android/backup/SharedPreferencesBackupHelper.java b/core/java/android/backup/SharedPreferencesBackupHelper.java
index f492629..4a7b399 100644
--- a/core/java/android/backup/SharedPreferencesBackupHelper.java
+++ b/core/java/android/backup/SharedPreferencesBackupHelper.java
@@ -30,7 +30,7 @@
private Context mContext;
private String[] mPrefGroups;
- public SharedPreferencesBackupHelper(Context context, String[] prefGroups) {
+ public SharedPreferencesBackupHelper(Context context, String... prefGroups) {
super(context);
mContext = context;
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 85d877a..27783ef 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -235,6 +235,12 @@
public static final int CONFIG_ORIENTATION = 0x0080;
/**
* Bit in {@link #configChanges} that indicates that the activity
+ * can itself handle changes to the screen layout. Set from the
+ * {@link android.R.attr#configChanges} attribute.
+ */
+ public static final int CONFIG_SCREEN_LAYOUT = 0x0100;
+ /**
+ * Bit in {@link #configChanges} that indicates that the activity
* can itself handle changes to the font scaling factor. Set from the
* {@link android.R.attr#configChanges} attribute. This is
* not a core resource configutation, but a higher-level value, so its
@@ -248,8 +254,8 @@
* Contains any combination of {@link #CONFIG_FONT_SCALE},
* {@link #CONFIG_MCC}, {@link #CONFIG_MNC},
* {@link #CONFIG_LOCALE}, {@link #CONFIG_TOUCHSCREEN},
- * {@link #CONFIG_KEYBOARD}, {@link #CONFIG_NAVIGATION}, and
- * {@link #CONFIG_ORIENTATION}. Set from the
+ * {@link #CONFIG_KEYBOARD}, {@link #CONFIG_NAVIGATION},
+ * {@link #CONFIG_ORIENTATION}, and {@link #CONFIG_SCREEN_LAYOUT}. Set from the
* {@link android.R.attr#configChanges} attribute.
*/
public int configChanges;
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 2a2cf93..bcf95b6 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -138,10 +138,27 @@
/**
* Value for {@link #flags}: true when the application's window can be
- * expanded over default window size in target density (320x480 for
- * 1.0 density, 480x720 for 1.5 density etc)
+ * reduced in size for smaller screens. Corresponds to
+ * {@link android.R.styleable#AndroidManifestSupportsScreens_smallScreens
+ * android:smallScreens}.
*/
- public static final int FLAG_SUPPORTS_LARGE_SCREENS = 1<<9;
+ public static final int FLAG_SUPPORTS_SMALL_SCREENS = 1<<9;
+
+ /**
+ * Value for {@link #flags}: true when the application's window can be
+ * displayed on normal screens. Corresponds to
+ * {@link android.R.styleable#AndroidManifestSupportsScreens_normalScreens
+ * android:normalScreens}.
+ */
+ public static final int FLAG_SUPPORTS_NORMAL_SCREENS = 1<<10;
+
+ /**
+ * Value for {@link #flags}: true when the application's window can be
+ * increased in size for larger screens. Corresponds to
+ * {@link android.R.styleable#AndroidManifestSupportsScreens_largeScreens
+ * android:smallScreens}.
+ */
+ public static final int FLAG_SUPPORTS_LARGE_SCREENS = 1<<11;
/**
* Value for {@link #flags}: this is false if the application has set
@@ -149,7 +166,7 @@
*
* {@hide}
*/
- public static final int FLAG_ALLOW_BACKUP = 1<<10;
+ public static final int FLAG_ALLOW_BACKUP = 1<<12;
/**
* Indicates that the application supports any densities;
@@ -164,7 +181,9 @@
* {@link #FLAG_PERSISTENT}, {@link #FLAG_FACTORY_TEST}, and
* {@link #FLAG_ALLOW_TASK_REPARENTING}
* {@link #FLAG_ALLOW_CLEAR_USER_DATA}, {@link #FLAG_UPDATED_SYSTEM_APP},
- * {@link #FLAG_TEST_ONLY}.
+ * {@link #FLAG_TEST_ONLY}, {@link #FLAG_SUPPORTS_SMALL_SCREENS},
+ * {@link #FLAG_SUPPORTS_NORMAL_SCREENS},
+ * {@link #FLAG_SUPPORTS_LARGE_SCREENS}.
*/
public int flags = 0;
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index ab9518e..558b0c3 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -668,6 +668,11 @@
}
sa.recycle();
+ // Resource boolean are -1, so 1 means we don't know the value.
+ int supportsSmallScreens = 1;
+ int supportsNormalScreens = 1;
+ int supportsLargeScreens = 1;
+
int outerDepth = parser.getDepth();
while ((type=parser.next()) != parser.END_DOCUMENT
&& (type != parser.END_TAG || parser.getDepth() > outerDepth)) {
@@ -876,8 +881,24 @@
XmlUtils.skipCurrentTag(parser);
- } else if (tagName.equals("expandable")) {
- pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS;
+ } else if (tagName.equals("supports-screens")) {
+ sa = res.obtainAttributes(attrs,
+ com.android.internal.R.styleable.AndroidManifestSupportsScreens);
+
+ // This is a trick to get a boolean and still able to detect
+ // if a value was actually set.
+ supportsSmallScreens = sa.getInteger(
+ com.android.internal.R.styleable.AndroidManifestSupportsScreens_smallScreens,
+ supportsSmallScreens);
+ supportsNormalScreens = sa.getInteger(
+ com.android.internal.R.styleable.AndroidManifestSupportsScreens_normalScreens,
+ supportsNormalScreens);
+ supportsLargeScreens = sa.getInteger(
+ com.android.internal.R.styleable.AndroidManifestSupportsScreens_largeScreens,
+ supportsLargeScreens);
+
+ sa.recycle();
+
XmlUtils.skipCurrentTag(parser);
} else {
Log.w(TAG, "Bad element under <manifest>: "
@@ -910,7 +931,20 @@
pkg.usesLibraryFiles = new String[pkg.usesLibraries.size()];
pkg.usesLibraries.toArray(pkg.usesLibraryFiles);
}
- // TODO: enable all density & expandable if target sdk is higher than donut
+
+ if (supportsSmallScreens < 0 || (supportsSmallScreens > 0
+ && pkg.applicationInfo.targetSdkVersion
+ >= android.os.Build.VERSION_CODES.CUR_DEVELOPMENT)) {
+ pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS;
+ }
+ if (supportsNormalScreens != 0) {
+ pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS;
+ }
+ if (supportsLargeScreens < 0 || (supportsLargeScreens > 0
+ && pkg.applicationInfo.targetSdkVersion
+ >= android.os.Build.VERSION_CODES.CUR_DEVELOPMENT)) {
+ pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS;
+ }
int size = pkg.supportsDensityList.size();
if (size > 0) {
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 1c91736..5c7b01f 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -601,7 +601,7 @@
public native final void setConfiguration(int mcc, int mnc, String locale,
int orientation, int touchscreen, int density, int keyboard,
int keyboardHidden, int navigation, int screenWidth, int screenHeight,
- int majorVersion);
+ int screenLayout, int majorVersion);
/**
* Retrieve the resource identifier for the given resource name.
diff --git a/core/java/android/content/res/CompatibilityInfo.java b/core/java/android/content/res/CompatibilityInfo.java
index 179b9bd..4e6fe07 100644
--- a/core/java/android/content/res/CompatibilityInfo.java
+++ b/core/java/android/content/res/CompatibilityInfo.java
@@ -65,7 +65,7 @@
/**
* A compatibility flags
*/
- private int compatibilityFlags;
+ private int mCompatibilityFlags;
/**
* A flag mask to tell if the application needs scaling (when mApplicationScale != 1.0f)
@@ -101,7 +101,11 @@
*/
public final float applicationInvertedScale;
-
+ /**
+ * The flags from ApplicationInfo.
+ */
+ public final int appFlags;
+
/**
* Window size in Compatibility Mode, in real pixels. This is updated by
* {@link DisplayMetrics#updateMetrics}.
@@ -117,8 +121,10 @@
private int mXOffset;
public CompatibilityInfo(ApplicationInfo appInfo) {
+ appFlags = appInfo.flags;
+
if ((appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS) != 0) {
- compatibilityFlags = EXPANDABLE | CONFIGURED_EXPANDABLE;
+ mCompatibilityFlags = EXPANDABLE | CONFIGURED_EXPANDABLE;
}
float packageDensityScale = -1.0f;
@@ -149,13 +155,16 @@
}
applicationInvertedScale = 1.0f / applicationScale;
if (applicationScale != 1.0f) {
- compatibilityFlags |= SCALING_REQUIRED;
+ mCompatibilityFlags |= SCALING_REQUIRED;
}
}
private CompatibilityInfo() {
+ appFlags = ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS
+ | ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS
+ | ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS;
applicationScale = applicationInvertedScale = 1.0f;
- compatibilityFlags = EXPANDABLE | CONFIGURED_EXPANDABLE;
+ mCompatibilityFlags = EXPANDABLE | CONFIGURED_EXPANDABLE;
}
/**
@@ -175,9 +184,9 @@
*/
public void setExpandable(boolean expandable) {
if (expandable) {
- compatibilityFlags |= CompatibilityInfo.EXPANDABLE;
+ mCompatibilityFlags |= CompatibilityInfo.EXPANDABLE;
} else {
- compatibilityFlags &= ~CompatibilityInfo.EXPANDABLE;
+ mCompatibilityFlags &= ~CompatibilityInfo.EXPANDABLE;
}
}
@@ -185,20 +194,20 @@
* @return true if the application is configured to be expandable.
*/
public boolean isConfiguredExpandable() {
- return (compatibilityFlags & CompatibilityInfo.CONFIGURED_EXPANDABLE) != 0;
+ return (mCompatibilityFlags & CompatibilityInfo.CONFIGURED_EXPANDABLE) != 0;
}
/**
* @return true if the scaling is required
*/
public boolean isScalingRequired() {
- return (compatibilityFlags & SCALING_REQUIRED) != 0;
+ return (mCompatibilityFlags & SCALING_REQUIRED) != 0;
}
@Override
public String toString() {
return "CompatibilityInfo{scale=" + applicationScale +
- ", compatibility flag=" + compatibilityFlags + "}";
+ ", compatibility flag=" + mCompatibilityFlags + "}";
}
/**
@@ -222,13 +231,13 @@
* @param params the window's parameter
*/
public Translator getTranslator(WindowManager.LayoutParams params) {
- if ( (compatibilityFlags & CompatibilityInfo.SCALING_EXPANDABLE_MASK)
+ if ( (mCompatibilityFlags & CompatibilityInfo.SCALING_EXPANDABLE_MASK)
== CompatibilityInfo.EXPANDABLE) {
if (DBG) Log.d(TAG, "no translation required");
return null;
}
- if ((compatibilityFlags & CompatibilityInfo.EXPANDABLE) == 0) {
+ if ((mCompatibilityFlags & CompatibilityInfo.EXPANDABLE) == 0) {
if ((params.flags & WindowManager.LayoutParams.FLAG_NO_COMPATIBILITY_SCALING) != 0) {
if (DBG) Log.d(TAG, "translation for surface view selected");
return new Translator(X_SHIFT_WINDOW, false, 1.0f, 1.0f);
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index bb3486c..577aa60 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -116,6 +116,18 @@
*/
public int orientation;
+ public static final int SCREENLAYOUT_UNDEFINED = 0;
+ public static final int SCREENLAYOUT_SMALL = 1;
+ public static final int SCREENLAYOUT_NORMAL = 2;
+ public static final int SCREENLAYOUT_LARGE = 3;
+
+ /**
+ * Overall layout of the screen. May be one of
+ * {@link #SCREENLAYOUT_SMALL}, {@link #SCREENLAYOUT_NORMAL},
+ * or {@link #SCREENLAYOUT_LARGE}.
+ */
+ public int screenLayout;
+
/**
* Construct an invalid Configuration. You must call {@link #setToDefaults}
* for this object to be valid. {@more}
@@ -141,6 +153,7 @@
hardKeyboardHidden = o.hardKeyboardHidden;
navigation = o.navigation;
orientation = o.orientation;
+ screenLayout = o.screenLayout;
}
public String toString() {
@@ -165,6 +178,8 @@
sb.append(navigation);
sb.append(" orien=");
sb.append(orientation);
+ sb.append(" layout=");
+ sb.append(screenLayout);
sb.append('}');
return sb.toString();
}
@@ -183,6 +198,7 @@
hardKeyboardHidden = HARDKEYBOARDHIDDEN_UNDEFINED;
navigation = NAVIGATION_UNDEFINED;
orientation = ORIENTATION_UNDEFINED;
+ screenLayout = SCREENLAYOUT_UNDEFINED;
}
/** {@hide} */
@@ -253,6 +269,11 @@
changed |= ActivityInfo.CONFIG_ORIENTATION;
orientation = delta.orientation;
}
+ if (delta.screenLayout != SCREENLAYOUT_UNDEFINED
+ && screenLayout != delta.screenLayout) {
+ changed |= ActivityInfo.CONFIG_SCREEN_LAYOUT;
+ screenLayout = delta.screenLayout;
+ }
return changed;
}
@@ -276,9 +297,11 @@
* {@link android.content.pm.ActivityInfo#CONFIG_KEYBOARD
* PackageManager.ActivityInfo.CONFIG_KEYBOARD},
* {@link android.content.pm.ActivityInfo#CONFIG_NAVIGATION
- * PackageManager.ActivityInfo.CONFIG_NAVIGATION}, or
+ * PackageManager.ActivityInfo.CONFIG_NAVIGATION},
* {@link android.content.pm.ActivityInfo#CONFIG_ORIENTATION
- * PackageManager.ActivityInfo.CONFIG_ORIENTATION}.
+ * PackageManager.ActivityInfo.CONFIG_ORIENTATION}, or
+ * {@link android.content.pm.ActivityInfo#CONFIG_SCREEN_LAYOUT
+ * PackageManager.ActivityInfo.CONFIG_SCREEN_LAYOUT}.
*/
public int diff(Configuration delta) {
int changed = 0;
@@ -319,6 +342,10 @@
&& orientation != delta.orientation) {
changed |= ActivityInfo.CONFIG_ORIENTATION;
}
+ if (delta.screenLayout != SCREENLAYOUT_UNDEFINED
+ && screenLayout != delta.screenLayout) {
+ changed |= ActivityInfo.CONFIG_SCREEN_LAYOUT;
+ }
return changed;
}
@@ -368,6 +395,7 @@
dest.writeInt(hardKeyboardHidden);
dest.writeInt(navigation);
dest.writeInt(orientation);
+ dest.writeInt(screenLayout);
}
public static final Parcelable.Creator<Configuration> CREATOR
@@ -399,6 +427,7 @@
hardKeyboardHidden = source.readInt();
navigation = source.readInt();
orientation = source.readInt();
+ screenLayout = source.readInt();
}
public int compareTo(Configuration that) {
@@ -428,6 +457,8 @@
n = this.navigation - that.navigation;
if (n != 0) return n;
n = this.orientation - that.orientation;
+ if (n != 0) return n;
+ n = this.screenLayout - that.screenLayout;
//if (n != 0) return n;
return n;
}
@@ -450,6 +481,6 @@
return ((int)this.fontScale) + this.mcc + this.mnc
+ this.locale.hashCode() + this.touchscreen
+ this.keyboard + this.keyboardHidden + this.hardKeyboardHidden
- + this.navigation + this.orientation;
+ + this.navigation + this.orientation + this.screenLayout;
}
}
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index cb9d46e..d7512bb 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -1267,7 +1267,8 @@
}
if (metrics != null) {
mMetrics.setTo(metrics);
- mMetrics.updateMetrics(mCompatibilityInfo, mConfiguration.orientation);
+ mMetrics.updateMetrics(mCompatibilityInfo,
+ mConfiguration.orientation, mConfiguration.screenLayout);
}
mMetrics.scaledDensity = mMetrics.density * mConfiguration.fontScale;
@@ -1299,7 +1300,7 @@
mConfiguration.touchscreen,
(int)(mMetrics.density*160), mConfiguration.keyboard,
keyboardHidden, mConfiguration.navigation, width, height,
- sSdkVersion);
+ mConfiguration.screenLayout, sSdkVersion);
int N = mDrawableCache.size();
if (DEBUG_CONFIG) {
Log.d(TAG, "Cleaning up drawables config changes: 0x"
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index ca579b6..3ce951f 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -39,13 +39,16 @@
public class Camera {
private static final String TAG = "Camera";
- // These match the enum in libs/android_runtime/android_hardware_Camera.cpp
- private static final int SHUTTER_CALLBACK = 0;
- private static final int RAW_PICTURE_CALLBACK = 1;
- private static final int JPEG_PICTURE_CALLBACK = 2;
- private static final int PREVIEW_CALLBACK = 3;
- private static final int AUTOFOCUS_CALLBACK = 4;
- private static final int ERROR_CALLBACK = 5;
+ // These match the enums in frameworks/base/include/ui/Camera.h
+ private static final int CAMERA_MSG_ERROR = 0;
+ private static final int CAMERA_MSG_SHUTTER = 1;
+ private static final int CAMERA_MSG_FOCUS = 2;
+ private static final int CAMERA_MSG_ZOOM = 3;
+ private static final int CAMERA_MSG_PREVIEW_FRAME = 4;
+ private static final int CAMERA_MSG_VIDEO_FRAME = 5;
+ private static final int CAMERA_MSG_POSTVIEW_FRAME = 6;
+ private static final int CAMERA_MSG_RAW_IMAGE = 7;
+ private static final int CAMERA_MSG_COMPRESSED_IMAGE = 8;
private int mNativeContext; // accessed by native methods
private EventHandler mEventHandler;
@@ -152,7 +155,11 @@
* @throws IOException if the method fails.
*/
public final void setPreviewDisplay(SurfaceHolder holder) throws IOException {
- setPreviewDisplay(holder.getSurface());
+ if (holder != null) {
+ setPreviewDisplay(holder.getSurface());
+ } else {
+ setPreviewDisplay((Surface)null);
+ }
}
private native final void setPreviewDisplay(Surface surface);
@@ -231,22 +238,23 @@
@Override
public void handleMessage(Message msg) {
switch(msg.what) {
- case SHUTTER_CALLBACK:
+ case CAMERA_MSG_SHUTTER:
if (mShutterCallback != null) {
mShutterCallback.onShutter();
}
return;
- case RAW_PICTURE_CALLBACK:
+
+ case CAMERA_MSG_RAW_IMAGE:
if (mRawImageCallback != null)
mRawImageCallback.onPictureTaken((byte[])msg.obj, mCamera);
return;
- case JPEG_PICTURE_CALLBACK:
+ case CAMERA_MSG_COMPRESSED_IMAGE:
if (mJpegCallback != null)
mJpegCallback.onPictureTaken((byte[])msg.obj, mCamera);
return;
- case PREVIEW_CALLBACK:
+ case CAMERA_MSG_PREVIEW_FRAME:
if (mPreviewCallback != null) {
mPreviewCallback.onPreviewFrame((byte[])msg.obj, mCamera);
if (mOneShot) {
@@ -255,12 +263,12 @@
}
return;
- case AUTOFOCUS_CALLBACK:
+ case CAMERA_MSG_FOCUS:
if (mAutoFocusCallback != null)
mAutoFocusCallback.onAutoFocus(msg.arg1 == 0 ? false : true, mCamera);
return;
- case ERROR_CALLBACK:
+ case CAMERA_MSG_ERROR :
Log.e(TAG, "Error " + msg.arg1);
if (mErrorCallback != null)
mErrorCallback.onError(msg.arg1, mCamera);
diff --git a/core/java/android/net/http/RequestHandle.java b/core/java/android/net/http/RequestHandle.java
index c4ee5b0..6a97951 100644
--- a/core/java/android/net/http/RequestHandle.java
+++ b/core/java/android/net/http/RequestHandle.java
@@ -159,11 +159,11 @@
e.printStackTrace();
}
- // update the "cookie" header based on the redirected url
- mHeaders.remove("cookie");
+ // update the "Cookie" header based on the redirected url
+ mHeaders.remove("Cookie");
String cookie = CookieManager.getInstance().getCookie(mUri);
if (cookie != null && cookie.length() > 0) {
- mHeaders.put("cookie", cookie);
+ mHeaders.put("Cookie", cookie);
}
if ((statusCode == 302 || statusCode == 303) && mMethod.equals("POST")) {
diff --git a/core/java/android/os/AsyncTask.java b/core/java/android/os/AsyncTask.java
index 6c13582..abfb274 100644
--- a/core/java/android/os/AsyncTask.java
+++ b/core/java/android/os/AsyncTask.java
@@ -127,12 +127,12 @@
public abstract class AsyncTask<Params, Progress, Result> {
private static final String LOG_TAG = "AsyncTask";
- private static final int CORE_POOL_SIZE = 1;
- private static final int MAXIMUM_POOL_SIZE = 10;
+ private static final int CORE_POOL_SIZE = 5;
+ private static final int MAXIMUM_POOL_SIZE = 128;
private static final int KEEP_ALIVE = 10;
private static final BlockingQueue<Runnable> sWorkQueue =
- new LinkedBlockingQueue<Runnable>(MAXIMUM_POOL_SIZE);
+ new LinkedBlockingQueue<Runnable>(10);
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 333c7cb..1214abc 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -573,7 +573,21 @@
* directly to a gid.
*/
public static final native int getGidForName(String name);
-
+
+ /**
+ * Returns a uid for a currently running process.
+ * @param pid the process id
+ * @return the uid of the process, or -1 if the process is not running.
+ * @hide pending API council review
+ */
+ public static final int getUidForPid(int pid) {
+ String[] procStatusLabels = { "Uid:" };
+ long[] procStatusValues = new long[1];
+ procStatusValues[0] = -1;
+ Process.readProcLines("/proc/" + pid + "/status", procStatusLabels, procStatusValues);
+ return (int) procStatusValues[0];
+ }
+
/**
* Set the priority of a thread, based on Linux priorities.
*
diff --git a/core/java/android/preference/PreferenceScreen.java b/core/java/android/preference/PreferenceScreen.java
index 6ea2528..95e54324 100644
--- a/core/java/android/preference/PreferenceScreen.java
+++ b/core/java/android/preference/PreferenceScreen.java
@@ -150,7 +150,7 @@
// 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..1ba5e25e 100644
--- a/core/java/android/provider/Browser.java
+++ b/core/java/android/provider/Browser.java
@@ -91,6 +91,17 @@
*/
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/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 51d1951..bc7b5be 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -344,7 +344,10 @@
// Check if file exists with a FileInputStream
FileInputStream stream = new FileInputStream(imagePath);
try {
- return insertImage(cr, BitmapFactory.decodeFile(imagePath), name, description);
+ Bitmap bm = BitmapFactory.decodeFile(imagePath);
+ String ret = insertImage(cr, bm, name, description);
+ bm.recycle();
+ return ret;
} finally {
try {
stream.close();
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 5dc03eb..7356326 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1274,6 +1274,50 @@
public static final String DTMF_TONE_WHEN_DIALING = "dtmf_tone";
/**
+ * CDMA only settings
+ * DTMF tone type played by the dialer when dialing.
+ * 0 = Normal
+ * 1 = Long
+ * @hide
+ */
+ public static final String DTMF_TONE_TYPE_WHEN_DIALING = "dtmf_tone_type";
+
+ /**
+ * CDMA only settings
+ * Emergency Tone 0 = Off
+ * 1 = Alert
+ * 2 = Vibrate
+ * @hide
+ */
+ public static final String EMERGENCY_TONE = "emergency_tone";
+
+ /**
+ * CDMA only settings
+ * Whether the auto retry is enabled. The value is
+ * boolean (1 or 0).
+ * @hide
+ */
+ public static final String CALL_AUTO_RETRY = "call_auto_retry";
+
+ /**
+ * Whether the hearing aid is enabled. The value is
+ * boolean (1 or 0).
+ * @hide
+ */
+ public static final String HEARING_AID = "hearing_aid";
+
+ /**
+ * CDMA only settings
+ * TTY Mode
+ * 0 = OFF
+ * 1 = FULL
+ * 2 = VCO
+ * 3 = HCO
+ * @hide
+ */
+ public static final String TTY_MODE = "tty_mode";
+
+ /**
* Whether the sounds effects (key clicks, lid open ...) are enabled. The value is
* boolean (1 or 0).
*/
diff --git a/core/java/android/speech/tts/ITts.aidl b/core/java/android/speech/tts/ITts.aidl
index 75c3b30..15f3876 100755
--- a/core/java/android/speech/tts/ITts.aidl
+++ b/core/java/android/speech/tts/ITts.aidl
@@ -33,6 +33,8 @@
void speak(in String text, in int queueMode, in String[] params);
+ void speakIpa(in String ipaText, in int queueMode, in String[] params);
+
boolean isSpeaking();
void stop();
@@ -41,9 +43,15 @@
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);
+ boolean synthesizeToFile(in String text, in String[] params, in String outputDirectory);
+
+ boolean synthesizeIpaToFile(in String ipaText, in String[] params, in String outputDirectory);
void playEarcon(in String earcon, in int queueMode, in String[] params);
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
index c064284..b245713 100755
--- a/core/java/android/speech/tts/TextToSpeech.java
+++ b/core/java/android/speech/tts/TextToSpeech.java
@@ -46,10 +46,6 @@
* Denotes a generic operation failure.
*/
public static final int TTS_ERROR = -1;
- /**
- * Denotes a failure due to a missing resource.
- */
- public static final int TTS_ERROR_MISSING_RESOURCE = -2;
/**
* Queue mode where all entries in the playback queue (media to be played
@@ -61,6 +57,37 @@
*/
public static final int TTS_QUEUE_ADD = 1;
+
+ /**
+ * Denotes the language is available exactly as specified by the locale
+ */
+ public static final int TTS_LANG_COUNTRY_VAR_AVAILABLE = 2;
+
+
+ /**
+ * Denotes the language is available for the language and country specified
+ * by the locale, but not the variant.
+ */
+ public static final int TTS_LANG_COUNTRY_AVAILABLE = 1;
+
+
+ /**
+ * Denotes the language is available for the language by the locale,
+ * but not the country and variant.
+ */
+ public static final int TTS_LANG_AVAILABLE = 0;
+
+ /**
+ * Denotes the language data is missing.
+ */
+ public static final int TTS_LANG_MISSING_DATA = -1;
+
+ /**
+ * Denotes the language is not supported by the current TTS engine.
+ */
+ public static final int TTS_LANG_NOT_SUPPORTED = -2;
+
+
/**
* Called when the TTS has initialized.
*
@@ -72,15 +99,6 @@
}
/**
- * Called when the TTS has finished speaking by itself (speaking
- * finished without being canceled).
- *
- */
- public interface OnSpeechCompletedListener {
- public void onSpeechCompleted();
- }
-
- /**
* Internal constants for the TTS functionality
*
* {@hide}
@@ -100,6 +118,16 @@
public static final int CHECK_VOICE_DATA_BAD_DATA = -1;
public static final int CHECK_VOICE_DATA_MISSING_DATA = -2;
public static final int CHECK_VOICE_DATA_MISSING_DATA_NO_SDCARD = -3;
+
+ // keys for the parameters passed with speak commands
+ public static final String TTS_KEY_PARAM_RATE = "rate";
+ public static final String TTS_KEY_PARAM_LANGUAGE = "language";
+ public static final String TTS_KEY_PARAM_COUNTRY = "country";
+ public static final String TTS_KEY_PARAM_VARIANT = "variant";
+ public static final int TTS_PARAM_POSITION_RATE = 0;
+ public static final int TTS_PARAM_POSITION_LANGUAGE = 2;
+ public static final int TTS_PARAM_POSITION_COUNTRY = 4;
+ public static final int TTS_PARAM_POSITION_VARIANT = 6;
}
/**
@@ -112,11 +140,11 @@
private OnInitListener mInitListener = null;
private boolean mStarted = false;
private final Object mStartLock = new Object();
- private ITtsCallback mITtsCallback;
- private OnSpeechCompletedListener mSpeechCompListener = null;
- private final Object mSpeechCompListenerLock = new Object();
-
-
+ private int mCachedRate = Engine.FALLBACK_TTS_DEFAULT_RATE;
+ private String mCachedLang = Engine.FALLBACK_TTS_DEFAULT_LANG;
+ private String mCachedCountry = Engine.FALLBACK_TTS_DEFAULT_COUNTRY;
+ private String mCachedVariant = Engine.FALLBACK_TTS_DEFAULT_VARIANT;
+ private String[] mCachedParams;
/**
* The constructor for the TTS.
@@ -130,24 +158,23 @@
public TextToSpeech(Context context, OnInitListener listener) {
mContext = context;
mInitListener = listener;
+
+ mCachedParams = new String[2*4]; //4 parameters, store key and value
+ mCachedParams[Engine.TTS_PARAM_POSITION_RATE] = Engine.TTS_KEY_PARAM_RATE;
+ mCachedParams[Engine.TTS_PARAM_POSITION_LANGUAGE] = Engine.TTS_KEY_PARAM_LANGUAGE;
+ mCachedParams[Engine.TTS_PARAM_POSITION_COUNTRY] = Engine.TTS_KEY_PARAM_COUNTRY;
+ mCachedParams[Engine.TTS_PARAM_POSITION_VARIANT] = Engine.TTS_KEY_PARAM_VARIANT;
+ updateCachedParamArray();
+
initTts();
}
- public void setOnSpeechCompletedListener(final OnSpeechCompletedListener listener) {
- synchronized(mSpeechCompListenerLock) {
- mSpeechCompListener = listener;
- }
- }
-
-
- private boolean dataFilesCheck() {
- // TODO #TTS# config manager will be in settings
- Log.i("TTS_FIXME", "FIXME in Tts: config manager will be in settings");
- // TODO #TTS# implement checking of the correct installation of
- // the data files.
-
- return true;
+ private void updateCachedParamArray() {
+ mCachedParams[Engine.TTS_PARAM_POSITION_RATE+1] = String.valueOf(mCachedRate);
+ mCachedParams[Engine.TTS_PARAM_POSITION_LANGUAGE+1] = mCachedLang;
+ mCachedParams[Engine.TTS_PARAM_POSITION_COUNTRY+1] = mCachedCountry;
+ mCachedParams[Engine.TTS_PARAM_POSITION_VARIANT+1] = mCachedVariant;
}
@@ -159,34 +186,7 @@
public void onServiceConnected(ComponentName name, IBinder service) {
synchronized(mStartLock) {
mITts = ITts.Stub.asInterface(service);
- try {
- mITtsCallback = new ITtsCallback.Stub() {
- public void markReached(String mark)
- throws RemoteException {
- // call the listener of that event, but not
- // while locked.
- OnSpeechCompletedListener listener = null;
- synchronized(mSpeechCompListenerLock) {
- listener = mSpeechCompListener;
- }
- if (listener != null) {
- listener.onSpeechCompleted();
- }
- }
- };
- mITts.registerCallback(mITtsCallback);
-
- } catch (RemoteException e) {
- initTts();
- return;
- }
-
mStarted = true;
- // The callback can become null if the Android OS decides to
- // restart the TTS process as well as whatever is using it.
- // In such cases, do nothing - the error handling from the
- // speaking calls will kick in and force a proper restart of
- // the TTS.
if (mInitListener != null) {
// TODO manage failures and missing resources
mInitListener.onInit(TTS_SUCCESS);
@@ -251,14 +251,17 @@
*
* @param resourceId
* Example: <b><code>R.raw.south_south_east</code></b>
+ *
+ * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
*/
- public void addSpeech(String text, String packagename, int resourceId) {
+ public int addSpeech(String text, String packagename, int resourceId) {
synchronized(mStartLock) {
if (!mStarted) {
- return;
+ return TTS_ERROR;
}
try {
mITts.addSpeech(text, packagename, resourceId);
+ return TTS_SUCCESS;
} catch (RemoteException e) {
// TTS died; restart it.
mStarted = false;
@@ -272,6 +275,7 @@
mStarted = false;
initTts();
}
+ return TTS_ERROR;
}
}
@@ -285,14 +289,17 @@
* @param filename
* The full path to the sound file (for example:
* "/sdcard/mysounds/hello.wav")
+ *
+ * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
*/
- public void addSpeech(String text, String filename) {
+ public int addSpeech(String text, String filename) {
synchronized (mStartLock) {
if (!mStarted) {
- return;
+ return TTS_ERROR;
}
try {
mITts.addSpeechFile(text, filename);
+ return TTS_SUCCESS;
} catch (RemoteException e) {
// TTS died; restart it.
mStarted = false;
@@ -306,6 +313,7 @@
mStarted = false;
initTts();
}
+ return TTS_ERROR;
}
}
@@ -324,17 +332,20 @@
* See TTS_QUEUE_ADD and TTS_QUEUE_FLUSH.
* @param params
* The hashmap of speech parameters to be used.
+ *
+ * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
*/
- public void speak(String text, int queueMode, HashMap<String,String> params)
+ public int speak(String text, int queueMode, HashMap<String,String> params)
{
synchronized (mStartLock) {
Log.i("TTS received: ", text);
if (!mStarted) {
- return;
+ return TTS_ERROR;
}
try {
- // TODO support extra parameters, passing null for the moment
- mITts.speak(text, queueMode, null);
+ // 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,55 @@
mStarted = false;
initTts();
}
+ return TTS_ERROR;
+ }
+ }
+
+
+ /**
+ * Speaks the IPA string using the specified queuing strategy and speech
+ * parameters. Note that the speech parameters are not universally supported
+ * by all engines and will be treated as a hint. The TTS library will try to
+ * fulfill these parameters as much as possible, but there is no guarantee
+ * that the voice used will have the properties specified.
+ *
+ * @param ipaText
+ * The string of IPA text to be spoken.
+ * @param queueMode
+ * The queuing strategy to use.
+ * 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 int speakIpa(String ipaText, int queueMode, HashMap<String,String> params)
+ {
+ 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;
}
}
@@ -361,16 +421,19 @@
* See TTS_QUEUE_ADD and TTS_QUEUE_FLUSH.
* @param params
* The hashmap of parameters to be used.
+ *
+ * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
*/
- public void playEarcon(String earcon, int queueMode,
+ public int playEarcon(String earcon, int queueMode,
HashMap<String,String> params) {
synchronized (mStartLock) {
if (!mStarted) {
- return;
+ return TTS_ERROR;
}
try {
// TODO support extra parameters, passing null for the moment
mITts.playEarcon(earcon, queueMode, null);
+ return TTS_SUCCESS;
} catch (RemoteException e) {
// TTS died; restart it.
mStarted = false;
@@ -384,12 +447,45 @@
mStarted = false;
initTts();
}
+ return TTS_ERROR;
}
}
-
- public void playSilence(long durationInMs, int queueMode) {
- // TODO implement, already present in TTS service
+ /**
+ * 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 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;
+ initTts();
+ } catch (NullPointerException e) {
+ // TTS died; restart it.
+ mStarted = false;
+ initTts();
+ } catch (IllegalStateException e) {
+ // TTS died; restart it.
+ mStarted = false;
+ initTts();
+ }
+ return TTS_ERROR;
+ }
}
@@ -425,14 +521,17 @@
/**
* Stops speech from the TTS.
+ *
+ * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
*/
- public void stop() {
+ public int stop() {
synchronized (mStartLock) {
if (!mStarted) {
- return;
+ return TTS_ERROR;
}
try {
mITts.stop();
+ return TTS_SUCCESS;
} catch (RemoteException e) {
// TTS died; restart it.
mStarted = false;
@@ -446,6 +545,7 @@
mStarted = false;
initTts();
}
+ return TTS_ERROR;
}
}
@@ -462,21 +562,27 @@
* The speech rate for the TTS engine. 1 is the normal speed,
* lower values slow down the speech (0.5 is half the normal speech rate),
* greater values accelerate it (2 is twice the normal speech rate).
+ *
+ * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
*/
- public void setSpeechRate(float speechRate) {
+ public int setSpeechRate(float speechRate) {
synchronized (mStartLock) {
if (!mStarted) {
- return;
+ return TTS_SUCCESS;
}
try {
if (speechRate > 0) {
- mITts.setSpeechRate((int)(speechRate*100));
+ mCachedRate = (int)(speechRate*100);
+ updateCachedParamArray();
+ mITts.setSpeechRate(mCachedRate);
+ return TTS_SUCCESS;
}
} catch (RemoteException e) {
// TTS died; restart it.
mStarted = false;
initTts();
}
+ return TTS_ERROR;
}
}
@@ -493,21 +599,25 @@
* The pitch for the TTS engine. 1 is the normal pitch,
* lower values lower the tone of the synthesized voice,
* greater values increase it.
+ *
+ * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
*/
- public void setPitch(float pitch) {
+ public int setPitch(float pitch) {
synchronized (mStartLock) {
if (!mStarted) {
- return;
+ return TTS_ERROR;
}
try {
if (pitch > 0) {
mITts.setPitch((int)(pitch*100));
+ return TTS_SUCCESS;
}
} catch (RemoteException e) {
// TTS died; restart it.
mStarted = false;
initTts();
}
+ return TTS_ERROR;
}
}
@@ -521,25 +631,86 @@
*
* @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 {
- mITts.setLanguage(loc.getISO3Language(), loc.getISO3Country(), loc.getVariant());
+ mCachedLang = loc.getISO3Language();
+ mCachedCountry = loc.getISO3Country();
+ 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;
}
}
/**
- * Speaks the given text using the specified queueing mode and parameters.
+ * 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.
+ *
+ * @param loc
+ * The Locale describing the language to be used.
+ *
+ * @return one of TTS_LANG_NOT_SUPPORTED, TTS_LANG_MISSING_DATA, TTS_LANG_AVAILABLE,
+ * TTS_LANG_COUNTRY_AVAILABLE, TTS_LANG_COUNTRY_VAR_AVAILABLE.
+ */
+ public int isLanguageAvailable(Locale loc) {
+ synchronized (mStartLock) {
+ if (!mStarted) {
+ return TTS_LANG_NOT_SUPPORTED;
+ }
+ try {
+ return mITts.isLanguageAvailable(loc.getISO3Language(), loc.getISO3Country(),
+ loc.getVariant());
+ } catch (RemoteException e) {
+ // TTS died; restart it.
+ mStarted = false;
+ initTts();
+ }
+ return TTS_LANG_NOT_SUPPORTED;
+ }
+ }
+
+
+ /**
+ * Synthesizes the given text to a file using the specified parameters.
*
* @param text
* The String of text that should be synthesized
@@ -548,17 +719,20 @@
* @param filename
* The string that gives the full output filename; it should be
* something like "/sdcard/myappsounds/mysound.wav".
- * @return A boolean that indicates if the synthesis succeeded
+ *
+ * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
*/
- public boolean synthesizeToFile(String text, HashMap<String,String> params,
+ public int synthesizeToFile(String text, HashMap<String,String> params,
String filename) {
synchronized (mStartLock) {
if (!mStarted) {
- return false;
+ return TTS_ERROR;
}
try {
// TODO support extra parameters, passing null for the moment
- return mITts.synthesizeToFile(text, null, filename);
+ if (mITts.synthesizeToFile(text, null, filename)){
+ return TTS_SUCCESS;
+ }
} catch (RemoteException e) {
// TTS died; restart it.
mStarted = false;
@@ -572,9 +746,52 @@
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/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java
index d89ada0..4179edb 100644
--- a/core/java/android/util/DisplayMetrics.java
+++ b/core/java/android/util/DisplayMetrics.java
@@ -103,40 +103,43 @@
/**
* Update the display metrics based on the compatibility info and orientation
+ * NOTE: DO NOT EXPOSE THIS API! It is introducing a circular dependency
+ * with the higher-level android.res package.
* {@hide}
*/
- public void updateMetrics(CompatibilityInfo compatibilityInfo, int orientation) {
+ public void updateMetrics(CompatibilityInfo compatibilityInfo, int orientation,
+ int screenLayout) {
int xOffset = 0;
if (!compatibilityInfo.isConfiguredExpandable()) {
// Note: this assume that configuration is updated before calling
// updateMetrics method.
- int defaultWidth;
- int defaultHeight;
- switch (orientation) {
- case Configuration.ORIENTATION_LANDSCAPE: {
- defaultWidth = (int)(CompatibilityInfo.DEFAULT_PORTRAIT_HEIGHT * density);
- defaultHeight = (int)(CompatibilityInfo.DEFAULT_PORTRAIT_WIDTH * density);
- break;
- }
- case Configuration.ORIENTATION_PORTRAIT:
- case Configuration.ORIENTATION_SQUARE:
- default: {
- defaultWidth = (int)(CompatibilityInfo.DEFAULT_PORTRAIT_WIDTH * density);
- defaultHeight = (int)(CompatibilityInfo.DEFAULT_PORTRAIT_HEIGHT * density);
- break;
- }
- case Configuration.ORIENTATION_UNDEFINED: {
- // don't change
- return;
- }
- }
-
- if (defaultWidth == widthPixels && defaultHeight == heightPixels) {
- // the screen size is same as expected size. make it expandable
- compatibilityInfo.setExpandable(true);
- } else {
+ if (screenLayout == Configuration.SCREENLAYOUT_LARGE) {
+ // This is a large screen device and the app is not
+ // compatible with large screens, to diddle it.
+
compatibilityInfo.setExpandable(false);
- // adjust the size only when the device's screen is bigger.
+ // Figure out the compatibility width and height of the screen.
+ int defaultWidth;
+ int defaultHeight;
+ switch (orientation) {
+ case Configuration.ORIENTATION_LANDSCAPE: {
+ defaultWidth = (int)(CompatibilityInfo.DEFAULT_PORTRAIT_HEIGHT * density);
+ defaultHeight = (int)(CompatibilityInfo.DEFAULT_PORTRAIT_WIDTH * density);
+ break;
+ }
+ case Configuration.ORIENTATION_PORTRAIT:
+ case Configuration.ORIENTATION_SQUARE:
+ default: {
+ defaultWidth = (int)(CompatibilityInfo.DEFAULT_PORTRAIT_WIDTH * density);
+ defaultHeight = (int)(CompatibilityInfo.DEFAULT_PORTRAIT_HEIGHT * density);
+ break;
+ }
+ case Configuration.ORIENTATION_UNDEFINED: {
+ // don't change
+ return;
+ }
+ }
+
if (defaultWidth < widthPixels) {
// content/window's x offset in original pixels
xOffset = ((widthPixels - defaultWidth) / 2);
@@ -145,6 +148,10 @@
if (defaultHeight < heightPixels) {
heightPixels = defaultHeight;
}
+
+ } else {
+ // the screen size is same as expected size. make it expandable
+ compatibilityInfo.setExpandable(true);
}
}
compatibilityInfo.setVisibleRect(xOffset, widthPixels, heightPixels);
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 @@
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 @@
}
/**
+ * <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>
+ *
+ * <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
- *
+ * @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 @@
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 @@
}
/**
+ * <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 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
final View child = children[i];
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
child.setDrawingCacheEnabled(true);
- child.buildDrawingCache();
+ child.buildDrawingCache(true);
}
}
@@ -1208,7 +1208,7 @@
bindLayoutAnimation(child);
if (cache) {
child.setDrawingCacheEnabled(true);
- child.buildDrawingCache();
+ child.buildDrawingCache(true);
}
}
}
@@ -1448,7 +1448,7 @@
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 @@
}
/**
- * The default implementation does nothing.
+ * The default implementation turns this into the enter key.
*/
public boolean performEditorAction(int actionCode) {
- return false;
+ long eventTime = SystemClock.uptimeMillis();
+ sendKeyEvent(new KeyEvent(eventTime, eventTime,
+ KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER, 0, 0, 0, 0,
+ KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE
+ | KeyEvent.FLAG_EDITOR_ACTION));
+ sendKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), eventTime,
+ KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER, 0, 0, 0, 0,
+ KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE
+ | KeyEvent.FLAG_EDITOR_ACTION));
+ return true;
}
/**
diff --git a/core/java/android/webkit/FrameLoader.java b/core/java/android/webkit/FrameLoader.java
index 6f1b160..66ab021 100644
--- a/core/java/android/webkit/FrameLoader.java
+++ b/core/java/android/webkit/FrameLoader.java
@@ -364,7 +364,7 @@
String cookie = CookieManager.getInstance().getCookie(
mListener.getWebAddress());
if (cookie != null && cookie.length() > 0) {
- mHeaders.put("cookie", cookie);
+ mHeaders.put("Cookie", cookie);
}
}
}
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index dcba943..ec671d5 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -69,7 +69,24 @@
}
int value;
}
-
+
+ /**
+ * Enum for specifying the WebView's desired density.
+ * FAR makes 100% looking like in 240dpi
+ * MEDIUM makes 100% looking like in 160dpi
+ * CLOSE makes 100% looking like in 120dpi
+ * @hide Pending API council approval
+ */
+ public enum ZoomDensity {
+ FAR(150), // 240dpi
+ MEDIUM(100), // 160dpi
+ CLOSE(75); // 120dpi
+ ZoomDensity(int size) {
+ value = size;
+ }
+ int value;
+ }
+
/**
* Default cache usage pattern Use with {@link #setCacheMode}.
*/
@@ -105,6 +122,8 @@
LOW
}
+ // WebView associated with this WebSettings.
+ private WebView mWebView;
// BrowserFrame used to access the native frame pointer.
private BrowserFrame mBrowserFrame;
// Flag to prevent multiple SYNC messages at one time.
@@ -145,6 +164,7 @@
// Don't need to synchronize the get/set methods as they
// are basic types, also none of these values are used in
// native WebCore code.
+ private ZoomDensity mDefaultZoom = ZoomDensity.MEDIUM;
private RenderPriority mRenderPriority = RenderPriority.NORMAL;
private int mOverrideCacheMode = LOAD_DEFAULT;
private boolean mSaveFormData = true;
@@ -237,9 +257,10 @@
* Package constructor to prevent clients from creating a new settings
* instance.
*/
- WebSettings(Context context) {
+ WebSettings(Context context, WebView webview) {
mEventHandler = new EventHandler();
mContext = context;
+ mWebView = webview;
mDefaultTextEncoding = context.getString(com.android.internal.
R.string.default_text_encoding);
@@ -447,6 +468,31 @@
}
/**
+ * Set the default zoom density of the page. This should be called from UI
+ * thread.
+ * @param zoom A ZoomDensity value
+ * @see WebSettings.ZoomDensity
+ * @hide Pending API council approval
+ */
+ public void setDefaultZoom(ZoomDensity zoom) {
+ if (mDefaultZoom != zoom) {
+ mDefaultZoom = zoom;
+ mWebView.updateDefaultZoomDensity(zoom.value);
+ }
+ }
+
+ /**
+ * Get the default zoom density of the page. This should be called from UI
+ * thread.
+ * @return A ZoomDensity value
+ * @see WebSettings.ZoomDensity
+ * @hide Pending API council approval
+ */
+ public ZoomDensity getDefaultZoom() {
+ return mDefaultZoom;
+ }
+
+ /**
* Enables using light touches to make a selection and activate mouseovers.
*/
public void setLightTouchEnabled(boolean enabled) {
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 1179c1c..429f0f9 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -408,7 +408,7 @@
// default scale. Depending on the display density.
static int DEFAULT_SCALE_PERCENT;
- private float DEFAULT_SCALE;
+ private float mDefaultScale;
// set to true temporarily while the zoom control is being dragged
private boolean mPreviewZoomOnly = false;
@@ -640,7 +640,7 @@
mZoomFitPageButton.setOnClickListener(
new View.OnClickListener() {
public void onClick(View v) {
- zoomWithPreview(DEFAULT_SCALE);
+ zoomWithPreview(mDefaultScale);
updateZoomButtonsEnabled();
}
});
@@ -663,7 +663,7 @@
// or out.
mZoomButtonsController.setZoomInEnabled(canZoomIn);
mZoomButtonsController.setZoomOutEnabled(canZoomOut);
- mZoomFitPageButton.setEnabled(mActualScale != DEFAULT_SCALE);
+ mZoomFitPageButton.setEnabled(mActualScale != mDefaultScale);
}
mZoomOverviewButton.setVisibility(canZoomScrollOut() ? View.VISIBLE:
View.GONE);
@@ -685,7 +685,7 @@
mNavSlop = (int) (16 * density);
// density adjusted scale factors
DEFAULT_SCALE_PERCENT = (int) (100 * density);
- DEFAULT_SCALE = density;
+ mDefaultScale = density;
mActualScale = density;
mInvActualScale = 1 / density;
DEFAULT_MAX_ZOOM_SCALE = 4.0f * density;
@@ -694,6 +694,23 @@
mMinZoomScale = DEFAULT_MIN_ZOOM_SCALE;
}
+ /* package */void updateDefaultZoomDensity(int zoomDensity) {
+ final float density = getContext().getResources().getDisplayMetrics().density
+ * 100 / zoomDensity;
+ if (Math.abs(density - mDefaultScale) > 0.01) {
+ float scaleFactor = density / mDefaultScale;
+ // adjust the limits
+ mNavSlop = (int) (16 * density);
+ DEFAULT_SCALE_PERCENT = (int) (100 * density);
+ DEFAULT_MAX_ZOOM_SCALE = 4.0f * density;
+ DEFAULT_MIN_ZOOM_SCALE = 0.25f * density;
+ mDefaultScale = density;
+ mMaxZoomScale *= scaleFactor;
+ mMinZoomScale *= scaleFactor;
+ setNewZoomScale(mActualScale * scaleFactor, false);
+ }
+ }
+
/* package */ boolean onSavePassword(String schemePlusHost, String username,
String password, final Message resumeMsg) {
boolean rVal = false;
@@ -4172,8 +4189,8 @@
float oldScale = mActualScale;
// snap to DEFAULT_SCALE if it is close
- if (scale > (DEFAULT_SCALE - 0.05) && scale < (DEFAULT_SCALE + 0.05)) {
- scale = DEFAULT_SCALE;
+ if (scale > (mDefaultScale - 0.05) && scale < (mDefaultScale + 0.05)) {
+ scale = mDefaultScale;
}
setNewZoomScale(scale, false);
@@ -4554,9 +4571,11 @@
break;
}
case SWITCH_TO_LONGPRESS: {
- mTouchMode = TOUCH_DONE_MODE;
- performLongClick();
- updateTextEntry();
+ if (!mPreventDrag) {
+ mTouchMode = TOUCH_DONE_MODE;
+ performLongClick();
+ updateTextEntry();
+ }
break;
}
case SWITCH_TO_ENTER:
@@ -4689,7 +4708,7 @@
int initialScale = msg.arg1;
int viewportWidth = msg.arg2;
// start a new page with DEFAULT_SCALE zoom scale.
- float scale = DEFAULT_SCALE;
+ float scale = mDefaultScale;
if (mInitialScale > 0) {
scale = mInitialScale / 100.0f;
} else {
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index e2efb43..a5fa41e 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -139,7 +139,7 @@
// ready.
mEventHub = new EventHub();
// Create a WebSettings object for maintaining all settings
- mSettings = new WebSettings(mContext);
+ mSettings = new WebSettings(mContext, mWebView);
// The WebIconDatabase needs to be initialized within the UI thread so
// just request the instance here.
WebIconDatabase.getInstance();
@@ -1560,6 +1560,20 @@
// set the viewport settings from WebKit
setViewportSettingsFromNative();
+ // adjust the default scale to match the density
+ if (WebView.DEFAULT_SCALE_PERCENT != 100) {
+ float adjust = (float) WebView.DEFAULT_SCALE_PERCENT / 100.0f;
+ if (mViewportInitialScale > 0) {
+ mViewportInitialScale *= adjust;
+ }
+ if (mViewportMinimumScale > 0) {
+ mViewportMinimumScale *= adjust;
+ }
+ if (mViewportMaximumScale > 0) {
+ mViewportMaximumScale *= adjust;
+ }
+ }
+
// infer the values if they are not defined.
if (mViewportWidth == 0) {
if (mViewportInitialScale == 0) {
@@ -1586,7 +1600,7 @@
mViewportMaximumScale = mViewportInitialScale;
} else if (mViewportInitialScale == 0) {
mViewportInitialScale = mViewportMaximumScale;
- }
+ }
}
if (mViewportWidth < 0
&& mViewportInitialScale == WebView.DEFAULT_SCALE_PERCENT) {
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 @@
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 955475e4..e62dda5 100644
--- a/core/java/android/widget/RelativeLayout.java
+++ b/core/java/android/widget/RelativeLayout.java
@@ -41,6 +41,7 @@
import java.util.TreeSet;
import java.util.LinkedList;
import java.util.HashSet;
+import java.util.ArrayList;
/**
* A Layout where the positions of the children can be described in relation to each other or to the
@@ -339,11 +340,17 @@
int right = Integer.MIN_VALUE;
int bottom = Integer.MIN_VALUE;
+ boolean offsetHorizontalAxis = false;
+ boolean offsetVerticalAxis = false;
+
if ((horizontalGravity || verticalGravity) && mIgnoreGravity != View.NO_ID) {
ignore = findViewById(mIgnoreGravity);
}
- View[] views = mSortedVerticalChildren;
+ final boolean isWrapContentWidth = widthMode != MeasureSpec.EXACTLY;
+ final boolean isWrapContentHeight = heightMode != MeasureSpec.EXACTLY;
+
+ View[] views = mSortedHorizontalChildren;
int count = views.length;
for (int i = 0; i < count; i++) {
View child = views[i];
@@ -351,13 +358,16 @@
LayoutParams params = (LayoutParams) child.getLayoutParams();
applyHorizontalSizeRules(params, myWidth);
- measureChildHorizontal(child, params, myWidth);
- positionChildHorizontal(child, params, myWidth);
+ measureChildHorizontal(child, params, myWidth, myHeight);
+ if (positionChildHorizontal(child, params, myWidth, isWrapContentWidth)) {
+ offsetHorizontalAxis = true;
+ }
}
}
- views = mSortedHorizontalChildren;
+ views = mSortedVerticalChildren;
count = views.length;
+
for (int i = 0; i < count; i++) {
View child = views[i];
if (child.getVisibility() != GONE) {
@@ -365,12 +375,15 @@
applyVerticalSizeRules(params, myHeight);
measureChild(child, params, myWidth, myHeight);
- positionChildVertical(child, params, myHeight);
+ if (positionChildVertical(child, params, myHeight, isWrapContentHeight)) {
+ offsetVerticalAxis = true;
+ }
- if (widthMode != MeasureSpec.EXACTLY) {
+ if (isWrapContentWidth) {
width = Math.max(width, params.mRight);
}
- if (heightMode != MeasureSpec.EXACTLY) {
+
+ if (isWrapContentHeight) {
height = Math.max(height, params.mBottom);
}
@@ -406,7 +419,7 @@
}
}
- if (widthMode != MeasureSpec.EXACTLY) {
+ if (isWrapContentWidth) {
// Width already has left padding in it since it was calculated by looking at
// the right of each child view
width += mPaddingRight;
@@ -417,8 +430,22 @@
width = Math.max(width, getSuggestedMinimumWidth());
width = resolveSize(width, widthMeasureSpec);
+
+ if (offsetHorizontalAxis) {
+ for (int i = 0; i < count; i++) {
+ View child = getChildAt(i);
+ if (child.getVisibility() != GONE) {
+ LayoutParams params = (LayoutParams) child.getLayoutParams();
+ final int[] rules = params.getRules();
+ if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_HORIZONTAL] != 0) {
+ centerHorizontal(child, params, width);
+ }
+ }
+ }
+ }
}
- if (heightMode != MeasureSpec.EXACTLY) {
+
+ if (isWrapContentHeight) {
// Height already has top padding in it since it was calculated by looking at
// the bottom of each child view
height += mPaddingBottom;
@@ -429,6 +456,19 @@
height = Math.max(height, getSuggestedMinimumHeight());
height = resolveSize(height, heightMeasureSpec);
+
+ if (offsetVerticalAxis) {
+ for (int i = 0; i < count; i++) {
+ View child = getChildAt(i);
+ if (child.getVisibility() != GONE) {
+ LayoutParams params = (LayoutParams) child.getLayoutParams();
+ final int[] rules = params.getRules();
+ if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_VERTICAL] != 0) {
+ centerVertical(child, params, height);
+ }
+ }
+ }
+ }
}
if (horizontalGravity || verticalGravity) {
@@ -510,13 +550,18 @@
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
- private void measureChildHorizontal(View child, LayoutParams params, int myWidth) {
+ private void measureChildHorizontal(View child, LayoutParams params, int myWidth, int myHeight) {
int childWidthMeasureSpec = getChildMeasureSpec(params.mLeft,
params.mRight, params.width,
params.leftMargin, params.rightMargin,
mPaddingLeft, mPaddingRight,
myWidth);
- int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+ int childHeightMeasureSpec;
+ if (params.width == LayoutParams.FILL_PARENT) {
+ childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(myHeight, MeasureSpec.EXACTLY);
+ } else {
+ childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(myHeight, MeasureSpec.AT_MOST);
+ }
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
@@ -599,7 +644,9 @@
return MeasureSpec.makeMeasureSpec(childSpecSize, childSpecMode);
}
- private void positionChildHorizontal(View child, LayoutParams params, int myWidth) {
+ private boolean positionChildHorizontal(View child, LayoutParams params, int myWidth,
+ boolean wrapContent) {
+
int[] rules = params.getRules();
if (params.mLeft < 0 && params.mRight >= 0) {
@@ -610,16 +657,25 @@
params.mRight = params.mLeft + child.getMeasuredWidth();
} else if (params.mLeft < 0 && params.mRight < 0) {
// Both left and right vary
- if (0 != rules[CENTER_IN_PARENT] || 0 != rules[CENTER_HORIZONTAL]) {
- centerHorizontal(child, params, myWidth);
+ if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_HORIZONTAL] != 0) {
+ if (!wrapContent) {
+ centerHorizontal(child, params, myWidth);
+ } else {
+ params.mLeft = mPaddingLeft + params.leftMargin;
+ params.mRight = params.mLeft + child.getMeasuredWidth();
+ }
+ return true;
} else {
params.mLeft = mPaddingLeft + params.leftMargin;
params.mRight = params.mLeft + child.getMeasuredWidth();
}
}
+ return false;
}
- private void positionChildVertical(View child, LayoutParams params, int myHeight) {
+ private boolean positionChildVertical(View child, LayoutParams params, int myHeight,
+ boolean wrapContent) {
+
int[] rules = params.getRules();
if (params.mTop < 0 && params.mBottom >= 0) {
@@ -630,13 +686,20 @@
params.mBottom = params.mTop + child.getMeasuredHeight();
} else if (params.mTop < 0 && params.mBottom < 0) {
// Both top and bottom vary
- if (0 != rules[CENTER_IN_PARENT] || 0 != rules[CENTER_VERTICAL]) {
- centerVertical(child, params, myHeight);
+ if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_VERTICAL] != 0) {
+ if (!wrapContent) {
+ centerVertical(child, params, myHeight);
+ } else {
+ params.mTop = mPaddingTop + params.topMargin;
+ params.mBottom = params.mTop + child.getMeasuredHeight();
+ }
+ return true;
} else {
params.mTop = mPaddingTop + params.topMargin;
params.mBottom = params.mTop + child.getMeasuredHeight();
}
}
+ return false;
}
private void applyHorizontalSizeRules(LayoutParams childParams, int myWidth) {
@@ -766,14 +829,14 @@
private View getRelatedView(int[] rules, int relation) {
int id = rules[relation];
if (id != 0) {
- DependencyGraph.Node node = mGraph.mNodes.get(id);
+ DependencyGraph.Node node = mGraph.mKeyNodes.get(id);
if (node == null) return null;
View v = node.view;
// Find the first non-GONE view up the chain
while (v.getVisibility() == View.GONE) {
rules = ((LayoutParams) v.getLayoutParams()).getRules();
- node = mGraph.mNodes.get((rules[relation]));
+ node = mGraph.mKeyNodes.get((rules[relation]));
if (node == null) return null;
v = node.view;
}
@@ -1109,10 +1172,15 @@
private static class DependencyGraph {
/**
+ * List of all views in the graph.
+ */
+ private ArrayList<Node> mNodes = new ArrayList<Node>();
+
+ /**
* List of nodes in the graph. Each node is identified by its
* view id (see View#getId()).
*/
- private SparseArray<Node> mNodes = new SparseArray<Node>();
+ private SparseArray<Node> mKeyNodes = new SparseArray<Node>();
/**
* Temporary data structure used to build the list of roots
@@ -1124,14 +1192,15 @@
* Clears the graph.
*/
void clear() {
- final SparseArray<Node> nodes = mNodes;
+ final ArrayList<Node> nodes = mNodes;
final int count = nodes.size();
for (int i = 0; i < count; i++) {
- nodes.valueAt(i).release();
+ nodes.get(i).release();
}
nodes.clear();
+ mKeyNodes.clear();
mRoots.clear();
}
@@ -1141,7 +1210,14 @@
* @param view The view to be added as a node to the graph.
*/
void add(View view) {
- mNodes.put(view.getId(), Node.acquire(view));
+ final int id = view.getId();
+ final Node node = Node.acquire(view);
+
+ if (id != View.NO_ID) {
+ mKeyNodes.put(id, node);
+ }
+
+ mNodes.add(node);
}
/**
@@ -1192,20 +1268,21 @@
* @return A list of node, each being a root of the graph
*/
private LinkedList<Node> findRoots(int[] rulesFilter) {
- final SparseArray<Node> nodes = mNodes;
+ final SparseArray<Node> keyNodes = mKeyNodes;
+ final ArrayList<Node> nodes = mNodes;
final int count = nodes.size();
// Find roots can be invoked several times, so make sure to clear
// all dependents and dependencies before running the algorithm
for (int i = 0; i < count; i++) {
- final Node node = nodes.valueAt(i);
+ final Node node = nodes.get(i);
node.dependents.clear();
node.dependencies.clear();
}
// Builds up the dependents and dependencies for each node of the graph
for (int i = 0; i < count; i++) {
- final Node node = nodes.valueAt(i);
+ final Node node = nodes.get(i);
final LayoutParams layoutParams = (LayoutParams) node.view.getLayoutParams();
final int[] rules = layoutParams.mRules;
@@ -1217,11 +1294,14 @@
final int rule = rules[rulesFilter[j]];
if (rule > 0) {
// The node this node depends on
- final Node dependency = nodes.get(rule);
+ final Node dependency = keyNodes.get(rule);
if (dependency == node) {
throw new IllegalStateException("A view cannot have a dependency" +
" on itself");
}
+ if (dependency == null) {
+ continue;
+ }
// Add the current node as a dependent
dependency.dependents.add(node);
// Add a dependency to the current node
@@ -1235,7 +1315,7 @@
// Finds all the roots in the graph: all nodes with no dependencies
for (int i = 0; i < count; i++) {
- final Node node = nodes.valueAt(i);
+ final Node node = nodes.get(i);
if (node.dependencies.size() == 0) roots.add(node);
}
@@ -1323,7 +1403,9 @@
/*
* START POOL IMPLEMENTATION
*/
- private static final int POOL_LIMIT = 12;
+ // The pool is static, so all nodes instances are shared across
+ // activities, that's why we give it a rather high limit
+ private static final int POOL_LIMIT = 100;
private static final Pool<Node> sPool = Pools.synchronizedPool(
Pools.finitePool(new PoolableManager<Node>() {
public Node newInstance() {
diff --git a/core/java/com/android/internal/backup/IBackupTransport.aidl b/core/java/com/android/internal/backup/IBackupTransport.aidl
index ec63528..4bef265 100644
--- a/core/java/com/android/internal/backup/IBackupTransport.aidl
+++ b/core/java/com/android/internal/backup/IBackupTransport.aidl
@@ -41,6 +41,20 @@
- adb: close the file
*/
/**
+ * Ask the transport where, on local device storage, to keep backup state blobs.
+ * This is per-transport so that mock transports used for testing can coexist with
+ * "live" backup services without interfering with the live bookkeeping. The
+ * returned string should be a name that is expected to be unambiguous among all
+ * available backup transports; the name of the class implementing the transport
+ * is a good choice.
+ *
+ * @return A unique name, suitable for use as a file or directory name, that the
+ * Backup Manager could use to disambiguate state files associated with
+ * different backup transports.
+ */
+ String transportDirName();
+
+ /**
* Verify that this is a suitable time for a backup pass. This should return zero
* if a backup is reasonable right now, some positive value otherwise. This method
* will be called outside of the {@link #startSession}/{@link #endSession} pair.
diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java
index 0fbbb3f..c5d9d403 100644
--- a/core/java/com/android/internal/backup/LocalTransport.java
+++ b/core/java/com/android/internal/backup/LocalTransport.java
@@ -30,6 +30,9 @@
private static final String TAG = "LocalTransport";
private static final boolean DEBUG = true;
+ private static final String TRANSPORT_DIR_NAME
+ = "com.android.internal.backup.LocalTransport";
+
private Context mContext;
private PackageManager mPackageManager;
private File mDataDir = new File(Environment.getDownloadCacheDirectory(), "backup");
@@ -43,6 +46,11 @@
mPackageManager = context.getPackageManager();
}
+
+ public String transportDirName() throws RemoteException {
+ return TRANSPORT_DIR_NAME;
+ }
+
public long requestBackupTime() throws RemoteException {
// any time is a good time for local backup
return 0;
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index fc4a9c4..a03802d 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -2836,14 +2836,12 @@
* @param name process name
* @return the statistics object for the process
*/
- public Uid.Proc getProcessStatsLocked(String name) {
+ public Uid.Proc getProcessStatsLocked(String name, int pid) {
int uid;
if (mUidCache.containsKey(name)) {
uid = mUidCache.get(name);
} else {
- // TODO: Find the actual uid from /proc/pid/status. For now use the hashcode of the
- // process name
- uid = name.hashCode();
+ uid = Process.getUidForPid(pid);
mUidCache.put(name, uid);
}
Uid u = getUidStatsLocked(uid);
diff --git a/core/java/com/android/internal/util/BitwiseInputStream.java b/core/java/com/android/internal/util/BitwiseInputStream.java
index 4757919..86f74f3 100644
--- a/core/java/com/android/internal/util/BitwiseInputStream.java
+++ b/core/java/com/android/internal/util/BitwiseInputStream.java
@@ -65,30 +65,31 @@
/**
* 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 byte read(int bits) throws AccessException {
+ public int read(int bits) throws AccessException {
int index = mPos >>> 3;
int offset = 16 - (mPos & 0x07) - bits; // &7==%8
if ((bits < 0) || (bits > 8) || ((mPos + bits) > mEnd)) {
throw new AccessException("illegal read " +
"(pos " + mPos + ", end " + mEnd + ", bits " + bits + ")");
}
- int data = (mBuf[index] & 0x00FF) << 8;
- if (offset < 8) data |= (mBuf[index + 1] & 0xFF);
+ int data = (mBuf[index] & 0xFF) << 8;
+ if (offset < 8) data |= mBuf[index + 1] & 0xFF;
data >>>= offset;
data &= (-1 >>> (32 - bits));
mPos += bits;
- return (byte)data;
+ return data;
}
/**
* 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 1b974ce..70c0be8 100644
--- a/core/java/com/android/internal/util/BitwiseOutputStream.java
+++ b/core/java/com/android/internal/util/BitwiseOutputStream.java
@@ -82,6 +82,9 @@
/**
* 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
*/
@@ -95,8 +98,8 @@
int offset = 16 - (mPos & 0x07) - bits; // &7==%8
data <<= offset;
mPos += bits;
- mBuf[index] |= (data >>> 8);
- if (offset < 8) mBuf[index + 1] |= (data & 0x00FF);
+ mBuf[index] |= data >>> 8;
+ if (offset < 8) mBuf[index + 1] |= data & 0xFF;
}
/**
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 80c2489..888cb11 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -19,6 +19,7 @@
ActivityManager.cpp \
AndroidRuntime.cpp \
CursorWindow.cpp \
+ Time.cpp \
com_google_android_gles_jni_EGLImpl.cpp \
com_google_android_gles_jni_GLImpl.cpp.arm \
android_opengl_GLES10.cpp \
@@ -119,7 +120,8 @@
com_android_internal_graphics_NativeUtils.cpp \
android_backup_BackupDataInput.cpp \
android_backup_BackupDataOutput.cpp \
- android_backup_FileBackupHelperBase.cpp
+ android_backup_FileBackupHelperBase.cpp \
+ android_backup_BackupHelperDispatcher.cpp
LOCAL_C_INCLUDES += \
$(JNI_H_INCLUDE) \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 7350348..c815301 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -158,6 +158,7 @@
extern int register_android_backup_BackupDataInput(JNIEnv *env);
extern int register_android_backup_BackupDataOutput(JNIEnv *env);
extern int register_android_backup_FileBackupHelperBase(JNIEnv *env);
+extern int register_android_backup_BackupHelperDispatcher(JNIEnv *env);
static AndroidRuntime* gCurRuntime = NULL;
@@ -1131,6 +1132,7 @@
REG_JNI(register_android_backup_BackupDataInput),
REG_JNI(register_android_backup_BackupDataOutput),
REG_JNI(register_android_backup_FileBackupHelperBase),
+ REG_JNI(register_android_backup_BackupHelperDispatcher),
};
/*
diff --git a/libs/ui/Time.cpp b/core/jni/Time.cpp
similarity index 98%
rename from libs/ui/Time.cpp
rename to core/jni/Time.cpp
index b5539135..f3037f3 100644
--- a/libs/ui/Time.cpp
+++ b/core/jni/Time.cpp
@@ -1,4 +1,4 @@
-#include <utils/TimeUtils.h>
+#include "TimeUtils.h"
#include <stdio.h>
#include <cutils/tztime.h>
diff --git a/include/utils/TimeUtils.h b/core/jni/TimeUtils.h
similarity index 100%
rename from include/utils/TimeUtils.h
rename to core/jni/TimeUtils.h
diff --git a/core/jni/android_backup_BackupHelperDispatcher.cpp b/core/jni/android_backup_BackupHelperDispatcher.cpp
new file mode 100644
index 0000000..2e3f0b9
--- /dev/null
+++ b/core/jni/android_backup_BackupHelperDispatcher.cpp
@@ -0,0 +1,259 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "BackupHelperDispatcher_native"
+#include <utils/Log.h>
+
+#include "JNIHelp.h"
+#include <android_runtime/AndroidRuntime.h>
+
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <unistd.h>
+
+
+#define VERSION_1_HEADER 0x01706c48 // 'Hlp'1 little endian
+
+namespace android
+{
+
+struct chunk_header_v1 {
+ int headerSize;
+ int version;
+ int dataSize; // corresponds to Header.chunkSize
+ int nameLength; // not including the NULL terminator, which is not written to the file
+};
+
+// java.io.FileDescriptor
+static jfieldID s_descriptorField = 0;
+static jfieldID s_chunkSizeField = 0;
+static jfieldID s_keyPrefixField = 0;
+
+static int
+readHeader_native(JNIEnv* env, jobject clazz, jobject headerObj, jobject fdObj)
+{
+ chunk_header_v1 flattenedHeader;
+ int fd;
+ ssize_t amt;
+ String8 keyPrefix;
+ char* buf;
+
+ fd = env->GetIntField(fdObj, s_descriptorField);
+
+ amt = read(fd, &flattenedHeader.headerSize, sizeof(flattenedHeader.headerSize));
+ if (amt != sizeof(flattenedHeader.headerSize)) {
+ return -1;
+ }
+
+ int remainingHeader = flattenedHeader.headerSize - sizeof(flattenedHeader.headerSize);
+
+ if (flattenedHeader.headerSize < (int)sizeof(chunk_header_v1)) {
+ LOGW("Skipping unknown header: %d bytes", flattenedHeader.headerSize);
+ if (remainingHeader > 0) {
+ lseek(fd, remainingHeader, SEEK_CUR);
+ // >0 means skip this chunk
+ return 1;
+ }
+ }
+
+ amt = read(fd, &flattenedHeader.version,
+ sizeof(chunk_header_v1)-sizeof(flattenedHeader.headerSize));
+ if (amt <= 0) {
+ LOGW("Failed reading chunk header");
+ return -1;
+ }
+ remainingHeader -= sizeof(chunk_header_v1)-sizeof(flattenedHeader.headerSize);
+
+ if (flattenedHeader.version != VERSION_1_HEADER) {
+ LOGW("Skipping unknown header version: 0x%08x, %d bytes", flattenedHeader.version,
+ flattenedHeader.headerSize);
+ if (remainingHeader > 0) {
+ lseek(fd, remainingHeader, SEEK_CUR);
+ // >0 means skip this chunk
+ return 1;
+ }
+ }
+
+#if 0
+ LOGD("chunk header:");
+ LOGD(" headerSize=%d", flattenedHeader.headerSize);
+ LOGD(" version=0x%08x", flattenedHeader.version);
+ LOGD(" dataSize=%d", flattenedHeader.dataSize);
+ LOGD(" nameLength=%d", flattenedHeader.nameLength);
+#endif
+
+ if (flattenedHeader.dataSize < 0 || flattenedHeader.nameLength < 0 ||
+ remainingHeader < flattenedHeader.nameLength) {
+ LOGW("Malformed V1 header remainingHeader=%d dataSize=%d nameLength=%d", remainingHeader,
+ flattenedHeader.dataSize, flattenedHeader.nameLength);
+ return -1;
+ }
+
+ buf = keyPrefix.lockBuffer(flattenedHeader.nameLength);
+ if (buf == NULL) {
+ LOGW("unable to allocate %d bytes", flattenedHeader.nameLength);
+ return -1;
+ }
+
+ amt = read(fd, buf, flattenedHeader.nameLength);
+ buf[flattenedHeader.nameLength] = 0;
+
+ keyPrefix.unlockBuffer(flattenedHeader.nameLength);
+
+ remainingHeader -= flattenedHeader.nameLength;
+
+ if (remainingHeader > 0) {
+ lseek(fd, remainingHeader, SEEK_CUR);
+ }
+
+ env->SetIntField(headerObj, s_chunkSizeField, flattenedHeader.dataSize);
+ env->SetObjectField(headerObj, s_keyPrefixField, env->NewStringUTF(keyPrefix.string()));
+
+ return 0;
+}
+
+static int
+skipChunk_native(JNIEnv* env, jobject clazz, jobject fdObj, jint bytesToSkip)
+{
+ int fd;
+
+ fd = env->GetIntField(fdObj, s_descriptorField);
+
+ lseek(fd, bytesToSkip, SEEK_CUR);
+
+ return 0;
+}
+
+static int
+padding_len(int len)
+{
+ len = len % 4;
+ return len == 0 ? len : 4 - len;
+}
+
+static int
+allocateHeader_native(JNIEnv* env, jobject clazz, jobject headerObj, jobject fdObj)
+{
+ int pos;
+ jstring nameObj;
+ int nameLength;
+ int namePadding;
+ int headerSize;
+ int fd;
+
+ fd = env->GetIntField(fdObj, s_descriptorField);
+
+ nameObj = (jstring)env->GetObjectField(headerObj, s_keyPrefixField);
+
+ nameLength = env->GetStringUTFLength(nameObj);
+ namePadding = padding_len(nameLength);
+
+ headerSize = sizeof(chunk_header_v1) + nameLength + namePadding;
+
+ pos = lseek(fd, 0, SEEK_CUR);
+
+ lseek(fd, headerSize, SEEK_CUR);
+
+ return pos;
+}
+
+static int
+writeHeader_native(JNIEnv* env, jobject clazz, jobject headerObj, jobject fdObj, jint pos)
+{
+ int err;
+ chunk_header_v1 header;
+ int fd;
+ int namePadding;
+ int prevPos;
+ jstring nameObj;
+ const char* buf;
+
+ fd = env->GetIntField(fdObj, s_descriptorField);
+ prevPos = lseek(fd, 0, SEEK_CUR);
+
+ nameObj = (jstring)env->GetObjectField(headerObj, s_keyPrefixField);
+ header.nameLength = env->GetStringUTFLength(nameObj);
+ namePadding = padding_len(header.nameLength);
+
+ header.headerSize = sizeof(chunk_header_v1) + header.nameLength + namePadding;
+ header.version = VERSION_1_HEADER;
+ header.dataSize = prevPos - (pos + header.headerSize);
+
+ lseek(fd, pos, SEEK_SET);
+ err = write(fd, &header, sizeof(chunk_header_v1));
+ if (err != sizeof(chunk_header_v1)) {
+ return errno;
+ }
+
+ buf = env->GetStringUTFChars(nameObj, NULL);
+ err = write(fd, buf, header.nameLength);
+ env->ReleaseStringUTFChars(nameObj, buf);
+ if (err != header.nameLength) {
+ return errno;
+ }
+
+ if (namePadding != 0) {
+ int zero = 0;
+ err = write(fd, &zero, namePadding);
+ if (err != namePadding) {
+ return errno;
+ }
+ }
+
+ lseek(fd, prevPos, SEEK_SET);
+ return 0;
+}
+
+static const JNINativeMethod g_methods[] = {
+ { "readHeader_native",
+ "(Landroid/backup/BackupHelperDispatcher$Header;Ljava/io/FileDescriptor;)I",
+ (void*)readHeader_native },
+ { "skipChunk_native",
+ "(Ljava/io/FileDescriptor;I)I",
+ (void*)skipChunk_native },
+ { "allocateHeader_native",
+ "(Landroid/backup/BackupHelperDispatcher$Header;Ljava/io/FileDescriptor;)I",
+ (void*)allocateHeader_native },
+ { "writeHeader_native",
+ "(Landroid/backup/BackupHelperDispatcher$Header;Ljava/io/FileDescriptor;I)I",
+ (void*)writeHeader_native },
+};
+
+int register_android_backup_BackupHelperDispatcher(JNIEnv* env)
+{
+ jclass clazz;
+
+ clazz = env->FindClass("java/io/FileDescriptor");
+ LOG_FATAL_IF(clazz == NULL, "Unable to find class java.io.FileDescriptor");
+ s_descriptorField = env->GetFieldID(clazz, "descriptor", "I");
+ LOG_FATAL_IF(s_descriptorField == NULL,
+ "Unable to find descriptor field in java.io.FileDescriptor");
+
+ clazz = env->FindClass("android/backup/BackupHelperDispatcher$Header");
+ LOG_FATAL_IF(clazz == NULL,
+ "Unable to find class android.backup.BackupHelperDispatcher.Header");
+ s_chunkSizeField = env->GetFieldID(clazz, "chunkSize", "I");
+ LOG_FATAL_IF(s_chunkSizeField == NULL,
+ "Unable to find chunkSize field in android.backup.BackupHelperDispatcher.Header");
+ s_keyPrefixField = env->GetFieldID(clazz, "keyPrefix", "Ljava/lang/String;");
+ LOG_FATAL_IF(s_keyPrefixField == NULL,
+ "Unable to find keyPrefix field in android.backup.BackupHelperDispatcher.Header");
+
+ return AndroidRuntime::registerNativeMethods(env, "android/backup/BackupHelperDispatcher",
+ g_methods, NELEM(g_methods));
+}
+
+}
diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp
index b07ba7d..3550716 100644
--- a/core/jni/android_hardware_Camera.cpp
+++ b/core/jni/android_hardware_Camera.cpp
@@ -125,37 +125,8 @@
return;
}
JNIEnv *env = AndroidRuntime::getJNIEnv();
-
- // parse message
- switch (msgType) {
- case CAMERA_MSG_ERROR:
- LOGV("errorCallback");
- int error;
- switch (ext1) {
- case DEAD_OBJECT:
- error = kCameraErrorMediaServer;
- break;
- default:
- error = kCameraErrorUnknown;
- break;
- }
- env->CallStaticVoidMethod(mCameraJClass, fields.post_event,
- mCameraJObjectWeak, kErrorCallback, error, 0, NULL);
- break;
- case CAMERA_MSG_FOCUS:
- LOGV("autoFocusCallback");
- env->CallStaticVoidMethod(mCameraJClass, fields.post_event,
- mCameraJObjectWeak, kAutoFocusCallback, ext1, 0, NULL);
- break;
- case CAMERA_MSG_SHUTTER:
- LOGV("shutterCallback");
- env->CallStaticVoidMethod(mCameraJClass, fields.post_event,
- mCameraJObjectWeak, kShutterCallback, 0, 0, NULL);
- break;
- default:
- LOGV("notifyCallback(%d, %d, %d)", msgType, ext1, ext2);
- break;
- }
+ env->CallStaticVoidMethod(mCameraJClass, fields.post_event,
+ mCameraJObjectWeak, msgType, ext1, ext2);
}
void JNICameraContext::copyAndPost(JNIEnv* env, const sp<IMemory>& dataPtr, int msgType)
@@ -203,27 +174,20 @@
// return data based on callback type
switch(msgType) {
- case CAMERA_MSG_PREVIEW_FRAME:
- LOGV("previewCallback");
- copyAndPost(env, dataPtr, kPreviewCallback);
- break;
case CAMERA_MSG_VIDEO_FRAME:
- LOGV("recordingCallback");
+ // should never happen
break;
+ // don't return raw data to Java
case CAMERA_MSG_RAW_IMAGE:
LOGV("rawCallback");
env->CallStaticVoidMethod(mCameraJClass, fields.post_event,
- mCameraJObjectWeak, kRawCallback, 0, 0, NULL);
- break;
- case CAMERA_MSG_COMPRESSED_IMAGE:
- LOGV("jpegCallback");
- copyAndPost(env, dataPtr, kJpegCallback);
+ mCameraJObjectWeak, msgType, 0, 0, NULL);
break;
default:
LOGV("dataCallback(%d, %p)", msgType, dataPtr.get());
+ copyAndPost(env, dataPtr, msgType);
break;
}
-
}
// connect to camera service
@@ -298,7 +262,10 @@
sp<Camera> camera = get_native_camera(env, thiz, NULL);
if (camera == 0) return;
- sp<Surface> surface = reinterpret_cast<Surface*>(env->GetIntField(jSurface, fields.surface));
+ sp<Surface> surface = NULL;
+ if (jSurface != NULL) {
+ surface = reinterpret_cast<Surface*>(env->GetIntField(jSurface, fields.surface));
+ }
if (camera->setPreviewDisplay(surface) != NO_ERROR) {
jniThrowException(env, "java/io/IOException", "setPreviewDisplay failed");
}
diff --git a/core/jni/android_text_format_Time.cpp b/core/jni/android_text_format_Time.cpp
index 7c208e9..98f4e03 100644
--- a/core/jni/android_text_format_Time.cpp
+++ b/core/jni/android_text_format_Time.cpp
@@ -23,7 +23,7 @@
#include "jni.h"
#include "utils/misc.h"
#include "android_runtime/AndroidRuntime.h"
-#include <utils/TimeUtils.h>
+#include "TimeUtils.h"
#include <nativehelper/JNIHelp.h>
#include <cutils/tztime.h>
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index d147bcc..2d90ba4 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -535,7 +535,7 @@
jint keyboard, jint keyboardHidden,
jint navigation,
jint screenWidth, jint screenHeight,
- jint sdkVersion)
+ jint screenLayout, jint sdkVersion)
{
AssetManager* am = assetManagerForJavaObject(env, clazz);
if (am == NULL) {
@@ -557,6 +557,7 @@
config.navigation = (uint8_t)navigation;
config.screenWidth = (uint16_t)screenWidth;
config.screenHeight = (uint16_t)screenHeight;
+ config.screenLayout = (uint8_t)screenLayout;
config.sdkVersion = (uint16_t)sdkVersion;
config.minorVersion = 0;
am->setConfiguration(config, locale8);
@@ -1567,7 +1568,7 @@
(void*) android_content_AssetManager_setLocale },
{ "getLocales", "()[Ljava/lang/String;",
(void*) android_content_AssetManager_getLocales },
- { "setConfiguration", "(IILjava/lang/String;IIIIIIIII)V",
+ { "setConfiguration", "(IILjava/lang/String;IIIIIIIIII)V",
(void*) android_content_AssetManager_setConfiguration },
{ "getResourceIdentifier","(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
(void*) android_content_AssetManager_getResourceIdentifier },
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index 945a325..aee0ed7 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -269,9 +269,9 @@
void android_os_Process_setThreadPriority(JNIEnv* env, jobject clazz,
jint pid, jint pri)
{
- if (pri == ANDROID_PRIORITY_BACKGROUND) {
+ if (pri >= ANDROID_PRIORITY_BACKGROUND) {
add_pid_to_cgroup(pid, ANDROID_TGROUP_BG_NONINTERACT);
- } else if (getpriority(PRIO_PROCESS, pid) == ANDROID_PRIORITY_BACKGROUND) {
+ } else if (getpriority(PRIO_PROCESS, pid) >= ANDROID_PRIORITY_BACKGROUND) {
add_pid_to_cgroup(pid, ANDROID_TGROUP_DEFAULT);
}
@@ -496,7 +496,7 @@
const String8& field = fields[i];
if (strncmp(p, field.string(), field.length()) == 0) {
p += field.length();
- while (*p == ' ') p++;
+ while (*p == ' ' || *p == '\t') p++;
char* num = p;
while (*p >= '0' && *p <= '9') p++;
skipToEol = *p != '\n';
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
deleted file mode 100644
index 7c637fd..0000000
--- a/core/res/res/drawable/progress_circular_background.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/progress_circular_background_small.png b/core/res/res/drawable/progress_circular_background_small.png
deleted file mode 100644
index 6b8ba9b..0000000
--- a/core/res/res/drawable/progress_circular_background_small.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/progress_circular_indeterminate.png b/core/res/res/drawable/progress_circular_indeterminate.png
deleted file mode 100644
index 125a264..0000000
--- a/core/res/res/drawable/progress_circular_indeterminate.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/progress_indeterminate.xml b/core/res/res/drawable/progress_indeterminate.xml
deleted file mode 100644
index 1bf715e5..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
deleted file mode 100644
index 9160108..0000000
--- a/core/res/res/drawable/progress_particle.png
+++ /dev/null
Binary files differ
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
deleted file mode 100755
index 9611d97..0000000
--- a/core/res/res/drawable/search_spinner_anim10.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/search_spinner_anim11.png b/core/res/res/drawable/search_spinner_anim11.png
deleted file mode 100755
index 4261704..0000000
--- a/core/res/res/drawable/search_spinner_anim11.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/search_spinner_anim12.png b/core/res/res/drawable/search_spinner_anim12.png
deleted file mode 100755
index 0602314..0000000
--- a/core/res/res/drawable/search_spinner_anim12.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/search_spinner_anim2.png b/core/res/res/drawable/search_spinner_anim2.png
deleted file mode 100755
index 05d58e0..0000000
--- a/core/res/res/drawable/search_spinner_anim2.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/search_spinner_anim3.png b/core/res/res/drawable/search_spinner_anim3.png
deleted file mode 100755
index 69fa9c1..0000000
--- a/core/res/res/drawable/search_spinner_anim3.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/search_spinner_anim4.png b/core/res/res/drawable/search_spinner_anim4.png
deleted file mode 100755
index 9201bac..0000000
--- a/core/res/res/drawable/search_spinner_anim4.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/search_spinner_anim5.png b/core/res/res/drawable/search_spinner_anim5.png
deleted file mode 100755
index f0c7101..0000000
--- a/core/res/res/drawable/search_spinner_anim5.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/search_spinner_anim6.png b/core/res/res/drawable/search_spinner_anim6.png
deleted file mode 100755
index 99d1d4e..0000000
--- a/core/res/res/drawable/search_spinner_anim6.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/search_spinner_anim7.png b/core/res/res/drawable/search_spinner_anim7.png
deleted file mode 100755
index 8ca3358..0000000
--- a/core/res/res/drawable/search_spinner_anim7.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/search_spinner_anim8.png b/core/res/res/drawable/search_spinner_anim8.png
deleted file mode 100755
index 408d723..0000000
--- a/core/res/res/drawable/search_spinner_anim8.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/search_spinner_anim9.png b/core/res/res/drawable/search_spinner_anim9.png
deleted file mode 100755
index 42a2c65..0000000
--- a/core/res/res/drawable/search_spinner_anim9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/search_spinner_anim1.png b/core/res/res/drawable/spinner_black_20.png
similarity index 100%
rename from core/res/res/drawable/search_spinner_anim1.png
rename to core/res/res/drawable/spinner_black_20.png
Binary files differ
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/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 91cd9fd..7571e24 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -512,6 +512,9 @@
<!-- The screen orientation has changed, that is the user has
rotated the device. -->
<flag name="orientation" value="0x0080" />
+ <!-- The screen orientation has changed, that is the user has
+ rotated the device. -->
+ <flag name="screenLayout" value="0x0100" />
<!-- The font scaling factor has changed, that is the user has
selected a new global font size. -->
<flag name="fontScale" value="0x40000000" />
@@ -829,8 +832,59 @@
<p>This appears as a child tag of the
{@link #AndroidManifest manifest} tag. -->
<declare-styleable name="AndroidManifestSupportsDensity" parent="AndroidManifest">
- <!-- Required value of the density in dip (device independent pixel). -->
- <attr name="density" format="integer" />
+ <!-- Required value of the density in dip (device independent pixel).
+ You should use one of the pre-defined constants for the standard
+ screen densities defined here.
+ -->
+ <attr name="density" format="integer">
+ <!-- A low density screen, such as a QVGA or WQVGA screen in a
+ typical hand-held phone. The constant for this is 120. -->
+ <enum name="low" value="120" />
+ <!-- A medium density screen, such as an HVGA screen in a
+ typical hand-held phone. The constant for this is 160. -->
+ <enum name="medium" value="160" />
+ <!-- A high density screen, such as a VGA or WVGA screen in a
+ typical hand-held phone. The constant for this is 240. -->
+ <enum name="high" value="240" />
+ </attr>
+ </declare-styleable>
+
+ <!-- The <code>supports-screens</code> specifies the screen dimensions an
+ application supports. By default a modern application supports all
+ screen sizes and must explicitly disable certain screen sizes here;
+ older applications are assumed to only support the traditional normal
+ (HVGA) screen size. Note that screen size is a separate axis from
+ density, and is determined as the available pixels to an application
+ after density scaling has been applied.
+
+ <p>This appears as a child tag of the
+ {@link #AndroidManifest manifest} tag. -->
+ <declare-styleable name="AndroidManifestSupportsScreens" parent="AndroidManifest">
+ <!-- Indicates whether the application supports smaller screen form-factors.
+ A small screen is defined as one with a smaller aspect ratio than
+ the traditional HVGA screen; that is, for a portrait screen, less
+ tall than an HVGA screen. In practice, this means a QVGA low
+ density or VGA high density screen. An application that does
+ not support small screens <em>will not be available</em> for
+ small screen devices, since there is little the platform can do
+ to make such an application work on a smaller screen. -->
+ <attr name="smallScreens" format="boolean" />
+ <!-- Indicates whether an application supports the normal screen
+ form-factors. Traditionally this is an HVGA normal density
+ screen, but WQVGA low density and WVGA high density are also
+ considered to be normal. This attribute is true by default,
+ and applications currently should leave it that way. -->
+ <attr name="normalScreens" format="boolean" />
+ <!-- Indicates whether the application supports larger screen form-factors.
+ A large screen is defined as a screen that is significantly larger
+ than a normal phone screen, and thus may require some special care
+ on the application's part to make good use of it. An example would
+ be a VGA <em>normal density</em> screen, though even larger screens
+ are certainly possible. An application that does not support
+ large screens will be placed as a postage stamp on such a
+ screen, so that it retains the dimensions it was originally
+ designed for. -->
+ <attr name="largeScreens" format="boolean" />
</declare-styleable>
<!-- The <code>expandable</code> specifies if this package supports screen metrics
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 4634b50..14d8dbe 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1117,6 +1117,9 @@
<public type="attr" name="glEsVersion" />
<public type="attr" name="queryAfterZeroResults" />
<public type="attr" name="dropDownHeight" />
+ <public type="attr" name="smallScreens" />
+ <public type="attr" name="normalScreens" />
+ <public type="attr" name="largeScreens" />
<public-padding type="attr" name="donut_resource_pad" end="0x0101029f" />
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 @@
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 @@
@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 8486532..21cb73b 100644
--- a/include/tts/TtsEngine.h
+++ b/include/tts/TtsEngine.h
@@ -69,6 +69,14 @@
TTS_MISSING_RESOURCES = -6
};
+enum tts_support_result {
+ TTS_LANG_COUNTRY_VAR_AVAILABLE = 2,
+ TTS_LANG_COUNTRY_AVAILABLE = 1,
+ TTS_LANG_AVAILABLE = 0,
+ TTS_LANG_MISSING_DATA = -1,
+ TTS_LANG_NOT_SUPPORTED = -2
+};
+
class TtsEngine
{
public:
@@ -86,19 +94,32 @@
// @return TTS_SUCCESS, or TTS_FAILURE
virtual tts_result stop();
+ // Returns the level of support for the language, country and variant.
+ // @return TTS_LANG_COUNTRY_VAR_AVAILABLE if the language, country and variant are supported,
+ // and the corresponding resources are correctly installed
+ // TTS_LANG_COUNTRY_AVAILABLE if the language and country are supported and the
+ // corresponding resources are correctly installed, but there is no match for
+ // the specified variant
+ // TTS_LANG_AVAILABLE if the language is supported and the
+ // corresponding resources are correctly installed, but there is no match for
+ // the specified country and variant
+ // TTS_LANG_MISSING_DATA if the required resources to provide any level of support
+ // for the language are not correctly installed
+ // TTS_LANG_NOT_SUPPORTED if the language is not supported by the TTS engine.
+ virtual tts_support_result isLanguageAvailable(const char *lang, const char *country,
+ const char *variant);
+
// Load the resources associated with the specified language. The loaded
// language will only be used once a call to setLanguage() with the same
- // language value is issued. Language values are based on the Android
- // conventions for localization as described in the Android platform
- // documentation on internationalization. This implies that language
- // data is specified in the format xx-rYY, where xx is a two letter
- // ISO 639-1 language code in lowercase and rYY is a two letter
- // ISO 3166-1-alpha-2 language code in uppercase preceded by a
- // lowercase "r".
- // @param value pointer to the language value
- // @param size length of the language value
+ // language value is issued. Language and country values are coded according to the ISO three
+ // letter codes for languages and countries, as can be retrieved from a java.util.Locale
+ // instance. The variant value is encoded as the variant string retrieved from a
+ // java.util.Locale instance built with that variant data.
+ // @param lang pointer to the ISO three letter code for the language
+ // @param country pointer to the ISO three letter code for the country
+ // @param variant pointer to the variant code
// @return TTS_SUCCESS, or TTS_FAILURE
- virtual tts_result loadLanguage(const char *value, const size_t size);
+ virtual tts_result loadLanguage(const char *lang, const char *country, const char *variant);
// Load the resources associated with the specified language, country and Locale variant.
// The loaded language will only be used once a call to setLanguageFromLocale() with the same
@@ -112,16 +133,26 @@
// @return TTS_SUCCESS, or TTS_FAILURE
virtual tts_result setLanguage(const char *lang, const char *country, const char *variant);
- // Retrieve the currently set language, or an empty "value" if no language
- // has been set.
- // @param[out] value pointer to the retrieved language value
- // @param[inout] iosize in: stores the size available to store the language
- // value in *value
- // out: stores the size required to hold the language
- // value if getLanguage() returned
- // TTS_PROPERTY_SIZE_TOO_SMALL, unchanged otherwise.
- // @return TTS_SUCCESS, or TTS_PROPERTY_SIZE_TOO_SMALL, or TTS_FAILURE
- virtual tts_result getLanguage(char *value, size_t *iosize);
+ // Retrieve the currently set language, country and variant, or empty strings if none of
+ // parameters have been set. Language and country are represented by their 3-letter ISO code
+ // @param[out] pointer to the retrieved 3-letter code language value
+ // @param[out] pointer to the retrieved 3-letter code country value
+ // @param[out] pointer to the retrieved variant value
+ // @return TTS_SUCCESS, or TTS_FAILURE
+ virtual tts_result getLanguage(char *language, char *country, char *variant);
+
+ // Notifies the engine what audio parameters should be used for the synthesis.
+ // This is meant to be used as a hint, the engine implementation will set the output values
+ // to those of the synthesis format, based on a given hint.
+ // @param[inout] encoding in: the desired audio sample format
+ // out: the format used by the TTS engine
+ // @param[inout] rate in: the desired audio sample rate
+ // out: the sample rate used by the TTS engine
+ // @param[inout] channels in: the desired number of audio channels
+ // out: the number of channels used by the TTS engine
+ // @return TTS_SUCCESS, or TTS_FAILURE
+ virtual tts_result setAudioFormat(AudioSystem::audio_format& encoding, uint32_t& rate,
+ int& channels);
// Set a property for the the TTS engine
// "size" is the maximum size of "value" for properties "property"
diff --git a/include/ui/Camera.h b/include/ui/Camera.h
index bbc21c4..97e0e90 100644
--- a/include/ui/Camera.h
+++ b/include/ui/Camera.h
@@ -63,16 +63,12 @@
#define FRAME_CALLBACK_FLAG_CAMERA 0x05
#define FRAME_CALLBACK_FLAG_BARCODE_SCANNER 0x07
-// msgType in notifyCallback function
+// msgType in notifyCallback and dataCallback functions
enum {
- CAMERA_MSG_ERROR,
+ CAMERA_MSG_ERROR = 0,
CAMERA_MSG_SHUTTER,
CAMERA_MSG_FOCUS,
- CAMERA_MSG_ZOOM
-};
-
-// msgType in dataCallback function
-enum {
+ CAMERA_MSG_ZOOM,
CAMERA_MSG_PREVIEW_FRAME,
CAMERA_MSG_VIDEO_FRAME,
CAMERA_MSG_POSTVIEW_FRAME,
diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h
index f1029b7..5c41ead 100644
--- a/include/utils/ResourceTypes.h
+++ b/include/utils/ResourceTypes.h
@@ -866,7 +866,7 @@
uint8_t keyboard;
uint8_t navigation;
uint8_t inputFlags;
- uint8_t pad0;
+ uint8_t inputPad0;
};
uint32_t input;
};
@@ -905,6 +905,23 @@
uint32_t version;
};
+ enum {
+ SCREENLAYOUT_ANY = 0x0000,
+ SCREENLAYOUT_SMALL = 0x0001,
+ SCREENLAYOUT_NORMAL = 0x0002,
+ SCREENLAYOUT_LARGE = 0x0003,
+ };
+
+ union {
+ struct {
+ uint8_t screenLayout;
+ uint8_t screenConfigPad0;
+ uint8_t screenConfigPad1;
+ uint8_t screenConfigPad2;
+ };
+ uint32_t screenConfig;
+ };
+
inline void copyFromDeviceNoSwap(const ResTable_config& o) {
const size_t size = dtohl(o.size);
if (size >= sizeof(ResTable_config)) {
@@ -950,6 +967,8 @@
diff = (int32_t)(screenSize - o.screenSize);
if (diff != 0) return diff;
diff = (int32_t)(version - o.version);
+ if (diff != 0) return diff;
+ diff = (int32_t)(screenLayout - o.screenLayout);
return (int)diff;
}
@@ -967,7 +986,8 @@
CONFIG_ORIENTATION = 0x0080,
CONFIG_DENSITY = 0x0100,
CONFIG_SCREEN_SIZE = 0x0200,
- CONFIG_VERSION = 0x0400
+ CONFIG_VERSION = 0x0400,
+ CONFIG_SCREEN_LAYOUT = 0x0800
};
// Compare two configuration, returning CONFIG_* flags set for each value
@@ -985,6 +1005,7 @@
if (navigation != o.navigation) diffs |= CONFIG_NAVIGATION;
if (screenSize != o.screenSize) diffs |= CONFIG_SCREEN_SIZE;
if (version != o.version) diffs |= CONFIG_VERSION;
+ if (screenLayout != o.screenLayout) diffs |= CONFIG_SCREEN_LAYOUT;
return diffs;
}
@@ -1062,6 +1083,13 @@
}
}
+ if (screenConfig || o.screenConfig) {
+ if (screenLayout != o.screenLayout) {
+ if (!screenLayout) return false;
+ if (!o.screenLayout) return true;
+ }
+ }
+
if (version || o.version) {
if (sdkVersion != o.sdkVersion) {
if (!sdkVersion) return false;
@@ -1191,6 +1219,12 @@
}
}
+ if (screenConfig || o.screenConfig) {
+ if ((screenLayout != o.screenLayout) && requested->screenLayout) {
+ return (screenLayout);
+ }
+ }
+
if (version || o.version) {
if ((sdkVersion != o.sdkVersion) && requested->sdkVersion) {
return (sdkVersion);
@@ -1282,6 +1316,12 @@
return false;
}
}
+ if (screenConfig != 0) {
+ if (settings.screenLayout != 0 && screenLayout != 0
+ && screenLayout != settings.screenLayout) {
+ return false;
+ }
+ }
if (version != 0) {
if (settings.sdkVersion != 0 && sdkVersion != 0
&& sdkVersion != settings.sdkVersion) {
@@ -1310,13 +1350,13 @@
String8 toString() const {
char buf[200];
- sprintf(buf, "imsi=%d/%d lang=%c%c reg=%c%c orient=0x%02x touch=0x%02x dens=0x%02x "
- "kbd=0x%02x nav=0x%02x input=0x%02x screenW=0x%04x screenH=0x%04x vers=%d.%d",
+ sprintf(buf, "imsi=%d/%d lang=%c%c reg=%c%c orient=%d touch=%d dens=%d "
+ "kbd=%d nav=%d input=%d scrnW=%d scrnH=%d layout=%d vers=%d.%d",
mcc, mnc,
language[0] ? language[0] : '-', language[1] ? language[1] : '-',
country[0] ? country[0] : '-', country[1] ? country[1] : '-',
orientation, touchscreen, density, keyboard, navigation, inputFlags,
- screenWidth, screenHeight, sdkVersion, minorVersion);
+ screenWidth, screenHeight, screenLayout, sdkVersion, minorVersion);
return String8(buf);
}
};
diff --git a/libs/surfaceflinger/SurfaceFlinger.cpp b/libs/surfaceflinger/SurfaceFlinger.cpp
index 0e998bf..ef4a8ea 100644
--- a/libs/surfaceflinger/SurfaceFlinger.cpp
+++ b/libs/surfaceflinger/SurfaceFlinger.cpp
@@ -771,10 +771,11 @@
dirty.orSelf(layer->visibleRegionScreen);
layer->contentDirty = false;
} else {
- // compute the exposed region
- // dirty = what's visible now - what's wasn't covered before
- // = what's visible now & what's was covered before
- dirty = visibleRegion.intersect(layer->coveredRegionScreen);
+ /* compute the exposed region:
+ * exposed = what's VISIBLE and NOT COVERED now
+ * but was COVERED before
+ */
+ dirty = (visibleRegion - coveredRegion) & layer->coveredRegionScreen;
}
dirty.subtractSelf(aboveOpaqueLayers);
@@ -783,7 +784,7 @@
// updade aboveOpaqueLayers/aboveCoveredLayers for next (lower) layer
aboveOpaqueLayers.orSelf(opaqueRegion);
- aboveCoveredLayers.orSelf(bounds);
+ aboveCoveredLayers.orSelf(visibleRegion);
// Store the visible region is screen space
layer->setVisibleRegion(visibleRegion);
diff --git a/libs/ui/Android.mk b/libs/ui/Android.mk
index b3b2104b..7bbe38b 100644
--- a/libs/ui/Android.mk
+++ b/libs/ui/Android.mk
@@ -24,8 +24,7 @@
Region.cpp \
Surface.cpp \
SurfaceComposerClient.cpp \
- SurfaceFlingerSynchro.cpp \
- Time.cpp
+ SurfaceFlingerSynchro.cpp
LOCAL_SHARED_LIBRARIES := \
libcorecg \
diff --git a/libs/ui/Camera.cpp b/libs/ui/Camera.cpp
index bb22dab..a481ce7 100644
--- a/libs/ui/Camera.cpp
+++ b/libs/ui/Camera.cpp
@@ -149,21 +149,21 @@
status_t Camera::setPreviewDisplay(const sp<Surface>& surface)
{
LOGV("setPreviewDisplay");
- if (surface == 0) {
- LOGE("app passed NULL surface");
- return NO_INIT;
- }
sp <ICamera> c = mCamera;
if (c == 0) return NO_INIT;
- return c->setPreviewDisplay(surface->getISurface());
+ if (surface != 0) {
+ return c->setPreviewDisplay(surface->getISurface());
+ } else {
+ LOGD("app passed NULL surface");
+ return c->setPreviewDisplay(0);
+ }
}
status_t Camera::setPreviewDisplay(const sp<ISurface>& surface)
{
LOGV("setPreviewDisplay");
if (surface == 0) {
- LOGE("app passed NULL surface");
- return NO_INIT;
+ LOGD("app passed NULL surface");
}
sp <ICamera> c = mCamera;
if (c == 0) return NO_INIT;
@@ -171,7 +171,7 @@
}
-// start preview mode, must call setPreviewDisplay first
+// start preview mode
status_t Camera::startPreview()
{
LOGV("startPreview");
diff --git a/libs/utils/BackupData.cpp b/libs/utils/BackupData.cpp
index 0868cff..cce754a 100644
--- a/libs/utils/BackupData.cpp
+++ b/libs/utils/BackupData.cpp
@@ -107,7 +107,10 @@
} else {
k = key;
}
- LOGD("m_keyPrefix=%s key=%s k=%s", m_keyPrefix.string(), key.string(), k.string());
+ if (true) {
+ LOGD("Writing entity: prefix='%s' key='%s' dataSize=%d", m_keyPrefix.string(), key.string(),
+ dataSize);
+ }
entity_header_v1 header;
ssize_t keyLen;
diff --git a/libs/utils/BackupHelpers.cpp b/libs/utils/BackupHelpers.cpp
index 99a4abc..4ad9b51 100644
--- a/libs/utils/BackupHelpers.cpp
+++ b/libs/utils/BackupHelpers.cpp
@@ -68,11 +68,15 @@
const static int CURRENT_METADATA_VERSION = 1;
-#if 1 // TEST_BACKUP_HELPERS
+#if 1
+#define LOGP(f, x...)
+#else
+#if TEST_BACKUP_HELPERS
#define LOGP(f, x...) printf(f "\n", x)
#else
#define LOGP(x...) LOGD(x)
#endif
+#endif
const static int ROUND_UP[4] = { 0, 3, 2, 1 };
@@ -349,7 +353,6 @@
err = stat(file, &st);
if (err != 0) {
- LOGW("Error stating file %s", file);
r.deleted = true;
} else {
r.deleted = false;
diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp
index e4f9f0f..7a33220 100644
--- a/libs/utils/ResourceTypes.cpp
+++ b/libs/utils/ResourceTypes.cpp
@@ -3919,7 +3919,7 @@
printf(" NON-INTEGER ResTable_type ADDRESS: %p\n", type);
continue;
}
- printf(" config %d lang=%c%c cnt=%c%c orien=%d touch=%d density=%d key=%d infl=%d nav=%d w=%d h=%d\n",
+ printf(" config %d lang=%c%c cnt=%c%c orien=%d touch=%d density=%d key=%d infl=%d nav=%d w=%d h=%d lyt=%d\n",
(int)configIndex,
type->config.language[0] ? type->config.language[0] : '-',
type->config.language[1] ? type->config.language[1] : '-',
@@ -3932,7 +3932,8 @@
type->config.inputFlags,
type->config.navigation,
dtohs(type->config.screenWidth),
- dtohs(type->config.screenHeight));
+ dtohs(type->config.screenHeight),
+ type->config.screenLayout);
size_t entryCount = dtohl(type->entryCount);
uint32_t entriesStart = dtohl(type->entriesStart);
if ((entriesStart&0x3) != 0) {
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 @@
{
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_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/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp
index 25e31ee..de323b3 100644
--- a/opengl/libs/EGL/egl.cpp
+++ b/opengl/libs/EGL/egl.cpp
@@ -1052,23 +1052,25 @@
if (!validate_display_context(dpy, ctx))
return EGL_FALSE;
+ EGLSurface impl_draw = EGL_NO_SURFACE;
+ EGLSurface impl_read = EGL_NO_SURFACE;
egl_context_t * const c = get_context(ctx);
if (draw != EGL_NO_SURFACE) {
egl_surface_t const * d = get_surface(draw);
if (!d) return setError(EGL_BAD_SURFACE, EGL_FALSE);
if (d->impl != c->impl)
return setError(EGL_BAD_MATCH, EGL_FALSE);
- draw = d->surface;
+ impl_draw = d->surface;
}
if (read != EGL_NO_SURFACE) {
egl_surface_t const * r = get_surface(read);
if (!r) return setError(EGL_BAD_SURFACE, EGL_FALSE);
if (r->impl != c->impl)
return setError(EGL_BAD_MATCH, EGL_FALSE);
- read = r->surface;
+ impl_read = r->surface;
}
EGLBoolean result = c->cnx->hooks->egl.eglMakeCurrent(
- dp->dpys[c->impl], draw, read, c->context);
+ dp->dpys[c->impl], impl_draw, impl_read, c->context);
if (result == EGL_TRUE) {
setGlThreadSpecific(c->cnx->hooks);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index b2f3557..660b469 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -592,6 +592,21 @@
loadIntegerSetting(stmt, Settings.System.SCREEN_OFF_TIMEOUT,
R.integer.def_screen_off_timeout);
+ // Set default cdma emergency tone
+ loadSetting(stmt, Settings.System.EMERGENCY_TONE, 0);
+
+ // Set default cdma call auto retry
+ loadSetting(stmt, Settings.System.CALL_AUTO_RETRY, 0);
+
+ // Set default cdma DTMF type
+ loadSetting(stmt, Settings.System.DTMF_TONE_TYPE_WHEN_DIALING, 0);
+
+ // Set default hearing aid
+ loadSetting(stmt, Settings.System.HEARING_AID, 0);
+
+ // Set default tty mode
+ loadSetting(stmt, Settings.System.TTY_MODE, 0);
+
loadBooleanSetting(stmt, Settings.System.AIRPLANE_MODE_ON,
R.bool.def_airplane_mode_on);
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 0dafcc1..a55b704 100644
--- a/packages/TtsService/jni/android_tts_SynthProxy.cpp
+++ b/packages/TtsService/jni/android_tts_SynthProxy.cpp
@@ -199,16 +199,29 @@
if (wav == NULL) {
delete pForAfter;
LOGV("Null: speech has completed");
+ return TTS_CALLBACK_HALT;
}
if (bufferSize > 0){
fwrite(wav, 1, bufferSize, pForAfter->outputFile);
}
}
- // TODO update to call back into the SynthProxy class through the
+ // Future update:
+ // For sync points in the speech, call back into the SynthProxy class through the
// javaTTSFields.synthProxyMethodPost methode to notify
- // playback has completed if the synthesis is done, i.e.
- // if status == TTS_SYNTH_DONE
- //delete pForAfter;
+ // playback has completed if the synthesis is done or if a marker has been reached.
+
+ if (status == TTS_SYNTH_DONE) {
+ // this struct was allocated in the original android_tts_SynthProxy_speak call,
+ // all processing matching this call is now done.
+ LOGV("Speech synthesis done.");
+ 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;
+ }
// we don't update the wav (output) parameter as we'll let the next callback
// write at the same location, we've consumed the data already, but we need
@@ -270,6 +283,33 @@
}
+static int
+android_tts_SynthProxy_isLanguageAvailable(JNIEnv *env, jobject thiz, jint jniData,
+ jstring language, jstring country, jstring variant)
+{
+ int result = TTS_LANG_NOT_SUPPORTED;
+
+ if (jniData == 0) {
+ LOGE("android_tts_SynthProxy_isLanguageAvailable(): invalid JNI data");
+ return result;
+ }
+
+ SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
+ const char *langNativeString = env->GetStringUTFChars(language, 0);
+ const char *countryNativeString = env->GetStringUTFChars(country, 0);
+ const char *variantNativeString = env->GetStringUTFChars(variant, 0);
+ // TODO check return codes
+ if (pSynthData->mNativeSynthInterface) {
+ result = pSynthData->mNativeSynthInterface->isLanguageAvailable(langNativeString,
+ countryNativeString, variantNativeString);
+ }
+ env->ReleaseStringUTFChars(language, langNativeString);
+ env->ReleaseStringUTFChars(country, countryNativeString);
+ env->ReleaseStringUTFChars(variant, variantNativeString);
+ return result;
+}
+
+
static void
android_tts_SynthProxy_setLanguage(JNIEnv *env, jobject thiz, jint jniData,
jstring language, jstring country, jstring variant)
@@ -289,8 +329,32 @@
variantNativeString);
}
env->ReleaseStringUTFChars(language, langNativeString);
- env->ReleaseStringUTFChars(language, countryNativeString);
- env->ReleaseStringUTFChars(language, variantNativeString);
+ env->ReleaseStringUTFChars(country, countryNativeString);
+ env->ReleaseStringUTFChars(variant, variantNativeString);
+}
+
+
+static void
+android_tts_SynthProxy_loadLanguage(JNIEnv *env, jobject thiz, jint jniData,
+ jstring language, jstring country, jstring variant)
+{
+ if (jniData == 0) {
+ LOGE("android_tts_SynthProxy_loadLanguage(): invalid JNI data");
+ return;
+ }
+
+ 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) {
+ pSynthData->mNativeSynthInterface->loadLanguage(langNativeString, countryNativeString,
+ variantNativeString);
+ }
+ env->ReleaseStringUTFChars(language, langNativeString);
+ env->ReleaseStringUTFChars(country, countryNativeString);
+ env->ReleaseStringUTFChars(variant, variantNativeString);
}
@@ -338,7 +402,6 @@
}
-// TODO: Refactor this to get rid of any assumptions about sample rate, etc.
static void
android_tts_SynthProxy_synthesizeToFile(JNIEnv *env, jobject thiz, jint jniData,
jstring textJavaString, jstring filenameJavaString)
@@ -349,6 +412,21 @@
}
SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
+ if (!pSynthData->mNativeSynthInterface) {
+ LOGE("android_tts_SynthProxy_synthesizeToFile(): invalid engine handle");
+ return;
+ }
+
+ // Retrieve audio parameters before writing the file header
+ AudioSystem::audio_format encoding = DEFAULT_TTS_FORMAT;
+ uint32_t rate = DEFAULT_TTS_RATE;
+ int channels = DEFAULT_TTS_NB_CHANNELS;
+ pSynthData->mNativeSynthInterface->setAudioFormat(encoding, rate, channels);
+
+ if ((encoding != AudioSystem::PCM_16_BIT) && (encoding != AudioSystem::PCM_8_BIT)) {
+ LOGE("android_tts_SynthProxy_synthesizeToFile(): engine uses invalid format");
+ return;
+ }
const char *filenameNativeString =
env->GetStringUTFChars(filenameJavaString, 0);
@@ -360,6 +438,12 @@
pForAfter->outputFile = fopen(filenameNativeString, "wb");
+ if (pForAfter->outputFile == NULL) {
+ LOGE("android_tts_SynthProxy_synthesizeToFile(): error creating output file");
+ delete pForAfter;
+ return;
+ }
+
// Write 44 blank bytes for WAV header, then come back and fill them in
// after we've written the audio data
char header[44];
@@ -368,10 +452,8 @@
unsigned int unique_identifier;
// TODO check return codes
- if (pSynthData->mNativeSynthInterface) {
- pSynthData->mNativeSynthInterface->synthesizeText(textNativeString, pSynthData->mBuffer, pSynthData->mBufferSize,
- (void *)pForAfter);
- }
+ pSynthData->mNativeSynthInterface->synthesizeText(textNativeString, pSynthData->mBuffer,
+ pSynthData->mBufferSize, (void *)pForAfter);
long filelen = ftell(pForAfter->outputFile);
@@ -393,12 +475,14 @@
((uint32_t *)(&header[16]))[0] = 16; // size of fmt
+ int sampleSizeInByte = (encoding == AudioSystem::PCM_16_BIT ? 2 : 1);
+
((unsigned short *)(&header[20]))[0] = 1; // format
- ((unsigned short *)(&header[22]))[0] = 1; // channels
- ((uint32_t *)(&header[24]))[0] = 22050; // samplerate
- ((uint32_t *)(&header[28]))[0] = 44100; // byterate
- ((unsigned short *)(&header[32]))[0] = 2; // block align
- ((unsigned short *)(&header[34]))[0] = 16; // bits per sample
+ ((unsigned short *)(&header[22]))[0] = channels; // channels
+ ((uint32_t *)(&header[24]))[0] = rate; // samplerate
+ ((uint32_t *)(&header[28]))[0] = rate * sampleSizeInByte * channels;// byterate
+ ((unsigned short *)(&header[32]))[0] = sampleSizeInByte * channels; // block align
+ ((unsigned short *)(&header[34]))[0] = sampleSizeInByte * 8; // bits per sample
header[36] = 'd';
header[37] = 'a';
@@ -414,6 +498,9 @@
fflush(pForAfter->outputFile);
fclose(pForAfter->outputFile);
+ delete pForAfter;
+ pForAfter = NULL;
+
env->ReleaseStringUTFChars(textJavaString, textNativeString);
env->ReleaseStringUTFChars(filenameJavaString, filenameNativeString);
}
@@ -441,8 +528,8 @@
if (pSynthData->mNativeSynthInterface) {
const char *textNativeString = env->GetStringUTFChars(textJavaString, 0);
- pSynthData->mNativeSynthInterface->synthesizeText(textNativeString, pSynthData->mBuffer, pSynthData->mBufferSize,
- (void *)pForAfter);
+ pSynthData->mNativeSynthInterface->synthesizeText(textNativeString, pSynthData->mBuffer,
+ pSynthData->mBufferSize, (void *)pForAfter);
env->ReleaseStringUTFChars(textJavaString, textNativeString);
}
}
@@ -501,23 +588,34 @@
}
-JNIEXPORT jstring JNICALL
+static jobjectArray
android_tts_SynthProxy_getLanguage(JNIEnv *env, jobject thiz, jint jniData)
{
if (jniData == 0) {
LOGE("android_tts_SynthProxy_getLanguage(): invalid JNI data");
- return env->NewStringUTF("");
+ return NULL;
}
SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
- size_t bufSize = 100;
- char buf[bufSize];
- memset(buf, 0, bufSize);
- // TODO check return codes
+
if (pSynthData->mNativeSynthInterface) {
- pSynthData->mNativeSynthInterface->getLanguage(buf, &bufSize);
+ size_t bufSize = 100;
+ char lang[bufSize];
+ char country[bufSize];
+ char variant[bufSize];
+ memset(lang, 0, bufSize);
+ memset(country, 0, bufSize);
+ memset(variant, 0, bufSize);
+ jobjectArray retLocale = (jobjectArray)env->NewObjectArray(3,
+ env->FindClass("java/lang/String"), env->NewStringUTF(""));
+ pSynthData->mNativeSynthInterface->getLanguage(lang, country, variant);
+ env->SetObjectArrayElement(retLocale, 0, env->NewStringUTF(lang));
+ env->SetObjectArrayElement(retLocale, 1, env->NewStringUTF(country));
+ env->SetObjectArrayElement(retLocale, 2, env->NewStringUTF(variant));
+ return retLocale;
+ } else {
+ return NULL;
}
- return env->NewStringUTF(buf);
}
@@ -555,10 +653,18 @@
"(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
},
+ { "native_loadLanguage",
+ "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V",
+ (void*)android_tts_SynthProxy_loadLanguage
+ },
{ "native_setSpeechRate",
"(II)V",
(void*)android_tts_SynthProxy_setSpeechRate
@@ -572,7 +678,7 @@
(void*)android_tts_SynthProxy_playAudioBuffer
},
{ "native_getLanguage",
- "(I)Ljava/lang/String;",
+ "(I)[Ljava/lang/String;",
(void*)android_tts_SynthProxy_getLanguage
},
{ "native_getRate",
diff --git a/packages/TtsService/src/android/tts/SynthProxy.java b/packages/TtsService/src/android/tts/SynthProxy.java
index 3bdff37..91fe3b7 100755
--- a/packages/TtsService/src/android/tts/SynthProxy.java
+++ b/packages/TtsService/src/android/tts/SynthProxy.java
@@ -68,49 +68,64 @@
// 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);
}
/**
- * Sets the speech rate
+ * Loads the language: it's not set, but prepared for use later.
+ */
+ public void loadLanguage(String language, String country, String variant) {
+ native_loadLanguage(mJniData, language, country, variant);
+ }
+
+ /**
+ * 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);
@@ -147,17 +162,23 @@
private native final void native_synthesizeToFile(int jniData, String text, String filename);
+ private native final int native_isLanguageAvailable(int jniData, String language,
+ String country, String variant);
+
private native final void native_setLanguage(int jniData, String language, String country,
String variant);
+ private native final void native_loadLanguage(int jniData, String language, String country,
+ String variant);
+
private native final void native_setSpeechRate(int jniData, int speechRate);
-
+
private native final void native_setPitch(int jniData, int speechRate);
// TODO add buffer format
private native final void native_playAudioBuffer(int jniData, int bufferPointer, int bufferSize);
- private native final String native_getLanguage(int jniData);
+ private native final String[] native_getLanguage(int jniData);
private native final int native_getRate(int jniData);
diff --git a/packages/TtsService/src/android/tts/TtsService.java b/packages/TtsService/src/android/tts/TtsService.java
index 6fa3141..b1e6425 100755
--- a/packages/TtsService/src/android/tts/TtsService.java
+++ b/packages/TtsService/src/android/tts/TtsService.java
@@ -47,12 +47,13 @@
public class TtsService extends Service implements OnCompletionListener {
private static class SpeechItem {
- public static final int SPEECH = 0;
- public static final int EARCON = 1;
- public static final int SILENCE = 2;
+ public static final int TEXT = 0;
+ public static final int IPA = 1;
+ public static final int EARCON = 2;
+ public static final int SILENCE = 3;
public String mText = null;
public ArrayList<String> mParams = null;
- public int mType = SPEECH;
+ public int mType = TEXT;
public long mDuration = 0;
public SpeechItem(String text, ArrayList<String> params, int itemType) {
@@ -89,6 +90,9 @@
}
}
+ 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";
private static final String PKGNAME = "android.tts";
@@ -108,7 +112,6 @@
private final ReentrantLock synthesizerLock = new ReentrantLock();
private SynthProxy nativeSynth;
-
@Override
public void onCreate() {
super.onCreate();
@@ -145,13 +148,10 @@
private void setDefaultSettings() {
-
- // TODO handle default language
- setLanguage("eng", "USA", "");
+ setLanguage(this.getDefaultLanguage(), getDefaultCountry(), getDefaultLocVariant());
// speech rate
setSpeechRate(getDefaultRate());
-
}
@@ -217,6 +217,17 @@
}
+ private int isLanguageAvailable(String lang, String country, String variant) {
+ Log.v("TTS", "TtsService.isLanguageAvailable(" + lang + ", " + country + ", " +variant+")");
+ return nativeSynth.isLanguageAvailable(lang, country, variant);
+ }
+
+
+ private String[] getLanguage() {
+ return nativeSynth.getLanguage();
+ }
+
+
private void setLanguage(String lang, String country, String variant) {
Log.v("TTS", "TtsService.setLanguage(" + lang + ", " + country + ", " + variant + ")");
if (isDefaultEnforced()) {
@@ -298,7 +309,29 @@
if (queueMode == 0) {
stop();
}
- mSpeechQueue.add(new SpeechItem(text, params, SpeechItem.SPEECH));
+ mSpeechQueue.add(new SpeechItem(text, params, SpeechItem.TEXT));
+ if (!mIsSpeaking) {
+ processSpeechQueue();
+ }
+ }
+
+ /**
+ * Speaks the given IPA text using the specified queueing mode and parameters.
+ *
+ * @param ipaText
+ * The IPA text that should be spoken
+ * @param queueMode
+ * 0 for no queue (interrupts all previous utterances), 1 for
+ * queued
+ * @param params
+ * An ArrayList of parameters. This is not implemented for all
+ * engines.
+ */
+ private void speakIpa(String ipaText, int queueMode, ArrayList<String> params) {
+ if (queueMode == 0) {
+ stop();
+ }
+ mSpeechQueue.add(new SpeechItem(ipaText, params, SpeechItem.IPA));
if (!mIsSpeaking) {
processSpeechQueue();
}
@@ -392,6 +425,26 @@
synth.start();
return;
}
+ if (params != null){
+ String language = "";
+ String country = "";
+ String variant = "";
+ for (int i = 0; i < params.size() - 1; i = i + 2){
+ String param = params.get(i);
+ if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_RATE)){
+ setSpeechRate(Integer.parseInt(params.get(i+1)));
+ } else if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_LANGUAGE)){
+ language = params.get(i+1);
+ } else if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_COUNTRY)){
+ country = params.get(i+1);
+ } else if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_VARIANT)){
+ variant = params.get(i+1);
+ }
+ }
+ if (language.length() > 0){
+ setLanguage(language, country, variant);
+ }
+ }
nativeSynth.speak(text);
} catch (InterruptedException e) {
e.printStackTrace();
@@ -424,6 +477,11 @@
return sr;
}
+ private void broadcastTtsQueueProcessingCompleted(){
+ Intent i = new Intent(Intent.ACTION_TTS_QUEUE_PROCESSING_COMPLETED);
+ sendBroadcast(i);
+ }
+
private void dispatchSpeechCompletedCallbacks(String mark) {
Log.i("TTS callback", "dispatch started");
// Broadcast to all clients the new value.
@@ -440,6 +498,33 @@
Log.i("TTS callback", "dispatch completed to " + N);
}
+ private SpeechItem splitCurrentTextIfNeeded(SpeechItem currentSpeechItem){
+ if (currentSpeechItem.mText.length() < MAX_SPEECH_ITEM_CHAR_LENGTH){
+ return currentSpeechItem;
+ } else {
+ ArrayList<SpeechItem> splitItems = new ArrayList<SpeechItem>();
+ int start = 0;
+ int end = start + MAX_SPEECH_ITEM_CHAR_LENGTH - 1;
+ String splitText;
+ SpeechItem splitItem;
+ while (end < currentSpeechItem.mText.length()){
+ splitText = currentSpeechItem.mText.substring(start, end);
+ splitItem = new SpeechItem(splitText, null, SpeechItem.TEXT);
+ splitItems.add(splitItem);
+ start = end;
+ end = start + MAX_SPEECH_ITEM_CHAR_LENGTH - 1;
+ }
+ splitText = currentSpeechItem.mText.substring(start);
+ splitItem = new SpeechItem(splitText, null, SpeechItem.TEXT);
+ splitItems.add(splitItem);
+ mSpeechQueue.remove(0);
+ for (int i = splitItems.size() - 1; i >= 0; i--){
+ mSpeechQueue.add(0, splitItems.get(i));
+ }
+ return mSpeechQueue.get(0);
+ }
+ }
+
private void processSpeechQueue() {
boolean speechQueueAvailable = false;
try {
@@ -449,11 +534,7 @@
}
if (mSpeechQueue.size() < 1) {
mIsSpeaking = false;
- // Dispatch a completion here as this is the
- // only place where speech completes normally.
- // Nothing left to say in the queue is a special case
- // that is always a "mark" - associated text is null.
- dispatchSpeechCompletedCallbacks("");
+ broadcastTtsQueueProcessingCompleted();
return;
}
@@ -464,11 +545,12 @@
// processSpeechQueue to continue running the queue
Log.i("TTS processing: ", currentSpeechItem.mText);
if (sr == null) {
- if (currentSpeechItem.mType == SpeechItem.SPEECH) {
- // TODO: Split text up into smaller chunks before accepting
- // them for processing.
+ if (currentSpeechItem.mType == SpeechItem.TEXT) {
+ currentSpeechItem = splitCurrentTextIfNeeded(currentSpeechItem);
speakInternalOnly(currentSpeechItem.mText,
currentSpeechItem.mParams);
+ } else if (currentSpeechItem.mType == SpeechItem.IPA) {
+ // TODO Implement IPA support
} else {
// This is either silence or an earcon that was missing
silence(currentSpeechItem.mDuration);
@@ -534,8 +616,7 @@
}
/**
- * Synthesizes the given text using the specified queuing mode and
- * parameters.
+ * Synthesizes the given text to a file using the specified parameters.
*
* @param text
* The String of text that should be synthesized
@@ -564,8 +645,7 @@
return false;
}
// Don't allow a filename that is too long
- // TODO use platform constant
- if (filename.length() > 250) {
+ if (filename.length() > MAX_FILENAME_LENGTH) {
return false;
}
nativeSynth.synthesizeToFile(text, filename);
@@ -580,6 +660,51 @@
return true;
}
+ /**
+ * Synthesizes the given IPA text to a file using the specified parameters.
+ *
+ * @param ipaText
+ * The String of IPA text that should be synthesized
+ * @param params
+ * An ArrayList of parameters. The first element of this array
+ * controls the type of voice to use.
+ * @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
+ */
+ private boolean synthesizeIpaToFile(String ipaText, ArrayList<String> params,
+ String filename, boolean calledFromApi) {
+ // Only stop everything if this is a call made by an outside app trying
+ // to
+ // use the API. Do NOT stop if this is a call from within the service as
+ // clearing the speech queue here would be a mistake.
+ if (calledFromApi) {
+ stop();
+ }
+ Log.i("TTS", "Synthesizing IPA to " + filename);
+ boolean synthAvailable = false;
+ try {
+ synthAvailable = synthesizerLock.tryLock();
+ if (!synthAvailable) {
+ return false;
+ }
+ // Don't allow a filename that is too long
+ if (filename.length() > MAX_FILENAME_LENGTH) {
+ return false;
+ }
+ // TODO: Add nativeSynth.synthesizeIpaToFile(text, filename);
+ } finally {
+ // This check is needed because finally will always run; even if the
+ // method returns somewhere in the try block.
+ if (synthAvailable) {
+ synthesizerLock.unlock();
+ }
+ }
+ Log.i("TTS", "Completed synthesis for " + filename);
+ return true;
+ }
+
@Override
public IBinder onBind(Intent intent) {
if (ACTION.equals(intent.getAction())) {
@@ -626,6 +751,27 @@
}
/**
+ * Speaks the given IPA text using the specified queueing mode and
+ * parameters.
+ *
+ * @param ipaText
+ * The IPA text that should be spoken
+ * @param queueMode
+ * 0 for no queue (interrupts all previous utterances), 1 for
+ * queued
+ * @param params
+ * An ArrayList of parameters. The first element of this
+ * array controls the type of voice to use.
+ */
+ public void speakIpa(String ipaText, int queueMode, String[] params) {
+ ArrayList<String> speakingParams = new ArrayList<String>();
+ if (params != null) {
+ speakingParams = new ArrayList<String>(Arrays.asList(params));
+ }
+ mSelf.speakIpa(ipaText, queueMode, speakingParams);
+ }
+
+ /**
* Plays the earcon using the specified queueing mode and parameters.
*
* @param earcon
@@ -757,6 +903,30 @@
}
/**
+ * Returns the level of support for the specified language.
+ *
+ * @param lang the three letter ISO language code.
+ * @param country the three letter ISO country code.
+ * @param variant the variant code associated with the country and language pair.
+ * @return one of TTS_LANG_NOT_SUPPORTED, TTS_LANG_MISSING_DATA, TTS_LANG_AVAILABLE,
+ * TTS_LANG_COUNTRY_AVAILABLE, TTS_LANG_COUNTRY_VAR_AVAILABLE as defined in
+ * android.speech.tts.TextToSpeech.
+ */
+ public int isLanguageAvailable(String lang, String country, String variant) {
+ return mSelf.isLanguageAvailable(lang, country, variant);
+ }
+
+ /**
+ * Returns the currently set language / country / variant strings representing the
+ * language used by the TTS engine.
+ * @return null is no language is set, or an array of 3 string containing respectively
+ * the language, country and variant.
+ */
+ public String[] getLanguage() {
+ return mSelf.getLanguage();
+ }
+
+ /**
* Sets the speech rate for the TTS, which affects the synthesized voice.
*
* @param lang the three letter ISO language code.
@@ -768,7 +938,7 @@
}
/**
- * Speaks the given text using the specified queueing mode and
+ * Synthesizes the given text to a file using the specified
* parameters.
*
* @param text
@@ -789,6 +959,29 @@
}
return mSelf.synthesizeToFile(text, speakingParams, filename, true);
}
+
+ /**
+ * Synthesizes the given IPA text to a file using the specified
+ * parameters.
+ *
+ * @param ipaText
+ * The String of IPA text that should be synthesized
+ * @param params
+ * An ArrayList of parameters. The first element of this
+ * array controls the type of voice to use.
+ * @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
+ */
+ public boolean synthesizeIpaToFile(String ipaText, String[] params,
+ String filename) {
+ ArrayList<String> speakingParams = new ArrayList<String>();
+ if (params != null) {
+ speakingParams = new ArrayList<String>(Arrays.asList(params));
+ }
+ return mSelf.synthesizeIpaToFile(ipaText, speakingParams, filename, true);
+ }
};
}
diff --git a/packages/VpnServices/src/com/android/server/vpn/AndroidServiceProxy.java b/packages/VpnServices/src/com/android/server/vpn/AndroidServiceProxy.java
index a12db8c..2ad218f 100644
--- a/packages/VpnServices/src/com/android/server/vpn/AndroidServiceProxy.java
+++ b/packages/VpnServices/src/com/android/server/vpn/AndroidServiceProxy.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007, The Android Open Source Project
+ * 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.
diff --git a/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecService.java b/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecService.java
index 8358c5c..877fa6b 100644
--- a/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecService.java
+++ b/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecService.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007, The Android Open Source Project
+ * 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.
@@ -25,8 +25,7 @@
* The service that manages the L2TP-over-IPSec VPN connection.
*/
class L2tpIpsecService extends VpnService<L2tpIpsecProfile> {
- private static final String IPSEC_SERVICE = "racoon";
- private static final String L2TP_SERVICE = "mtpd";
+ private static final String IPSEC_DAEMON = "racoon";
@Override
protected void connect(String serverIp, String username, String password)
@@ -34,7 +33,7 @@
String hostIp = getHostIp();
// IPSEC
- AndroidServiceProxy ipsecService = startService(IPSEC_SERVICE);
+ AndroidServiceProxy ipsecService = startService(IPSEC_DAEMON);
ipsecService.sendCommand(
String.format("SETKEY %s %s", hostIp, serverIp));
ipsecService.sendCommand(String.format("SET_CERTS %s %s %s %s",
@@ -42,11 +41,11 @@
getUserkeyPath()));
// L2TP
- AndroidServiceProxy l2tpService = startService(L2TP_SERVICE);
- l2tpService.sendCommand2("l2tp", serverIp, "1701", "",
- "file", getPppOptionFilePath(),
- "name", username,
- "password", password);
+ L2tpIpsecProfile p = getProfile();
+ MtpdHelper.sendCommand(this, L2tpService.L2TP_DAEMON, serverIp,
+ L2tpService.L2TP_PORT,
+ (p.isSecretEnabled() ? p.getSecretString() : null),
+ username, password);
}
private String getCaCertPath() {
diff --git a/packages/VpnServices/src/com/android/server/vpn/L2tpService.java b/packages/VpnServices/src/com/android/server/vpn/L2tpService.java
index 9aad7a1..9273f35 100644
--- a/packages/VpnServices/src/com/android/server/vpn/L2tpService.java
+++ b/packages/VpnServices/src/com/android/server/vpn/L2tpService.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007, The Android Open Source Project
+ * 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.
@@ -24,19 +24,15 @@
* The service that manages the L2TP VPN connection.
*/
class L2tpService extends VpnService<L2tpProfile> {
- private static final String L2TP_SERVICE = "mtpd";
+ static final String L2TP_DAEMON = "l2tp";
+ static final String L2TP_PORT = "1701";
@Override
protected void connect(String serverIp, String username, String password)
throws IOException {
- String hostIp = getHostIp();
-
- // L2TP
- AndroidServiceProxy l2tpService = startService(L2TP_SERVICE);
- l2tpService.sendCommand2("l2tp", serverIp, "1701", "",
- "file", getPppOptionFilePath(),
- "name", username,
- "password", password);
+ L2tpProfile p = getProfile();
+ MtpdHelper.sendCommand(this, L2TP_DAEMON, serverIp, L2TP_PORT,
+ (p.isSecretEnabled() ? p.getSecretString() : null),
+ username, password);
}
-
}
diff --git a/packages/VpnServices/src/com/android/server/vpn/MtpdHelper.java b/packages/VpnServices/src/com/android/server/vpn/MtpdHelper.java
new file mode 100644
index 0000000..6160900
--- /dev/null
+++ b/packages/VpnServices/src/com/android/server/vpn/MtpdHelper.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2009, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vpn;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * A helper class for sending commands to the MTP daemon (mtpd).
+ */
+class MtpdHelper {
+ private static final String MTPD_SERVICE = "mtpd";
+ private static final String VPN_LINKNAME = "vpn";
+ private static final String PPP_ARGS_SEPARATOR = "";
+
+ static void sendCommand(VpnService<?> vpnService, String protocol,
+ String serverIp, String port, String secret, String username,
+ String password) throws IOException {
+ ArrayList<String> args = new ArrayList<String>();
+ args.addAll(Arrays.asList(protocol, serverIp, port));
+ if (secret != null) args.add(secret);
+ args.add(PPP_ARGS_SEPARATOR);
+ addPppArguments(vpnService, args, serverIp, username, password);
+
+ AndroidServiceProxy mtpd = vpnService.startService(MTPD_SERVICE);
+ mtpd.sendCommand2(args.toArray(new String[args.size()]));
+ }
+
+ private static void addPppArguments(VpnService<?> vpnService,
+ ArrayList<String> args, String serverIp, String username,
+ String password) throws IOException {
+ args.addAll(Arrays.asList(
+ "linkname", VPN_LINKNAME,
+ "name", username,
+ "password", password,
+ "ipparam", serverIp + "@" + vpnService.getGatewayIp(),
+ "refuse-eap", "nodefaultroute", "usepeerdns",
+ "idle", "1800",
+ "mtu", "1400",
+ "mru", "1400"));
+ }
+
+ private MtpdHelper() {
+ }
+}
diff --git a/packages/VpnServices/src/com/android/server/vpn/NormalProcessProxy.java b/packages/VpnServices/src/com/android/server/vpn/NormalProcessProxy.java
index f20d85f..f0bbc34 100644
--- a/packages/VpnServices/src/com/android/server/vpn/NormalProcessProxy.java
+++ b/packages/VpnServices/src/com/android/server/vpn/NormalProcessProxy.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007, The Android Open Source Project
+ * 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.
diff --git a/packages/VpnServices/src/com/android/server/vpn/PptpService.java b/packages/VpnServices/src/com/android/server/vpn/PptpService.java
new file mode 100644
index 0000000..01362a5
--- /dev/null
+++ b/packages/VpnServices/src/com/android/server/vpn/PptpService.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2009, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vpn;
+
+import android.net.vpn.PptpProfile;
+
+import java.io.IOException;
+
+/**
+ * The service that manages the PPTP VPN connection.
+ */
+class PptpService extends VpnService<PptpProfile> {
+ static final String PPTP_DAEMON = "pptp";
+ static final String PPTP_PORT = "1723";
+ @Override
+ protected void connect(String serverIp, String username, String password)
+ throws IOException {
+ MtpdHelper.sendCommand(this, PPTP_DAEMON, serverIp, PPTP_PORT, null,
+ username, password);
+ }
+
+}
diff --git a/packages/VpnServices/src/com/android/server/vpn/ProcessProxy.java b/packages/VpnServices/src/com/android/server/vpn/ProcessProxy.java
index 14d7521..50fbf4b 100644
--- a/packages/VpnServices/src/com/android/server/vpn/ProcessProxy.java
+++ b/packages/VpnServices/src/com/android/server/vpn/ProcessProxy.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007, The Android Open Source Project
+ * 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.
diff --git a/packages/VpnServices/src/com/android/server/vpn/VpnService.java b/packages/VpnServices/src/com/android/server/vpn/VpnService.java
index fdefe9c..44127ff 100644
--- a/packages/VpnServices/src/com/android/server/vpn/VpnService.java
+++ b/packages/VpnServices/src/com/android/server/vpn/VpnService.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007, The Android Open Source Project
+ * 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.
@@ -20,6 +20,7 @@
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
+import android.net.NetworkUtils;
import android.net.vpn.VpnManager;
import android.net.vpn.VpnProfile;
import android.net.vpn.VpnState;
@@ -31,8 +32,10 @@
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
+import java.net.NetworkInterface;
import java.net.Socket;
import java.util.ArrayList;
+import java.util.Enumeration;
import java.util.List;
/**
@@ -153,16 +156,25 @@
}
/**
- * Returns the IP of the specified host name.
+ * Returns the IP address of the specified host name.
*/
protected String getIp(String hostName) throws IOException {
- InetAddress iaddr = InetAddress.getByName(hostName);
- byte[] aa = iaddr.getAddress();
- StringBuilder sb = new StringBuilder().append(byteToInt(aa[0]));
- for (int i = 1; i < aa.length; i++) {
- sb.append(".").append(byteToInt(aa[i]));
+ return InetAddress.getByName(hostName).getHostAddress();
+ }
+
+ /**
+ * Returns the IP address of the default gateway.
+ */
+ protected String getGatewayIp() throws IOException {
+ Enumeration<NetworkInterface> ifces =
+ NetworkInterface.getNetworkInterfaces();
+ for (; ifces.hasMoreElements(); ) {
+ NetworkInterface ni = ifces.nextElement();
+ int gateway = NetworkUtils.getDefaultRoute(ni.getName());
+ if (gateway == 0) continue;
+ return toInetAddress(gateway).getHostAddress();
}
- return sb.toString();
+ throw new IOException("Default gateway is not available");
}
/**
@@ -170,7 +182,7 @@
* connection is established.
*/
protected String getConnectMonitorFile() {
- return "/etc/ppp/ip-up";
+ return "/etc/ppp/ip-up-vpn";
}
/**
@@ -461,12 +473,18 @@
}
private String reallyGetHostIp() throws IOException {
- Socket s = new Socket();
- s.connect(new InetSocketAddress("www.google.com", 80), DNS_TIMEOUT);
- String ipAddress = s.getLocalAddress().getHostAddress();
- Log.d(TAG, "Host IP: " + ipAddress);
- s.close();
- return ipAddress;
+ Enumeration<NetworkInterface> ifces =
+ NetworkInterface.getNetworkInterfaces();
+ for (; ifces.hasMoreElements(); ) {
+ NetworkInterface ni = ifces.nextElement();
+ int gateway = NetworkUtils.getDefaultRoute(ni.getName());
+ if (gateway == 0) continue;
+ Enumeration<InetAddress> addrs = ni.getInetAddresses();
+ for (; addrs.hasMoreElements(); ) {
+ return addrs.nextElement().getHostAddress();
+ }
+ }
+ throw new IOException("Host IP is not available");
}
private String getProfileSubpath(String subpath) throws IOException {
@@ -496,8 +514,13 @@
return ((message == null) || (message.length() == 0));
}
- private static int byteToInt(byte b) {
- return ((int) b) & 0x0FF;
+ private InetAddress toInetAddress(int addr) throws IOException {
+ byte[] aa = new byte[4];
+ for (int i= 0; i < aa.length; i++) {
+ aa[i] = (byte) (addr & 0x0FF);
+ addr >>= 8;
+ }
+ return InetAddress.getByAddress(aa);
}
private class ServiceHelper implements ProcessProxy.Callback {
diff --git a/packages/VpnServices/src/com/android/server/vpn/VpnServiceBinder.java b/packages/VpnServices/src/com/android/server/vpn/VpnServiceBinder.java
index 7195ea61..63fc858 100644
--- a/packages/VpnServices/src/com/android/server/vpn/VpnServiceBinder.java
+++ b/packages/VpnServices/src/com/android/server/vpn/VpnServiceBinder.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007, The Android Open Source Project
+ * 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.
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index efa6179..c67f0b5 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -34,7 +34,6 @@
import android.content.pm.Signature;
import android.net.Uri;
import android.os.Binder;
-import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
@@ -43,12 +42,13 @@
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;
@@ -60,7 +60,6 @@
import java.io.EOFException;
import java.io.File;
import java.io.FileDescriptor;
-import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.RandomAccessFile;
@@ -68,13 +67,17 @@
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.Iterator;
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;
@@ -87,10 +90,11 @@
private Context mContext;
private PackageManager mPackageManager;
- private final IActivityManager mActivityManager;
+ private IActivityManager mActivityManager;
+ private boolean mEnabled; // access to this is synchronized on 'this'
private final BackupHandler mBackupHandler = new BackupHandler();
// map UIDs to the set of backup client services within that UID's app set
- private SparseArray<HashSet<ApplicationInfo>> mBackupParticipants
+ private final SparseArray<HashSet<ApplicationInfo>> mBackupParticipants
= new SparseArray<HashSet<ApplicationInfo>>();
// set of backup services that have pending changes
private class BackupRequest {
@@ -109,8 +113,8 @@
// Backups that we haven't started yet.
private HashMap<ApplicationInfo,BackupRequest> mPendingBackups
= new HashMap<ApplicationInfo,BackupRequest>();
- // Do we need to back up the package manager metadata on the next pass?
- private boolean mDoPackageManager;
+
+ // Pseudoname that we use for the Package Manager metadata "package"
private static final String PACKAGE_MANAGER_SENTINEL = "@pm@";
// locking around the pending-backup management
@@ -128,12 +132,27 @@
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;
- private File mStateDir;
+ private class RestoreParams {
+ public IBackupTransport transport;
+ public IRestoreObserver observer;
+ public long token;
+
+ RestoreParams(IBackupTransport _transport, IRestoreObserver _obs, long _token) {
+ transport = _transport;
+ observer = _obs;
+ token = _token;
+ }
+ }
+
+ // Where we keep our journal files and other bookkeeping
+ private File mBaseStateDir;
private File mDataDir;
private File mJournalDir;
private File mJournal;
@@ -145,13 +164,15 @@
mActivityManager = ActivityManagerNative.getDefault();
// Set up our bookkeeping
- mStateDir = new File(Environment.getDataDirectory(), "backup");
- mStateDir.mkdirs();
+ // !!! 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();
// Set up the backup-request journaling
- mJournalDir = new File(mStateDir, "pending");
- mJournalDir.mkdirs();
+ mJournalDir = new File(mBaseStateDir, "pending");
+ mJournalDir.mkdirs(); // creates mBaseStateDir along the way
makeJournalLocked(); // okay because no other threads are running yet
// Build our mapping of uid to backup client services. This implicitly
@@ -164,12 +185,19 @@
// 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_LOCAL;
- //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"));
@@ -228,6 +256,13 @@
}
}
+ // 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) {
@@ -273,11 +308,13 @@
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);
}
};
@@ -289,7 +326,7 @@
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;
@@ -336,9 +373,8 @@
case MSG_RUN_RESTORE:
{
- int token = msg.arg1;
- IBackupTransport transport = (IBackupTransport)msg.obj;
- (new PerformRestoreThread(transport, token)).start();
+ RestoreParams params = (RestoreParams)msg.obj;
+ (new PerformRestoreThread(params.transport, params.observer, params.token)).start();
break;
}
}
@@ -373,7 +409,6 @@
mBackupParticipants.put(uid, set);
}
set.add(pkg.applicationInfo);
- backUpPackageManagerData();
}
}
}
@@ -417,7 +452,6 @@
for (ApplicationInfo entry: set) {
if (entry.packageName.equals(pkg.packageName)) {
set.remove(entry);
- backUpPackageManagerData();
break;
}
}
@@ -460,34 +494,27 @@
addPackageParticipantsLockedInner(packageName, allApps);
}
- private void backUpPackageManagerData() {
- // No need to schedule a backup just for the metadata; just piggyback on
- // the next actual data backup.
- synchronized(this) {
- mDoPackageManager = true;
+ // The queue lock should be held when scheduling a backup pass
+ private void scheduleBackupPassLocked(long 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");
+ }
}
}
- // 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);
- }
-
// 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;
}
}
@@ -530,6 +557,19 @@
// 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) {
@@ -565,6 +605,7 @@
private static final String TAG = "PerformBackupThread";
IBackupTransport mTransport;
ArrayList<BackupRequest> mQueue;
+ File mStateDir;
File mJournal;
public PerformBackupThread(IBackupTransport transport, ArrayList<BackupRequest> queue,
@@ -572,32 +613,31 @@
mTransport = transport;
mQueue = queue;
mJournal = journal;
+
+ try {
+ mStateDir = new File(mBaseStateDir, transport.transportDirName());
+ } catch (RemoteException e) {
+ // can't happen; the transport is local
+ }
+ mStateDir.mkdirs();
}
@Override
public void run() {
if (DEBUG) Log.v(TAG, "Beginning backup of " + mQueue.size() + " targets");
- // First, back up the package manager metadata if necessary
- boolean doPackageManager;
- synchronized (BackupManagerService.this) {
- doPackageManager = mDoPackageManager;
- mDoPackageManager = false;
- }
- if (doPackageManager) {
- // The package manager doesn't have a proper <application> etc, but since
- // it's running here in the system process we can just set up its agent
- // directly and use a synthetic BackupRequest.
- if (DEBUG) Log.i(TAG, "Running PM backup pass as well");
-
- PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(
- mPackageManager, allAgentPackages());
- BackupRequest pmRequest = new BackupRequest(new ApplicationInfo(), false);
- pmRequest.appInfo.packageName = PACKAGE_MANAGER_SENTINEL;
- processOneBackup(pmRequest,
- IBackupAgent.Stub.asInterface(pmAgent.onBind()),
- mTransport);
- }
+ // The package manager doesn't have a proper <application> etc, but since
+ // it's running here in the system process we can just set up its agent
+ // directly and use a synthetic BackupRequest. We always run this pass
+ // because it's cheap and this way we guarantee that we don't get out of
+ // step even if we're selecting among various transports at run time.
+ PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(
+ mPackageManager, allAgentPackages());
+ BackupRequest pmRequest = new BackupRequest(new ApplicationInfo(), false);
+ pmRequest.appInfo.packageName = PACKAGE_MANAGER_SENTINEL;
+ processOneBackup(pmRequest,
+ IBackupAgent.Stub.asInterface(pmAgent.onBind()),
+ mTransport);
// Now run all the backups in our queue
doQueuedBackups(mTransport);
@@ -759,8 +799,10 @@
class PerformRestoreThread extends Thread {
private IBackupTransport mTransport;
- private int mToken;
+ private IRestoreObserver mObserver;
+ private long mToken;
private RestoreSet mImage;
+ private File mStateDir;
class RestoreRequest {
public PackageInfo app;
@@ -772,9 +814,18 @@
}
}
- PerformRestoreThread(IBackupTransport transport, int restoreSetToken) {
+ PerformRestoreThread(IBackupTransport transport, IRestoreObserver observer,
+ long restoreSetToken) {
mTransport = transport;
+ mObserver = observer;
mToken = restoreSetToken;
+
+ try {
+ mStateDir = new File(mBaseStateDir, transport.transportDirName());
+ } catch (RemoteException e) {
+ // can't happen; the transport is local
+ }
+ mStateDir.mkdirs();
}
@Override
@@ -795,6 +846,8 @@
* 4. shut down the transport
*/
+ int error = -1; // assume error
+
// build the set of apps to restore
try {
RestoreSet[] images = mTransport.getAvailableRestoreSets();
@@ -821,9 +874,19 @@
List<PackageInfo> agentPackages = allAgentPackages();
restorePackages.addAll(agentPackages);
- // STOPSHIP TODO: pick out the set for this token (instead of images[0])
- long token = images[0].token;
- if (!mTransport.startRestore(token, restorePackages.toArray(new PackageInfo[0]))) {
+ // let the observer know that we're running
+ if (mObserver != null) {
+ try {
+ // !!! TODO: get an actual count from the transport after
+ // its startRestore() runs?
+ mObserver.restoreStarting(restorePackages.size());
+ } catch (RemoteException e) {
+ Log.d(TAG, "Restore observer died at restoreStarting");
+ mObserver = null;
+ }
+ }
+
+ if (!mTransport.startRestore(mToken, restorePackages.toArray(new PackageInfo[0]))) {
// STOPSHIP TODO: Handle the failure somehow?
Log.e(TAG, "Error starting restore operation");
return;
@@ -848,6 +911,7 @@
mPackageManager, agentPackages);
processOneRestore(omPackage, 0, IBackupAgent.Stub.asInterface(pmAgent.onBind()));
+ int count = 0;
for (;;) {
packageName = mTransport.nextRestorePackage();
if (packageName == null) {
@@ -858,6 +922,16 @@
break;
}
+ if (mObserver != null) {
+ ++count;
+ try {
+ mObserver.onUpdate(count);
+ } catch (RemoteException e) {
+ Log.d(TAG, "Restore observer died in onUpdate");
+ mObserver = null;
+ }
+ }
+
Metadata metaInfo = pmAgent.getRestoredMetadata(packageName);
if (metaInfo == null) {
Log.e(TAG, "Missing metadata for " + packageName);
@@ -901,6 +975,9 @@
mActivityManager.unbindBackupAgent(packageInfo.applicationInfo);
}
}
+
+ // if we get this far, report success to the observer
+ error = 0;
} catch (NameNotFoundException e) {
// STOPSHIP TODO: Handle the failure somehow?
Log.e(TAG, "Invalid paackage restoring data", e);
@@ -913,6 +990,14 @@
} catch (RemoteException e) {
Log.e(TAG, "Error finishing restore", e);
}
+
+ if (mObserver != null) {
+ try {
+ mObserver.restoreFinished(error);
+ } catch (RemoteException e) {
+ Log.d(TAG, "Restore observer died at restoreFinished");
+ }
+ }
}
}
@@ -1016,7 +1101,7 @@
if (DEBUG) {
int numKeys = mPendingBackups.size();
- Log.d(TAG, "Scheduling backup for " + numKeys + " participants:");
+ Log.d(TAG, "Now awaiting backup for " + numKeys + " participants:");
for (BackupRequest b : mPendingBackups.values()) {
Log.d(TAG, " + " + b + " agent=" + b.appInfo.backupAgentName);
}
@@ -1046,7 +1131,7 @@
// Run a backup pass immediately for any applications that have declared
// that they have pending updates.
public void backupNow() throws RemoteException {
- mContext.enforceCallingPermission("android.permission.BACKUP", "tryBackupNow");
+ mContext.enforceCallingPermission("android.permission.BACKUP", "backupNow");
if (DEBUG) Log.v(TAG, "Scheduling immediate backup pass");
synchronized (mQueueLock) {
@@ -1054,21 +1139,78 @@
}
}
- // 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
}
- // Select which transport to use for the next backup operation
- public int selectBackupTransport(int transportId) {
+ // 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;
+ }
+
+ // 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
@@ -1106,7 +1248,7 @@
}
// Hand off a restore session
- public IRestoreSession beginRestoreSession(int transportID) {
+ public IRestoreSession beginRestoreSession(String transport) {
mContext.enforceCallingPermission("android.permission.BACKUP", "beginRestoreSession");
synchronized(this) {
@@ -1114,7 +1256,7 @@
Log.d(TAG, "Restore session requested but one already active");
return null;
}
- mActiveRestoreSession = new RestoreSession(transportID);
+ mActiveRestoreSession = new RestoreSession(transport);
}
return mActiveRestoreSession;
}
@@ -1127,8 +1269,8 @@
private IBackupTransport mRestoreTransport = null;
RestoreSet[] mRestoreSets = null;
- RestoreSession(int transportID) {
- mRestoreTransport = getTransport(transportID);
+ RestoreSession(String transport) {
+ mRestoreTransport = getTransport(transport);
}
// --- Binder interface ---
@@ -1150,15 +1292,15 @@
}
}
- public int performRestore(int token) throws android.os.RemoteException {
+ public int performRestore(long token, IRestoreObserver observer)
+ throws android.os.RemoteException {
mContext.enforceCallingPermission("android.permission.BACKUP", "performRestore");
if (mRestoreSets != null) {
for (int i = 0; i < mRestoreSets.length; i++) {
if (token == mRestoreSets[i].token) {
- Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE,
- mRestoreTransport);
- msg.arg1 = token;
+ Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
+ msg.obj = new RestoreParams(mRestoreTransport, observer, token);
mBackupHandler.sendMessage(msg);
return 0;
}
@@ -1189,6 +1331,11 @@
@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++) {
@@ -1197,14 +1344,12 @@
pw.println(uid);
HashSet<ApplicationInfo> participants = mBackupParticipants.valueAt(i);
for (ApplicationInfo app: participants) {
- pw.print(" ");
- pw.println(app.toString());
+ pw.println(" " + app.toString());
}
}
pw.println("Pending: " + mPendingBackups.size());
for (BackupRequest req : mPendingBackups.values()) {
- pw.print(" ");
- pw.println(req);
+ pw.println(" " + req);
}
}
}
diff --git a/services/java/com/android/server/PackageManagerBackupAgent.java b/services/java/com/android/server/PackageManagerBackupAgent.java
index 66fb86d..16f14e8 100644
--- a/services/java/com/android/server/PackageManagerBackupAgent.java
+++ b/services/java/com/android/server/PackageManagerBackupAgent.java
@@ -120,7 +120,10 @@
// write its signature block to the output, keyed on the package name.
for (PackageInfo pkg : mAllPackages) {
String packName = pkg.packageName;
- if (!existing.contains(packName)) {
+ if (packName.equals(GLOBAL_METADATA_KEY)) {
+ // We've already handled the metadata key; skip it here
+ continue;
+ } else if (!existing.contains(packName)) {
// We haven't stored this app's signatures yet, so we do that now
try {
PackageInfo info = mPackageManager.getPackageInfo(packName,
diff --git a/services/java/com/android/server/Watchdog.java b/services/java/com/android/server/Watchdog.java
index fef3598..68bf4fb 100644
--- a/services/java/com/android/server/Watchdog.java
+++ b/services/java/com/android/server/Watchdog.java
@@ -504,6 +504,7 @@
if (mPhoneMemMonitor.checkLocked(curTime, mPhonePid,
mPhonePss)) {
// Just kill the phone process and let it restart.
+ Log.i(TAG, "Watchdog is killing the phone process");
Process.killProcess(mPhonePid);
}
} else {
@@ -848,6 +849,7 @@
// Only kill the process if the debugger is not attached.
if (!Debug.isDebuggerConnected()) {
+ Log.i(TAG, "Watchdog is killing the system process");
Process.killProcess(Process.myPid());
}
}
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 3b47ae7..9bad153 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -77,6 +77,7 @@
import android.os.SystemProperties;
import android.os.TokenWatcher;
import android.provider.Settings;
+import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.Log;
import android.util.SparseIntArray;
@@ -415,7 +416,8 @@
final Rect mTempRect = new Rect();
final Configuration mTempConfiguration = new Configuration();
-
+ int screenLayout = Configuration.SCREENLAYOUT_UNDEFINED;
+
public static WindowManagerService main(Context context,
PowerManagerService pm, boolean haveInputMethods) {
WMThread thr = new WMThread(context, pm, haveInputMethods);
@@ -3724,6 +3726,40 @@
orientation = Configuration.ORIENTATION_LANDSCAPE;
}
config.orientation = orientation;
+
+ if (screenLayout == Configuration.SCREENLAYOUT_UNDEFINED) {
+ // Note we only do this once because at this point we don't
+ // expect the screen to change in this way at runtime, and want
+ // to avoid all of this computation for every config change.
+ DisplayMetrics dm = new DisplayMetrics();
+ mDisplay.getMetrics(dm);
+ int longSize = dw;
+ int shortSize = dh;
+ if (longSize < shortSize) {
+ int tmp = longSize;
+ longSize = shortSize;
+ shortSize = tmp;
+ }
+ longSize = (int)(longSize/dm.density);
+ shortSize = (int)(shortSize/dm.density);
+
+ // These semi-magic numbers define our compatibility modes for
+ // applications with different screens. Don't change unless you
+ // make sure to test lots and lots of apps!
+ if (longSize < 470) {
+ // This is shorter than an HVGA normal density screen (which
+ // is 480 pixels on its long side).
+ screenLayout = Configuration.SCREENLAYOUT_SMALL;
+ } else if (longSize > 490 && shortSize > 330) {
+ // This is larger than an HVGA normal density screen (which
+ // is 480x320 pixels).
+ screenLayout = Configuration.SCREENLAYOUT_LARGE;
+ } else {
+ screenLayout = Configuration.SCREENLAYOUT_NORMAL;
+ }
+ }
+ config.screenLayout = screenLayout;
+
config.keyboardHidden = Configuration.KEYBOARDHIDDEN_NO;
config.hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_NO;
mPolicy.adjustConfigurationLw(config);
@@ -3798,7 +3834,7 @@
"dispatchPointer " + ev);
Object targetObj = mKeyWaiter.waitForNextEventTarget(null, qev,
- ev, true, false);
+ ev, true, false, pid, uid);
int action = ev.getAction();
@@ -3996,7 +4032,7 @@
TAG, "dispatchTrackball [" + ev.getAction() +"] <" + ev.getX() + ", " + ev.getY() + ">");
Object focusObj = mKeyWaiter.waitForNextEventTarget(null, qev,
- ev, false, false);
+ ev, false, false, pid, uid);
if (focusObj == null) {
Log.w(TAG, "No focus window, dropping trackball: " + ev);
if (qev != null) {
@@ -4067,7 +4103,7 @@
if (DEBUG_INPUT) Log.v(TAG, "Dispatch key: " + event);
Object focusObj = mKeyWaiter.waitForNextEventTarget(event, null,
- null, false, false);
+ null, false, false, pid, uid);
if (focusObj == null) {
Log.w(TAG, "No focus window, dropping: " + event);
return INJECT_FAILED;
@@ -4184,10 +4220,14 @@
KeyEvent newEvent = new KeyEvent(downTime, eventTime, action, code, repeatCount, metaState,
deviceId, scancode, KeyEvent.FLAG_FROM_SYSTEM);
- int result = dispatchKey(newEvent, Binder.getCallingPid(), Binder.getCallingUid());
+ final int pid = Binder.getCallingPid();
+ final int uid = Binder.getCallingUid();
+ final long ident = Binder.clearCallingIdentity();
+ final int result = dispatchKey(newEvent, pid, uid);
if (sync) {
- mKeyWaiter.waitForNextEventTarget(null, null, null, false, true);
+ mKeyWaiter.waitForNextEventTarget(null, null, null, false, true, pid, uid);
}
+ Binder.restoreCallingIdentity(ident);
switch (result) {
case INJECT_NO_PERMISSION:
throw new SecurityException(
@@ -4208,10 +4248,14 @@
* @return Returns true if event was dispatched, false if it was dropped for any reason
*/
public boolean injectPointerEvent(MotionEvent ev, boolean sync) {
- int result = dispatchPointer(null, ev, Binder.getCallingPid(), Binder.getCallingUid());
+ final int pid = Binder.getCallingPid();
+ final int uid = Binder.getCallingUid();
+ final long ident = Binder.clearCallingIdentity();
+ final int result = dispatchPointer(null, ev, pid, uid);
if (sync) {
- mKeyWaiter.waitForNextEventTarget(null, null, null, false, true);
+ mKeyWaiter.waitForNextEventTarget(null, null, null, false, true, pid, uid);
}
+ Binder.restoreCallingIdentity(ident);
switch (result) {
case INJECT_NO_PERMISSION:
throw new SecurityException(
@@ -4232,10 +4276,14 @@
* @return Returns true if event was dispatched, false if it was dropped for any reason
*/
public boolean injectTrackballEvent(MotionEvent ev, boolean sync) {
- int result = dispatchTrackball(null, ev, Binder.getCallingPid(), Binder.getCallingUid());
+ final int pid = Binder.getCallingPid();
+ final int uid = Binder.getCallingUid();
+ final long ident = Binder.clearCallingIdentity();
+ final int result = dispatchTrackball(null, ev, pid, uid);
if (sync) {
- mKeyWaiter.waitForNextEventTarget(null, null, null, false, true);
+ mKeyWaiter.waitForNextEventTarget(null, null, null, false, true, pid, uid);
}
+ Binder.restoreCallingIdentity(ident);
switch (result) {
case INJECT_NO_PERMISSION:
throw new SecurityException(
@@ -4344,7 +4392,7 @@
*/
Object waitForNextEventTarget(KeyEvent nextKey, QueuedEvent qev,
MotionEvent nextMotion, boolean isPointerEvent,
- boolean failIfTimeout) {
+ boolean failIfTimeout, int callingPid, int callingUid) {
long startTime = SystemClock.uptimeMillis();
long keyDispatchingTimeout = 5 * 1000;
long waitedFor = 0;
@@ -4362,7 +4410,7 @@
", mLastWin=" + mLastWin);
if (targetIsNew) {
Object target = findTargetWindow(nextKey, qev, nextMotion,
- isPointerEvent);
+ isPointerEvent, callingPid, callingUid);
if (target == SKIP_TARGET_TOKEN) {
// The user has pressed a special key, and we are
// dropping all pending events before it.
@@ -4538,7 +4586,8 @@
}
Object findTargetWindow(KeyEvent nextKey, QueuedEvent qev,
- MotionEvent nextMotion, boolean isPointerEvent) {
+ MotionEvent nextMotion, boolean isPointerEvent,
+ int callingPid, int callingUid) {
mOutsideTouchTargets = null;
if (nextKey != null) {
@@ -4547,9 +4596,16 @@
final int repeatCount = nextKey.getRepeatCount();
final boolean down = nextKey.getAction() != KeyEvent.ACTION_UP;
boolean dispatch = mKeyWaiter.checkShouldDispatchKey(keycode);
+
if (!dispatch) {
- mPolicy.interceptKeyTi(null, keycode,
- nextKey.getMetaState(), down, repeatCount);
+ if (callingUid == 0 ||
+ mContext.checkPermission(
+ android.Manifest.permission.INJECT_EVENTS,
+ callingPid, callingUid)
+ == PackageManager.PERMISSION_GRANTED) {
+ mPolicy.interceptKeyTi(null, keycode,
+ nextKey.getMetaState(), down, repeatCount);
+ }
Log.w(TAG, "Event timeout during app switch: dropping "
+ nextKey);
return SKIP_TARGET_TOKEN;
@@ -4564,9 +4620,16 @@
wakeupIfNeeded(focus, LocalPowerManager.BUTTON_EVENT);
- if (mPolicy.interceptKeyTi(focus,
- keycode, nextKey.getMetaState(), down, repeatCount)) {
- return CONSUMED_EVENT_TOKEN;
+ if (callingUid == 0 ||
+ (focus != null && callingUid == focus.mSession.mUid) ||
+ mContext.checkPermission(
+ android.Manifest.permission.INJECT_EVENTS,
+ callingPid, callingUid)
+ == PackageManager.PERMISSION_GRANTED) {
+ if (mPolicy.interceptKeyTi(focus,
+ keycode, nextKey.getMetaState(), down, repeatCount)) {
+ return CONSUMED_EVENT_TOKEN;
+ }
}
return focus;
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 0d9d2b0..6d04b6b 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -1518,8 +1518,7 @@
}
}
- final BatteryStatsImpl bstats =
- (BatteryStatsImpl) mBatteryStatsService.getActiveStatistics();
+ final BatteryStatsImpl bstats = mBatteryStatsService.getActiveStatistics();
synchronized(bstats) {
synchronized(mPidsSelfLocked) {
if (haveNewCpuStats) {
@@ -1534,7 +1533,7 @@
ps.addCpuTimeLocked(st.rel_utime, st.rel_stime);
} else {
BatteryStatsImpl.Uid.Proc ps =
- bstats.getProcessStatsLocked(st.name);
+ bstats.getProcessStatsLocked(st.name, st.pid);
if (ps != null) {
ps.addCpuTimeLocked(st.rel_utime, st.rel_stime);
}
@@ -10473,8 +10472,17 @@
// done with this agent
public void unbindBackupAgent(ApplicationInfo appInfo) {
if (DEBUG_BACKUP) Log.v(TAG, "unbindBackupAgent: " + appInfo);
+ if (appInfo == null) {
+ Log.w(TAG, "unbind backup agent for null app");
+ return;
+ }
synchronized(this) {
+ if (mBackupAppName == null) {
+ Log.w(TAG, "Unbinding backup agent with no active backup");
+ return;
+ }
+
if (!mBackupAppName.equals(appInfo.packageName)) {
Log.e(TAG, "Unbind of " + appInfo + " but is not the current backup target");
return;
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index d3942fc..dbe8431 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -701,17 +701,6 @@
}
/**
- * Note: calls extractNetworkPortion(), so do not use for
- * SIM EF[ADN] style records
- *
- * Exceptions thrown if extractNetworkPortion(s).length() == 0
- */
- public static byte[]
- networkPortionToCalledPartyBCD(String s) {
- return numberToCalledPartyBCD(extractNetworkPortion(s));
- }
-
- /**
* Return true iff the network portion of <code>address</code> is,
* as far as we can tell on the device, suitable for use as an SMS
* destination address.
@@ -744,12 +733,25 @@
}
/**
+ * Note: calls extractNetworkPortion(), so do not use for
+ * SIM EF[ADN] style records
+ *
+ * Returns null if network portion is empty.
+ */
+ public static byte[]
+ networkPortionToCalledPartyBCD(String s) {
+ String networkPortion = extractNetworkPortion(s);
+ return numberToCalledPartyBCDHelper(networkPortion, false);
+ }
+
+ /**
* Same as {@link #networkPortionToCalledPartyBCD}, but includes a
* one-byte length prefix.
*/
public static byte[]
networkPortionToCalledPartyBCDWithLength(String s) {
- return numberToCalledPartyBCDWithLength(extractNetworkPortion(s));
+ String networkPortion = extractNetworkPortion(s);
+ return numberToCalledPartyBCDHelper(networkPortion, true);
}
/**
@@ -761,61 +763,46 @@
*/
public static byte[]
numberToCalledPartyBCD(String number) {
- // The extra byte required for '+' is taken into consideration while calculating
- // length of ret.
- int size = (hasPlus(number) ? number.length() - 1 : number.length());
- byte[] ret = new byte[(size + 1) / 2 + 1];
-
- return numberToCalledPartyBCDHelper(ret, 0, number);
+ return numberToCalledPartyBCDHelper(number, false);
}
/**
- * Same as {@link #numberToCalledPartyBCD}, but includes a
- * one-byte length prefix.
+ * If includeLength is true, prepend a one-byte length value to
+ * the return array.
*/
private static byte[]
- numberToCalledPartyBCDWithLength(String number) {
- // The extra byte required for '+' is taken into consideration while calculating
- // length of ret.
- int size = (hasPlus(number) ? number.length() - 1 : number.length());
- int length = (size + 1) / 2 + 1;
- byte[] ret = new byte[length + 1];
+ numberToCalledPartyBCDHelper(String number, boolean includeLength) {
+ int numberLenReal = number.length();
+ int numberLenEffective = numberLenReal;
+ boolean hasPlus = number.indexOf('+') != -1;
+ if (hasPlus) numberLenEffective--;
- ret[0] = (byte) (length & 0xff);
- return numberToCalledPartyBCDHelper(ret, 1, number);
- }
+ if (numberLenEffective == 0) return null;
- private static boolean
- hasPlus(String s) {
- return s.indexOf('+') >= 0;
- }
+ int resultLen = (numberLenEffective + 1) / 2; // Encoded numbers require only 4 bits each.
+ int extraBytes = 1; // Prepended TOA byte.
+ if (includeLength) extraBytes++; // Optional prepended length byte.
+ resultLen += extraBytes;
- private static byte[]
- numberToCalledPartyBCDHelper(byte[] ret, int offset, String number) {
- if (hasPlus(number)) {
- number = number.replaceAll("\\+", "");
- ret[offset] = (byte) TOA_International;
- } else {
- ret[offset] = (byte) TOA_Unknown;
+ byte[] result = new byte[resultLen];
+
+ int digitCount = 0;
+ for (int i = 0; i < numberLenReal; i++) {
+ char c = number.charAt(i);
+ if (c == '+') continue;
+ int shift = ((digitCount & 0x01) == 1) ? 4 : 0;
+ result[extraBytes + (digitCount >> 1)] |= (byte)((charToBCD(c) & 0x0F) << shift);
+ digitCount++;
}
- int size = number.length();
- int curChar = 0;
- int countFullBytes = ret.length - offset - 1 - ((size - curChar) & 1);
- for (int i = 1; i < 1 + countFullBytes; i++) {
- ret[offset + i]
- = (byte) ((charToBCD(number.charAt(curChar++)))
- | (charToBCD(number.charAt(curChar++))) << 4);
- }
+ // 1-fill any trailing odd nibble/quartet.
+ if ((digitCount & 0x01) == 1) result[extraBytes + (digitCount >> 1)] |= 0xF0;
- // The left-over octet for odd-length phone numbers should be
- // filled with 0xf.
- if (countFullBytes + offset < ret.length - 1) {
- ret[ret.length - 1]
- = (byte) (charToBCD(number.charAt(curChar))
- | (0xf << 4));
- }
- return ret;
+ int offset = 0;
+ if (includeLength) result[offset++] = (byte)(resultLen - 1);
+ result[offset] = (byte)(hasPlus ? TOA_International : TOA_Unknown);
+
+ return result;
}
/** all of 'a' up to len must match non-US trunk prefix ('0') */
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index a79eb3a..bf5df88 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -612,6 +612,21 @@
}
/**
+ * Returns the voice mail count.
+ * <p>
+ * Requires Permission:
+ * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+ * @hide
+ */
+ public int getVoiceMessageCount() {
+ try {
+ return getITelephony().getVoiceMessageCount();
+ } catch (RemoteException ex) {
+ }
+ return 0;
+ }
+
+ /**
* Retrieves the alphabetic identifier associated with the voice
* mail number.
* <p>
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 @@
//***** 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/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 6e6f64c..d83b135 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -241,7 +241,7 @@
/**
* Returns the unread count of voicemails
*/
- int getCountVoiceMessages();
+ int getVoiceMessageCount();
}
diff --git a/telephony/java/com/android/internal/telephony/IccRecords.java b/telephony/java/com/android/internal/telephony/IccRecords.java
index 114094b..ea24c25 100644
--- a/telephony/java/com/android/internal/telephony/IccRecords.java
+++ b/telephony/java/com/android/internal/telephony/IccRecords.java
@@ -196,7 +196,7 @@
* If not available (eg, on an older CPHS SIM) -1 is returned if
* getVoiceMessageWaiting() is true
*/
- public int getCountVoiceMessages() {
+ public int getVoiceMessageCount() {
return countVoiceMessages;
}
diff --git a/telephony/java/com/android/internal/telephony/Phone.java b/telephony/java/com/android/internal/telephony/Phone.java
index cdbfd61..c8d384d 100644
--- a/telephony/java/com/android/internal/telephony/Phone.java
+++ b/telephony/java/com/android/internal/telephony/Phone.java
@@ -831,7 +831,7 @@
* Returns unread voicemail count. This count is shown when the voicemail
* notification is expanded.<p>
*/
- int getCountVoiceMessages();
+ int getVoiceMessageCount();
/**
* Returns the alpha tag associated with the voice mail number.
diff --git a/telephony/java/com/android/internal/telephony/PhoneBase.java b/telephony/java/com/android/internal/telephony/PhoneBase.java
index 0783fe0..a26e729 100644
--- a/telephony/java/com/android/internal/telephony/PhoneBase.java
+++ b/telephony/java/com/android/internal/telephony/PhoneBase.java
@@ -631,6 +631,11 @@
mNotifier.notifyDataActivity(this);
}
+ public void notifyMessageWaitingIndicator() {
+ // This function is added to send the notification to DefaultPhoneNotifier.
+ mNotifier.notifyMessageWaitingChanged(this);
+ }
+
public void notifyDataConnection(String reason) {
mNotifier.notifyDataConnection(this, reason);
}
@@ -638,7 +643,7 @@
public abstract String getPhoneName();
/** @hide */
- public int getCountVoiceMessages(){
+ public int getVoiceMessageCount(){
return 0;
}
diff --git a/telephony/java/com/android/internal/telephony/PhoneProxy.java b/telephony/java/com/android/internal/telephony/PhoneProxy.java
index 4bb24dc..5b3c8dd 100644
--- a/telephony/java/com/android/internal/telephony/PhoneProxy.java
+++ b/telephony/java/com/android/internal/telephony/PhoneProxy.java
@@ -435,8 +435,8 @@
}
/** @hide */
- public int getCountVoiceMessages(){
- return mActivePhone.getCountVoiceMessages();
+ public int getVoiceMessageCount(){
+ return mActivePhone.getVoiceMessageCount();
}
public String getVoiceMailAlphaTag() {
diff --git a/telephony/java/com/android/internal/telephony/RIL.java b/telephony/java/com/android/internal/telephony/RIL.java
index 9f780c9..4db3e5b 100644
--- a/telephony/java/com/android/internal/telephony/RIL.java
+++ b/telephony/java/com/android/internal/telephony/RIL.java
@@ -1365,7 +1365,6 @@
RILRequest rr
= RILRequest.obtain(RIL_REQUEST_CDMA_SMS_ACKNOWLEDGE, result);
- rr.mp.writeInt(2);
rr.mp.writeInt(success ? 0 : 1); //RIL_CDMA_SMS_ErrorClass
// cause code according to X.S004-550E
rr.mp.writeInt(cause);
diff --git a/telephony/java/com/android/internal/telephony/SMSDispatcher.java b/telephony/java/com/android/internal/telephony/SMSDispatcher.java
index a3016fa..890ea63 100644
--- a/telephony/java/com/android/internal/telephony/SMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/SMSDispatcher.java
@@ -78,6 +78,7 @@
protected static final String[] RAW_PROJECTION = new String[] {
"pdu",
"sequence",
+ "destination_port",
};
static final int MAIL_SEND_SMS = 1;
@@ -289,7 +290,13 @@
sms = (SmsMessage) ar.result;
try {
if (mStorageAvailable) {
- dispatchMessage(sms.mWrappedSmsMessage);
+ int result = dispatchMessage(sms.mWrappedSmsMessage);
+ if (result != Activity.RESULT_OK) {
+ // RESULT_OK means that message was broadcast for app(s) to handle.
+ // Any other result, we should ack here.
+ boolean handled = (result == Intents.RESULT_SMS_HANDLED);
+ acknowledgeLastIncomingSms(handled, result, null);
+ }
} else {
acknowledgeLastIncomingSms(false, Intents.RESULT_SMS_OUT_OF_MEMORY, null);
}
@@ -469,8 +476,11 @@
* Dispatches an incoming SMS messages.
*
* @param sms the incoming message from the phone
+ * @return a result code from {@link Telephony.Sms.Intents}, or
+ * {@link Activity#RESULT_OK} if the message has been broadcast
+ * to applications
*/
- protected abstract void dispatchMessage(SmsMessageBase sms);
+ protected abstract int dispatchMessage(SmsMessageBase sms);
/**
@@ -478,8 +488,11 @@
* the part is stored for later processing.
*
* NOTE: concatRef (naturally) needs to be non-null, but portAddrs can be null.
+ * @return a result code from {@link Telephony.Sms.Intents}, or
+ * {@link Activity#RESULT_OK} if the message has been broadcast
+ * to applications
*/
- protected void processMessagePart(SmsMessageBase sms,
+ protected int processMessagePart(SmsMessageBase sms,
SmsHeader.ConcatRef concatRef, SmsHeader.PortAddrs portAddrs) {
// Lookup all other related parts
@@ -506,8 +519,7 @@
values.put("destination_port", portAddrs.destPort);
}
mResolver.insert(mRawUri, values);
- acknowledgeLastIncomingSms(true, Intents.RESULT_SMS_HANDLED, null);
- return;
+ return Intents.RESULT_SMS_HANDLED;
}
// All the parts are in place, deal with them
@@ -529,8 +541,7 @@
} catch (SQLException e) {
Log.e(TAG, "Can't access multipart SMS database", e);
// TODO: Would OUT_OF_MEMORY be more appropriate?
- acknowledgeLastIncomingSms(false, Intents.RESULT_SMS_GENERIC_ERROR, null);
- return;
+ return Intents.RESULT_SMS_GENERIC_ERROR;
} finally {
if (cursor != null) cursor.close();
}
@@ -555,7 +566,7 @@
output.write(data, 0, data.length);
}
// Handle the PUSH
- mWapPush.dispatchWapPdu(output.toByteArray());
+ return mWapPush.dispatchWapPdu(output.toByteArray());
} else {
// The messages were sent to a port, so concoct a URI for it
dispatchPortAddressedPdus(pdus, portAddrs.destPort);
@@ -564,6 +575,7 @@
// The messages were not sent to a port
dispatchPdus(pdus);
}
+ return Activity.RESULT_OK;
}
/**
diff --git a/telephony/java/com/android/internal/telephony/WapPushOverSms.java b/telephony/java/com/android/internal/telephony/WapPushOverSms.java
index a9aacda..99709406 100644
--- a/telephony/java/com/android/internal/telephony/WapPushOverSms.java
+++ b/telephony/java/com/android/internal/telephony/WapPushOverSms.java
@@ -16,8 +16,10 @@
package com.android.internal.telephony;
+import android.app.Activity;
import android.content.Context;
import android.content.Intent;
+import android.provider.Telephony;
import android.provider.Telephony.Sms.Intents;
import android.util.Config;
import android.util.Log;
@@ -51,8 +53,11 @@
* wap-230-wsp-20010705-a section 8 for details on the WAP PDU format.
*
* @param pdu The WAP PDU, made up of one or more SMS PDUs
+ * @return a result code from {@link Telephony.Sms.Intents}, or
+ * {@link Activity#RESULT_OK} if the message has been broadcast
+ * to applications
*/
- public void dispatchWapPdu(byte[] pdu) {
+ public int dispatchWapPdu(byte[] pdu) {
if (Config.LOGD) Log.d(LOG_TAG, "Rx: " + IccUtils.bytesToHexString(pdu));
@@ -64,7 +69,7 @@
if ((pduType != WspTypeDecoder.PDU_TYPE_PUSH) &&
(pduType != WspTypeDecoder.PDU_TYPE_CONFIRMED_PUSH)) {
if (Config.LOGD) Log.w(LOG_TAG, "Received non-PUSH WAP PDU. Type = " + pduType);
- return;
+ return Intents.RESULT_SMS_HANDLED;
}
pduDecoder = new WspTypeDecoder(pdu);
@@ -77,7 +82,7 @@
*/
if (pduDecoder.decodeUintvarInteger(index) == false) {
if (Config.LOGD) Log.w(LOG_TAG, "Received PDU. Header Length error.");
- return;
+ return Intents.RESULT_SMS_GENERIC_ERROR;
}
headerLength = (int)pduDecoder.getValue32();
index += pduDecoder.getDecodedDataLength();
@@ -98,7 +103,7 @@
*/
if (pduDecoder.decodeContentType(index) == false) {
if (Config.LOGD) Log.w(LOG_TAG, "Received PDU. Header Content-Type error.");
- return;
+ return Intents.RESULT_SMS_GENERIC_ERROR;
}
int binaryContentType;
String mimeType = pduDecoder.getValueString();
@@ -128,7 +133,7 @@
Log.w(LOG_TAG,
"Received PDU. Unsupported Content-Type = " + binaryContentType);
}
- return;
+ return Intents.RESULT_SMS_HANDLED;
}
} else {
if (mimeType.equals(WspTypeDecoder.CONTENT_MIME_TYPE_B_DRM_RIGHTS_XML)) {
@@ -145,7 +150,7 @@
binaryContentType = WspTypeDecoder.CONTENT_TYPE_B_MMS;
} else {
if (Config.LOGD) Log.w(LOG_TAG, "Received PDU. Unknown Content-Type = " + mimeType);
- return;
+ return Intents.RESULT_SMS_HANDLED;
}
}
index += pduDecoder.getDecodedDataLength();
@@ -167,6 +172,7 @@
if (dispatchedByApplication == false) {
dispatchWapPdu_default(pdu, transactionId, pduType, mimeType, dataIndex);
}
+ return Activity.RESULT_OK;
}
private void dispatchWapPdu_default(
diff --git a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
index 9aa7eab..3362de8 100755
--- a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
@@ -19,6 +19,7 @@
import android.app.ActivityManagerNative;
import android.content.Context;
import android.content.Intent;
+import android.content.SharedPreferences;
import android.os.AsyncResult;
import android.os.Handler;
import android.os.Looper;
@@ -26,6 +27,7 @@
import android.os.Registrant;
import android.os.RegistrantList;
import android.os.SystemProperties;
+import android.preference.PreferenceManager;
import android.provider.Settings;
import android.telephony.CellLocation;
import android.telephony.PhoneNumberUtils;
@@ -40,6 +42,7 @@
import com.android.internal.telephony.Connection;
import com.android.internal.telephony.DataConnection;
import com.android.internal.telephony.IccCard;
+import com.android.internal.telephony.IccException;
import com.android.internal.telephony.IccFileHandler;
import com.android.internal.telephony.IccPhoneBookInterfaceManager;
import com.android.internal.telephony.IccSmsInterfaceManager;
@@ -65,6 +68,9 @@
// Default Emergency Callback Mode exit timer
private static final int DEFAULT_ECM_EXIT_TIMER_VALUE = 30000;
+ static final String VM_COUNT_CDMA = "vm_count_key_cdma";
+ private static final String VM_NUMBER_CDMA = "vm_number_key_cdma";
+ private String mVmNumber = null;
//***** Instance Variables
CdmaCallTracker mCT;
@@ -147,6 +153,9 @@
// This is needed to handle phone process crashes
String inEcm=SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE, "false");
mIsPhoneInECMState = inEcm.equals("true");
+
+ // Notify voicemails.
+ notifier.notifyMessageWaitingChanged(this);
}
public void dispose() {
@@ -300,7 +309,7 @@
public boolean
getMessageWaitingIndicator() {
- return mRuimRecords.getVoiceMessageWaiting();
+ return (getVoiceMessageCount() > 0);
}
public List<? extends MmiCode>
@@ -678,22 +687,33 @@
public void setVoiceMailNumber(String alphaTag,
String voiceMailNumber,
Message onComplete) {
- //mSIMRecords.setVoiceMailNumber(alphaTag, voiceMailNumber, onComplete);
- //TODO: Where do we have to store this value has to be clarified with QC
+ Message resp;
+ mVmNumber = voiceMailNumber;
+ resp = h.obtainMessage(EVENT_SET_VM_NUMBER_DONE, 0, 0, onComplete);
+ mRuimRecords.setVoiceMailNumber(alphaTag, mVmNumber, resp);
}
public String getVoiceMailNumber() {
- //TODO: Where can we get this value has to be clarified with QC
- //return mSIMRecords.getVoiceMailNumber();
-// throw new RuntimeException();
- return "*86";
+ String number = null;
+ SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
+ // TODO(Moto): The default value of voicemail number should be read from a system property
+ number = sp.getString(VM_NUMBER_CDMA, "*86");
+ return number;
}
/* Returns Number of Voicemails
* @hide
*/
- public int getCountVoiceMessages() {
- return mRuimRecords.getCountVoiceMessages();
+ public int getVoiceMessageCount() {
+ int voicemailCount = mRuimRecords.getVoiceMessageCount();
+ // If mRuimRecords.getVoiceMessageCount returns zero, then there is possibility
+ // that phone was power cycled and would have lost the voicemail count.
+ // So get the count from preferences.
+ if (voicemailCount == 0) {
+ SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
+ voicemailCount = sp.getInt(VM_COUNT_CDMA, 0);
+ }
+ return voicemailCount;
}
public String getVoiceMailAlphaTag() {
@@ -820,9 +840,10 @@
mRuimRecords.setVoiceMessageWaiting(1, mwi ? -1 : 0);
}
- public void
- notifyMessageWaitingIndicator() {
- mNotifier.notifyMessageWaitingChanged(this);
+ /* This function is overloaded to send number of voicemails instead of sending true/false */
+ /*package*/ void
+ updateMessageWaitingIndicator(int mwi) {
+ mRuimRecords.setVoiceMessageWaiting(1, mwi);
}
/**
@@ -984,6 +1005,19 @@
}
break;
+ case EVENT_SET_VM_NUMBER_DONE:{
+ ar = (AsyncResult)msg.obj;
+ if (IccException.class.isInstance(ar.exception)) {
+ storeVoiceMailNumber(mVmNumber);
+ ar.exception = null;
+ }
+ onComplete = (Message) ar.userObj;
+ if (onComplete != null) {
+ AsyncResult.forMessage(onComplete, ar.result, ar.exception);
+ onComplete.sendToTarget();
+ }
+ }
+
default:{
throw new RuntimeException("unexpected event not handled");
}
@@ -1198,4 +1232,16 @@
int defRoamInd = getServiceState().getCdmaDefaultRoamingIndicator();
return mEriManager.getCdmaEriText(roamInd, defRoamInd);
}
+
+ /**
+ * Store the voicemail number in preferences
+ */
+ private void storeVoiceMailNumber(String number) {
+ // Update the preference value of voicemail number
+ SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
+ SharedPreferences.Editor editor = sp.edit();
+ editor.putString(VM_NUMBER_CDMA, number);
+ editor.commit();
+ }
+
}
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java
index da9fd0c4..ed2ea90 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java
@@ -253,11 +253,7 @@
// Always unmute when answering a new call
setMute(false);
cm.acceptCall(obtainCompleteMessage());
- } else if ((foregroundCall.connections.size() > 0) &&
- (ringingCall.getState() == CdmaCall.State.WAITING)) {
- // TODO(Moto): jsh asks, "Is this check necessary? I don't think it should be
- // possible to have no fg connection and a WAITING call, but if we should hit
- // this situation, is a CallStateExcetion appropriate?"
+ } else if (ringingCall.getState() == CdmaCall.State.WAITING) {
CdmaConnection cwConn = (CdmaConnection)(ringingCall.getLatestConnection());
// Since there is no network response for supplimentary
@@ -530,10 +526,6 @@
CdmaConnection cn = (CdmaConnection)foregroundCall.connections.get(n);
droppedDuringPoll.add(cn);
}
- // TODO(Moto): jsh asks, "Are we sure we don't need to do this for
- // ringingCall as well? What if there's a WAITING call (ie, UI timer
- // hasn't expired, moving it to DISCONNECTED)? How/when will it
- // transition to DISCONNECTED?"
}
foregroundCall.setGeneric(false);
// Dropped connections are removed from the CallTracker
@@ -681,8 +673,12 @@
// set the ringing call state to IDLE here to avoid a race condition
// where a new call waiting could get a hang up from an old call
// waiting ringingCall.
- // TODO(Moto): jsh asks, "Should we call conn.ondisconnect() here or Somewhere?"
- ringingCall.detach(conn);
+ //
+ // PhoneApp does the call log itself since only PhoneApp knows
+ // the hangup reason is user ignoring or timing out. So conn.onDisconnect()
+ // is not called here. Instead, conn.onLocalDisconnect() is called.
+ conn.onLocalDisconnect();
+ phone.notifyCallStateChanged();
return;
} else {
try {
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaConnection.java b/telephony/java/com/android/internal/telephony/cdma/CdmaConnection.java
index 588bdf0..025382d 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaConnection.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaConnection.java
@@ -452,12 +452,7 @@
this.cause = cause;
if (!disconnected) {
- index = -1;
-
- disconnectTime = System.currentTimeMillis();
- duration = SystemClock.elapsedRealtime() - connectTimeReal;
- disconnected = true;
-
+ doDisconnect();
if (Config.LOGD) Log.d(LOG_TAG,
"[CDMAConn] onDisconnect: cause=" + cause);
@@ -470,6 +465,21 @@
releaseWakeLock();
}
+ /** Called when the call waiting connection has been hung up */
+ /*package*/ void
+ onLocalDisconnect() {
+ if (!disconnected) {
+ doDisconnect();
+ if (Config.LOGD) Log.d(LOG_TAG,
+ "[CDMAConn] onLoalDisconnect" );
+
+ if (parent != null) {
+ parent.detach(this);
+ }
+ }
+ releaseWakeLock();
+ }
+
// Returns true if state has changed, false if nothing changed
/*package*/ boolean
update (DriverCall dc) {
@@ -587,6 +597,14 @@
}
private void
+ doDisconnect() {
+ index = -1;
+ disconnectTime = System.currentTimeMillis();
+ duration = SystemClock.elapsedRealtime() - connectTimeReal;
+ disconnected = true;
+ }
+
+ private void
onStartedHolding() {
holdingStartTime = SystemClock.elapsedRealtime();
}
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.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.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 @@
// 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 @@
startNetStatPoll();
// reset reconnect timer
nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS;
+ mReregisterOnReconnectFailure = false;
}
private void resetPollStats() {
@@ -619,6 +616,21 @@
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 @@
// 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 @@
// 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 @@
} 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/CdmaSMSDispatcher.java b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
index 2b4a700..ecdc8f6 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
@@ -20,11 +20,14 @@
import android.app.Activity;
import android.app.PendingIntent;
import android.content.ContentValues;
+import android.content.SharedPreferences;
import android.database.Cursor;
import android.database.SQLException;
import android.os.AsyncResult;
import android.os.Message;
+import android.provider.Telephony;
import android.provider.Telephony.Sms.Intents;
+import android.preference.PreferenceManager;
import android.util.Config;
import android.util.Log;
@@ -64,17 +67,12 @@
Log.d(TAG, "handleStatusReport is a special GSM function, should never be called in CDMA!");
}
- /**
- * Dispatches an incoming SMS messages.
- *
- * @param smsb the incoming message from the phone
- */
- protected void dispatchMessage(SmsMessageBase smsb) {
+ /** {@inheritDoc} */
+ protected int dispatchMessage(SmsMessageBase smsb) {
// If sms is null, means there was a parsing error.
- // TODO: Should NAK this.
if (smsb == null) {
- return;
+ return Intents.RESULT_SMS_GENERIC_ERROR;
}
// Decode BD stream and set sms variables.
@@ -83,27 +81,6 @@
int teleService = sms.getTeleService();
boolean handled = false;
- // Teleservices W(E)MT and VMN are handled together:
- if ((teleService == SmsEnvelope.TELESERVICE_WMT)
- || (teleService == SmsEnvelope.TELESERVICE_WEMT)
- || (teleService == SmsEnvelope.TELESERVICE_VMN)) {
- // From here on we need decoded BD.
- // Special case the message waiting indicator messages
- if (sms.isMWISetMessage()) {
- mCdmaPhone.updateMessageWaitingIndicator(true);
- handled |= sms.isMwiDontStore();
- if (Config.LOGD) {
- Log.d(TAG, "Received voice mail indicator set SMS shouldStore=" + !handled);
- }
- } else if (sms.isMWIClearMessage()) {
- mCdmaPhone.updateMessageWaitingIndicator(false);
- handled |= sms.isMwiDontStore();
- if (Config.LOGD) {
- Log.d(TAG, "Received voice mail indicator clear SMS shouldStore=" + !handled);
- }
- }
- }
-
if (sms.getUserData() == null) {
if (Config.LOGD) {
Log.d(TAG, "Received SMS without user data");
@@ -111,11 +88,25 @@
handled = true;
}
- if (handled) return;
+ if (handled) {
+ return Intents.RESULT_SMS_HANDLED;
+ }
if (SmsEnvelope.TELESERVICE_WAP == teleService){
- processCdmaWapPdu(sms.getUserData(), sms.messageRef, sms.getOriginatingAddress());
- return;
+ return processCdmaWapPdu(sms.getUserData(), sms.messageRef,
+ sms.getOriginatingAddress());
+ } else if (SmsEnvelope.TELESERVICE_VMN == teleService) {
+ // handling Voicemail
+ int voicemailCount = sms.getNumOfVoicemails();
+ Log.d(TAG, "Voicemail count=" + voicemailCount);
+ // Store the voicemail count in preferences.
+ SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(
+ ((CDMAPhone) mPhone).getContext());
+ SharedPreferences.Editor editor = sp.edit();
+ editor.putInt(CDMAPhone.VM_COUNT_CDMA, voicemailCount);
+ editor.commit();
+ ((CDMAPhone) mPhone).updateMessageWaitingIndicator(voicemailCount);
+ return Intents.RESULT_SMS_HANDLED;
}
/**
@@ -145,17 +136,19 @@
if (smsHeader != null && smsHeader.portAddrs != null) {
if (smsHeader.portAddrs.destPort == SmsHeader.PORT_WAP_PUSH) {
// GSM-style WAP indication
- mWapPush.dispatchWapPdu(sms.getUserData());
+ return mWapPush.dispatchWapPdu(sms.getUserData());
+ } else {
+ // The message was sent to a port, so concoct a URI for it.
+ dispatchPortAddressedPdus(pdus, smsHeader.portAddrs.destPort);
}
- // The message was sent to a port, so concoct a URI for it.
- dispatchPortAddressedPdus(pdus, smsHeader.portAddrs.destPort);
} else {
// Normal short and non-port-addressed message, dispatch it.
dispatchPdus(pdus);
}
+ return Activity.RESULT_OK;
} else {
// Process the message part.
- processMessagePart(sms, smsHeader.concatRef, smsHeader.portAddrs);
+ return processMessagePart(sms, smsHeader.concatRef, smsHeader.portAddrs);
}
}
@@ -165,29 +158,35 @@
* WDP segments are gathered until a datagram completes and gets dispatched.
*
* @param pdu The WAP-WDP PDU segment
+ * @return a result code from {@link Telephony.Sms.Intents}, or
+ * {@link Activity#RESULT_OK} if the message has been broadcast
+ * to applications
*/
- protected void processCdmaWapPdu(byte[] pdu, int referenceNumber, String address) {
+ protected int processCdmaWapPdu(byte[] pdu, int referenceNumber, String address) {
int segment;
int totalSegments;
int index = 0;
int msgType;
- int sourcePort;
- int destinationPort;
+ int sourcePort = 0;
+ int destinationPort = 0;
msgType = pdu[index++];
if (msgType != 0){
Log.w(TAG, "Received a WAP SMS which is not WDP. Discard.");
- return;
+ return Intents.RESULT_SMS_HANDLED;
}
totalSegments = pdu[index++]; // >=1
segment = pdu[index++]; // >=0
- //process WDP segment
- sourcePort = (0xFF & pdu[index++]) << 8;
- sourcePort |= 0xFF & pdu[index++];
- destinationPort = (0xFF & pdu[index++]) << 8;
- destinationPort |= 0xFF & pdu[index++];
+ // Only the first segment contains sourcePort and destination Port
+ if (segment == 0) {
+ //process WDP segment
+ sourcePort = (0xFF & pdu[index++]) << 8;
+ sourcePort |= 0xFF & pdu[index++];
+ destinationPort = (0xFF & pdu[index++]) << 8;
+ destinationPort |= 0xFF & pdu[index++];
+ }
// Lookup all other related parts
StringBuilder where = new StringBuilder("reference_number =");
@@ -217,7 +216,7 @@
mResolver.insert(mRawUri, values);
- return;
+ return Intents.RESULT_SMS_HANDLED;
}
// All the parts are in place, deal with them
@@ -228,6 +227,11 @@
for (int i = 0; i < cursorCount; i++) {
cursor.moveToNext();
int cursorSequence = (int)cursor.getLong(sequenceColumn);
+ // Read the destination port from the first segment
+ if (cursorSequence == 0) {
+ int destinationPortColumn = cursor.getColumnIndex("destination_port");
+ destinationPort = (int)cursor.getLong(destinationPortColumn);
+ }
pdus[cursorSequence] = HexDump.hexStringToByteArray(
cursor.getString(pduColumn));
}
@@ -237,7 +241,7 @@
mResolver.delete(mRawUri, where.toString(), whereArgs);
} catch (SQLException e) {
Log.e(TAG, "Can't access multipart SMS database", e);
- return; // TODO: NACK the message or something, don't just discard.
+ return Intents.RESULT_SMS_GENERIC_ERROR;
} finally {
if (cursor != null) cursor.close();
}
@@ -257,15 +261,14 @@
switch (destinationPort) {
case SmsHeader.PORT_WAP_PUSH:
// Handle the PUSH
- mWapPush.dispatchWapPdu(datagram);
- break;
+ return mWapPush.dispatchWapPdu(datagram);
default:{
pdus = new byte[1][];
pdus[0] = datagram;
// The messages were sent to any other WAP port
dispatchPortAddressedPdus(pdus, destinationPort);
- break;
+ return Activity.RESULT_OK;
}
}
}
diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
index 63d2c47..3a92064 100644
--- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
@@ -773,6 +773,12 @@
return asciiDigit;
}
+ /** This function shall be called to get the number of voicemails.
+ * @hide
+ */
+ /*package*/ int getNumOfVoicemails() {
+ return mBearerData.numberOfMessages;
+ }
}
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 a835dee..eaeda64 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
@@ -83,7 +83,7 @@
public static final int MESSAGE_TYPE_DELIVER_REPORT = 0x07;
public static final int MESSAGE_TYPE_SUBMIT_REPORT = 0x08;
- public byte messageType;
+ public int messageType;
/**
* 16-bit value indicating the message ID, which increments modulo 65536.
@@ -102,7 +102,7 @@
public static final int PRIORITY_EMERGENCY = 0x3;
public boolean priorityIndicatorSet = false;
- public byte priority = PRIORITY_NORMAL;
+ public int priority = PRIORITY_NORMAL;
/**
* Supported privacy modes for CDMA SMS messages
@@ -114,7 +114,7 @@
public static final int PRIVACY_SECRET = 0x3;
public boolean privacyIndicatorSet = false;
- public byte privacy = PRIVACY_NOT_RESTRICTED;
+ public int privacy = PRIVACY_NOT_RESTRICTED;
/**
* Supported alert priority modes for CDMA SMS messages
@@ -139,7 +139,7 @@
public static final int DISPLAY_MODE_USER = 0x2;
public boolean displayModeSet = false;
- public byte displayMode = DISPLAY_MODE_DEFAULT;
+ public int displayMode = DISPLAY_MODE_DEFAULT;
/**
* Language Indicator values. NOTE: the spec (3GPP2 C.S0015-B,
@@ -799,7 +799,7 @@
private static void decodeUserData(BearerData bData, BitwiseInputStream inStream)
throws BitwiseInputStream.AccessException
{
- byte paramBytes = inStream.read(8);
+ int paramBytes = inStream.read(8);
bData.userData = new UserData();
bData.userData.msgEncoding = inStream.read(5);
bData.userData.msgEncodingSet = true;
@@ -867,7 +867,7 @@
inStream.skip(offset);
byte[] expandedData = new byte[numFields];
for (int i = 0; i < numFields; i++) {
- expandedData[i] = inStream.read(7);
+ expandedData[i] = (byte)inStream.read(7);
}
return new String(expandedData, 0, numFields, "US-ASCII");
} catch (java.io.UnsupportedEncodingException ex) {
@@ -919,10 +919,126 @@
}
}
+ /**
+ * 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
{
- byte paramBytes = inStream.read(8);
+ int paramBytes = inStream.read(8);
if (paramBytes != 1) {
throw new CodingException("REPLY_OPTION subparam size incorrect");
}
@@ -985,7 +1101,7 @@
private static void decodeCallbackNumber(BearerData bData, BitwiseInputStream inStream)
throws BitwiseInputStream.AccessException, CodingException
{
- byte paramBytes = inStream.read(8);
+ int paramBytes = inStream.read(8);
CdmaSmsAddress addr = new CdmaSmsAddress();
addr.digitMode = inStream.read(1);
byte fieldBits = 4;
@@ -1219,7 +1335,18 @@
throw new CodingException("missing MESSAGE_IDENTIFIER subparam");
}
if (bData.userData != null) {
- decodeUserDataPayload(bData.userData, bData.hasUserDataHeader);
+ if (bData.userData.msgEncoding == UserData.ENCODING_IS91_EXTENDED_PROTOCOL) {
+ if ((foundSubparamMask ^
+ (1 << SUBPARAM_MESSAGE_IDENTIFIER) ^
+ (1 << SUBPARAM_USER_DATA))
+ != 0) {
+ Log.e(LOG_TAG, "IS-91 must occur without extra subparams (" +
+ foundSubparamMask + ")");
+ }
+ decodeIs91(bData);
+ } else {
+ decodeUserDataPayload(bData.userData, bData.hasUserDataHeader);
+ }
}
return bData;
} catch (BitwiseInputStream.AccessException ex) {
diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java b/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java
index 917ec9d..4d799665 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java
@@ -29,7 +29,7 @@
static public final int DIGIT_MODE_4BIT_DTMF = 0x00;
static public final int DIGIT_MODE_8BIT_CHAR = 0x01;
- public byte digitMode;
+ public int digitMode;
/**
* Number Mode Indicator is 1-bit value that indicates whether the
@@ -39,7 +39,7 @@
static public final int NUMBER_MODE_NOT_DATA_NETWORK = 0x00;
static public final int NUMBER_MODE_DATA_NETWORK = 0x01;
- public byte numberMode;
+ public int numberMode;
/**
* Number Types for data networks.
@@ -65,7 +65,7 @@
* This field shall be set to the number of address digits
* (See 3GPP2 C.S0015-B, v2, 3.4.3.3)
*/
- public byte numberOfDigits;
+ public int numberOfDigits;
/**
* Numbering Plan identification is a 0 or 4-bit value that
@@ -78,7 +78,7 @@
//static protected final int NUMBERING_PLAN_TELEX = 0x4;
//static protected final int NUMBERING_PLAN_PRIVATE = 0x9;
- public byte numberPlan;
+ public int numberPlan;
/**
* NOTE: the parsed string address and the raw byte array values
diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java b/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java
index d8a48cc..34cbbfa 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java
@@ -40,12 +40,26 @@
public static final int ENCODING_GSM_DCS = 0x0A;
/**
+ * IS-91 message types.
+ * (See TIA/EIS/IS-91-A-ENGL 1999, table 3.7.1.1-3)
+ */
+ public static final int IS91_MSG_TYPE_VOICEMAIL_STATUS = 0x82;
+ public static final int IS91_MSG_TYPE_SHORT_MESSAGE_FULL = 0x83;
+ public static final int IS91_MSG_TYPE_CLI = 0x84;
+ public static final int IS91_MSG_TYPE_SHORT_MESSAGE = 0x85;
+
+ /**
* IA5 data encoding character mappings.
* (See CCITT Rec. T.50 Tables 1 and 3)
*
* Note this mapping is the the same as for printable ASCII
* characters, with a 0x20 offset, meaning that the ASCII SPACE
* character occurs with code 0x20.
+ *
+ * Note this mapping is also equivalent to that used by the IS-91
+ * protocol, except for the latter using only 6 bits, and hence
+ * mapping only entries up to the '_' character.
+ *
*/
public static final char[] IA5_MAP = {
' ', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/',
@@ -96,7 +110,6 @@
public int msgEncoding;
public boolean msgEncodingSet = false;
- // XXX needed when encoding is IS91 or DCS (not supported yet):
public int msgType;
/**
diff --git a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
index 70d71fc..d1e4b4f 100755
--- a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
@@ -448,11 +448,6 @@
}
public void
- notifyMessageWaitingIndicator() {
- mNotifier.notifyMessageWaitingChanged(this);
- }
-
- public void
notifyCallForwardingIndicator() {
mNotifier.notifyCallForwardingChanged(this);
}
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
index 035c690..c33d4b6 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 @@
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 @@
// 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 @@
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 @@
//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 @@
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 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 @@
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 @@
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 @@
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 @@
if (!isConnected) {
// reset reconnect timer
nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS;
+ mReregisterOnReconnectFailure = false;
trySetupData(Phone.REASON_APN_CHANGED);
}
}
@@ -882,6 +890,7 @@
startNetStatPoll();
// reset reconnect timer
nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS;
+ mReregisterOnReconnectFailure = false;
}
private void setupDnsProperties() {
@@ -952,7 +961,7 @@
} 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 @@
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 @@
// 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 @@
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 @@
// 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 @@
}
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 @@
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 @@
} 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 @@
*/
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 @@
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 @@
*/
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 @@
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/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
index f87392a..2770ddc 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
@@ -78,24 +78,16 @@
}
}
}
-
- if (mCm != null) {
- mCm.acknowledgeLastIncomingGsmSms(true, Intents.RESULT_SMS_HANDLED, null);
- }
+ acknowledgeLastIncomingSms(true, Intents.RESULT_SMS_HANDLED, null);
}
- /**
- * Dispatches an incoming SMS messages.
- *
- * @param sms the incoming message from the phone
- */
- protected void dispatchMessage(SmsMessageBase smsb) {
+ /** {@inheritDoc} */
+ protected int dispatchMessage(SmsMessageBase smsb) {
// If sms is null, means there was a parsing error.
- // TODO: Should NAK this.
if (smsb == null) {
- return;
+ return Intents.RESULT_SMS_GENERIC_ERROR;
}
SmsMessage sms = (SmsMessage) smsb;
boolean handled = false;
@@ -115,7 +107,9 @@
}
}
- if (handled) return;
+ if (handled) {
+ return Intents.RESULT_SMS_HANDLED;
+ }
SmsHeader smsHeader = sms.getUserDataHeader();
// See if message is partial or port addressed.
@@ -126,17 +120,19 @@
if (smsHeader != null && smsHeader.portAddrs != null) {
if (smsHeader.portAddrs.destPort == SmsHeader.PORT_WAP_PUSH) {
- mWapPush.dispatchWapPdu(sms.getUserData());
+ return mWapPush.dispatchWapPdu(sms.getUserData());
+ } else {
+ // The message was sent to a port, so concoct a URI for it.
+ dispatchPortAddressedPdus(pdus, smsHeader.portAddrs.destPort);
}
- // The message was sent to a port, so concoct a URI for it.
- dispatchPortAddressedPdus(pdus, smsHeader.portAddrs.destPort);
} else {
// Normal short and non-port-addressed message, dispatch it.
dispatchPdus(pdus);
}
+ return Activity.RESULT_OK;
} else {
// Process the message part.
- processMessagePart(sms, smsHeader.concatRef, smsHeader.portAddrs);
+ return processMessagePart(sms, smsHeader.concatRef, smsHeader.portAddrs);
}
}
diff --git a/tests/AndroidTests/src/com/android/unit_tests/BitwiseStreamsTest.java b/tests/AndroidTests/src/com/android/unit_tests/BitwiseStreamsTest.java
index a935247..c5562b3 100644
--- a/tests/AndroidTests/src/com/android/unit_tests/BitwiseStreamsTest.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/BitwiseStreamsTest.java
@@ -25,6 +25,8 @@
import android.util.Log;
+import java.util.Random;
+
public class BitwiseStreamsTest extends AndroidTestCase {
private final static String LOG_TAG = "BitwiseStreamsTest";
@@ -39,7 +41,7 @@
BitwiseInputStream inStream = new BitwiseInputStream(outBuf);
byte[] inBufDup = new byte[inBuf.length];
inStream.skip(offset);
- for (int i = 0; i < inBufDup.length; i++) inBufDup[i] = inStream.read(8);
+ for (int i = 0; i < inBufDup.length; i++) inBufDup[i] = (byte)inStream.read(8);
assertEquals(HexDump.toHexString(inBuf), HexDump.toHexString(inBufDup));
}
@@ -53,7 +55,7 @@
BitwiseInputStream inStream = new BitwiseInputStream(outStream.toByteArray());
inStream.skip(offset);
byte[] inBufDup = new byte[inBuf.length];
- for (int i = 0; i < inBufDup.length; i++) inBufDup[i] = inStream.read(8);
+ for (int i = 0; i < inBufDup.length; i++) inBufDup[i] = (byte)inStream.read(8);
assertEquals(HexDump.toHexString(inBuf), HexDump.toHexString(inBufDup));
}
@@ -67,7 +69,7 @@
BitwiseInputStream inStream = new BitwiseInputStream(outStream.toByteArray());
inStream.skip(offset);
byte[] inBufDup = new byte[inBuf.length];
- for (int i = 0; i < inBufDup.length; i++) inBufDup[i] = inStream.read(8);
+ for (int i = 0; i < inBufDup.length; i++) inBufDup[i] = (byte)inStream.read(8);
assertEquals(HexDump.toHexString(inBuf), HexDump.toHexString(inBufDup));
}
@@ -84,12 +86,33 @@
BitwiseInputStream inStream = new BitwiseInputStream(outStream.toByteArray());
inStream.skip(offset);
byte[] inBufDup = new byte[inBuf.length];
- for (int i = 0; i < inBufDup.length; i++) inBufDup[i] = inStream.read(8);
+ for (int i = 0; i < inBufDup.length; i++) inBufDup[i] = (byte)inStream.read(8);
assertEquals(HexDump.toHexString(inBuf), HexDump.toHexString(inBufDup));
}
@SmallTest
public void testFive() throws Exception {
+ Random random = new Random();
+ int iterations = 10000;
+ int[] sizeArr = new int[iterations];
+ int[] valueArr = new int[iterations];
+ BitwiseOutputStream outStream = new BitwiseOutputStream(iterations * 4);
+ for (int i = 0; i < iterations; i++) {
+ int x = random.nextInt();
+ int size = (x & 0x07) + 1;
+ int value = x & (-1 >>> (32 - size));
+ sizeArr[i] = size;
+ valueArr[i] = value;
+ outStream.write(size, value);
+ }
+ BitwiseInputStream inStream = new BitwiseInputStream(outStream.toByteArray());
+ for (int i = 0; i < iterations; i++) {
+ assertEquals(valueArr[i], inStream.read(sizeArr[i]));
+ }
+ }
+
+ @SmallTest
+ public void testSix() throws Exception {
int num_runs = 10;
long start = android.os.SystemClock.elapsedRealtime();
for (int run = 0; run < num_runs; run++) {
@@ -104,7 +127,7 @@
BitwiseInputStream inStream = new BitwiseInputStream(outStream.toByteArray());
inStream.skip(offset);
byte[] inBufDup = new byte[inBuf.length];
- for (int i = 0; i < inBufDup.length; i++) inBufDup[i] = inStream.read(8);
+ for (int i = 0; i < inBufDup.length; i++) inBufDup[i] = (byte)inStream.read(8);
assertEquals(HexDump.toHexString(inBuf), HexDump.toHexString(inBufDup));
}
long end = android.os.SystemClock.elapsedRealtime();
diff --git a/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java b/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java
index b2529811..2ff0a6a 100644
--- a/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java
@@ -30,6 +30,8 @@
import java.util.Iterator;
+import java.lang.Integer;
+
import android.util.Log;
public class CdmaSmsTest extends AndroidTestCase {
@@ -171,14 +173,14 @@
assertEquals(bearerData.msgCenterTimeStamp.minute, 1);
assertEquals(bearerData.msgCenterTimeStamp.second, 59);
assertEquals(bearerData.validityPeriodAbsolute, null);
- assertEquals(bearerData.validityPeriodRelative, -63);
+ assertEquals(bearerData.validityPeriodRelative, 193);
assertEquals(bearerData.deferredDeliveryTimeAbsolute.year, 1997);
assertEquals(bearerData.deferredDeliveryTimeAbsolute.month, 5);
assertEquals(bearerData.deferredDeliveryTimeAbsolute.monthDay, 18);
assertEquals(bearerData.deferredDeliveryTimeAbsolute.hour, 0);
assertEquals(bearerData.deferredDeliveryTimeAbsolute.minute, 0);
assertEquals(bearerData.deferredDeliveryTimeAbsolute.second, 0);
- assertEquals(bearerData.deferredDeliveryTimeRelative, -57);
+ assertEquals(bearerData.deferredDeliveryTimeRelative, 199);
assertEquals(bearerData.hasUserDataHeader, false);
assertEquals(bearerData.userData.msgEncoding, UserData.ENCODING_7BIT_ASCII);
assertEquals(bearerData.userData.numFields, 2);
@@ -225,7 +227,7 @@
assertEquals(bearerData.deferredDeliveryTimeAbsolute.hour, 0);
assertEquals(bearerData.deferredDeliveryTimeAbsolute.minute, 0);
assertEquals(bearerData.deferredDeliveryTimeAbsolute.second, 0);
- assertEquals(bearerData.deferredDeliveryTimeRelative, -110);
+ assertEquals(bearerData.deferredDeliveryTimeRelative, 146);
assertEquals(bearerData.hasUserDataHeader, false);
assertEquals(bearerData.userData.msgEncoding, UserData.ENCODING_7BIT_ASCII);
assertEquals(bearerData.userData.numFields, 2);
@@ -679,4 +681,15 @@
assertEquals(revBearerData.displayModeSet, true);
assertEquals(revBearerData.displayMode, bearerData.displayMode);
}
+
+ @SmallTest
+ public void testIs91() throws Exception {
+ String pdu1 = "000320001001070c2039acc13880";
+ BearerData bd1 = BearerData.decode(HexDump.hexStringToByteArray(pdu1));
+ assertEquals(bd1.callbackNumber.address, "3598271");
+ String pdu4 = "000320001001080c283c314724b34e";
+ BearerData bd4 = BearerData.decode(HexDump.hexStringToByteArray(pdu4));
+ assertEquals(bd4.userData.payloadStr, "ABCDEFG");
+ }
+
}
diff --git a/tests/CoreTests/com/android/internal/telephony/PhoneNumberUtilsTest.java b/tests/CoreTests/com/android/internal/telephony/PhoneNumberUtilsTest.java
index e8bd239..577d384 100644
--- a/tests/CoreTests/com/android/internal/telephony/PhoneNumberUtilsTest.java
+++ b/tests/CoreTests/com/android/internal/telephony/PhoneNumberUtilsTest.java
@@ -93,6 +93,13 @@
assertEquals(b[i], bRet[i]);
}
+ bRet = PhoneNumberUtils.networkPortionToCalledPartyBCDWithLength("+17005550020");
+ assertEquals(8, bRet.length);
+ assertEquals(bRet[0], 7);
+ for (int i = 1; i < 8; i++) {
+ assertEquals(b[i - 1], bRet[i]);
+ }
+
bRet = PhoneNumberUtils.networkPortionToCalledPartyBCD("7005550020");
assertEquals("7005550020",
PhoneNumberUtils.calledPartyBCDToString(bRet, 0, bRet.length));
diff --git a/tests/DpiTest/AndroidManifest.xml b/tests/DpiTest/AndroidManifest.xml
index f71cff2..64ad7be 100644
--- a/tests/DpiTest/AndroidManifest.xml
+++ b/tests/DpiTest/AndroidManifest.xml
@@ -16,6 +16,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.google.android.test.dpi">
+ <uses-sdk android:minSdkVersion="3" android:targetSdkVersion="3" />
+ <supports-screens android:smallScreens="true" />
<application android:label="DpiTest">
<activity android:name="DpiTestActivity" android:label="DpiTest">
<intent-filter>
diff --git a/tests/DpiTest/res/values-largeScreen/strings.xml b/tests/DpiTest/res/values-largeScreen/strings.xml
new file mode 100644
index 0000000..f4dd543
--- /dev/null
+++ b/tests/DpiTest/res/values-largeScreen/strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+ <string name="act_title">DpiTest: Large Screen</string>
+</resources>
diff --git a/tests/DpiTest/res/values-normalScreen/strings.xml b/tests/DpiTest/res/values-normalScreen/strings.xml
new file mode 100644
index 0000000..256d696
--- /dev/null
+++ b/tests/DpiTest/res/values-normalScreen/strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+ <string name="act_title">DpiTest: Normal Screen</string>
+</resources>
diff --git a/tests/DpiTest/res/values-smallScreen/strings.xml b/tests/DpiTest/res/values-smallScreen/strings.xml
new file mode 100644
index 0000000..cdb4ac9
--- /dev/null
+++ b/tests/DpiTest/res/values-smallScreen/strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+ <string name="act_title">DpiTest: Small Screen</string>
+</resources>
diff --git a/tests/DpiTest/res/values/strings.xml b/tests/DpiTest/res/values/strings.xml
new file mode 100644
index 0000000..ef924ac
--- /dev/null
+++ b/tests/DpiTest/res/values/strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+ <string name="act_title">DpiTest: Unknown Screen</string>
+</resources>
diff --git a/tests/DpiTest/src/com/google/android/test/dpi/DpiTestActivity.java b/tests/DpiTest/src/com/google/android/test/dpi/DpiTestActivity.java
index 3759622..5a9f3f5 100644
--- a/tests/DpiTest/src/com/google/android/test/dpi/DpiTestActivity.java
+++ b/tests/DpiTest/src/com/google/android/test/dpi/DpiTestActivity.java
@@ -34,6 +34,7 @@
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ this.setTitle(R.string.act_title);
LinearLayout root = new LinearLayout(this);
root.setOrientation(LinearLayout.VERTICAL);
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/FileList.java b/tests/DumpRenderTree/src/com/android/dumprendertree/FileList.java
index 0218317..e741177 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/FileList.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/FileList.java
@@ -23,7 +23,9 @@
import java.util.Map;
import java.io.File;
+import android.app.AlertDialog;
import android.app.ListActivity;
+import android.content.DialogInterface;
import android.view.KeyEvent;
import android.view.View;
import android.widget.ListView;
@@ -31,7 +33,7 @@
import android.os.Bundle;
-public abstract class FileList extends ListActivity
+public abstract class FileList extends ListActivity
{
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (keyCode)
@@ -39,7 +41,7 @@
case KeyEvent.KEYCODE_DPAD_LEFT:
if (mPath.length() > mBaseLength) {
File f = new File(mPath);
- mFocusFile = f.getName();
+ mFocusFile = f.getName();
mFocusIndex = 0;
f = f.getParentFile();
mPath = f.getPath();
@@ -47,7 +49,7 @@
return true;
}
break;
-
+
case KeyEvent.KEYCODE_DPAD_RIGHT:
{
Map map = (Map) getListView().getItemAtPosition(getListView().getSelectedItemPosition());
@@ -61,24 +63,24 @@
}
return true;
}
-
+
default:
break;
}
return super.onKeyDown(keyCode, event);
}
- public void onCreate(Bundle icicle)
+ public void onCreate(Bundle icicle)
{
super.onCreate(icicle);
setupPath();
updateList();
}
-
+
protected List getData()
{
List myData = new ArrayList<HashMap>();
-
+
File f = new File(mPath);
if (!f.exists()) {
addItem(myData, "!LayoutTests path missing!", "");
@@ -103,10 +105,10 @@
addItem(myData, files[i], path);
}
}
-
+
return myData;
}
-
+
protected void addItem(List<Map> data, String name, String path)
{
HashMap temp = new HashMap();
@@ -114,34 +116,58 @@
temp.put("path", path);
data.add(temp);
}
-
+
protected void onListItemClick(ListView l, View v, int position, long id)
{
- Map map = (Map) l.getItemAtPosition(position);
- String path = (String)map.get("path");
+ Map map = (Map) l.getItemAtPosition(position);
+ final String path = (String)map.get("path");
if ((new File(path)).isDirectory()) {
- mPath = path;
- mFocusFile = null;
- updateList();
+ final CharSequence[] items = {"Open", "Run"};
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle("Select an Action");
+ builder.setSingleChoiceItems(items, -1,
+ new DialogInterface.OnClickListener(){
+ public void onClick(DialogInterface dialog, int which) {
+ switch (which) {
+ case OPEN_DIRECTORY:
+ dialog.dismiss();
+ mPath = path;
+ mFocusFile = null;
+ updateList();
+ break;
+ case RUN_TESTS:
+ dialog.dismiss();
+ processDirectory(path, false);
+ break;
+ }
+ }
+ });
+ builder.create().show();
} else {
processFile(path, false);
}
}
-
+
+ /*
+ * This function is called when the user has selected a directory in the
+ * list and wants to perform an action on it instead of navigating into
+ * the directory.
+ */
+ abstract void processDirectory(String path, boolean selection);
/*
* This function is called when the user has selected a file in the
* file list. The selected file could be a file or a directory.
* The flag indicates if this was from a selection or not.
*/
abstract void processFile(String filename, boolean selection);
-
+
/*
* This function is called when the file list is being built. Return
* true if the file is to be added to the file list.
*/
abstract boolean fileFilter(File f);
-
+
protected void updateList() {
setListAdapter(new SimpleAdapter(this,
getData(),
@@ -152,16 +178,19 @@
setTitle(title);
getListView().setSelection(mFocusIndex);
}
-
- protected void setupPath()
+
+ protected void setupPath()
{
mPath = "/sdcard/android/layout_tests";
mBaseLength = mPath.length();
}
-
+
protected String mPath;
protected int mBaseLength;
protected String mFocusFile;
protected int mFocusIndex;
-
+
+ private final static int OPEN_DIRECTORY = 0;
+ private final static int RUN_TESTS = 1;
+
}
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/FsUtils.java b/tests/DumpRenderTree/src/com/android/dumprendertree/FsUtils.java
new file mode 100644
index 0000000..cc2f1f5
--- /dev/null
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/FsUtils.java
@@ -0,0 +1,80 @@
+package com.android.dumprendertree;
+
+import android.util.Log;
+
+import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+
+public class FsUtils {
+
+ private static final String LOGTAG = "FsUtils";
+ private FsUtils() {
+ //no creation of instances
+ }
+
+ public static void findLayoutTestsRecursively(BufferedOutputStream bos,
+ String dir) throws IOException {
+ Log.v(LOGTAG, "Searching tests under " + dir);
+
+ File d = new File(dir);
+ if (!d.isDirectory()) {
+ throw new AssertionError("A directory expected, but got " + dir);
+ }
+
+ String[] files = d.list();
+ for (int i = 0; i < files.length; i++) {
+ String s = dir + "/" + files[i];
+ if (FileFilter.ignoreTest(s)) {
+ Log.v(LOGTAG, " Ignoring: " + s);
+ continue;
+ }
+ if (s.toLowerCase().endsWith(".html")
+ || s.toLowerCase().endsWith(".xml")) {
+ bos.write(s.getBytes());
+ bos.write('\n');
+ continue;
+ }
+
+ File f = new File(s);
+ if (f.isDirectory()) {
+ findLayoutTestsRecursively(bos, s);
+ continue;
+ }
+
+ Log.v(LOGTAG, "Skipping " + s);
+ }
+ }
+
+ public static void updateTestStatus(String statusFile, String s) {
+ try {
+ BufferedOutputStream bos = new BufferedOutputStream(
+ new FileOutputStream(statusFile));
+ bos.write(s.getBytes());
+ bos.close();
+ } catch (Exception e) {
+ Log.e(LOGTAG, "Cannot update file " + statusFile);
+ }
+ }
+
+ public static String readTestStatus(String statusFile) {
+ // read out the test name it stopped last time.
+ String status = null;
+ File testStatusFile = new File(statusFile);
+ if(testStatusFile.exists()) {
+ try {
+ BufferedReader inReader = new BufferedReader(
+ new FileReader(testStatusFile));
+ status = inReader.readLine();
+ inReader.close();
+ } catch (IOException e) {
+ Log.e(LOGTAG, "Error reading test status.", e);
+ }
+ }
+ return status;
+ }
+
+}
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java
index f169a26..a03490d 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java
@@ -178,15 +178,13 @@
private void resumeTestList() {
// read out the test name it stoped last time.
try {
- BufferedReader inReader = new BufferedReader(new FileReader(TEST_STATUS_FILE));
- String line = inReader.readLine();
+ String line = FsUtils.readTestStatus(TEST_STATUS_FILE);
for (int i = 0; i < mTestList.size(); i++) {
if (mTestList.elementAt(i).equals(line)) {
mTestList = new Vector<String>(mTestList.subList(i+1, mTestList.size()));
break;
}
}
- inReader.close();
} catch (Exception e) {
Log.e(LOGTAG, "Error reading " + TEST_STATUS_FILE);
}
@@ -204,18 +202,7 @@
Log.e(LOGTAG, "Fail to delete " + TEST_STATUS_FILE + " : " + e.getMessage());
}
}
-
- private void updateTestStatus(String s) {
- // Write TEST_STATUS_FILE
- try {
- BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(TEST_STATUS_FILE));
- bos.write(s.getBytes());
- bos.close();
- } catch (Exception e) {
- Log.e(LOGTAG, "Cannot update file " + TEST_STATUS_FILE);
- }
- }
-
+
private String getResultFile(String test) {
String shortName = test.substring(0, test.lastIndexOf('.'));
// Write actual results to result directory.
@@ -392,12 +379,12 @@
// Run tests.
for (int i = 0; i < mTestList.size(); i++) {
String s = mTestList.elementAt(i);
- updateTestStatus(s);
+ FsUtils.updateTestStatus(TEST_STATUS_FILE, s);
// Run tests
runTestAndWaitUntilDone(activity, s, runner.mTimeoutInMillis);
}
- updateTestStatus("#DONE");
+ FsUtils.updateTestStatus(TEST_STATUS_FILE, "#DONE");
activity.finish();
}
@@ -424,7 +411,7 @@
try {
File tests_list = new File(LAYOUT_TESTS_LIST_FILE);
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(tests_list, false));
- findTestsRecursively(bos, getTestPath());
+ FsUtils.findLayoutTestsRecursively(bos, getTestPath());
bos.flush();
bos.close();
} catch (Exception e) {
@@ -432,38 +419,6 @@
}
}
- private void findTestsRecursively(BufferedOutputStream bos, String dir) throws IOException {
- Log.v(LOGTAG, "Searching tests under " + dir);
-
- File d = new File(dir);
- if (!d.isDirectory()) {
- throw new AssertionError("A directory expected, but got " + dir);
- }
-
- String[] files = d.list();
- for (int i = 0; i < files.length; i++) {
- String s = dir + "/" + files[i];
- if (FileFilter.ignoreTest(s)) {
- Log.v(LOGTAG, " Ignoring: " + s);
- continue;
- }
- if (s.toLowerCase().endsWith(".html")
- || s.toLowerCase().endsWith(".xml")) {
- bos.write(s.getBytes());
- bos.write('\n');
- continue;
- }
-
- File f = new File(s);
- if (f.isDirectory()) {
- findTestsRecursively(bos, s);
- continue;
- }
-
- Log.v(LOGTAG, "Skipping " + s);
- }
- }
-
// Running all the layout tests at once sometimes
// causes the dumprendertree to run out of memory.
// So, additional tests are added to run the tests
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/Menu.java b/tests/DumpRenderTree/src/com/android/dumprendertree/Menu.java
index 00e0f89..e15ab65 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/Menu.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/Menu.java
@@ -17,19 +17,23 @@
package com.android.dumprendertree;
import android.content.Intent;
-import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
+import java.io.BufferedOutputStream;
import java.io.File;
+import java.io.FileOutputStream;
public class Menu extends FileList {
-
- public void onCreate(Bundle icicle)
- {
+
+ private static final int MENU_START = 0x01;
+ private static String LOGTAG = "MenuActivity";
+ static final String LAYOUT_TESTS_LIST_FILE = "/sdcard/android/layout_tests_list.txt";
+
+ public void onCreate(Bundle icicle) {
super.onCreate(icicle);
}
-
+
boolean fileFilter(File f) {
if (f.getName().startsWith("."))
return false;
@@ -41,14 +45,36 @@
return true;
return false;
}
-
- void processFile(String filename, boolean selection)
- {
+
+ void processFile(String filename, boolean selection) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setClass(this, TestShellActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.putExtra(TestShellActivity.TEST_URL, "file://" + filename);
startActivity(intent);
}
+
+ @Override
+ void processDirectory(String path, boolean selection) {
+ generateTestList(path);
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setClass(this, TestShellActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
+ intent.putExtra(TestShellActivity.UI_AUTO_TEST, LAYOUT_TESTS_LIST_FILE);
+ startActivity(intent);
+ }
+
+ private void generateTestList(String path) {
+ try {
+ File tests_list = new File(LAYOUT_TESTS_LIST_FILE);
+ BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(tests_list, false));
+ FsUtils.findLayoutTestsRecursively(bos, path);
+ bos.flush();
+ bos.close();
+ } catch (Exception e) {
+ Log.e(LOGTAG, "Error when creating test list: " + e.getMessage());
+ }
+ }
+
}
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/ReliabilityTest.java b/tests/DumpRenderTree/src/com/android/dumprendertree/ReliabilityTest.java
index 16973be..de39800 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/ReliabilityTest.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/ReliabilityTest.java
@@ -45,7 +45,7 @@
//always try to resume first, hence cleaning up status will be the
//responsibility of driver scripts
- String lastUrl = readTestStatus();
+ String lastUrl = FsUtils.readTestStatus(TEST_STATUS_FILE);
if(lastUrl != null && !TEST_DONE.equals(lastUrl))
fastForward(listReader, lastUrl);
@@ -62,7 +62,7 @@
continue;
start = System.currentTimeMillis();
Log.v(LOGTAG, "Testing URL: " + url);
- updateTestStatus(url);
+ FsUtils.updateTestStatus(TEST_STATUS_FILE, url);
activity.reset();
//use message to send new URL to avoid interacting with
//WebView in non-UI thread
@@ -92,7 +92,7 @@
System.gc();
System.gc();
}
- updateTestStatus(TEST_DONE);
+ FsUtils.updateTestStatus(TEST_STATUS_FILE, TEST_DONE);
activity.finish();
listReader.close();
}
@@ -122,35 +122,6 @@
}
}
- private void updateTestStatus(String s) {
- // write last tested url into status file
- try {
- BufferedOutputStream bos = new BufferedOutputStream(
- new FileOutputStream(TEST_STATUS_FILE));
- bos.write(s.getBytes());
- bos.close();
- } catch (IOException e) {
- Log.e(LOGTAG, "Cannot update file " + TEST_STATUS_FILE, e);
- }
- }
-
- private String readTestStatus() {
- // read out the test name it stopped last time.
- String status = null;
- File testStatusFile = new File(TEST_STATUS_FILE);
- if(testStatusFile.exists()) {
- try {
- BufferedReader inReader = new BufferedReader(
- new FileReader(testStatusFile));
- status = inReader.readLine();
- inReader.close();
- } catch (IOException e) {
- Log.e(LOGTAG, "Error reading test status.", e);
- }
- }
- return status;
- }
-
private void fastForward(BufferedReader testListReader, String lastUrl) {
//fastforward the BufferedReader to the position right after last url
if(lastUrl == null)
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
index 1ba291c..0d22eca 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
@@ -17,7 +17,10 @@
package com.android.dumprendertree;
import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
import android.content.Intent;
+import android.content.DialogInterface.OnClickListener;
import android.graphics.Bitmap;
import android.net.http.SslError;
import android.os.Bundle;
@@ -35,21 +38,24 @@
import android.webkit.WebViewClient;
import android.widget.LinearLayout;
+import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
+import java.io.FileReader;
import java.io.IOException;
import java.util.Vector;
public class TestShellActivity extends Activity implements LayoutTestController {
-
+
static enum DumpDataType {DUMP_AS_TEXT, EXT_REPR, NO_OP}
-
+
public class AsyncHandler extends Handler {
@Override
public void handleMessage(Message msg) {
if (msg.what == MSG_TIMEOUT) {
mTimedOut = true;
- mCallback.timedOut(mWebView.getUrl());
+ if(mCallback != null)
+ mCallback.timedOut(mWebView.getUrl());
requestWebKitData();
return;
} else if (msg.what == MSG_WEBKIT_DATA) {
@@ -63,10 +69,10 @@
public void requestWebKitData() {
Message callback = mHandler.obtainMessage(MSG_WEBKIT_DATA);
-
+
if (mRequestedWebKitData)
throw new AssertionError("Requested webkit data twice: " + mWebView.getUrl());
-
+
mRequestedWebKitData = true;
switch (mDumpDataType) {
case DUMP_AS_TEXT:
@@ -79,12 +85,12 @@
finished();
break;
}
- }
+ }
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
-
+
LinearLayout contentView = new LinearLayout(this);
contentView.setOrientation(LinearLayout.VERTICAL);
setContentView(contentView);
@@ -133,59 +139,122 @@
mWebView.addJavascriptInterface(mCallbackProxy, "layoutTestController");
mWebView.addJavascriptInterface(mCallbackProxy, "eventSender");
contentView.addView(mWebView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT, 0.0f));
-
+
mWebView.getSettings().setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL);
-
+
mHandler = new AsyncHandler();
-
+
Intent intent = getIntent();
if (intent != null) {
executeIntent(intent);
}
}
-
+
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
executeIntent(intent);
}
-
+
private void executeIntent(Intent intent) {
resetTestStatus();
if (!Intent.ACTION_VIEW.equals(intent.getAction())) {
return;
}
-
+
mTestUrl = intent.getStringExtra(TEST_URL);
- if (mTestUrl == null)
+ if (mTestUrl == null) {
+ mUiAutoTestPath = intent.getStringExtra(UI_AUTO_TEST);
+ if(mUiAutoTestPath != null) {
+ beginUiAutoTest();
+ }
return;
-
+ }
+
mResultFile = intent.getStringExtra(RESULT_FILE);
mTimeoutInMillis = intent.getIntExtra(TIMEOUT_IN_MILLIS, 0);
Log.v(LOGTAG, " Loading " + mTestUrl);
mWebView.loadUrl(mTestUrl);
-
+
if (mTimeoutInMillis > 0) {
// Create a timeout timer
Message m = mHandler.obtainMessage(MSG_TIMEOUT);
mHandler.sendMessageDelayed(m, mTimeoutInMillis);
}
}
-
+
+ private void beginUiAutoTest() {
+ try {
+ mTestListReader = new BufferedReader(
+ new FileReader(mUiAutoTestPath));
+ } catch (IOException ioe) {
+ Log.e(LOGTAG, "Failed to open test list for read.", ioe);
+ finishUiAutoTest();
+ return;
+ }
+ moveToNextTest();
+ }
+
+ private void finishUiAutoTest() {
+ try {
+ if(mTestListReader != null)
+ mTestListReader.close();
+ } catch (IOException ioe) {
+ Log.w(LOGTAG, "Failed to close test list file.", ioe);
+ }
+ finished();
+ }
+
+ private void moveToNextTest() {
+ String url = null;
+ try {
+ url = mTestListReader.readLine();
+ } catch (IOException ioe) {
+ Log.e(LOGTAG, "Failed to read next test.", ioe);
+ finishUiAutoTest();
+ return;
+ }
+ if (url == null) {
+ mUiAutoTestPath = null;
+ finishUiAutoTest();
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setMessage("All tests finished. Exit?")
+ .setCancelable(false)
+ .setPositiveButton("Yes", new OnClickListener(){
+ public void onClick(DialogInterface dialog, int which) {
+ TestShellActivity.this.finish();
+ }
+ })
+ .setNegativeButton("No", new OnClickListener(){
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.cancel();
+ }
+ });
+ builder.create().show();
+ return;
+ }
+ url = "file://" + url;
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
+ intent.putExtra(TestShellActivity.TEST_URL, url);
+ intent.putExtra(TIMEOUT_IN_MILLIS, 10000);
+ executeIntent(intent);
+ }
+
@Override
protected void onStop() {
super.onStop();
mWebView.stopLoading();
}
-
+
@Override
protected void onDestroy() {
super.onDestroy();
mWebView.destroy();
mWebView = null;
}
-
+
@Override
public void onLowMemory() {
super.onLowMemory();
@@ -199,13 +268,13 @@
finished();
return;
}
-
+
try {
File parentDir = new File(mResultFile).getParentFile();
if (!parentDir.exists()) {
parentDir.mkdirs();
}
-
+
FileOutputStream os = new FileOutputStream(mResultFile);
if (timeout) {
Log.w("Layout test: Timeout", mResultFile);
@@ -222,22 +291,27 @@
os.flush();
os.close();
} catch (IOException ex) {
- Log.e(LOGTAG, "Cannot write to " + mResultFile + ", " + ex.getMessage());
+ Log.e(LOGTAG, "Cannot write to " + mResultFile + ", " + ex.getMessage());
}
finished();
}
-
+
public void setCallback(TestShellCallback callback) {
mCallback = callback;
}
-
+
public void finished() {
- if (mCallback != null) {
- mCallback.finished();
+ if (mUiAutoTestPath != null) {
+ //don't really finish here
+ moveToNextTest();
+ } else {
+ if (mCallback != null) {
+ mCallback.finished();
+ }
}
}
-
+
public void setDefaultDumpDataType(DumpDataType defaultDumpDataType) {
mDefaultDumpDataType = defaultDumpDataType;
}
@@ -257,7 +331,7 @@
String url = mWebView.getUrl();
Log.v(LOGTAG, "waitUntilDone called: " + url);
}
-
+
public void notifyDone() {
String url = mWebView.getUrl();
Log.v(LOGTAG, "notifyDone called: " + url);
@@ -266,7 +340,7 @@
mChromeClient.onProgressChanged(mWebView, 100);
}
}
-
+
public void display() {
mWebView.invalidate();
}
@@ -332,7 +406,7 @@
}
public void queueScript(String scriptToRunInCurrentContext) {
- mWebView.loadUrl("javascript:"+scriptToRunInCurrentContext);
+ mWebView.loadUrl("javascript:"+scriptToRunInCurrentContext);
}
public void repaintSweepHorizontally() {
@@ -359,7 +433,7 @@
public void testRepaint() {
mWebView.invalidate();
}
-
+
private final WebChromeClient mChromeClient = new WebChromeClient() {
@Override
public void onProgressChanged(WebView view, int newProgress) {
@@ -406,7 +480,7 @@
result.confirm();
return true;
}
-
+
@Override
public boolean onJsConfirm(WebView view, String url, String message,
JsResult result) {
@@ -419,7 +493,7 @@
result.confirm();
return true;
}
-
+
@Override
public boolean onJsPrompt(WebView view, String url, String message,
String defaultValue, JsPromptResult result) {
@@ -435,7 +509,7 @@
return true;
}
};
-
+
private void resetTestStatus() {
mWaitUntilDone = false;
mDumpDataType = mDefaultDumpDataType;
@@ -444,17 +518,19 @@
mRequestedWebKitData = false;
mEventSender.resetMouse();
}
-
+
private WebView mWebView;
private WebViewEventSender mEventSender;
private AsyncHandler mHandler;
private TestShellCallback mCallback;
private CallbackProxy mCallbackProxy;
-
+
private String mTestUrl;
private String mResultFile;
private int mTimeoutInMillis;
+ private String mUiAutoTestPath;
+ private BufferedReader mTestListReader;
// States
private boolean mTimedOut;
@@ -472,13 +548,14 @@
private Vector mWebHistory;
static final String TIMEOUT_STR = "**Test timeout";
-
+
static final int MSG_TIMEOUT = 0;
static final int MSG_WEBKIT_DATA = 1;
static final String LOGTAG="TestShell";
-
+
static final String TEST_URL = "TestUrl";
static final String RESULT_FILE = "ResultFile";
static final String TIMEOUT_IN_MILLIS = "TimeoutInMillis";
+ static final String UI_AUTO_TEST = "UiAutoTest";
}
diff --git a/tests/backup/src/com/android/backuptest/BackupTestAgent.java b/tests/backup/src/com/android/backuptest/BackupTestAgent.java
index c6acc66..8e4fd39 100644
--- a/tests/backup/src/com/android/backuptest/BackupTestAgent.java
+++ b/tests/backup/src/com/android/backuptest/BackupTestAgent.java
@@ -18,11 +18,15 @@
import android.backup.BackupHelperAgent;
import android.backup.FileBackupHelper;
+import android.backup.SharedPreferencesBackupHelper;
public class BackupTestAgent extends BackupHelperAgent
{
public void onCreate() {
addHelper("data_files", new FileBackupHelper(this, BackupTestActivity.FILE_NAME));
+ addHelper("more_data_files", new FileBackupHelper(this, "another_file.txt", "3.txt",
+ "empty.txt"));
+ addHelper("shared_prefs", new SharedPreferencesBackupHelper(this, "settings", "raw"));
}
}
diff --git a/tests/backup/test_backup.sh b/tests/backup/test_backup.sh
new file mode 100755
index 0000000..dbf9ed2
--- /dev/null
+++ b/tests/backup/test_backup.sh
@@ -0,0 +1,27 @@
+#!/bin/bash
+
+#adb kill-server
+
+# set the transport
+adb shell bmgr transport 1
+
+# load up the three files
+adb shell "rm /data/data/com.android.backuptest/files/* ; \
+ mkdir /data/data/com.android.backuptest ; \
+ mkdir /data/data/com.android.backuptest/files ; \
+ mkdir /data/data/com.android.backuptest/shared_prefs ; \
+ echo -n \"<map><int name=\\\"pref\\\" value=\\\"1\\\" /></map>\" > /data/data/com.android.backuptest/shared_prefs/raw.xml ; \
+ echo -n first file > /data/data/com.android.backuptest/files/file.txt ; \
+ echo -n asdf > /data/data/com.android.backuptest/files/another_file.txt ; \
+ echo -n 3 > /data/data/com.android.backuptest/files/3.txt ; \
+ echo -n "" > /data/data/com.android.backuptest/files/empty.txt ; \
+"
+
+# say that the data has changed
+adb shell bmgr backup com.android.backuptest
+
+# run the backup
+adb shell bmgr run
+
+
+
diff --git a/tests/backup/test_restore.sh b/tests/backup/test_restore.sh
new file mode 100755
index 0000000..ccf29cf
--- /dev/null
+++ b/tests/backup/test_restore.sh
@@ -0,0 +1,53 @@
+#!/bin/bash
+
+function check_file
+{
+ data=$(adb shell cat /data/data/com.android.backuptest/$1)
+ if [ "$data" = "$2" ] ; then
+ echo "$1 has correct value [$2]"
+ else
+ echo $1 is INCORRECT
+ echo " value: [$data]"
+ echo " expected: [$2]"
+ fi
+}
+
+# delete the old data
+echo --- Previous files
+adb shell "ls -l /data/data/com.android.backuptest/files"
+adb shell "rm /data/data/com.android.backuptest/files/*"
+echo --- Previous shared_prefs
+adb shell "ls -l /data/data/com.android.backuptest/shared_prefs"
+adb shell "rm /data/data/com.android.backuptest/shared_prefs/*"
+echo --- Erased files and shared_prefs
+adb shell "ls -l /data/data/com.android.backuptest/files"
+adb shell "ls -l /data/data/com.android.backuptest/shared_prefs"
+echo ---
+
+echo
+echo
+echo
+
+# run the restore
+adb shell bmgr restore 0
+
+echo
+echo
+echo
+
+# check the results
+check_file files/file.txt "first file"
+check_file files/another_file.txt "asdf"
+check_file files/3.txt "3"
+check_file files/empty.txt ""
+check_file shared_prefs/raw.xml '<map><int name="pref" value="1" /></map>'
+
+echo
+echo
+echo
+echo --- Restored files
+adb shell "ls -l /data/data/com.android.backuptest/files"
+echo --- Restored shared_prefs
+adb shell "ls -l /data/data/com.android.backuptest/shared_prefs"
+echo ---
+echo
diff --git a/tools/aapt/AaptAssets.cpp b/tools/aapt/AaptAssets.cpp
index 6bc1ee6..67af116 100644
--- a/tools/aapt/AaptAssets.cpp
+++ b/tools/aapt/AaptAssets.cpp
@@ -187,6 +187,13 @@
return 0;
}
+ // screen layout
+ if (getScreenLayoutName(part.string(), &config)) {
+ *axis = AXIS_SCREENLAYOUT;
+ *value = config.screenLayout;
+ return 0;
+ }
+
// version
if (getVersionName(part.string(), &config)) {
*axis = AXIS_VERSION;
@@ -202,7 +209,7 @@
{
Vector<String8> parts;
- String8 mcc, mnc, loc, orient, den, touch, key, keysHidden, nav, size, vers;
+ String8 mcc, mnc, loc, orient, den, touch, key, keysHidden, nav, size, layout, vers;
const char *p = dir;
const char *q;
@@ -378,6 +385,18 @@
//printf("not screen size: %s\n", part.string());
}
+ if (getScreenLayoutName(part.string())) {
+ layout = part;
+
+ index++;
+ if (index == N) {
+ goto success;
+ }
+ part = parts[index];
+ } else {
+ //printf("not screen layout: %s\n", part.string());
+ }
+
if (getVersionName(part.string())) {
vers = part;
@@ -404,6 +423,7 @@
this->keyboard = key;
this->navigation = nav;
this->screenSize = size;
+ this->screenLayout = layout;
this->version = vers;
// what is this anyway?
@@ -435,6 +455,8 @@
s += ",";
s += screenSize;
s += ",";
+ s += screenLayout;
+ s += ",";
s += version;
return s;
}
@@ -483,6 +505,10 @@
s += "-";
s += screenSize;
}
+ if (this->screenLayout != "") {
+ s += "-";
+ s += screenLayout;
+ }
if (this->version != "") {
s += "-";
s += version;
@@ -786,6 +812,26 @@
return true;
}
+bool AaptGroupEntry::getScreenLayoutName(const char* name,
+ ResTable_config* out)
+{
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) out->screenLayout = out->SCREENLAYOUT_ANY;
+ return true;
+ } else if (strcmp(name, "smallscreen") == 0) {
+ if (out) out->screenLayout = out->SCREENLAYOUT_SMALL;
+ return true;
+ } else if (strcmp(name, "normalscreen") == 0) {
+ if (out) out->screenLayout = out->SCREENLAYOUT_NORMAL;
+ return true;
+ } else if (strcmp(name, "largescreen") == 0) {
+ if (out) out->screenLayout = out->SCREENLAYOUT_LARGE;
+ return true;
+ }
+
+ return false;
+}
+
bool AaptGroupEntry::getVersionName(const char* name,
ResTable_config* out)
{
@@ -828,6 +874,7 @@
if (v == 0) v = keyboard.compare(o.keyboard);
if (v == 0) v = navigation.compare(o.navigation);
if (v == 0) v = screenSize.compare(o.screenSize);
+ if (v == 0) v = screenLayout.compare(o.screenLayout);
if (v == 0) v = version.compare(o.version);
return v;
}
@@ -846,6 +893,7 @@
getKeyboardName(keyboard.string(), ¶ms);
getNavigationName(navigation.string(), ¶ms);
getScreenSizeName(screenSize.string(), ¶ms);
+ getScreenLayoutName(screenLayout.string(), ¶ms);
getVersionName(version.string(), ¶ms);
return params;
}
diff --git a/tools/aapt/AaptAssets.h b/tools/aapt/AaptAssets.h
index 01c8140..3b96412 100644
--- a/tools/aapt/AaptAssets.h
+++ b/tools/aapt/AaptAssets.h
@@ -37,6 +37,7 @@
AXIS_KEYBOARD,
AXIS_NAVIGATION,
AXIS_SCREENSIZE,
+ AXIS_SCREENLAYOUT,
AXIS_VERSION
};
@@ -62,6 +63,7 @@
String8 keyboard;
String8 navigation;
String8 screenSize;
+ String8 screenLayout;
String8 version;
bool initFromDirName(const char* dir, String8* resType);
@@ -78,6 +80,7 @@
static bool getKeyboardName(const char* name, ResTable_config* out = NULL);
static bool getNavigationName(const char* name, ResTable_config* out = NULL);
static bool getScreenSizeName(const char* name, ResTable_config* out = NULL);
+ static bool getScreenLayoutName(const char* name, ResTable_config* out = NULL);
static bool getVersionName(const char* name, ResTable_config* out = NULL);
int compare(const AaptGroupEntry& o) const;
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index 503f661..e04491d 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -329,6 +329,9 @@
TARGET_SDK_VERSION_ATTR = 0x01010270,
TEST_ONLY_ATTR = 0x01010272,
DENSITY_ATTR = 0x0101026c,
+ SMALL_SCREEN_ATTR = 0x01010284,
+ NORMAL_SCREEN_ATTR = 0x01010285,
+ LARGE_SCREEN_ATTR = 0x01010286,
};
const char *getComponentName(String8 &pkgName, String8 &componentName) {
@@ -499,6 +502,10 @@
bool isLauncherActivity = false;
bool withinApplication = false;
bool withinReceiver = false;
+ int targetSdk = 0;
+ int smallScreen = 1;
+ int normalScreen = 1;
+ int largeScreen = 1;
String8 pkg;
String8 activityName;
String8 activityLabel;
@@ -572,8 +579,10 @@
error.string());
goto bail;
}
+ if (name == "Donut") targetSdk = 4;
printf("sdkVersion:'%s'\n", name.string());
} else if (code != -1) {
+ targetSdk = code;
printf("sdkVersion:'%d'\n", code);
}
code = getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
@@ -585,8 +594,12 @@
error.string());
goto bail;
}
+ if (name == "Donut" && targetSdk < 4) targetSdk = 4;
printf("targetSdkVersion:'%s'\n", name.string());
} else if (code != -1) {
+ if (targetSdk < code) {
+ targetSdk = code;
+ }
printf("targetSdkVersion:'%d'\n", code);
}
} else if (tag == "uses-configuration") {
@@ -625,6 +638,13 @@
goto bail;
}
printf("supports-density:'%d'\n", dens);
+ } else if (tag == "supports-screens") {
+ smallScreen = getIntegerAttribute(tree,
+ SMALL_SCREEN_ATTR, NULL, 1);
+ normalScreen = getIntegerAttribute(tree,
+ NORMAL_SCREEN_ATTR, NULL, 1);
+ largeScreen = getIntegerAttribute(tree,
+ LARGE_SCREEN_ATTR, NULL, 1);
}
} else if (depth == 3 && withinApplication) {
withinActivity = false;
@@ -733,6 +753,25 @@
}
}
+ // Determine default values for any unspecified screen sizes,
+ // based on the target SDK of the package. As of 4 (donut)
+ // the screen size support was introduced, so all default to
+ // enabled.
+ if (smallScreen > 0) {
+ smallScreen = targetSdk >= 4 ? -1 : 0;
+ }
+ if (normalScreen > 0) {
+ normalScreen = -1;
+ }
+ if (largeScreen > 0) {
+ largeScreen = targetSdk >= 4 ? -1 : 0;
+ }
+ printf("supports-screens:");
+ if (smallScreen != 0) printf(" 'small'");
+ if (normalScreen != 0) printf(" 'normal'");
+ if (largeScreen != 0) printf(" 'large'");
+ printf("\n");
+
printf("locales:");
Vector<String8> locales;
res.getLocales(&locales);
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeAssetManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeAssetManager.java
index 1fa11af..06dd96f 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeAssetManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeAssetManager.java
@@ -59,7 +59,7 @@
public void setConfiguration(int mcc, int mnc, String locale,
int orientation, int touchscreen, int density, int keyboard,
int keyboardHidden, int navigation, int screenWidth, int screenHeight,
- int version) {
+ int screenLayout, int version) {
Configuration c = new Configuration();
c.mcc = mcc;
@@ -70,5 +70,6 @@
c.keyboardHidden = keyboardHidden;
c.navigation = navigation;
c.orientation = orientation;
+ c.screenLayout = screenLayout;
}
}
diff --git a/vpn/java/android/net/vpn/IVpnService.aidl b/vpn/java/android/net/vpn/IVpnService.aidl
index 0e658df..fedccb0 100644
--- a/vpn/java/android/net/vpn/IVpnService.aidl
+++ b/vpn/java/android/net/vpn/IVpnService.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007, The Android Open Source Project
+ * 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.
diff --git a/vpn/java/android/net/vpn/L2tpIpsecProfile.java b/vpn/java/android/net/vpn/L2tpIpsecProfile.java
index 181619d..4ae2dec 100644
--- a/vpn/java/android/net/vpn/L2tpIpsecProfile.java
+++ b/vpn/java/android/net/vpn/L2tpIpsecProfile.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007, The Android Open Source Project
+ * 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.
@@ -19,15 +19,14 @@
import android.os.Parcel;
/**
- * The profile for L2TP-over-IPSec type of VPN.
+ * The profile for certificate-based L2TP-over-IPSec type of VPN.
* {@hide}
*/
-public class L2tpIpsecProfile extends VpnProfile {
+public class L2tpIpsecProfile extends L2tpProfile {
private static final long serialVersionUID = 1L;
private String mUserCertificate;
private String mCaCertificate;
- private String mUserkey;
@Override
public VpnType getType() {
@@ -50,20 +49,11 @@
return mUserCertificate;
}
- public void setUserkey(String name) {
- mUserkey = name;
- }
-
- public String getUserkey() {
- return mUserkey;
- }
-
@Override
protected void readFromParcel(Parcel in) {
super.readFromParcel(in);
mCaCertificate = in.readString();
mUserCertificate = in.readString();
- mUserkey = in.readString();
}
@Override
@@ -71,6 +61,5 @@
super.writeToParcel(parcel, flags);
parcel.writeString(mCaCertificate);
parcel.writeString(mUserCertificate);
- parcel.writeString(mUserkey);
}
}
diff --git a/vpn/java/android/net/vpn/L2tpIpsecPskProfile.java b/vpn/java/android/net/vpn/L2tpIpsecPskProfile.java
new file mode 100644
index 0000000..7a03018
--- /dev/null
+++ b/vpn/java/android/net/vpn/L2tpIpsecPskProfile.java
@@ -0,0 +1,54 @@
+/*
+ * 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.net.vpn;
+
+import android.os.Parcel;
+
+/**
+ * The profile for pre-shared-key-based L2TP-over-IPSec type of VPN.
+ * {@hide}
+ */
+public class L2tpIpsecPskProfile extends L2tpProfile {
+ private static final long serialVersionUID = 1L;
+
+ private String mPresharedKey;
+
+ @Override
+ public VpnType getType() {
+ return VpnType.L2TP_IPSEC_PSK;
+ }
+
+ public void setPresharedKey(String key) {
+ mPresharedKey = key;
+ }
+
+ public String getPresharedKey() {
+ return mPresharedKey;
+ }
+
+ @Override
+ protected void readFromParcel(Parcel in) {
+ super.readFromParcel(in);
+ mPresharedKey = in.readString();
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ super.writeToParcel(parcel, flags);
+ parcel.writeString(mPresharedKey);
+ }
+}
diff --git a/vpn/java/android/net/vpn/L2tpProfile.java b/vpn/java/android/net/vpn/L2tpProfile.java
index 59d4981..dbba0c5 100644
--- a/vpn/java/android/net/vpn/L2tpProfile.java
+++ b/vpn/java/android/net/vpn/L2tpProfile.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007, The Android Open Source Project
+ * 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.
@@ -16,6 +16,8 @@
package android.net.vpn;
+import android.os.Parcel;
+
/**
* The profile for L2TP type of VPN.
* {@hide}
@@ -23,8 +25,44 @@
public class L2tpProfile extends VpnProfile {
private static final long serialVersionUID = 1L;
+ private boolean mSecret;
+ private String mSecretString;
+
@Override
public VpnType getType() {
return VpnType.L2TP;
}
+
+ /**
+ * Enables/disables the secret for authenticating tunnel connection.
+ */
+ public void setSecretEnabled(boolean enabled) {
+ mSecret = enabled;
+ }
+
+ public boolean isSecretEnabled() {
+ return mSecret;
+ }
+
+ public void setSecretString(String secret) {
+ mSecretString = secret;
+ }
+
+ public String getSecretString() {
+ return mSecretString;
+ }
+
+ @Override
+ protected void readFromParcel(Parcel in) {
+ super.readFromParcel(in);
+ mSecret = in.readInt() > 0;
+ mSecretString = in.readString();
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ super.writeToParcel(parcel, flags);
+ parcel.writeInt(mSecret ? 1 : 0);
+ parcel.writeString(mSecretString);
+ }
}
diff --git a/vpn/java/android/net/vpn/PptpProfile.java b/vpn/java/android/net/vpn/PptpProfile.java
new file mode 100644
index 0000000..c68bb71
--- /dev/null
+++ b/vpn/java/android/net/vpn/PptpProfile.java
@@ -0,0 +1,30 @@
+/*
+ * 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.net.vpn;
+
+/**
+ * The profile for PPTP type of VPN.
+ * {@hide}
+ */
+public class PptpProfile extends VpnProfile {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public VpnType getType() {
+ return VpnType.PPTP;
+ }
+}
diff --git a/vpn/java/android/net/vpn/VpnManager.java b/vpn/java/android/net/vpn/VpnManager.java
index 98795bd..dc70b26 100644
--- a/vpn/java/android/net/vpn/VpnManager.java
+++ b/vpn/java/android/net/vpn/VpnManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007, The Android Open Source Project
+ * 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.
diff --git a/vpn/java/android/net/vpn/VpnProfile.aidl b/vpn/java/android/net/vpn/VpnProfile.aidl
index ad34bfc..edeaef0 100644
--- a/vpn/java/android/net/vpn/VpnProfile.aidl
+++ b/vpn/java/android/net/vpn/VpnProfile.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007, The Android Open Source Project
+ * 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.
diff --git a/vpn/java/android/net/vpn/VpnProfile.java b/vpn/java/android/net/vpn/VpnProfile.java
index 9e24da4..bd6c809 100644
--- a/vpn/java/android/net/vpn/VpnProfile.java
+++ b/vpn/java/android/net/vpn/VpnProfile.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007, The Android Open Source Project
+ * 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.
diff --git a/vpn/java/android/net/vpn/VpnState.java b/vpn/java/android/net/vpn/VpnState.java
index 977d938..ebd9364 100644
--- a/vpn/java/android/net/vpn/VpnState.java
+++ b/vpn/java/android/net/vpn/VpnState.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007, The Android Open Source Project
+ * 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.
diff --git a/vpn/java/android/net/vpn/VpnType.java b/vpn/java/android/net/vpn/VpnType.java
index 91b0ea2..c7df943 100644
--- a/vpn/java/android/net/vpn/VpnType.java
+++ b/vpn/java/android/net/vpn/VpnType.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007, The Android Open Source Project
+ * 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.
@@ -21,14 +21,21 @@
* {@hide}
*/
public enum VpnType {
- L2TP_IPSEC("L2TP/IPSec", L2tpIpsecProfile.class),
- L2TP("L2TP", L2tpProfile.class);
+ PPTP("PPTP", "", PptpProfile.class),
+ L2TP("L2TP", "", L2tpProfile.class),
+ L2TP_IPSEC_PSK("L2TP/IPSec PSK", "Pre-shared key based L2TP/IPSec VPN",
+ L2tpIpsecPskProfile.class),
+ L2TP_IPSEC("L2TP/IPSec CRT", "Certificate based L2TP/IPSec VPN",
+ L2tpIpsecProfile.class);
private String mDisplayName;
+ private String mDescription;
private Class<? extends VpnProfile> mClass;
- VpnType(String displayName, Class<? extends VpnProfile> klass) {
+ VpnType(String displayName, String description,
+ Class<? extends VpnProfile> klass) {
mDisplayName = displayName;
+ mDescription = description;
mClass = klass;
}
@@ -36,6 +43,10 @@
return mDisplayName;
}
+ public String getDescription() {
+ return mDescription;
+ }
+
public Class<? extends VpnProfile> getProfileClass() {
return mClass;
}