diff options
96 files changed, 2110 insertions, 1328 deletions
diff --git a/api/current.txt b/api/current.txt index 8e8c750..a8bf949 100644 --- a/api/current.txt +++ b/api/current.txt @@ -2017,6 +2017,7 @@ package android.accessibilityservice { method protected void onServiceConnected(); method public final boolean performGlobalAction(int); method public final void setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo); + field public static final int GESTURE_DOUBLE_TAP = 17; // 0x11 field public static final int GESTURE_SWIPE_DOWN = 2; // 0x2 field public static final int GESTURE_SWIPE_DOWN_AND_LEFT = 15; // 0xf field public static final int GESTURE_SWIPE_DOWN_AND_RIGHT = 16; // 0x10 @@ -2033,8 +2034,7 @@ package android.accessibilityservice { field public static final int GESTURE_SWIPE_UP_AND_DOWN = 7; // 0x7 field public static final int GESTURE_SWIPE_UP_AND_LEFT = 13; // 0xd field public static final int GESTURE_SWIPE_UP_AND_RIGHT = 14; // 0xe - field public static final int GESTURE_TWO_FINGER_LONG_PRESS = 18; // 0x12 - field public static final int GESTURE_TWO_FINGER_TAP = 17; // 0x11 + field public static final int GESTURE_TAP_AND_HOLD = 18; // 0x12 field public static final int GLOBAL_ACTION_BACK = 1; // 0x1 field public static final int GLOBAL_ACTION_HOME = 2; // 0x2 field public static final int GLOBAL_ACTION_NOTIFICATIONS = 4; // 0x4 @@ -11236,7 +11236,6 @@ package android.media { method public void unselectTrack(int); field public static final int SAMPLE_FLAG_ENCRYPTED = 2; // 0x2 field public static final int SAMPLE_FLAG_SYNC = 1; // 0x1 - field public static final int SEEK_TO_CLOSEST = 3; // 0x3 field public static final int SEEK_TO_CLOSEST_SYNC = 2; // 0x2 field public static final int SEEK_TO_NEXT_SYNC = 1; // 0x1 field public static final int SEEK_TO_PREVIOUS_SYNC = 0; // 0x0 @@ -12709,21 +12708,6 @@ package android.net.http { package android.net.nsd { - public class DnsSdServiceInfo implements android.os.Parcelable { - ctor public DnsSdServiceInfo(); - method public int describeContents(); - method public java.net.InetAddress getHost(); - method public int getPort(); - method public java.lang.String getServiceName(); - method public java.lang.String getServiceType(); - method public void setHost(java.net.InetAddress); - method public void setPort(int); - method public void setServiceName(java.lang.String); - method public void setServiceType(java.lang.String); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator CREATOR; - } - public class DnsSdTxtRecord implements android.os.Parcelable { ctor public DnsSdTxtRecord(); ctor public DnsSdTxtRecord(byte[]); @@ -12740,53 +12724,56 @@ package android.net.nsd { field public static final android.os.Parcelable.Creator CREATOR; } - public class NsdManager { - method public void deinitialize(android.net.nsd.NsdManager.Channel); - method public void discoverServices(android.net.nsd.NsdManager.Channel, java.lang.String, android.net.nsd.NsdManager.DnsSdDiscoveryListener); - method public void initialize(android.content.Context, android.os.Looper, android.net.nsd.NsdManager.ChannelListener); - method public void registerService(android.net.nsd.NsdManager.Channel, java.lang.String, java.lang.String, int, android.net.nsd.NsdManager.DnsSdRegisterListener); - method public void resolveService(android.net.nsd.NsdManager.Channel, java.lang.String, java.lang.String, android.net.nsd.NsdManager.DnsSdResolveListener); - method public void stopServiceDiscovery(android.net.nsd.NsdManager.Channel, android.net.nsd.NsdManager.ActionListener); - method public void unregisterService(android.net.nsd.NsdManager.Channel, int, android.net.nsd.NsdManager.ActionListener); + public final class NsdManager { + method public void discoverServices(java.lang.String, int, android.net.nsd.NsdManager.DiscoveryListener); + method public void registerService(android.net.nsd.NsdServiceInfo, int, android.net.nsd.NsdManager.RegistrationListener); + method public void resolveService(android.net.nsd.NsdServiceInfo, android.net.nsd.NsdManager.ResolveListener); + method public void stopServiceDiscovery(android.net.nsd.NsdManager.DiscoveryListener); + method public void unregisterService(android.net.nsd.NsdManager.RegistrationListener); field public static final java.lang.String ACTION_NSD_STATE_CHANGED = "android.net.nsd.STATE_CHANGED"; - field public static final int ALREADY_ACTIVE = 3; // 0x3 - field public static final int BUSY = 2; // 0x2 - field public static final int ERROR = 0; // 0x0 field public static final java.lang.String EXTRA_NSD_STATE = "nsd_state"; - field public static final int MAX_REGS_REACHED = 4; // 0x4 + field public static final int FAILURE_ALREADY_ACTIVE = 3; // 0x3 + field public static final int FAILURE_INTERNAL_ERROR = 0; // 0x0 + field public static final int FAILURE_MAX_LIMIT = 4; // 0x4 field public static final int NSD_STATE_DISABLED = 1; // 0x1 field public static final int NSD_STATE_ENABLED = 2; // 0x2 - field public static final int UNSUPPORTED = 1; // 0x1 - } - - public static abstract interface NsdManager.ActionListener { - method public abstract void onFailure(int); - method public abstract void onSuccess(); + field public static final int PROTOCOL_DNS_SD = 1; // 0x1 } - public static class NsdManager.Channel { + public static abstract interface NsdManager.DiscoveryListener { + method public abstract void onDiscoveryStarted(java.lang.String); + method public abstract void onDiscoveryStopped(java.lang.String); + method public abstract void onServiceFound(android.net.nsd.NsdServiceInfo); + method public abstract void onServiceLost(android.net.nsd.NsdServiceInfo); + method public abstract void onStartDiscoveryFailed(java.lang.String, int); + method public abstract void onStopDiscoveryFailed(java.lang.String, int); } - public static abstract interface NsdManager.ChannelListener { - method public abstract void onChannelConnected(android.net.nsd.NsdManager.Channel); - method public abstract void onChannelDisconnected(); + public static abstract interface NsdManager.RegistrationListener { + method public abstract void onRegistrationFailed(android.net.nsd.NsdServiceInfo, int); + method public abstract void onServiceRegistered(android.net.nsd.NsdServiceInfo); + method public abstract void onServiceUnregistered(android.net.nsd.NsdServiceInfo); + method public abstract void onUnregistrationFailed(android.net.nsd.NsdServiceInfo, int); } - public static abstract interface NsdManager.DnsSdDiscoveryListener { - method public abstract void onFailure(int); - method public abstract void onServiceFound(android.net.nsd.DnsSdServiceInfo); - method public abstract void onServiceLost(android.net.nsd.DnsSdServiceInfo); - method public abstract void onStarted(java.lang.String); + public static abstract interface NsdManager.ResolveListener { + method public abstract void onResolveFailed(android.net.nsd.NsdServiceInfo, int); + method public abstract void onServiceResolved(android.net.nsd.NsdServiceInfo); } - public static abstract interface NsdManager.DnsSdRegisterListener { - method public abstract void onFailure(int); - method public abstract void onServiceRegistered(int, android.net.nsd.DnsSdServiceInfo); - } - - public static abstract interface NsdManager.DnsSdResolveListener { - method public abstract void onFailure(int); - method public abstract void onServiceResolved(android.net.nsd.DnsSdServiceInfo); + public final class NsdServiceInfo implements android.os.Parcelable { + ctor public NsdServiceInfo(); + method public int describeContents(); + method public java.net.InetAddress getHost(); + method public int getPort(); + method public java.lang.String getServiceName(); + method public java.lang.String getServiceType(); + method public void setHost(java.net.InetAddress); + method public void setPort(int); + method public void setServiceName(java.lang.String); + method public void setServiceType(java.lang.String); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator CREATOR; } } diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java index b644dd1..c559e54 100644 --- a/core/java/android/accessibilityservice/AccessibilityService.java +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -284,14 +284,14 @@ public abstract class AccessibilityService extends Service { public static final int GESTURE_SWIPE_DOWN_AND_RIGHT = 16; /** - * The user has performed a two finger tap gesture on the touch screen. + * The user has performed a double tap gesture on the touch screen. */ - public static final int GESTURE_TWO_FINGER_TAP = 17; + public static final int GESTURE_DOUBLE_TAP = 17; /** - * The user has performed a two finger long press gesture on the touch screen. + * The user has performed a tap and hold gesture on the touch screen. */ - public static final int GESTURE_TWO_FINGER_LONG_PRESS = 18; + public static final int GESTURE_TAP_AND_HOLD = 18; /** * The {@link Intent} that must be declared as handled by the service. @@ -408,8 +408,8 @@ public abstract class AccessibilityService extends Service { * @see #GESTURE_SWIPE_RIGHT_AND_DOWN * @see #GESTURE_CLOCKWISE_CIRCLE * @see #GESTURE_COUNTER_CLOCKWISE_CIRCLE - * @see #GESTURE_TWO_FINGER_TAP - * @see #GESTURE_TWO_FINGER_LONG_PRESS + * @see #GESTURE_DOUBLE_TAP + * @see #GESTURE_TAP_AND_HOLD */ protected boolean onGesture(int gestureId) { // TODO: Describe the default gesture processing in the javaDoc once it is finalized. diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 64a05a8..4943c9a 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -387,7 +387,7 @@ class ContextImpl extends Context { public Object createService(ContextImpl ctx) { IBinder b = ServiceManager.getService(NSD_SERVICE); INsdManager service = INsdManager.Stub.asInterface(b); - return new NsdManager(service); + return new NsdManager(ctx.getOuterContext(), service); }}); // Note: this was previously cached in a static variable, but diff --git a/core/java/android/app/TaskStackBuilder.java b/core/java/android/app/TaskStackBuilder.java index e546f6c..14c5736 100644 --- a/core/java/android/app/TaskStackBuilder.java +++ b/core/java/android/app/TaskStackBuilder.java @@ -196,18 +196,12 @@ public class TaskStackBuilder { try { ActivityInfo info = pm.getActivityInfo(sourceActivityName, 0); String parentActivity = info.parentActivityName; - Intent parent = new Intent().setComponent( - new ComponentName(info.packageName, parentActivity)); - while (parent != null) { + while (parentActivity != null) { + Intent parent = new Intent().setComponent( + new ComponentName(info.packageName, parentActivity)); mIntents.add(insertAt, parent); info = pm.getActivityInfo(parent.getComponent(), 0); parentActivity = info.parentActivityName; - if (parentActivity != null) { - parent = new Intent().setComponent( - new ComponentName(info.packageName, parentActivity)); - } else { - parent = null; - } } } catch (NameNotFoundException e) { Log.e(TAG, "Bad ComponentName while traversing activity parent metadata"); diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java index c9bacba..01b68d4 100644 --- a/core/java/android/appwidget/AppWidgetHostView.java +++ b/core/java/android/appwidget/AppWidgetHostView.java @@ -208,8 +208,10 @@ public class AppWidgetHostView extends FrameLayout { } /** - * Provide guidance about the size of this widget to the AppWidgetManager. This information - * gets embedded into the AppWidgetExtras and causes a callback to the AppWidgetProvider. + * Provide guidance about the size of this widget to the AppWidgetManager. The widths and + * heights should correspond to the full area the AppWidgetHostView is given. Padding added by + * the framework will be accounted for automatically. This information gets embedded into the + * AppWidget options and causes a callback to the AppWidgetProvider. * @see AppWidgetProvider#onAppWidgetOptionsChanged(Context, AppWidgetManager, int, Bundle) * * @param options The bundle of options, in addition to the size information, @@ -225,10 +227,20 @@ public class AppWidgetHostView extends FrameLayout { if (options == null) { options = new Bundle(); } - options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, minWidth); - options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, minHeight); - options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, maxWidth); - options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, maxHeight); + + Rect padding = new Rect(); + if (mInfo != null) { + padding = getDefaultPaddingForWidget(mContext, mInfo.provider, padding); + } + float density = getResources().getDisplayMetrics().density; + + int xPaddingDips = (int) ((padding.left + padding.right) / density); + int yPaddingDips = (int) ((padding.top + padding.bottom) / density); + + options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, minWidth - xPaddingDips); + options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, minHeight - yPaddingDips); + options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, maxWidth - xPaddingDips); + options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, maxHeight - yPaddingDips); updateAppWidgetOptions(options); } diff --git a/core/java/android/content/SyncStorageEngine.java b/core/java/android/content/SyncStorageEngine.java index 6c7e940..226e107 100644 --- a/core/java/android/content/SyncStorageEngine.java +++ b/core/java/android/content/SyncStorageEngine.java @@ -197,6 +197,29 @@ public class SyncStorageEngine extends Handler { long delayUntil; final ArrayList<Pair<Bundle, Long>> periodicSyncs; + /** + * Copy constructor for making deep-ish copies. Only the bundles stored + * in periodic syncs can make unexpected changes. + * + * @param toCopy AuthorityInfo to be copied. + */ + AuthorityInfo(AuthorityInfo toCopy) { + account = toCopy.account; + userId = toCopy.userId; + authority = toCopy.authority; + ident = toCopy.ident; + enabled = toCopy.enabled; + syncable = toCopy.syncable; + backoffTime = toCopy.backoffTime; + backoffDelay = toCopy.backoffDelay; + delayUntil = toCopy.delayUntil; + periodicSyncs = new ArrayList<Pair<Bundle, Long>>(); + for (Pair<Bundle, Long> sync : toCopy.periodicSyncs) { + // Still not a perfect copy, because we are just copying the mappings. + periodicSyncs.add(Pair.create(new Bundle(sync.first), sync.second)); + } + } + AuthorityInfo(Account account, int userId, String authority, int ident) { this.account = account; this.userId = userId; @@ -1212,7 +1235,8 @@ public class SyncStorageEngine extends Handler { final int N = mAuthorities.size(); ArrayList<AuthorityInfo> infos = new ArrayList<AuthorityInfo>(N); for (int i=0; i<N; i++) { - infos.add(mAuthorities.valueAt(i)); + // Make deep copy because AuthorityInfo syncs are liable to change. + infos.add(new AuthorityInfo(mAuthorities.valueAt(i))); } return infos; } diff --git a/core/java/android/content/res/AssetFileDescriptor.java b/core/java/android/content/res/AssetFileDescriptor.java index 9893133..7d46710 100644 --- a/core/java/android/content/res/AssetFileDescriptor.java +++ b/core/java/android/content/res/AssetFileDescriptor.java @@ -52,6 +52,9 @@ public class AssetFileDescriptor implements Parcelable { */ public AssetFileDescriptor(ParcelFileDescriptor fd, long startOffset, long length) { + if (fd == null) { + throw new IllegalArgumentException("fd must not be null"); + } if (length < 0 && startOffset != 0) { throw new IllegalArgumentException( "startOffset must be 0 when using UNKNOWN_LENGTH"); diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 332f40a..33dea6c 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -402,7 +402,7 @@ public class InputMethodService extends AbstractInputMethodService { mShowInputFlags = 0; mShowInputRequested = false; mShowInputForced = false; - hideWindow(); + doHideWindow(); if (resultReceiver != null) { resultReceiver.send(wasVis != isInputViewShown() ? InputMethodManager.RESULT_HIDDEN @@ -737,7 +737,7 @@ public class InputMethodService extends AbstractInputMethodService { onDisplayCompletions(completions); } } else { - hideWindow(); + doHideWindow(); } } else if (mCandidatesVisibility == View.VISIBLE) { // If the candidates are currently visible, make sure the @@ -745,7 +745,7 @@ public class InputMethodService extends AbstractInputMethodService { showWindow(false); } else { // Otherwise hide the window. - hideWindow(); + doHideWindow(); } // If user uses hard keyboard, IME button should always be shown. boolean showing = onEvaluateInputViewShown(); @@ -1096,7 +1096,7 @@ public class InputMethodService extends AbstractInputMethodService { if (shown) { showWindow(false); } else { - hideWindow(); + doHideWindow(); } } } @@ -1449,9 +1449,13 @@ public class InputMethodService extends AbstractInputMethodService { mCandidatesViewStarted = false; } + private void doHideWindow() { + mImm.setImeWindowStatus(mToken, 0, mBackDisposition); + hideWindow(); + } + public void hideWindow() { finishViews(); - mImm.setImeWindowStatus(mToken, 0, mBackDisposition); if (mWindowVisible) { mWindow.hide(); mWindowVisible = false; @@ -1703,7 +1707,7 @@ public class InputMethodService extends AbstractInputMethodService { // If we have the window visible for some other reason -- // most likely to show candidates -- then just get rid // of it. This really shouldn't happen, but just in case... - if (doIt) hideWindow(); + if (doIt) doHideWindow(); } return true; } diff --git a/core/java/android/net/nsd/NsdManager.java b/core/java/android/net/nsd/NsdManager.java index 77e97e1..08ba728 100644 --- a/core/java/android/net/nsd/NsdManager.java +++ b/core/java/android/net/nsd/NsdManager.java @@ -22,12 +22,16 @@ import android.content.Context; import android.os.Binder; import android.os.IBinder; import android.os.Handler; +import android.os.HandlerThread; import android.os.Looper; import android.os.Message; import android.os.RemoteException; import android.os.Messenger; import android.text.TextUtils; import android.util.Log; +import android.util.SparseArray; + +import java.util.concurrent.CountDownLatch; import com.android.internal.util.AsyncChannel; import com.android.internal.util.Protocol; @@ -39,88 +43,73 @@ import com.android.internal.util.Protocol; * B. Another example use case is an application discovering printers on the network. * * <p> The API currently supports DNS based service discovery and discovery is currently - * limited to a local network over Multicast DNS. In future, it will be extended to - * support wide area discovery and other service discovery mechanisms. - * DNS service discovery is described at http://files.dns-sd.org/draft-cheshire-dnsext-dns-sd.txt + * limited to a local network over Multicast DNS. DNS service discovery is described at + * http://files.dns-sd.org/draft-cheshire-dnsext-dns-sd.txt * * <p> The API is asynchronous and responses to requests from an application are on listener - * callbacks provided by the application. The application must invoke {@link #initialize} before - * doing any other operation. + * callbacks on a seperate thread. * * <p> There are three main operations the API supports - registration, discovery and resolution. * <pre> * Application start * | - * | <---------------------------------------------- - * initialize() | - * | | - * | Wait until channel connects | - * | before doing any operation | - * | | - * onChannelConnected() __________ | - * | | | - * | | | - * | onServiceRegistered() | | - * Register any local services / | | - * to be advertised with \ | | If application needs to - * registerService() onFailure() | | do any further operations - * | | | again, it needs to - * | | | initialize() connection - * discoverServices() | | to framework again - * | | | - * Maintain a list to track | | - * discovered services | | - * | | | - * |---------> |-> onChannelDisconnected() - * | | | - * | onServiceFound() | - * | | | - * | add service to list | - * | | | - * |<---------- | - * | | - * |---------> | - * | | | - * | onServiceLost() | - * | | | - * | remove service from list | - * | | | - * |<---------- | - * | | - * | | - * | Connect to a service | - * | from list ? | - * | | - * resolveService() | - * | | - * onServiceResolved() | - * | | - * Establish connection to service | - * with the host and port information | - * | | - * | ___________| - * deinitialize() - * when done with all operations - * or before quit + * | + * | onServiceRegistered() + * Register any local services / + * to be advertised with \ + * registerService() onRegistrationFailed() + * | + * | + * discoverServices() + * | + * Maintain a list to track + * discovered services + * | + * |---------> + * | | + * | onServiceFound() + * | | + * | add service to list + * | | + * |<---------- + * | + * |---------> + * | | + * | onServiceLost() + * | | + * | remove service from list + * | | + * |<---------- + * | + * | + * | Connect to a service + * | from list ? + * | + * resolveService() + * | + * onServiceResolved() + * | + * Establish connection to service + * with the host and port information * * </pre> * An application that needs to advertise itself over a network for other applications to * discover it can do so with a call to {@link #registerService}. If Example is a http based * application that can provide HTML data to peer services, it can register a name "Example" * with service type "_http._tcp". A successful registration is notified with a callback to - * {@link DnsSdRegisterListener#onServiceRegistered} and a failure to register is notified - * over {@link DnsSdRegisterListener#onFailure} + * {@link RegistrationListener#onServiceRegistered} and a failure to register is notified + * over {@link RegistrationListener#onRegistrationFailed} * * <p> A peer application looking for http services can initiate a discovery for "_http._tcp" * with a call to {@link #discoverServices}. A service found is notified with a callback - * to {@link DnsSdDiscoveryListener#onServiceFound} and a service lost is notified on - * {@link DnsSdDiscoveryListener#onServiceLost}. + * to {@link DiscoveryListener#onServiceFound} and a service lost is notified on + * {@link DiscoveryListener#onServiceLost}. * * <p> Once the peer application discovers the "Example" http srevice, and needs to receive data * from the "Example" application, it can initiate a resolve with {@link #resolveService} to * resolve the host and port details for the purpose of establishing a connection. A successful - * resolve is notified on {@link DnsSdResolveListener#onServiceResolved} and a failure is notified - * on {@link DnsSdResolveListener#onFailure}. + * resolve is notified on {@link ResolveListener#onServiceResolved} and a failure is notified + * on {@link ResolveListener#onResolveFailed}. * * Applications can reserve for a service type at * http://www.iana.org/form/ports-service. Existing services can be found at @@ -129,9 +118,9 @@ import com.android.internal.util.Protocol; * Get an instance of this class by calling {@link android.content.Context#getSystemService(String) * Context.getSystemService(Context.NSD_SERVICE)}. * - * {@see DnsSdServiceInfo} + * {@see NsdServiceInfo} */ -public class NsdManager { +public final class NsdManager { private static final String TAG = "NsdManager"; INsdManager mService; @@ -204,13 +193,6 @@ public class NsdManager { public static final int UNREGISTER_SERVICE_SUCCEEDED = BASE + 14; /** @hide */ - public static final int UPDATE_SERVICE = BASE + 15; - /** @hide */ - public static final int UPDATE_SERVICE_FAILED = BASE + 16; - /** @hide */ - public static final int UPDATE_SERVICE_SUCCEEDED = BASE + 17; - - /** @hide */ public static final int RESOLVE_SERVICE = BASE + 18; /** @hide */ public static final int RESOLVE_SERVICE_FAILED = BASE + 19; @@ -218,17 +200,27 @@ public class NsdManager { public static final int RESOLVE_SERVICE_SUCCEEDED = BASE + 20; /** @hide */ - public static final int STOP_RESOLVE = BASE + 21; - /** @hide */ - public static final int STOP_RESOLVE_FAILED = BASE + 22; - /** @hide */ - public static final int STOP_RESOLVE_SUCCEEDED = BASE + 23; - - /** @hide */ public static final int ENABLE = BASE + 24; /** @hide */ public static final int DISABLE = BASE + 25; + /** @hide */ + public static final int NATIVE_DAEMON_EVENT = BASE + 26; + + /** Dns based service discovery protocol */ + public static final int PROTOCOL_DNS_SD = 0x0001; + + private Context mContext; + + private static final int INVALID_LISTENER_KEY = 0; + private int mListenerKey = 1; + private final SparseArray mListenerMap = new SparseArray(); + private final SparseArray<NsdServiceInfo> mServiceMap = new SparseArray<NsdServiceInfo>(); + private final Object mMapLock = new Object(); + + private final AsyncChannel mAsyncChannel = new AsyncChannel(); + private ServiceHandler mHandler; + private final CountDownLatch mConnected = new CountDownLatch(1); /** * Create a new Nsd instance. Applications use @@ -238,271 +230,213 @@ public class NsdManager { * @hide - hide this because it takes in a parameter of type INsdManager, which * is a system private class. */ - public NsdManager(INsdManager service) { + public NsdManager(Context context, INsdManager service) { mService = service; + mContext = context; + init(); } /** - * Passed with onFailure() calls. + * Failures are passed with {@link RegistrationListener#onRegistrationFailed}, + * {@link RegistrationListener#onUnregistrationFailed}, + * {@link DiscoveryListener#onStartDiscoveryFailed}, + * {@link DiscoveryListener#onStopDiscoveryFailed} or {@link ResolveListener#onResolveFailed}. + * * Indicates that the operation failed due to an internal error. */ - public static final int ERROR = 0; + public static final int FAILURE_INTERNAL_ERROR = 0; /** - * Passed with onFailure() calls. - * Indicates that the operation failed because service discovery - * is unsupported on the device. - */ - public static final int UNSUPPORTED = 1; - - /** - * Passed with onFailure() calls. - * Indicates that the operation failed because the framework is - * busy and unable to service the request. - */ - public static final int BUSY = 2; - - /** - * Passed with onFailure() calls. * Indicates that the operation failed because it is already active. */ - public static final int ALREADY_ACTIVE = 3; + public static final int FAILURE_ALREADY_ACTIVE = 3; /** - * Passed with onFailure() calls. - * Indicates that the operation failed because maximum limit on - * service registrations has reached. + * Indicates that the operation failed because the maximum outstanding + * requests from the applications have reached. */ - public static final int MAX_REGS_REACHED = 4; - - /** Interface for callback invocation when framework channel is connected or lost */ - public interface ChannelListener { - /** - * The channel to the framework is connected. - * Application can initiate calls into the framework using the channel instance passed. - */ - public void onChannelConnected(Channel c); - /** - * The channel to the framework has been disconnected. - * Application could try re-initializing using {@link #initialize} - */ - public void onChannelDisconnected(); - } + public static final int FAILURE_MAX_LIMIT = 4; - /** Generic interface for callback invocation for a success or failure */ - public interface ActionListener { + /** Interface for callback invocation for service discovery */ + public interface DiscoveryListener { - public void onFailure(int errorCode); + public void onStartDiscoveryFailed(String serviceType, int errorCode); - public void onSuccess(); - } + public void onStopDiscoveryFailed(String serviceType, int errorCode); - /** Interface for callback invocation for service discovery */ - public interface DnsSdDiscoveryListener { + public void onDiscoveryStarted(String serviceType); - public void onFailure(int errorCode); + public void onDiscoveryStopped(String serviceType); - public void onStarted(String serviceType); + public void onServiceFound(NsdServiceInfo serviceInfo); - public void onServiceFound(DnsSdServiceInfo serviceInfo); - - public void onServiceLost(DnsSdServiceInfo serviceInfo); + public void onServiceLost(NsdServiceInfo serviceInfo); } /** Interface for callback invocation for service registration */ - public interface DnsSdRegisterListener { - - public void onFailure(int errorCode); + public interface RegistrationListener { - public void onServiceRegistered(int registeredId, DnsSdServiceInfo serviceInfo); - } + public void onRegistrationFailed(NsdServiceInfo serviceInfo, int errorCode); - /** @hide */ - public interface DnsSdUpdateRegistrationListener { + public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode); - public void onFailure(int errorCode); + public void onServiceRegistered(NsdServiceInfo serviceInfo); - public void onServiceUpdated(int registeredId, DnsSdTxtRecord txtRecord); + public void onServiceUnregistered(NsdServiceInfo serviceInfo); } /** Interface for callback invocation for service resolution */ - public interface DnsSdResolveListener { + public interface ResolveListener { - public void onFailure(int errorCode); + public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode); - public void onServiceResolved(DnsSdServiceInfo serviceInfo); + public void onServiceResolved(NsdServiceInfo serviceInfo); } - /** - * A channel that connects the application to the NetworkService framework. - * Most service operations require a Channel as an argument. An instance of Channel is obtained - * by doing a call on {@link #initialize} - */ - public static class Channel { - Channel(Looper looper, ChannelListener l) { - mAsyncChannel = new AsyncChannel(); - mHandler = new ServiceHandler(looper); - mChannelListener = l; + private class ServiceHandler extends Handler { + ServiceHandler(Looper looper) { + super(looper); } - private ChannelListener mChannelListener; - private DnsSdDiscoveryListener mDnsSdDiscoveryListener; - private ActionListener mDnsSdStopDiscoveryListener; - private DnsSdRegisterListener mDnsSdRegisterListener; - private ActionListener mDnsSdUnregisterListener; - private DnsSdUpdateRegistrationListener mDnsSdUpdateListener; - private DnsSdResolveListener mDnsSdResolveListener; - private ActionListener mDnsSdStopResolveListener; - - private AsyncChannel mAsyncChannel; - private ServiceHandler mHandler; - class ServiceHandler extends Handler { - ServiceHandler(Looper looper) { - super(looper); - } - @Override - public void handleMessage(Message message) { - switch (message.what) { - case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: - mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION); - break; - case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED: - if (mChannelListener != null) { - mChannelListener.onChannelConnected(Channel.this); - } - break; - case AsyncChannel.CMD_CHANNEL_DISCONNECTED: - if (mChannelListener != null) { - mChannelListener.onChannelDisconnected(); - mChannelListener = null; - } - break; - case DISCOVER_SERVICES_STARTED: - if (mDnsSdDiscoveryListener != null) { - mDnsSdDiscoveryListener.onStarted((String) message.obj); - } - break; - case DISCOVER_SERVICES_FAILED: - if (mDnsSdDiscoveryListener != null) { - mDnsSdDiscoveryListener.onFailure(message.arg1); - } - break; - case SERVICE_FOUND: - if (mDnsSdDiscoveryListener != null) { - mDnsSdDiscoveryListener.onServiceFound( - (DnsSdServiceInfo) message.obj); - } - break; - case SERVICE_LOST: - if (mDnsSdDiscoveryListener != null) { - mDnsSdDiscoveryListener.onServiceLost( - (DnsSdServiceInfo) message.obj); - } - break; - case STOP_DISCOVERY_FAILED: - if (mDnsSdStopDiscoveryListener != null) { - mDnsSdStopDiscoveryListener.onFailure(message.arg1); - } - break; - case STOP_DISCOVERY_SUCCEEDED: - if (mDnsSdStopDiscoveryListener != null) { - mDnsSdStopDiscoveryListener.onSuccess(); - } - break; - case REGISTER_SERVICE_FAILED: - if (mDnsSdRegisterListener != null) { - mDnsSdRegisterListener.onFailure(message.arg1); - } - break; - case REGISTER_SERVICE_SUCCEEDED: - if (mDnsSdRegisterListener != null) { - mDnsSdRegisterListener.onServiceRegistered(message.arg1, - (DnsSdServiceInfo) message.obj); - } - break; - case UNREGISTER_SERVICE_FAILED: - if (mDnsSdUnregisterListener != null) { - mDnsSdUnregisterListener.onFailure(message.arg1); - } - break; - case UNREGISTER_SERVICE_SUCCEEDED: - if (mDnsSdUnregisterListener != null) { - mDnsSdUnregisterListener.onSuccess(); - } - break; - case UPDATE_SERVICE_FAILED: - if (mDnsSdUpdateListener != null) { - mDnsSdUpdateListener.onFailure(message.arg1); - } - break; - case UPDATE_SERVICE_SUCCEEDED: - if (mDnsSdUpdateListener != null) { - mDnsSdUpdateListener.onServiceUpdated(message.arg1, - (DnsSdTxtRecord) message.obj); - } - break; - case RESOLVE_SERVICE_FAILED: - if (mDnsSdResolveListener != null) { - mDnsSdResolveListener.onFailure(message.arg1); - } - break; - case RESOLVE_SERVICE_SUCCEEDED: - if (mDnsSdResolveListener != null) { - mDnsSdResolveListener.onServiceResolved( - (DnsSdServiceInfo) message.obj); - } - break; - case STOP_RESOLVE_FAILED: - if (mDnsSdStopResolveListener!= null) { - mDnsSdStopResolveListener.onFailure(message.arg1); - } - break; - case STOP_RESOLVE_SUCCEEDED: - if (mDnsSdStopResolveListener != null) { - mDnsSdStopResolveListener.onSuccess(); - } - break; - default: - Log.d(TAG, "Ignored " + message); - break; - } + @Override + public void handleMessage(Message message) { + Object listener = getListener(message.arg2); + boolean listenerRemove = true; + switch (message.what) { + case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: + mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION); + mConnected.countDown(); + break; + case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED: + // Ignore + break; + case AsyncChannel.CMD_CHANNEL_DISCONNECTED: + Log.e(TAG, "Channel lost"); + break; + case DISCOVER_SERVICES_STARTED: + String s = ((NsdServiceInfo) message.obj).getServiceType(); + ((DiscoveryListener) listener).onDiscoveryStarted(s); + // Keep listener until stop discovery + listenerRemove = false; + break; + case DISCOVER_SERVICES_FAILED: + ((DiscoveryListener) listener).onStartDiscoveryFailed( + getNsdService(message.arg2).getServiceType(), message.arg1); + break; + case SERVICE_FOUND: + ((DiscoveryListener) listener).onServiceFound((NsdServiceInfo) message.obj); + // Keep listener until stop discovery + listenerRemove = false; + break; + case SERVICE_LOST: + ((DiscoveryListener) listener).onServiceLost((NsdServiceInfo) message.obj); + // Keep listener until stop discovery + listenerRemove = false; + break; + case STOP_DISCOVERY_FAILED: + ((DiscoveryListener) listener).onStopDiscoveryFailed( + getNsdService(message.arg2).getServiceType(), message.arg1); + break; + case STOP_DISCOVERY_SUCCEEDED: + ((DiscoveryListener) listener).onDiscoveryStopped( + getNsdService(message.arg2).getServiceType()); + break; + case REGISTER_SERVICE_FAILED: + ((RegistrationListener) listener).onRegistrationFailed( + getNsdService(message.arg2), message.arg1); + break; + case REGISTER_SERVICE_SUCCEEDED: + ((RegistrationListener) listener).onServiceRegistered( + (NsdServiceInfo) message.obj); + // Keep listener until unregister + listenerRemove = false; + break; + case UNREGISTER_SERVICE_FAILED: + ((RegistrationListener) listener).onUnregistrationFailed( + getNsdService(message.arg2), message.arg1); + break; + case UNREGISTER_SERVICE_SUCCEEDED: + ((RegistrationListener) listener).onServiceUnregistered( + getNsdService(message.arg2)); + break; + case RESOLVE_SERVICE_FAILED: + ((ResolveListener) listener).onResolveFailed( + getNsdService(message.arg2), message.arg1); + break; + case RESOLVE_SERVICE_SUCCEEDED: + ((ResolveListener) listener).onServiceResolved((NsdServiceInfo) message.obj); + break; + default: + Log.d(TAG, "Ignored " + message); + break; + } + if (listenerRemove) { + removeListener(message.arg2); } } - } + } - private static void checkChannel(Channel c) { - if (c == null) throw new IllegalArgumentException("Channel needs to be initialized"); + private int putListener(Object listener, NsdServiceInfo s) { + if (listener == null) return INVALID_LISTENER_KEY; + int key; + synchronized (mMapLock) { + do { + key = mListenerKey++; + } while (key == INVALID_LISTENER_KEY); + mListenerMap.put(key, listener); + mServiceMap.put(key, s); + } + return key; } - /** - * Registers the application with the service discovery framework. This function - * must be the first to be called before any other operations are performed. No service - * discovery operations must be performed until the ChannelListener callback notifies - * that the channel is connected - * - * @param srcContext is the context of the source - * @param srcLooper is the Looper on which the callbacks are receivied - * @param listener for callback at loss of framework communication. Cannot be null. - */ - public void initialize(Context srcContext, Looper srcLooper, ChannelListener listener) { - Messenger messenger = getMessenger(); - if (messenger == null) throw new RuntimeException("Failed to initialize"); - if (listener == null) throw new IllegalArgumentException("ChannelListener cannot be null"); + private Object getListener(int key) { + if (key == INVALID_LISTENER_KEY) return null; + synchronized (mMapLock) { + return mListenerMap.get(key); + } + } + + private NsdServiceInfo getNsdService(int key) { + synchronized (mMapLock) { + return mServiceMap.get(key); + } + } + + private void removeListener(int key) { + if (key == INVALID_LISTENER_KEY) return; + synchronized (mMapLock) { + mListenerMap.remove(key); + mServiceMap.remove(key); + } + } - Channel c = new Channel(srcLooper, listener); - c.mAsyncChannel.connect(srcContext, c.mHandler, messenger); + private int getListenerKey(Object listener) { + synchronized (mMapLock) { + int valueIndex = mListenerMap.indexOfValue(listener); + if (valueIndex != -1) { + return mListenerMap.keyAt(valueIndex); + } + } + return INVALID_LISTENER_KEY; } + /** - * Disconnects application from service discovery framework. No further operations - * will succeed until a {@link #initialize} is called again. - * - * @param c channel initialized with {@link #initialize} + * Initialize AsyncChannel */ - public void deinitialize(Channel c) { - checkChannel(c); - c.mAsyncChannel.disconnect(); + private void init() { + final Messenger messenger = getMessenger(); + if (messenger == null) throw new RuntimeException("Failed to initialize"); + HandlerThread t = new HandlerThread("NsdManager"); + t.start(); + mHandler = new ServiceHandler(t.getLooper()); + mAsyncChannel.connect(mContext, mHandler, messenger); + try { + mConnected.await(); + } catch (InterruptedException e) { + Log.e(TAG, "interrupted wait at init"); + } } /** @@ -510,45 +444,51 @@ public class NsdManager { * * <p> The function call immediately returns after sending a request to register service * to the framework. The application is notified of a success to initiate - * discovery through the callback {@link DnsSdRegisterListener#onServiceRegistered} or a failure - * through {@link DnsSdRegisterListener#onFailure}. + * discovery through the callback {@link RegistrationListener#onServiceRegistered} or a failure + * through {@link RegistrationListener#onRegistrationFailed}. * - * @param c is the channel created at {@link #initialize} - * @param serviceType The service type being advertised. - * @param port on which the service is listenering for incoming connections - * @param listener for success or failure callback. Can be null. + * @param serviceInfo The service being registered + * @param protocolType The service discovery protocol + * @param listener The listener notifies of a successful registration and is used to + * unregister this service through a call on {@link #unregisterService}. Cannot be null. */ - public void registerService(Channel c, String serviceName, String serviceType, int port, - DnsSdRegisterListener listener) { - checkChannel(c); - if (TextUtils.isEmpty(serviceName) || TextUtils.isEmpty(serviceType)) { + public void registerService(NsdServiceInfo serviceInfo, int protocolType, + RegistrationListener listener) { + if (TextUtils.isEmpty(serviceInfo.getServiceName()) || + TextUtils.isEmpty(serviceInfo.getServiceType())) { throw new IllegalArgumentException("Service name or type cannot be empty"); } - if (port <= 0) { + if (serviceInfo.getPort() <= 0) { throw new IllegalArgumentException("Invalid port number"); } - DnsSdServiceInfo serviceInfo = new DnsSdServiceInfo(serviceName, serviceType, null); - serviceInfo.setPort(port); - c.mDnsSdRegisterListener = listener; - c.mAsyncChannel.sendMessage(REGISTER_SERVICE, serviceInfo); + if (listener == null) { + throw new IllegalArgumentException("listener cannot be null"); + } + if (protocolType != PROTOCOL_DNS_SD) { + throw new IllegalArgumentException("Unsupported protocol"); + } + mAsyncChannel.sendMessage(REGISTER_SERVICE, 0, putListener(listener, serviceInfo), + serviceInfo); } /** - * Unregister a service registered through {@link #registerService} - * @param c is the channel created at {@link #initialize} - * @param registeredId is obtained at {@link DnsSdRegisterListener#onServiceRegistered} - * @param listener provides callbacks for success or failure. Can be null. + * Unregister a service registered through {@link #registerService}. A successful + * unregister is notified to the application with a call to + * {@link RegistrationListener#onServiceUnregistered}. + * + * @param listener This should be the listener object that was passed to + * {@link #registerService}. It identifies the service that should be unregistered + * and notifies of a successful unregistration. */ - public void unregisterService(Channel c, int registeredId, ActionListener listener) { - checkChannel(c); - c.mDnsSdUnregisterListener = listener; - c.mAsyncChannel.sendMessage(UNREGISTER_SERVICE, registeredId); - } - - /** @hide */ - public void updateService(Channel c, int registeredId, DnsSdTxtRecord txtRecord) { - checkChannel(c); - c.mAsyncChannel.sendMessage(UPDATE_SERVICE, registeredId, 0, txtRecord); + public void unregisterService(RegistrationListener listener) { + int id = getListenerKey(listener); + if (id == INVALID_LISTENER_KEY) { + throw new IllegalArgumentException("listener not registered"); + } + if (listener == null) { + throw new IllegalArgumentException("listener cannot be null"); + } + mAsyncChannel.sendMessage(UNREGISTER_SERVICE, 0, id); } /** @@ -558,51 +498,61 @@ public class NsdManager { * * <p> The function call immediately returns after sending a request to start service * discovery to the framework. The application is notified of a success to initiate - * discovery through the callback {@link DnsSdDiscoveryListener#onStarted} or a failure - * through {@link DnsSdDiscoveryListener#onFailure}. + * discovery through the callback {@link DiscoveryListener#onDiscoveryStarted} or a failure + * through {@link DiscoveryListener#onStartDiscoveryFailed}. * * <p> Upon successful start, application is notified when a service is found with - * {@link DnsSdDiscoveryListener#onServiceFound} or when a service is lost with - * {@link DnsSdDiscoveryListener#onServiceLost}. + * {@link DiscoveryListener#onServiceFound} or when a service is lost with + * {@link DiscoveryListener#onServiceLost}. * * <p> Upon failure to start, service discovery is not active and application does * not need to invoke {@link #stopServiceDiscovery} * - * @param c is the channel created at {@link #initialize} * @param serviceType The service type being discovered. Examples include "_http._tcp" for * http services or "_ipp._tcp" for printers - * @param listener provides callbacks when service is found or lost. Cannot be null. + * @param protocolType The service discovery protocol + * @param listener The listener notifies of a successful discovery and is used + * to stop discovery on this serviceType through a call on {@link #stopServiceDiscovery}. + * Cannot be null. */ - public void discoverServices(Channel c, String serviceType, DnsSdDiscoveryListener listener) { - checkChannel(c); + public void discoverServices(String serviceType, int protocolType, DiscoveryListener listener) { if (listener == null) { - throw new IllegalStateException("Discovery listener needs to be set first"); + throw new IllegalArgumentException("listener cannot be null"); } if (TextUtils.isEmpty(serviceType)) { - throw new IllegalStateException("Service type cannot be empty"); + throw new IllegalArgumentException("Service type cannot be empty"); } - DnsSdServiceInfo s = new DnsSdServiceInfo(); + + if (protocolType != PROTOCOL_DNS_SD) { + throw new IllegalArgumentException("Unsupported protocol"); + } + + NsdServiceInfo s = new NsdServiceInfo(); s.setServiceType(serviceType); - c.mDnsSdDiscoveryListener = listener; - c.mAsyncChannel.sendMessage(DISCOVER_SERVICES, s); + mAsyncChannel.sendMessage(DISCOVER_SERVICES, 0, putListener(listener, s), s); } /** * Stop service discovery initiated with {@link #discoverServices}. An active service - * discovery is notified to the application with {@link DnsSdDiscoveryListener#onStarted} - * and it stays active until the application invokes a stop service discovery. + * discovery is notified to the application with {@link DiscoveryListener#onDiscoveryStarted} + * and it stays active until the application invokes a stop service discovery. A successful + * stop is notified to with a call to {@link DiscoveryListener#onDiscoveryStopped}. * - * <p> Upon failure to start service discovery notified through - * {@link DnsSdDiscoveryListener#onFailure} service discovery is not active and - * application does not need to stop it. + * <p> Upon failure to stop service discovery, application is notified through + * {@link DiscoveryListener#onStopDiscoveryFailed}. * - * @param c is the channel created at {@link #initialize} - * @param listener notifies success or failure. Can be null. + * @param listener This should be the listener object that was passed to {@link #discoverServices}. + * It identifies the discovery that should be stopped and notifies of a successful stop. */ - public void stopServiceDiscovery(Channel c, ActionListener listener) { - checkChannel(c); - c.mDnsSdStopDiscoveryListener = listener; - c.mAsyncChannel.sendMessage(STOP_DISCOVERY); + public void stopServiceDiscovery(DiscoveryListener listener) { + int id = getListenerKey(listener); + if (id == INVALID_LISTENER_KEY) { + throw new IllegalArgumentException("service discovery not active on listener"); + } + if (listener == null) { + throw new IllegalArgumentException("listener cannot be null"); + } + mAsyncChannel.sendMessage(STOP_DISCOVERY, 0, id); } /** @@ -610,30 +560,19 @@ public class NsdManager { * establishing a connection to fetch the IP and port details on which to setup * the connection. * - * @param c is the channel created at {@link #initialize} - * @param serviceName of the the service - * @param serviceType of the service + * @param serviceInfo service to be resolved * @param listener to receive callback upon success or failure. Cannot be null. */ - public void resolveService(Channel c, String serviceName, String serviceType, - DnsSdResolveListener listener) { - checkChannel(c); - if (TextUtils.isEmpty(serviceName) || TextUtils.isEmpty(serviceType)) { + public void resolveService(NsdServiceInfo serviceInfo, ResolveListener listener) { + if (TextUtils.isEmpty(serviceInfo.getServiceName()) || + TextUtils.isEmpty(serviceInfo.getServiceType())) { throw new IllegalArgumentException("Service name or type cannot be empty"); } - if (listener == null) throw new - IllegalStateException("Resolve listener cannot be null"); - c.mDnsSdResolveListener = listener; - DnsSdServiceInfo serviceInfo = new DnsSdServiceInfo(serviceName, serviceType, null); - c.mAsyncChannel.sendMessage(RESOLVE_SERVICE, serviceInfo); - } - - /** @hide */ - public void stopServiceResolve(Channel c) { - checkChannel(c); - if (c.mDnsSdResolveListener == null) throw new - IllegalStateException("Resolve listener needs to be set first"); - c.mAsyncChannel.sendMessage(STOP_RESOLVE); + if (listener == null) { + throw new IllegalArgumentException("listener cannot be null"); + } + mAsyncChannel.sendMessage(RESOLVE_SERVICE, 0, putListener(listener, serviceInfo), + serviceInfo); } /** Internal use only @hide */ diff --git a/core/java/android/net/nsd/DnsSdServiceInfo.java b/core/java/android/net/nsd/NsdServiceInfo.java index 66abd3a..205a21d 100644 --- a/core/java/android/net/nsd/DnsSdServiceInfo.java +++ b/core/java/android/net/nsd/NsdServiceInfo.java @@ -25,7 +25,7 @@ import java.net.InetAddress; * A class representing service information for network service discovery * {@see NsdManager} */ -public class DnsSdServiceInfo implements NetworkServiceInfo, Parcelable { +public final class NsdServiceInfo implements Parcelable { private String mServiceName; @@ -37,36 +37,32 @@ public class DnsSdServiceInfo implements NetworkServiceInfo, Parcelable { private int mPort; - public DnsSdServiceInfo() { + public NsdServiceInfo() { } /** @hide */ - public DnsSdServiceInfo(String sn, String rt, DnsSdTxtRecord tr) { + public NsdServiceInfo(String sn, String rt, DnsSdTxtRecord tr) { mServiceName = sn; mServiceType = rt; mTxtRecord = tr; } /** Get the service name */ - @Override public String getServiceName() { return mServiceName; } /** Set the service name */ - @Override public void setServiceName(String s) { mServiceName = s; } /** Get the service type */ - @Override public String getServiceType() { return mServiceType; } /** Set the service type */ - @Override public void setServiceType(String s) { mServiceType = s; } @@ -132,10 +128,10 @@ public class DnsSdServiceInfo implements NetworkServiceInfo, Parcelable { } /** Implement the Parcelable interface */ - public static final Creator<DnsSdServiceInfo> CREATOR = - new Creator<DnsSdServiceInfo>() { - public DnsSdServiceInfo createFromParcel(Parcel in) { - DnsSdServiceInfo info = new DnsSdServiceInfo(); + public static final Creator<NsdServiceInfo> CREATOR = + new Creator<NsdServiceInfo>() { + public NsdServiceInfo createFromParcel(Parcel in) { + NsdServiceInfo info = new NsdServiceInfo(); info.mServiceName = in.readString(); info.mServiceType = in.readString(); info.mTxtRecord = in.readParcelable(null); @@ -150,8 +146,8 @@ public class DnsSdServiceInfo implements NetworkServiceInfo, Parcelable { return info; } - public DnsSdServiceInfo[] newArray(int size) { - return new DnsSdServiceInfo[size]; + public NsdServiceInfo[] newArray(int size) { + return new NsdServiceInfo[size]; } }; } diff --git a/core/java/android/os/IBinder.java b/core/java/android/os/IBinder.java index 0586d9e..b7bc45f 100644 --- a/core/java/android/os/IBinder.java +++ b/core/java/android/os/IBinder.java @@ -140,6 +140,9 @@ public interface IBinder { */ int LIKE_TRANSACTION = ('_'<<24)|('L'<<16)|('I'<<8)|'K'; + /** @hide */ + int SYSPROPS_TRANSACTION = ('_'<<24)|('S'<<16)|('P'<<8)|'R'; + /** * Flag to {@link #transact}: this is a one-way call, meaning that the * caller returns immediately, without waiting for a result from the diff --git a/core/java/android/os/ServiceManagerNative.java b/core/java/android/os/ServiceManagerNative.java index 43b5128..be24426 100644 --- a/core/java/android/os/ServiceManagerNative.java +++ b/core/java/android/os/ServiceManagerNative.java @@ -16,6 +16,8 @@ package android.os; +import java.util.ArrayList; + /** * Native implementation of the service manager. Most clients will only @@ -151,14 +153,32 @@ class ServiceManagerProxy implements IServiceManager { } public String[] listServices() throws RemoteException { - Parcel data = Parcel.obtain(); - Parcel reply = Parcel.obtain(); - data.writeInterfaceToken(IServiceManager.descriptor); - mRemote.transact(LIST_SERVICES_TRANSACTION, data, reply, 0); - String[] list = reply.readStringArray(); - reply.recycle(); - data.recycle(); - return list; + ArrayList<String> services = new ArrayList<String>(); + int n = 0; + while (true) { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IServiceManager.descriptor); + data.writeInt(n); + n++; + try { + boolean res = mRemote.transact(LIST_SERVICES_TRANSACTION, data, reply, 0); + if (!res) { + break; + } + } catch (RuntimeException e) { + // The result code that is returned by the C++ code can + // cause the call to throw an exception back instead of + // returning a nice result... so eat it here and go on. + break; + } + services.add(reply.readString()); + reply.recycle(); + data.recycle(); + } + String[] array = new String[services.size()]; + services.toArray(array); + return array; } public void setPermissionController(IPermissionController controller) diff --git a/core/java/android/os/SystemProperties.java b/core/java/android/os/SystemProperties.java index 619bf8d..156600e 100644 --- a/core/java/android/os/SystemProperties.java +++ b/core/java/android/os/SystemProperties.java @@ -16,6 +16,10 @@ package android.os; +import java.util.ArrayList; + +import android.util.Log; + /** * Gives access to the system properties store. The system properties @@ -28,12 +32,15 @@ public class SystemProperties public static final int PROP_NAME_MAX = 31; public static final int PROP_VALUE_MAX = 91; + private static final ArrayList<Runnable> sChangeCallbacks = new ArrayList<Runnable>(); + private static native String native_get(String key); private static native String native_get(String key, String def); private static native int native_get_int(String key, int def); private static native long native_get_long(String key, long def); private static native boolean native_get_boolean(String key, boolean def); private static native void native_set(String key, String def); + private static native void native_add_change_callback(); /** * Get the value for the given key. @@ -124,4 +131,26 @@ public class SystemProperties } native_set(key, val); } + + public static void addChangeCallback(Runnable callback) { + synchronized (sChangeCallbacks) { + if (sChangeCallbacks.size() == 0) { + native_add_change_callback(); + } + sChangeCallbacks.add(callback); + } + } + + static void callChangeCallbacks() { + synchronized (sChangeCallbacks) { + //Log.i("foo", "Calling " + sChangeCallbacks.size() + " change callbacks!"); + if (sChangeCallbacks.size() == 0) { + return; + } + ArrayList<Runnable> callbacks = new ArrayList<Runnable>(sChangeCallbacks); + for (int i=0; i<callbacks.size(); i++) { + callbacks.get(i).run(); + } + } + } } diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java index ac9ee26..911183d 100644 --- a/core/java/android/os/Trace.java +++ b/core/java/android/os/Trace.java @@ -39,13 +39,29 @@ public final class Trace { public static final long TRACE_TAG_SYNC_MANAGER = 1L << 7; public static final long TRACE_TAG_AUDIO = 1L << 8; - private static final long sEnabledTags = nativeGetEnabledTags(); + public static final int TRACE_FLAGS_START_BIT = 1; + public static final String[] TRACE_TAGS = { + "Graphics", "Input", "View", "WebView", "Window Manager", + "Activity Manager", "Sync Manager", "Audio" + }; + + public static final String PROPERTY_TRACE_TAG_ENABLEFLAGS = "debug.atrace.tags.enableflags"; + + private static long sEnabledTags = nativeGetEnabledTags(); private static native long nativeGetEnabledTags(); private static native void nativeTraceCounter(long tag, String name, int value); private static native void nativeTraceBegin(long tag, String name); private static native void nativeTraceEnd(long tag); + static { + SystemProperties.addChangeCallback(new Runnable() { + @Override public void run() { + sEnabledTags = nativeGetEnabledTags(); + } + }); + } + private Trace() { } diff --git a/core/java/android/preference/MultiCheckPreference.java b/core/java/android/preference/MultiCheckPreference.java index 735f66a..6953075 100644 --- a/core/java/android/preference/MultiCheckPreference.java +++ b/core/java/android/preference/MultiCheckPreference.java @@ -136,11 +136,25 @@ public class MultiCheckPreference extends DialogPreference { * * @return The array of values. */ - public CharSequence[] getEntryValues() { + public String[] getEntryValues() { return mEntryValues; } /** + * Get the boolean state of a given value. + */ + public boolean getValue(int index) { + return mSetValues[index]; + } + + /** + * Set the boolean state of a given value. + */ + public void setValue(int index, boolean state) { + mSetValues[index] = state; + } + + /** * Sets the current values. */ public void setValues(boolean[] values) { diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 2c49bd2..ba8c4c9 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -1557,6 +1557,9 @@ public final class Settings { * will likely be removed in a future release with support for * audio/vibe feedback profiles. * + * Not used anymore. On devices with vibrator, the user explicitly selects + * silent or vibrate mode. + * Kept for use by legacy database upgrade code in DatabaseHelper. * @hide */ public static final String VIBRATE_IN_SILENT = "vibrate_in_silent"; @@ -1746,6 +1749,20 @@ public final class Settings { public static final String USER_ROTATION = "user_rotation"; /** + * Whether the phone vibrates when it is ringing due to an incoming call. This will + * be used by Phone and Setting apps; it shouldn't affect other apps. + * The value is boolean (1 or 0). + * + * Note: this is not same as "vibrate on ring", which had been available until ICS. + * It was about AudioManager's setting and thus affected all the applications which + * relied on the setting, while this is purely about the vibration setting for incoming + * calls. + * + * @hide + */ + public static final String VIBRATE_WHEN_RINGING = "vibrate_when_ringing"; + + /** * Whether the audible DTMF tones are played by the dialer when dialing. The value is * boolean (1 or 0). */ @@ -1983,7 +2000,6 @@ public final class Settings { SCREEN_BRIGHTNESS, SCREEN_BRIGHTNESS_MODE, SCREEN_AUTO_BRIGHTNESS_ADJ, - VIBRATE_ON, VIBRATE_INPUT_DEVICES, MODE_RINGER, MODE_RINGER_STREAMS_AFFECTED, @@ -2002,7 +2018,6 @@ public final class Settings { VOLUME_ALARM + APPEND_FOR_LAST_AUDIBLE, VOLUME_NOTIFICATION + APPEND_FOR_LAST_AUDIBLE, VOLUME_BLUETOOTH_SCO + APPEND_FOR_LAST_AUDIBLE, - VIBRATE_IN_SILENT, TEXT_AUTO_REPLACE, TEXT_AUTO_CAPS, TEXT_AUTO_PUNCTUATE, @@ -2029,6 +2044,7 @@ public final class Settings { SIP_CALL_OPTIONS, SIP_RECEIVE_CALLS, POINTER_SPEED, + VIBRATE_WHEN_RINGING }; // Settings moved to Settings.Secure diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 2befcce..aad6756 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -4676,13 +4676,13 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal } RectF position = mAttachInfo.mTmpTransformRect; - position.setEmpty(); + position.set(0, 0, mRight - mLeft, mBottom - mTop); if (!hasIdentityMatrix()) { getMatrix().mapRect(position); } - position.offset(mLeft, mRight); + position.offset(mLeft, mTop); ViewParent parent = mParent; while (parent instanceof View) { @@ -17250,7 +17250,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal /** * Show where the margins, bounds and layout bounds are for each view. */ - final boolean mDebugLayout = SystemProperties.getBoolean(DEBUG_LAYOUT_PROPERTY, false); + boolean mDebugLayout = SystemProperties.getBoolean(DEBUG_LAYOUT_PROPERTY, false); /** * Point used to compute visible regions. diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 5f295cb..6e6fab2 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -408,6 +408,7 @@ public final class ViewRootImpl implements ViewParent, PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); mAttachInfo.mScreenOn = powerManager.isScreenOn(); + loadSystemProperties(); } /** @@ -846,6 +847,16 @@ public final class ViewRootImpl implements ViewParent, scheduleTraversals(); } + void invalidateWorld(View view) { + view.invalidate(); + if (view instanceof ViewGroup) { + ViewGroup parent = (ViewGroup)view; + for (int i=0; i<parent.getChildCount(); i++) { + invalidateWorld(parent.getChildAt(i)); + } + } + } + public void invalidateChild(View child, Rect dirty) { invalidateChildInParent(null, dirty); } @@ -2730,6 +2741,7 @@ public final class ViewRootImpl implements ViewParent, private final static int MSG_INVALIDATE_DISPLAY_LIST = 21; private final static int MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST = 22; private final static int MSG_DISPATCH_DONE_ANIMATING = 23; + private final static int MSG_INVALIDATE_WORLD = 24; final class ViewRootHandler extends Handler { @Override @@ -2997,6 +3009,9 @@ public final class ViewRootImpl implements ViewParent, case MSG_DISPATCH_DONE_ANIMATING: { handleDispatchDoneAnimating(); } break; + case MSG_INVALIDATE_WORLD: { + invalidateWorld(mView); + } break; } } } @@ -4016,6 +4031,17 @@ public final class ViewRootImpl implements ViewParent, mHandler.sendMessage(msg); } + public void loadSystemProperties() { + boolean layout = SystemProperties.getBoolean( + View.DEBUG_LAYOUT_PROPERTY, false); + if (layout != mAttachInfo.mDebugLayout) { + mAttachInfo.mDebugLayout = layout; + if (!mHandler.hasMessages(MSG_INVALIDATE_WORLD)) { + mHandler.sendEmptyMessageDelayed(MSG_INVALIDATE_WORLD, 200); + } + } + } + private void destroyHardwareRenderer() { AttachInfo attachInfo = mAttachInfo; HardwareRenderer hardwareRenderer = attachInfo.mHardwareRenderer; diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java index b5690e9..5d33cec 100644 --- a/core/java/android/view/WindowManagerImpl.java +++ b/core/java/android/view/WindowManagerImpl.java @@ -23,6 +23,7 @@ import android.content.res.Configuration; import android.graphics.PixelFormat; import android.opengl.ManagedEGLContext; import android.os.IBinder; +import android.os.SystemProperties; import android.util.AndroidRuntimeException; import android.util.Log; import android.view.inputmethod.InputMethodManager; @@ -112,6 +113,8 @@ public class WindowManagerImpl implements WindowManager { private WindowManager.LayoutParams[] mParams; private boolean mNeedsEglTerminate; + private Runnable mSystemPropertyUpdater = null; + private final static Object sLock = new Object(); private final static WindowManagerImpl sWindowManager = new WindowManagerImpl(); private final static HashMap<CompatibilityInfo, WindowManager> sCompatWindowManagers @@ -237,6 +240,22 @@ public class WindowManagerImpl implements WindowManager { View panelParentView = null; synchronized (this) { + // Start watching for system property changes. + if (mSystemPropertyUpdater == null) { + mSystemPropertyUpdater = new Runnable() { + @Override public void run() { + synchronized (this) { + synchronized (this) { + for (ViewRootImpl root : mRoots) { + root.loadSystemProperties(); + } + } + } + } + }; + SystemProperties.addChangeCallback(mSystemPropertyUpdater); + } + // Here's an odd/questionable case: if someone tries to add a // view multiple times, then we simply bump up a nesting count // and they need to remove the view the corresponding number of diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java index e725e75..388cfb3 100644 --- a/core/java/android/view/WindowManagerPolicy.java +++ b/core/java/android/view/WindowManagerPolicy.java @@ -1017,7 +1017,7 @@ public interface WindowManagerPolicy { /** * Called when we have finished booting and can now display the home - * screen to the user. This wilWl happen after systemReady(), and at + * screen to the user. This will happen after systemReady(), and at * this point the display is active. */ public void enableScreenAfterBoot(); diff --git a/core/java/android/webkit/WebViewClassic.java b/core/java/android/webkit/WebViewClassic.java index 17e4efb..057c3d1 100644 --- a/core/java/android/webkit/WebViewClassic.java +++ b/core/java/android/webkit/WebViewClassic.java @@ -609,6 +609,10 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc // The presumed scroll rate for the first scroll of edit text static private final long TEXT_SCROLL_FIRST_SCROLL_MS = 16; + // Buffer pixels of the caret rectangle when moving edit text into view + // after resize. + static private final int EDIT_RECT_BUFFER = 10; + // true means redraw the screen all-the-time. Only with AUTO_REDRAW_HACK private boolean mAutoRedraw; @@ -3294,7 +3298,8 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc } public int getPageBackgroundColor() { - return nativeGetBackgroundColor(); + if (mNativeClass == 0) return Color.WHITE; + return nativeGetBackgroundColor(mNativeClass); } /** @@ -4451,7 +4456,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc if (mNativeClass == 0) { return 0; } - return nativeGetBaseLayer(); + return nativeGetBaseLayer(mNativeClass); } private void onZoomAnimationStart() { @@ -5574,9 +5579,76 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc // However, do not update the base layer as that hasn't changed setNewPicture(mLoadedPicture, false); } + if (mIsEditingText) { + scrollEditIntoView(); + } relocateAutoCompletePopup(); } + /** + * Scrolls the edit field into view using the minimum scrolling necessary. + * If the edit field is too large to fit in the visible window, the caret + * dimensions are used so that at least the caret is visible. + * A buffer of EDIT_RECT_BUFFER in view pixels is used to offset the + * edit rectangle to ensure a margin with the edge of the screen. + */ + private void scrollEditIntoView() { + Rect visibleRect = new Rect(viewToContentX(getScrollX()), + viewToContentY(getScrollY()), + viewToContentX(getScrollX() + getWidth()), + viewToContentY(getScrollY() + getViewHeightWithTitle())); + if (visibleRect.contains(mEditTextContentBounds)) { + return; // no need to scroll + } + syncSelectionCursors(); + final int buffer = Math.max(1, viewToContentDimension(EDIT_RECT_BUFFER)); + Rect showRect = new Rect( + Math.max(0, mEditTextContentBounds.left - buffer), + Math.max(0, mEditTextContentBounds.top - buffer), + mEditTextContentBounds.right + buffer, + mEditTextContentBounds.bottom + buffer); + Point caretTop = calculateCaretTop(); + if (visibleRect.width() < mEditTextContentBounds.width()) { + // The whole edit won't fit in the width, so use the caret rect + if (mSelectCursorBase.x < caretTop.x) { + showRect.left = Math.max(0, mSelectCursorBase.x - buffer); + showRect.right = caretTop.x + buffer; + } else { + showRect.left = Math.max(0, caretTop.x - buffer); + showRect.right = mSelectCursorBase.x + buffer; + } + } + if (visibleRect.height() < mEditTextContentBounds.height()) { + // The whole edit won't fit in the height, so use the caret rect + if (mSelectCursorBase.y > caretTop.y) { + showRect.top = Math.max(0, caretTop.y - buffer); + showRect.bottom = mSelectCursorBase.y + buffer; + } else { + showRect.top = Math.max(0, mSelectCursorBase.y - buffer); + showRect.bottom = caretTop.y + buffer; + } + } + + if (visibleRect.contains(showRect)) { + return; // no need to scroll + } + + int scrollX = visibleRect.left; + if (visibleRect.left > showRect.left) { + scrollX = showRect.left; + } else if (visibleRect.right < showRect.right) { + scrollX = Math.max(0, showRect.right - visibleRect.width()); + } + int scrollY = visibleRect.top; + if (visibleRect.top > showRect.top) { + scrollY = showRect.top; + } else if (visibleRect.bottom < showRect.bottom) { + scrollY = Math.max(0, showRect.bottom - visibleRect.height()); + } + + contentScrollTo(scrollX, scrollY, false); + } + @Override public void onScrollChanged(int l, int t, int oldl, int oldt) { if (!mInOverScrollMode) { @@ -8586,7 +8658,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc private native void nativeSetHeightCanMeasure(boolean measure); private native boolean nativeSetBaseLayer(int nativeInstance, int layer, boolean showVisualIndicator, boolean isPictureAfterFirstLayout); - private native int nativeGetBaseLayer(); + private native int nativeGetBaseLayer(int nativeInstance); private native void nativeCopyBaseContentToPicture(Picture pict); private native boolean nativeHasContent(); private native void nativeStopGL(); @@ -8614,7 +8686,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc */ private native boolean nativeScrollLayer(int nativeInstance, int layer, int newX, int newY); private native void nativeSetIsScrolling(boolean isScrolling); - private native int nativeGetBackgroundColor(); + private native int nativeGetBackgroundColor(int nativeInstance); native boolean nativeSetProperty(String key, String value); native String nativeGetProperty(String key); /** diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index a3b613b..3aafba5 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -25,6 +25,7 @@ import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.graphics.drawable.TransitionDrawable; +import android.os.Bundle; import android.os.Debug; import android.os.Handler; import android.os.Parcel; @@ -57,6 +58,7 @@ import android.view.ViewGroup; import android.view.ViewParent; import android.view.ViewTreeObserver; import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeInfo; import android.view.animation.Interpolator; import android.view.animation.LinearInterpolator; @@ -648,6 +650,11 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te private int mGlowPaddingLeft; private int mGlowPaddingRight; + /** + * Used for interacting with list items from an accessibility service. + */ + private ListItemAccessibilityDelegate mAccessibilityDelegate; + private int mLastAccessibilityScrollEventFromIndex; private int mLastAccessibilityScrollEventToIndex; @@ -2121,9 +2128,77 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te child.setLayoutParams(lp); } + if (AccessibilityManager.getInstance(mContext).isEnabled()) { + if (mAccessibilityDelegate == null) { + mAccessibilityDelegate = new ListItemAccessibilityDelegate(); + } + child.setAccessibilityDelegate(mAccessibilityDelegate); + } + return child; } + class ListItemAccessibilityDelegate extends AccessibilityDelegate { + @Override + public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) { + super.onInitializeAccessibilityNodeInfo(host, info); + + final int position = getPositionForView(host); + + if (position == INVALID_POSITION) { + return; + } + + if (isClickable()) { + info.addAction(AccessibilityNodeInfo.ACTION_CLICK); + info.setClickable(true); + } + + if (isLongClickable()) { + info.addAction(AccessibilityNodeInfo.ACTION_LONG_CLICK); + info.setLongClickable(true); + } + + info.addAction(AccessibilityNodeInfo.ACTION_SELECT); + + if (position == getSelectedItemPosition()) { + info.setSelected(true); + } + } + + @Override + public boolean performAccessibilityAction(View host, int action, Bundle arguments) { + final int position = getPositionForView(host); + + if (position == INVALID_POSITION) { + return false; + } + + final long id = getItemIdAtPosition(position); + + switch (action) { + case AccessibilityNodeInfo.ACTION_SELECT: + setSelection(position); + return true; + case AccessibilityNodeInfo.ACTION_CLICK: + if (!super.performAccessibilityAction(host, action, arguments)) { + return performItemClick(host, position, id); + } + return true; + case AccessibilityNodeInfo.ACTION_LONG_CLICK: + if (!super.performAccessibilityAction(host, action, arguments)) { + return performLongPress(host, position, id); + } + return true; + case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS: + smoothScrollToPosition(position); + break; + } + + return super.performAccessibilityAction(host, action, arguments); + } + } + void positionSelector(int position, View sel) { if (position != INVALID_POSITION) { mSelectorPosition = position; @@ -4009,17 +4084,19 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te void start(final int position) { stop(); + if (mDataChanged) { + // Wait until we're back in a stable state to try this. + post(new Runnable() { + @Override public void run() { + start(position); + } + }); + return; + } + final int childCount = getChildCount(); if (childCount == 0) { // Can't scroll without children. - if (mDataChanged) { - // But we might have something in a minute. - post(new Runnable() { - @Override public void run() { - start(position); - } - }); - } return; } @@ -4058,17 +4135,19 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te return; } + if (mDataChanged) { + // Wait until we're back in a stable state to try this. + post(new Runnable() { + @Override public void run() { + start(position, boundPosition); + } + }); + return; + } + final int childCount = getChildCount(); if (childCount == 0) { // Can't scroll without children. - if (mDataChanged) { - // But we might have something in a minute. - post(new Runnable() { - @Override public void run() { - start(position, boundPosition); - } - }); - } return; } @@ -4129,9 +4208,26 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te startWithOffset(position, offset, SCROLL_DURATION); } - void startWithOffset(int position, int offset, int duration) { + void startWithOffset(final int position, int offset, final int duration) { stop(); + if (mDataChanged) { + // Wait until we're back in a stable state to try this. + final int postOffset = offset; + post(new Runnable() { + @Override public void run() { + startWithOffset(position, postOffset, duration); + } + }); + return; + } + + final int childCount = getChildCount(); + if (childCount == 0) { + // Can't scroll without children. + return; + } + offset += getPaddingTop(); mTargetPos = position; @@ -4141,7 +4237,6 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te mMode = MOVE_OFFSET; final int firstPos = mFirstPosition; - final int childCount = getChildCount(); final int lastPos = firstPos + childCount - 1; int viewTravelCount; @@ -4514,7 +4609,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te if (distance == 0 || mItemCount == 0 || childCount == 0 || (firstPos == 0 && getChildAt(0).getTop() == topLimit && distance < 0) || - (lastPos == mItemCount - 1 && + (lastPos == mItemCount && getChildAt(childCount - 1).getBottom() == bottomLimit && distance > 0)) { mFlingRunnable.endFling(); if (mPositionScroller != null) { @@ -5651,6 +5746,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te // Don't reclaim header or footer views, or views that should be ignored if (lp != null && mRecycler.shouldRecycleViewType(lp.viewType)) { views.add(child); + child.setAccessibilityDelegate(null); if (listener != null) { // Pretend they went through the scrap heap listener.onMovedToScrapHeap(child); @@ -6206,6 +6302,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te mScrapViews[viewType].add(scrap); } + scrap.setAccessibilityDelegate(null); if (mRecyclerListener != null) { mRecyclerListener.onMovedToScrapHeap(scrap); } @@ -6267,6 +6364,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te lp.scrappedFromPosition = mFirstActivePosition + i; scrapViews.add(victim); + victim.setAccessibilityDelegate(null); if (hasListener) { mRecyclerListener.onMovedToScrapHeap(victim); } diff --git a/core/java/android/widget/SpellChecker.java b/core/java/android/widget/SpellChecker.java index e1103dd..ebf8a4a 100644 --- a/core/java/android/widget/SpellChecker.java +++ b/core/java/android/widget/SpellChecker.java @@ -427,12 +427,6 @@ public class SpellChecker implements SpellCheckerSessionListener { if (spellCheckSpanStart < 0 || spellCheckSpanEnd <= spellCheckSpanStart) return; // span was removed in the meantime - final int suggestionsCount = suggestionsInfo.getSuggestionsCount(); - if (suggestionsCount <= 0) { - // A negative suggestion count is possible - return; - } - final int start; final int end; if (offset != USE_SPAN_RANGE && length != USE_SPAN_RANGE) { @@ -443,9 +437,15 @@ public class SpellChecker implements SpellCheckerSessionListener { end = spellCheckSpanEnd; } - String[] suggestions = new String[suggestionsCount]; - for (int i = 0; i < suggestionsCount; i++) { - suggestions[i] = suggestionsInfo.getSuggestionAt(i); + final int suggestionsCount = suggestionsInfo.getSuggestionsCount(); + String[] suggestions; + if (suggestionsCount > 0) { + suggestions = new String[suggestionsCount]; + for (int i = 0; i < suggestionsCount; i++) { + suggestions[i] = suggestionsInfo.getSuggestionAt(i); + } + } else { + suggestions = ArrayUtils.emptyArray(String.class); } SuggestionSpan suggestionSpan = new SuggestionSpan(mTextView.getContext(), suggestions, @@ -453,7 +453,7 @@ public class SpellChecker implements SpellCheckerSessionListener { // TODO: Remove mIsSentenceSpellCheckSupported by extracting an interface // to share the logic of word level spell checker and sentence level spell checker if (mIsSentenceSpellCheckSupported) { - final long key = TextUtils.packRangeInLong(start, end); + final Long key = Long.valueOf(TextUtils.packRangeInLong(start, end)); final SuggestionSpan tempSuggestionSpan = mSuggestionSpanCache.get(key); if (tempSuggestionSpan != null) { if (DBG) { @@ -611,6 +611,9 @@ public class SpellChecker implements SpellCheckerSessionListener { if (spellCheckEnd < start) { break; } + if (spellCheckEnd <= spellCheckStart) { + break; + } if (createSpellCheckSpan) { addSpellCheckSpan(editable, spellCheckStart, spellCheckEnd); } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index f2334ae..555c974 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -397,7 +397,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener /** * EditText specific data, created on demand when one of the Editor fields is used. - * See {@link #createEditorIfNeeded(String)}. + * See {@link #createEditorIfNeeded()}. */ private Editor mEditor; @@ -798,20 +798,20 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener break; case com.android.internal.R.styleable.TextView_imeOptions: - createEditorIfNeeded("IME options specified in constructor"); + createEditorIfNeeded(); mEditor.createInputContentTypeIfNeeded(); mEditor.mInputContentType.imeOptions = a.getInt(attr, mEditor.mInputContentType.imeOptions); break; case com.android.internal.R.styleable.TextView_imeActionLabel: - createEditorIfNeeded("IME action label specified in constructor"); + createEditorIfNeeded(); mEditor.createInputContentTypeIfNeeded(); mEditor.mInputContentType.imeActionLabel = a.getText(attr); break; case com.android.internal.R.styleable.TextView_imeActionId: - createEditorIfNeeded("IME action id specified in constructor"); + createEditorIfNeeded(); mEditor.createInputContentTypeIfNeeded(); mEditor.mInputContentType.imeActionId = a.getInt(attr, mEditor.mInputContentType.imeActionId); @@ -883,7 +883,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } try { - createEditorIfNeeded("inputMethod in ctor"); + createEditorIfNeeded(); mEditor.mKeyListener = (KeyListener) c.newInstance(); } catch (InstantiationException ex) { throw new RuntimeException(ex); @@ -898,7 +898,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mEditor.mInputType = EditorInfo.TYPE_CLASS_TEXT; } } else if (digits != null) { - createEditorIfNeeded("digits in ctor"); + createEditorIfNeeded(); mEditor.mKeyListener = DigitsKeyListener.getInstance(digits.toString()); // If no input type was specified, we will default to generic // text, since we can't tell the IME about the set of digits @@ -910,11 +910,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // If set, the input type overrides what was set using the deprecated singleLine flag. singleLine = !isMultilineInputType(inputType); } else if (phone) { - createEditorIfNeeded("dialer in ctor"); + createEditorIfNeeded(); mEditor.mKeyListener = DialerKeyListener.getInstance(); mEditor.mInputType = inputType = EditorInfo.TYPE_CLASS_PHONE; } else if (numeric != 0) { - createEditorIfNeeded("numeric in ctor"); + createEditorIfNeeded(); mEditor.mKeyListener = DigitsKeyListener.getInstance((numeric & SIGNED) != 0, (numeric & DECIMAL) != 0); inputType = EditorInfo.TYPE_CLASS_NUMBER; @@ -951,7 +951,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener break; } - createEditorIfNeeded("text input in ctor"); + createEditorIfNeeded(); mEditor.mKeyListener = TextKeyListener.getInstance(autotext, cap); mEditor.mInputType = inputType; } else if (isTextSelectable()) { @@ -964,7 +964,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // So that selection can be changed using arrow keys and touch is handled. setMovementMethod(ArrowKeyMovementMethod.getInstance()); } else if (editable) { - createEditorIfNeeded("editable input in ctor"); + createEditorIfNeeded(); mEditor.mKeyListener = TextKeyListener.getInstance(); mEditor.mInputType = EditorInfo.TYPE_CLASS_TEXT; } else { @@ -987,7 +987,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener webPasswordInputType, numberPasswordInputType); if (selectallonfocus) { - createEditorIfNeeded("selectallonfocus in constructor"); + createEditorIfNeeded(); mEditor.mSelectAllOnFocus = true; if (bufferType == BufferType.NORMAL) @@ -1335,7 +1335,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener fixFocusableAndClickableSettings(); if (input != null) { - createEditorIfNeeded("input is not null"); + createEditorIfNeeded(); try { mEditor.mInputType = mEditor.mKeyListener.getInputType(); } catch (IncompatibleClassChangeError e) { @@ -1355,7 +1355,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private void setKeyListenerOnly(KeyListener input) { if (mEditor == null && input == null) return; // null is the default value - createEditorIfNeeded("setKeyListenerOnly"); + createEditorIfNeeded(); if (mEditor.mKeyListener != input) { mEditor.mKeyListener = input; if (input != null && !(mText instanceof Editable)) { @@ -2383,7 +2383,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener */ @android.view.RemotableViewMethod public final void setShowSoftInputOnFocus(boolean show) { - createEditorIfNeeded("setShowSoftInputOnFocus"); + createEditorIfNeeded(); mEditor.mShowSoftInputOnFocus = show; } @@ -3263,7 +3263,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener Selection.setSelection((Spannable) mText, ss.selStart, ss.selEnd); if (ss.frozenWithFocus) { - createEditorIfNeeded("restore instance with focus"); + createEditorIfNeeded(); mEditor.mFrozenWithFocus = true; } } @@ -3424,7 +3424,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (type == BufferType.EDITABLE || getKeyListener() != null || needEditableForNotification) { - createEditorIfNeeded("setText with BufferType.EDITABLE or non null mInput"); + createEditorIfNeeded(); Editable t = mEditableFactory.newEditable(text); text = t; setFilters(t, mFilters); @@ -3768,7 +3768,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener */ public void setRawInputType(int type) { if (type == InputType.TYPE_NULL && mEditor == null) return; //TYPE_NULL is the default value - createEditorIfNeeded("non null input type"); + createEditorIfNeeded(); mEditor.mInputType = type; } @@ -3811,7 +3811,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } setRawInputType(type); if (direct) { - createEditorIfNeeded("setInputType"); + createEditorIfNeeded(); mEditor.mKeyListener = input; } else { setKeyListenerOnly(input); @@ -3837,7 +3837,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @attr ref android.R.styleable#TextView_imeOptions */ public void setImeOptions(int imeOptions) { - createEditorIfNeeded("IME options specified"); + createEditorIfNeeded(); mEditor.createInputContentTypeIfNeeded(); mEditor.mInputContentType.imeOptions = imeOptions; } @@ -3864,7 +3864,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @attr ref android.R.styleable#TextView_imeActionId */ public void setImeActionLabel(CharSequence label, int actionId) { - createEditorIfNeeded("IME action label specified"); + createEditorIfNeeded(); mEditor.createInputContentTypeIfNeeded(); mEditor.mInputContentType.imeActionLabel = label; mEditor.mInputContentType.imeActionId = actionId; @@ -3901,7 +3901,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * modifier will, however, allow the user to insert a newline character. */ public void setOnEditorActionListener(OnEditorActionListener l) { - createEditorIfNeeded("Editor action listener set"); + createEditorIfNeeded(); mEditor.createInputContentTypeIfNeeded(); mEditor.mInputContentType.onEditorActionListener = l; } @@ -3998,7 +3998,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @attr ref android.R.styleable#TextView_privateImeOptions */ public void setPrivateImeOptions(String type) { - createEditorIfNeeded("Private IME option set"); + createEditorIfNeeded(); mEditor.createInputContentTypeIfNeeded(); mEditor.mInputContentType.privateImeOptions = type; } @@ -4026,7 +4026,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @attr ref android.R.styleable#TextView_editorExtras */ public void setInputExtras(int xmlResId) throws XmlPullParserException, IOException { - createEditorIfNeeded("Input extra set"); + createEditorIfNeeded(); XmlResourceParser parser = getResources().getXml(xmlResId); mEditor.createInputContentTypeIfNeeded(); mEditor.mInputContentType.extras = new Bundle(); @@ -4045,7 +4045,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener */ public Bundle getInputExtras(boolean create) { if (mEditor == null && !create) return null; - createEditorIfNeeded("get Input extra"); + createEditorIfNeeded(); if (mEditor.mInputContentType == null) { if (!create) return null; mEditor.createInputContentTypeIfNeeded(); @@ -4097,7 +4097,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * be cleared (and you should provide a <code>null</code> icon as well). */ public void setError(CharSequence error, Drawable icon) { - createEditorIfNeeded("setError"); + createEditorIfNeeded(); mEditor.setError(error, icon); } @@ -4609,7 +4609,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener public void setTextIsSelectable(boolean selectable) { if (!selectable && mEditor == null) return; // false is default value with no edit data - createEditorIfNeeded("setTextIsSelectable"); + createEditorIfNeeded(); if (mEditor.mTextIsSelectable == selectable) return; mEditor.mTextIsSelectable = selectable; @@ -5422,7 +5422,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @return Returns true if the text was successfully extracted, else false. */ public boolean extractText(ExtractedTextRequest request, ExtractedText outText) { - createEditorIfNeeded("extractText"); + createEditorIfNeeded(); return mEditor.extractText(request, outText); } @@ -6836,7 +6836,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener */ @android.view.RemotableViewMethod public void setSelectAllOnFocus(boolean selectAllOnFocus) { - createEditorIfNeeded("setSelectAllOnFocus"); + createEditorIfNeeded(); mEditor.mSelectAllOnFocus = selectAllOnFocus; if (selectAllOnFocus && !(mText instanceof Spannable)) { @@ -6855,7 +6855,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener @android.view.RemotableViewMethod public void setCursorVisible(boolean visible) { if (visible && mEditor == null) return; // visible is the default value with no edit data - createEditorIfNeeded("setCursorVisible"); + createEditorIfNeeded(); if (mEditor.mCursorVisible != visible) { mEditor.mCursorVisible = visible; invalidate(); @@ -7914,7 +7914,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * that case, to allow for quick replacement. */ public void setCustomSelectionActionModeCallback(ActionMode.Callback actionModeCallback) { - createEditorIfNeeded("custom selection action mode set"); + createEditorIfNeeded(); mEditor.mCustomSelectionActionModeCallback = actionModeCallback; } @@ -8285,16 +8285,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * Also note that for performance reasons, the mEditor is created when needed, but not * reset when no more edit-specific fields are needed. */ - private void createEditorIfNeeded(String reason) { + private void createEditorIfNeeded() { if (mEditor == null) { - if (!(this instanceof EditText)) { - Log.e(LOG_TAG + " EDITOR", "Creating an Editor on a regular TextView. " + reason); - } mEditor = new Editor(this); - } else { - if (!(this instanceof EditText)) { - Log.d(LOG_TAG + " EDITOR", "Redundant Editor creation. " + reason); - } } } diff --git a/core/java/com/android/internal/content/PackageHelper.java b/core/java/com/android/internal/content/PackageHelper.java index 48ed561..246b0c9 100644 --- a/core/java/com/android/internal/content/PackageHelper.java +++ b/core/java/com/android/internal/content/PackageHelper.java @@ -57,25 +57,25 @@ public class PackageHelper { public static final int APP_INSTALL_INTERNAL = 1; public static final int APP_INSTALL_EXTERNAL = 2; - public static IMountService getMountService() { + public static IMountService getMountService() throws RemoteException { IBinder service = ServiceManager.getService("mount"); if (service != null) { return IMountService.Stub.asInterface(service); } else { Log.e(TAG, "Can't get mount service"); + throw new RemoteException("Could not contact mount service"); } - return null; } public static String createSdDir(int sizeMb, String cid, String sdEncKey, int uid, boolean isExternal) { // Create mount point via MountService - IMountService mountService = getMountService(); + try { + IMountService mountService = getMountService(); - if (localLOGV) - Log.i(TAG, "Size of container " + sizeMb + " MB"); + if (localLOGV) + Log.i(TAG, "Size of container " + sizeMb + " MB"); - try { int rc = mountService.createSecureContainer(cid, sizeMb, "ext4", sdEncKey, uid, isExternal); if (rc != StorageResultCode.OperationSucceeded) { diff --git a/core/java/com/android/internal/util/FileRotator.java b/core/java/com/android/internal/util/FileRotator.java index 6cfb97d..26235f1 100644 --- a/core/java/com/android/internal/util/FileRotator.java +++ b/core/java/com/android/internal/util/FileRotator.java @@ -19,8 +19,6 @@ package com.android.internal.util; import android.os.FileUtils; import android.util.Slog; -import com.android.internal.util.FileRotator.Rewriter; - import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; @@ -29,8 +27,11 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; import libcore.io.IoUtils; +import libcore.io.Streams; /** * Utility that rotates files over time, similar to {@code logrotate}. There is @@ -137,10 +138,38 @@ public class FileRotator { public void deleteAll() { final FileInfo info = new FileInfo(mPrefix); for (String name : mBasePath.list()) { - if (!info.parse(name)) continue; + if (info.parse(name)) { + // delete each file that matches parser + new File(mBasePath, name).delete(); + } + } + } + + /** + * Dump all files managed by this rotator for debugging purposes. + */ + public void dumpAll(OutputStream os) throws IOException { + final ZipOutputStream zos = new ZipOutputStream(os); + try { + final FileInfo info = new FileInfo(mPrefix); + for (String name : mBasePath.list()) { + if (info.parse(name)) { + final ZipEntry entry = new ZipEntry(name); + zos.putNextEntry(entry); - // delete each file that matches parser - new File(mBasePath, name).delete(); + final File file = new File(mBasePath, name); + final FileInputStream is = new FileInputStream(file); + try { + Streams.copy(is, zos); + } finally { + IoUtils.closeQuietly(is); + } + + zos.closeEntry(); + } + } + } finally { + IoUtils.closeQuietly(zos); } } @@ -159,22 +188,22 @@ public class FileRotator { public void combineActive(final Reader reader, final Writer writer, long currentTimeMillis) throws IOException { rewriteActive(new Rewriter() { - /** {@inheritDoc} */ + @Override public void reset() { // ignored } - /** {@inheritDoc} */ + @Override public void read(InputStream in) throws IOException { reader.read(in); } - /** {@inheritDoc} */ + @Override public boolean shouldWrite() { return true; } - /** {@inheritDoc} */ + @Override public void write(OutputStream out) throws IOException { writer.write(out); } @@ -224,11 +253,11 @@ public class FileRotator { // write success, delete backup backupFile.delete(); - } catch (IOException e) { + } catch (Throwable t) { // write failed, delete file and restore backup file.delete(); backupFile.renameTo(file); - throw e; + throw rethrowAsIoException(t); } } else { @@ -241,11 +270,11 @@ public class FileRotator { // write success, delete empty backup backupFile.delete(); - } catch (IOException e) { + } catch (Throwable t) { // write failed, delete file and empty backup file.delete(); backupFile.delete(); - throw e; + throw rethrowAsIoException(t); } } } @@ -353,6 +382,14 @@ public class FileRotator { } } + private static IOException rethrowAsIoException(Throwable t) throws IOException { + if (t instanceof IOException) { + throw (IOException) t; + } else { + throw new IOException(t.getMessage(), t); + } + } + /** * Details for a rotated file, either parsed from an existing filename, or * ready to be built into a new filename. diff --git a/core/java/com/android/internal/view/menu/ListMenuPresenter.java b/core/java/com/android/internal/view/menu/ListMenuPresenter.java index a331bec..b3e2d27 100644 --- a/core/java/com/android/internal/view/menu/ListMenuPresenter.java +++ b/core/java/com/android/internal/view/menu/ListMenuPresenter.java @@ -220,7 +220,6 @@ public class ListMenuPresenter implements MenuPresenter, AdapterView.OnItemClick private int mExpandedIndex = -1; public MenuAdapter() { - registerDataSetObserver(new ExpandedIndexObserver()); findExpandedIndex(); } @@ -273,12 +272,11 @@ public class ListMenuPresenter implements MenuPresenter, AdapterView.OnItemClick } mExpandedIndex = -1; } - } - private class ExpandedIndexObserver extends DataSetObserver { @Override - public void onChanged() { - mAdapter.findExpandedIndex(); + public void notifyDataSetChanged() { + findExpandedIndex(); + super.notifyDataSetChanged(); } } } diff --git a/core/java/com/android/internal/view/menu/MenuPopupHelper.java b/core/java/com/android/internal/view/menu/MenuPopupHelper.java index 329b457..cacc86b 100644 --- a/core/java/com/android/internal/view/menu/MenuPopupHelper.java +++ b/core/java/com/android/internal/view/menu/MenuPopupHelper.java @@ -307,7 +307,6 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On public MenuAdapter(MenuBuilder menu) { mAdapterMenu = menu; - registerDataSetObserver(new ExpandedIndexObserver()); findExpandedIndex(); } @@ -363,12 +362,11 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On } mExpandedIndex = -1; } - } - private class ExpandedIndexObserver extends DataSetObserver { @Override - public void onChanged() { - mAdapter.findExpandedIndex(); + public void notifyDataSetChanged() { + findExpandedIndex(); + super.notifyDataSetChanged(); } } } diff --git a/core/jni/android_os_SystemProperties.cpp b/core/jni/android_os_SystemProperties.cpp index 66af965..677396d1 100644 --- a/core/jni/android_os_SystemProperties.cpp +++ b/core/jni/android_os_SystemProperties.cpp @@ -15,7 +15,11 @@ ** limitations under the License. */ +#define LOG_TAG "SysPropJNI" + #include "cutils/properties.h" +#include "utils/misc.h" +#include <utils/Log.h> #include "jni.h" #include "android_runtime/AndroidRuntime.h" #include <nativehelper/JNIHelp.h> @@ -65,6 +69,7 @@ static jint SystemProperties_get_int(JNIEnv *env, jobject clazz, int len; const char* key; char buf[PROPERTY_VALUE_MAX]; + char* end; jint result = defJ; if (keyJ == NULL) { @@ -76,9 +81,10 @@ static jint SystemProperties_get_int(JNIEnv *env, jobject clazz, len = property_get(key, buf, ""); if (len > 0) { - jint temp; - if (sscanf(buf, "%d", &temp) == 1) - result = temp; + result = strtol(buf, &end, 0); + if (end == buf) { + result = defJ; + } } env->ReleaseStringUTFChars(keyJ, key); @@ -93,6 +99,7 @@ static jlong SystemProperties_get_long(JNIEnv *env, jobject clazz, int len; const char* key; char buf[PROPERTY_VALUE_MAX]; + char* end; jlong result = defJ; if (keyJ == NULL) { @@ -104,9 +111,10 @@ static jlong SystemProperties_get_long(JNIEnv *env, jobject clazz, len = property_get(key, buf, ""); if (len > 0) { - jlong temp; - if (sscanf(buf, "%lld", &temp) == 1) - result = temp; + result = strtoll(buf, &end, 0); + if (end == buf) { + result = defJ; + } } env->ReleaseStringUTFChars(keyJ, key); @@ -184,6 +192,34 @@ static void SystemProperties_set(JNIEnv *env, jobject clazz, } } +static JavaVM* sVM = NULL; +static jclass sClazz = NULL; +static jmethodID sCallChangeCallbacks; + +static void do_report_sysprop_change() { + //ALOGI("Java SystemProperties: VM=%p, Clazz=%p", sVM, sClazz); + if (sVM != NULL && sClazz != NULL) { + JNIEnv* env; + if (sVM->GetEnv((void **)&env, JNI_VERSION_1_4) >= 0) { + //ALOGI("Java SystemProperties: calling %p", sCallChangeCallbacks); + env->CallStaticVoidMethod(sClazz, sCallChangeCallbacks); + } + } +} + +static void SystemProperties_add_change_callback(JNIEnv *env, jobject clazz) +{ + // This is called with the Java lock held. + if (sVM == NULL) { + env->GetJavaVM(&sVM); + } + if (sClazz == NULL) { + sClazz = (jclass) env->NewGlobalRef(clazz); + sCallChangeCallbacks = env->GetStaticMethodID(sClazz, "callChangeCallbacks", "()V"); + add_sysprop_change_callback(do_report_sysprop_change, -10000); + } +} + static JNINativeMethod method_table[] = { { "native_get", "(Ljava/lang/String;)Ljava/lang/String;", (void*) SystemProperties_getS }, @@ -197,6 +233,8 @@ static JNINativeMethod method_table[] = { (void*) SystemProperties_get_boolean }, { "native_set", "(Ljava/lang/String;Ljava/lang/String;)V", (void*) SystemProperties_set }, + { "native_add_change_callback", "()V", + (void*) SystemProperties_add_change_callback }, }; int register_android_os_SystemProperties(JNIEnv *env) diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp index 0f99fb2..04dc49f 100644 --- a/core/jni/android_util_Binder.cpp +++ b/core/jni/android_util_Binder.cpp @@ -308,6 +308,12 @@ protected: env->DeleteLocalRef(excep2); } + // Need to always call through the native implementation of + // SYSPROPS_TRANSACTION. + if (code == SYSPROPS_TRANSACTION) { + BBinder::onTransact(code, data, reply, flags); + } + //aout << "onTransact to Java code; result=" << res << endl // << "Transact from " << this << " to Java code returning " // << reply << ": " << *reply << endl; diff --git a/core/res/res/values-sw600dp/dimens.xml b/core/res/res/values-sw600dp/dimens.xml index 61136e3..c5727ea 100644 --- a/core/res/res/values-sw600dp/dimens.xml +++ b/core/res/res/values-sw600dp/dimens.xml @@ -18,6 +18,10 @@ */ --> <resources> + <!-- The width that is used when creating thumbnails of applications. --> + <dimen name="thumbnail_width">200dp</dimen> + <!-- The height that is used when creating thumbnails of applications. --> + <dimen name="thumbnail_height">177dp</dimen> <!-- The maximum number of action buttons that should be permitted within an action bar/action mode. This will be used to determine how many showAsAction="ifRoom" items can fit. "always" items can override this. --> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index f812822..734151b 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -19,9 +19,9 @@ --> <resources> <!-- The width that is used when creating thumbnails of applications. --> - <dimen name="thumbnail_width">120dp</dimen> + <dimen name="thumbnail_width">164dp</dimen> <!-- The height that is used when creating thumbnails of applications. --> - <dimen name="thumbnail_height">120dp</dimen> + <dimen name="thumbnail_height">145dp</dimen> <!-- The standard size (both width and height) of an application icon that will be displayed in the app launcher and elsewhere. --> <dimen name="app_icon_size">48dip</dimen> diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java index 19aa77b..f7b0cd0 100644 --- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java +++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java @@ -246,9 +246,7 @@ public class ConnectivityManagerTestActivity extends Activity { initializeNetworkStates(); - mWifiManager.setWifiEnabled(true); log("Clear Wifi before we start the test."); - sleep(SHORT_TIMEOUT); removeConfiguredNetworksAndDisableWifi(); mWifiRegexs = mCM.getTetherableWifiRegexs(); } @@ -645,6 +643,11 @@ public class ConnectivityManagerTestActivity extends Activity { */ public boolean disconnectAP() { // remove saved networks + if (!mWifiManager.isWifiEnabled()) { + log("Enabled wifi before remove configured networks"); + mWifiManager.setWifiEnabled(true); + sleep(SHORT_TIMEOUT); + } List<WifiConfiguration> wifiConfigList = mWifiManager.getConfiguredNetworks(); log("size of wifiConfigList: " + wifiConfigList.size()); for (WifiConfiguration wifiConfig: wifiConfigList) { diff --git a/core/tests/coretests/src/com/android/internal/util/FileRotatorTest.java b/core/tests/coretests/src/com/android/internal/util/FileRotatorTest.java index 94d1cb6..95f0e67 100644 --- a/core/tests/coretests/src/com/android/internal/util/FileRotatorTest.java +++ b/core/tests/coretests/src/com/android/internal/util/FileRotatorTest.java @@ -187,12 +187,12 @@ public class FileRotatorTest extends AndroidTestCase { rotate.combineActive(reader, new Writer() { public void write(OutputStream out) throws IOException { new DataOutputStream(out).writeUTF("bar"); - throw new ProtocolException("yikes"); + throw new NullPointerException("yikes"); } }, currentTime); fail("woah, somehow able to write exception"); - } catch (ProtocolException e) { + } catch (IOException e) { // expected from above } diff --git a/data/fonts/Android.mk b/data/fonts/Android.mk index 13f2480..16a98d3 100644 --- a/data/fonts/Android.mk +++ b/data/fonts/Android.mk @@ -142,6 +142,8 @@ font_src_files := \ Roboto-Bold.ttf \ Roboto-Italic.ttf \ Roboto-BoldItalic.ttf \ + Roboto-Light.ttf \ + Roboto-LightItalic.ttf \ DroidNaskh-Regular.ttf \ DroidNaskh-Regular-SystemUI.ttf \ DroidSansDevanagari-Regular.ttf \ diff --git a/data/fonts/Roboto-Light.ttf b/data/fonts/Roboto-Light.ttf Binary files differnew file mode 100644 index 0000000..b50399e --- /dev/null +++ b/data/fonts/Roboto-Light.ttf diff --git a/data/fonts/Roboto-LightItalic.ttf b/data/fonts/Roboto-LightItalic.ttf Binary files differnew file mode 100644 index 0000000..a1fdc8d --- /dev/null +++ b/data/fonts/Roboto-LightItalic.ttf diff --git a/data/fonts/fonts.mk b/data/fonts/fonts.mk index 7a84df6..397ccda 100644 --- a/data/fonts/fonts.mk +++ b/data/fonts/fonts.mk @@ -24,6 +24,8 @@ PRODUCT_PACKAGES := \ Roboto-Bold.ttf \ Roboto-Italic.ttf \ Roboto-BoldItalic.ttf \ + Roboto-Light.ttf \ + Roboto-LightItalic.ttf \ DroidNaskh-Regular.ttf \ DroidNaskh-Regular-SystemUI.ttf \ DroidSansDevanagari-Regular.ttf \ diff --git a/data/fonts/system_fonts.xml b/data/fonts/system_fonts.xml index d2fe546..95c4f70 100644 --- a/data/fonts/system_fonts.xml +++ b/data/fonts/system_fonts.xml @@ -36,6 +36,16 @@ <family> <nameset> + <name>sans-serif-light</name> + </nameset> + <fileset> + <file>Roboto-Light.ttf</file> + <file>Roboto-LightItalic.ttf</file> + </fileset> + </family> + + <family> + <nameset> <name>serif</name> <name>times</name> <name>times new roman</name> diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java index 2174d06..1892fce 100644 --- a/media/java/android/media/AudioService.java +++ b/media/java/android/media/AudioService.java @@ -55,6 +55,7 @@ import android.os.PowerManager; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemProperties; +import android.os.Vibrator; import android.provider.Settings; import android.provider.Settings.System; import android.telephony.PhoneStateListener; @@ -119,19 +120,18 @@ public class AudioService extends IAudioService.Stub implements OnFinished { private static final int MSG_PERSIST_VOLUME = 1; private static final int MSG_PERSIST_MASTER_VOLUME = 2; private static final int MSG_PERSIST_RINGER_MODE = 3; - private static final int MSG_PERSIST_VIBRATE_SETTING = 4; - private static final int MSG_MEDIA_SERVER_DIED = 5; - private static final int MSG_MEDIA_SERVER_STARTED = 6; - private static final int MSG_PLAY_SOUND_EFFECT = 7; - private static final int MSG_BTA2DP_DOCK_TIMEOUT = 8; - private static final int MSG_LOAD_SOUND_EFFECTS = 9; - private static final int MSG_SET_FORCE_USE = 10; - private static final int MSG_PERSIST_MEDIABUTTONRECEIVER = 11; - private static final int MSG_BT_HEADSET_CNCT_FAILED = 12; - private static final int MSG_RCDISPLAY_CLEAR = 13; - private static final int MSG_RCDISPLAY_UPDATE = 14; - private static final int MSG_SET_ALL_VOLUMES = 15; - private static final int MSG_PERSIST_MASTER_VOLUME_MUTE = 16; + private static final int MSG_MEDIA_SERVER_DIED = 4; + private static final int MSG_MEDIA_SERVER_STARTED = 5; + private static final int MSG_PLAY_SOUND_EFFECT = 6; + private static final int MSG_BTA2DP_DOCK_TIMEOUT = 7; + private static final int MSG_LOAD_SOUND_EFFECTS = 8; + private static final int MSG_SET_FORCE_USE = 9; + private static final int MSG_PERSIST_MEDIABUTTONRECEIVER = 10; + private static final int MSG_BT_HEADSET_CNCT_FAILED = 11; + private static final int MSG_RCDISPLAY_CLEAR = 12; + private static final int MSG_RCDISPLAY_UPDATE = 13; + private static final int MSG_SET_ALL_VOLUMES = 14; + private static final int MSG_PERSIST_MASTER_VOLUME_MUTE = 15; // flags for MSG_PERSIST_VOLUME indicating if current and/or last audible volume should be @@ -241,6 +241,20 @@ public class AudioService extends IAudioService.Stub implements OnFinished { }; private int[] mStreamVolumeAlias; + // stream names used by dumpStreamStates() + private final String[] STREAM_NAMES = new String[] { + "STREAM_VOICE_CALL", + "STREAM_SYSTEM", + "STREAM_RING", + "STREAM_MUSIC", + "STREAM_ALARM", + "STREAM_NOTIFICATION", + "STREAM_BLUETOOTH_SCO", + "STREAM_SYSTEM_ENFORCED", + "STREAM_DTMF", + "STREAM_TTS" + }; + private final AudioSystem.ErrorCallback mAudioSystemCallback = new AudioSystem.ErrorCallback() { public void onError(int error) { switch (error) { @@ -282,14 +296,15 @@ public class AudioService extends IAudioService.Stub implements OnFinished { private int mMuteAffectedStreams; /** - * Has multiple bits per vibrate type to indicate the type's vibrate - * setting. See {@link #setVibrateSetting(int, int)}. - * <p> - * NOTE: This is not the final decision of whether vibrate is on/off for the - * type since it depends on the ringer mode. See {@link #shouldVibrate(int)}. + * NOTE: setVibrateSetting(), getVibrateSetting(), shouldVibrate() are deprecated. + * mVibrateSetting is just maintained during deprecation period but vibration policy is + * now only controlled by mHasVibrator and mRingerMode */ private int mVibrateSetting; + // Is there a vibrator + private final boolean mHasVibrator; + // Broadcast receiver for device connections intent broadcasts private final BroadcastReceiver mReceiver = new AudioServiceBroadcastReceiver(); @@ -388,6 +403,9 @@ public class AudioService extends IAudioService.Stub implements OnFinished { PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "mediaKeyEvent"); + Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE); + mHasVibrator = vibrator == null ? false : vibrator.hasVibrator(); + // Intialized volume MAX_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL] = SystemProperties.getInt( "ro.config.vc_call_vol_steps", @@ -507,6 +525,16 @@ public class AudioService extends IAudioService.Stub implements OnFinished { } } + private void dumpStreamStates(PrintWriter pw) { + pw.println("\nStream volumes (device: index)"); + int numStreamTypes = AudioSystem.getNumStreamTypes(); + for (int i = 0; i < numStreamTypes; i++) { + pw.println("- "+STREAM_NAMES[i]+":"); + mStreamStates[i].dump(pw); + pw.println(""); + } + } + private void updateStreamVolumeAlias(boolean updateVolumes) { int dtmfStreamAlias; @@ -538,18 +566,34 @@ public class AudioService extends IAudioService.Stub implements OnFinished { private void readPersistedSettings() { final ContentResolver cr = mContentResolver; - int ringerMode = System.getInt(cr, System.MODE_RINGER, AudioManager.RINGER_MODE_NORMAL); + int ringerModeFromSettings = + System.getInt(cr, System.MODE_RINGER, AudioManager.RINGER_MODE_NORMAL); + int ringerMode = ringerModeFromSettings; // sanity check in case the settings are restored from a device with incompatible // ringer modes if (!AudioManager.isValidRingerMode(ringerMode)) { ringerMode = AudioManager.RINGER_MODE_NORMAL; + } + if ((ringerMode == AudioManager.RINGER_MODE_VIBRATE) && !mHasVibrator) { + ringerMode = AudioManager.RINGER_MODE_SILENT; + } + if (ringerMode != ringerModeFromSettings) { System.putInt(cr, System.MODE_RINGER, ringerMode); } synchronized(mSettingsLock) { mRingerMode = ringerMode; } - mVibrateSetting = System.getInt(cr, System.VIBRATE_ON, 0); + // System.VIBRATE_ON is not used any more but defaults for mVibrateSetting + // are still needed while setVibrateSetting() and getVibrateSetting() are being deprecated. + mVibrateSetting = getValueForVibrateSetting(0, + AudioManager.VIBRATE_TYPE_NOTIFICATION, + mHasVibrator ? AudioManager.VIBRATE_SETTING_ONLY_SILENT + : AudioManager.VIBRATE_SETTING_OFF); + mVibrateSetting = getValueForVibrateSetting(mVibrateSetting, + AudioManager.VIBRATE_TYPE_RINGER, + mHasVibrator ? AudioManager.VIBRATE_SETTING_ONLY_SILENT + : AudioManager.VIBRATE_SETTING_OFF); // make sure settings for ringer mode are consistent with device type: non voice capable // devices (tablets) include media stream in silent mode whereas phones don't. @@ -639,8 +683,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished { // If either the client forces allowing ringer modes for this adjustment, // or the stream type is one that is affected by ringer modes if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) || - streamTypeAlias == AudioSystem.STREAM_RING || - (!mVoiceCapable && streamTypeAlias == AudioSystem.STREAM_MUSIC)) { + (streamTypeAlias == getMasterStreamType())) { int ringerMode = getRingerMode(); // do not vibrate if already in vibrate mode if (ringerMode == AudioManager.RINGER_MODE_VIBRATE) { @@ -648,7 +691,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished { } // Check if the ringer mode changes with this volume adjustment. If // it does, it will handle adjusting the volume, so we won't below - adjustVolume = checkForRingerModeChange(oldIndex, direction, streamTypeAlias); + adjustVolume = checkForRingerModeChange(oldIndex, direction); } // If stream is muted, adjust last audible index only @@ -724,9 +767,8 @@ public class AudioService extends IAudioService.Stub implements OnFinished { (mStreamVolumeAlias[streamType] == getMasterStreamType())) { int newRingerMode; if (index == 0) { - newRingerMode = System.getInt(mContentResolver, System.VIBRATE_IN_SILENT, 1) == 1 - ? AudioManager.RINGER_MODE_VIBRATE - : AudioManager.RINGER_MODE_SILENT; + newRingerMode = mHasVibrator ? AudioManager.RINGER_MODE_VIBRATE + : AudioManager.RINGER_MODE_SILENT; setStreamVolumeInt(mStreamVolumeAlias[streamType], index, device, @@ -1070,7 +1112,6 @@ public class AudioService extends IAudioService.Stub implements OnFinished { // on voice capable devices if (mVoiceCapable && mStreamVolumeAlias[streamType] == AudioSystem.STREAM_RING) { - Set set = mStreamStates[streamType].mLastAudibleIndex.entrySet(); Iterator i = set.iterator(); while (i.hasNext()) { @@ -1111,6 +1152,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished { /** @see AudioManager#shouldVibrate(int) */ public boolean shouldVibrate(int vibrateType) { + if (!mHasVibrator) return false; switch (getVibrateSetting(vibrateType)) { @@ -1131,21 +1173,20 @@ public class AudioService extends IAudioService.Stub implements OnFinished { /** @see AudioManager#getVibrateSetting(int) */ public int getVibrateSetting(int vibrateType) { + if (!mHasVibrator) return AudioManager.VIBRATE_SETTING_OFF; return (mVibrateSetting >> (vibrateType * 2)) & 3; } /** @see AudioManager#setVibrateSetting(int, int) */ public void setVibrateSetting(int vibrateType, int vibrateSetting) { + if (!mHasVibrator) return; + mVibrateSetting = getValueForVibrateSetting(mVibrateSetting, vibrateType, vibrateSetting); // Broadcast change broadcastVibrateSetting(vibrateType); - // Post message to set ringer mode (it in turn will post a message - // to persist) - sendMsg(mAudioHandler, MSG_PERSIST_VIBRATE_SETTING, SENDMSG_NOOP, 0, 0, - null, 0); } /** @@ -1967,48 +2008,56 @@ public class AudioService extends IAudioService.Stub implements OnFinished { * adjusting volume. If so, this will set the proper ringer mode and volume * indices on the stream states. */ - private boolean checkForRingerModeChange(int oldIndex, int direction, int streamType) { + private boolean checkForRingerModeChange(int oldIndex, int direction) { boolean adjustVolumeIndex = true; int ringerMode = getRingerMode(); - int newRingerMode = ringerMode; int uiIndex = (oldIndex + 5) / 10; - boolean vibeInSilent = System.getInt(mContentResolver, System.VIBRATE_IN_SILENT, 1) == 1; - - if (ringerMode == RINGER_MODE_NORMAL) { - if ((direction == AudioManager.ADJUST_LOWER) && (uiIndex <= 1)) { - // enter silent mode if current index is the last audible one and not repeating a - // volume key down - if (vibeInSilent || mPrevVolDirection != AudioManager.ADJUST_LOWER) { - // "silent mode", but which one? - newRingerMode = vibeInSilent ? RINGER_MODE_VIBRATE : RINGER_MODE_SILENT; - } - if (uiIndex == 0 || - (!vibeInSilent && - mPrevVolDirection == AudioManager.ADJUST_LOWER && - mVoiceCapable && streamType == AudioSystem.STREAM_RING)) { - adjustVolumeIndex = false; + + switch (ringerMode) { + case RINGER_MODE_NORMAL: + if (direction == AudioManager.ADJUST_LOWER) { + if (mHasVibrator) { + if (uiIndex == 1) { + ringerMode = RINGER_MODE_VIBRATE; + } + } else { + if (uiIndex == 0 && mPrevVolDirection != AudioManager.ADJUST_LOWER) { + ringerMode = RINGER_MODE_SILENT; + } } } - } else if (ringerMode == RINGER_MODE_VIBRATE) { + break; + case RINGER_MODE_VIBRATE: + if (!mHasVibrator) { + Log.e(TAG, "checkForRingerModeChange() current ringer mode is vibrate" + + "but no vibrator is present"); + break; + } if ((direction == AudioManager.ADJUST_LOWER)) { - // Set it to silent, if it wasn't a long-press if (mPrevVolDirection != AudioManager.ADJUST_LOWER) { - newRingerMode = RINGER_MODE_SILENT; + ringerMode = RINGER_MODE_SILENT; } } else if (direction == AudioManager.ADJUST_RAISE) { - newRingerMode = RINGER_MODE_NORMAL; + ringerMode = RINGER_MODE_NORMAL; } adjustVolumeIndex = false; - } else { + break; + case RINGER_MODE_SILENT: if (direction == AudioManager.ADJUST_RAISE) { - // exiting silent mode - // If VIBRATE_IN_SILENT, then go into vibrate mode - newRingerMode = vibeInSilent ? RINGER_MODE_VIBRATE : RINGER_MODE_NORMAL; + if (mHasVibrator) { + ringerMode = RINGER_MODE_VIBRATE; + } else { + ringerMode = RINGER_MODE_NORMAL; + } } adjustVolumeIndex = false; + break; + default: + Log.e(TAG, "checkForRingerModeChange() wrong ringer mode: "+ringerMode); + break; } - setRingerMode(newRingerMode); + setRingerMode(ringerMode); mPrevVolDirection = direction; @@ -2217,9 +2266,6 @@ public class AudioService extends IAudioService.Stub implements OnFinished { } public void readSettings() { - boolean checkSilentVolume = (mRingerMode == AudioManager.RINGER_MODE_NORMAL) && - isStreamAffectedByRingerMode(mStreamType); - int remainingDevices = AudioSystem.DEVICE_OUT_ALL; for (int i = 0; remainingDevices != 0; i++) { @@ -2248,12 +2294,13 @@ public class AudioService extends IAudioService.Stub implements OnFinished { index : AudioManager.DEFAULT_STREAM_VOLUME[mStreamType]; int lastAudibleIndex = Settings.System.getInt(mContentResolver, name, defaultIndex); - // a last audible index of 0 is never stored, except on non-voice capable devices - // (e.g. tablets) for the music stream type, where the music stream volume can reach - // 0 without the device being in silent mode + // a last audible index of 0 should never be stored for ring and notification + // streams on phones (voice capable devices). + // same for system stream on phones and tablets if ((lastAudibleIndex == 0) && - (mVoiceCapable || - (mStreamVolumeAlias[mStreamType] != AudioSystem.STREAM_MUSIC))) { + ((mVoiceCapable && + (mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_RING)) || + (mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_SYSTEM))) { lastAudibleIndex = AudioManager.DEFAULT_STREAM_VOLUME[mStreamType]; // Correct the data base sendMsg(mAudioHandler, @@ -2265,12 +2312,13 @@ public class AudioService extends IAudioService.Stub implements OnFinished { PERSIST_DELAY); } mLastAudibleIndex.put(device, getValidIndex(10 * lastAudibleIndex)); - // the initial index should never be 0 for a stream affected by ringer mode if not - // in silent or vibrate mode. - // this is permitted on tablets for music stream type. - if (checkSilentVolume && (index == 0) && - (mVoiceCapable || - (mStreamVolumeAlias[mStreamType] != AudioSystem.STREAM_MUSIC))) { + // the initial index should never be 0 for ring and notification streams on phones + // (voice capable devices) if not in silent or vibrate mode. + // same for system stream on phones and tablets + if ((index == 0) && (mRingerMode == AudioManager.RINGER_MODE_NORMAL) && + ((mVoiceCapable && + (mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_RING)) || + (mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_SYSTEM))) { index = lastAudibleIndex; // Correct the data base sendMsg(mAudioHandler, @@ -2328,14 +2376,22 @@ public class AudioService extends IAudioService.Stub implements OnFinished { mLastAudibleIndex.put(device, index); } // Apply change to all streams using this one as alias + // if changing volume of current device, also change volume of current + // device on aliased stream + boolean currentDevice = (device == getDeviceForStream(mStreamType)); int numStreamTypes = AudioSystem.getNumStreamTypes(); for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) { - if (streamType != mStreamType && mStreamVolumeAlias[streamType] == mStreamType) { - mStreamStates[streamType].setIndex(rescaleIndex(index, - mStreamType, - streamType), - getDeviceForStream(streamType), + if (streamType != mStreamType && + mStreamVolumeAlias[streamType] == mStreamType) { + int scaledIndex = rescaleIndex(index, mStreamType, streamType); + mStreamStates[streamType].setIndex(scaledIndex, + device, lastAudible); + if (currentDevice) { + mStreamStates[streamType].setIndex(scaledIndex, + getDeviceForStream(streamType), + lastAudible); + } } } return true; @@ -2544,6 +2600,25 @@ public class AudioService extends IAudioService.Stub implements OnFinished { return handler; } } + + private void dump(PrintWriter pw) { + pw.print(" Current: "); + Set set = mIndex.entrySet(); + Iterator i = set.iterator(); + while (i.hasNext()) { + Map.Entry entry = (Map.Entry)i.next(); + pw.print(Integer.toHexString(((Integer)entry.getKey()).intValue()) + + ": " + ((((Integer)entry.getValue()).intValue() + 5) / 10)+", "); + } + pw.print("\n Last audible: "); + set = mLastAudibleIndex.entrySet(); + i = set.iterator(); + while (i.hasNext()) { + Map.Entry entry = (Map.Entry)i.next(); + pw.print(Integer.toHexString(((Integer)entry.getKey()).intValue()) + + ": " + ((((Integer)entry.getValue()).intValue() + 5) / 10)+", "); + } + } } /** Thread that handles native AudioSystem control. */ @@ -2631,10 +2706,6 @@ public class AudioService extends IAudioService.Stub implements OnFinished { System.putInt(mContentResolver, System.MODE_RINGER, ringerMode); } - private void persistVibrateSetting() { - System.putInt(mContentResolver, System.VIBRATE_ON, mVibrateSetting); - } - private void playSoundEffect(int effectType, int volume) { synchronized (mSoundEffectsLock) { if (mSoundPool == null) { @@ -2734,10 +2805,6 @@ public class AudioService extends IAudioService.Stub implements OnFinished { persistRingerMode(getRingerMode()); break; - case MSG_PERSIST_VIBRATE_SETTING: - persistVibrateSetting(); - break; - case MSG_MEDIA_SERVER_DIED: if (!mMediaServerOk) { Log.e(TAG, "Media server died."); @@ -4366,5 +4433,6 @@ public class AudioService extends IAudioService.Stub implements OnFinished { // TODO probably a lot more to do here than just the audio focus and remote control stacks dumpFocusStack(pw); dumpRCStack(pw); + dumpStreamStates(pw); } } diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java index 9bafa5c..55071ec 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -260,6 +260,8 @@ public class AudioSystem public static final String DEVICE_OUT_AUX_DIGITAL_NAME = "aux_digital"; public static final String DEVICE_OUT_ANLG_DOCK_HEADSET_NAME = "analog_dock"; public static final String DEVICE_OUT_DGTL_DOCK_HEADSET_NAME = "digital_dock"; + public static final String DEVICE_OUT_USB_ACCESSORY_NAME = "usb_accessory"; + public static final String DEVICE_OUT_USB_DEVICE_NAME = "usb_device"; public static String getDeviceName(int device) { @@ -290,6 +292,10 @@ public class AudioSystem return DEVICE_OUT_ANLG_DOCK_HEADSET_NAME; case DEVICE_OUT_DGTL_DOCK_HEADSET: return DEVICE_OUT_DGTL_DOCK_HEADSET_NAME; + case DEVICE_OUT_USB_ACCESSORY: + return DEVICE_OUT_USB_ACCESSORY_NAME; + case DEVICE_OUT_USB_DEVICE: + return DEVICE_OUT_USB_DEVICE_NAME; case DEVICE_IN_DEFAULT: default: return ""; diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java index 58b30db..d3a00c2 100644 --- a/media/java/android/media/MediaExtractor.java +++ b/media/java/android/media/MediaExtractor.java @@ -225,11 +225,6 @@ final public class MediaExtractor { * If possible, seek to the sync sample closest to the specified time */ public static final int SEEK_TO_CLOSEST_SYNC = 2; - /** - * If possible, seek to a sample closest to the specified time, which may - * NOT be a sync sample! - */ - public static final int SEEK_TO_CLOSEST = 3; /** * All selected tracks seek near the requested time according to the diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index 7540c6f..9f0fd48 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -743,9 +743,14 @@ public class MediaPlayer } Parcel request = Parcel.obtain(); Parcel reply = Parcel.obtain(); - request.writeInterfaceToken(IMEDIA_PLAYER); - request.writeInt(INVOKE_ID_SET_VIDEO_SCALE_MODE); - invoke(request, reply); + try { + request.writeInterfaceToken(IMEDIA_PLAYER); + request.writeInt(INVOKE_ID_SET_VIDEO_SCALE_MODE); + invoke(request, reply); + } finally { + request.recycle(); + reply.recycle(); + } } /** @@ -1642,11 +1647,16 @@ public class MediaPlayer public TrackInfo[] getTrackInfo() throws IllegalStateException { Parcel request = Parcel.obtain(); Parcel reply = Parcel.obtain(); - request.writeInterfaceToken(IMEDIA_PLAYER); - request.writeInt(INVOKE_ID_GET_TRACK_INFO); - invoke(request, reply); - TrackInfo trackInfo[] = reply.createTypedArray(TrackInfo.CREATOR); - return trackInfo; + try { + request.writeInterfaceToken(IMEDIA_PLAYER); + request.writeInt(INVOKE_ID_GET_TRACK_INFO); + invoke(request, reply); + TrackInfo trackInfo[] = reply.createTypedArray(TrackInfo.CREATOR); + return trackInfo; + } finally { + request.recycle(); + reply.recycle(); + } } /* Do not change these values without updating their counterparts @@ -1791,13 +1801,18 @@ public class MediaPlayer Parcel request = Parcel.obtain(); Parcel reply = Parcel.obtain(); - request.writeInterfaceToken(IMEDIA_PLAYER); - request.writeInt(INVOKE_ID_ADD_EXTERNAL_SOURCE_FD); - request.writeFileDescriptor(fd); - request.writeLong(offset); - request.writeLong(length); - request.writeString(mimeType); - invoke(request, reply); + try { + request.writeInterfaceToken(IMEDIA_PLAYER); + request.writeInt(INVOKE_ID_ADD_EXTERNAL_SOURCE_FD); + request.writeFileDescriptor(fd); + request.writeLong(offset); + request.writeLong(length); + request.writeString(mimeType); + invoke(request, reply); + } finally { + request.recycle(); + reply.recycle(); + } } /** @@ -1854,10 +1869,15 @@ public class MediaPlayer throws IllegalStateException { Parcel request = Parcel.obtain(); Parcel reply = Parcel.obtain(); - request.writeInterfaceToken(IMEDIA_PLAYER); - request.writeInt(select? INVOKE_ID_SELECT_TRACK: INVOKE_ID_DESELECT_TRACK); - request.writeInt(index); - invoke(request, reply); + try { + request.writeInterfaceToken(IMEDIA_PLAYER); + request.writeInt(select? INVOKE_ID_SELECT_TRACK: INVOKE_ID_DESELECT_TRACK); + request.writeInt(index); + invoke(request, reply); + } finally { + request.recycle(); + reply.recycle(); + } } diff --git a/media/jni/android_media_MediaExtractor.cpp b/media/jni/android_media_MediaExtractor.cpp index c93baf1..351ff04 100644 --- a/media/jni/android_media_MediaExtractor.cpp +++ b/media/jni/android_media_MediaExtractor.cpp @@ -313,7 +313,7 @@ static void android_media_MediaExtractor_seekTo( } if (mode < MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC - || mode > MediaSource::ReadOptions::SEEK_CLOSEST) { + || mode >= MediaSource::ReadOptions::SEEK_CLOSEST) { jniThrowException(env, "java/lang/IllegalArgumentException", NULL); return; } diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java index abf713b..b0939de 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java @@ -63,7 +63,7 @@ public class DatabaseHelper extends SQLiteOpenHelper { // database gets upgraded properly. At a minimum, please confirm that 'upgradeVersion' // is properly propagated through your change. Not doing so will result in a loss of user // settings. - private static final int DATABASE_VERSION = 76; + private static final int DATABASE_VERSION = 78; private Context mContext; @@ -657,7 +657,7 @@ public class DatabaseHelper extends SQLiteOpenHelper { upgradeVersion = 53; } - + if (upgradeVersion == 53) { /* * New settings for set install location UI no longer initiated here. @@ -1031,6 +1031,29 @@ public class DatabaseHelper extends SQLiteOpenHelper { upgradeVersion = 76; } + /************* The following are Jelly Bean changes ************/ + + if (upgradeVersion == 76) { + // Removed VIBRATE_IN_SILENT setting + db.beginTransaction(); + try { + db.execSQL("DELETE FROM system WHERE name='" + + Settings.System.VIBRATE_IN_SILENT + "'"); + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + + upgradeVersion = 77; + } + + if (upgradeVersion == 77) { + // Introduce "vibrate when ringing" setting + loadVibrateWhenRingingSetting(db); + + upgradeVersion = 78; + } + // *** Remember to update DATABASE_VERSION above! if (upgradeVersion != currentVersion) { @@ -1124,7 +1147,7 @@ public class DatabaseHelper extends SQLiteOpenHelper { try { stmt = db.compileStatement("INSERT OR REPLACE INTO system(name,value)" + " VALUES(?,?);"); - + // Set the timeout to 30 minutes in milliseconds loadSetting(stmt, Settings.System.SCREEN_OFF_TIMEOUT, Integer.toString(30 * 60 * 1000)); @@ -1286,7 +1309,7 @@ public class DatabaseHelper extends SQLiteOpenHelper { try { stmt = db.compileStatement("INSERT OR IGNORE INTO system(name,value)" + " VALUES(?,?);"); - + loadSetting(stmt, Settings.System.VOLUME_MUSIC, AudioManager.DEFAULT_STREAM_VOLUME[AudioManager.STREAM_MUSIC]); loadSetting(stmt, Settings.System.VOLUME_RING, @@ -1307,12 +1330,10 @@ public class DatabaseHelper extends SQLiteOpenHelper { stmt, Settings.System.VOLUME_BLUETOOTH_SCO, AudioManager.DEFAULT_STREAM_VOLUME[AudioManager.STREAM_BLUETOOTH_SCO]); - + loadSetting(stmt, Settings.System.MODE_RINGER, AudioManager.RINGER_MODE_NORMAL); - - loadVibrateSetting(db, false); - + // By default: // - ringtones, notification, system and music streams are affected by ringer mode // on non voice capable devices (tablets) @@ -1337,6 +1358,8 @@ public class DatabaseHelper extends SQLiteOpenHelper { } finally { if (stmt != null) stmt.close(); } + + loadVibrateWhenRingingSetting(db); } private void loadVibrateSetting(SQLiteDatabase db, boolean deleteOld) { @@ -1348,7 +1371,7 @@ public class DatabaseHelper extends SQLiteOpenHelper { try { stmt = db.compileStatement("INSERT OR IGNORE INTO system(name,value)" + " VALUES(?,?);"); - + // Vibrate on by default for ringer, on for notification int vibrate = 0; vibrate = AudioService.getValueForVibrateSetting(vibrate, @@ -1362,6 +1385,24 @@ public class DatabaseHelper extends SQLiteOpenHelper { } } + private void loadVibrateWhenRingingSetting(SQLiteDatabase db) { + // The default should be off. VIBRATE_SETTING_ONLY_SILENT should also be ignored here. + // Phone app should separately check whether AudioManager#getRingerMode() returns + // RINGER_MODE_VIBRATE, with which the device should vibrate anyway. + int vibrateSetting = getIntValueFromSystem(db, Settings.System.VIBRATE_ON, + AudioManager.VIBRATE_SETTING_OFF); + boolean vibrateWhenRinging = ((vibrateSetting & 3) == AudioManager.VIBRATE_SETTING_ON); + + SQLiteStatement stmt = null; + try { + stmt = db.compileStatement("INSERT OR IGNORE INTO system(name,value)" + + " VALUES(?,?);"); + loadSetting(stmt, Settings.System.VIBRATE_WHEN_RINGING, vibrateWhenRinging ? 1 : 0); + } finally { + if (stmt != null) stmt.close(); + } + } + private void loadSettings(SQLiteDatabase db) { loadSystemSettings(db); loadSecureSettings(db); @@ -1372,7 +1413,7 @@ public class DatabaseHelper extends SQLiteOpenHelper { try { stmt = db.compileStatement("INSERT OR IGNORE INTO system(name,value)" + " VALUES(?,?);"); - + loadBooleanSetting(stmt, Settings.System.DIM_SCREEN, R.bool.def_dim_screen); loadSetting(stmt, Settings.System.STAY_ON_WHILE_PLUGGED_IN, @@ -1381,31 +1422,31 @@ public class DatabaseHelper extends SQLiteOpenHelper { ? 1 : 0); loadIntegerSetting(stmt, Settings.System.SCREEN_OFF_TIMEOUT, R.integer.def_screen_off_timeout); - + // Set default cdma emergency tone loadSetting(stmt, Settings.System.EMERGENCY_TONE, 0); - + // Set default cdma call auto retry loadSetting(stmt, Settings.System.CALL_AUTO_RETRY, 0); - + // Set default cdma DTMF type loadSetting(stmt, Settings.System.DTMF_TONE_TYPE_WHEN_DIALING, 0); - + // Set default hearing aid loadSetting(stmt, Settings.System.HEARING_AID, 0); - + // Set default tty mode loadSetting(stmt, Settings.System.TTY_MODE, 0); - + loadBooleanSetting(stmt, Settings.System.AIRPLANE_MODE_ON, R.bool.def_airplane_mode_on); - + loadStringSetting(stmt, Settings.System.AIRPLANE_MODE_RADIOS, R.string.def_airplane_mode_radios); - + loadStringSetting(stmt, Settings.System.AIRPLANE_MODE_TOGGLEABLE_RADIOS, R.string.airplane_mode_toggleable_radios); - + loadBooleanSetting(stmt, Settings.System.AUTO_TIME, R.bool.def_auto_time); // Sync time to NITZ @@ -1414,17 +1455,17 @@ public class DatabaseHelper extends SQLiteOpenHelper { loadIntegerSetting(stmt, Settings.System.SCREEN_BRIGHTNESS, R.integer.def_screen_brightness); - + loadBooleanSetting(stmt, Settings.System.SCREEN_BRIGHTNESS_MODE, R.bool.def_screen_brightness_automatic_mode); - + loadDefaultAnimationSettings(stmt); - + loadBooleanSetting(stmt, Settings.System.ACCELEROMETER_ROTATION, R.bool.def_accelerometer_rotation); - + loadDefaultHapticSettings(stmt); - + loadBooleanSetting(stmt, Settings.System.NOTIFICATION_LIGHT_PULSE, R.bool.def_notification_pulse); loadSetting(stmt, Settings.Secure.SET_INSTALL_LOCATION, 0); @@ -1433,9 +1474,6 @@ public class DatabaseHelper extends SQLiteOpenHelper { loadUISoundEffectsSettings(stmt); - loadBooleanSetting(stmt, Settings.System.VIBRATE_IN_SILENT, - R.bool.def_vibrate_in_silent); - loadIntegerSetting(stmt, Settings.System.POINTER_SPEED, R.integer.def_pointer_speed); @@ -1492,41 +1530,41 @@ public class DatabaseHelper extends SQLiteOpenHelper { try { stmt = db.compileStatement("INSERT OR IGNORE INTO secure(name,value)" + " VALUES(?,?);"); - + loadBooleanSetting(stmt, Settings.Secure.BLUETOOTH_ON, R.bool.def_bluetooth_on); - + // Data roaming default, based on build loadSetting(stmt, Settings.Secure.DATA_ROAMING, "true".equalsIgnoreCase( SystemProperties.get("ro.com.android.dataroaming", "false")) ? 1 : 0); - + loadBooleanSetting(stmt, Settings.Secure.INSTALL_NON_MARKET_APPS, R.bool.def_install_non_market_apps); - + loadStringSetting(stmt, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, R.string.def_location_providers_allowed); - + loadBooleanSetting(stmt, Settings.Secure.ASSISTED_GPS_ENABLED, R.bool.assisted_gps_enabled); - + loadIntegerSetting(stmt, Settings.Secure.NETWORK_PREFERENCE, R.integer.def_network_preference); - + loadBooleanSetting(stmt, Settings.Secure.USB_MASS_STORAGE_ENABLED, R.bool.def_usb_mass_storage_enabled); - + loadBooleanSetting(stmt, Settings.Secure.WIFI_ON, R.bool.def_wifi_on); loadBooleanSetting(stmt, Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, R.bool.def_networks_available_notification_on); - + String wifiWatchList = SystemProperties.get("ro.com.android.wifi-watchlist"); if (!TextUtils.isEmpty(wifiWatchList)) { loadSetting(stmt, Settings.Secure.WIFI_WATCHDOG_WATCH_LIST, wifiWatchList); } - + // Set the preferred network mode to 0 = Global, CDMA default int type; if (BaseCommands.getLteOnCdmaModeStatic() == Phone.LTE_ON_CDMA_TRUE) { @@ -1536,30 +1574,30 @@ public class DatabaseHelper extends SQLiteOpenHelper { RILConstants.PREFERRED_NETWORK_MODE); } loadSetting(stmt, Settings.Secure.PREFERRED_NETWORK_MODE, type); - + // Enable or disable Cell Broadcast SMS loadSetting(stmt, Settings.Secure.CDMA_CELL_BROADCAST_SMS, RILConstants.CDMA_CELL_BROADCAST_SMS_DISABLED); - + // Don't do this. The SystemServer will initialize ADB_ENABLED from a // persistent system property instead. //loadSetting(stmt, Settings.Secure.ADB_ENABLED, 0); - + // Allow mock locations default, based on build loadSetting(stmt, Settings.Secure.ALLOW_MOCK_LOCATION, "1".equals(SystemProperties.get("ro.allow.mock.location")) ? 1 : 0); - + loadSecure35Settings(stmt); - + loadBooleanSetting(stmt, Settings.Secure.MOUNT_PLAY_NOTIFICATION_SND, R.bool.def_mount_play_notification_snd); - + loadBooleanSetting(stmt, Settings.Secure.MOUNT_UMS_AUTOSTART, R.bool.def_mount_ums_autostart); - + loadBooleanSetting(stmt, Settings.Secure.MOUNT_UMS_PROMPT, R.bool.def_mount_ums_prompt); - + loadBooleanSetting(stmt, Settings.Secure.MOUNT_UMS_NOTIFY_ENABLED, R.bool.def_mount_ums_notify_enabled); diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java index 3e7d86a..18e7faa 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java @@ -43,9 +43,6 @@ public class SettingsHelper { private IContentService mContentService; private IPowerManager mPowerManager; - private boolean mSilent; - private boolean mVibrate; - public SettingsHelper(Context context) { mContext = context; mAudioManager = (AudioManager) context @@ -119,18 +116,6 @@ public class SettingsHelper { } } - private void setRingerMode() { - if (mSilent) { - mAudioManager.setRingerMode(mVibrate ? AudioManager.RINGER_MODE_VIBRATE : - AudioManager.RINGER_MODE_SILENT); - } else { - mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL); - mAudioManager.setVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER, - mVibrate ? AudioManager.VIBRATE_SETTING_ON - : AudioManager.VIBRATE_SETTING_OFF); - } - } - byte[] getLocaleData() { Configuration conf = mContext.getResources().getConfiguration(); final Locale loc = conf.locale; diff --git a/packages/SystemUI/res/drawable-hdpi/notification_panel_bg.9.png b/packages/SystemUI/res/drawable-hdpi/notification_panel_bg.9.png Binary files differnew file mode 100644 index 0000000..8a0a30f --- /dev/null +++ b/packages/SystemUI/res/drawable-hdpi/notification_panel_bg.9.png diff --git a/packages/SystemUI/res/drawable-hdpi/status_bar_close_off.9.png b/packages/SystemUI/res/drawable-hdpi/status_bar_close_off.9.png Binary files differnew file mode 100644 index 0000000..bc6462b --- /dev/null +++ b/packages/SystemUI/res/drawable-hdpi/status_bar_close_off.9.png diff --git a/packages/SystemUI/res/drawable-hdpi/status_bar_close_on.9.png b/packages/SystemUI/res/drawable-hdpi/status_bar_close_on.9.png Binary files differindex 69f3543..f4e28ae 100644 --- a/packages/SystemUI/res/drawable-hdpi/status_bar_close_on.9.png +++ b/packages/SystemUI/res/drawable-hdpi/status_bar_close_on.9.png diff --git a/packages/SystemUI/res/drawable-mdpi/notification_panel_bg.9.png b/packages/SystemUI/res/drawable-mdpi/notification_panel_bg.9.png Binary files differnew file mode 100644 index 0000000..25f15e6 --- /dev/null +++ b/packages/SystemUI/res/drawable-mdpi/notification_panel_bg.9.png diff --git a/packages/SystemUI/res/drawable-mdpi/status_bar_close_off.9.png b/packages/SystemUI/res/drawable-mdpi/status_bar_close_off.9.png Binary files differnew file mode 100644 index 0000000..4f5bba5 --- /dev/null +++ b/packages/SystemUI/res/drawable-mdpi/status_bar_close_off.9.png diff --git a/packages/SystemUI/res/drawable-mdpi/status_bar_close_on.9.png b/packages/SystemUI/res/drawable-mdpi/status_bar_close_on.9.png Binary files differindex fb30982..ef7afb8 100644 --- a/packages/SystemUI/res/drawable-mdpi/status_bar_close_on.9.png +++ b/packages/SystemUI/res/drawable-mdpi/status_bar_close_on.9.png diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/notification_panel_bg.9.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/notification_panel_bg.9.png Binary files differnew file mode 100644 index 0000000..2ff93d3 --- /dev/null +++ b/packages/SystemUI/res/drawable-sw600dp-hdpi/notification_panel_bg.9.png diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/notification_panel_bg.9.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/notification_panel_bg.9.png Binary files differnew file mode 100644 index 0000000..430f913 --- /dev/null +++ b/packages/SystemUI/res/drawable-sw600dp-mdpi/notification_panel_bg.9.png diff --git a/packages/SystemUI/res/drawable-sw600dp-xhdpi/notification_panel_bg.9.png b/packages/SystemUI/res/drawable-sw600dp-xhdpi/notification_panel_bg.9.png Binary files differnew file mode 100644 index 0000000..807241a --- /dev/null +++ b/packages/SystemUI/res/drawable-sw600dp-xhdpi/notification_panel_bg.9.png diff --git a/packages/SystemUI/res/drawable-sw720dp-hdpi/notification_panel_bg.9.png b/packages/SystemUI/res/drawable-sw720dp-hdpi/notification_panel_bg.9.png Binary files differnew file mode 100644 index 0000000..2ff93d3 --- /dev/null +++ b/packages/SystemUI/res/drawable-sw720dp-hdpi/notification_panel_bg.9.png diff --git a/packages/SystemUI/res/drawable-sw720dp-mdpi/notification_panel_bg.9.png b/packages/SystemUI/res/drawable-sw720dp-mdpi/notification_panel_bg.9.png Binary files differnew file mode 100644 index 0000000..430f913 --- /dev/null +++ b/packages/SystemUI/res/drawable-sw720dp-mdpi/notification_panel_bg.9.png diff --git a/packages/SystemUI/res/drawable-sw720dp-xhdpi/notification_panel_bg.9.png b/packages/SystemUI/res/drawable-sw720dp-xhdpi/notification_panel_bg.9.png Binary files differnew file mode 100644 index 0000000..807241a --- /dev/null +++ b/packages/SystemUI/res/drawable-sw720dp-xhdpi/notification_panel_bg.9.png diff --git a/packages/SystemUI/res/drawable-xhdpi/notification_panel_bg.9.png b/packages/SystemUI/res/drawable-xhdpi/notification_panel_bg.9.png Binary files differnew file mode 100644 index 0000000..60e7418 --- /dev/null +++ b/packages/SystemUI/res/drawable-xhdpi/notification_panel_bg.9.png diff --git a/packages/SystemUI/res/drawable-xhdpi/status_bar_close_off.9.png b/packages/SystemUI/res/drawable-xhdpi/status_bar_close_off.9.png Binary files differnew file mode 100644 index 0000000..e243e50 --- /dev/null +++ b/packages/SystemUI/res/drawable-xhdpi/status_bar_close_off.9.png diff --git a/packages/SystemUI/res/drawable-xhdpi/status_bar_close_on.9.png b/packages/SystemUI/res/drawable-xhdpi/status_bar_close_on.9.png Binary files differindex fe2c642..cdad949 100644 --- a/packages/SystemUI/res/drawable-xhdpi/status_bar_close_on.9.png +++ b/packages/SystemUI/res/drawable-xhdpi/status_bar_close_on.9.png diff --git a/packages/SystemUI/res/drawable/ic_notify_rotation.xml b/packages/SystemUI/res/drawable/ic_notify_rotation.xml index 213af80..11bc22c 100644 --- a/packages/SystemUI/res/drawable/ic_notify_rotation.xml +++ b/packages/SystemUI/res/drawable/ic_notify_rotation.xml @@ -14,14 +14,23 @@ limitations under the License. --> -<selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:state_checked="true" - android:drawable="@drawable/ic_notify_rotation_on_normal" /> - <item android:state_checked="true" android:state_pressed="true" - android:drawable="@drawable/ic_notify_rotation_on_pressed" /> - <item android:state_pressed="true" - android:drawable="@drawable/ic_notify_rotation_off_pressed" /> - <item - android:drawable="@drawable/ic_notify_rotation_off_normal" /> +<selector xmlns:android="http://schemas.android.com/apk/res/android" + android:constantSize="true"> + <item android:state_checked="true" android:state_pressed="true"> + <bitmap android:src="@drawable/ic_notify_rotation_on_pressed" + android:gravity="center" /> + </item> + <item android:state_checked="true"> + <bitmap android:src="@drawable/ic_notify_rotation_on_normal" + android:gravity="center" /> + </item> + <item android:state_pressed="true"> + <bitmap android:src="@drawable/ic_notify_rotation_off_pressed" + android:gravity="center" /> + </item> + <item> + <bitmap android:src="@drawable/ic_notify_rotation_off_normal" + android:gravity="center" /> + </item> </selector> diff --git a/packages/SystemUI/res/drawable/status_bar_close.xml b/packages/SystemUI/res/drawable/status_bar_close.xml new file mode 100644 index 0000000..2efc3c3 --- /dev/null +++ b/packages/SystemUI/res/drawable/status_bar_close.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2012 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. +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_pressed="true" + android:drawable="@drawable/status_bar_close_on" /> + <item + android:drawable="@drawable/status_bar_close_off" /> +</selector> + diff --git a/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml b/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml index fcdd56c..ec2abe0 100644 --- a/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml +++ b/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml @@ -45,7 +45,7 @@ android:fadingEdge="horizontal" android:scrollbars="none" android:fadingEdgeLength="@dimen/status_bar_recents_scroll_fading_edge_length" - android:layout_gravity="bottom|left" + android:layout_gravity="bottom|right" android:orientation="horizontal" android:clipToPadding="false" android:clipChildren="false"> diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml index 8715a99..f69aac8 100644 --- a/packages/SystemUI/res/layout/status_bar_expanded.xml +++ b/packages/SystemUI/res/layout/status_bar_expanded.xml @@ -25,25 +25,24 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" - android:background="@drawable/notification_tracking_bg" - android:paddingTop="@*android:dimen/status_bar_height" + android:background="@drawable/notification_panel_bg" + android:paddingTop="@dimen/notification_panel_padding_top" android:layout_marginLeft="@dimen/notification_panel_margin_left" > - <RelativeLayout + <LinearLayout android:layout_width="match_parent" - android:layout_height="52dp" - android:paddingTop="3dp" - android:paddingBottom="5dp" - android:paddingRight="3dp" + android:layout_height="wrap_content" + android:paddingTop="@dimen/notification_panel_header_padding_top" android:background="@drawable/notification_header_bg" + android:orientation="horizontal" + android:gravity="center_vertical" + android:baselineAligned="false" > <com.android.systemui.statusbar.policy.Clock android:id="@+id/clock" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_alignParentLeft="true" - android:layout_centerVertical="true" android:layout_marginLeft="8dp" android:singleLine="true" android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Clock" @@ -52,64 +51,41 @@ <com.android.systemui.statusbar.policy.DateView android:id="@+id/date" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_centerVertical="true" - android:layout_toRightOf="@id/clock" android:layout_marginLeft="8dp" - android:paddingLeft="8dp" + android:layout_marginRight="8dp" android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Date" /> - <!-- - <com.android.systemui.statusbar.phone.CarrierLabel - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_weight="1" - android:layout_marginTop="1dp" - android:layout_marginLeft="5dp" - android:layout_gravity="center_vertical" - android:paddingBottom="1dp" - android:paddingLeft="4dp" - android:textAppearance="?android:attr/textAppearanceLarge" - android:textColor="?android:attr/textColorSecondary" - /> - --> <com.android.systemui.statusbar.RotationToggle android:id="@+id/rotation_lock_button" android:layout_width="32dp" android:layout_height="32dp" - android:layout_centerVertical="true" - android:layout_toRightOf="@id/date" - android:layout_marginLeft="12dp" + android:layout_margin="8dp" android:button="@drawable/ic_notify_rotation" android:contentDescription="@string/accessibility_rotation_lock_off" /> <ImageView android:id="@+id/settings_button" - android:layout_width="32dp" - android:layout_height="32dp" - android:layout_marginLeft="8dp" - android:layout_centerVertical="true" - android:layout_toRightOf="@id/rotation_lock_button" + android:layout_width="48dp" + android:layout_height="48dp" + android:scaleType="center" android:src="@drawable/ic_notify_quicksettings" android:contentDescription="@string/accessibility_settings_button" /> + <Space + android:layout_width="0dp" + android:layout_height="0dp" + android:layout_weight="1" + /> + <ImageView android:id="@+id/clear_all_button" - android:layout_width="32dp" - android:layout_height="32dp" - android:layout_marginLeft="8dp" - android:layout_centerVertical="true" - android:layout_alignParentRight="true" + android:layout_width="48dp" + android:layout_height="48dp" + android:scaleType="center" android:src="@drawable/ic_notify_clear" android:contentDescription="@string/accessibility_clear_all" /> - </RelativeLayout> - - <View - android:layout_width="match_parent" - android:layout_height="2dp" - android:layout_marginTop="52dp" - android:background="@drawable/status_bar_hr" - /> + </LinearLayout> <ScrollView android:id="@+id/scroll" @@ -117,8 +93,8 @@ android:layout_height="match_parent" android:fadingEdge="none" android:overScrollMode="ifContentScrolls" - android:layout_marginTop="54dp" - android:layout_marginBottom="34dp" + android:layout_marginTop="@dimen/notification_panel_header_height" + android:layout_marginBottom="@dimen/close_handle_underlap" > <com.android.systemui.statusbar.policy.NotificationRowLayout android:id="@+id/latestItems" @@ -128,14 +104,6 @@ /> </ScrollView> - <ImageView - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:src="@drawable/title_bar_shadow" - android:layout_marginTop="54dp" - android:scaleType="fitXY" - /> - <com.android.systemui.statusbar.phone.CloseDragHandle android:id="@+id/close" android:layout_width="match_parent" android:layout_height="@dimen/close_handle_height" @@ -144,10 +112,10 @@ > <ImageView android:layout_width="match_parent" - android:layout_height="34dp" + android:layout_height="@dimen/close_handle_height" android:layout_gravity="bottom" android:scaleType="fitXY" - android:src="@drawable/status_bar_close_on" + android:src="@drawable/status_bar_close" /> </com.android.systemui.statusbar.phone.CloseDragHandle> diff --git a/packages/SystemUI/res/layout/status_bar_tracking.xml b/packages/SystemUI/res/layout/status_bar_tracking.xml deleted file mode 100644 index c1b0066..0000000 --- a/packages/SystemUI/res/layout/status_bar_tracking.xml +++ /dev/null @@ -1,60 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2008 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. ---> - -<com.android.systemui.statusbar.phone.TrackingView - xmlns:android="http://schemas.android.com/apk/res/android" - android:orientation="vertical" - android:visibility="gone" - android:focusable="true" - android:descendantFocusability="afterDescendants" - android:paddingBottom="0px" - android:paddingLeft="0px" - android:paddingRight="0px" - > - - <FrameLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_weight="1" - android:background="@drawable/notification_tracking_bg" - > - <com.android.systemui.statusbar.phone.CarrierLabel - android:textAppearance="@style/TextAppearance.StatusBar.Clock" - android:layout_height="wrap_content" - android:layout_width="match_parent" - android:layout_gravity="bottom" - android:gravity="center" - android:paddingBottom="20dp" - /> - </FrameLayout> - - <com.android.systemui.statusbar.phone.CloseDragHandle android:id="@+id/close" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="vertical" - > - <ImageView - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_gravity="bottom" - android:scaleType="fitXY" - android:src="@drawable/status_bar_close_on" - /> - - </com.android.systemui.statusbar.phone.CloseDragHandle> - -</com.android.systemui.statusbar.phone.TrackingView> diff --git a/core/java/android/net/nsd/NetworkServiceInfo.java b/packages/SystemUI/res/values-sw600dp-land/values-land-sw600dp/dimens.xml index 34d83d1..e440de1 100644 --- a/core/java/android/net/nsd/NetworkServiceInfo.java +++ b/packages/SystemUI/res/values-sw600dp-land/values-land-sw600dp/dimens.xml @@ -1,32 +1,21 @@ -/* - * Copyright (C) 2012 The Android Open Source Project +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright (c) 2012, 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 + * 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.nsd; - -/** - * Interface for a network service. - * - * {@hide} - */ -public interface NetworkServiceInfo { - - String getServiceName(); - void setServiceName(String s); - - String getServiceType(); - void setServiceType(String s); - -} +*/ +--> +<resources> + <!-- Recent Applications parameters --> + <dimen name="status_bar_recents_app_label_width">190dip</dimen> +</resources> diff --git a/packages/SystemUI/res/values-sw600dp-port/dimens.xml b/packages/SystemUI/res/values-sw600dp-port/dimens.xml new file mode 100644 index 0000000..7dc91d1 --- /dev/null +++ b/packages/SystemUI/res/values-sw600dp-port/dimens.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright (c) 2012, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ +--> +<resources> + <!-- Recent Applications parameters --> + <dimen name="status_bar_recents_app_label_width">140dip</dimen> +</resources> diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml index 50a61b1..2cb99ff 100644 --- a/packages/SystemUI/res/values-sw600dp/dimens.xml +++ b/packages/SystemUI/res/values-sw600dp/dimens.xml @@ -16,8 +16,8 @@ */ --> <resources> - <!-- The width of the notification panel window --> - <dimen name="notification_panel_width">446dp</dimen> + <!-- The width of the notification panel window: 446 + 16 + 16 (padding in the bg drawable) --> + <dimen name="notification_panel_width">478dp</dimen> <!-- Layout parameters for the notification panel --> <dimen name="notification_panel_margin_bottom">192dp</dimen> @@ -36,4 +36,10 @@ <!-- Height of search panel including navigation bar height --> <dimen name="navbar_search_panel_height">300dip</dimen> + <!-- Extra space above the clock in the panel; on this device, zero --> + <dimen name="notification_panel_header_padding_top">0dp</dimen> + + <!-- Size of application thumbnail --> + <dimen name="status_bar_recents_thumbnail_width">200dp</dimen> + <dimen name="status_bar_recents_thumbnail_height">177dp</dimen> </resources> diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index 9257195..ac2779f 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -20,11 +20,11 @@ <drawable name="notification_number_text_color">#ffffffff</drawable> <drawable name="ticker_background_color">#ff1d1d1d</drawable> <drawable name="status_bar_background">#ff000000</drawable> + <color name="notification_panel_solid_background">#ff000000</color> <drawable name="status_bar_recents_app_thumbnail_background">#88000000</drawable> <color name="status_bar_recents_app_label_color">#ffffffff</color> <drawable name="status_bar_notification_row_background_color">#ff090909</drawable> <drawable name="notification_header_bg">#FF000000</drawable> - <drawable name="notification_tracking_bg">#66000000</drawable> <color name="notification_list_shadow_top">#80000000</color> <drawable name="recents_callout_line">#99ffffff</drawable> <drawable name="notification_item_background_legacy_color">#ffaaaaaa</drawable> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 276d74b..b908188 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -125,7 +125,19 @@ <dimen name="navbar_search_panel_height">230dip</dimen> <!-- Height of the draggable handle at the bottom of the phone notification panel --> - <dimen name="close_handle_height">34dp</dimen> + <dimen name="close_handle_height">32dp</dimen> + + <!-- Amount of close_handle that will not overlap the notification list --> + <dimen name="close_handle_underlap">18dp</dimen> + + <!-- Height of the notification panel header bar --> + <dimen name="notification_panel_header_height">48dp</dimen> + + <!-- Height of the notification panel header bar --> + <dimen name="notification_panel_padding_top">@*android:dimen/status_bar_height</dimen> + + <!-- Extra space above the clock in the panel; half of (notification_panel_header_height - 32) --> + <dimen name="notification_panel_header_padding_top">0dp</dimen> <!-- Layout parameters for the notification panel --> <dimen name="notification_panel_margin_bottom">0dp</dimen> diff --git a/packages/SystemUI/src/com/android/systemui/SearchPanelView.java b/packages/SystemUI/src/com/android/systemui/SearchPanelView.java index 185ca5b..57f15a8 100644 --- a/packages/SystemUI/src/com/android/systemui/SearchPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/SearchPanelView.java @@ -146,7 +146,7 @@ public class SearchPanelView extends FrameLayout implements } } - public void show(boolean show, boolean animate) { + public void show(final boolean show, boolean animate) { if (animate) { if (mShowing != show) { mShowing = show; @@ -156,21 +156,24 @@ public class SearchPanelView extends FrameLayout implements mShowing = show; onAnimationEnd(null); } - setVisibility(show ? View.VISIBLE : View.GONE); - if (show) { - setFocusable(true); - setFocusableInTouchMode(true); - requestFocus(); - } + postDelayed(new Runnable() { + public void run() { + setVisibility(show ? View.VISIBLE : View.INVISIBLE); + if (show) { + setFocusable(true); + setFocusableInTouchMode(true); + requestFocus(); + } + } + }, show ? 0 : 100); } public void hide(boolean animate) { - if (!animate) { - setVisibility(View.GONE); - } if (mBar != null) { // This will indirectly cause show(false, ...) to get called mBar.animateCollapse(); + } else { + setVisibility(View.INVISIBLE); } } diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java index e865b9c..995ee43 100644 --- a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java @@ -91,6 +91,7 @@ public class RecentsPanelView extends FrameLayout implements OnItemClickListener private int mThumbnailWidth; private boolean mFitThumbnailToXY; private int mRecentItemLayoutId; + private boolean mFirstScreenful = true; public static interface OnRecentsPanelVisibilityChangedListener { public void onRecentsPanelVisibilityChanged(boolean visible); @@ -206,6 +207,22 @@ public class RecentsPanelView extends FrameLayout implements OnItemClickListener } } + public RecentsPanelView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public RecentsPanelView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + mContext = context; + updateValuesFromResources(); + + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RecentsPanelView, + defStyle, 0); + + mRecentItemLayoutId = a.getResourceId(R.styleable.RecentsPanelView_recentItemLayout, 0); + a.recycle(); + } + public int numItemsInOneScreenful() { if (mRecentsContainer instanceof RecentsScrollView){ RecentsScrollView scrollView @@ -297,6 +314,7 @@ public class RecentsPanelView extends FrameLayout implements OnItemClickListener mRecentTasksDirty = true; mWaitingToShow = false; mReadyToShow = false; + mRecentsNoApps.setVisibility(View.INVISIBLE); } if (animate) { if (mShowing != show) { @@ -415,21 +433,6 @@ public class RecentsPanelView extends FrameLayout implements OnItemClickListener super.setVisibility(visibility); } - public RecentsPanelView(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public RecentsPanelView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - mContext = context; - updateValuesFromResources(); - - TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RecentsPanelView, - defStyle, 0); - - mRecentItemLayoutId = a.getResourceId(R.styleable.RecentsPanelView_recentItemLayout, 0); - } - public void updateValuesFromResources() { final Resources res = mContext.getResources(); mThumbnailWidth = Math.round(res.getDimension(R.dimen.status_bar_recents_thumbnail_width)); @@ -572,7 +575,7 @@ public class RecentsPanelView extends FrameLayout implements OnItemClickListener showIfReady(); } - // additional optimization when we have sofware system buttons - start loading the recent + // additional optimization when we have software system buttons - start loading the recent // tasks on touch down @Override public boolean onTouch(View v, MotionEvent ev) { @@ -631,7 +634,6 @@ public class RecentsPanelView extends FrameLayout implements OnItemClickListener } } - boolean mFirstScreenful; public void onTasksLoaded(ArrayList<TaskDescription> tasks) { if (!mFirstScreenful && tasks.size() == 0) { return; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index f6f41b8..f5f2e28 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -106,6 +106,7 @@ public class PhoneStatusBar extends BaseStatusBar { = "com.android.internal.policy.statusbar.START"; private static final boolean ENABLE_INTRUDERS = false; + private static final boolean DIM_BEHIND_EXPANDED_PANEL = false; static final int EXPANDED_LEAVE_ALONE = -10000; static final int EXPANDED_FULL_OPEN = -10001; @@ -212,6 +213,7 @@ public class PhoneStatusBar extends BaseStatusBar { Choreographer mChoreographer; boolean mAnimating; + boolean mClosing; // only valid when mAnimating; indicates the initial acceleration float mAnimY; float mAnimVel; float mAnimAccel; @@ -311,6 +313,12 @@ public class PhoneStatusBar extends BaseStatusBar { } }); + if (!ActivityManager.isHighEndGfx(mDisplay)) { + mStatusBarWindow.setBackground(null); + mNotificationPanel.setBackgroundColor(context.getResources().getColor( + R.color.notification_panel_solid_background)); + } + if (ENABLE_INTRUDERS) { mIntruderAlertView = (IntruderAlertView) View.inflate(context, R.layout.intruder_alert, null); mIntruderAlertView.setVisibility(View.GONE); @@ -459,6 +467,9 @@ public class PhoneStatusBar extends BaseStatusBar { // .03, the item disappears entirely (as if alpha = 0) and that discontinuity looks // a bit jarring mRecentsPanel.setMinSwipeAlpha(0.03f); + if (mNavigationBarView != null) { + mNavigationBarView.getRecentsButton().setOnTouchListener(mRecentsPanel); + } } @Override @@ -474,7 +485,6 @@ public class PhoneStatusBar extends BaseStatusBar { WindowManager.LayoutParams lp = (android.view.WindowManager.LayoutParams) mNavigationBarView.getLayoutParams(); lp.flags &= ~WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; - lp.flags &= ~WindowManager.LayoutParams.FLAG_SLIPPERY; WindowManagerImpl.getDefault().updateViewLayout(mNavigationBarView, lp); } @@ -484,7 +494,6 @@ public class PhoneStatusBar extends BaseStatusBar { WindowManager.LayoutParams lp = (android.view.WindowManager.LayoutParams) mNavigationBarView.getLayoutParams(); lp.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; - lp.flags |= WindowManager.LayoutParams.FLAG_SLIPPERY; WindowManagerImpl.getDefault().updateViewLayout(mNavigationBarView, lp); } @@ -565,8 +574,7 @@ public class PhoneStatusBar extends BaseStatusBar { | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL - | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH - | WindowManager.LayoutParams.FLAG_SLIPPERY, + | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH, PixelFormat.OPAQUE); // this will allow the navbar to run in an overlay on devices that support this if (ActivityManager.isHighEndGfx(mDisplay)) { @@ -1270,14 +1278,26 @@ public class PhoneStatusBar extends BaseStatusBar { } } + void resetLastAnimTime() { + mAnimLastTimeNanos = System.nanoTime(); + if (SPEW) { + Throwable t = new Throwable(); + t.fillInStackTrace(); + Slog.d(TAG, "resetting last anim time=" + mAnimLastTimeNanos, t); + } + } + void doAnimation(long frameTimeNanos) { if (mAnimating) { - if (SPEW) Slog.d(TAG, "doAnimation"); + if (SPEW) Slog.d(TAG, "doAnimation dt=" + (frameTimeNanos - mAnimLastTimeNanos)); if (SPEW) Slog.d(TAG, "doAnimation before mAnimY=" + mAnimY); incrementAnim(frameTimeNanos); - if (SPEW) Slog.d(TAG, "doAnimation after mAnimY=" + mAnimY); + if (SPEW) { + Slog.d(TAG, "doAnimation after mAnimY=" + mAnimY); + Slog.d(TAG, "doAnimation expandedViewMax=" + getExpandedViewMaxHeight()); + } - if (mAnimY >= getExpandedViewMaxHeight()-1) { + if (mAnimY >= getExpandedViewMaxHeight()-1 && !mClosing) { if (SPEW) Slog.d(TAG, "Animation completed to expanded state."); mAnimating = false; updateExpandedViewPos(EXPANDED_FULL_OPEN); @@ -1285,14 +1305,14 @@ public class PhoneStatusBar extends BaseStatusBar { return; } - if (mAnimY == 0 && mAnimAccel == 0 && mAnimVel == 0) { + if (mAnimY == 0 && mAnimAccel == 0 && mClosing) { if (SPEW) Slog.d(TAG, "Animation completed to collapsed state."); mAnimating = false; performCollapse(); return; } - if (mAnimY < getStatusBarHeight()) { + if (mAnimY < getStatusBarHeight() && mClosing) { // Draw one more frame with the bar positioned at the top of the screen // before ending the animation so that the user sees the bar in // its final position. The call to performCollapse() causes a window @@ -1314,6 +1334,7 @@ public class PhoneStatusBar extends BaseStatusBar { mPile.setLayerType(View.LAYER_TYPE_NONE, null); mVelocityTracker.recycle(); mVelocityTracker = null; + mCloseView.setPressed(false); } void incrementAnim(long frameTimeNanos) { @@ -1330,6 +1351,9 @@ public class PhoneStatusBar extends BaseStatusBar { } void doRevealAnimation(long frameTimeNanos) { + if (SPEW) { + Slog.d(TAG, "doRevealAnimation: dt=" + (frameTimeNanos - mAnimLastTimeNanos)); + } final int h = getCloseViewHeight() + getStatusBarHeight(); if (mAnimatingReveal && mAnimating && mAnimY < h) { incrementAnim(frameTimeNanos); @@ -1349,6 +1373,8 @@ public class PhoneStatusBar extends BaseStatusBar { Slog.d(TAG, "panel: beginning to track the user's touch, y=" + y + " opening=" + opening); } + mCloseView.setPressed(true); + mTracking = true; mPile.setLayerType(View.LAYER_TYPE_HARDWARE, null); mVelocityTracker = VelocityTracker.obtain(); @@ -1359,7 +1385,7 @@ public class PhoneStatusBar extends BaseStatusBar { updateExpandedViewPos((int)mAnimY); mAnimating = true; mAnimatingReveal = true; - mAnimLastTimeNanos = System.nanoTime(); + resetLastAnimTime(); mChoreographer.removeCallbacks(Choreographer.CALLBACK_ANIMATION, mAnimationCallback, null); mChoreographer.removeCallbacks(Choreographer.CALLBACK_ANIMATION, @@ -1433,8 +1459,9 @@ public class PhoneStatusBar extends BaseStatusBar { //Slog.d(TAG, "mAnimY=" + mAnimY + " mAnimVel=" + mAnimVel // + " mAnimAccel=" + mAnimAccel); - mAnimLastTimeNanos = System.nanoTime(); + resetLastAnimTime(); mAnimating = true; + mClosing = mAnimAccel < 0; mChoreographer.removeCallbacks(Choreographer.CALLBACK_ANIMATION, mAnimationCallback, null); @@ -1474,8 +1501,8 @@ public class PhoneStatusBar extends BaseStatusBar { if (!mExpanded) { mViewDelta = statusBarSize - y; } else { -// mCloseView.getLocationOnScreen(mAbsPos)...? -// mViewDelta = mAbsPos[1] + mTrackingView.getHeight() - y; + mCloseView.getLocationOnScreen(mAbsPos); + mViewDelta = mAbsPos[1] + statusBarSize + getCloseViewHeight() - y; // XXX: not closeViewHeight, but paddingBottom from the 9patch } if ((!mExpanded && y < hitSize) || // @@ add taps outside the panel if it's not full-screen @@ -1987,11 +2014,14 @@ public class PhoneStatusBar extends BaseStatusBar { Slog.v(TAG, "updated cropView height=" + panelh + " grav=" + lp.gravity); } mNotificationPanel.setLayoutParams(lp); - // woo, special effects - final int barh = getCloseViewHeight() + getStatusBarHeight(); - final float frac = saturate((float)(panelh - barh) / (disph - barh)); - final int color = ((int)(0xB0 * Math.sin(frac * 1.57f))) << 24; - mStatusBarWindow.setBackgroundColor(color); + + if (DIM_BEHIND_EXPANDED_PANEL && ActivityManager.isHighEndGfx(mDisplay)) { + // woo, special effects + final int barh = getCloseViewHeight() + getStatusBarHeight(); + final float frac = saturate((float)(panelh - barh) / (disph - barh)); + final int color = ((int)(0xB0 * Math.sin(frac * 1.57f))) << 24; + mStatusBarWindow.setBackgroundColor(color); + } } void updateDisplaySize() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java index 5f18b5d..374226d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java @@ -122,8 +122,7 @@ public class PhoneStatusBarPolicy { action.equals(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED)) { updateBluetooth(intent); } - else if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION) || - action.equals(AudioManager.VIBRATE_SETTING_CHANGED_ACTION)) { + else if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) { updateVolume(); } else if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) { @@ -144,7 +143,6 @@ public class PhoneStatusBarPolicy { filter.addAction(Intent.ACTION_ALARM_CHANGED); filter.addAction(Intent.ACTION_SYNC_STATE_CHANGED); filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION); - filter.addAction(AudioManager.VIBRATE_SETTING_CHANGED_ACTION); filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); filter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED); filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED); @@ -238,7 +236,7 @@ public class PhoneStatusBarPolicy { final int iconId; String contentDescription = null; - if (audioManager.shouldVibrate(AudioManager.VIBRATE_TYPE_RINGER)) { + if (ringerMode == AudioManager.RINGER_MODE_VIBRATE) { iconId = R.drawable.stat_sys_ringer_vibrate; contentDescription = mContext.getString(R.string.accessibility_ringer_vibrate); } else { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/VolumeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/VolumeController.java index c9da01a..43cb85e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/VolumeController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/VolumeController.java @@ -20,6 +20,7 @@ import android.content.ContentResolver; import android.content.Context; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.Vibrator; import android.media.AudioManager; import android.provider.Settings; import android.util.Slog; @@ -36,10 +37,16 @@ public class VolumeController implements ToggleSlider.Listener { private boolean mMute; private int mVolume; + // Is there a vibrator + private final boolean mHasVibrator; public VolumeController(Context context, ToggleSlider control) { mContext = context; mControl = control; + + Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE); + mHasVibrator = vibrator == null ? false : vibrator.hasVibrator(); + mAudioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE); mMute = mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_NORMAL; @@ -54,10 +61,8 @@ public class VolumeController implements ToggleSlider.Listener { public void onChanged(ToggleSlider view, boolean tracking, boolean mute, int level) { if (!tracking) { if (mute) { - boolean vibeInSilent = (1 == Settings.System.getInt(mContext.getContentResolver(), - Settings.System.VIBRATE_IN_SILENT, 1)); mAudioManager.setRingerMode( - vibeInSilent ? AudioManager.RINGER_MODE_VIBRATE + mHasVibrator ? AudioManager.RINGER_MODE_VIBRATE : AudioManager.RINGER_MODE_SILENT); } else { mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java index a394596..b0830ee 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java @@ -704,7 +704,6 @@ public class TabletStatusBar extends BaseStatusBar implements WindowManager.LayoutParams lp = (android.view.WindowManager.LayoutParams) mStatusBarView.getLayoutParams(); lp.flags &= ~WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; - lp.flags &= ~WindowManager.LayoutParams.FLAG_SLIPPERY; WindowManagerImpl.getDefault().updateViewLayout(mStatusBarView, lp); } @@ -714,7 +713,6 @@ public class TabletStatusBar extends BaseStatusBar implements WindowManager.LayoutParams lp = (android.view.WindowManager.LayoutParams) mStatusBarView.getLayoutParams(); lp.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; - lp.flags |= WindowManager.LayoutParams.FLAG_SLIPPERY; WindowManagerImpl.getDefault().updateViewLayout(mStatusBarView, lp); } diff --git a/policy/src/com/android/internal/policy/impl/FaceUnlock.java b/policy/src/com/android/internal/policy/impl/FaceUnlock.java index 6e09b7f..6590fb3 100644 --- a/policy/src/com/android/internal/policy/impl/FaceUnlock.java +++ b/policy/src/com/android/internal/policy/impl/FaceUnlock.java @@ -28,6 +28,7 @@ import android.content.Intent; import android.content.ServiceConnection; import android.os.Handler; import android.os.IBinder; +import android.os.Looper; import android.os.Message; import android.os.RemoteException; import android.telephony.TelephonyManager; @@ -112,10 +113,14 @@ public class FaceUnlock implements BiometricSensorUnlock, Handler.Callback { /** * Sets the Face Unlock view to visible, hiding it after the specified amount of time. If - * timeoutMillis is 0, no hide is performed. + * timeoutMillis is 0, no hide is performed. Called on the UI thread. */ public void show(long timeoutMillis) { if (DEBUG) Log.d(TAG, "show()"); + if (mHandler.getLooper() != Looper.myLooper()) { + Log.e(TAG, "show() called off of the UI thread"); + } + removeDisplayMessages(); if (mFaceUnlockView != null) { mFaceUnlockView.setVisibility(View.VISIBLE); @@ -138,9 +143,14 @@ public class FaceUnlock implements BiometricSensorUnlock, Handler.Callback { /** * Binds to the Face Unlock service. Face Unlock will be started when the bind completes. The * Face Unlock view is displayed to hide the backup lock while the service is starting up. + * Called on the UI thread. */ public boolean start() { if (DEBUG) Log.d(TAG, "start()"); + if (mHandler.getLooper() != Looper.myLooper()) { + Log.e(TAG, "start() called off of the UI thread"); + } + if (mIsRunning) { Log.w(TAG, "start() called when already running"); } @@ -170,10 +180,14 @@ public class FaceUnlock implements BiometricSensorUnlock, Handler.Callback { } /** - * Stops Face Unlock and unbinds from the service. + * Stops Face Unlock and unbinds from the service. Called on the UI thread. */ public boolean stop() { if (DEBUG) Log.d(TAG, "stop()"); + if (mHandler.getLooper() != Looper.myLooper()) { + Log.e(TAG, "stop() called off of the UI thread"); + } + boolean mWasRunning = mIsRunning; stopUi(); diff --git a/policy/src/com/android/internal/policy/impl/LockScreen.java b/policy/src/com/android/internal/policy/impl/LockScreen.java index 8b0d858..c7a30e2 100644 --- a/policy/src/com/android/internal/policy/impl/LockScreen.java +++ b/policy/src/com/android/internal/policy/impl/LockScreen.java @@ -34,6 +34,7 @@ import android.content.Context; import android.content.Intent; import android.content.res.Configuration; import android.content.res.Resources; +import android.os.Vibrator; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; @@ -82,6 +83,8 @@ class LockScreen extends LinearLayout implements KeyguardScreen { private boolean mCameraDisabled; private boolean mSearchDisabled; private SearchManager mSearchManager; + // Is there a vibrator + private final boolean mHasVibrator; InfoCallbackImpl mInfoCallback = new InfoCallbackImpl() { @@ -385,11 +388,7 @@ class LockScreen extends LinearLayout implements KeyguardScreen { // toggle silent mode mSilentMode = !mSilentMode; if (mSilentMode) { - final boolean vibe = (Settings.System.getInt( - mContext.getContentResolver(), - Settings.System.VIBRATE_IN_SILENT, 1) == 1); - - mAudioManager.setRingerMode(vibe + mAudioManager.setRingerMode(mHasVibrator ? AudioManager.RINGER_MODE_VIBRATE : AudioManager.RINGER_MODE_SILENT); } else { @@ -451,6 +450,8 @@ class LockScreen extends LinearLayout implements KeyguardScreen { setFocusableInTouchMode(true); setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS); + Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE); + mHasVibrator = vibrator == null ? false : vibrator.hasVibrator(); mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); mSilentMode = isSilentMode(); mUnlockWidget = findViewById(R.id.unlock_widget); diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java index 4b91422..cc7050a 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java @@ -2098,7 +2098,6 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { if (widthMode == AT_MOST) { final TypedValue tvw = isPortrait ? mFixedWidthMinor : mFixedWidthMajor; if (tvw != null && tvw.type != TypedValue.TYPE_NULL) { - fixedWidth = true; final int w; if (tvw.type == TypedValue.TYPE_DIMENSION) { w = (int) tvw.getDimension(metrics); @@ -2112,6 +2111,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { final int widthSize = MeasureSpec.getSize(widthMeasureSpec); widthMeasureSpec = MeasureSpec.makeMeasureSpec( Math.min(w, widthSize), EXACTLY); + fixedWidth = true; } } } diff --git a/services/input/EventHub.cpp b/services/input/EventHub.cpp index f80ac18..50bfee6 100644 --- a/services/input/EventHub.cpp +++ b/services/input/EventHub.cpp @@ -16,7 +16,7 @@ #define LOG_TAG "EventHub" -// #define LOG_NDEBUG 0 +#define LOG_NDEBUG 0 #include "EventHub.h" @@ -767,11 +767,7 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz size_t count = size_t(readSize) / sizeof(struct input_event); for (size_t i = 0; i < count; i++) { const struct input_event& iev = readBuffer[i]; - ALOGV("%s got: t0=%d, t1=%d, type=%d, code=%d, value=%d", - device->path.string(), - (int) iev.time.tv_sec, (int) iev.time.tv_usec, - iev.type, iev.code, iev.value); - + nsecs_t delta = 0; #ifdef HAVE_POSIX_CLOCKS // Use the time specified in the event instead of the current time // so that downstream code can get more accurate estimates of @@ -786,10 +782,23 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz // system call that also queries ktime_get_ts(). event->when = nsecs_t(iev.time.tv_sec) * 1000000000LL + nsecs_t(iev.time.tv_usec) * 1000LL; - ALOGV("event time %lld, now %lld", event->when, now); + delta = now - event->when; + + // Only log verbose if events are older that 1ms + if (delta > 1 * 1000000LL) { + ALOGV("event time %lld, now %lld, delta %lldus", event->when, now, delta / 1000LL); + } #else event->when = now; #endif + if (delta > 1 * 1000000LL) { + ALOGV("%s got: t0=%d, t1=%d, type=%d, code=%d, value=%d", + device->path.string(), + (int) iev.time.tv_sec, (int) iev.time.tv_usec, + iev.type, iev.code, iev.value); + } + + event->deviceId = deviceId; event->type = iev.type; event->code = iev.code; diff --git a/services/input/InputDispatcher.cpp b/services/input/InputDispatcher.cpp index dad4ef4..ada9d9e 100644 --- a/services/input/InputDispatcher.cpp +++ b/services/input/InputDispatcher.cpp @@ -185,7 +185,7 @@ InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& polic mPolicy(policy), mPendingEvent(NULL), mAppSwitchSawKeyDown(false), mAppSwitchDueTime(LONG_LONG_MAX), mNextUnblockedEvent(NULL), - mDispatchEnabled(true), mDispatchFrozen(false), mInputFilterEnabled(false), + mDispatchEnabled(false), mDispatchFrozen(false), mInputFilterEnabled(false), mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) { mLooper = new Looper(false); diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java index 8e3b825..a49ccf7 100644 --- a/services/java/com/android/server/InputMethodManagerService.java +++ b/services/java/com/android/server/InputMethodManagerService.java @@ -1142,6 +1142,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (mCurToken != null) { try { if (DEBUG) Slog.v(TAG, "Removing window token: " + mCurToken); + if ((mImeWindowVis & InputMethodService.IME_ACTIVE) != 0) { + // The current IME is shown. Hence an IME switch (transition) is happening. + mWindowManagerService.saveLastInputMethodWindowForTransition(); + } mIWindowManager.removeWindowToken(mCurToken); } catch (RemoteException e) { } @@ -2410,17 +2414,63 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } - private static class ImeSubtypeListItem { + private static class ImeSubtypeListItem implements Comparable<ImeSubtypeListItem> { public final CharSequence mImeName; public final CharSequence mSubtypeName; public final InputMethodInfo mImi; public final int mSubtypeId; + private final boolean mIsSystemLocale; + private final boolean mIsSystemLanguage; + public ImeSubtypeListItem(CharSequence imeName, CharSequence subtypeName, - InputMethodInfo imi, int subtypeId) { + InputMethodInfo imi, int subtypeId, String subtypeLocale, String systemLocale) { mImeName = imeName; mSubtypeName = subtypeName; mImi = imi; mSubtypeId = subtypeId; + if (TextUtils.isEmpty(subtypeLocale)) { + mIsSystemLocale = false; + mIsSystemLanguage = false; + } else { + mIsSystemLocale = subtypeLocale.equals(systemLocale); + mIsSystemLanguage = mIsSystemLocale + || subtypeLocale.startsWith(systemLocale.substring(0, 2)); + } + } + + @Override + public int compareTo(ImeSubtypeListItem other) { + if (TextUtils.isEmpty(mImeName)) { + return 1; + } + if (TextUtils.isEmpty(other.mImeName)) { + return -1; + } + if (!TextUtils.equals(mImeName, other.mImeName)) { + return mImeName.toString().compareTo(other.mImeName.toString()); + } + if (TextUtils.equals(mSubtypeName, other.mSubtypeName)) { + return 0; + } + if (mIsSystemLocale) { + return -1; + } + if (other.mIsSystemLocale) { + return 1; + } + if (mIsSystemLanguage) { + return -1; + } + if (other.mIsSystemLanguage) { + return 1; + } + if (TextUtils.isEmpty(mSubtypeName)) { + return 1; + } + if (TextUtils.isEmpty(other.mSubtypeName)) { + return -1; + } + return mSubtypeName.toString().compareTo(other.mSubtypeName.toString()); } } @@ -2619,7 +2669,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub return getSubtypeIdFromHashCode(imi, subtypeId); } - private int getSubtypeIdFromHashCode(InputMethodInfo imi, int subtypeHashCode) { + private static boolean isValidSubtypeId(InputMethodInfo imi, int subtypeHashCode) { + return getSubtypeIdFromHashCode(imi, subtypeHashCode) != NOT_A_SUBTYPE_ID; + } + + private static int getSubtypeIdFromHashCode(InputMethodInfo imi, int subtypeHashCode) { if (imi != null) { final int subtypeCount = imi.getSubtypeCount(); for (int i = 0; i < subtypeCount; ++i) { @@ -2844,6 +2898,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub */ @Override public InputMethodSubtype getCurrentInputMethodSubtype() { + if (mCurMethodId == null) { + return null; + } boolean subtypeIsSelected = false; try { subtypeIsSelected = Settings.Secure.getInt(mContext.getContentResolver(), @@ -2851,36 +2908,35 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } catch (SettingNotFoundException e) { } synchronized (mMethodMap) { - if (!subtypeIsSelected || mCurrentSubtype == null) { - String lastInputMethodId = Settings.Secure.getString( - mContext.getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD); - int subtypeId = getSelectedInputMethodSubtypeId(lastInputMethodId); + final InputMethodInfo imi = mMethodMap.get(mCurMethodId); + if (imi == null || imi.getSubtypeCount() == 0) { + return null; + } + if (!subtypeIsSelected || mCurrentSubtype == null + || !isValidSubtypeId(imi, mCurrentSubtype.hashCode())) { + int subtypeId = getSelectedInputMethodSubtypeId(mCurMethodId); if (subtypeId == NOT_A_SUBTYPE_ID) { - InputMethodInfo imi = mMethodMap.get(lastInputMethodId); - if (imi != null) { - // If there are no selected subtypes, the framework will try to find - // the most applicable subtype from explicitly or implicitly enabled - // subtypes. - List<InputMethodSubtype> explicitlyOrImplicitlyEnabledSubtypes = - getEnabledInputMethodSubtypeList(imi, true); - // If there is only one explicitly or implicitly enabled subtype, - // just returns it. - if (explicitlyOrImplicitlyEnabledSubtypes.size() == 1) { - mCurrentSubtype = explicitlyOrImplicitlyEnabledSubtypes.get(0); - } else if (explicitlyOrImplicitlyEnabledSubtypes.size() > 1) { + // If there are no selected subtypes, the framework will try to find + // the most applicable subtype from explicitly or implicitly enabled + // subtypes. + List<InputMethodSubtype> explicitlyOrImplicitlyEnabledSubtypes = + getEnabledInputMethodSubtypeList(imi, true); + // If there is only one explicitly or implicitly enabled subtype, + // just returns it. + if (explicitlyOrImplicitlyEnabledSubtypes.size() == 1) { + mCurrentSubtype = explicitlyOrImplicitlyEnabledSubtypes.get(0); + } else if (explicitlyOrImplicitlyEnabledSubtypes.size() > 1) { + mCurrentSubtype = findLastResortApplicableSubtypeLocked( + mRes, explicitlyOrImplicitlyEnabledSubtypes, + SUBTYPE_MODE_KEYBOARD, null, true); + if (mCurrentSubtype == null) { mCurrentSubtype = findLastResortApplicableSubtypeLocked( - mRes, explicitlyOrImplicitlyEnabledSubtypes, - SUBTYPE_MODE_KEYBOARD, null, true); - if (mCurrentSubtype == null) { - mCurrentSubtype = findLastResortApplicableSubtypeLocked( - mRes, explicitlyOrImplicitlyEnabledSubtypes, null, null, - true); - } + mRes, explicitlyOrImplicitlyEnabledSubtypes, null, null, + true); } } } else { - mCurrentSubtype = - getSubtypes(mMethodMap.get(lastInputMethodId)).get(subtypeId); + mCurrentSubtype = getSubtypes(imi).get(subtypeId); } } return mCurrentSubtype; @@ -2946,10 +3002,13 @@ public class InputMethodManagerService extends IInputMethodManager.Stub private final Context mContext; private final PackageManager mPm; private final InputMethodManagerService mImms; + private final String mSystemLocaleStr; public InputMethodAndSubtypeListManager(Context context, InputMethodManagerService imms) { mContext = context; mPm = context.getPackageManager(); mImms = imms; + mSystemLocaleStr = + imms.mLastSystemLocale != null ? imms.mLastSystemLocale.toString() : ""; } private final TreeMap<InputMethodInfo, List<InputMethodSubtype>> mSortedImmis = @@ -2979,7 +3038,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } final int N = imList.size(); final int currentSubtypeId = subtype != null - ? mImms.getSubtypeIdFromHashCode(imi, subtype.hashCode()) + ? getSubtypeIdFromHashCode(imi, subtype.hashCode()) : NOT_A_SUBTYPE_ID; for (int i = 0; i < N; ++i) { final ImeSubtypeListItem isli = imList.get(i); @@ -3037,7 +3096,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub subtype.overridesImplicitlyEnabledSubtype() ? null : subtype.getDisplayName(mContext, imi.getPackageName(), imi.getServiceInfo().applicationInfo); - imList.add(new ImeSubtypeListItem(imeLabel, subtypeLabel, imi, j)); + imList.add(new ImeSubtypeListItem(imeLabel, subtypeLabel, imi, j, + subtype.getLocale(), mSystemLocaleStr)); // Removing this subtype from enabledSubtypeSet because we no longer // need to add an entry of this subtype to imList to avoid duplicated @@ -3046,9 +3106,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } } else { - imList.add(new ImeSubtypeListItem(imeLabel, null, imi, NOT_A_SUBTYPE_ID)); + imList.add(new ImeSubtypeListItem(imeLabel, null, imi, NOT_A_SUBTYPE_ID, + null, mSystemLocaleStr)); } } + Collections.sort(imList); return imList; } } @@ -3356,10 +3418,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub for (Pair<String, ArrayList<String>> enabledIme: enabledImes) { if (enabledIme.first.equals(imeId)) { final ArrayList<String> explicitlyEnabledSubtypes = enabledIme.second; + final InputMethodInfo imi = mMethodMap.get(imeId); if (explicitlyEnabledSubtypes.size() == 0) { // If there are no explicitly enabled subtypes, applicable subtypes are // enabled implicitly. - InputMethodInfo imi = mMethodMap.get(imeId); // If IME is enabled and no subtypes are enabled, applicable subtypes // are enabled implicitly, so needs to treat them to be enabled. if (imi != null && imi.getSubtypeCount() > 0) { @@ -3379,7 +3441,17 @@ public class InputMethodManagerService extends IInputMethodManager.Stub for (String s: explicitlyEnabledSubtypes) { if (s.equals(subtypeHashCode)) { // If both imeId and subtypeId are enabled, return subtypeId. - return s; + try { + final int hashCode = Integer.valueOf(subtypeHashCode); + // Check whether the subtype id is valid or not + if (isValidSubtypeId(imi, hashCode)) { + return s; + } else { + return NOT_A_SUBTYPE_ID_STR; + } + } catch (NumberFormatException e) { + return NOT_A_SUBTYPE_ID_STR; + } } } } diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java index 663a031..52ba665 100755 --- a/services/java/com/android/server/NotificationManagerService.java +++ b/services/java/com/android/server/NotificationManagerService.java @@ -1046,7 +1046,7 @@ public class NotificationManagerService extends INotificationManager.Stub final boolean useDefaultVibrate = (notification.defaults & Notification.DEFAULT_VIBRATE) != 0; if ((useDefaultVibrate || notification.vibrate != null) - && audioManager.shouldVibrate(AudioManager.VIBRATE_TYPE_NOTIFICATION)) { + && !(audioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT)) { mVibrateNotification = r; mVibrator.vibrate(useDefaultVibrate ? DEFAULT_VIBRATE_PATTERN diff --git a/services/java/com/android/server/NsdService.java b/services/java/com/android/server/NsdService.java index f33bf8b..cc8e6a4 100644 --- a/services/java/com/android/server/NsdService.java +++ b/services/java/com/android/server/NsdService.java @@ -20,7 +20,7 @@ import android.content.Context; import android.content.ContentResolver; import android.content.Intent; import android.content.pm.PackageManager; -import android.net.nsd.DnsSdServiceInfo; +import android.net.nsd.NsdServiceInfo; import android.net.nsd.DnsSdTxtRecord; import android.net.nsd.INsdManager; import android.net.nsd.NsdManager; @@ -32,6 +32,7 @@ import android.os.Messenger; import android.os.IBinder; import android.provider.Settings; import android.util.Slog; +import android.util.SparseArray; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -72,13 +73,16 @@ public class NsdService extends INsdManager.Stub { */ private HashMap<Messenger, ClientInfo> mClients = new HashMap<Messenger, ClientInfo>(); + /* A map from unique id to client info */ + private SparseArray<ClientInfo> mIdToClientInfoMap= new SparseArray<ClientInfo>(); + private AsyncChannel mReplyChannel = new AsyncChannel(); private int INVALID_ID = 0; private int mUniqueId = 1; private static final int BASE = Protocol.BASE_NSD_MANAGER; - private static final int CMD_TO_STRING_COUNT = NsdManager.STOP_RESOLVE - BASE + 1; + private static final int CMD_TO_STRING_COUNT = NsdManager.RESOLVE_SERVICE - BASE + 1; private static String[] sCmdToString = new String[CMD_TO_STRING_COUNT]; static { @@ -87,7 +91,6 @@ public class NsdService extends INsdManager.Stub { sCmdToString[NsdManager.REGISTER_SERVICE - BASE] = "REGISTER"; sCmdToString[NsdManager.UNREGISTER_SERVICE - BASE] = "UNREGISTER"; sCmdToString[NsdManager.RESOLVE_SERVICE - BASE] = "RESOLVE"; - sCmdToString[NsdManager.STOP_RESOLVE - BASE] = "STOP-RESOLVE"; } private static String cmdToString(int cmd) { @@ -101,9 +104,9 @@ public class NsdService extends INsdManager.Stub { private class NsdStateMachine extends StateMachine { - private DefaultState mDefaultState = new DefaultState(); - private DisabledState mDisabledState = new DisabledState(); - private EnabledState mEnabledState = new EnabledState(); + private final DefaultState mDefaultState = new DefaultState(); + private final DisabledState mDisabledState = new DisabledState(); + private final EnabledState mEnabledState = new EnabledState(); @Override protected String getMessageInfo(Message msg) { @@ -151,29 +154,26 @@ public class NsdService extends INsdManager.Stub { ac.connect(mContext, getHandler(), msg.replyTo); break; case NsdManager.DISCOVER_SERVICES: - mReplyChannel.replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED, - NsdManager.BUSY); + replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED, + NsdManager.FAILURE_INTERNAL_ERROR); break; case NsdManager.STOP_DISCOVERY: - mReplyChannel.replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED, - NsdManager.ERROR); + replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED, + NsdManager.FAILURE_INTERNAL_ERROR); break; case NsdManager.REGISTER_SERVICE: - mReplyChannel.replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED, - NsdManager.ERROR); + replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED, + NsdManager.FAILURE_INTERNAL_ERROR); break; case NsdManager.UNREGISTER_SERVICE: - mReplyChannel.replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_FAILED, - NsdManager.ERROR); + replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_FAILED, + NsdManager.FAILURE_INTERNAL_ERROR); break; case NsdManager.RESOLVE_SERVICE: - mReplyChannel.replyToMessage(msg, NsdManager.RESOLVE_SERVICE_FAILED, - NsdManager.ERROR); - break; - case NsdManager.STOP_RESOLVE: - mReplyChannel.replyToMessage(msg, NsdManager.STOP_RESOLVE_FAILED, - NsdManager.ERROR); + replyToMessage(msg, NsdManager.RESOLVE_SERVICE_FAILED, + NsdManager.FAILURE_INTERNAL_ERROR); break; + case NsdManager.NATIVE_DAEMON_EVENT: default: Slog.e(TAG, "Unhandled " + msg); return NOT_HANDLED; @@ -217,11 +217,30 @@ public class NsdService extends INsdManager.Stub { } } + private boolean requestLimitReached(ClientInfo clientInfo) { + if (clientInfo.mClientIds.size() >= ClientInfo.MAX_LIMIT) { + if (DBG) Slog.d(TAG, "Exceeded max outstanding requests " + clientInfo); + return true; + } + return false; + } + + private void storeRequestMap(int clientId, int globalId, ClientInfo clientInfo) { + clientInfo.mClientIds.put(clientId, globalId); + mIdToClientInfoMap.put(globalId, clientInfo); + } + + private void removeRequestMap(int clientId, int globalId, ClientInfo clientInfo) { + clientInfo.mClientIds.remove(clientId); + mIdToClientInfoMap.remove(globalId); + } + @Override public boolean processMessage(Message msg) { ClientInfo clientInfo; - DnsSdServiceInfo servInfo; + NsdServiceInfo servInfo; boolean result = HANDLED; + int id; switch (msg.what) { case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: //First client @@ -244,111 +263,112 @@ public class NsdService extends INsdManager.Stub { break; case NsdManager.DISCOVER_SERVICES: if (DBG) Slog.d(TAG, "Discover services"); - servInfo = (DnsSdServiceInfo) msg.obj; + servInfo = (NsdServiceInfo) msg.obj; clientInfo = mClients.get(msg.replyTo); - if (clientInfo.mDiscoveryId != INVALID_ID) { - //discovery already in progress - if (DBG) Slog.d(TAG, "discovery in progress"); - mReplyChannel.replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED, - NsdManager.ALREADY_ACTIVE); + + if (requestLimitReached(clientInfo)) { + replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED, + NsdManager.FAILURE_MAX_LIMIT); break; } - clientInfo.mDiscoveryId = getUniqueId(); - if (discoverServices(clientInfo.mDiscoveryId, servInfo.getServiceType())) { - mReplyChannel.replyToMessage(msg, NsdManager.DISCOVER_SERVICES_STARTED); + + id = getUniqueId(); + if (discoverServices(id, servInfo.getServiceType())) { + if (DBG) { + Slog.d(TAG, "Discover " + msg.arg2 + " " + id + + servInfo.getServiceType()); + } + storeRequestMap(msg.arg2, id, clientInfo); + replyToMessage(msg, NsdManager.DISCOVER_SERVICES_STARTED, servInfo); } else { - mReplyChannel.replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED, - NsdManager.ERROR); - clientInfo.mDiscoveryId = INVALID_ID; + stopServiceDiscovery(id); + replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED, + NsdManager.FAILURE_INTERNAL_ERROR); } break; case NsdManager.STOP_DISCOVERY: if (DBG) Slog.d(TAG, "Stop service discovery"); clientInfo = mClients.get(msg.replyTo); - if (clientInfo.mDiscoveryId == INVALID_ID) { - //already stopped - if (DBG) Slog.d(TAG, "discovery already stopped"); - mReplyChannel.replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED, - NsdManager.ALREADY_ACTIVE); + + try { + id = clientInfo.mClientIds.get(msg.arg2).intValue(); + } catch (NullPointerException e) { + replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED, + NsdManager.FAILURE_INTERNAL_ERROR); break; } - if (stopServiceDiscovery(clientInfo.mDiscoveryId)) { - clientInfo.mDiscoveryId = INVALID_ID; - mReplyChannel.replyToMessage(msg, NsdManager.STOP_DISCOVERY_SUCCEEDED); + removeRequestMap(msg.arg2, id, clientInfo); + if (stopServiceDiscovery(id)) { + replyToMessage(msg, NsdManager.STOP_DISCOVERY_SUCCEEDED); } else { - mReplyChannel.replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED, - NsdManager.ERROR); + replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED, + NsdManager.FAILURE_INTERNAL_ERROR); } break; case NsdManager.REGISTER_SERVICE: if (DBG) Slog.d(TAG, "Register service"); clientInfo = mClients.get(msg.replyTo); - if (clientInfo.mRegisteredIds.size() >= ClientInfo.MAX_REG) { - if (DBG) Slog.d(TAG, "register service exceeds limit"); - mReplyChannel.replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED, - NsdManager.MAX_REGS_REACHED); + if (requestLimitReached(clientInfo)) { + replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED, + NsdManager.FAILURE_MAX_LIMIT); + break; } - int id = getUniqueId(); - if (registerService(id, (DnsSdServiceInfo) msg.obj)) { - clientInfo.mRegisteredIds.add(id); + id = getUniqueId(); + if (registerService(id, (NsdServiceInfo) msg.obj)) { + if (DBG) Slog.d(TAG, "Register " + msg.arg2 + " " + id); + storeRequestMap(msg.arg2, id, clientInfo); + // Return success after mDns reports success } else { - mReplyChannel.replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED, - NsdManager.ERROR); + unregisterService(id); + replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED, + NsdManager.FAILURE_INTERNAL_ERROR); } break; case NsdManager.UNREGISTER_SERVICE: if (DBG) Slog.d(TAG, "unregister service"); clientInfo = mClients.get(msg.replyTo); - int regId = msg.arg1; - if (clientInfo.mRegisteredIds.remove(new Integer(regId)) && - unregisterService(regId)) { - mReplyChannel.replyToMessage(msg, - NsdManager.UNREGISTER_SERVICE_SUCCEEDED); + try { + id = clientInfo.mClientIds.get(msg.arg2).intValue(); + } catch (NullPointerException e) { + replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_FAILED, + NsdManager.FAILURE_INTERNAL_ERROR); + break; + } + removeRequestMap(msg.arg2, id, clientInfo); + if (unregisterService(id)) { + replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_SUCCEEDED); } else { - mReplyChannel.replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_FAILED, - NsdManager.ERROR); + replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_FAILED, + NsdManager.FAILURE_INTERNAL_ERROR); } break; - case NsdManager.UPDATE_SERVICE: - if (DBG) Slog.d(TAG, "Update service"); - //TODO: implement - mReplyChannel.replyToMessage(msg, NsdManager.UPDATE_SERVICE_FAILED); - break; case NsdManager.RESOLVE_SERVICE: if (DBG) Slog.d(TAG, "Resolve service"); - servInfo = (DnsSdServiceInfo) msg.obj; + servInfo = (NsdServiceInfo) msg.obj; clientInfo = mClients.get(msg.replyTo); - if (clientInfo.mResolveId != INVALID_ID) { - //first cancel existing resolve - stopResolveService(clientInfo.mResolveId); - } - clientInfo.mResolveId = getUniqueId(); - if (!resolveService(clientInfo.mResolveId, servInfo)) { - mReplyChannel.replyToMessage(msg, NsdManager.RESOLVE_SERVICE_FAILED, - NsdManager.ERROR); - clientInfo.mResolveId = INVALID_ID; - } - break; - case NsdManager.STOP_RESOLVE: - if (DBG) Slog.d(TAG, "Stop resolve"); - clientInfo = mClients.get(msg.replyTo); - if (clientInfo.mResolveId == INVALID_ID) { - //already stopped - if (DBG) Slog.d(TAG, "resolve already stopped"); - mReplyChannel.replyToMessage(msg, NsdManager.STOP_RESOLVE_FAILED, - NsdManager.ALREADY_ACTIVE); + + if (clientInfo.mResolvedService != null) { + replyToMessage(msg, NsdManager.RESOLVE_SERVICE_FAILED, + NsdManager.FAILURE_ALREADY_ACTIVE); break; } - if (stopResolveService(clientInfo.mResolveId)) { - clientInfo.mResolveId = INVALID_ID; - mReplyChannel.replyToMessage(msg, NsdManager.STOP_RESOLVE_SUCCEEDED); + + id = getUniqueId(); + if (resolveService(id, servInfo)) { + clientInfo.mResolvedService = new NsdServiceInfo(); + storeRequestMap(msg.arg2, id, clientInfo); } else { - mReplyChannel.replyToMessage(msg, NsdManager.STOP_RESOLVE_FAILED, - NsdManager.ERROR); + replyToMessage(msg, NsdManager.RESOLVE_SERVICE_FAILED, + NsdManager.FAILURE_INTERNAL_ERROR); } break; + case NsdManager.NATIVE_DAEMON_EVENT: + NativeEvent event = (NativeEvent) msg.obj; + handleNativeEvent(event.code, event.raw, + NativeDaemonEvent.unescapeArgs(event.raw)); + break; default: result = NOT_HANDLED; break; @@ -439,121 +459,144 @@ public class NsdService extends INsdManager.Stub { public static final int SERVICE_GET_ADDR_SUCCESS = 612; } + private class NativeEvent { + int code; + String raw; + + NativeEvent(int code, String raw) { + this.code = code; + this.raw = raw; + } + } + class NativeCallbackReceiver implements INativeDaemonConnectorCallbacks { public void onDaemonConnected() { mNativeDaemonConnected.countDown(); } public boolean onEvent(int code, String raw, String[] cooked) { - ClientInfo clientInfo; - DnsSdServiceInfo servInfo; - int id = Integer.parseInt(cooked[1]); - switch (code) { - case NativeResponseCode.SERVICE_FOUND: - /* NNN uniqueId serviceName regType domain */ - if (DBG) Slog.d(TAG, "SERVICE_FOUND Raw: " + raw); - clientInfo = getClientByDiscovery(id); - if (clientInfo == null) break; - - servInfo = new DnsSdServiceInfo(cooked[2], cooked[3], null); - clientInfo.mChannel.sendMessage(NsdManager.SERVICE_FOUND, servInfo); - break; - case NativeResponseCode.SERVICE_LOST: - /* NNN uniqueId serviceName regType domain */ - if (DBG) Slog.d(TAG, "SERVICE_LOST Raw: " + raw); - clientInfo = getClientByDiscovery(id); - if (clientInfo == null) break; - - servInfo = new DnsSdServiceInfo(cooked[2], cooked[3], null); - clientInfo.mChannel.sendMessage(NsdManager.SERVICE_LOST, servInfo); - break; - case NativeResponseCode.SERVICE_DISCOVERY_FAILED: - /* NNN uniqueId errorCode */ - if (DBG) Slog.d(TAG, "SERVICE_DISC_FAILED Raw: " + raw); - clientInfo = getClientByDiscovery(id); - if (clientInfo == null) break; - - clientInfo.mChannel.sendMessage(NsdManager.DISCOVER_SERVICES_FAILED, - NsdManager.ERROR); - break; - case NativeResponseCode.SERVICE_REGISTERED: - /* NNN regId serviceName regType */ - if (DBG) Slog.d(TAG, "SERVICE_REGISTERED Raw: " + raw); - clientInfo = getClientByRegistration(id); - if (clientInfo == null) break; - - servInfo = new DnsSdServiceInfo(cooked[2], null, null); - clientInfo.mChannel.sendMessage(NsdManager.REGISTER_SERVICE_SUCCEEDED, - id, 0, servInfo); - break; - case NativeResponseCode.SERVICE_REGISTRATION_FAILED: - /* NNN regId errorCode */ - if (DBG) Slog.d(TAG, "SERVICE_REGISTER_FAILED Raw: " + raw); - clientInfo = getClientByRegistration(id); - if (clientInfo == null) break; - - clientInfo.mChannel.sendMessage(NsdManager.REGISTER_SERVICE_FAILED, - NsdManager.ERROR); - break; - case NativeResponseCode.SERVICE_UPDATED: - /* NNN regId */ - break; - case NativeResponseCode.SERVICE_UPDATE_FAILED: - /* NNN regId errorCode */ - break; - case NativeResponseCode.SERVICE_RESOLVED: - /* NNN resolveId fullName hostName port txtlen txtdata */ - if (DBG) Slog.d(TAG, "SERVICE_RESOLVED Raw: " + raw); - clientInfo = getClientByResolve(id); - if (clientInfo == null) break; - - int index = cooked[2].indexOf("."); - if (index == -1) { - Slog.e(TAG, "Invalid service found " + raw); - break; - } - String name = cooked[2].substring(0, index); - String rest = cooked[2].substring(index); - String type = rest.replace(".local.", ""); + // TODO: NDC translates a message to a callback, we could enhance NDC to + // directly interact with a state machine through messages + NativeEvent event = new NativeEvent(code, raw); + mNsdStateMachine.sendMessage(NsdManager.NATIVE_DAEMON_EVENT, event); + return true; + } + } - clientInfo.mResolvedService = new DnsSdServiceInfo(name, type, null); - clientInfo.mResolvedService.setPort(Integer.parseInt(cooked[4])); + private void handleNativeEvent(int code, String raw, String[] cooked) { + NsdServiceInfo servInfo; + int id = Integer.parseInt(cooked[1]); + ClientInfo clientInfo = mIdToClientInfoMap.get(id); + if (clientInfo == null) { + Slog.e(TAG, "Unique id with no client mapping: " + id); + return; + } - stopResolveService(id); - getAddrInfo(id, cooked[3]); + /* This goes in response as msg.arg2 */ + int clientId = -1; + int keyId = clientInfo.mClientIds.indexOfValue(id); + if (keyId != -1) { + clientId = clientInfo.mClientIds.keyAt(keyId); + } + switch (code) { + case NativeResponseCode.SERVICE_FOUND: + /* NNN uniqueId serviceName regType domain */ + if (DBG) Slog.d(TAG, "SERVICE_FOUND Raw: " + raw); + servInfo = new NsdServiceInfo(cooked[2], cooked[3], null); + clientInfo.mChannel.sendMessage(NsdManager.SERVICE_FOUND, 0, + clientId, servInfo); + break; + case NativeResponseCode.SERVICE_LOST: + /* NNN uniqueId serviceName regType domain */ + if (DBG) Slog.d(TAG, "SERVICE_LOST Raw: " + raw); + servInfo = new NsdServiceInfo(cooked[2], cooked[3], null); + clientInfo.mChannel.sendMessage(NsdManager.SERVICE_LOST, 0, + clientId, servInfo); + break; + case NativeResponseCode.SERVICE_DISCOVERY_FAILED: + /* NNN uniqueId errorCode */ + if (DBG) Slog.d(TAG, "SERVICE_DISC_FAILED Raw: " + raw); + clientInfo.mChannel.sendMessage(NsdManager.DISCOVER_SERVICES_FAILED, + NsdManager.FAILURE_INTERNAL_ERROR, clientId); + break; + case NativeResponseCode.SERVICE_REGISTERED: + /* NNN regId serviceName regType */ + if (DBG) Slog.d(TAG, "SERVICE_REGISTERED Raw: " + raw); + servInfo = new NsdServiceInfo(cooked[2], null, null); + clientInfo.mChannel.sendMessage(NsdManager.REGISTER_SERVICE_SUCCEEDED, + id, clientId, servInfo); + break; + case NativeResponseCode.SERVICE_REGISTRATION_FAILED: + /* NNN regId errorCode */ + if (DBG) Slog.d(TAG, "SERVICE_REGISTER_FAILED Raw: " + raw); + clientInfo.mChannel.sendMessage(NsdManager.REGISTER_SERVICE_FAILED, + NsdManager.FAILURE_INTERNAL_ERROR, clientId); + break; + case NativeResponseCode.SERVICE_UPDATED: + /* NNN regId */ + break; + case NativeResponseCode.SERVICE_UPDATE_FAILED: + /* NNN regId errorCode */ + break; + case NativeResponseCode.SERVICE_RESOLVED: + /* NNN resolveId fullName hostName port txtlen txtdata */ + if (DBG) Slog.d(TAG, "SERVICE_RESOLVED Raw: " + raw); + int index = cooked[2].indexOf("."); + if (index == -1) { + Slog.e(TAG, "Invalid service found " + raw); break; - case NativeResponseCode.SERVICE_RESOLUTION_FAILED: - case NativeResponseCode.SERVICE_GET_ADDR_FAILED: - /* NNN resolveId errorCode */ - if (DBG) Slog.d(TAG, "SERVICE_RESOLVE_FAILED Raw: " + raw); - clientInfo = getClientByResolve(id); - if (clientInfo == null) break; + } + String name = cooked[2].substring(0, index); + String rest = cooked[2].substring(index); + String type = rest.replace(".local.", ""); + + clientInfo.mResolvedService.setServiceName(name); + clientInfo.mResolvedService.setServiceType(type); + clientInfo.mResolvedService.setPort(Integer.parseInt(cooked[4])); + stopResolveService(id); + if (!getAddrInfo(id, cooked[3])) { clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED, - NsdManager.ERROR); - break; - case NativeResponseCode.SERVICE_GET_ADDR_SUCCESS: - /* NNN resolveId hostname ttl addr */ - if (DBG) Slog.d(TAG, "SERVICE_GET_ADDR_SUCCESS Raw: " + raw); - clientInfo = getClientByResolve(id); - if (clientInfo == null || clientInfo.mResolvedService == null) break; - - try { - clientInfo.mResolvedService.setHost(InetAddress.getByName(cooked[4])); - clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_SUCCEEDED, - clientInfo.mResolvedService); - clientInfo.mResolvedService = null; - clientInfo.mResolveId = INVALID_ID; - } catch (java.net.UnknownHostException e) { - clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED, - NsdManager.ERROR); - } - stopGetAddrInfo(id); - break; - default: - break; - } - return false; + NsdManager.FAILURE_INTERNAL_ERROR, clientId); + mIdToClientInfoMap.remove(id); + clientInfo.mResolvedService = null; + } + break; + case NativeResponseCode.SERVICE_RESOLUTION_FAILED: + /* NNN resolveId errorCode */ + if (DBG) Slog.d(TAG, "SERVICE_RESOLVE_FAILED Raw: " + raw); + stopResolveService(id); + mIdToClientInfoMap.remove(id); + clientInfo.mResolvedService = null; + clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED, + NsdManager.FAILURE_INTERNAL_ERROR, clientId); + break; + case NativeResponseCode.SERVICE_GET_ADDR_FAILED: + /* NNN resolveId errorCode */ + stopGetAddrInfo(id); + mIdToClientInfoMap.remove(id); + clientInfo.mResolvedService = null; + if (DBG) Slog.d(TAG, "SERVICE_RESOLVE_FAILED Raw: " + raw); + clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED, + NsdManager.FAILURE_INTERNAL_ERROR, clientId); + break; + case NativeResponseCode.SERVICE_GET_ADDR_SUCCESS: + /* NNN resolveId hostname ttl addr */ + if (DBG) Slog.d(TAG, "SERVICE_GET_ADDR_SUCCESS Raw: " + raw); + try { + clientInfo.mResolvedService.setHost(InetAddress.getByName(cooked[4])); + clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_SUCCEEDED, + 0, clientId, clientInfo.mResolvedService); + } catch (java.net.UnknownHostException e) { + clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED, + NsdManager.FAILURE_INTERNAL_ERROR, clientId); + } + stopGetAddrInfo(id); + mIdToClientInfoMap.remove(id); + clientInfo.mResolvedService = null; + break; + default: + break; } } @@ -579,7 +622,7 @@ public class NsdService extends INsdManager.Stub { return true; } - private boolean registerService(int regId, DnsSdServiceInfo service) { + private boolean registerService(int regId, NsdServiceInfo service) { if (DBG) Slog.d(TAG, "registerService: " + regId + " " + service); try { //Add txtlen and txtdata @@ -637,7 +680,7 @@ public class NsdService extends INsdManager.Stub { return true; } - private boolean resolveService(int resolveId, DnsSdServiceInfo service) { + private boolean resolveService(int resolveId, NsdServiceInfo service) { if (DBG) Slog.d(TAG, "resolveService: " + resolveId + " " + service); try { mNativeConnector.execute("mdnssd", "resolve", resolveId, service.getServiceName(), @@ -700,49 +743,52 @@ public class NsdService extends INsdManager.Stub { mNsdStateMachine.dump(fd, pw, args); } - private ClientInfo getClientByDiscovery(int discoveryId) { - for (ClientInfo c: mClients.values()) { - if (c.mDiscoveryId == discoveryId) { - return c; - } - } - return null; + /* arg2 on the source message has an id that needs to be retained in replies + * see NsdManager for details */ + private Message obtainMessage(Message srcMsg) { + Message msg = Message.obtain(); + msg.arg2 = srcMsg.arg2; + return msg; } - private ClientInfo getClientByResolve(int resolveId) { - for (ClientInfo c: mClients.values()) { - if (c.mResolveId == resolveId) { - return c; - } - } - return null; + private void replyToMessage(Message msg, int what) { + if (msg.replyTo == null) return; + Message dstMsg = obtainMessage(msg); + dstMsg.what = what; + mReplyChannel.replyToMessage(msg, dstMsg); } - private ClientInfo getClientByRegistration(int regId) { - for (ClientInfo c: mClients.values()) { - if (c.mRegisteredIds.contains(regId)) { - return c; - } - } - return null; + private void replyToMessage(Message msg, int what, int arg1) { + if (msg.replyTo == null) return; + Message dstMsg = obtainMessage(msg); + dstMsg.what = what; + dstMsg.arg1 = arg1; + mReplyChannel.replyToMessage(msg, dstMsg); + } + + private void replyToMessage(Message msg, int what, Object obj) { + if (msg.replyTo == null) return; + Message dstMsg = obtainMessage(msg); + dstMsg.what = what; + dstMsg.obj = obj; + mReplyChannel.replyToMessage(msg, dstMsg); } /* Information tracked per client */ private class ClientInfo { - private static final int MAX_REG = 5; + private static final int MAX_LIMIT = 10; private AsyncChannel mChannel; private Messenger mMessenger; - private int mDiscoveryId; - private int mResolveId; /* Remembers a resolved service until getaddrinfo completes */ - private DnsSdServiceInfo mResolvedService; - private ArrayList<Integer> mRegisteredIds = new ArrayList<Integer>(); + private NsdServiceInfo mResolvedService; + + /* A map from client id to unique id sent to mDns */ + private SparseArray<Integer> mClientIds = new SparseArray<Integer>(); private ClientInfo(AsyncChannel c, Messenger m) { mChannel = c; mMessenger = m; - mDiscoveryId = mResolveId = INVALID_ID; if (DBG) Slog.d(TAG, "New client, channel: " + c + " messenger: " + m); } @@ -751,11 +797,10 @@ public class NsdService extends INsdManager.Stub { StringBuffer sb = new StringBuffer(); sb.append("mChannel ").append(mChannel).append("\n"); sb.append("mMessenger ").append(mMessenger).append("\n"); - sb.append("mDiscoveryId ").append(mDiscoveryId).append("\n"); - sb.append("mResolveId ").append(mResolveId).append("\n"); sb.append("mResolvedService ").append(mResolvedService).append("\n"); - for(int regId : mRegisteredIds) { - sb.append("regId ").append(regId).append("\n"); + for(int i = 0; i< mClientIds.size(); i++) { + sb.append("clientId ").append(mClientIds.keyAt(i)); + sb.append(" mDnsId ").append(mClientIds.valueAt(i)).append("\n"); } return sb.toString(); } diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 0f8d151..aa7de82 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -1567,6 +1567,31 @@ public final class ActivityManagerService extends ActivityManagerNative @Override public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { + if (code == SYSPROPS_TRANSACTION) { + // We need to tell all apps about the system property change. + ArrayList<IBinder> procs = new ArrayList<IBinder>(); + synchronized(this) { + for (SparseArray<ProcessRecord> apps : mProcessNames.getMap().values()) { + final int NA = apps.size(); + for (int ia=0; ia<NA; ia++) { + ProcessRecord app = apps.valueAt(ia); + if (app.thread != null) { + procs.add(app.thread.asBinder()); + } + } + } + } + + int N = procs.size(); + for (int i=0; i<N; i++) { + Parcel data2 = Parcel.obtain(); + try { + procs.get(i).transact(IBinder.SYSPROPS_TRANSACTION, data2, null, 0); + } catch (RemoteException e) { + } + data2.recycle(); + } + } try { return super.onTransact(code, data, reply, flags); } catch (RuntimeException e) { @@ -4098,6 +4123,10 @@ public final class ActivityManagerService extends ActivityManagerNative EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_ENABLE_SCREEN, SystemClock.uptimeMillis()); mWindowManager.enableScreenAfterBoot(); + + synchronized (this) { + updateEventDispatchingLocked(); + } } public void showBootMessage(final CharSequence msg, final boolean always) { @@ -6686,7 +6715,7 @@ public final class ActivityManagerService extends ActivityManagerNative synchronized(this) { mWentToSleep = true; - mWindowManager.setEventDispatching(false); + updateEventDispatchingLocked(); if (!mSleeping) { mSleeping = true; @@ -6712,7 +6741,7 @@ public final class ActivityManagerService extends ActivityManagerNative synchronized(this) { mShuttingDown = true; - mWindowManager.setEventDispatching(false); + updateEventDispatchingLocked(); if (mMainStack.mResumedActivity != null) { mMainStack.stopIfSleepingLocked(); @@ -6776,11 +6805,15 @@ public final class ActivityManagerService extends ActivityManagerNative synchronized(this) { mWentToSleep = false; - mWindowManager.setEventDispatching(true); + updateEventDispatchingLocked(); comeOutOfSleepIfNeededLocked(); } } + private void updateEventDispatchingLocked() { + mWindowManager.setEventDispatching(mBooted && !mWentToSleep && !mShuttingDown); + } + public void setLockScreenShown(boolean shown) { if (checkCallingPermission(android.Manifest.permission.DEVICE_POWER) != PackageManager.PERMISSION_GRANTED) { @@ -11049,7 +11082,7 @@ public final class ActivityManagerService extends ActivityManagerNative updateOomAdjLocked(r.app); } int flags = 0; - if (si.deliveryCount > 0) { + if (si.deliveryCount > 1) { flags |= Service.START_FLAG_RETRY; } if (si.doneExecutingCount > 0) { diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java index 1e14f5b..c300411 100755 --- a/services/java/com/android/server/am/ActivityStack.java +++ b/services/java/com/android/server/am/ActivityStack.java @@ -1794,10 +1794,6 @@ final class ActivityStack { mService.mWindowManager.prepareAppTransition( WindowManagerPolicy.TRANSIT_NONE, keepCurTransition); mNoAnimActivities.add(r); - } else if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) { - mService.mWindowManager.prepareAppTransition( - WindowManagerPolicy.TRANSIT_TASK_OPEN, keepCurTransition); - mNoAnimActivities.remove(r); } else { mService.mWindowManager.prepareAppTransition(newTask ? WindowManagerPolicy.TRANSIT_TASK_OPEN diff --git a/services/java/com/android/server/net/NetworkStatsRecorder.java b/services/java/com/android/server/net/NetworkStatsRecorder.java index 2ce7771..c3ecf54 100644 --- a/services/java/com/android/server/net/NetworkStatsRecorder.java +++ b/services/java/com/android/server/net/NetworkStatsRecorder.java @@ -26,6 +26,7 @@ import android.net.NetworkStats.NonMonotonicObserver; import android.net.NetworkStatsHistory; import android.net.NetworkTemplate; import android.net.TrafficStats; +import android.os.DropBoxManager; import android.util.Log; import android.util.MathUtils; import android.util.Slog; @@ -34,6 +35,7 @@ import com.android.internal.util.FileRotator; import com.android.internal.util.IndentingPrintWriter; import com.google.android.collect.Sets; +import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.File; import java.io.IOException; @@ -43,6 +45,8 @@ import java.lang.ref.WeakReference; import java.util.HashSet; import java.util.Map; +import libcore.io.IoUtils; + /** * Logic to record deltas between periodic {@link NetworkStats} snapshots into * {@link NetworkStatsHistory} that belong to {@link NetworkStatsCollection}. @@ -56,8 +60,14 @@ public class NetworkStatsRecorder { private static final boolean LOGD = false; private static final boolean LOGV = false; + private static final String TAG_NETSTATS_DUMP = "netstats_dump"; + + /** Dump before deleting in {@link #recoverFromWtf()}. */ + private static final boolean DUMP_BEFORE_DELETE = true; + private final FileRotator mRotator; private final NonMonotonicObserver<String> mObserver; + private final DropBoxManager mDropBox; private final String mCookie; private final long mBucketDuration; @@ -74,9 +84,10 @@ public class NetworkStatsRecorder { private WeakReference<NetworkStatsCollection> mComplete; public NetworkStatsRecorder(FileRotator rotator, NonMonotonicObserver<String> observer, - String cookie, long bucketDuration, boolean onlyTags) { + DropBoxManager dropBox, String cookie, long bucketDuration, boolean onlyTags) { mRotator = checkNotNull(rotator, "missing FileRotator"); mObserver = checkNotNull(observer, "missing NonMonotonicObserver"); + mDropBox = checkNotNull(dropBox, "missing DropBoxManager"); mCookie = cookie; mBucketDuration = bucketDuration; @@ -122,6 +133,7 @@ public class NetworkStatsRecorder { mComplete = new WeakReference<NetworkStatsCollection>(complete); } catch (IOException e) { Log.wtf(TAG, "problem completely reading network stats", e); + recoverFromWtf(); } } return complete; @@ -212,6 +224,7 @@ public class NetworkStatsRecorder { mPending.reset(); } catch (IOException e) { Log.wtf(TAG, "problem persisting pending stats", e); + recoverFromWtf(); } } } @@ -226,6 +239,7 @@ public class NetworkStatsRecorder { mRotator.rewriteAll(new RemoveUidRewriter(mBucketDuration, uid)); } catch (IOException e) { Log.wtf(TAG, "problem removing UID " + uid, e); + recoverFromWtf(); } // clear UID from current stats snapshot @@ -355,4 +369,25 @@ public class NetworkStatsRecorder { mSinceBoot.dump(pw); } } + + /** + * Recover from {@link FileRotator} failure by dumping state to + * {@link DropBoxManager} and deleting contents. + */ + private void recoverFromWtf() { + if (DUMP_BEFORE_DELETE) { + final ByteArrayOutputStream os = new ByteArrayOutputStream(); + try { + mRotator.dumpAll(os); + } catch (IOException e) { + // ignore partial contents + os.reset(); + } finally { + IoUtils.closeQuietly(os); + } + mDropBox.addData(TAG_NETSTATS_DUMP, os.toByteArray(), 0); + } + + mRotator.deleteAll(); + } } diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java index 1a56b80..e710b33 100644 --- a/services/java/com/android/server/net/NetworkStatsService.java +++ b/services/java/com/android/server/net/NetworkStatsService.java @@ -338,9 +338,11 @@ public class NetworkStatsService extends INetworkStatsService.Stub { private NetworkStatsRecorder buildRecorder( String prefix, NetworkStatsSettings.Config config, boolean includeTags) { + final DropBoxManager dropBox = (DropBoxManager) mContext.getSystemService( + Context.DROPBOX_SERVICE); return new NetworkStatsRecorder(new FileRotator( mBaseDir, prefix, config.rotateAgeMillis, config.deleteAgeMillis), - mNonMonotonicObserver, prefix, config.bucketDuration, includeTags); + mNonMonotonicObserver, dropBox, prefix, config.bucketDuration, includeTags); } private void shutdownLocked() { diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java index 4ae3c57..7c14d49 100644 --- a/services/java/com/android/server/pm/PackageManagerService.java +++ b/services/java/com/android/server/pm/PackageManagerService.java @@ -1050,7 +1050,7 @@ public class PackageManagerService extends IPackageManager.Stub { mSystemInstallObserver.startWatching(); scanDirLI(mSystemAppDir, PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0); - + // Collect all vendor packages. mVendorAppDir = new File("/vendor/app"); mVendorInstallObserver = new AppDirObserver( @@ -1068,8 +1068,30 @@ public class PackageManagerService extends IPackageManager.Stub { Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator(); while (psit.hasNext()) { PackageSetting ps = psit.next(); - if ((ps.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0 - || mPackages.containsKey(ps.name)) { + + /* + * If this is not a system app, it can't be a + * disable system app. + */ + if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) { + continue; + } + + /* + * If the package is scanned, it's not erased. + */ + if (mPackages.containsKey(ps.name)) { + /* + * If the system app is both scanned and in the + * disabled packages list, then it must have been + * added via OTA. Remove it from the currently + * scanned package so the previously user-installed + * application can be scanned. + */ + if (mSettings.isDisabledSystemPackageLPr(ps.name)) { + mPackages.remove(ps.name); + } + continue; } @@ -3096,15 +3118,21 @@ public class PackageManagerService extends IPackageManager.Stub { + "reverting from " + ps.codePathString + ": new version " + pkg.mVersionCode + " better than installed " + ps.versionCode); - InstallArgs args = new FileInstallArgs(ps.codePathString, + InstallArgs args = createInstallArgs(ps.pkgFlags, ps.codePathString, ps.resourcePathString, ps.nativeLibraryPathString); - args.cleanUpResourcesLI(); - mSettings.enableSystemPackageLPw(ps.name); + synchronized (mInstaller) { + args.cleanUpResourcesLI(); + } + synchronized (mPackages) { + mSettings.enableSystemPackageLPw(ps.name); + } } } } + if (updatedPkg != null) { - // An updated system app will not have the PARSE_IS_SYSTEM flag set initially + // An updated system app will not have the PARSE_IS_SYSTEM flag set + // initially parseFlags |= PackageParser.PARSE_IS_SYSTEM; } // Verify certificates against what was last scanned @@ -3112,6 +3140,49 @@ public class PackageManagerService extends IPackageManager.Stub { Slog.w(TAG, "Failed verifying certificates for package:" + pkg.packageName); return null; } + + /* + * A new system app appeared, but we already had a non-system one of the + * same name installed earlier. + */ + boolean shouldHideSystemApp = false; + if (updatedPkg == null && ps != null + && (parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0 && !isSystemApp(ps)) { + /* + * Check to make sure the signatures match first. If they don't, + * wipe the installed application and its data. + */ + if (compareSignatures(ps.signatures.mSignatures, pkg.mSignatures) + != PackageManager.SIGNATURE_MATCH) { + deletePackageLI(pkg.packageName, true, 0, null, false); + ps = null; + } else { + /* + * If the newly-added system app is an older version than the + * already installed version, hide it. It will be scanned later + * and re-added like an update. + */ + if (pkg.mVersionCode < ps.versionCode) { + shouldHideSystemApp = true; + } else { + /* + * The newly found system app is a newer version that the + * one previously installed. Simply remove the + * already-installed application and replace it with our own + * while keeping the application data. + */ + Slog.w(TAG, "Package " + ps.name + " at " + scanFile + "reverting from " + + ps.codePathString + ": new version " + pkg.mVersionCode + + " better than installed " + ps.versionCode); + InstallArgs args = createInstallArgs(ps.pkgFlags, ps.codePathString, + ps.resourcePathString, ps.nativeLibraryPathString); + synchronized (mInstaller) { + args.cleanUpResourcesLI(); + } + } + } + } + // The apk is forward locked (not public) if its code and resources // are kept in different files. // TODO grab this value from PackageSettings @@ -3135,7 +3206,27 @@ public class PackageManagerService extends IPackageManager.Stub { // Set application objects path explicitly. setApplicationInfoPaths(pkg, codePath, resPath); // Note that we invoke the following method only if we are about to unpack an application - return scanPackageLI(pkg, parseFlags, scanMode | SCAN_UPDATE_SIGNATURE, currentTime); + PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanMode + | SCAN_UPDATE_SIGNATURE, currentTime); + + /* + * If the system app should be overridden by a previously installed + * data, hide the system app now and let the /data/app scan pick it up + * again. + */ + if (shouldHideSystemApp) { + synchronized (mPackages) { + /* + * We have to grant systems permissions before we hide, because + * grantPermissions will assume the package update is trying to + * expand its permissions. + */ + grantPermissionsLPw(pkg, true); + mSettings.disableSystemPackageLPw(pkg.packageName); + } + } + + return scannedPkg; } private static void setApplicationInfoPaths(PackageParser.Package pkg, String destCodePath, @@ -7177,6 +7268,10 @@ public class PackageManagerService extends IPackageManager.Stub { return (info.flags & ApplicationInfo.FLAG_SYSTEM) != 0; } + private static boolean isSystemApp(PackageSetting ps) { + return (ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0; + } + private static boolean isUpdatedSystemApp(PackageParser.Package pkg) { return (pkg.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0; } diff --git a/services/java/com/android/server/wm/InputMonitor.java b/services/java/com/android/server/wm/InputMonitor.java index c28cfa2..c4bb519 100644 --- a/services/java/com/android/server/wm/InputMonitor.java +++ b/services/java/com/android/server/wm/InputMonitor.java @@ -41,7 +41,9 @@ final class InputMonitor implements InputManagerService.Callbacks { private boolean mInputDispatchFrozen; // When true, input dispatch proceeds normally. Otherwise all events are dropped. - private boolean mInputDispatchEnabled = true; + // Initially false, so that input does not get dispatched until boot is finished at + // which point the ActivityManager will enable dispatching. + private boolean mInputDispatchEnabled; // When true, need to call updateInputWindowsLw(). private boolean mUpdateInputWindowsNeeded = true; diff --git a/services/java/com/android/server/wm/WindowAnimator.java b/services/java/com/android/server/wm/WindowAnimator.java index 2e817ca..5536559 100644 --- a/services/java/com/android/server/wm/WindowAnimator.java +++ b/services/java/com/android/server/wm/WindowAnimator.java @@ -85,6 +85,15 @@ public class WindowAnimator { mPolicy = policy; } + void hideWallpapersLocked() { + for (final WindowToken token : mService.mWallpaperTokens) { + for (final WindowState wallpaper : token.windows) { + wallpaper.mWinAnimator.hide(); + } + token.hidden = true; + } + } + private void testWallpaperAndBackgroundLocked() { if (mWindowDetachedWallpaper != mDetachedWallpaper) { if (WindowManagerService.DEBUG_WALLPAPER) Slog.v(TAG, diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java index 8eda9ca..8957edf 100755 --- a/services/java/com/android/server/wm/WindowManagerService.java +++ b/services/java/com/android/server/wm/WindowManagerService.java @@ -3430,9 +3430,6 @@ public class WindowManagerService extends IWindowManager.Stub synchronized(mWindowMap) { WindowToken wtoken = mTokenMap.remove(token); if (wtoken != null) { - if (wtoken.windowType == TYPE_INPUT_METHOD && mInputMethodWindow != null) { - mPolicy.setLastInputMethodWindowLw(mInputMethodWindow, mInputMethodTarget); - } boolean delayed = false; if (!wtoken.hidden) { wtoken.hidden = true; @@ -3825,7 +3822,8 @@ public class WindowManagerService extends IWindowManager.Stub synchronized(mWindowMap) { if (DEBUG_APP_TRANSITIONS) Slog.v( TAG, "Prepare app transition: transit=" + transit - + " mNextAppTransition=" + mNextAppTransition); + + " mNextAppTransition=" + mNextAppTransition + + "\nCallers=" + Debug.getCallers(3)); if (okToDisplay()) { if (mNextAppTransition == WindowManagerPolicy.TRANSIT_UNSET || mNextAppTransition == WindowManagerPolicy.TRANSIT_NONE) { @@ -4237,7 +4235,7 @@ public class WindowManagerService extends IWindowManager.Stub e = new RuntimeException(); e.fillInStackTrace(); } - Slog.v(TAG, "setAppVisibility(" + token + ", " + visible + Slog.v(TAG, "setAppVisibility(" + token + ", visible=" + visible + "): mNextAppTransition=" + mNextAppTransition + " hidden=" + wtoken.hidden + " hiddenRequested=" + wtoken.hiddenRequested, e); @@ -5149,7 +5147,7 @@ public class WindowManagerService extends IWindowManager.Stub //Slog.i(TAG, "******* TELLING SURFACE FLINGER WE ARE BOOTED!"); Parcel data = Parcel.obtain(); data.writeInterfaceToken("android.ui.ISurfaceComposer"); - surfaceFlinger.transact(IBinder.FIRST_CALL_TRANSACTION, + surfaceFlinger.transact(IBinder.FIRST_CALL_TRANSACTION, // BOOT_FINISHED data, null, 0); data.recycle(); } @@ -6673,8 +6671,7 @@ public class WindowManagerService extends IWindowManager.Stub public static final int SET_TRANSPARENT_REGION = ANIMATOR_WHAT_OFFSET + 1; public static final int SET_WALLPAPER_OFFSET = ANIMATOR_WHAT_OFFSET + 2; public static final int SET_DIM_PARAMETERS = ANIMATOR_WHAT_OFFSET + 3; - public static final int SET_MOVE_ANIMATION = ANIMATOR_WHAT_OFFSET + 4; - public static final int CLEAR_PENDING_ACTIONS = ANIMATOR_WHAT_OFFSET + 5; + public static final int CLEAR_PENDING_ACTIONS = ANIMATOR_WHAT_OFFSET + 4; private Session mLastReportedHold; @@ -7158,18 +7155,6 @@ public class WindowManagerService extends IWindowManager.Stub break; } - case SET_MOVE_ANIMATION: { - WindowAnimator.SetAnimationParams params = - (WindowAnimator.SetAnimationParams) msg.obj; - WindowStateAnimator winAnimator = params.mWinAnimator; - winAnimator.setAnimation(params.mAnimation); - winAnimator.mAnimDw = params.mAnimDw; - winAnimator.mAnimDh = params.mAnimDh; - - scheduleAnimationLocked(); - break; - } - case CLEAR_PENDING_ACTIONS: { mAnimator.clearPendingActions(); break; @@ -7866,8 +7851,10 @@ public class WindowManagerService extends IWindowManager.Stub mToTopApps.clear(); } + // if wallpaper is animating in or out set oldWallpaper to null else to wallpaper WindowState oldWallpaper = mWallpaperTarget != null && mWallpaperTarget.mWinAnimator.isAnimating() + && !mWallpaperTarget.mWinAnimator.isDummyAnimation() ? null : mWallpaperTarget; adjustWallpaperWindowsLocked(); @@ -8415,9 +8402,6 @@ public class WindowManagerService extends IWindowManager.Stub winAnimator.setAnimation(a); winAnimator.mAnimDw = w.mLastFrame.left - w.mFrame.left; winAnimator.mAnimDh = w.mLastFrame.top - w.mFrame.top; - } else { - winAnimator.mAnimDw = innerDw; - winAnimator.mAnimDh = innerDh; } //Slog.i(TAG, "Window " + this + " clearing mContentChanged - done placing"); @@ -9266,6 +9250,15 @@ public class WindowManagerService extends IWindowManager.Stub } } + // It is assumed that this method is called only by InputMethodManagerService. + public void saveLastInputMethodWindowForTransition() { + synchronized (mWindowMap) { + if (mInputMethodWindow != null) { + mPolicy.setLastInputMethodWindowLw(mInputMethodWindow, mInputMethodTarget); + } + } + } + @Override public boolean hasNavigationBar() { return mPolicy.hasNavigationBar(); diff --git a/services/java/com/android/server/wm/WindowStateAnimator.java b/services/java/com/android/server/wm/WindowStateAnimator.java index 4f08d92..642de73 100644 --- a/services/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/java/com/android/server/wm/WindowStateAnimator.java @@ -177,6 +177,13 @@ class WindowStateAnimator { || atoken.inPendingTransaction)); } + /** Is the window animating the DummyAnimation? */ + boolean isDummyAnimation() { + final AppWindowToken atoken = mWin.mAppToken; + return atoken != null + && atoken.mAppAnimator.animation == AppWindowAnimator.sDummyAnimation; + } + /** Is this window currently animating? */ boolean isWindowAnimating() { return mAnimation != null; @@ -363,19 +370,33 @@ class WindowStateAnimator { mWin.mDestroying = true; if (WindowState.SHOW_TRANSACTIONS) WindowManagerService.logSurface( mWin, "HIDE (finishExit)", null); - mSurfaceShown = false; - try { - mSurface.hide(); - } catch (RuntimeException e) { - Slog.w(TAG, "Error hiding surface in " + this, e); - } - mLastHidden = true; + hide(); } mWin.mExiting = false; if (mWin.mRemoveOnExit) { mService.mPendingRemove.add(mWin); mWin.mRemoveOnExit = false; } + if (mService.mWallpaperTarget == mWin) { + mAnimator.hideWallpapersLocked(); + } + } + + void hide() { + if (!mLastHidden) { + //dump(); + mLastHidden = true; + if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(mWin, + "HIDE (performLayout)", null); + if (mSurface != null) { + mSurfaceShown = false; + try { + mSurface.hide(); + } catch (RuntimeException e) { + Slog.w(TAG, "Exception hiding surface in " + mWin); + } + } + } } boolean finishDrawingLocked() { @@ -987,20 +1008,7 @@ class WindowStateAnimator { setSurfaceBoundaries(recoveringMemory); if (w.mAttachedHidden || !w.isReadyForDisplay()) { - if (!mLastHidden) { - //dump(); - mLastHidden = true; - if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w, - "HIDE (performLayout)", null); - if (mSurface != null) { - mSurfaceShown = false; - try { - mSurface.hide(); - } catch (RuntimeException e) { - Slog.w(TAG, "Exception hiding surface in " + w); - } - } - } + hide(); // If we are waiting for this window to handle an // orientation change, well, it is hidden, so // doesn't really matter. Note that this does diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java index 5afe56c..f740718 100644 --- a/telephony/java/android/telephony/PhoneNumberUtils.java +++ b/telephony/java/android/telephony/PhoneNumberUtils.java @@ -1711,12 +1711,8 @@ public class PhoneNumberUtils return false; } - // STOPSHIP: remove this after figuring out issue 5914560, 6383850. Log.d(LOG_TAG, "System property doesn't provide any emergency numbers." - + " Use embedded logic for determining emergency numbers." - + " number: " + toLogSafePhoneNumber(number) - + ", Iso: " + defaultCountryIso - + ", useExactMatch: " + useExactMatch); + + " Use embedded logic for determining ones."); // No ecclist system property, so use our own list. if (defaultCountryIso != null) { @@ -1735,21 +1731,6 @@ public class PhoneNumberUtils } } - private static String toLogSafePhoneNumber(String number) { - // Do exactly same thing as Uri#toSafeString() does, which will enable us to compare - // sanitized phone numbers. - StringBuilder builder = new StringBuilder(); - for (int i = 0; i < number.length(); i++) { - char c = number.charAt(i); - if (c == '-' || c == '@' || c == '.') { - builder.append(c); - } else { - builder.append('x'); - } - } - return builder.toString(); - } - /** * Checks if a given number is an emergency number for the country that the user is in. The * current country is determined using the CountryDetector. diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java index 16f1575..9aed8c8 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java @@ -307,6 +307,11 @@ public final class Canvas_Delegate { } @LayoutlibDelegate + /*package*/ static void freeTextLayoutCaches() { + // nothing to be done here yet. + } + + @LayoutlibDelegate /*package*/ static int initRaster(int nativeBitmapOrZero) { if (nativeBitmapOrZero > 0) { // get the Bitmap from the int |