Merge changes I799532f7,I5cffa6eb
* changes:
SurfaceTexture: fix a comment
SurfaceTexture: remove getAllocator
diff --git a/Android.mk b/Android.mk
index 335fb73..3b0fc59 100644
--- a/Android.mk
+++ b/Android.mk
@@ -158,6 +158,11 @@
core/java/com/android/internal/os/IResultReceiver.aidl \
core/java/com/android/internal/statusbar/IStatusBar.aidl \
core/java/com/android/internal/statusbar/IStatusBarService.aidl \
+ core/java/com/android/internal/textservice/ISpellCheckerService.aidl \
+ core/java/com/android/internal/textservice/ISpellCheckerSession.aidl \
+ core/java/com/android/internal/textservice/ISpellCheckerSessionListener.aidl \
+ core/java/com/android/internal/textservice/ITextServicesManager.aidl \
+ core/java/com/android/internal/textservice/ITextServicesSessionListener.aidl \
core/java/com/android/internal/view/IInputContext.aidl \
core/java/com/android/internal/view/IInputContextCallback.aidl \
core/java/com/android/internal/view/IInputMethod.aidl \
@@ -266,6 +271,11 @@
frameworks/base/core/java/android/view/Surface.aidl \
frameworks/base/core/java/android/view/WindowManager.aidl \
frameworks/base/core/java/android/widget/RemoteViews.aidl \
+ frameworks/base/core/java/com/android/internal/textservice/ISpellCheckerService.aidl \
+ frameworks/base/core/java/com/android/internal/textservice/ISpellCheckerSession.aidl \
+ frameworks/base/core/java/com/android/internal/textservice/ISpellCheckerSessionListener.aidl \
+ frameworks/base/core/java/com/android/internal/textservice/ITextServicesManager.aidl \
+ frameworks/base/core/java/com/android/internal/textservice/ITextServicesSessionListener.aidl \
frameworks/base/core/java/com/android/internal/view/IInputContext.aidl \
frameworks/base/core/java/com/android/internal/view/IInputMethod.aidl \
frameworks/base/core/java/com/android/internal/view/IInputMethodCallback.aidl \
diff --git a/api/current.txt b/api/current.txt
index 0976380..f88001d 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -21,6 +21,7 @@
field public static final java.lang.String BIND_DEVICE_ADMIN = "android.permission.BIND_DEVICE_ADMIN";
field public static final java.lang.String BIND_INPUT_METHOD = "android.permission.BIND_INPUT_METHOD";
field public static final java.lang.String BIND_REMOTEVIEWS = "android.permission.BIND_REMOTEVIEWS";
+ field public static final java.lang.String BIND_TEXT_SERVICE = "android.permission.BIND_TEXT_SERVICE";
field public static final java.lang.String BIND_WALLPAPER = "android.permission.BIND_WALLPAPER";
field public static final java.lang.String BLUETOOTH = "android.permission.BLUETOOTH";
field public static final java.lang.String BLUETOOTH_ADMIN = "android.permission.BLUETOOTH_ADMIN";
@@ -4783,6 +4784,7 @@
field public static final java.lang.String SENSOR_SERVICE = "sensor";
field public static final java.lang.String STORAGE_SERVICE = "storage";
field public static final java.lang.String TELEPHONY_SERVICE = "phone";
+ field public static final java.lang.String TEXT_SERVICES_MANAGER_SERVICE = "textservices";
field public static final java.lang.String UI_MODE_SERVICE = "uimode";
field public static final java.lang.String USB_SERVICE = "usb";
field public static final java.lang.String VIBRATOR_SERVICE = "vibrator";
@@ -17941,6 +17943,30 @@
}
+package android.service.textservice {
+
+ public abstract class SpellCheckerService extends android.app.Service {
+ ctor public SpellCheckerService();
+ method public void cancel();
+ method public abstract android.view.textservice.SuggestionsInfo getSuggestions(android.view.textservice.TextInfo, int, java.lang.String);
+ method public android.view.textservice.SuggestionsInfo[] getSuggestionsMultiple(android.view.textservice.TextInfo[], java.lang.String, int, boolean);
+ method public final android.os.IBinder onBind(android.content.Intent);
+ field public static final java.lang.String SERVICE_INTERFACE;
+ }
+
+ public class SpellCheckerSession {
+ method public void close();
+ method public void getSuggestions(android.view.textservice.TextInfo, int);
+ method public void getSuggestions(android.view.textservice.TextInfo[], int, boolean);
+ method public boolean isSessionDisconnected();
+ }
+
+ public static abstract interface SpellCheckerSession.SpellCheckerSessionListener {
+ method public abstract void onGetSuggestions(android.view.textservice.SuggestionsInfo[]);
+ }
+
+}
+
package android.service.wallpaper {
public abstract class WallpaperService extends android.app.Service {
@@ -21004,6 +21030,11 @@
method public void onPrepareSubMenu(android.view.SubMenu);
}
+ public abstract interface CollapsibleActionView {
+ method public abstract void onActionViewCollapsed();
+ method public abstract void onActionViewExpanded();
+ }
+
public abstract interface ContextMenu implements android.view.Menu {
method public abstract void clearHeader();
method public abstract android.view.ContextMenu setHeaderIcon(int);
@@ -22714,10 +22745,8 @@
ctor public ViewDebug();
method public static void dumpCapturedView(java.lang.String, java.lang.Object);
method public static void startHierarchyTracing(java.lang.String, android.view.View);
- method public static void startLooperProfiling(java.io.File);
method public static void startRecyclerTracing(java.lang.String, android.view.View);
method public static void stopHierarchyTracing();
- method public static void stopLooperProfiling();
method public static void stopRecyclerTracing();
method public static void trace(android.view.View, android.view.ViewDebug.RecyclerTraceType, int...);
method public static void trace(android.view.View, android.view.ViewDebug.HierarchyTraceType);
@@ -24018,6 +24047,54 @@
}
+package android.view.textservice {
+
+ public final class SpellCheckerInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method public android.content.ComponentName getComponent();
+ method public java.lang.String getId();
+ method public java.lang.String getPackageName();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator CREATOR;
+ }
+
+ public final class SuggestionsInfo implements android.os.Parcelable {
+ ctor public SuggestionsInfo(int, java.lang.String[]);
+ ctor public SuggestionsInfo(int, java.lang.String[], int, int);
+ ctor public SuggestionsInfo(android.os.Parcel);
+ method public int describeContents();
+ method public int getCookie();
+ method public int getSequence();
+ method public java.lang.String getSuggestionAt(int);
+ method public int getSuggestionsAttributes();
+ method public int getSuggestionsCount();
+ method public void setCookieAndSequence(int, int);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator CREATOR;
+ field public static final int RESULT_ATTR_IN_THE_DICTIONARY = 1; // 0x1
+ field public static final int RESULT_ATTR_LOOKS_TYPO = 4; // 0x4
+ field public static final int RESULT_ATTR_SUGGESTIONS_AVAILABLE = 2; // 0x2
+ }
+
+ public final class TextInfo implements android.os.Parcelable {
+ ctor public TextInfo(java.lang.String);
+ ctor public TextInfo(java.lang.String, int, int);
+ ctor public TextInfo(android.os.Parcel);
+ method public int describeContents();
+ method public int getCookie();
+ method public int getSequence();
+ method public java.lang.String getText();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator CREATOR;
+ }
+
+ public final class TextServicesManager {
+ method public android.view.textservice.SpellCheckerInfo getCurrentSpellChecker(java.util.Locale);
+ method public android.service.textservice.SpellCheckerSession newSpellCheckerSession(android.view.textservice.SpellCheckerInfo, java.util.Locale, android.service.textservice.SpellCheckerSession.SpellCheckerSessionListener);
+ }
+
+}
+
package android.webkit {
public final deprecated class CacheManager {
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 3fb1736..6dfa12b 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -468,10 +468,16 @@
String profileFile = null;
boolean start = false;
boolean wall = false;
+ int profileType = 0;
String process = null;
String cmd = nextArgRequired();
+ if ("looper".equals(cmd)) {
+ cmd = nextArgRequired();
+ profileType = 1;
+ }
+
if ("start".equals(cmd)) {
start = true;
wall = "--wall".equals(nextOption());
@@ -516,7 +522,7 @@
} else if (start) {
//removeWallOption();
}
- if (!mAm.profileControl(process, start, profileFile, fd)) {
+ if (!mAm.profileControl(process, start, profileFile, fd, profileType)) {
wall = false;
throw new AndroidException("PROFILE FAILED on process " + process);
}
@@ -1076,8 +1082,8 @@
" am broadcast <INTENT>\n" +
" am instrument [-r] [-e <NAME> <VALUE>] [-p] [-w]\n" +
" [--no-window-animation] <COMPONENT>\n" +
- " am profile start <PROCESS> <FILE>\n" +
- " am profile stop <PROCESS>\n" +
+ " am profile [looper] start <PROCESS> <FILE>\n" +
+ " am profile [looper] stop <PROCESS>\n" +
" am dumpheap [flags] <PROCESS> <FILE>\n" +
" am monitor [--gdb <port>]\n" +
" am screen-compat [on|off] <PACKAGE>\n" +
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 2a731a3..b7cd829 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1104,10 +1104,11 @@
data.enforceInterface(IActivityManager.descriptor);
String process = data.readString();
boolean start = data.readInt() != 0;
+ int profileType = data.readInt();
String path = data.readString();
ParcelFileDescriptor fd = data.readInt() != 0
? data.readFileDescriptor() : null;
- boolean res = profileControl(process, start, path, fd);
+ boolean res = profileControl(process, start, path, fd, profileType);
reply.writeNoException();
reply.writeInt(res ? 1 : 0);
return true;
@@ -2888,13 +2889,14 @@
}
public boolean profileControl(String process, boolean start,
- String path, ParcelFileDescriptor fd) throws RemoteException
+ String path, ParcelFileDescriptor fd, int profileType) throws RemoteException
{
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeString(process);
data.writeInt(start ? 1 : 0);
+ data.writeInt(profileType);
data.writeString(path);
if (fd != null) {
data.writeInt(1);
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index f6cd866..9bbbd6c 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -676,11 +676,12 @@
queueOrSendMessage(H.ACTIVITY_CONFIGURATION_CHANGED, token);
}
- public void profilerControl(boolean start, String path, ParcelFileDescriptor fd) {
+ public void profilerControl(boolean start, String path, ParcelFileDescriptor fd,
+ int profileType) {
ProfilerControlData pcd = new ProfilerControlData();
pcd.path = path;
pcd.fd = fd;
- queueOrSendMessage(H.PROFILER_CONTROL, pcd, start ? 1 : 0);
+ queueOrSendMessage(H.PROFILER_CONTROL, pcd, start ? 1 : 0, profileType);
}
public void dumpHeap(boolean managed, String path, ParcelFileDescriptor fd) {
@@ -954,7 +955,7 @@
}
public void scheduleTrimMemory(int level) {
- queueOrSendMessage(H.TRIM_MEMORY, level);
+ queueOrSendMessage(H.TRIM_MEMORY, null, level);
}
}
@@ -1148,7 +1149,7 @@
handleActivityConfigurationChanged((IBinder)msg.obj);
break;
case PROFILER_CONTROL:
- handleProfilerControl(msg.arg1 != 0, (ProfilerControlData)msg.obj);
+ handleProfilerControl(msg.arg1 != 0, (ProfilerControlData)msg.obj, msg.arg2);
break;
case CREATE_BACKUP_AGENT:
handleCreateBackupAgent((CreateBackupAgentData)msg.obj);
@@ -1184,8 +1185,10 @@
break;
case UPDATE_PACKAGE_COMPATIBILITY_INFO:
handleUpdatePackageCompatibilityInfo((UpdateCompatibilityData)msg.obj);
+ break;
case TRIM_MEMORY:
handleTrimMemory(msg.arg1);
+ break;
}
if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + msg.what);
}
@@ -3469,11 +3472,18 @@
performConfigurationChanged(r.activity, mCompatConfiguration);
}
- final void handleProfilerControl(boolean start, ProfilerControlData pcd) {
+ final void handleProfilerControl(boolean start, ProfilerControlData pcd, int profileType) {
if (start) {
try {
- Debug.startMethodTracing(pcd.path, pcd.fd.getFileDescriptor(),
- 8 * 1024 * 1024, 0);
+ switch (profileType) {
+ case 1:
+ ViewDebug.startLooperProfiling(pcd.path, pcd.fd.getFileDescriptor());
+ break;
+ default:
+ Debug.startMethodTracing(pcd.path, pcd.fd.getFileDescriptor(),
+ 8 * 1024 * 1024, 0);
+ break;
+ }
} catch (RuntimeException e) {
Slog.w(TAG, "Profiling failed on path " + pcd.path
+ " -- can the process access this path?");
@@ -3485,7 +3495,15 @@
}
}
} else {
- Debug.stopMethodTracing();
+ switch (profileType) {
+ case 1:
+ ViewDebug.stopLooperProfiling();
+ break;
+ default:
+ Debug.stopMethodTracing();
+ break;
+
+ }
}
}
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index 942f245..9a5b527 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -376,10 +376,11 @@
{
data.enforceInterface(IApplicationThread.descriptor);
boolean start = data.readInt() != 0;
+ int profileType = data.readInt();
String path = data.readString();
ParcelFileDescriptor fd = data.readInt() != 0
? data.readFileDescriptor() : null;
- profilerControl(start, path, fd);
+ profilerControl(start, path, fd, profileType);
return true;
}
@@ -936,10 +937,11 @@
}
public void profilerControl(boolean start, String path,
- ParcelFileDescriptor fd) throws RemoteException {
+ ParcelFileDescriptor fd, int profileType) throws RemoteException {
Parcel data = Parcel.obtain();
data.writeInterfaceToken(IApplicationThread.descriptor);
data.writeInt(start ? 1 : 0);
+ data.writeInt(profileType);
data.writeString(path);
if (fd != null) {
data.writeInt(1);
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index d2323e7..6289730 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -83,6 +83,7 @@
import android.view.WindowManagerImpl;
import android.view.accessibility.AccessibilityManager;
import android.view.inputmethod.InputMethodManager;
+import android.view.textservice.TextServicesManager;
import android.accounts.AccountManager;
import android.accounts.IAccountManager;
import android.app.admin.DevicePolicyManager;
@@ -320,6 +321,11 @@
return InputMethodManager.getInstance(ctx);
}});
+ registerService(TEXT_SERVICES_MANAGER_SERVICE, new ServiceFetcher() {
+ public Object createService(ContextImpl ctx) {
+ return TextServicesManager.getInstance();
+ }});
+
registerService(KEYGUARD_SERVICE, new ServiceFetcher() {
public Object getService(ContextImpl ctx) {
// TODO: why isn't this caching it? It wasn't
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index c82c9ec..789d3a6 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -1695,6 +1695,7 @@
public void dispatchDestroy() {
mDestroyed = true;
+ execPendingActions();
moveToState(Fragment.INITIALIZING, false);
mActivity = null;
}
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 93c821c..64d77e8 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -284,7 +284,7 @@
// Turn on/off profiling in a particular process.
public boolean profileControl(String process, boolean start,
- String path, ParcelFileDescriptor fd) throws RemoteException;
+ String path, ParcelFileDescriptor fd, int profileType) throws RemoteException;
public boolean shutdown(int timeout) throws RemoteException;
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index 9de0bf4..d0607d0 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -105,7 +105,7 @@
throws RemoteException;
void scheduleLowMemory() throws RemoteException;
void scheduleActivityConfigurationChanged(IBinder token) throws RemoteException;
- void profilerControl(boolean start, String path, ParcelFileDescriptor fd)
+ void profilerControl(boolean start, String path, ParcelFileDescriptor fd, int profileType)
throws RemoteException;
void dumpHeap(boolean managed, String path, ParcelFileDescriptor fd)
throws RemoteException;
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index 019652c..1ef99a1 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -388,6 +388,10 @@
TypedValue.complexToDimensionPixelSize(info.minWidth, mDisplayMetrics);
info.minHeight =
TypedValue.complexToDimensionPixelSize(info.minHeight, mDisplayMetrics);
+ info.minResizeWidth =
+ TypedValue.complexToDimensionPixelSize(info.minResizeWidth, mDisplayMetrics);
+ info.minResizeHeight =
+ TypedValue.complexToDimensionPixelSize(info.minResizeHeight, mDisplayMetrics);
}
return providers;
}
@@ -411,6 +415,10 @@
TypedValue.complexToDimensionPixelSize(info.minWidth, mDisplayMetrics);
info.minHeight =
TypedValue.complexToDimensionPixelSize(info.minHeight, mDisplayMetrics);
+ info.minResizeWidth =
+ TypedValue.complexToDimensionPixelSize(info.minResizeWidth, mDisplayMetrics);
+ info.minResizeHeight =
+ TypedValue.complexToDimensionPixelSize(info.minResizeHeight, mDisplayMetrics);
}
return info;
}
diff --git a/core/java/android/appwidget/AppWidgetProviderInfo.java b/core/java/android/appwidget/AppWidgetProviderInfo.java
index b8c5b02..9c352d5 100644
--- a/core/java/android/appwidget/AppWidgetProviderInfo.java
+++ b/core/java/android/appwidget/AppWidgetProviderInfo.java
@@ -187,6 +187,8 @@
}
this.minWidth = in.readInt();
this.minHeight = in.readInt();
+ this.minResizeWidth = in.readInt();
+ this.minResizeHeight = in.readInt();
this.updatePeriodMillis = in.readInt();
this.initialLayout = in.readInt();
if (0 != in.readInt()) {
@@ -208,6 +210,8 @@
}
out.writeInt(this.minWidth);
out.writeInt(this.minHeight);
+ out.writeInt(this.minResizeWidth);
+ out.writeInt(this.minResizeHeight);
out.writeInt(this.updatePeriodMillis);
out.writeInt(this.initialLayout);
if (this.configure != null) {
diff --git a/core/java/android/content/ComponentCallbacks.java b/core/java/android/content/ComponentCallbacks.java
index 92b98fd..37cc141 100644
--- a/core/java/android/content/ComponentCallbacks.java
+++ b/core/java/android/content/ComponentCallbacks.java
@@ -56,11 +56,8 @@
static final int TRIM_MEMORY_COMPLETE = 80;
/** @hide */
- static final int TRIM_MEMORY_MODERATE = 60;
+ static final int TRIM_MEMORY_MODERATE = 50;
/** @hide */
- static final int TRIM_MEMORY_BACKGROUND = 40;
-
- /** @hide */
- static final int TRIM_MEMORY_INVISIBLE = 20;
+ static final int TRIM_MEMORY_BACKGROUND = 20;
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index fed6d81..0a2253c8 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1612,6 +1612,15 @@
/**
* Use with {@link #getSystemService} to retrieve a
+ * {@link android.view.textservice.TextServicesManager} for accessing
+ * text services.
+ *
+ * @see #getSystemService
+ */
+ public static final String TEXT_SERVICES_MANAGER_SERVICE = "textservices";
+
+ /**
+ * Use with {@link #getSystemService} to retrieve a
* {@link android.appwidget.AppWidgetManager} for accessing AppWidgets.
*
* @hide
diff --git a/core/java/android/inputmethodservice/ExtractEditLayout.java b/core/java/android/inputmethodservice/ExtractEditLayout.java
index eafff49..5cfa998 100644
--- a/core/java/android/inputmethodservice/ExtractEditLayout.java
+++ b/core/java/android/inputmethodservice/ExtractEditLayout.java
@@ -172,7 +172,10 @@
@Override
public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
- return mCallback.onActionItemClicked(this, item);
+ if (mCallback != null) {
+ return mCallback.onActionItemClicked(this, item);
+ }
+ return false;
}
@Override
diff --git a/core/java/android/net/EthernetDataTracker.java b/core/java/android/net/EthernetDataTracker.java
index a866436..b035c51 100644
--- a/core/java/android/net/EthernetDataTracker.java
+++ b/core/java/android/net/EthernetDataTracker.java
@@ -103,6 +103,10 @@
public void interfaceRemoved(String iface) {
mTracker.interfaceRemoved(iface);
}
+
+ public void limitReached(String limitName, String iface) {
+ // Ignored.
+ }
}
private EthernetDataTracker() {
diff --git a/core/java/android/net/INetworkManagementEventObserver.aidl b/core/java/android/net/INetworkManagementEventObserver.aidl
index 4436e6e..a97f203 100644
--- a/core/java/android/net/INetworkManagementEventObserver.aidl
+++ b/core/java/android/net/INetworkManagementEventObserver.aidl
@@ -52,4 +52,14 @@
* @param iface The interface.
*/
void interfaceRemoved(String iface);
+
+ /**
+ * A networking quota limit has been reached. The quota might not
+ * be specific to an interface.
+ *
+ * @param limitName The name of the limit that triggered.
+ * @param iface The interface on which the limit was detected.
+ */
+ void limitReached(String limitName, String iface);
+
}
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index 9826bec..132f3ba 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -58,8 +58,8 @@
private ProxyProperties mHttpProxy;
public static class CompareResult<T> {
- public ArrayList<T> removed = new ArrayList<T>();
- public ArrayList<T> added = new ArrayList<T>();
+ public Collection<T> removed = new ArrayList<T>();
+ public Collection<T> added = new ArrayList<T>();
@Override
public String toString() {
diff --git a/core/java/android/net/VpnBuilder.java b/core/java/android/net/VpnBuilder.java
new file mode 100644
index 0000000..4582523
--- /dev/null
+++ b/core/java/android/net/VpnBuilder.java
@@ -0,0 +1,413 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.app.Activity;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+
+import com.android.internal.net.VpnConfig;
+
+import java.net.InetAddress;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.DatagramSocket;
+import java.net.Socket;
+import java.util.ArrayList;
+
+/**
+ * VpnBuilder is a framework which enables applications to build their
+ * own VPN solutions. In general, it creates a virtual network interface,
+ * configures addresses and routing rules, and returns a file descriptor
+ * to the application. Each read from the descriptor retrieves an outgoing
+ * packet which was routed to the interface. Each write to the descriptor
+ * injects an incoming packet just like it was received from the interface.
+ * The framework is running on Internet Protocol (IP), so packets are
+ * always started with IP headers. The application then completes a VPN
+ * connection by processing and exchanging packets with a remote server
+ * over a secured tunnel.
+ *
+ * <p>Letting applications intercept packets raises huge security concerns.
+ * Besides, a VPN application can easily break the network, and two of them
+ * may conflict with each other. The framework takes several actions to
+ * address these issues. Here are some key points:
+ * <ul>
+ * <li>User action is required to create a VPN connection.</li>
+ * <li>There can be only one VPN connection running at the same time. The
+ * existing interface is deactivated when a new one is created.</li>
+ * <li>A system-managed notification is shown during the lifetime of a
+ * VPN connection.</li>
+ * <li>A system-managed dialog gives the information of the current VPN
+ * connection. It also provides a button to disconnect.</li>
+ * <li>The network is restored automatically when the file descriptor is
+ * closed. It also covers the cases when a VPN application is crashed
+ * or killed by the system.</li>
+ * </ul>
+ *
+ * <p>There are two primary methods in this class: {@link #prepare} and
+ * {@link #establish}. The former deals with the user action and stops
+ * the existing VPN connection created by another application. The latter
+ * creates a VPN interface using the parameters supplied to this builder.
+ * An application must call {@link #prepare} to grant the right to create
+ * an interface, and it can be revoked at any time by another application.
+ * The application got revoked is notified by an {@link #ACTION_VPN_REVOKED}
+ * broadcast. Here are the general steps to create a VPN connection:
+ * <ol>
+ * <li>When the user press the button to connect, call {@link #prepare}
+ * and launch the intent if necessary.</li>
+ * <li>Register a receiver for {@link #ACTION_VPN_REVOKED} broadcasts.
+ * <li>Connect to the remote server and negotiate the network parameters
+ * of the VPN connection.</li>
+ * <li>Use those parameters to configure a VpnBuilder and create a VPN
+ * interface by calling {@link #establish}.</li>
+ * <li>Start processing packets between the returned file descriptor and
+ * the VPN tunnel.</li>
+ * <li>When an {@link #ACTION_VPN_REVOKED} broadcast is received, the
+ * interface is already deactivated by the framework. Close the file
+ * descriptor and shut down the VPN tunnel gracefully.
+ * </ol>
+ * Methods in this class can be used in activities and services. However,
+ * the intent returned from {@link #prepare} must be launched from an
+ * activity. The broadcast receiver can be registered at any time, but doing
+ * it before calling {@link #establish} effectively avoids race conditions.
+ *
+ * <p class="note">Using this class requires
+ * {@link android.Manifest.permission#VPN} permission.
+ * @hide
+ */
+public class VpnBuilder {
+
+ /**
+ * Broadcast intent action indicating that the VPN application has been
+ * revoked. This can be only received by the target application on the
+ * receiver explicitly registered using {@link Context#registerReceiver}.
+ *
+ * <p>This is a protected intent that can only be sent by the system.
+ */
+ public static final String ACTION_VPN_REVOKED = VpnConfig.ACTION_VPN_REVOKED;
+
+ /**
+ * Use IConnectivityManager instead since those methods are hidden and
+ * not available in ConnectivityManager.
+ */
+ private static IConnectivityManager getService() {
+ return IConnectivityManager.Stub.asInterface(
+ ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
+ }
+
+ /**
+ * Prepare to establish a VPN connection. This method returns {@code null}
+ * if the VPN application is already prepared. Otherwise, it returns an
+ * {@link Intent} to a system activity. The application should launch the
+ * activity using {@link Activity#startActivityForResult} to get itself
+ * prepared. The activity may pop up a dialog to require user action, and
+ * the result will come back to the application through its
+ * {@link Activity#onActivityResult}. The application becomes prepared if
+ * the result is {@link Activity#RESULT_OK}, and it is granted to create a
+ * VPN interface by calling {@link #establish}.
+ *
+ * <p>Only one application can be granted at the same time. The right
+ * is revoked when another application is granted. The application
+ * losing the right will be notified by an {@link #ACTION_VPN_REVOKED}
+ * broadcast, and its VPN interface will be deactivated by the system.
+ * The application should then notify the remote server and disconnect
+ * gracefully. Unless the application becomes prepared again, subsequent
+ * calls to {@link #establish} will return {@code null}.
+ *
+ * @see #establish
+ * @see #ACTION_VPN_REVOKED
+ */
+ public static Intent prepare(Context context) {
+ try {
+ if (getService().prepareVpn(context.getPackageName(), null)) {
+ return null;
+ }
+ } catch (RemoteException e) {
+ // ignore
+ }
+ return VpnConfig.getIntentForConfirmation();
+ }
+
+ private VpnConfig mConfig = new VpnConfig();
+ private StringBuilder mAddresses = new StringBuilder();
+ private StringBuilder mRoutes = new StringBuilder();
+
+ /**
+ * Set the name of this session. It will be displayed in system-managed
+ * dialogs and notifications. This is recommended not required.
+ */
+ public VpnBuilder setSession(String session) {
+ mConfig.session = session;
+ return this;
+ }
+
+ /**
+ * Set the {@link PendingIntent} to an activity for users to configure
+ * the VPN connection. If it is not set, the button to configure will
+ * not be shown in system-managed dialogs.
+ */
+ public VpnBuilder setConfigureIntent(PendingIntent intent) {
+ mConfig.configureIntent = intent;
+ return this;
+ }
+
+ /**
+ * Set the maximum transmission unit (MTU) of the VPN interface. If it
+ * is not set, the default value in the operating system will be used.
+ *
+ * @throws IllegalArgumentException if the value is not positive.
+ */
+ public VpnBuilder setMtu(int mtu) {
+ if (mtu <= 0) {
+ throw new IllegalArgumentException("Bad mtu");
+ }
+ mConfig.mtu = mtu;
+ return this;
+ }
+
+ /**
+ * Private method to validate address and prefixLength.
+ */
+ private static void check(InetAddress address, int prefixLength) {
+ if (address.isLoopbackAddress()) {
+ throw new IllegalArgumentException("Bad address");
+ }
+ if (address instanceof Inet4Address) {
+ if (prefixLength < 0 || prefixLength > 32) {
+ throw new IllegalArgumentException("Bad prefixLength");
+ }
+ } else if (address instanceof Inet6Address) {
+ if (prefixLength < 0 || prefixLength > 128) {
+ throw new IllegalArgumentException("Bad prefixLength");
+ }
+ } else {
+ throw new IllegalArgumentException("Unsupported family");
+ }
+ }
+
+ /**
+ * Convenience method to add a network address to the VPN interface
+ * using a numeric address string. See {@link InetAddress} for the
+ * definitions of numeric address formats.
+ *
+ * @throws IllegalArgumentException if the address is invalid.
+ * @see #addAddress(InetAddress, int)
+ */
+ public VpnBuilder addAddress(String address, int prefixLength) {
+ return addAddress(InetAddress.parseNumericAddress(address), prefixLength);
+ }
+
+ /**
+ * Add a network address to the VPN interface. Both IPv4 and IPv6
+ * addresses are supported. At least one address must be set before
+ * calling {@link #establish}.
+ *
+ * @throws IllegalArgumentException if the address is invalid.
+ */
+ public VpnBuilder addAddress(InetAddress address, int prefixLength) {
+ check(address, prefixLength);
+
+ if (address.isAnyLocalAddress()) {
+ throw new IllegalArgumentException("Bad address");
+ }
+
+ mAddresses.append(String.format(" %s/%d", address.getHostAddress(), prefixLength));
+ return this;
+ }
+
+ /**
+ * Convenience method to add a network route to the VPN interface
+ * using a numeric address string. See {@link InetAddress} for the
+ * definitions of numeric address formats.
+ *
+ * @see #addRoute(InetAddress, int)
+ * @throws IllegalArgumentException if the route is invalid.
+ */
+ public VpnBuilder addRoute(String address, int prefixLength) {
+ return addRoute(InetAddress.parseNumericAddress(address), prefixLength);
+ }
+
+ /**
+ * Add a network route to the VPN interface. Both IPv4 and IPv6
+ * routes are supported.
+ *
+ * @throws IllegalArgumentException if the route is invalid.
+ */
+ public VpnBuilder addRoute(InetAddress address, int prefixLength) {
+ check(address, prefixLength);
+
+ int offset = prefixLength / 8;
+ byte[] bytes = address.getAddress();
+ if (offset < bytes.length) {
+ if ((byte)(bytes[offset] << (prefixLength % 8)) != 0) {
+ throw new IllegalArgumentException("Bad address");
+ }
+ while (++offset < bytes.length) {
+ if (bytes[offset] != 0) {
+ throw new IllegalArgumentException("Bad address");
+ }
+ }
+ }
+
+ mRoutes.append(String.format(" %s/%d", address.getHostAddress(), prefixLength));
+ return this;
+ }
+
+ /**
+ * Convenience method to add a DNS server to the VPN connection
+ * using a numeric address string. See {@link InetAddress} for the
+ * definitions of numeric address formats.
+ *
+ * @throws IllegalArgumentException if the address is invalid.
+ * @see #addDnsServer(InetAddress)
+ */
+ public VpnBuilder addDnsServer(String address) {
+ return addDnsServer(InetAddress.parseNumericAddress(address));
+ }
+
+ /**
+ * Add a DNS server to the VPN connection. Both IPv4 and IPv6
+ * addresses are supported. If none is set, the DNS servers of
+ * the default network will be used.
+ *
+ * @throws IllegalArgumentException if the address is invalid.
+ */
+ public VpnBuilder addDnsServer(InetAddress address) {
+ if (address.isLoopbackAddress() || address.isAnyLocalAddress()) {
+ throw new IllegalArgumentException("Bad address");
+ }
+ if (mConfig.dnsServers == null) {
+ mConfig.dnsServers = new ArrayList<String>();
+ }
+ mConfig.dnsServers.add(address.getHostAddress());
+ return this;
+ }
+
+ /**
+ * Add a search domain to the DNS resolver.
+ */
+ public VpnBuilder addSearchDomain(String domain) {
+ if (mConfig.searchDomains == null) {
+ mConfig.searchDomains = new ArrayList<String>();
+ }
+ mConfig.searchDomains.add(domain);
+ return this;
+ }
+
+ /**
+ * Create a VPN interface using the parameters supplied to this builder.
+ * The interface works on IP packets, and a file descriptor is returned
+ * for the application to access them. Each read retrieves an outgoing
+ * packet which was routed to the interface. Each write injects an
+ * incoming packet just like it was received from the interface. The file
+ * descriptor is put into non-blocking mode by default to avoid blocking
+ * Java threads. To use the file descriptor completely in native space,
+ * see {@link ParcelFileDescriptor#detachFd()}. The application MUST
+ * close the file descriptor when the VPN connection is terminated. The
+ * VPN interface will be removed and the network will be restored by the
+ * framework automatically.
+ *
+ * <p>To avoid conflicts, there can be only one active VPN interface at
+ * the same time. Usually network parameters are never changed during the
+ * lifetime of a VPN connection. It is also common for an application to
+ * create a new file descriptor after closing the previous one. However,
+ * it is rare but not impossible to have two interfaces while performing a
+ * seamless handover. In this case, the old interface will be deactivated
+ * when the new one is configured successfully. Both file descriptors are
+ * valid but now outgoing packets will be routed to the new interface.
+ * Therefore, after draining the old file descriptor, the application MUST
+ * close it and start using the new file descriptor. If the new interface
+ * cannot be created, the existing interface and its file descriptor remain
+ * untouched.
+ *
+ * <p>An exception will be thrown if the interface cannot be created for
+ * any reason. However, this method returns {@code null} if the application
+ * is not prepared or is revoked by another application. This helps solve
+ * possible race conditions while handling {@link #ACTION_VPN_REVOKED}
+ * broadcasts.
+ *
+ * @return {@link ParcelFileDescriptor} of the VPN interface, or
+ * {@code null} if the application is not prepared.
+ * @throws IllegalArgumentException if a parameter is not accepted by the
+ * operating system.
+ * @throws IllegalStateException if a parameter cannot be applied by the
+ * operating system.
+ * @see #prepare
+ */
+ public ParcelFileDescriptor establish() {
+ mConfig.addresses = mAddresses.toString();
+ mConfig.routes = mRoutes.toString();
+
+ try {
+ return getService().establishVpn(mConfig);
+ } catch (RemoteException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ /**
+ * Protect a socket from VPN connections. The socket will be bound to the
+ * current default network interface, so its traffic will not be forwarded
+ * through VPN. This method is useful if some connections need to be kept
+ * outside of VPN. For example, a VPN tunnel should protect itself if its
+ * destination is covered by VPN routes. Otherwise its outgoing packets
+ * will be sent back to the VPN interface and cause an infinite loop.
+ *
+ * <p>The socket is NOT closed by this method.
+ *
+ * @return {@code true} on success.
+ */
+ public static boolean protect(int socket) {
+ ParcelFileDescriptor dup = null;
+ try {
+ dup = ParcelFileDescriptor.fromFd(socket);
+ return getService().protectVpn(dup);
+ } catch (Exception e) {
+ return false;
+ } finally {
+ try {
+ dup.close();
+ } catch (Exception e) {
+ // ignore
+ }
+ }
+ }
+
+ /**
+ * Protect a {@link Socket} from VPN connections.
+ *
+ * @return {@code true} on success.
+ * @see #protect(int)
+ */
+ public static boolean protect(Socket socket) {
+ return protect(socket.getFileDescriptor$().getInt$());
+ }
+
+ /**
+ * Protect a {@link DatagramSocket} from VPN connections.
+ *
+ * @return {@code true} on success.
+ * @see #protect(int)
+ */
+ public static boolean protect(DatagramSocket socket) {
+ return protect(socket.getFileDescriptor$().getInt$());
+ }
+}
diff --git a/core/java/android/os/Handler.java b/core/java/android/os/Handler.java
index cd39d5c..bc37244 100644
--- a/core/java/android/os/Handler.java
+++ b/core/java/android/os/Handler.java
@@ -361,7 +361,8 @@
/**
* Remove any pending posts of Runnable <var>r</var> with Object
- * <var>token</var> that are in the message queue.
+ * <var>token</var> that are in the message queue. If <var>token</var> is null,
+ * all callbacks will be removed.
*/
public final void removeCallbacks(Runnable r, Object token)
{
@@ -517,7 +518,8 @@
/**
* Remove any pending posts of messages with code 'what' and whose obj is
- * 'object' that are in the message queue.
+ * 'object' that are in the message queue. If <var>token</var> is null,
+ * all messages will be removed.
*/
public final void removeMessages(int what, Object object) {
mQueue.removeMessages(this, what, object, true);
@@ -525,7 +527,8 @@
/**
* Remove any pending posts of callbacks and sent messages whose
- * <var>obj</var> is <var>token</var>.
+ * <var>obj</var> is <var>token</var>. If <var>token</var> is null,
+ * all callbacks and messages will be removed.
*/
public final void removeCallbacksAndMessages(Object token) {
mQueue.removeCallbacksAndMessages(this, token);
diff --git a/core/java/android/preference/PreferenceActivity.java b/core/java/android/preference/PreferenceActivity.java
index c90de17..78c9010 100644
--- a/core/java/android/preference/PreferenceActivity.java
+++ b/core/java/android/preference/PreferenceActivity.java
@@ -563,6 +563,12 @@
// Single pane, showing just a prefs fragment.
findViewById(com.android.internal.R.id.headers).setVisibility(View.GONE);
mPrefsContainer.setVisibility(View.VISIBLE);
+ if (initialTitle != 0) {
+ CharSequence initialTitleStr = getText(initialTitle);
+ CharSequence initialShortTitleStr = initialShortTitle != 0
+ ? getText(initialShortTitle) : null;
+ showBreadCrumbs(initialTitleStr, initialShortTitleStr);
+ }
} else if (mHeaders.size() > 0) {
setListAdapter(new HeaderAdapter(this, mHeaders));
if (!mSinglePane) {
@@ -1093,6 +1099,10 @@
} else {
getListView().clearChoices();
}
+ showBreadCrumbs(header);
+ }
+
+ void showBreadCrumbs(Header header) {
if (header != null) {
CharSequence title = header.getBreadCrumbTitle(getResources());
if (title == null) title = header.getTitle(getResources());
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index c299891..4a719ec 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -1439,6 +1439,13 @@
CONTENT_URI, "strequent");
/**
+ * The content:// style URI for showing frequently contacted person listing.
+ * @hide
+ */
+ public static final Uri CONTENT_FREQUENT_URI = Uri.withAppendedPath(
+ CONTENT_URI, "frequent");
+
+ /**
* The content:// style URI used for "type-to-filter" functionality on the
* {@link #CONTENT_STREQUENT_URI} URI. The filter string will be used to match
* various parts of the contact name. The filter argument should be passed
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index cb87e94..1cd46de 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -16,8 +16,6 @@
package android.provider;
-
-
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.app.SearchManager;
@@ -48,7 +46,6 @@
import java.util.HashMap;
import java.util.HashSet;
-
/**
* The Settings provider contains global system-level device preferences.
*/
@@ -3737,6 +3734,15 @@
*/
public static final String VOICE_RECOGNITION_SERVICE = "voice_recognition_service";
+
+ /**
+ * The {@link ComponentName} string of the service to be used as the spell checker
+ * service which is one of the services managed by the text service manager.
+ *
+ * @hide
+ */
+ public static final String SPELL_CHECKER_SERVICE = "spell_checker_service";
+
/**
* What happens when the user presses the Power button while in-call
* and the screen is on.<br/>
diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java
index ab569a1..d68d8ba 100755
--- a/core/java/android/server/BluetoothService.java
+++ b/core/java/android/server/BluetoothService.java
@@ -527,10 +527,19 @@
break;
case MESSAGE_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY:
address = (String)msg.obj;
- if (address != null) {
+ if (address == null) return;
+ int attempt = mBondState.getAttempt(address);
+
+ // Try only if attemps are in progress and cap it 2 attempts
+ // The 2 attempts cap is a fail safe if the stack returns
+ // an incorrect error code for bonding failures and if the pin
+ // is entered wrongly twice we should abort.
+ if (attempt > 0 && attempt <= 2) {
+ mBondState.attempt(address);
createBond(address);
return;
}
+ if (attempt > 0) mBondState.clearPinAttempts(address);
break;
}
}
@@ -741,7 +750,6 @@
BluetoothDevice.BOND_NONE, result);
return;
}
- mBondState.attempt(address);
}
/*package*/ BluetoothDevice getRemoteDevice(String address) {
diff --git a/core/java/android/service/textservice/SpellCheckerService.java b/core/java/android/service/textservice/SpellCheckerService.java
new file mode 100644
index 0000000..6ac99ca
--- /dev/null
+++ b/core/java/android/service/textservice/SpellCheckerService.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2011 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.service.textservice;
+
+import com.android.internal.textservice.ISpellCheckerService;
+import com.android.internal.textservice.ISpellCheckerSession;
+import com.android.internal.textservice.ISpellCheckerSessionListener;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.view.textservice.SuggestionsInfo;
+import android.view.textservice.TextInfo;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * SpellCheckerService provides an abstract base class for a spell checker.
+ * This class combines a service to the system with the spell checker service interface that
+ * spell checker must implement.
+ */
+public abstract class SpellCheckerService extends Service {
+ private static final String TAG = SpellCheckerService.class.getSimpleName();
+ public static final String SERVICE_INTERFACE = SpellCheckerService.class.getName();
+
+ private final SpellCheckerServiceBinder mBinder = new SpellCheckerServiceBinder(this);
+
+ /**
+ * Get suggestions for specified text in TextInfo.
+ * This function will run on the incoming IPC thread. So, this is not called on the main thread,
+ * but will be called in series on another thread.
+ * @param textInfo the text metadata
+ * @param suggestionsLimit the number of limit of suggestions returned
+ * @param locale the locale for getting suggestions
+ * @return SuggestionInfo which contains suggestions for textInfo
+ */
+ public abstract SuggestionsInfo getSuggestions(
+ TextInfo textInfo, int suggestionsLimit, String locale);
+
+ /**
+ * A batch process of onGetSuggestions.
+ * This function will run on the incoming IPC thread. So, this is not called on the main thread,
+ * but will be called in series on another thread.
+ * @param textInfos an array of the text metadata
+ * @param locale the locale for getting suggestions
+ * @param suggestionsLimit the number of limit of suggestions returned
+ * @param sequentialWords true if textInfos can be treated as sequential words.
+ * @return an array of SuggestionInfo of onGetSuggestions
+ */
+ public SuggestionsInfo[] getSuggestionsMultiple(
+ TextInfo[] textInfos, String locale, int suggestionsLimit, boolean sequentialWords) {
+ final int length = textInfos.length;
+ final SuggestionsInfo[] retval = new SuggestionsInfo[length];
+ for (int i = 0; i < length; ++i) {
+ retval[i] = getSuggestions(textInfos[i], suggestionsLimit, locale);
+ retval[i].setCookieAndSequence(textInfos[i].getCookie(), textInfos[i].getSequence());
+ }
+ return retval;
+ }
+
+ /**
+ * Request to abort all tasks executed in SpellChecker.
+ * This function will run on the incoming IPC thread. So, this is not called on the main thread,
+ * but will be called in series on another thread.
+ */
+ public void cancel() {}
+
+ /**
+ * Implement to return the implementation of the internal spell checker
+ * service interface. Subclasses should not override.
+ */
+ @Override
+ public final IBinder onBind(final Intent intent) {
+ return mBinder;
+ }
+
+ private static class SpellCheckerSessionImpl extends ISpellCheckerSession.Stub {
+ private final WeakReference<SpellCheckerService> mInternalServiceRef;
+ private final String mLocale;
+ private final ISpellCheckerSessionListener mListener;
+
+ public SpellCheckerSessionImpl(
+ SpellCheckerService service, String locale, ISpellCheckerSessionListener listener) {
+ mInternalServiceRef = new WeakReference<SpellCheckerService>(service);
+ mLocale = locale;
+ mListener = listener;
+ }
+
+ @Override
+ public void getSuggestionsMultiple(
+ TextInfo[] textInfos, int suggestionsLimit, boolean sequentialWords) {
+ final SpellCheckerService service = mInternalServiceRef.get();
+ if (service == null) return;
+ try {
+ mListener.onGetSuggestions(
+ service.getSuggestionsMultiple(textInfos, mLocale,
+ suggestionsLimit, sequentialWords));
+ } catch (RemoteException e) {
+ }
+ }
+
+ @Override
+ public void cancel() {
+ final SpellCheckerService service = mInternalServiceRef.get();
+ if (service == null) return;
+ service.cancel();
+ }
+ }
+
+ private static class SpellCheckerServiceBinder extends ISpellCheckerService.Stub {
+ private final WeakReference<SpellCheckerService> mInternalServiceRef;
+
+ public SpellCheckerServiceBinder(SpellCheckerService service) {
+ mInternalServiceRef = new WeakReference<SpellCheckerService>(service);
+ }
+
+ @Override
+ public ISpellCheckerSession getISpellCheckerSession(
+ String locale, ISpellCheckerSessionListener listener) {
+ final SpellCheckerService service = mInternalServiceRef.get();
+ if (service == null) return null;
+ return new SpellCheckerSessionImpl(service, locale, listener);
+ }
+ }
+}
diff --git a/core/java/android/service/textservice/SpellCheckerSession.java b/core/java/android/service/textservice/SpellCheckerSession.java
new file mode 100644
index 0000000..a575220
--- /dev/null
+++ b/core/java/android/service/textservice/SpellCheckerSession.java
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2011 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.service.textservice;
+
+import com.android.internal.textservice.ISpellCheckerSession;
+import com.android.internal.textservice.ISpellCheckerSessionListener;
+import com.android.internal.textservice.ITextServicesManager;
+import com.android.internal.textservice.ITextServicesSessionListener;
+
+import android.os.Handler;
+import android.os.Message;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.textservice.SuggestionsInfo;
+import android.view.textservice.TextInfo;
+
+import java.util.LinkedList;
+import java.util.Queue;
+
+/**
+ * The SpellCheckerSession interface provides the per client functionality of SpellCheckerService.
+ */
+public class SpellCheckerSession {
+ private static final String TAG = SpellCheckerSession.class.getSimpleName();
+ private static final boolean DBG = false;
+
+ private static final int MSG_ON_GET_SUGGESTION_MULTIPLE = 1;
+
+ private final InternalListener mInternalListener;
+ private final ITextServicesManager mTextServicesManager;
+ private final SpellCheckerSessionListenerImpl mSpellCheckerSessionListenerImpl;
+
+ private boolean mIsUsed;
+ private SpellCheckerSessionListener mSpellCheckerSessionListener;
+
+ /** Handler that will execute the main tasks */
+ private final Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_ON_GET_SUGGESTION_MULTIPLE:
+ handleOnGetSuggestionsMultiple((SuggestionsInfo[]) msg.obj);
+ break;
+ }
+ }
+ };
+
+ /**
+ * Constructor
+ * @hide
+ */
+ public SpellCheckerSession(ITextServicesManager tsm, SpellCheckerSessionListener listener) {
+ if (listener == null || tsm == null) {
+ throw new NullPointerException();
+ }
+ mSpellCheckerSessionListenerImpl = new SpellCheckerSessionListenerImpl(mHandler);
+ mInternalListener = new InternalListener();
+ mTextServicesManager = tsm;
+ mIsUsed = true;
+ mSpellCheckerSessionListener = listener;
+ }
+
+ /**
+ * @return true if the connection to a text service of this session is disconnected and not
+ * alive.
+ */
+ public boolean isSessionDisconnected() {
+ return mSpellCheckerSessionListenerImpl.isDisconnected();
+ }
+
+ /**
+ * Finish this session and allow TextServicesManagerService to disconnect the bound spell
+ * checker.
+ */
+ public void close() {
+ mIsUsed = false;
+ try {
+ mTextServicesManager.finishSpellCheckerService(mSpellCheckerSessionListenerImpl);
+ } catch (RemoteException e) {
+ // do nothing
+ }
+ }
+
+ /**
+ * Get candidate strings for a substring of the specified text.
+ * @param textInfo text metadata for a spell checker
+ * @param suggestionsLimit the number of limit of suggestions returned
+ */
+ public void getSuggestions(TextInfo textInfo, int suggestionsLimit) {
+ getSuggestions(new TextInfo[] {textInfo}, suggestionsLimit, false);
+ }
+
+ /**
+ * A batch process of getSuggestions
+ * @param textInfos an array of text metadata for a spell checker
+ * @param suggestionsLimit the number of limit of suggestions returned
+ * @param sequentialWords true if textInfos can be treated as sequential words.
+ */
+ public void getSuggestions(
+ TextInfo[] textInfos, int suggestionsLimit, boolean sequentialWords) {
+ // TODO: Handle multiple words suggestions by using WordBreakIterator
+ mSpellCheckerSessionListenerImpl.getSuggestionsMultiple(
+ textInfos, suggestionsLimit, sequentialWords);
+ }
+
+ private void handleOnGetSuggestionsMultiple(SuggestionsInfo[] suggestionInfos) {
+ mSpellCheckerSessionListener.onGetSuggestions(suggestionInfos);
+ }
+
+ private static class SpellCheckerSessionListenerImpl extends ISpellCheckerSessionListener.Stub {
+ private static final int TASK_CANCEL = 1;
+ private static final int TASK_GET_SUGGESTIONS_MULTIPLE = 2;
+ private final Queue<SpellCheckerParams> mPendingTasks =
+ new LinkedList<SpellCheckerParams>();
+ private final Handler mHandler;
+
+ private boolean mOpened;
+ private ISpellCheckerSession mISpellCheckerSession;
+
+ public SpellCheckerSessionListenerImpl(Handler handler) {
+ mOpened = false;
+ mHandler = handler;
+ }
+
+ private static class SpellCheckerParams {
+ public final int mWhat;
+ public final TextInfo[] mTextInfos;
+ public final int mSuggestionsLimit;
+ public final boolean mSequentialWords;
+ public SpellCheckerParams(int what, TextInfo[] textInfos, int suggestionsLimit,
+ boolean sequentialWords) {
+ mWhat = what;
+ mTextInfos = textInfos;
+ mSuggestionsLimit = suggestionsLimit;
+ mSequentialWords = sequentialWords;
+ }
+ }
+
+ private void processTask(SpellCheckerParams scp) {
+ switch (scp.mWhat) {
+ case TASK_CANCEL:
+ processCancel();
+ break;
+ case TASK_GET_SUGGESTIONS_MULTIPLE:
+ processGetSuggestionsMultiple(scp);
+ break;
+ }
+ }
+
+ public synchronized void onServiceConnected(ISpellCheckerSession session) {
+ mISpellCheckerSession = session;
+ mOpened = true;
+ if (DBG)
+ Log.d(TAG, "onServiceConnected - Success");
+ while (!mPendingTasks.isEmpty()) {
+ processTask(mPendingTasks.poll());
+ }
+ }
+
+ public void getSuggestionsMultiple(
+ TextInfo[] textInfos, int suggestionsLimit, boolean sequentialWords) {
+ processOrEnqueueTask(
+ new SpellCheckerParams(TASK_GET_SUGGESTIONS_MULTIPLE, textInfos,
+ suggestionsLimit, sequentialWords));
+ }
+
+ public boolean isDisconnected() {
+ return mOpened && mISpellCheckerSession == null;
+ }
+
+ public boolean checkOpenConnection() {
+ if (mISpellCheckerSession != null) {
+ return true;
+ }
+ Log.e(TAG, "not connected to the spellchecker service.");
+ return false;
+ }
+
+ private void processOrEnqueueTask(SpellCheckerParams scp) {
+ if (mISpellCheckerSession == null) {
+ mPendingTasks.offer(scp);
+ } else {
+ processTask(scp);
+ }
+ }
+
+ private void processCancel() {
+ if (!checkOpenConnection()) {
+ return;
+ }
+ try {
+ mISpellCheckerSession.cancel();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to cancel " + e);
+ }
+ }
+
+ private void processGetSuggestionsMultiple(SpellCheckerParams scp) {
+ if (!checkOpenConnection()) {
+ return;
+ }
+ try {
+ mISpellCheckerSession.getSuggestionsMultiple(
+ scp.mTextInfos, scp.mSuggestionsLimit, scp.mSequentialWords);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to get suggestions " + e);
+ }
+ }
+
+ @Override
+ public void onGetSuggestions(SuggestionsInfo[] results) {
+ mHandler.sendMessage(Message.obtain(mHandler, MSG_ON_GET_SUGGESTION_MULTIPLE, results));
+ }
+ }
+
+ /**
+ * Callback for getting results from text services
+ */
+ public interface SpellCheckerSessionListener {
+ /**
+ * Callback for "getSuggestions"
+ * @param results an array of results of getSuggestions
+ */
+ public void onGetSuggestions(SuggestionsInfo[] results);
+ }
+
+ private class InternalListener extends ITextServicesSessionListener.Stub {
+ @Override
+ public void onServiceConnected(ISpellCheckerSession session) {
+ mSpellCheckerSessionListenerImpl.onServiceConnected(session);
+ }
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ super.finalize();
+ if (mIsUsed) {
+ Log.e(TAG, "SpellCheckerSession was not finished properly." +
+ "You should call finishShession() when you finished to use a spell checker.");
+ close();
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public ITextServicesSessionListener getTextServicesSessionListener() {
+ return mInternalListener;
+ }
+
+ /**
+ * @hide
+ */
+ public ISpellCheckerSessionListener getSpellCheckerSessionListener() {
+ return mSpellCheckerSessionListenerImpl;
+ }
+}
diff --git a/core/java/android/util/JsonReader.java b/core/java/android/util/JsonReader.java
index f139372..f2a86c9 100644
--- a/core/java/android/util/JsonReader.java
+++ b/core/java/android/util/JsonReader.java
@@ -740,8 +740,8 @@
limit += total;
// if this is the first read, consume an optional byte order mark (BOM) if it exists
- if (bufferStartLine == 1 && bufferStartColumn == 1
- && limit > 0 && buffer[0] == '\ufeff') {
+ if (bufferStartLine == 1 && bufferStartColumn == 1
+ && limit > 0 && buffer[0] == '\ufeff') {
pos++;
bufferStartColumn--;
}
@@ -852,7 +852,7 @@
private boolean skipTo(String toFind) throws IOException {
outer:
- for (; pos + toFind.length() < limit || fillBuffer(toFind.length()); pos++) {
+ for (; pos + toFind.length() <= limit || fillBuffer(toFind.length()); pos++) {
for (int c = 0; c < toFind.length(); c++) {
if (buffer[pos + c] != toFind.charAt(c)) {
continue outer;
diff --git a/core/java/android/view/CollapsibleActionView.java b/core/java/android/view/CollapsibleActionView.java
new file mode 100644
index 0000000..ab2365e
--- /dev/null
+++ b/core/java/android/view/CollapsibleActionView.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2011 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.view;
+
+import android.view.MenuItem.OnActionExpandListener;
+
+/**
+ * When a {@link View} implements this interface it will receive callbacks
+ * when expanded or collapsed as an action view alongside the optional,
+ * app-specified callbacks to {@link OnActionExpandListener}.
+ *
+ * <p>See {@link MenuItem} for more information about action views.
+ * See {@link android.app.ActionBar} for more information about the action bar.
+ */
+public interface CollapsibleActionView {
+ /**
+ * Called when this view is expanded as an action view.
+ * See {@link MenuItem#expandActionView()}.
+ */
+ public void onActionViewExpanded();
+
+ /**
+ * Called when this view is collapsed as an action view.
+ * See {@link MenuItem#collapseActionView()}.
+ */
+ public void onActionViewCollapsed();
+}
diff --git a/core/java/android/view/DisplayList.java b/core/java/android/view/DisplayList.java
index 4484d59..f4c0249 100644
--- a/core/java/android/view/DisplayList.java
+++ b/core/java/android/view/DisplayList.java
@@ -41,15 +41,6 @@
abstract void end();
/**
- * Indicates whether this display list can be replayed or not.
- *
- * @return True if the display list can be replayed, false otherwise.
- *
- * @see android.view.HardwareCanvas#drawDisplayList(DisplayList)
- */
- abstract boolean isReady();
-
- /**
* Invalidates the display list, indicating that it should be repopulated
* with new drawing commands prior to being used again. Calling this method
* causes calls to {@link #isValid()} to return <code>false</code>.
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index 80244bb..d22fa6e 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -51,6 +51,7 @@
// The native renderer will be destroyed when this object dies.
// DO NOT overwrite this reference once it is set.
+ @SuppressWarnings("unused")
private CanvasFinalizer mFinalizer;
private int mWidth;
@@ -97,12 +98,8 @@
protected GLES20Canvas(boolean record, boolean translucent) {
mOpaque = !translucent;
- setupRenderer(record);
- }
-
- protected void setupRenderer(boolean record) {
if (record) {
- mRenderer = nGetDisplayListRenderer(mRenderer);
+ mRenderer = nCreateDisplayListRenderer();
} else {
mRenderer = nCreateRenderer();
}
@@ -114,43 +111,31 @@
if (mRenderer == 0) {
throw new IllegalStateException("Could not create GLES20Canvas renderer");
} else {
- mFinalizer = CanvasFinalizer.getFinalizer(mFinalizer, mRenderer);
+ mFinalizer = new CanvasFinalizer(mRenderer);
}
}
+ protected void resetDisplayListRenderer() {
+ nResetDisplayListRenderer(mRenderer);
+ }
+
private static native int nCreateRenderer();
private static native int nCreateLayerRenderer(int layer);
- private static native int nGetDisplayListRenderer(int renderer);
+ private static native int nCreateDisplayListRenderer();
+ private static native void nResetDisplayListRenderer(int renderer);
private static native void nDestroyRenderer(int renderer);
- private static class CanvasFinalizer {
- int mRenderer;
+ private static final class CanvasFinalizer {
+ private final int mRenderer;
- // Factory method returns new instance if old one is null, or old instance
- // otherwise, destroying native renderer along the way as necessary
- static CanvasFinalizer getFinalizer(CanvasFinalizer oldFinalizer, int renderer) {
- if (oldFinalizer == null) {
- return new CanvasFinalizer(renderer);
- }
- oldFinalizer.replaceNativeObject(renderer);
- return oldFinalizer;
- }
-
- private CanvasFinalizer(int renderer) {
+ public CanvasFinalizer(int renderer) {
mRenderer = renderer;
}
- private void replaceNativeObject(int newRenderer) {
- if (mRenderer != 0 && newRenderer != mRenderer) {
- nDestroyRenderer(mRenderer);
- }
- mRenderer = newRenderer;
- }
-
@Override
protected void finalize() throws Throwable {
try {
- replaceNativeObject(0);
+ nDestroyRenderer(mRenderer);
} finally {
super.finalize();
}
@@ -322,11 +307,11 @@
// Display list
///////////////////////////////////////////////////////////////////////////
- int getDisplayList() {
- return nGetDisplayList(mRenderer);
+ int getDisplayList(int displayList) {
+ return nGetDisplayList(mRenderer, displayList);
}
- private static native int nGetDisplayList(int renderer);
+ private static native int nGetDisplayList(int renderer, int displayList);
static void destroyDisplayList(int displayList) {
nDestroyDisplayList(displayList);
@@ -337,7 +322,7 @@
@Override
public boolean drawDisplayList(DisplayList displayList, int width, int height, Rect dirty) {
return nDrawDisplayList(mRenderer,
- ((GLES20DisplayList) displayList).mNativeDisplayList, width, height, dirty);
+ ((GLES20DisplayList) displayList).getNativeDisplayList(), width, height, dirty);
}
private static native boolean nDrawDisplayList(int renderer, int displayList,
@@ -345,7 +330,7 @@
@Override
void outputDisplayList(DisplayList displayList) {
- nOutputDisplayList(mRenderer, ((GLES20DisplayList) displayList).mNativeDisplayList);
+ nOutputDisplayList(mRenderer, ((GLES20DisplayList) displayList).getNativeDisplayList());
}
private static native void nOutputDisplayList(int renderer, int displayList);
diff --git a/core/java/android/view/GLES20DisplayList.java b/core/java/android/view/GLES20DisplayList.java
index aeff31f..9e649cea 100644
--- a/core/java/android/view/GLES20DisplayList.java
+++ b/core/java/android/view/GLES20DisplayList.java
@@ -16,52 +16,50 @@
package android.view;
-import java.lang.ref.WeakReference;
+import android.graphics.Bitmap;
+
+import java.util.ArrayList;
/**
* An implementation of display list for OpenGL ES 2.0.
*/
class GLES20DisplayList extends DisplayList {
- private GLES20Canvas mCanvas;
+ // These lists ensure that any Bitmaps recorded by a DisplayList are kept alive as long
+ // as the DisplayList is alive. The Bitmaps are populated by the GLES20RecordingCanvas.
+ final ArrayList<Bitmap> mBitmaps = new ArrayList<Bitmap>(5);
- private boolean mStarted = false;
- private boolean mRecorded = false;
- private boolean mValid = false;
-
- int mNativeDisplayList;
- WeakReference<View> hostView;
+ private GLES20RecordingCanvas mCanvas;
+ private boolean mValid;
// The native display list will be destroyed when this object dies.
// DO NOT overwrite this reference once it is set.
- @SuppressWarnings("unused")
private DisplayListFinalizer mFinalizer;
- public GLES20DisplayList(View view) {
- hostView = new WeakReference<View>(view);
+ int getNativeDisplayList() {
+ if (!mValid || mFinalizer == null) {
+ throw new IllegalStateException("The display list is not valid.");
+ }
+ return mFinalizer.mNativeDisplayList;
}
@Override
HardwareCanvas start() {
- if (mStarted) {
+ if (mCanvas != null) {
throw new IllegalStateException("Recording has already started");
}
- if (mCanvas != null) {
- ((GLES20RecordingCanvas) mCanvas).reset();
- } else {
- mCanvas = new GLES20RecordingCanvas(true);
- }
- mStarted = true;
- mRecorded = false;
- mValid = true;
-
+ mValid = false;
+ mCanvas = GLES20RecordingCanvas.obtain(this);
+ mCanvas.start();
return mCanvas;
}
@Override
void invalidate() {
- mStarted = false;
- mRecorded = false;
+ if (mCanvas != null) {
+ mCanvas.recycle();
+ mCanvas = null;
+ }
mValid = false;
}
@@ -73,48 +71,28 @@
@Override
void end() {
if (mCanvas != null) {
- mStarted = false;
- mRecorded = true;
-
- mNativeDisplayList = mCanvas.getDisplayList();
- mFinalizer = DisplayListFinalizer.getFinalizer(mFinalizer, mNativeDisplayList);
+ if (mFinalizer != null) {
+ mCanvas.end(mFinalizer.mNativeDisplayList);
+ } else {
+ mFinalizer = new DisplayListFinalizer(mCanvas.end(0));
+ }
+ mCanvas.recycle();
+ mCanvas = null;
+ mValid = true;
}
}
- @Override
- boolean isReady() {
- return !mStarted && mRecorded;
- }
-
private static class DisplayListFinalizer {
- int mNativeDisplayList;
+ final int mNativeDisplayList;
- // Factory method returns new instance if old one is null, or old instance
- // otherwise, destroying native display list along the way as necessary
- static DisplayListFinalizer getFinalizer(DisplayListFinalizer oldFinalizer,
- int nativeDisplayList) {
- if (oldFinalizer == null) {
- return new DisplayListFinalizer(nativeDisplayList);
- }
- oldFinalizer.replaceNativeObject(nativeDisplayList);
- return oldFinalizer;
- }
-
- private DisplayListFinalizer(int nativeDisplayList) {
+ public DisplayListFinalizer(int nativeDisplayList) {
mNativeDisplayList = nativeDisplayList;
}
- private void replaceNativeObject(int newNativeDisplayList) {
- if (mNativeDisplayList != 0 && mNativeDisplayList != newNativeDisplayList) {
- GLES20Canvas.destroyDisplayList(mNativeDisplayList);
- }
- mNativeDisplayList = newNativeDisplayList;
- }
-
@Override
protected void finalize() throws Throwable {
try {
- replaceNativeObject(0);
+ GLES20Canvas.destroyDisplayList(mNativeDisplayList);
} finally {
super.finalize();
}
diff --git a/core/java/android/view/GLES20RecordingCanvas.java b/core/java/android/view/GLES20RecordingCanvas.java
index ec94fe7..078222b 100644
--- a/core/java/android/view/GLES20RecordingCanvas.java
+++ b/core/java/android/view/GLES20RecordingCanvas.java
@@ -24,8 +24,10 @@
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
-
-import java.util.ArrayList;
+import android.util.Pool;
+import android.util.Poolable;
+import android.util.PoolableManager;
+import android.util.Pools;
/**
* An implementation of a GL canvas that records drawing operations.
@@ -33,62 +35,94 @@
* Bitmap objects that it draws, preventing the backing memory of Bitmaps from being freed while
* the DisplayList is still holding a native reference to the memory.
*/
-class GLES20RecordingCanvas extends GLES20Canvas {
- // These lists ensure that any Bitmaps recorded by a DisplayList are kept alive as long
- // as the DisplayList is alive.
- @SuppressWarnings({"MismatchedQueryAndUpdateOfCollection"})
- private final ArrayList<Bitmap> mBitmaps = new ArrayList<Bitmap>(5);
+class GLES20RecordingCanvas extends GLES20Canvas implements Poolable<GLES20RecordingCanvas> {
+ // The recording canvas pool should be large enough to handle a deeply nested
+ // view hierarchy because display lists are generated recursively.
+ private static final int POOL_LIMIT = 50;
- GLES20RecordingCanvas(boolean translucent) {
- super(true, translucent);
+ private static final Pool<GLES20RecordingCanvas> sPool = Pools.synchronizedPool(
+ Pools.finitePool(new PoolableManager<GLES20RecordingCanvas>() {
+ public GLES20RecordingCanvas newInstance() {
+ return new GLES20RecordingCanvas();
+ }
+ @Override
+ public void onAcquired(GLES20RecordingCanvas element) {
+ }
+ @Override
+ public void onReleased(GLES20RecordingCanvas element) {
+ }
+ }, POOL_LIMIT));
+
+ private GLES20RecordingCanvas mNextPoolable;
+ private boolean mIsPooled;
+
+ private GLES20DisplayList mDisplayList;
+
+ private GLES20RecordingCanvas() {
+ super(true /*record*/, true /*translucent*/);
+ }
+
+ static GLES20RecordingCanvas obtain(GLES20DisplayList displayList) {
+ GLES20RecordingCanvas canvas = sPool.acquire();
+ canvas.mDisplayList = displayList;
+ return canvas;
+ }
+
+ void recycle() {
+ mDisplayList = null;
+ resetDisplayListRenderer();
+ sPool.release(this);
+ }
+
+ void start() {
+ mDisplayList.mBitmaps.clear();
+ }
+
+ int end(int nativeDisplayList) {
+ return getDisplayList(nativeDisplayList);
}
private void recordShaderBitmap(Paint paint) {
if (paint != null) {
final Shader shader = paint.getShader();
if (shader instanceof BitmapShader) {
- mBitmaps.add(((BitmapShader) shader).mBitmap);
+ mDisplayList.mBitmaps.add(((BitmapShader) shader).mBitmap);
}
}
}
- void reset() {
- mBitmaps.clear();
- setupRenderer(true);
- }
-
@Override
public void drawPatch(Bitmap bitmap, byte[] chunks, RectF dst, Paint paint) {
super.drawPatch(bitmap, chunks, dst, paint);
- mBitmaps.add(bitmap);
+ mDisplayList.mBitmaps.add(bitmap);
// Shaders in the Paint are ignored when drawing a Bitmap
}
@Override
public void drawBitmap(Bitmap bitmap, float left, float top, Paint paint) {
super.drawBitmap(bitmap, left, top, paint);
- mBitmaps.add(bitmap);
+ mDisplayList.mBitmaps.add(bitmap);
// Shaders in the Paint are ignored when drawing a Bitmap
}
@Override
public void drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint) {
super.drawBitmap(bitmap, matrix, paint);
- mBitmaps.add(bitmap);
+ mDisplayList.mBitmaps.add(bitmap);
// Shaders in the Paint are ignored when drawing a Bitmap
}
@Override
public void drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) {
super.drawBitmap(bitmap, src, dst, paint);
- mBitmaps.add(bitmap);
+ mDisplayList.mBitmaps.add(bitmap);
// Shaders in the Paint are ignored when drawing a Bitmap
}
@Override
public void drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint) {
super.drawBitmap(bitmap, src, dst, paint);
- mBitmaps.add(bitmap);
+ mDisplayList.mBitmaps.add(bitmap);
// Shaders in the Paint are ignored when drawing a Bitmap
}
@@ -111,7 +145,7 @@
int vertOffset, int[] colors, int colorOffset, Paint paint) {
super.drawBitmapMesh(bitmap, meshWidth, meshHeight, verts, vertOffset, colors, colorOffset,
paint);
- mBitmaps.add(bitmap);
+ mDisplayList.mBitmaps.add(bitmap);
// Shaders in the Paint are ignored when drawing a Bitmap
}
@@ -270,4 +304,24 @@
colorOffset, indices, indexOffset, indexCount, paint);
recordShaderBitmap(paint);
}
+
+ @Override
+ public GLES20RecordingCanvas getNextPoolable() {
+ return mNextPoolable;
+ }
+
+ @Override
+ public void setNextPoolable(GLES20RecordingCanvas element) {
+ mNextPoolable = element;
+ }
+
+ @Override
+ public boolean isPooled() {
+ return mIsPooled;
+ }
+
+ @Override
+ public void setPooled(boolean isPooled) {
+ mIsPooled = isPooled;
+ }
}
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index b865b50..503b54b 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -189,7 +189,7 @@
*
* @return A new display list.
*/
- abstract DisplayList createDisplayList(View v);
+ abstract DisplayList createDisplayList();
/**
* Creates a new hardware layer. A hardware layer built by calling this
@@ -852,8 +852,8 @@
}
@Override
- DisplayList createDisplayList(View v) {
- return new GLES20DisplayList(v);
+ DisplayList createDisplayList() {
+ return new GLES20DisplayList();
}
@Override
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 0108ecf..ecb391d 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -9099,7 +9099,10 @@
mPrivateFlags2 |= LAYOUT_DIRECTION_RESOLVED;
}
- private void resolvePadding() {
+ /**
+ * @hide
+ */
+ protected void resolvePadding() {
// If the user specified the absolute padding (either with android:padding or
// android:paddingLeft/Top/Right/Bottom), use this padding, otherwise
// use the default padding or the padding from the background drawable
@@ -9830,7 +9833,7 @@
// we copy in child display lists into ours in drawChild()
mRecreateDisplayList = true;
if (mDisplayList == null) {
- mDisplayList = mAttachInfo.mHardwareRenderer.createDisplayList(this);
+ mDisplayList = mAttachInfo.mHardwareRenderer.createDisplayList();
// If we're creating a new display list, make sure our parent gets invalidated
// since they will need to recreate their display list to account for this
// new child display list.
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 3798c9d..4acf48c 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -25,6 +25,7 @@
import android.os.Environment;
import android.os.Looper;
import android.os.Message;
+import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.SystemClock;
import android.util.DisplayMetrics;
@@ -36,7 +37,7 @@
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.File;
-import java.io.FileNotFoundException;
+import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
@@ -50,6 +51,9 @@
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
@@ -426,22 +430,22 @@
* and obtain the traces. Both methods must be invoked on the
* same thread.
*
- * @param traceFile The path where to write the looper traces
- *
- * @see #stopLooperProfiling()
+ * @hide
*/
- public static void startLooperProfiling(File traceFile) {
+ public static void startLooperProfiling(String path, FileDescriptor fileDescriptor) {
if (sLooperProfilerStorage.get() == null) {
- LooperProfiler profiler = new LooperProfiler(traceFile);
+ LooperProfiler profiler = new LooperProfiler(path, fileDescriptor);
sLooperProfilerStorage.set(profiler);
Looper.myLooper().setMessageLogging(profiler);
}
- }
+ }
/**
* Stops profiling the looper associated with the current thread.
*
- * @see #startLooperProfiling(java.io.File)
+ * @see #startLooperProfiling(String, java.io.FileDescriptor)
+ *
+ * @hide
*/
public static void stopLooperProfiling() {
LooperProfiler profiler = sLooperProfilerStorage.get();
@@ -453,21 +457,33 @@
}
private static class LooperProfiler implements Looper.Profiler, Printer {
- private static final int LOOPER_PROFILER_VERSION = 1;
-
private static final String LOG_TAG = "LooperProfiler";
+ private static final int TRACE_VERSION_NUMBER = 3;
+ private static final int ACTION_EXIT_METHOD = 0x1;
+ private static final int HEADER_SIZE = 32;
+ private static final String HEADER_MAGIC = "SLOW";
+ private static final short HEADER_RECORD_SIZE = (short) 14;
+
private final long mTraceWallStart;
private final long mTraceThreadStart;
private final ArrayList<Entry> mTraces = new ArrayList<Entry>(512);
- private final File mTraceFile;
- private final HashMap<String, Short> mTraceNames = new HashMap<String, Short>(32);
- private short mTraceId = 0;
+ private final HashMap<String, Integer> mTraceNames = new HashMap<String, Integer>(32);
+ private int mTraceId = 0;
- LooperProfiler(File traceFile) {
- mTraceFile = traceFile;
+ private final String mPath;
+ private ParcelFileDescriptor mFileDescriptor;
+
+ LooperProfiler(String path, FileDescriptor fileDescriptor) {
+ mPath = path;
+ try {
+ mFileDescriptor = ParcelFileDescriptor.dup(fileDescriptor);
+ } catch (IOException e) {
+ Log.e(LOG_TAG, "Could not write trace file " + mPath, e);
+ throw new RuntimeException(e);
+ }
mTraceWallStart = SystemClock.currentTimeMicro();
mTraceThreadStart = SystemClock.currentThreadTimeMicro();
}
@@ -490,11 +506,11 @@
mTraces.add(entry);
}
- private short getTraceId(Message message) {
+ private int getTraceId(Message message) {
String name = message.getTarget().getMessageName(message);
- Short traceId = mTraceNames.get(name);
+ Integer traceId = mTraceNames.get(name);
if (traceId == null) {
- traceId = mTraceId++;
+ traceId = mTraceId++ << 4;
mTraceNames.put(name, traceId);
}
return traceId;
@@ -507,62 +523,135 @@
public void run() {
saveTraces();
}
- }, "LooperProfiler[" + mTraceFile + "]").start();
+ }, "LooperProfiler[" + mPath + "]").start();
}
private void saveTraces() {
- FileOutputStream fos;
- try {
- fos = new FileOutputStream(mTraceFile);
- } catch (FileNotFoundException e) {
- Log.e(LOG_TAG, "Could not open trace file: " + mTraceFile);
- return;
- }
-
+ FileOutputStream fos = new FileOutputStream(mFileDescriptor.getFileDescriptor());
DataOutputStream out = new DataOutputStream(new BufferedOutputStream(fos));
try {
- out.writeInt(LOOPER_PROFILER_VERSION);
- out.writeLong(mTraceWallStart);
- out.writeLong(mTraceThreadStart);
+ writeHeader(out, mTraceWallStart, mTraceNames, mTraces);
+ out.flush();
- out.writeInt(mTraceNames.size());
- for (Map.Entry<String, Short> entry : mTraceNames.entrySet()) {
- saveTraceName(entry.getKey(), entry.getValue(), out);
- }
+ writeTraces(fos, out.size(), mTraceWallStart, mTraceThreadStart, mTraces);
- out.writeInt(mTraces.size());
- for (Entry entry : mTraces) {
- saveTrace(entry, out);
- }
-
- Log.d(LOG_TAG, "Looper traces ready: " + mTraceFile);
+ Log.d(LOG_TAG, "Looper traces ready: " + mPath);
} catch (IOException e) {
- Log.e(LOG_TAG, "Could not write trace file: ", e);
+ Log.e(LOG_TAG, "Could not write trace file " + mPath, e);
} finally {
try {
out.close();
} catch (IOException e) {
- // Ignore
+ Log.e(LOG_TAG, "Could not write trace file " + mPath, e);
+ }
+ try {
+ mFileDescriptor.close();
+ } catch (IOException e) {
+ Log.e(LOG_TAG, "Could not write trace file " + mPath, e);
}
}
}
-
- private void saveTraceName(String name, short id, DataOutputStream out) throws IOException {
- out.writeShort(id);
- out.writeUTF(name);
+
+ private static void writeTraces(FileOutputStream out, long offset, long wallStart,
+ long threadStart, ArrayList<Entry> entries) throws IOException {
+
+ FileChannel channel = out.getChannel();
+
+ // Header
+ ByteBuffer buffer = ByteBuffer.allocateDirect(HEADER_SIZE);
+ buffer.put(HEADER_MAGIC.getBytes());
+ buffer = buffer.order(ByteOrder.LITTLE_ENDIAN);
+ buffer.putShort((short) TRACE_VERSION_NUMBER); // version
+ buffer.putShort((short) HEADER_SIZE); // offset to data
+ buffer.putLong(wallStart); // start time in usec
+ buffer.putShort(HEADER_RECORD_SIZE); // size of a record in bytes
+ // padding to 32 bytes
+ for (int i = 0; i < HEADER_SIZE - 18; i++) {
+ buffer.put((byte) 0);
+ }
+
+ buffer.flip();
+ channel.position(offset).write(buffer);
+
+ buffer = ByteBuffer.allocateDirect(14).order(ByteOrder.LITTLE_ENDIAN);
+ for (Entry entry : entries) {
+ buffer.putShort((short) 1); // we simulate only one thread
+ buffer.putInt(entry.traceId); // entering method
+ buffer.putInt((int) (entry.threadStart - threadStart));
+ buffer.putInt((int) (entry.wallStart - wallStart));
+
+ buffer.flip();
+ channel.write(buffer);
+ buffer.clear();
+
+ buffer.putShort((short) 1);
+ buffer.putInt(entry.traceId | ACTION_EXIT_METHOD); // exiting method
+ buffer.putInt((int) (entry.threadStart + entry.threadTime - threadStart));
+ buffer.putInt((int) (entry.wallStart + entry.wallTime - wallStart));
+
+ buffer.flip();
+ channel.write(buffer);
+ buffer.clear();
+ }
+
+ channel.close();
}
+
+ private static void writeHeader(DataOutputStream out, long start,
+ HashMap<String, Integer> names, ArrayList<Entry> entries) throws IOException {
+
+ Entry last = entries.get(entries.size() - 1);
+ long wallTotal = (last.wallStart + last.wallTime) - start;
+
+ startSection("version", out);
+ addValue(null, Integer.toString(TRACE_VERSION_NUMBER), out);
+ addValue("data-file-overflow", "false", out);
+ addValue("clock", "dual", out);
+ addValue("elapsed-time-usec", Long.toString(wallTotal), out);
+ addValue("num-method-calls", Integer.toString(entries.size()), out);
+ addValue("clock-call-overhead-nsec", "1", out);
+ addValue("vm", "dalvik", out);
+
+ startSection("threads", out);
+ addThreadId(1, "main", out);
+
+ startSection("methods", out);
+ addMethods(names, out);
+
+ startSection("end", out);
+ }
+
+ private static void addMethods(HashMap<String, Integer> names, DataOutputStream out)
+ throws IOException {
+
+ for (Map.Entry<String, Integer> name : names.entrySet()) {
+ out.writeBytes(String.format("0x%08x\tEventQueue\t%s\t()V\tLooper\t-2\n",
+ name.getValue(), name.getKey()));
+ }
+ }
+
+ private static void addThreadId(int id, String name, DataOutputStream out)
+ throws IOException {
- private void saveTrace(Entry entry, DataOutputStream out) throws IOException {
- out.writeShort(entry.traceId);
- out.writeLong(entry.wallStart);
- out.writeLong(entry.wallTime);
- out.writeLong(entry.threadStart);
- out.writeLong(entry.threadTime);
+ out.writeBytes(Integer.toString(id) + '\t' + name + '\n');
+ }
+
+ private static void addValue(String name, String value, DataOutputStream out)
+ throws IOException {
+
+ if (name != null) {
+ out.writeBytes(name + "=");
+ }
+ out.writeBytes(value + '\n');
+ }
+
+ private static void startSection(String name, DataOutputStream out) throws IOException {
+ out.writeBytes("*" + name + '\n');
}
static class Entry {
- short traceId;
+ int traceId;
long wallStart;
long wallTime;
long threadStart;
diff --git a/core/java/android/view/textservice/SpellCheckerInfo.aidl b/core/java/android/view/textservice/SpellCheckerInfo.aidl
new file mode 100644
index 0000000..eb5dfcc
--- /dev/null
+++ b/core/java/android/view/textservice/SpellCheckerInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2011 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.view.textservice;
+
+parcelable SpellCheckerInfo;
diff --git a/core/java/android/view/textservice/SpellCheckerInfo.java b/core/java/android/view/textservice/SpellCheckerInfo.java
new file mode 100644
index 0000000..1205adf
--- /dev/null
+++ b/core/java/android/view/textservice/SpellCheckerInfo.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2011 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.view.textservice;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * This class is used to specify meta information of an spell checker.
+ */
+public final class SpellCheckerInfo implements Parcelable {
+ private final ResolveInfo mService;
+ private final String mId;
+
+ /**
+ * Constructor.
+ * @hide
+ */
+ public SpellCheckerInfo(Context context, ResolveInfo service) {
+ mService = service;
+ ServiceInfo si = service.serviceInfo;
+ mId = new ComponentName(si.packageName, si.name).flattenToShortString();
+ }
+
+ /**
+ * Constructor.
+ * @hide
+ */
+ public SpellCheckerInfo(Parcel source) {
+ mId = source.readString();
+ mService = ResolveInfo.CREATOR.createFromParcel(source);
+ }
+
+ /**
+ * Return a unique ID for this spell checker. The ID is generated from
+ * the package and class name implementing the method.
+ */
+ public String getId() {
+ return mId;
+ }
+
+
+ /**
+ * Return the component of the service that implements.
+ */
+ public ComponentName getComponent() {
+ return new ComponentName(
+ mService.serviceInfo.packageName, mService.serviceInfo.name);
+ }
+
+ /**
+ * Return the .apk package that implements this input method.
+ */
+ public String getPackageName() {
+ return mService.serviceInfo.packageName;
+ }
+
+ /**
+ * Used to package this object into a {@link Parcel}.
+ *
+ * @param dest The {@link Parcel} to be written.
+ * @param flags The flags used for parceling.
+ */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mId);
+ mService.writeToParcel(dest, flags);
+ }
+
+
+ /**
+ * Used to make this class parcelable.
+ */
+ public static final Parcelable.Creator<SpellCheckerInfo> CREATOR
+ = new Parcelable.Creator<SpellCheckerInfo>() {
+ @Override
+ public SpellCheckerInfo createFromParcel(Parcel source) {
+ return new SpellCheckerInfo(source);
+ }
+
+ @Override
+ public SpellCheckerInfo[] newArray(int size) {
+ return new SpellCheckerInfo[size];
+ }
+ };
+
+ /**
+ * Used to make this class parcelable.
+ */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+}
diff --git a/core/java/android/view/textservice/SuggestionsInfo.aidl b/core/java/android/view/textservice/SuggestionsInfo.aidl
new file mode 100644
index 0000000..66e20d2
--- /dev/null
+++ b/core/java/android/view/textservice/SuggestionsInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2011 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.view.textservice;
+
+parcelable SuggestionsInfo;
diff --git a/core/java/android/view/textservice/SuggestionsInfo.java b/core/java/android/view/textservice/SuggestionsInfo.java
new file mode 100644
index 0000000..b0ccbea
--- /dev/null
+++ b/core/java/android/view/textservice/SuggestionsInfo.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2011 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.view.textservice;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * This class contains a metadata of suggestions from the text service
+ */
+public final class SuggestionsInfo implements Parcelable {
+ /**
+ * Flag of the attributes of the suggestions that can be obtained by
+ * {@link #getSuggestionsAttributes}: this tells that the requested word was found
+ * in the dictionary in the text service.
+ */
+ public static final int RESULT_ATTR_IN_THE_DICTIONARY = 0x0001;
+ /** Flag of the attributes of the suggestions that can be obtained by
+ * {@link #getSuggestionsAttributes}: this tells that there are one or more suggestions
+ * available for the requested word. This doesn't necessarily mean that the suggestions
+ * are actually in this SuggestionsInfo. For instance, the caller could have been asked to
+ * limit the maximum number of suggestions returned.
+ */
+ public static final int RESULT_ATTR_SUGGESTIONS_AVAILABLE = 0x0002;
+ /**
+ * Flag of the attributes of the suggestions that can be obtained by
+ * {@link #getSuggestionsAttributes}: this tells that the text service thinks the requested
+ * word looks a typo.
+ */
+ public static final int RESULT_ATTR_LOOKS_TYPO = 0x0004;
+ private final int mSuggestionsAttributes;
+ private final String[] mSuggestions;
+ private int mCookie;
+ private int mSequence;
+
+ /**
+ * Constructor.
+ * @param suggestionsAttributes from the text service
+ * @param suggestions from the text service
+ */
+ public SuggestionsInfo(int suggestionsAttributes, String[] suggestions) {
+ if (suggestions == null) {
+ throw new NullPointerException();
+ }
+ mSuggestionsAttributes = suggestionsAttributes;
+ mSuggestions = suggestions;
+ mCookie = 0;
+ mSequence = 0;
+ }
+
+ /**
+ * Constructor.
+ * @param suggestionsAttributes from the text service
+ * @param suggestions from the text service
+ * @param cookie the cookie of the input TextInfo
+ * @param sequence the cookie of the input TextInfo
+ */
+ public SuggestionsInfo(
+ int suggestionsAttributes, String[] suggestions, int cookie, int sequence) {
+ if (suggestions == null) {
+ throw new NullPointerException();
+ }
+ mSuggestionsAttributes = suggestionsAttributes;
+ mSuggestions = suggestions;
+ mCookie = cookie;
+ mSequence = sequence;
+ }
+
+ public SuggestionsInfo(Parcel source) {
+ mSuggestionsAttributes = source.readInt();
+ mSuggestions = source.readStringArray();
+ mCookie = source.readInt();
+ mSequence = source.readInt();
+ }
+
+ /**
+ * Used to package this object into a {@link Parcel}.
+ *
+ * @param dest The {@link Parcel} to be written.
+ * @param flags The flags used for parceling.
+ */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mSuggestionsAttributes);
+ dest.writeStringArray(mSuggestions);
+ dest.writeInt(mCookie);
+ dest.writeInt(mSequence);
+ }
+
+ /**
+ * Set the cookie and the sequence of SuggestionsInfo which are set to TextInfo from a client
+ * application
+ * @param cookie the cookie of an input TextInfo
+ * @param sequence the cookie of an input TextInfo
+ */
+ public void setCookieAndSequence(int cookie, int sequence) {
+ mCookie = cookie;
+ mSequence = sequence;
+ }
+
+ /**
+ * @return the cookie which may be set by a client application
+ */
+ public int getCookie() {
+ return mCookie;
+ }
+
+ /**
+ * @return the sequence which may be set by a client application
+ */
+ public int getSequence() {
+ return mSequence;
+ }
+
+ /**
+ * @return the attributes of suggestions. This includes whether the spell checker has the word
+ * in its dictionary or not and whether the spell checker has confident suggestions for the
+ * word or not.
+ */
+ public int getSuggestionsAttributes() {
+ return mSuggestionsAttributes;
+ }
+
+ /**
+ * @return the count of suggestions
+ */
+ public int getSuggestionsCount() {
+ return mSuggestions.length;
+ }
+
+ /**
+ * @param i the id of suggestions
+ * @return the suggestion at the specified id
+ */
+ public String getSuggestionAt(int i) {
+ return mSuggestions[i];
+ }
+
+ /**
+ * Used to make this class parcelable.
+ */
+ public static final Parcelable.Creator<SuggestionsInfo> CREATOR
+ = new Parcelable.Creator<SuggestionsInfo>() {
+ @Override
+ public SuggestionsInfo createFromParcel(Parcel source) {
+ return new SuggestionsInfo(source);
+ }
+
+ @Override
+ public SuggestionsInfo[] newArray(int size) {
+ return new SuggestionsInfo[size];
+ }
+ };
+
+ /**
+ * Used to make this class parcelable.
+ */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+}
diff --git a/core/java/android/view/textservice/TextInfo.aidl b/core/java/android/view/textservice/TextInfo.aidl
new file mode 100644
index 0000000..d231d76
--- /dev/null
+++ b/core/java/android/view/textservice/TextInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2011 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.view.textservice;
+
+parcelable TextInfo;
diff --git a/core/java/android/view/textservice/TextInfo.java b/core/java/android/view/textservice/TextInfo.java
new file mode 100644
index 0000000..b534eb0
--- /dev/null
+++ b/core/java/android/view/textservice/TextInfo.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2011 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.view.textservice;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+/**
+ * This class contains a metadata of the input of TextService
+ */
+public final class TextInfo implements Parcelable {
+ private final String mText;
+ private final int mCookie;
+ private final int mSequence;
+
+ /**
+ * Constructor.
+ * @param text the text which will be input to TextService
+ */
+ public TextInfo(String text) {
+ this(text, 0, 0);
+ }
+
+ /**
+ * Constructor.
+ * @param text the text which will be input to TextService
+ * @param cookie the cookie for this TextInfo
+ * @param sequence the sequence number for this TextInfo
+ */
+ public TextInfo(String text, int cookie, int sequence) {
+ if (TextUtils.isEmpty(text)) {
+ throw new IllegalArgumentException(text);
+ }
+ mText = text;
+ mCookie = cookie;
+ mSequence = sequence;
+ }
+
+ public TextInfo(Parcel source) {
+ mText = source.readString();
+ mCookie = source.readInt();
+ mSequence = source.readInt();
+ }
+
+ /**
+ * Used to package this object into a {@link Parcel}.
+ *
+ * @param dest The {@link Parcel} to be written.
+ * @param flags The flags used for parceling.
+ */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mText);
+ dest.writeInt(mCookie);
+ dest.writeInt(mSequence);
+ }
+
+ /**
+ * @return the text which is an input of a text service
+ */
+ public String getText() {
+ return mText;
+ }
+
+ /**
+ * @return the cookie of TextInfo
+ */
+ public int getCookie() {
+ return mCookie;
+ }
+
+ /**
+ * @return the sequence of TextInfo
+ */
+ public int getSequence() {
+ return mSequence;
+ }
+
+ /**
+ * Used to make this class parcelable.
+ */
+ public static final Parcelable.Creator<TextInfo> CREATOR
+ = new Parcelable.Creator<TextInfo>() {
+ @Override
+ public TextInfo createFromParcel(Parcel source) {
+ return new TextInfo(source);
+ }
+
+ @Override
+ public TextInfo[] newArray(int size) {
+ return new TextInfo[size];
+ }
+ };
+
+ /**
+ * Used to make this class parcelable.
+ */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+}
diff --git a/core/java/android/view/textservice/TextServicesManager.java b/core/java/android/view/textservice/TextServicesManager.java
new file mode 100644
index 0000000..6fa7e4d
--- /dev/null
+++ b/core/java/android/view/textservice/TextServicesManager.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2011 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.view.textservice;
+
+import com.android.internal.textservice.ITextServicesManager;
+
+import android.content.Context;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.view.textservice.SpellCheckerInfo;
+import android.service.textservice.SpellCheckerSession;
+import android.service.textservice.SpellCheckerSession.SpellCheckerSessionListener;
+
+import java.util.Locale;
+
+/**
+ * System API to the overall text services, which arbitrates interaction between applications
+ * and text services. You can retrieve an instance of this interface with
+ * {@link Context#getSystemService(String) Context.getSystemService()}.
+ *
+ * The user can change the current text services in Settings. And also applications can specify
+ * the target text services.
+ */
+public final class TextServicesManager {
+ private static final String TAG = TextServicesManager.class.getSimpleName();
+
+ private static TextServicesManager sInstance;
+ private static ITextServicesManager sService;
+
+ private TextServicesManager() {
+ if (sService == null) {
+ IBinder b = ServiceManager.getService(Context.TEXT_SERVICES_MANAGER_SERVICE);
+ sService = ITextServicesManager.Stub.asInterface(b);
+ }
+ }
+
+ /**
+ * Retrieve the global TextServicesManager instance, creating it if it doesn't already exist.
+ * @hide
+ */
+ public static TextServicesManager getInstance() {
+ synchronized (TextServicesManager.class) {
+ if (sInstance != null) {
+ return sInstance;
+ }
+ sInstance = new TextServicesManager();
+ }
+ return sInstance;
+ }
+
+
+ /**
+ * Get the current spell checker service info for the specified locale.
+ * @param locale locale of a spell checker
+ * @return SpellCheckerInfo for the specified locale.
+ */
+ // TODO: Add a method to get enabled spell checkers.
+ public SpellCheckerInfo getCurrentSpellChecker(Locale locale) {
+ if (locale == null) {
+ throw new NullPointerException("locale is null");
+ }
+ try {
+ return sService.getCurrentSpellChecker(locale.toString());
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Get a spell checker session for a specified spell checker
+ * @param info SpellCheckerInfo of the spell checker
+ * @param locale the locale for the spell checker
+ * @param listener a spell checker session lister for getting results from a spell checker.
+ * @return the spell checker session of the spell checker
+ */
+ public SpellCheckerSession newSpellCheckerSession(
+ SpellCheckerInfo info, Locale locale, SpellCheckerSessionListener listener) {
+ if (info == null || locale == null || listener == null) {
+ throw new NullPointerException();
+ }
+ final SpellCheckerSession session = new SpellCheckerSession(sService, listener);
+ try {
+ sService.getSpellCheckerService(
+ info, locale.toString(), session.getTextServicesSessionListener(),
+ session.getSpellCheckerSessionListener());
+ } catch (RemoteException e) {
+ return null;
+ }
+ return session;
+ }
+}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 73db03e..6a3b2ff 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -9119,20 +9119,12 @@
return nativeTileProfilingNumTilesInFrame(frame);
}
/** @hide only used by profiling tests */
- public int tileProfilingGetX(int frame, int tile) {
- return nativeTileProfilingGetX(frame, tile);
+ public int tileProfilingGetInt(int frame, int tile, String key) {
+ return nativeTileProfilingGetInt(frame, tile, key);
}
/** @hide only used by profiling tests */
- public int tileProfilingGetY(int frame, int tile) {
- return nativeTileProfilingGetY(frame, tile);
- }
- /** @hide only used by profiling tests */
- public boolean tileProfilingGetReady(int frame, int tile) {
- return nativeTileProfilingGetReady(frame, tile);
- }
- /** @hide only used by profiling tests */
- public int tileProfilingGetLevel(int frame, int tile) {
- return nativeTileProfilingGetLevel(frame, tile);
+ public float tileProfilingGetFloat(int frame, int tile, String key) {
+ return nativeTileProfilingGetFloat(frame, tile, key);
}
private native int nativeCacheHitFramePointer();
@@ -9262,10 +9254,8 @@
private native void nativeTileProfilingClear();
private native int nativeTileProfilingNumFrames();
private native int nativeTileProfilingNumTilesInFrame(int frame);
- private native int nativeTileProfilingGetX(int frame, int tile);
- private native int nativeTileProfilingGetY(int frame, int tile);
- private native boolean nativeTileProfilingGetReady(int frame, int tile);
- private native int nativeTileProfilingGetLevel(int frame, int tile);
+ private native int nativeTileProfilingGetInt(int frame, int tile, String key);
+ private native float nativeTileProfilingGetFloat(int frame, int tile, String key);
// Never call this version except by updateCachedTextfield(String) -
// we always want to pass in our generation number.
private native void nativeUpdateCachedTextfield(String updatedText,
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index d7a2526..8d8023b 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -1069,6 +1069,15 @@
+ " arg1=" + msg.arg1 + " arg2=" + msg.arg2
+ " obj=" + msg.obj);
}
+ if (mWebView == null
+ && msg.what != EventHub.RESUME_TIMERS
+ && msg.what != EventHub.PAUSE_TIMERS) {
+ if (DebugFlags.WEB_VIEW_CORE) {
+ Log.v(LOGTAG, "Rejecting message " + msg.what
+ + " because we are destroyed");
+ }
+ return;
+ }
switch (msg.what) {
case WEBKIT_DRAW:
webkitDraw();
@@ -1757,30 +1766,17 @@
}
/**
- * Removes pending messages and trigger a DESTROY message to send to
- * WebCore.
+ * Sends a DESTROY message to WebCore.
* Called from UI thread.
*/
void destroy() {
- // We don't want anyone to post a message between removing pending
- // messages and sending the destroy message.
synchronized (mEventHub) {
- // RESUME_TIMERS and PAUSE_TIMERS are per process base. They need to
- // be preserved even the WebView is destroyed.
- // Note: we should not have more than one RESUME_TIMERS/PAUSE_TIMERS
- boolean hasResume = mEventHub.hasMessages(EventHub.RESUME_TIMERS);
- boolean hasPause = mEventHub.hasMessages(EventHub.PAUSE_TIMERS);
- mEventHub.removeMessages();
+ // Do not call removeMessages as then we risk removing PAUSE_TIMERS
+ // or RESUME_TIMERS messages, which we must still handle as they
+ // are per process. DESTROY will instead trigger a white list in
+ // mEventHub, skipping any remaining messages in the queue
mEventHub.sendMessageAtFrontOfQueue(
Message.obtain(null, EventHub.DESTROY));
- if (hasPause) {
- mEventHub.sendMessageAtFrontOfQueue(
- Message.obtain(null, EventHub.PAUSE_TIMERS));
- }
- if (hasResume) {
- mEventHub.sendMessageAtFrontOfQueue(
- Message.obtain(null, EventHub.RESUME_TIMERS));
- }
mEventHub.blockMessages();
}
}
@@ -2113,13 +2109,17 @@
// called from JNI or WebView thread
/* package */ void contentDraw() {
- // don't update the Picture until we have an initial width and finish
- // the first layout
- if (mCurrentViewWidth == 0 || !mBrowserFrame.firstLayoutDone()) {
- return;
- }
- // only fire an event if this is our first request
synchronized (this) {
+ if (mWebView == null || mBrowserFrame == null) {
+ // We were destroyed
+ return;
+ }
+ // don't update the Picture until we have an initial width and finish
+ // the first layout
+ if (mCurrentViewWidth == 0 || !mBrowserFrame.firstLayoutDone()) {
+ return;
+ }
+ // only fire an event if this is our first request
if (mDrawIsScheduled) return;
mDrawIsScheduled = true;
if (mDrawIsPaused) return;
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 8f8c1d0..b7c1687 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -4638,9 +4638,6 @@
childrenTop += getVerticalFadingEdgeLength();
}
}
- // Don't ever focus a disabled item.
- if (!mAdapter.isEnabled(i)) continue;
-
if (top >= childrenTop) {
// Found a view whose top is fully visisble
selectedPos = firstPosition + i;
diff --git a/core/java/android/widget/CheckedTextView.java b/core/java/android/widget/CheckedTextView.java
index 49616cc..f3a6da7 100644
--- a/core/java/android/widget/CheckedTextView.java
+++ b/core/java/android/widget/CheckedTextView.java
@@ -39,8 +39,9 @@
private boolean mChecked;
private int mCheckMarkResource;
private Drawable mCheckMarkDrawable;
- private int mBasePaddingRight;
+ private int mBasePadding;
private int mCheckMarkWidth;
+ private boolean mNeedRequestlayout;
private static final int[] CHECKED_STATE_SET = {
R.attr.state_checked
@@ -123,6 +124,7 @@
mCheckMarkDrawable.setCallback(null);
unscheduleDrawable(mCheckMarkDrawable);
}
+ mNeedRequestlayout = (d != mCheckMarkDrawable);
if (d != null) {
d.setCallback(this);
d.setVisible(getVisibility() == VISIBLE, false);
@@ -130,19 +132,35 @@
setMinHeight(d.getIntrinsicHeight());
mCheckMarkWidth = d.getIntrinsicWidth();
- mUserPaddingRight = mCheckMarkWidth + mBasePaddingRight;
d.setState(getDrawableState());
} else {
- mUserPaddingRight = mBasePaddingRight;
+ mCheckMarkWidth = 0;
}
mCheckMarkDrawable = d;
- requestLayout();
+ // Do padding resolution. This will call setPadding() and do a requestLayout() if needed.
+ resolvePadding();
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ protected void resolvePadding() {
+ super.resolvePadding();
+ int newPadding = (mCheckMarkDrawable != null) ?
+ mCheckMarkWidth + mBasePadding : mBasePadding;
+ mNeedRequestlayout |= (mPaddingRight != newPadding);
+ mPaddingRight = newPadding;
+ if (mNeedRequestlayout) {
+ requestLayout();
+ mNeedRequestlayout = false;
+ }
}
@Override
public void setPadding(int left, int top, int right, int bottom) {
super.setPadding(left, top, right, bottom);
- mBasePaddingRight = mUserPaddingRight;
+ mBasePadding = mPaddingRight;
}
@Override
@@ -167,9 +185,9 @@
int right = getWidth();
checkMarkDrawable.setBounds(
- right - mUserPaddingRight,
+ right - mPaddingRight,
y,
- right - mUserPaddingRight + mCheckMarkWidth,
+ right - mPaddingRight + mCheckMarkWidth,
y + height);
checkMarkDrawable.draw(canvas);
}
diff --git a/core/java/com/android/internal/textservice/ISpellCheckerService.aidl b/core/java/com/android/internal/textservice/ISpellCheckerService.aidl
new file mode 100644
index 0000000..ff00492
--- /dev/null
+++ b/core/java/com/android/internal/textservice/ISpellCheckerService.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2011 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.internal.textservice;
+
+import com.android.internal.textservice.ISpellCheckerSession;
+import com.android.internal.textservice.ISpellCheckerSessionListener;
+
+/**
+ * Public interface to the global spell checker.
+ * @hide
+ */
+interface ISpellCheckerService {
+ ISpellCheckerSession getISpellCheckerSession(
+ String locale, ISpellCheckerSessionListener listener);
+}
diff --git a/core/java/com/android/internal/textservice/ISpellCheckerSession.aidl b/core/java/com/android/internal/textservice/ISpellCheckerSession.aidl
new file mode 100644
index 0000000..79e43510c0
--- /dev/null
+++ b/core/java/com/android/internal/textservice/ISpellCheckerSession.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2011 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.internal.textservice;
+
+import android.view.textservice.TextInfo;
+
+/**
+ * @hide
+ */
+oneway interface ISpellCheckerSession {
+ void getSuggestionsMultiple(
+ in TextInfo[] textInfos, int suggestionsLimit, boolean multipleWords);
+ void cancel();
+}
diff --git a/core/java/com/android/internal/textservice/ISpellCheckerSessionListener.aidl b/core/java/com/android/internal/textservice/ISpellCheckerSessionListener.aidl
new file mode 100644
index 0000000..796b06e
--- /dev/null
+++ b/core/java/com/android/internal/textservice/ISpellCheckerSessionListener.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2011 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.internal.textservice;
+
+import android.view.textservice.SuggestionsInfo;
+
+/**
+ * @hide
+ */
+oneway interface ISpellCheckerSessionListener {
+ void onGetSuggestions(in SuggestionsInfo[] results);
+}
diff --git a/core/java/com/android/internal/textservice/ITextServicesManager.aidl b/core/java/com/android/internal/textservice/ITextServicesManager.aidl
new file mode 100644
index 0000000..ad0c1ff
--- /dev/null
+++ b/core/java/com/android/internal/textservice/ITextServicesManager.aidl
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2011 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.internal.textservice;
+
+import com.android.internal.textservice.ISpellCheckerSessionListener;
+import com.android.internal.textservice.ITextServicesSessionListener;
+
+import android.content.ComponentName;
+import android.view.textservice.SpellCheckerInfo;
+
+/**
+ * Interface to the text service manager.
+ * @hide
+ */
+interface ITextServicesManager {
+ SpellCheckerInfo getCurrentSpellChecker(String locale);
+ oneway void getSpellCheckerService(in SpellCheckerInfo info, in String locale,
+ in ITextServicesSessionListener tsListener,
+ in ISpellCheckerSessionListener scListener);
+ oneway void finishSpellCheckerService(in ISpellCheckerSessionListener listener);
+}
diff --git a/core/java/com/android/internal/textservice/ITextServicesSessionListener.aidl b/core/java/com/android/internal/textservice/ITextServicesSessionListener.aidl
new file mode 100644
index 0000000..ecb6cd0
--- /dev/null
+++ b/core/java/com/android/internal/textservice/ITextServicesSessionListener.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2011 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.internal.textservice;
+
+import com.android.internal.textservice.ISpellCheckerSession;
+
+import android.view.textservice.SpellCheckerInfo;
+
+/**
+ * Interface to the text service session.
+ * @hide
+ */
+interface ITextServicesSessionListener {
+ oneway void onServiceConnected(in ISpellCheckerSession spellCheckerSession);
+}
diff --git a/core/java/com/android/internal/view/menu/MenuBuilder.java b/core/java/com/android/internal/view/menu/MenuBuilder.java
index 164d581..159b3da 100644
--- a/core/java/com/android/internal/view/menu/MenuBuilder.java
+++ b/core/java/com/android/internal/view/menu/MenuBuilder.java
@@ -50,6 +50,8 @@
private static final String LOGTAG = "MenuBuilder";
private static final String PRESENTER_KEY = "android:menu:presenters";
+ private static final String ACTION_VIEW_STATES_KEY = "android:menu:actionviewstates";
+ private static final String EXPANDED_ACTION_VIEW_ID = "android:menu:expandedactionview";
private static final int[] sCategoryToOrder = new int[] {
1, /* No category */
@@ -308,6 +310,67 @@
dispatchRestoreInstanceState(state);
}
+ public void saveActionViewStates(Bundle outStates) {
+ SparseArray<Parcelable> viewStates = null;
+
+ final int itemCount = size();
+ for (int i = 0; i < itemCount; i++) {
+ final MenuItem item = getItem(i);
+ final View v = item.getActionView();
+ if (v != null && v.getId() != View.NO_ID) {
+ if (viewStates == null) {
+ viewStates = new SparseArray<Parcelable>();
+ }
+ v.saveHierarchyState(viewStates);
+ if (item.isActionViewExpanded()) {
+ outStates.putInt(EXPANDED_ACTION_VIEW_ID, item.getItemId());
+ }
+ }
+ if (item.hasSubMenu()) {
+ final SubMenuBuilder subMenu = (SubMenuBuilder) item.getSubMenu();
+ subMenu.saveActionViewStates(outStates);
+ }
+ }
+
+ if (viewStates != null) {
+ outStates.putSparseParcelableArray(getActionViewStatesKey(), viewStates);
+ }
+ }
+
+ public void restoreActionViewStates(Bundle states) {
+ if (states == null) {
+ return;
+ }
+
+ SparseArray<Parcelable> viewStates = states.getSparseParcelableArray(
+ getActionViewStatesKey());
+
+ final int itemCount = size();
+ for (int i = 0; i < itemCount; i++) {
+ final MenuItem item = getItem(i);
+ final View v = item.getActionView();
+ if (v != null && v.getId() != View.NO_ID) {
+ v.restoreHierarchyState(viewStates);
+ }
+ if (item.hasSubMenu()) {
+ final SubMenuBuilder subMenu = (SubMenuBuilder) item.getSubMenu();
+ subMenu.restoreActionViewStates(states);
+ }
+ }
+
+ final int expandedId = states.getInt(EXPANDED_ACTION_VIEW_ID);
+ if (expandedId > 0) {
+ MenuItem itemToExpand = findItem(expandedId);
+ if (itemToExpand != null) {
+ itemToExpand.expandActionView();
+ }
+ }
+ }
+
+ protected String getActionViewStatesKey() {
+ return ACTION_VIEW_STATES_KEY;
+ }
+
public void setCallback(Callback cb) {
mCallback = cb;
}
diff --git a/core/java/com/android/internal/view/menu/MenuItemImpl.java b/core/java/com/android/internal/view/menu/MenuItemImpl.java
index 541d101..b0a002d 100644
--- a/core/java/com/android/internal/view/menu/MenuItemImpl.java
+++ b/core/java/com/android/internal/view/menu/MenuItemImpl.java
@@ -553,6 +553,9 @@
public MenuItem setActionView(View view) {
mActionView = view;
mActionProvider = null;
+ if (view != null && view.getId() == View.NO_ID && mId > 0) {
+ view.setId(mId);
+ }
mMenu.onItemActionRequestChanged(this);
return this;
}
diff --git a/core/java/com/android/internal/view/menu/SubMenuBuilder.java b/core/java/com/android/internal/view/menu/SubMenuBuilder.java
index fb1cd5e..92acf8c 100644
--- a/core/java/com/android/internal/view/menu/SubMenuBuilder.java
+++ b/core/java/com/android/internal/view/menu/SubMenuBuilder.java
@@ -121,4 +121,13 @@
public boolean collapseItemActionView(MenuItemImpl item) {
return mParentMenu.collapseItemActionView(item);
}
+
+ @Override
+ public String getActionViewStatesKey() {
+ final int itemId = mItem != null ? mItem.getItemId() : 0;
+ if (itemId == 0) {
+ return null;
+ }
+ return super.getActionViewStatesKey() + ":" + itemId;
+ }
}
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
index e03858b..8b74f3d 100644
--- a/core/java/com/android/internal/widget/ActionBarView.java
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -42,6 +42,7 @@
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
+import android.view.CollapsibleActionView;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.Menu;
@@ -1304,6 +1305,10 @@
if (mCustomNavView != null) mCustomNavView.setVisibility(GONE);
requestLayout();
item.setActionViewExpanded(true);
+
+ if (mExpandedActionView instanceof CollapsibleActionView) {
+ ((CollapsibleActionView) mExpandedActionView).onActionViewExpanded();
+ }
return true;
}
@@ -1330,11 +1335,16 @@
if (mCustomNavView != null && (mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) {
mCustomNavView.setVisibility(VISIBLE);
}
+ View collapsedView = mExpandedActionView;
mExpandedActionView = null;
mExpandedHomeLayout.setIcon(null);
mCurrentExpandedItem = null;
requestLayout();
item.setActionViewExpanded(false);
+
+ if (collapsedView instanceof CollapsibleActionView) {
+ ((CollapsibleActionView) collapsedView).onActionViewCollapsed();
+ }
return true;
}
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index b0c2f2c..b06de9d 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -576,18 +576,18 @@
// ----------------------------------------------------------------------------
static DisplayList* android_view_GLES20Canvas_getDisplayList(JNIEnv* env,
- jobject clazz, DisplayListRenderer* renderer) {
- return renderer->getDisplayList();
+ jobject clazz, DisplayListRenderer* renderer, DisplayList* displayList) {
+ return renderer->getDisplayList(displayList);
}
-static OpenGLRenderer* android_view_GLES20Canvas_getDisplayListRenderer(JNIEnv* env,
+static OpenGLRenderer* android_view_GLES20Canvas_createDisplayListRenderer(JNIEnv* env,
+ jobject clazz) {
+ return new DisplayListRenderer;
+}
+
+static void android_view_GLES20Canvas_resetDisplayListRenderer(JNIEnv* env,
jobject clazz, DisplayListRenderer* renderer) {
- if (renderer == NULL) {
- renderer = new DisplayListRenderer;
- } else {
- renderer->reset();
- }
- return renderer;
+ renderer->reset();
}
static void android_view_GLES20Canvas_destroyDisplayList(JNIEnv* env,
@@ -812,9 +812,10 @@
{ "nGetClipBounds", "(ILandroid/graphics/Rect;)Z",
(void*) android_view_GLES20Canvas_getClipBounds },
- { "nGetDisplayList", "(I)I", (void*) android_view_GLES20Canvas_getDisplayList },
+ { "nGetDisplayList", "(II)I", (void*) android_view_GLES20Canvas_getDisplayList },
{ "nDestroyDisplayList", "(I)V", (void*) android_view_GLES20Canvas_destroyDisplayList },
- { "nGetDisplayListRenderer", "(I)I", (void*) android_view_GLES20Canvas_getDisplayListRenderer },
+ { "nCreateDisplayListRenderer", "()I", (void*) android_view_GLES20Canvas_createDisplayListRenderer },
+ { "nResetDisplayListRenderer", "(I)V", (void*) android_view_GLES20Canvas_resetDisplayListRenderer },
{ "nDrawDisplayList", "(IIIILandroid/graphics/Rect;)Z",
(void*) android_view_GLES20Canvas_drawDisplayList },
{ "nOutputDisplayList", "(II)V", (void*) android_view_GLES20Canvas_outputDisplayList },
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 103a326..91003d1 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1120,6 +1120,13 @@
android:description="@string/permdesc_bindInputMethod"
android:protectionLevel="signature" />
+ <!-- Must be required by a TextService (e.g. SpellCheckerService)
+ to ensure that only the system can bind to it. -->
+ <permission android:name="android.permission.BIND_TEXT_SERVICE"
+ android:label="@string/permlab_bindTextService"
+ android:description="@string/permdesc_bindTextService"
+ android:protectionLevel="signature" />
+
<!-- Must be required by a {@link android.service.wallpaper.WallpaperService},
to ensure that only the system can bind to it. -->
<permission android:name="android.permission.BIND_WALLPAPER"
@@ -1197,7 +1204,7 @@
<permission android:name="android.permission.READ_FRAME_BUFFER"
android:label="@string/permlab_readFrameBuffer"
android:description="@string/permdesc_readFrameBuffer"
- android:protectionLevel="signature" />
+ android:protectionLevel="signatureOrSystem" />
<!-- Required to be able to disable the device (very dangerous!). -->
<permission android:name="android.permission.BRICK"
diff --git a/core/res/res/anim/screen_rotate_minus_90_enter.xml b/core/res/res/anim/screen_rotate_minus_90_enter.xml
index 30518e0..61aa72a 100644
--- a/core/res/res/anim/screen_rotate_minus_90_enter.xml
+++ b/core/res/res/anim/screen_rotate_minus_90_enter.xml
@@ -19,11 +19,6 @@
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
- <scale android:fromXScale="100%p" android:toXScale="100%"
- android:fromYScale="100%p" android:toYScale="100%"
- android:pivotX="50%" android:pivotY="50%"
- android:interpolator="@interpolator/decelerate_quint"
- android:duration="@android:integer/config_mediumAnimTime" />
<rotate android:fromDegrees="-90" android:toDegrees="0"
android:pivotX="50%" android:pivotY="50%"
android:interpolator="@interpolator/decelerate_quint"
diff --git a/core/res/res/anim/screen_rotate_plus_90_enter.xml b/core/res/res/anim/screen_rotate_plus_90_enter.xml
index 20943c8..53b0ccd 100644
--- a/core/res/res/anim/screen_rotate_plus_90_enter.xml
+++ b/core/res/res/anim/screen_rotate_plus_90_enter.xml
@@ -19,11 +19,6 @@
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
- <scale android:fromXScale="100%p" android:toXScale="100%"
- android:fromYScale="100%p" android:toYScale="100%"
- android:pivotX="50%" android:pivotY="50%"
- android:interpolator="@interpolator/decelerate_quint"
- android:duration="@android:integer/config_mediumAnimTime" />
<rotate android:fromDegrees="90" android:toDegrees="0"
android:pivotX="50%" android:pivotY="50%"
android:interpolator="@interpolator/decelerate_quint"
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 509ee69..feac38d 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -705,6 +705,12 @@
interface of an input method. Should never be needed for normal applications.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permlab_bindTextService">bind to a text service</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permdesc_bindTextService">Allows the holder to bind to the top-level
+ interface of a text service(e.g. SpellCheckerService). Should never be needed for normal applications.</string>
+
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_bindWallpaper">bind to a wallpaper</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_bindWallpaper">Allows the holder to bind to the top-level
@@ -2675,12 +2681,14 @@
<!-- USB_STORAGE_ERROR dialog ok button-->
<string name="dlg_ok">OK</string>
- <!-- USB_PREFERENCES: Notification for wehen the user connects the phone to a computer via USB in MTP mode. This is the title -->
+ <!-- USB_PREFERENCES: Notification for when the user connects the phone to a computer via USB in MTP mode. This is the title -->
<string name="usb_mtp_notification_title">Connected as a media device</string>
- <!-- USB_PREFERENCES: Notification for wehen the user connects the phone to a computer via USB in PTP mode. This is the title -->
+ <!-- USB_PREFERENCES: Notification for when the user connects the phone to a computer via USB in PTP mode. This is the title -->
<string name="usb_ptp_notification_title">Connected as a camera</string>
- <!-- USB_PREFERENCES: Notification for wehen the user connects the phone to a computer via USB in mass storage mode (for installer CD image). This is the title -->
+ <!-- USB_PREFERENCES: Notification for when the user connects the phone to a computer via USB in mass storage mode (for installer CD image). This is the title -->
<string name="usb_cd_installer_notification_title">Connected as an installer</string>
+ <!-- USB_PREFERENCES: Notification for when a USB accessory is attached. This is the title -->
+ <string name="usb_accessory_notification_title">Connected to a USB accessory</string>
<!-- See USB_PREFERENCES. This is the message. -->
<string name="usb_notification_message">Touch for other USB options</string>
diff --git a/docs/html/guide/developing/tools/adb.jd b/docs/html/guide/developing/tools/adb.jd
index 78d12ef..d32cf66 100644
--- a/docs/html/guide/developing/tools/adb.jd
+++ b/docs/html/guide/developing/tools/adb.jd
@@ -280,7 +280,7 @@
<td>Run PPP over USB.
<ul>
<li><code><tty></code> — the tty for PPP stream. For example <code>dev:/dev/omap_csmi_ttyl</code>. </li>
-<li><code>[parm]... </code> &mdash zero or more PPP/PPPD options, such as <code>defaultroute</code>, <code>local</code>, <code>notty</code>, etc.</li></ul>
+<li><code>[parm]... </code> — zero or more PPP/PPPD options, such as <code>defaultroute</code>, <code>local</code>, <code>notty</code>, etc.</li></ul>
<p>Note that you should not automatically start a PPP connection. </p></td>
<td></td>
diff --git a/docs/html/guide/topics/intents/intents-filters.jd b/docs/html/guide/topics/intents/intents-filters.jd
index 5905214..3f94553 100644
--- a/docs/html/guide/topics/intents/intents-filters.jd
+++ b/docs/html/guide/topics/intents/intents-filters.jd
@@ -927,7 +927,7 @@
<p>
The first, primary, purpose of this activity is to enable the user to
-interact with a single note &mdash to either {@code VIEW} the note or
+interact with a single note — to either {@code VIEW} the note or
{@code EDIT} it. (The {@code EDIT_NOTE} category is a synonym for
{@code EDIT}.) The intent would contain the URI for data matching the
MIME type <code>vnd.android.cursor.item/vnd.google.note</code> —
diff --git a/docs/html/guide/topics/manifest/activity-element.jd b/docs/html/guide/topics/manifest/activity-element.jd
index 3486212..743832c 100644
--- a/docs/html/guide/topics/manifest/activity-element.jd
+++ b/docs/html/guide/topics/manifest/activity-element.jd
@@ -710,7 +710,7 @@
The setting must be one of the values listed in the following table, or a
combination of one "{@code state...}" value plus one "{@code adjust...}"
value. Setting multiple values in either group — multiple
-"{@code state...}" values, for example &mdash has undefined results.
+"{@code state...}" values, for example — has undefined results.
Individual values are separated by a vertical bar ({@code |}). For example:
</p>
@@ -801,4 +801,4 @@
<dt>see also:</dt>
<dd><code><a href="{@docRoot}guide/topics/manifest/application-element.html"><application></a></code>
<br/><code><a href="{@docRoot}guide/topics/manifest/activity-alias-element.html"><activity-alias></a></code></dd>
-</dl>
\ No newline at end of file
+</dl>
diff --git a/docs/html/guide/topics/providers/content-providers.jd b/docs/html/guide/topics/providers/content-providers.jd
index 2a84c26..513886a 100644
--- a/docs/html/guide/topics/providers/content-providers.jd
+++ b/docs/html/guide/topics/providers/content-providers.jd
@@ -277,7 +277,7 @@
constants for their columns. For example, the
{@link android.provider.Contacts.Phones android.provider.Contacts.Phones} class
defines constants for the names of the columns in the phone table illustrated
-earlier &mdash {@code _ID}, {@code NUMBER}, {@code NUMBER_KEY}, {@code NAME},
+earlier — {@code _ID}, {@code NUMBER}, {@code NUMBER_KEY}, {@code NAME},
and so on.</li>
<li><p>A filter detailing which rows to return, formatted as an SQL {@code WHERE}
diff --git a/graphics/jni/android_renderscript_RenderScript.cpp b/graphics/jni/android_renderscript_RenderScript.cpp
index 4a85faf..3476bd5 100644
--- a/graphics/jni/android_renderscript_RenderScript.cpp
+++ b/graphics/jni/android_renderscript_RenderScript.cpp
@@ -504,6 +504,7 @@
void* ptr = bitmap.getPixels();
rsAllocationCopyToBitmap(con, (RsAllocation)alloc, ptr, bitmap.getSize());
bitmap.unlockPixels();
+ bitmap.notifyPixelsChanged();
}
static void ReleaseBitmapCallback(void *bmp)
diff --git a/include/media/stagefright/DataSource.h b/include/media/stagefright/DataSource.h
index 48d1464..713af92 100644
--- a/include/media/stagefright/DataSource.h
+++ b/include/media/stagefright/DataSource.h
@@ -20,6 +20,7 @@
#include <sys/types.h>
+#include <media/stagefright/MediaErrors.h>
#include <utils/Errors.h>
#include <utils/KeyedVector.h>
#include <utils/List.h>
@@ -61,6 +62,10 @@
return 0;
}
+ virtual status_t reconnectAtOffset(off64_t offset) {
+ return ERROR_UNSUPPORTED;
+ }
+
////////////////////////////////////////////////////////////////////////////
bool sniff(String8 *mimeType, float *confidence, sp<AMessage> *meta);
diff --git a/include/media/stagefright/HardwareAPI.h b/include/media/stagefright/HardwareAPI.h
index 946a0aa..32eed3f 100644
--- a/include/media/stagefright/HardwareAPI.h
+++ b/include/media/stagefright/HardwareAPI.h
@@ -99,6 +99,13 @@
OMX_U32 nUsage; // OUT
};
+// An enum OMX_COLOR_FormatAndroidOpaque to indicate an opaque colorformat
+// is declared in media/stagefright/openmax/OMX_IVCommon.h
+// This will inform the encoder that the actual
+// colorformat will be relayed by the GRalloc Buffers.
+// OMX_COLOR_FormatAndroidOpaque = 0x7F000001,
+
+
} // namespace android
extern android::OMXPluginBase *createOMXPlugin();
diff --git a/include/media/stagefright/MediaSource.h b/include/media/stagefright/MediaSource.h
index 37dbcd8..3818e63 100644
--- a/include/media/stagefright/MediaSource.h
+++ b/include/media/stagefright/MediaSource.h
@@ -29,7 +29,7 @@
class MediaBuffer;
class MetaData;
-struct MediaSource : public RefBase {
+struct MediaSource : public virtual RefBase {
MediaSource();
// To be called before any other methods on this object, except
diff --git a/include/media/stagefright/SurfaceMediaSource.h b/include/media/stagefright/SurfaceMediaSource.h
new file mode 100644
index 0000000..e1852ec
--- /dev/null
+++ b/include/media/stagefright/SurfaceMediaSource.h
@@ -0,0 +1,353 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+#ifndef ANDROID_GUI_SURFACEMEDIASOURCE_H
+#define ANDROID_GUI_SURFACEMEDIASOURCE_H
+
+#include <gui/ISurfaceTexture.h>
+
+#include <utils/threads.h>
+#include <utils/Vector.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MediaBuffer.h>
+
+namespace android {
+// ----------------------------------------------------------------------------
+
+class IGraphicBufferAlloc;
+class String8;
+class GraphicBuffer;
+
+class SurfaceMediaSource : public BnSurfaceTexture, public MediaSource,
+ public MediaBufferObserver {
+public:
+ enum { MIN_UNDEQUEUED_BUFFERS = 3 };
+ enum {
+ MIN_ASYNC_BUFFER_SLOTS = MIN_UNDEQUEUED_BUFFERS + 1,
+ MIN_SYNC_BUFFER_SLOTS = MIN_UNDEQUEUED_BUFFERS
+ };
+ enum { NUM_BUFFER_SLOTS = 32 };
+ enum { NO_CONNECTED_API = 0 };
+
+ struct FrameAvailableListener : public virtual RefBase {
+ // onFrameAvailable() is called from queueBuffer() is the FIFO is
+ // empty. You can use SurfaceMediaSource::getQueuedCount() to
+ // figure out if there are more frames waiting.
+ // This is called without any lock held can be called concurrently by
+ // multiple threads.
+ virtual void onFrameAvailable() = 0;
+ };
+
+ SurfaceMediaSource(uint32_t bufW, uint32_t bufH);
+
+ virtual ~SurfaceMediaSource();
+
+
+ // For the MediaSource interface for use by StageFrightRecorder:
+ virtual status_t start(MetaData *params = NULL);
+ virtual status_t stop();
+ virtual status_t read(
+ MediaBuffer **buffer, const ReadOptions *options = NULL);
+ virtual sp<MetaData> getFormat();
+
+ // Get / Set the frame rate used for encoding. Default fps = 30
+ void setFrameRate(uint32_t fps) ;
+ uint32_t getFrameRate( ) const;
+
+ // The call for the StageFrightRecorder to tell us that
+ // it is done using the MediaBuffer data so that its state
+ // can be set to FREE for dequeuing
+ virtual void signalBufferReturned(MediaBuffer* buffer);
+ // end of MediaSource interface
+
+ uint32_t getBufferCount( ) const { return mBufferCount;}
+
+
+ // setBufferCount updates the number of available buffer slots. After
+ // calling this all buffer slots are both unallocated and owned by the
+ // SurfaceMediaSource object (i.e. they are not owned by the client).
+ virtual status_t setBufferCount(int bufferCount);
+
+ virtual sp<GraphicBuffer> requestBuffer(int buf);
+
+ // dequeueBuffer gets the next buffer slot index for the client to use. If a
+ // buffer slot is available then that slot index is written to the location
+ // pointed to by the buf argument and a status of OK is returned. If no
+ // slot is available then a status of -EBUSY is returned and buf is
+ // unmodified.
+ virtual status_t dequeueBuffer(int *buf, uint32_t w, uint32_t h,
+ uint32_t format, uint32_t usage);
+
+ // queueBuffer returns a filled buffer to the SurfaceMediaSource. In addition, a
+ // timestamp must be provided for the buffer. The timestamp is in
+ // nanoseconds, and must be monotonically increasing. Its other semantics
+ // (zero point, etc) are client-dependent and should be documented by the
+ // client.
+ virtual status_t queueBuffer(int buf, int64_t timestamp,
+ uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform);
+ virtual void cancelBuffer(int buf);
+
+ // onFrameReceivedLocked informs the buffer consumers (StageFrightRecorder)
+ // or listeners that a frame has been received
+ // The buffer is not made available for dequeueing immediately. We need to
+ // wait to hear from StageFrightRecorder to set the buffer FREE
+ // Make sure this is called when the mutex is locked
+ virtual status_t onFrameReceivedLocked();
+
+ virtual status_t setScalingMode(int mode) { } // no op for encoding
+ virtual int query(int what, int* value);
+
+ // Just confirming to the ISurfaceTexture interface as of now
+ virtual status_t setCrop(const Rect& reg) { return OK; }
+ virtual status_t setTransform(uint32_t transform) {return OK;}
+
+ // setSynchronousMode set whether dequeueBuffer is synchronous or
+ // asynchronous. In synchronous mode, dequeueBuffer blocks until
+ // a buffer is available, the currently bound buffer can be dequeued and
+ // queued buffers will be retired in order.
+ // The default mode is synchronous.
+ // TODO: Clarify the minute differences bet sycn /async
+ // modes (S.Encoder vis-a-vis SurfaceTexture)
+ virtual status_t setSynchronousMode(bool enabled);
+
+ // connect attempts to connect a client API to the SurfaceMediaSource. This
+ // must be called before any other ISurfaceTexture methods are called except
+ // for getAllocator.
+ //
+ // This method will fail if the connect was previously called on the
+ // SurfaceMediaSource and no corresponding disconnect call was made.
+ virtual status_t connect(int api);
+
+ // disconnect attempts to disconnect a client API from the SurfaceMediaSource.
+ // Calling this method will cause any subsequent calls to other
+ // ISurfaceTexture methods to fail except for getAllocator and connect.
+ // Successfully calling connect after this will allow the other methods to
+ // succeed again.
+ //
+ // This method will fail if the the SurfaceMediaSource is not currently
+ // connected to the specified client API.
+ virtual status_t disconnect(int api);
+
+ // getqueuedCount returns the number of queued frames waiting in the
+ // FIFO. In asynchronous mode, this always returns 0 or 1 since
+ // frames are not accumulating in the FIFO.
+ size_t getQueuedCount() const;
+
+ // setBufferCountServer set the buffer count. If the client has requested
+ // a buffer count using setBufferCount, the server-buffer count will
+ // take effect once the client sets the count back to zero.
+ status_t setBufferCountServer(int bufferCount);
+
+ // getTimestamp retrieves the timestamp associated with the image
+ // set by the most recent call to updateFrameInfoLocked().
+ //
+ // The timestamp is in nanoseconds, and is monotonically increasing. Its
+ // other semantics (zero point, etc) are source-dependent and should be
+ // documented by the source.
+ int64_t getTimestamp();
+
+ // setFrameAvailableListener sets the listener object that will be notified
+ // when a new frame becomes available.
+ void setFrameAvailableListener(const sp<FrameAvailableListener>& listener);
+
+ // getAllocator retrieves the binder object that must be referenced as long
+ // as the GraphicBuffers dequeued from this SurfaceMediaSource are referenced.
+ // Holding this binder reference prevents SurfaceFlinger from freeing the
+ // buffers before the client is done with them.
+ sp<IBinder> getAllocator();
+
+
+ // getCurrentBuffer returns the buffer associated with the current image.
+ sp<GraphicBuffer> getCurrentBuffer() const;
+
+ // dump our state in a String
+ void dump(String8& result) const;
+ void dump(String8& result, const char* prefix, char* buffer,
+ size_t SIZE) const;
+
+ protected:
+
+ // freeAllBuffers frees the resources (both GraphicBuffer and EGLImage) for
+ // all slots.
+ void freeAllBuffers();
+ static bool isExternalFormat(uint32_t format);
+
+private:
+
+ status_t setBufferCountServerLocked(int bufferCount);
+
+ enum { INVALID_BUFFER_SLOT = -1 };
+
+ struct BufferSlot {
+
+ BufferSlot()
+ : mBufferState(BufferSlot::FREE),
+ mRequestBufferCalled(false),
+ mTimestamp(0) {
+ }
+
+ // mGraphicBuffer points to the buffer allocated for this slot or is
+ // NULL if no buffer has been allocated.
+ sp<GraphicBuffer> mGraphicBuffer;
+
+ // BufferState represents the different states in which a buffer slot
+ // can be.
+ enum BufferState {
+ // FREE indicates that the buffer is not currently being used and
+ // will not be used in the future until it gets dequeued and
+ // subseqently queued by the client.
+ FREE = 0,
+
+ // DEQUEUED indicates that the buffer has been dequeued by the
+ // client, but has not yet been queued or canceled. The buffer is
+ // considered 'owned' by the client, and the server should not use
+ // it for anything.
+ //
+ // Note that when in synchronous-mode (mSynchronousMode == true),
+ // the buffer that's currently attached to the texture may be
+ // dequeued by the client. That means that the current buffer can
+ // be in either the DEQUEUED or QUEUED state. In asynchronous mode,
+ // however, the current buffer is always in the QUEUED state.
+ DEQUEUED = 1,
+
+ // QUEUED indicates that the buffer has been queued by the client,
+ // and has not since been made available for the client to dequeue.
+ // Attaching the buffer to the texture does NOT transition the
+ // buffer away from the QUEUED state. However, in Synchronous mode
+ // the current buffer may be dequeued by the client under some
+ // circumstances. See the note about the current buffer in the
+ // documentation for DEQUEUED.
+ QUEUED = 2,
+ };
+
+ // mBufferState is the current state of this buffer slot.
+ BufferState mBufferState;
+
+ // mRequestBufferCalled is used for validating that the client did
+ // call requestBuffer() when told to do so. Technically this is not
+ // needed but useful for debugging and catching client bugs.
+ bool mRequestBufferCalled;
+
+ // mTimestamp is the current timestamp for this buffer slot. This gets
+ // to set by queueBuffer each time this slot is queued.
+ int64_t mTimestamp;
+ };
+
+ // mSlots is the array of buffer slots that must be mirrored on the client
+ // side. This allows buffer ownership to be transferred between the client
+ // and server without sending a GraphicBuffer over binder. The entire array
+ // is initialized to NULL at construction time, and buffers are allocated
+ // for a slot when requestBuffer is called with that slot's index.
+ BufferSlot mSlots[NUM_BUFFER_SLOTS];
+
+ // mDefaultWidth holds the default width of allocated buffers. It is used
+ // in requestBuffers() if a width and height of zero is specified.
+ uint32_t mDefaultWidth;
+
+ // mDefaultHeight holds the default height of allocated buffers. It is used
+ // in requestBuffers() if a width and height of zero is specified.
+ uint32_t mDefaultHeight;
+
+ // mPixelFormat holds the pixel format of allocated buffers. It is used
+ // in requestBuffers() if a format of zero is specified.
+ uint32_t mPixelFormat;
+
+ // mBufferCount is the number of buffer slots that the client and server
+ // must maintain. It defaults to MIN_ASYNC_BUFFER_SLOTS and can be changed
+ // by calling setBufferCount or setBufferCountServer
+ int mBufferCount;
+
+ // mClientBufferCount is the number of buffer slots requested by the
+ // client. The default is zero, which means the client doesn't care how
+ // many buffers there are
+ int mClientBufferCount;
+
+ // mServerBufferCount buffer count requested by the server-side
+ int mServerBufferCount;
+
+ // mCurrentSlot is the buffer slot index of the buffer that is currently
+ // being used by buffer consumer
+ // (e.g. StageFrightRecorder in the case of SurfaceMediaSource or GLTexture
+ // in the case of SurfaceTexture).
+ // It is initialized to INVALID_BUFFER_SLOT,
+ // indicating that no buffer slot is currently bound to the texture. Note,
+ // however, that a value of INVALID_BUFFER_SLOT does not necessarily mean
+ // that no buffer is bound to the texture. A call to setBufferCount will
+ // reset mCurrentTexture to INVALID_BUFFER_SLOT.
+ int mCurrentSlot;
+
+
+ // mCurrentBuf is the graphic buffer of the current slot to be used by
+ // buffer consumer. It's possible that this buffer is not associated
+ // with any buffer slot, so we must track it separately in order to
+ // properly use IGraphicBufferAlloc::freeAllGraphicBuffersExcept.
+ sp<GraphicBuffer> mCurrentBuf;
+
+
+ // mCurrentTimestamp is the timestamp for the current texture. It
+ // gets set to mLastQueuedTimestamp each time updateTexImage is called.
+ int64_t mCurrentTimestamp;
+
+ // mGraphicBufferAlloc is the connection to SurfaceFlinger that is used to
+ // allocate new GraphicBuffer objects.
+ sp<IGraphicBufferAlloc> mGraphicBufferAlloc;
+
+ // mFrameAvailableListener is the listener object that will be called when a
+ // new frame becomes available. If it is not NULL it will be called from
+ // queueBuffer.
+ sp<FrameAvailableListener> mFrameAvailableListener;
+
+ // mSynchronousMode whether we're in synchronous mode or not
+ bool mSynchronousMode;
+
+ // mConnectedApi indicates the API that is currently connected to this
+ // SurfaceTexture. It defaults to NO_CONNECTED_API (= 0), and gets updated
+ // by the connect and disconnect methods.
+ int mConnectedApi;
+
+ // mDequeueCondition condition used for dequeueBuffer in synchronous mode
+ mutable Condition mDequeueCondition;
+
+
+ // mQueue is a FIFO of queued buffers used in synchronous mode
+ typedef Vector<int> Fifo;
+ Fifo mQueue;
+
+ // mMutex is the mutex used to prevent concurrent access to the member
+ // variables of SurfaceMediaSource objects. It must be locked whenever the
+ // member variables are accessed.
+ mutable Mutex mMutex;
+
+ ////////////////////////// For MediaSource
+ // Set to a default of 30 fps if not specified by the client side
+ int32_t mFrameRate;
+
+ // mStarted is a flag to check if the recording has started
+ bool mStarted;
+
+ // mFrameAvailableCondition condition used to indicate whether there
+ // is a frame available for dequeuing
+ Condition mFrameAvailableCondition;
+ Condition mFrameCompleteCondition;
+
+ // Avoid copying and equating and default constructor
+ DISALLOW_IMPLICIT_CONSTRUCTORS(SurfaceMediaSource);
+};
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_GUI_SURFACEMEDIASOURCE_H
diff --git a/include/media/stagefright/openmax/OMX_IVCommon.h b/include/media/stagefright/openmax/OMX_IVCommon.h
index 7ed072b..97170d7 100644
--- a/include/media/stagefright/openmax/OMX_IVCommon.h
+++ b/include/media/stagefright/openmax/OMX_IVCommon.h
@@ -16,29 +16,29 @@
* -------------------------------------------------------------------
*/
/**
- * Copyright (c) 2008 The Khronos Group Inc.
- *
+ * Copyright (c) 2008 The Khronos Group Inc.
+ *
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject
- * to the following conditions:
+ * to the following conditions:
* The above copyright notice and this permission notice shall be included
- * in all copies or substantial portions of the Software.
- *
+ * in all copies or substantial portions of the Software.
+ *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
-/**
+/**
* @file OMX_IVCommon.h - OpenMax IL version 1.1.2
* The structures needed by Video and Image components to exchange
* parameters and configuration data with the components.
@@ -53,7 +53,7 @@
/**
* Each OMX header must include all required header files to allow the header
* to compile without errors. The includes below are required for this header
- * file to compile successfully
+ * file to compile successfully
*/
#include <OMX_Core.h>
@@ -64,8 +64,8 @@
*/
-/**
- * Enumeration defining possible uncompressed image/video formats.
+/**
+ * Enumeration defining possible uncompressed image/video formats.
*
* ENUMS:
* Unused : Placeholder value when format is N/A
@@ -113,7 +113,7 @@
OMX_COLOR_Format16bitBGR565,
OMX_COLOR_Format18bitRGB666,
OMX_COLOR_Format18bitARGB1665,
- OMX_COLOR_Format19bitARGB1666,
+ OMX_COLOR_Format19bitARGB1666,
OMX_COLOR_Format24bitRGB888,
OMX_COLOR_Format24bitBGR888,
OMX_COLOR_Format24bitARGB1887,
@@ -136,55 +136,62 @@
OMX_COLOR_FormatRawBayer8bit,
OMX_COLOR_FormatRawBayer10bit,
OMX_COLOR_FormatRawBayer8bitcompressed,
- OMX_COLOR_FormatL2,
- OMX_COLOR_FormatL4,
- OMX_COLOR_FormatL8,
- OMX_COLOR_FormatL16,
- OMX_COLOR_FormatL24,
+ OMX_COLOR_FormatL2,
+ OMX_COLOR_FormatL4,
+ OMX_COLOR_FormatL8,
+ OMX_COLOR_FormatL16,
+ OMX_COLOR_FormatL24,
OMX_COLOR_FormatL32,
OMX_COLOR_FormatYUV420PackedSemiPlanar,
OMX_COLOR_FormatYUV422PackedSemiPlanar,
OMX_COLOR_Format18BitBGR666,
OMX_COLOR_Format24BitARGB6666,
OMX_COLOR_Format24BitABGR6666,
- OMX_COLOR_FormatKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */
+ OMX_COLOR_FormatKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */
OMX_COLOR_FormatVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */
+ /**<Reserved android opaque colorformat. Tells the encoder that
+ * the actual colorformat will be relayed by the
+ * Gralloc Buffers.
+ * FIXME: In the process of reserving some enum values for
+ * Android-specific OMX IL colorformats. Change this enum to
+ * an acceptable range once that is done.*/
+ OMX_COLOR_FormatAndroidOpaque = 0x7F000001,
OMX_TI_COLOR_FormatYUV420PackedSemiPlanar = 0x7F000100,
OMX_QCOM_COLOR_FormatYVU420SemiPlanar = 0x7FA30C00,
OMX_COLOR_FormatMax = 0x7FFFFFFF
} OMX_COLOR_FORMATTYPE;
-/**
+/**
* Defines the matrix for conversion from RGB to YUV or vice versa.
- * iColorMatrix should be initialized with the fixed point values
+ * iColorMatrix should be initialized with the fixed point values
* used in converting between formats.
*/
typedef struct OMX_CONFIG_COLORCONVERSIONTYPE {
OMX_U32 nSize; /**< Size of the structure in bytes */
- OMX_VERSIONTYPE nVersion; /**< OMX specification version info */
+ OMX_VERSIONTYPE nVersion; /**< OMX specification version info */
OMX_U32 nPortIndex; /**< Port that this struct applies to */
OMX_S32 xColorMatrix[3][3]; /**< Stored in signed Q16 format */
OMX_S32 xColorOffset[4]; /**< Stored in signed Q16 format */
}OMX_CONFIG_COLORCONVERSIONTYPE;
-/**
- * Structure defining percent to scale each frame dimension. For example:
+/**
+ * Structure defining percent to scale each frame dimension. For example:
* To make the width 50% larger, use fWidth = 1.5 and to make the width
* 1/2 the original size, use fWidth = 0.5
*/
typedef struct OMX_CONFIG_SCALEFACTORTYPE {
OMX_U32 nSize; /**< Size of the structure in bytes */
- OMX_VERSIONTYPE nVersion; /**< OMX specification version info */
+ OMX_VERSIONTYPE nVersion; /**< OMX specification version info */
OMX_U32 nPortIndex; /**< Port that this struct applies to */
OMX_S32 xWidth; /**< Fixed point value stored as Q16 */
OMX_S32 xHeight; /**< Fixed point value stored as Q16 */
}OMX_CONFIG_SCALEFACTORTYPE;
-/**
- * Enumeration of possible image filter types
+/**
+ * Enumeration of possible image filter types
*/
typedef enum OMX_IMAGEFILTERTYPE {
OMX_ImageFilterNone,
@@ -195,23 +202,23 @@
OMX_ImageFilterOilPaint,
OMX_ImageFilterHatch,
OMX_ImageFilterGpen,
- OMX_ImageFilterAntialias,
- OMX_ImageFilterDeRing,
+ OMX_ImageFilterAntialias,
+ OMX_ImageFilterDeRing,
OMX_ImageFilterSolarize,
- OMX_ImageFilterKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */
+ OMX_ImageFilterKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */
OMX_ImageFilterVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */
OMX_ImageFilterMax = 0x7FFFFFFF
} OMX_IMAGEFILTERTYPE;
-/**
- * Image filter configuration
+/**
+ * Image filter configuration
*
* STRUCT MEMBERS:
- * nSize : Size of the structure in bytes
+ * nSize : Size of the structure in bytes
* nVersion : OMX specification version information
- * nPortIndex : Port that this structure applies to
- * eImageFilter : Image filter type enumeration
+ * nPortIndex : Port that this structure applies to
+ * eImageFilter : Image filter type enumeration
*/
typedef struct OMX_CONFIG_IMAGEFILTERTYPE {
OMX_U32 nSize;
@@ -221,22 +228,22 @@
} OMX_CONFIG_IMAGEFILTERTYPE;
-/**
- * Customized U and V for color enhancement
+/**
+ * Customized U and V for color enhancement
*
* STRUCT MEMBERS:
* nSize : Size of the structure in bytes
- * nVersion : OMX specification version information
+ * nVersion : OMX specification version information
* nPortIndex : Port that this structure applies to
* bColorEnhancement : Enable/disable color enhancement
- * nCustomizedU : Practical values: 16-240, range: 0-255, value set for
+ * nCustomizedU : Practical values: 16-240, range: 0-255, value set for
* U component
- * nCustomizedV : Practical values: 16-240, range: 0-255, value set for
+ * nCustomizedV : Practical values: 16-240, range: 0-255, value set for
* V component
*/
typedef struct OMX_CONFIG_COLORENHANCEMENTTYPE {
OMX_U32 nSize;
- OMX_VERSIONTYPE nVersion;
+ OMX_VERSIONTYPE nVersion;
OMX_U32 nPortIndex;
OMX_BOOL bColorEnhancement;
OMX_U8 nCustomizedU;
@@ -244,12 +251,12 @@
} OMX_CONFIG_COLORENHANCEMENTTYPE;
-/**
- * Define color key and color key mask
+/**
+ * Define color key and color key mask
*
* STRUCT MEMBERS:
* nSize : Size of the structure in bytes
- * nVersion : OMX specification version information
+ * nVersion : OMX specification version information
* nPortIndex : Port that this structure applies to
* nARGBColor : 32bit Alpha, Red, Green, Blue Color
* nARGBMask : 32bit Mask for Alpha, Red, Green, Blue channels
@@ -263,12 +270,12 @@
} OMX_CONFIG_COLORKEYTYPE;
-/**
- * List of color blend types for pre/post processing
+/**
+ * List of color blend types for pre/post processing
*
* ENUMS:
* None : No color blending present
- * AlphaConstant : Function is (alpha_constant * src) +
+ * AlphaConstant : Function is (alpha_constant * src) +
* (1 - alpha_constant) * dst)
* AlphaPerPixel : Function is (alpha * src) + (1 - alpha) * dst)
* Alternate : Function is alternating pixels from src and dst
@@ -284,21 +291,21 @@
OMX_ColorBlendAnd,
OMX_ColorBlendOr,
OMX_ColorBlendInvert,
- OMX_ColorBlendKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */
+ OMX_ColorBlendKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */
OMX_ColorBlendVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */
OMX_ColorBlendMax = 0x7FFFFFFF
} OMX_COLORBLENDTYPE;
-/**
- * Color blend configuration
+/**
+ * Color blend configuration
*
* STRUCT MEMBERS:
- * nSize : Size of the structure in bytes
- * nVersion : OMX specification version information
- * nPortIndex : Port that this structure applies to
+ * nSize : Size of the structure in bytes
+ * nVersion : OMX specification version information
+ * nPortIndex : Port that this structure applies to
* nRGBAlphaConstant : Constant global alpha values when global alpha is used
- * eColorBlend : Color blend type enumeration
+ * eColorBlend : Color blend type enumeration
*/
typedef struct OMX_CONFIG_COLORBLENDTYPE {
OMX_U32 nSize;
@@ -309,15 +316,15 @@
} OMX_CONFIG_COLORBLENDTYPE;
-/**
+/**
* Hold frame dimension
*
* STRUCT MEMBERS:
- * nSize : Size of the structure in bytes
+ * nSize : Size of the structure in bytes
* nVersion : OMX specification version information
- * nPortIndex : Port that this structure applies to
- * nWidth : Frame width in pixels
- * nHeight : Frame height in pixels
+ * nPortIndex : Port that this structure applies to
+ * nWidth : Frame width in pixels
+ * nHeight : Frame height in pixels
*/
typedef struct OMX_FRAMESIZETYPE {
OMX_U32 nSize;
@@ -329,69 +336,69 @@
/**
- * Rotation configuration
+ * Rotation configuration
*
* STRUCT MEMBERS:
- * nSize : Size of the structure in bytes
+ * nSize : Size of the structure in bytes
* nVersion : OMX specification version information
* nPortIndex : Port that this structure applies to
- * nRotation : +/- integer rotation value
+ * nRotation : +/- integer rotation value
*/
typedef struct OMX_CONFIG_ROTATIONTYPE {
OMX_U32 nSize;
OMX_VERSIONTYPE nVersion;
OMX_U32 nPortIndex;
- OMX_S32 nRotation;
+ OMX_S32 nRotation;
} OMX_CONFIG_ROTATIONTYPE;
-/**
- * Possible mirroring directions for pre/post processing
+/**
+ * Possible mirroring directions for pre/post processing
*
* ENUMS:
- * None : No mirroring
- * Vertical : Vertical mirroring, flip on X axis
- * Horizontal : Horizontal mirroring, flip on Y axis
+ * None : No mirroring
+ * Vertical : Vertical mirroring, flip on X axis
+ * Horizontal : Horizontal mirroring, flip on Y axis
* Both : Both vertical and horizontal mirroring
*/
typedef enum OMX_MIRRORTYPE {
OMX_MirrorNone = 0,
OMX_MirrorVertical,
OMX_MirrorHorizontal,
- OMX_MirrorBoth,
- OMX_MirrorKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */
+ OMX_MirrorBoth,
+ OMX_MirrorKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */
OMX_MirrorVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */
- OMX_MirrorMax = 0x7FFFFFFF
+ OMX_MirrorMax = 0x7FFFFFFF
} OMX_MIRRORTYPE;
-/**
- * Mirroring configuration
+/**
+ * Mirroring configuration
*
* STRUCT MEMBERS:
- * nSize : Size of the structure in bytes
+ * nSize : Size of the structure in bytes
* nVersion : OMX specification version information
- * nPortIndex : Port that this structure applies to
- * eMirror : Mirror type enumeration
+ * nPortIndex : Port that this structure applies to
+ * eMirror : Mirror type enumeration
*/
typedef struct OMX_CONFIG_MIRRORTYPE {
OMX_U32 nSize;
- OMX_VERSIONTYPE nVersion;
+ OMX_VERSIONTYPE nVersion;
OMX_U32 nPortIndex;
OMX_MIRRORTYPE eMirror;
} OMX_CONFIG_MIRRORTYPE;
-/**
- * Position information only
+/**
+ * Position information only
*
* STRUCT MEMBERS:
- * nSize : Size of the structure in bytes
+ * nSize : Size of the structure in bytes
* nVersion : OMX specification version information
* nPortIndex : Port that this structure applies to
- * nX : X coordinate for the point
- * nY : Y coordinate for the point
- */
+ * nX : X coordinate for the point
+ * nY : Y coordinate for the point
+ */
typedef struct OMX_CONFIG_POINTTYPE {
OMX_U32 nSize;
OMX_VERSIONTYPE nVersion;
@@ -401,37 +408,37 @@
} OMX_CONFIG_POINTTYPE;
-/**
- * Frame size plus position
+/**
+ * Frame size plus position
*
* STRUCT MEMBERS:
- * nSize : Size of the structure in bytes
- * nVersion : OMX specification version information
- * nPortIndex : Port that this structure applies to
+ * nSize : Size of the structure in bytes
+ * nVersion : OMX specification version information
+ * nPortIndex : Port that this structure applies to
* nLeft : X Coordinate of the top left corner of the rectangle
* nTop : Y Coordinate of the top left corner of the rectangle
- * nWidth : Width of the rectangle
- * nHeight : Height of the rectangle
+ * nWidth : Width of the rectangle
+ * nHeight : Height of the rectangle
*/
typedef struct OMX_CONFIG_RECTTYPE {
OMX_U32 nSize;
- OMX_VERSIONTYPE nVersion;
- OMX_U32 nPortIndex;
- OMX_S32 nLeft;
+ OMX_VERSIONTYPE nVersion;
+ OMX_U32 nPortIndex;
+ OMX_S32 nLeft;
OMX_S32 nTop;
OMX_U32 nWidth;
OMX_U32 nHeight;
} OMX_CONFIG_RECTTYPE;
-/**
- * Deblocking state; it is required to be set up before starting the codec
+/**
+ * Deblocking state; it is required to be set up before starting the codec
*
* STRUCT MEMBERS:
- * nSize : Size of the structure in bytes
- * nVersion : OMX specification version information
+ * nSize : Size of the structure in bytes
+ * nVersion : OMX specification version information
* nPortIndex : Port that this structure applies to
- * bDeblocking : Enable/disable deblocking mode
+ * bDeblocking : Enable/disable deblocking mode
*/
typedef struct OMX_PARAM_DEBLOCKINGTYPE {
OMX_U32 nSize;
@@ -441,13 +448,13 @@
} OMX_PARAM_DEBLOCKINGTYPE;
-/**
- * Stabilization state
+/**
+ * Stabilization state
*
* STRUCT MEMBERS:
- * nSize : Size of the structure in bytes
- * nVersion : OMX specification version information
- * nPortIndex : Port that this structure applies to
+ * nSize : Size of the structure in bytes
+ * nVersion : OMX specification version information
+ * nPortIndex : Port that this structure applies to
* bStab : Enable/disable frame stabilization state
*/
typedef struct OMX_CONFIG_FRAMESTABTYPE {
@@ -458,8 +465,8 @@
} OMX_CONFIG_FRAMESTABTYPE;
-/**
- * White Balance control type
+/**
+ * White Balance control type
*
* STRUCT MEMBERS:
* SunLight : Referenced in JSR-234
@@ -476,20 +483,20 @@
OMX_WhiteBalControlIncandescent,
OMX_WhiteBalControlFlash,
OMX_WhiteBalControlHorizon,
- OMX_WhiteBalControlKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */
+ OMX_WhiteBalControlKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */
OMX_WhiteBalControlVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */
OMX_WhiteBalControlMax = 0x7FFFFFFF
} OMX_WHITEBALCONTROLTYPE;
-/**
- * White Balance control configuration
+/**
+ * White Balance control configuration
*
* STRUCT MEMBERS:
- * nSize : Size of the structure in bytes
+ * nSize : Size of the structure in bytes
* nVersion : OMX specification version information
- * nPortIndex : Port that this structure applies to
- * eWhiteBalControl : White balance enumeration
+ * nPortIndex : Port that this structure applies to
+ * eWhiteBalControl : White balance enumeration
*/
typedef struct OMX_CONFIG_WHITEBALCONTROLTYPE {
OMX_U32 nSize;
@@ -499,8 +506,8 @@
} OMX_CONFIG_WHITEBALCONTROLTYPE;
-/**
- * Exposure control type
+/**
+ * Exposure control type
*/
typedef enum OMX_EXPOSURECONTROLTYPE {
OMX_ExposureControlOff = 0,
@@ -513,20 +520,20 @@
OMX_ExposureControlBeach,
OMX_ExposureControlLargeAperture,
OMX_ExposureControlSmallApperture,
- OMX_ExposureControlKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */
+ OMX_ExposureControlKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */
OMX_ExposureControlVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */
OMX_ExposureControlMax = 0x7FFFFFFF
} OMX_EXPOSURECONTROLTYPE;
-/**
- * White Balance control configuration
+/**
+ * White Balance control configuration
*
* STRUCT MEMBERS:
- * nSize : Size of the structure in bytes
+ * nSize : Size of the structure in bytes
* nVersion : OMX specification version information
- * nPortIndex : Port that this structure applies to
- * eExposureControl : Exposure control enumeration
+ * nPortIndex : Port that this structure applies to
+ * eExposureControl : Exposure control enumeration
*/
typedef struct OMX_CONFIG_EXPOSURECONTROLTYPE {
OMX_U32 nSize;
@@ -536,16 +543,16 @@
} OMX_CONFIG_EXPOSURECONTROLTYPE;
-/**
- * Defines sensor supported mode.
+/**
+ * Defines sensor supported mode.
*
* STRUCT MEMBERS:
- * nSize : Size of the structure in bytes
+ * nSize : Size of the structure in bytes
* nVersion : OMX specification version information
- * nPortIndex : Port that this structure applies to
- * nFrameRate : Single shot mode is indicated by a 0
+ * nPortIndex : Port that this structure applies to
+ * nFrameRate : Single shot mode is indicated by a 0
* bOneShot : Enable for single shot, disable for streaming
- * sFrameSize : Framesize
+ * sFrameSize : Framesize
*/
typedef struct OMX_PARAM_SENSORMODETYPE {
OMX_U32 nSize;
@@ -557,13 +564,13 @@
} OMX_PARAM_SENSORMODETYPE;
-/**
- * Defines contrast level
+/**
+ * Defines contrast level
*
* STRUCT MEMBERS:
- * nSize : Size of the structure in bytes
- * nVersion : OMX specification version information
- * nPortIndex : Port that this structure applies to
+ * nSize : Size of the structure in bytes
+ * nVersion : OMX specification version information
+ * nPortIndex : Port that this structure applies to
* nContrast : Values allowed for contrast -100 to 100, zero means no change
*/
typedef struct OMX_CONFIG_CONTRASTTYPE {
@@ -574,14 +581,14 @@
} OMX_CONFIG_CONTRASTTYPE;
-/**
- * Defines brightness level
+/**
+ * Defines brightness level
*
* STRUCT MEMBERS:
- * nSize : Size of the structure in bytes
- * nVersion : OMX specification version information
- * nPortIndex : Port that this structure applies to
- * nBrightness : 0-100%
+ * nSize : Size of the structure in bytes
+ * nVersion : OMX specification version information
+ * nPortIndex : Port that this structure applies to
+ * nBrightness : 0-100%
*/
typedef struct OMX_CONFIG_BRIGHTNESSTYPE {
OMX_U32 nSize;
@@ -591,16 +598,16 @@
} OMX_CONFIG_BRIGHTNESSTYPE;
-/**
- * Defines backlight level configuration for a video sink, e.g. LCD panel
+/**
+ * Defines backlight level configuration for a video sink, e.g. LCD panel
*
* STRUCT MEMBERS:
* nSize : Size of the structure in bytes
- * nVersion : OMX specification version information
+ * nVersion : OMX specification version information
* nPortIndex : Port that this structure applies to
* nBacklight : Values allowed for backlight 0-100%
- * nTimeout : Number of milliseconds before backlight automatically turns
- * off. A value of 0x0 disables backight timeout
+ * nTimeout : Number of milliseconds before backlight automatically turns
+ * off. A value of 0x0 disables backight timeout
*/
typedef struct OMX_CONFIG_BACKLIGHTTYPE {
OMX_U32 nSize;
@@ -611,12 +618,12 @@
} OMX_CONFIG_BACKLIGHTTYPE;
-/**
- * Defines setting for Gamma
+/**
+ * Defines setting for Gamma
*
* STRUCT MEMBERS:
* nSize : Size of the structure in bytes
- * nVersion : OMX specification version information
+ * nVersion : OMX specification version information
* nPortIndex : Port that this structure applies to
* nGamma : Values allowed for gamma -100 to 100, zero means no change
*/
@@ -628,14 +635,14 @@
} OMX_CONFIG_GAMMATYPE;
-/**
- * Define for setting saturation
- *
+/**
+ * Define for setting saturation
+ *
* STRUCT MEMBERS:
* nSize : Size of the structure in bytes
* nVersion : OMX specification version information
* nPortIndex : Port that this structure applies to
- * nSaturation : Values allowed for saturation -100 to 100, zero means
+ * nSaturation : Values allowed for saturation -100 to 100, zero means
* no change
*/
typedef struct OMX_CONFIG_SATURATIONTYPE {
@@ -646,14 +653,14 @@
} OMX_CONFIG_SATURATIONTYPE;
-/**
- * Define for setting Lightness
+/**
+ * Define for setting Lightness
*
* STRUCT MEMBERS:
* nSize : Size of the structure in bytes
* nVersion : OMX specification version information
* nPortIndex : Port that this structure applies to
- * nLightness : Values allowed for lightness -100 to 100, zero means no
+ * nLightness : Values allowed for lightness -100 to 100, zero means no
* change
*/
typedef struct OMX_CONFIG_LIGHTNESSTYPE {
@@ -664,17 +671,17 @@
} OMX_CONFIG_LIGHTNESSTYPE;
-/**
- * Plane blend configuration
+/**
+ * Plane blend configuration
*
* STRUCT MEMBERS:
- * nSize : Size of the structure in bytes
+ * nSize : Size of the structure in bytes
* nVersion : OMX specification version information
* nPortIndex : Index of input port associated with the plane.
- * nDepth : Depth of the plane in relation to the screen. Higher
- * numbered depths are "behind" lower number depths.
+ * nDepth : Depth of the plane in relation to the screen. Higher
+ * numbered depths are "behind" lower number depths.
* This number defaults to the Port Index number.
- * nAlpha : Transparency blending component for the entire plane.
+ * nAlpha : Transparency blending component for the entire plane.
* See blending modes for more detail.
*/
typedef struct OMX_CONFIG_PLANEBLENDTYPE {
@@ -686,17 +693,17 @@
} OMX_CONFIG_PLANEBLENDTYPE;
-/**
+/**
* Define interlace type
*
* STRUCT MEMBERS:
- * nSize : Size of the structure in bytes
- * nVersion : OMX specification version information
+ * nSize : Size of the structure in bytes
+ * nVersion : OMX specification version information
* nPortIndex : Port that this structure applies to
- * bEnable : Enable control variable for this functionality
+ * bEnable : Enable control variable for this functionality
* (see below)
- * nInterleavePortIndex : Index of input or output port associated with
- * the interleaved plane.
+ * nInterleavePortIndex : Index of input or output port associated with
+ * the interleaved plane.
* pPlanarPortIndexes[4] : Index of input or output planar ports.
*/
typedef struct OMX_PARAM_INTERLEAVETYPE {
@@ -708,8 +715,8 @@
} OMX_PARAM_INTERLEAVETYPE;
-/**
- * Defines the picture effect used for an input picture
+/**
+ * Defines the picture effect used for an input picture
*/
typedef enum OMX_TRANSITIONEFFECTTYPE {
OMX_EffectNone,
@@ -719,18 +726,18 @@
OMX_EffectDissolve,
OMX_EffectWipe,
OMX_EffectUnspecifiedMixOfTwoScenes,
- OMX_EffectKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */
+ OMX_EffectKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */
OMX_EffectVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */
OMX_EffectMax = 0x7FFFFFFF
} OMX_TRANSITIONEFFECTTYPE;
-/**
- * Structure used to configure current transition effect
+/**
+ * Structure used to configure current transition effect
*
* STRUCT MEMBERS:
* nSize : Size of the structure in bytes
- * nVersion : OMX specification version information
+ * nVersion : OMX specification version information
* nPortIndex : Port that this structure applies to
* eEffect : Effect to enable
*/
@@ -742,43 +749,43 @@
} OMX_CONFIG_TRANSITIONEFFECTTYPE;
-/**
- * Defines possible data unit types for encoded video data. The data unit
+/**
+ * Defines possible data unit types for encoded video data. The data unit
* types are used both for encoded video input for playback as well as
- * encoded video output from recording.
+ * encoded video output from recording.
*/
typedef enum OMX_DATAUNITTYPE {
OMX_DataUnitCodedPicture,
OMX_DataUnitVideoSegment,
OMX_DataUnitSeveralSegments,
OMX_DataUnitArbitraryStreamSection,
- OMX_DataUnitKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */
+ OMX_DataUnitKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */
OMX_DataUnitVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */
OMX_DataUnitMax = 0x7FFFFFFF
} OMX_DATAUNITTYPE;
-/**
- * Defines possible encapsulation types for coded video data unit. The
- * encapsulation information is used both for encoded video input for
- * playback as well as encoded video output from recording.
+/**
+ * Defines possible encapsulation types for coded video data unit. The
+ * encapsulation information is used both for encoded video input for
+ * playback as well as encoded video output from recording.
*/
typedef enum OMX_DATAUNITENCAPSULATIONTYPE {
OMX_DataEncapsulationElementaryStream,
OMX_DataEncapsulationGenericPayload,
OMX_DataEncapsulationRtpPayload,
- OMX_DataEncapsulationKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */
+ OMX_DataEncapsulationKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */
OMX_DataEncapsulationVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */
OMX_DataEncapsulationMax = 0x7FFFFFFF
} OMX_DATAUNITENCAPSULATIONTYPE;
-/**
- * Structure used to configure the type of being decoded/encoded
+/**
+ * Structure used to configure the type of being decoded/encoded
*/
typedef struct OMX_PARAM_DATAUNITTYPE {
OMX_U32 nSize; /**< Size of the structure in bytes */
- OMX_VERSIONTYPE nVersion; /**< OMX specification version information */
+ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */
OMX_U32 nPortIndex; /**< Port that this structure applies to */
OMX_DATAUNITTYPE eUnitType;
OMX_DATAUNITENCAPSULATIONTYPE eEncapsulationType;
@@ -786,25 +793,25 @@
/**
- * Defines dither types
+ * Defines dither types
*/
typedef enum OMX_DITHERTYPE {
OMX_DitherNone,
OMX_DitherOrdered,
OMX_DitherErrorDiffusion,
OMX_DitherOther,
- OMX_DitherKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */
+ OMX_DitherKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */
OMX_DitherVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */
OMX_DitherMax = 0x7FFFFFFF
} OMX_DITHERTYPE;
-/**
- * Structure used to configure current type of dithering
+/**
+ * Structure used to configure current type of dithering
*/
typedef struct OMX_CONFIG_DITHERTYPE {
OMX_U32 nSize; /**< Size of the structure in bytes */
- OMX_VERSIONTYPE nVersion; /**< OMX specification version information */
+ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */
OMX_U32 nPortIndex; /**< Port that this structure applies to */
OMX_DITHERTYPE eDither; /**< Type of dithering to use */
} OMX_CONFIG_DITHERTYPE;
@@ -813,28 +820,28 @@
OMX_U32 nSize;
OMX_VERSIONTYPE nVersion;
OMX_U32 nPortIndex; /**< Port that this structure applies to */
- OMX_BOOL bContinuous; /**< If true then ignore frame rate and emit capture
+ OMX_BOOL bContinuous; /**< If true then ignore frame rate and emit capture
* data as fast as possible (otherwise obey port's frame rate). */
- OMX_BOOL bFrameLimited; /**< If true then terminate capture after the port emits the
- * specified number of frames (otherwise the port does not
- * terminate the capture until instructed to do so by the client).
- * Even if set, the client may manually terminate the capture prior
+ OMX_BOOL bFrameLimited; /**< If true then terminate capture after the port emits the
+ * specified number of frames (otherwise the port does not
+ * terminate the capture until instructed to do so by the client).
+ * Even if set, the client may manually terminate the capture prior
* to reaching the limit. */
OMX_U32 nFrameLimit; /**< Limit on number of frames emitted during a capture (only
* valid if bFrameLimited is set). */
} OMX_CONFIG_CAPTUREMODETYPE;
typedef enum OMX_METERINGTYPE {
-
+
OMX_MeteringModeAverage, /**< Center-weighted average metering. */
OMX_MeteringModeSpot, /**< Spot (partial) metering. */
OMX_MeteringModeMatrix, /**< Matrix or evaluative metering. */
-
- OMX_MeteringKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */
+
+ OMX_MeteringKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */
OMX_MeteringVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */
OMX_EVModeMax = 0x7fffffff
} OMX_METERINGTYPE;
-
+
typedef struct OMX_CONFIG_EXPOSUREVALUETYPE {
OMX_U32 nSize;
OMX_VERSIONTYPE nVersion;
@@ -843,14 +850,14 @@
OMX_S32 xEVCompensation; /**< Fixed point value stored as Q16 */
OMX_U32 nApertureFNumber; /**< e.g. nApertureFNumber = 2 implies "f/2" - Q16 format */
OMX_BOOL bAutoAperture; /**< Whether aperture number is defined automatically */
- OMX_U32 nShutterSpeedMsec; /**< Shutterspeed in milliseconds */
- OMX_BOOL bAutoShutterSpeed; /**< Whether shutter speed is defined automatically */
+ OMX_U32 nShutterSpeedMsec; /**< Shutterspeed in milliseconds */
+ OMX_BOOL bAutoShutterSpeed; /**< Whether shutter speed is defined automatically */
OMX_U32 nSensitivity; /**< e.g. nSensitivity = 100 implies "ISO 100" */
OMX_BOOL bAutoSensitivity; /**< Whether sensitivity is defined automatically */
} OMX_CONFIG_EXPOSUREVALUETYPE;
-/**
- * Focus region configuration
+/**
+ * Focus region configuration
*
* STRUCT MEMBERS:
* nSize : Size of the structure in bytes
@@ -881,8 +888,8 @@
OMX_BOOL bBottomRight;
} OMX_CONFIG_FOCUSREGIONTYPE;
-/**
- * Focus Status type
+/**
+ * Focus Status type
*/
typedef enum OMX_FOCUSSTATUSTYPE {
OMX_FocusStatusOff = 0,
@@ -890,13 +897,13 @@
OMX_FocusStatusReached,
OMX_FocusStatusUnableToReach,
OMX_FocusStatusLost,
- OMX_FocusStatusKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */
+ OMX_FocusStatusKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */
OMX_FocusStatusVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */
OMX_FocusStatusMax = 0x7FFFFFFF
} OMX_FOCUSSTATUSTYPE;
-/**
- * Focus status configuration
+/**
+ * Focus status configuration
*
* STRUCT MEMBERS:
* nSize : Size of the structure in bytes
diff --git a/include/ui/PixelFormat.h b/include/ui/PixelFormat.h
index f46f25c..848c5a1 100644
--- a/include/ui/PixelFormat.h
+++ b/include/ui/PixelFormat.h
@@ -55,7 +55,7 @@
PIXEL_FORMAT_OPAQUE = -1,
// System chooses an opaque format (no alpha bits required)
-
+
// real pixel formats supported for rendering -----------------------------
PIXEL_FORMAT_RGBA_8888 = HAL_PIXEL_FORMAT_RGBA_8888, // 4x8-bit RGBA
@@ -84,7 +84,7 @@
INDEX_GREEN = 2,
INDEX_BLUE = 3
};
-
+
enum { // components
ALPHA = 1,
RGB = 2,
@@ -98,10 +98,10 @@
uint8_t h;
uint8_t l;
};
-
+
inline PixelFormatInfo() : version(sizeof(PixelFormatInfo)) { }
size_t getScanlineSize(unsigned int width) const;
- size_t getSize(size_t ci) const {
+ size_t getSize(size_t ci) const {
return (ci <= 3) ? (cinfo[ci].h - cinfo[ci].l) : 0;
}
size_t version;
@@ -112,7 +112,7 @@
szinfo cinfo[4];
struct {
uint8_t h_alpha;
- uint8_t l_alpha;
+ uint8_t l_alpha;
uint8_t h_red;
uint8_t l_red;
uint8_t h_green;
diff --git a/libs/gui/SurfaceTextureClient.cpp b/libs/gui/SurfaceTextureClient.cpp
index b4d76bb..688b99b 100644
--- a/libs/gui/SurfaceTextureClient.cpp
+++ b/libs/gui/SurfaceTextureClient.cpp
@@ -55,6 +55,9 @@
mQueryWidth = 0;
mQueryHeight = 0;
mQueryFormat = 0;
+ mDefaultWidth = 0;
+ mDefaultHeight = 0;
+ mTransformHint = 0;
mConnectedToCpu = false;
}
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index 8b1caeee..886c05c 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -883,7 +883,6 @@
///////////////////////////////////////////////////////////////////////////////
DisplayListRenderer::DisplayListRenderer(): mWriter(MIN_WRITER_SIZE) {
- mDisplayList = NULL;
}
DisplayListRenderer::~DisplayListRenderer() {
@@ -923,13 +922,13 @@
// Operations
///////////////////////////////////////////////////////////////////////////////
-DisplayList* DisplayListRenderer::getDisplayList() {
- if (mDisplayList == NULL) {
- mDisplayList = new DisplayList(*this);
+DisplayList* DisplayListRenderer::getDisplayList(DisplayList* displayList) {
+ if (!displayList) {
+ displayList = new DisplayList(*this);
} else {
- mDisplayList->initFromDisplayListRenderer(*this, true);
+ displayList->initFromDisplayListRenderer(*this, true);
}
- return mDisplayList;
+ return displayList;
}
void DisplayListRenderer::setViewport(int width, int height) {
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index b83259f..8157631 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -217,7 +217,7 @@
DisplayListRenderer();
~DisplayListRenderer();
- DisplayList* getDisplayList();
+ DisplayList* getDisplayList(DisplayList* displayList);
void setViewport(int width, int height);
void prepareDirty(float left, float top, float right, float bottom, bool opaque);
@@ -474,8 +474,6 @@
SkWriter32 mWriter;
- DisplayList *mDisplayList;
-
int mRestoreSaveCount;
friend class DisplayList;
diff --git a/libs/ui/FramebufferNativeWindow.cpp b/libs/ui/FramebufferNativeWindow.cpp
index 412552e..0e8ae61 100644
--- a/libs/ui/FramebufferNativeWindow.cpp
+++ b/libs/ui/FramebufferNativeWindow.cpp
@@ -310,35 +310,21 @@
int operation, ...)
{
switch (operation) {
- case NATIVE_WINDOW_SET_USAGE:
- // TODO: we should implement this
- return NO_ERROR;
case NATIVE_WINDOW_CONNECT:
- // TODO: we should implement this
- return NO_ERROR;
case NATIVE_WINDOW_DISCONNECT:
- // TODO: we should implement this
- return NO_ERROR;
- case NATIVE_WINDOW_LOCK:
- return INVALID_OPERATION;
- case NATIVE_WINDOW_UNLOCK_AND_POST:
- return INVALID_OPERATION;
- case NATIVE_WINDOW_SET_CROP:
- return INVALID_OPERATION;
- case NATIVE_WINDOW_SET_BUFFER_COUNT:
- // TODO: we should implement this
- return INVALID_OPERATION;
+ case NATIVE_WINDOW_SET_USAGE:
case NATIVE_WINDOW_SET_BUFFERS_GEOMETRY:
- return INVALID_OPERATION;
- case NATIVE_WINDOW_SET_BUFFERS_TRANSFORM:
- return INVALID_OPERATION;
- case NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP:
- return INVALID_OPERATION;
case NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS:
- return INVALID_OPERATION;
case NATIVE_WINDOW_SET_BUFFERS_FORMAT:
- // TODO: we should implement this
+ case NATIVE_WINDOW_SET_BUFFERS_TRANSFORM:
+ // TODO: we should implement these
return NO_ERROR;
+
+ case NATIVE_WINDOW_LOCK:
+ case NATIVE_WINDOW_UNLOCK_AND_POST:
+ case NATIVE_WINDOW_SET_CROP:
+ case NATIVE_WINDOW_SET_BUFFER_COUNT:
+ case NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP:
case NATIVE_WINDOW_SET_SCALING_MODE:
return INVALID_OPERATION;
}
diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp
index ed6e3c7..a11fb80 100644
--- a/media/libmedia/mediaplayer.cpp
+++ b/media/libmedia/mediaplayer.cpp
@@ -228,6 +228,7 @@
NATIVE_WINDOW_API_MEDIA);
if (err != OK) {
+ LOGE("setVideoSurface failed: %d", err);
// Note that we must do the reset before disconnecting from the ANW.
// Otherwise queue/dequeue calls could be made on the disconnected
// ANW, which may result in errors.
@@ -277,6 +278,7 @@
NATIVE_WINDOW_API_MEDIA);
if (err != OK) {
+ LOGE("setVideoSurfaceTexture failed: %d", err);
// Note that we must do the reset before disconnecting from the ANW.
// Otherwise queue/dequeue calls could be made on the disconnected
// ANW, which may result in errors.
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index e17e1e8..3a3c082 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -42,6 +42,7 @@
SampleTable.cpp \
StagefrightMediaScanner.cpp \
StagefrightMetadataRetriever.cpp \
+ SurfaceMediaSource.cpp \
ThrottledSource.cpp \
TimeSource.cpp \
TimedEventQueue.cpp \
diff --git a/media/libstagefright/NuCachedSource2.cpp b/media/libstagefright/NuCachedSource2.cpp
index 77a6602..4edb613 100644
--- a/media/libstagefright/NuCachedSource2.cpp
+++ b/media/libstagefright/NuCachedSource2.cpp
@@ -185,7 +185,8 @@
mFinalStatus(OK),
mLastAccessPos(0),
mFetching(true),
- mLastFetchTimeUs(-1) {
+ mLastFetchTimeUs(-1),
+ mNumRetriesLeft(kMaxNumRetries) {
mLooper->setName("NuCachedSource2");
mLooper->registerHandler(mReflector);
mLooper->start();
@@ -254,7 +255,27 @@
void NuCachedSource2::fetchInternal() {
LOGV("fetchInternal");
- CHECK_EQ(mFinalStatus, (status_t)OK);
+ {
+ Mutex::Autolock autoLock(mLock);
+ CHECK(mFinalStatus == OK || mNumRetriesLeft > 0);
+
+ if (mFinalStatus != OK) {
+ --mNumRetriesLeft;
+
+ status_t err =
+ mSource->reconnectAtOffset(mCacheOffset + mCache->totalSize());
+
+ if (err == ERROR_UNSUPPORTED) {
+ mNumRetriesLeft = 0;
+ return;
+ } else if (err != OK) {
+ LOGI("The attempt to reconnect failed, %d retries remaining",
+ mNumRetriesLeft);
+
+ return;
+ }
+ }
+ }
PageCache::Page *page = mCache->acquirePage();
@@ -264,14 +285,23 @@
Mutex::Autolock autoLock(mLock);
if (n < 0) {
- LOGE("source returned error %ld", n);
+ LOGE("source returned error %ld, %d retries left", n, mNumRetriesLeft);
mFinalStatus = n;
mCache->releasePage(page);
} else if (n == 0) {
LOGI("ERROR_END_OF_STREAM");
+
+ mNumRetriesLeft = 0;
mFinalStatus = ERROR_END_OF_STREAM;
+
mCache->releasePage(page);
} else {
+ if (mFinalStatus != OK) {
+ LOGI("retrying a previously failed read succeeded.");
+ }
+ mNumRetriesLeft = kMaxNumRetries;
+ mFinalStatus = OK;
+
page->mSize = n;
mCache->appendPage(page);
}
@@ -280,7 +310,7 @@
void NuCachedSource2::onFetch() {
LOGV("onFetch");
- if (mFinalStatus != OK) {
+ if (mFinalStatus != OK && mNumRetriesLeft == 0) {
LOGV("EOS reached, done prefetching for now");
mFetching = false;
}
@@ -308,8 +338,19 @@
restartPrefetcherIfNecessary_l();
}
- (new AMessage(kWhatFetchMore, mReflector->id()))->post(
- mFetching ? 0 : 100000ll);
+ int64_t delayUs;
+ if (mFetching) {
+ if (mFinalStatus != OK && mNumRetriesLeft > 0) {
+ // We failed this time and will try again in 3 seconds.
+ delayUs = 3000000ll;
+ } else {
+ delayUs = 0;
+ }
+ } else {
+ delayUs = 100000ll;
+ }
+
+ (new AMessage(kWhatFetchMore, mReflector->id()))->post(delayUs);
}
void NuCachedSource2::onRead(const sp<AMessage> &msg) {
@@ -345,7 +386,7 @@
bool ignoreLowWaterThreshold, bool force) {
static const size_t kGrayArea = 1024 * 1024;
- if (mFetching || mFinalStatus != OK) {
+ if (mFetching || (mFinalStatus != OK && mNumRetriesLeft == 0)) {
return;
}
@@ -427,6 +468,12 @@
size_t NuCachedSource2::approxDataRemaining_l(status_t *finalStatus) {
*finalStatus = mFinalStatus;
+
+ if (mFinalStatus != OK && mNumRetriesLeft > 0) {
+ // Pretend that everything is fine until we're out of retries.
+ *finalStatus = OK;
+ }
+
off64_t lastBytePosCached = mCacheOffset + mCache->totalSize();
if (mLastAccessPos < lastBytePosCached) {
return lastBytePosCached - mLastAccessPos;
diff --git a/media/libstagefright/SurfaceMediaSource.cpp b/media/libstagefright/SurfaceMediaSource.cpp
new file mode 100644
index 0000000..db208dd
--- /dev/null
+++ b/media/libstagefright/SurfaceMediaSource.cpp
@@ -0,0 +1,752 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// #define LOG_NDEBUG 0
+#define LOG_TAG "SurfaceMediaSource"
+
+#include <media/stagefright/SurfaceMediaSource.h>
+#include <ui/GraphicBuffer.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/openmax/OMX_IVCommon.h>
+
+#include <surfaceflinger/ISurfaceComposer.h>
+#include <surfaceflinger/SurfaceComposerClient.h>
+#include <surfaceflinger/IGraphicBufferAlloc.h>
+#include <OMX_Component.h>
+
+#include <utils/Log.h>
+#include <utils/String8.h>
+
+namespace android {
+
+SurfaceMediaSource::SurfaceMediaSource(uint32_t bufW, uint32_t bufH) :
+ mDefaultWidth(bufW),
+ mDefaultHeight(bufH),
+ mPixelFormat(0),
+ mBufferCount(MIN_ASYNC_BUFFER_SLOTS),
+ mClientBufferCount(0),
+ mServerBufferCount(MIN_ASYNC_BUFFER_SLOTS),
+ mCurrentSlot(INVALID_BUFFER_SLOT),
+ mCurrentTimestamp(0),
+ mSynchronousMode(true),
+ mConnectedApi(NO_CONNECTED_API),
+ mFrameRate(30),
+ mStarted(false) {
+ LOGV("SurfaceMediaSource::SurfaceMediaSource");
+ sp<ISurfaceComposer> composer(ComposerService::getComposerService());
+ mGraphicBufferAlloc = composer->createGraphicBufferAlloc();
+}
+
+SurfaceMediaSource::~SurfaceMediaSource() {
+ LOGV("SurfaceMediaSource::~SurfaceMediaSource");
+ if (mStarted) {
+ stop();
+ }
+ freeAllBuffers();
+}
+
+size_t SurfaceMediaSource::getQueuedCount() const {
+ Mutex::Autolock lock(mMutex);
+ return mQueue.size();
+}
+
+status_t SurfaceMediaSource::setBufferCountServerLocked(int bufferCount) {
+ if (bufferCount > NUM_BUFFER_SLOTS)
+ return BAD_VALUE;
+
+ // special-case, nothing to do
+ if (bufferCount == mBufferCount)
+ return OK;
+
+ if (!mClientBufferCount &&
+ bufferCount >= mBufferCount) {
+ // easy, we just have more buffers
+ mBufferCount = bufferCount;
+ mServerBufferCount = bufferCount;
+ mDequeueCondition.signal();
+ } else {
+ // we're here because we're either
+ // - reducing the number of available buffers
+ // - or there is a client-buffer-count in effect
+
+ // less than 2 buffers is never allowed
+ if (bufferCount < 2)
+ return BAD_VALUE;
+
+ // when there is non client-buffer-count in effect, the client is not
+ // allowed to dequeue more than one buffer at a time,
+ // so the next time they dequeue a buffer, we know that they don't
+ // own one. the actual resizing will happen during the next
+ // dequeueBuffer.
+
+ mServerBufferCount = bufferCount;
+ }
+ return OK;
+}
+
+// Called from the consumer side
+status_t SurfaceMediaSource::setBufferCountServer(int bufferCount) {
+ Mutex::Autolock lock(mMutex);
+ return setBufferCountServerLocked(bufferCount);
+}
+
+status_t SurfaceMediaSource::setBufferCount(int bufferCount) {
+ LOGV("SurfaceMediaSource::setBufferCount");
+ if (bufferCount > NUM_BUFFER_SLOTS) {
+ LOGE("setBufferCount: bufferCount is larger than the number of buffer slots");
+ return BAD_VALUE;
+ }
+
+ Mutex::Autolock lock(mMutex);
+ // Error out if the user has dequeued buffers
+ for (int i = 0 ; i < mBufferCount ; i++) {
+ if (mSlots[i].mBufferState == BufferSlot::DEQUEUED) {
+ LOGE("setBufferCount: client owns some buffers");
+ return INVALID_OPERATION;
+ }
+ }
+
+ if (bufferCount == 0) {
+ const int minBufferSlots = mSynchronousMode ?
+ MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS;
+ mClientBufferCount = 0;
+ bufferCount = (mServerBufferCount >= minBufferSlots) ?
+ mServerBufferCount : minBufferSlots;
+ return setBufferCountServerLocked(bufferCount);
+ }
+
+ // We don't allow the client to set a buffer-count less than
+ // MIN_ASYNC_BUFFER_SLOTS (3), there is no reason for it.
+ if (bufferCount < MIN_ASYNC_BUFFER_SLOTS) {
+ return BAD_VALUE;
+ }
+
+ // here we're guaranteed that the client doesn't have dequeued buffers
+ // and will release all of its buffer references.
+ freeAllBuffers();
+ mBufferCount = bufferCount;
+ mClientBufferCount = bufferCount;
+ mCurrentSlot = INVALID_BUFFER_SLOT;
+ mQueue.clear();
+ mDequeueCondition.signal();
+ return OK;
+}
+
+sp<GraphicBuffer> SurfaceMediaSource::requestBuffer(int buf) {
+ LOGV("SurfaceMediaSource::requestBuffer");
+ Mutex::Autolock lock(mMutex);
+ if (buf < 0 || mBufferCount <= buf) {
+ LOGE("requestBuffer: slot index out of range [0, %d]: %d",
+ mBufferCount, buf);
+ return 0;
+ }
+ mSlots[buf].mRequestBufferCalled = true;
+ return mSlots[buf].mGraphicBuffer;
+}
+
+status_t SurfaceMediaSource::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
+ uint32_t format, uint32_t usage) {
+ LOGV("dequeueBuffer");
+
+
+ // Check for the buffer size- the client should just use the
+ // default width and height, and not try to set those.
+ // This is needed since
+ // the getFormat() returns mDefaultWidth/ Height for the OMX. It is
+ // queried by OMX in the beginning and not every time a frame comes.
+ // Not sure if there is a way to update the
+ // frame size while recording. So as of now, the client side
+ // sets the default values via the constructor, and the encoder is
+ // setup to encode frames of that size
+ // The design might need to change in the future.
+ // TODO: Currently just uses mDefaultWidth/Height. In the future
+ // we might declare mHeight and mWidth and check against those here.
+ if ((w != 0) || (h != 0)) {
+ LOGE("dequeuebuffer: invalid buffer size! Req: %dx%d, Found: %dx%d",
+ mDefaultWidth, mDefaultHeight, w, h);
+ return BAD_VALUE;
+ }
+
+ Mutex::Autolock lock(mMutex);
+
+ status_t returnFlags(OK);
+
+ int found, foundSync;
+ int dequeuedCount = 0;
+ bool tryAgain = true;
+ while (tryAgain) {
+ // We need to wait for the FIFO to drain if the number of buffer
+ // needs to change.
+ //
+ // The condition "number of buffer needs to change" is true if
+ // - the client doesn't care about how many buffers there are
+ // - AND the actual number of buffer is different from what was
+ // set in the last setBufferCountServer()
+ // - OR -
+ // setBufferCountServer() was set to a value incompatible with
+ // the synchronization mode (for instance because the sync mode
+ // changed since)
+ //
+ // As long as this condition is true AND the FIFO is not empty, we
+ // wait on mDequeueCondition.
+
+ int minBufferCountNeeded = mSynchronousMode ?
+ MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS;
+
+ if (!mClientBufferCount &&
+ ((mServerBufferCount != mBufferCount) ||
+ (mServerBufferCount < minBufferCountNeeded))) {
+ // wait for the FIFO to drain
+ while (!mQueue.isEmpty()) {
+ LOGV("Waiting for the FIFO to drain");
+ mDequeueCondition.wait(mMutex);
+ }
+ // need to check again since the mode could have changed
+ // while we were waiting
+ minBufferCountNeeded = mSynchronousMode ?
+ MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS;
+ }
+
+ if (!mClientBufferCount &&
+ ((mServerBufferCount != mBufferCount) ||
+ (mServerBufferCount < minBufferCountNeeded))) {
+ // here we're guaranteed that mQueue is empty
+ freeAllBuffers();
+ mBufferCount = mServerBufferCount;
+ if (mBufferCount < minBufferCountNeeded)
+ mBufferCount = minBufferCountNeeded;
+ mCurrentSlot = INVALID_BUFFER_SLOT;
+ returnFlags |= ISurfaceTexture::RELEASE_ALL_BUFFERS;
+ }
+
+ // look for a free buffer to give to the client
+ found = INVALID_BUFFER_SLOT;
+ foundSync = INVALID_BUFFER_SLOT;
+ dequeuedCount = 0;
+ for (int i = 0; i < mBufferCount; i++) {
+ const int state = mSlots[i].mBufferState;
+ if (state == BufferSlot::DEQUEUED) {
+ dequeuedCount++;
+ continue; // won't be continuing if could
+ // dequeue a non 'FREE' current slot like
+ // that in SurfaceTexture
+ }
+ // In case of Encoding, we do not deque the mCurrentSlot buffer
+ // since we follow synchronous mode (unlike possibly in
+ // SurfaceTexture that could be using the asynch mode
+ // or has some mechanism in GL to be able to wait till the
+ // currentslot is done using the data)
+ // Here, we have to wait for the MPEG4Writer(or equiv)
+ // to tell us when it's done using the current buffer
+ if (state == BufferSlot::FREE) {
+ foundSync = i;
+ // Unlike that in SurfaceTexture,
+ // We don't need to worry if it is the
+ // currentslot or not as it is in state FREE
+ found = i;
+ break;
+ }
+ }
+
+ // clients are not allowed to dequeue more than one buffer
+ // if they didn't set a buffer count.
+ if (!mClientBufferCount && dequeuedCount) {
+ return -EINVAL;
+ }
+
+ // See whether a buffer has been queued since the last setBufferCount so
+ // we know whether to perform the MIN_UNDEQUEUED_BUFFERS check below.
+ bool bufferHasBeenQueued = mCurrentSlot != INVALID_BUFFER_SLOT;
+ if (bufferHasBeenQueued) {
+ // make sure the client is not trying to dequeue more buffers
+ // than allowed.
+ const int avail = mBufferCount - (dequeuedCount+1);
+ if (avail < (MIN_UNDEQUEUED_BUFFERS-int(mSynchronousMode))) {
+ LOGE("dequeueBuffer: MIN_UNDEQUEUED_BUFFERS=%d exceeded (dequeued=%d)",
+ MIN_UNDEQUEUED_BUFFERS-int(mSynchronousMode),
+ dequeuedCount);
+ return -EBUSY;
+ }
+ }
+
+ // we're in synchronous mode and didn't find a buffer, we need to wait
+ // for for some buffers to be consumed
+ tryAgain = mSynchronousMode && (foundSync == INVALID_BUFFER_SLOT);
+ if (tryAgain) {
+ LOGW("Waiting..In synchronous mode and no buffer to dQ");
+ mDequeueCondition.wait(mMutex);
+ }
+ }
+
+ if (mSynchronousMode && found == INVALID_BUFFER_SLOT) {
+ // foundSync guaranteed to be != INVALID_BUFFER_SLOT
+ found = foundSync;
+ }
+
+ if (found == INVALID_BUFFER_SLOT) {
+ return -EBUSY;
+ }
+
+ const int buf = found;
+ *outBuf = found;
+
+ const bool useDefaultSize = !w && !h;
+ if (useDefaultSize) {
+ // use the default size
+ w = mDefaultWidth;
+ h = mDefaultHeight;
+ }
+
+ const bool updateFormat = (format != 0);
+ if (!updateFormat) {
+ // keep the current (or default) format
+ format = mPixelFormat;
+ }
+
+ // buffer is now in DEQUEUED (but can also be current at the same time,
+ // if we're in synchronous mode)
+ mSlots[buf].mBufferState = BufferSlot::DEQUEUED;
+
+ const sp<GraphicBuffer>& buffer(mSlots[buf].mGraphicBuffer);
+ if ((buffer == NULL) ||
+ (uint32_t(buffer->width) != w) ||
+ (uint32_t(buffer->height) != h) ||
+ (uint32_t(buffer->format) != format) ||
+ ((uint32_t(buffer->usage) & usage) != usage)) {
+ usage |= GraphicBuffer::USAGE_HW_TEXTURE;
+ status_t error;
+ sp<GraphicBuffer> graphicBuffer(
+ mGraphicBufferAlloc->createGraphicBuffer(
+ w, h, format, usage, &error));
+ if (graphicBuffer == 0) {
+ LOGE("dequeueBuffer: SurfaceComposer::createGraphicBuffer failed");
+ return error;
+ }
+ if (updateFormat) {
+ mPixelFormat = format;
+ }
+ mSlots[buf].mGraphicBuffer = graphicBuffer;
+ mSlots[buf].mRequestBufferCalled = false;
+ returnFlags |= ISurfaceTexture::BUFFER_NEEDS_REALLOCATION;
+ }
+ return returnFlags;
+}
+
+status_t SurfaceMediaSource::setSynchronousMode(bool enabled) {
+ Mutex::Autolock lock(mMutex);
+
+ status_t err = OK;
+ if (!enabled) {
+ // going to asynchronous mode, drain the queue
+ while (mSynchronousMode != enabled && !mQueue.isEmpty()) {
+ mDequeueCondition.wait(mMutex);
+ }
+ }
+
+ if (mSynchronousMode != enabled) {
+ // - if we're going to asynchronous mode, the queue is guaranteed to be
+ // empty here
+ // - if the client set the number of buffers, we're guaranteed that
+ // we have at least 3 (because we don't allow less)
+ mSynchronousMode = enabled;
+ mDequeueCondition.signal();
+ }
+ return err;
+}
+
+status_t SurfaceMediaSource::connect(int api) {
+ LOGV("SurfaceMediaSource::connect");
+ Mutex::Autolock lock(mMutex);
+ int err = NO_ERROR;
+ switch (api) {
+ case NATIVE_WINDOW_API_EGL:
+ case NATIVE_WINDOW_API_CPU:
+ case NATIVE_WINDOW_API_MEDIA:
+ case NATIVE_WINDOW_API_CAMERA:
+ if (mConnectedApi != NO_CONNECTED_API) {
+ err = -EINVAL;
+ } else {
+ mConnectedApi = api;
+ }
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
+ return err;
+}
+
+status_t SurfaceMediaSource::disconnect(int api) {
+ LOGV("SurfaceMediaSource::disconnect");
+ Mutex::Autolock lock(mMutex);
+ int err = NO_ERROR;
+ switch (api) {
+ case NATIVE_WINDOW_API_EGL:
+ case NATIVE_WINDOW_API_CPU:
+ case NATIVE_WINDOW_API_MEDIA:
+ case NATIVE_WINDOW_API_CAMERA:
+ if (mConnectedApi == api) {
+ mConnectedApi = NO_CONNECTED_API;
+ } else {
+ err = -EINVAL;
+ }
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
+ return err;
+}
+
+status_t SurfaceMediaSource::queueBuffer(int buf, int64_t timestamp,
+ uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform) {
+ LOGV("queueBuffer");
+
+ Mutex::Autolock lock(mMutex);
+ if (buf < 0 || buf >= mBufferCount) {
+ LOGE("queueBuffer: slot index out of range [0, %d]: %d",
+ mBufferCount, buf);
+ return -EINVAL;
+ } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) {
+ LOGE("queueBuffer: slot %d is not owned by the client (state=%d)",
+ buf, mSlots[buf].mBufferState);
+ return -EINVAL;
+ } else if (!mSlots[buf].mRequestBufferCalled) {
+ LOGE("queueBuffer: slot %d was enqueued without requesting a "
+ "buffer", buf);
+ return -EINVAL;
+ }
+
+ if (mSynchronousMode) {
+ // in synchronous mode we queue all buffers in a FIFO
+ mQueue.push_back(buf);
+ LOGV("Client queued buffer on slot: %d, Q size = %d",
+ buf, mQueue.size());
+ } else {
+ // in asynchronous mode we only keep the most recent buffer
+ if (mQueue.empty()) {
+ mQueue.push_back(buf);
+ } else {
+ Fifo::iterator front(mQueue.begin());
+ // buffer currently queued is freed
+ mSlots[*front].mBufferState = BufferSlot::FREE;
+ // and we record the new buffer index in the queued list
+ *front = buf;
+ }
+ }
+
+ mSlots[buf].mBufferState = BufferSlot::QUEUED;
+ mSlots[buf].mTimestamp = timestamp;
+ // TODO: (Confirm) Don't want to signal dequeue here.
+ // May be just in asynchronous mode?
+ // mDequeueCondition.signal();
+
+ // Once the queuing is done, we need to let the listener
+ // and signal the buffer consumer (encoder) know that a
+ // buffer is available
+ onFrameReceivedLocked();
+
+ *outWidth = mDefaultWidth;
+ *outHeight = mDefaultHeight;
+ *outTransform = 0;
+
+ return OK;
+}
+
+
+// onFrameReceivedLocked informs the buffer consumers (StageFrightRecorder)
+// or listeners that a frame has been received
+// It is supposed to be called only from queuebuffer.
+// The buffer is NOT made available for dequeueing immediately. We need to
+// wait to hear from StageFrightRecorder to set the buffer FREE
+// Make sure this is called when the mutex is locked
+status_t SurfaceMediaSource::onFrameReceivedLocked() {
+ LOGV("On Frame Received");
+ // Signal the encoder that a new frame has arrived
+ mFrameAvailableCondition.signal();
+
+ // call back the listener
+ // TODO: The listener may not be needed in SurfaceMediaSource at all.
+ // This can be made a SurfaceTexture specific thing
+ sp<FrameAvailableListener> listener;
+ if (mSynchronousMode || mQueue.empty()) {
+ listener = mFrameAvailableListener;
+ }
+
+ if (listener != 0) {
+ listener->onFrameAvailable();
+ }
+ return OK;
+}
+
+
+void SurfaceMediaSource::cancelBuffer(int buf) {
+ LOGV("SurfaceMediaSource::cancelBuffer");
+ Mutex::Autolock lock(mMutex);
+ if (buf < 0 || buf >= mBufferCount) {
+ LOGE("cancelBuffer: slot index out of range [0, %d]: %d",
+ mBufferCount, buf);
+ return;
+ } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) {
+ LOGE("cancelBuffer: slot %d is not owned by the client (state=%d)",
+ buf, mSlots[buf].mBufferState);
+ return;
+ }
+ mSlots[buf].mBufferState = BufferSlot::FREE;
+ mDequeueCondition.signal();
+}
+
+nsecs_t SurfaceMediaSource::getTimestamp() {
+ LOGV("SurfaceMediaSource::getTimestamp");
+ Mutex::Autolock lock(mMutex);
+ return mCurrentTimestamp;
+}
+
+
+void SurfaceMediaSource::setFrameAvailableListener(
+ const sp<FrameAvailableListener>& listener) {
+ LOGV("SurfaceMediaSource::setFrameAvailableListener");
+ Mutex::Autolock lock(mMutex);
+ mFrameAvailableListener = listener;
+}
+
+sp<IBinder> SurfaceMediaSource::getAllocator() {
+ LOGV("getAllocator");
+ return mGraphicBufferAlloc->asBinder();
+}
+
+
+void SurfaceMediaSource::freeAllBuffers() {
+ LOGV("freeAllBuffers");
+ for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
+ mSlots[i].mGraphicBuffer = 0;
+ mSlots[i].mBufferState = BufferSlot::FREE;
+ }
+}
+
+sp<GraphicBuffer> SurfaceMediaSource::getCurrentBuffer() const {
+ Mutex::Autolock lock(mMutex);
+ return mCurrentBuf;
+}
+
+int SurfaceMediaSource::query(int what, int* outValue)
+{
+ LOGV("query");
+ Mutex::Autolock lock(mMutex);
+ int value;
+ switch (what) {
+ case NATIVE_WINDOW_WIDTH:
+ value = mDefaultWidth;
+ if (!mDefaultWidth && !mDefaultHeight && mCurrentBuf != 0)
+ value = mCurrentBuf->width;
+ break;
+ case NATIVE_WINDOW_HEIGHT:
+ value = mDefaultHeight;
+ if (!mDefaultWidth && !mDefaultHeight && mCurrentBuf != 0)
+ value = mCurrentBuf->height;
+ break;
+ case NATIVE_WINDOW_FORMAT:
+ value = mPixelFormat;
+ break;
+ case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS:
+ value = mSynchronousMode ?
+ (MIN_UNDEQUEUED_BUFFERS-1) : MIN_UNDEQUEUED_BUFFERS;
+ break;
+ default:
+ return BAD_VALUE;
+ }
+ outValue[0] = value;
+ return NO_ERROR;
+}
+
+void SurfaceMediaSource::dump(String8& result) const
+{
+ char buffer[1024];
+ dump(result, "", buffer, 1024);
+}
+
+void SurfaceMediaSource::dump(String8& result, const char* prefix,
+ char* buffer, size_t SIZE) const
+{
+ Mutex::Autolock _l(mMutex);
+ snprintf(buffer, SIZE,
+ "%smBufferCount=%d, mSynchronousMode=%d, default-size=[%dx%d], "
+ "mPixelFormat=%d, \n",
+ prefix, mBufferCount, mSynchronousMode, mDefaultWidth, mDefaultHeight,
+ mPixelFormat);
+ result.append(buffer);
+
+ String8 fifo;
+ int fifoSize = 0;
+ Fifo::const_iterator i(mQueue.begin());
+ while (i != mQueue.end()) {
+ snprintf(buffer, SIZE, "%02d ", *i++);
+ fifoSize++;
+ fifo.append(buffer);
+ }
+
+ result.append(buffer);
+
+ struct {
+ const char * operator()(int state) const {
+ switch (state) {
+ case BufferSlot::DEQUEUED: return "DEQUEUED";
+ case BufferSlot::QUEUED: return "QUEUED";
+ case BufferSlot::FREE: return "FREE";
+ default: return "Unknown";
+ }
+ }
+ } stateName;
+
+ for (int i = 0; i < mBufferCount; i++) {
+ const BufferSlot& slot(mSlots[i]);
+ snprintf(buffer, SIZE,
+ "%s%s[%02d] state=%-8s, "
+ "timestamp=%lld\n",
+ prefix, (i==mCurrentSlot)?">":" ", i, stateName(slot.mBufferState),
+ slot.mTimestamp
+ );
+ result.append(buffer);
+ }
+}
+
+void SurfaceMediaSource::setFrameRate(uint32_t fps)
+{
+ Mutex::Autolock lock(mMutex);
+ mFrameRate = fps;
+}
+
+uint32_t SurfaceMediaSource::getFrameRate( ) const {
+ Mutex::Autolock lock(mMutex);
+ return mFrameRate;
+}
+
+status_t SurfaceMediaSource::start(MetaData *params)
+{
+ LOGV("start");
+ Mutex::Autolock lock(mMutex);
+ CHECK(!mStarted);
+ mStarted = true;
+ return OK;
+}
+
+
+status_t SurfaceMediaSource::stop()
+{
+ LOGV("Stop");
+
+ Mutex::Autolock lock(mMutex);
+ // TODO: Add waiting on mFrameCompletedCondition here?
+ mStarted = false;
+ mFrameAvailableCondition.signal();
+
+ return OK;
+}
+
+sp<MetaData> SurfaceMediaSource::getFormat()
+{
+ LOGV("getFormat");
+ Mutex::Autolock autoLock(mMutex);
+ sp<MetaData> meta = new MetaData;
+ // XXX: Check if this is right. or should we wait on some
+ // condition?
+ meta->setInt32(kKeyWidth, mDefaultWidth);
+ meta->setInt32(kKeyHeight, mDefaultHeight);
+ // The encoder format is set as an opaque colorformat
+ // The encoder will later find out the actual colorformat
+ // from the GL Frames itself.
+ meta->setInt32(kKeyColorFormat, OMX_COLOR_FormatAndroidOpaque);
+ meta->setInt32(kKeyStride, mDefaultWidth);
+ meta->setInt32(kKeySliceHeight, mDefaultHeight);
+ meta->setInt32(kKeyFrameRate, mFrameRate);
+ meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_RAW);
+ return meta;
+}
+
+status_t SurfaceMediaSource::read( MediaBuffer **buffer,
+ const ReadOptions *options)
+{
+ LOGV("Read. Size of queued buffer: %d", mQueue.size());
+ *buffer = NULL;
+
+ Mutex::Autolock autoLock(mMutex) ;
+ // If the recording has started and the queue is empty, then just
+ // wait here till the frames come in from the client side
+ while (mStarted && mQueue.empty()) {
+ LOGV("NO FRAMES! Recorder waiting for FrameAvailableCondition");
+ mFrameAvailableCondition.wait(mMutex);
+ }
+
+ // If the loop was exited as a result of stopping the recording,
+ // it is OK
+ if (!mStarted) {
+ return OK;
+ }
+
+ // Update the current buffer info
+ // TODO: mCurrentSlot can be made a bufferstate since there
+ // can be more than one "current" slots.
+ Fifo::iterator front(mQueue.begin());
+ mCurrentSlot = *front;
+ mCurrentBuf = mSlots[mCurrentSlot].mGraphicBuffer;
+ mCurrentTimestamp = mSlots[mCurrentSlot].mTimestamp;
+
+ // Pass the data to the MediaBuffer
+ // TODO: Change later to pass in only the metadata
+ *buffer = new MediaBuffer(mCurrentBuf);
+ (*buffer)->setObserver(this);
+ (*buffer)->add_ref();
+ (*buffer)->meta_data()->setInt64(kKeyTime, mCurrentTimestamp);
+
+ return OK;
+}
+
+void SurfaceMediaSource::signalBufferReturned(MediaBuffer *buffer) {
+ LOGV("signalBufferReturned");
+
+ bool foundBuffer = false;
+ Mutex::Autolock autoLock(mMutex);
+
+ if (!mStarted) {
+ LOGV("started = false. Nothing to do");
+ return;
+ }
+
+ for (Fifo::iterator it = mQueue.begin(); it != mQueue.end(); ++it) {
+ if (mSlots[*it].mGraphicBuffer == buffer->graphicBuffer()) {
+ LOGV("Buffer %d returned. Setting it 'FREE'. New Queue size = %d",
+ *it, mQueue.size()-1);
+ mSlots[*it].mBufferState = BufferSlot::FREE;
+ mQueue.erase(it);
+ buffer->setObserver(0);
+ buffer->release();
+ mDequeueCondition.signal();
+ mFrameCompleteCondition.signal();
+ foundBuffer = true;
+ break;
+ }
+ }
+
+ if (!foundBuffer) {
+ CHECK_EQ(0, "signalBufferReturned: bogus buffer");
+ }
+}
+
+
+
+} // end of namespace android
diff --git a/media/libstagefright/chromium_http/ChromiumHTTPDataSource.cpp b/media/libstagefright/chromium_http/ChromiumHTTPDataSource.cpp
index 588a74d..07a9eb8 100644
--- a/media/libstagefright/chromium_http/ChromiumHTTPDataSource.cpp
+++ b/media/libstagefright/chromium_http/ChromiumHTTPDataSource.cpp
@@ -25,6 +25,8 @@
#include "support.h"
+#include <cutils/properties.h> // for property_get
+
namespace android {
ChromiumHTTPDataSource::ChromiumHTTPDataSource(uint32_t flags)
@@ -111,7 +113,7 @@
mState = DISCONNECTED;
mCondition.broadcast();
- mURI.clear();
+ // mURI.clear();
mIOResult = err;
@@ -150,9 +152,19 @@
Mutex::Autolock autoLock(mLock);
if (mState != CONNECTED) {
- return ERROR_NOT_CONNECTED;
+ return INVALID_OPERATION;
}
+#if 0
+ char value[PROPERTY_VALUE_MAX];
+ if (property_get("media.stagefright.disable-net", value, 0)
+ && (!strcasecmp(value, "true") || !strcmp(value, "1"))) {
+ LOG_PRI(ANDROID_LOG_INFO, LOG_TAG, "Simulating that the network is down.");
+ disconnect_l();
+ return ERROR_IO;
+ }
+#endif
+
if (offset != mCurrentOffset) {
AString tmp = mURI;
KeyedVector<String8, String8> tmpHeaders = mHeaders;
@@ -236,7 +248,7 @@
CHECK_EQ((int)mState, (int)DISCONNECTING);
mState = DISCONNECTED;
- mURI.clear();
+ // mURI.clear();
mCondition.broadcast();
@@ -299,5 +311,21 @@
}
}
+status_t ChromiumHTTPDataSource::reconnectAtOffset(off64_t offset) {
+ Mutex::Autolock autoLock(mLock);
+
+ if (mURI.empty()) {
+ return INVALID_OPERATION;
+ }
+
+ LOG_PRI(ANDROID_LOG_INFO, LOG_TAG, "Reconnecting...");
+ status_t err = connect_l(mURI.c_str(), &mHeaders, offset);
+ if (err != OK) {
+ LOG_PRI(ANDROID_LOG_INFO, LOG_TAG, "Reconnect failed w/ err 0x%08x", err);
+ }
+
+ return err;
+}
+
} // namespace android
diff --git a/media/libstagefright/include/ChromiumHTTPDataSource.h b/media/libstagefright/include/ChromiumHTTPDataSource.h
index d833e2e..18f8913 100644
--- a/media/libstagefright/include/ChromiumHTTPDataSource.h
+++ b/media/libstagefright/include/ChromiumHTTPDataSource.h
@@ -51,6 +51,8 @@
virtual String8 getMIMEType() const;
+ virtual status_t reconnectAtOffset(off64_t offset);
+
protected:
virtual ~ChromiumHTTPDataSource();
diff --git a/media/libstagefright/include/NuCachedSource2.h b/media/libstagefright/include/NuCachedSource2.h
index 2d6cb84..22b2855 100644
--- a/media/libstagefright/include/NuCachedSource2.h
+++ b/media/libstagefright/include/NuCachedSource2.h
@@ -77,6 +77,10 @@
kWhatRead = 'read',
};
+ enum {
+ kMaxNumRetries = 10,
+ };
+
sp<DataSource> mSource;
sp<AHandlerReflector<NuCachedSource2> > mReflector;
sp<ALooper> mLooper;
@@ -93,6 +97,8 @@
bool mFetching;
int64_t mLastFetchTimeUs;
+ int32_t mNumRetriesLeft;
+
void onMessageReceived(const sp<AMessage> &msg);
void onFetch();
void onRead(const sp<AMessage> &msg);
diff --git a/media/libstagefright/tests/Android.mk b/media/libstagefright/tests/Android.mk
new file mode 100644
index 0000000..3ea8f39
--- /dev/null
+++ b/media/libstagefright/tests/Android.mk
@@ -0,0 +1,53 @@
+# Build the unit tests.
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+ifneq ($(TARGET_SIMULATOR),true)
+
+LOCAL_MODULE := SurfaceMediaSource_test
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := \
+ SurfaceMediaSource_test.cpp \
+ DummyRecorder.cpp \
+
+LOCAL_SHARED_LIBRARIES := \
+ libEGL \
+ libGLESv2 \
+ libandroid \
+ libbinder \
+ libcutils \
+ libgui \
+ libstlport \
+ libui \
+ libutils \
+ libstagefright \
+ libstagefright_omx \
+ libstagefright_foundation \
+
+LOCAL_STATIC_LIBRARIES := \
+ libgtest \
+ libgtest_main \
+
+LOCAL_C_INCLUDES := \
+ bionic \
+ bionic/libstdc++/include \
+ external/gtest/include \
+ external/stlport/stlport \
+ frameworks/base/media/libstagefright \
+ frameworks/base/media/libstagefright/include \
+ $(TOP)/frameworks/base/include/media/stagefright/openmax \
+
+include $(BUILD_EXECUTABLE)
+
+endif
+
+# Include subdirectory makefiles
+# ============================================================
+
+# If we're building with ONE_SHOT_MAKEFILE (mm, mmm), then what the framework
+# team really wants is to build the stuff defined by this makefile.
+ifeq (,$(ONE_SHOT_MAKEFILE))
+include $(call first-makefiles-under,$(LOCAL_PATH))
+endif
diff --git a/media/libstagefright/tests/DummyRecorder.cpp b/media/libstagefright/tests/DummyRecorder.cpp
new file mode 100644
index 0000000..8d75d6b
--- /dev/null
+++ b/media/libstagefright/tests/DummyRecorder.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "DummyRecorder"
+// #define LOG_NDEBUG 0
+
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaSource.h>
+#include "DummyRecorder.h"
+
+#include <utils/Log.h>
+
+namespace android {
+
+// static
+void *DummyRecorder::threadWrapper(void *pthis) {
+ LOGV("ThreadWrapper: %p", pthis);
+ DummyRecorder *writer = static_cast<DummyRecorder *>(pthis);
+ writer->readFromSource();
+ return NULL;
+}
+
+
+status_t DummyRecorder::start() {
+ LOGV("Start");
+ mStarted = true;
+
+ mSource->start();
+
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+ int err = pthread_create(&mThread, &attr, threadWrapper, this);
+ pthread_attr_destroy(&attr);
+
+ if (err) {
+ LOGE("Error creating thread!");
+ return -ENODEV;
+ }
+ return OK;
+}
+
+
+status_t DummyRecorder::stop() {
+ LOGV("Stop");
+ mStarted = false;
+
+ mSource->stop();
+ void *dummy;
+ pthread_join(mThread, &dummy);
+ status_t err = (status_t) dummy;
+
+ LOGV("Ending the reading thread");
+ return err;
+}
+
+// pretend to read the source buffers
+void DummyRecorder::readFromSource() {
+ LOGV("ReadFromSource");
+ if (!mStarted) {
+ return;
+ }
+
+ status_t err = OK;
+ MediaBuffer *buffer;
+ LOGV("A fake writer accessing the frames");
+ while (mStarted && (err = mSource->read(&buffer)) == OK){
+ // if not getting a valid buffer from source, then exit
+ if (buffer == NULL) {
+ return;
+ }
+ buffer->release();
+ buffer = NULL;
+ }
+}
+
+
+} // end of namespace android
diff --git a/media/libstagefright/tests/DummyRecorder.h b/media/libstagefright/tests/DummyRecorder.h
new file mode 100644
index 0000000..1cbea1b
--- /dev/null
+++ b/media/libstagefright/tests/DummyRecorder.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+#ifndef DUMMY_RECORDER_H_
+#define DUMMY_RECORDER_H_
+
+#include <pthread.h>
+#include <utils/String8.h>
+#include <media/stagefright/foundation/ABase.h>
+
+
+namespace android {
+
+class MediaSource;
+class MediaBuffer;
+
+class DummyRecorder {
+ public:
+ // The media source from which this will receive frames
+ sp<MediaSource> mSource;
+ bool mStarted;
+ pthread_t mThread;
+
+ status_t start();
+ status_t stop();
+
+ // actual entry point for the thread
+ void readFromSource();
+
+ // static function to wrap the actual thread entry point
+ static void *threadWrapper(void *pthis);
+
+ DummyRecorder(const sp<MediaSource> &source) : mSource(source)
+ , mStarted(false) {}
+ ~DummyRecorder( ) {}
+
+ private:
+
+ DISALLOW_EVIL_CONSTRUCTORS(DummyRecorder);
+};
+
+} // end of namespace android
+#endif
+
+
diff --git a/media/libstagefright/tests/SurfaceMediaSource_test.cpp b/media/libstagefright/tests/SurfaceMediaSource_test.cpp
new file mode 100644
index 0000000..ce10812
--- /dev/null
+++ b/media/libstagefright/tests/SurfaceMediaSource_test.cpp
@@ -0,0 +1,349 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "SurfaceMediaSource_test"
+// #define LOG_NDEBUG 0
+
+#include <gtest/gtest.h>
+#include <utils/String8.h>
+#include <utils/Errors.h>
+
+#include <media/stagefright/SurfaceMediaSource.h>
+
+#include <gui/SurfaceTextureClient.h>
+#include <ui/GraphicBuffer.h>
+#include <surfaceflinger/ISurfaceComposer.h>
+#include <surfaceflinger/Surface.h>
+#include <surfaceflinger/SurfaceComposerClient.h>
+
+#include <binder/ProcessState.h>
+#include <ui/FramebufferNativeWindow.h>
+
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/MPEG4Writer.h>
+#include <media/stagefright/OMXClient.h>
+#include <media/stagefright/OMXCodec.h>
+#include <OMX_Component.h>
+
+#include "DummyRecorder.h"
+
+namespace android {
+
+
+class SurfaceMediaSourceTest : public ::testing::Test {
+public:
+
+ SurfaceMediaSourceTest( ): mYuvTexWidth(64), mYuvTexHeight(66) { }
+ sp<MPEG4Writer> setUpWriter(OMXClient &client );
+ void oneBufferPass(int width, int height );
+ static void fillYV12Buffer(uint8_t* buf, int w, int h, int stride) ;
+ static void fillYV12BufferRect(uint8_t* buf, int w, int h,
+ int stride, const android_native_rect_t& rect) ;
+protected:
+
+ virtual void SetUp() {
+ mSMS = new SurfaceMediaSource(mYuvTexWidth, mYuvTexHeight);
+ mSMS->setSynchronousMode(true);
+ mSTC = new SurfaceTextureClient(mSMS);
+ mANW = mSTC;
+
+ }
+
+
+ virtual void TearDown() {
+ mSMS.clear();
+ mSTC.clear();
+ mANW.clear();
+ }
+
+ const int mYuvTexWidth;// = 64;
+ const int mYuvTexHeight;// = 66;
+
+ sp<SurfaceMediaSource> mSMS;
+ sp<SurfaceTextureClient> mSTC;
+ sp<ANativeWindow> mANW;
+
+};
+
+void SurfaceMediaSourceTest::oneBufferPass(int width, int height ) {
+ LOGV("One Buffer Pass");
+ ANativeWindowBuffer* anb;
+ ASSERT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb));
+ ASSERT_TRUE(anb != NULL);
+
+ sp<GraphicBuffer> buf(new GraphicBuffer(anb, false));
+ ASSERT_EQ(NO_ERROR, mANW->lockBuffer(mANW.get(), buf->getNativeBuffer()));
+
+ // Fill the buffer with the a checkerboard pattern
+ uint8_t* img = NULL;
+ buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
+ SurfaceMediaSourceTest::fillYV12Buffer(img, width, height, buf->getStride());
+ buf->unlock();
+
+ ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer()));
+}
+
+sp<MPEG4Writer> SurfaceMediaSourceTest::setUpWriter(OMXClient &client ) {
+ // Writing to a file
+ const char *fileName = "/sdcard/outputSurfEnc.mp4";
+ sp<MetaData> enc_meta = new MetaData;
+ enc_meta->setInt32(kKeyBitRate, 300000);
+ enc_meta->setInt32(kKeyFrameRate, 30);
+
+ enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG4);
+
+ sp<MetaData> meta = mSMS->getFormat();
+
+ int32_t width, height, stride, sliceHeight, colorFormat;
+ CHECK(meta->findInt32(kKeyWidth, &width));
+ CHECK(meta->findInt32(kKeyHeight, &height));
+ CHECK(meta->findInt32(kKeyStride, &stride));
+ CHECK(meta->findInt32(kKeySliceHeight, &sliceHeight));
+ CHECK(meta->findInt32(kKeyColorFormat, &colorFormat));
+
+ enc_meta->setInt32(kKeyWidth, width);
+ enc_meta->setInt32(kKeyHeight, height);
+ enc_meta->setInt32(kKeyIFramesInterval, 1);
+ enc_meta->setInt32(kKeyStride, stride);
+ enc_meta->setInt32(kKeySliceHeight, sliceHeight);
+ // TODO: overwriting the colorformat since the format set by GRAlloc
+ // could be wrong or not be read by OMX
+ enc_meta->setInt32(kKeyColorFormat, OMX_COLOR_FormatYUV420Planar);
+ // colorFormat);
+
+
+ sp<MediaSource> encoder =
+ OMXCodec::Create(
+ client.interface(), enc_meta, true /* createEncoder */, mSMS);
+
+ sp<MPEG4Writer> writer = new MPEG4Writer(fileName);
+ writer->addSource(encoder);
+
+ return writer;
+}
+
+// Fill a YV12 buffer with a multi-colored checkerboard pattern
+void SurfaceMediaSourceTest::fillYV12Buffer(uint8_t* buf, int w, int h, int stride) {
+ const int blockWidth = w > 16 ? w / 16 : 1;
+ const int blockHeight = h > 16 ? h / 16 : 1;
+ const int yuvTexOffsetY = 0;
+ int yuvTexStrideY = stride;
+ int yuvTexOffsetV = yuvTexStrideY * h;
+ int yuvTexStrideV = (yuvTexStrideY/2 + 0xf) & ~0xf;
+ int yuvTexOffsetU = yuvTexOffsetV + yuvTexStrideV * h/2;
+ int yuvTexStrideU = yuvTexStrideV;
+ for (int x = 0; x < w; x++) {
+ for (int y = 0; y < h; y++) {
+ int parityX = (x / blockWidth) & 1;
+ int parityY = (y / blockHeight) & 1;
+ unsigned char intensity = (parityX ^ parityY) ? 63 : 191;
+ buf[yuvTexOffsetY + (y * yuvTexStrideY) + x] = intensity;
+ if (x < w / 2 && y < h / 2) {
+ buf[yuvTexOffsetU + (y * yuvTexStrideU) + x] = intensity;
+ if (x * 2 < w / 2 && y * 2 < h / 2) {
+ buf[yuvTexOffsetV + (y*2 * yuvTexStrideV) + x*2 + 0] =
+ buf[yuvTexOffsetV + (y*2 * yuvTexStrideV) + x*2 + 1] =
+ buf[yuvTexOffsetV + ((y*2+1) * yuvTexStrideV) + x*2 + 0] =
+ buf[yuvTexOffsetV + ((y*2+1) * yuvTexStrideV) + x*2 + 1] =
+ intensity;
+ }
+ }
+ }
+ }
+}
+
+// Fill a YV12 buffer with red outside a given rectangle and green inside it.
+void SurfaceMediaSourceTest::fillYV12BufferRect(uint8_t* buf, int w,
+ int h, int stride, const android_native_rect_t& rect) {
+ const int yuvTexOffsetY = 0;
+ int yuvTexStrideY = stride;
+ int yuvTexOffsetV = yuvTexStrideY * h;
+ int yuvTexStrideV = (yuvTexStrideY/2 + 0xf) & ~0xf;
+ int yuvTexOffsetU = yuvTexOffsetV + yuvTexStrideV * h/2;
+ int yuvTexStrideU = yuvTexStrideV;
+ for (int x = 0; x < w; x++) {
+ for (int y = 0; y < h; y++) {
+ bool inside = rect.left <= x && x < rect.right &&
+ rect.top <= y && y < rect.bottom;
+ buf[yuvTexOffsetY + (y * yuvTexStrideY) + x] = inside ? 240 : 64;
+ if (x < w / 2 && y < h / 2) {
+ bool inside = rect.left <= 2*x && 2*x < rect.right &&
+ rect.top <= 2*y && 2*y < rect.bottom;
+ buf[yuvTexOffsetU + (y * yuvTexStrideU) + x] = 16;
+ buf[yuvTexOffsetV + (y * yuvTexStrideV) + x] =
+ inside ? 16 : 255;
+ }
+ }
+ }
+} ///////// End of class SurfaceMediaSourceTest
+
+///////////////////////////////////////////////////////////////////
+// Class to imitate the recording /////////////////////////////
+// ////////////////////////////////////////////////////////////////
+struct SimpleDummyRecorder {
+ sp<MediaSource> mSource;
+
+ SimpleDummyRecorder
+ (const sp<MediaSource> &source): mSource(source) {}
+
+ status_t start() { return mSource->start();}
+ status_t stop() { return mSource->stop();}
+
+ // fakes reading from a media source
+ status_t readFromSource() {
+ MediaBuffer *buffer;
+ status_t err = mSource->read(&buffer);
+ if (err != OK) {
+ return err;
+ }
+ buffer->release();
+ buffer = NULL;
+ return OK;
+ }
+};
+
+///////////////////////////////////////////////////////////////////
+// TESTS
+// Just pass one buffer from the native_window to the SurfaceMediaSource
+TEST_F(SurfaceMediaSourceTest, EncodingFromCpuFilledYV12BufferNpotOneBufferPass) {
+ LOGV("Testing OneBufferPass ******************************");
+
+ ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(),
+ 0, 0, HAL_PIXEL_FORMAT_YV12));
+ // OMX_COLOR_FormatYUV420Planar)); // ));
+ ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(),
+ GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN));
+
+ oneBufferPass(mYuvTexWidth, mYuvTexHeight);
+}
+
+// Pass the buffer with the wrong height and weight and should not be accepted
+TEST_F(SurfaceMediaSourceTest, EncodingFromCpuFilledYV12BufferNpotWrongSizeBufferPass) {
+ LOGV("Testing Wrong size BufferPass ******************************");
+
+ // setting the client side buffer size different than the server size
+ ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(),
+ 10, 10, HAL_PIXEL_FORMAT_YV12));
+ // OMX_COLOR_FormatYUV420Planar)); // ));
+ ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(),
+ GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN));
+
+ ANativeWindowBuffer* anb;
+
+ // make sure we get an error back when dequeuing!
+ ASSERT_NE(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb));
+}
+
+
+// pass multiple buffers from the native_window the SurfaceMediaSource
+// A dummy writer is used to simulate actual MPEG4Writer
+TEST_F(SurfaceMediaSourceTest, EncodingFromCpuFilledYV12BufferNpotMultiBufferPass) {
+ LOGV("Testing MultiBufferPass, Dummy Recorder *********************");
+ ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(),
+ 0, 0, HAL_PIXEL_FORMAT_YV12));
+ ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(),
+ GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN));
+ SimpleDummyRecorder writer(mSMS);
+ writer.start();
+
+ int32_t nFramesCount = 0;
+ while (nFramesCount < 300) {
+ oneBufferPass(mYuvTexWidth, mYuvTexHeight);
+
+ ASSERT_EQ(NO_ERROR, writer.readFromSource());
+
+ nFramesCount++;
+ }
+ writer.stop();
+}
+
+// Delayed pass of multiple buffers from the native_window the SurfaceMediaSource
+// A dummy writer is used to simulate actual MPEG4Writer
+TEST_F(SurfaceMediaSourceTest, EncodingFromCpuFilledYV12BufferNpotMultiBufferPassLag) {
+ LOGV("Testing MultiBufferPass, Dummy Recorder Lagging **************");
+ ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(),
+ 0, 0, HAL_PIXEL_FORMAT_YV12));
+ ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(),
+ GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN));
+ SimpleDummyRecorder writer(mSMS);
+ writer.start();
+
+ int32_t nFramesCount = 1;
+ const int FRAMES_LAG = mSMS->getBufferCount() - 1;
+ while (nFramesCount <= 300) {
+ oneBufferPass(mYuvTexWidth, mYuvTexHeight);
+ // Forcing the writer to lag behind a few frames
+ if (nFramesCount > FRAMES_LAG) {
+ ASSERT_EQ(NO_ERROR, writer.readFromSource());
+ }
+ nFramesCount++;
+ }
+ writer.stop();
+}
+
+// pass multiple buffers from the native_window the SurfaceMediaSource
+// A dummy writer (MULTITHREADED) is used to simulate actual MPEG4Writer
+TEST_F(SurfaceMediaSourceTest, EncodingFromCpuFilledYV12BufferNpotMultiBufferPassThreaded) {
+ LOGV("Testing MultiBufferPass, Dummy Recorder Multi-Threaded **********");
+ ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(),
+ 0, 0, HAL_PIXEL_FORMAT_YV12));
+ ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(),
+ GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN));
+
+ DummyRecorder writer(mSMS);
+ writer.start();
+
+ int32_t nFramesCount = 0;
+ while (nFramesCount <= 300) {
+ oneBufferPass(mYuvTexWidth, mYuvTexHeight);
+
+ nFramesCount++;
+ }
+ writer.stop();
+}
+
+// Test to examine the actual encoding. Temporarily disabled till the
+// colorformat and encoding from GRAlloc data is resolved
+TEST_F(SurfaceMediaSourceTest, DISABLED_EncodingFromCpuFilledYV12BufferNpotWrite) {
+ LOGV("Testing the whole pipeline with actual Recorder");
+ ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(),
+ 0, 0, HAL_PIXEL_FORMAT_YV12)); // OMX_COLOR_FormatYUV420Planar)); // ));
+ ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(),
+ GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN));
+
+ OMXClient client;
+ CHECK_EQ(OK, client.connect());
+
+ sp<MPEG4Writer> writer = setUpWriter(client);
+ int64_t start = systemTime();
+ CHECK_EQ(OK, writer->start());
+
+ int32_t nFramesCount = 0;
+ while (nFramesCount <= 300) {
+ oneBufferPass(mYuvTexWidth, mYuvTexHeight);
+ nFramesCount++;
+ }
+
+ CHECK_EQ(OK, writer->stop());
+ writer.clear();
+ int64_t end = systemTime();
+ client.disconnect();
+}
+
+
+} // namespace android
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 5298f2e..d7d7817 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -34,7 +34,7 @@
<string name="config_systemBarComponent" translatable="false">com.android.systemui.statusbar.tablet.TabletStatusBar</string>
<!-- Whether or not we show the number in the bar. -->
- <bool name="config_statusBarShowNumber">true</bool>
+ <bool name="config_statusBarShowNumber">false</bool>
<!-- How many icons may be shown at once in the system bar. Includes any
slots that may be reused for things like IME control. -->
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
index cca7947..ea54445 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
@@ -329,6 +329,7 @@
mRecentsGlowView = findViewById(R.id.recents_glow);
+ mRecentsScrim = (View) findViewById(R.id.recents_bg_protect);
mChoreo = new Choreographer(this, mRecentsScrim, mRecentsGlowView, this);
mRecentsDismissButton = findViewById(R.id.recents_dismiss_button);
mRecentsDismissButton.setOnClickListener(new OnClickListener() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java
index b4d2a14..d5885bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java
@@ -148,6 +148,11 @@
"now sliding child %d: %s (touchY=%.1f, rowHeight=%d, count=%d)",
childIdx, mSlidingChild, mInitialTouchY, mRowHeight, count));
}
+
+
+ // We need to prevent the surrounding ScrollView from intercepting us now;
+ // the scroll position will be locked while we swipe
+ requestDisallowInterceptTouchEvent(true);
}
}
break;
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index b963b13..e0debf7 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -388,6 +388,11 @@
st.isHandled = false;
mPreparedPanel = st;
+ if (st.frozenActionViewState != null) {
+ st.menu.restoreActionViewStates(st.frozenActionViewState);
+ st.frozenActionViewState = null;
+ }
+
return true;
}
@@ -652,7 +657,13 @@
@Override
public void invalidatePanelMenu(int featureId) {
PanelFeatureState st = getPanelState(featureId, true);
+ Bundle savedActionViewStates = null;
if (st.menu != null) {
+ savedActionViewStates = new Bundle();
+ st.menu.saveActionViewStates(savedActionViewStates);
+ if (savedActionViewStates.size() > 0) {
+ st.frozenActionViewState = savedActionViewStates;
+ }
st.menu.clear();
}
st.refreshMenuContent = true;
@@ -3024,6 +3035,12 @@
*/
Bundle frozenMenuState;
+ /**
+ * Contains the state of associated action views when told to freeze.
+ * These are saved across invalidations.
+ */
+ Bundle frozenActionViewState;
+
PanelFeatureState(int featureId) {
this.featureId = featureId;
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index ad6cebb..ae13ab5 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -166,22 +166,22 @@
static final int SYSTEM_DIALOG_LAYER = 6;
// toasts and the plugged-in battery thing
static final int TOAST_LAYER = 7;
- static final int STATUS_BAR_LAYER = 8;
- static final int STATUS_BAR_PANEL_LAYER = 9;
// SIM errors and unlock. Not sure if this really should be in a high layer.
- static final int PRIORITY_PHONE_LAYER = 10;
+ static final int PRIORITY_PHONE_LAYER = 8;
// like the ANR / app crashed dialogs
- static final int SYSTEM_ALERT_LAYER = 11;
+ static final int SYSTEM_ALERT_LAYER = 9;
// system-level error dialogs
- static final int SYSTEM_ERROR_LAYER = 12;
+ static final int SYSTEM_ERROR_LAYER = 10;
// on-screen keyboards and other such input method user interfaces go here.
- static final int INPUT_METHOD_LAYER = 13;
+ static final int INPUT_METHOD_LAYER = 11;
// on-screen keyboards and other such input method user interfaces go here.
- static final int INPUT_METHOD_DIALOG_LAYER = 14;
+ static final int INPUT_METHOD_DIALOG_LAYER = 12;
// the keyguard; nothing on top of these can take focus, since they are
// responsible for power management when displayed.
- static final int KEYGUARD_LAYER = 15;
- static final int KEYGUARD_DIALOG_LAYER = 16;
+ static final int KEYGUARD_LAYER = 13;
+ static final int KEYGUARD_DIALOG_LAYER = 14;
+ static final int STATUS_BAR_LAYER = 15;
+ static final int STATUS_BAR_PANEL_LAYER = 16;
// the navigation bar, if available, shows atop most things
static final int NAVIGATION_BAR_LAYER = 17;
// the drag layer: input for drag-and-drop is associated with this window,
@@ -1703,7 +1703,11 @@
}
}
}
- if (DEBUG_LAYOUT) Log.i(TAG, "mNavigationBar frame: " + navr);
+ if (DEBUG_LAYOUT) {
+ Log.i(TAG, "mNavigationBar frame: " + navr);
+ Log.i(TAG, String.format("mDock rect: (%d,%d - %d,%d)",
+ mDockLeft, mDockTop, mDockRight, mDockBottom));
+ }
// apply navigation bar insets
pf.left = df.left = vf.left = mDockLeft;
@@ -1713,6 +1717,25 @@
mStatusBar.computeFrameLw(pf, df, vf, vf);
+ // now, let's consider the navigation bar; if it exists, it must be removed from the
+ // available screen real estate (like an un-hideable status bar)
+ if (navr != null) {
+ if (navr.top == 0) {
+ // Navigation bar is vertical
+ if (mRestrictedScreenLeft == navr.left) {
+ mRestrictedScreenLeft = navr.right;
+ mRestrictedScreenWidth -= (navr.right - navr.left);
+ } else if ((mRestrictedScreenLeft+mRestrictedScreenWidth) == navr.right) {
+ mRestrictedScreenWidth -= (navr.right - navr.left);
+ }
+ } else {
+ // Navigation bar horizontal, at bottom
+ if ((mRestrictedScreenHeight+mRestrictedScreenTop) == navr.bottom) {
+ mRestrictedScreenHeight -= (navr.bottom-navr.top);
+ }
+ }
+ }
+
if (mStatusBar.isVisibleLw()) {
// If the status bar is hidden, we don't want to cause
// windows behind it to scroll.
@@ -1745,23 +1768,6 @@
mRestrictedScreenHeight -= (r.bottom-r.top);
}
- if (navr != null) {
- if (navr.top == 0) {
- // Navigation bar is vertical
- if (mRestrictedScreenLeft == navr.left) {
- mRestrictedScreenLeft = navr.right;
- mRestrictedScreenWidth -= (navr.right - navr.left);
- } else if ((mRestrictedScreenLeft+mRestrictedScreenWidth) == navr.right) {
- mRestrictedScreenWidth -= (navr.right - navr.left);
- }
- } else {
- // Navigation bar horizontal, at bottom
- if ((mRestrictedScreenHeight-mRestrictedScreenTop) == r.bottom) {
- mRestrictedScreenHeight -= (navr.bottom-navr.top);
- }
- }
- }
-
mContentTop = mCurTop = mDockTop = mRestrictedScreenTop;
mContentBottom = mCurBottom = mDockBottom
= mRestrictedScreenTop + mRestrictedScreenHeight;
@@ -1873,19 +1879,22 @@
// permission, so they have the same privileges as the status
// bar itself.
//
- // However, they should still dodge the navigation bar if it exists. A
- // straightforward way to do this is to only allow the status bar panels to
- // extend to the extrema of the allowable region for the IME dock.
+ // However, they should still dodge the navigation bar if it exists.
pf.left = df.left = hasNavBar ? mDockLeft : mUnrestrictedScreenLeft;
pf.top = df.top = mUnrestrictedScreenTop;
pf.right = df.right = hasNavBar
- ? mDockRight
+ ? mRestrictedScreenLeft+mRestrictedScreenWidth
: mUnrestrictedScreenLeft+mUnrestrictedScreenWidth;
pf.bottom = df.bottom = hasNavBar
- ? mDockBottom
+ ? mRestrictedScreenTop+mRestrictedScreenHeight
: mUnrestrictedScreenTop+mUnrestrictedScreenHeight;
+ if (DEBUG_LAYOUT) {
+ Log.v(TAG, String.format(
+ "Laying out status bar window: (%d,%d - %d,%d)",
+ pf.left, pf.top, pf.right, pf.bottom));
+ }
} else {
pf.left = df.left = mRestrictedScreenLeft;
pf.top = df.top = mRestrictedScreenTop;
@@ -1922,12 +1931,23 @@
pf.left = df.left = cf.left = hasNavBar ? mDockLeft : mUnrestrictedScreenLeft;
pf.top = df.top = cf.top = mUnrestrictedScreenTop;
pf.right = df.right = cf.right = hasNavBar
- ? mDockRight
+ ? mRestrictedScreenLeft+mRestrictedScreenWidth
: mUnrestrictedScreenLeft+mUnrestrictedScreenWidth;
pf.bottom = df.bottom = cf.bottom = hasNavBar
- ? mDockBottom
+ ? mRestrictedScreenTop+mRestrictedScreenHeight
: mUnrestrictedScreenTop+mUnrestrictedScreenHeight;
+ } else if (attrs.type == TYPE_NAVIGATION_BAR) {
+ // The navigation bar has Real Ultimate Power.
+ pf.left = df.left = mUnrestrictedScreenLeft;
+ pf.top = df.top = mUnrestrictedScreenTop;
+ pf.right = df.right = mUnrestrictedScreenLeft+mUnrestrictedScreenWidth;
+ pf.bottom = df.bottom = mUnrestrictedScreenTop+mUnrestrictedScreenHeight;
+ if (DEBUG_LAYOUT) {
+ Log.v(TAG, String.format(
+ "Laying out navigation bar window: (%d,%d - %d,%d)",
+ pf.left, pf.top, pf.right, pf.bottom));
+ }
} else {
pf.left = df.left = cf.left = mRestrictedScreenLeft;
pf.top = df.top = cf.top = mRestrictedScreenTop;
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 482336b..0323fe0 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -2954,7 +2954,7 @@
mStreamType = streamType;
// NOTE: audio_track_cblk_t::frameSize for 8 bit PCM data is based on a sample size of
// 16 bit because data is converted to 16 bit before being stored in buffer by AudioTrack
- mCblk->frameSize = audio_is_linear_pcm(format) ? mChannelCount * audio_bytes_per_sample(format) : sizeof(uint8_t);
+ mCblk->frameSize = audio_is_linear_pcm(format) ? mChannelCount * sizeof(int16_t) : sizeof(uint8_t);
}
}
diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java
index 438883e..a679ca7 100644
--- a/services/java/com/android/server/AppWidgetService.java
+++ b/services/java/com/android/server/AppWidgetService.java
@@ -230,8 +230,14 @@
pw.println(':');
pw.print(" min=("); pw.print(info.minWidth);
pw.print("x"); pw.print(info.minHeight);
+ pw.print(") minResize=("); pw.print(info.minResizeWidth);
+ pw.print("x"); pw.print(info.minResizeHeight);
pw.print(") updatePeriodMillis=");
pw.print(info.updatePeriodMillis);
+ pw.print(" resizeMode=");
+ pw.print(info.resizeMode);
+ pw.print(" autoAdvanceViewId=");
+ pw.print(info.autoAdvanceViewId);
pw.print(" initialLayout=#");
pw.print(Integer.toHexString(info.initialLayout));
pw.print(" zombie="); pw.println(p.zombie);
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 5dd3a6a..ea78b52 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -1090,7 +1090,7 @@
try {
InetAddress addr = InetAddress.getByAddress(hostAddress);
LinkProperties lp = tracker.getLinkProperties();
- return addRoute(lp, RouteInfo.makeHostRoute(addr));
+ return addRouteToAddress(lp, addr);
} catch (UnknownHostException e) {}
return false;
}
@@ -1103,6 +1103,31 @@
return modifyRoute(p.getInterfaceName(), p, r, 0, false);
}
+ private boolean addRouteToAddress(LinkProperties lp, InetAddress addr) {
+ return modifyRouteToAddress(lp, addr, true);
+ }
+
+ private boolean removeRouteToAddress(LinkProperties lp, InetAddress addr) {
+ return modifyRouteToAddress(lp, addr, false);
+ }
+
+ private boolean modifyRouteToAddress(LinkProperties lp, InetAddress addr, boolean doAdd) {
+ RouteInfo bestRoute = RouteInfo.selectBestRoute(lp.getRoutes(), addr);
+ if (bestRoute == null) {
+ bestRoute = RouteInfo.makeHostRoute(addr);
+ } else {
+ if (bestRoute.getGateway().equals(addr)) {
+ // if there is no better route, add the implied hostroute for our gateway
+ bestRoute = RouteInfo.makeHostRoute(addr);
+ } else {
+ // if we will connect to this through another route, add a direct route
+ // to it's gateway
+ bestRoute = RouteInfo.makeHostRoute(addr, bestRoute.getGateway());
+ }
+ }
+ return modifyRoute(lp.getInterfaceName(), lp, bestRoute, 0, doAdd);
+ }
+
private boolean modifyRoute(String ifaceName, LinkProperties lp, RouteInfo r, int cycleCount,
boolean doAdd) {
if ((ifaceName == null) || (lp == null) || (r == null)) return false;
@@ -1713,49 +1738,50 @@
*/
private void updateRoutes(LinkProperties newLp, LinkProperties curLp, boolean isLinkDefault) {
Collection<RouteInfo> routesToAdd = null;
- CompareResult<InetAddress> dnsDiff = null;
-
+ CompareResult<InetAddress> dnsDiff = new CompareResult<InetAddress>();
+ CompareResult<RouteInfo> routeDiff = new CompareResult<RouteInfo>();
if (curLp != null) {
// check for the delta between the current set and the new
- CompareResult<RouteInfo> routeDiff = curLp.compareRoutes(newLp);
+ routeDiff = curLp.compareRoutes(newLp);
dnsDiff = curLp.compareDnses(newLp);
-
- for (RouteInfo r : routeDiff.removed) {
- if (isLinkDefault || ! r.isDefaultRoute()) {
- removeRoute(curLp, r);
- }
- }
- routesToAdd = routeDiff.added;
+ } else if (newLp != null) {
+ routeDiff.added = newLp.getRoutes();
+ dnsDiff.added = newLp.getDnses();
}
- if (newLp != null) {
- // if we didn't get a diff from cur -> new, then just use the new
- if (routesToAdd == null) {
- routesToAdd = newLp.getRoutes();
+ for (RouteInfo r : routeDiff.removed) {
+ if (isLinkDefault || ! r.isDefaultRoute()) {
+ removeRoute(curLp, r);
}
+ }
- for (RouteInfo r : routesToAdd) {
- if (isLinkDefault || ! r.isDefaultRoute()) {
- addRoute(newLp, r);
- }
+ for (RouteInfo r : routeDiff.added) {
+ if (isLinkDefault || ! r.isDefaultRoute()) {
+ addRoute(newLp, r);
}
}
if (!isLinkDefault) {
// handle DNS routes
- Collection<InetAddress> dnsToAdd = null;
- if (dnsDiff != null) {
- dnsToAdd = dnsDiff.added;
- for (InetAddress dnsAddress : dnsDiff.removed) {
- removeRoute(curLp, RouteInfo.makeHostRoute(dnsAddress));
+ if (routeDiff.removed.size() == 0 && routeDiff.added.size() == 0) {
+ // no change in routes, check for change in dns themselves
+ for (InetAddress oldDns : dnsDiff.removed) {
+ removeRouteToAddress(curLp, oldDns);
}
- }
- if (newLp != null) {
- if (dnsToAdd == null) {
- dnsToAdd = newLp.getDnses();
+ for (InetAddress newDns : dnsDiff.added) {
+ addRouteToAddress(newLp, newDns);
}
- for(InetAddress dnsAddress : dnsToAdd) {
- addRoute(newLp, RouteInfo.makeHostRoute(dnsAddress));
+ } else {
+ // routes changed - remove all old dns entries and add new
+ if (curLp != null) {
+ for (InetAddress oldDns : curLp.getDnses()) {
+ removeRouteToAddress(curLp, oldDns);
+ }
+ }
+ if (newLp != null) {
+ for (InetAddress newDns : newLp.getDnses()) {
+ addRouteToAddress(newLp, newDns);
+ }
}
}
}
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 7399679..d39f565f 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -166,7 +166,7 @@
private final KeyguardManager mKeyguardManager;
private final Notification mImeSwitcherNotification;
private final PendingIntent mImeSwitchPendingIntent;
- private final boolean mShowOngoingImeSwitcherForPhones;
+ private boolean mShowOngoingImeSwitcherForPhones;
private boolean mNotificationShown;
class SessionState {
@@ -538,8 +538,8 @@
mImeSwitcherNotification.vibrate = null;
Intent intent = new Intent(Settings.ACTION_SHOW_INPUT_METHOD_PICKER);
mImeSwitchPendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
- mShowOngoingImeSwitcherForPhones = mRes.getBoolean(
- com.android.internal.R.bool.show_ongoing_ime_switcher);
+
+ mShowOngoingImeSwitcherForPhones = false;
synchronized (mMethodMap) {
mFileManager = new InputMethodFileManager(mMethodMap);
@@ -612,6 +612,8 @@
synchronized (mMethodMap) {
if (!mSystemReady) {
mSystemReady = true;
+ mShowOngoingImeSwitcherForPhones = mRes.getBoolean(
+ com.android.internal.R.bool.show_ongoing_ime_switcher);
try {
startInputInnerLocked();
} catch (RuntimeException e) {
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index 41e8a31..2366fcb 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -93,6 +93,7 @@
private static final String KEY_TX = "tx_bytes";
class NetdResponseCode {
+ /* Keep in sync with system/netd/ResponseCode.h */
public static final int InterfaceListResult = 110;
public static final int TetherInterfaceListResult = 111;
public static final int TetherDnsFwdTgtListResult = 112;
@@ -108,6 +109,7 @@
public static final int InterfaceTxThrottleResult = 219;
public static final int InterfaceChange = 600;
+ public static final int BandwidthControl = 601;
}
/**
@@ -265,6 +267,20 @@
}
/**
+ * Notify our observers of a limit reached.
+ */
+ private void notifyLimitReached(String limitName, String iface) {
+ for (INetworkManagementEventObserver obs : mObservers) {
+ try {
+ obs.limitReached(limitName, iface);
+ Slog.d(TAG, "Observer notified limit reached for " + limitName + " " + iface);
+ } catch (Exception ex) {
+ Slog.w(TAG, "Observer notifier failed", ex);
+ }
+ }
+ }
+
+ /**
* Let us know the daemon is connected
*/
protected void onConnected() {
@@ -286,33 +302,52 @@
}.start();
}
public boolean onEvent(int code, String raw, String[] cooked) {
- if (code == NetdResponseCode.InterfaceChange) {
- /*
- * a network interface change occured
- * Format: "NNN Iface added <name>"
- * "NNN Iface removed <name>"
- * "NNN Iface changed <name> <up/down>"
- * "NNN Iface linkstatus <name> <up/down>"
- */
- if (cooked.length < 4 || !cooked[1].equals("Iface")) {
+ switch (code) {
+ case NetdResponseCode.InterfaceChange:
+ /*
+ * a network interface change occured
+ * Format: "NNN Iface added <name>"
+ * "NNN Iface removed <name>"
+ * "NNN Iface changed <name> <up/down>"
+ * "NNN Iface linkstatus <name> <up/down>"
+ */
+ if (cooked.length < 4 || !cooked[1].equals("Iface")) {
+ throw new IllegalStateException(
+ String.format("Invalid event from daemon (%s)", raw));
+ }
+ if (cooked[2].equals("added")) {
+ notifyInterfaceAdded(cooked[3]);
+ return true;
+ } else if (cooked[2].equals("removed")) {
+ notifyInterfaceRemoved(cooked[3]);
+ return true;
+ } else if (cooked[2].equals("changed") && cooked.length == 5) {
+ notifyInterfaceStatusChanged(cooked[3], cooked[4].equals("up"));
+ return true;
+ } else if (cooked[2].equals("linkstate") && cooked.length == 5) {
+ notifyInterfaceLinkStateChanged(cooked[3], cooked[4].equals("up"));
+ return true;
+ }
throw new IllegalStateException(
String.format("Invalid event from daemon (%s)", raw));
- }
- if (cooked[2].equals("added")) {
- notifyInterfaceAdded(cooked[3]);
- return true;
- } else if (cooked[2].equals("removed")) {
- notifyInterfaceRemoved(cooked[3]);
- return true;
- } else if (cooked[2].equals("changed") && cooked.length == 5) {
- notifyInterfaceStatusChanged(cooked[3], cooked[4].equals("up"));
- return true;
- } else if (cooked[2].equals("linkstate") && cooked.length == 5) {
- notifyInterfaceLinkStateChanged(cooked[3], cooked[4].equals("up"));
- return true;
- }
- throw new IllegalStateException(
- String.format("Invalid event from daemon (%s)", raw));
+ // break;
+ case NetdResponseCode.BandwidthControl:
+ /*
+ * Bandwidth control needs some attention
+ * Format: "NNN limit alert <alertName> <ifaceName>"
+ */
+ if (cooked.length < 5 || !cooked[1].equals("limit")) {
+ throw new IllegalStateException(
+ String.format("Invalid event from daemon (%s)", raw));
+ }
+ if (cooked[2].equals("alert")) {
+ notifyLimitReached(cooked[3], cooked[4]);
+ return true;
+ }
+ throw new IllegalStateException(
+ String.format("Invalid event from daemon (%s)", raw));
+ // break;
+ default: break;
}
return false;
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 8c7e279..76665915 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -230,6 +230,7 @@
WallpaperManagerService wallpaper = null;
LocationManagerService location = null;
CountryDetectorService countryDetector = null;
+ TextServicesManagerService tsms = null;
if (factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
try {
@@ -273,6 +274,14 @@
}
try {
+ Slog.i(TAG, "Text Service Manager Service");
+ tsms = new TextServicesManagerService(context);
+ ServiceManager.addService(Context.TEXT_SERVICES_MANAGER_SERVICE, tsms);
+ } catch (Throwable e) {
+ Slog.e(TAG, "Failure starting Text Service Manager Service", e);
+ }
+
+ try {
Slog.i(TAG, "NetworkStats Service");
networkStats = new NetworkStatsService(context, networkManagement, alarm);
ServiceManager.addService(Context.NETWORK_STATS_SERVICE, networkStats);
@@ -538,6 +547,7 @@
final LocationManagerService locationF = location;
final CountryDetectorService countryDetectorF = countryDetector;
final NetworkTimeUpdateService networkTimeUpdaterF = networkTimeUpdater;
+ final TextServicesManagerService textServiceManagerServiceF = tsms;
// We now tell the activity manager it is okay to run third party
// code. It will call back into us once it has gotten to the state
@@ -571,6 +581,7 @@
if (countryDetectorF != null) countryDetectorF.systemReady();
if (throttleF != null) throttleF.systemReady();
if (networkTimeUpdaterF != null) networkTimeUpdaterF.systemReady();
+ if (textServiceManagerServiceF != null) textServiceManagerServiceF.systemReady();
}
});
diff --git a/services/java/com/android/server/TextServicesManagerService.java b/services/java/com/android/server/TextServicesManagerService.java
new file mode 100644
index 0000000..4a0c837
--- /dev/null
+++ b/services/java/com/android/server/TextServicesManagerService.java
@@ -0,0 +1,346 @@
+/*
+ * Copyright (C) 2011 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;
+
+import com.android.internal.content.PackageMonitor;
+import com.android.internal.textservice.ISpellCheckerService;
+import com.android.internal.textservice.ISpellCheckerSession;
+import com.android.internal.textservice.ISpellCheckerSessionListener;
+import com.android.internal.textservice.ITextServicesManager;
+import com.android.internal.textservice.ITextServicesSessionListener;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.service.textservice.SpellCheckerService;
+import android.util.Log;
+import android.util.Slog;
+import android.view.textservice.SpellCheckerInfo;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+
+public class TextServicesManagerService extends ITextServicesManager.Stub {
+ private static final String TAG = TextServicesManagerService.class.getSimpleName();
+ private static final boolean DBG = false;
+
+ private final Context mContext;
+ private boolean mSystemReady;
+ private final TextServicesMonitor mMonitor;
+ private final HashMap<String, SpellCheckerInfo> mSpellCheckerMap =
+ new HashMap<String, SpellCheckerInfo>();
+ private final ArrayList<SpellCheckerInfo> mSpellCheckerList = new ArrayList<SpellCheckerInfo>();
+ private final HashMap<String, SpellCheckerBindGroup> mSpellCheckerBindGroups =
+ new HashMap<String, SpellCheckerBindGroup>();
+
+ public void systemReady() {
+ if (!mSystemReady) {
+ mSystemReady = true;
+ }
+ }
+
+ public TextServicesManagerService(Context context) {
+ mSystemReady = false;
+ mContext = context;
+ mMonitor = new TextServicesMonitor();
+ mMonitor.register(context, true);
+ synchronized (mSpellCheckerMap) {
+ buildSpellCheckerMapLocked(context, mSpellCheckerList, mSpellCheckerMap);
+ }
+ }
+
+ private class TextServicesMonitor extends PackageMonitor {
+ @Override
+ public void onSomePackagesChanged() {
+ synchronized (mSpellCheckerMap) {
+ buildSpellCheckerMapLocked(mContext, mSpellCheckerList, mSpellCheckerMap);
+ // TODO: Update for each locale
+ SpellCheckerInfo sci = getCurrentSpellChecker(null);
+ if (sci == null) {
+ sci = findAvailSpellCheckerLocked(null, null);
+ if (sci == null) return;
+ // Set the current spell checker if there is one or more spell checkers
+ // available. In this case, "sci" is the first one in the available spell
+ // checkers.
+ setCurrentSpellChecker(sci);
+ }
+ final String packageName = sci.getPackageName();
+ final int change = isPackageDisappearing(packageName);
+ if (change == PACKAGE_PERMANENT_CHANGE || change == PACKAGE_TEMPORARY_CHANGE) {
+ // Package disappearing
+ setCurrentSpellChecker(findAvailSpellCheckerLocked(null, packageName));
+ } else if (isPackageModified(packageName)) {
+ // Package modified
+ setCurrentSpellChecker(findAvailSpellCheckerLocked(null, packageName));
+ }
+ }
+ }
+ }
+
+ private static void buildSpellCheckerMapLocked(Context context,
+ ArrayList<SpellCheckerInfo> list, HashMap<String, SpellCheckerInfo> map) {
+ list.clear();
+ map.clear();
+ final PackageManager pm = context.getPackageManager();
+ List<ResolveInfo> services = pm.queryIntentServices(
+ new Intent(SpellCheckerService.SERVICE_INTERFACE), PackageManager.GET_META_DATA);
+ final int N = services.size();
+ for (int i = 0; i < N; ++i) {
+ final ResolveInfo ri = services.get(i);
+ final ServiceInfo si = ri.serviceInfo;
+ final ComponentName compName = new ComponentName(si.packageName, si.name);
+ if (!android.Manifest.permission.BIND_TEXT_SERVICE.equals(si.permission)) {
+ Slog.w(TAG, "Skipping text service " + compName
+ + ": it does not require the permission "
+ + android.Manifest.permission.BIND_TEXT_SERVICE);
+ continue;
+ }
+ if (DBG) Slog.d(TAG, "Add: " + compName);
+ final SpellCheckerInfo sci = new SpellCheckerInfo(context, ri);
+ list.add(sci);
+ map.put(sci.getId(), sci);
+ }
+ }
+
+ // TODO: find an appropriate spell checker for specified locale
+ private SpellCheckerInfo findAvailSpellCheckerLocked(String locale, String prefPackage) {
+ final int spellCheckersCount = mSpellCheckerList.size();
+ if (spellCheckersCount == 0) {
+ Slog.w(TAG, "no available spell checker services found");
+ return null;
+ }
+ if (prefPackage != null) {
+ for (int i = 0; i < spellCheckersCount; ++i) {
+ final SpellCheckerInfo sci = mSpellCheckerList.get(i);
+ if (prefPackage.equals(sci.getPackageName())) {
+ return sci;
+ }
+ }
+ }
+ if (spellCheckersCount > 1) {
+ Slog.w(TAG, "more than one spell checker service found, picking first");
+ }
+ return mSpellCheckerList.get(0);
+ }
+
+ // TODO: Save SpellCheckerService by supported languages. Currently only one spell
+ // checker is saved.
+ @Override
+ public SpellCheckerInfo getCurrentSpellChecker(String locale) {
+ synchronized (mSpellCheckerMap) {
+ final String curSpellCheckerId =
+ Settings.Secure.getString(mContext.getContentResolver(),
+ Settings.Secure.SPELL_CHECKER_SERVICE);
+ if (TextUtils.isEmpty(curSpellCheckerId)) {
+ return null;
+ }
+ return mSpellCheckerMap.get(curSpellCheckerId);
+ }
+ }
+
+ @Override
+ public void getSpellCheckerService(SpellCheckerInfo info, String locale,
+ ITextServicesSessionListener tsListener, ISpellCheckerSessionListener scListener) {
+ if (!mSystemReady) {
+ return;
+ }
+ if (info == null || tsListener == null) {
+ Slog.e(TAG, "getSpellCheckerService: Invalid input.");
+ return;
+ }
+ final String sciId = info.getId();
+ synchronized(mSpellCheckerMap) {
+ if (!mSpellCheckerMap.containsKey(sciId)) {
+ return;
+ }
+ if (mSpellCheckerBindGroups.containsKey(sciId)) {
+ mSpellCheckerBindGroups.get(sciId).addListener(tsListener, locale, scListener);
+ return;
+ }
+ final InternalServiceConnection connection = new InternalServiceConnection(
+ sciId, locale, scListener);
+ final Intent serviceIntent = new Intent(SpellCheckerService.SERVICE_INTERFACE);
+ serviceIntent.setComponent(info.getComponent());
+ if (!mContext.bindService(serviceIntent, connection, Context.BIND_AUTO_CREATE)) {
+ Slog.e(TAG, "Failed to get a spell checker service.");
+ return;
+ }
+ final SpellCheckerBindGroup group = new SpellCheckerBindGroup(
+ connection, tsListener, locale, scListener);
+ mSpellCheckerBindGroups.put(sciId, group);
+ }
+ return;
+ }
+
+ @Override
+ public void finishSpellCheckerService(ISpellCheckerSessionListener listener) {
+ synchronized(mSpellCheckerMap) {
+ for (SpellCheckerBindGroup group : mSpellCheckerBindGroups.values()) {
+ if (group == null) continue;
+ group.removeListener(listener);
+ }
+ }
+ }
+
+ private void setCurrentSpellChecker(SpellCheckerInfo sci) {
+ if (sci == null || mSpellCheckerMap.containsKey(sci.getId())) return;
+ Settings.Secure.putString(mContext.getContentResolver(),
+ Settings.Secure.SPELL_CHECKER_SERVICE, sci == null ? "" : sci.getId());
+ }
+
+ // SpellCheckerBindGroup contains active text service session listeners.
+ // If there are no listeners anymore, the SpellCheckerBindGroup instance will be removed from
+ // mSpellCheckerBindGroups
+ private class SpellCheckerBindGroup {
+ final InternalServiceConnection mInternalConnection;
+ final ArrayList<InternalDeathRecipient> mListeners =
+ new ArrayList<InternalDeathRecipient>();
+
+ public SpellCheckerBindGroup(InternalServiceConnection connection,
+ ITextServicesSessionListener listener, String locale,
+ ISpellCheckerSessionListener scListener) {
+ mInternalConnection = connection;
+ addListener(listener, locale, scListener);
+ }
+
+ public void onServiceConnected(ISpellCheckerService spellChecker) {
+ synchronized(mSpellCheckerMap) {
+ for (InternalDeathRecipient listener : mListeners) {
+ try {
+ final ISpellCheckerSession session = spellChecker.getISpellCheckerSession(
+ listener.mScLocale, listener.mScListener);
+ listener.mTsListener.onServiceConnected(session);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+ }
+
+ public void addListener(ITextServicesSessionListener tsListener, String locale,
+ ISpellCheckerSessionListener scListener) {
+ synchronized(mSpellCheckerMap) {
+ try {
+ final int size = mListeners.size();
+ for (int i = 0; i < size; ++i) {
+ if (mListeners.get(i).hasSpellCheckerListener(scListener)) {
+ // do not add the lister if the group already contains this.
+ return;
+ }
+ }
+ final InternalDeathRecipient recipient = new InternalDeathRecipient(
+ this, tsListener, locale, scListener);
+ scListener.asBinder().linkToDeath(recipient, 0);
+ mListeners.add(new InternalDeathRecipient(
+ this, tsListener, locale, scListener));
+ } catch(RemoteException e) {
+ // do nothing
+ }
+ cleanLocked();
+ }
+ }
+
+ public void removeListener(ISpellCheckerSessionListener listener) {
+ synchronized(mSpellCheckerMap) {
+ final int size = mListeners.size();
+ final ArrayList<InternalDeathRecipient> removeList =
+ new ArrayList<InternalDeathRecipient>();
+ for (int i = 0; i < size; ++i) {
+ final InternalDeathRecipient tempRecipient = mListeners.get(i);
+ if(tempRecipient.hasSpellCheckerListener(listener)) {
+ removeList.add(tempRecipient);
+ }
+ }
+ final int removeSize = removeList.size();
+ for (int i = 0; i < removeSize; ++i) {
+ mListeners.remove(removeList.get(i));
+ }
+ cleanLocked();
+ }
+ }
+
+ private void cleanLocked() {
+ if (mListeners.isEmpty()) {
+ mSpellCheckerBindGroups.remove(this);
+ // Unbind service when there is no active clients.
+ mContext.unbindService(mInternalConnection);
+ }
+ }
+ }
+
+ private class InternalServiceConnection implements ServiceConnection {
+ private final ISpellCheckerSessionListener mListener;
+ private final String mSciId;
+ private final String mLocale;
+ public InternalServiceConnection(
+ String id, String locale, ISpellCheckerSessionListener listener) {
+ mSciId = id;
+ mLocale = locale;
+ mListener = listener;
+ }
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ synchronized(mSpellCheckerMap) {
+ ISpellCheckerService spellChecker = ISpellCheckerService.Stub.asInterface(service);
+ final SpellCheckerBindGroup group = mSpellCheckerBindGroups.get(mSciId);
+ if (group != null) {
+ group.onServiceConnected(spellChecker);
+ }
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ mSpellCheckerBindGroups.remove(mSciId);
+ }
+ }
+
+ private class InternalDeathRecipient implements IBinder.DeathRecipient {
+ public final ITextServicesSessionListener mTsListener;
+ public final ISpellCheckerSessionListener mScListener;
+ public final String mScLocale;
+ private final SpellCheckerBindGroup mGroup;
+ public InternalDeathRecipient(SpellCheckerBindGroup group,
+ ITextServicesSessionListener tsListener, String scLocale,
+ ISpellCheckerSessionListener scListener) {
+ mTsListener = tsListener;
+ mScListener = scListener;
+ mScLocale = scLocale;
+ mGroup = group;
+ }
+
+ public boolean hasSpellCheckerListener(ISpellCheckerSessionListener listener) {
+ return mScListener.equals(listener);
+ }
+
+ @Override
+ public void binderDied() {
+ mGroup.removeListener(mScListener);
+ }
+ }
+}
diff --git a/services/java/com/android/server/ThrottleService.java b/services/java/com/android/server/ThrottleService.java
index b8890aa..cd649ce 100644
--- a/services/java/com/android/server/ThrottleService.java
+++ b/services/java/com/android/server/ThrottleService.java
@@ -194,6 +194,7 @@
}
public void interfaceRemoved(String iface) {}
+ public void limitReached(String limitName, String iface) {}
}
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 0924b86..3389f33 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -3628,6 +3628,7 @@
app.setSchedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
app.forcingToForeground = null;
app.foregroundServices = false;
+ app.hasShownUi = false;
app.debugging = false;
mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
@@ -9218,6 +9219,7 @@
app.forcingToForeground = null;
app.foregroundServices = false;
app.foregroundActivities = false;
+ app.hasShownUi = false;
killServicesLocked(app, true);
@@ -9331,8 +9333,6 @@
// This app is persistent, so we need to keep its record around.
// If it is not already on the pending app list, add it there
// and start a new process for it.
- app.forcingToForeground = null;
- app.foregroundServices = false;
if (mPersistentStartingProcesses.indexOf(app) < 0) {
mPersistentStartingProcesses.add(app);
restart = true;
@@ -12728,21 +12728,31 @@
while (jt.hasNext() && adj > FOREGROUND_APP_ADJ) {
ServiceRecord s = jt.next();
if (s.startRequested) {
- if (now < (s.lastActivity+MAX_SERVICE_INACTIVITY)) {
- // This service has seen some activity within
- // recent memory, so we will keep its process ahead
- // of the background processes.
+ if (app.hasShownUi) {
+ // If this process has shown some UI, let it immediately
+ // go to the LRU list because it may be pretty heavy with
+ // UI stuff. We'll tag it with a label just to help
+ // debug and understand what is going on.
if (adj > SECONDARY_SERVER_ADJ) {
- adj = SECONDARY_SERVER_ADJ;
- app.adjType = "started-services";
- app.hidden = false;
+ app.adjType = "started-bg-ui-services";
}
- }
- // If we have let the service slide into the background
- // state, still have some text describing what it is doing
- // even though the service no longer has an impact.
- if (adj > SECONDARY_SERVER_ADJ) {
- app.adjType = "started-bg-services";
+ } else {
+ if (now < (s.lastActivity+MAX_SERVICE_INACTIVITY)) {
+ // This service has seen some activity within
+ // recent memory, so we will keep its process ahead
+ // of the background processes.
+ if (adj > SECONDARY_SERVER_ADJ) {
+ adj = SECONDARY_SERVER_ADJ;
+ app.adjType = "started-services";
+ app.hidden = false;
+ }
+ }
+ // If we have let the service slide into the background
+ // state, still have some text describing what it is doing
+ // even though the service no longer has an impact.
+ if (adj > SECONDARY_SERVER_ADJ) {
+ app.adjType = "started-bg-services";
+ }
}
// Don't kill this process because it is doing work; it
// has said it is doing work.
@@ -13351,15 +13361,15 @@
break;
}
}
- } else if (app.curAdj >= PERCEPTIBLE_APP_ADJ) {
- if (app.trimMemoryLevel < ComponentCallbacks.TRIM_MEMORY_INVISIBLE
+ } else if (app.curAdj == HEAVY_WEIGHT_APP_ADJ) {
+ if (app.trimMemoryLevel < ComponentCallbacks.TRIM_MEMORY_BACKGROUND
&& app.thread != null) {
try {
- app.thread.scheduleTrimMemory(ComponentCallbacks.TRIM_MEMORY_INVISIBLE);
+ app.thread.scheduleTrimMemory(ComponentCallbacks.TRIM_MEMORY_BACKGROUND);
} catch (RemoteException e) {
}
}
- app.trimMemoryLevel = ComponentCallbacks.TRIM_MEMORY_INVISIBLE;
+ app.trimMemoryLevel = ComponentCallbacks.TRIM_MEMORY_BACKGROUND;
} else {
app.trimMemoryLevel = 0;
}
@@ -13442,7 +13452,7 @@
}
public boolean profileControl(String process, boolean start,
- String path, ParcelFileDescriptor fd) throws RemoteException {
+ String path, ParcelFileDescriptor fd, int profileType) throws RemoteException {
try {
synchronized (this) {
@@ -13487,7 +13497,7 @@
}
}
- proc.thread.profilerControl(start, path, fd);
+ proc.thread.profilerControl(start, path, fd, profileType);
fd = null;
return true;
}
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index 0d89081..cc58eaf 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -559,6 +559,7 @@
r.forceNewConfig = false;
showAskCompatModeDialogLocked(r);
r.compat = mService.compatibilityInfoForPackageLocked(r.info.applicationInfo);
+ app.hasShownUi = true;
app.thread.scheduleLaunchActivity(new Intent(r.intent), r,
System.identityHashCode(r),
r.info, r.compat, r.icicle, results, newIntents, !andResume,
diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java
index 9e597aa..5b59363 100644
--- a/services/java/com/android/server/am/ProcessRecord.java
+++ b/services/java/com/android/server/am/ProcessRecord.java
@@ -66,6 +66,7 @@
boolean setIsForeground; // Running foreground UI when last set?
boolean foregroundServices; // Running any services that are foreground?
boolean foregroundActivities; // Running any activities that are foreground?
+ boolean hasShownUi; // Has UI been shown in this process since it was started?
boolean bad; // True if disabled in the bad process list
boolean killedBackground; // True when proc has been killed due to too many bg
String waitingToKill; // Process is waiting to be killed when in the bg; reason
@@ -185,6 +186,7 @@
pw.print(prefix); pw.print("curSchedGroup="); pw.print(curSchedGroup);
pw.print(" setSchedGroup="); pw.print(setSchedGroup);
pw.print(" trimMemoryLevel="); pw.println(trimMemoryLevel);
+ pw.print(" hasShownUi="); pw.println(hasShownUi);
pw.print(prefix); pw.print("setIsForeground="); pw.print(setIsForeground);
pw.print(" foregroundServices="); pw.print(foregroundServices);
pw.print(" forcingToForeground="); pw.println(forcingToForeground);
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
index d7d4b03..82bc505 100644
--- a/services/java/com/android/server/connectivity/Tethering.java
+++ b/services/java/com/android/server/connectivity/Tethering.java
@@ -288,6 +288,8 @@
}
}
+ public void limitReached(String limitName, String iface) {}
+
public int tether(String iface) {
Log.d(TAG, "Tethering " + iface);
TetherInterfaceSM sm = null;
diff --git a/services/java/com/android/server/connectivity/Vpn.java b/services/java/com/android/server/connectivity/Vpn.java
index 05e95a7..cf75d6b 100644
--- a/services/java/com/android/server/connectivity/Vpn.java
+++ b/services/java/com/android/server/connectivity/Vpn.java
@@ -248,6 +248,8 @@
}
}
+ public void limitReached(String limitName, String iface) {}
+
private void showNotification(VpnConfig config, String label, Bitmap icon) {
NotificationManager nm = (NotificationManager)
mContext.getSystemService(Context.NOTIFICATION_SERVICE);
diff --git a/services/java/com/android/server/usb/UsbDeviceManager.java b/services/java/com/android/server/usb/UsbDeviceManager.java
index c80cd0a..f183f83 100644
--- a/services/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/java/com/android/server/usb/UsbDeviceManager.java
@@ -19,6 +19,7 @@
import android.app.PendingIntent;
import android.app.Notification;
import android.app.NotificationManager;
+import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
@@ -80,6 +81,7 @@
private static final int MSG_ENABLE_ADB = 1;
private static final int MSG_SET_CURRENT_FUNCTION = 2;
private static final int MSG_SYSTEM_READY = 3;
+ private static final int MSG_BOOT_COMPLETED = 4;
// Delay for debouncing USB disconnects.
// We often get rapid connect/disconnect events when enabling USB functions,
@@ -87,7 +89,7 @@
private static final int UPDATE_DELAY = 1000;
private UsbHandler mHandler;
- private boolean mSystemReady;
+ private boolean mBootCompleted;
private final Context mContext;
private final ContentResolver mContentResolver;
@@ -141,10 +143,15 @@
Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
mHandler = new UsbHandler(thread.getLooper());
+
+ if (nativeIsStartRequested()) {
+ if (DEBUG) Slog.d(TAG, "accessory attached at boot");
+ setCurrentFunction(UsbManager.USB_FUNCTION_ACCESSORY, false);
+ }
}
public void systemReady() {
- mSystemReady = true;
+ if (DEBUG) Slog.d(TAG, "systemReady");
mNotificationManager = (NotificationManager)
mContext.getSystemService(Context.NOTIFICATION_SERVICE);
@@ -236,15 +243,22 @@
private String mCurrentFunctions;
private String mDefaultFunctions;
private UsbAccessory mCurrentAccessory;
- private boolean mDeferAccessoryAttached;
private int mUsbNotificationId;
private boolean mAdbNotificationShown;
+ private final BroadcastReceiver mBootCompletedReceiver = new BroadcastReceiver() {
+ public void onReceive(Context context, Intent intent) {
+ if (DEBUG) Slog.d(TAG, "boot completed");
+ mHandler.sendEmptyMessage(MSG_BOOT_COMPLETED);
+ }
+ };
+
private static final int NOTIFICATION_NONE = 0;
private static final int NOTIFICATION_MTP = 1;
private static final int NOTIFICATION_PTP = 2;
private static final int NOTIFICATION_INSTALLER = 3;
- private static final int NOTIFICATION_ADB = 4;
+ private static final int NOTIFICATION_ACCESSORY = 4;
+ private static final int NOTIFICATION_ADB = 5;
public UsbHandler(Looper looper) {
super(looper);
@@ -285,6 +299,9 @@
// Watch for USB configuration changes
mUEventObserver.startObserving(USB_STATE_MATCH);
mUEventObserver.startObserving(ACCESSORY_START_MATCH);
+
+ mContext.registerReceiver(mBootCompletedReceiver,
+ new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
} catch (Exception e) {
Slog.e(TAG, "Error initializing UsbHandler", e);
}
@@ -406,11 +423,9 @@
mCurrentAccessory = new UsbAccessory(strings);
Slog.d(TAG, "entering USB accessory mode: " + mCurrentAccessory);
// defer accessoryAttached if system is not ready
- if (mSystemReady) {
+ if (mBootCompleted) {
mSettingsManager.accessoryAttached(mCurrentAccessory);
- } else {
- mDeferAccessoryAttached = true;
- }
+ } // else handle in mBootCompletedReceiver
} else {
Slog.e(TAG, "nativeGetAccessoryStrings failed");
}
@@ -421,7 +436,7 @@
setEnabledFunctions(mDefaultFunctions);
if (mCurrentAccessory != null) {
- if (mSystemReady) {
+ if (mBootCompleted) {
mSettingsManager.accessoryDetached(mCurrentAccessory);
}
mCurrentAccessory = null;
@@ -463,7 +478,7 @@
// restore defaults when USB is disconnected
doSetCurrentFunctions(mDefaultFunctions);
}
- if (mSystemReady) {
+ if (mBootCompleted) {
updateUsbState();
}
break;
@@ -497,7 +512,10 @@
updateUsbNotification();
updateAdbNotification();
updateUsbState();
- if (mCurrentAccessory != null && mDeferAccessoryAttached) {
+ break;
+ case MSG_BOOT_COMPLETED:
+ mBootCompleted = true;
+ if (mCurrentAccessory != null) {
mSettingsManager.accessoryAttached(mCurrentAccessory);
}
break;
@@ -527,6 +545,10 @@
title = r.getText(
com.android.internal.R.string.usb_cd_installer_notification_title);
id = NOTIFICATION_INSTALLER;
+ } else if (containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_ACCESSORY)) {
+ title = r.getText(
+ com.android.internal.R.string.usb_accessory_notification_title);
+ id = NOTIFICATION_ACCESSORY;
} else {
Slog.e(TAG, "No known USB function in updateUsbNotification");
}
@@ -671,4 +693,5 @@
private native String[] nativeGetAccessoryStrings();
private native ParcelFileDescriptor nativeOpenAccessory();
+ private native boolean nativeIsStartRequested();
}
diff --git a/services/java/com/android/server/wm/BlackFrame.java b/services/java/com/android/server/wm/BlackFrame.java
index d8fd7fe..36f5dcb 100644
--- a/services/java/com/android/server/wm/BlackFrame.java
+++ b/services/java/com/android/server/wm/BlackFrame.java
@@ -32,10 +32,12 @@
final int top;
final Surface surface;
- BlackSurface(SurfaceSession session, int layer, int l, int t, int w, int h)
+ BlackSurface(SurfaceSession session, int layer, int l, int t, int r, int b)
throws Surface.OutOfResourcesException {
left = l;
top = t;
+ int w = r-l;
+ int h = b-t;
surface = new Surface(session, 0, "BlackSurface",
-1, w, h, PixelFormat.OPAQUE, Surface.FX_SURFACE_DIM);
if (WindowManagerService.SHOW_TRANSACTIONS ||
diff --git a/services/jni/com_android_server_UsbDeviceManager.cpp b/services/jni/com_android_server_UsbDeviceManager.cpp
index 6954171..40f0dbd 100644
--- a/services/jni/com_android_server_UsbDeviceManager.cpp
+++ b/services/jni/com_android_server_UsbDeviceManager.cpp
@@ -99,11 +99,26 @@
gParcelFileDescriptorOffsets.mConstructor, fileDescriptor);
}
+static jboolean android_server_UsbDeviceManager_isStartRequested(JNIEnv *env, jobject thiz)
+{
+ int fd = open(DRIVER_NAME, O_RDWR);
+ if (fd < 0) {
+ LOGE("could not open %s", DRIVER_NAME);
+ return false;
+ }
+ int result = ioctl(fd, ACCESSORY_IS_START_REQUESTED);
+ close(fd);
+ return (result == 1);
+}
+
+
static JNINativeMethod method_table[] = {
{ "nativeGetAccessoryStrings", "()[Ljava/lang/String;",
(void*)android_server_UsbDeviceManager_getAccessoryStrings },
{ "nativeOpenAccessory", "()Landroid/os/ParcelFileDescriptor;",
(void*)android_server_UsbDeviceManager_openAccessory },
+ { "nativeIsStartRequested", "()Z",
+ (void*)android_server_UsbDeviceManager_isStartRequested },
};
int register_android_server_UsbDeviceManager(JNIEnv *env)
diff --git a/services/surfaceflinger/SurfaceTextureLayer.cpp b/services/surfaceflinger/SurfaceTextureLayer.cpp
index 11f61cc..40659d4 100644
--- a/services/surfaceflinger/SurfaceTextureLayer.cpp
+++ b/services/surfaceflinger/SurfaceTextureLayer.cpp
@@ -60,6 +60,10 @@
sp<Layer> layer(mLayer.promote());
if (layer != NULL) {
+ uint32_t orientation = layer->getOrientation();
+ if (orientation & Transform::ROT_INVALID) {
+ orientation = 0;
+ }
*outTransform = layer->getOrientation();
}
diff --git a/telephony/java/com/android/internal/telephony/PhoneBase.java b/telephony/java/com/android/internal/telephony/PhoneBase.java
index 40a70a8..a0961ca 100644
--- a/telephony/java/com/android/internal/telephony/PhoneBase.java
+++ b/telephony/java/com/android/internal/telephony/PhoneBase.java
@@ -806,10 +806,10 @@
mNotifier.notifyDataConnection(this, reason, apnType, getDataConnectionState(apnType));
}
- public void notifyDataConnection() {
+ public void notifyDataConnection(String reason) {
String types[] = getActiveApnTypes();
for (String apnType : types) {
- mNotifier.notifyDataConnection(this, null, apnType, getDataConnectionState(apnType));
+ mNotifier.notifyDataConnection(this, reason, apnType, getDataConnectionState(apnType));
}
}
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java
index 0d551aa..5df2edd 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java
@@ -134,8 +134,6 @@
}
}
- // Not sure if this is needed in CDMALTE phone.
- // mDataRoaming = regCodeIsRoaming(regState);
mLteSS.setRadioTechnology(type);
mLteSS.setState(regCodeToServiceState(regState));
} else {
@@ -345,13 +343,14 @@
}
if (cm.getSimState().isSIMReady()) {
- // SIM is found on the device. If ERI roaming is OFF, use operator name
- // from CSIM record.
+ // SIM is found on the device. If ERI roaming is OFF and SID/NID matches
+ // one configfured in SIM, use operator name from CSIM record.
boolean showSpn =
((CdmaLteUiccRecords)phone.mIccRecords).getCsimSpnDisplayCondition();
int iconIndex = ss.getCdmaEriIconIndex();
- if (showSpn && (iconIndex == EriInfo.ROAMING_INDICATOR_OFF)) {
+ if (showSpn && (iconIndex == EriInfo.ROAMING_INDICATOR_OFF) &&
+ isInHomeSidNid(ss.getSystemId(), ss.getNetworkId())) {
ss.setOperatorAlphaLong(phone.mIccRecords.getServiceProviderName());
}
}
@@ -401,7 +400,7 @@
}
if ((hasCdmaDataConnectionChanged || hasNetworkTypeChanged)) {
- phone.notifyDataConnection();
+ phone.notifyDataConnection(null);
}
if (hasRoamingOn) {
@@ -469,6 +468,34 @@
}
/**
+ * Check whether the specified SID and NID pair appears in the HOME SID/NID list
+ * read from NV or SIM.
+ *
+ * @return true if provided sid/nid pair belongs to operator's home network.
+ */
+ private boolean isInHomeSidNid(int sid, int nid) {
+ // if SID/NID is not available, assume this is home network.
+ if (isSidsAllZeros()) return true;
+
+ // length of SID/NID shold be same
+ if (mHomeSystemId.length != mHomeNetworkId.length) return true;
+
+ if (sid == 0) return true;
+
+ for (int i = 0; i < mHomeSystemId.length; i++) {
+ // Use SID only if NID is a reserved value.
+ // SID 0 and NID 0 and 65535 are reserved. (C.0005 2.6.5.2)
+ if ((mHomeSystemId[i] == sid) &&
+ ((mHomeNetworkId[i] == 0) || (mHomeNetworkId[i] == 65535) ||
+ (nid == 0) || (nid == 65535) || (mHomeNetworkId[i] == nid))) {
+ return true;
+ }
+ }
+ // SID/NID are not in the list. So device is not in home network
+ return false;
+ }
+
+ /**
* Returns OTASP_NOT_NEEDED as its not needed for LTE
*/
@Override
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
index 24a468a..2cf4b88 100755
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
@@ -130,8 +130,8 @@
protected String mCurPlmn = null;
protected String mMdn;
- private int mHomeSystemId[] = null;
- private int mHomeNetworkId[] = null;
+ protected int mHomeSystemId[] = null;
+ protected int mHomeNetworkId[] = null;
protected String mMin;
protected String mPrlVersion;
protected boolean mIsMinInfoReady = false;
@@ -999,7 +999,7 @@
}
if (hasCdmaDataConnectionChanged || hasNetworkTypeChanged) {
- phone.notifyDataConnection();
+ phone.notifyDataConnection(null);
}
if (hasRoamingOn) {
@@ -1481,7 +1481,7 @@
}
}
- private boolean isSidsAllZeros() {
+ protected boolean isSidsAllZeros() {
if (mHomeSystemId != null) {
for (int i=0; i < mHomeSystemId.length; i++) {
if (mHomeSystemId[i] != 0) {
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
index 93f4b4e..d3645fa 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
@@ -915,7 +915,7 @@
}
if (hasRadioTechnologyChanged) {
- phone.notifyDataConnection(Phone.REASON_NW_TYPE_CHANGED, Phone.APN_TYPE_ALL);
+ phone.notifyDataConnection(Phone.REASON_NW_TYPE_CHANGED);
}
if (hasRoamingOn) {
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ListActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ListActivity.java
index 1493ab9..13b6129 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/ListActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ListActivity.java
@@ -93,12 +93,9 @@
}
public void startProfiling(View v) {
- ViewDebug.startLooperProfiling(new File(Environment.getExternalStorageDirectory(),
- "looper.trace"));
}
public void stopProfiling(View v) {
- ViewDebug.stopLooperProfiling();
}
@Override
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
index e776463..9aa70b0 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
@@ -87,8 +87,6 @@
mIsProcessing = false;
}
- // This is a hack to work around an invalidation bug
- mBitmapOut.setPixel(0, 0, 0);
mOutPixelsAllocation.copyTo(mBitmapOut);
mDisplayView.invalidate();
}
diff --git a/tests/TileBenchmark/AndroidManifest.xml b/tests/TileBenchmark/AndroidManifest.xml
index 663cc0d..ab61a9e 100644
--- a/tests/TileBenchmark/AndroidManifest.xml
+++ b/tests/TileBenchmark/AndroidManifest.xml
@@ -7,14 +7,16 @@
android:label="@string/app_name"
android:hardwareAccelerated="true">
<activity android:name=".ProfileActivity"
- android:label="@string/profile_activity">
+ android:label="@string/profile_activity"
+ android:theme="@android:style/Theme.Holo.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".PlaybackActivity"
- android:label="@string/playback_activity">
+ android:label="@string/playback_activity"
+ android:theme="@android:style/Theme.Holo.NoActionBar">
</activity>
</application>
</manifest>
diff --git a/tests/TileBenchmark/res/layout/main.xml b/tests/TileBenchmark/res/layout/main.xml
index 4a81da6..577c466 100644
--- a/tests/TileBenchmark/res/layout/main.xml
+++ b/tests/TileBenchmark/res/layout/main.xml
@@ -23,11 +23,11 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
- <Button
- android:id="@+id/inspect"
+ <Spinner
+ android:id="@+id/movement"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:text="@string/inspect_log"
+ android:prompt="@string/movement_method"
/>
<Spinner
android:id="@+id/velocity"
@@ -36,6 +36,13 @@
android:gravity="center_horizontal"
android:prompt="@string/desired_scroll_velocity"
/>
+ <ToggleButton
+ android:id="@+id/capture"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textOn="@string/capture_stop"
+ android:textOff="@string/capture_start"
+ />
<EditText
android:id="@+id/url"
android:layout_width="0dip"
@@ -44,6 +51,12 @@
android:imeOptions="actionGo"
android:layout_weight="1"
/>
+ <Button
+ android:id="@+id/inspect"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/inspect_log"
+ />
</LinearLayout>
<com.test.tilebenchmark.ProfiledWebView
android:id="@+id/web"
diff --git a/tests/TileBenchmark/res/values/colors.xml b/tests/TileBenchmark/res/values/colors.xml
index 3958083..dbb8e72 100644
--- a/tests/TileBenchmark/res/values/colors.xml
+++ b/tests/TileBenchmark/res/values/colors.xml
@@ -18,8 +18,17 @@
<color name="ready_tile">#ff4ac230</color>
<!-- The color of tiles with stale / invalid textures -->
<color name="unready_tile">#ff744400</color>
- <!-- Background color for logged URLs -->
- <color name="finished_url">#ff004000</color>
- <!-- Background color for URLs with logging in progress -->
- <color name="unfinished_url">#ff400000</color>
+ <!-- Viewport overlay in playback -->
+ <color name="view">#50000050</color>
+ <!-- Invalidated region overlay in playback - start color -->
+ <color name="inval_region_start">#80ff0000</color>
+ <!-- Invalidated region overlay in playback - stop color-->
+ <color name="inval_region_stop">#80ffffff</color>
+
+ <!-- Background color for not testing -->
+ <color name="background_not_testing">#ff000000</color>
+ <!-- Background color for during testing -->
+ <color name="background_start_testing">#ff400000</color>
+ <!-- Background color for testing complete -->
+ <color name="background_stop_testing">#ff004000</color>
</resources>
diff --git a/tests/TileBenchmark/res/values/strings.xml b/tests/TileBenchmark/res/values/strings.xml
index f70ee2c..66972ac 100644
--- a/tests/TileBenchmark/res/values/strings.xml
+++ b/tests/TileBenchmark/res/values/strings.xml
@@ -28,6 +28,10 @@
<string name="loadbutton">Load</string>
<!-- Button, opens the playback activity [CHAR LIMIT=20] -->
<string name="inspect_log">Inspect Log</string>
+ <!-- ToggleButton label when pressing starts capture [CHAR LIMIT=15] -->
+ <string name="capture_start">Start Capture</string>
+ <!-- ToggleButton label when pressing stops capture [CHAR LIMIT=15] -->
+ <string name="capture_stop">Stop Capture</string>
<!-- The speed of auto-scrolling [CHAR LIMIT=30] -->
<string name="desired_scroll_velocity">Choose Scroll Velocity</string>
<!-- Pixels moved per frame [CHAR LIMIT=10] -->
@@ -39,6 +43,21 @@
<item>200</item>
<item>400</item>
</string-array>
+ <!-- Drop down menu for selecting scrolling vs manual navigation for
+ capturing [CHAR LIMIT=15] -->
+ <string name="movement_method">Movement Method</string>
+ <!-- Drop down menu entry - automatically scroll to the end of the page
+ with scrollBy() [CHAR LIMIT=15] -->
+ <string name="movement_auto_scroll">Auto-scroll</string>
+ <!-- Drop down menu entry - [CHAR LIMIT=15] -->
+ <string name="movement_auto_fling">Auto-fling</string>
+ <!-- Drop down menu entry - manually navigate the page(s), hit 'capture'
+ button [CHAR LIMIT=15] -->
+ <string name="movement_manual">Manual</string>
+
+ <!-- Error popup indicating log data couldn't be loaded [CHAR LIMIT=60] -->
+ <string name="error_no_data">Error: log data could not be loaded.</string>
+
<!-- 25th percentile - 25% of frames fall below this value [CHAR LIMIT=12]
-->
<string name="percentile_25">25%ile</string>
@@ -56,7 +75,7 @@
<string name="format_stat">%4.4f</string>
<!-- Format string for displaying aggregate stats+values (nr of valid tiles,
etc.) [CHAR LIMIT=20] -->
- <string name="format_stat_name">%1$9s %2$3d</string>
+ <string name="format_stat_name">%1$-20s %2$3d</string>
<!-- Text hovering over canvas, number of tiles ready [CHAR LIMIT=15] -->
<string name="ready_tiles">Ready Tiles</string>
<!-- Text hovering over canvas, number tiles not ready [CHAR LIMIT=15] -->
@@ -64,4 +83,7 @@
<!-- Text hovering over canvas, number of tiles that haven't been
allocated to a place on the page [CHAR LIMIT=15] -->
<string name="unplaced_tiles">Unplaced Tiles</string>
+ <!-- Text hovering over canvas, number of invalidated regions this frame
+ [CHAR LIMIT=15] -->
+ <string name="number_invalidates">Invalidates</string>
</resources>
diff --git a/tests/TileBenchmark/src/com/test/tilebenchmark/PlaybackActivity.java b/tests/TileBenchmark/src/com/test/tilebenchmark/PlaybackActivity.java
index 5130f5d..36694a7 100644
--- a/tests/TileBenchmark/src/com/test/tilebenchmark/PlaybackActivity.java
+++ b/tests/TileBenchmark/src/com/test/tilebenchmark/PlaybackActivity.java
@@ -27,6 +27,7 @@
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;
+import android.widget.Toast;
import java.io.FileInputStream;
import java.io.IOException;
@@ -102,7 +103,10 @@
@Override
protected void onPostExecute(TileData data[][]) {
if (data == null) {
- data = genTestPattern();
+ Toast.makeText(getApplicationContext(),
+ getResources().getString(R.string.error_no_data),
+ Toast.LENGTH_LONG).show();
+ return;
}
mPlaybackView.setData(data);
@@ -166,23 +170,4 @@
new LoadFileTask().execute(ProfileActivity.TEMP_FILENAME);
}
-
- private TileData[][] genTestPattern() {
- final int XMAX = 5;
- final int FRAMEMAX = 99;
-
- TileData example[][] = new TileData[FRAMEMAX][];
- for (int frame = 0; frame < FRAMEMAX; frame++) {
- int numTiles = frame + 10;
-
- example[frame] = new TileData[numTiles];
- for (int t = 0; t < numTiles; t++) {
- int x = t % XMAX;
- int y = t / XMAX;
- boolean isReady = y * 10 < frame;
- example[frame][t] = new TileData(x, y, isReady, 0);
- }
- }
- return example;
- }
}
diff --git a/tests/TileBenchmark/src/com/test/tilebenchmark/PlaybackGraphs.java b/tests/TileBenchmark/src/com/test/tilebenchmark/PlaybackGraphs.java
index db4a341..35b1563 100644
--- a/tests/TileBenchmark/src/com/test/tilebenchmark/PlaybackGraphs.java
+++ b/tests/TileBenchmark/src/com/test/tilebenchmark/PlaybackGraphs.java
@@ -28,19 +28,17 @@
import java.util.Arrays;
public class PlaybackGraphs {
- private static final int BAR_WIDTH = PlaybackView.TILEX * 3;
+ private static final int BAR_WIDTH = PlaybackView.TILE_SCALE * 3;
private static final float CANVAS_SCALE = 0.2f;
private static final double IDEAL_FRAMES = 60;
private static final int LABELOFFSET = 100;
private static Paint whiteLabels;
- private static double viewportCoverage(int l, int b, int r, int t,
- int tileIndexX,
- int tileIndexY) {
- if (tileIndexX * PlaybackView.TILEX < r
- && (tileIndexX + 1) * PlaybackView.TILEX >= l
- && tileIndexY * PlaybackView.TILEY < t
- && (tileIndexY + 1) * PlaybackView.TILEY >= b) {
+ private static double viewportCoverage(TileData view, TileData tile) {
+ if (tile.left < view.right
+ && tile.right >= view.left
+ && tile.top < view.bottom
+ && tile.bottom >= view.top) {
return 1.0f;
}
return 0.0f;
@@ -76,13 +74,10 @@
// coverage graph
@Override
public double getValue(TileData[] frame) {
- int l = frame[0].x, b = frame[0].y;
- int r = frame[1].x, t = frame[1].y;
double total = 0, totalCount = 0;
- for (int tileID = 2; tileID < frame.length; tileID++) {
+ for (int tileID = 1; tileID < frame.length; tileID++) {
TileData data = frame[tileID];
- double coverage = viewportCoverage(l, b, r, t, data.x,
- data.y);
+ double coverage = viewportCoverage(frame[0], data);
total += coverage * (data.isReady ? 1 : 0);
totalCount += coverage;
}
@@ -158,7 +153,7 @@
public PlaybackGraphs() {
whiteLabels = new Paint();
whiteLabels.setColor(Color.WHITE);
- whiteLabels.setTextSize(PlaybackView.TILEY / 3);
+ whiteLabels.setTextSize(PlaybackView.TILE_SCALE / 3);
}
private ArrayList<ShapeDrawable> mShapes = new ArrayList<ShapeDrawable>();
@@ -177,11 +172,13 @@
int lastBar = 0;
for (int frameIndex = 0; frameIndex < tileProfilingData.length; frameIndex++) {
TileData frame[] = tileProfilingData[frameIndex];
- int newBar = (frame[0].y + frame[1].y) / 2;
+ int newBar = (frame[0].top + frame[0].bottom) / 2;
MetricGen s = Metrics[metricIndex];
double absoluteValue = s.getValue(frame);
double relativeValue = absoluteValue / s.getMax();
+ relativeValue = Math.min(1,relativeValue);
+ relativeValue = Math.max(0,relativeValue);
int rightPos = (int) (-BAR_WIDTH * metricIndex);
int leftPos = (int) (-BAR_WIDTH * (metricIndex + relativeValue));
@@ -207,7 +204,7 @@
ArrayList<ShapeDrawable> shapes) {
// Shapes drawn here are drawn relative to the viewRect
Rect viewRect = shapes.get(shapes.size() - 1).getBounds();
- canvas.translate(0, 5 * PlaybackView.TILEY - viewRect.top);
+ canvas.translate(0, 5 * PlaybackView.TILE_SCALE - viewRect.top);
for (ShapeDrawable shape : mShapes) {
shape.draw(canvas);
@@ -234,13 +231,15 @@
int yPos = LABELOFFSET;
canvas.drawText(label, xPos, yPos, whiteLabels);
for (int statIndex = 0; statIndex < Stats.length; statIndex++) {
- label = resources.getString(R.string.format_stat, mStats[metricIndex][statIndex]);
- yPos = LABELOFFSET + (1 + statIndex) * PlaybackView.TILEY / 2;
+ label = resources.getString(R.string.format_stat,
+ mStats[metricIndex][statIndex]);
+ yPos = LABELOFFSET + (1 + statIndex) * PlaybackView.TILE_SCALE
+ / 2;
canvas.drawText(label, xPos, yPos, whiteLabels);
}
}
for (int stringIndex = 0; stringIndex < strings.length; stringIndex++) {
- int yPos = LABELOFFSET + stringIndex * PlaybackView.TILEY / 2;
+ int yPos = LABELOFFSET + stringIndex * PlaybackView.TILE_SCALE / 2;
canvas.drawText(strings[stringIndex], 0, yPos, whiteLabels);
}
}
diff --git a/tests/TileBenchmark/src/com/test/tilebenchmark/PlaybackView.java b/tests/TileBenchmark/src/com/test/tilebenchmark/PlaybackView.java
index f104eac..edc8643 100644
--- a/tests/TileBenchmark/src/com/test/tilebenchmark/PlaybackView.java
+++ b/tests/TileBenchmark/src/com/test/tilebenchmark/PlaybackView.java
@@ -16,6 +16,9 @@
package com.test.tilebenchmark;
+import android.animation.ArgbEvaluator;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -30,8 +33,9 @@
import java.util.ArrayList;
public class PlaybackView extends View {
- public static final int TILEX = 300;
- public static final int TILEY = 300;
+ public static final int TILE_SCALE = 300;
+ private static final int INVAL_FLAG = -2;
+ private static final int INVAL_CYCLE = 250;
private Paint levelPaint = null, coordPaint = null, goldPaint = null;
private PlaybackGraphs mGraphs;
@@ -39,28 +43,46 @@
private ArrayList<ShapeDrawable> mTempShapes = new ArrayList<ShapeDrawable>();
private TileData mProfData[][] = null;
private GestureDetector mGestureDetector = null;
- private String mRenderStrings[] = new String[3];
+ private String mRenderStrings[] = new String[4];
private class TileDrawable extends ShapeDrawable {
TileData tile;
+ String label;
- public TileDrawable(TileData t) {
- int tileColorId = t.isReady ? R.color.ready_tile
- : R.color.unready_tile;
- getPaint().setColor(getResources().getColor(tileColorId));
-
- setBounds(t.x * TILEX, t.y * TILEY, (t.x + 1) * TILEX, (t.y + 1)
- * TILEY);
+ public TileDrawable(TileData t, int colorId) {
this.tile = t;
+ getPaint().setColor(getResources().getColor(colorId));
+ if (colorId == R.color.ready_tile
+ || colorId == R.color.unready_tile) {
+
+ label = (int) (t.left / TILE_SCALE) + ", "
+ + (int) (t.top / TILE_SCALE);
+ // ignore scale value for tiles
+ setBounds(t.left, t.top,
+ t.right, t.bottom);
+ } else {
+ setBounds((int) (t.left * t.scale),
+ (int) (t.top * t.scale),
+ (int) (t.right * t.scale),
+ (int) (t.bottom * t.scale));
+ }
+ }
+
+ @SuppressWarnings("unused")
+ public void setColor(int color) {
+ getPaint().setColor(color);
}
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
- canvas.drawText(Integer.toString(tile.level), getBounds().left,
- getBounds().bottom, levelPaint);
- canvas.drawText(tile.x + "," + tile.y, getBounds().left,
- ((getBounds().bottom + getBounds().top) / 2), coordPaint);
+ if (label != null) {
+ canvas.drawText(Integer.toString(tile.level), getBounds().left,
+ getBounds().bottom, levelPaint);
+ canvas.drawText(label, getBounds().left,
+ ((getBounds().bottom + getBounds().top) / 2),
+ coordPaint);
+ }
}
}
@@ -92,10 +114,10 @@
private void init() {
levelPaint = new Paint();
levelPaint.setColor(Color.WHITE);
- levelPaint.setTextSize(TILEY / 2);
+ levelPaint.setTextSize(TILE_SCALE / 2);
coordPaint = new Paint();
coordPaint.setColor(Color.BLACK);
- coordPaint.setTextSize(TILEY / 3);
+ coordPaint.setTextSize(TILE_SCALE / 3);
goldPaint = new Paint();
goldPaint.setColor(0xffa0e010);
mGraphs = new PlaybackGraphs();
@@ -110,6 +132,7 @@
}
mGraphs.draw(canvas, mTempShapes, mRenderStrings, getResources());
+ invalidate(); // may have animations, force redraw
}
public int setFrame(int frame) {
@@ -117,35 +140,66 @@
return 0;
}
- int readyTiles = 0, unreadyTiles = 0, unplacedTiles = 0;
+ int readyTiles = 0, unreadyTiles = 0, unplacedTiles = 0, numInvals = 0;
mTempShapes.clear();
- // draw actual tiles
- for (int tileID = 2; tileID < mProfData[frame].length; tileID++) {
- TileData t = mProfData[frame][tileID];
- mTempShapes.add(new TileDrawable(t));
- if (t.isReady) {
- readyTiles++;
+ // create tile shapes (as they're drawn on bottom)
+ for (TileData t : mProfData[frame]) {
+ if (t.level != INVAL_FLAG && t != mProfData[frame][0]) {
+ int colorId;
+ if (t.isReady) {
+ readyTiles++;
+ colorId = R.color.ready_tile;
+ } else {
+ unreadyTiles++;
+ colorId = R.color.unready_tile;
+ }
+ if (t.left < 0 || t.top < 0) {
+ unplacedTiles++;
+ }
+ mTempShapes.add(new TileDrawable(t, colorId));
} else {
- unreadyTiles++;
- }
- if (t.x < 0 || t.y < 0) {
- unplacedTiles++;
+ numInvals++;
}
}
+
+ // create invalidate shapes (drawn above tiles)
+ int invalId = 0;
+ for (TileData t : mProfData[frame]) {
+ if (t.level == INVAL_FLAG && t != mProfData[frame][0]) {
+ TileDrawable invalShape = new TileDrawable(t,
+ R.color.inval_region_start);
+ ValueAnimator tileAnimator = ObjectAnimator.ofInt(invalShape,
+ "color",
+ getResources().getColor(R.color.inval_region_start),
+ getResources().getColor(R.color.inval_region_stop));
+ tileAnimator.setDuration(numInvals * INVAL_CYCLE);
+ tileAnimator.setEvaluator(new ArgbEvaluator());
+ tileAnimator.setRepeatCount(ValueAnimator.INFINITE);
+ tileAnimator.setRepeatMode(ValueAnimator.RESTART);
+ float delay = (float) (invalId) * INVAL_CYCLE;
+ tileAnimator.setStartDelay((int) delay);
+ invalId++;
+ tileAnimator.start();
+
+ mTempShapes.add(invalShape);
+ }
+ }
+
mRenderStrings[0] = getResources().getString(R.string.format_stat_name,
getResources().getString(R.string.ready_tiles), readyTiles);
mRenderStrings[1] = getResources().getString(R.string.format_stat_name,
getResources().getString(R.string.unready_tiles), unreadyTiles);
mRenderStrings[2] = getResources().getString(R.string.format_stat_name,
- getResources().getString(R.string.unplaced_tiles), unplacedTiles);
+ getResources().getString(R.string.unplaced_tiles),
+ unplacedTiles);
+ mRenderStrings[3] = getResources().getString(R.string.format_stat_name,
+ getResources().getString(R.string.number_invalidates),
+ numInvals);
- // draw view rect (using first two TileData objects)
- ShapeDrawable viewShape = new ShapeDrawable();
- viewShape.getPaint().setColor(0xff0000ff);
- viewShape.setAlpha(64);
- viewShape.setBounds(mProfData[frame][0].x, mProfData[frame][0].y,
- mProfData[frame][1].x, mProfData[frame][1].y);
+ // draw view rect (using first TileData object, on top)
+ TileDrawable viewShape = new TileDrawable(mProfData[frame][0],
+ R.color.view);
mTempShapes.add(viewShape);
this.invalidate();
return frame;
diff --git a/tests/TileBenchmark/src/com/test/tilebenchmark/ProfileActivity.java b/tests/TileBenchmark/src/com/test/tilebenchmark/ProfileActivity.java
index 23b6275..1521807 100644
--- a/tests/TileBenchmark/src/com/test/tilebenchmark/ProfileActivity.java
+++ b/tests/TileBenchmark/src/com/test/tilebenchmark/ProfileActivity.java
@@ -38,6 +38,7 @@
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
+import android.widget.ToggleButton;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -58,11 +59,24 @@
// before test
Button mInspectButton;
+ ToggleButton mCaptureButton;
Spinner mVelocitySpinner;
+ Spinner mMovementSpinner;
EditText mUrl;
ProfiledWebView mWeb;
ProfileCallback mCallback;
+ LoggingWebViewClient mLoggingWebViewClient = new LoggingWebViewClient();
+ AutoLoggingWebViewClient mAutoLoggingWebViewClient = new AutoLoggingWebViewClient();
+
+ private enum TestingState {
+ NOT_TESTING,
+ PRE_TESTING,
+ START_TESTING,
+ STOP_TESTING,
+ SAVED_TESTING
+ };
+
private class VelocitySelectedListener implements OnItemSelectedListener {
@Override
public void onItemSelected(AdapterView<?> parent, View view,
@@ -77,6 +91,31 @@
}
}
+ private class MovementSelectedListener implements OnItemSelectedListener {
+ @Override
+ public void onItemSelected(AdapterView<?> parent, View view,
+ int position, long id) {
+ String movementStr = parent.getItemAtPosition(position).toString();
+ if (movementStr == getResources().getString(
+ R.string.movement_auto_scroll)
+ || movementStr == getResources().getString(
+ R.string.movement_auto_fling)) {
+ mWeb.setWebViewClient(mAutoLoggingWebViewClient);
+ mCaptureButton.setEnabled(false);
+ mVelocitySpinner.setEnabled(true);
+ } else if (movementStr == getResources().getString(
+ R.string.movement_manual)) {
+ mWeb.setWebViewClient(mLoggingWebViewClient);
+ mCaptureButton.setEnabled(true);
+ mVelocitySpinner.setEnabled(false);
+ }
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> parent) {
+ }
+ }
+
private class LoggingWebViewClient extends WebViewClient {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
@@ -88,6 +127,9 @@
super.onPageStarted(view, url, favicon);
mUrl.setText(url);
}
+ }
+
+ private class AutoLoggingWebViewClient extends LoggingWebViewClient {
@Override
public void onPageFinished(WebView view, String url) {
@@ -100,10 +142,16 @@
@Override
public void onFinish() {
- mWeb.startScrollTest(mCallback);
+ startViewProfiling(true);
}
}.start();
}
+
+ @Override
+ public void onPageStarted(WebView view, String url, Bitmap favicon) {
+ super.onPageStarted(view, url, favicon);
+ setTestingState(TestingState.PRE_TESTING);
+ }
}
private class StoreFileTask extends
@@ -125,24 +173,65 @@
@Override
protected void onPostExecute(Void v) {
- mUrl.setBackgroundResource(R.color.finished_url);
+ setTestingState(TestingState.SAVED_TESTING);
}
}
+ public void setTestingState(TestingState state) {
+ switch (state) {
+ case NOT_TESTING:
+ mUrl.setBackgroundResource(R.color.background_not_testing);
+ mInspectButton.setEnabled(true);
+ mMovementSpinner.setEnabled(true);
+ break;
+ case PRE_TESTING:
+ mInspectButton.setEnabled(false);
+ mMovementSpinner.setEnabled(false);
+ break;
+ case START_TESTING:
+ mUrl.setBackgroundResource(R.color.background_start_testing);
+ mInspectButton.setEnabled(false);
+ mMovementSpinner.setEnabled(false);
+ break;
+ case STOP_TESTING:
+ mUrl.setBackgroundResource(R.color.background_stop_testing);
+ break;
+ case SAVED_TESTING:
+ mInspectButton.setEnabled(true);
+ mMovementSpinner.setEnabled(true);
+ break;
+ }
+ }
+
+ /** auto - automatically scroll. */
+ private void startViewProfiling(boolean auto) {
+ if (!auto) {
+ // manual, toggle capture button to indicate capture state to user
+ mCaptureButton.setChecked(true);
+ }
+ mWeb.startScrollTest(mCallback, auto);
+ setTestingState(TestingState.START_TESTING);
+ }
+
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mInspectButton = (Button) findViewById(R.id.inspect);
+ mCaptureButton = (ToggleButton) findViewById(R.id.capture);
mVelocitySpinner = (Spinner) findViewById(R.id.velocity);
+ mMovementSpinner = (Spinner) findViewById(R.id.movement);
mUrl = (EditText) findViewById(R.id.url);
mWeb = (ProfiledWebView) findViewById(R.id.web);
mCallback = new ProfileCallback() {
@SuppressWarnings("unchecked")
@Override
public void profileCallback(TileData[][] data) {
- new StoreFileTask().execute(new Pair<String, TileData[][]>(TEMP_FILENAME, data));
+ new StoreFileTask().execute(new Pair<String, TileData[][]>(
+ TEMP_FILENAME, data));
+ mCaptureButton.setChecked(false);
+ setTestingState(TestingState.STOP_TESTING);
}
};
@@ -166,6 +255,33 @@
new VelocitySelectedListener());
mVelocitySpinner.setSelection(3);
+ // Movement spinner
+ String content[] = {
+ getResources().getString(R.string.movement_auto_scroll),
+ getResources().getString(R.string.movement_auto_fling),
+ getResources().getString(R.string.movement_manual)
+ };
+ adapter = new ArrayAdapter<CharSequence>(this,
+ android.R.layout.simple_spinner_item, content);
+ adapter.setDropDownViewResource(
+ android.R.layout.simple_spinner_dropdown_item);
+ mMovementSpinner.setAdapter(adapter);
+ mMovementSpinner.setOnItemSelectedListener(
+ new MovementSelectedListener());
+ mMovementSpinner.setSelection(0);
+
+ // Capture toggle button
+ mCaptureButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mCaptureButton.isChecked()) {
+ startViewProfiling(false);
+ } else {
+ mWeb.stopScrollTest();
+ }
+ }
+ });
+
// Custom profiling WebView
WebSettings settings = mWeb.getSettings();
settings.setJavaScriptEnabled(true);
@@ -180,12 +296,13 @@
public boolean onEditorAction(TextView v, int actionId,
KeyEvent event) {
String url = mUrl.getText().toString();
- mUrl.setBackgroundResource(R.color.unfinished_url);
mWeb.loadUrl(url);
mWeb.requestFocus();
return true;
}
});
+
+ setTestingState(TestingState.NOT_TESTING);
}
public void setCallback(ProfileCallback callback) {
diff --git a/tests/TileBenchmark/src/com/test/tilebenchmark/ProfiledWebView.java b/tests/TileBenchmark/src/com/test/tilebenchmark/ProfiledWebView.java
index 6560624..d3941be 100644
--- a/tests/TileBenchmark/src/com/test/tilebenchmark/ProfiledWebView.java
+++ b/tests/TileBenchmark/src/com/test/tilebenchmark/ProfiledWebView.java
@@ -59,12 +59,13 @@
}
/*
- * Called once the page is loaded to start scrolling for evaluating tiles
+ * Called once the page is loaded to start scrolling for evaluating tiles.
+ * If autoScrolling isn't set, stop must be called manually.
*/
- public void startScrollTest(ProfileCallback callback) {
- isScrolling = true;
+ public void startScrollTest(ProfileCallback callback, boolean autoScrolling) {
+ isScrolling = autoScrolling;
mCallback = callback;
- super.tileProfilingStart();
+ tileProfilingStart();
invalidate();
}
@@ -72,19 +73,31 @@
* Called once the page has stopped scrolling
*/
public void stopScrollTest() {
- float testRatio = super.tileProfilingStop();
+ super.tileProfilingStop();
+
+ if (mCallback == null) {
+ tileProfilingClear();
+ return;
+ }
TileData data[][] = new TileData[super.tileProfilingNumFrames()][];
for (int frame = 0; frame < data.length; frame++) {
data[frame] = new TileData[
- super.tileProfilingNumTilesInFrame(frame)];
+ tileProfilingNumTilesInFrame(frame)];
for (int tile = 0; tile < data[frame].length; tile++) {
- int x = super.tileProfilingGetX(frame, tile);
- int y = super.tileProfilingGetY(frame, tile);
- boolean isReady = super.tileProfilingGetReady(frame, tile);
- int level = super.tileProfilingGetLevel(frame, tile);
+ int left = tileProfilingGetInt(frame, tile, "left");
+ int top = tileProfilingGetInt(frame, tile, "top");
+ int right = tileProfilingGetInt(frame, tile, "right");
+ int bottom = tileProfilingGetInt(frame, tile, "bottom");
- data[frame][tile] = new TileData(x, y, isReady, level);
+ boolean isReady = super.tileProfilingGetInt(
+ frame, tile, "isReady") == 1;
+ int level = tileProfilingGetInt(frame, tile, "level");
+
+ float scale = tileProfilingGetFloat(frame, tile, "scale");
+
+ data[frame][tile] = new TileData(left, top, right, bottom,
+ isReady, level, scale);
}
}
super.tileProfilingClear();
diff --git a/tests/TileBenchmark/src/com/test/tilebenchmark/TileData.java b/tests/TileBenchmark/src/com/test/tilebenchmark/TileData.java
index 7d4bb9f..3e729a6 100644
--- a/tests/TileBenchmark/src/com/test/tilebenchmark/TileData.java
+++ b/tests/TileBenchmark/src/com/test/tilebenchmark/TileData.java
@@ -19,14 +19,24 @@
import java.io.Serializable;
public class TileData implements Serializable {
- public int x, y;
+ int left, top, right, bottom;
public boolean isReady;
public int level;
+ public float scale;
- public TileData(int x, int y, boolean isReady, int level) {
- this.x = x;
- this.y = y;
+ public TileData(int left, int top, int right, int bottom, boolean isReady,
+ int level, float scale) {
+ this.left = left;
+ this.right = right;
+ this.top = top;
+ this.bottom = bottom;
this.isReady = isReady;
this.level = level;
+ this.scale = scale;
+ }
+
+ public String toString() {
+ return "Tile (" + left + "," + top + ")->("
+ + right + "," + bottom + ")";
}
}
diff --git a/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java b/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java
index b09e04b..fa7cf21 100644
--- a/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java
@@ -68,7 +68,7 @@
public class WifiWatchdogStateMachine extends StateMachine {
- private static final boolean VDBG = true; //TODO : Remove this before merge
+ private static final boolean VDBG = false;
private static final boolean DBG = true;
private static final String WWSM_TAG = "WifiWatchdogStateMachine";