diff options
65 files changed, 1506 insertions, 604 deletions
diff --git a/api/current.xml b/api/current.xml index 4996e6a..cbccf93 100644 --- a/api/current.xml +++ b/api/current.xml @@ -95457,6 +95457,32 @@ visibility="public" > </method> +<method name="hasPermission" + return="boolean" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="device" type="android.hardware.usb.UsbDevice"> +</parameter> +</method> +<method name="hasPermission" + return="boolean" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="accessory" type="android.hardware.usb.UsbAccessory"> +</parameter> +</method> <method name="isFunctionEnabled" return="boolean" abstract="false" @@ -95509,6 +95535,36 @@ <parameter name="device" type="android.hardware.usb.UsbDevice"> </parameter> </method> +<method name="requestPermission" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="device" type="android.hardware.usb.UsbDevice"> +</parameter> +<parameter name="pi" type="android.app.PendingIntent"> +</parameter> +</method> +<method name="requestPermission" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="accessory" type="android.hardware.usb.UsbAccessory"> +</parameter> +<parameter name="pi" type="android.app.PendingIntent"> +</parameter> +</method> <field name="ACTION_USB_ACCESSORY_ATTACHED" type="java.lang.String" transient="false" @@ -95586,6 +95642,17 @@ visibility="public" > </field> +<field name="EXTRA_PERMISSION_GRANTED" + type="java.lang.String" + transient="false" + volatile="false" + value=""permission"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="USB_CONFIGURATION" type="java.lang.String" transient="false" @@ -244856,6 +244923,25 @@ <parameter name="realm" type="java.lang.String"> </parameter> </method> +<method name="onReceivedLoginRequest" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="view" type="android.webkit.WebView"> +</parameter> +<parameter name="realm" type="java.lang.String"> +</parameter> +<parameter name="account" type="java.lang.String"> +</parameter> +<parameter name="args" type="java.lang.String"> +</parameter> +</method> <method name="onReceivedSslError" return="void" abstract="false" @@ -267304,7 +267390,7 @@ deprecated="not deprecated" visibility="public" > -<parameter name="arg0" type="T"> +<parameter name="t" type="T"> </parameter> </method> </interface> diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 539e946..cc1f81c 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -401,10 +401,10 @@ class ContextImpl extends Context { return new UiModeManager(); }}); - registerService(USB_SERVICE, new StaticServiceFetcher() { - public Object createStaticService() { + registerService(USB_SERVICE, new ServiceFetcher() { + public Object createService(ContextImpl ctx) { IBinder b = ServiceManager.getService(USB_SERVICE); - return new UsbManager(IUsbManager.Stub.asInterface(b)); + return new UsbManager(ctx, IUsbManager.Stub.asInterface(b)); }}); registerService(VIBRATOR_SERVICE, new ServiceFetcher() { diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 440cb54..efe2633 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -1150,8 +1150,12 @@ public class DevicePolicyManager { * fail (most commonly returning {@link #ENCRYPTION_STATUS_ACTIVE}). * * <p>This policy controls encryption of the secure (application data) storage area. Data - * written to other areas (e.g. the directory returned by - * {@link android.os.Environment#getExternalStorageDirectory()} may or may not be encrypted. + * written to other storage areas may or may not be encrypted, and this policy does not require + * or control the encryption of any other storage areas. + * There is one exception: If {@link android.os.Environment#isExternalStorageEmulated()} is + * {@code true}, then the directory returned by + * {@link android.os.Environment#getExternalStorageDirectory()} must be written to disk + * within the encrypted storage area. * * <p>Important Note: On some devices, it is possible to encrypt storage without requiring * the user to create a device PIN or Password. In this case, the storage is encrypted, but diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java index dd4096b..2111cce 100644 --- a/core/java/android/hardware/SensorManager.java +++ b/core/java/android/hardware/SensorManager.java @@ -2051,8 +2051,8 @@ public class SensorManager if (rv.length == 4) { Q[0] = rv[3]; } else { - //In this case, the w component of the quaternion is known to be a positive number - Q[0] = (float)Math.sqrt(1 - rv[0]*rv[0] - rv[1]*rv[1] - rv[2]*rv[2]); + Q[0] = 1 - rv[0]*rv[0] - rv[1]*rv[1] - rv[2]*rv[2]; + Q[0] = (Q[0] > 0) ? (float)Math.sqrt(Q[0]) : 0; } Q[1] = rv[0]; Q[2] = rv[1]; diff --git a/core/java/android/hardware/usb/IUsbManager.aidl b/core/java/android/hardware/usb/IUsbManager.aidl index be65bdb..495fa21 100644 --- a/core/java/android/hardware/usb/IUsbManager.aidl +++ b/core/java/android/hardware/usb/IUsbManager.aidl @@ -16,6 +16,7 @@ package android.hardware.usb; +import android.app.PendingIntent; import android.hardware.usb.UsbAccessory; import android.hardware.usb.UsbDevice; import android.os.Bundle; @@ -50,6 +51,25 @@ interface IUsbManager */ void setAccessoryPackage(in UsbAccessory accessory, String packageName); + /* Returns true if the caller has permission to access the device. */ + boolean hasDevicePermission(in UsbDevice device); + + /* Returns true if the caller has permission to access the accessory. */ + boolean hasAccessoryPermission(in UsbAccessory accessory); + + /* Requests permission for the given package to access the device. + * Will display a system dialog to query the user if permission + * had not already been given. + */ + void requestDevicePermission(in UsbDevice device, String packageName, in PendingIntent pi); + + /* Requests permission for the given package to access the accessory. + * Will display a system dialog to query the user if permission + * had not already been given. Result is returned via pi. + */ + void requestAccessoryPermission(in UsbAccessory accessory, String packageName, + in PendingIntent pi); + /* Grants permission for the given UID to access the device */ void grantDevicePermission(in UsbDevice device, int uid); @@ -57,8 +77,8 @@ interface IUsbManager void grantAccessoryPermission(in UsbAccessory accessory, int uid); /* Returns true if the USB manager has default preferences or permissions for the package */ - boolean hasDefaults(String packageName, int uid); + boolean hasDefaults(String packageName); /* Clears default preferences and permissions for the package */ - oneway void clearDefaults(String packageName, int uid); + oneway void clearDefaults(String packageName); } diff --git a/core/java/android/hardware/usb/UsbAccessory.java b/core/java/android/hardware/usb/UsbAccessory.java index 6cd9178..7d66caa 100644 --- a/core/java/android/hardware/usb/UsbAccessory.java +++ b/core/java/android/hardware/usb/UsbAccessory.java @@ -109,6 +109,14 @@ public class UsbAccessory implements Parcelable { } @Override + public int hashCode() { + return ((mManufacturer == null ? 0 : mManufacturer.hashCode()) ^ + (mModel == null ? 0 : mModel.hashCode()) ^ + (mType == null ? 0 : mType.hashCode()) ^ + (mVersion == null ? 0 : mVersion.hashCode())); + } + + @Override public String toString() { return "UsbAccessory[mManufacturer=" + mManufacturer + ", mModel=" + mModel + diff --git a/core/java/android/hardware/usb/UsbDevice.java b/core/java/android/hardware/usb/UsbDevice.java index 37bd82b..39254b38 100644 --- a/core/java/android/hardware/usb/UsbDevice.java +++ b/core/java/android/hardware/usb/UsbDevice.java @@ -270,6 +270,11 @@ public final class UsbDevice implements Parcelable { } @Override + public int hashCode() { + return mName.hashCode(); + } + + @Override public String toString() { return "UsbDevice[mName=" + mName + ",mVendorId=" + mVendorId + ",mProductId=" + mProductId + ",mClass=" + mClass + diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java index 6683179..e80c744 100644 --- a/core/java/android/hardware/usb/UsbManager.java +++ b/core/java/android/hardware/usb/UsbManager.java @@ -17,6 +17,8 @@ package android.hardware.usb; +import android.app.PendingIntent; +import android.content.Context; import android.os.Bundle; import android.os.ParcelFileDescriptor; import android.os.RemoteException; @@ -98,7 +100,7 @@ public class UsbManager { * * This intent is sent when a USB accessory is detached. * <ul> - * <li> {@link #EXTRA_ACCESSORY} containing the {@link android.hardware.usb.UsbAccessory} + * <li> {@link #EXTRA_ACCESSORY} containing the {@link UsbAccessory} * for the attached accessory that was detached * </ul> */ @@ -176,12 +178,22 @@ public class UsbManager { */ public static final String EXTRA_ACCESSORY = "accessory"; - private IUsbManager mService; + /** + * Name of extra added to the {@link android.app.PendingIntent} + * passed into {@link #requestPermission(UsbDevice, PendingIntent)} + * or {@link #requestPermission(UsbAccessory, PendingIntent)} + * containing a boolean value indicating whether the user granted permission or not. + */ + public static final String EXTRA_PERMISSION_GRANTED = "permission"; + + private final Context mContext; + private final IUsbManager mService; /** * {@hide} */ - public UsbManager(IUsbManager service) { + public UsbManager(Context context, IUsbManager service) { + mContext = context; mService = service; } @@ -245,7 +257,7 @@ public class UsbManager { return new UsbAccessory[] { accessory }; } } catch (RemoteException e) { - Log.e(TAG, "RemoteException in getAccessoryList" , e); + Log.e(TAG, "RemoteException in getAccessoryList", e); return null; } } @@ -260,11 +272,99 @@ public class UsbManager { try { return mService.openAccessory(accessory); } catch (RemoteException e) { - Log.e(TAG, "RemoteException in openAccessory" , e); + Log.e(TAG, "RemoteException in openAccessory", e); return null; } } + /** + * Returns true if the caller has permission to access the device. + * Permission might have been granted temporarily via + * {@link #requestPermission(UsbDevice, PendingIntent)} or + * by the user choosing the caller as the default application for the device. + * + * @param device to check permissions for + * @return true if caller has permission + */ + public boolean hasPermission(UsbDevice device) { + try { + return mService.hasDevicePermission(device); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in hasPermission", e); + return false; + } + } + + /** + * Returns true if the caller has permission to access the accessory. + * Permission might have been granted temporarily via + * {@link #requestPermission(UsbAccessory, PendingIntent)} or + * by the user choosing the caller as the default application for the accessory. + * + * @param accessory to check permissions for + * @return true if caller has permission + */ + public boolean hasPermission(UsbAccessory accessory) { + try { + return mService.hasAccessoryPermission(accessory); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in hasPermission", e); + return false; + } + } + + /** + * Requests temporary permission for the given package to access the device. + * This may result in a system dialog being displayed to the user + * if permission had not already been granted. + * Success or failure is returned via the {@link android.app.PendingIntent} pi. + * If successful, this grants the caller permission to access the device only + * until the device is disconnected. + * + * The following extras will be added to pi: + * <ul> + * <li> {@link #EXTRA_DEVICE} containing the device passed into this call + * <li> {@link #EXTRA_PERMISSION_GRANTED} containing boolean indicating whether + * permission was granted by the user + * </ul> + * + * @param device to request permissions for + * @param pi PendingIntent for returning result + */ + public void requestPermission(UsbDevice device, PendingIntent pi) { + try { + mService.requestDevicePermission(device, mContext.getPackageName(), pi); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in requestPermission", e); + } + } + + /** + * Requests temporary permission for the given package to access the accessory. + * This may result in a system dialog being displayed to the user + * if permission had not already been granted. + * Success or failure is returned via the {@link android.app.PendingIntent} pi. + * If successful, this grants the caller permission to access the device only + * until the device is disconnected. + * + * The following extras will be added to pi: + * <ul> + * <li> {@link #EXTRA_ACCESSORY} containing the accessory passed into this call + * <li> {@link #EXTRA_PERMISSION_GRANTED} containing boolean indicating whether + * permission was granted by the user + * </ul> + * + * @param accessory to request permissions for + * @param pi PendingIntent for returning result + */ + public void requestPermission(UsbAccessory accessory, PendingIntent pi) { + try { + mService.requestAccessoryPermission(accessory, mContext.getPackageName(), pi); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in requestPermission", e); + } + } + private static File getFunctionEnableFile(String function) { return new File("/sys/class/usb_composite/" + function + "/enable"); } diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java index ec5030c..e308c2c 100644 --- a/core/java/android/os/Environment.java +++ b/core/java/android/os/Environment.java @@ -423,9 +423,16 @@ public class Environment { /** * Returns whether the device has an external storage device which is - * emulated. If true, the device does not have real external storage - * and certain system services such as the package manager use this + * emulated. If true, the device does not have real external storage, and the directory + * returned by {@link #getExternalStorageDirectory()} will be allocated using a portion of + * the internal storage system. + * + * <p>Certain system services, such as the package manager, use this * to determine where to install an application. + * + * <p>Emulated external storage may also be encrypted - see + * {@link android.app.admin.DevicePolicyManager#setStorageEncryption( + * android.content.ComponentName, boolean)} for additional details. */ public static boolean isExternalStorageEmulated() { if (mIsExternalStorageEmulated == null) { diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java index c078c08..0cf7ae6 100644 --- a/core/java/android/view/HardwareRenderer.java +++ b/core/java/android/view/HardwareRenderer.java @@ -609,7 +609,7 @@ public abstract class HardwareRenderer { DisplayList displayList = view.getDisplayList(); if (displayList != null) { if (canvas.drawDisplayList(displayList, mRedrawClip)) { - if (mRedrawClip.isEmpty()) { + if (mRedrawClip.isEmpty() || view.getParent() == null) { view.invalidate(); } else { view.getParent().invalidateChild(view, mRedrawClip); diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 6ef680b..f9692da 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -3680,7 +3680,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager // flag coming from the child that initiated the invalidate if (view != null) { if ((view.mViewFlags & FADING_EDGE_MASK) != 0 && - view.getSolidColor() == 0 && !view.isOpaque()) { + view.getSolidColor() == 0) { opaqueFlag = DIRTY; } if ((view.mPrivateFlags & DIRTY_MASK) != DIRTY) { diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java index ab4bfe1..c7a7374 100644 --- a/core/java/android/webkit/BrowserFrame.java +++ b/core/java/android/webkit/BrowserFrame.java @@ -1237,11 +1237,17 @@ class BrowserFrame extends Handler { } } - /*package*/ SearchBox getSearchBox() { return mSearchBox; } + /** + * Called by JNI when processing the X-Auto-Login header. + */ + private void autoLogin(String realm, String account, String args) { + mCallbackProxy.onReceivedLoginRequest(realm, account, args); + } + //========================================================================== // native functions //========================================================================== diff --git a/core/java/android/webkit/CallbackProxy.java b/core/java/android/webkit/CallbackProxy.java index c66d701..23fd12d 100644 --- a/core/java/android/webkit/CallbackProxy.java +++ b/core/java/android/webkit/CallbackProxy.java @@ -117,6 +117,7 @@ class CallbackProxy extends Handler { private static final int AUTH_CREDENTIALS = 137; private static final int SET_INSTALLABLE_WEBAPP = 138; private static final int NOTIFY_SEARCHBOX_LISTENERS = 139; + private static final int AUTO_LOGIN = 140; // Message triggered by the client to resume execution private static final int NOTIFY = 200; @@ -770,7 +771,7 @@ class CallbackProxy extends Handler { (WebHistoryItem) msg.obj, msg.arg1); } break; - case AUTH_CREDENTIALS: + case AUTH_CREDENTIALS: { String host = msg.getData().getString("host"); String realm = msg.getData().getString("realm"); username = msg.getData().getString("username"); @@ -778,6 +779,7 @@ class CallbackProxy extends Handler { mWebView.setHttpAuthUsernamePassword( host, realm, username, password); break; + } case SET_INSTALLABLE_WEBAPP: if (mWebChromeClient != null) { mWebChromeClient.setInstallableWebApp(); @@ -789,6 +791,17 @@ class CallbackProxy extends Handler { @SuppressWarnings("unchecked") List<String> suggestions = (List<String>) msg.obj; searchBox.handleSuggestions(msg.getData().getString("query"), suggestions); + break; + case AUTO_LOGIN: { + if (mWebViewClient != null) { + String realm = msg.getData().getString("realm"); + String account = msg.getData().getString("account"); + String args = msg.getData().getString("args"); + mWebViewClient.onReceivedLoginRequest(mWebView, realm, + account, args); + } + break; + } } } @@ -1051,6 +1064,20 @@ class CallbackProxy extends Handler { sendMessage(msg); } + void onReceivedLoginRequest(String realm, String account, String args) { + // Do an unsynchronized quick check to avoid posting if no callback has + // been set. + if (mWebViewClient == null) { + return; + } + Message msg = obtainMessage(AUTO_LOGIN); + Bundle bundle = msg.getData(); + bundle.putString("realm", realm); + bundle.putString("account", account); + bundle.putString("args", args); + sendMessage(msg); + } + //-------------------------------------------------------------------------- // DownloadListener functions. //-------------------------------------------------------------------------- diff --git a/core/java/android/webkit/DebugFlags.java b/core/java/android/webkit/DebugFlags.java index 2dac7e9..3cb5e24 100644 --- a/core/java/android/webkit/DebugFlags.java +++ b/core/java/android/webkit/DebugFlags.java @@ -31,7 +31,7 @@ class DebugFlags { public static final boolean CACHE_MANAGER = false; public static final boolean CALLBACK_PROXY = false; public static final boolean COOKIE_MANAGER = false; - public static final boolean COOKIE_SYNC_MANAGER = true; + public static final boolean COOKIE_SYNC_MANAGER = false; public static final boolean FRAME_LOADER = false; public static final boolean J_WEB_CORE_JAVA_BRIDGE = false;// HIGHLY VERBOSE public static final boolean LOAD_LISTENER = false; @@ -41,7 +41,7 @@ class DebugFlags { public static final boolean URL_UTIL = false; public static final boolean WEB_BACK_FORWARD_LIST = false; public static final boolean WEB_SETTINGS = false; - public static final boolean WEB_SYNC_MANAGER = true; + public static final boolean WEB_SYNC_MANAGER = false; public static final boolean WEB_TEXT_VIEW = false; public static final boolean WEB_VIEW = false; public static final boolean WEB_VIEW_CORE = false; diff --git a/core/java/android/webkit/WebTextView.java b/core/java/android/webkit/WebTextView.java index 28b0951..0f24edc 100644 --- a/core/java/android/webkit/WebTextView.java +++ b/core/java/android/webkit/WebTextView.java @@ -26,14 +26,15 @@ import android.graphics.Paint; import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.drawable.Drawable; +import android.os.Bundle; import android.os.Handler; import android.os.Message; +import android.os.ResultReceiver; import android.text.BoringLayout.Metrics; import android.text.DynamicLayout; import android.text.Editable; import android.text.InputFilter; import android.text.Layout; -import android.text.Layout.Alignment; import android.text.Selection; import android.text.Spannable; import android.text.TextPaint; @@ -136,6 +137,23 @@ import junit.framework.Assert; // Used to determine whether onFocusChanged was called as a result of // calling remove(). private boolean mInsideRemove; + private class MyResultReceiver extends ResultReceiver { + @Override + protected void onReceiveResult(int resultCode, Bundle resultData) { + if (resultCode == InputMethodManager.RESULT_SHOWN + && mWebView != null) { + mWebView.revealSelection(); + } + } + + /** + * @param handler + */ + public MyResultReceiver(Handler handler) { + super(handler); + } + } + private MyResultReceiver mReceiver; // Types used with setType. Keep in sync with CachedInput.h private static final int NORMAL_TEXT_FIELD = 0; @@ -184,7 +202,7 @@ import junit.framework.Assert; } } }; - + mReceiver = new MyResultReceiver(mHandler); } public void setAutoFillable(int queryId) { @@ -362,6 +380,8 @@ import junit.framework.Assert; } } + /* package */ ResultReceiver getResultReceiver() { return mReceiver; } + /** * Determine whether this WebTextView currently represents the node * represented by ptr. diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 1316235..705eefc 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -497,15 +497,6 @@ public class WebView extends AbsoluteLayout // default is not set, the UI will continue handle them. private boolean mDeferTouchProcess; - // Currently, multi-touch events are sent to WebKit first then back to - // WebView while single-touch events are handled in WebView first. - // So there is a chance that a single-touch move event is handled in WebView - // before multi-touch events are finished. - // if mIsHandlingMultiTouch is true, which means multi-touch event handling - // is not finished, then any single-touch move event will be skipped. - // FIXME: send single-touch events to WebKit first then back to WebView. - private boolean mIsHandlingMultiTouch = false; - // to avoid interfering with the current touch events, track them // separately. Currently no snapping or fling in the deferred process mode private int mDeferTouchMode = TOUCH_DONE_MODE; @@ -4036,15 +4027,10 @@ public class WebView extends AbsoluteLayout } } - void setBaseLayer(int layer, Rect invalRect, boolean showVisualIndciator) { + void setBaseLayer(int layer, Region invalRegion, boolean showVisualIndicator) { if (mNativeClass == 0) return; - if (invalRect == null) { - Rect rect = new Rect(0, 0, mContentWidth, mContentHeight); - nativeSetBaseLayer(layer, rect, showVisualIndciator); - } else { - nativeSetBaseLayer(layer, invalRect, showVisualIndciator); - } + nativeSetBaseLayer(layer, invalRegion, showVisualIndicator); } private void onZoomAnimationStart() { @@ -4284,7 +4270,7 @@ public class WebView extends AbsoluteLayout if (isTextView) { rebuildWebTextView(); if (inEditingMode()) { - imm.showSoftInput(mWebTextView, 0); + imm.showSoftInput(mWebTextView, 0, mWebTextView.getResultReceiver()); if (zoom) { didUpdateWebTextViewDimensions(INTERSECTS_SCREEN); } @@ -5479,7 +5465,6 @@ public class WebView extends AbsoluteLayout case MotionEvent.ACTION_DOWN: { mPreventDefault = PREVENT_DEFAULT_NO; mConfirmMove = false; - mIsHandlingMultiTouch = false; mInitialHitTestResult = null; if (!mScroller.isFinished()) { // stop the current scroll animation, but if this is @@ -5571,7 +5556,7 @@ public class WebView extends AbsoluteLayout ted.mReprocess = mDeferTouchProcess; ted.mNativeLayer = nativeScrollableLayer( contentX, contentY, ted.mNativeLayerRect, null); - ted.mDontEnqueueResult = true; + ted.mSequence = mTouchEventQueue.nextTouchSequence(); mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted); if (mDeferTouchProcess) { // still needs to set them for compute deltaX/Y @@ -5618,7 +5603,7 @@ public class WebView extends AbsoluteLayout ted.mReprocess = mDeferTouchProcess; ted.mNativeLayer = mScrollingLayer; ted.mNativeLayerRect.set(mScrollingLayerRect); - ted.mDontEnqueueResult = true; + ted.mSequence = mTouchEventQueue.nextTouchSequence(); mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted); mLastSentTouchTime = eventTime; if (mDeferTouchProcess) { @@ -5800,7 +5785,7 @@ public class WebView extends AbsoluteLayout ted.mReprocess = mDeferTouchProcess; ted.mNativeLayer = mScrollingLayer; ted.mNativeLayerRect.set(mScrollingLayerRect); - ted.mDontEnqueueResult = true; + ted.mSequence = mTouchEventQueue.nextTouchSequence(); mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted); } mLastTouchUpTime = eventTime; @@ -5823,7 +5808,7 @@ public class WebView extends AbsoluteLayout ted.mNativeLayer = nativeScrollableLayer( contentX, contentY, ted.mNativeLayerRect, null); - ted.mDontEnqueueResult = true; + ted.mSequence = mTouchEventQueue.nextTouchSequence(); mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted); } else if (mPreventDefault != PREVENT_DEFAULT_YES){ mZoomManager.handleDoubleTap(mLastTouchX, mLastTouchY); @@ -6017,7 +6002,6 @@ public class WebView extends AbsoluteLayout // set mLastTouchX/Y to the remaining point mLastTouchX = Math.round(x); mLastTouchY = Math.round(y); - mIsHandlingMultiTouch = false; } else if (action == MotionEvent.ACTION_MOVE) { // negative x or y indicate it is on the edge, skip it. if (x < 0 || y < 0) { @@ -6041,7 +6025,7 @@ public class WebView extends AbsoluteLayout ted.mAction = MotionEvent.ACTION_CANCEL; ted.mNativeLayer = nativeScrollableLayer( x, y, ted.mNativeLayerRect, null); - ted.mDontEnqueueResult = true; + ted.mSequence = mTouchEventQueue.nextTouchSequence(); mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted); mPreventDefault = PREVENT_DEFAULT_IGNORE; } @@ -7291,8 +7275,6 @@ public class WebView extends AbsoluteLayout private void handleQueuedMotionEvent(MotionEvent ev) { int action = ev.getActionMasked(); if (ev.getPointerCount() > 1) { // Multi-touch - mIsHandlingMultiTouch = true; - handleMultiTouchInWebView(ev); } else { final ScaleGestureDetector detector = mZoomManager.getMultiTouchGestureDetector(); @@ -7331,7 +7313,6 @@ public class WebView extends AbsoluteLayout if (ted.mPoints.length > 1) { // multi-touch if (ted.mAction == MotionEvent.ACTION_POINTER_UP && ted.mMotionEvent.getPointerCount() == 2) { - mIsHandlingMultiTouch = false; } if (!ted.mNativeResult) { mPreventDefault = PREVENT_DEFAULT_NO; @@ -7525,7 +7506,7 @@ public class WebView extends AbsoluteLayout ted.mNativeLayer = nativeScrollableLayer( ted.mPoints[0].x, ted.mPoints[0].y, ted.mNativeLayerRect, null); - ted.mDontEnqueueResult = true; + ted.mSequence = mTouchEventQueue.nextTouchSequence(); mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted); } else if (mPreventDefault != PREVENT_DEFAULT_YES) { mTouchMode = TOUCH_DONE_MODE; @@ -7571,7 +7552,7 @@ public class WebView extends AbsoluteLayout case NEW_PICTURE_MSG_ID: { // called for new content final WebViewCore.DrawData draw = (WebViewCore.DrawData) msg.obj; - setBaseLayer(draw.mBaseLayer, draw.mInvalRegion.getBounds(), + setBaseLayer(draw.mBaseLayer, draw.mInvalRegion, getSettings().getShowVisualIndicator()); final Point viewSize = draw.mViewSize; WebViewCore.ViewState viewState = draw.mViewState; @@ -7741,9 +7722,12 @@ public class WebView extends AbsoluteLayout break; } TouchEventData ted = (TouchEventData) msg.obj; - if (!ted.mDontEnqueueResult) { - mTouchEventQueue.enqueueTouchEvent(ted); - } + + // WebCore is responding to us; remove pending timeout. + // It will be re-posted when needed. + removeMessages(PREVENT_DEFAULT_TIMEOUT); + + mTouchEventQueue.enqueueTouchEvent(ted); break; case REQUEST_KEYBOARD: @@ -8613,7 +8597,7 @@ public class WebView extends AbsoluteLayout private native void nativeSetFindIsEmpty(); private native void nativeSetFindIsUp(boolean isUp); private native void nativeSetHeightCanMeasure(boolean measure); - private native void nativeSetBaseLayer(int layer, Rect invalRect, + private native void nativeSetBaseLayer(int layer, Region invalRegion, boolean showVisualIndicator); private native void nativeShowCursorTimed(); private native void nativeReplaceBaseContent(int content); diff --git a/core/java/android/webkit/WebViewClient.java b/core/java/android/webkit/WebViewClient.java index db605de..65026a5 100644 --- a/core/java/android/webkit/WebViewClient.java +++ b/core/java/android/webkit/WebViewClient.java @@ -256,4 +256,18 @@ public class WebViewClient { */ public void onScaleChanged(WebView view, float oldScale, float newScale) { } + + /** + * Notify the host application that a request to automatically log in the + * user has been processed. + * @param view The WebView requesting the login. + * @param realm The account realm used to look up accounts. + * @param account An optional account. If not null, the account should be + * checked against accounts on the device. If it is a valid + * account, it should be used to log in the user. + * @param args Authenticator specific arguments used to log in the user. + */ + public void onReceivedLoginRequest(WebView view, String realm, + String account, String args) { + } } diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index f367b93..b920a30 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -827,7 +827,6 @@ final class WebViewCore { Rect mNativeLayerRect = new Rect(); long mSequence; boolean mNativeResult; - boolean mDontEnqueueResult; } static class GeolocationPermissionsData { diff --git a/core/java/android/webkit/ZoomManager.java b/core/java/android/webkit/ZoomManager.java index 942425a..716a215 100644 --- a/core/java/android/webkit/ZoomManager.java +++ b/core/java/android/webkit/ZoomManager.java @@ -1008,14 +1008,15 @@ class ZoomManager { final Point viewSize = drawData.mViewSize; updateZoomRange(viewState, viewSize.x, drawData.mMinPrefWidth); setupZoomOverviewWidth(drawData, mWebView.getViewWidth()); + final float overviewScale = getZoomOverviewScale(); if (!mMinZoomScaleFixed) { - mMinZoomScale = getZoomOverviewScale(); + mMinZoomScale = (mInitialScale > 0) ? + Math.min(mInitialScale, overviewScale) : overviewScale; mMaxZoomScale = Math.max(mMaxZoomScale, mMinZoomScale); } if (!mWebView.drawHistory()) { float scale; - final float overviewScale = getZoomOverviewScale(); WebSettings settings = mWebView.getSettings(); if (mInitialScale > 0) { diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java index d92588c..17b3bda 100644 --- a/core/java/android/widget/HorizontalScrollView.java +++ b/core/java/android/widget/HorizontalScrollView.java @@ -477,8 +477,7 @@ public class HorizontalScrollView extends FrameLayout { break; case MotionEvent.ACTION_POINTER_DOWN: { final int index = ev.getActionIndex(); - final float x = ev.getX(index); - mLastMotionX = x; + mLastMotionX = ev.getX(index); mActivePointerId = ev.getPointerId(index); break; } @@ -1446,6 +1445,7 @@ public class HorizontalScrollView extends FrameLayout { super.setOverScrollMode(mode); } + @SuppressWarnings({"SuspiciousNameCombination"}) @Override public void draw(Canvas canvas) { super.draw(canvas); diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java index 2d164fd..6088654 100644 --- a/core/java/android/widget/SearchView.java +++ b/core/java/android/widget/SearchView.java @@ -83,6 +83,7 @@ public class SearchView extends LinearLayout { private CursorAdapter mSuggestionsAdapter; private View mSearchButton; private View mSubmitButton; + private View mSearchPlate; private View mSubmitArea; private ImageView mCloseButton; private View mSearchEditFrame; @@ -190,6 +191,7 @@ public class SearchView extends LinearLayout { mQueryTextView.setSearchView(this); mSearchEditFrame = findViewById(R.id.search_edit_frame); + mSearchPlate = findViewById(R.id.search_plate); mSubmitArea = findViewById(R.id.submit_area); mSubmitButton = findViewById(R.id.search_go_btn); mCloseButton = (ImageView) findViewById(R.id.search_close_btn); @@ -258,6 +260,7 @@ public class SearchView extends LinearLayout { mSearchable = searchable; if (mSearchable != null) { updateSearchAutoComplete(); + updateQueryHint(); } // Cache the voice search capability mVoiceButtonEnabled = hasVoiceSearch(); @@ -575,19 +578,19 @@ public class SearchView extends LinearLayout { } private void updateSubmitButton(boolean hasText) { - mSubmitButton.setVisibility( - isSubmitAreaEnabled() ? (hasText ? VISIBLE : INVISIBLE) : GONE); + int visibility = GONE; + if (isSubmitAreaEnabled() && hasFocus() && (hasText || !mVoiceButtonEnabled)) { + visibility = VISIBLE; + } + mSubmitButton.setVisibility(visibility); } private void updateSubmitArea() { int visibility = GONE; - if (isSubmitAreaEnabled()) { - if (mSubmitButton.getVisibility() == VISIBLE - || mVoiceButton.getVisibility() == VISIBLE) { - visibility = VISIBLE; - } else { - visibility = INVISIBLE; - } + if (isSubmitAreaEnabled() + && (mSubmitButton.getVisibility() == VISIBLE + || mVoiceButton.getVisibility() == VISIBLE)) { + visibility = VISIBLE; } mSubmitArea.setVisibility(visibility); } @@ -601,6 +604,11 @@ public class SearchView extends LinearLayout { mCloseButton.getDrawable().setState(hasText ? ENABLED_STATE_SET : EMPTY_STATE_SET); } + private void updateFocusedState(boolean focused) { + mSearchPlate.getBackground().setState(focused ? FOCUSED_STATE_SET : EMPTY_STATE_SET); + mSubmitArea.getBackground().setState(focused ? FOCUSED_STATE_SET : EMPTY_STATE_SET); + } + private void setImeVisibility(boolean visible) { InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE); @@ -851,16 +859,11 @@ public class SearchView extends LinearLayout { * Update the visibility of the voice button. There are actually two voice search modes, * either of which will activate the button. * @param empty whether the search query text field is empty. If it is, then the other - * criteria apply to make the voice button visible. Otherwise the voice button will not - * be visible - i.e., if the user has typed a query, remove the voice button. + * criteria apply to make the voice button visible. */ private void updateVoiceButton(boolean empty) { - // If the voice button is to be visible, show it - // Else, make it gone if the submit button is enabled, otherwise invisible to - // avoid losing the real-estate - int visibility = mSubmitButtonEnabled ? GONE : INVISIBLE; - - if (mVoiceButtonEnabled && !isIconified() && empty) { + int visibility = GONE; + if (mVoiceButtonEnabled && !isIconified() && (empty || !mSubmitButtonEnabled)) { visibility = VISIBLE; mSubmitButton.setVisibility(GONE); } @@ -958,7 +961,8 @@ public class SearchView extends LinearLayout { } void onTextFocusChanged() { - updateCloseButton(); + updateViewsVisibility(isIconified()); + updateFocusedState(mQueryTextView.hasFocus()); } private boolean onItemClicked(int position, int actionKey, String actionMsg) { diff --git a/core/java/android/widget/StackView.java b/core/java/android/widget/StackView.java index bab469b..21c61bd 100644 --- a/core/java/android/widget/StackView.java +++ b/core/java/android/widget/StackView.java @@ -531,6 +531,8 @@ public class StackView extends AdapterViewAnimator { @Override protected void dispatchDraw(Canvas canvas) { + boolean expandClipRegion = false; + canvas.getClipBounds(stackInvalidateRect); final int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { @@ -540,12 +542,22 @@ public class StackView extends AdapterViewAnimator { child.getAlpha() == 0f || child.getVisibility() != VISIBLE) { lp.resetInvalidateRect(); } - stackInvalidateRect.union(lp.getInvalidateRect()); + Rect childInvalidateRect = lp.getInvalidateRect(); + if (!childInvalidateRect.isEmpty()) { + expandClipRegion = true; + stackInvalidateRect.union(childInvalidateRect); + } + } + + // We only expand the clip bounds if necessary. + if (expandClipRegion) { + canvas.save(Canvas.CLIP_SAVE_FLAG); + canvas.clipRect(stackInvalidateRect, Region.Op.UNION); + super.dispatchDraw(canvas); + canvas.restore(); + } else { + super.dispatchDraw(canvas); } - canvas.save(Canvas.CLIP_SAVE_FLAG); - canvas.clipRect(stackInvalidateRect, Region.Op.UNION); - super.dispatchDraw(canvas); - canvas.restore(); } private void onLayout() { diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java index a41b348..586ba87 100644 --- a/core/java/com/android/internal/widget/ActionBarView.java +++ b/core/java/com/android/internal/widget/ActionBarView.java @@ -43,6 +43,7 @@ import android.view.ViewGroup; import android.view.ViewParent; import android.view.Window; import android.widget.AdapterView; +import android.widget.FrameLayout; import android.widget.HorizontalScrollView; import android.widget.ImageView; import android.widget.LinearLayout; @@ -957,4 +958,60 @@ public class ActionBarView extends ViewGroup { } } } + + private static class HomeView extends FrameLayout { + private View mUpView; + private View mIconView; + + public HomeView(Context context) { + this(context, null); + } + + public HomeView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void onFinishInflate() { + mUpView = findViewById(com.android.internal.R.id.up); + mIconView = (ImageView) findViewById(com.android.internal.R.id.home); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + measureChildWithMargins(mUpView, widthMeasureSpec, 0, heightMeasureSpec, 0); + final LayoutParams upLp = (LayoutParams) mUpView.getLayoutParams(); + int width = upLp.leftMargin + mUpView.getMeasuredWidth() + upLp.rightMargin; + int height = upLp.topMargin + mUpView.getMeasuredHeight() + upLp.bottomMargin; + measureChildWithMargins(mIconView, widthMeasureSpec, width, heightMeasureSpec, 0); + final LayoutParams iconLp = (LayoutParams) mIconView.getLayoutParams(); + width += iconLp.leftMargin + mIconView.getMeasuredWidth() + iconLp.rightMargin; + height = Math.max(height, + iconLp.topMargin + mIconView.getMeasuredHeight() + iconLp.bottomMargin); + setMeasuredDimension(width, height); + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + final int vCenter = (b - t) / 2; + int width = r - l; + if (mUpView.getVisibility() != GONE) { + final LayoutParams upLp = (LayoutParams) mUpView.getLayoutParams(); + final int upHeight = mUpView.getMeasuredHeight(); + final int upWidth = mUpView.getMeasuredWidth(); + final int upTop = t + vCenter - upHeight / 2; + mUpView.layout(l, upTop, l + upWidth, upTop + upHeight); + final int upOffset = upLp.leftMargin + upWidth + upLp.rightMargin; + width -= upOffset; + l += upOffset; + } + final LayoutParams iconLp = (LayoutParams) mIconView.getLayoutParams(); + final int iconHeight = mIconView.getMeasuredHeight(); + final int iconWidth = mIconView.getMeasuredWidth(); + final int hCenter = (r - l) / 2; + final int iconLeft = l + iconLp.leftMargin + hCenter - iconWidth / 2; + final int iconTop = t + iconLp.topMargin + vCenter - iconHeight / 2; + mIconView.layout(iconLeft, iconTop, iconLeft + iconWidth, iconTop + iconHeight); + } + } } diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp index f78f83c..b6619ab 100644 --- a/core/jni/android_media_AudioRecord.cpp +++ b/core/jni/android_media_AudioRecord.cpp @@ -315,7 +315,11 @@ static jint android_media_AudioRecord_readInByteArray(JNIEnv *env, jobject thiz } // get the pointer to where we'll record the audio - recordBuff = (jbyte *)env->GetPrimitiveArrayCritical(javaAudioData, NULL); + // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such + // a way that it becomes much more efficient. When doing so, we will have to prevent the + // AudioSystem callback to be called while in critical section (in case of media server + // process crash for instance) + recordBuff = (jbyte *)env->GetByteArrayElements(javaAudioData, NULL); if (recordBuff == NULL) { LOGE("Error retrieving destination for recorded audio data, can't record"); @@ -327,7 +331,7 @@ static jint android_media_AudioRecord_readInByteArray(JNIEnv *env, jobject thiz ssize_t readSize = lpRecorder->read(recordBuff + offsetInBytes, sizeInBytes > (jint)recorderBuffSize ? (jint)recorderBuffSize : sizeInBytes ); - env->ReleasePrimitiveArrayCritical(javaAudioData, recordBuff, 0); + env->ReleaseByteArrayElements(javaAudioData, recordBuff, 0); return (jint) readSize; } diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp index 8409adc..44d2a52 100644 --- a/core/jni/android_media_AudioTrack.cpp +++ b/core/jni/android_media_AudioTrack.cpp @@ -530,8 +530,12 @@ static jint android_media_AudioTrack_native_write(JNIEnv *env, jobject thiz, } // get the pointer for the audio data from the java array + // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such + // a way that it becomes much more efficient. When doing so, we will have to prevent the + // AudioSystem callback to be called while in critical section (in case of media server + // process crash for instance) if (javaAudioData) { - cAudioData = (jbyte *)env->GetPrimitiveArrayCritical(javaAudioData, NULL); + cAudioData = (jbyte *)env->GetByteArrayElements(javaAudioData, NULL); if (cAudioData == NULL) { LOGE("Error retrieving source of audio data to play, can't play"); return 0; // out of memory or no data to load @@ -543,7 +547,7 @@ static jint android_media_AudioTrack_native_write(JNIEnv *env, jobject thiz, jint written = writeToTrack(lpTrack, javaAudioFormat, cAudioData, offsetInBytes, sizeInBytes); - env->ReleasePrimitiveArrayCritical(javaAudioData, cAudioData, 0); + env->ReleaseByteArrayElements(javaAudioData, cAudioData, 0); //LOGV("write wrote %d (tried %d) bytes in the native AudioTrack with offset %d", // (int)written, (int)(sizeInBytes), (int)offsetInBytes); diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 0ad174f..c684e7e 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1379,12 +1379,6 @@ android:excludeFromRecents="true"> </activity> - <activity android:name="com.android.server.usb.UsbResolverActivity" - android:theme="@style/Theme.Holo.Dialog.Alert" - android:finishOnCloseSystemDialogs="true" - android:excludeFromRecents="true"> - </activity> - <service android:name="com.android.server.LoadAverageService" android:exported="true" /> diff --git a/core/res/res/drawable-hdpi/text_cursor_holo_dark.9.png b/core/res/res/drawable-hdpi/text_cursor_holo_dark.9.png Binary files differindex b9435b6..ae77fa0 100644 --- a/core/res/res/drawable-hdpi/text_cursor_holo_dark.9.png +++ b/core/res/res/drawable-hdpi/text_cursor_holo_dark.9.png diff --git a/core/res/res/drawable-hdpi/text_cursor_holo_light.9.png b/core/res/res/drawable-hdpi/text_cursor_holo_light.9.png Binary files differindex 477d820..c6bdfcc 100644 --- a/core/res/res/drawable-hdpi/text_cursor_holo_light.9.png +++ b/core/res/res/drawable-hdpi/text_cursor_holo_light.9.png diff --git a/core/res/res/drawable-mdpi/text_cursor_holo_dark.9.png b/core/res/res/drawable-mdpi/text_cursor_holo_dark.9.png Binary files differindex b9435b6..ae77fa0 100644 --- a/core/res/res/drawable-mdpi/text_cursor_holo_dark.9.png +++ b/core/res/res/drawable-mdpi/text_cursor_holo_dark.9.png diff --git a/core/res/res/drawable-mdpi/text_cursor_holo_light.9.png b/core/res/res/drawable-mdpi/text_cursor_holo_light.9.png Binary files differindex 477d820..c6bdfcc 100644 --- a/core/res/res/drawable-mdpi/text_cursor_holo_light.9.png +++ b/core/res/res/drawable-mdpi/text_cursor_holo_light.9.png diff --git a/core/res/res/layout/action_bar_home.xml b/core/res/res/layout/action_bar_home.xml index 7867577..c82f91d 100644 --- a/core/res/res/layout/action_bar_home.xml +++ b/core/res/res/layout/action_bar_home.xml @@ -14,14 +14,14 @@ limitations under the License. --> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:background="?android:attr/selectableItemBackground" - android:orientation="horizontal"> +<view xmlns:android="http://schemas.android.com/apk/res/android" + class="com.android.internal.widget.ActionBarView$HomeView" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:background="?android:attr/selectableItemBackground" > <ImageView android:id="@android:id/up" android:src="?android:attr/homeAsUpIndicator" - android:layout_gravity="top|left" + android:layout_gravity="center_vertical|left" android:visibility="gone" android:layout_width="wrap_content" android:layout_height="wrap_content" @@ -33,4 +33,4 @@ android:paddingRight="16dip" android:layout_gravity="center" android:scaleType="center" /> -</LinearLayout> +</view> diff --git a/core/res/res/layout/search_view.xml b/core/res/res/layout/search_view.xml index 93b6deb..c52b73f 100644 --- a/core/res/res/layout/search_view.xml +++ b/core/res/res/layout/search_view.xml @@ -54,6 +54,10 @@ android:layout_height="wrap_content" android:layout_weight="1" android:layout_gravity="center_vertical" + android:layout_marginLeft="4dip" + android:layout_marginRight="4dip" + android:layout_marginTop="4dip" + android:layout_marginBottom="4dip" android:orientation="horizontal"> <!-- Inner layout contains the app icon, button(s) and EditText --> diff --git a/docs/html/images/avd-manager.png b/docs/html/images/avd-manager.png Binary files differindex 69ce972..c33d8a8 100644 --- a/docs/html/images/avd-manager.png +++ b/docs/html/images/avd-manager.png diff --git a/docs/html/images/billing_package.png b/docs/html/images/billing_package.png Binary files differindex ec04c2d..951e117 100755..100644 --- a/docs/html/images/billing_package.png +++ b/docs/html/images/billing_package.png diff --git a/docs/html/images/developing/adt-props-isLib.png b/docs/html/images/developing/adt-props-isLib.png Binary files differindex 18bdb33..49c9111 100644 --- a/docs/html/images/developing/adt-props-isLib.png +++ b/docs/html/images/developing/adt-props-isLib.png diff --git a/docs/html/images/developing/adt-props-libRef.png b/docs/html/images/developing/adt-props-libRef.png Binary files differindex e61df51..73bccbd 100644 --- a/docs/html/images/developing/adt-props-libRef.png +++ b/docs/html/images/developing/adt-props-libRef.png diff --git a/docs/html/images/licensing_add_library.png b/docs/html/images/licensing_add_library.png Binary files differindex 90b4435..3bbe6d5 100644 --- a/docs/html/images/licensing_add_library.png +++ b/docs/html/images/licensing_add_library.png diff --git a/docs/html/images/licensing_gapis_8.png b/docs/html/images/licensing_gapis_8.png Binary files differindex 43ad262..480d989 100644 --- a/docs/html/images/licensing_gapis_8.png +++ b/docs/html/images/licensing_gapis_8.png diff --git a/docs/html/images/licensing_package.png b/docs/html/images/licensing_package.png Binary files differindex 5da5632..eb2c5cf 100644 --- a/docs/html/images/licensing_package.png +++ b/docs/html/images/licensing_package.png diff --git a/docs/html/sdk/eclipse-adt.jd b/docs/html/sdk/eclipse-adt.jd index 3a7b39f..97717fe 100644 --- a/docs/html/sdk/eclipse-adt.jd +++ b/docs/html/sdk/eclipse-adt.jd @@ -594,13 +594,14 @@ href="#troubleshooting">Troubleshooting</a> section.</p> <p>ADT is a plugin for the Eclipse IDE. Before you can install or use ADT, you must have a compatible version of Eclipse installed on your development -computer. </p> +computer. Check the <a +href="requirements.html">System Requirements</a> document for +a list of Eclipse versions that are compatible with the Android SDK.</li></p> <ul> <li>If Eclipse is already installed on your computer, make sure that it is -a version that is compatible with ADT and the Android SDK. Check the <a -href="requirements.html">System Requirements</a> document for -a list of Eclipse versions that are compatible with the Android SDK.</li> +a version that is compatible with ADT and the Android SDK. + <li>If you need to install or update Eclipse, you can download it from this location: @@ -608,7 +609,7 @@ location: "http://www.eclipse.org/downloads/">http://www.eclipse.org/downloads/</a> </p> -<p>For Eclipse 3.5 or newer, the "Eclipse Classic" version is recommended. Otherwise, a Java or RCP +<p>The "Eclipse Classic" version is recommended. Otherwise, a Java or RCP version of Eclipse is recommended.</p></li> </ul> @@ -624,19 +625,15 @@ ADT installation as described in the steps below. </p> <h3 id="downloading">Downloading the ADT Plugin</h3> -<p>Use Update Manager feature of your Eclipse installation to install the latest +<p>Use the Update Manager feature of your Eclipse installation to install the latest revision of ADT on your development computer.<p> <p>Assuming that you have a compatible version of the Eclipse IDE installed, as described in <a href="#preparing">Preparing for Installation</a>, above, follow these steps to download the ADT plugin and install it in your Eclipse -environment. </p> +environment.</p> + -<table style="font-size:100%"> -<tr><th>Eclipse 3.5 (Galileo) and 3.6 (Helios)</th><th>Eclipse 3.4 (Ganymede)</th></tr> -<tr> -<td width="45%"> -<!-- 3.5+ steps --> <ol> <li>Start Eclipse, then select <strong>Help</strong> > <strong>Install New Software...</strong>.</li> @@ -655,35 +652,6 @@ the checkbox next to Developer Tools and click <strong>Next</strong>.</li> <li>When the installation completes, restart Eclipse. </li> </ol> -</td> -<td width="50%"> - -<!-- 3.4 steps --> -<ol> - <li>Start Eclipse, then select <strong>Help</strong> > <strong>Software Updates...</strong>. -In the dialog that appears, click the <strong>Available Software</strong> tab.</li> - <li>Click <strong>Add Site</strong>.</li> - <li>In the Add Site dialog that appears, enter this URL in the "Location" field: - <pre>https://dl-ssl.google.com/android/eclipse/</pre> - <p>Note: If you have trouble acquiring the plugin, you can try - using "http" in the URL, instead of "https" (https is preferred for - security reasons).</p> - <p>Click <strong>OK</strong>.</p> - </li> - <li>Back in the Available Software view, you should see the plugin listed by the URL, - with "Developer Tools" nested within it. Select the checkbox next to Developer Tools, - which will automatically select the nested tools. Then click - <strong>Install</strong></li> - <li>On the subsequent Install window, all of the included tools - should be checked. Click <strong>Next</strong>. </li> - <li>Read and accept the license agreements, then click <strong>Finish</strong>.</li> - <li>When the installation completes, restart Eclipse. </li> - -</ol> -</td> -</tr> -</table> - <h3 id="configuring">Configuring the ADT Plugin</h3> <p>Once you've successfully downloaded ADT as described above, the next step @@ -807,11 +775,6 @@ Eclipse Installed Software window using <strong>Help</strong> <p>Follow the steps below to check whether an update is available and, if so, to install it. </p> -<table style="font-size:100%"> -<tr><th>Eclipse 3.5 (Galileo) and 3.6 (Helios)</th><th>Eclipse 3.4 (Ganymede)</th></tr> -<tr> -<td> -<!-- 3.5+ steps --> <ol> <li>Select <strong>Help</strong> > <strong>Check for Updates</strong>. <p>If there are no updates available, a dialog will say so and you're done.</p></li> @@ -823,25 +786,6 @@ to install it. </p> Android Development Tools.</li> <li>Restart Eclipse.</li> </ol> -</td> - -<td width="50%"> -<!-- 3.4 steps --> -<ol> - <li>Select <strong>Help</strong> > <strong>Software Updates</strong>.</li> - <li>Select the <strong>Available Software</strong> tab.</li> - <li>If there are updates available, select Android DDMS, Android Development Tools, - and Android Hierarchy Viewer, then click <strong>Update</strong>.</li> - <li>In the resulting Available Updates dialog, ensure that each of the listed tools - are selected, then click <strong>Next</strong>.</li> - <li>Read and accept the license agreement and then click <strong>Finish</strong>. - This will download and install the latest version of Android DDMS and - Android Development Tools.</li> - <li>Restart Eclipse.</li> -</ol> -</td> -</tr> -</table> <p>If you encounter problems during the update, remove the existing ADT plugin from Eclipse, then diff --git a/docs/html/sdk/installing.jd b/docs/html/sdk/installing.jd index b0fd761..a1080c2 100644 --- a/docs/html/sdk/installing.jd +++ b/docs/html/sdk/installing.jd @@ -96,13 +96,14 @@ href="http://java.sun.com/javase/downloads/index.jsp">JDK</a>, if you don't have <p>If you will be developing in Eclipse with the Android Development Tools (ADT) Plugin—the recommended path if you are new to Android—make sure that you have a suitable version of Eclipse -installed on your computer (3.4 or newer is recommended). If you need -to install Eclipse, you can download it from this location: </p> +installed on your computer as described in the +<a href="requirements.html">System Requirements</a> document. +If you need to install Eclipse, you can download it from this location: </p> <p style="margin-left:2em;"><a href= "http://www.eclipse.org/downloads/">http://www.eclipse.org/downloads/</a></p> -<p>For Eclipse 3.5 or newer, the "Eclipse Classic" version is recommended. Otherwise, a Java or +<p>The "Eclipse Classic" version is recommended. Otherwise, a Java or RCP version of Eclipse is recommended.</p> diff --git a/docs/html/sdk/requirements.jd b/docs/html/sdk/requirements.jd index 3d62dd9..f12d0aa 100644 --- a/docs/html/sdk/requirements.jd +++ b/docs/html/sdk/requirements.jd @@ -24,7 +24,9 @@ Android applications using the Android SDK. </p> <h4 style="margin-top:.25em"><em>Eclipse IDE</em></h4> <ul> - <li>Eclipse 3.4 (Ganymede) or greater</li> + <li>Eclipse 3.5 (Galileo) or greater +<p class="note"><strong>Note:</strong> Eclipse 3.4 (Ganymede) is no longer +supported with the latest version of ADT.</p></li> <li>Eclipse <a href="http://www.eclipse.org/jdt">JDT</a> plugin (included in most Eclipse IDE packages) </li> <li>If you need to install or update Eclipse, you can download it from <a diff --git a/include/media/AudioRecord.h b/include/media/AudioRecord.h index 5f7cd90..293764d 100644 --- a/include/media/AudioRecord.h +++ b/include/media/AudioRecord.h @@ -346,12 +346,14 @@ private: }; bool processAudioBuffer(const sp<ClientRecordThread>& thread); - status_t openRecord(uint32_t sampleRate, + status_t openRecord_l(uint32_t sampleRate, int format, int channelCount, int frameCount, uint32_t flags, audio_io_handle_t input); + audio_io_handle_t getInput_l(); + status_t restoreRecord_l(audio_track_cblk_t*& cblk); sp<IAudioRecord> mAudioRecord; sp<IMemory> mCblkMemory; diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h index 813a905..3e346db 100644 --- a/include/media/AudioTrack.h +++ b/include/media/AudioTrack.h @@ -437,7 +437,7 @@ private: }; bool processAudioBuffer(const sp<AudioTrackThread>& thread); - status_t createTrack(int streamType, + status_t createTrack_l(int streamType, uint32_t sampleRate, int format, int channelCount, @@ -446,6 +446,10 @@ private: const sp<IMemory>& sharedBuffer, audio_io_handle_t output, bool enforceFrameCount); + void flush_l(); + status_t setLoop_l(uint32_t loopStart, uint32_t loopEnd, int loopCount); + audio_io_handle_t getOutput_l(); + status_t restoreTrack_l(audio_track_cblk_t*& cblk, bool fromStart); sp<IAudioTrack> mAudioTrack; sp<IMemory> mCblkMemory; diff --git a/include/private/media/AudioTrackShared.h b/include/private/media/AudioTrackShared.h index c6990bf..4610135 100644 --- a/include/private/media/AudioTrackShared.h +++ b/include/private/media/AudioTrackShared.h @@ -31,6 +31,7 @@ namespace android { #define MAX_STARTUP_TIMEOUT_MS 3000 // Longer timeout period at startup to cope with A2DP init time #define MAX_RUN_TIMEOUT_MS 1000 #define WAIT_PERIOD_MS 10 +#define RESTORE_TIMEOUT_MS 5000 // Maximum waiting time for a track to be restored #define CBLK_UNDERRUN_MSK 0x0001 #define CBLK_UNDERRUN_ON 0x0001 // underrun (out) or overrrun (in) indication @@ -47,6 +48,12 @@ namespace android { #define CBLK_DISABLED_MSK 0x0010 #define CBLK_DISABLED_ON 0x0010 // track disabled by AudioFlinger due to underrun: #define CBLK_DISABLED_OFF 0x0000 // must be re-started +#define CBLK_RESTORING_MSK 0x0020 +#define CBLK_RESTORING_ON 0x0020 // track is being restored after invalidation +#define CBLK_RESTORING_OFF 0x0000 // by AudioFlinger +#define CBLK_RESTORED_MSK 0x0040 +#define CBLK_RESTORED_ON 0x0040 // track has been restored after invalidation +#define CBLK_RESTORED_OFF 0x0040 // by AudioFlinger struct audio_track_cblk_t { diff --git a/libs/rs/rsContext.cpp b/libs/rs/rsContext.cpp index 4a5620d..f9e29f1 100644 --- a/libs/rs/rsContext.cpp +++ b/libs/rs/rsContext.cpp @@ -592,11 +592,11 @@ void * Context::helperThreadProc(void *vrsc) { void Context::launchThreads(WorkerCallback_t cbk, void *data) { mWorkers.mLaunchData = data; mWorkers.mLaunchCallback = cbk; - mWorkers.mRunningCount = (int)mWorkers.mCount; + android_atomic_release_store(mWorkers.mCount, &mWorkers.mRunningCount); for (uint32_t ct = 0; ct < mWorkers.mCount; ct++) { mWorkers.mLaunchSignals[ct].set(); } - while (mWorkers.mRunningCount) { + while (android_atomic_acquire_load(&mWorkers.mRunningCount) != 0) { mWorkers.mCompleteSignal.wait(); } } @@ -707,8 +707,8 @@ bool Context::initContext(Device *dev, const RsSurfaceConfig *sc) { } mWorkers.mCompleteSignal.init(); - mWorkers.mRunningCount = 0; - mWorkers.mLaunchCount = 0; + android_atomic_release_store(mWorkers.mCount, &mWorkers.mRunningCount); + android_atomic_release_store(0, &mWorkers.mLaunchCount); for (uint32_t ct=0; ct < mWorkers.mCount; ct++) { status = pthread_create(&mWorkers.mThreadId[ct], &threadAttr, helperThreadProc, this); if (status) { @@ -717,6 +717,9 @@ bool Context::initContext(Device *dev, const RsSurfaceConfig *sc) { break; } } + while (android_atomic_acquire_load(&mWorkers.mRunningCount) != 0) { + usleep(100); + } pthread_attr_destroy(&threadAttr); return true; } @@ -736,14 +739,14 @@ Context::~Context() { // Cleanup compute threads. mWorkers.mLaunchData = NULL; mWorkers.mLaunchCallback = NULL; - mWorkers.mRunningCount = (int)mWorkers.mCount; + android_atomic_release_store(mWorkers.mCount, &mWorkers.mRunningCount); for (uint32_t ct = 0; ct < mWorkers.mCount; ct++) { mWorkers.mLaunchSignals[ct].set(); } for (uint32_t ct = 0; ct < mWorkers.mCount; ct++) { status = pthread_join(mWorkers.mThreadId[ct], &res); } - rsAssert(!mWorkers.mRunningCount); + rsAssert(android_atomic_acquire_load(&mWorkers.mRunningCount) == 0); // Global structure cleanup. pthread_mutex_lock(&gInitMutex); diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java index 5a73d2d..fd12e19 100644 --- a/media/java/android/media/AudioService.java +++ b/media/java/android/media/AudioService.java @@ -805,7 +805,7 @@ public class AudioService extends IAudioService.Stub { if (mode != mMode) { // automatically handle audio focus for mode changes - handleFocusForCalls(mMode, mode); + handleFocusForCalls(mMode, mode, cb); if (AudioSystem.setPhoneState(mode) == AudioSystem.AUDIO_STATUS_OK) { mMode = mode; @@ -864,7 +864,7 @@ public class AudioService extends IAudioService.Stub { } /** pre-condition: oldMode != newMode */ - private void handleFocusForCalls(int oldMode, int newMode) { + private void handleFocusForCalls(int oldMode, int newMode, IBinder cb) { // if ringing if (newMode == AudioSystem.MODE_RINGTONE) { // if not ringing silently @@ -872,8 +872,8 @@ public class AudioService extends IAudioService.Stub { if (ringVolume > 0) { // request audio focus for the communication focus entry requestAudioFocus(AudioManager.STREAM_RING, - AudioManager.AUDIOFOCUS_GAIN_TRANSIENT, - null, null /* both allowed to be null only for this clientId */, + AudioManager.AUDIOFOCUS_GAIN_TRANSIENT, cb, + null /* IAudioFocusDispatcher allowed to be null only for this clientId */, IN_VOICE_COMM_FOCUS_ID /*clientId*/); } @@ -884,8 +884,8 @@ public class AudioService extends IAudioService.Stub { // request audio focus for the communication focus entry // (it's ok if focus was already requested during ringing) requestAudioFocus(AudioManager.STREAM_RING, - AudioManager.AUDIOFOCUS_GAIN_TRANSIENT, - null, null /* both allowed to be null only for this clientId */, + AudioManager.AUDIOFOCUS_GAIN_TRANSIENT, cb, + null /* IAudioFocusDispatcher allowed to be null only for this clientId */, IN_VOICE_COMM_FOCUS_ID /*clientId*/); } // if exiting call @@ -2547,10 +2547,9 @@ public class AudioService extends IAudioService.Stub { // the main stream type for the audio focus request is currently not used. It may // potentially be used to handle multiple stream type-dependent audio focuses. - // we need a valid binder callback for clients other than the AudioService's phone - // state listener - if (!IN_VOICE_COMM_FOCUS_ID.equals(clientId) && ((cb == null) || !cb.pingBinder())) { - Log.i(TAG, " AudioFocus DOA client for requestAudioFocus(), exiting"); + // we need a valid binder callback for clients + if (!cb.pingBinder()) { + Log.e(TAG, " AudioFocus DOA client for requestAudioFocus(), aborting."); return AudioManager.AUDIOFOCUS_REQUEST_FAILED; } @@ -2591,17 +2590,14 @@ public class AudioService extends IAudioService.Stub { }//synchronized(mAudioFocusLock) // handle the potential premature death of the new holder of the focus - // (premature death == death before abandoning focus) for a client which is not the - // AudioService's phone state listener - if (!IN_VOICE_COMM_FOCUS_ID.equals(clientId)) { - // Register for client death notification - AudioFocusDeathHandler afdh = new AudioFocusDeathHandler(cb); - try { - cb.linkToDeath(afdh, 0); - } catch (RemoteException e) { - // client has already died! - Log.w(TAG, "AudioFocus requestAudioFocus() could not link to "+cb+" binder death"); - } + // (premature death == death before abandoning focus) + // Register for client death notification + AudioFocusDeathHandler afdh = new AudioFocusDeathHandler(cb); + try { + cb.linkToDeath(afdh, 0); + } catch (RemoteException e) { + // client has already died! + Log.w(TAG, "AudioFocus requestAudioFocus() could not link to "+cb+" binder death"); } return AudioManager.AUDIOFOCUS_REQUEST_GRANTED; diff --git a/media/java/android/mtp/MtpClient.java b/media/java/android/mtp/MtpClient.java index 40e2f9b..d25dcb9 100644 --- a/media/java/android/mtp/MtpClient.java +++ b/media/java/android/mtp/MtpClient.java @@ -16,6 +16,7 @@ package android.mtp; +import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -41,6 +42,9 @@ public class MtpClient { private static final String TAG = "MtpClient"; + private static final String ACTION_USB_PERMISSION = + "android.mtp.MtpClient.action.USB_PERMISSION"; + private final Context mContext; private final UsbManager mUsbManager; private final ArrayList<Listener> mListeners = new ArrayList<Listener>(); @@ -49,29 +53,47 @@ public class MtpClient { // mDevices is also used for synchronization in this class. private final HashMap<String, MtpDevice> mDevices = new HashMap<String, MtpDevice>(); + private final PendingIntent mPermissionIntent; + private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); UsbDevice usbDevice = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); String deviceName = usbDevice.getDeviceName(); synchronized (mDevices) { MtpDevice mtpDevice = mDevices.get(deviceName); - if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(intent.getAction())) { + if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) { if (mtpDevice == null) { mtpDevice = openDeviceLocked(usbDevice); } if (mtpDevice != null) { - mDevices.put(deviceName, mtpDevice); for (Listener listener : mListeners) { listener.deviceAdded(mtpDevice); } } - } else if (mtpDevice != null) { - mDevices.remove(deviceName); - for (Listener listener : mListeners) { - listener.deviceRemoved(mtpDevice); + } else if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) { + if (mtpDevice != null) { + mDevices.remove(deviceName); + for (Listener listener : mListeners) { + listener.deviceRemoved(mtpDevice); + } + } + } else if (ACTION_USB_PERMISSION.equals(action)) { + boolean permission = intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, + false); + Log.d(TAG, "ACTION_USB_PERMISSION: " + permission); + if (permission) { + if (mtpDevice == null) { + mtpDevice = openDeviceLocked(usbDevice); + } + if (mtpDevice != null) { + for (Listener listener : mListeners) { + listener.deviceAdded(mtpDevice); + } + } } } } @@ -126,10 +148,11 @@ public class MtpClient { public MtpClient(Context context) { mContext = context; mUsbManager = (UsbManager)context.getSystemService(Context.USB_SERVICE); - + mPermissionIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ACTION_USB_PERMISSION), 0); IntentFilter filter = new IntentFilter(); filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED); filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED); + filter.addAction(ACTION_USB_PERMISSION); context.registerReceiver(mUsbReceiver, filter); } @@ -142,9 +165,14 @@ public class MtpClient { */ private MtpDevice openDeviceLocked(UsbDevice usbDevice) { if (isCamera(usbDevice)) { - MtpDevice mtpDevice = new MtpDevice(usbDevice); - if (mtpDevice.open(mUsbManager)) { - return mtpDevice; + if (!mUsbManager.hasPermission(usbDevice)) { + mUsbManager.requestPermission(usbDevice, mPermissionIntent); + } else { + MtpDevice mtpDevice = new MtpDevice(usbDevice); + if (mtpDevice.open(mUsbManager)) { + mDevices.put(usbDevice.getDeviceName(), mtpDevice); + return mtpDevice; + } } } return null; @@ -218,13 +246,8 @@ public class MtpClient { // Query the USB manager since devices might have attached // before we added our listener. for (UsbDevice usbDevice : mUsbManager.getDeviceList().values()) { - String deviceName = usbDevice.getDeviceName(); - MtpDevice mtpDevice = mDevices.get(deviceName); - if (mtpDevice == null) { - mtpDevice = openDeviceLocked(usbDevice); - } - if (mtpDevice != null) { - mDevices.put(deviceName, mtpDevice); + if (mDevices.get(usbDevice.getDeviceName()) == null) { + openDeviceLocked(usbDevice); } } diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp index 1d6ffa0..a18bedb 100644 --- a/media/libmedia/AudioRecord.cpp +++ b/media/libmedia/AudioRecord.cpp @@ -128,6 +128,9 @@ status_t AudioRecord::set( { LOGV("set(): sampleRate %d, channels %d, frameCount %d",sampleRate, channels, frameCount); + + AutoMutex lock(mLock); + if (mAudioRecord != 0) { return INVALID_OPERATION; } @@ -183,7 +186,7 @@ status_t AudioRecord::set( mSessionId = sessionId; // create the IAudioRecord - status = openRecord(sampleRate, format, channelCount, + status = openRecord_l(sampleRate, format, channelCount, frameCount, flags, input); if (status != NO_ERROR) { return status; @@ -282,21 +285,31 @@ status_t AudioRecord::start() } AutoMutex lock(mLock); + // acquire a strong reference on the IAudioRecord and IMemory so that they cannot be destroyed + // while we are accessing the cblk + sp <IAudioRecord> audioRecord = mAudioRecord; + sp <IMemory> iMem = mCblkMemory; + audio_track_cblk_t* cblk = mCblk; if (mActive == 0) { mActive = 1; - ret = mAudioRecord->start(); - if (ret == DEAD_OBJECT) { - LOGV("start() dead IAudioRecord: creating a new one"); - ret = openRecord(mCblk->sampleRate, mFormat, mChannelCount, - mFrameCount, mFlags, getInput()); - if (ret == NO_ERROR) { - ret = mAudioRecord->start(); + + cblk->lock.lock(); + if (!(cblk->flags & CBLK_INVALID_MSK)) { + cblk->lock.unlock(); + ret = mAudioRecord->start(); + cblk->lock.lock(); + if (ret == DEAD_OBJECT) { + cblk->flags |= CBLK_INVALID_MSK; } } + if (cblk->flags & CBLK_INVALID_MSK) { + ret = restoreRecord_l(cblk); + } + cblk->lock.unlock(); if (ret == NO_ERROR) { - mNewPosition = mCblk->user + mUpdatePeriod; - mCblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS; - mCblk->waitTimeMs = 0; + mNewPosition = cblk->user + mUpdatePeriod; + cblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS; + cblk->waitTimeMs = 0; if (t != 0) { t->run("ClientRecordThread", THREAD_PRIORITY_AUDIO_CLIENT); } else { @@ -353,6 +366,7 @@ bool AudioRecord::stopped() const uint32_t AudioRecord::getSampleRate() { + AutoMutex lock(mLock); return mCblk->sampleRate; } @@ -400,6 +414,7 @@ status_t AudioRecord::getPosition(uint32_t *position) { if (position == 0) return BAD_VALUE; + AutoMutex lock(mLock); *position = mCblk->user; return NO_ERROR; @@ -415,7 +430,8 @@ unsigned int AudioRecord::getInputFramesLost() // ------------------------------------------------------------------------- -status_t AudioRecord::openRecord( +// must be called with mLock held +status_t AudioRecord::openRecord_l( uint32_t sampleRate, int format, int channelCount, @@ -459,6 +475,7 @@ status_t AudioRecord::openRecord( status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, int32_t waitCount) { + AutoMutex lock(mLock); int active; status_t result; audio_track_cblk_t* cblk = mCblk; @@ -483,7 +500,19 @@ status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, int32_t waitCount) cblk->lock.unlock(); return WOULD_BLOCK; } - result = cblk->cv.waitRelative(cblk->lock, milliseconds(waitTimeMs)); + if (!(cblk->flags & CBLK_INVALID_MSK)) { + mLock.unlock(); + result = cblk->cv.waitRelative(cblk->lock, milliseconds(waitTimeMs)); + cblk->lock.unlock(); + mLock.lock(); + if (mActive == 0) { + return status_t(STOPPED); + } + cblk->lock.lock(); + } + if (cblk->flags & CBLK_INVALID_MSK) { + goto create_new_record; + } if (__builtin_expect(result!=NO_ERROR, false)) { cblk->waitTimeMs += waitTimeMs; if (cblk->waitTimeMs >= cblk->bufferTimeoutMs) { @@ -491,16 +520,17 @@ status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, int32_t waitCount) "user=%08x, server=%08x", cblk->user, cblk->server); cblk->lock.unlock(); result = mAudioRecord->start(); + cblk->lock.lock(); if (result == DEAD_OBJECT) { - LOGW("obtainBuffer() dead IAudioRecord: creating a new one"); - result = openRecord(cblk->sampleRate, mFormat, mChannelCount, - mFrameCount, mFlags, getInput()); - if (result == NO_ERROR) { - cblk = mCblk; - mAudioRecord->start(); - } + cblk->flags |= CBLK_INVALID_MSK; +create_new_record: + result = AudioRecord::restoreRecord_l(cblk); + } + if (result != NO_ERROR) { + LOGW("obtainBuffer create Track error %d", result); + cblk->lock.unlock(); + return result; } - cblk->lock.lock(); cblk->waitTimeMs = 0; } if (--waitCount == 0) { @@ -540,12 +570,19 @@ status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, int32_t waitCount) void AudioRecord::releaseBuffer(Buffer* audioBuffer) { - audio_track_cblk_t* cblk = mCblk; - cblk->stepUser(audioBuffer->frameCount); + AutoMutex lock(mLock); + mCblk->stepUser(audioBuffer->frameCount); } audio_io_handle_t AudioRecord::getInput() { + AutoMutex lock(mLock); + return getInput_l(); +} + +// must be called with mLock held +audio_io_handle_t AudioRecord::getInput_l() +{ mInput = AudioSystem::getInput(mInputSource, mCblk->sampleRate, mFormat, mChannels, @@ -573,6 +610,12 @@ ssize_t AudioRecord::read(void* buffer, size_t userSize) return BAD_VALUE; } + mLock.lock(); + // acquire a strong reference on the IAudioRecord and IMemory so that they cannot be destroyed + // while we are accessing the cblk + sp <IAudioRecord> audioRecord = mAudioRecord; + sp <IMemory> iMem = mCblkMemory; + mLock.unlock(); do { @@ -613,9 +656,17 @@ bool AudioRecord::processAudioBuffer(const sp<ClientRecordThread>& thread) uint32_t frames = mRemainingFrames; size_t readSize; + mLock.lock(); + // acquire a strong reference on the IAudioRecord and IMemory so that they cannot be destroyed + // while we are accessing the cblk + sp <IAudioRecord> audioRecord = mAudioRecord; + sp <IMemory> iMem = mCblkMemory; + audio_track_cblk_t* cblk = mCblk; + mLock.unlock(); + // Manage marker callback if (!mMarkerReached && (mMarkerPosition > 0)) { - if (mCblk->user >= mMarkerPosition) { + if (cblk->user >= mMarkerPosition) { mCbf(EVENT_MARKER, mUserData, (void *)&mMarkerPosition); mMarkerReached = true; } @@ -623,7 +674,7 @@ bool AudioRecord::processAudioBuffer(const sp<ClientRecordThread>& thread) // Manage new position callback if (mUpdatePeriod > 0) { - while (mCblk->user >= mNewPosition) { + while (cblk->user >= mNewPosition) { mCbf(EVENT_NEW_POS, mUserData, (void *)&mNewPosition); mNewPosition += mUpdatePeriod; } @@ -669,11 +720,11 @@ bool AudioRecord::processAudioBuffer(const sp<ClientRecordThread>& thread) // Manage overrun callback - if (mActive && (mCblk->framesAvailable_l() == 0)) { - LOGV("Overrun user: %x, server: %x, flags %04x", mCblk->user, mCblk->server, mCblk->flags); - if ((mCblk->flags & CBLK_UNDERRUN_MSK) == CBLK_UNDERRUN_OFF) { + if (mActive && (cblk->framesAvailable() == 0)) { + LOGV("Overrun user: %x, server: %x, flags %04x", cblk->user, cblk->server, cblk->flags); + if ((cblk->flags & CBLK_UNDERRUN_MSK) == CBLK_UNDERRUN_OFF) { mCbf(EVENT_OVERRUN, mUserData, 0); - mCblk->flags |= CBLK_UNDERRUN_ON; + cblk->flags |= CBLK_UNDERRUN_ON; } } @@ -685,6 +736,69 @@ bool AudioRecord::processAudioBuffer(const sp<ClientRecordThread>& thread) return true; } +// must be called with mLock and cblk.lock held. Callers must also hold strong references on +// the IAudioRecord and IMemory in case they are recreated here. +// If the IAudioRecord is successfully restored, the cblk pointer is updated +status_t AudioRecord::restoreRecord_l(audio_track_cblk_t*& cblk) +{ + status_t result; + + if (!(cblk->flags & CBLK_RESTORING_MSK)) { + LOGW("dead IAudioRecord, creating a new one"); + + cblk->flags |= CBLK_RESTORING_ON; + // signal old cblk condition so that other threads waiting for available buffers stop + // waiting now + cblk->cv.broadcast(); + cblk->lock.unlock(); + + // if the new IAudioRecord is created, openRecord_l() will modify the + // following member variables: mAudioRecord, mCblkMemory and mCblk. + // It will also delete the strong references on previous IAudioRecord and IMemory + result = openRecord_l(cblk->sampleRate, mFormat, mChannelCount, + mFrameCount, mFlags, getInput_l()); + if (result == NO_ERROR) { + result = mAudioRecord->start(); + } + if (result != NO_ERROR) { + mActive = false; + } + + // signal old cblk condition for other threads waiting for restore completion + cblk->lock.lock(); + cblk->flags |= CBLK_RESTORED_MSK; + cblk->cv.broadcast(); + cblk->lock.unlock(); + } else { + if (!(cblk->flags & CBLK_RESTORED_MSK)) { + LOGW("dead IAudioRecord, waiting for a new one to be created"); + mLock.unlock(); + result = cblk->cv.waitRelative(cblk->lock, milliseconds(RESTORE_TIMEOUT_MS)); + cblk->lock.unlock(); + mLock.lock(); + } else { + LOGW("dead IAudioRecord, already restored"); + result = NO_ERROR; + cblk->lock.unlock(); + } + if (result != NO_ERROR || mActive == 0) { + result = status_t(STOPPED); + } + } + LOGV("restoreRecord_l() status %d mActive %d cblk %p, old cblk %p flags %08x old flags %08x", + result, mActive, mCblk, cblk, mCblk->flags, cblk->flags); + + if (result == NO_ERROR) { + // from now on we switch to the newly created cblk + cblk = mCblk; + } + cblk->lock.lock(); + + LOGW_IF(result != NO_ERROR, "restoreRecord_l() error %d", result); + + return result; +} + // ========================================================================= AudioRecord::ClientRecordThread::ClientRecordThread(AudioRecord& receiver, bool bCanCallJava) diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index c1bed59..8d8f67b 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -148,6 +148,7 @@ status_t AudioTrack::set( LOGV_IF(sharedBuffer != 0, "sharedBuffer: %p, size: %d", sharedBuffer->pointer(), sharedBuffer->size()); + AutoMutex lock(mLock); if (mAudioTrack != 0) { LOGE("Track already in use"); return INVALID_OPERATION; @@ -211,8 +212,15 @@ status_t AudioTrack::set( mAuxEffectId = 0; // create the IAudioTrack - status_t status = createTrack(streamType, sampleRate, format, channelCount, - frameCount, flags, sharedBuffer, output, true); + status_t status = createTrack_l(streamType, + sampleRate, + format, + channelCount, + frameCount, + flags, + sharedBuffer, + output, + true); if (status != NO_ERROR) { return status; @@ -312,37 +320,38 @@ void AudioTrack::start() } AutoMutex lock(mLock); + // acquire a strong reference on the IMemory and IAudioTrack so that they cannot be destroyed + // while we are accessing the cblk + sp <IAudioTrack> audioTrack = mAudioTrack; + sp <IMemory> iMem = mCblkMemory; + audio_track_cblk_t* cblk = mCblk; + if (mActive == 0) { mActive = 1; - mNewPosition = mCblk->server + mUpdatePeriod; - mCblk->bufferTimeoutMs = MAX_STARTUP_TIMEOUT_MS; - mCblk->waitTimeMs = 0; - mCblk->flags &= ~CBLK_DISABLED_ON; + mNewPosition = cblk->server + mUpdatePeriod; + cblk->bufferTimeoutMs = MAX_STARTUP_TIMEOUT_MS; + cblk->waitTimeMs = 0; + cblk->flags &= ~CBLK_DISABLED_ON; if (t != 0) { t->run("AudioTrackThread", THREAD_PRIORITY_AUDIO_CLIENT); } else { setpriority(PRIO_PROCESS, 0, THREAD_PRIORITY_AUDIO_CLIENT); } - if (mCblk->flags & CBLK_INVALID_MSK) { - LOGW("start() track %p invalidated, creating a new one", this); - // no need to clear the invalid flag as this cblk will not be used anymore - // force new track creation - status = DEAD_OBJECT; - } else { + LOGV("start %p before lock cblk %p", this, mCblk); + cblk->lock.lock(); + if (!(cblk->flags & CBLK_INVALID_MSK)) { + cblk->lock.unlock(); status = mAudioTrack->start(); - } - if (status == DEAD_OBJECT) { - LOGV("start() dead IAudioTrack: creating a new one"); - status = createTrack(mStreamType, mCblk->sampleRate, mFormat, mChannelCount, - mFrameCount, mFlags, mSharedBuffer, getOutput(), false); - if (status == NO_ERROR) { - status = mAudioTrack->start(); - if (status == NO_ERROR) { - mNewPosition = mCblk->server + mUpdatePeriod; - } + cblk->lock.lock(); + if (status == DEAD_OBJECT) { + cblk->flags |= CBLK_INVALID_MSK; } } + if (cblk->flags & CBLK_INVALID_MSK) { + status = restoreTrack_l(cblk, true); + } + cblk->lock.unlock(); if (status != NO_ERROR) { LOGV("start() failed"); mActive = 0; @@ -375,14 +384,14 @@ void AudioTrack::stop() mAudioTrack->stop(); // Cancel loops (If we are in the middle of a loop, playback // would not stop until loopCount reaches 0). - setLoop(0, 0, 0); + setLoop_l(0, 0, 0); // the playback head position will reset to 0, so if a marker is set, we need // to activate it again mMarkerReached = false; // Force flush if a shared buffer is used otherwise audioflinger // will not stop before end of buffer is reached. if (mSharedBuffer != 0) { - flush(); + flush_l(); } if (t != 0) { t->requestExit(); @@ -403,6 +412,13 @@ bool AudioTrack::stopped() const void AudioTrack::flush() { + AutoMutex lock(mLock); + flush_l(); +} + +// must be called with mLock held +void AudioTrack::flush_l() +{ LOGV("flush"); // clear playback marker and periodic update counter @@ -445,6 +461,7 @@ status_t AudioTrack::setVolume(float left, float right) return BAD_VALUE; } + AutoMutex lock(mLock); mVolume[LEFT] = left; mVolume[RIGHT] = right; @@ -470,6 +487,7 @@ status_t AudioTrack::setAuxEffectSendLevel(float level) if (level > 1.0f) { return BAD_VALUE; } + AutoMutex lock(mLock); mSendLevel = level; @@ -495,17 +513,26 @@ status_t AudioTrack::setSampleRate(int rate) // Resampler implementation limits input sampling rate to 2 x output sampling rate. if (rate <= 0 || rate > afSamplingRate*2 ) return BAD_VALUE; + AutoMutex lock(mLock); mCblk->sampleRate = rate; return NO_ERROR; } uint32_t AudioTrack::getSampleRate() { + AutoMutex lock(mLock); return mCblk->sampleRate; } status_t AudioTrack::setLoop(uint32_t loopStart, uint32_t loopEnd, int loopCount) { + AutoMutex lock(mLock); + return setLoop_l(loopStart, loopEnd, loopCount); +} + +// must be called with mLock held +status_t AudioTrack::setLoop_l(uint32_t loopStart, uint32_t loopEnd, int loopCount) +{ audio_track_cblk_t* cblk = mCblk; Mutex::Autolock _l(cblk->lock); @@ -540,6 +567,7 @@ status_t AudioTrack::setLoop(uint32_t loopStart, uint32_t loopEnd, int loopCount status_t AudioTrack::getLoop(uint32_t *loopStart, uint32_t *loopEnd, int *loopCount) { + AutoMutex lock(mLock); if (loopStart != 0) { *loopStart = mCblk->loopStart; } @@ -599,6 +627,7 @@ status_t AudioTrack::getPositionUpdatePeriod(uint32_t *updatePeriod) status_t AudioTrack::setPosition(uint32_t position) { + AutoMutex lock(mLock); Mutex::Autolock _l(mCblk->lock); if (!stopped()) return INVALID_OPERATION; @@ -614,7 +643,7 @@ status_t AudioTrack::setPosition(uint32_t position) status_t AudioTrack::getPosition(uint32_t *position) { if (position == 0) return BAD_VALUE; - + AutoMutex lock(mLock); *position = mCblk->server; return NO_ERROR; @@ -622,9 +651,11 @@ status_t AudioTrack::getPosition(uint32_t *position) status_t AudioTrack::reload() { + AutoMutex lock(mLock); + if (!stopped()) return INVALID_OPERATION; - flush(); + flush_l(); mCblk->stepUser(mCblk->frameCount); @@ -633,6 +664,13 @@ status_t AudioTrack::reload() audio_io_handle_t AudioTrack::getOutput() { + AutoMutex lock(mLock); + return getOutput_l(); +} + +// must be called with mLock held +audio_io_handle_t AudioTrack::getOutput_l() +{ return AudioSystem::getOutput((AudioSystem::stream_type)mStreamType, mCblk->sampleRate, mFormat, mChannels, (AudioSystem::output_flags)mFlags); } @@ -654,7 +692,8 @@ status_t AudioTrack::attachAuxEffect(int effectId) // ------------------------------------------------------------------------- -status_t AudioTrack::createTrack( +// must be called with mLock held +status_t AudioTrack::createTrack_l( int streamType, uint32_t sampleRate, int format, @@ -774,6 +813,7 @@ status_t AudioTrack::createTrack( status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount) { + AutoMutex lock(mLock); int active; status_t result; audio_track_cblk_t* cblk = mCblk; @@ -800,12 +840,17 @@ status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount) return WOULD_BLOCK; } if (!(cblk->flags & CBLK_INVALID_MSK)) { + mLock.unlock(); result = cblk->cv.waitRelative(cblk->lock, milliseconds(waitTimeMs)); + cblk->lock.unlock(); + mLock.lock(); + if (mActive == 0) { + return status_t(STOPPED); + } + cblk->lock.lock(); } + if (cblk->flags & CBLK_INVALID_MSK) { - LOGW("obtainBuffer() track %p invalidated, creating a new one", this); - // no need to clear the invalid flag as this cblk will not be used anymore - cblk->lock.unlock(); goto create_new_track; } if (__builtin_expect(result!=NO_ERROR, false)) { @@ -819,18 +864,17 @@ status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount) //unlock cblk mutex before calling mAudioTrack->start() (see issue #1617140) cblk->lock.unlock(); result = mAudioTrack->start(); + cblk->lock.lock(); if (result == DEAD_OBJECT) { - LOGW("obtainBuffer() dead IAudioTrack: creating a new one"); + cblk->flags |= CBLK_INVALID_MSK; create_new_track: - result = createTrack(mStreamType, cblk->sampleRate, mFormat, mChannelCount, - mFrameCount, mFlags, mSharedBuffer, getOutput(), false); - if (result == NO_ERROR) { - cblk = mCblk; - cblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS; - mAudioTrack->start(); - } + result = restoreTrack_l(cblk, false); + } + if (result != NO_ERROR) { + LOGW("obtainBuffer create Track error %d", result); + cblk->lock.unlock(); + return result; } - cblk->lock.lock(); } cblk->waitTimeMs = 0; } @@ -848,7 +892,7 @@ create_new_track: } // restart track if it was disabled by audioflinger due to previous underrun - if (cblk->flags & CBLK_DISABLED_MSK) { + if (mActive && (cblk->flags & CBLK_DISABLED_MSK)) { cblk->flags &= ~CBLK_DISABLED_ON; LOGW("obtainBuffer() track %p disabled, restarting", this); mAudioTrack->start(); @@ -883,8 +927,8 @@ create_new_track: void AudioTrack::releaseBuffer(Buffer* audioBuffer) { - audio_track_cblk_t* cblk = mCblk; - cblk->stepUser(audioBuffer->frameCount); + AutoMutex lock(mLock); + mCblk->stepUser(audioBuffer->frameCount); } // ------------------------------------------------------------------------- @@ -903,6 +947,13 @@ ssize_t AudioTrack::write(const void* buffer, size_t userSize) LOGV("write %p: %d bytes, mActive=%d", this, userSize, mActive); + // acquire a strong reference on the IMemory and IAudioTrack so that they cannot be destroyed + // while we are accessing the cblk + mLock.lock(); + sp <IAudioTrack> audioTrack = mAudioTrack; + sp <IMemory> iMem = mCblkMemory; + mLock.unlock(); + ssize_t written = 0; const int8_t *src = (const int8_t *)buffer; Buffer audioBuffer; @@ -953,21 +1004,29 @@ bool AudioTrack::processAudioBuffer(const sp<AudioTrackThread>& thread) uint32_t frames; size_t writtenSize; + mLock.lock(); + // acquire a strong reference on the IMemory and IAudioTrack so that they cannot be destroyed + // while we are accessing the cblk + sp <IAudioTrack> audioTrack = mAudioTrack; + sp <IMemory> iMem = mCblkMemory; + audio_track_cblk_t* cblk = mCblk; + mLock.unlock(); + // Manage underrun callback - if (mActive && (mCblk->framesReady() == 0)) { - LOGV("Underrun user: %x, server: %x, flags %04x", mCblk->user, mCblk->server, mCblk->flags); - if ((mCblk->flags & CBLK_UNDERRUN_MSK) == CBLK_UNDERRUN_OFF) { + if (mActive && (cblk->framesReady() == 0)) { + LOGV("Underrun user: %x, server: %x, flags %04x", cblk->user, cblk->server, cblk->flags); + if ((cblk->flags & CBLK_UNDERRUN_MSK) == CBLK_UNDERRUN_OFF) { mCbf(EVENT_UNDERRUN, mUserData, 0); - if (mCblk->server == mCblk->frameCount) { + if (cblk->server == cblk->frameCount) { mCbf(EVENT_BUFFER_END, mUserData, 0); } - mCblk->flags |= CBLK_UNDERRUN_ON; + cblk->flags |= CBLK_UNDERRUN_ON; if (mSharedBuffer != 0) return false; } } // Manage loop end callback - while (mLoopCount > mCblk->loopCount) { + while (mLoopCount > cblk->loopCount) { int loopCount = -1; mLoopCount--; if (mLoopCount >= 0) loopCount = mLoopCount; @@ -977,7 +1036,7 @@ bool AudioTrack::processAudioBuffer(const sp<AudioTrackThread>& thread) // Manage marker callback if (!mMarkerReached && (mMarkerPosition > 0)) { - if (mCblk->server >= mMarkerPosition) { + if (cblk->server >= mMarkerPosition) { mCbf(EVENT_MARKER, mUserData, (void *)&mMarkerPosition); mMarkerReached = true; } @@ -985,7 +1044,7 @@ bool AudioTrack::processAudioBuffer(const sp<AudioTrackThread>& thread) // Manage new position callback if (mUpdatePeriod > 0) { - while (mCblk->server >= mNewPosition) { + while (cblk->server >= mNewPosition) { mCbf(EVENT_NEW_POS, mUserData, (void *)&mNewPosition); mNewPosition += mUpdatePeriod; } @@ -1068,6 +1127,84 @@ bool AudioTrack::processAudioBuffer(const sp<AudioTrackThread>& thread) return true; } +// must be called with mLock and cblk.lock held. Callers must also hold strong references on +// the IAudioTrack and IMemory in case they are recreated here. +// If the IAudioTrack is successfully restored, the cblk pointer is updated +status_t AudioTrack::restoreTrack_l(audio_track_cblk_t*& cblk, bool fromStart) +{ + status_t result; + + if (!(cblk->flags & CBLK_RESTORING_MSK)) { + LOGW("dead IAudioTrack, creating a new one from %s", + fromStart ? "start()" : "obtainBuffer()"); + + cblk->flags |= CBLK_RESTORING_ON; + // signal old cblk condition so that other threads waiting for available buffers stop + // waiting now + cblk->cv.broadcast(); + cblk->lock.unlock(); + + // if the new IAudioTrack is created, createTrack_l() will modify the + // following member variables: mAudioTrack, mCblkMemory and mCblk. + // It will also delete the strong references on previous IAudioTrack and IMemory + result = createTrack_l(mStreamType, + cblk->sampleRate, + mFormat, + mChannelCount, + mFrameCount, + mFlags, + mSharedBuffer, + getOutput_l(), + false); + + if (result == NO_ERROR) { + if (!fromStart) { + mCblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS; + } + result = mAudioTrack->start(); + if (fromStart && result == NO_ERROR) { + mNewPosition = mCblk->server + mUpdatePeriod; + } + } + if (result != NO_ERROR) { + mActive = false; + } + + // signal old cblk condition for other threads waiting for restore completion + cblk->lock.lock(); + cblk->flags |= CBLK_RESTORED_MSK; + cblk->cv.broadcast(); + cblk->lock.unlock(); + } else { + if (!(cblk->flags & CBLK_RESTORED_MSK)) { + LOGW("dead IAudioTrack, waiting for a new one"); + mLock.unlock(); + result = cblk->cv.waitRelative(cblk->lock, milliseconds(RESTORE_TIMEOUT_MS)); + cblk->lock.unlock(); + mLock.lock(); + } else { + LOGW("dead IAudioTrack, already restored"); + result = NO_ERROR; + cblk->lock.unlock(); + } + if (result != NO_ERROR || mActive == 0) { + result = status_t(STOPPED); + } + } + LOGV("restoreTrack_l() status %d mActive %d cblk %p, old cblk %p flags %08x old flags %08x", + result, mActive, mCblk, cblk, mCblk->flags, cblk->flags); + + if (result == NO_ERROR) { + // from now on we switch to the newly created cblk + cblk = mCblk; + } + cblk->lock.lock(); + + LOGW_IF(result != NO_ERROR, "restoreTrack_l() error %d", result); + + return result; +} + status_t AudioTrack::dump(int fd, const Vector<String16>& args) const { @@ -1197,7 +1334,9 @@ bool audio_track_cblk_t::stepServer(uint32_t frameCount) this->server = s; - cv.signal(); + if (!(flags & CBLK_INVALID_MSK)) { + cv.signal(); + } lock.unlock(); return true; } diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index d3d1750..fee245f 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -8,6 +8,7 @@ <uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <uses-permission android:name="android.permission.GET_TASKS" /> + <uses-permission android:name="android.permission.MANAGE_USB" /> <application android:persistent="true" @@ -39,5 +40,22 @@ android:exported="true"> </activity> + <!-- started from UsbDeviceSettingsManager --> + <activity android:name=".usb.UsbPermissionActivity" + android:exported="true" + android:permission="android.permission.MANAGE_USB" + android:theme="@*android:style/Theme.Holo.Dialog.Alert" + android:finishOnCloseSystemDialogs="true" + android:excludeFromRecents="true"> + </activity> + + <!-- started from UsbDeviceSettingsManager --> + <activity android:name=".usb.UsbResolverActivity" + android:exported="true" + android:permission="android.permission.MANAGE_USB" + android:theme="@*android:style/Theme.Holo.Dialog.Alert" + android:finishOnCloseSystemDialogs="true" + android:excludeFromRecents="true"> + </activity> </application> </manifest> diff --git a/packages/SystemUI/res/layout-xlarge/status_bar_recent_item.xml b/packages/SystemUI/res/layout-xlarge/status_bar_recent_item.xml index bfa6c36..3f172e6 100644 --- a/packages/SystemUI/res/layout-xlarge/status_bar_recent_item.xml +++ b/packages/SystemUI/res/layout-xlarge/status_bar_recent_item.xml @@ -38,8 +38,8 @@ android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" - android:layout_marginLeft="123dip" - android:layout_marginTop="16dip" + android:layout_marginLeft="131dip" + android:layout_marginTop="13dip" android:maxWidth="@dimen/status_bar_recents_thumbnail_max_width" android:maxHeight="@dimen/status_bar_recents_thumbnail_max_height" android:adjustViewBounds="true" diff --git a/packages/SystemUI/res/layout-xlarge/status_bar_recent_panel.xml b/packages/SystemUI/res/layout-xlarge/status_bar_recent_panel.xml index eda19b7..42940be 100644 --- a/packages/SystemUI/res/layout-xlarge/status_bar_recent_panel.xml +++ b/packages/SystemUI/res/layout-xlarge/status_bar_recent_panel.xml @@ -51,7 +51,7 @@ android:stackFromBottom="true" android:fadingEdge="vertical" android:scrollbars="none" - android:fadingEdgeLength="30dip" + android:fadingEdgeLength="20dip" android:listSelector="@drawable/recents_thumbnail_bg_selector" /> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index ebd48e7..becad6a 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -115,4 +115,11 @@ <!-- Label of a toggle switch to disable use of the physical keyboard in favor of the IME. [CHAR LIMIT=25] --> <string name="status_bar_use_physical_keyboard">Use physical keyboard</string> + + <!-- Prompt for the USB device permission dialog [CHAR LIMIT=80] --> + <string name="usb_device_permission_prompt">Allow the application %1$s to access the USB device?</string> + + <!-- Prompt for the USB accessory permission dialog [CHAR LIMIT=80] --> + <string name="usb_accessory_permission_prompt">Allow the application %1$s to access the USB accessory?</string> + </resources> diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbDisconnectedReceiver.java b/packages/SystemUI/src/com/android/systemui/usb/UsbDisconnectedReceiver.java new file mode 100644 index 0000000..1edebbb --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/usb/UsbDisconnectedReceiver.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.usb; + +import android.app.Activity; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.hardware.usb.UsbAccessory; +import android.hardware.usb.UsbDevice; +import android.hardware.usb.UsbManager; + +// This class is used to close UsbPermissionsActivity and UsbResolverActivity +// if their device/accessory is disconnected while the dialog is still open +class UsbDisconnectedReceiver extends BroadcastReceiver { + private final Activity mActivity; + private UsbDevice mDevice; + private UsbAccessory mAccessory; + + public UsbDisconnectedReceiver(Activity activity, UsbDevice device) { + mActivity = activity; + mDevice = device; + + IntentFilter filter = new IntentFilter(UsbManager.ACTION_USB_DEVICE_DETACHED); + activity.registerReceiver(this, filter); + } + + public UsbDisconnectedReceiver(Activity activity, UsbAccessory accessory) { + mActivity = activity; + mAccessory = accessory; + + IntentFilter filter = new IntentFilter(UsbManager.ACTION_USB_ACCESSORY_DETACHED); + activity.registerReceiver(this, filter); + } + + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) { + UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); + if (device != null && device.equals(mDevice)) { + mActivity.finish(); + } + } else if (UsbManager.ACTION_USB_ACCESSORY_DETACHED.equals(action)) { + UsbAccessory accessory = + (UsbAccessory)intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY); + if (accessory != null && accessory.equals(mAccessory)) { + mActivity.finish(); + } + } + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java new file mode 100644 index 0000000..f1784df --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.usb; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.PendingIntent; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.hardware.usb.IUsbManager; +import android.hardware.usb.UsbDevice; +import android.hardware.usb.UsbAccessory; +import android.hardware.usb.UsbManager; +import android.os.Bundle; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.CheckBox; +import android.widget.CompoundButton; +import android.widget.TextView; + +import com.android.internal.app.AlertActivity; +import com.android.internal.app.AlertController; + +import com.android.systemui.R; + +public class UsbPermissionActivity extends AlertActivity + implements DialogInterface.OnClickListener, CheckBox.OnCheckedChangeListener { + + private static final String TAG = "UsbPermissionActivity"; + + private CheckBox mAlwaysCheck; + private TextView mClearDefaultHint; + private UsbDevice mDevice; + private UsbAccessory mAccessory; + private PendingIntent mPendingIntent; + private String mPackageName; + private int mUid; + private boolean mPermissionGranted; + private UsbDisconnectedReceiver mDisconnectedReceiver; + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + + Intent intent = getIntent(); + mDevice = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); + mAccessory = (UsbAccessory)intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY); + mPendingIntent = (PendingIntent)intent.getParcelableExtra(Intent.EXTRA_INTENT); + mUid = intent.getIntExtra("uid", 0); + mPackageName = intent.getStringExtra("package"); + + PackageManager packageManager = getPackageManager(); + ApplicationInfo aInfo; + try { + aInfo = packageManager.getApplicationInfo(mPackageName, 0); + } catch (PackageManager.NameNotFoundException e) { + Log.e(TAG, "unable to look up package name", e); + finish(); + return; + } + String appName = aInfo.loadLabel(packageManager).toString(); + + final AlertController.AlertParams ap = mAlertParams; + ap.mIcon = aInfo.loadIcon(packageManager); + ap.mTitle = appName; + if (mDevice == null) { + ap.mMessage = getString(R.string.usb_accessory_permission_prompt, appName); + mDisconnectedReceiver = new UsbDisconnectedReceiver(this, mAccessory); + } else { + ap.mMessage = getString(R.string.usb_device_permission_prompt, appName); + mDisconnectedReceiver = new UsbDisconnectedReceiver(this, mDevice); + } + ap.mPositiveButtonText = getString(com.android.internal.R.string.ok); + ap.mNegativeButtonText = getString(com.android.internal.R.string.cancel); + ap.mPositiveButtonListener = this; + ap.mNegativeButtonListener = this; + + // add "always use" checkbox + LayoutInflater inflater = (LayoutInflater)getSystemService( + Context.LAYOUT_INFLATER_SERVICE); + ap.mView = inflater.inflate(com.android.internal.R.layout.always_use_checkbox, null); + mAlwaysCheck = (CheckBox)ap.mView.findViewById(com.android.internal.R.id.alwaysUse); + mAlwaysCheck.setText(com.android.internal.R.string.alwaysUse); + mAlwaysCheck.setOnCheckedChangeListener(this); + mClearDefaultHint = (TextView)ap.mView.findViewById( + com.android.internal.R.id.clearDefaultHint); + mClearDefaultHint.setVisibility(View.GONE); + + setupAlert(); + + } + + @Override + public void onDestroy() { + IBinder b = ServiceManager.getService(USB_SERVICE); + IUsbManager service = IUsbManager.Stub.asInterface(b); + + // send response via pending intent + Intent intent = new Intent(); + try { + if (mDevice != null) { + intent.putExtra(UsbManager.EXTRA_DEVICE, mDevice); + if (mPermissionGranted) { + service.grantDevicePermission(mDevice, mUid); + if (mAlwaysCheck.isChecked()) { + service.setDevicePackage(mDevice, mPackageName); + } + } + } + if (mAccessory != null) { + intent.putExtra(UsbManager.EXTRA_ACCESSORY, mAccessory); + if (mPermissionGranted) { + service.grantAccessoryPermission(mAccessory, mUid); + if (mAlwaysCheck.isChecked()) { + service.setAccessoryPackage(mAccessory, mPackageName); + } + } + } + intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, mPermissionGranted); + mPendingIntent.send(this, 0, intent); + } catch (PendingIntent.CanceledException e) { + Log.w(TAG, "PendingIntent was cancelled"); + } catch (RemoteException e) { + Log.e(TAG, "IUsbService connection failed", e); + } + + if (mDisconnectedReceiver != null) { + unregisterReceiver(mDisconnectedReceiver); + } + super.onDestroy(); + } + + public void onClick(DialogInterface dialog, int which) { + if (which == AlertDialog.BUTTON_POSITIVE) { + mPermissionGranted = true; + } + finish(); + } + + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + if (mClearDefaultHint == null) return; + + if(isChecked) { + mClearDefaultHint.setVisibility(View.VISIBLE); + } else { + mClearDefaultHint.setVisibility(View.GONE); + } + } +} diff --git a/services/java/com/android/server/usb/UsbResolverActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbResolverActivity.java index e8a09a5..84d73dd 100644 --- a/services/java/com/android/server/usb/UsbResolverActivity.java +++ b/packages/SystemUI/src/com/android/systemui/usb/UsbResolverActivity.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.usb; +package com.android.systemui.usb; import com.android.internal.app.ResolverActivity; @@ -39,6 +39,10 @@ public class UsbResolverActivity extends ResolverActivity { public static final String TAG = "UsbResolverActivity"; public static final String EXTRA_RESOLVE_INFOS = "rlist"; + private UsbDevice mDevice; + private UsbAccessory mAccessory; + private UsbDisconnectedReceiver mDisconnectedReceiver; + @Override protected void onCreate(Bundle savedInstanceState) { Intent intent = getIntent(); @@ -50,7 +54,6 @@ public class UsbResolverActivity extends ResolverActivity { } Intent target = (Intent)targetParcelable; ArrayList<ResolveInfo> rList = intent.getParcelableArrayListExtra(EXTRA_RESOLVE_INFOS); - Log.d(TAG, "rList.size() " + rList.size()); CharSequence title = getResources().getText(com.android.internal.R.string.chooseUsbActivity); super.onCreate(savedInstanceState, target, title, null, rList, true, /* Set alwaysUseOption to true to enable "always use this app" checkbox. */ @@ -58,6 +61,27 @@ public class UsbResolverActivity extends ResolverActivity { This is necessary because this activity is needed for the user to allow the application permission to access the device */ ); + + mDevice = (UsbDevice)target.getParcelableExtra(UsbManager.EXTRA_DEVICE); + if (mDevice != null) { + mDisconnectedReceiver = new UsbDisconnectedReceiver(this, mDevice); + } else { + mAccessory = (UsbAccessory)target.getParcelableExtra(UsbManager.EXTRA_ACCESSORY); + if (mAccessory == null) { + Log.e(TAG, "no device or accessory"); + finish(); + return; + } + mDisconnectedReceiver = new UsbDisconnectedReceiver(this, mAccessory); + } + } + + @Override + protected void onDestroy() { + if (mDisconnectedReceiver != null) { + unregisterReceiver(mDisconnectedReceiver); + } + super.onDestroy(); } protected void onIntentSelected(ResolveInfo ri, Intent intent, boolean alwaysCheck) { @@ -65,28 +89,24 @@ public class UsbResolverActivity extends ResolverActivity { IBinder b = ServiceManager.getService(USB_SERVICE); IUsbManager service = IUsbManager.Stub.asInterface(b); int uid = ri.activityInfo.applicationInfo.uid; - String action = intent.getAction(); - if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) { - UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); + if (mDevice != null) { // grant permission for the device - service.grantDevicePermission(device, uid); + service.grantDevicePermission(mDevice, uid); // set or clear default setting if (alwaysCheck) { - service.setDevicePackage(device, ri.activityInfo.packageName); + service.setDevicePackage(mDevice, ri.activityInfo.packageName); } else { - service.setDevicePackage(device, null); + service.setDevicePackage(mDevice, null); } - } else if (UsbManager.ACTION_USB_ACCESSORY_ATTACHED.equals(action)) { - UsbAccessory accessory = (UsbAccessory)intent.getParcelableExtra( - UsbManager.EXTRA_ACCESSORY); + } else if (mAccessory != null) { // grant permission for the accessory - service.grantAccessoryPermission(accessory, uid); + service.grantAccessoryPermission(mAccessory, uid); // set or clear default setting if (alwaysCheck) { - service.setAccessoryPackage(accessory, ri.activityInfo.packageName); + service.setAccessoryPackage(mAccessory, ri.activityInfo.packageName); } else { - service.setAccessoryPackage(accessory, null); + service.setAccessoryPackage(mAccessory, null); } } diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java index adc49ae..3c6c427 100644 --- a/services/java/com/android/server/WifiService.java +++ b/services/java/com/android/server/WifiService.java @@ -62,6 +62,7 @@ import android.util.Slog; import java.util.ArrayList; import java.util.List; import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicBoolean; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -99,9 +100,6 @@ public class WifiService extends IWifiManager.Stub { /* Chipset supports background scan */ private final boolean mBackgroundScanSupported; - // true if the user enabled Wifi while in airplane mode - private AtomicBoolean mAirplaneModeOverwridden = new AtomicBoolean(false); - private final LockList mLocks = new LockList(); // some wifi lock statistics private int mFullHighPerfLocksAcquired; @@ -144,6 +142,14 @@ public class WifiService extends IWifiManager.Stub { private static final String ACTION_DEVICE_IDLE = "com.android.server.WifiManager.action.DEVICE_IDLE"; + private static final int WIFI_DISABLED = 0; + private static final int WIFI_ENABLED = 1; + /* Wifi enabled while in airplane mode */ + private static final int WIFI_ENABLED_AIRPLANE_OVERRIDE = 2; + + private AtomicInteger mWifiState = new AtomicInteger(WIFI_DISABLED); + private AtomicBoolean mAirplaneModeOn = new AtomicBoolean(false); + private boolean mIsReceiverRegistered = false; @@ -338,11 +344,11 @@ public class WifiService extends IWifiManager.Stub { new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - // clear our flag indicating the user has overwridden airplane mode - mAirplaneModeOverwridden.set(false); - // on airplane disable, restore Wifi if the saved state indicates so - if (!isAirplaneModeOn() && testAndClearWifiSavedState()) { - persistWifiEnabled(true); + mAirplaneModeOn.set(isAirplaneModeOn()); + /* On airplane mode disable, restore wifi state if necessary */ + if (!mAirplaneModeOn.get() && (testAndClearWifiSavedState() || + mWifiState.get() == WIFI_ENABLED_AIRPLANE_OVERRIDE)) { + persistWifiEnabled(true); } updateWifiState(); } @@ -402,9 +408,10 @@ public class WifiService extends IWifiManager.Stub { * This function is used only at boot time */ public void checkAndStartWifi() { - /* Start if Wi-Fi is enabled or the saved state indicates Wi-Fi was on */ - boolean wifiEnabled = !isAirplaneModeOn() - && (getPersistedWifiEnabled() || testAndClearWifiSavedState()); + mAirplaneModeOn.set(isAirplaneModeOn()); + mWifiState.set(getPersistedWifiState()); + /* Start if Wi-Fi should be enabled or the saved state indicates Wi-Fi was on */ + boolean wifiEnabled = shouldWifiBeEnabled() || testAndClearWifiSavedState(); Slog.i(TAG, "WifiService starting up with Wi-Fi " + (wifiEnabled ? "enabled" : "disabled")); setWifiEnabled(wifiEnabled); @@ -423,21 +430,39 @@ public class WifiService extends IWifiManager.Stub { return (wifiSavedState == 1); } - private boolean getPersistedWifiEnabled() { + private int getPersistedWifiState() { final ContentResolver cr = mContext.getContentResolver(); try { - return Settings.Secure.getInt(cr, Settings.Secure.WIFI_ON) == 1; + return Settings.Secure.getInt(cr, Settings.Secure.WIFI_ON); } catch (Settings.SettingNotFoundException e) { - Settings.Secure.putInt(cr, Settings.Secure.WIFI_ON, 0); - return false; + Settings.Secure.putInt(cr, Settings.Secure.WIFI_ON, WIFI_DISABLED); + return WIFI_DISABLED; + } + } + + private boolean shouldWifiBeEnabled() { + if (mAirplaneModeOn.get()) { + return mWifiState.get() == WIFI_ENABLED_AIRPLANE_OVERRIDE; + } else { + return mWifiState.get() != WIFI_DISABLED; } } private void persistWifiEnabled(boolean enabled) { final ContentResolver cr = mContext.getContentResolver(); - Settings.Secure.putInt(cr, Settings.Secure.WIFI_ON, enabled ? 1 : 0); + if (enabled) { + if (isAirplaneModeOn() && isAirplaneToggleable()) { + mWifiState.set(WIFI_ENABLED_AIRPLANE_OVERRIDE); + } else { + mWifiState.set(WIFI_ENABLED); + } + } else { + mWifiState.set(WIFI_DISABLED); + } + Settings.Secure.putInt(cr, Settings.Secure.WIFI_ON, mWifiState.get()); } + /** * see {@link android.net.wifi.WifiManager#pingSupplicant()} * @return {@code true} if the operation succeeds, {@code false} otherwise @@ -490,11 +515,6 @@ public class WifiService extends IWifiManager.Stub { Slog.e(TAG, "Invoking mWifiStateMachine.setWifiEnabled\n"); } - // set a flag if the user is enabling Wifi while in airplane mode - if (enable && isAirplaneModeOn() && isAirplaneToggleable()) { - mAirplaneModeOverwridden.set(true); - } - if (enable) { reportStartWorkSource(); } @@ -1037,11 +1057,8 @@ public class WifiService extends IWifiManager.Stub { } private void updateWifiState() { - boolean wifiEnabled = getPersistedWifiEnabled(); - boolean airplaneMode = isAirplaneModeOn() && !mAirplaneModeOverwridden.get(); boolean lockHeld = mLocks.hasLocks(); int strongestLockMode = WifiManager.WIFI_MODE_FULL; - boolean wifiShouldBeEnabled = wifiEnabled && !airplaneMode; boolean wifiShouldBeStarted = !mDeviceIdle || lockHeld; if (lockHeld) { @@ -1053,11 +1070,11 @@ public class WifiService extends IWifiManager.Stub { } /* Disable tethering when airplane mode is enabled */ - if (airplaneMode) { + if (mAirplaneModeOn.get()) { mWifiStateMachine.setWifiApEnabled(null, false); } - if (wifiShouldBeEnabled) { + if (shouldWifiBeEnabled()) { if (wifiShouldBeStarted) { reportStartWorkSource(); mWifiStateMachine.setWifiEnabled(true); diff --git a/services/java/com/android/server/usb/UsbDeviceSettingsManager.java b/services/java/com/android/server/usb/UsbDeviceSettingsManager.java index 2f22fe1..25eac6f 100644 --- a/services/java/com/android/server/usb/UsbDeviceSettingsManager.java +++ b/services/java/com/android/server/usb/UsbDeviceSettingsManager.java @@ -16,11 +16,13 @@ package com.android.server.usb; +import android.app.PendingIntent; import android.content.ActivityNotFoundException; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; @@ -33,7 +35,7 @@ import android.os.Binder; import android.os.FileUtils; import android.os.Process; import android.util.Log; -import android.util.SparseArray; +import android.util.SparseBooleanArray; import android.util.Xml; import com.android.internal.content.PackageMonitor; @@ -63,16 +65,16 @@ class UsbDeviceSettingsManager { private final Context mContext; - // maps UID to user approved USB devices - private final SparseArray<ArrayList<DeviceFilter>> mDevicePermissionMap = - new SparseArray<ArrayList<DeviceFilter>>(); - // maps UID to user approved USB accessories - private final SparseArray<ArrayList<AccessoryFilter>> mAccessoryPermissionMap = - new SparseArray<ArrayList<AccessoryFilter>>(); + // Temporary mapping USB device name to list of UIDs with permissions for the device + private final HashMap<String, SparseBooleanArray> mDevicePermissionMap = + new HashMap<String, SparseBooleanArray>(); + // Temporary mapping UsbAccessory to list of UIDs with permissions for the accessory + private final HashMap<UsbAccessory, SparseBooleanArray> mAccessoryPermissionMap = + new HashMap<UsbAccessory, SparseBooleanArray>(); // Maps DeviceFilter to user preferred application package private final HashMap<DeviceFilter, String> mDevicePreferenceMap = new HashMap<DeviceFilter, String>(); - // Maps DeviceFilter to user preferred application package + // Maps AccessoryFilter to user preferred application package private final HashMap<AccessoryFilter, String> mAccessoryPreferenceMap = new HashMap<AccessoryFilter, String>(); @@ -352,15 +354,6 @@ class UsbDeviceSettingsManager { } } } - - public void onUidRemoved(int uid) { - synchronized (mLock) { - // clear all permissions for the UID - if (clearUidDefaultsLocked(uid)) { - writeSettingsLocked(); - } - } - } } MyPackageMonitor mPackageMonitor = new MyPackageMonitor(); @@ -372,44 +365,6 @@ class UsbDeviceSettingsManager { mPackageMonitor.register(context, true); } - private void readDevicePermission(XmlPullParser parser) - throws XmlPullParserException, IOException { - int uid = -1; - ArrayList<DeviceFilter> filters = new ArrayList<DeviceFilter>(); - int count = parser.getAttributeCount(); - for (int i = 0; i < count; i++) { - if ("uid".equals(parser.getAttributeName(i))) { - uid = Integer.parseInt(parser.getAttributeValue(i)); - break; - } - } - XmlUtils.nextElement(parser); - while ("usb-device".equals(parser.getName())) { - filters.add(DeviceFilter.read(parser)); - XmlUtils.nextElement(parser); - } - mDevicePermissionMap.put(uid, filters); - } - - private void readAccessoryPermission(XmlPullParser parser) - throws XmlPullParserException, IOException { - int uid = -1; - ArrayList<AccessoryFilter> filters = new ArrayList<AccessoryFilter>(); - int count = parser.getAttributeCount(); - for (int i = 0; i < count; i++) { - if ("uid".equals(parser.getAttributeName(i))) { - uid = Integer.parseInt(parser.getAttributeValue(i)); - break; - } - } - XmlUtils.nextElement(parser); - while ("usb-accessory".equals(parser.getName())) { - filters.add(AccessoryFilter.read(parser)); - XmlUtils.nextElement(parser); - } - mAccessoryPermissionMap.put(uid, filters); - } - private void readPreference(XmlPullParser parser) throws XmlPullParserException, IOException { String packageName = null; @@ -441,11 +396,7 @@ class UsbDeviceSettingsManager { XmlUtils.nextElement(parser); while (parser.getEventType() != XmlPullParser.END_DOCUMENT) { String tagName = parser.getName(); - if ("device-permission".equals(tagName)) { - readDevicePermission(parser); - } else if ("accessory-permission".equals(tagName)) { - readAccessoryPermission(parser); - } else if ("preference".equals(tagName)) { + if ("preference".equals(tagName)) { readPreference(parser); } else { XmlUtils.nextElement(parser); @@ -478,32 +429,6 @@ class UsbDeviceSettingsManager { serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); serializer.startTag(null, "settings"); - int count = mDevicePermissionMap.size(); - for (int i = 0; i < count; i++) { - int uid = mDevicePermissionMap.keyAt(i); - ArrayList<DeviceFilter> filters = mDevicePermissionMap.valueAt(i); - serializer.startTag(null, "device-permission"); - serializer.attribute(null, "uid", Integer.toString(uid)); - int filterCount = filters.size(); - for (int j = 0; j < filterCount; j++) { - filters.get(j).write(serializer); - } - serializer.endTag(null, "device-permission"); - } - - count = mAccessoryPermissionMap.size(); - for (int i = 0; i < count; i++) { - int uid = mAccessoryPermissionMap.keyAt(i); - ArrayList<AccessoryFilter> filters = mAccessoryPermissionMap.valueAt(i); - serializer.startTag(null, "accessory-permission"); - serializer.attribute(null, "uid", Integer.toString(uid)); - int filterCount = filters.size(); - for (int j = 0; j < filterCount; j++) { - filters.get(j).write(serializer); - } - serializer.endTag(null, "accessory-permission"); - } - for (DeviceFilter filter : mDevicePreferenceMap.keySet()) { serializer.startTag(null, "preference"); serializer.attribute(null, "package", mDevicePreferenceMap.get(filter)); @@ -602,53 +527,26 @@ class UsbDeviceSettingsManager { } public void deviceAttached(UsbDevice device) { - Intent deviceIntent = new Intent(UsbManager.ACTION_USB_DEVICE_ATTACHED); - deviceIntent.putExtra(UsbManager.EXTRA_DEVICE, device); - deviceIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_ATTACHED); + intent.putExtra(UsbManager.EXTRA_DEVICE, device); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); ArrayList<ResolveInfo> matches; String defaultPackage; synchronized (mLock) { - matches = getDeviceMatchesLocked(device, deviceIntent); + matches = getDeviceMatchesLocked(device, intent); // Launch our default activity directly, if we have one. // Otherwise we will start the UsbResolverActivity to allow the user to choose. defaultPackage = mDevicePreferenceMap.get(new DeviceFilter(device)); } - int count = matches.size(); - // don't show the resolver activity if there are no choices available - if (count == 0) return; - - if (defaultPackage != null) { - for (int i = 0; i < count; i++) { - ResolveInfo rInfo = matches.get(i); - if (rInfo.activityInfo != null && - defaultPackage.equals(rInfo.activityInfo.packageName)) { - try { - deviceIntent.setComponent(new ComponentName( - defaultPackage, rInfo.activityInfo.name)); - mContext.startActivity(deviceIntent); - } catch (ActivityNotFoundException e) { - Log.e(TAG, "startActivity failed", e); - } - return; - } - } - } - - Intent intent = new Intent(mContext, UsbResolverActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - - intent.putExtra(Intent.EXTRA_INTENT, deviceIntent); - intent.putParcelableArrayListExtra(UsbResolverActivity.EXTRA_RESOLVE_INFOS, matches); - try { - mContext.startActivity(intent); - } catch (ActivityNotFoundException e) { - Log.w(TAG, "unable to start UsbResolverActivity"); - } + resolveActivity(intent, matches, defaultPackage, device, null); } public void deviceDetached(UsbDevice device) { + // clear temporary permissions for the device + mDevicePermissionMap.remove(device.getDeviceName()); + Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_DETACHED); intent.putExtra(UsbManager.EXTRA_DEVICE, device); Log.d(TAG, "usbDeviceRemoved, sending " + intent); @@ -656,93 +554,198 @@ class UsbDeviceSettingsManager { } public void accessoryAttached(UsbAccessory accessory) { - Intent accessoryIntent = new Intent(UsbManager.ACTION_USB_ACCESSORY_ATTACHED); - accessoryIntent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory); - accessoryIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + Intent intent = new Intent(UsbManager.ACTION_USB_ACCESSORY_ATTACHED); + intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); ArrayList<ResolveInfo> matches; String defaultPackage; synchronized (mLock) { - matches = getAccessoryMatchesLocked(accessory, accessoryIntent); + matches = getAccessoryMatchesLocked(accessory, intent); // Launch our default activity directly, if we have one. // Otherwise we will start the UsbResolverActivity to allow the user to choose. defaultPackage = mAccessoryPreferenceMap.get(new AccessoryFilter(accessory)); } + resolveActivity(intent, matches, defaultPackage, null, accessory); + } + + public void accessoryDetached(UsbAccessory accessory) { + // clear temporary permissions for the accessory + mAccessoryPermissionMap.remove(accessory); + + Intent intent = new Intent( + UsbManager.ACTION_USB_ACCESSORY_DETACHED); + intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory); + mContext.sendBroadcast(intent); + } + + private void resolveActivity(Intent intent, ArrayList<ResolveInfo> matches, + String defaultPackage, UsbDevice device, UsbAccessory accessory) { int count = matches.size(); // don't show the resolver activity if there are no choices available if (count == 0) return; - if (defaultPackage != null) { + ResolveInfo defaultRI = null; + if (count == 1 && defaultPackage == null) { + // Check to see if our single choice is on the system partition. + // If so, treat it as our default without calling UsbResolverActivity + ResolveInfo rInfo = matches.get(0); + if (rInfo.activityInfo != null && + rInfo.activityInfo.applicationInfo != null && + (rInfo.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { + defaultRI = rInfo; + } + } + + if (defaultRI == null && defaultPackage != null) { + // look for default activity for (int i = 0; i < count; i++) { ResolveInfo rInfo = matches.get(i); if (rInfo.activityInfo != null && defaultPackage.equals(rInfo.activityInfo.packageName)) { - try { - accessoryIntent.setComponent(new ComponentName( - defaultPackage, rInfo.activityInfo.name)); - mContext.startActivity(accessoryIntent); - } catch (ActivityNotFoundException e) { - Log.e(TAG, "startActivity failed", e); - } - return; + defaultRI = rInfo; + break; } } } - Intent intent = new Intent(mContext, UsbResolverActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + if (defaultRI != null) { + // grant permission for default activity + if (device != null) { + grantDevicePermission(device, defaultRI.activityInfo.applicationInfo.uid); + } else if (accessory != null) { + grantAccessoryPermission(accessory, defaultRI.activityInfo.applicationInfo.uid); + } - intent.putExtra(Intent.EXTRA_INTENT, accessoryIntent); - intent.putParcelableArrayListExtra(UsbResolverActivity.EXTRA_RESOLVE_INFOS, matches); - try { - mContext.startActivity(intent); - } catch (ActivityNotFoundException e) { - Log.w(TAG, "unable to start UsbResolverActivity"); + // start default activity directly + try { + intent.setComponent( + new ComponentName(defaultRI.activityInfo.packageName, + defaultRI.activityInfo.name)); + mContext.startActivity(intent); + } catch (ActivityNotFoundException e) { + Log.e(TAG, "startActivity failed", e); + } + } else { + long identity = Binder.clearCallingIdentity(); + + // start UsbResolverActivity so user can choose an activity + Intent resolverIntent = new Intent(); + resolverIntent.setClassName("com.android.systemui", + "com.android.systemui.usb.UsbResolverActivity"); + resolverIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + resolverIntent.putExtra(Intent.EXTRA_INTENT, intent); + resolverIntent.putParcelableArrayListExtra("rlist", matches); + try { + mContext.startActivity(resolverIntent); + } catch (ActivityNotFoundException e) { + Log.e(TAG, "unable to start UsbResolverActivity"); + } finally { + Binder.restoreCallingIdentity(identity); + } } } - public void accessoryDetached(UsbAccessory accessory) { - Intent intent = new Intent( - UsbManager.ACTION_USB_ACCESSORY_DETACHED); - intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory); - mContext.sendBroadcast(intent); + public boolean hasPermission(UsbDevice device) { + synchronized (mLock) { + SparseBooleanArray uidList = mDevicePermissionMap.get(device.getDeviceName()); + if (uidList == null) { + return false; + } + return uidList.get(Binder.getCallingUid()); + } } - public void checkPermission(UsbDevice device) { - if (device == null) return; + public boolean hasPermission(UsbAccessory accessory) { synchronized (mLock) { - ArrayList<DeviceFilter> filterList = mDevicePermissionMap.get(Binder.getCallingUid()); - if (filterList != null) { - int count = filterList.size(); - for (int i = 0; i < count; i++) { - DeviceFilter filter = filterList.get(i); - if (filter.equals(device)) { - // permission allowed - return; - } - } + SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory); + if (uidList == null) { + return false; } + return uidList.get(Binder.getCallingUid()); + } + } + + public void checkPermission(UsbDevice device) { + if (!hasPermission(device)) { + throw new SecurityException("User has not given permission to device " + device); } - throw new SecurityException("User has not given permission to device " + device); } public void checkPermission(UsbAccessory accessory) { - if (accessory == null) return; - synchronized (mLock) { - ArrayList<AccessoryFilter> filterList = mAccessoryPermissionMap.get(Binder.getCallingUid()); - if (filterList != null) { - int count = filterList.size(); - for (int i = 0; i < count; i++) { - AccessoryFilter filter = filterList.get(i); - if (filter.equals(accessory)) { - // permission allowed - return; - } - } + if (!hasPermission(accessory)) { + throw new SecurityException("User has not given permission to accessory " + accessory); + } + } + + private void requestPermissionDialog(Intent intent, String packageName, PendingIntent pi) { + int uid = Binder.getCallingUid(); + + // compare uid with packageName to foil apps pretending to be someone else + try { + ApplicationInfo aInfo = mContext.getPackageManager().getApplicationInfo(packageName, 0); + if (aInfo.uid != uid) { + throw new IllegalArgumentException("package " + packageName + + " does not match caller's uid " + uid); } + } catch (PackageManager.NameNotFoundException e) { + throw new IllegalArgumentException("package " + packageName + " not found"); } - throw new SecurityException("User has not given permission to accessory " + accessory); + + long identity = Binder.clearCallingIdentity(); + intent.setClassName("com.android.systemui", + "com.android.systemui.usb.UsbPermissionActivity"); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.putExtra(Intent.EXTRA_INTENT, pi); + intent.putExtra("package", packageName); + intent.putExtra("uid", uid); + try { + mContext.startActivity(intent); + } catch (ActivityNotFoundException e) { + Log.e(TAG, "unable to start UsbPermissionActivity"); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + public void requestPermission(UsbDevice device, String packageName, PendingIntent pi) { + Intent intent = new Intent(); + + // respond immediately if permission has already been granted + if (hasPermission(device)) { + intent.putExtra(UsbManager.EXTRA_DEVICE, device); + intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true); + try { + pi.send(mContext, 0, intent); + } catch (PendingIntent.CanceledException e) { + Log.w(TAG, "requestPermission PendingIntent was cancelled"); + } + return; + } + + // start UsbPermissionActivity so user can choose an activity + intent.putExtra(UsbManager.EXTRA_DEVICE, device); + requestPermissionDialog(intent, packageName, pi); + } + + public void requestPermission(UsbAccessory accessory, String packageName, PendingIntent pi) { + Intent intent = new Intent(); + + // respond immediately if permission has already been granted + if (hasPermission(accessory)) { + intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory); + intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true); + try { + pi.send(mContext, 0, intent); + } catch (PendingIntent.CanceledException e) { + Log.w(TAG, "requestPermission PendingIntent was cancelled"); + } + return; + } + + intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory); + requestPermissionDialog(intent, packageName, pi); } public void setDevicePackage(UsbDevice device, String packageName) { @@ -783,73 +786,43 @@ class UsbDeviceSettingsManager { public void grantDevicePermission(UsbDevice device, int uid) { synchronized (mLock) { - ArrayList<DeviceFilter> filterList = mDevicePermissionMap.get(uid); - if (filterList == null) { - filterList = new ArrayList<DeviceFilter>(); - mDevicePermissionMap.put(uid, filterList); - } else { - int count = filterList.size(); - for (int i = 0; i < count; i++) { - if (filterList.get(i).equals(device)) return; - } + String deviceName = device.getDeviceName(); + SparseBooleanArray uidList = mDevicePermissionMap.get(deviceName); + if (uidList == null) { + uidList = new SparseBooleanArray(1); + mDevicePermissionMap.put(deviceName, uidList); } - filterList.add(new DeviceFilter(device)); - writeSettingsLocked(); + uidList.put(uid, true); } } public void grantAccessoryPermission(UsbAccessory accessory, int uid) { synchronized (mLock) { - ArrayList<AccessoryFilter> filterList = mAccessoryPermissionMap.get(uid); - if (filterList == null) { - filterList = new ArrayList<AccessoryFilter>(); - mAccessoryPermissionMap.put(uid, filterList); - } else { - int count = filterList.size(); - for (int i = 0; i < count; i++) { - if (filterList.get(i).equals(accessory)) return; - } + SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory); + if (uidList == null) { + uidList = new SparseBooleanArray(1); + mAccessoryPermissionMap.put(accessory, uidList); } - filterList.add(new AccessoryFilter(accessory)); - writeSettingsLocked(); + uidList.put(uid, true); } } - public boolean hasDefaults(String packageName, int uid) { + public boolean hasDefaults(String packageName) { synchronized (mLock) { - if (mDevicePermissionMap.get(uid) != null) return true; - if (mAccessoryPermissionMap.get(uid) != null) return true; if (mDevicePreferenceMap.values().contains(packageName)) return true; if (mAccessoryPreferenceMap.values().contains(packageName)) return true; return false; } } - public void clearDefaults(String packageName, int uid) { + public void clearDefaults(String packageName) { synchronized (mLock) { - boolean packageCleared = clearPackageDefaultsLocked(packageName); - boolean uidCleared = clearUidDefaultsLocked(uid); - if (packageCleared || uidCleared) { + if (clearPackageDefaultsLocked(packageName)) { writeSettingsLocked(); } } } - private boolean clearUidDefaultsLocked(int uid) { - boolean cleared = false; - int index = mDevicePermissionMap.indexOfKey(uid); - if (index >= 0) { - mDevicePermissionMap.removeAt(index); - cleared = true; - } - index = mAccessoryPermissionMap.indexOfKey(uid); - if (index >= 0) { - mAccessoryPermissionMap.removeAt(index); - cleared = true; - } - return cleared; - } - private boolean clearPackageDefaultsLocked(String packageName) { boolean cleared = false; synchronized (mLock) { @@ -882,24 +855,24 @@ class UsbDeviceSettingsManager { public void dump(FileDescriptor fd, PrintWriter pw) { synchronized (mLock) { pw.println(" Device permissions:"); - int count = mDevicePermissionMap.size(); - for (int i = 0; i < count; i++) { - int uid = mDevicePermissionMap.keyAt(i); - pw.println(" " + "uid " + uid + ":"); - ArrayList<DeviceFilter> filters = mDevicePermissionMap.valueAt(i); - for (DeviceFilter filter : filters) { - pw.println(" " + filter); + for (String deviceName : mDevicePermissionMap.keySet()) { + pw.print(" " + deviceName + ": "); + SparseBooleanArray uidList = mDevicePermissionMap.get(deviceName); + int count = uidList.size(); + for (int i = 0; i < count; i++) { + pw.print(Integer.toString(uidList.keyAt(i)) + " "); } + pw.println(""); } pw.println(" Accessory permissions:"); - count = mAccessoryPermissionMap.size(); - for (int i = 0; i < count; i++) { - int uid = mAccessoryPermissionMap.keyAt(i); - pw.println(" " + "uid " + uid + ":"); - ArrayList<AccessoryFilter> filters = mAccessoryPermissionMap.valueAt(i); - for (AccessoryFilter filter : filters) { - pw.println(" " + filter); + for (UsbAccessory accessory : mAccessoryPermissionMap.keySet()) { + pw.print(" " + accessory + ": "); + SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory); + int count = uidList.size(); + for (int i = 0; i < count; i++) { + pw.print(Integer.toString(uidList.keyAt(i)) + " "); } + pw.println(""); } pw.println(" Device preferences:"); for (DeviceFilter filter : mDevicePreferenceMap.keySet()) { diff --git a/services/java/com/android/server/usb/UsbService.java b/services/java/com/android/server/usb/UsbService.java index 8170c61..71f2a9b 100644 --- a/services/java/com/android/server/usb/UsbService.java +++ b/services/java/com/android/server/usb/UsbService.java @@ -16,6 +16,7 @@ package com.android.server.usb; +import android.app.PendingIntent; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; @@ -454,6 +455,24 @@ public class UsbService extends IUsbManager.Stub { mDeviceManager.setAccessoryPackage(accessory, packageName); } + public boolean hasDevicePermission(UsbDevice device) { + return mDeviceManager.hasPermission(device); + } + + public boolean hasAccessoryPermission(UsbAccessory accessory) { + return mDeviceManager.hasPermission(accessory); + } + + public void requestDevicePermission(UsbDevice device, String packageName, + PendingIntent pi) { + mDeviceManager.requestPermission(device, packageName, pi); + } + + public void requestAccessoryPermission(UsbAccessory accessory, String packageName, + PendingIntent pi) { + mDeviceManager.requestPermission(accessory, packageName, pi); + } + public void grantDevicePermission(UsbDevice device, int uid) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); mDeviceManager.grantDevicePermission(device, uid); @@ -464,14 +483,14 @@ public class UsbService extends IUsbManager.Stub { mDeviceManager.grantAccessoryPermission(accessory, uid); } - public boolean hasDefaults(String packageName, int uid) { + public boolean hasDefaults(String packageName) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); - return mDeviceManager.hasDefaults(packageName, uid); + return mDeviceManager.hasDefaults(packageName); } - public void clearDefaults(String packageName, int uid) { + public void clearDefaults(String packageName) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); - mDeviceManager.clearDefaults(packageName, uid); + mDeviceManager.clearDefaults(packageName); } /* diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java index ee5c1f0..33e6a36 100644 --- a/services/java/com/android/server/wm/WindowManagerService.java +++ b/services/java/com/android/server/wm/WindowManagerService.java @@ -4799,13 +4799,17 @@ public class WindowManagerService extends IWindowManager.Stub if (maxLayer < ws.mAnimLayer) { maxLayer = ws.mAnimLayer; } - final Rect wf = ws.mFrame; - final Rect cr = ws.mContentInsets; - int left = wf.left + cr.left; - int top = wf.top + cr.top; - int right = wf.right - cr.right; - int bottom = wf.bottom - cr.bottom; - frame.union(left, top, right, bottom); + + // Don't include wallpaper in bounds calculation + if (!ws.mIsWallpaper) { + final Rect wf = ws.mFrame; + final Rect cr = ws.mContentInsets; + int left = wf.left + cr.left; + int top = wf.top + cr.top; + int right = wf.right - cr.right; + int bottom = wf.bottom - cr.bottom; + frame.union(left, top, right, bottom); + } } Binder.restoreCallingIdentity(ident); @@ -5460,8 +5464,9 @@ public class WindowManagerService extends IWindowManager.Stub shortSize = (int)(shortSize/dm.density); // These semi-magic numbers define our compatibility modes for - // applications with different screens. Don't change unless you - // make sure to test lots and lots of apps! + // applications with different screens. These are guarantees to + // app developers about the space they can expect for a particular + // configuration. DO NOT CHANGE! if (longSize < 470) { // This is shorter than an HVGA normal density screen (which // is 480 pixels on its long side). @@ -5469,12 +5474,12 @@ public class WindowManagerService extends IWindowManager.Stub | Configuration.SCREENLAYOUT_LONG_NO; } else { // What size is this screen screen? - if (longSize >= 800 && shortSize >= 600) { - // SVGA or larger screens at medium density are the point + if (longSize >= 960 && shortSize >= 720) { + // 1.5xVGA or larger screens at medium density are the point // at which we consider it to be an extra large screen. mScreenLayout = Configuration.SCREENLAYOUT_SIZE_XLARGE; - } else if (longSize >= 530 && shortSize >= 400) { - // SVGA or larger screens at high density are the point + } else if (longSize >= 640 && shortSize >= 480) { + // VGA or larger screens at medium density are the point // at which we consider it to be a large screen. mScreenLayout = Configuration.SCREENLAYOUT_SIZE_LARGE; } else { diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp index 3be3b1b..bd4e787 100644 --- a/services/jni/com_android_server_InputManager.cpp +++ b/services/jni/com_android_server_InputManager.cpp @@ -727,14 +727,14 @@ void NativeInputManager::handleInterceptActions(jint wmActions, nsecs_t when, }; if (wmActions & WM_ACTION_GO_TO_SLEEP) { -#ifdef DEBUG_INPUT_DISPATCHER_POLICY +#if DEBUG_INPUT_DISPATCHER_POLICY LOGD("handleInterceptActions: Going to sleep."); #endif android_server_PowerManagerService_goToSleep(when); } if (wmActions & WM_ACTION_POKE_USER_ACTIVITY) { -#ifdef DEBUG_INPUT_DISPATCHER_POLICY +#if DEBUG_INPUT_DISPATCHER_POLICY LOGD("handleInterceptActions: Poking user activity."); #endif android_server_PowerManagerService_userActivity(when, POWER_MANAGER_BUTTON_EVENT); @@ -743,7 +743,7 @@ void NativeInputManager::handleInterceptActions(jint wmActions, nsecs_t when, if (wmActions & WM_ACTION_PASS_TO_USER) { policyFlags |= POLICY_FLAG_PASS_TO_USER; } else { -#ifdef DEBUG_INPUT_DISPATCHER_POLICY +#if DEBUG_INPUT_DISPATCHER_POLICY LOGD("handleInterceptActions: Not passing key to user."); #endif } diff --git a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java index 85bc785..db14e53 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java @@ -393,7 +393,7 @@ public final class Path_Delegate { for (int i = 0 ; i < 3 ; i++) { if (radii[i * 2] != radii[(i + 1) * 2] || radii[i * 2 + 1] != radii[(i + 1) * 2 + 1]) { Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, - "Different corner size is not support in Path.addRoundRect.", + "Different corner sizes are not supported in Path.addRoundRect.", null, null /*data*/); break; } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java index acc7379..e6e9647 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java @@ -192,7 +192,7 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge { Capability.UNBOUND_RENDERING, Capability.CUSTOM_BACKGROUND_COLOR, Capability.RENDER, - Capability.LAYOUT_ONLY, + //Capability.LAYOUT_ONLY, // disable to run on ADT 10.0 which doesn't include this. Capability.EMBEDDED_LAYOUT, Capability.VIEW_MANIPULATION, Capability.PLAY_ANIMATION, diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java index 77c1789..138a455 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java @@ -488,18 +488,29 @@ public final class BridgeTypedArray extends TypedArray { return defValue; } - float f = getDimension(index, defValue); - final int res = (int)(f+0.5f); - if (res != 0) return res; - if (f == 0) return 0; - if (f > 0) return 1; // this is to support ]0;1[ range (since >=1 is handled 2 lines above) - if (f < 0) { - // negative values are not allowed in pixel dimensions - Bridge.getLog().error(LayoutLog.TAG_BROKEN, - "Negative pixel dimension: " + s, - null, null /*data*/); + if (ResourceHelper.stringToFloat(s, mValue)) { + float f = mValue.getDimension(mBridgeResources.mMetrics); + + if (f < 0) { + // negative values are not allowed in pixel dimensions + Bridge.getLog().error(LayoutLog.TAG_BROKEN, + "Negative pixel dimension: " + s, + null, null /*data*/); + return defValue; + } + + if (f == 0) return 0; + if (f < 1) return 1; + + return (int)(f+0.5f); } + // looks like we were unable to resolve the dimension value + Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT, + String.format( + "\"%1$s\" in attribute \"%2$s\" is not a valid format.", + s, mNames[index]), null /*data*/); + return defValue; } |