diff options
Diffstat (limited to 'core/java')
48 files changed, 1152 insertions, 901 deletions
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java index 68c9926..1e238f0 100644 --- a/core/java/android/accessibilityservice/AccessibilityService.java +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -130,20 +130,11 @@ import android.view.accessibility.AccessibilityNodeInfo; * For security purposes an accessibility service can retrieve only the content of the * currently active window. The currently active window is defined as the window from * which was fired the last event of the following types: - * {@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_START}, - * {@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_END}, - * {@link AccessibilityEvent#TYPE_VIEW_CLICKED}, - * {@link AccessibilityEvent#TYPE_VIEW_FOCUSED}, + * {@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED}, * {@link AccessibilityEvent#TYPE_VIEW_HOVER_ENTER}, * {@link AccessibilityEvent#TYPE_VIEW_HOVER_EXIT}, - * {@link AccessibilityEvent#TYPE_VIEW_LONG_CLICKED}, - * {@link AccessibilityEvent#TYPE_VIEW_SELECTED}, - * {@link AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED}, - * {@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED}, - * {@link AccessibilityEvent#TYPE_VIEW_SCROLLED}, - * {@link AccessibilityEvent#TYPE_VIEW_TEXT_SELECTION_CHANGED}, - * {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED}. - * In other words, the active window is the one where the user interaction is taking place. + * In other words, the last window that was shown or the last window that the user has touched + * during touch exploration. * </p> * <p> * The entry point for retrieving window content is through calling diff --git a/core/java/android/app/ActionBar.java b/core/java/android/app/ActionBar.java index 36940c2..a217867 100644 --- a/core/java/android/app/ActionBar.java +++ b/core/java/android/app/ActionBar.java @@ -29,10 +29,16 @@ import android.view.Window; import android.widget.SpinnerAdapter; /** - * This is the public interface to the contextual ActionBar. - * The ActionBar acts as a replacement for the title bar in Activities. - * It provides facilities for creating toolbar actions as well as - * methods of navigating around an application. + * Acts as a replacement for the title bar in Activities. + * The action bar provides facilities for creating toolbar actions as well as + * methods of navigating the application. + * <p>By default, the action bar appears at the top of every activity, with the application icon on + * the left, followed by the activity title. Items from the activity's options menu are also + * accessible from the action bar.</p> + * <p>From your activity, you can retrieve an instance of {@link ActionBar} by calling {@link + * android.app.Activity#getActionBar getActionBar()}.</p> + * <p>For more information, read the <a href="{@docRoot}guide/topics/ui/actionbar.html">Action + * Bar</a> developer guide.</p> */ public abstract class ActionBar { /** diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java index cb97c46..e2746d4 100644 --- a/core/java/android/app/Fragment.java +++ b/core/java/android/app/Fragment.java @@ -162,6 +162,9 @@ final class FragmentState implements Parcelable { * constructor to instantiate it. If the empty constructor is not available, * a runtime exception will occur in some cases during state restore. * + * <p>For more documentation, also see the <a + * href="{@docRoot}guide/topics/fundamentals/fragments.html">Fragments</a> developer guide.</p> + * * <p>Topics covered here: * <ol> * <li><a href="#OlderPlatforms">Older Platforms</a> @@ -880,7 +883,7 @@ public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener public void setHasOptionsMenu(boolean hasMenu) { if (mHasMenu != hasMenu) { mHasMenu = hasMenu; - if (isAdded() && !isHidden()) { + if (isAdded() && !isHidden() && isResumed()) { mActivity.invalidateOptionsMenu(); } } diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java index 789d3a6..24550c5 100644 --- a/core/java/android/app/FragmentManager.java +++ b/core/java/android/app/FragmentManager.java @@ -978,7 +978,7 @@ final class FragmentManagerImpl extends FragmentManager { } } - if (mNeedMenuInvalidate && mActivity != null) { + if (mNeedMenuInvalidate && mActivity != null && mCurState == Fragment.RESUMED) { mActivity.invalidateOptionsMenu(); mNeedMenuInvalidate = false; } diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index b993bd8..ca6f085 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -1143,6 +1143,69 @@ public final class BluetoothAdapter { } } + /** + * Enable control of the Bluetooth Adapter for a single application. + * + * <p>Some applications need to use Bluetooth for short periods of time to + * transfer data but don't want all the associated implications like + * automatic connection to headsets etc. + * + * <p> Multiple applications can call this. This is reference counted and + * Bluetooth disabled only when no one else is using it. There will be no UI + * shown to the user while bluetooth is being enabled. Any user action will + * override this call. For example, if user wants Bluetooth on and the last + * user of this API wanted to disable Bluetooth, Bluetooth will not be + * turned off. + * + * <p> This API is only meant to be used by internal applications. Third + * party applications but use {@link #enable} and {@link #disable} APIs. + * + * <p> If this API returns true, it means the callback will be called. + * The callback will be called with the current state of Bluetooth. + * If the state is not what was requested, an internal error would be the + * reason. + * + * @param on True for on, false for off. + * @param callback The callback to notify changes to the state. + * @hide + */ + public boolean changeApplicationBluetoothState(boolean on, + BluetoothStateChangeCallback callback) { + if (callback == null) return false; + + try { + return mService.changeApplicationBluetoothState(on, new + StateChangeCallbackWrapper(callback), new Binder()); + } catch (RemoteException e) { + Log.e(TAG, "changeBluetoothState", e); + } + return false; + } + + /** + * @hide + */ + public interface BluetoothStateChangeCallback { + public void onBluetoothStateChange(boolean on); + } + + /** + * @hide + */ + public class StateChangeCallbackWrapper extends IBluetoothStateChangeCallback.Stub { + private BluetoothStateChangeCallback mCallback; + + StateChangeCallbackWrapper(BluetoothStateChangeCallback + callback) { + mCallback = callback; + } + + @Override + public void onBluetoothStateChange(boolean on) { + mCallback.onBluetoothStateChange(on); + } + } + private Set<BluetoothDevice> toDeviceSet(String[] addresses) { Set<BluetoothDevice> devices = new HashSet<BluetoothDevice>(addresses.length); for (int i = 0; i < addresses.length; i++) { diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl index 183772d..be43c51 100644 --- a/core/java/android/bluetooth/IBluetooth.aidl +++ b/core/java/android/bluetooth/IBluetooth.aidl @@ -17,6 +17,7 @@ package android.bluetooth; import android.bluetooth.IBluetoothCallback; +import android.bluetooth.IBluetoothStateChangeCallback; import android.bluetooth.IBluetoothHealthCallback; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHealthAppConfiguration; @@ -52,6 +53,9 @@ interface IBluetooth byte[] readOutOfBandData(); int getAdapterConnectionState(); + boolean changeApplicationBluetoothState(boolean on, + in IBluetoothStateChangeCallback callback, in + IBinder b); boolean createBond(in String address); boolean createBondOutOfBand(in String address, in byte[] hash, in byte[] randomizer); diff --git a/core/java/android/bluetooth/IBluetoothStateChangeCallback.aidl b/core/java/android/bluetooth/IBluetoothStateChangeCallback.aidl new file mode 100644 index 0000000..feccdce --- /dev/null +++ b/core/java/android/bluetooth/IBluetoothStateChangeCallback.aidl @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2011, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.bluetooth; + +/** + * System private API for Bluetooth state change callback. + * + * {@hide} + */ +interface IBluetoothStateChangeCallback +{ + void onBluetoothStateChange(boolean on); +} diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index cdda910..2a02446 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -129,7 +129,7 @@ public abstract class Context { /** * Flag for {@link #bindService}: don't allow this binding to raise * the target service's process to the foreground scheduling priority. - * It will still be raised to the at least the same memory priority + * It will still be raised to at least the same memory priority * as the client (so that its process will not be killable in any * situation where the client is not killable), but for CPU scheduling * purposes it may be left in the background. This only has an impact @@ -138,6 +138,16 @@ public abstract class Context { */ public static final int BIND_NOT_FOREGROUND = 0x0004; + /** + * Flag for {@link #bindService}: allow the process hosting the bound + * service to go through its normal memory management. It will be + * treated more like a running service, allowing the system to + * (temporarily) expunge the process if low on memory or for some other + * whim it may have. + * @hide + */ + public static final int BIND_ALLOW_OOM_MANAGEMENT = 0x0008; + /** Return an AssetManager instance for your application's package. */ public abstract AssetManager getAssets(); diff --git a/core/java/android/content/Loader.java b/core/java/android/content/Loader.java index 4e70b74..368c33e 100644 --- a/core/java/android/content/Loader.java +++ b/core/java/android/content/Loader.java @@ -40,6 +40,8 @@ import java.io.PrintWriter; * * <p>Most implementations should not derive directly from this class, but * instead inherit from {@link AsyncTaskLoader}.</p> + * <p>For more information, see the <a + * href="{@docRoot}guide/topics/fundamentals/loaders.html">Loaders</a> developer guide.</p> * * @param <D> The result returned when the load is complete */ diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java index 2cb8a86..5be6ec1 100644 --- a/core/java/android/content/SyncManager.java +++ b/core/java/android/content/SyncManager.java @@ -1691,12 +1691,28 @@ public class SyncManager implements OnAccountsUpdateListener { continue; } - // skip the sync if it isn't manual and auto sync or - // background data usage is disabled + final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo; + syncAdapterInfo = mSyncAdapters.getServiceInfo( + SyncAdapterType.newKey(op.authority, op.account.type)); + + // only proceed if network is connected for requesting UID + final boolean uidNetworkConnected; + if (syncAdapterInfo != null) { + final NetworkInfo networkInfo = getConnectivityManager() + .getActiveNetworkInfoForUid(syncAdapterInfo.uid); + uidNetworkConnected = networkInfo != null && networkInfo.isConnected(); + } else { + uidNetworkConnected = false; + } + + // skip the sync if it isn't manual, and auto sync or + // background data usage is disabled or network is + // disconnected for the target UID. if (!op.extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, false) && (syncableState > 0) && (!masterSyncAutomatically || !backgroundDataUsageAllowed + || !uidNetworkConnected || !mSyncStorageEngine.getSyncAutomatically( op.account, op.authority))) { operationIterator.remove(); diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java index a168260..d6dbc15 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -19,6 +19,7 @@ package android.hardware; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.graphics.ImageFormat; +import android.graphics.Point; import android.graphics.Rect; import android.graphics.SurfaceTexture; import android.os.Handler; @@ -35,6 +36,7 @@ import java.util.HashMap; import java.util.List; import java.util.StringTokenizer; + /** * The Camera class is used to set image capture settings, start/stop preview, * snap pictures, and retrieve frames for encoding for video. This class is a @@ -128,7 +130,9 @@ public class Camera { private static final int CAMERA_MSG_POSTVIEW_FRAME = 0x040; private static final int CAMERA_MSG_RAW_IMAGE = 0x080; private static final int CAMERA_MSG_COMPRESSED_IMAGE = 0x100; - private static final int CAMERA_MSG_ALL_MSGS = 0x1FF; + private static final int CAMERA_MSG_RAW_IMAGE_NOTIFY = 0x200; + private static final int CAMERA_MSG_FACE = 0x400; + private static final int CAMERA_MSG_ALL_MSGS = 0x4FF; private int mNativeContext; // accessed by native methods private EventHandler mEventHandler; @@ -139,9 +143,11 @@ public class Camera { private PictureCallback mPostviewCallback; private AutoFocusCallback mAutoFocusCallback; private OnZoomChangeListener mZoomListener; + private FaceDetectionListener mFaceListener; private ErrorCallback mErrorCallback; private boolean mOneShot; private boolean mWithBuffer; + private boolean mFaceDetectionRunning = false; /** * Broadcast Action: A new picture is taken by the camera, and the entry of @@ -160,6 +166,25 @@ public class Camera { public static final String ACTION_NEW_VIDEO = "android.hardware.action.NEW_VIDEO"; /** + * Hardware face detection. It does not use much CPU. + * + * @see #startFaceDetection(int) + * @see Parameters#getMaxNumDetectedFaces(int) + * @see #CAMERA_FACE_DETECTION_SW + * @hide + */ + public static final int CAMERA_FACE_DETECTION_HW = 0; + + /** + * Software face detection. It uses some CPU. Applications must use + * {@link #setPreviewTexture(SurfaceTexture)} for preview in this mode. + * + * @see #CAMERA_FACE_DETECTION_HW + * @hide + */ + public static final int CAMERA_FACE_DETECTION_SW = 1; + + /** * Returns the number of physical cameras available on this device. */ public native static int getNumberOfCameras(); @@ -295,6 +320,7 @@ public class Camera { */ public final void release() { native_release(); + mFaceDetectionRunning = false; } /** @@ -460,7 +486,12 @@ public class Camera { * Stops capturing and drawing preview frames to the surface, and * resets the camera for a future call to {@link #startPreview()}. */ - public native final void stopPreview(); + public final void stopPreview() { + _stopPreview(); + mFaceDetectionRunning = false; + } + + private native final void _stopPreview(); /** * Return current preview state. @@ -690,6 +721,12 @@ public class Camera { } return; + case CAMERA_MSG_FACE: + if (mFaceListener != null) { + mFaceListener.onFaceDetection((FaceMetadata[])msg.obj, mCamera); + } + return; + case CAMERA_MSG_ERROR : Log.e(TAG, "Error " + msg.arg1); if (mErrorCallback != null) { @@ -1031,6 +1068,139 @@ public class Camera { mZoomListener = listener; } + /** + * Callback interface for face detected in the preview frame. + * + * @hide + */ + public interface FaceDetectionListener + { + /** + * Notify the listener of the detected faces in the preview frame. + * + * @param faceMetadata the face information. The list is sorted by the + * score. The highest score is the first element. + * @param camera the Camera service object + */ + void onFaceDetection(FaceMetadata[] faceMetadata, Camera camera); + } + + /** + * Registers a listener to be notified about the face detected of the + * preview frame. + * + * @param listener the listener to notify + * @see #startFaceDetection(int) + * @hide + */ + public final void setFaceDetectionListener(FaceDetectionListener listener) + { + mFaceListener = listener; + } + + /** + * Start the face detection. This should be called after preview is started. + * The camera will notify {@link FaceDetectionListener} of the detected + * faces in the preview frame. The detected faces may be the same as the + * previous ones. Applications should call {@link #stopFaceDetection} to + * stop the face detection. This method is supported if {@link + * Parameters#getMaxNumDetectedFaces(int)} returns a number larger than 0. + * Hardware and software face detection cannot be used at the same time. + * If the face detection has started, apps should not call this again. + * + * In hardware face detection mode, {@link Parameters#setWhiteBalance(String)}, + * {@link Parameters#setFocusAreas(List)}, and {@link Parameters#setMeteringAreas(List)} + * have no effect. + * + * @param type face detection type. This can be either {@link + * #CAMERA_FACE_DETECTION_HW} or {@link #CAMERA_FACE_DETECTION_SW} + * @throws IllegalArgumentException if the face detection type is + * unsupported or invalid. + * @throws RuntimeException if the method fails or the face detection is + * already running. + * @see #CAMERA_FACE_DETECTION_HW + * @see #CAMERA_FACE_DETECTION_SW + * @see FaceDetectionListener + * @see #stopFaceDetection() + * @see Parameters#getMaxNumDetectedFaces(int) + * @hide + */ + public final void startFaceDetection(int type) { + if (type != CAMERA_FACE_DETECTION_HW && type != CAMERA_FACE_DETECTION_SW) { + throw new IllegalArgumentException("Invalid face detection type " + type); + } + if (mFaceDetectionRunning) { + throw new RuntimeException("Face detection is already running"); + } + _startFaceDetection(type); + mFaceDetectionRunning = true; + } + + /** + * Stop the face detection. + * + * @see #startFaceDetection(int) + * @hide + */ + public final void stopFaceDetection() { + _stopFaceDetection(); + mFaceDetectionRunning = false; + } + + private native final void _startFaceDetection(int type); + private native final void _stopFaceDetection(); + + /** + * The information of a face. + * + * @hide + */ + public static class FaceMetadata { + /** + * Bounds of the face. (-1000, -1000) represents the top-left of the + * camera field of view, and (1000, 1000) represents the bottom-right of + * the field of view. This is supported by both hardware and software + * face detection. + * + * @see #startFaceDetection(int) + */ + Rect face; + + /** + * The confidence level of the face. The range is 1 to 100. 100 is the + * highest confidence. This is supported by both hardware and software + * face detction. + * + * @see #startFaceDetection(int) + */ + int score; + + /** + * An unique id per face while the face is visible to the tracker. If + * the face leaves the field-of-view and comes back, it will get a new + * id. If the value is 0, id is not supported. + */ + int id; + + /** + * The coordinates of the center of the left eye. null if this is not + * supported. + */ + Point leftEye; + + /** + * The coordinates of the center of the right eye. null if this is not + * supported. + */ + Point rightEye; + + /** + * The coordinates of the center of the mouth. null if this is not + * supported. + */ + Point mouth; + } + // Error codes match the enum in include/ui/Camera.h /** @@ -1295,6 +1465,8 @@ public class Camera { private static final String KEY_VIDEO_SIZE = "video-size"; private static final String KEY_PREFERRED_PREVIEW_SIZE_FOR_VIDEO = "preferred-preview-size-for-video"; + private static final String KEY_MAX_NUM_DETECTED_FACES_HW = "max-num-detected-faces-hw"; + private static final String KEY_MAX_NUM_DETECTED_FACES_SW = "max-num-detected-faces-sw"; // Parameter key suffix for supported values. private static final String SUPPORTED_VALUES_SUFFIX = "-values"; @@ -2977,6 +3149,25 @@ public class Camera { set(KEY_METERING_AREAS, meteringAreas); } + /** + * Gets the maximum number of detected faces supported. This is the + * maximum length of the list returned from {@link FaceDetectionListener}. + * If the return value is 0, face detection of the specified type is not + * supported. + * + * @return the maximum number of detected face supported by the camera. + * @see #startFaceDetection(int) + * @hide + */ + public int getMaxNumDetectedFaces(int type) { + if (type == CAMERA_FACE_DETECTION_HW) { + return getInt(KEY_MAX_NUM_DETECTED_FACES_HW, 0); + } else if (type == CAMERA_FACE_DETECTION_SW){ + return getInt(KEY_MAX_NUM_DETECTED_FACES_SW, 0); + } + throw new IllegalArgumentException("Invalid face detection type " + type); + } + // Splits a comma delimited string to an ArrayList of String. // Return null if the passing string is null or the size is 0. private ArrayList<String> split(String str) { diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl index 82495e3..6fde746 100644 --- a/core/java/android/net/INetworkPolicyManager.aidl +++ b/core/java/android/net/INetworkPolicyManager.aidl @@ -18,6 +18,7 @@ package android.net; import android.net.INetworkPolicyListener; import android.net.NetworkPolicy; +import android.net.NetworkTemplate; /** * Interface that creates and modifies network policy rules. @@ -37,4 +38,6 @@ interface INetworkPolicyManager { void setNetworkPolicies(in NetworkPolicy[] policies); NetworkPolicy[] getNetworkPolicies(); + void snoozePolicy(in NetworkTemplate template); + } diff --git a/core/java/android/net/NetworkPolicy.java b/core/java/android/net/NetworkPolicy.java index 52cab30..aaad8a1 100644 --- a/core/java/android/net/NetworkPolicy.java +++ b/core/java/android/net/NetworkPolicy.java @@ -21,6 +21,8 @@ import static com.android.internal.util.Preconditions.checkNotNull; import android.os.Parcel; import android.os.Parcelable; +import com.android.internal.util.Objects; + /** * Policy for networks matching a {@link NetworkTemplate}, including usage cycle * and limits to be enforced. @@ -30,20 +32,21 @@ import android.os.Parcelable; public class NetworkPolicy implements Parcelable, Comparable<NetworkPolicy> { public static final long WARNING_DISABLED = -1; public static final long LIMIT_DISABLED = -1; + public static final long SNOOZE_NEVER = -1; public final NetworkTemplate template; public int cycleDay; public long warningBytes; public long limitBytes; + public long lastSnooze; - // TODO: teach how to snooze limit for current cycle - - public NetworkPolicy( - NetworkTemplate template, int cycleDay, long warningBytes, long limitBytes) { + public NetworkPolicy(NetworkTemplate template, int cycleDay, long warningBytes, long limitBytes, + long lastSnooze) { this.template = checkNotNull(template, "missing NetworkTemplate"); this.cycleDay = cycleDay; this.warningBytes = warningBytes; this.limitBytes = limitBytes; + this.lastSnooze = lastSnooze; } public NetworkPolicy(Parcel in) { @@ -51,6 +54,7 @@ public class NetworkPolicy implements Parcelable, Comparable<NetworkPolicy> { cycleDay = in.readInt(); warningBytes = in.readLong(); limitBytes = in.readLong(); + lastSnooze = in.readLong(); } /** {@inheritDoc} */ @@ -59,6 +63,7 @@ public class NetworkPolicy implements Parcelable, Comparable<NetworkPolicy> { dest.writeInt(cycleDay); dest.writeLong(warningBytes); dest.writeLong(limitBytes); + dest.writeLong(lastSnooze); } /** {@inheritDoc} */ @@ -80,9 +85,25 @@ public class NetworkPolicy implements Parcelable, Comparable<NetworkPolicy> { } @Override + public int hashCode() { + return Objects.hashCode(template, cycleDay, warningBytes, limitBytes, lastSnooze); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof NetworkPolicy) { + final NetworkPolicy other = (NetworkPolicy) obj; + return Objects.equal(template, other.template) && cycleDay == other.cycleDay + && warningBytes == other.warningBytes && limitBytes == other.limitBytes + && lastSnooze == other.lastSnooze; + } + return false; + } + + @Override public String toString() { return "NetworkPolicy[" + template + "]: cycleDay=" + cycleDay + ", warningBytes=" - + warningBytes + ", limitBytes=" + limitBytes; + + warningBytes + ", limitBytes=" + limitBytes + ", lastSnooze=" + lastSnooze; } public static final Creator<NetworkPolicy> CREATOR = new Creator<NetworkPolicy>() { diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java index 593b2b7..1e9d813 100644 --- a/core/java/android/net/NetworkPolicyManager.java +++ b/core/java/android/net/NetworkPolicyManager.java @@ -52,26 +52,10 @@ public class NetworkPolicyManager { private static final boolean ALLOW_PLATFORM_APP_POLICY = true; /** - * {@link Intent} action launched when user selects {@link NetworkPolicy} - * warning notification. + * {@link Intent} extra that indicates which {@link NetworkTemplate} rule it + * applies to. */ - public static final String ACTION_DATA_USAGE_WARNING = - "android.intent.action.DATA_USAGE_WARNING"; - - /** - * {@link Intent} action launched when user selects {@link NetworkPolicy} - * limit notification. - */ - public static final String ACTION_DATA_USAGE_LIMIT = - "android.intent.action.DATA_USAGE_LIMIT"; - - /** - * {@link Intent} extra included in {@link #ACTION_DATA_USAGE_WARNING} and - * {@link #ACTION_DATA_USAGE_LIMIT} to indicate which - * {@link NetworkTemplate} rule it applies to. - */ - public static final String EXTRA_NETWORK_TEMPLATE = - "android.intent.extra.NETWORK_TEMPLATE"; + public static final String EXTRA_NETWORK_TEMPLATE = "android.net.NETWORK_TEMPLATE"; private INetworkPolicyManager mService; diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl index 1174e3b..3704248 100644 --- a/core/java/android/os/INetworkManagementService.aidl +++ b/core/java/android/os/INetworkManagementService.aidl @@ -212,7 +212,7 @@ interface INetworkManagementService /** * Set quota for an interface. */ - void setInterfaceQuota(String iface, long quota); + void setInterfaceQuota(String iface, long quotaBytes); /** * Remove quota for an interface. @@ -220,6 +220,21 @@ interface INetworkManagementService void removeInterfaceQuota(String iface); /** + * Set alert for an interface; requires that iface already has quota. + */ + void setInterfaceAlert(String iface, long alertBytes); + + /** + * Remove alert for an interface. + */ + void removeInterfaceAlert(String iface); + + /** + * Set alert across all interfaces. + */ + void setGlobalAlert(long alertBytes); + + /** * Control network activity of a UID over interfaces with a quota limit. */ void setUidNetworkRules(int uid, boolean rejectOnQuotaInterfaces); @@ -252,12 +267,12 @@ interface INetworkManagementService void setDnsServersForInterface(String iface, in String[] servers); /** - * Flush the DNS cache associated with the default interface + * Flush the DNS cache associated with the default interface. */ void flushDefaultDnsCache(); /** - * Flush the DNS cache associated with the specified interface + * Flush the DNS cache associated with the specified interface. */ void flushInterfaceDnsCache(String iface); } diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl index 0067e94..9a53d76 100644 --- a/core/java/android/os/IPowerManager.aidl +++ b/core/java/android/os/IPowerManager.aidl @@ -20,12 +20,15 @@ package android.os; import android.os.WorkSource; /** @hide */ + interface IPowerManager { + // WARNING: changes in acquireWakeLock() signature must be reflected in IPowerManager.cpp/h void acquireWakeLock(int flags, IBinder lock, String tag, in WorkSource ws); void updateWakeLockWorkSource(IBinder lock, in WorkSource ws); void goToSleep(long time); void goToSleepWithReason(long time, int reason); + // WARNING: changes in releaseWakeLock() signature must be reflected in IPowerManager.cpp/h void releaseWakeLock(IBinder lock, int flags); void userActivity(long when, boolean noChangeLights); void userActivityWithForce(long when, boolean noChangeLights, boolean force); diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java index e23d6f3..b8ef7be 100644 --- a/core/java/android/provider/CallLog.java +++ b/core/java/android/provider/CallLog.java @@ -189,7 +189,6 @@ public class CallLog { * Unlike the {@link #NEW} field, which requires the user to have acknowledged the * existence of the entry, this implies the user has interacted with the entry. * <P>Type: INTEGER (boolean)</P> - * @hide */ public static final String IS_READ = "is_read"; diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java index b2c1386..5765dde 100644 --- a/core/java/android/provider/ContactsContract.java +++ b/core/java/android/provider/ContactsContract.java @@ -1973,6 +1973,16 @@ public final class ContactsContract { public static final String DATA_SET = "data_set"; /** + * A concatenation of the account type and data set (delimited by a forward + * slash) - if the data set is empty, this will be the same as the account + * type. For applications that need to be aware of the data set, this can + * be used instead of account type to distinguish sets of data. This is + * never intended to be used for specifying accounts. + * @hide + */ + public static final String ACCOUNT_TYPE_AND_DATA_SET = "account_type_and_data_set"; + + /** * The aggregation mode for this contact. * <P>Type: INTEGER</P> */ @@ -6444,6 +6454,16 @@ public final class ContactsContract { public static final String DATA_SET = "data_set"; /** + * A concatenation of the account type and data set (delimited by a forward + * slash) - if the data set is empty, this will be the same as the account + * type. For applications that need to be aware of the data set, this can + * be used instead of account type to distinguish sets of data. This is + * never intended to be used for specifying accounts. + * @hide + */ + public static final String ACCOUNT_TYPE_AND_DATA_SET = "account_type_and_data_set"; + + /** * The display title of this group. * <p> * Type: TEXT @@ -7871,6 +7891,19 @@ public final class ContactsContract { * @hide */ public static final String ACCOUNT = "com.android.contacts.extra.ACCOUNT"; + + /** + * Used to specify the data set within the account in which to create the + * new contact. + * <p> + * This value is optional - if it is not specified, the contact will be + * created in the base account, with no data set. + * <p> + * Type: String + * + * @hide + */ + public static final String DATA_SET = "com.android.contacts.extra.DATA_SET"; } } } diff --git a/core/java/android/provider/VoicemailContract.java b/core/java/android/provider/VoicemailContract.java index 2e5a495..a31374f 100644 --- a/core/java/android/provider/VoicemailContract.java +++ b/core/java/android/provider/VoicemailContract.java @@ -123,14 +123,8 @@ public class VoicemailContract { */ public static final String DURATION = Calls.DURATION; /** - * Whether this is a new voicemail (i.e. has not been heard). - * <P>Type: INTEGER (boolean)</P> - */ - public static final String NEW = Calls.NEW; - /** * Whether this item has been read or otherwise consumed by the user. * <P>Type: INTEGER (boolean)</P> - * @hide */ public static final String IS_READ = Calls.IS_READ; /** diff --git a/core/java/android/server/BluetoothAdapterStateMachine.java b/core/java/android/server/BluetoothAdapterStateMachine.java index ae91465..83f5a9f 100644 --- a/core/java/android/server/BluetoothAdapterStateMachine.java +++ b/core/java/android/server/BluetoothAdapterStateMachine.java @@ -17,11 +17,13 @@ package android.server; import android.bluetooth.BluetoothAdapter; +import android.bluetooth.IBluetoothStateChangeCallback; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.os.Binder; import android.os.Message; +import android.os.RemoteException; import android.provider.Settings; import android.util.Log; @@ -34,17 +36,18 @@ import java.io.PrintWriter; /** * Bluetooth Adapter StateMachine * All the states are at the same level, ie, no hierarchy. - * (BluetootOn) - * | ^ - * TURN_OFF | | BECOME_PAIRABLE - * AIRPLANE_MODE_ON | | - * V | - * (Switching) - * | ^ - * BECOME_NON_PAIRABLE& | | TURN_ON(_CONTINUE)/TURN_ON_FOR_PRIVILEGED - * ALL_DEVICES_DISCONNECTED | | - * V | - * (HotOff) + * (BluetootOn)<----------------------<- + * | ^ -------------------->- | + * | | | | + * TURN_OFF | | BECAME_PAIRABLE m1 | | USER_TURN_ON + * AIRPLANE_MODE_ON | | | | + * V | | | + * (Switching) (PerProcessState) + * | ^ | | + * BECAME_NON_PAIRABLE& | | TURN_ON(_CONTINUE) | | + * ALL_DEVICES_DISCONNECTED | | m2 | | + * V |------------------------< | BECAME_PAIRABLE + * (HotOff)---------------------------- PER_PROCESS_TURN_ON * / ^ * / | SERVICE_RECORD_LOADED * | | @@ -55,6 +58,10 @@ import java.io.PrintWriter; * V | * (PowerOff) <----- initial state * + * Legend: + * m1 = USER_TURN_OFF + * m2 = Transition to HotOff when number of process wanting BT on is 0. + * BECAME_NON_PAIRABLE will make the transition. */ final class BluetoothAdapterStateMachine extends StateMachine { private static final String TAG = "BluetoothAdapterStateMachine"; @@ -63,24 +70,24 @@ final class BluetoothAdapterStateMachine extends StateMachine { // Message(what) to take an action // // We get this message when user tries to turn on BT - public static final int USER_TURN_ON = 1; + static final int USER_TURN_ON = 1; // We get this message when user tries to turn off BT - public static final int USER_TURN_OFF = 2; + static final int USER_TURN_OFF = 2; // Message(what) to report a event that the state machine need to respond to // // Event indicates sevice records have been loaded - public static final int SERVICE_RECORD_LOADED = 51; + static final int SERVICE_RECORD_LOADED = 51; // Event indicates all the remote Bluetooth devices has been disconnected - public static final int ALL_DEVICES_DISCONNECTED = 52; + static final int ALL_DEVICES_DISCONNECTED = 52; // Event indicates the Bluetooth is connectable - public static final int BECOME_PAIRABLE = 53; + static final int BECAME_PAIRABLE = 53; // Event indicates the Bluetooth is non-connectable. - public static final int BECOME_NON_PAIRABLE = 54; + static final int BECAME_NON_PAIRABLE = 54; // Event indicates airplane mode is turned on - public static final int AIRPLANE_MODE_ON = 55; + static final int AIRPLANE_MODE_ON = 55; // Event indicates airplane mode is turned off - public static final int AIRPLANE_MODE_OFF = 56; + static final int AIRPLANE_MODE_OFF = 56; // private internal messages // @@ -95,8 +102,9 @@ final class BluetoothAdapterStateMachine extends StateMachine { private static final int TURN_ON_CONTINUE = 102; // Unload firmware, turning off Bluetooth module power private static final int TURN_COLD = 103; - // For NFC, turn on bluetooth for certain process - private static final int TURN_ON_FOR_PRIVILEGED = 104; + // Per process enable / disable messages + static final int PER_PROCESS_TURN_ON = 104; + static final int PER_PROCESS_TURN_OFF = 105; private Context mContext; private BluetoothService mBluetoothService; @@ -107,6 +115,7 @@ final class BluetoothAdapterStateMachine extends StateMachine { private HotOff mHotOff; private WarmUp mWarmUp; private PowerOff mPowerOff; + private PerProcessState mPerProcessState; // this is the BluetoothAdapter state that reported externally private int mPublicState; @@ -123,12 +132,15 @@ final class BluetoothAdapterStateMachine extends StateMachine { mHotOff = new HotOff(); mWarmUp = new WarmUp(); mPowerOff = new PowerOff(); + mPerProcessState = new PerProcessState(); addState(mBluetoothOn); addState(mSwitching); addState(mHotOff); addState(mWarmUp); addState(mPowerOff); + addState(mPerProcessState); + setInitialState(mPowerOff); mPublicState = BluetoothAdapter.STATE_OFF; @@ -142,12 +154,9 @@ final class BluetoothAdapterStateMachine extends StateMachine { * Bluetooth module's power is off, firmware is not loaded. */ private class PowerOff extends State { - private boolean mPersistSwitchOn = false; - @Override public void enter() { - if (DBG) log("Enter PowerOff: " + mPersistSwitchOn); - mPersistSwitchOn = false; + if (DBG) log("Enter PowerOff: "); } @Override public boolean processMessage(Message message) { @@ -162,7 +171,7 @@ final class BluetoothAdapterStateMachine extends StateMachine { if (prepareBluetooth()) { // this is user request, save the setting if ((Boolean) message.obj) { - mPersistSwitchOn = true; + persistSwitchSetting(true); } // We will continue turn the BT on all the way to the BluetoothOn state deferMessage(obtainMessage(TURN_ON_CONTINUE)); @@ -191,9 +200,21 @@ final class BluetoothAdapterStateMachine extends StateMachine { transitionTo(mPowerOff); broadcastState(BluetoothAdapter.STATE_OFF); } + } else if (mContext.getResources().getBoolean + (com.android.internal.R.bool.config_bluetooth_adapter_quick_switch)) { + sendMessage(TURN_HOT); } break; - case AIRPLANE_MODE_ON: // ignore + case PER_PROCESS_TURN_ON: + if (prepareBluetooth()) { + transitionTo(mWarmUp); + } + deferMessage(obtainMessage(PER_PROCESS_TURN_ON)); + break; + case PER_PROCESS_TURN_OFF: + perProcessCallback(false, (IBluetoothStateChangeCallback) message.obj); + break; + case AIRPLANE_MODE_ON: case USER_TURN_OFF: // ignore break; default: @@ -276,6 +297,8 @@ final class BluetoothAdapterStateMachine extends StateMachine { // on to the BluetoothOn state case AIRPLANE_MODE_ON: case AIRPLANE_MODE_OFF: + case PER_PROCESS_TURN_ON: + case PER_PROCESS_TURN_OFF: deferMessage(message); break; case USER_TURN_OFF: // ignore @@ -294,12 +317,9 @@ final class BluetoothAdapterStateMachine extends StateMachine { * non-connectable. */ private class HotOff extends State { - private boolean mPersistSwitchOn = false; - @Override public void enter() { - if (DBG) log("Enter HotOff: " + mPersistSwitchOn); - mPersistSwitchOn = false; + if (DBG) log("Enter HotOff:"); } @Override @@ -310,9 +330,10 @@ final class BluetoothAdapterStateMachine extends StateMachine { switch(message.what) { case USER_TURN_ON: if ((Boolean) message.obj) { - mPersistSwitchOn = true; + persistSwitchSetting(true); } // let it fall to TURN_ON_CONTINUE: + //$FALL-THROUGH$ case TURN_ON_CONTINUE: mBluetoothService.switchConnectable(true); transitionTo(mSwitching); @@ -328,13 +349,25 @@ final class BluetoothAdapterStateMachine extends StateMachine { break; case AIRPLANE_MODE_OFF: if (getBluetoothPersistedSetting()) { - mBluetoothService.switchConnectable(true); transitionTo(mSwitching); + mBluetoothService.switchConnectable(true); broadcastState(BluetoothAdapter.STATE_TURNING_ON); } break; case USER_TURN_OFF: // ignore break; + case PER_PROCESS_TURN_ON: + transitionTo(mPerProcessState); + + // Resend the PER_PROCESS_TURN_ON message so that the callback + // can be sent through. + deferMessage(message); + + mBluetoothService.switchConnectable(true); + break; + case PER_PROCESS_TURN_OFF: + perProcessCallback(false, (IBluetoothStateChangeCallback)message.obj); + break; default: return NOT_HANDLED; } @@ -356,11 +389,7 @@ final class BluetoothAdapterStateMachine extends StateMachine { boolean retValue = HANDLED; switch(message.what) { - case BECOME_PAIRABLE: - if (mPowerOff.mPersistSwitchOn || mHotOff.mPersistSwitchOn) { - persistSwitchSetting(true); - mPowerOff.mPersistSwitchOn = mHotOff.mPersistSwitchOn = false; - } + case BECAME_PAIRABLE: String[] propVal = {"Pairable", mBluetoothService.getProperty("Pairable")}; mEventLoop.onPropertyChanged(propVal); @@ -369,7 +398,7 @@ final class BluetoothAdapterStateMachine extends StateMachine { transitionTo(mBluetoothOn); broadcastState(BluetoothAdapter.STATE_ON); break; - case BECOME_NON_PAIRABLE: + case BECAME_NON_PAIRABLE: if (mBluetoothService.getAdapterConnectionState() == BluetoothAdapter.STATE_DISCONNECTED) { transitionTo(mHotOff); @@ -385,9 +414,12 @@ final class BluetoothAdapterStateMachine extends StateMachine { case USER_TURN_ON: case AIRPLANE_MODE_OFF: case AIRPLANE_MODE_ON: + case PER_PROCESS_TURN_ON: + case PER_PROCESS_TURN_OFF: case USER_TURN_OFF: deferMessage(message); break; + default: return NOT_HANDLED; } @@ -395,10 +427,6 @@ final class BluetoothAdapterStateMachine extends StateMachine { } private void finishSwitchingOff() { - if (mBluetoothOn.mPersistBluetoothOff) { - persistSwitchSetting(false); - mBluetoothOn.mPersistBluetoothOff = false; - } mBluetoothService.finishDisable(); if (mContext.getResources().getBoolean (com.android.internal.R.bool.config_bluetooth_adapter_quick_switch)) { @@ -415,7 +443,6 @@ final class BluetoothAdapterStateMachine extends StateMachine { @Override public void enter() { if (DBG) log("Enter BluetoothOn: " + mPersistBluetoothOff); - mPersistBluetoothOff = false; } @Override public boolean processMessage(Message message) { @@ -425,22 +452,40 @@ final class BluetoothAdapterStateMachine extends StateMachine { switch(message.what) { case USER_TURN_OFF: if ((Boolean) message.obj) { - mPersistBluetoothOff = true; + persistSwitchSetting(false); + } + + if (mBluetoothService.isDiscovering()) { + mBluetoothService.cancelDiscovery(); + } + if (!mBluetoothService.isApplicationStateChangeTrackerEmpty()) { + transitionTo(mPerProcessState); + deferMessage(obtainMessage(USER_TURN_OFF)); + break; } - // let it fall through to AIRPLANE_MODE_ON + //$FALL-THROUGH$ to AIRPLANE_MODE_ON case AIRPLANE_MODE_ON: transitionTo(mSwitching); broadcastState(BluetoothAdapter.STATE_TURNING_OFF); mBluetoothService.switchConnectable(false); mBluetoothService.disconnectDevices(); + // we turn all the way to PowerOff with AIRPLANE_MODE_ON if (message.what == AIRPLANE_MODE_ON) { + // We inform all the per process callbacks + allProcessesCallback(false); deferMessage(obtainMessage(AIRPLANE_MODE_ON)); } break; case AIRPLANE_MODE_OFF: // ignore case USER_TURN_ON: // ignore break; + case PER_PROCESS_TURN_ON: + perProcessCallback(true, (IBluetoothStateChangeCallback)message.obj); + break; + case PER_PROCESS_TURN_OFF: + perProcessCallback(false, (IBluetoothStateChangeCallback)message.obj); + break; default: return NOT_HANDLED; } @@ -449,6 +494,101 @@ final class BluetoothAdapterStateMachine extends StateMachine { } + + private class PerProcessState extends State { + IBluetoothStateChangeCallback mCallback = null; + + @Override + public void enter() { + if (DBG) log("Enter PerProcessState"); + } + + @Override + public boolean processMessage(Message message) { + if (DBG) log("PerProcessState process message: " + message.what); + + boolean retValue = HANDLED; + switch (message.what) { + case PER_PROCESS_TURN_ON: + mCallback = (IBluetoothStateChangeCallback)getCurrentMessage().obj; + + // If this is not the first application call the callback. + if (mBluetoothService.getNumberOfApplicationStateChangeTrackers() > 1) { + perProcessCallback(true, mCallback); + } + break; + case BECAME_PAIRABLE: + perProcessCallback(true, mCallback); + break; + case USER_TURN_ON: + broadcastState(BluetoothAdapter.STATE_TURNING_ON); + persistSwitchSetting(true); + + String[] propVal = {"Pairable", mBluetoothService.getProperty("Pairable")}; + mEventLoop.onPropertyChanged(propVal); + + // run bluetooth now that it's turned on + mBluetoothService.runBluetooth(); + transitionTo(mBluetoothOn); + broadcastState(BluetoothAdapter.STATE_ON); + break; + case USER_TURN_OFF: + broadcastState(BluetoothAdapter.STATE_TURNING_OFF); + if (mBluetoothService.getAdapterConnectionState() != + BluetoothAdapter.STATE_DISCONNECTED) { + mBluetoothService.disconnectDevices(); + break; + } + //$FALL-THROUGH$ all devices are already disconnected + case ALL_DEVICES_DISCONNECTED: + mBluetoothService.finishDisable(); + broadcastState(BluetoothAdapter.STATE_OFF); + break; + case PER_PROCESS_TURN_OFF: + perProcessCallback(false, (IBluetoothStateChangeCallback)message.obj); + if (mBluetoothService.isApplicationStateChangeTrackerEmpty()) { + mBluetoothService.switchConnectable(false); + } + break; + case BECAME_NON_PAIRABLE: + transitionTo(mHotOff); + if (!mContext.getResources().getBoolean + (com.android.internal.R.bool.config_bluetooth_adapter_quick_switch)) { + deferMessage(obtainMessage(TURN_COLD)); + } + break; + case AIRPLANE_MODE_ON: + mBluetoothService.switchConnectable(false); + allProcessesCallback(false); + // we turn all the way to PowerOff with AIRPLANE_MODE_ON + deferMessage(obtainMessage(AIRPLANE_MODE_ON)); + break; + default: + return NOT_HANDLED; + } + return retValue; + } + } + + + private void perProcessCallback(boolean on, IBluetoothStateChangeCallback c) { + if (c == null) return; + + try { + c.onBluetoothStateChange(on); + } catch (RemoteException e) {} + } + + private void allProcessesCallback(boolean on) { + for (IBluetoothStateChangeCallback c: + mBluetoothService.getApplicationStateChangeCallbacks()) { + perProcessCallback(on, c); + } + if (!on) { + mBluetoothService.clearApplicationStateChangeTracker(); + } + } + /** * Return the public BluetoothAdapter state */ @@ -476,7 +616,7 @@ final class BluetoothAdapterStateMachine extends StateMachine { private void broadcastState(int newState) { - if (DBG) log("Bluetooth state " + mPublicState + " -> " + newState); + log("Bluetooth state " + mPublicState + " -> " + newState); if (mPublicState == newState) { return; } diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java index 107a2a9..2cab05c 100644 --- a/core/java/android/server/BluetoothEventLoop.java +++ b/core/java/android/server/BluetoothEventLoop.java @@ -340,9 +340,9 @@ class BluetoothEventLoop { if (name.equals("Pairable")) { if (pairable.equals("true")) { - mBluetoothState.sendMessage(BluetoothAdapterStateMachine.BECOME_PAIRABLE); + mBluetoothState.sendMessage(BluetoothAdapterStateMachine.BECAME_PAIRABLE); } else { - mBluetoothState.sendMessage(BluetoothAdapterStateMachine.BECOME_NON_PAIRABLE); + mBluetoothState.sendMessage(BluetoothAdapterStateMachine.BECAME_NON_PAIRABLE); } } diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java index 34f1971..9e66957 100755 --- a/core/java/android/server/BluetoothService.java +++ b/core/java/android/server/BluetoothService.java @@ -39,6 +39,7 @@ import android.bluetooth.BluetoothUuid; import android.bluetooth.IBluetooth; import android.bluetooth.IBluetoothCallback; import android.bluetooth.IBluetoothHealthCallback; +import android.bluetooth.IBluetoothStateChangeCallback; import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; @@ -68,13 +69,15 @@ import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileWriter; -import java.io.InputStreamReader; import java.io.IOException; +import java.io.InputStreamReader; import java.io.PrintWriter; import java.io.RandomAccessFile; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -101,6 +104,8 @@ public class BluetoothService extends IBluetooth.Stub { private final BluetoothBondState mBondState; // local cache of bondings private final IBatteryStats mBatteryStats; private final Context mContext; + private Map<Integer, IBluetoothStateChangeCallback> mStateChangeTracker = + Collections.synchronizedMap(new HashMap<Integer, IBluetoothStateChangeCallback>()); private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN; static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH; @@ -114,6 +119,9 @@ public class BluetoothService extends IBluetooth.Stub { private static final int MESSAGE_UUID_INTENT = 1; private static final int MESSAGE_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 2; + private static final int RFCOMM_RECORD_REAPER = 10; + private static final int STATE_CHANGE_REAPER = 11; + // The time (in millisecs) to delay the pairing attempt after the first // auto pairing attempt fails. We use an exponential delay with // INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY as the initial value and @@ -1466,7 +1474,7 @@ public class BluetoothService extends IBluetooth.Stub { int pid = Binder.getCallingPid(); mServiceRecordToPid.put(new Integer(handle), new Integer(pid)); try { - b.linkToDeath(new Reaper(handle, pid), 0); + b.linkToDeath(new Reaper(handle, pid, RFCOMM_RECORD_REAPER), 0); } catch (RemoteException e) {} return handle; } @@ -1489,20 +1497,85 @@ public class BluetoothService extends IBluetooth.Stub { } private class Reaper implements IBinder.DeathRecipient { - int pid; - int handle; - Reaper(int handle, int pid) { - this.pid = pid; - this.handle = handle; + int mPid; + int mHandle; + int mType; + + Reaper(int handle, int pid, int type) { + mPid = pid; + mHandle = handle; + mType = type; + } + + Reaper(int pid, int type) { + mPid = pid; + mType = type; } + + @Override public void binderDied() { synchronized (BluetoothService.this) { - if (DBG) Log.d(TAG, "Tracked app " + pid + " died"); - checkAndRemoveRecord(handle, pid); + if (DBG) Log.d(TAG, "Tracked app " + mPid + " died" + "Type:" + mType); + if (mType == RFCOMM_RECORD_REAPER) { + checkAndRemoveRecord(mHandle, mPid); + } else if (mType == STATE_CHANGE_REAPER) { + mStateChangeTracker.remove(mPid); + } } } } + + @Override + public boolean changeApplicationBluetoothState(boolean on, + IBluetoothStateChangeCallback callback, IBinder binder) { + int pid = Binder.getCallingPid(); + //mStateChangeTracker is a synchronized map + if (!mStateChangeTracker.containsKey(pid)) { + if (on) { + mStateChangeTracker.put(pid, callback); + } else { + return false; + } + } else if (!on) { + mStateChangeTracker.remove(pid); + } + + if (binder != null) { + try { + binder.linkToDeath(new Reaper(pid, STATE_CHANGE_REAPER), 0); + } catch (RemoteException e) { + return false; + } + } + + int type; + if (on) { + type = BluetoothAdapterStateMachine.PER_PROCESS_TURN_ON; + } else { + type = BluetoothAdapterStateMachine.PER_PROCESS_TURN_OFF; + } + + mBluetoothState.sendMessage(type, callback); + return true; + } + + boolean isApplicationStateChangeTrackerEmpty() { + return mStateChangeTracker.isEmpty(); + } + + void clearApplicationStateChangeTracker() { + mStateChangeTracker.clear(); + } + + Collection<IBluetoothStateChangeCallback> getApplicationStateChangeCallbacks() { + return mStateChangeTracker.values(); + } + + int getNumberOfApplicationStateChangeTrackers() { + return mStateChangeTracker.size(); + } + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java index 1926c92..b4e8ab4 100644 --- a/core/java/android/speech/tts/TextToSpeechService.java +++ b/core/java/android/speech/tts/TextToSpeechService.java @@ -227,7 +227,7 @@ public abstract class TextToSpeechService extends Service { private boolean mFirstIdle = true; public SynthThread() { - super(SYNTH_THREAD_NAME, android.os.Process.THREAD_PRIORITY_AUDIO); + super(SYNTH_THREAD_NAME, android.os.Process.THREAD_PRIORITY_DEFAULT); } @Override diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index 5ab2024..d9efe0c 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -25,16 +25,18 @@ import android.os.SystemClock; import android.util.DisplayMetrics; import android.util.Slog; +/** + * Provides information about the display size and density. + */ public class Display { static final String TAG = "Display"; static final boolean DEBUG_COMPAT = false; /** - * Specify the default Display + * The default Display id. */ public static final int DEFAULT_DISPLAY = 0; - /** * Use {@link android.view.WindowManager#getDefaultDisplay() * WindowManager.getDefaultDisplay()} to create a Display object. @@ -55,16 +57,6 @@ public class Display { init(display); } - /** @hide */ - public static void setCompatibilityInfo(CompatibilityInfo compatInfo) { - if (compatInfo != null && (compatInfo.isScalingRequired() - || !compatInfo.supportsScreen())) { - sCompatibilityInfo = compatInfo; - } else { - sCompatibilityInfo = null; - } - } - /** * Returns the index of this display. This is currently undefined; do * not use. @@ -80,25 +72,29 @@ public class Display { native static int getDisplayCount(); /** - * Returns the raw size of the display, in pixels. Note that this - * should <em>not</em> generally be used for computing layouts, since - * a device will typically have screen decoration (such as a status bar) + * Gets the size of the display, in pixels. + * <p> + * Note that this value should <em>not</em> be used for computing layouts, + * since a device will typically have screen decoration (such as a status bar) * along the edges of the display that reduce the amount of application - * space available from the raw size returned here. This value is - * adjusted for you based on the current rotation of the display. + * space available from the size returned here. Layouts should instead use + * the window size. + * </p><p> + * The size is adjusted based on the current rotation of the display. + * </p><p> + * The size returned by this method does not necessarily represent the + * actual raw size (native resolution) of the display. The returned size may + * be adjusted to exclude certain system decor elements that are always visible. + * It may also be scaled to provide compatibility with older applications that + * were originally designed for smaller displays. + * </p> + * + * @param outSize A {@link Point} object to receive the size information. */ public void getSize(Point outSize) { getSizeInternal(outSize, true); } - /** - * Returns the raw size of the display, in pixels. Note that this - * should <em>not</em> generally be used for computing layouts, since - * a device will typically have screen decoration (such as a status bar) - * along the edges of the display that reduce the amount of application - * space available from the raw size returned here. This value is - * adjusted for you based on the current rotation of the display. - */ private void getSizeInternal(Point outSize, boolean doCompat) { try { IWindowManager wm = getWindowManager(); @@ -118,8 +114,8 @@ public class Display { } else { // This is just for boot-strapping, initializing the // system process before the window manager is up. - outSize.x = getRealWidth(); - outSize.y = getRealHeight(); + outSize.x = getRawWidth(); + outSize.y = getRawHeight(); } if (DEBUG_COMPAT && doCompat) Slog.v(TAG, "Returning display size: " + outSize); } catch (RemoteException e) { @@ -128,7 +124,10 @@ public class Display { } /** - * This is just easier for some parts of the framework. + * Gets the size of the display as a rectangle, in pixels. + * + * @param outSize A {@link Rect} object to receive the size information. + * @see #getSize(Point) */ public void getRectSize(Rect outSize) { synchronized (mTmpPoint) { @@ -182,14 +181,49 @@ public class Display { } } - /** @hide Returns the actual screen size, not including any decor. */ - native public int getRealWidth(); - /** @hide Returns the actual screen size, not including any decor. */ - native public int getRealHeight(); + /** + * Gets the real size of the display without subtracting any window decor or + * applying any compatibility scale factors. + * <p> + * The real size may be smaller than the raw size when the window manager + * is emulating a smaller display (using adb shell am display-size). + * </p><p> + * The size is adjusted based on the current rotation of the display. + * </p> + * @hide + */ + public void getRealSize(Point outSize) { + try { + IWindowManager wm = getWindowManager(); + if (wm != null) { + wm.getRealDisplaySize(outSize); + } else { + // This is just for boot-strapping, initializing the + // system process before the window manager is up. + outSize.x = getRawWidth(); + outSize.y = getRawHeight(); + } + } catch (RemoteException e) { + Slog.w("Display", "Unable to get real display size", e); + } + } - /** @hide special for when we are faking the screen size. */ + /** + * Gets the raw width of the display, in pixels. + * <p> + * The size is adjusted based on the current rotation of the display. + * </p> + * @hide + */ native public int getRawWidth(); - /** @hide special for when we are faking the screen size. */ + + /** + * Gets the raw height of the display, in pixels. + * <p> + * The size is adjusted based on the current rotation of the display. + * </p> + * @hide + */ native public int getRawHeight(); /** @@ -235,17 +269,24 @@ public class Display { } /** - * Initialize a DisplayMetrics object from this display's data. - * - * @param outMetrics + * Gets display metrics that describe the size and density of this display. + * <p> + * The size is adjusted based on the current rotation of the display. + * </p><p> + * The size returned by this method does not necessarily represent the + * actual raw size (native resolution) of the display. The returned size may + * be adjusted to exclude certain system decor elements that are always visible. + * It may also be scaled to provide compatibility with older applications that + * were originally designed for smaller displays. + * </p> + * + * @param outMetrics A {@link DisplayMetrics} object to receive the metrics. */ public void getMetrics(DisplayMetrics outMetrics) { synchronized (mTmpPoint) { getSizeInternal(mTmpPoint, false); - outMetrics.widthPixels = mTmpPoint.x; - outMetrics.heightPixels = mTmpPoint.y; + getMetricsWithSize(outMetrics, mTmpPoint.x, mTmpPoint.y); } - getNonSizeMetrics(outMetrics); CompatibilityInfo ci = mCompatibilityInfo.getIfNeeded(); if (ci != null) { @@ -257,22 +298,44 @@ public class Display { } /** - * Initialize a DisplayMetrics object from this display's data. - * - * @param outMetrics + * Gets display metrics based on the real size of this display. * @hide */ public void getRealMetrics(DisplayMetrics outMetrics) { - outMetrics.widthPixels = getRealWidth(); - outMetrics.heightPixels = getRealHeight(); - getNonSizeMetrics(outMetrics); + synchronized (mTmpPoint) { + getRealSize(mTmpPoint); + getMetricsWithSize(outMetrics, mTmpPoint.x, mTmpPoint.y); + } + } + + /** + * If the display is mirrored to an external HDMI display, returns the + * width of that display. + * @hide + */ + public int getRawExternalWidth() { + return 1280; + } + + /** + * If the display is mirrored to an external HDMI display, returns the + * height of that display. + * @hide + */ + public int getRawExternalHeight() { + return 720; } - private void getNonSizeMetrics(DisplayMetrics outMetrics) { + /** + * Gets display metrics based on an explicit assumed display size. + * @hide + */ + public void getMetricsWithSize(DisplayMetrics outMetrics, + int width, int height) { outMetrics.densityDpi = (int)((mDensity*DisplayMetrics.DENSITY_DEFAULT)+.5f); - outMetrics.noncompatWidthPixels = outMetrics.widthPixels; - outMetrics.noncompatHeightPixels = outMetrics.heightPixels; + outMetrics.noncompatWidthPixels = outMetrics.widthPixels = width; + outMetrics.noncompatHeightPixels = outMetrics.heightPixels = height; outMetrics.density = outMetrics.noncompatDensity = mDensity; outMetrics.scaledDensity = outMetrics.noncompatScaledDensity = outMetrics.density; @@ -315,8 +378,6 @@ public class Display { private static boolean sInitialized = false; private static IWindowManager sWindowManager; - private static volatile CompatibilityInfo sCompatibilityInfo; - /** * Returns a display object which uses the metric's width/height instead. * @hide diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java index d22fa6e..ac0abc3 100644 --- a/core/java/android/view/GLES20Canvas.java +++ b/core/java/android/view/GLES20Canvas.java @@ -51,7 +51,7 @@ class GLES20Canvas extends HardwareCanvas { // The native renderer will be destroyed when this object dies. // DO NOT overwrite this reference once it is set. - @SuppressWarnings("unused") + @SuppressWarnings({"unused", "FieldCanBeLocal"}) private CanvasFinalizer mFinalizer; private int mWidth; @@ -277,14 +277,25 @@ class GLES20Canvas extends HardwareCanvas { /////////////////////////////////////////////////////////////////////////// /** + * Must match Caches::FlushMode values + * * @see #flushCaches(int) */ - public static final int FLUSH_CACHES_MODERATE = 0; + public static final int FLUSH_CACHES_LAYERS = 0; + + /** + * Must match Caches::FlushMode values + * + * @see #flushCaches(int) + */ + public static final int FLUSH_CACHES_MODERATE = 1; /** + * Must match Caches::FlushMode values + * * @see #flushCaches(int) */ - public static final int FLUSH_CACHES_FULL = 1; + public static final int FLUSH_CACHES_FULL = 2; /** * Flush caches to reclaim as much memory as possible. The amount of memory diff --git a/core/java/android/view/GLES20RecordingCanvas.java b/core/java/android/view/GLES20RecordingCanvas.java index 078222b..c987f48 100644 --- a/core/java/android/view/GLES20RecordingCanvas.java +++ b/core/java/android/view/GLES20RecordingCanvas.java @@ -38,7 +38,7 @@ import android.util.Pools; class GLES20RecordingCanvas extends GLES20Canvas implements Poolable<GLES20RecordingCanvas> { // The recording canvas pool should be large enough to handle a deeply nested // view hierarchy because display lists are generated recursively. - private static final int POOL_LIMIT = 50; + private static final int POOL_LIMIT = 25; private static final Pool<GLES20RecordingCanvas> sPool = Pools.synchronizedPool( Pools.finitePool(new PoolableManager<GLES20RecordingCanvas>() { diff --git a/core/java/android/view/GLES20RenderLayer.java b/core/java/android/view/GLES20RenderLayer.java index 7adac1c..41f16e2 100644 --- a/core/java/android/view/GLES20RenderLayer.java +++ b/core/java/android/view/GLES20RenderLayer.java @@ -24,7 +24,6 @@ import android.graphics.Canvas; * {@link Canvas} that can be used to render into an FBO using OpenGL. */ class GLES20RenderLayer extends GLES20Layer { - private int mLayerWidth; private int mLayerHeight; diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java index 14a77b4..5404e3a 100644 --- a/core/java/android/view/HardwareRenderer.java +++ b/core/java/android/view/HardwareRenderer.java @@ -140,6 +140,13 @@ public abstract class HardwareRenderer { abstract void updateSurface(SurfaceHolder holder) throws Surface.OutOfResourcesException; /** + * Destoys the layers used by the specified view hierarchy. + * + * @param view The root of the view hierarchy + */ + abstract void destroyLayers(View view); + + /** * This method should be invoked whenever the current hardware renderer * context should be reset. */ @@ -283,7 +290,7 @@ public abstract class HardwareRenderer { * see {@link android.content.ComponentCallbacks} */ static void trimMemory(int level) { - Gl20Renderer.flushCaches(level); + Gl20Renderer.trimMemory(level); } /** @@ -622,18 +629,8 @@ public abstract class HardwareRenderer { + "from multiple threads"); } - /* - * The window size has changed, so we need to create a new - * surface. - */ - if (mEglSurface != null && mEglSurface != EGL_NO_SURFACE) { - /* - * Unbind and destroy the old EGL surface, if - * there is one. - */ - sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - sEgl.eglDestroySurface(sEglDisplay, mEglSurface); - } + // In case we need to destroy an existing surface + destroySurface(); // Create an EGL surface we can render into. mEglSurface = sEgl.eglCreateWindowSurface(sEglDisplay, sEglConfig, holder, null); @@ -677,22 +674,13 @@ public abstract class HardwareRenderer { } EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) { - int[] attrib_list = { EGL_CONTEXT_CLIENT_VERSION, mGlVersion, EGL_NONE }; + int[] attribs = { EGL_CONTEXT_CLIENT_VERSION, mGlVersion, EGL_NONE }; return egl.eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT, - mGlVersion != 0 ? attrib_list : null); + mGlVersion != 0 ? attribs : null); } @Override - void initializeIfNeeded(int width, int height, View.AttachInfo attachInfo, - SurfaceHolder holder) throws Surface.OutOfResourcesException { - if (isRequested()) { - checkEglErrors(); - super.initializeIfNeeded(width, height, attachInfo, holder); - } - } - - @Override void destroy(boolean full) { if (full && mCanvas != null) { mCanvas = null; @@ -702,15 +690,21 @@ public abstract class HardwareRenderer { mDestroyed = true; - sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - sEgl.eglDestroySurface(sEglDisplay, mEglSurface); + destroySurface(); - mEglSurface = null; mGl = null; setEnabled(false); } + void destroySurface() { + if (mEglSurface != null && mEglSurface != EGL_NO_SURFACE) { + sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + sEgl.eglDestroySurface(sEglDisplay, mEglSurface); + mEglSurface = null; + } + } + @Override void invalidate() { // Cancels any existing buffer to ensure we'll get a buffer @@ -726,6 +720,7 @@ public abstract class HardwareRenderer { @Override void setup(int width, int height) { + checkCurrent(); mCanvas.setViewport(width, height); } @@ -819,7 +814,7 @@ public abstract class HardwareRenderer { * {@link #SURFACE_STATE_UPDATED} if the EGL context was changed or * {@link #SURFACE_STATE_SUCCESS} if the EGL context was the correct one */ - private int checkCurrent() { + int checkCurrent() { if (mEglThread != Thread.currentThread()) { throw new IllegalStateException("Hardware acceleration can only be used with a " + "single UI thread.\nOriginal thread: " + mEglThread + "\n" + @@ -847,6 +842,9 @@ public abstract class HardwareRenderer { static class Gl20Renderer extends GlRenderer { private GLES20Canvas mGlCanvas; + private static EGLSurface sPbuffer; + private static final Object[] sPbufferLock = new Object[0]; + Gl20Renderer(boolean translucent) { super(2, translucent); } @@ -926,14 +924,54 @@ public abstract class HardwareRenderer { return ((GLES20TextureLayer) layer).getSurfaceTexture(); } + @Override + void destroyLayers(View view) { + if (view != null && isEnabled()) { + checkCurrent(); + destroyHardwareLayer(view); + GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_LAYERS); + } + } + + private void destroyHardwareLayer(View view) { + view.destroyLayer(); + if (view instanceof ViewGroup) { + ViewGroup group = (ViewGroup) view; + + int count = group.getChildCount(); + for (int i = 0; i < count; i++) { + destroyHardwareLayer(group.getChildAt(i)); + } + } + } + static HardwareRenderer create(boolean translucent) { if (GLES20Canvas.isAvailable()) { return new Gl20Renderer(translucent); } return null; } - - static void flushCaches(int level) { + + static void trimMemory(int level) { + if (sEgl == null || sEglConfig == null) return; + + EGLContext eglContext = sEglContextStorage.get(); + // We do not have OpenGL objects + if (eglContext == null) { + return; + } else { + synchronized (sPbufferLock) { + // Create a temporary 1x1 pbuffer so we have a context + // to clear our OpenGL objects + if (sPbuffer == null) { + sPbuffer = sEgl.eglCreatePbufferSurface(sEglDisplay, sEglConfig, new int[] { + EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE + }); + } + } + sEgl.eglMakeCurrent(sEglDisplay, sPbuffer, sPbuffer, eglContext); + } + switch (level) { case ComponentCallbacks.TRIM_MEMORY_MODERATE: GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_MODERATE); diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index ad17edf..81cd798 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -55,6 +55,7 @@ interface IWindowManager boolean inputMethodClientHasFocus(IInputMethodClient client); void getDisplaySize(out Point size); + void getRealDisplaySize(out Point size); int getMaximumSizeDimension(); void setForcedDisplaySize(int longDimen, int shortDimen); diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java index 88f59d4..da5c7b2 100644 --- a/core/java/android/view/MotionEvent.java +++ b/core/java/android/view/MotionEvent.java @@ -1123,7 +1123,10 @@ public final class MotionEvent extends InputEvent implements Parcelable { } /** - * Button constant: Primary button (left mouse button, stylus tip). + * Button constant: Primary button (left mouse button). + * + * This button constant is not set in response to simple touches with a finger + * or stylus tip. The user must actually push a button. * * @see #getButtonState */ @@ -1215,55 +1218,32 @@ public final class MotionEvent extends InputEvent implements Parcelable { public static final int TOOL_TYPE_UNKNOWN = 0; /** - * Tool type constant: The tool is a finger directly touching the display. - * - * This is a <em>direct</em> positioning tool. + * Tool type constant: The tool is a finger. * * @see #getToolType */ public static final int TOOL_TYPE_FINGER = 1; /** - * Tool type constant: The tool is a stylus directly touching the display - * or hovering slightly above it. - * - * This is a <em>direct</em> positioning tool. + * Tool type constant: The tool is a stylus. * * @see #getToolType */ public static final int TOOL_TYPE_STYLUS = 2; /** - * Tool type constant: The tool is a mouse or trackpad that translates - * relative motions into cursor movements on the display. - * - * This is an <em>indirect</em> positioning tool. + * Tool type constant: The tool is a mouse or trackpad. * * @see #getToolType */ public static final int TOOL_TYPE_MOUSE = 3; /** - * Tool type constant: The tool is a finger on a touch pad that is not - * directly attached to the display. Finger movements on the touch pad - * may be translated into touches on the display, possibly with visual feedback. - * - * This is an <em>indirect</em> positioning tool. - * - * @see #getToolType - */ - public static final int TOOL_TYPE_INDIRECT_FINGER = 4; - - /** - * Tool type constant: The tool is a stylus on a digitizer tablet that is not - * attached to the display. Stylus movements on the digitizer may be translated - * into touches on the display, possibly with visual feedback. - * - * This is an <em>indirect</em> positioning tool. + * Tool type constant: The tool is an eraser or a stylus being used in an inverted posture. * * @see #getToolType */ - public static final int TOOL_TYPE_INDIRECT_STYLUS = 5; + public static final int TOOL_TYPE_ERASER = 4; // NOTE: If you add a new tool type here you must also add it to: // native/include/android/input.h @@ -1276,8 +1256,7 @@ public final class MotionEvent extends InputEvent implements Parcelable { names.append(TOOL_TYPE_FINGER, "TOOL_TYPE_FINGER"); names.append(TOOL_TYPE_STYLUS, "TOOL_TYPE_STYLUS"); names.append(TOOL_TYPE_MOUSE, "TOOL_TYPE_MOUSE"); - names.append(TOOL_TYPE_INDIRECT_FINGER, "TOOL_TYPE_INDIRECT_FINGER"); - names.append(TOOL_TYPE_INDIRECT_STYLUS, "TOOL_TYPE_INDIRECT_STYLUS"); + names.append(TOOL_TYPE_ERASER, "TOOL_TYPE_ERASER"); } // Private value for history pos that obtains the current sample. diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 2213188..5dcb84f 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -672,19 +672,23 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit private static final int FITS_SYSTEM_WINDOWS = 0x00000002; /** - * This view is visible. Use with {@link #setVisibility(int)}. + * This view is visible. + * Use with {@link #setVisibility} and <a href="#attr_android:visibility">{@code + * android:visibility}. */ public static final int VISIBLE = 0x00000000; /** * This view is invisible, but it still takes up space for layout purposes. - * Use with {@link #setVisibility(int)}. + * Use with {@link #setVisibility} and <a href="#attr_android:visibility">{@code + * android:visibility}. */ public static final int INVISIBLE = 0x00000004; /** * This view is invisible, and it doesn't take any space for layout - * purposes. Use with {@link #setVisibility(int)}. + * purposes. Use with {@link #setVisibility} and <a href="#attr_android:visibility">{@code + * android:visibility}. */ public static final int GONE = 0x00000008; @@ -9226,10 +9230,7 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit destroyDrawingCache(); - if (mHardwareLayer != null) { - mHardwareLayer.destroy(); - mHardwareLayer = null; - } + destroyLayer(); if (mDisplayList != null) { mDisplayList.invalidate(); @@ -9601,21 +9602,10 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit // Destroy any previous software drawing cache if needed switch (mLayerType) { case LAYER_TYPE_HARDWARE: - if (mHardwareLayer != null) { - mHardwareLayer.destroy(); - mHardwareLayer = null; - } + destroyLayer(); // fall through - unaccelerated views may use software layer mechanism instead case LAYER_TYPE_SOFTWARE: - if (mDrawingCache != null) { - mDrawingCache.recycle(); - mDrawingCache = null; - } - - if (mUnscaledDrawingCache != null) { - mUnscaledDrawingCache.recycle(); - mUnscaledDrawingCache = null; - } + destroyDrawingCache(); break; default: break; @@ -9741,6 +9731,13 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit return mHardwareLayer; } + void destroyLayer() { + if (mHardwareLayer != null) { + mHardwareLayer.destroy(); + mHardwareLayer = null; + } + } + /** * <p>Enables or disables the drawing cache. When the drawing cache is enabled, the next call * to {@link #getDrawingCache()} or {@link #buildDrawingCache()} will draw the view in a diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 8cf03a6..697636e 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -788,7 +788,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager final View[] children = mChildren; for (int i = 0; i < childrenCount; i++) { View child = children[i]; - if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) { + if ((child.mPrivateFlags & IS_ROOT_NAMESPACE) == 0) { child.findViewsWithText(outViews, text); } } @@ -2162,6 +2162,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager @Override public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(info); + if ((mPrivateFlags & IS_ROOT_NAMESPACE) != 0) { + return; + } for (int i = 0, count = mChildrenCount; i < count; i++) { View child = mChildren[i]; if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) { diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 470493d..76e16a9 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -235,6 +235,7 @@ public final class ViewRootImpl extends Handler implements ViewParent, final Configuration mLastConfiguration = new Configuration(); final Configuration mPendingConfiguration = new Configuration(); + class ResizedInfo { Rect coveredInsets; Rect visibleInsets; @@ -540,6 +541,13 @@ public final class ViewRootImpl extends Handler implements ViewParent, } } + private void destroyHardwareResources() { + if (mAttachInfo.mHardwareRenderer.isEnabled()) { + mAttachInfo.mHardwareRenderer.destroyLayers(mView); + } + mAttachInfo.mHardwareRenderer.destroy(false); + } + private void enableHardwareAcceleration(WindowManager.LayoutParams attrs) { mAttachInfo.mHardwareAccelerated = false; mAttachInfo.mHardwareAccelerationRequested = false; @@ -824,8 +832,10 @@ public final class ViewRootImpl extends Handler implements ViewParent, if (lp.type == WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL) { // NOTE -- system code, won't try to do compat mode. Display disp = WindowManagerImpl.getDefault().getDefaultDisplay(); - desiredWindowWidth = disp.getRealWidth(); - desiredWindowHeight = disp.getRealHeight(); + Point size = new Point(); + disp.getRealSize(size); + desiredWindowWidth = size.x; + desiredWindowHeight = size.y; } else { DisplayMetrics packageMetrics = mView.getContext().getResources().getDisplayMetrics(); @@ -869,7 +879,7 @@ public final class ViewRootImpl extends Handler implements ViewParent, host.dispatchWindowVisibilityChanged(viewVisibility); if (viewVisibility != View.VISIBLE || mNewSurfaceNeeded) { if (mAttachInfo.mHardwareRenderer != null) { - mAttachInfo.mHardwareRenderer.destroy(false); + destroyHardwareResources(); } } if (viewVisibility == View.GONE) { @@ -979,8 +989,10 @@ public final class ViewRootImpl extends Handler implements ViewParent, if (lp.type == WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL) { // NOTE -- system code, won't try to do compat mode. Display disp = WindowManagerImpl.getDefault().getDefaultDisplay(); - desiredWindowWidth = disp.getRealWidth(); - desiredWindowHeight = disp.getRealHeight(); + Point size = new Point(); + disp.getRealSize(size); + desiredWindowWidth = size.x; + desiredWindowHeight = size.y; } else { DisplayMetrics packageMetrics = res.getDisplayMetrics(); desiredWindowWidth = packageMetrics.widthPixels; diff --git a/core/java/android/webkit/DebugFlags.java b/core/java/android/webkit/DebugFlags.java index 3cb5e24..a21d3ee 100644 --- a/core/java/android/webkit/DebugFlags.java +++ b/core/java/android/webkit/DebugFlags.java @@ -47,7 +47,7 @@ class DebugFlags { public static final boolean WEB_VIEW_CORE = false; /* * Set to true to allow the WebTextView to draw on top of the web page in a - * different color so that you can see how the two line up. + * different color with no background so you can see how the two line up. */ public static final boolean DRAW_WEBTEXTVIEW = false; } diff --git a/core/java/android/webkit/WebTextView.java b/core/java/android/webkit/WebTextView.java index d54230c..b8c4e22 100644 --- a/core/java/android/webkit/WebTextView.java +++ b/core/java/android/webkit/WebTextView.java @@ -25,7 +25,7 @@ import android.graphics.ColorFilter; import android.graphics.Paint; import android.graphics.PixelFormat; import android.graphics.Rect; -import android.graphics.drawable.Drawable; +import android.graphics.drawable.ColorDrawable; import android.os.Bundle; import android.os.Handler; import android.os.Message; @@ -87,7 +87,6 @@ import junit.framework.Assert; // Keep track of the text before the change so we know whether we actually // need to send down the DOM events. private String mPreChange; - private Drawable mBackground; // Variables for keeping track of the touch down, to send to the WebView // when a drag starts private float mDragStartX; @@ -190,6 +189,8 @@ import junit.framework.Assert; // that other applications that use embedded WebViews will properly // display the text in password textfields. setTextColor(DebugFlags.DRAW_WEBTEXTVIEW ? Color.RED : Color.BLACK); + setBackgroundDrawable(DebugFlags.DRAW_WEBTEXTVIEW ? null : new ColorDrawable(Color.WHITE)); + // This helps to align the text better with the text in the web page. setIncludeFontPadding(false); @@ -423,24 +424,6 @@ import junit.framework.Assert; // makeNewLayout does. super.makeNewLayout(w, hintWidth, boring, hintBoring, ellipsisWidth, bringIntoView); - - // For fields that do not draw, create a layout which is altered so that - // the text lines up. - if (DebugFlags.DRAW_WEBTEXTVIEW || willNotDraw()) { - float lineHeight = -1; - if (mWebView != null) { - float height = mWebView.nativeFocusCandidateLineHeight(); - if (height != -1) { - lineHeight = height * mWebView.getScale(); - } - } - CharSequence text = getText(); - // Copy from the existing Layout. - mLayout = new WebTextViewLayout(text, text, getPaint(), mLayout.getWidth(), - mLayout.getAlignment(), mLayout.getSpacingMultiplier(), - mLayout.getSpacingAdd(), false, null, ellipsisWidth, - lineHeight); - } lineUpScroll(); } @@ -491,51 +474,6 @@ import junit.framework.Assert; return connection; } - /** - * In general, TextView makes a call to InputMethodManager.updateSelection - * in onDraw. However, in the general case of WebTextView, we do not draw. - * This method is called by WebView.onDraw to take care of the part that - * needs to be called. - */ - /* package */ void onDrawSubstitute() { - if (!willNotDraw()) { - // If the WebTextView is set to draw, such as in the case of a - // password, onDraw calls updateSelection(), so this code path is - // unnecessary. - return; - } - // This code is copied from TextView.onDraw(). That code does not get - // executed, however, because the WebTextView does not draw, allowing - // webkit's drawing to show through. - InputMethodManager imm = InputMethodManager.peekInstance(); - if (imm != null && imm.isActive(this)) { - Spannable sp = (Spannable) getText(); - int selStart = Selection.getSelectionStart(sp); - int selEnd = Selection.getSelectionEnd(sp); - int candStart = EditableInputConnection.getComposingSpanStart(sp); - int candEnd = EditableInputConnection.getComposingSpanEnd(sp); - imm.updateSelection(this, selStart, selEnd, candStart, candEnd); - } - } - - @Override - protected void onDraw(Canvas canvas) { - // onDraw should only be called for password fields. If WebTextView is - // still drawing, but is no longer corresponding to a password field, - // remove it. - if (!DebugFlags.DRAW_WEBTEXTVIEW && (mWebView == null - || !mWebView.nativeFocusCandidateIsPassword() - || !isSameTextField(mWebView.nativeFocusCandidatePointer()))) { - // Although calling remove() would seem to make more sense here, - // changing it to not be a password field will make it not draw. - // Other code will make sure that it is removed completely, but this - // way the user will not see it. - setInPassword(false); - } else { - super.onDraw(canvas); - } - } - @Override public void onEditorAction(int actionCode) { switch (actionCode) { @@ -928,102 +866,6 @@ import junit.framework.Assert; if (mWebView != null) mWebView.incrementTextGeneration(); } - /** - * Determine whether to use the system-wide password disguising method, - * or to use none. - * @param inPassword True if the textfield is a password field. - */ - /* package */ void setInPassword(boolean inPassword) { - if (inPassword) { - setInputType(InputType.TYPE_CLASS_TEXT | EditorInfo. - TYPE_TEXT_VARIATION_WEB_PASSWORD); - createBackground(); - } - // For password fields, draw the WebTextView. For others, just show - // webkit's drawing. - if (!DebugFlags.DRAW_WEBTEXTVIEW) { - setWillNotDraw(!inPassword); - } - setBackgroundDrawable(inPassword ? mBackground : null); - } - - /** - * Private class used for the background of a password textfield. - */ - private static class OutlineDrawable extends Drawable { - private Paint mBackgroundPaint; - private Paint mOutlinePaint; - private float[] mLines; - public OutlineDrawable() { - mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mBackgroundPaint.setColor(Color.WHITE); - - mOutlinePaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mOutlinePaint.setColor(Color.BLACK); - mOutlinePaint.setStyle(Paint.Style.STROKE); - - mLines = new float[16]; - } - @Override - public void setBounds(int left, int top, int right, int bottom) { - super.setBounds(left, top, right, bottom); - bottom--; - right -= 2; - // Top line - mLines[0] = left; - mLines[1] = top + 1; - mLines[2] = right; - mLines[3] = top + 1; - // Right line - mLines[4] = right; - mLines[5] = top; - mLines[6] = right; - mLines[7] = bottom; - // Bottom line - mLines[8] = left; - mLines[9] = bottom; - mLines[10] = right; - mLines[11] = bottom; - // Left line - mLines[12] = left + 1; - mLines[13] = top; - mLines[14] = left + 1; - mLines[15] = bottom; - } - @Override - public void draw(Canvas canvas) { - // Draw the background. - canvas.drawRect(getBounds(), mBackgroundPaint); - // Draw the outline. - canvas.drawLines(mLines, mOutlinePaint); - } - // Always want it to be opaque. - @Override - public int getOpacity() { - return PixelFormat.OPAQUE; - } - // These are needed because they are abstract in Drawable. - @Override - public void setAlpha(int alpha) { } - @Override - public void setColorFilter(ColorFilter cf) { } - } - - /** - * Create a background for the WebTextView and set up the paint for drawing - * the text. This way, we can see the password transformation of the - * system, which (optionally) shows the actual text before changing to dots. - * The background is necessary to hide the webkit-drawn text beneath. - */ - private void createBackground() { - if (mBackground != null) { - return; - } - mBackground = new OutlineDrawable(); - - setGravity(Gravity.CENTER_VERTICAL); - } - @Override public void setInputType(int type) { mFromSetInputType = true; @@ -1072,7 +914,8 @@ import junit.framework.Assert; lp.height = height; } if (getParent() == null) { - mWebView.addView(this, lp); + // Insert the view so that it's drawn first (at index 0) + mWebView.addView(this, 0, lp); } else { setLayoutParams(lp); } @@ -1145,7 +988,6 @@ import junit.framework.Assert; /* package */ void setType(int type) { if (mWebView == null) return; boolean single = true; - boolean inPassword = false; int maxLength = -1; int inputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT; @@ -1167,7 +1009,7 @@ import junit.framework.Assert; imeOptions |= EditorInfo.IME_ACTION_NONE; break; case PASSWORD: - inPassword = true; + inputType |= EditorInfo.TYPE_TEXT_VARIATION_WEB_PASSWORD; imeOptions |= EditorInfo.IME_ACTION_GO; break; case SEARCH: @@ -1219,7 +1061,7 @@ import junit.framework.Assert; setHorizontallyScrolling(single); setInputType(inputType); setImeOptions(imeOptions); - setInPassword(inPassword); + setVisibility(VISIBLE); AutoCompleteAdapter adapter = null; setAdapterCustom(adapter); } diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index e24ab58..a935a67 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -4068,9 +4068,6 @@ public class WebView extends AbsoluteLayout if (AUTO_REDRAW_HACK && mAutoRedraw) { invalidate(); } - if (inEditingMode()) { - mWebTextView.onDrawSubstitute(); - } mWebViewCore.signalRepaintDone(); if (mOverScrollGlow != null && mOverScrollGlow.drawEdgeGlows(canvas)) { @@ -4287,18 +4284,18 @@ public class WebView extends AbsoluteLayout private void onZoomAnimationStart() { // If it is in password mode, turn it off so it does not draw misplaced. - if (inEditingMode() && nativeFocusCandidateIsPassword()) { - mWebTextView.setInPassword(false); + if (inEditingMode()) { + mWebTextView.setVisibility(INVISIBLE); } } private void onZoomAnimationEnd() { // adjust the edit text view if needed - if (inEditingMode() && didUpdateWebTextViewDimensions(FULLY_ON_SCREEN) - && nativeFocusCandidateIsPassword()) { + if (inEditingMode() + && didUpdateWebTextViewDimensions(FULLY_ON_SCREEN)) { // If it is a password field, start drawing the WebTextView once // again. - mWebTextView.setInPassword(true); + mWebTextView.setVisibility(VISIBLE); } } @@ -4593,37 +4590,23 @@ public class WebView extends AbsoluteLayout } String text = nativeFocusCandidateText(); int nodePointer = nativeFocusCandidatePointer(); - if (alreadyThere && mWebTextView.isSameTextField(nodePointer)) { - // It is possible that we have the same textfield, but it has moved, - // i.e. In the case of opening/closing the screen. - // In that case, we need to set the dimensions, but not the other - // aspects. - // If the text has been changed by webkit, update it. However, if - // there has been more UI text input, ignore it. We will receive - // another update when that text is recognized. - if (text != null && !text.equals(mWebTextView.getText().toString()) - && nativeTextGeneration() == mTextGeneration) { - mWebTextView.setTextAndKeepSelection(text); - } - } else { - mWebTextView.setGravity(nativeFocusCandidateIsRtlText() ? - Gravity.RIGHT : Gravity.NO_GRAVITY); - // This needs to be called before setType, which may call - // requestFormData, and it needs to have the correct nodePointer. - mWebTextView.setNodePointer(nodePointer); - mWebTextView.setType(nativeFocusCandidateType()); - updateWebTextViewPadding(); - if (null == text) { - if (DebugFlags.WEB_VIEW) { - Log.v(LOGTAG, "rebuildWebTextView null == text"); - } - text = ""; - } - mWebTextView.setTextAndKeepSelection(text); - InputMethodManager imm = InputMethodManager.peekInstance(); - if (imm != null && imm.isActive(mWebTextView)) { - imm.restartInput(mWebTextView); + mWebTextView.setGravity(nativeFocusCandidateIsRtlText() ? + Gravity.RIGHT : Gravity.NO_GRAVITY); + // This needs to be called before setType, which may call + // requestFormData, and it needs to have the correct nodePointer. + mWebTextView.setNodePointer(nodePointer); + mWebTextView.setType(nativeFocusCandidateType()); + updateWebTextViewPadding(); + if (null == text) { + if (DebugFlags.WEB_VIEW) { + Log.v(LOGTAG, "rebuildWebTextView null == text"); } + text = ""; + } + mWebTextView.setTextAndKeepSelection(text); + InputMethodManager imm = InputMethodManager.peekInstance(); + if (imm != null && imm.isActive(mWebTextView)) { + imm.restartInput(mWebTextView); } if (isFocused()) { mWebTextView.requestFocus(); @@ -8120,19 +8103,7 @@ public class WebView extends AbsoluteLayout // and representing the same node as the pointer. if (inEditingMode() && mWebTextView.isSameTextField(msg.arg1)) { - if (msg.getData().getBoolean("password")) { - Spannable text = (Spannable) mWebTextView.getText(); - int start = Selection.getSelectionStart(text); - int end = Selection.getSelectionEnd(text); - mWebTextView.setInPassword(true); - // Restore the selection, which may have been - // ruined by setInPassword. - Spannable pword = - (Spannable) mWebTextView.getText(); - Selection.setSelection(pword, start, end); - // If the text entry has created more events, ignore - // this one. - } else if (msg.arg2 == mTextGeneration) { + if (msg.arg2 == mTextGeneration) { String text = (String) msg.obj; if (null == text) { text = ""; diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index b7c1687..c4458df 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -2817,9 +2817,11 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te mPositionScroller.stop(); } mTouchMode = TOUCH_MODE_OVERSCROLL; + mMotionX = (int) ev.getX(); mMotionY = mLastY = (int) ev.getY(); mMotionCorrection = 0; mActivePointerId = ev.getPointerId(0); + mDirection = 0; break; } @@ -2994,56 +2996,57 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te mDirection = newDirection; } - if (mDirection != newDirection) { - // Coming back to 'real' list scrolling - incrementalDeltaY = -newScroll; - mScrollY = 0; - invalidateParentIfNeeded(); - - // No need to do all this work if we're not going to move anyway - if (incrementalDeltaY != 0) { - trackMotionScroll(incrementalDeltaY, incrementalDeltaY); - } - - // Check to see if we are back in - View motionView = this.getChildAt(mMotionPosition - mFirstPosition); - if (motionView != null) { - mTouchMode = TOUCH_MODE_SCROLL; - - // We did not scroll the full amount. Treat this essentially like the - // start of a new touch scroll - final int motionPosition = findClosestMotionRow(y); - - mMotionCorrection = 0; - motionView = getChildAt(motionPosition - mFirstPosition); - mMotionViewOriginalTop = motionView.getTop(); - mMotionY = y; - mMotionPosition = motionPosition; - } + int overScrollDistance = -incrementalDeltaY; + if ((newScroll < 0 && oldScroll >= 0) || (newScroll > 0 && oldScroll <= 0)) { + overScrollDistance = -oldScroll; + incrementalDeltaY += overScrollDistance; } else { - overScrollBy(0, -incrementalDeltaY, 0, mScrollY, 0, 0, + incrementalDeltaY = 0; + } + + if (overScrollDistance != 0) { + overScrollBy(0, overScrollDistance, 0, mScrollY, 0, 0, 0, mOverscrollDistance, true); final int overscrollMode = getOverScrollMode(); if (overscrollMode == OVER_SCROLL_ALWAYS || (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && !contentFits())) { if (rawDeltaY > 0) { - mEdgeGlowTop.onPull((float) -incrementalDeltaY / getHeight()); + mEdgeGlowTop.onPull((float) overScrollDistance / getHeight()); if (!mEdgeGlowBottom.isFinished()) { mEdgeGlowBottom.onRelease(); } } else if (rawDeltaY < 0) { - mEdgeGlowBottom.onPull((float) -incrementalDeltaY / getHeight()); + mEdgeGlowBottom.onPull((float) overScrollDistance / getHeight()); if (!mEdgeGlowTop.isFinished()) { mEdgeGlowTop.onRelease(); } } invalidate(); } - if (Math.abs(mOverscrollDistance) == Math.abs(mScrollY)) { - // Don't allow overfling if we're at the edge. - mVelocityTracker.clear(); + } + + if (incrementalDeltaY != 0) { + // Coming back to 'real' list scrolling + mScrollY = 0; + invalidateParentIfNeeded(); + + // No need to do all this work if we're not going to move anyway + if (incrementalDeltaY != 0) { + trackMotionScroll(incrementalDeltaY, incrementalDeltaY); } + + mTouchMode = TOUCH_MODE_SCROLL; + + // We did not scroll the full amount. Treat this essentially like the + // start of a new touch scroll + final int motionPosition = findClosestMotionRow(y); + + mMotionCorrection = 0; + View motionView = getChildAt(motionPosition - mFirstPosition); + mMotionViewOriginalTop = motionView != null ? motionView.getTop() : 0; + mMotionY = y; + mMotionPosition = motionPosition; } mLastY = y; mDirection = newDirection; @@ -3320,12 +3323,6 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te mScrollY = scrollY; invalidateParentIfNeeded(); - if (clampedY) { - // Velocity is broken by hitting the limit; don't start a fling off of this. - if (mVelocityTracker != null) { - mVelocityTracker.clear(); - } - } awakenScrollBars(); } } @@ -3616,9 +3613,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } void startOverfling(int initialVelocity) { - final int min = mScrollY > 0 ? Integer.MIN_VALUE : 0; - final int max = mScrollY > 0 ? 0 : Integer.MAX_VALUE; - mScroller.fling(0, mScrollY, 0, initialVelocity, 0, 0, min, max, 0, getHeight()); + mScroller.fling(0, mScrollY, 0, initialVelocity, 0, 0, + Integer.MIN_VALUE, Integer.MAX_VALUE, 0, getHeight()); mTouchMode = TOUCH_MODE_OVERFLING; invalidate(); post(this); @@ -3768,10 +3764,22 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te final OverScroller scroller = mScroller; if (scroller.computeScrollOffset()) { final int scrollY = mScrollY; - final int deltaY = scroller.getCurrY() - scrollY; + final int currY = scroller.getCurrY(); + final int deltaY = currY - scrollY; if (overScrollBy(0, deltaY, 0, scrollY, 0, 0, 0, mOverflingDistance, false)) { - startSpringback(); + final boolean crossDown = scrollY <= 0 && currY > 0; + final boolean crossUp = scrollY >= 0 && currY < 0; + if (crossDown || crossUp) { + int velocity = (int) scroller.getCurrVelocity(); + if (crossUp) velocity = -velocity; + + // Don't flywheel from this; we're just continuing things. + scroller.abortAnimation(); + start(velocity); + } else { + startSpringback(); + } } else { invalidate(); post(this); diff --git a/core/java/android/widget/ActivityChooserView.java b/core/java/android/widget/ActivityChooserView.java index d85f8a4..45d73af 100644 --- a/core/java/android/widget/ActivityChooserView.java +++ b/core/java/android/widget/ActivityChooserView.java @@ -29,16 +29,7 @@ import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.ActivityChooserModel; import android.widget.ActivityChooserModel.ActivityChooserModelClient; -import android.widget.AdapterView; -import android.widget.BaseAdapter; -import android.widget.ImageButton; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.ListPopupWindow; -import android.widget.PopupWindow; -import android.widget.TextView; import com.android.internal.R; @@ -85,12 +76,22 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod /** * The expand activities action button; */ - private final ImageButton mExpandActivityOverflowButton; + private final FrameLayout mExpandActivityOverflowButton; + + /** + * The image for the expand activities action button; + */ + private final ImageView mExpandActivityOverflowButtonImage; /** * The default activities action button; */ - private final ImageButton mDefaultActionButton; + private final FrameLayout mDefaultActivityButton; + + /** + * The image for the default activities action button; + */ + private final ImageView mDefaultActivityButtonImage; /** * The maximal width of the list popup. @@ -185,13 +186,16 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod mActivityChooserContent = (LinearLayout) findViewById(R.id.activity_chooser_view_content); - mDefaultActionButton = (ImageButton) findViewById(R.id.default_activity_button); - mDefaultActionButton.setOnClickListener(mCallbacks); - mDefaultActionButton.setOnLongClickListener(mCallbacks); + mDefaultActivityButton = (FrameLayout) findViewById(R.id.default_activity_button); + mDefaultActivityButton.setOnClickListener(mCallbacks); + mDefaultActivityButton.setOnLongClickListener(mCallbacks); + mDefaultActivityButtonImage = (ImageView) mDefaultActivityButton.findViewById(R.id.image); - mExpandActivityOverflowButton = (ImageButton) findViewById(R.id.expand_activities_button); + mExpandActivityOverflowButton = (FrameLayout) findViewById(R.id.expand_activities_button); mExpandActivityOverflowButton.setOnClickListener(mCallbacks); - mExpandActivityOverflowButton.setImageDrawable(expandActivityOverflowButtonDrawable); + mExpandActivityOverflowButtonImage = + (ImageView) mExpandActivityOverflowButton.findViewById(R.id.image); + mExpandActivityOverflowButtonImage.setImageDrawable(expandActivityOverflowButtonDrawable); mAdapter = new ActivityChooserViewAdapter(); mAdapter.registerDataSetObserver(new DataSetObserver() { @@ -230,7 +234,7 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod * @param drawable The drawable. */ public void setExpandActivityOverflowButtonDrawable(Drawable drawable) { - mExpandActivityOverflowButton.setImageDrawable(drawable); + mExpandActivityOverflowButtonImage.setImageDrawable(drawable); } /** @@ -391,7 +395,7 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod private void updateButtons() { final int activityCount = mAdapter.getActivityCount(); if (activityCount > 0) { - mDefaultActionButton.setVisibility(VISIBLE); + mDefaultActivityButton.setVisibility(VISIBLE); if (mAdapter.getCount() > 0) { mExpandActivityOverflowButton.setEnabled(true); } else { @@ -399,9 +403,9 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod } ResolveInfo activity = mAdapter.getDefaultActivity(); PackageManager packageManager = mContext.getPackageManager(); - mDefaultActionButton.setImageDrawable(activity.loadIcon(packageManager)); + mDefaultActivityButtonImage.setImageDrawable(activity.loadIcon(packageManager)); } else { - mDefaultActionButton.setVisibility(View.INVISIBLE); + mDefaultActivityButton.setVisibility(View.INVISIBLE); mExpandActivityOverflowButton.setEnabled(false); } } @@ -440,7 +444,7 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod // View.OnClickListener public void onClick(View view) { - if (view == mDefaultActionButton) { + if (view == mDefaultActivityButton) { dismissPopup(); ResolveInfo defaultActivity = mAdapter.getDefaultActivity(); final int index = mAdapter.getDataModel().getActivityIndex(defaultActivity); @@ -457,7 +461,7 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod // OnLongClickListener#onLongClick @Override public boolean onLongClick(View view) { - if (view == mDefaultActionButton) { + if (view == mDefaultActivityButton) { if (mAdapter.getCount() > 0) { mIsSelectingDefaultActivity = true; showPopupUnchecked(mInitialActivityCount); diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java index 5eba1a0..0b0b812 100644 --- a/core/java/android/widget/FrameLayout.java +++ b/core/java/android/widget/FrameLayout.java @@ -34,12 +34,17 @@ import android.widget.RemoteViews.RemoteView; /** * FrameLayout is designed to block out an area on the screen to display - * a single item. You can add multiple children to a FrameLayout and control their - * position within the FrameLayout using {@link android.widget.FrameLayout.LayoutParams#gravity}. - * Children are drawn in a stack, with the most recently added child on top. - * The size of the frame layout is the size of its largest child (plus padding), visible - * or not (if the FrameLayout's parent permits). Views that are GONE are used for sizing - * only if {@link #setMeasureAllChildren(boolean) setMeasureAllChildren()} + * a single item. Generally, FrameLayout should be used to hold a single child view, because it can + * be difficult to organize child views in a way that's scalable to different screen sizes without + * the children overlapping each other. You can, however, add multiple children to a FrameLayout + * and control their position within the FrameLayout by assigning gravity to each child, using the + * <a href="FrameLayout.LayoutParams.html#attr_android:layout_gravity">{@code + * android:layout_gravity}</a> attribute. + * <p>Child views are drawn in a stack, with the most recently added child on top. + * The size of the FrameLayout is the size of its largest child (plus padding), visible + * or not (if the FrameLayout's parent permits). Views that are {@link android.view.View#GONE} are + * used for sizing + * only if {@link #setMeasureAllChildren(boolean) setConsiderGoneChildrenWhenMeasuring()} * is set to true. * * @attr ref android.R.styleable#FrameLayout_foreground diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java index 55b73df..91b19ed 100644 --- a/core/java/android/widget/SearchView.java +++ b/core/java/android/widget/SearchView.java @@ -221,6 +221,7 @@ public class SearchView extends LinearLayout implements CollapsibleActionView { mCloseButton.setOnClickListener(mOnClickListener); mSubmitButton.setOnClickListener(mOnClickListener); mVoiceButton.setOnClickListener(mOnClickListener); + mQueryTextView.setOnClickListener(mOnClickListener); mQueryTextView.addTextChangedListener(mTextWatcher); mQueryTextView.setOnEditorActionListener(mOnEditorActionListener); @@ -319,7 +320,9 @@ public class SearchView extends LinearLayout implements CollapsibleActionView { // If it is not iconified, then give the focus to the text field if (!isIconified()) { boolean result = mQueryTextView.requestFocus(direction, previouslyFocusedRect); - if (result) updateViewsVisibility(false); + if (result) { + updateViewsVisibility(false); + } return result; } else { return super.requestFocus(direction, previouslyFocusedRect); @@ -330,9 +333,9 @@ public class SearchView extends LinearLayout implements CollapsibleActionView { @Override public void clearFocus() { mClearingFocus = true; + setImeVisibility(false); super.clearFocus(); mQueryTextView.clearFocus(); - setImeVisibility(false); mClearingFocus = false; } @@ -681,6 +684,8 @@ public class SearchView extends LinearLayout implements CollapsibleActionView { onSubmitQuery(); } else if (v == mVoiceButton) { onVoiceClicked(); + } else if (v == mQueryTextView) { + forceSuggestionQuery(); } } }; @@ -1029,6 +1034,9 @@ public class SearchView extends LinearLayout implements CollapsibleActionView { void onTextFocusChanged() { updateViewsVisibility(isIconified()); updateFocusedState(mQueryTextView.hasFocus()); + if (mQueryTextView.hasFocus()) { + forceSuggestionQuery(); + } } @Override @@ -1041,8 +1049,9 @@ public class SearchView extends LinearLayout implements CollapsibleActionView { */ @Override public void onActionViewCollapsed() { + clearFocus(); + updateViewsVisibility(true); mQueryTextView.setText(""); - setIconified(true); mExpandedInActionView = false; } @@ -1376,6 +1385,11 @@ public class SearchView extends LinearLayout implements CollapsibleActionView { } } + private void forceSuggestionQuery() { + mQueryTextView.doBeforeTextChanged(); + mQueryTextView.doAfterTextChanged(); + } + static boolean isLandscapeMode(Context context) { return context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE; diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index a7324b0..127b6a3 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -329,7 +329,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private int mTextEditPasteWindowLayout, mTextEditSidePasteWindowLayout; private int mTextEditNoPasteWindowLayout, mTextEditSideNoPasteWindowLayout; - private int mTextEditSuggestionsWindowLayout; private int mTextEditSuggestionItemLayout; private SuggestionsPopupWindow mSuggestionsPopupWindow; private SuggestionRangeSpan mSuggestionRangeSpan; @@ -830,10 +829,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mTextEditSideNoPasteWindowLayout = a.getResourceId(attr, 0); break; - case com.android.internal.R.styleable.TextView_textEditSuggestionsWindowLayout: - mTextEditSuggestionsWindowLayout = a.getResourceId(attr, 0); - break; - case com.android.internal.R.styleable.TextView_textEditSuggestionItemLayout: mTextEditSuggestionItemLayout = a.getResourceId(attr, 0); break; @@ -8780,65 +8775,55 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private class SuggestionsPopupWindow implements OnClickListener { private static final int MAX_NUMBER_SUGGESTIONS = 5; private static final int NO_SUGGESTIONS = -1; - private final PopupWindow mContainer; - private ViewGroup mSuggestionViewGroup; + private final PopupWindow mPopupWindow; + private LinearLayout mSuggestionsContainer; private WordIterator mSuggestionWordIterator; private TextAppearanceSpan[] mHighlightSpans = new TextAppearanceSpan[0]; public SuggestionsPopupWindow() { - mContainer = new PopupWindow(TextView.this.mContext, null, + mPopupWindow = new PopupWindow(TextView.this.mContext, null, com.android.internal.R.attr.textSuggestionsWindowStyle); - mContainer.setSplitTouchEnabled(true); - mContainer.setClippingEnabled(false); - mContainer.setWindowLayoutType(WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL); + mPopupWindow.setWindowLayoutType(WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL); + mPopupWindow.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED); + mPopupWindow.setOutsideTouchable(true); - mContainer.setWidth(ViewGroup.LayoutParams.WRAP_CONTENT); - mContainer.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT); - } + mPopupWindow.setWidth(ViewGroup.LayoutParams.WRAP_CONTENT); + mPopupWindow.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT); - private class SuggestionInfo { - int suggestionStart, suggestionEnd; // range of suggestion item with replacement text - int spanStart, spanEnd; // range in TextView where text should be inserted - SuggestionSpan suggestionSpan; // the SuggestionSpan that this TextView represents - int suggestionIndex; // the index of the suggestion inside suggestionSpan - } + mSuggestionsContainer = new LinearLayout(TextView.this.mContext); + mSuggestionsContainer.setOrientation(LinearLayout.VERTICAL); - private void initSuggestionViewGroup() { - if (mSuggestionViewGroup == null) { - LayoutInflater inflater = (LayoutInflater) TextView.this.mContext. - getSystemService(Context.LAYOUT_INFLATER_SERVICE); + LayoutInflater inflater = (LayoutInflater) TextView.this.mContext. + getSystemService(Context.LAYOUT_INFLATER_SERVICE); - if (inflater == null) { - throw new IllegalArgumentException( - "Unable to create TextEdit suggestion window inflater"); - } + if (inflater == null) { + throw new IllegalArgumentException( + "Unable to create inflater for TextEdit suggestions"); + } - View view = inflater.inflate(mTextEditSuggestionsWindowLayout, null); + // Inflate the suggestion items once and for all. + for (int i = 0; i < MAX_NUMBER_SUGGESTIONS; i++) { + View childView = inflater.inflate(mTextEditSuggestionItemLayout, + mSuggestionsContainer, false); - if (! (view instanceof ViewGroup)) { + if (! (childView instanceof TextView)) { throw new IllegalArgumentException( - "Inflated TextEdit suggestion window is not a ViewGroup: " + view); + "Inflated TextEdit suggestion item is not a TextView: " + childView); } - mSuggestionViewGroup = (ViewGroup) view; - - // Inflate the suggestion items once and for all. - for (int i = 0; i < MAX_NUMBER_SUGGESTIONS; i++) { - View childView = inflater.inflate(mTextEditSuggestionItemLayout, - mSuggestionViewGroup, false); - - if (! (childView instanceof TextView)) { - throw new IllegalArgumentException( - "Inflated TextEdit suggestion item is not a TextView: " + childView); - } + childView.setTag(new SuggestionInfo()); + mSuggestionsContainer.addView(childView); + childView.setOnClickListener(this); + } - childView.setTag(new SuggestionInfo()); - mSuggestionViewGroup.addView(childView); - childView.setOnClickListener(this); - } + mPopupWindow.setContentView(mSuggestionsContainer); + } - mContainer.setContentView(mSuggestionViewGroup); - } + private class SuggestionInfo { + int suggestionStart, suggestionEnd; // range of suggestion item with replacement text + int spanStart, spanEnd; // range in TextView where text should be inserted + SuggestionSpan suggestionSpan; // the SuggestionSpan that this TextView represents + int suggestionIndex; // the index of the suggestion inside suggestionSpan } public void show() { @@ -8849,8 +8834,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener SuggestionSpan[] suggestionSpans = spannable.getSpans(pos, pos, SuggestionSpan.class); final int nbSpans = suggestionSpans.length; - initSuggestionViewGroup(); - int totalNbSuggestions = 0; int spanUnionStart = mText.length(); int spanUnionEnd = 0; @@ -8865,7 +8848,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener String[] suggestions = suggestionSpan.getSuggestions(); int nbSuggestions = suggestions.length; for (int suggestionIndex = 0; suggestionIndex < nbSuggestions; suggestionIndex++) { - TextView textView = (TextView) mSuggestionViewGroup.getChildAt( + TextView textView = (TextView) mSuggestionsContainer.getChildAt( totalNbSuggestions); textView.setText(suggestions[suggestionIndex]); SuggestionInfo suggestionInfo = (SuggestionInfo) textView.getTag(); @@ -8885,7 +8868,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (totalNbSuggestions == 0) { // TODO Replace by final text, use a dedicated layout, add a fade out timer... - TextView textView = (TextView) mSuggestionViewGroup.getChildAt(0); + TextView textView = (TextView) mSuggestionsContainer.getChildAt(0); textView.setText("No suggestions available"); SuggestionInfo suggestionInfo = (SuggestionInfo) textView.getTag(); suggestionInfo.spanStart = NO_SUGGESTIONS; @@ -8896,22 +8879,22 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); for (int i = 0; i < totalNbSuggestions; i++) { - final TextView textView = (TextView) mSuggestionViewGroup.getChildAt(i); + final TextView textView = (TextView) mSuggestionsContainer.getChildAt(i); highlightTextDifferences(textView, spanUnionStart, spanUnionEnd); } } for (int i = 0; i < totalNbSuggestions; i++) { - mSuggestionViewGroup.getChildAt(i).setVisibility(VISIBLE); + mSuggestionsContainer.getChildAt(i).setVisibility(VISIBLE); } for (int i = totalNbSuggestions; i < MAX_NUMBER_SUGGESTIONS; i++) { - mSuggestionViewGroup.getChildAt(i).setVisibility(GONE); + mSuggestionsContainer.getChildAt(i).setVisibility(GONE); } final DisplayMetrics displayMetrics = mContext.getResources().getDisplayMetrics(); final int screenWidth = displayMetrics.widthPixels; final int screenHeight = displayMetrics.heightPixels; - mSuggestionViewGroup.measure( + mSuggestionsContainer.measure( View.MeasureSpec.makeMeasureSpec(screenWidth, View.MeasureSpec.AT_MOST), View.MeasureSpec.makeMeasureSpec(screenHeight, View.MeasureSpec.AT_MOST)); @@ -9071,11 +9054,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if ((mText instanceof Editable) && mSuggestionRangeSpan != null) { ((Editable) mText).removeSpan(mSuggestionRangeSpan); } - mContainer.dismiss(); + mPopupWindow.dismiss(); } public boolean isShowing() { - return mContainer.isShowing(); + return mPopupWindow.isShowing(); } @Override @@ -9139,7 +9122,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } void positionAtCursor() { - View contentView = mContainer.getContentView(); + View contentView = mPopupWindow.getContentView(); int width = contentView.getMeasuredWidth(); int height = contentView.getMeasuredHeight(); final int offset = TextView.this.getSelectionStart(); @@ -9173,7 +9156,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener coords[0] = Math.min(displayMetrics.widthPixels - width, coords[0]); coords[0] = Math.max(0, coords[0]); - mContainer.showAtLocation(TextView.this, Gravity.NO_GRAVITY, coords[0], coords[1]); + mPopupWindow.showAtLocation(TextView.this, Gravity.NO_GRAVITY, coords[0], coords[1]); } } diff --git a/core/java/com/android/internal/content/NativeLibraryHelper.java b/core/java/com/android/internal/content/NativeLibraryHelper.java index 9ae7def..1531946 100644 --- a/core/java/com/android/internal/content/NativeLibraryHelper.java +++ b/core/java/com/android/internal/content/NativeLibraryHelper.java @@ -1,22 +1,9 @@ package com.android.internal.content; -import android.content.pm.PackageManager; import android.os.Build; -import android.os.FileUtils; -import android.os.SystemProperties; -import android.util.Log; -import android.util.Pair; import android.util.Slog; import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.util.Enumeration; -import java.util.LinkedList; -import java.util.List; -import java.util.zip.ZipEntry; -import java.util.zip.ZipException; -import java.util.zip.ZipFile; /** * Native libraries helper. @@ -28,270 +15,22 @@ public class NativeLibraryHelper { private static final boolean DEBUG_NATIVE = false; - /* - * The following constants are returned by listPackageSharedLibsForAbiLI - * to indicate if native shared libraries were found in the package. - * Values are: - * PACKAGE_INSTALL_NATIVE_FOUND_LIBRARIES => native libraries found and installed - * PACKAGE_INSTALL_NATIVE_NO_LIBRARIES => no native libraries in package - * PACKAGE_INSTALL_NATIVE_ABI_MISMATCH => native libraries for another ABI found - * in package (and not installed) - * - */ - private static final int PACKAGE_INSTALL_NATIVE_FOUND_LIBRARIES = 0; - private static final int PACKAGE_INSTALL_NATIVE_NO_LIBRARIES = 1; - private static final int PACKAGE_INSTALL_NATIVE_ABI_MISMATCH = 2; + private static native long nativeSumNativeBinaries(String file, String cpuAbi, String cpuAbi2); - // Directory in the APK that holds all the native shared libraries. - private static final String APK_LIB = "lib/"; - private static final int APK_LIB_LENGTH = APK_LIB.length(); - - // Prefix that native shared libraries must have. - private static final String LIB_PREFIX = "lib"; - private static final int LIB_PREFIX_LENGTH = LIB_PREFIX.length(); - - // Suffix that the native shared libraries must have. - private static final String LIB_SUFFIX = ".so"; - private static final int LIB_SUFFIX_LENGTH = LIB_SUFFIX.length(); - - // Name of the GDB binary. - private static final String GDBSERVER = "gdbserver"; - - // the minimum length of a valid native shared library of the form - // lib/<something>/lib<name>.so. - private static final int MIN_ENTRY_LENGTH = APK_LIB_LENGTH + 2 + LIB_PREFIX_LENGTH + 1 - + LIB_SUFFIX_LENGTH; - - /* - * Find all files of the form lib/<cpuAbi>/lib<name>.so in the .apk - * and add them to a list to be installed later. - * - * NOTE: this method may throw an IOException if the library cannot - * be copied to its final destination, e.g. if there isn't enough - * room left on the data partition, or a ZipException if the package - * file is malformed. - */ - private static int listPackageSharedLibsForAbiLI(ZipFile zipFile, - String cpuAbi, List<Pair<ZipEntry, String>> libEntries) throws IOException, - ZipException { - final int cpuAbiLen = cpuAbi.length(); - boolean hasNativeLibraries = false; - boolean installedNativeLibraries = false; - - if (DEBUG_NATIVE) { - Slog.d(TAG, "Checking " + zipFile.getName() + " for shared libraries of CPU ABI type " - + cpuAbi); - } - - Enumeration<? extends ZipEntry> entries = zipFile.entries(); - - while (entries.hasMoreElements()) { - ZipEntry entry = entries.nextElement(); - - // skip directories - if (entry.isDirectory()) { - continue; - } - String entryName = entry.getName(); - - /* - * Check that the entry looks like lib/<something>/lib<name>.so - * here, but don't check the ABI just yet. - * - * - must be sufficiently long - * - must end with LIB_SUFFIX, i.e. ".so" - * - must start with APK_LIB, i.e. "lib/" - */ - if (entryName.length() < MIN_ENTRY_LENGTH || !entryName.endsWith(LIB_SUFFIX) - || !entryName.startsWith(APK_LIB)) { - continue; - } - - // file name must start with LIB_PREFIX, i.e. "lib" - int lastSlash = entryName.lastIndexOf('/'); - - if (lastSlash < 0 - || !entryName.regionMatches(lastSlash + 1, LIB_PREFIX, 0, LIB_PREFIX_LENGTH)) { - continue; - } - - hasNativeLibraries = true; - - // check the cpuAbi now, between lib/ and /lib<name>.so - if (lastSlash != APK_LIB_LENGTH + cpuAbiLen - || !entryName.regionMatches(APK_LIB_LENGTH, cpuAbi, 0, cpuAbiLen)) - continue; - - /* - * Extract the library file name, ensure it doesn't contain - * weird characters. we're guaranteed here that it doesn't contain - * a directory separator though. - */ - String libFileName = entryName.substring(lastSlash+1); - if (!FileUtils.isFilenameSafe(new File(libFileName))) { - continue; - } - - installedNativeLibraries = true; - - if (DEBUG_NATIVE) { - Log.d(TAG, "Caching shared lib " + entry.getName()); - } - - libEntries.add(Pair.create(entry, libFileName)); - } - if (!hasNativeLibraries) - return PACKAGE_INSTALL_NATIVE_NO_LIBRARIES; - - if (!installedNativeLibraries) - return PACKAGE_INSTALL_NATIVE_ABI_MISMATCH; - - return PACKAGE_INSTALL_NATIVE_FOUND_LIBRARIES; + public static long sumNativeBinariesLI(File apkFile) { + final String cpuAbi = Build.CPU_ABI; + final String cpuAbi2 = Build.CPU_ABI2; + return nativeSumNativeBinaries(apkFile.getPath(), cpuAbi, cpuAbi2); } - /* - * Find the gdbserver executable program in a package at - * lib/<cpuAbi>/gdbserver and add it to the list of binaries - * to be copied out later. - * - * Returns PACKAGE_INSTALL_NATIVE_FOUND_LIBRARIES on success, - * or PACKAGE_INSTALL_NATIVE_NO_LIBRARIES otherwise. - */ - private static int listPackageGdbServerLI(ZipFile zipFile, String cpuAbi, - List<Pair<ZipEntry, String>> nativeFiles) throws IOException, ZipException { - final String apkGdbServerPath = "lib/" + cpuAbi + "/" + GDBSERVER; - - Enumeration<? extends ZipEntry> entries = zipFile.entries(); - - while (entries.hasMoreElements()) { - ZipEntry entry = entries.nextElement(); - // skip directories - if (entry.isDirectory()) { - continue; - } - String entryName = entry.getName(); - - if (!entryName.equals(apkGdbServerPath)) { - continue; - } - - if (false) { - Log.d(TAG, "Found gdbserver: " + entry.getName()); - } + private native static int nativeCopyNativeBinaries(String filePath, String sharedLibraryPath, + String cpuAbi, String cpuAbi2); - final String installGdbServerPath = GDBSERVER; - nativeFiles.add(Pair.create(entry, installGdbServerPath)); - - return PACKAGE_INSTALL_NATIVE_FOUND_LIBRARIES; - } - return PACKAGE_INSTALL_NATIVE_NO_LIBRARIES; - } - - /* - * Examine shared libraries stored in the APK as - * lib/<cpuAbi>/lib<name>.so and add them to a list to be copied - * later. - * - * This function will first try the main CPU ABI defined by Build.CPU_ABI - * (which corresponds to ro.product.cpu.abi), and also try an alternate - * one if ro.product.cpu.abi2 is defined. - */ - public static int listPackageNativeBinariesLI(ZipFile zipFile, - List<Pair<ZipEntry, String>> nativeFiles) throws ZipException, IOException { - String cpuAbi = Build.CPU_ABI; - - int result = listPackageSharedLibsForAbiLI(zipFile, cpuAbi, nativeFiles); - - /* - * Some architectures are capable of supporting several CPU ABIs - * for example, 'armeabi-v7a' also supports 'armeabi' native code - * this is indicated by the definition of the ro.product.cpu.abi2 - * system property. - * - * only scan the package twice in case of ABI mismatch - */ - if (result == PACKAGE_INSTALL_NATIVE_ABI_MISMATCH) { - final String cpuAbi2 = SystemProperties.get("ro.product.cpu.abi2", null); - if (cpuAbi2 != null) { - result = listPackageSharedLibsForAbiLI(zipFile, cpuAbi2, nativeFiles); - } - - if (result == PACKAGE_INSTALL_NATIVE_ABI_MISMATCH) { - Slog.w(TAG, "Native ABI mismatch from package file"); - return PackageManager.INSTALL_FAILED_INVALID_APK; - } - - if (result == PACKAGE_INSTALL_NATIVE_FOUND_LIBRARIES) { - cpuAbi = cpuAbi2; - } - } - - /* - * Debuggable packages may have gdbserver embedded, so add it to - * the list to the list of items to be extracted (as lib/gdbserver) - * into the application's native library directory later. - */ - if (result == PACKAGE_INSTALL_NATIVE_FOUND_LIBRARIES) { - listPackageGdbServerLI(zipFile, cpuAbi, nativeFiles); - } - return PackageManager.INSTALL_SUCCEEDED; - } - - public static int copyNativeBinariesLI(File scanFile, File sharedLibraryDir) { - /* - * Check all the native files that need to be copied and add - * that to the container size. - */ - ZipFile zipFile; - try { - zipFile = new ZipFile(scanFile); - - List<Pair<ZipEntry, String>> nativeFiles = new LinkedList<Pair<ZipEntry, String>>(); - - NativeLibraryHelper.listPackageNativeBinariesLI(zipFile, nativeFiles); - - final int N = nativeFiles.size(); - - for (int i = 0; i < N; i++) { - final Pair<ZipEntry, String> entry = nativeFiles.get(i); - - File destFile = new File(sharedLibraryDir, entry.second); - copyNativeBinaryLI(zipFile, entry.first, sharedLibraryDir, destFile); - } - zipFile.close(); - } catch (ZipException e) { - Slog.w(TAG, "Failed to extract data from package file", e); - return PackageManager.INSTALL_FAILED_INVALID_APK; - } catch (IOException e) { - Slog.w(TAG, "Failed to cache package shared libs", e); - return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; - } - - return PackageManager.INSTALL_SUCCEEDED; - } - - private static void copyNativeBinaryLI(ZipFile zipFile, ZipEntry entry, - File binaryDir, File binaryFile) throws IOException { - InputStream inputStream = zipFile.getInputStream(entry); - try { - File tempFile = File.createTempFile("tmp", "tmp", binaryDir); - String tempFilePath = tempFile.getPath(); - // XXX package manager can't change owner, so the executable files for - // now need to be left as world readable and owned by the system. - if (!FileUtils.copyToFile(inputStream, tempFile) - || !tempFile.setLastModified(entry.getTime()) - || FileUtils.setPermissions(tempFilePath, FileUtils.S_IRUSR | FileUtils.S_IWUSR - | FileUtils.S_IRGRP | FileUtils.S_IXUSR | FileUtils.S_IXGRP - | FileUtils.S_IXOTH | FileUtils.S_IROTH, -1, -1) != 0 - || !tempFile.renameTo(binaryFile)) { - // Failed to properly write file. - tempFile.delete(); - throw new IOException("Couldn't create cached binary " + binaryFile + " in " - + binaryDir); - } - } finally { - inputStream.close(); - } + public static int copyNativeBinariesIfNeededLI(File apkFile, File sharedLibraryDir) { + final String cpuAbi = Build.CPU_ABI; + final String cpuAbi2 = Build.CPU_ABI2; + return nativeCopyNativeBinaries(apkFile.getPath(), sharedLibraryDir.getPath(), cpuAbi, + cpuAbi2); } // Convenience method to call removeNativeBinariesFromDirLI(File) diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index 6ec186d..16336e0 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -474,7 +474,7 @@ public class ZygoteInit { String args[] = { "--setuid=1000", "--setgid=1000", - "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,3001,3002,3003", + "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,3001,3002,3003,3006", "--capabilities=130104352,130104352", "--runtime-init", "--nice-name=system_server", diff --git a/core/java/com/android/internal/view/menu/ActionMenuView.java b/core/java/com/android/internal/view/menu/ActionMenuView.java index bf2965b..bff621c 100644 --- a/core/java/com/android/internal/view/menu/ActionMenuView.java +++ b/core/java/com/android/internal/view/menu/ActionMenuView.java @@ -36,8 +36,8 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo private boolean mReserveOverflow; private ActionMenuPresenter mPresenter; - private boolean mUpdateContentsBeforeMeasure; private boolean mFormatItems; + private int mFormatItemsWidth; private int mMinCellSize; private int mMeasuredExtraWidth; @@ -71,19 +71,21 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo } @Override - public void requestLayout() { - // Layout can influence how many action items fit. - mUpdateContentsBeforeMeasure = true; - super.requestLayout(); - } - - @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // If we've been given an exact size to match, apply special formatting during layout. + final boolean wasFormatted = mFormatItems; mFormatItems = MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY; - if (mUpdateContentsBeforeMeasure && mMenu != null) { + + if (wasFormatted != mFormatItems) { + mFormatItemsWidth = 0; // Reset this when switching modes + } + + // Special formatting can change whether items can fit as action buttons. + // Kick the menu and update presenters when this changes. + final int widthSize = MeasureSpec.getMode(widthMeasureSpec); + if (mFormatItems && mMenu != null && widthSize != mFormatItemsWidth) { + mFormatItemsWidth = widthSize; mMenu.onItemsChanged(true); - mUpdateContentsBeforeMeasure = false; } if (mFormatItems) { diff --git a/core/java/com/android/internal/view/menu/MenuBuilder.java b/core/java/com/android/internal/view/menu/MenuBuilder.java index 159b3da..19cbe25 100644 --- a/core/java/com/android/internal/view/menu/MenuBuilder.java +++ b/core/java/com/android/internal/view/menu/MenuBuilder.java @@ -27,6 +27,7 @@ import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.Parcelable; +import android.util.Log; import android.util.SparseArray; import android.view.ActionProvider; import android.view.ContextMenu.ContextMenuInfo; @@ -47,7 +48,7 @@ import java.util.concurrent.CopyOnWriteArrayList; * standard menu UI. */ public class MenuBuilder implements Menu { - private static final String LOGTAG = "MenuBuilder"; + private static final String TAG = "MenuBuilder"; private static final String PRESENTER_KEY = "android:menu:presenters"; private static final String ACTION_VIEW_STATES_KEY = "android:menu:actionviewstates"; diff --git a/core/java/com/android/internal/view/menu/MenuItemImpl.java b/core/java/com/android/internal/view/menu/MenuItemImpl.java index b0a002d..8b53bb8 100644 --- a/core/java/com/android/internal/view/menu/MenuItemImpl.java +++ b/core/java/com/android/internal/view/menu/MenuItemImpl.java @@ -563,7 +563,7 @@ public final class MenuItemImpl implements MenuItem { public MenuItem setActionView(int resId) { final Context context = mMenu.getContext(); final LayoutInflater inflater = LayoutInflater.from(context); - setActionView(inflater.inflate(resId, new LinearLayout(context))); + setActionView(inflater.inflate(resId, new LinearLayout(context), false)); return this; } diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java index 09262e0..468f28e 100644 --- a/core/java/com/android/internal/widget/ActionBarView.java +++ b/core/java/com/android/internal/widget/ActionBarView.java @@ -1323,11 +1323,18 @@ public class ActionBarView extends AbsActionBarView { if (mExpandedActionView instanceof CollapsibleActionView) { ((CollapsibleActionView) mExpandedActionView).onActionViewExpanded(); } + return true; } @Override public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item) { + // Do this before detaching the actionview from the hierarchy, in case + // it needs to dismiss the soft keyboard, etc. + if (mExpandedActionView instanceof CollapsibleActionView) { + ((CollapsibleActionView) mExpandedActionView).onActionViewCollapsed(); + } + removeView(mExpandedActionView); removeView(mExpandedHomeLayout); if ((mDisplayOptions & ActionBar.DISPLAY_SHOW_HOME) != 0) { @@ -1349,16 +1356,12 @@ public class ActionBarView extends AbsActionBarView { if (mCustomNavView != null && (mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) { mCustomNavView.setVisibility(VISIBLE); } - View collapsedView = mExpandedActionView; mExpandedActionView = null; mExpandedHomeLayout.setIcon(null); mCurrentExpandedItem = null; requestLayout(); item.setActionViewExpanded(false); - if (collapsedView instanceof CollapsibleActionView) { - ((CollapsibleActionView) collapsedView).onActionViewCollapsed(); - } return true; } diff --git a/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java b/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java index 04bb689..94990b6 100644 --- a/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java +++ b/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java @@ -45,7 +45,7 @@ import com.android.internal.R; */ public class MultiWaveView extends View { private static final String TAG = "MultiWaveView"; - private static final boolean DEBUG = true; + private static final boolean DEBUG = false; // Wave state machine private static final int STATE_IDLE = 0; |
