diff options
84 files changed, 2785 insertions, 320 deletions
diff --git a/Android.mk b/Android.mk index 335fb732b214..3b0fc599ed17 100644 --- a/Android.mk +++ b/Android.mk @@ -158,6 +158,11 @@ LOCAL_SRC_FILES += \ 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 @@ aidl_files := \ 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 93f7a8f26b88..f88001d825f1 100644 --- a/api/current.txt +++ b/api/current.txt @@ -21,6 +21,7 @@ package android { 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 @@ package android.content { 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.security { } +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 @@ package android.view { 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); @@ -24016,6 +24047,54 @@ package android.view.inputmethod { } +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/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index 2178971a3453..b7cd829563c9 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -1104,10 +1104,10 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM 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; - int profileType = data.readInt(); boolean res = profileControl(process, start, path, fd, profileType); reply.writeNoException(); reply.writeInt(res ? 1 : 0); @@ -2896,6 +2896,7 @@ class ActivityManagerProxy implements IActivityManager data.writeInterfaceToken(IActivityManager.descriptor); data.writeString(process); data.writeInt(start ? 1 : 0); + data.writeInt(profileType); data.writeString(path); if (fd != null) { data.writeInt(1); @@ -2903,7 +2904,6 @@ class ActivityManagerProxy implements IActivityManager } else { data.writeInt(0); } - data.writeInt(profileType); mRemote.transact(PROFILE_CONTROL_TRANSACTION, data, reply, 0); reply.readException(); boolean res = reply.readInt() != 0; diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java index c8a3a9474112..9a5b527d7c5b 100644 --- a/core/java/android/app/ApplicationThreadNative.java +++ b/core/java/android/app/ApplicationThreadNative.java @@ -376,10 +376,10 @@ public abstract class ApplicationThreadNative extends Binder { 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; - int profileType = data.readInt(); profilerControl(start, path, fd, profileType); return true; } @@ -941,6 +941,7 @@ class ApplicationThreadProxy implements IApplicationThread { 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); @@ -948,7 +949,6 @@ class ApplicationThreadProxy implements IApplicationThread { } else { data.writeInt(0); } - data.writeInt(profileType); mRemote.transact(PROFILER_CONTROL_TRANSACTION, data, null, IBinder.FLAG_ONEWAY); data.recycle(); diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index d2323e7defec..6289730cb56e 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -83,6 +83,7 @@ import android.view.ContextThemeWrapper; 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 @@ class ContextImpl extends Context { 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 c82c9ecf7a8f..789d3a692775 100644 --- a/core/java/android/app/FragmentManager.java +++ b/core/java/android/app/FragmentManager.java @@ -1695,6 +1695,7 @@ final class FragmentManagerImpl extends FragmentManager { public void dispatchDestroy() { mDestroyed = true; + execPendingActions(); moveToState(Fragment.INITIALIZING, false); mActivity = null; } diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index fed6d815c9f2..0a2253c8dc75 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -1612,6 +1612,15 @@ public abstract class Context { /** * 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 eafff49528f5..5cfa998857b7 100644 --- a/core/java/android/inputmethodservice/ExtractEditLayout.java +++ b/core/java/android/inputmethodservice/ExtractEditLayout.java @@ -172,7 +172,10 @@ public class ExtractEditLayout extends LinearLayout { @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 a866436ed77d..b035c51d3da5 100644 --- a/core/java/android/net/EthernetDataTracker.java +++ b/core/java/android/net/EthernetDataTracker.java @@ -103,6 +103,10 @@ public class EthernetDataTracker implements NetworkStateTracker { 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 4436e6e444ee..a97f2030a146 100644 --- a/core/java/android/net/INetworkManagementEventObserver.aidl +++ b/core/java/android/net/INetworkManagementEventObserver.aidl @@ -52,4 +52,14 @@ interface INetworkManagementEventObserver { * @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/VpnBuilder.java b/core/java/android/net/VpnBuilder.java new file mode 100644 index 000000000000..458252345152 --- /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 cd39d5cc6dee..bc372447d63e 100644 --- a/core/java/android/os/Handler.java +++ b/core/java/android/os/Handler.java @@ -361,7 +361,8 @@ public class Handler { /** * 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 @@ public class Handler { /** * 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 @@ public class Handler { /** * 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 c90de176896b..78c9010af8fd 100644 --- a/core/java/android/preference/PreferenceActivity.java +++ b/core/java/android/preference/PreferenceActivity.java @@ -563,6 +563,12 @@ public abstract class PreferenceActivity extends ListActivity implements // 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 @@ public abstract class PreferenceActivity extends ListActivity implements } 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 c2998916add4..4a719ec7b9a1 100644 --- a/core/java/android/provider/ContactsContract.java +++ b/core/java/android/provider/ContactsContract.java @@ -1439,6 +1439,13 @@ public final class ContactsContract { 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 cb87e94164bb..1cd46dedd2d2 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.net.URISyntaxException; import java.util.HashMap; import java.util.HashSet; - /** * The Settings provider contains global system-level device preferences. */ @@ -3737,6 +3734,15 @@ public final class Settings { */ 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/service/textservice/SpellCheckerService.java b/core/java/android/service/textservice/SpellCheckerService.java new file mode 100644 index 000000000000..6ac99cab0ebf --- /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 000000000000..a575220ef915 --- /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 f1393721cb41..f2a86c969368 100644 --- a/core/java/android/util/JsonReader.java +++ b/core/java/android/util/JsonReader.java @@ -740,8 +740,8 @@ public final class JsonReader implements Closeable { 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 @@ public final class JsonReader implements Closeable { 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 000000000000..ab2365eb744c --- /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 4484d59a3ce4..f4c0249ab5a9 100644 --- a/core/java/android/view/DisplayList.java +++ b/core/java/android/view/DisplayList.java @@ -41,15 +41,6 @@ public abstract class DisplayList { 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 80244bbe9244..d22fa6e501ec 100644 --- a/core/java/android/view/GLES20Canvas.java +++ b/core/java/android/view/GLES20Canvas.java @@ -51,6 +51,7 @@ class GLES20Canvas extends HardwareCanvas { // 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 @@ class GLES20Canvas extends HardwareCanvas { 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 @@ class GLES20Canvas extends HardwareCanvas { 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; - - // 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 static final class CanvasFinalizer { + private final int mRenderer; - 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 @@ class GLES20Canvas extends HardwareCanvas { // 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 @@ class GLES20Canvas extends HardwareCanvas { @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 @@ class GLES20Canvas extends HardwareCanvas { @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 aeff31f8a193..9e649cea63dc 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; - - private boolean mStarted = false; - private boolean mRecorded = false; - private boolean mValid = false; + // 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); - 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) { - throw new IllegalStateException("Recording has already started"); - } - if (mCanvas != null) { - ((GLES20RecordingCanvas) mCanvas).reset(); - } else { - mCanvas = new GLES20RecordingCanvas(true); + throw new IllegalStateException("Recording has already started"); } - 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 @@ class GLES20DisplayList extends DisplayList { @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; - - // 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; - } + final int mNativeDisplayList; - 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 ec94fe70ae91..078222be6fe9 100644 --- a/core/java/android/view/GLES20RecordingCanvas.java +++ b/core/java/android/view/GLES20RecordingCanvas.java @@ -24,8 +24,10 @@ import android.graphics.Path; 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 @@ import java.util.ArrayList; * 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; + + 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; - GLES20RecordingCanvas(boolean translucent) { - super(true, translucent); + 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 @@ class GLES20RecordingCanvas extends GLES20Canvas { 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 @@ class GLES20RecordingCanvas extends GLES20Canvas { 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 b865b5007200..503b54bd437d 100644 --- a/core/java/android/view/HardwareRenderer.java +++ b/core/java/android/view/HardwareRenderer.java @@ -189,7 +189,7 @@ public abstract class HardwareRenderer { * * @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 @@ public abstract class HardwareRenderer { } @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 930eb25412f4..ecb391d12bf1 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -9833,7 +9833,7 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit // 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 5f61cbbf2d05..4acf48c6b316 100644 --- a/core/java/android/view/ViewDebug.java +++ b/core/java/android/view/ViewDebug.java @@ -25,6 +25,7 @@ import android.os.Debug; 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; @@ -50,6 +51,9 @@ import java.lang.reflect.AccessibleObject; 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; @@ -453,24 +457,33 @@ public class ViewDebug { } 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 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; private final String mPath; - private final FileDescriptor mFileDescriptor; + private ParcelFileDescriptor mFileDescriptor; LooperProfiler(String path, FileDescriptor fileDescriptor) { mPath = path; - mFileDescriptor = fileDescriptor; + 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(); } @@ -493,11 +506,11 @@ public class ViewDebug { 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; @@ -514,51 +527,131 @@ public class ViewDebug { } private void saveTraces() { - FileOutputStream fos = new FileOutputStream(mFileDescriptor); + 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); - - out.writeInt(mTraceNames.size()); - for (Map.Entry<String, Short> entry : mTraceNames.entrySet()) { - saveTraceName(entry.getKey(), entry.getValue(), out); - } + writeHeader(out, mTraceWallStart, mTraceNames, mTraces); + out.flush(); - out.writeInt(mTraces.size()); - for (Entry entry : mTraces) { - saveTrace(entry, out); - } + writeTraces(fos, out.size(), mTraceWallStart, mTraceThreadStart, mTraces); 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 000000000000..eb5dfcc01c88 --- /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 000000000000..1205adfaceb3 --- /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 000000000000..66e20d24ce1c --- /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 000000000000..b0ccbea7d91a --- /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 000000000000..d231d7638a75 --- /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 000000000000..b534eb0be607 --- /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 000000000000..6fa7e4ddf9cd --- /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/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index 8f8c1d069b9d..b7c1687debef 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -4638,9 +4638,6 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te 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/com/android/internal/textservice/ISpellCheckerService.aidl b/core/java/com/android/internal/textservice/ISpellCheckerService.aidl new file mode 100644 index 000000000000..ff0049276bce --- /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 000000000000..79e43510c00a --- /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 000000000000..796b06eb06d6 --- /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 000000000000..ad0c1ff3f816 --- /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 000000000000..ecb6cd0f85d1 --- /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 164d5811c31e..159b3da440bd 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 @@ public class MenuBuilder implements Menu { 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 @@ public class MenuBuilder implements Menu { 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 541d10120dd6..b0a002d2ce8d 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 final class MenuItemImpl implements MenuItem { 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 fb1cd5e81ef7..92acf8cda8f5 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 class SubMenuBuilder extends MenuBuilder implements SubMenu { 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 e03858b4af13..8b74f3d00428 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.text.TextUtils; 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 @@ public class ActionBarView extends AbsActionBarView { if (mCustomNavView != null) mCustomNavView.setVisibility(GONE); requestLayout(); item.setActionViewExpanded(true); + + if (mExpandedActionView instanceof CollapsibleActionView) { + ((CollapsibleActionView) mExpandedActionView).onActionViewExpanded(); + } return true; } @@ -1330,11 +1335,16 @@ public class ActionBarView extends AbsActionBarView { 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 b0c2f2c2da07..b06de9d1b91e 100644 --- a/core/jni/android_view_GLES20Canvas.cpp +++ b/core/jni/android_view_GLES20Canvas.cpp @@ -576,18 +576,18 @@ static void android_view_GLES20Canvas_drawTextRun(JNIEnv* env, jobject clazz, // ---------------------------------------------------------------------------- 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_createDisplayListRenderer(JNIEnv* env, + jobject clazz) { + return new DisplayListRenderer; } -static OpenGLRenderer* android_view_GLES20Canvas_getDisplayListRenderer(JNIEnv* env, +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 @@ static JNINativeMethod gMethods[] = { { "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 c67b6252253a..91003d181b9c 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" 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 30518e02784b..61aa72a3b307 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 20943c853578..53b0ccd43f05 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 5b25e2dff638..feac38d6d695 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 diff --git a/docs/html/guide/developing/tools/adb.jd b/docs/html/guide/developing/tools/adb.jd index 78d12effd8b7..d32cf6684eda 100644 --- a/docs/html/guide/developing/tools/adb.jd +++ b/docs/html/guide/developing/tools/adb.jd @@ -280,7 +280,7 @@ instance, see <a href="{@docRoot}guide/developing/building/index.html">Building <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 59052143523e..3f9455391a86 100644 --- a/docs/html/guide/topics/intents/intents-filters.jd +++ b/docs/html/guide/topics/intents/intents-filters.jd @@ -927,7 +927,7 @@ as described by its two intent filters: <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 34862121ae82..743832c6e763 100644 --- a/docs/html/guide/topics/manifest/activity-element.jd +++ b/docs/html/guide/topics/manifest/activity-element.jd @@ -710,7 +710,7 @@ the soft keyboard.</li> 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 @@ Level 3.</dd> <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 2a84c263f652..513886afbe23 100644 --- a/docs/html/guide/topics/providers/content-providers.jd +++ b/docs/html/guide/topics/providers/content-providers.jd @@ -277,7 +277,7 @@ are returned. All the content providers that come with the platform define 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 4a85faf7dd2e..3476bd5aa646 100644 --- a/graphics/jni/android_renderscript_RenderScript.cpp +++ b/graphics/jni/android_renderscript_RenderScript.cpp @@ -504,6 +504,7 @@ nAllocationCopyToBitmap(JNIEnv *_env, jobject _this, RsContext con, jint alloc, 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 48d1464336a1..713af92860ef 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 @@ public: return 0; } + virtual status_t reconnectAtOffset(off64_t offset) { + return ERROR_UNSUPPORTED; + } + //////////////////////////////////////////////////////////////////////////// bool sniff(String8 *mimeType, float *confidence, sp<AMessage> *meta); diff --git a/libs/gui/SurfaceTextureClient.cpp b/libs/gui/SurfaceTextureClient.cpp index 1dc6cd2cf751..986fc7e37164 100644 --- a/libs/gui/SurfaceTextureClient.cpp +++ b/libs/gui/SurfaceTextureClient.cpp @@ -55,6 +55,9 @@ void SurfaceTextureClient::init() { 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 8b1caeeedf59..886c05c64c07 100644 --- a/libs/hwui/DisplayListRenderer.cpp +++ b/libs/hwui/DisplayListRenderer.cpp @@ -883,7 +883,6 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level) /////////////////////////////////////////////////////////////////////////////// DisplayListRenderer::DisplayListRenderer(): mWriter(MIN_WRITER_SIZE) { - mDisplayList = NULL; } DisplayListRenderer::~DisplayListRenderer() { @@ -923,13 +922,13 @@ void DisplayListRenderer::reset() { // 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 b83259f82450..81576313c15f 100644 --- a/libs/hwui/DisplayListRenderer.h +++ b/libs/hwui/DisplayListRenderer.h @@ -217,7 +217,7 @@ public: 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 @@ private: SkWriter32 mWriter; - DisplayList *mDisplayList; - int mRestoreSaveCount; friend class DisplayList; diff --git a/libs/ui/FramebufferNativeWindow.cpp b/libs/ui/FramebufferNativeWindow.cpp index 412552ec8eca..0e8ae619fc5c 100644 --- a/libs/ui/FramebufferNativeWindow.cpp +++ b/libs/ui/FramebufferNativeWindow.cpp @@ -310,35 +310,21 @@ int FramebufferNativeWindow::perform(ANativeWindow* window, 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 + case NATIVE_WINDOW_SET_USAGE: + case NATIVE_WINDOW_SET_BUFFERS_GEOMETRY: + case NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS: + case NATIVE_WINDOW_SET_BUFFERS_FORMAT: + case NATIVE_WINDOW_SET_BUFFERS_TRANSFORM: + // TODO: we should implement these 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_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 - return NO_ERROR; case NATIVE_WINDOW_SET_SCALING_MODE: return INVALID_OPERATION; } diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp index ed6e3c7aa7a5..a11fb804f134 100644 --- a/media/libmedia/mediaplayer.cpp +++ b/media/libmedia/mediaplayer.cpp @@ -228,6 +228,7 @@ status_t MediaPlayer::setVideoSurface(const sp<Surface>& surface) 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 @@ status_t MediaPlayer::setVideoSurfaceTexture( 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/NuCachedSource2.cpp b/media/libstagefright/NuCachedSource2.cpp index 77a66024a60d..4edb613c33b6 100644 --- a/media/libstagefright/NuCachedSource2.cpp +++ b/media/libstagefright/NuCachedSource2.cpp @@ -185,7 +185,8 @@ NuCachedSource2::NuCachedSource2(const sp<DataSource> &source) 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::onMessageReceived(const sp<AMessage> &msg) { 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 @@ void NuCachedSource2::fetchInternal() { 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::fetchInternal() { 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 @@ void NuCachedSource2::onFetch() { 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 @@ void NuCachedSource2::restartPrefetcherIfNecessary_l( 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(status_t *finalStatus) { 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/chromium_http/ChromiumHTTPDataSource.cpp b/media/libstagefright/chromium_http/ChromiumHTTPDataSource.cpp index 588a74d99242..07a9eb892c5d 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 @@ void ChromiumHTTPDataSource::onConnectionFailed(status_t err) { mState = DISCONNECTED; mCondition.broadcast(); - mURI.clear(); + // mURI.clear(); mIOResult = err; @@ -150,8 +152,18 @@ ssize_t ChromiumHTTPDataSource::readAt(off64_t offset, void *data, size_t size) 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; @@ -236,7 +248,7 @@ void ChromiumHTTPDataSource::onDisconnectComplete() { CHECK_EQ((int)mState, (int)DISCONNECTING); mState = DISCONNECTED; - mURI.clear(); + // mURI.clear(); mCondition.broadcast(); @@ -299,5 +311,21 @@ void ChromiumHTTPDataSource::clearDRMState_l() { } } +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 d833e2efc1ba..18f89130c747 100644 --- a/media/libstagefright/include/ChromiumHTTPDataSource.h +++ b/media/libstagefright/include/ChromiumHTTPDataSource.h @@ -51,6 +51,8 @@ struct ChromiumHTTPDataSource : public HTTPBase { 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 2d6cb848292a..22b2855204e9 100644 --- a/media/libstagefright/include/NuCachedSource2.h +++ b/media/libstagefright/include/NuCachedSource2.h @@ -77,6 +77,10 @@ private: kWhatRead = 'read', }; + enum { + kMaxNumRetries = 10, + }; + sp<DataSource> mSource; sp<AHandlerReflector<NuCachedSource2> > mReflector; sp<ALooper> mLooper; @@ -93,6 +97,8 @@ private: 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/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 5298f2e66927..d7d7817fc15b 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 cca7947e827d..ea544456430d 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 @@ public class RecentsPanelView extends RelativeLayout 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 b4d2a14ecb07..d5885bb81c25 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 @@ public class NotificationRowLayout extends ViewGroup { "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 b963b131f59f..e0debf7ca994 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 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { st.isHandled = false; mPreparedPanel = st; + if (st.frozenActionViewState != null) { + st.menu.restoreActionViewStates(st.frozenActionViewState); + st.frozenActionViewState = null; + } + return true; } @@ -652,7 +657,13 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { @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 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { */ 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 ad6cebb60b78..ae13ab5a17a9 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 @@ public class PhoneWindowManager implements WindowManagerPolicy { 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 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } } - 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 @@ public class PhoneWindowManager implements WindowManagerPolicy { 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 @@ public class PhoneWindowManager implements WindowManagerPolicy { 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 @@ public class PhoneWindowManager implements WindowManagerPolicy { // 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 @@ public class PhoneWindowManager implements WindowManagerPolicy { 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 482336ba9eb8..0323fe0001a3 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -2954,7 +2954,7 @@ AudioFlinger::PlaybackThread::Track::Track( 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/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java index 73996795205e..d39f565f7f84 100644 --- a/services/java/com/android/server/InputMethodManagerService.java +++ b/services/java/com/android/server/InputMethodManagerService.java @@ -166,7 +166,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub 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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub 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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub 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 41e8a3148223..2366fcb41c8d 100644 --- a/services/java/com/android/server/NetworkManagementService.java +++ b/services/java/com/android/server/NetworkManagementService.java @@ -93,6 +93,7 @@ class NetworkManagementService extends INetworkManagementService.Stub { 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 @@ class NetworkManagementService extends INetworkManagementService.Stub { public static final int InterfaceTxThrottleResult = 219; public static final int InterfaceChange = 600; + public static final int BandwidthControl = 601; } /** @@ -265,6 +267,20 @@ class NetworkManagementService extends INetworkManagementService.Stub { } /** + * 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 @@ class NetworkManagementService extends INetworkManagementService.Stub { }.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 8c7e279b7132..766659153821 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -230,6 +230,7 @@ class ServerThread extends Thread { WallpaperManagerService wallpaper = null; LocationManagerService location = null; CountryDetectorService countryDetector = null; + TextServicesManagerService tsms = null; if (factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) { try { @@ -273,6 +274,14 @@ class ServerThread extends Thread { } 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 @@ class ServerThread extends Thread { 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 @@ class ServerThread extends Thread { 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 000000000000..4a0c837df0c6 --- /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 b8890aa2aa8e..cd649ce4c554 100644 --- a/services/java/com/android/server/ThrottleService.java +++ b/services/java/com/android/server/ThrottleService.java @@ -194,6 +194,7 @@ public class ThrottleService extends IThrottleManager.Stub { } public void interfaceRemoved(String iface) {} + public void limitReached(String limitName, String iface) {} } diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java index d7d4b0346795..82bc505ea707 100644 --- a/services/java/com/android/server/connectivity/Tethering.java +++ b/services/java/com/android/server/connectivity/Tethering.java @@ -288,6 +288,8 @@ public class Tethering extends INetworkManagementEventObserver.Stub { } } + 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 05e95a7d3d89..cf75d6bbaed0 100644 --- a/services/java/com/android/server/connectivity/Vpn.java +++ b/services/java/com/android/server/connectivity/Vpn.java @@ -248,6 +248,8 @@ public class Vpn extends INetworkManagementEventObserver.Stub { } } + 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/wm/BlackFrame.java b/services/java/com/android/server/wm/BlackFrame.java index d8fd7fe84a73..36f5dcb97056 100644 --- a/services/java/com/android/server/wm/BlackFrame.java +++ b/services/java/com/android/server/wm/BlackFrame.java @@ -32,10 +32,12 @@ public class BlackFrame { 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/surfaceflinger/SurfaceTextureLayer.cpp b/services/surfaceflinger/SurfaceTextureLayer.cpp index 11f61ccb1ada..40659d4d16be 100644 --- a/services/surfaceflinger/SurfaceTextureLayer.cpp +++ b/services/surfaceflinger/SurfaceTextureLayer.cpp @@ -60,6 +60,10 @@ status_t SurfaceTextureLayer::queueBuffer(int buf, int64_t timestamp, 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 40a70a806ce1..a0961caaa40d 100644 --- a/telephony/java/com/android/internal/telephony/PhoneBase.java +++ b/telephony/java/com/android/internal/telephony/PhoneBase.java @@ -806,10 +806,10 @@ public abstract class PhoneBase extends Handler implements Phone { 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 0d551aade502..5df2edd6a281 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 @@ public class CdmaLteServiceStateTracker extends CdmaServiceStateTracker { } } - // Not sure if this is needed in CDMALTE phone. - // mDataRoaming = regCodeIsRoaming(regState); mLteSS.setRadioTechnology(type); mLteSS.setState(regCodeToServiceState(regState)); } else { @@ -345,13 +343,14 @@ public class CdmaLteServiceStateTracker extends CdmaServiceStateTracker { } 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 @@ public class CdmaLteServiceStateTracker extends CdmaServiceStateTracker { } if ((hasCdmaDataConnectionChanged || hasNetworkTypeChanged)) { - phone.notifyDataConnection(); + phone.notifyDataConnection(null); } if (hasRoamingOn) { @@ -469,6 +468,34 @@ public class CdmaLteServiceStateTracker extends CdmaServiceStateTracker { } /** + * 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 24a468a7d2c1..2cf4b8812a37 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 @@ public class CdmaServiceStateTracker extends ServiceStateTracker { 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 @@ public class CdmaServiceStateTracker extends ServiceStateTracker { } if (hasCdmaDataConnectionChanged || hasNetworkTypeChanged) { - phone.notifyDataConnection(); + phone.notifyDataConnection(null); } if (hasRoamingOn) { @@ -1481,7 +1481,7 @@ public class CdmaServiceStateTracker extends ServiceStateTracker { } } - 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 93f4b4ea579a..d3645faba86f 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 @@ final class GsmServiceStateTracker extends ServiceStateTracker { } 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/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java index e77646397f9e..9aa70b0e0b60 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 @@ public class ImageProcessingActivity extends Activity 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/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java b/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java index b09e04b04c5f..fa7cf21627bf 100644 --- a/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java +++ b/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java @@ -68,7 +68,7 @@ import java.util.regex.Pattern; 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"; |