Merge change 5906 into donut
* changes:
resolve complex value in application context instead of system context.
diff --git a/api/current.xml b/api/current.xml
index 673d053..7faca03 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -3408,50 +3408,6 @@
visibility="public"
>
</field>
-<field name="donut_resource_pad22"
- type="int"
- transient="false"
- volatile="false"
- value="16843402"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="donut_resource_pad23"
- type="int"
- transient="false"
- volatile="false"
- value="16843401"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="donut_resource_pad24"
- type="int"
- transient="false"
- volatile="false"
- value="16843400"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="donut_resource_pad25"
- type="int"
- transient="false"
- volatile="false"
- value="16843399"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
<field name="donut_resource_pad3"
type="int"
transient="false"
@@ -6730,6 +6686,17 @@
visibility="public"
>
</field>
+<field name="progressBarStyleInverse"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843399"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="progressBarStyleLarge"
type="int"
transient="false"
@@ -6741,6 +6708,17 @@
visibility="public"
>
</field>
+<field name="progressBarStyleLargeInverse"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843401"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="progressBarStyleSmall"
type="int"
transient="false"
@@ -6752,6 +6730,17 @@
visibility="public"
>
</field>
+<field name="progressBarStyleSmallInverse"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843400"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="progressBarStyleSmallTitle"
type="int"
transient="false"
@@ -7302,6 +7291,17 @@
visibility="public"
>
</field>
+<field name="searchSettingsDescription"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843402"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="searchSuggestAuthority"
type="int"
transient="false"
@@ -15156,6 +15156,17 @@
visibility="public"
>
</field>
+<field name="Widget_ProgressBar_Inverse"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16973915"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="Widget_ProgressBar_Large"
type="int"
transient="false"
@@ -15167,6 +15178,17 @@
visibility="public"
>
</field>
+<field name="Widget_ProgressBar_Large_Inverse"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16973916"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="Widget_ProgressBar_Small"
type="int"
transient="false"
@@ -15178,6 +15200,17 @@
visibility="public"
>
</field>
+<field name="Widget_ProgressBar_Small_Inverse"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16973917"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="Widget_RatingBar"
type="int"
transient="false"
@@ -15409,39 +15442,6 @@
visibility="public"
>
</field>
-<field name="donut_resource_pad20"
- type="int"
- transient="false"
- volatile="false"
- value="16973917"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="donut_resource_pad21"
- type="int"
- transient="false"
- volatile="false"
- value="16973916"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="donut_resource_pad22"
- type="int"
- transient="false"
- volatile="false"
- value="16973915"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
<field name="donut_resource_pad3"
type="int"
transient="false"
@@ -31346,6 +31346,17 @@
visibility="public"
>
</field>
+<field name="ACTION_BATTERY_OKAY"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""android.intent.action.BATTERY_OKAY""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="ACTION_BOOT_COMPLETED"
type="java.lang.String"
transient="false"
@@ -56678,6 +56689,47 @@
</package>
<package name="android.graphics.drawable"
>
+<interface name="Animatable"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="isRunning"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="start"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="stop"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+</interface>
<class name="AnimationDrawable"
extends="android.graphics.drawable.DrawableContainer"
abstract="false"
@@ -56686,6 +56738,8 @@
deprecated="not deprecated"
visibility="public"
>
+<implements name="android.graphics.drawable.Animatable">
+</implements>
<implements name="java.lang.Runnable">
</implements>
<constructor name="AnimationDrawable"
diff --git a/camera/libcameraservice/CameraService.cpp b/camera/libcameraservice/CameraService.cpp
index 105d4d2..022fe5a 100644
--- a/camera/libcameraservice/CameraService.cpp
+++ b/camera/libcameraservice/CameraService.cpp
@@ -410,11 +410,14 @@
// pass the buffered ISurface to the camera service
status_t CameraService::Client::setPreviewDisplay(const sp<ISurface>& surface)
{
- LOGD("setPreviewDisplay(%p) (pid %d)", surface.get(), getCallingPid());
+ LOGD("setPreviewDisplay(%p) (pid %d)",
+ ((surface == NULL) ? NULL : surface.get()), getCallingPid());
Mutex::Autolock lock(mLock);
status_t result = checkPid();
if (result != NO_ERROR) return result;
+
Mutex::Autolock surfaceLock(mSurfaceLock);
+ result = NO_ERROR;
// asBinder() is safe on NULL (returns NULL)
if (surface->asBinder() != mSurface->asBinder()) {
if (mSurface != 0 && !mUseOverlay) {
@@ -422,8 +425,17 @@
mSurface->unregisterBuffers();
}
mSurface = surface;
+ // If preview has been already started, set overlay or register preview
+ // buffers now.
+ if (mHardware->previewEnabled()) {
+ if (mUseOverlay) {
+ result = setOverlay();
+ } else if (mSurface != 0) {
+ result = registerPreviewBuffers();
+ }
+ }
}
- return NO_ERROR;
+ return result;
}
// set the preview callback flag to affect how the received frames from
@@ -436,7 +448,7 @@
mPreviewCallbackFlag = callback_flag;
}
-// start preview mode, must call setPreviewDisplay first
+// start preview mode
status_t CameraService::Client::startCameraMode(camera_mode mode)
{
int callingPid = getCallingPid();
@@ -456,16 +468,18 @@
return INVALID_OPERATION;
}
- if (mSurface == 0) {
- LOGE("setPreviewDisplay must be called before startCameraMode!");
- return INVALID_OPERATION;
- }
-
switch(mode) {
case CAMERA_RECORDING_MODE:
+ if (mSurface == 0) {
+ LOGE("setPreviewDisplay must be called before startRecordingMode.");
+ return INVALID_OPERATION;
+ }
return startRecordingMode();
default: // CAMERA_PREVIEW_MODE
+ if (mSurface == 0) {
+ LOGD("mSurface is not set yet.");
+ }
return startPreviewMode();
}
}
@@ -498,6 +512,62 @@
return ret;
}
+status_t CameraService::Client::setOverlay()
+{
+ LOGD("setOverlay");
+ int w, h;
+ CameraParameters params(mHardware->getParameters());
+ params.getPreviewSize(&w, &h);
+
+ const char *format = params.getPreviewFormat();
+ int fmt;
+ if (!strcmp(format, "yuv422i"))
+ fmt = OVERLAY_FORMAT_YCbCr_422_I;
+ else if (!strcmp(format, "rgb565"))
+ fmt = OVERLAY_FORMAT_RGB_565;
+ else {
+ LOGE("Invalid preview format for overlays");
+ return -EINVAL;
+ }
+
+ status_t ret = NO_ERROR;
+ if (mSurface != 0) {
+ sp<OverlayRef> ref = mSurface->createOverlay(w, h, fmt);
+ ret = mHardware->setOverlay(new Overlay(ref));
+ } else {
+ ret = mHardware->setOverlay(NULL);
+ }
+ if (ret != NO_ERROR) {
+ LOGE("mHardware->setOverlay() failed with status %d\n", ret);
+ }
+ return ret;
+}
+
+status_t CameraService::Client::registerPreviewBuffers()
+{
+ int w, h;
+ CameraParameters params(mHardware->getParameters());
+ params.getPreviewSize(&w, &h);
+
+ uint32_t transform = 0;
+ if (params.getOrientation() ==
+ CameraParameters::CAMERA_ORIENTATION_PORTRAIT) {
+ LOGV("portrait mode");
+ transform = ISurface::BufferHeap::ROT_90;
+ }
+ ISurface::BufferHeap buffers(w, h, w, h,
+ PIXEL_FORMAT_YCbCr_420_SP,
+ transform,
+ 0,
+ mHardware->getPreviewHeap());
+
+ status_t ret = mSurface->registerBuffers(buffers);
+ if (ret != NO_ERROR) {
+ LOGE("registerBuffers failed with status %d", ret);
+ }
+ return ret;
+}
+
status_t CameraService::Client::startPreviewMode()
{
LOGD("startPreviewMode (pid %d)", getCallingPid());
@@ -511,55 +581,24 @@
#if DEBUG_DUMP_PREVIEW_FRAME_TO_FILE
debug_frame_cnt = 0;
#endif
- status_t ret = UNKNOWN_ERROR;
- int w, h;
- CameraParameters params(mHardware->getParameters());
- params.getPreviewSize(&w, &h);
+ status_t ret = NO_ERROR;
if (mUseOverlay) {
- const char *format = params.getPreviewFormat();
- int fmt;
- LOGD("Use Overlays");
- if (!strcmp(format, "yuv422i"))
- fmt = OVERLAY_FORMAT_YCbCr_422_I;
- else if (!strcmp(format, "rgb565"))
- fmt = OVERLAY_FORMAT_RGB_565;
- else {
- LOGE("Invalid preview format for overlays");
- return -EINVAL;
+ // If preview display has been set, set overlay now.
+ if (mSurface != 0) {
+ ret = setOverlay();
}
- sp<OverlayRef> ref = mSurface->createOverlay(w, h, fmt);
- ret = mHardware->setOverlay(new Overlay(ref));
- if (ret != NO_ERROR) {
- LOGE("mHardware->setOverlay() failed with status %d\n", ret);
- return ret;
- }
+ if (ret != NO_ERROR) return ret;
ret = mHardware->startPreview(NULL, mCameraService.get());
- if (ret != NO_ERROR)
- LOGE("mHardware->startPreview() failed with status %d\n", ret);
-
} else {
ret = mHardware->startPreview(previewCallback,
mCameraService.get());
- if (ret == NO_ERROR) {
-
- mSurface->unregisterBuffers();
-
- uint32_t transform = 0;
- if (params.getOrientation() ==
- CameraParameters::CAMERA_ORIENTATION_PORTRAIT) {
- LOGV("portrait mode");
- transform = ISurface::BufferHeap::ROT_90;
- }
- ISurface::BufferHeap buffers(w, h, w, h,
- PIXEL_FORMAT_YCbCr_420_SP,
- transform,
- 0,
- mHardware->getPreviewHeap());
-
- mSurface->registerBuffers(buffers);
- } else {
- LOGE("mHardware->startPreview() failed with status %d", ret);
+ if (ret != NO_ERROR) return ret;
+ // If preview display has been set, register preview buffers now.
+ if (mSurface != 0) {
+ // Unregister here because the surface registered with raw heap.
+ mSurface->unregisterBuffers();
+ ret = registerPreviewBuffers();
}
}
return ret;
diff --git a/camera/libcameraservice/CameraService.h b/camera/libcameraservice/CameraService.h
index 8b8b54c..0f07673 100644
--- a/camera/libcameraservice/CameraService.h
+++ b/camera/libcameraservice/CameraService.h
@@ -157,6 +157,8 @@
status_t startCameraMode(camera_mode mode);
status_t startPreviewMode();
status_t startRecordingMode();
+ status_t setOverlay();
+ status_t registerPreviewBuffers();
// Ensures atomicity among the public methods
mutable Mutex mLock;
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 62dc651..5ee29ac 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -3981,7 +3981,10 @@
ProviderRecord pr = mProviderMap.get(name);
if (pr.mProvider.asBinder() == provider.asBinder()) {
Log.i(TAG, "Removing dead content provider: " + name);
- mProviderMap.remove(name);
+ ProviderRecord removed = mProviderMap.remove(name);
+ if (removed != null) {
+ removed.mProvider.asBinder().unlinkToDeath(removed, 0);
+ }
}
}
}
@@ -3990,7 +3993,10 @@
ProviderRecord pr = mProviderMap.get(name);
if (pr.mProvider.asBinder() == provider.asBinder()) {
Log.i(TAG, "Removing dead content provider: " + name);
- mProviderMap.remove(name);
+ ProviderRecord removed = mProviderMap.remove(name);
+ if (removed != null) {
+ removed.mProvider.asBinder().unlinkToDeath(removed, 0);
+ }
}
}
diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java
index 6fe4896..0785029 100644
--- a/core/java/android/app/SearchDialog.java
+++ b/core/java/android/app/SearchDialog.java
@@ -34,6 +34,7 @@
import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Animatable;
import android.net.Uri;
import android.os.Bundle;
import android.os.SystemClock;
@@ -241,8 +242,15 @@
// Reset any stored values from last time dialog was shown.
mStoredComponentName = null;
mStoredAppSearchData = null;
-
- return doShow(initialQuery, selectInitialQuery, componentName, appSearchData, globalSearch);
+
+ boolean success = doShow(initialQuery, selectInitialQuery, componentName, appSearchData,
+ globalSearch);
+ if (success) {
+ // Display the drop down as soon as possible instead of waiting for the rest of the
+ // pending UI stuff to get done, so that things appear faster to the user.
+ mSearchAutoComplete.showDropDownAfterLayout();
+ }
+ return success;
}
/**
@@ -422,11 +430,11 @@
if (working) {
mSearchAutoComplete.setCompoundDrawablesWithIntrinsicBounds(
null, null, mWorkingSpinner, null);
-// mWorkingSpinner.start();
+ ((Animatable) mWorkingSpinner).start();
} else {
mSearchAutoComplete.setCompoundDrawablesWithIntrinsicBounds(
null, null, null, null);
-// mWorkingSpinner.stop();
+ ((Animatable) mWorkingSpinner).stop();
}
}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 6fe5506..6b723bc 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1290,6 +1290,13 @@
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_BATTERY_LOW = "android.intent.action.BATTERY_LOW";
/**
+ * Broadcast Action: Indicates the battery is now okay after being low.
+ * This will be sent after {@link #ACTION_BATTERY_LOW} once the battery has
+ * gone back up to an okay state.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_BATTERY_OKAY = "android.intent.action.BATTERY_OKAY";
+ /**
* Broadcast Action: External power has been connected to the device.
* This is intended for applications that wish to register specifically to this notification.
* Unlike ACTION_BATTERY_CHANGED, applications will be woken for this and so do not have to
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 09fbc97..3ce951f 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -155,7 +155,11 @@
* @throws IOException if the method fails.
*/
public final void setPreviewDisplay(SurfaceHolder holder) throws IOException {
- setPreviewDisplay(holder.getSurface());
+ if (holder != null) {
+ setPreviewDisplay(holder.getSurface());
+ } else {
+ setPreviewDisplay((Surface)null);
+ }
}
private native final void setPreviewDisplay(Surface surface);
diff --git a/core/java/android/net/http/RequestHandle.java b/core/java/android/net/http/RequestHandle.java
index c4ee5b0..6a97951 100644
--- a/core/java/android/net/http/RequestHandle.java
+++ b/core/java/android/net/http/RequestHandle.java
@@ -159,11 +159,11 @@
e.printStackTrace();
}
- // update the "cookie" header based on the redirected url
- mHeaders.remove("cookie");
+ // update the "Cookie" header based on the redirected url
+ mHeaders.remove("Cookie");
String cookie = CookieManager.getInstance().getCookie(mUri);
if (cookie != null && cookie.length() > 0) {
- mHeaders.put("cookie", cookie);
+ mHeaders.put("Cookie", cookie);
}
if ((statusCode == 302 || statusCode == 303) && mMethod.equals("POST")) {
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 51d1951..bc7b5be 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -344,7 +344,10 @@
// Check if file exists with a FileInputStream
FileInputStream stream = new FileInputStream(imagePath);
try {
- return insertImage(cr, BitmapFactory.decodeFile(imagePath), name, description);
+ Bitmap bm = BitmapFactory.decodeFile(imagePath);
+ String ret = insertImage(cr, bm, name, description);
+ bm.recycle();
+ return ret;
} finally {
try {
stream.close();
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 7356326..2cca837 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2828,6 +2828,13 @@
"vending_heartbeat_frequency_ms";
/**
+ * Frequency in milliseconds at which we should resend pending download
+ * requests to the API Server from the Vending Machine client.
+ */
+ public static final String VENDING_PENDING_DOWNLOAD_RESEND_FREQUENCY_MS =
+ "vending_pd_resend_frequency_ms";
+
+ /**
* URL that points to the legal terms of service to display in Settings.
* <p>
* This should be a https URL. For a pretty user-friendly URL, use
diff --git a/core/java/android/server/search/SearchableInfo.java b/core/java/android/server/search/SearchableInfo.java
index 90dfa0b..8ef1f15 100644
--- a/core/java/android/server/search/SearchableInfo.java
+++ b/core/java/android/server/search/SearchableInfo.java
@@ -67,6 +67,7 @@
private final int mSearchImeOptions;
private final boolean mIncludeInGlobalSearch;
private final boolean mQueryAfterZeroResults;
+ private final String mSettingsDescription;
private final String mSuggestAuthority;
private final String mSuggestPath;
private final String mSuggestSelection;
@@ -134,6 +135,14 @@
public boolean shouldRewriteQueryFromText() {
return 0 != (mSearchMode & SEARCH_MODE_QUERY_REWRITE_FROM_TEXT);
}
+
+ /**
+ * Gets the description to use for this source in system search settings, or null if
+ * none has been specified.
+ */
+ public String getSettingsDescription() {
+ return mSettingsDescription;
+ }
/**
* Retrieve the path for obtaining search suggestions.
@@ -280,6 +289,8 @@
mQueryAfterZeroResults = a.getBoolean(
com.android.internal.R.styleable.Searchable_queryAfterZeroResults, false);
+ mSettingsDescription = a.getString(
+ com.android.internal.R.styleable.Searchable_searchSettingsDescription);
mSuggestAuthority = a.getString(
com.android.internal.R.styleable.Searchable_searchSuggestAuthority);
mSuggestPath = a.getString(
@@ -448,6 +459,7 @@
+ ",suggestAuthority=" + searchable.getSuggestAuthority()
+ ",target=" + searchable.getSearchActivity().getClassName()
+ ",global=" + searchable.shouldIncludeInGlobalSearch()
+ + ",settingsDescription=" + searchable.getSettingsDescription()
+ ",threshold=" + searchable.getSuggestThreshold());
} else {
Log.d(LOG_TAG, "Checked " + activityInfo.name + ", no searchable meta-data");
@@ -686,7 +698,8 @@
mSearchImeOptions = in.readInt();
mIncludeInGlobalSearch = in.readInt() != 0;
mQueryAfterZeroResults = in.readInt() != 0;
-
+
+ mSettingsDescription = in.readString();
mSuggestAuthority = in.readString();
mSuggestPath = in.readString();
mSuggestSelection = in.readString();
@@ -723,6 +736,7 @@
dest.writeInt(mIncludeInGlobalSearch ? 1 : 0);
dest.writeInt(mQueryAfterZeroResults ? 1 : 0);
+ dest.writeString(mSettingsDescription);
dest.writeString(mSuggestAuthority);
dest.writeString(mSuggestPath);
dest.writeString(mSuggestSelection);
diff --git a/core/java/android/text/method/Touch.java b/core/java/android/text/method/Touch.java
index f2fb9cb..dfc16f5 100644
--- a/core/java/android/text/method/Touch.java
+++ b/core/java/android/text/method/Touch.java
@@ -81,6 +81,12 @@
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
+ ds = buffer.getSpans(0, buffer.length(), DragState.class);
+
+ for (int i = 0; i < ds.length; i++) {
+ buffer.removeSpan(ds[i]);
+ }
+
buffer.setSpan(new DragState(event.getX(), event.getY(),
widget.getScrollX(), widget.getScrollY()),
0, 0, Spannable.SPAN_MARK_MARK);
diff --git a/core/java/android/webkit/FrameLoader.java b/core/java/android/webkit/FrameLoader.java
index 6f1b160..66ab021 100644
--- a/core/java/android/webkit/FrameLoader.java
+++ b/core/java/android/webkit/FrameLoader.java
@@ -364,7 +364,7 @@
String cookie = CookieManager.getInstance().getCookie(
mListener.getWebAddress());
if (cookie != null && cookie.length() > 0) {
- mHeaders.put("cookie", cookie);
+ mHeaders.put("Cookie", cookie);
}
}
}
diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java
index e84e5b0..2182384 100644
--- a/core/java/android/widget/AutoCompleteTextView.java
+++ b/core/java/android/widget/AutoCompleteTextView.java
@@ -124,6 +124,7 @@
private boolean mBlockCompletion;
private AutoCompleteTextView.ListSelectorHider mHideSelector;
+ private Runnable mShowDropDownRunnable;
private AutoCompleteTextView.PassThroughClickListener mPassThroughClickListener;
@@ -1080,6 +1081,15 @@
}
/**
+ * Issues a runnable to show the dropdown as soon as possible.
+ *
+ * @hide internal used only by Search Dialog
+ */
+ public void showDropDownAfterLayout() {
+ post(mShowDropDownRunnable);
+ }
+
+ /**
* <p>Displays the drop down on screen.</p>
*/
public void showDropDown() {
@@ -1190,6 +1200,22 @@
mHideSelector = new ListSelectorHider();
+ /**
+ * This Runnable exists for the sole purpose of checking if the view layout has got
+ * completed and if so call showDropDown to display the drop down. This is used to show
+ * the drop down as soon as possible after user opens up the search dialog, without
+ * waiting for the normal UI pipeline to do it's job which is slower than this method.
+ */
+ mShowDropDownRunnable = new Runnable() {
+ public void run() {
+ // View layout should be all done before displaying the drop down.
+ View view = getDropDownAnchorView();
+ if (view != null && view.getWindowToken() != null) {
+ showDropDown();
+ }
+ }
+ };
+
mDropDownList = new DropDownListView(context);
mDropDownList.setSelector(mDropDownListHighlight);
mDropDownList.setAdapter(mAdapter);
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index 441414a..2c9e71e 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -30,6 +30,7 @@
import android.graphics.drawable.LayerDrawable;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.StateListDrawable;
+import android.graphics.drawable.Animatable;
import android.graphics.drawable.shapes.RoundRectShape;
import android.graphics.drawable.shapes.Shape;
import android.util.AttributeSet;
@@ -683,7 +684,7 @@
return;
}
- if (mIndeterminateDrawable instanceof AnimationDrawable) {
+ if (mIndeterminateDrawable instanceof Animatable) {
mShouldStartAnimationDrawable = true;
mAnimation = null;
} else {
@@ -708,8 +709,8 @@
void stopAnimation() {
mAnimation = null;
mTransformation = null;
- if (mIndeterminateDrawable instanceof AnimationDrawable) {
- ((AnimationDrawable) mIndeterminateDrawable).stop();
+ if (mIndeterminateDrawable instanceof Animatable) {
+ ((Animatable) mIndeterminateDrawable).stop();
mShouldStartAnimationDrawable = false;
}
}
@@ -818,8 +819,8 @@
}
d.draw(canvas);
canvas.restore();
- if (mShouldStartAnimationDrawable && d instanceof AnimationDrawable) {
- ((AnimationDrawable) d).start();
+ if (mShouldStartAnimationDrawable && d instanceof Animatable) {
+ ((Animatable) d).start();
mShouldStartAnimationDrawable = false;
}
}
diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp
index 8e48b38..3dcc09f 100644
--- a/core/jni/android_hardware_Camera.cpp
+++ b/core/jni/android_hardware_Camera.cpp
@@ -171,6 +171,10 @@
// VM pointer will be NULL if object is released
Mutex::Autolock _l(mLock);
JNIEnv *env = AndroidRuntime::getJNIEnv();
+ if (mCameraJObjectWeak == NULL) {
+ LOGW("callback on dead camera object");
+ return;
+ }
// return data based on callback type
switch(msgType) {
@@ -262,7 +266,10 @@
sp<Camera> camera = get_native_camera(env, thiz, NULL);
if (camera == 0) return;
- sp<Surface> surface = reinterpret_cast<Surface*>(env->GetIntField(jSurface, fields.surface));
+ sp<Surface> surface = NULL;
+ if (jSurface != NULL) {
+ surface = reinterpret_cast<Surface*>(env->GetIntField(jSurface, fields.surface));
+ }
if (camera->setPreviewDisplay(surface) != NO_ERROR) {
jniThrowException(env, "java/io/IOException", "setPreviewDisplay failed");
}
diff --git a/core/res/res/drawable/progress_large.xml b/core/res/res/drawable/progress_large.xml
index 4669104..4f016bc 100644
--- a/core/res/res/drawable/progress_large.xml
+++ b/core/res/res/drawable/progress_large.xml
@@ -1,45 +1,25 @@
<?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.
+<!--
+/*
+**
+** 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.
+** 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.
+*/
-->
-
-
-<rotate xmlns:android="http://schemas.android.com/apk/res/android"
- android:pivotX="50%" android:pivotY="50%"
- android:fromDegrees="0" android:toDegrees="360">
-
- <shape
- android:shape="ring"
- android:innerRadiusRatio="3"
- android:thicknessRatio="8"
- android:useLevel="false">
-
- <size
- android:width="76dip"
- android:height="76dip"
- />
-
- <gradient
- android:type="sweep"
- android:useLevel="false"
- android:startColor="#4c737373"
- android:centerColor="#4c737373"
- android:centerY="0.50"
- android:endColor="#ffffd300"
- />
-
- </shape>
-
-</rotate>
-
+<animated-rotate xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@drawable/spinner_black_76"
+ android:pivotX="50%"
+ android:pivotY="50%"
+ android:framesCount="12"
+ android:frameDuration="100" />
diff --git a/core/res/res/drawable/progress_large_white.xml b/core/res/res/drawable/progress_large_white.xml
new file mode 100644
index 0000000..c690ed4
--- /dev/null
+++ b/core/res/res/drawable/progress_large_white.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** 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.
+** 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.
+*/
+-->
+<animated-rotate xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@drawable/spinner_white_76"
+ android:pivotX="50%"
+ android:pivotY="50%"
+ android:framesCount="12"
+ android:frameDuration="100" />
diff --git a/core/res/res/drawable/progress_medium.xml b/core/res/res/drawable/progress_medium.xml
index 92aebb5..eb1bd50 100644
--- a/core/res/res/drawable/progress_medium.xml
+++ b/core/res/res/drawable/progress_medium.xml
@@ -1,43 +1,25 @@
<?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.
+<!--
+/*
+**
+** 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.
+** 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.
+*/
-->
-
-<rotate xmlns:android="http://schemas.android.com/apk/res/android"
- android:pivotX="50%" android:pivotY="50%"
- android:fromDegrees="0" android:toDegrees="360">
-
- <shape
- android:shape="ring"
- android:innerRadiusRatio="3"
- android:thicknessRatio="8"
- android:useLevel="false">
-
- <size
- android:width="48dip"
- android:height="48dip"
- />
-
- <gradient
- android:type="sweep"
- android:useLevel="false"
- android:startColor="#4c737373"
- android:centerColor="#4c737373"
- android:centerY="0.50"
- android:endColor="#ffffd300"
- />
-
- </shape>
-
-</rotate>
+<animated-rotate xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@drawable/spinner_black_48"
+ android:pivotX="50%"
+ android:pivotY="50%"
+ android:framesCount="12"
+ android:frameDuration="100" />
diff --git a/core/res/res/drawable/progress_medium_white.xml b/core/res/res/drawable/progress_medium_white.xml
new file mode 100644
index 0000000..b4f9b31
--- /dev/null
+++ b/core/res/res/drawable/progress_medium_white.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** 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.
+** 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.
+*/
+-->
+<animated-rotate xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@drawable/spinner_white_48"
+ android:pivotX="50%"
+ android:pivotY="50%"
+ android:framesCount="12"
+ android:frameDuration="100" />
diff --git a/core/res/res/drawable/progress_small.xml b/core/res/res/drawable/progress_small.xml
index e5b0021..e0ee5e4 100644
--- a/core/res/res/drawable/progress_small.xml
+++ b/core/res/res/drawable/progress_small.xml
@@ -1,45 +1,25 @@
<?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.
+<!--
+/*
+**
+** 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.
+** 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.
+*/
-->
-
-
-<rotate xmlns:android="http://schemas.android.com/apk/res/android"
- android:pivotX="50%" android:pivotY="50%"
- android:fromDegrees="0" android:toDegrees="360">
-
- <!-- An extra pixel is added on both ratios for stroke -->
- <shape
- android:shape="ring"
- android:innerRadiusRatio="3.2"
- android:thicknessRatio="5.333"
- android:useLevel="false">
-
- <size
- android:width="16dip"
- android:height="16dip"
- />
-
- <gradient
- android:type="sweep"
- android:useLevel="false"
- android:startColor="#4c737373"
- android:centerColor="#4c737373"
- android:centerY="0.50"
- android:endColor="#ffffd300"
- />
-
- </shape>
-
-</rotate>
+<animated-rotate xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@drawable/spinner_black_16"
+ android:pivotX="50%"
+ android:pivotY="50%"
+ android:framesCount="12"
+ android:frameDuration="100" />
diff --git a/core/res/res/drawable/progress_small_titlebar.xml b/core/res/res/drawable/progress_small_titlebar.xml
index cf8e41cb..8cfba86 100644
--- a/core/res/res/drawable/progress_small_titlebar.xml
+++ b/core/res/res/drawable/progress_small_titlebar.xml
@@ -1,45 +1,25 @@
<?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.
+<!--
+/*
+**
+** 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.
+** 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.
+*/
-->
-
-
-<rotate xmlns:android="http://schemas.android.com/apk/res/android"
- android:pivotX="50%" android:pivotY="50%"
- android:fromDegrees="0" android:toDegrees="360">
-
- <!-- An extra pixel is added on both ratios for stroke -->
- <shape
- android:shape="ring"
- android:innerRadiusRatio="3.2"
- android:thicknessRatio="5.333"
- android:useLevel="false">
-
- <size
- android:width="16dip"
- android:height="16dip"
- />
-
- <gradient
- android:type="sweep"
- android:useLevel="false"
- android:startColor="#ff666666"
- android:centerColor="#ff666666"
- android:centerY="0.50"
- android:endColor="#ffffd300"
- />
-
- </shape>
-
-</rotate>
+<animated-rotate xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@drawable/spinner_white_16"
+ android:pivotX="50%"
+ android:pivotY="50%"
+ android:framesCount="12"
+ android:frameDuration="100" />
diff --git a/core/res/res/drawable/progress_small_white.xml b/core/res/res/drawable/progress_small_white.xml
new file mode 100644
index 0000000..8cfba86
--- /dev/null
+++ b/core/res/res/drawable/progress_small_white.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** 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.
+** 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.
+*/
+-->
+<animated-rotate xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@drawable/spinner_white_16"
+ android:pivotX="50%"
+ android:pivotY="50%"
+ android:framesCount="12"
+ android:frameDuration="100" />
diff --git a/core/res/res/drawable/spinner_black_16.png b/core/res/res/drawable/spinner_black_16.png
new file mode 100644
index 0000000..5ee33ce
--- /dev/null
+++ b/core/res/res/drawable/spinner_black_16.png
Binary files differ
diff --git a/core/res/res/drawable/spinner_black_48.png b/core/res/res/drawable/spinner_black_48.png
new file mode 100644
index 0000000..3a68192
--- /dev/null
+++ b/core/res/res/drawable/spinner_black_48.png
Binary files differ
diff --git a/core/res/res/drawable/spinner_black_76.png b/core/res/res/drawable/spinner_black_76.png
new file mode 100644
index 0000000..ec57460
--- /dev/null
+++ b/core/res/res/drawable/spinner_black_76.png
Binary files differ
diff --git a/core/res/res/drawable/spinner_white_16.png b/core/res/res/drawable/spinner_white_16.png
new file mode 100644
index 0000000..dd2e1fd
--- /dev/null
+++ b/core/res/res/drawable/spinner_white_16.png
Binary files differ
diff --git a/core/res/res/drawable/spinner_white_48.png b/core/res/res/drawable/spinner_white_48.png
new file mode 100644
index 0000000..d25a33e
--- /dev/null
+++ b/core/res/res/drawable/spinner_white_48.png
Binary files differ
diff --git a/core/res/res/drawable/spinner_white_76.png b/core/res/res/drawable/spinner_white_76.png
new file mode 100644
index 0000000..f53e8ff
--- /dev/null
+++ b/core/res/res/drawable/spinner_white_76.png
Binary files differ
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index d070107..10308b6 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -350,6 +350,12 @@
<attr name="progressBarStyleSmallTitle" format="reference" />
<!-- Large ProgressBar style. This is a large circular progress bar. -->
<attr name="progressBarStyleLarge" format="reference" />
+ <!-- Inverse ProgressBar style. This is a medium circular progress bar. -->
+ <attr name="progressBarStyleInverse" format="reference" />
+ <!-- Small inverse ProgressBar style. This is a small circular progress bar. -->
+ <attr name="progressBarStyleSmallInverse" format="reference" />
+ <!-- Large inverse ProgressBar style. This is a large circular progress bar. -->
+ <attr name="progressBarStyleLargeInverse" format="reference" />
<!-- Default SeekBar style. -->
<attr name="seekBarStyle" format="reference" />
<!-- Default RatingBar style. -->
@@ -2873,6 +2879,11 @@
results for "bo", it would not be queried again for "bob".
The default value is <code>false</code>. <i>Optional attribute.</i>. -->
<attr name="queryAfterZeroResults" format="boolean" />
+
+ <!-- If provided, this string will be used to describe the searchable item in the
+ searchable items settings within system search settings. <i>Optional
+ attribute.</i> -->
+ <attr name="searchSettingsDescription" format="string" />
</declare-styleable>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 14d8dbe..148935f 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1120,11 +1120,18 @@
<public type="attr" name="smallScreens" />
<public type="attr" name="normalScreens" />
<public type="attr" name="largeScreens" />
+ <public type="attr" name="progressBarStyleInverse" />
+ <public type="attr" name="progressBarStyleSmallInverse" />
+ <public type="attr" name="progressBarStyleLargeInverse" />
+ <public type="attr" name="searchSettingsDescription" />
<public-padding type="attr" name="donut_resource_pad" end="0x0101029f" />
<public-padding type="id" name="donut_resource_pad" end="0x01020040" />
+ <public type="style" name="Widget.ProgressBar.Inverse" id="0x0103005b" />
+ <public type="style" name="Widget.ProgressBar.Large.Inverse" id="0x0103005c" />
+ <public type="style" name="Widget.ProgressBar.Small.Inverse" id="0x0103005d" />
<public-padding type="style" name="donut_resource_pad" end="0x01030070" />
<public-padding type="string" name="donut_resource_pad" end="0x01040030" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 9f1ed40..1ce9c76 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1786,6 +1786,11 @@
<!-- See EXTMEDIA_FORMAT. This is the button text to format the sd card. -->
<string name="extmedia_format_button_format">Format</string>
+ <!-- Title of notification shown when ADB is actively connected to the phone. -->
+ <string name="adb_active_notification_title">USB debugging connected</string>
+ <!-- Message of notification shown when ADB is actively connected to the phone. -->
+ <string name="adb_active_notification_message">A computer is connected to your phone.</string>
+
<!-- Used to replace %s in urls retreived from the signin server with locales. For Some -->
<!-- devices we don't support all the locales we ship to and need to replace the '%s' with a -->
<!-- locale string based on mcc values. By default (0-length string) we don't replace the %s -->
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 648a7dd..7d235ec 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -243,7 +243,7 @@
<style name="Widget.ProgressBar">
<item name="android:indeterminateOnly">true</item>
- <item name="android:indeterminateDrawable">@android:drawable/progress_medium</item>
+ <item name="android:indeterminateDrawable">@android:drawable/progress_medium_white</item>
<item name="android:indeterminateBehavior">repeat</item>
<item name="android:indeterminateDuration">3500</item>
<item name="android:minWidth">48dip</item>
@@ -253,7 +253,7 @@
</style>
<style name="Widget.ProgressBar.Large">
- <item name="android:indeterminateDrawable">@android:drawable/progress_large</item>
+ <item name="android:indeterminateDrawable">@android:drawable/progress_large_white</item>
<item name="android:minWidth">76dip</item>
<item name="android:maxWidth">76dip</item>
<item name="android:minHeight">76dip</item>
@@ -261,13 +261,25 @@
</style>
<style name="Widget.ProgressBar.Small">
- <item name="android:indeterminateDrawable">@android:drawable/progress_small</item>
+ <item name="android:indeterminateDrawable">@android:drawable/progress_small_white</item>
<item name="android:minWidth">16dip</item>
<item name="android:maxWidth">16dip</item>
<item name="android:minHeight">16dip</item>
<item name="android:maxHeight">16dip</item>
</style>
+ <style name="Widget.ProgressBar.Inverse">
+ <item name="android:indeterminateDrawable">@android:drawable/progress_medium</item>
+ </style>
+
+ <style name="Widget.ProgressBar.Large.Inverse">
+ <item name="android:indeterminateDrawable">@android:drawable/progress_large</item>
+ </style>
+
+ <style name="Widget.ProgressBar.Small.Inverse">
+ <item name="android:indeterminateDrawable">@android:drawable/progress_small</item>
+ </style>
+
<style name="Widget.ProgressBar.Small.Title">
<item name="android:indeterminateDrawable">@android:drawable/progress_small_titlebar</item>
</style>
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index f37d514..bd6e1df 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -154,6 +154,9 @@
<item name="progressBarStyleSmall">@android:style/Widget.ProgressBar.Small</item>
<item name="progressBarStyleSmallTitle">@android:style/Widget.ProgressBar.Small.Title</item>
<item name="progressBarStyleLarge">@android:style/Widget.ProgressBar.Large</item>
+ <item name="progressBarStyleInverse">@android:style/Widget.ProgressBar.Inverse</item>
+ <item name="progressBarStyleSmallInverse">@android:style/Widget.ProgressBar.Small.Inverse</item>
+ <item name="progressBarStyleLargeInverse">@android:style/Widget.ProgressBar.Large.Inverse</item>
<item name="seekBarStyle">@android:style/Widget.SeekBar</item>
<item name="ratingBarStyle">@android:style/Widget.RatingBar</item>
<item name="ratingBarStyleIndicator">@android:style/Widget.RatingBar.Indicator</item>
@@ -233,6 +236,13 @@
<item name="listViewStyle">@android:style/Widget.ListView.White</item>
<item name="listDivider">@drawable/divider_horizontal_bright</item>
<item name="listSeparatorTextViewStyle">@android:style/Widget.TextView.ListSeparator.White</item>
+
+ <item name="progressBarStyle">@android:style/Widget.ProgressBar.Inverse</item>
+ <item name="progressBarStyleSmall">@android:style/Widget.ProgressBar.Small.Inverse</item>
+ <item name="progressBarStyleLarge">@android:style/Widget.ProgressBar.Large.Inverse</item>
+ <item name="progressBarStyleInverse">@android:style/Widget.ProgressBar</item>
+ <item name="progressBarStyleSmallInverse">@android:style/Widget.ProgressBar.Small</item>
+ <item name="progressBarStyleLargeInverse">@android:style/Widget.ProgressBar.Large</item>
</style>
<!-- Variant of the light theme with no title bar -->
diff --git a/graphics/java/android/graphics/NinePatch.java b/graphics/java/android/graphics/NinePatch.java
index 2b24ef2..778c903 100644
--- a/graphics/java/android/graphics/NinePatch.java
+++ b/graphics/java/android/graphics/NinePatch.java
@@ -57,7 +57,9 @@
mBitmap = patch.mBitmap;
mChunk = patch.mChunk;
mSrcName = patch.mSrcName;
- mPaint = new Paint(patch.mPaint);
+ if (patch.mPaint != null) {
+ mPaint = new Paint(patch.mPaint);
+ }
validateNinePatchChunk(mBitmap.ni(), mChunk);
}
@@ -120,7 +122,6 @@
public native static boolean isNinePatchChunk(byte[] chunk);
- private final Rect mRect = new Rect();
private final Bitmap mBitmap;
private final byte[] mChunk;
private Paint mPaint;
diff --git a/graphics/java/android/graphics/drawable/Animatable.java b/graphics/java/android/graphics/drawable/Animatable.java
new file mode 100644
index 0000000..9dc62c3
--- /dev/null
+++ b/graphics/java/android/graphics/drawable/Animatable.java
@@ -0,0 +1,39 @@
+/*
+ * 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;
+
+/**
+ * Interface that drawables suporting animations should implement.
+ */
+public interface Animatable {
+ /**
+ * Starts the drawable's animation.
+ */
+ void start();
+
+ /**
+ * Stops the drawable's animation.
+ */
+ void stop();
+
+ /**
+ * Indicates whether the animation is running.
+ *
+ * @return True if the animation is running, false otherwise.
+ */
+ boolean isRunning();
+}
diff --git a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
index 08d295d..ac96f20 100644
--- a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
@@ -35,11 +35,14 @@
/**
* @hide
*/
-public class AnimatedRotateDrawable extends Drawable implements Drawable.Callback, Runnable {
+public class AnimatedRotateDrawable extends Drawable implements Drawable.Callback, Runnable,
+ Animatable {
+
private AnimatedRotateState mState;
private boolean mMutated;
private float mCurrentDegrees;
private float mIncrement;
+ private boolean mRunning;
public AnimatedRotateDrawable() {
this(null);
@@ -80,10 +83,24 @@
drawable.draw(canvas);
canvas.restoreToCount(saveCount);
-
- nextFrame();
}
-
+
+ public void start() {
+ if (!mRunning) {
+ mRunning = true;
+ nextFrame();
+ }
+ }
+
+ public void stop() {
+ mRunning = false;
+ unscheduleSelf(this);
+ }
+
+ public boolean isRunning() {
+ return mRunning;
+ }
+
private void nextFrame() {
unscheduleSelf(this);
scheduleSelf(this, SystemClock.uptimeMillis() + mState.mFrameDuration);
@@ -96,8 +113,8 @@
if (mCurrentDegrees > (360.0f - mIncrement)) {
mCurrentDegrees = 0.0f;
}
- nextFrame();
invalidateSelf();
+ nextFrame();
}
@Override
diff --git a/graphics/java/android/graphics/drawable/AnimationDrawable.java b/graphics/java/android/graphics/drawable/AnimationDrawable.java
index bab1703..68718c9 100644
--- a/graphics/java/android/graphics/drawable/AnimationDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimationDrawable.java
@@ -71,7 +71,7 @@
* @attr ref android.R.styleable#AnimationDrawableItem_duration
* @attr ref android.R.styleable#AnimationDrawableItem_drawable
*/
-public class AnimationDrawable extends DrawableContainer implements Runnable {
+public class AnimationDrawable extends DrawableContainer implements Runnable, Animatable {
private final AnimationState mAnimationState;
private int mCurFrame = -1;
private boolean mMutated;
diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h
index 5c41ead..eb4151a 100644
--- a/include/utils/ResourceTypes.h
+++ b/include/utils/ResourceTypes.h
@@ -1441,7 +1441,7 @@
* This is the beginning of information about an entry in the resource
* table. It holds the reference to the name of this entry, and is
* immediately followed by one of:
- * * A Res_value structures, if FLAG_COMPLEX is -not- set.
+ * * A Res_value structure, if FLAG_COMPLEX is -not- set.
* * An array of ResTable_map structures, if FLAG_COMPLEX is set.
* These supply a set of name/value mappings of data.
*/
@@ -1843,6 +1843,8 @@
status_t parsePackage(
const ResTable_package* const pkg, const Header* const header);
+ void print_value(const Package* pkg, const Res_value& value) const;
+
mutable Mutex mLock;
status_t mError;
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/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp
index 7a33220..4a5063a 100644
--- a/libs/utils/ResourceTypes.cpp
+++ b/libs/utils/ResourceTypes.cpp
@@ -3845,7 +3845,7 @@
& Res_value::COMPLEX_RADIX_MASK];
printf("%f", value);
- if (isFraction) {
+ if (!isFraction) {
switch ((complex>>Res_value::COMPLEX_UNIT_SHIFT)&Res_value::COMPLEX_UNIT_MASK) {
case Res_value::COMPLEX_UNIT_PX: printf("px"); break;
case Res_value::COMPLEX_UNIT_DIP: printf("dp"); break;
@@ -3864,6 +3864,49 @@
}
}
+void ResTable::print_value(const Package* pkg, const Res_value& value) const
+{
+ if (value.dataType == Res_value::TYPE_NULL) {
+ printf("(null)\n");
+ } else if (value.dataType == Res_value::TYPE_REFERENCE) {
+ printf("(reference) 0x%08x\n", value.data);
+ } else if (value.dataType == Res_value::TYPE_ATTRIBUTE) {
+ printf("(attribute) 0x%08x\n", value.data);
+ } else if (value.dataType == Res_value::TYPE_STRING) {
+ size_t len;
+ const char16_t* str = pkg->header->values.stringAt(
+ value.data, &len);
+ if (str == NULL) {
+ printf("(string) null\n");
+ } else {
+ printf("(string) \"%s\"\n",
+ String8(str, len).string());
+ }
+ } else if (value.dataType == Res_value::TYPE_FLOAT) {
+ printf("(float) %g\n", *(const float*)&value.data);
+ } else if (value.dataType == Res_value::TYPE_DIMENSION) {
+ printf("(dimension) ");
+ print_complex(value.data, false);
+ printf("\n");
+ } else if (value.dataType == Res_value::TYPE_FRACTION) {
+ printf("(fraction) ");
+ print_complex(value.data, true);
+ printf("\n");
+ } else if (value.dataType >= Res_value::TYPE_FIRST_COLOR_INT
+ || value.dataType <= Res_value::TYPE_LAST_COLOR_INT) {
+ printf("(color) #%08x\n", value.data);
+ } else if (value.dataType == Res_value::TYPE_INT_BOOLEAN) {
+ printf("(boolean) %s\n", value.data ? "true" : "false");
+ } else if (value.dataType >= Res_value::TYPE_FIRST_INT
+ || value.dataType <= Res_value::TYPE_LAST_INT) {
+ printf("(int) 0x%08x or %d\n", value.data, value.data);
+ } else {
+ printf("(unknown type) t=0x%02x d=0x%08x (s=0x%04x r=0x%02x)\n",
+ (int)value.dataType, (int)value.data,
+ (int)value.size, (int)value.res0);
+ }
+}
+
void ResTable::print(bool inclValues) const
{
if (mError != 0) {
@@ -3985,27 +4028,31 @@
continue;
}
- const Res_value* value = NULL;
+ uint16_t esize = dtohs(ent->size);
+ if ((esize&0x3) != 0) {
+ printf("NON-INTEGER ResTable_entry SIZE: %p\n", (void*)esize);
+ continue;
+ }
+ if ((thisOffset+esize) > typeSize) {
+ printf("ResTable_entry OUT OF BOUNDS: %p+%p+%p (size is %p)\n",
+ (void*)entriesStart, (void*)thisOffset,
+ (void*)esize, (void*)typeSize);
+ continue;
+ }
+
+ const Res_value* valuePtr = NULL;
+ const ResTable_map_entry* bagPtr = NULL;
+ Res_value value;
if ((dtohs(ent->flags)&ResTable_entry::FLAG_COMPLEX) != 0) {
printf("<bag>");
+ bagPtr = (const ResTable_map_entry*)ent;
} else {
- uint16_t esize = dtohs(ent->size);
- if ((esize&0x3) != 0) {
- printf("NON-INTEGER ResTable_entry SIZE: %p\n", (void*)esize);
- continue;
- }
- if ((thisOffset+esize) > typeSize) {
- printf("ResTable_entry OUT OF BOUNDS: %p+%p+%p (size is %p)\n",
- (void*)entriesStart, (void*)thisOffset,
- (void*)esize, (void*)typeSize);
- continue;
- }
-
- value = (const Res_value*)
+ valuePtr = (const Res_value*)
(((const uint8_t*)ent) + esize);
+ value.copyFrom_dtoh(*valuePtr);
printf("t=0x%02x d=0x%08x (s=0x%04x r=0x%02x)",
- (int)value->dataType, (int)dtohl(value->data),
- (int)dtohs(value->size), (int)value->res0);
+ (int)value.dataType, (int)value.data,
+ (int)value.size, (int)value.res0);
}
if ((dtohs(ent->flags)&ResTable_entry::FLAG_PUBLIC) != 0) {
@@ -4014,44 +4061,23 @@
printf("\n");
if (inclValues) {
- if (value != NULL) {
+ if (valuePtr != NULL) {
printf(" ");
- if (value->dataType == Res_value::TYPE_NULL) {
- printf("(null)\n");
- } else if (value->dataType == Res_value::TYPE_REFERENCE) {
- printf("(reference) 0x%08x\n", value->data);
- } else if (value->dataType == Res_value::TYPE_ATTRIBUTE) {
- printf("(attribute) 0x%08x\n", value->data);
- } else if (value->dataType == Res_value::TYPE_STRING) {
- size_t len;
- const char16_t* str = pkg->header->values.stringAt(
- value->data, &len);
- if (str == NULL) {
- printf("(string) null\n");
- } else {
- printf("(string) \"%s\"\n",
- String8(str, len).string());
- }
- } else if (value->dataType == Res_value::TYPE_FLOAT) {
- printf("(float) %g\n", *(const float*)&value->data);
- } else if (value->dataType == Res_value::TYPE_DIMENSION) {
- printf("(dimension) ");
- print_complex(value->data, false);
- printf("\n");
- } else if (value->dataType == Res_value::TYPE_FRACTION) {
- printf("(fraction) ");
- print_complex(value->data, true);
- printf("\n");
- } else if (value->dataType >= Res_value::TYPE_FIRST_COLOR_INT
- || value->dataType <= Res_value::TYPE_LAST_COLOR_INT) {
- printf("(color) #%08x\n", value->data);
- } else if (value->dataType == Res_value::TYPE_INT_BOOLEAN) {
- printf("(boolean) %s\n", value->data ? "true" : "false");
- } else if (value->dataType >= Res_value::TYPE_FIRST_INT
- || value->dataType <= Res_value::TYPE_LAST_INT) {
- printf("(int) 0x%08x or %d\n", value->data, value->data);
- } else {
- printf("(unknown type)\n");
+ print_value(pkg, value);
+ } else if (bagPtr != NULL) {
+ const int N = dtohl(bagPtr->count);
+ const ResTable_map* mapPtr = (const ResTable_map*)
+ (((const uint8_t*)ent) + esize);
+ printf(" Parent=0x%08x, Count=%d\n",
+ dtohl(bagPtr->parent.ident), N);
+ for (int i=0; i<N; i++) {
+ printf(" #%i (Key=0x%08x): ",
+ i, dtohl(mapPtr->name.ident));
+ value.copyFrom_dtoh(mapPtr->value);
+ print_value(pkg, value);
+ const size_t size = dtohs(mapPtr->value.size);
+ mapPtr = (ResTable_map*)(((const uint8_t*)mapPtr)
+ + size + sizeof(*mapPtr)-sizeof(mapPtr->value));
}
}
}
diff --git a/location/java/com/android/internal/location/GpsLocationProvider.java b/location/java/com/android/internal/location/GpsLocationProvider.java
index edd1ea0..4a51e31 100755
--- a/location/java/com/android/internal/location/GpsLocationProvider.java
+++ b/location/java/com/android/internal/location/GpsLocationProvider.java
@@ -617,6 +617,9 @@
synchronized(mListeners) {
mListeners.remove(this);
}
+ if (mListener != null) {
+ mListener.asBinder().unlinkToDeath(this, 0);
+ }
}
}
diff --git a/location/java/com/android/internal/location/LocationProviderProxy.java b/location/java/com/android/internal/location/LocationProviderProxy.java
index bd7088c..4ae424a 100644
--- a/location/java/com/android/internal/location/LocationProviderProxy.java
+++ b/location/java/com/android/internal/location/LocationProviderProxy.java
@@ -53,6 +53,12 @@
}
}
+ public void unlinkProvider() {
+ if (mProvider != null) {
+ mProvider.asBinder().unlinkToDeath(this, 0);
+ }
+ }
+
public String getName() {
return mName;
}
@@ -255,5 +261,6 @@
public void binderDied() {
Log.w(TAG, "Location Provider " + mName + " died");
mDead = true;
+ mProvider.asBinder().unlinkToDeath(this, 0);
}
}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java
index fd3a4ba..e76967d 100755
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java
@@ -501,4 +501,6 @@
"http://sridharg.googlejunta.com/yslau/stress_media/mp3_regular.mp3";
public static final String STREAM_MPEG4_QVGA_128k =
"http://sridharg.googlejunta.com/yslau/stress_media/mpeg4_qvga_24fps.3gp";
+ public static final int STREAM_H264_480_360_1411k_DURATION = 46000;
+ public static final int VIDEO_H263_AAC_DURATION = 501000;
}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaRecorderStressTestRunner.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaRecorderStressTestRunner.java
index 12eacd3..ae9e102 100755
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaRecorderStressTestRunner.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaRecorderStressTestRunner.java
@@ -19,6 +19,7 @@
import android.test.InstrumentationTestRunner;
import android.test.InstrumentationTestSuite;
import com.android.mediaframeworktest.stress.MediaRecorderStressTest;
+import com.android.mediaframeworktest.stress.MediaPlayerStressTest;
import junit.framework.TestSuite;
@@ -28,6 +29,7 @@
public TestSuite getAllTests() {
TestSuite suite = new InstrumentationTestSuite(this);
suite.addTestSuite(MediaRecorderStressTest.class);
+ suite.addTestSuite(MediaPlayerStressTest.class);
return suite;
}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaPlayerStressTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaPlayerStressTest.java
new file mode 100644
index 0000000..5e213d7
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaPlayerStressTest.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mediaframeworktest.stress;
+
+import com.android.mediaframeworktest.MediaFrameworkTest;
+
+import android.hardware.Camera;
+import android.media.MediaPlayer;
+import android.media.MediaRecorder;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.util.Log;
+import android.view.SurfaceHolder;
+
+import com.android.mediaframeworktest.MediaNames;
+
+import java.util.Random;
+
+/**
+ * Junit / Instrumentation test case for the media player
+ */
+public class MediaPlayerStressTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> {
+ private String TAG = "MediaPlayerStressTest";
+ private MediaRecorder mRecorder;
+ private Camera mCamera;
+
+ private static final int NUMBER_OF_RANDOM_REPOSITION_AND_PLAY = 10;
+ private static final int NUMBER_OF_RANDOM_REPOSITION_AND_PLAY_SHORT = 5;
+ private static final int NUMBER_OF_STRESS_LOOPS = 1000;
+ private static final int PLAYBACK_END_TOLERANCE = 5000;
+ private static final int WAIT_UNTIL_PLAYBACK_FINISH = 515000 ;
+
+ public MediaPlayerStressTest() {
+ super("com.android.mediaframeworktest", MediaFrameworkTest.class);
+ }
+
+ protected void setUp() throws Exception {
+ getActivity();
+ super.setUp();
+ }
+
+ @LargeTest
+ public void testStressHWDecoderRelease() throws Exception {
+ SurfaceHolder mSurfaceHolder;
+ long randomseed = System.currentTimeMillis();
+ Random generator = new Random(randomseed);
+ Log.v(TAG, "Random seed: " + randomseed);
+ int video_duration = MediaNames.STREAM_H264_480_360_1411k_DURATION;
+ int random_play_time;
+
+ mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder();
+ try {
+ for (int i = 0; i < NUMBER_OF_STRESS_LOOPS; i++) {
+ MediaPlayer mp = new MediaPlayer();
+ mp.setDataSource(MediaNames.STREAM_H264_480_360_1411k);
+ mp.setDisplay(MediaFrameworkTest.mSurfaceView.getHolder());
+ mp.prepare();
+ mp.start();
+ // seek and play
+ for (int j = 0; j < generator.nextInt(10); j++) {
+ random_play_time =
+ generator.nextInt(MediaNames.STREAM_H264_480_360_1411k_DURATION / 2);
+ Log.v(TAG, "Play time = " + random_play_time);
+ Thread.sleep(random_play_time);
+ int seek_time = MediaNames.STREAM_H264_480_360_1411k_DURATION / 2;
+ Log.v(TAG, "Seek time = " + seek_time);
+ mp.seekTo(seek_time);
+ }
+ mp.release();
+ }
+
+ } catch (Exception e) {
+ Log.v(TAG, e.toString());
+ }
+ }
+
+ @LargeTest
+ public void testStressGetCurrentPosition() throws Exception {
+ SurfaceHolder mSurfaceHolder;
+ long randomseed = System.currentTimeMillis();
+ Random generator = new Random(randomseed);
+ Log.v(TAG, "Random seed: " + randomseed);
+ int video_duration = MediaNames.VIDEO_H263_AAC_DURATION;
+ int random_play_time = 0;
+ int random_seek_time = 0;
+
+ mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder();
+ try {
+ for (int i = 0; i < NUMBER_OF_STRESS_LOOPS; i++) {
+ MediaPlayer mp = new MediaPlayer();
+ mp.setDataSource(MediaNames.VIDEO_H263_AMR);
+ mp.setDisplay(MediaFrameworkTest.mSurfaceView.getHolder());
+ mp.prepare();
+ mp.start();
+ // Random seek and play
+ for (int j = 0; j < generator.nextInt(10); j++) {
+ random_play_time =
+ generator.nextInt(video_duration / 2);
+ Log.v(TAG, "Play time = " + random_play_time);
+ Thread.sleep(random_play_time);
+ random_seek_time =
+ generator.nextInt(video_duration / 2);
+ Log.v(TAG, "Seek time = " + random_seek_time);
+ mp.seekTo(random_seek_time);
+ }
+ //wait until the movie finish and check the current position
+ //Make sure the wait time is long enough
+ long wait_until_playback_finish = video_duration - random_seek_time + PLAYBACK_END_TOLERANCE * 2;
+ Thread.sleep(wait_until_playback_finish);
+ Log.v(TAG, "CurrentPosition = " + mp.getCurrentPosition());
+ if ( mp.isPlaying() || mp.getCurrentPosition() > (video_duration + PLAYBACK_END_TOLERANCE)){
+ assertTrue("Current PlayTime greater than duration", false);
+ }
+ mp.release();
+ }
+
+ } catch (Exception e) {
+ Log.v(TAG, e.toString());
+ }
+ }
+}
+
diff --git a/packages/TtsService/AndroidManifest.xml b/packages/TtsService/AndroidManifest.xml
index 1dc25c6..fab2534 100755
--- a/packages/TtsService/AndroidManifest.xml
+++ b/packages/TtsService/AndroidManifest.xml
@@ -12,4 +12,5 @@
</service>
</application>
<uses-permission android:name="android.permission.INTERNET"/>
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
</manifest>
diff --git a/packages/TtsService/jni/android_tts_SynthProxy.cpp b/packages/TtsService/jni/android_tts_SynthProxy.cpp
index 69ba062..a55b704 100644
--- a/packages/TtsService/jni/android_tts_SynthProxy.cpp
+++ b/packages/TtsService/jni/android_tts_SynthProxy.cpp
@@ -199,6 +199,7 @@
if (wav == NULL) {
delete pForAfter;
LOGV("Null: speech has completed");
+ return TTS_CALLBACK_HALT;
}
if (bufferSize > 0){
fwrite(wav, 1, bufferSize, pForAfter->outputFile);
@@ -213,8 +214,12 @@
// this struct was allocated in the original android_tts_SynthProxy_speak call,
// all processing matching this call is now done.
LOGV("Speech synthesis done.");
- delete pForAfter;
- pForAfter = NULL;
+ if (pForAfter->usageMode == USAGEMODE_PLAY_IMMEDIATELY) {
+ // only delete for direct playback. When writing to a file, we still have work to do
+ // in android_tts_SynthProxy_synthesizeToFile. The struct will be deleted there.
+ delete pForAfter;
+ pForAfter = NULL;
+ }
return TTS_CALLBACK_HALT;
}
@@ -397,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)
@@ -408,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);
@@ -419,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];
@@ -427,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);
@@ -452,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';
@@ -473,6 +498,9 @@
fflush(pForAfter->outputFile);
fclose(pForAfter->outputFile);
+ delete pForAfter;
+ pForAfter = NULL;
+
env->ReleaseStringUTFChars(textJavaString, textNativeString);
env->ReleaseStringUTFChars(filenameJavaString, filenameNativeString);
}
@@ -500,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);
}
}
diff --git a/packages/TtsService/src/android/tts/TtsService.java b/packages/TtsService/src/android/tts/TtsService.java
index b1e6425..b5f5b37 100755
--- a/packages/TtsService/src/android/tts/TtsService.java
+++ b/packages/TtsService/src/android/tts/TtsService.java
@@ -51,10 +51,13 @@
public static final int IPA = 1;
public static final int EARCON = 2;
public static final int SILENCE = 3;
+ public static final int TEXT_TO_FILE = 5;
+ public static final int IPA_TO_FILE = 6;
public String mText = null;
public ArrayList<String> mParams = null;
public int mType = TEXT;
public long mDuration = 0;
+ public String mFilename = null;
public SpeechItem(String text, ArrayList<String> params, int itemType) {
mText = text;
@@ -65,6 +68,14 @@
public SpeechItem(long silenceTime) {
mDuration = silenceTime;
}
+
+ public SpeechItem(String text, ArrayList<String> params, int itemType, String filename) {
+ mText = text;
+ mParams = params;
+ mType = itemType;
+ mFilename = filename;
+ }
+
}
/**
@@ -464,6 +475,60 @@
synth.start();
}
+ private void synthToFileInternalOnly(final String text,
+ final ArrayList<String> params, final String filename) {
+ class SynthThread implements Runnable {
+ public void run() {
+ Log.i("TTS", "Synthesizing to " + filename);
+ boolean synthAvailable = false;
+ try {
+ synthAvailable = synthesizerLock.tryLock();
+ if (!synthAvailable) {
+ Thread.sleep(100);
+ Thread synth = (new Thread(new SynthThread()));
+ synth.setPriority(Thread.MIN_PRIORITY);
+ 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.synthesizeToFile(text, filename);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ } finally {
+ // This check is needed because finally will always run;
+ // even if the
+ // method returns somewhere in the try block.
+ if (synthAvailable) {
+ synthesizerLock.unlock();
+ }
+ processSpeechQueue();
+ }
+ }
+ }
+ Thread synth = (new Thread(new SynthThread()));
+ synth.setPriority(Thread.MIN_PRIORITY);
+ synth.start();
+ }
+
private SoundResource getSoundResource(SpeechItem speechItem) {
SoundResource sr = null;
String text = speechItem.mText;
@@ -549,6 +614,9 @@
currentSpeechItem = splitCurrentTextIfNeeded(currentSpeechItem);
speakInternalOnly(currentSpeechItem.mText,
currentSpeechItem.mParams);
+ } else if (currentSpeechItem.mType == SpeechItem.TEXT_TO_FILE) {
+ synthToFileInternalOnly(currentSpeechItem.mText,
+ currentSpeechItem.mParams, currentSpeechItem.mFilename);
} else if (currentSpeechItem.mType == SpeechItem.IPA) {
// TODO Implement IPA support
} else {
@@ -629,34 +697,20 @@
* @return A boolean that indicates if the synthesis succeeded
*/
private boolean synthesizeToFile(String text, 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();
+ String filename) {
+ // Don't allow a filename that is too long
+ if (filename.length() > MAX_FILENAME_LENGTH) {
+ return false;
}
- Log.i("TTS", "Synthesizing 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;
- }
- nativeSynth.synthesizeToFile(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();
- }
+ // Don't allow anything longer than the max text length; since this
+ // is synthing to a file, don't even bother splitting it.
+ if (text.length() >= MAX_SPEECH_ITEM_CHAR_LENGTH){
+ return false;
}
- Log.i("TTS", "Completed synthesis for " + filename);
+ mSpeechQueue.add(new SpeechItem(text, params, SpeechItem.TEXT_TO_FILE, filename));
+ if (!mIsSpeaking) {
+ processSpeechQueue();
+ }
return true;
}
@@ -957,7 +1011,7 @@
if (params != null) {
speakingParams = new ArrayList<String>(Arrays.asList(params));
}
- return mSelf.synthesizeToFile(text, speakingParams, filename, true);
+ return mSelf.synthesizeToFile(text, speakingParams, filename);
}
/**
diff --git a/packages/VpnServices/src/com/android/server/vpn/AndroidServiceProxy.java b/packages/VpnServices/src/com/android/server/vpn/AndroidServiceProxy.java
index 2ad218f..7dd9d9e 100644
--- a/packages/VpnServices/src/com/android/server/vpn/AndroidServiceProxy.java
+++ b/packages/VpnServices/src/com/android/server/vpn/AndroidServiceProxy.java
@@ -78,20 +78,10 @@
/**
* Sends a command with arguments to the service through the control socket.
- * Each argument is sent as a C-style zero-terminated string.
*/
public void sendCommand(String ...args) throws IOException {
OutputStream out = getControlSocketOutput();
for (String arg : args) outputString(out, arg);
- checkSocketResult();
- }
-
- /**
- * Sends a command with arguments to the service through the control socket.
- */
- public void sendCommand2(String ...args) throws IOException {
- OutputStream out = getControlSocketOutput();
- for (String arg : args) outputString2(out, arg);
out.write(END_OF_ARGUMENTS);
out.flush();
checkSocketResult();
@@ -128,8 +118,9 @@
if (data == 0) {
// re-establish the connection:
- // synchronized here so that checkSocketResult() returns
- // when new mKeepaliveSocket is available for next cmd
+ // synchronized here so that checkSocketResult()
+ // returns when new mKeepaliveSocket is available for
+ // next cmd
synchronized (this) {
setResultAndCloseControlSocket((byte) data);
s = mKeepaliveSocket = createServiceSocket();
@@ -244,12 +235,6 @@
}
private void outputString(OutputStream out, String s) throws IOException {
- out.write(s.getBytes());
- out.write(0);
- out.flush();
- }
-
- private void outputString2(OutputStream out, String s) throws IOException {
byte[] bytes = s.getBytes();
out.write(bytes.length);
out.write(bytes);
diff --git a/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecPskService.java b/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecPskService.java
new file mode 100644
index 0000000..6abf81c
--- /dev/null
+++ b/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecPskService.java
@@ -0,0 +1,49 @@
+/*
+ * 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.L2tpIpsecPskProfile;
+
+import java.io.IOException;
+
+/**
+ * The service that manages the preshared key based L2TP-over-IPSec VPN
+ * connection.
+ */
+class L2tpIpsecPskService extends VpnService<L2tpIpsecPskProfile> {
+ private static final String IPSEC_DAEMON = "racoon";
+
+ @Override
+ protected void connect(String serverIp, String username, String password)
+ throws IOException {
+ String hostIp = getHostIp();
+ L2tpIpsecPskProfile p = getProfile();
+
+ // IPSEC
+ AndroidServiceProxy ipsecService = startService(IPSEC_DAEMON);
+ ipsecService.sendCommand(hostIp, serverIp, L2tpService.L2TP_PORT,
+ p.getPresharedKey());
+
+ sleep(2000); // 2 seconds
+
+ // L2TP
+ MtpdHelper.sendCommand(this, L2tpService.L2TP_DAEMON, serverIp,
+ L2tpService.L2TP_PORT,
+ (p.isSecretEnabled() ? p.getSecretString() : null),
+ username, password);
+ }
+}
diff --git a/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecService.java b/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecService.java
index 877fa6b..bd14110 100644
--- a/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecService.java
+++ b/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecService.java
@@ -22,7 +22,7 @@
import java.io.IOException;
/**
- * The service that manages the L2TP-over-IPSec VPN connection.
+ * The service that manages the certificate based L2TP-over-IPSec VPN connection.
*/
class L2tpIpsecService extends VpnService<L2tpIpsecProfile> {
private static final String IPSEC_DAEMON = "racoon";
@@ -34,11 +34,10 @@
// IPSEC
AndroidServiceProxy ipsecService = startService(IPSEC_DAEMON);
- ipsecService.sendCommand(
- String.format("SETKEY %s %s", hostIp, serverIp));
- ipsecService.sendCommand(String.format("SET_CERTS %s %s %s %s",
- serverIp, getCaCertPath(), getUserCertPath(),
- getUserkeyPath()));
+ ipsecService.sendCommand(hostIp, serverIp, L2tpService.L2TP_PORT,
+ getUserkeyPath(), getUserCertPath(), getCaCertPath());
+
+ sleep(2000); // 2 seconds
// L2TP
L2tpIpsecProfile p = getProfile();
diff --git a/packages/VpnServices/src/com/android/server/vpn/MtpdHelper.java b/packages/VpnServices/src/com/android/server/vpn/MtpdHelper.java
index 6160900..16d253a 100644
--- a/packages/VpnServices/src/com/android/server/vpn/MtpdHelper.java
+++ b/packages/VpnServices/src/com/android/server/vpn/MtpdHelper.java
@@ -38,7 +38,7 @@
addPppArguments(vpnService, args, serverIp, username, password);
AndroidServiceProxy mtpd = vpnService.startService(MTPD_SERVICE);
- mtpd.sendCommand2(args.toArray(new String[args.size()]));
+ mtpd.sendCommand(args.toArray(new String[args.size()]));
}
private static void addPppArguments(VpnService<?> vpnService,
diff --git a/packages/VpnServices/src/com/android/server/vpn/VpnService.java b/packages/VpnServices/src/com/android/server/vpn/VpnService.java
index 44127ff..6e5d46b 100644
--- a/packages/VpnServices/src/com/android/server/vpn/VpnService.java
+++ b/packages/VpnServices/src/com/android/server/vpn/VpnService.java
@@ -24,14 +24,12 @@
import android.net.vpn.VpnManager;
import android.net.vpn.VpnProfile;
import android.net.vpn.VpnState;
-import android.os.FileObserver;
import android.os.SystemProperties;
+import android.text.TextUtils;
import android.util.Log;
-import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
-import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import java.net.Socket;
import java.util.ArrayList;
@@ -43,21 +41,18 @@
*/
abstract class VpnService<E extends VpnProfile> {
private static final int NOTIFICATION_ID = 1;
- private static final String PROFILES_ROOT = VpnManager.PROFILES_PATH + "/";
- public static final String DEFAULT_CONFIG_PATH = "/etc";
- private static final int DNS_TIMEOUT = 3000; // ms
private static final String DNS1 = "net.dns1";
private static final String DNS2 = "net.dns2";
+ private static final String VPN_DNS1 = "vpn.dns1";
+ private static final String VPN_DNS2 = "vpn.dns2";
+ private static final String VPN_UP = "vpn.up";
+ private static final String VPN_IS_UP = "1";
+ private static final String VPN_IS_DOWN = "0";
+
private static final String REMOTE_IP = "net.ipremote";
private static final String DNS_DOMAIN_SUFFICES = "net.dns.search";
- private static final String SERVER_IP = "net.vpn.server_ip";
- private static final int VPN_TIMEOUT = 30000; // milliseconds
- private static final int ONE_SECOND = 1000; // milliseconds
- private static final int FIVE_SECOND = 5000; // milliseconds
-
- private static final String LOGWRAPPER = "/system/bin/logwrapper";
private final String TAG = VpnService.class.getSimpleName();
E mProfile;
@@ -76,13 +71,6 @@
private long mStartTime; // VPN connection start time
- // monitors if the VPN connection is sucessfully established
- private FileMonitor mConnectMonitor;
-
- // watch dog timer; fired up if the connection cannot be established within
- // VPN_TIMEOUT
- private Object mWatchdog;
-
// for helping managing multiple Android services
private ServiceHelper mServiceHelper = new ServiceHelper();
@@ -110,19 +98,6 @@
return mServiceHelper.startService(serviceName);
}
- protected String getPppOptionFilePath() throws IOException {
- String subpath = getProfileSubpath("/ppp/peers");
- String[] kids = new File(subpath).list();
- if ((kids == null) || (kids.length == 0)) {
- throw new IOException("no option file found in " + subpath);
- }
- if (kids.length > 1) {
- Log.w(TAG, "more than one option file found in " + subpath
- + ", arbitrarily choose " + kids[0]);
- }
- return subpath + "/" + kids[0];
- }
-
/**
* Returns the VPN profile associated with the connection.
*/
@@ -131,23 +106,6 @@
}
/**
- * Returns the profile path where configuration files reside.
- */
- protected String getProfilePath() throws IOException {
- String path = PROFILES_ROOT + mProfile.getId();
- File dir = new File(path);
- if (!dir.exists()) throw new IOException("Profile dir does not exist");
- return path;
- }
-
- /**
- * Returns the path where default configuration files reside.
- */
- protected String getDefaultConfigPath() throws IOException {
- return DEFAULT_CONFIG_PATH;
- }
-
- /**
* Returns the host IP for establishing the VPN connection.
*/
protected String getHostIp() throws IOException {
@@ -178,14 +136,6 @@
}
/**
- * Returns the path of the script file that is executed when the VPN
- * connection is established.
- */
- protected String getConnectMonitorFile() {
- return "/etc/ppp/ip-up-vpn";
- }
-
- /**
* Sets the system property. The method is blocked until the value is
* settled in.
* @param name the name of the property
@@ -222,10 +172,10 @@
broadcastConnectivity(VpnState.CONNECTING);
String serverIp = getIp(getProfile().getServerName());
- setSystemProperty(SERVER_IP, serverIp);
- onBeforeConnect();
+ onBeforeConnect();
connect(serverIp, username, password);
+ waitUntilConnectedOrTimedout();
}
synchronized void onDisconnect(boolean cleanUpServices) {
@@ -259,39 +209,36 @@
}
}
- private void createConnectMonitor() {
- mConnectMonitor = new FileMonitor(getConnectMonitorFile(),
- new Runnable() {
- public void run() {
- onConnectMonitorTriggered();
- }
- });
- }
-
private void onBeforeConnect() {
mNotification.disableNotification();
- createConnectMonitor();
- mConnectMonitor.startWatching();
- saveOriginalDnsProperties();
-
- mWatchdog = startTimer(VPN_TIMEOUT, new Runnable() {
- public void run() {
- synchronized (VpnService.this) {
- if (mState == VpnState.CONNECTING) {
- Log.d(TAG, " watchdog timer is fired !!");
- onError();
- }
- }
- }
- });
+ SystemProperties.set(VPN_DNS1, "-");
+ SystemProperties.set(VPN_DNS2, "-");
+ SystemProperties.set(VPN_UP, VPN_IS_DOWN);
+ Log.d(TAG, " VPN UP: " + SystemProperties.get(VPN_UP));
}
- private synchronized void onConnectMonitorTriggered() {
- Log.d(TAG, "onConnectMonitorTriggered()");
+ private void waitUntilConnectedOrTimedout() {
+ sleep(2000); // 2 seconds
+ for (int i = 0; i < 60; i++) {
+ if (VPN_IS_UP.equals(SystemProperties.get(VPN_UP))) {
+ onConnected();
+ return;
+ }
+ sleep(500); // 0.5 second
+ }
- stopTimer(mWatchdog);
- mConnectMonitor.stopWatching();
+ synchronized (this) {
+ if (mState == VpnState.CONNECTING) {
+ Log.d(TAG, " connecting timed out !!");
+ onError();
+ }
+ }
+ }
+
+ private synchronized void onConnected() {
+ Log.d(TAG, "onConnected()");
+
saveVpnDnsProperties();
saveAndSetDomainSuffices();
startConnectivityMonitor();
@@ -310,8 +257,6 @@
restoreOriginalDnsProperties();
restoreOriginalDomainSuffices();
- if (mConnectMonitor != null) mConnectMonitor.stopWatching();
- if (mWatchdog != null) stopTimer(mWatchdog);
mState = VpnState.IDLE;
broadcastConnectivity(VpnState.IDLE);
@@ -345,13 +290,6 @@
}
}
- private void saveOriginalDnsProperties() {
- mOriginalDns1 = SystemProperties.get(DNS1);
- mOriginalDns2 = SystemProperties.get(DNS2);
- Log.d(TAG, String.format("save original dns prop: %s, %s",
- mOriginalDns1, mOriginalDns2));
- }
-
private void restoreOriginalDnsProperties() {
// restore only if they are not overridden
if (mVpnDns1.equals(SystemProperties.get(DNS1))) {
@@ -365,15 +303,21 @@
}
private void saveVpnDnsProperties() {
- mVpnDns1 = mVpnDns2 = "";
+ mOriginalDns1 = mOriginalDns2 = "";
for (int i = 0; i < 10; i++) {
- mVpnDns1 = SystemProperties.get(DNS1);
- mVpnDns2 = SystemProperties.get(DNS2);
- if (mVpnDns1.equals(mOriginalDns1)) {
+ mVpnDns1 = SystemProperties.get(VPN_DNS1);
+ mVpnDns2 = SystemProperties.get(VPN_DNS2);
+ if (mOriginalDns1.equals(mVpnDns1)) {
Log.d(TAG, "wait for vpn dns to settle in..." + i);
sleep(500);
} else {
- Log.d(TAG, String.format("save vpn dns prop: %s, %s",
+ mOriginalDns1 = SystemProperties.get(DNS1);
+ mOriginalDns2 = SystemProperties.get(DNS2);
+ SystemProperties.set(DNS1, mVpnDns1);
+ SystemProperties.set(DNS2, mVpnDns2);
+ Log.d(TAG, String.format("save original dns prop: %s, %s",
+ mOriginalDns1, mOriginalDns2));
+ Log.d(TAG, String.format("set vpn dns prop: %s, %s",
mVpnDns1, mVpnDns2));
return;
}
@@ -381,23 +325,11 @@
Log.e(TAG, "saveVpnDnsProperties(): DNS not updated??");
}
- private void restoreVpnDnsProperties() {
- if (isNullOrEmpty(mVpnDns1) && isNullOrEmpty(mVpnDns2)) {
- return;
- }
- Log.d(TAG, String.format("restore vpn dns prop: %s --> %s",
- SystemProperties.get(DNS1), mVpnDns1));
- Log.d(TAG, String.format("restore vpn dns prop: %s --> %s",
- SystemProperties.get(DNS2), mVpnDns2));
- SystemProperties.set(DNS1, mVpnDns1);
- SystemProperties.set(DNS2, mVpnDns2);
- }
-
private void saveAndSetDomainSuffices() {
mOriginalDomainSuffices = SystemProperties.get(DNS_DOMAIN_SUFFICES);
Log.d(TAG, "save original dns search: " + mOriginalDomainSuffices);
String list = mProfile.getDomainSuffices();
- if (!isNullOrEmpty(list)) {
+ if (!TextUtils.isEmpty(list)) {
SystemProperties.set(DNS_DOMAIN_SUFFICES, list);
}
}
@@ -423,7 +355,7 @@
if (mState != VpnState.CONNECTED) break;
mNotification.update();
checkConnectivity();
- VpnService.this.wait(ONE_SECOND);
+ VpnService.this.wait(1000); // 1 second
}
}
} catch (InterruptedException e) {
@@ -446,32 +378,6 @@
}
}
- private Object startTimer(final int milliseconds, final Runnable task) {
- Thread thread = new Thread(new Runnable() {
- public void run() {
- Log.d(TAG, "watchdog timer started");
- Thread t = Thread.currentThread();
- try {
- synchronized (t) {
- t.wait(milliseconds);
- }
- task.run();
- } catch (InterruptedException e) {
- // ignored
- }
- Log.d(TAG, "watchdog timer stopped");
- }
- });
- thread.start();
- return thread;
- }
-
- private void stopTimer(Object timer) {
- synchronized (timer) {
- timer.notify();
- }
- }
-
private String reallyGetHostIp() throws IOException {
Enumeration<NetworkInterface> ifces =
NetworkInterface.getNetworkInterfaces();
@@ -487,33 +393,13 @@
throw new IOException("Host IP is not available");
}
- private String getProfileSubpath(String subpath) throws IOException {
- String path = getProfilePath() + subpath;
- if (new File(path).exists()) {
- return path;
- } else {
- Log.w(TAG, "Profile subpath does not exist: " + path
- + ", use default one");
- String path2 = getDefaultConfigPath() + subpath;
- if (!new File(path2).exists()) {
- throw new IOException("Profile subpath does not exist at "
- + path + " or " + path2);
- }
- return path2;
- }
- }
-
- private void sleep(int ms) {
+ protected void sleep(int ms) {
try {
Thread.currentThread().sleep(ms);
} catch (InterruptedException e) {
}
}
- private static boolean isNullOrEmpty(String message) {
- return ((message == null) || (message.length() == 0));
- }
-
private InetAddress toInetAddress(int addr) throws IOException {
byte[] aa = new byte[4];
for (int i= 0; i < aa.length; i++) {
@@ -564,20 +450,6 @@
}
}
- private class FileMonitor extends FileObserver {
- private Runnable mCallback;
-
- FileMonitor(String path, Runnable callback) {
- super(path, CLOSE_NOWRITE);
- mCallback = callback;
- }
-
- @Override
- public void onEvent(int event, String path) {
- if ((event & CLOSE_NOWRITE) > 0) mCallback.run();
- }
- }
-
// Helper class for showing, updating notification.
private class NotificationHelper {
void update() {
diff --git a/services/java/com/android/server/AttributeCache.java b/services/java/com/android/server/AttributeCache.java
index 459ae52..81378dc 100644
--- a/services/java/com/android/server/AttributeCache.java
+++ b/services/java/com/android/server/AttributeCache.java
@@ -17,56 +17,36 @@
package com.android.server;
-import android.content.ComponentName;
-import android.content.ContentResolver;
import android.content.Context;
-import android.content.SharedPreferences;
-import android.content.Intent;
-import android.content.BroadcastReceiver;
+import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
-import android.provider.Settings;
-import android.util.Config;
-import android.util.Log;
+import android.util.SparseArray;
+import java.util.HashMap;
import java.util.WeakHashMap;
-public final class AttributeCache extends BroadcastReceiver {
+/**
+ * TODO: This should be better integrated into the system so it doesn't need
+ * special calls from the activity manager to clear it.
+ */
+public final class AttributeCache {
private static AttributeCache sInstance = null;
private final Context mContext;
- private final WeakHashMap<Key, Entry> mMap =
- new WeakHashMap<Key, Entry>();
- private final WeakHashMap<String, Context> mContexts =
- new WeakHashMap<String, Context>();
+ private final WeakHashMap<String, Package> mPackages =
+ new WeakHashMap<String, Package>();
+ private final Configuration mConfiguration = new Configuration();
- final static class Key {
- public final String packageName;
- public final int resId;
- public final int[] styleable;
+ public final static class Package {
+ public final Context context;
+ private final SparseArray<HashMap<int[], Entry>> mMap
+ = new SparseArray<HashMap<int[], Entry>>();
- public Key(String inPackageName, int inResId, int[] inStyleable) {
- packageName = inPackageName;
- resId = inResId;
- styleable = inStyleable;
- }
-
- @Override public boolean equals(Object obj) {
- try {
- if (obj != null) {
- Key other = (Key)obj;
- return packageName.equals(other.packageName)
- && resId == other.resId
- && styleable == other.styleable;
- }
- } catch (ClassCastException e) {
- }
- return false;
- }
-
- @Override public int hashCode() {
- return packageName.hashCode() + resId;
+ public Package(Context c) {
+ context = c;
}
}
@@ -94,36 +74,68 @@
mContext = context;
}
+ public void removePackage(String packageName) {
+ synchronized (this) {
+ mPackages.remove(packageName);
+ }
+ }
+
+ public void updateConfiguration(Configuration config) {
+ synchronized (this) {
+ int changes = mConfiguration.updateFrom(config);
+ if ((changes & ~(ActivityInfo.CONFIG_FONT_SCALE |
+ ActivityInfo.CONFIG_KEYBOARD_HIDDEN |
+ ActivityInfo.CONFIG_ORIENTATION)) != 0) {
+ // The configurations being masked out are ones that commonly
+ // change so we don't want flushing the cache... all others
+ // will flush the cache.
+ mPackages.clear();
+ }
+ }
+ }
+
public Entry get(String packageName, int resId, int[] styleable) {
synchronized (this) {
- Key key = new Key(packageName, resId, styleable);
- Entry ent = mMap.get(key);
- if (ent != null) {
- return ent;
- }
- Context context = mContexts.get(packageName);
- if (context == null) {
+ Package pkg = mPackages.get(packageName);
+ HashMap<int[], Entry> map = null;
+ Entry ent = null;
+ if (pkg != null) {
+ map = pkg.mMap.get(resId);
+ if (map != null) {
+ ent = map.get(styleable);
+ if (ent != null) {
+ return ent;
+ }
+ }
+ } else {
+ Context context;
try {
context = mContext.createPackageContext(packageName, 0);
if (context == null) {
return null;
}
- mContexts.put(packageName, context);
} catch (PackageManager.NameNotFoundException e) {
return null;
}
+ pkg = new Package(context);
+ mPackages.put(packageName, pkg);
}
+
+ if (map == null) {
+ map = new HashMap<int[], Entry>();
+ pkg.mMap.put(resId, map);
+ }
+
try {
- ent = new Entry(context,
- context.obtainStyledAttributes(resId, styleable));
- mMap.put(key, ent);
+ ent = new Entry(pkg.context,
+ pkg.context.obtainStyledAttributes(resId, styleable));
+ map.put(styleable, ent);
} catch (Resources.NotFoundException e) {
return null;
}
+
return ent;
}
}
- @Override public void onReceive(Context context, Intent intent) {
- }
}
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index c67f0b5..b15c06b 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -33,6 +33,7 @@
import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.net.Uri;
+import android.provider.Settings;
import android.os.Binder;
import android.os.Bundle;
import android.os.Environment;
@@ -42,7 +43,6 @@
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
-import android.os.SystemProperties;
import android.util.Log;
import android.util.SparseArray;
@@ -74,12 +74,13 @@
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";
+ // Secure settings
+ private static final String BACKUP_TRANSPORT_SETTING = "backup_transport";
+ private static final String BACKUP_ENABLED_SETTING = "backup_enabled";
- // Default time to wait after data changes before we back up the data
- private static final long COLLECTION_INTERVAL = 3 * 60 * 1000;
+ // How often we perform a backup pass. Privileged external callers can
+ // trigger an immediate pass.
+ private static final long BACKUP_INTERVAL = 60 * 60 * 1000;
private static final int MSG_RUN_BACKUP = 1;
private static final int MSG_RUN_FULL_BACKUP = 2;
@@ -166,7 +167,8 @@
// Set up our bookkeeping
// !!! STOPSHIP: make this disabled by default so that we then gate on
// setupwizard or other opt-out UI
- mEnabled = SystemProperties.getBoolean(BACKUP_ENABLED_PROPERTY, true);
+ mEnabled = (Settings.Secure.getInt(mContext.getContentResolver(),
+ BACKUP_ENABLED_SETTING, 1) != 0);
mBaseStateDir = new File(Environment.getDataDirectory(), "backup");
mDataDir = Environment.getDownloadCacheDirectory();
@@ -191,8 +193,13 @@
mGoogleTransport = null;
// !!! TODO: set up the default transport name "the right way"
- mCurrentTransport = SystemProperties.get(BACKUP_TRANSPORT_PROPERTY,
- "com.google.android.backup/.BackupTransportService");
+ mCurrentTransport = Settings.Secure.getString(mContext.getContentResolver(),
+ BACKUP_TRANSPORT_SETTING);
+ if (mCurrentTransport == null) {
+ mCurrentTransport = "com.google.android.backup/.BackupTransportService";
+ Settings.Secure.putString(mContext.getContentResolver(),
+ BACKUP_TRANSPORT_SETTING, mCurrentTransport);
+ }
if (DEBUG) Log.v(TAG, "Starting with transport " + mCurrentTransport);
// Attach to the Google backup transport. When this comes up, it will set
@@ -204,7 +211,7 @@
context.bindService(intent, mGoogleConnection, Context.BIND_AUTO_CREATE);
// Now that we know about valid backup participants, parse any
- // leftover journal files and schedule a new backup pass
+ // leftover journal files into the pending backup set
parseLeftoverJournals();
// Register for broadcasts about package install, etc., so we can
@@ -214,7 +221,13 @@
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
filter.addDataScheme("package");
mContext.registerReceiver(mBroadcastReceiver, filter);
- }
+
+ // Schedule the first backup pass -- okay because no other threads are
+ // running yet
+ if (mEnabled) {
+ scheduleBackupPassLocked(BACKUP_INTERVAL);
+ }
+}
private void makeJournalLocked() {
try {
@@ -336,35 +349,39 @@
ArrayList<BackupRequest> queue = new ArrayList<BackupRequest>();
File oldJournal = mJournal;
synchronized (mQueueLock) {
- if (mPendingBackups.size() == 0) {
- Log.v(TAG, "Backup requested but nothing pending");
- break;
- }
-
- for (BackupRequest b: mPendingBackups.values()) {
- queue.add(b);
- }
- Log.v(TAG, "clearing pending backups");
- mPendingBackups.clear();
-
- // Start a new backup-queue journal file too
- if (mJournalStream != null) {
- try {
- mJournalStream.close();
- } catch (IOException e) {
- // don't need to do anything
+ // Do we have any work to do?
+ if (mPendingBackups.size() > 0) {
+ for (BackupRequest b: mPendingBackups.values()) {
+ queue.add(b);
}
- makeJournalLocked();
- }
+ Log.v(TAG, "clearing pending backups");
+ mPendingBackups.clear();
- // At this point, we have started a new journal file, and the old
- // file identity is being passed to the backup processing thread.
- // When it completes successfully, that old journal file will be
- // deleted. If we crash prior to that, the old journal is parsed
- // at next boot and the journaled requests fulfilled.
+ // Start a new backup-queue journal file too
+ if (mJournalStream != null) {
+ try {
+ mJournalStream.close();
+ } catch (IOException e) {
+ // don't need to do anything
+ }
+ makeJournalLocked();
+ }
+
+ // At this point, we have started a new journal file, and the old
+ // file identity is being passed to the backup processing thread.
+ // When it completes successfully, that old journal file will be
+ // deleted. If we crash prior to that, the old journal is parsed
+ // at next boot and the journaled requests fulfilled.
+ (new PerformBackupThread(transport, queue, oldJournal)).start();
+ } else {
+ Log.v(TAG, "Backup requested but nothing pending");
+ }
}
- (new PerformBackupThread(transport, queue, oldJournal)).start();
+ // Schedule the next pass.
+ synchronized (mQueueLock) {
+ scheduleBackupPassLocked(BACKUP_INTERVAL);
+ }
break;
}
@@ -626,6 +643,9 @@
public void run() {
if (DEBUG) Log.v(TAG, "Beginning backup of " + mQueue.size() + " targets");
+ // Backups run at background priority
+ Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+
// 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
@@ -1106,10 +1126,6 @@
Log.d(TAG, " + " + b + " agent=" + b.appInfo.backupAgentName);
}
}
- // Schedule a backup pass in a few minutes. As backup-eligible data
- // keeps changing, continue to defer the backup pass until things
- // settle down, to avoid extra overhead.
- scheduleBackupPassLocked(COLLECTION_INTERVAL);
}
} else {
Log.w(TAG, "dataChanged but no participant pkg " + packageName);
@@ -1145,20 +1161,21 @@
boolean wasEnabled = mEnabled;
synchronized (this) {
- SystemProperties.set(BACKUP_ENABLED_PROPERTY, enable ? "true" : "false");
+ Settings.Secure.putInt(mContext.getContentResolver(), BACKUP_ENABLED_SETTING,
+ enable ? 1 : 0);
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);
- }
+ synchronized (mQueueLock) {
+ if (enable && !wasEnabled) {
+ // if we've just been enabled, start scheduling backup passes
+ scheduleBackupPassLocked(BACKUP_INTERVAL);
+ } else if (!enable) {
+ // No longer enabled, so stop running backups.
+ mBackupHandler.removeMessages(MSG_RUN_BACKUP);
}
}
-}
+ }
// Report whether the backup mechanism is currently enabled
public boolean isBackupEnabled() {
@@ -1203,7 +1220,8 @@
if (mTransports.get(transport) != null) {
prevTransport = mCurrentTransport;
mCurrentTransport = transport;
- SystemProperties.set(BACKUP_TRANSPORT_PROPERTY, transport);
+ Settings.Secure.putString(mContext.getContentResolver(), BACKUP_TRANSPORT_SETTING,
+ transport);
Log.v(TAG, "selectBackupTransport() set " + mCurrentTransport
+ " returning " + prevTransport);
} else {
@@ -1331,13 +1349,22 @@
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
synchronized (mQueueLock) {
+ pw.println("Backup Manager is " + (mEnabled ? "enabled" : "disabled"));
+ boolean scheduled = mBackupHandler.hasMessages(MSG_RUN_BACKUP);
+ if (scheduled != mEnabled) {
+ if (mEnabled) {
+ pw.println("ERROR: backups enabled but none scheduled!");
+ } else {
+ pw.println("ERROR: backups are scheduled but not enabled!");
+ }
+ }
pw.println("Available transports:");
for (String t : listAllTransports()) {
String pad = (t.equals(mCurrentTransport)) ? " * " : " ";
pw.println(pad + t);
}
int N = mBackupParticipants.size();
- pw.println("Participants:");
+ pw.println("Participants: " + N);
for (int i=0; i<N; i++) {
int uid = mBackupParticipants.keyAt(i);
pw.print(" uid: ");
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index fc37290..0f5b3fd 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -507,6 +507,7 @@
private void removeProvider(LocationProviderProxy provider) {
mProviders.remove(provider);
+ provider.unlinkProvider();
mProvidersByName.remove(provider.getName());
}
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 4a2808b..854138c 100644
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -25,15 +25,20 @@
import android.app.INotificationManager;
import android.app.ITransientNotification;
import android.app.Notification;
+import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.StatusBarManager;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.ContentQueryMap;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
+import android.database.ContentObserver;
import android.media.AsyncPlayer;
import android.media.AudioManager;
import android.net.Uri;
@@ -88,6 +93,12 @@
private NotificationRecord mVibrateNotification;
private Vibrator mVibrator = new Vibrator();
+ // adb
+ private int mBatteryPlugged;
+ private boolean mAdbEnabled = false;
+ private boolean mAdbNotificationShown = false;
+ private Notification mAdbNotification;
+
private ArrayList<NotificationRecord> mNotificationList;
private ArrayList<ToastRecord> mToastQueue;
@@ -297,6 +308,9 @@
mBatteryFull = batteryFull;
updateLights();
}
+
+ mBatteryPlugged = intent.getIntExtra("plugged", 0);
+ updateAdbNotification();
} else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
|| action.equals(Intent.ACTION_PACKAGE_RESTARTED)) {
Uri uri = intent.getData();
@@ -312,6 +326,31 @@
}
};
+ class SettingsObserver extends ContentObserver {
+ SettingsObserver(Handler handler) {
+ super(handler);
+ }
+
+ void observe() {
+ ContentResolver resolver = mContext.getContentResolver();
+ resolver.registerContentObserver(Settings.Secure.getUriFor(
+ Settings.Secure.ADB_ENABLED), false, this);
+ update();
+ }
+
+ @Override public void onChange(boolean selfChange) {
+ update();
+ }
+
+ public void update() {
+ ContentResolver resolver = mContext.getContentResolver();
+ mAdbEnabled = Settings.Secure.getInt(resolver,
+ Settings.Secure.ADB_ENABLED, 0) != 0;
+ updateAdbNotification();
+ }
+ }
+ private final SettingsObserver mSettingsObserver;
+
NotificationManagerService(Context context, StatusBarService statusBar,
HardwareService hardware)
{
@@ -333,6 +372,9 @@
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
mContext.registerReceiver(mIntentReceiver, filter);
+
+ mSettingsObserver = new SettingsObserver(mHandler);
+ mSettingsObserver.observe();
}
// Toasts
@@ -892,6 +934,62 @@
return -1;
}
+ // This is here instead of StatusBarPolicy because it is an important
+ // security feature that we don't want people customizing the platform
+ // to accidentally lose.
+ private void updateAdbNotification() {
+ if (mAdbEnabled && mBatteryPlugged == BatteryManager.BATTERY_PLUGGED_USB) {
+ if (!mAdbNotificationShown) {
+ NotificationManager notificationManager = (NotificationManager) mContext
+ .getSystemService(Context.NOTIFICATION_SERVICE);
+ if (notificationManager != null) {
+ Resources r = mContext.getResources();
+ CharSequence title = r.getText(
+ com.android.internal.R.string.adb_active_notification_title);
+ CharSequence message = r.getText(
+ com.android.internal.R.string.adb_active_notification_message);
+
+ if (mAdbNotification == null) {
+ mAdbNotification = new Notification();
+ mAdbNotification.icon = com.android.internal.R.drawable.stat_sys_warning;
+ mAdbNotification.when = 0;
+ mAdbNotification.flags = Notification.FLAG_ONGOING_EVENT;
+ mAdbNotification.tickerText = title;
+ mAdbNotification.defaults |= Notification.DEFAULT_SOUND;
+ }
+
+ Intent intent = new Intent(
+ Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
+ Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+ // Note: we are hard-coding the component because this is
+ // an important security UI that we don't want anyone
+ // intercepting.
+ intent.setComponent(new ComponentName("com.android.settings",
+ "com.android.settings.DevelopmentSettings"));
+ PendingIntent pi = PendingIntent.getActivity(mContext, 0,
+ intent, 0);
+
+ mAdbNotification.setLatestEventInfo(mContext, title, message, pi);
+
+ mAdbNotificationShown = true;
+ notificationManager.notify(
+ com.android.internal.R.string.adb_active_notification_title,
+ mAdbNotification);
+ }
+ }
+
+ } else if (mAdbNotificationShown) {
+ NotificationManager notificationManager = (NotificationManager) mContext
+ .getSystemService(Context.NOTIFICATION_SERVICE);
+ if (notificationManager != null) {
+ mAdbNotificationShown = false;
+ notificationManager.cancel(
+ com.android.internal.R.string.adb_active_notification_title);
+ }
+ }
+ }
+
// ======================================================================
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
diff --git a/services/java/com/android/server/PackageManagerBackupAgent.java b/services/java/com/android/server/PackageManagerBackupAgent.java
index 16f14e8..a1b4c268 100644
--- a/services/java/com/android/server/PackageManagerBackupAgent.java
+++ b/services/java/com/android/server/PackageManagerBackupAgent.java
@@ -59,7 +59,14 @@
private List<PackageInfo> mAllPackages;
private PackageManager mPackageManager;
+ // version & signature info of each app in a restore set
private HashMap<String, Metadata> mRestoredSignatures;
+ // The version info of each backed-up app as read from the state file
+ private HashMap<String, Metadata> mStateVersions = new HashMap<String, Metadata>();
+
+ private final HashSet<String> mExisting = new HashSet<String>();
+ private int mStoredSdkVersion;
+ private String mStoredIncrementalVersion;
public class Metadata {
public int versionCode;
@@ -96,24 +103,39 @@
ByteArrayOutputStream bufStream = new ByteArrayOutputStream(); // we'll reuse these
DataOutputStream outWriter = new DataOutputStream(bufStream);
- HashSet<String> existing = parseStateFile(oldState);
+ parseStateFile(oldState);
+
+ // If the stored version string differs, we need to re-backup all
+ // of the metadata. We force this by removing everything from the
+ // "already backed up" map built by parseStateFile().
+ if (mStoredIncrementalVersion == null
+ || !mStoredIncrementalVersion.equals(Build.VERSION.INCREMENTAL)) {
+ Log.i(TAG, "Previous metadata " + mStoredIncrementalVersion + " mismatch vs "
+ + Build.VERSION.INCREMENTAL + " - rewriting");
+ mExisting.clear();
+ }
try {
/*
* Global metadata:
*
- * int version -- the SDK version of the OS itself on the device
- * that produced this backup set. Used to reject
- * backups from later OSes onto earlier ones.
+ * int SDKversion -- the SDK version of the OS itself on the device
+ * that produced this backup set. Used to reject
+ * backups from later OSes onto earlier ones.
+ * String incremental -- the incremental release name of the OS stored in
+ * the backup set.
*/
- if (!existing.contains(GLOBAL_METADATA_KEY)) {
+ if (!mExisting.contains(GLOBAL_METADATA_KEY)) {
if (DEBUG) Log.v(TAG, "Storing global metadata key");
outWriter.writeInt(Build.VERSION.SDK_INT);
+ outWriter.writeUTF(Build.VERSION.INCREMENTAL);
byte[] metadata = bufStream.toByteArray();
data.writeEntityHeader(GLOBAL_METADATA_KEY, metadata.length);
data.writeEntityData(metadata, metadata.length);
} else {
if (DEBUG) Log.v(TAG, "Global metadata key already stored");
+ // don't consider it to have been skipped/deleted
+ mExisting.remove(GLOBAL_METADATA_KEY);
}
// For each app we have on device, see if we've backed it up yet. If not,
@@ -123,11 +145,36 @@
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
+ } else {
+ PackageInfo info = null;
try {
- PackageInfo info = mPackageManager.getPackageInfo(packName,
+ info = mPackageManager.getPackageInfo(packName,
PackageManager.GET_SIGNATURES);
+ } catch (NameNotFoundException e) {
+ // Weird; we just found it, and now are told it doesn't exist.
+ // Treat it as having been removed from the device.
+ mExisting.add(packName);
+ continue;
+ }
+
+ boolean doBackup = false;
+ if (!mExisting.contains(packName)) {
+ // We haven't backed up this app before
+ doBackup = true;
+ } else {
+ // We *have* backed this one up before. Check whether the version
+ // of the backup matches the version of the current app; if they
+ // don't match, the app has been updated and we need to store its
+ // metadata again. In either case, take it out of mExisting so that
+ // we don't consider it deleted later.
+ if (info.versionCode != mStateVersions.get(packName).versionCode) {
+ doBackup = true;
+ }
+ mExisting.remove(packName);
+ }
+
+ if (doBackup) {
+ // We need to store this app's metadata
/*
* Metadata for each package:
*
@@ -135,7 +182,7 @@
* byte[] signatures -- [len] flattened Signature[] of the package
*/
- // marshall the version code in a canonical form
+ // marshal the version code in a canonical form
bufStream.reset();
outWriter.writeInt(info.versionCode);
byte[] versionBuf = bufStream.toByteArray();
@@ -153,18 +200,6 @@
data.writeEntityHeader(packName, versionBuf.length + sigs.length);
data.writeEntityData(versionBuf, versionBuf.length);
data.writeEntityData(sigs, sigs.length);
- } catch (NameNotFoundException e) {
- // Weird; we just found it, and now are told it doesn't exist.
- // Treat it as having been removed from the device.
- existing.add(packName);
- }
- } else {
- // We've already backed up this app. Remove it from the set so
- // we can tell at the end what has disappeared from the device.
- // !!! TODO: take out the debugging message
- if (DEBUG) Log.v(TAG, "= already backed up metadata for " + packName);
- if (!existing.remove(packName)) {
- Log.d(TAG, "*** failed to remove " + packName + " from package set!");
}
}
}
@@ -172,7 +207,7 @@
// At this point, the only entries in 'existing' are apps that were
// mentioned in the saved state file, but appear to no longer be present
// on the device. Write a deletion entity for them.
- for (String app : existing) {
+ for (String app : mExisting) {
// !!! TODO: take out this msg
if (DEBUG) Log.v(TAG, "- removing metadata for deleted pkg " + app);
try {
@@ -215,17 +250,21 @@
DataInputStream in = new DataInputStream(baStream);
if (key.equals(GLOBAL_METADATA_KEY)) {
- storedSystemVersion = in.readInt();
+ int storedSdkVersion = in.readInt();
if (DEBUG) Log.v(TAG, " storedSystemVersion = " + storedSystemVersion);
if (storedSystemVersion > Build.VERSION.SDK_INT) {
// returning before setting the sig map means we rejected the restore set
Log.w(TAG, "Restore set was from a later version of Android; not restoring");
return;
}
+ mStoredSdkVersion = storedSdkVersion;
+ mStoredIncrementalVersion = in.readUTF();
// !!! TODO: remove this debugging output
if (DEBUG) {
Log.i(TAG, "Restore set version " + storedSystemVersion
- + " is compatible with OS version " + Build.VERSION.SDK_INT);
+ + " is compatible with OS version " + Build.VERSION.SDK_INT
+ + " (" + mStoredIncrementalVersion + " vs "
+ + Build.VERSION.INCREMENTAL + ")");
}
} else {
// it's a file metadata record
@@ -302,31 +341,45 @@
}
// Util: parse out an existing state file into a usable structure
- private HashSet<String> parseStateFile(ParcelFileDescriptor stateFile) {
- HashSet<String> set = new HashSet<String>();
+ private void parseStateFile(ParcelFileDescriptor stateFile) {
+ mExisting.clear();
+ mStateVersions.clear();
+ mStoredSdkVersion = 0;
+ mStoredIncrementalVersion = null;
+
// The state file is just the list of app names we have stored signatures for
+ // with the exception of the metadata block, to which is also appended the
+ // version numbers corresponding with the last time we wrote this PM block.
+ // If they mismatch the current system, we'll re-store the metadata key.
FileInputStream instream = new FileInputStream(stateFile.getFileDescriptor());
DataInputStream in = new DataInputStream(instream);
int bufSize = 256;
byte[] buf = new byte[bufSize];
try {
- int nameSize = in.readInt();
- if (bufSize < nameSize) {
- bufSize = nameSize + 32;
- buf = new byte[bufSize];
+ String pkg = in.readUTF();
+ if (pkg.equals(GLOBAL_METADATA_KEY)) {
+ mStoredSdkVersion = in.readInt();
+ mStoredIncrementalVersion = in.readUTF();
+ mExisting.add(GLOBAL_METADATA_KEY);
+ } else {
+ Log.e(TAG, "No global metadata in state file!");
+ return;
}
- in.read(buf, 0, nameSize);
- String pkg = new String(buf, 0, nameSize);
- set.add(pkg);
+
+ // The global metadata was first; now read all the apps
+ while (true) {
+ pkg = in.readUTF();
+ int versionCode = in.readInt();
+ mExisting.add(pkg);
+ mStateVersions.put(pkg, new Metadata(versionCode, null));
+ }
} catch (EOFException eof) {
// safe; we're done
} catch (IOException e) {
// whoops, bad state file. abort.
- Log.e(TAG, "Unable to read Package Manager state file");
- return null;
+ Log.e(TAG, "Unable to read Package Manager state file: " + e);
}
- return set;
}
// Util: write out our new backup state file
@@ -336,15 +389,14 @@
try {
// by the time we get here we know we've stored the global metadata record
- byte[] metaNameBuf = GLOBAL_METADATA_KEY.getBytes();
- out.writeInt(metaNameBuf.length);
- out.write(metaNameBuf);
+ out.writeUTF(GLOBAL_METADATA_KEY);
+ out.writeInt(Build.VERSION.SDK_INT);
+ out.writeUTF(Build.VERSION.INCREMENTAL);
// now write all the app names too
for (PackageInfo pkg : pkgs) {
- byte[] pkgNameBuf = pkg.packageName.getBytes();
- out.writeInt(pkgNameBuf.length);
- out.write(pkgNameBuf);
+ out.writeUTF(pkg.packageName);
+ out.writeInt(pkg.versionCode);
}
} catch (IOException e) {
Log.e(TAG, "Unable to write package manager state file!");
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 972aa98..4ea2831 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -3292,6 +3292,7 @@
if (extras != null) {
intent.putExtras(extras);
}
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
am.broadcastIntent(
null, intent,
null, null, 0, null, null, null, false, false);
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index c5ea5fa9..79d78ad1 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -709,7 +709,10 @@
p.awakeOnSet = true;
}
} else {
- mPokeLocks.remove(token);
+ PokeLock rLock = mPokeLocks.remove(token);
+ if (rLock != null) {
+ token.unlinkToDeath(rLock, 0);
+ }
}
int oldPokey = mPokey;
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index 5df88b2..f5e2e3d 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -1875,7 +1875,9 @@
private WifiLock removeLock(IBinder binder) {
int index = findLockByBinder(binder);
if (index >= 0) {
- return mList.remove(index);
+ WifiLock ret = mList.remove(index);
+ ret.unlinkDeathRecipient();
+ return ret;
} else {
return null;
}
@@ -1987,6 +1989,10 @@
binderDied();
}
}
+
+ void unlinkDeathRecipient() {
+ mBinder.unlinkToDeath(this, 0);
+ }
}
private class Multicaster extends DeathRecipient {
@@ -2054,7 +2060,10 @@
private void removeMulticasterLocked(int i, int uid)
{
- mMulticasters.remove(i);
+ Multicaster removed = mMulticasters.remove(i);
+ if (removed != null) {
+ removed.unlinkDeathRecipient();
+ }
if (mMulticasters.size() == 0) {
WifiNative.startPacketFiltering();
}
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 9bad153..cfa625c 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -3407,7 +3407,10 @@
synchronized (mWindowMap) {
for (int i=0; i<mRotationWatchers.size(); i++) {
if (watcherBinder == mRotationWatchers.get(i).asBinder()) {
- mRotationWatchers.remove(i);
+ IRotationWatcher removed = mRotationWatchers.remove(i);
+ if (removed != null) {
+ removed.asBinder().unlinkToDeath(this, 0);
+ }
i--;
}
}
@@ -5442,6 +5445,7 @@
} catch (RemoteException e) {
}
synchronized(mWindowMap) {
+ mClient.asBinder().unlinkToDeath(this, 0);
mClientDead = true;
killSessionLocked();
}
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 6d04b6b..f716571 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -17,6 +17,7 @@
package com.android.server.am;
import com.android.internal.os.BatteryStatsImpl;
+import com.android.server.AttributeCache;
import com.android.server.IntentResolver;
import com.android.server.ProcessMap;
import com.android.server.ProcessStats;
@@ -61,7 +62,6 @@
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.net.Uri;
-import android.os.BatteryStats;
import android.os.Binder;
import android.os.Bundle;
import android.os.Environment;
@@ -10715,6 +10715,10 @@
if (!intent.getBooleanExtra(Intent.EXTRA_DONT_KILL_APP, false)) {
uninstallPackageLocked(ssp,
intent.getIntExtra(Intent.EXTRA_UID, -1), false);
+ AttributeCache ac = AttributeCache.instance();
+ if (ac != null) {
+ ac.removePackage(ssp);
+ }
}
}
}
@@ -11765,6 +11769,11 @@
Intent intent = new Intent(Intent.ACTION_CONFIGURATION_CHANGED);
broadcastIntentLocked(null, null, intent, null, null, 0, null, null,
null, false, false, MY_PID, Process.SYSTEM_UID);
+
+ AttributeCache ac = AttributeCache.instance();
+ if (ac != null) {
+ ac.updateConfiguration(mConfiguration);
+ }
}
}
diff --git a/services/java/com/android/server/status/StatusBarPolicy.java b/services/java/com/android/server/status/StatusBarPolicy.java
index dd3d38c..7a8d4e5e 100644
--- a/services/java/com/android/server/status/StatusBarPolicy.java
+++ b/services/java/com/android/server/status/StatusBarPolicy.java
@@ -99,7 +99,7 @@
private IBinder mBatteryIcon;
private IconData mBatteryData;
private boolean mBatteryFirst = true;
- private boolean mBatteryPlugged;
+ private int mBatteryPlugged;
private int mBatteryLevel;
private int mBatteryThreshold = 0; // index into mBatteryThresholds
private int[] mBatteryThresholds = new int[] { 20, 15, -1 };
@@ -108,6 +108,7 @@
private View mBatteryView;
private int mBatteryViewSequence;
private boolean mBatteryShowLowOnEndCall = false;
+ private boolean mSentLowBatteryBroadcast = false;
private static final boolean SHOW_LOW_BATTERY_WARNING = true;
// phone
@@ -581,7 +582,7 @@
mBatteryData.iconLevel = intent.getIntExtra("level", 0);
mService.updateIcon(mBatteryIcon, mBatteryData, null);
- boolean plugged = intent.getIntExtra("plugged", 0) != 0;
+ int plugged = intent.getIntExtra("plugged", 0);
int level = intent.getIntExtra("level", -1);
if (false) {
Log.d(TAG, "updateBattery level=" + level
@@ -592,7 +593,7 @@
+ " mBatteryFirst=" + mBatteryFirst);
}
- boolean oldPlugged = mBatteryPlugged;
+ int oldPlugged = mBatteryPlugged;
int oldThreshold = mBatteryThreshold;
pickNextBatteryLevel(level);
@@ -619,11 +620,12 @@
Log.d(TAG, "plugged=" + plugged + " oldPlugged=" + oldPlugged + " level=" + level
+ " mBatteryThreshold=" + mBatteryThreshold + " oldThreshold=" + oldThreshold);
}
- if (!plugged
- && ((oldPlugged && level < mBatteryThresholds[BATTERY_THRESHOLD_WARNING])
+ if (plugged == 0
+ && ((oldPlugged != 0 && level < mBatteryThresholds[BATTERY_THRESHOLD_WARNING])
|| (mBatteryThreshold > oldThreshold
&& mBatteryThreshold > BATTERY_THRESHOLD_WARNING))) {
// Broadcast the low battery warning
+ mSentLowBatteryBroadcast = true;
mContext.sendBroadcast(new Intent(Intent.ACTION_BATTERY_LOW));
if (SHOW_LOW_BATTERY_WARNING) {
@@ -639,7 +641,11 @@
mBatteryShowLowOnEndCall = true;
}
}
- } else if (mBatteryThreshold == BATTERY_THRESHOLD_CLOSE_WARNING) {
+ } else if (mBatteryThreshold < BATTERY_THRESHOLD_WARNING) {
+ if (mSentLowBatteryBroadcast == true) {
+ mSentLowBatteryBroadcast = false;
+ mContext.sendBroadcast(new Intent(Intent.ACTION_BATTERY_OKAY));
+ }
if (SHOW_LOW_BATTERY_WARNING) {
if (mLowBatteryDialog != null) {
mLowBatteryDialog.dismiss();
@@ -763,7 +769,7 @@
}
if (mPhoneState == TelephonyManager.CALL_STATE_IDLE) {
if (mBatteryShowLowOnEndCall) {
- if (!mBatteryPlugged) {
+ if (mBatteryPlugged == 0) {
showLowBatteryWarning();
}
mBatteryShowLowOnEndCall = false;
diff --git a/services/java/com/android/server/status/StatusBarService.java b/services/java/com/android/server/status/StatusBarService.java
index 48cbace..b44168a 100644
--- a/services/java/com/android/server/status/StatusBarService.java
+++ b/services/java/com/android/server/status/StatusBarService.java
@@ -119,6 +119,7 @@
public void binderDied() {
Log.i(TAG, "binder died for pkg=" + pkg);
disable(0, token, pkg);
+ token.unlinkToDeath(this, 0);
}
}
@@ -494,6 +495,7 @@
if (what == 0 || !token.isBinderAlive()) {
if (tok != null) {
mDisableRecords.remove(i);
+ tok.token.unlinkToDeath(tok, 0);
}
} else {
if (tok == null) {
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index bf5df88..c9dcd8b 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -667,7 +667,10 @@
} catch (RemoteException ex) {
// the phone process is restarting.
return CALL_STATE_IDLE;
- }
+ } catch (NullPointerException ex) {
+ // the phone process is restarting.
+ return CALL_STATE_IDLE;
+ }
}
/** Data connection activity: No traffic. */
@@ -701,7 +704,10 @@
} catch (RemoteException ex) {
// the phone process is restarting.
return DATA_ACTIVITY_NONE;
- }
+ } catch (NullPointerException ex) {
+ // the phone process is restarting.
+ return DATA_ACTIVITY_NONE;
+ }
}
/** Data connection state: Disconnected. IP traffic not available. */
diff --git a/telephony/java/com/android/internal/telephony/WspTypeDecoder.java b/telephony/java/com/android/internal/telephony/WspTypeDecoder.java
index 2984fa8..3bbe0e1 100644
--- a/telephony/java/com/android/internal/telephony/WspTypeDecoder.java
+++ b/telephony/java/com/android/internal/telephony/WspTypeDecoder.java
@@ -187,22 +187,30 @@
}
/**
- * Decode the "Extension-media" type for WSP pdu
- *
- * @param startIndex The starting position of the "Extension-media" in this pdu
- *
- * @return false when error(not a Extension-media) occur
- * return value can be retrieved by getValueString() method
- * length of data in pdu can be retrieved by getValue32() method
- */
+ * Decode the "Extension-media" type for WSP PDU.
+ *
+ * @param startIndex The starting position of the "Extension-media" in this PDU.
+ *
+ * @return false on error, such as if there is no Extension-media at startIndex.
+ * Side-effects: updates stringValue (available with getValueString()), which will be
+ * null on error. The length of the data in the PDU is available with getValue32(), 0
+ * on error.
+ */
public boolean decodeExtensionMedia(int startIndex) {
int index = startIndex;
- while (wspData[index] != 0) {
+ dataLength = 0;
+ stringValue = null;
+ int length = wspData.length;
+ boolean rtrn = index < length;
+
+ while (index < length && wspData[index] != 0) {
index++;
}
+
dataLength = index - startIndex + 1;
stringValue = new String(wspData, startIndex, dataLength - 1);
- return true;
+
+ return rtrn;
}
/**
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
index e00ee83..c33d4b6 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
@@ -1398,7 +1398,7 @@
protected void onVoiceCallEnded() {
if (state == State.CONNECTED) {
- if (mGsmPhone.mSST.isConcurrentVoiceAndData()) {
+ if (!mGsmPhone.mSST.isConcurrentVoiceAndData()) {
startNetStatPoll();
phone.notifyDataConnection(Phone.REASON_VOICE_CALL_ENDED);
} else {
diff --git a/test-runner/android/test/MoreAsserts.java b/test-runner/android/test/MoreAsserts.java
index 2e74644..9e0d018 100644
--- a/test-runner/android/test/MoreAsserts.java
+++ b/test-runner/android/test/MoreAsserts.java
@@ -24,6 +24,7 @@
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
+import java.util.ArrayList;
import java.util.regex.MatchResult;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -316,8 +317,11 @@
*/
public static void assertContentsInOrder(
String message, Iterable<?> actual, Object... expected) {
- Assert.assertEquals(message,
- Arrays.asList(expected), Lists.newArrayList(actual));
+ ArrayList actualList = new ArrayList();
+ for (Object o : actual) {
+ actualList.add(o);
+ }
+ Assert.assertEquals(message, Arrays.asList(expected), actualList);
}
/**
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/LoadTestsAutoTest.java b/tests/DumpRenderTree/src/com/android/dumprendertree/LoadTestsAutoTest.java
index c792e8e..cbcac6c 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/LoadTestsAutoTest.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/LoadTestsAutoTest.java
@@ -16,18 +16,15 @@
package com.android.dumprendertree;
+import dalvik.system.VMRuntime;
+
import android.app.Instrumentation;
import android.content.Intent;
-
-import android.util.Log;
-
import android.os.Bundle;
import android.os.Debug;
-import android.os.Debug.MemoryInfo;
+import android.os.Process;
import android.test.ActivityInstrumentationTestCase2;
-
-import com.android.dumprendertree.TestShellActivity;
-import com.android.dumprendertree.TestShellCallback;
+import android.util.Log;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -70,9 +67,7 @@
TestShellActivity activity = (TestShellActivity) getActivity();
Log.v(LOGTAG, "About to run tests, calling gc first...");
- Runtime.getRuntime().runFinalization();
- Runtime.getRuntime().gc();
- Runtime.getRuntime().gc();
+ freeMem();
// Run tests
runTestAndWaitUntilDone(activity, runner.mTestPath, runner.mTimeoutInMillis);
@@ -83,46 +78,73 @@
activity.finish();
}
+ private void freeMem() {
+ Log.v(LOGTAG, "freeMem: calling gc/finalization...");
+ final VMRuntime runtime = VMRuntime.getRuntime();
+
+ runtime.gcSoftReferences();
+ runtime.runFinalizationSync();
+ runtime.gcSoftReferences();
+ runtime.runFinalizationSync();
+ runtime.gcSoftReferences();
+ runtime.runFinalizationSync();
+ Runtime.getRuntime().runFinalization();
+ Runtime.getRuntime().gc();
+ Runtime.getRuntime().gc();
+
+ }
+
+ private void printRow(PrintStream ps, String format, Object...objs) {
+ ps.println(String.format(format, objs));
+ }
+
private void dumpMemoryInfo() {
try {
- Log.v(LOGTAG, "About to dump meminfo, calling gc first...");
- Runtime.getRuntime().runFinalization();
- Runtime.getRuntime().gc();
- Runtime.getRuntime().gc();
-
+ freeMem();
Log.v(LOGTAG, "Dumping memory information.");
FileOutputStream out = new FileOutputStream(LOAD_TEST_RESULT, true);
PrintStream ps = new PrintStream(out);
- MemoryInfo mi = new MemoryInfo();
- Debug.getMemoryInfo(mi);
-
- //try to fake the dumpsys format
- //this will eventually be changed to XML
- String format = "%15s:%9d%9d%9d%9d";
- String pss =
- String.format(format, "(Pss)",
- mi.nativePss, mi.dalvikPss, mi.otherPss,
- mi.nativePss + mi.dalvikPss + mi.otherPss);
- String sd =
- String.format(format, "(shared dirty)",
- mi.nativeSharedDirty, mi.dalvikSharedDirty, mi.otherSharedDirty,
- mi.nativeSharedDirty + mi.dalvikSharedDirty + mi.otherSharedDirty);
- String pd =
- String.format(format, "(priv dirty)",
- mi.nativePrivateDirty, mi.dalvikPrivateDirty, mi.otherPrivateDirty,
- mi.nativePrivateDirty + mi.dalvikPrivateDirty + mi.otherPrivateDirty);
-
ps.print("\n\n\n");
- ps.println("** MEMINFO in pid 0 [com.android.dumprendertree] **");
- ps.println(" native dalvik other total");
- ps.println(" size: 12060 5255 N/A 17315");
- ps.println(" allocated: 12060 5255 N/A 17315");
- ps.println(" free: 12060 5255 N/A 17315");
- ps.println(pss);
- ps.println(sd);
- ps.println(pd);
+ ps.println("** MEMINFO in pid " + Process.myPid()
+ + " [com.android.dumprendertree] **");
+ String formatString = "%17s %8s %8s %8s %8s";
+
+ long nativeMax = Debug.getNativeHeapSize() / 1024;
+ long nativeAllocated = Debug.getNativeHeapAllocatedSize() / 1024;
+ long nativeFree = Debug.getNativeHeapFreeSize() / 1024;
+ Runtime runtime = Runtime.getRuntime();
+ long dalvikMax = runtime.totalMemory() / 1024;
+ long dalvikFree = runtime.freeMemory() / 1024;
+ long dalvikAllocated = dalvikMax - dalvikFree;
+
+
+ Debug.MemoryInfo memInfo = new Debug.MemoryInfo();
+ Debug.getMemoryInfo(memInfo);
+
+ final int nativeShared = memInfo.nativeSharedDirty;
+ final int dalvikShared = memInfo.dalvikSharedDirty;
+ final int otherShared = memInfo.otherSharedDirty;
+
+ final int nativePrivate = memInfo.nativePrivateDirty;
+ final int dalvikPrivate = memInfo.dalvikPrivateDirty;
+ final int otherPrivate = memInfo.otherPrivateDirty;
+
+ printRow(ps, formatString, "", "native", "dalvik", "other", "total");
+ printRow(ps, formatString, "size:", nativeMax, dalvikMax, "N/A", nativeMax + dalvikMax);
+ printRow(ps, formatString, "allocated:", nativeAllocated, dalvikAllocated, "N/A",
+ nativeAllocated + dalvikAllocated);
+ printRow(ps, formatString, "free:", nativeFree, dalvikFree, "N/A",
+ nativeFree + dalvikFree);
+
+ printRow(ps, formatString, "(Pss):", memInfo.nativePss, memInfo.dalvikPss,
+ memInfo.otherPss, memInfo.nativePss + memInfo.dalvikPss + memInfo.otherPss);
+
+ printRow(ps, formatString, "(shared dirty):", nativeShared, dalvikShared, otherShared,
+ nativeShared + dalvikShared + otherShared);
+ printRow(ps, formatString, "(priv dirty):", nativePrivate, dalvikPrivate, otherPrivate,
+ nativePrivate + dalvikPrivate + otherPrivate);
ps.print("\n\n\n");
ps.flush();
ps.close();
@@ -142,7 +164,7 @@
LoadTestsAutoTest.this.notifyAll();
}
}
-
+
public void timedOut(String url) {
}
});