diff options
52 files changed, 1253 insertions, 1261 deletions
diff --git a/api/current.xml b/api/current.xml index aa6471b..9ccb8f7 100644 --- a/api/current.xml +++ b/api/current.xml @@ -118963,6 +118963,17 @@ visibility="public" > </field> +<field name="KEYCODE_MUTE" + type="int" + transient="false" + volatile="false" + value="91" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="KEYCODE_N" type="int" transient="false" @@ -121255,6 +121266,17 @@ <parameter name="rate" type="int"> </parameter> </constructor> +<method name="canDetectOrientation" + return="boolean" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="disable" return="void" abstract="false" diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 8236943..849a37d 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -890,9 +890,9 @@ public class Activity extends ContextThemeWrapper } /** - * Called after {@link #onCreate} or {@link #onStop} when the current - * activity is now being displayed to the user. It will - * be followed by {@link #onRestart}. + * Called after {@link #onCreate} — or after {@link #onRestart} when + * the activity had been stopped, but is now again being displayed to the + * user. It will be followed by {@link #onResume}. * * <p><em>Derived classes must call through to the super class's * implementation of this method. If they do not, an exception will be @@ -907,9 +907,9 @@ public class Activity extends ContextThemeWrapper } /** - * Called after {@link #onStart} when the current activity is being + * Called after {@link #onStop} when the current activity is being * re-displayed to the user (the user has navigated back to it). It will - * be followed by {@link #onResume}. + * be followed by {@link #onStart} and then {@link #onResume}. * * <p>For activities that are using raw {@link Cursor} objects (instead of * creating them through @@ -923,6 +923,7 @@ public class Activity extends ContextThemeWrapper * thrown.</em></p> * * @see #onStop + * @see #onStart * @see #onResume */ protected void onRestart() { @@ -1220,7 +1221,7 @@ public class Activity extends ContextThemeWrapper /** * Called when you are no longer visible to the user. You will next - * receive either {@link #onStart}, {@link #onDestroy}, or nothing, + * receive either {@link #onRestart}, {@link #onDestroy}, or nothing, * depending on later user activity. * * <p>Note that this method may never be called, in low memory situations diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java index 3981f27..e232c2c 100644 --- a/core/java/android/hardware/SensorManager.java +++ b/core/java/android/hardware/SensorManager.java @@ -620,6 +620,9 @@ public class SensorManager extends IRotationWatcher.Stub */ @Deprecated public boolean registerListener(SensorListener listener, int sensors, int rate) { + if (listener == null) { + return false; + } boolean result = false; result = registerLegacyListener(SENSOR_ACCELEROMETER, Sensor.TYPE_ACCELEROMETER, listener, sensors, rate) || result; @@ -638,6 +641,9 @@ public class SensorManager extends IRotationWatcher.Stub private boolean registerLegacyListener(int legacyType, int type, SensorListener listener, int sensors, int rate) { + if (listener == null) { + return false; + } boolean result = false; // Are we activating this legacy sensor? if ((sensors & legacyType) != 0) { @@ -693,6 +699,9 @@ public class SensorManager extends IRotationWatcher.Stub private void unregisterLegacyListener(int legacyType, int type, SensorListener listener, int sensors) { + if (listener == null) { + return; + } // do we know about this listener? LegacyListener legacyListener = null; synchronized (mLegacyListenersMap) { @@ -800,6 +809,9 @@ public class SensorManager extends IRotationWatcher.Stub */ public boolean registerListener(SensorEventListener listener, Sensor sensor, int rate, Handler handler) { + if (listener == null || sensor == null) { + return false; + } boolean result; int delay = -1; switch (rate) { @@ -856,6 +868,9 @@ public class SensorManager extends IRotationWatcher.Stub } private void unregisterListener(Object listener, Sensor sensor) { + if (listener == null || sensor == null) { + return; + } try { synchronized (sListeners) { final int size = sListeners.size(); @@ -881,6 +896,9 @@ public class SensorManager extends IRotationWatcher.Stub } private void unregisterListener(Object listener) { + if (listener == null) { + return; + } try { synchronized (sListeners) { final int size = sListeners.size(); diff --git a/core/java/android/inputmethodservice/SoftInputWindow.java b/core/java/android/inputmethodservice/SoftInputWindow.java index da67c6d..c37845f 100644 --- a/core/java/android/inputmethodservice/SoftInputWindow.java +++ b/core/java/android/inputmethodservice/SoftInputWindow.java @@ -141,7 +141,8 @@ class SoftInputWindow extends Dialog { lp.gravity = Gravity.BOTTOM; lp.width = -1; // Let the input method window's orientation follow sensor based rotation - lp.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_USER; + // Turn this off for now, it is very problematic. + //lp.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_USER; getWindow().setAttributes(lp); getWindow().setFlags( diff --git a/core/java/android/preference/Preference.java b/core/java/android/preference/Preference.java index 3820f28..a255438 100644 --- a/core/java/android/preference/Preference.java +++ b/core/java/android/preference/Preference.java @@ -667,7 +667,6 @@ public class Preference implements Comparable<Preference>, OnDependencyChangeLis * {@link SharedPreferences}. This should be unique for the package. * * @param key The key for the preference. - * @see #getId() */ public void setKey(String key) { mKey = key; @@ -1460,7 +1459,6 @@ public class Preference implements Comparable<Preference>, OnDependencyChangeLis * @param container The Bundle in which to save the instance of this Preference. * * @see #restoreHierarchyState - * @see #dispatchSaveInstanceState * @see #onSaveInstanceState */ public void saveHierarchyState(Bundle container) { @@ -1474,7 +1472,6 @@ public class Preference implements Comparable<Preference>, OnDependencyChangeLis * * @param container The Bundle in which to save the instance of this Preference. * - * @see #dispatchRestoreInstanceState * @see #saveHierarchyState * @see #onSaveInstanceState */ @@ -1503,7 +1500,6 @@ public class Preference implements Comparable<Preference>, OnDependencyChangeLis * The default implementation returns null. * @see #onRestoreInstanceState * @see #saveHierarchyState - * @see #dispatchSaveInstanceState */ protected Parcelable onSaveInstanceState() { mBaseMethodCalled = true; @@ -1516,7 +1512,6 @@ public class Preference implements Comparable<Preference>, OnDependencyChangeLis * @param container The Bundle that holds the previously saved state. * * @see #saveHierarchyState - * @see #dispatchRestoreInstanceState * @see #onRestoreInstanceState */ public void restoreHierarchyState(Bundle container) { @@ -1530,7 +1525,6 @@ public class Preference implements Comparable<Preference>, OnDependencyChangeLis * not want to save state for their children. * * @param container The Bundle that holds the previously saved state. - * @see #dispatchSaveInstanceState * @see #restoreHierarchyState * @see #onRestoreInstanceState */ @@ -1557,7 +1551,6 @@ public class Preference implements Comparable<Preference>, OnDependencyChangeLis * {@link #onSaveInstanceState}. * @see #onSaveInstanceState * @see #restoreHierarchyState - * @see #dispatchRestoreInstanceState */ protected void onRestoreInstanceState(Parcelable state) { mBaseMethodCalled = true; diff --git a/core/java/android/preference/PreferenceScreen.java b/core/java/android/preference/PreferenceScreen.java index 9929b96..5353b53 100644 --- a/core/java/android/preference/PreferenceScreen.java +++ b/core/java/android/preference/PreferenceScreen.java @@ -100,7 +100,6 @@ public final class PreferenceScreen extends PreferenceGroup implements AdapterVi * * @return An adapter that provides the {@link Preference} contained in this * {@link PreferenceScreen}. - * @see PreferenceGroupAdapter */ public ListAdapter getRootAdapter() { if (mRootAdapter == null) { diff --git a/core/java/android/provider/Checkin.java b/core/java/android/provider/Checkin.java index 688e19a..0cdac53 100644 --- a/core/java/android/provider/Checkin.java +++ b/core/java/android/provider/Checkin.java @@ -126,6 +126,11 @@ public final class Checkin { /** Valid tag values. Extend as necessary for your needs. */ public enum Tag { + BROWSER_SNAP_CENTER, + BROWSER_TEXT_SIZE_CHANGE, + BROWSER_ZOOM_OVERVIEW, + BROWSER_ZOOM_RING, + BROWSER_ZOOM_RING_DRAG, CRASHES_REPORTED, CRASHES_TRUNCATED, ELAPSED_REALTIME_SEC, diff --git a/core/java/android/provider/Gmail.java b/core/java/android/provider/Gmail.java index 253dd4a..5b3c223 100644 --- a/core/java/android/provider/Gmail.java +++ b/core/java/android/provider/Gmail.java @@ -1477,10 +1477,20 @@ public final class Gmail { LABEL_OUTBOX, LABEL_DRAFT, LABEL_ALL, LABEL_SPAM, LABEL_TRASH); + + private static final Set<String> USER_MEANINGFUL_SYSTEM_LABELS_SET = + Sets.newHashSet( + SORTED_USER_MEANINGFUL_SYSTEM_LABELS.toArray( + new String[]{})); + public static List<String> getSortedUserMeaningfulSystemLabels() { return SORTED_USER_MEANINGFUL_SYSTEM_LABELS; } + public static Set<String> getUserMeaningfulSystemLabelsSet() { + return USER_MEANINGFUL_SYSTEM_LABELS_SET; + } + /** * If you are ever tempted to remove outbox or draft from this set make sure you have a * way to stop draft and outbox messages from getting purged before they are sent to the diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java index 2897b4e..b91bc9d 100644 --- a/core/java/android/provider/MediaStore.java +++ b/core/java/android/provider/MediaStore.java @@ -158,6 +158,12 @@ public final class MediaStore public final static String EXTRA_VIDEO_QUALITY = "android.intent.extra.videoQuality"; /** + * Specify the maximum allowed size. + * @hide + */ + public final static String EXTRA_SIZE_LIMIT = "android.intent.extra.sizeLimit"; + + /** * The name of the Intent-extra used to indicate a content resolver Uri to be used to * store the requested image or video. */ diff --git a/core/java/android/server/BluetoothDeviceService.java b/core/java/android/server/BluetoothDeviceService.java index 86d5a1e..fa53a60 100644 --- a/core/java/android/server/BluetoothDeviceService.java +++ b/core/java/android/server/BluetoothDeviceService.java @@ -47,8 +47,8 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.io.UnsupportedEncodingException; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; -import java.util.List; import java.util.Map; public class BluetoothDeviceService extends IBluetoothDevice.Stub { @@ -121,7 +121,7 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { * Bring down bluetooth. Returns true on success. * * @param saveSetting If true, disable BT in settings - * + * */ public synchronized boolean disable(boolean saveSetting) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, @@ -172,7 +172,7 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { * This turns on/off the underlying hardware. * * @param saveSetting If true, enable BT in settings - * + * * @return True on success (so far), guaranteeing the callback with be * notified when complete. */ @@ -275,6 +275,14 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { private final HashMap<String, Integer> mState = new HashMap<String, Integer>(); private final HashMap<String, Integer> mPinAttempt = new HashMap<String, Integer>(); private final ArrayList<String> mAutoPairingFailures = new ArrayList<String>(); + // List of all the vendor_id prefix of Bluetooth addresses for which + // auto pairing is not attempted + private final ArrayList<String> mAutoPairingBlacklisted = + new ArrayList<String>(Arrays.asList( + "00:02:C7", "00:16:FE", "00:19:C1", "00:1B:FB", "00:1E:3D", //ALPS + "00:21:4F", "00:23:06", "00:24:33", "00:A0:79", // ALPS + "00:0E:6D", "00:13:E0", "00:21:E8", "00:60:57"// Murata for Prius 2007 + )); public synchronized void loadBondState() { if (!mIsEnabled) { @@ -322,6 +330,13 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { mContext.sendBroadcast(intent, BLUETOOTH_PERM); } + public boolean isAutoPairingBlacklisted(String address) { + for (String blacklistAddress : mAutoPairingBlacklisted) { + if (address.startsWith(blacklistAddress)) return true; + } + return false; + } + public synchronized int getBondState(String address) { Integer state = mState.get(address); if (state == null) { diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java index 187ec2c..8b09583 100644 --- a/core/java/android/server/BluetoothEventLoop.java +++ b/core/java/android/server/BluetoothEventLoop.java @@ -312,7 +312,8 @@ class BluetoothEventLoop { case BluetoothClass.Device.AUDIO_VIDEO_PORTABLE_AUDIO: case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO: case BluetoothClass.Device.AUDIO_VIDEO_HIFI_AUDIO: - if (!mBluetoothService.getBondState().hasAutoPairingFailed(address)) { + if (!mBluetoothService.getBondState().hasAutoPairingFailed(address) && + !mBluetoothService.getBondState().isAutoPairingBlacklisted(address)) { mBluetoothService.getBondState().attempt(address); mBluetoothService.setPin(address, BluetoothDevice.convertPinToBytes("0000")); return; diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java index d5434b6..d6ea91c 100644 --- a/core/java/android/view/KeyEvent.java +++ b/core/java/android/view/KeyEvent.java @@ -117,7 +117,8 @@ public class KeyEvent implements Parcelable { public static final int KEYCODE_PREVIOUSSONG = 88; public static final int KEYCODE_REWIND = 89; public static final int KEYCODE_FORWARD = 90; - private static final int LAST_KEYCODE = KEYCODE_FORWARD; + public static final int KEYCODE_MUTE = 91; + private static final int LAST_KEYCODE = KEYCODE_MUTE; // NOTE: If you add a new keycode here you must also add it to: // isSystem() @@ -502,6 +503,7 @@ public class KeyEvent implements Parcelable { case KEYCODE_ENDCALL: case KEYCODE_VOLUME_UP: case KEYCODE_VOLUME_DOWN: + case KEYCODE_MUTE: case KEYCODE_POWER: case KEYCODE_HEADSETHOOK: case KEYCODE_PLAYPAUSE: diff --git a/core/java/android/view/OrientationEventListener.java b/core/java/android/view/OrientationEventListener.java index cddec11..391ba1e 100755 --- a/core/java/android/view/OrientationEventListener.java +++ b/core/java/android/view/OrientationEventListener.java @@ -69,8 +69,11 @@ public abstract class OrientationEventListener { public OrientationEventListener(Context context, int rate) { mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE); mRate = rate; - mSensorEventListener = new SensorEventListenerImpl(); mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); + if (mSensor != null) { + // Create listener only if sensors do exist + mSensorEventListener = new SensorEventListenerImpl(); + } } void registerListener(OrientationListener lis) { @@ -82,6 +85,10 @@ public abstract class OrientationEventListener { * {@link #onOrientationChanged} when the device orientation changes. */ public void enable() { + if (mSensor == null) { + Log.w(TAG, "Cannot detect sensors. Not enabled"); + return; + } if (mEnabled == false) { if (localLOGV) Log.d(TAG, "OrientationEventListener enabled"); mSensorManager.registerListener(mSensorEventListener, mSensor, mRate); @@ -93,6 +100,10 @@ public abstract class OrientationEventListener { * Disables the OrientationEventListener. */ public void disable() { + if (mSensor == null) { + Log.w(TAG, "Cannot detect sensors. Invalid disable"); + return; + } if (mEnabled == true) { if (localLOGV) Log.d(TAG, "OrientationEventListener disabled"); mSensorManager.unregisterListener(mSensorEventListener); @@ -138,6 +149,13 @@ public abstract class OrientationEventListener { } } + + /* + * Returns true if sensor is enabled and false otherwise + */ + public boolean canDetectOrientation() { + return mSensor != null; + } /** * Called when the orientation of the device has changed. diff --git a/core/java/android/view/SurfaceHolder.java b/core/java/android/view/SurfaceHolder.java index 21a72e7..3d0dda3 100644 --- a/core/java/android/view/SurfaceHolder.java +++ b/core/java/android/view/SurfaceHolder.java @@ -240,7 +240,7 @@ public interface SurfaceHolder { * in particular there is no guarantee that the content of the Surface * will remain unchanged when lockCanvas() is called again. * - * @see android.view.SurfaceHolder.lockCanvas + * @see #lockCanvas() * * @param canvas The Canvas previously returned by lockCanvas(). */ diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java index db8829f..3cfaf1b 100644 --- a/core/java/android/view/ViewRoot.java +++ b/core/java/android/view/ViewRoot.java @@ -1817,6 +1817,11 @@ public final class ViewRoot extends Handler implements ViewParent, if (event != null) { event.recycle(); } + // If we reach this, we delivered a trackball event to mView and + // mView consumed it. Because we will not translate the trackball + // event into a key event, touch mode will not exit, so we exit + // touch mode here. + ensureTouchMode(false); //noinspection ReturnInsideFinallyBlock return; } @@ -2140,7 +2145,7 @@ public final class ViewRoot extends Handler implements ViewParent, } boolean keyHandled = mView.dispatchKeyEvent(event); - if ((!keyHandled && isDown) || (action == KeyEvent.ACTION_MULTIPLE)) { + if (!keyHandled && isDown) { int direction = 0; switch (event.getKeyCode()) { case KeyEvent.KEYCODE_DPAD_LEFT: @@ -2541,7 +2546,7 @@ public final class ViewRoot extends Handler implements ViewParent, * for us to consider the user to be doing fast trackball movements, * and thus apply an acceleration. */ - static final long FAST_MOVE_TIME = 100; + static final long FAST_MOVE_TIME = 150; /** * Scaling factor to the time (in milliseconds) between events to how @@ -2549,7 +2554,7 @@ public final class ViewRoot extends Handler implements ViewParent, * is < FAST_MOVE_TIME this multiplies the acceleration; when > * FAST_MOVE_TIME it divides it. */ - static final float ACCEL_MOVE_SCALING_FACTOR = (1.0f/50); + static final float ACCEL_MOVE_SCALING_FACTOR = (1.0f/40); float position; float absPosition; diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java index 051f823..0f15b17 100644 --- a/core/java/android/view/WindowManagerPolicy.java +++ b/core/java/android/view/WindowManagerPolicy.java @@ -129,14 +129,22 @@ public interface WindowManagerPolicy { Rect contentFrame, Rect visibleFrame); /** - * Retrieve the current frame of the window. Must be called with the - * window manager lock held. + * Retrieve the current frame of the window that has been assigned by + * the window manager. Must be called with the window manager lock held. * * @return Rect The rectangle holding the window frame. */ public Rect getFrameLw(); /** + * Retrieve the current frame of the window that is actually shown. + * Must be called with the window manager lock held. + * + * @return Rect The rectangle holding the shown window frame. + */ + public Rect getShownFrameLw(); + + /** * Retrieve the frame of the display that this window was last * laid out in. Must be called with the * window manager lock held. @@ -273,9 +281,12 @@ public interface WindowManagerPolicy { * false, this is based on the currently requested * frame, which any current animation will be moving * towards. + * @param onlyOpaque If true, this will only pass if the window is + * also opaque. * @return Returns true if the window is both full screen and opaque */ - public boolean fillsScreenLw(int width, int height, boolean shownFrame); + public boolean fillsScreenLw(int width, int height, boolean shownFrame, + boolean onlyOpaque); /** * Returns true if this window has been shown on screen at some time in diff --git a/core/java/android/view/WindowOrientationListener.java b/core/java/android/view/WindowOrientationListener.java index 4aa3f7a..5877932 100755 --- a/core/java/android/view/WindowOrientationListener.java +++ b/core/java/android/view/WindowOrientationListener.java @@ -74,8 +74,11 @@ public abstract class WindowOrientationListener { public WindowOrientationListener(Context context, int rate) { mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE); mRate = rate; - mSensorEventListener = new SensorEventListenerImpl(); mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); + if (mSensor != null) { + // Create listener only if sensors do exist + mSensorEventListener = new SensorEventListenerImpl(); + } } /** @@ -83,6 +86,10 @@ public abstract class WindowOrientationListener { * {@link #onOrientationChanged} when the device orientation changes. */ public void enable() { + if (mSensor == null) { + Log.w(TAG, "Cannot detect sensors. Not enabled"); + return; + } if (mEnabled == false) { if (localLOGV) Log.d(TAG, "WindowOrientationListener enabled"); mSensorManager.registerListener(mSensorEventListener, mSensor, mRate); @@ -94,6 +101,10 @@ public abstract class WindowOrientationListener { * Disables the WindowOrientationListener. */ public void disable() { + if (mSensor == null) { + Log.w(TAG, "Cannot detect sensors. Invalid disable"); + return; + } if (mEnabled == true) { if (localLOGV) Log.d(TAG, "WindowOrientationListener disabled"); mSensorManager.unregisterListener(mSensorEventListener); @@ -145,6 +156,13 @@ public abstract class WindowOrientationListener { } } + /* + * Returns true if sensor is enabled and false otherwise + */ + public boolean canDetectOrientation() { + return mSensor != null; + } + /** * Called when the orientation of the device has changed. * orientation parameter is in degrees, ranging from 0 to 359. diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java index 5c470cf..4e2b2ab 100644 --- a/core/java/android/webkit/WebSettings.java +++ b/core/java/android/webkit/WebSettings.java @@ -20,6 +20,8 @@ import android.content.Context; import android.os.Build; import android.os.Handler; import android.os.Message; +import android.provider.Checkin; + import java.lang.SecurityException; import android.content.pm.PackageManager; @@ -408,6 +410,10 @@ public class WebSettings { * @see WebSettings.TextSize */ public synchronized void setTextSize(TextSize t) { + if (WebView.mLogEvent && mTextSize != t ) { + Checkin.updateStats(mContext.getContentResolver(), + Checkin.Stats.Tag.BROWSER_TEXT_SIZE_CHANGE, 1, 0.0); + } mTextSize = t; postSync(); } diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 4d9a8fb..c59a5fc 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -38,11 +38,13 @@ import android.os.Handler; import android.os.Message; import android.os.ServiceManager; import android.os.SystemClock; +import android.provider.Checkin; import android.text.IClipboard; import android.text.Selection; import android.text.Spannable; import android.util.AttributeSet; import android.util.Config; +import android.util.EventLog; import android.util.Log; import android.view.animation.AlphaAnimation; import android.view.animation.Animation; @@ -450,6 +452,13 @@ public class WebView extends AbsoluteLayout // Used to match key downs and key ups private boolean mGotKeyDown; + /* package */ static boolean mLogEvent = true; + private static final int EVENT_LOG_ZOOM_LEVEL_CHANGE = 70101; + private static final int EVENT_LOG_DOUBLE_TAP_DURATION = 70102; + + // for event log + private long mLastTouchUpTime = 0; + /** * URI scheme for telephone number */ @@ -556,7 +565,11 @@ public class WebView extends AbsoluteLayout private ZoomRingController.OnZoomListener mZoomListener = new ZoomRingController.OnZoomListener() { - + + private float mClockwiseBound; + private float mCounterClockwiseBound; + private float mStartScale; + public void onCenter(int x, int y) { // Don't translate when the control is invoked, hence we do nothing // in this callback @@ -564,6 +577,10 @@ public class WebView extends AbsoluteLayout public void onBeginPan() { setZoomOverviewVisible(false); + if (mLogEvent) { + Checkin.updateStats(mContext.getContentResolver(), + Checkin.Stats.Tag.BROWSER_ZOOM_RING_DRAG, 1, 0.0); + } } public boolean onPan(int deltaX, int deltaY) { @@ -576,12 +593,25 @@ public class WebView extends AbsoluteLayout public void onVisibilityChanged(boolean visible) { if (visible) { switchOutDrawHistory(); + if (mMaxZoomScale - 1 > ZOOM_RING_STEPS * 0.01f) { + mClockwiseBound = (float) (2 * Math.PI - MAX_ZOOM_RING_ANGLE); + } else { + mClockwiseBound = (float) (2 * Math.PI); + } + mZoomRingController.setThumbClockwiseBound(mClockwiseBound); + if (1 - mMinZoomScale > ZOOM_RING_STEPS * 0.01f) { + mCounterClockwiseBound = MAX_ZOOM_RING_ANGLE; + } else { + mCounterClockwiseBound = 0; + } + mZoomRingController + .setThumbCounterclockwiseBound(mCounterClockwiseBound); float angle = 0f; - if (mActualScale > 1) { + if (mActualScale > 1 && mClockwiseBound < (float) (2 * Math.PI)) { angle = -(float) Math.round(ZOOM_RING_STEPS * (mActualScale - 1) / (mMaxZoomScale - 1)) / ZOOM_RING_STEPS; - } else if (mActualScale < 1) { + } else if (mActualScale < 1 && mCounterClockwiseBound > 0) { angle = (float) Math.round(ZOOM_RING_STEPS * (1 - mActualScale) / (1 - mMinZoomScale)) / ZOOM_RING_STEPS; @@ -590,16 +620,26 @@ public class WebView extends AbsoluteLayout // Show the zoom overview tab on the ring setZoomOverviewVisible(true); + if (mLogEvent) { + Checkin.updateStats(mContext.getContentResolver(), + Checkin.Stats.Tag.BROWSER_ZOOM_RING, 1, 0.0); + } } } public void onBeginDrag() { mPreviewZoomOnly = true; + mStartScale = mActualScale; setZoomOverviewVisible(false); } public void onEndDrag() { mPreviewZoomOnly = false; + if (mLogEvent) { + EventLog.writeEvent(EVENT_LOG_ZOOM_LEVEL_CHANGE, + (int) mStartScale * 100, (int) mActualScale * 100, + System.currentTimeMillis()); + } setNewZoomScale(mActualScale, true); } @@ -616,21 +656,21 @@ public class WebView extends AbsoluteLayout mZoomCenterY = (float) centerY; float scale = 1.0f; - if (curAngle > (float) Math.PI) - curAngle -= (float) 2 * Math.PI; - if (curAngle > 0) { - if (curAngle >= MAX_ZOOM_RING_ANGLE) { + // curAngle is [0, 2 * Math.PI) + if (curAngle < (float) Math.PI) { + if (curAngle >= mCounterClockwiseBound) { scale = mMinZoomScale; } else { scale = 1 - (float) Math.round(curAngle / ZOOM_RING_ANGLE_UNIT) / ZOOM_RING_STEPS * (1 - mMinZoomScale); } - } else if (curAngle < 0) { - if (curAngle <= -MAX_ZOOM_RING_ANGLE) { + } else { + if (curAngle <= mClockwiseBound) { scale = mMaxZoomScale; } else { - scale = 1 + (float) Math.round(-curAngle + scale = 1 + (float) Math.round( + ((float) 2 * Math.PI - curAngle) / ZOOM_RING_ANGLE_UNIT) / ZOOM_RING_STEPS * (mMaxZoomScale - 1); } @@ -687,12 +727,11 @@ public class WebView extends AbsoluteLayout mScroller = new Scroller(context); mZoomRingController = new ZoomRingController(context, this); mZoomRingController.setResetThumbAutomatically(false); - mZoomRingController.setThumbClockwiseBound( - (float) (2 * Math.PI - MAX_ZOOM_RING_ANGLE)); - mZoomRingController.setThumbCounterclockwiseBound(MAX_ZOOM_RING_ANGLE); mZoomRingController.setCallback(mZoomListener); mZoomRingController.setZoomRingTrack( com.android.internal.R.drawable.zoom_ring_track_absolute); + mZoomRingController.setPannerAcceleration(160); + mZoomRingController.setPannerStartAcceleratingDuration(700); createZoomRingOverviewTab(); } @@ -730,6 +769,10 @@ public class WebView extends AbsoluteLayout public void onClick(View v) { // Hide the zoom ring mZoomRingController.setVisible(false); + if (mLogEvent) { + Checkin.updateStats(mContext.getContentResolver(), + Checkin.Stats.Tag.BROWSER_ZOOM_OVERVIEW, 1, 0.0); + } zoomScrollOut(); }}); @@ -3468,7 +3511,7 @@ public class WebView extends AbsoluteLayout // update mMinZoomScale if (mMinContentWidth > MAX_FLOAT_CONTENT_WIDTH) { boolean atMin = Math.abs(mActualScale - mMinZoomScale) < 0.01f; - mMinZoomScale = (float) getViewWidth() / mMinContentWidth; + mMinZoomScale = (float) getViewWidth() / mContentWidth; if (atMin) { // if the WebView was at the minimum zoom scale, keep it. e,g., // the WebView was at the minimum zoom scale at the portrait @@ -3535,7 +3578,8 @@ public class WebView extends AbsoluteLayout return false; } - if (mShowZoomRingTutorial && mMinZoomScale < mMaxZoomScale) { + if (mShowZoomRingTutorial && getSettings().supportZoom() + && (mMaxZoomScale - mMinZoomScale) > ZOOM_RING_STEPS * 0.01f) { ZoomRingController.showZoomTutorialOnce(mContext); mShowZoomRingTutorial = false; mPrivateHandler.sendMessageDelayed(mPrivateHandler @@ -3616,10 +3660,18 @@ public class WebView extends AbsoluteLayout mPrivateHandler.removeMessages(RELEASE_SINGLE_TAP); mZoomRingController.setVisible(true); mInZoomTapDragMode = true; + if (mLogEvent) { + EventLog.writeEvent(EVENT_LOG_DOUBLE_TAP_DURATION, + (eventTime - mLastTouchUpTime), eventTime); + } return mZoomRingController.handleDoubleTapEvent(ev); } else { mTouchMode = TOUCH_INIT_MODE; mPreventDrag = mForwardTouchEvents; + if (mLogEvent && eventTime - mLastTouchUpTime < 1000) { + EventLog.writeEvent(EVENT_LOG_DOUBLE_TAP_DURATION, + (eventTime - mLastTouchUpTime), eventTime); + } } // don't trigger the link if zoom ring is visible if (mTouchMode == TOUCH_INIT_MODE @@ -3783,17 +3835,23 @@ public class WebView extends AbsoluteLayout break; } case MotionEvent.ACTION_UP: { + mLastTouchUpTime = eventTime; switch (mTouchMode) { case TOUCH_INIT_MODE: // tap if (mZoomRingController.isVisible()) { - // don't trigger the link if zoom ring is visible + // don't trigger the link if zoom ring is visible, + // but still allow the double tap + mPrivateHandler.sendMessageDelayed(mPrivateHandler + .obtainMessage(RELEASE_SINGLE_TAP, + new Boolean(false)), + DOUBLE_TAP_TIMEOUT); break; } mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS); - if (getSettings().supportZoom() - && (mMinZoomScale < mMaxZoomScale)) { + if (getSettings().supportZoom()) { mPrivateHandler.sendMessageDelayed(mPrivateHandler - .obtainMessage(RELEASE_SINGLE_TAP), + .obtainMessage(RELEASE_SINGLE_TAP, + new Boolean(true)), DOUBLE_TAP_TIMEOUT); } else { // do short press now @@ -3841,7 +3899,8 @@ public class WebView extends AbsoluteLayout // as tap instead of short press. mTouchMode = TOUCH_INIT_MODE; mPrivateHandler.sendMessageDelayed(mPrivateHandler - .obtainMessage(RELEASE_SINGLE_TAP), + .obtainMessage(RELEASE_SINGLE_TAP, + new Boolean(true)), DOUBLE_TAP_TIMEOUT); } else { mTouchMode = TOUCH_DONE_MODE; @@ -3963,6 +4022,7 @@ public class WebView extends AbsoluteLayout + " time=" + time + " mLastFocusTime=" + mLastFocusTime); } + if (isInTouchMode()) requestFocusFromTouch(); return false; // let common code in onKeyDown at it } if (ev.getAction() == MotionEvent.ACTION_UP) { @@ -4399,7 +4459,12 @@ public class WebView extends AbsoluteLayout int contentX = viewToContent((int) mLastTouchX + mScrollX); int contentY = viewToContent((int) mLastTouchY + mScrollY); int contentSize = ViewConfiguration.get(getContext()).getScaledTouchSlop(); - nativeMotionUp(contentX, contentY, contentSize, true); + if (nativeMotionUp(contentX, contentY, contentSize, true)) { + if (mLogEvent) { + Checkin.updateStats(mContext.getContentResolver(), + Checkin.Stats.Tag.BROWSER_SNAP_CENTER, 1, 0.0); + } + } if (nativeUpdateFocusNode() && !mFocusNode.mIsTextField && !mFocusNode.mIsTextArea) { playSoundEffect(SoundEffectConstants.CLICK); @@ -4619,7 +4684,9 @@ public class WebView extends AbsoluteLayout } case RELEASE_SINGLE_TAP: { mTouchMode = TOUCH_DONE_MODE; - doShortPress(); + if ((Boolean)msg.obj) { + doShortPress(); + } break; } case SWITCH_TO_ENTER: @@ -4671,7 +4738,7 @@ public class WebView extends AbsoluteLayout mMinContentWidth = msg.arg1; if (mMinContentWidth > MAX_FLOAT_CONTENT_WIDTH) { mMinZoomScale = (float) getViewWidth() - / mMinContentWidth; + / draw.mWidthHeight.x; } // We update the layout (i.e. request a layout from the // view system) if the last view size that we sent to @@ -5236,7 +5303,8 @@ public class WebView extends AbsoluteLayout private native Rect nativeGetNavBounds(); private native void nativeInstrumentReport(); private native void nativeMarkNodeInvalid(int node); - private native void nativeMotionUp(int x, int y, int slop, boolean isClick); + // return true if the page has been scrolled + private native boolean nativeMotionUp(int x, int y, int slop, boolean isClick); // returns false if it handled the key private native boolean nativeMoveFocus(int keyCode, int count, boolean noScroll); diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index 45113ab..a7261c5 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -1197,6 +1197,7 @@ final class WebViewCore { Log.w(LOGTAG, "skip viewSizeChanged as w is 0"); return; } + // negative scale indicate that WebCore should reuse the current scale float scale = (float) viewWidth / w; if (mSettings.getUseWideViewPort() && (w < mViewportWidth || mViewportWidth == -1)) { @@ -1224,7 +1225,7 @@ final class WebViewCore { // keep the same width and screen width so that there is // no reflow when zoom-out width = minContentWidth; - screenWidth = Math.min(screenWidth, viewWidth); + screenWidth = Math.min(screenWidth, Math.abs(viewWidth)); } else { width = Math.max(w, minContentWidth); } @@ -1536,11 +1537,11 @@ final class WebViewCore { // white space in the GMail which uses WebView for message view. if (mWebView != null && mWebView.mHeightCanMeasure) { mWebView.mLastHeightSent = 0; - // Send a negative scale to indicate that WebCore should reuse the - // current scale + // Send a negative screen width to indicate that WebCore should + // reuse the current scale mEventHub.sendMessage(Message.obtain(null, EventHub.VIEW_SIZE_CHANGED, mWebView.mLastWidthSent, - mWebView.mLastHeightSent, new Integer(-1))); + mWebView.mLastHeightSent, -mWebView.mLastWidthSent)); } mBrowserFrame.didFirstLayout(); diff --git a/core/java/android/webkit/gears/GearsPluginSettings.java b/core/java/android/webkit/gears/GearsPluginSettings.java deleted file mode 100644 index d36d3fb..0000000 --- a/core/java/android/webkit/gears/GearsPluginSettings.java +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright 2008 The Android Open Source Project -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// 2. Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// 3. Neither the name of Google Inc. nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED -// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO -// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; -// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR -// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -package android.webkit.gears; - -import android.content.ComponentName; -import android.content.Context; -import android.content.ServiceConnection; -import android.os.IBinder; -import android.util.Log; -import android.webkit.Plugin; -import android.webkit.Plugin.PreferencesClickHandler; - -/** - * Simple bridge class intercepting the click in the - * browser plugin list and calling the Gears settings - * dialog. - */ -public class GearsPluginSettings { - - private static final String TAG = "Gears-J-GearsPluginSettings"; - private Context context; - - public GearsPluginSettings(Plugin plugin) { - plugin.setClickHandler(new ClickHandler()); - } - - /** - * We do not need to call the dialog synchronously here (doing so - * actually cause a lot of problems as the main message loop is also - * blocked), which is why we simply call it via a thread. - */ - private class ClickHandler implements PreferencesClickHandler { - public void handleClickEvent(Context aContext) { - context = aContext; - Thread startService = new Thread(new StartService()); - startService.run(); - } - } - - private static native void runSettingsDialog(Context c); - - /** - * StartService is the runnable we use to open the dialog. - * We bind the service to serviceConnection; upon - * onServiceConnected the dialog will be called from the - * native side using the runSettingsDialog method. - */ - private class StartService implements Runnable { - public void run() { - HtmlDialogAndroid.bindToService(context, serviceConnection); - } - } - - /** - * ServiceConnection instance. - * onServiceConnected is called upon connection with the service; - * we can then safely open the dialog. - */ - private ServiceConnection serviceConnection = new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - IGearsDialogService gearsDialogService = - IGearsDialogService.Stub.asInterface(service); - HtmlDialogAndroid.setGearsDialogService(gearsDialogService); - runSettingsDialog(context); - context.unbindService(serviceConnection); - HtmlDialogAndroid.setGearsDialogService(null); - } - public void onServiceDisconnected(ComponentName className) { - HtmlDialogAndroid.setGearsDialogService(null); - } - }; -} diff --git a/core/java/android/webkit/gears/HtmlDialogAndroid.java b/core/java/android/webkit/gears/HtmlDialogAndroid.java deleted file mode 100644 index 6209ab9..0000000 --- a/core/java/android/webkit/gears/HtmlDialogAndroid.java +++ /dev/null @@ -1,174 +0,0 @@ -// Copyright 2008 The Android Open Source Project -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// 2. Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// 3. Neither the name of Google Inc. nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED -// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO -// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; -// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR -// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -package android.webkit.gears; - -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; -import android.os.IBinder; -import android.os.RemoteException; -import android.util.Log; -import android.webkit.CacheManager; - -import java.io.FileInputStream; -import java.io.IOException; - -/** - * Utility class to call a modal HTML dialog on Android - */ -public class HtmlDialogAndroid { - - private static final String TAG = "Gears-J-HtmlDialog"; - private static final String DIALOG_PACKAGE = "com.android.browser"; - private static final String DIALOG_SERVICE = DIALOG_PACKAGE - + ".GearsDialogService"; - private static final String DIALOG_INTERFACE = DIALOG_PACKAGE - + ".IGearsDialogService"; - - private static IGearsDialogService gearsDialogService; - - public static void setGearsDialogService(IGearsDialogService service) { - gearsDialogService = service; - } - - /** - * Bind to the GearsDialogService. - */ - public static boolean bindToService(Context context, - ServiceConnection serviceConnection) { - Intent dialogIntent = new Intent(); - dialogIntent.setClassName(DIALOG_PACKAGE, DIALOG_SERVICE); - dialogIntent.setAction(DIALOG_INTERFACE); - return context.bindService(dialogIntent, serviceConnection, - Context.BIND_AUTO_CREATE); - } - - /** - * Bind to the GearsDialogService synchronously. - * The service is started using our own defaultServiceConnection - * handler, and we wait until the handler notifies us. - */ - public void synchronousBindToService(Context context) { - try { - if (bindToService(context, defaultServiceConnection)) { - if (gearsDialogService == null) { - synchronized(defaultServiceConnection) { - defaultServiceConnection.wait(3000); // timeout after 3s - } - } - } - } catch (InterruptedException e) { - Log.e(TAG, "exception: " + e); - } - } - - /** - * Read the HTML content from the disk - */ - public String readHTML(String filePath) { - FileInputStream inputStream = null; - String content = ""; - try { - inputStream = new FileInputStream(filePath); - StringBuffer out = new StringBuffer(); - byte[] buffer = new byte[4096]; - for (int n; (n = inputStream.read(buffer)) != -1;) { - out.append(new String(buffer, 0, n)); - } - content = out.toString(); - } catch (IOException e) { - Log.e(TAG, "exception: " + e); - } finally { - if (inputStream != null) { - try { - inputStream.close(); - } catch (IOException e) { - Log.e(TAG, "exception: " + e); - } - } - } - return content; - } - - /** - * Open an HTML dialog synchronously and waits for its completion. - * The dialog is accessed through the GearsDialogService provided by - * the Android Browser. - * We can be called either directly, and then gearsDialogService will - * not be set and we will bind to the service synchronously, and unbind - * after calling the service, or called indirectly via GearsPluginSettings. - * In the latter case, GearsPluginSettings does the binding/unbinding. - */ - public String showDialog(Context context, String htmlFilePath, - String arguments) { - - CacheManager.endCacheTransaction(); - - String ret = null; - boolean synchronousCall = false; - if (gearsDialogService == null) { - synchronousCall = true; - synchronousBindToService(context); - } - - try { - if (gearsDialogService != null) { - String htmlContent = readHTML(htmlFilePath); - if (htmlContent.length() > 0) { - ret = gearsDialogService.showDialog(htmlContent, arguments, - !synchronousCall); - } - } else { - Log.e(TAG, "Could not connect to the GearsDialogService!"); - } - if (synchronousCall) { - context.unbindService(defaultServiceConnection); - gearsDialogService = null; - } - } catch (RemoteException e) { - Log.e(TAG, "remote exception: " + e); - gearsDialogService = null; - } - - CacheManager.startCacheTransaction(); - - return ret; - } - - private ServiceConnection defaultServiceConnection = - new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - synchronized (defaultServiceConnection) { - gearsDialogService = IGearsDialogService.Stub.asInterface(service); - defaultServiceConnection.notify(); - } - } - public void onServiceDisconnected(ComponentName className) { - gearsDialogService = null; - } - }; -} diff --git a/core/java/android/webkit/gears/IGearsDialogService.java b/core/java/android/webkit/gears/IGearsDialogService.java deleted file mode 100644 index 82a3bd9..0000000 --- a/core/java/android/webkit/gears/IGearsDialogService.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * This file is auto-generated. DO NOT MODIFY. - * Original file: android.webkit.gears/IGearsDialogService.aidl - */ -package android.webkit.gears; -import java.lang.String; -import android.os.RemoteException; -import android.os.IBinder; -import android.os.IInterface; -import android.os.Binder; -import android.os.Parcel; -public interface IGearsDialogService extends android.os.IInterface -{ -/** Local-side IPC implementation stub class. */ -public static abstract class Stub extends android.os.Binder implements android.webkit.gears.IGearsDialogService -{ -private static final java.lang.String DESCRIPTOR = "com.android.browser.IGearsDialogService"; -/** Construct the stub at attach it to the interface. */ -public Stub() -{ -this.attachInterface(this, DESCRIPTOR); -} -/** - * Cast an IBinder object into an IGearsDialogService interface, - * generating a proxy if needed. - */ -public static android.webkit.gears.IGearsDialogService asInterface(android.os.IBinder obj) -{ -if ((obj==null)) { -return null; -} -android.webkit.gears.IGearsDialogService in = (android.webkit.gears.IGearsDialogService)obj.queryLocalInterface(DESCRIPTOR); -if ((in!=null)) { -return in; -} -return new android.webkit.gears.IGearsDialogService.Stub.Proxy(obj); -} -public android.os.IBinder asBinder() -{ -return this; -} -public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException -{ -switch (code) -{ -case INTERFACE_TRANSACTION: -{ -reply.writeString(DESCRIPTOR); -return true; -} -case TRANSACTION_showDialog: -{ -data.enforceInterface(DESCRIPTOR); -java.lang.String _arg0; -_arg0 = data.readString(); -java.lang.String _arg1; -_arg1 = data.readString(); -boolean _arg2; -_arg2 = (0!=data.readInt()); -java.lang.String _result = this.showDialog(_arg0, _arg1, _arg2); -reply.writeNoException(); -reply.writeString(_result); -return true; -} -} -return super.onTransact(code, data, reply, flags); -} -private static class Proxy implements android.webkit.gears.IGearsDialogService -{ -private android.os.IBinder mRemote; -Proxy(android.os.IBinder remote) -{ -mRemote = remote; -} -public android.os.IBinder asBinder() -{ -return mRemote; -} -public java.lang.String getInterfaceDescriptor() -{ -return DESCRIPTOR; -} -public java.lang.String showDialog(java.lang.String htmlContent, java.lang.String dialogArguments, boolean inSettings) throws android.os.RemoteException -{ -android.os.Parcel _data = android.os.Parcel.obtain(); -android.os.Parcel _reply = android.os.Parcel.obtain(); -java.lang.String _result; -try { -_data.writeInterfaceToken(DESCRIPTOR); -_data.writeString(htmlContent); -_data.writeString(dialogArguments); -_data.writeInt(((inSettings)?(1):(0))); -mRemote.transact(Stub.TRANSACTION_showDialog, _data, _reply, 0); -_reply.readException(); -_result = _reply.readString(); -} -finally { -_reply.recycle(); -_data.recycle(); -} -return _result; -} -} -static final int TRANSACTION_showDialog = (IBinder.FIRST_CALL_TRANSACTION + 0); -} -public java.lang.String showDialog(java.lang.String htmlContent, java.lang.String dialogArguments, boolean inSettings) throws android.os.RemoteException; -} diff --git a/core/java/android/widget/Adapter.java b/core/java/android/widget/Adapter.java index e952dd5..f2b3e2a 100644 --- a/core/java/android/widget/Adapter.java +++ b/core/java/android/widget/Adapter.java @@ -116,7 +116,7 @@ public interface Adapter { * can be converted to the other in {@link #getView}. Note: Integers must be in the * range 0 to {@link #getViewTypeCount} - 1. {@link #IGNORE_ITEM_VIEW_TYPE} can * also be returned. - * @see IGNORE_ITEM_VIEW_TYPE + * @see #IGNORE_ITEM_VIEW_TYPE */ int getItemViewType(int position); diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index bdc54ff..c852be5 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -4132,8 +4132,20 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener removeParcelableSpans(content, start, end); content.replace(start, end, text.text); } - Selection.setSelection((Spannable)getText(), - text.selectionStart, text.selectionEnd); + + // Now set the selection position... make sure it is in range, to + // avoid crashes. If this is a partial update, it is possible that + // the underlying text may have changed, causing us problems here. + // Also we just don't want to trust clients to do the right thing. + Spannable sp = (Spannable)getText(); + final int N = sp.length(); + int start = text.selectionStart; + if (start < 0) start = 0; + else if (start > N) start = N; + int end = text.selectionEnd; + if (end < 0) end = 0; + else if (end > N) end = N; + Selection.setSelection(sp, start, end); } /** diff --git a/core/java/android/widget/ZoomRing.java b/core/java/android/widget/ZoomRing.java index 22881b3..a29e1a0 100644 --- a/core/java/android/widget/ZoomRing.java +++ b/core/java/android/widget/ZoomRing.java @@ -84,6 +84,10 @@ public class ZoomRing extends View { private Drawable mThumbPlusArrowDrawable; /** Shown beneath the thumb if we can still zoom out. */ private Drawable mThumbMinusArrowDrawable; + private static final int THUMB_ARROW_PLUS = 1 << 0; + private static final int THUMB_ARROW_MINUS = 1 << 1; + /** Bitwise-OR of {@link #THUMB_ARROW_MINUS} and {@link #THUMB_ARROW_PLUS} */ + private int mThumbArrowsToDraw; private static final int THUMB_ARROWS_FADE_DURATION = 300; private long mThumbArrowsFadeStartTime; private int mThumbArrowsAlpha = 255; @@ -166,7 +170,7 @@ public class ZoomRing extends View { // TODO: add padding to drawable setBackgroundResource(R.drawable.zoom_ring_track); // TODO get from style - setRingBounds(30, Integer.MAX_VALUE); + setRingBounds(43, Integer.MAX_VALUE); mThumbHalfHeight = mThumbDrawable.getIntrinsicHeight() / 2; mThumbHalfWidth = mThumbDrawable.getIntrinsicWidth() / 2; @@ -276,7 +280,7 @@ public class ZoomRing extends View { thumbCenterX + mThumbHalfWidth, thumbCenterY + mThumbHalfHeight); - if (mThumbArrowsAlpha > 0) { + if (mThumbArrowsToDraw > 0) { setThumbArrowsAngle(angle); } @@ -420,12 +424,15 @@ public class ZoomRing extends View { switch (action) { case MotionEvent.ACTION_DOWN: - mCallback.onUserInteractionStarted(); - if (time - mPreviousUpTime <= DOUBLE_TAP_DISMISS_TIMEOUT) { mCallback.onZoomRingDismissed(true); + onTouchUp(time); + + // Dismissing, so halt here + return true; } + mCallback.onUserInteractionStarted(); mPreviousDownX = x; mPreviousDownY = y; resetState(); @@ -441,23 +448,7 @@ public class ZoomRing extends View { case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: - if (mMode == MODE_MOVE_ZOOM_RING || mMode == MODE_WAITING_FOR_MOVE_ZOOM_RING) { - mCallback.onZoomRingSetMovableHintVisible(false); - if (mMode == MODE_MOVE_ZOOM_RING) { - mCallback.onZoomRingMovingStopped(); - } - } else if (mMode == MODE_DRAG_THUMB || mMode == MODE_TAP_DRAG || - mMode == MODE_WAITING_FOR_DRAG_THUMB) { - onThumbDragStopped(); - - if (mMode == MODE_DRAG_THUMB) { - // Animate back to a tick - setThumbAngleAnimated(mPreviousCallbackAngle, 0); - } - } - - mPreviousUpTime = time; - mCallback.onUserInteractionStopped(); + onTouchUp(time); return true; default: @@ -524,10 +515,9 @@ public class ZoomRing extends View { deltaThumbAndTick = getDelta(mThumbAngle, tickAngle, !oldDirectionIsCcw); boundAngle = getBoundIfExceeds(mThumbAngle, deltaThumbAndTick); if (boundAngle != Integer.MIN_VALUE) { - Log - .d( - TAG, - "Tapped somewhere where the shortest distance goes through a bound, but then the opposite direction also went through a bound!"); + // Not allowed to be here, it is between two bounds + mMode = MODE_IGNORE_UNTIL_UP; + return true; } } } @@ -574,6 +564,26 @@ public class ZoomRing extends View { return true; } + + private void onTouchUp(long time) { + if (mMode == MODE_MOVE_ZOOM_RING || mMode == MODE_WAITING_FOR_MOVE_ZOOM_RING) { + mCallback.onZoomRingSetMovableHintVisible(false); + if (mMode == MODE_MOVE_ZOOM_RING) { + mCallback.onZoomRingMovingStopped(); + } + } else if (mMode == MODE_DRAG_THUMB || mMode == MODE_TAP_DRAG || + mMode == MODE_WAITING_FOR_DRAG_THUMB) { + onThumbDragStopped(); + + if (mMode == MODE_DRAG_THUMB) { + // Animate back to a tick + setThumbAngleAnimated(mPreviousCallbackAngle, 0); + } + } + + mPreviousUpTime = time; + mCallback.onUserInteractionStopped(); + } private boolean isDeltaInBounds(int startAngle, int deltaAngle) { return getBoundIfExceeds(startAngle, deltaAngle) == Integer.MIN_VALUE; @@ -766,9 +776,11 @@ public class ZoomRing extends View { } } - int deltaAngle = getDelta(mThumbAngle, touchAngle, useDirection, ccw); - mAcculumalatedTrailAngle += Math.toDegrees(deltaAngle / (double) RADIAN_INT_MULTIPLIER); - + if (DRAW_TRAIL) { + int deltaAngle = getDelta(mThumbAngle, touchAngle, useDirection, ccw); + mAcculumalatedTrailAngle += Math.toDegrees(deltaAngle / (double) RADIAN_INT_MULTIPLIER); + } + if (animateThumbToNewAngle) { if (useDirection) { setThumbAngleAnimated(touchAngle, 0, ccw); @@ -851,15 +863,10 @@ public class ZoomRing extends View { if (DRAW_TRAIL) { mTrail.draw(canvas); } - - // If we aren't near the bounds, draw the corresponding arrows - int callbackAngle = mPreviousCallbackAngle; - if (callbackAngle < mThumbCwBound - RADIAN_INT_ERROR || - callbackAngle > mThumbCwBound + RADIAN_INT_ERROR) { + if ((mThumbArrowsToDraw & THUMB_ARROW_PLUS) != 0) { mThumbPlusArrowDrawable.draw(canvas); } - if (callbackAngle < mThumbCcwBound - RADIAN_INT_ERROR || - callbackAngle > mThumbCcwBound + RADIAN_INT_ERROR) { + if ((mThumbArrowsToDraw & THUMB_ARROW_MINUS) != 0) { mThumbMinusArrowDrawable.draw(canvas); } mThumbDrawable.draw(canvas); @@ -875,8 +882,21 @@ public class ZoomRing extends View { public void setThumbArrowsVisible(boolean visible) { if (visible) { mThumbArrowsAlpha = 255; - mThumbPlusArrowDrawable.setAlpha(255); - mThumbMinusArrowDrawable.setAlpha(255); + int callbackAngle = mPreviousCallbackAngle; + if (callbackAngle < mThumbCwBound - RADIAN_INT_ERROR || + callbackAngle > mThumbCwBound + RADIAN_INT_ERROR) { + mThumbPlusArrowDrawable.setAlpha(255); + mThumbArrowsToDraw |= THUMB_ARROW_PLUS; + } else { + mThumbArrowsToDraw &= ~THUMB_ARROW_PLUS; + } + if (callbackAngle < mThumbCcwBound - RADIAN_INT_ERROR || + callbackAngle > mThumbCcwBound + RADIAN_INT_ERROR) { + mThumbMinusArrowDrawable.setAlpha(255); + mThumbArrowsToDraw |= THUMB_ARROW_MINUS; + } else { + mThumbArrowsToDraw &= ~THUMB_ARROW_MINUS; + } invalidate(); } else if (mThumbArrowsAlpha == 255) { // Only start fade if we're fully visible (otherwise another fade is happening already) @@ -886,17 +906,24 @@ public class ZoomRing extends View { } private void onThumbArrowsFadeTick() { - if (mThumbArrowsAlpha <= 0) return; + if (mThumbArrowsAlpha <= 0) { + mThumbArrowsToDraw = 0; + return; + } mThumbArrowsAlpha = (int) (255 - (255 * (SystemClock.elapsedRealtime() - mThumbArrowsFadeStartTime) / THUMB_ARROWS_FADE_DURATION)); if (mThumbArrowsAlpha < 0) mThumbArrowsAlpha = 0; - mThumbPlusArrowDrawable.setAlpha(mThumbArrowsAlpha); - mThumbMinusArrowDrawable.setAlpha(mThumbArrowsAlpha); - invalidateDrawable(mThumbPlusArrowDrawable); - invalidateDrawable(mThumbMinusArrowDrawable); - + if ((mThumbArrowsToDraw & THUMB_ARROW_PLUS) != 0) { + mThumbPlusArrowDrawable.setAlpha(mThumbArrowsAlpha); + invalidateDrawable(mThumbPlusArrowDrawable); + } + if ((mThumbArrowsToDraw & THUMB_ARROW_MINUS) != 0) { + mThumbMinusArrowDrawable.setAlpha(mThumbArrowsAlpha); + invalidateDrawable(mThumbMinusArrowDrawable); + } + if (!mHandler.hasMessages(MSG_THUMB_ARROWS_FADE_TICK)) { mHandler.sendEmptyMessage(MSG_THUMB_ARROWS_FADE_TICK); } diff --git a/core/java/android/widget/ZoomRingController.java b/core/java/android/widget/ZoomRingController.java index 31074b6..2e97fda 100644 --- a/core/java/android/widget/ZoomRingController.java +++ b/core/java/android/widget/ZoomRingController.java @@ -57,7 +57,7 @@ import android.view.animation.DecelerateInterpolator; public class ZoomRingController implements ZoomRing.OnZoomRingCallback, View.OnTouchListener, View.OnKeyListener { - private static final int ZOOM_RING_RADIUS_INSET = 10; + private static final int ZOOM_RING_RADIUS_INSET = 24; private static final int ZOOM_RING_RECENTERING_DURATION = 500; @@ -79,7 +79,7 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, private static final int MAX_PAN_GAP = 20; private static final int MAX_INITIATE_PAN_GAP = 10; // TODO view config - private static final int INITIATE_PAN_DELAY = 400; + private static final int INITIATE_PAN_DELAY = 300; private static final String SETTING_NAME_SHOWN_TOAST = "shown_zoom_ring_toast"; @@ -146,7 +146,7 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, private int mZoomRingHeight; /** Invokes panning of owner view if the zoom ring is touching an edge. */ - private Panner mPanner = new Panner(); + private Panner mPanner; private long mTouchingEdgeStartTime; private boolean mPanningEnabledForThisInteraction; @@ -241,6 +241,7 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, mContext = context; mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + mPanner = new Panner(); mOwnerView = ownerView; mZoomRing = new ZoomRing(context); @@ -389,6 +390,8 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, }; } + mPanningArrows.setAnimation(null); + mHandler.post(mPostedVisibleInitializer); // Handle configuration changes when visible @@ -409,12 +412,13 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, } else { mOwnerView.setOnTouchListener(null); } - + // No longer care about configuration changes mContext.unregisterReceiver(mConfigurationChangedReceiver); mWindowManager.removeView(mContainer); - + mHandler.removeCallbacks(mPostedVisibleInitializer); + if (mCallback != null) { mCallback.onVisibilityChanged(false); } @@ -464,6 +468,9 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, public boolean handleDoubleTapEvent(MotionEvent event) { int action = event.getAction(); + // TODO: make sure this works well with the + // ownerView.setOnTouchListener(this) instead of window receiving + // touches if (action == MotionEvent.ACTION_DOWN) { mTouchMode = TOUCH_MODE_WAITING_FOR_TAP_DRAG_MOVEMENT; int x = (int) event.getX(); @@ -493,6 +500,7 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, mViewConfig.getScaledTouchSlop()) { mZoomRing.setTapDragMode(true, x, y); mTouchMode = TOUCH_MODE_FORWARDING_FOR_TAP_DRAG; + setTouchTargetView(mZoomRing); } return true; @@ -587,7 +595,6 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, public void onZoomRingMovingStarted() { mScroller.abortAnimation(); - mPanningEnabledForThisInteraction = false; mTouchingEdgeStartTime = 0; if (mCallback != null) { mCallback.onBeginPan(); @@ -658,6 +665,8 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, if (!horizontalPanning) { // Neither are panning, reset any timer to start pan mode mTouchingEdgeStartTime = 0; + mPanningEnabledForThisInteraction = false; + mPanner.stop(); } } } @@ -752,6 +761,7 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, // The ring was dismissed but we need to throw away all events until the up if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) { mOwnerView.setOnTouchListener(null); + setTouchTargetView(null); mReleaseTouchListenerOnUp = false; } @@ -763,16 +773,13 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, switch (action) { case MotionEvent.ACTION_DOWN: - targetView = mTouchTargetView = - getViewForTouch((int) event.getRawX(), (int) event.getRawY()); - if (targetView != null) { - targetView.getLocationInWindow(mTouchTargetLocationInWindow); - } + targetView = getViewForTouch((int) event.getRawX(), (int) event.getRawY()); + setTouchTargetView(targetView); break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: - mTouchTargetView = null; + setTouchTargetView(null); break; } @@ -799,6 +806,13 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, } } + private void setTouchTargetView(View view) { + mTouchTargetView = view; + if (view != null) { + view.getLocationInWindow(mTouchTargetLocationInWindow); + } + } + /** * Returns the View that should receive a touch at the given coordinates. * @@ -950,6 +964,22 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, } } + public void setPannerStartVelocity(float startVelocity) { + mPanner.mStartVelocity = startVelocity; + } + + public void setPannerAcceleration(float acceleration) { + mPanner.mAcceleration = acceleration; + } + + public void setPannerMaxVelocity(float maxVelocity) { + mPanner.mMaxVelocity = maxVelocity; + } + + public void setPannerStartAcceleratingDuration(int duration) { + mPanner.mStartAcceleratingDuration = duration; + } + private class Panner implements Runnable { private static final int RUN_DELAY = 15; private static final float STOP_SLOWDOWN = 0.8f; @@ -967,6 +997,13 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, /** The time of the last callback to pan the map/browser/etc. */ private long mPreviousCallbackTime; + // TODO Adjust to be DPI safe + private float mStartVelocity = 135; + private float mAcceleration = 160; + private float mMaxVelocity = 1000; + private int mStartAcceleratingDuration = 700; + private float mVelocity; + /** -100 (full left) to 0 (none) to 100 (full right) */ public void setHorizontalStrength(int horizontalStrength) { if (mHorizontalStrength == 0 && mVerticalStrength == 0 && horizontalStrength != 0) { @@ -1013,11 +1050,12 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, boolean firstRun = mPreviousCallbackTime == 0; long curTime = SystemClock.elapsedRealtime(); - int panAmount = getPanAmount(mStartTime, mPreviousCallbackTime, curTime); + int panAmount = getPanAmount(mPreviousCallbackTime, curTime); mPreviousCallbackTime = curTime; if (firstRun) { mStartTime = curTime; + mVelocity = mStartVelocity; } else { int panX = panAmount * mHorizontalStrength / 100; int panY = panAmount * mVerticalStrength / 100; @@ -1030,12 +1068,22 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, mUiHandler.postDelayed(this, RUN_DELAY); } - // TODO make setter for this value so zoom clients can have different pan rates, if they want - private static final int PAN_VELOCITY_PX_S = 30; - private int getPanAmount(long startTime, long previousTime, long currentTime) { - return (int) ((currentTime - previousTime) * PAN_VELOCITY_PX_S / 100); + private int getPanAmount(long previousTime, long currentTime) { + if (mVelocity > mMaxVelocity) { + mVelocity = mMaxVelocity; + } else if (mVelocity < mMaxVelocity) { + // See if it's time to add in some acceleration + if (currentTime - mStartTime > mStartAcceleratingDuration) { + mVelocity += (currentTime - previousTime) * mAcceleration / 1000; + } + } + + return (int) ((currentTime - previousTime) * mVelocity) / 1000; } + } + + public interface OnZoomListener { void onBeginDrag(); diff --git a/core/java/com/android/internal/widget/TextProgressBar.java b/core/java/com/android/internal/widget/TextProgressBar.java index d068865..5bf4601 100644 --- a/core/java/com/android/internal/widget/TextProgressBar.java +++ b/core/java/com/android/internal/widget/TextProgressBar.java @@ -104,6 +104,7 @@ public class TextProgressBar extends RelativeLayout implements OnChronometerTick * @param durationBase Use the {@link SystemClock#elapsedRealtime} time * base. */ + @android.view.RemotableViewMethod public void setDurationBase(long durationBase) { mDurationBase = durationBase; diff --git a/core/java/com/google/android/util/GoogleWebContentHelper.java b/core/java/com/google/android/util/GoogleWebContentHelper.java index 7500ec3..2911420 100644 --- a/core/java/com/google/android/util/GoogleWebContentHelper.java +++ b/core/java/com/google/android/util/GoogleWebContentHelper.java @@ -206,7 +206,7 @@ public class GoogleWebContentHelper { WebSettings settings = mWebView.getSettings(); settings.setCacheMode(WebSettings.LOAD_NO_CACHE); - mProgressBar = mLayout.findViewById(com.android.internal.R.id.progress); + mProgressBar = mLayout.findViewById(com.android.internal.R.id.progressContainer); TextView message = (TextView) mProgressBar.findViewById(com.android.internal.R.id.message); message.setText(com.android.internal.R.string.googlewebcontenthelper_loading); diff --git a/core/res/res/color/tertiary_text_dark.xml b/core/res/res/color/tertiary_text_dark.xml index 7e61fc8..7ce3580 100644 --- a/core/res/res/color/tertiary_text_dark.xml +++ b/core/res/res/color/tertiary_text_dark.xml @@ -18,7 +18,7 @@ <item android:state_enabled="false" android:color="#808080"/>  <item android:state_window_focused="false" android:color="#808080"/> <item android:state_pressed="true" android:color="#808080"/> - <item android:state_selected="true" android:color="#808080"/> + <item android:state_selected="true" android:color="@android:color/dim_foreground_light"/> <item android:color="#808080"/> <!-- not selected --> </selector> diff --git a/core/res/res/drawable/sym_action_add.png b/core/res/res/drawable/sym_action_add.png Binary files differnew file mode 100644 index 0000000..af637b3 --- /dev/null +++ b/core/res/res/drawable/sym_action_add.png diff --git a/core/res/res/drawable/zoom_ring_track_absolute.png b/core/res/res/drawable/zoom_ring_track_absolute.png Binary files differindex 6b38c53..0195b9e 100644 --- a/core/res/res/drawable/zoom_ring_track_absolute.png +++ b/core/res/res/drawable/zoom_ring_track_absolute.png diff --git a/core/res/res/layout/google_web_content_helper_layout.xml b/core/res/res/layout/google_web_content_helper_layout.xml index 7409621..40f84bf 100644 --- a/core/res/res/layout/google_web_content_helper_layout.xml +++ b/core/res/res/layout/google_web_content_helper_layout.xml @@ -20,7 +20,7 @@ <!-- Include the indeterminate progress dialog's layout. --> <include - android:id="@+id/progress" + android:id="@+id/progressContainer" layout="@android:layout/progress_dialog" /> <WebView diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 6e6e074..c1a6440 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -762,6 +762,7 @@ <enum name="KEYCODE_PREVIOUSSONG" value="88" /> <enum name="KEYCODE_REWIND" value="89" /> <enum name="KEYCODE_FORWARD" value="90" /> + <enum name="KEYCODE_MUTE" value="91" /> </attr> <!-- ***************************************************************** --> diff --git a/include/ui/KeycodeLabels.h b/include/ui/KeycodeLabels.h index 53c1188..efa6d2b 100644 --- a/include/ui/KeycodeLabels.h +++ b/include/ui/KeycodeLabels.h @@ -113,6 +113,7 @@ static const KeycodeLabel KEYCODES[] = { { "PREVIOUSSONG", 88 }, { "REWIND", 89 }, { "FORWARD", 90 }, + { "MUTE", 91 }, // NOTE: If you add a new keycode here you must also add it to: // (enum KeyCode, in this file) @@ -216,7 +217,8 @@ typedef enum KeyCode { kKeyCodeNextSong = 87, kKeyCodePreviousSong = 88, kKeyCodeRewind = 89, - kKeyCodeForward = 90 + kKeyCodeForward = 90, + kKeyCodeMute = 91 } KeyCode; static const KeycodeLabel FLAGS[] = { diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp index 3d39181..e833c85 100644 --- a/media/libmedia/AudioRecord.cpp +++ b/media/libmedia/AudioRecord.cpp @@ -400,7 +400,7 @@ status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, int32_t waitCount) uint32_t u = cblk->user; uint32_t bufferEnd = cblk->userBase + cblk->frameCount; - if (u + framesReady > bufferEnd) { + if (u + framesReq > bufferEnd) { framesReq = bufferEnd - u; } diff --git a/media/libmedia/ToneGenerator.cpp b/media/libmedia/ToneGenerator.cpp index 7fafc56..5416629 100644 --- a/media/libmedia/ToneGenerator.cpp +++ b/media/libmedia/ToneGenerator.cpp @@ -175,27 +175,28 @@ bool ToneGenerator::startTone(int toneType) { if (mState == TONE_INIT) { if (prepareWave()) { LOGV("Immediate start, time %d\n", (unsigned int)(systemTime()/1000000)); - + lResult = true; mState = TONE_STARTING; mLock.unlock(); mpAudioTrack->start(); mLock.lock(); if (mState == TONE_STARTING) { + LOGV("Wait for start callback"); if (mWaitCbkCond.waitRelative(mLock, seconds(1)) != NO_ERROR) { LOGE("--- Immediate start timed out"); mState = TONE_IDLE; + lResult = false; } } - - if (mState == TONE_PLAYING) - lResult = true; + } else { + mState == TONE_IDLE; } } else { LOGV("Delayed start\n"); mState = TONE_RESTARTING; if (mWaitCbkCond.waitRelative(mLock, seconds(1)) == NO_ERROR) { - if (mState != TONE_INIT) { + if (mState != TONE_IDLE) { lResult = true; } LOGV("cond received"); @@ -206,7 +207,8 @@ bool ToneGenerator::startTone(int toneType) { } mLock.unlock(); - LOGV("Tone started, time %d\n", (unsigned int)(systemTime()/1000000)); + LOGV_IF(lResult, "Tone started, time %d\n", (unsigned int)(systemTime()/1000000)); + LOGW_IF(!lResult, "Tone start failed!!!, time %d\n", (unsigned int)(systemTime()/1000000)); return lResult; } @@ -457,8 +459,11 @@ audioCallback_EndLoop: if (lpToneGen->prepareWave()) { lpToneGen->mState = TONE_STARTING; } else { - lpToneGen->mState = TONE_INIT; + LOGW("Cbk restarting prepareWave() failed\n"); + lpToneGen->mState = TONE_IDLE; lpToneGen->mpAudioTrack->stop(); + // Force loop exit + lNumSmp = 0; } lSignal = true; break; diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaAudioTrackTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaAudioTrackTest.java index ae6a834..b6a0848 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaAudioTrackTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaAudioTrackTest.java @@ -19,6 +19,8 @@ package com.android.mediaframeworktest.functional; import com.android.mediaframeworktest.MediaFrameworkTest; import com.android.mediaframeworktest.MediaNames; +import android.media.AudioFormat; +import android.media.AudioManager; import android.media.AudioTrack; import android.content.Context; import android.test.ActivityInstrumentationTestCase2; @@ -47,12 +49,127 @@ public class MediaAudioTrackTest extends ActivityInstrumentationTestCase2<MediaF protected void tearDown() throws Exception { super.tearDown(); } + + //----------------------------------------------------------------- + // private class to hold test reslts + public class TestResults { + public boolean mResult = false; + public String mResultLog = ""; + public TestResults(boolean b, String s) { mResult = b; mResultLog = s; } + } + + //----------------------------------------------------------------- + // generic test methods + public TestResults constructorTestMultiSampleRate( + // parameters tested by this method + int _inTest_streamType, int _inTest_mode, int _inTest_config, + // parameter-dependent expected results + int _expected_stateForMode) { + + int[] testSampleRates = {8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000}; + String failedRates = "Failure for rate(s): "; + boolean localRes, finalRes = true; + + for(int i = 0 ; i < testSampleRates.length ; i++) { + //Log.v("MediaAudioTrackTest", "[ constructorTestMultiSampleRate ] testing "+ testSampleRates[i]); + AudioTrack track = null; + try { + track = new AudioTrack( + _inTest_streamType, + testSampleRates[i], + _inTest_config, + AudioFormat.ENCODING_PCM_16BIT, + AudioTrack.getMinBufferSize(testSampleRates[i], + _inTest_config, AudioFormat.ENCODING_PCM_16BIT),//testSampleRates[i]*4 + _inTest_mode); + } catch(IllegalArgumentException iae) { + Log.e("MediaAudioTrackTest", "[ constructorTestMultiSampleRate ] exception at SR " + + testSampleRates[i]+": \n" + iae); + } + if(track != null) { + localRes = (track.getState() == _expected_stateForMode); + track.release(); + } + else { + localRes = false; + } + + if (!localRes) { + //log the error for the test runner + failedRates += Integer.toString(testSampleRates[i]) + "Hz "; + //log the error for logcat + Log.e("MediaAudioTrackTest", "[ constructorTestMultiSampleRate ] failed to construct " + +"AudioTrack(streamType="+_inTest_streamType + +", sampleRateInHz=" + testSampleRates[i] + +", channelConfig=" + _inTest_config + +", audioFormat=AudioFormat.ENCODING_PCM_16BIT" + +", bufferSizeInBytes=" + AudioTrack.getMinBufferSize(testSampleRates[i], + _inTest_config, AudioFormat.ENCODING_PCM_16BIT) + +", mode="+ _inTest_mode ); + //mark test as failed + finalRes = false; + } + } + return new TestResults(finalRes, failedRates); + } + + //----------------------------------------------------------------- + // AUDIOTRACK TESTS: + //---------------------------------- + + //----------------------------------------------------------------- + // AudioTrack constructor and AudioTrack.getMinBufferSize(...) + //---------------------------------- - //Test case 1: Set the invalid volume + //Test case 1: constructor for streaming AudioTrack, mono, 16bit at misc valid sample rates + @MediumTest + public void testConstructorMono16MusicStream() throws Exception { + + TestResults res = constructorTestMultiSampleRate( + AudioManager.STREAM_MUSIC, AudioTrack.MODE_STREAM, + AudioFormat.CHANNEL_CONFIGURATION_MONO, + AudioTrack.STATE_INITIALIZED); + + assertTrue("testConstructorMono16MusicStream: " + res.mResultLog, res.mResult); + } + + + //Test case 2: constructor for streaming AudioTrack, stereo, 16bit at misc valid sample rates @MediumTest - public void testGetMinVolume() throws Exception { - //To Do: Create the test case for GetMinVolume - assertTrue("testGetMinVolume", true); + public void testConstructorStereo16MusicStream() throws Exception { + + TestResults res = constructorTestMultiSampleRate( + AudioManager.STREAM_MUSIC, AudioTrack.MODE_STREAM, + AudioFormat.CHANNEL_CONFIGURATION_STEREO, + AudioTrack.STATE_INITIALIZED); + + assertTrue("testConstructorStereo16MusicStream: " + res.mResultLog, res.mResult); + } + + + //Test case 3: constructor for static AudioTrack, mono, 16bit at misc valid sample rates + @MediumTest + public void testConstructorMono16MusicStatic() throws Exception { + + TestResults res = constructorTestMultiSampleRate( + AudioManager.STREAM_MUSIC, AudioTrack.MODE_STATIC, + AudioFormat.CHANNEL_CONFIGURATION_MONO, + AudioTrack.STATE_NO_STATIC_DATA); + + assertTrue("testConstructorMono16MusicStatic: " + res.mResultLog, res.mResult); + } + + + //Test case 4: constructor for static AudioTrack, stereo, 16bit at misc valid sample rates + @MediumTest + public void testConstructorStereo16MusicStatic() throws Exception { + + TestResults res = constructorTestMultiSampleRate( + AudioManager.STREAM_MUSIC, AudioTrack.MODE_STATIC, + AudioFormat.CHANNEL_CONFIGURATION_STEREO, + AudioTrack.STATE_NO_STATIC_DATA); + + assertTrue("testConstructorStereo16MusicStatic: " + res.mResultLog, res.mResult); } } diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/TonesAutoTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/TonesAutoTest.java index 0209305..da18e74 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/TonesAutoTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/TonesAutoTest.java @@ -88,7 +88,7 @@ import android.media.AudioManager; toneGen = new ToneGenerator(AudioManager.STREAM_MUSIC, 100); - for (type = ToneGenerator.TONE_PROP_BEEP; type <= ToneGenerator.TONE_PROP_PROMPT; type++) { + for (type = ToneGenerator.TONE_PROP_BEEP; type <= ToneGenerator.TONE_PROP_BEEP2; type++) { if (toneGen.startTone(type)) { Thread.sleep(1000); toneGen.stopTone(); diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderPrepareStateUnitTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderPrepareStateUnitTest.java index dfd544a..134144d 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderPrepareStateUnitTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderPrepareStateUnitTest.java @@ -55,7 +55,7 @@ public class MediaRecorderPrepareStateUnitTest extends AndroidTestCase implement try { recorder.prepare(); } catch (IOException exception) { - fail("recorder.prepare() failed"); + throw new RuntimeException(); } } diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderSetOutputFileStateUnitTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderSetOutputFileStateUnitTest.java index 50e235b..712a758 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderSetOutputFileStateUnitTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderSetOutputFileStateUnitTest.java @@ -29,24 +29,22 @@ public class MediaRecorderSetOutputFileStateUnitTest extends AndroidTestCase imp private MediaRecorderStateUnitTestTemplate mTestTemplate = new MediaRecorderStateUnitTestTemplate(); /** * 1. It is valid to call setOutputFile() in the following states: - * {DataSourceConfigured}. + * {DataSourceConfigured, Initial, Initialized, Prepared, Recording, Error}. * 2. It is invalid to call setOutputFile() in the following states: - * {Initial, Initialized, Prepared, Recording, Error} + * {} * * @param stateErrors the MediaRecorderStateErrors to check against. */ public void checkStateErrors(MediaRecorderStateErrors stateErrors) { // Valid states. assertTrue(!stateErrors.errorInDataSourceConfiguredState); - - // Invalid states. - assertTrue(stateErrors.errorInPreparedState); - assertTrue(stateErrors.errorInRecordingState); - assertTrue(stateErrors.errorInErrorState); - assertTrue(stateErrors.errorInInitialState); - assertTrue(stateErrors.errorInInitialStateAfterReset); - assertTrue(stateErrors.errorInInitialStateAfterStop); - assertTrue(stateErrors.errorInInitializedState); + assertTrue(!stateErrors.errorInPreparedState); + assertTrue(!stateErrors.errorInRecordingState); + assertTrue(!stateErrors.errorInErrorState); + assertTrue(!stateErrors.errorInInitialState); + assertTrue(!stateErrors.errorInInitialStateAfterReset); + assertTrue(!stateErrors.errorInInitialStateAfterStop); + assertTrue(!stateErrors.errorInInitializedState); } public void invokeMethodUnderTest(MediaRecorder recorder) { diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderStateUnitTestTemplate.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderStateUnitTestTemplate.java index f350467..9edc9aa 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderStateUnitTestTemplate.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderStateUnitTestTemplate.java @@ -90,9 +90,15 @@ class MediaRecorderStateUnitTestTemplate extends AndroidTestCase { } } + // FIXME: + // In the past, stop() == reset(). + // However, this is no longer true. The plan is to have a STOPPED state. + // and from STOPPED state, start can be called without the need to + // do the recording configuration again. private void setMediaRecorderToInitialStateAfterStop() { try { mMediaRecorder.reset(); +/* mMediaRecorder.setAudioSource(AUDIO_SOURCE); mMediaRecorder.setOutputFormat(OUTPUT_FORMAT); mMediaRecorder.setAudioEncoder(AUDIO_ENCODER); @@ -100,6 +106,7 @@ class MediaRecorderStateUnitTestTemplate extends AndroidTestCase { mMediaRecorder.prepare(); mMediaRecorder.start(); mMediaRecorder.stop(); +*/ } catch(Exception e) { fail("setMediaRecorderToInitialStateAfterReset: Exception " + e.getClass().getName() + " was thrown."); } diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java index 16b5819..f41d21f 100644 --- a/services/java/com/android/server/PowerManagerService.java +++ b/services/java/com/android/server/PowerManagerService.java @@ -246,8 +246,13 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage public void acquire() { if (!mRefCounted || mCount++ == 0) { - PowerManagerService.this.acquireWakeLockLocked(mFlags, mToken, - MY_UID, mTag); + long ident = Binder.clearCallingIdentity(); + try { + PowerManagerService.this.acquireWakeLockLocked(mFlags, mToken, + MY_UID, mTag); + } finally { + Binder.restoreCallingIdentity(ident); + } } } @@ -1285,8 +1290,9 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage } finally { Binder.restoreCallingIdentity(identity); } + mPowerState &= ~SCREEN_ON_BIT; if (!mScreenBrightness.animating) { - err = turnScreenOffLocked(becauseOfUser); + err = screenOffFinishedAnimating(becauseOfUser); } else { mOffBecauseOfUser = becauseOfUser; err = 0; @@ -1297,33 +1303,25 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage } } - private int turnScreenOffLocked(boolean becauseOfUser) { - if ((mPowerState&SCREEN_ON_BIT) != 0) { - EventLog.writeEvent(LOG_POWER_SCREEN_STATE, 0, becauseOfUser ? 1 : 0, - mTotalTouchDownTime, mTouchCycles); - mLastTouchDown = 0; - int err = Power.setScreenState(false); + private int screenOffFinishedAnimating(boolean becauseOfUser) { + // I don't think we need to check the current state here because all of these + // Power.setScreenState and sendNotificationLocked can both handle being + // called multiple times in the same state. -joeo + EventLog.writeEvent(LOG_POWER_SCREEN_STATE, 0, becauseOfUser ? 1 : 0, + mTotalTouchDownTime, mTouchCycles); + mLastTouchDown = 0; + int err = Power.setScreenState(false); + if (mScreenOnStartTime != 0) { mScreenOnTime += SystemClock.elapsedRealtime() - mScreenOnStartTime; mScreenOnStartTime = 0; - if (err == 0) { - // - // FIXME(joeo) - // - // The problem that causes the screen not to come on is that this isn't - // called until after the animation is done. It needs to be set right - // away, and the anmiation's state needs to be recorded separately. - // - // - - mPowerState &= ~SCREEN_ON_BIT; - int why = becauseOfUser - ? WindowManagerPolicy.OFF_BECAUSE_OF_USER - : WindowManagerPolicy.OFF_BECAUSE_OF_TIMEOUT; - sendNotificationLocked(false, why); - } - return err; } - return 0; + if (err == 0) { + int why = becauseOfUser + ? WindowManagerPolicy.OFF_BECAUSE_OF_USER + : WindowManagerPolicy.OFF_BECAUSE_OF_TIMEOUT; + sendNotificationLocked(false, why); + } + return err; } private boolean batteryIsLow() { @@ -1538,7 +1536,7 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage animating = more; if (!more) { if (mask == Power.SCREEN_LIGHT && curIntValue == Power.BRIGHTNESS_OFF) { - turnScreenOffLocked(mOffBecauseOfUser); + screenOffFinishedAnimating(mOffBecauseOfUser); } } return more; diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java index fc22bad..10a2d29 100644 --- a/services/java/com/android/server/WindowManagerService.java +++ b/services/java/com/android/server/WindowManagerService.java @@ -2088,6 +2088,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } int or = wtoken.requestedOrientation; + // If this application is fullscreen, then just take whatever + // orientation it has and ignores whatever is under it. + if (wtoken.appFullscreen) { + return or; + } // If this application has requested an explicit orientation, // then use it. if (or == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE || @@ -5594,6 +5599,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo return mFrame; } + public Rect getShownFrameLw() { + return mShownFrame; + } + public Rect getDisplayFrameLw() { return mDisplayFrame; } @@ -6221,10 +6230,16 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } public boolean fillsScreenLw(int screenWidth, int screenHeight, - boolean shownFrame) { + boolean shownFrame, boolean onlyOpaque) { if (mSurface == null) { return false; } + if (mAppToken != null && !mAppToken.appFullscreen) { + return false; + } + if (onlyOpaque && mAttrs.format != PixelFormat.OPAQUE) { + return false; + } final Rect frame = shownFrame ? mShownFrame : mFrame; if (frame.left <= 0 && frame.top <= 0 && frame.right >= screenWidth diff --git a/telephony/java/com/android/internal/telephony/WapPushOverSms.java b/telephony/java/com/android/internal/telephony/WapPushOverSms.java new file mode 100644 index 0000000..66fa943 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/WapPushOverSms.java @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony; + +import android.content.Intent; +import android.provider.Telephony.Sms.Intents; +import android.util.Config; +import android.util.Log; +import com.android.internal.telephony.gsm.GSMPhone; +import com.android.internal.telephony.gsm.SimUtils; + + +/** + * WAP push handler class. + * + * @hide + */ +public class WapPushOverSms { + private static final String LOG_TAG = "WAP PUSH"; + + private final GSMPhone mPhone; + private WspTypeDecoder pduDecoder; + + + public WapPushOverSms(GSMPhone phone) { + mPhone = phone; + } + + /** + * Dispatches inbound messages that are in the WAP PDU format. See + * wap-230-wsp-20010705-a section 8 for details on the WAP PDU format. + * + * @param pdu The WAP PDU, made up of one or more SMS PDUs + */ + public void dispatchWapPdu(byte[] pdu) { + + if (Config.LOGD) Log.d(LOG_TAG, "Rx: " + SimUtils.bytesToHexString(pdu)); + + int index = 0; + int transactionId = pdu[index++] & 0xFF; + int pduType = pdu[index++] & 0xFF; + int headerLength = 0; + + if ((pduType != WspTypeDecoder.PDU_TYPE_PUSH) && + (pduType != WspTypeDecoder.PDU_TYPE_CONFIRMED_PUSH)) { + if (Config.LOGD) Log.w(LOG_TAG, "Received non-PUSH WAP PDU. Type = " + pduType); + return; + } + + pduDecoder = new WspTypeDecoder(pdu); + + /** + * Parse HeaderLen(unsigned integer). + * From wap-230-wsp-20010705-a section 8.1.2 + * The maximum size of a uintvar is 32 bits. + * So it will be encoded in no more than 5 octets. + */ + if (pduDecoder.decodeUintvarInteger(index) == false) { + if (Config.LOGD) Log.w(LOG_TAG, "Received PDU. Header Length error."); + return; + } + headerLength = (int)pduDecoder.getValue32(); + index += pduDecoder.getDecodedDataLength(); + + int headerStartIndex = index; + + /** + * Parse Content-Type. + * From wap-230-wsp-20010705-a section 8.4.2.24 + * + * Content-type-value = Constrained-media | Content-general-form + * Content-general-form = Value-length Media-type + * Media-type = (Well-known-media | Extension-Media) *(Parameter) + * Value-length = Short-length | (Length-quote Length) + * Short-length = <Any octet 0-30> (octet <= WAP_PDU_SHORT_LENGTH_MAX) + * Length-quote = <Octet 31> (WAP_PDU_LENGTH_QUOTE) + * Length = Uintvar-integer + */ + if (pduDecoder.decodeContentType(index) == false) { + if (Config.LOGD) Log.w(LOG_TAG, "Received PDU. Header Content-Type error."); + return; + } + int binaryContentType; + String mimeType = pduDecoder.getValueString(); + if (mimeType == null) { + binaryContentType = (int)pduDecoder.getValue32(); + switch (binaryContentType) { + case WspTypeDecoder.CONTENT_TYPE_B_DRM_RIGHTS_XML: + mimeType = WspTypeDecoder.CONTENT_MIME_TYPE_B_DRM_RIGHTS_XML; + break; + case WspTypeDecoder.CONTENT_TYPE_B_DRM_RIGHTS_WBXML: + mimeType = WspTypeDecoder.CONTENT_MIME_TYPE_B_DRM_RIGHTS_WBXML; + break; + case WspTypeDecoder.CONTENT_TYPE_B_PUSH_SI: + mimeType = WspTypeDecoder.CONTENT_MIME_TYPE_B_PUSH_SI; + break; + case WspTypeDecoder.CONTENT_TYPE_B_PUSH_SL: + mimeType = WspTypeDecoder.CONTENT_MIME_TYPE_B_PUSH_SL; + break; + case WspTypeDecoder.CONTENT_TYPE_B_PUSH_CO: + mimeType = WspTypeDecoder.CONTENT_MIME_TYPE_B_PUSH_CO; + break; + case WspTypeDecoder.CONTENT_TYPE_B_MMS: + mimeType = WspTypeDecoder.CONTENT_MIME_TYPE_B_MMS; + break; + default: + if (Config.LOGD) { + Log.w(LOG_TAG, + "Received PDU. Unsupported Content-Type = " + binaryContentType); + } + return; + } + } else { + if (mimeType.equals(WspTypeDecoder.CONTENT_MIME_TYPE_B_DRM_RIGHTS_XML)) { + binaryContentType = WspTypeDecoder.CONTENT_TYPE_B_DRM_RIGHTS_XML; + } else if (mimeType.equals(WspTypeDecoder.CONTENT_MIME_TYPE_B_DRM_RIGHTS_WBXML)) { + binaryContentType = WspTypeDecoder.CONTENT_TYPE_B_DRM_RIGHTS_WBXML; + } else if (mimeType.equals(WspTypeDecoder.CONTENT_MIME_TYPE_B_PUSH_SI)) { + binaryContentType = WspTypeDecoder.CONTENT_TYPE_B_PUSH_SI; + } else if (mimeType.equals(WspTypeDecoder.CONTENT_MIME_TYPE_B_PUSH_SL)) { + binaryContentType = WspTypeDecoder.CONTENT_TYPE_B_PUSH_SL; + } else if (mimeType.equals(WspTypeDecoder.CONTENT_MIME_TYPE_B_PUSH_CO)) { + binaryContentType = WspTypeDecoder.CONTENT_TYPE_B_PUSH_CO; + } else if (mimeType.equals(WspTypeDecoder.CONTENT_MIME_TYPE_B_MMS)) { + binaryContentType = WspTypeDecoder.CONTENT_TYPE_B_MMS; + } else { + if (Config.LOGD) Log.w(LOG_TAG, "Received PDU. Unknown Content-Type = " + mimeType); + return; + } + } + index += pduDecoder.getDecodedDataLength(); + + int dataIndex = headerStartIndex + headerLength; + boolean dispatchedByApplication = false; + switch (binaryContentType) { + case WspTypeDecoder.CONTENT_TYPE_B_PUSH_CO: + dispatchWapPdu_PushCO(pdu, transactionId, pduType); + dispatchedByApplication = true; + break; + case WspTypeDecoder.CONTENT_TYPE_B_MMS: + dispatchWapPdu_MMS(pdu, transactionId, pduType, dataIndex); + dispatchedByApplication = true; + break; + default: + break; + } + if (dispatchedByApplication == false) { + dispatchWapPdu_default(pdu, transactionId, pduType, mimeType, dataIndex); + } + } + + + + private void dispatchWapPdu_default( + byte[] pdu, int transactionId, int pduType, String mimeType, int dataIndex) { + byte[] data; + + data = new byte[pdu.length - dataIndex]; + System.arraycopy(pdu, dataIndex, data, 0, data.length); + + Intent intent = new Intent(Intents.WAP_PUSH_RECEIVED_ACTION); + intent.setType(mimeType); + intent.putExtra("transactionId", transactionId); + intent.putExtra("pduType", pduType); + intent.putExtra("data", data); + + mPhone.getContext().sendBroadcast( + intent, "android.permission.RECEIVE_WAP_PUSH"); + } + + private void dispatchWapPdu_PushCO(byte[] pdu, int transactionId, int pduType) { + Intent intent = new Intent(Intents.WAP_PUSH_RECEIVED_ACTION); + intent.setType(WspTypeDecoder.CONTENT_MIME_TYPE_B_PUSH_CO); + intent.putExtra("transactionId", transactionId); + intent.putExtra("pduType", pduType); + intent.putExtra("data", pdu); + + mPhone.getContext().sendBroadcast( + intent, "android.permission.RECEIVE_WAP_PUSH"); + } + + private void dispatchWapPdu_MMS(byte[] pdu, int transactionId, int pduType, int dataIndex) { + byte[] data; + + data = new byte[pdu.length - dataIndex]; + System.arraycopy(pdu, dataIndex, data, 0, data.length); + + Intent intent = new Intent(Intents.WAP_PUSH_RECEIVED_ACTION); + intent.setType(WspTypeDecoder.CONTENT_MIME_TYPE_B_MMS); + intent.putExtra("transactionId", transactionId); + intent.putExtra("pduType", pduType); + intent.putExtra("data", data); + + mPhone.getContext().sendBroadcast( + intent, "android.permission.RECEIVE_MMS"); + } +} diff --git a/telephony/java/com/android/internal/telephony/WspTypeDecoder.java b/telephony/java/com/android/internal/telephony/WspTypeDecoder.java new file mode 100644 index 0000000..2984fa8 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/WspTypeDecoder.java @@ -0,0 +1,344 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony; + + +/** + * Implement the WSP data type decoder. + * + * @hide + */ +public class WspTypeDecoder { + + private static final int WAP_PDU_SHORT_LENGTH_MAX = 30; + private static final int WAP_PDU_LENGTH_QUOTE = 31; + + public static final int PDU_TYPE_PUSH = 0x06; + public static final int PDU_TYPE_CONFIRMED_PUSH = 0x07; + + public static final int CONTENT_TYPE_B_DRM_RIGHTS_XML = 0x4a; + public static final int CONTENT_TYPE_B_DRM_RIGHTS_WBXML = 0x4b; + public static final int CONTENT_TYPE_B_PUSH_SI = 0x2e; + public static final int CONTENT_TYPE_B_PUSH_SL = 0x30; + public static final int CONTENT_TYPE_B_PUSH_CO = 0x32; + public static final int CONTENT_TYPE_B_MMS = 0x3e; + + public static final String CONTENT_MIME_TYPE_B_DRM_RIGHTS_XML = + "application/vnd.oma.drm.rights+xml"; + public static final String CONTENT_MIME_TYPE_B_DRM_RIGHTS_WBXML = + "application/vnd.oma.drm.rights+wbxml"; + public static final String CONTENT_MIME_TYPE_B_PUSH_SI = "application/vnd.wap.sic"; + public static final String CONTENT_MIME_TYPE_B_PUSH_SL = "application/vnd.wap.slc"; + public static final String CONTENT_MIME_TYPE_B_PUSH_CO = "application/vnd.wap.coc"; + public static final String CONTENT_MIME_TYPE_B_MMS = "application/vnd.wap.mms-message"; + + public static final int PARAMETER_ID_X_WAP_APPLICATION_ID = 0x2f; + + + byte[] wspData; + int dataLength; + long unsigned32bit; + String stringValue; + + public WspTypeDecoder(byte[] pdu) { + wspData = pdu; + } + + /** + * Decode the "Text-string" type for WSP pdu + * + * @param startIndex The starting position of the "Text-string" in this pdu + * + * @return false when error(not a Text-string) occur + * return value can be retrieved by getValueString() method + * length of data in pdu can be retrieved by getValue32() method + */ + public boolean decodeTextString(int startIndex) { + int index = startIndex; + while (wspData[index] != 0) { + index++; + } + dataLength = index - startIndex + 1; + if (wspData[startIndex] == 127) { + stringValue = new String(wspData, startIndex+1, dataLength - 2); + } else { + stringValue = new String(wspData, startIndex, dataLength - 1); + } + return true; + } + + /** + * Decode the "Short-integer" type for WSP pdu + * + * @param startIndex The starting position of the "Short-integer" in this pdu + * + * @return false when error(not a Short-integer) occur + * return value can be retrieved by getValue32() method + * length of data in pdu can be retrieved by getValue32() method + */ + public boolean decodeShortInteger(int startIndex) { + if ((wspData[startIndex] & 0x80) == 0) { + return false; + } + unsigned32bit = wspData[startIndex] & 0x7f; + dataLength = 1; + return true; + } + + /** + * Decode the "Long-integer" type for WSP pdu + * + * @param startIndex The starting position of the "Long-integer" in this pdu + * + * @return false when error(not a Long-integer) occur + * return value can be retrieved by getValue32() method + * length of data in pdu can be retrieved by getValue32() method + */ + public boolean decodeLongInteger(int startIndex) { + int lengthMultiOctet = wspData[startIndex] & 0xff; + + if (lengthMultiOctet > WAP_PDU_SHORT_LENGTH_MAX) { + return false; + } + unsigned32bit = 0; + for (int i=1; i<=lengthMultiOctet; i++) { + unsigned32bit = (unsigned32bit << 8) | (wspData[startIndex+i] & 0xff); + } + dataLength = 1+lengthMultiOctet; + return true; + } + + /** + * Decode the "Integer-Value" type for WSP pdu + * + * @param startIndex The starting position of the "Integer-Value" in this pdu + * + * @return false when error(not a Integer-Value) occur + * return value can be retrieved by getValue32() method + * length of data in pdu can be retrieved by getValue32() method + */ + public boolean decodeIntegerValue(int startIndex) { + if (decodeShortInteger(startIndex) == true) { + return true; + } + return decodeLongInteger(startIndex); + } + + /** + * Decode the "Uintvar-integer" type for WSP pdu + * + * @param startIndex The starting position of the "Uintvar-integer" in this pdu + * + * @return false when error(not a Uintvar-integer) occur + * return value can be retrieved by getValue32() method + * length of data in pdu can be retrieved by getValue32() method + */ + public boolean decodeUintvarInteger(int startIndex) { + int index = startIndex; + + unsigned32bit = 0; + while ((wspData[index] & 0x80) != 0) { + if ((index - startIndex) >= 4) { + return false; + } + unsigned32bit = (unsigned32bit << 7) | (wspData[index] & 0x7f); + index++; + } + unsigned32bit = (unsigned32bit << 7) | (wspData[index] & 0x7f); + dataLength = index - startIndex + 1; + return true; + } + + /** + * Decode the "Value-length" type for WSP pdu + * + * @param startIndex The starting position of the "Value-length" in this pdu + * + * @return false when error(not a Value-length) occur + * return value can be retrieved by getValue32() method + * length of data in pdu can be retrieved by getValue32() method + */ + public boolean decodeValueLength(int startIndex) { + if ((wspData[startIndex] & 0xff) > WAP_PDU_LENGTH_QUOTE) { + return false; + } + if (wspData[startIndex] < WAP_PDU_LENGTH_QUOTE) { + unsigned32bit = wspData[startIndex]; + dataLength = 1; + } else { + decodeUintvarInteger(startIndex+1); + dataLength ++; + } + return true; + } + + /** + * Decode the "Extension-media" type for WSP pdu + * + * @param startIndex The starting position of the "Extension-media" in this pdu + * + * @return false when error(not a Extension-media) occur + * return value can be retrieved by getValueString() method + * length of data in pdu can be retrieved by getValue32() method + */ + public boolean decodeExtensionMedia(int startIndex) { + int index = startIndex; + while (wspData[index] != 0) { + index++; + } + dataLength = index - startIndex + 1; + stringValue = new String(wspData, startIndex, dataLength - 1); + return true; + } + + /** + * Decode the "Constrained-encoding" type for WSP pdu + * + * @param startIndex The starting position of the "Constrained-encoding" in this pdu + * + * @return false when error(not a Constrained-encoding) occur + * return value can be retrieved first by getValueString() and second by getValue32() method + * length of data in pdu can be retrieved by getValue32() method + */ + public boolean decodeConstrainedEncoding(int startIndex) { + if (decodeShortInteger(startIndex) == true) { + stringValue = null; + return true; + } + return decodeExtensionMedia(startIndex); + } + + /** + * Decode the "Content-type" type for WSP pdu + * + * @param startIndex The starting position of the "Content-type" in this pdu + * + * @return false when error(not a Content-type) occur + * return value can be retrieved first by getValueString() and second by getValue32() + * method length of data in pdu can be retrieved by getValue32() method + */ + public boolean decodeContentType(int startIndex) { + int mediaPrefixLength; + long mediaFieldLength; + + if (decodeValueLength(startIndex) == false) { + return decodeConstrainedEncoding(startIndex); + } + mediaPrefixLength = getDecodedDataLength(); + mediaFieldLength = getValue32(); + if (decodeIntegerValue(startIndex + mediaPrefixLength) == true) { + dataLength += mediaPrefixLength; + stringValue = null; + return true; + } + if (decodeExtensionMedia(startIndex + mediaPrefixLength) == true) { + dataLength += mediaPrefixLength; + return true; + } + return false; + } + + /** + * Decode the "Content length" type for WSP pdu + * + * @param startIndex The starting position of the "Content length" in this pdu + * + * @return false when error(not a Content length) occur + * return value can be retrieved by getValue32() method + * length of data in pdu can be retrieved by getValue32() method + */ + public boolean decodeContentLength(int startIndex) { + return decodeIntegerValue(startIndex); + } + + /** + * Decode the "Content location" type for WSP pdu + * + * @param startIndex The starting position of the "Content location" in this pdu + * + * @return false when error(not a Content location) occur + * return value can be retrieved by getValueString() method + * length of data in pdu can be retrieved by getValue32() method + */ + public boolean decodeContentLocation(int startIndex) { + return decodeTextString(startIndex); + } + + /** + * Decode the "X-Wap-Application-Id" type for WSP pdu + * + * @param startIndex The starting position of the "X-Wap-Application-Id" in this pdu + * + * @return false when error(not a X-Wap-Application-Id) occur + * return value can be retrieved first by getValueString() and second by getValue32() + * method length of data in pdu can be retrieved by getValue32() method + */ + public boolean decodeXWapApplicationId(int startIndex) { + if (decodeIntegerValue(startIndex) == true) { + stringValue = null; + return true; + } + return decodeTextString(startIndex); + } + + /** + * Decode the "X-Wap-Content-URI" type for WSP pdu + * + * @param startIndex The starting position of the "X-Wap-Content-URI" in this pdu + * + * @return false when error(not a X-Wap-Content-URI) occur + * return value can be retrieved by getValueString() method + * length of data in pdu can be retrieved by getValue32() method + */ + public boolean decodeXWapContentURI(int startIndex) { + return decodeTextString(startIndex); + } + + /** + * Decode the "X-Wap-Initiator-URI" type for WSP pdu + * + * @param startIndex The starting position of the "X-Wap-Initiator-URI" in this pdu + * + * @return false when error(not a X-Wap-Initiator-URI) occur + * return value can be retrieved by getValueString() method + * length of data in pdu can be retrieved by getValue32() method + */ + public boolean decodeXWapInitiatorURI(int startIndex) { + return decodeTextString(startIndex); + } + + /** + * The data length of latest operation. + */ + public int getDecodedDataLength() { + return dataLength; + } + + /** + * The 32-bits result of latest operation. + */ + public long getValue32() { + return unsigned32bit; + } + + /** + * The String result of latest operation. + */ + public String getValueString() { + return stringValue; + } +} diff --git a/telephony/java/com/android/internal/telephony/gsm/SMSDispatcher.java b/telephony/java/com/android/internal/telephony/gsm/SMSDispatcher.java index 4176004..6eea1d4 100644 --- a/telephony/java/com/android/internal/telephony/gsm/SMSDispatcher.java +++ b/telephony/java/com/android/internal/telephony/gsm/SMSDispatcher.java @@ -37,6 +37,7 @@ import android.provider.Settings; import android.provider.Telephony.Sms.Intents; import android.telephony.gsm.SmsMessage; import android.telephony.gsm.SmsManager; +import com.android.internal.telephony.WapPushOverSms; import android.telephony.ServiceState; import android.util.Config; import com.android.internal.util.HexDump; @@ -62,37 +63,6 @@ final class SMSDispatcher extends Handler { /** Default timeout for SMS sent query */ private static final int DEFAULT_SMS_TIMOUEOUT = 6000; - private static final int WAP_PDU_TYPE_PUSH = 0x06; - - private static final int WAP_PDU_TYPE_CONFIRMED_PUSH = 0x07; - - private static final byte DRM_RIGHTS_XML = (byte)0xca; - - private static final String DRM_RIGHTS_XML_MIME_TYPE = "application/vnd.oma.drm.rights+xml"; - - private static final byte DRM_RIGHTS_WBXML = (byte)0xcb; - - private static final String DRM_RIGHTS_WBXML_MIME_TYPE = - "application/vnd.oma.drm.rights+wbxml"; - - private static final byte WAP_SI_MIME_PORT = (byte)0xae; - - private static final String WAP_SI_MIME_TYPE = "application/vnd.wap.sic"; - - private static final byte WAP_SL_MIME_PORT = (byte)0xb0; - - private static final String WAP_SL_MIME_TYPE = "application/vnd.wap.slc"; - - private static final byte WAP_CO_MIME_PORT = (byte)0xb2; - - private static final String WAP_CO_MIME_TYPE = "application/vnd.wap.coc"; - - private static final int WAP_PDU_SHORT_LENGTH_MAX = 30; - - private static final int WAP_PDU_LENGTH_QUOTE = 31; - - private static final String MMS_MIME_TYPE = "application/vnd.wap.mms-message"; - private static final String[] RAW_PROJECTION = new String[] { "pdu", "sequence", @@ -124,6 +94,8 @@ final class SMSDispatcher extends Handler { private final GSMPhone mPhone; + private final WapPushOverSms mWapPush; + private final Context mContext; private final ContentResolver mResolver; @@ -208,6 +180,7 @@ final class SMSDispatcher extends Handler { SMSDispatcher(GSMPhone phone) { mPhone = phone; + mWapPush = new WapPushOverSms(phone); mContext = phone.getContext(); mResolver = mContext.getContentResolver(); mCm = phone.mCM; @@ -538,7 +511,7 @@ final class SMSDispatcher extends Handler { if (destPort != -1) { if (destPort == SmsHeader.PORT_WAP_PUSH) { - dispatchWapPdu(sms.getUserData()); + mWapPush.dispatchWapPdu(sms.getUserData()); } // The message was sent to a port, so concoct a URI for it dispatchPortAddressedPdus(pdus, destPort); @@ -621,7 +594,7 @@ final class SMSDispatcher extends Handler { } // Handle the PUSH - dispatchWapPdu(output.toByteArray()); + mWapPush.dispatchWapPdu(output.toByteArray()); break; } @@ -663,122 +636,6 @@ final class SMSDispatcher extends Handler { intent, "android.permission.RECEIVE_SMS"); } - /** - * Dispatches inbound messages that are in the WAP PDU format. See - * wap-230-wsp-20010705-a section 8 for details on the WAP PDU format. - * - * @param pdu The WAP PDU, made up of one or more SMS PDUs - */ - private void dispatchWapPdu(byte[] pdu) { - int index = 0; - int transactionId = pdu[index++] & 0xFF; - int pduType = pdu[index++] & 0xFF; - int headerLength = 0; - - if ((pduType != WAP_PDU_TYPE_PUSH) && - (pduType != WAP_PDU_TYPE_CONFIRMED_PUSH)) { - Log.w(TAG, "Received non-PUSH WAP PDU. Type = " + pduType); - return; - } - - /** - * Parse HeaderLen(unsigned integer). - * From wap-230-wsp-20010705-a section 8.1.2 - * The maximum size of a uintvar is 32 bits. - * So it will be encoded in no more than 5 octets. - */ - int temp = 0; - do { - temp = pdu[index++]; - headerLength = headerLength << 7; - headerLength |= temp & 0x7F; - } while ((temp & 0x80) != 0); - - int headerStartIndex = index; - - /** - * Parse Content-Type. - * From wap-230-wsp-20010705-a section 8.4.2.24 - * - * Content-type-value = Constrained-media | Content-general-form - * Content-general-form = Value-length Media-type - * Media-type = (Well-known-media | Extension-Media) *(Parameter) - * Value-length = Short-length | (Length-quote Length) - * Short-length = <Any octet 0-30> (octet <= WAP_PDU_SHORT_LENGTH_MAX) - * Length-quote = <Octet 31> (WAP_PDU_LENGTH_QUOTE) - * Length = Uintvar-integer - */ - // Parse Value-length. - if ((pdu[index] & 0xff) <= WAP_PDU_SHORT_LENGTH_MAX) { - // Short-length. - index++; - } else if (pdu[index] == WAP_PDU_LENGTH_QUOTE) { - // Skip Length-quote. - index++; - // Skip Length. - // Now we assume 8bit is enough to store the content-type length. - index++; - } - String mimeType; - switch (pdu[headerStartIndex]) - { - case DRM_RIGHTS_XML: - mimeType = DRM_RIGHTS_XML_MIME_TYPE; - break; - case DRM_RIGHTS_WBXML: - mimeType = DRM_RIGHTS_WBXML_MIME_TYPE; - break; - case WAP_SI_MIME_PORT: - // application/vnd.wap.sic - mimeType = WAP_SI_MIME_TYPE; - break; - case WAP_SL_MIME_PORT: - mimeType = WAP_SL_MIME_TYPE; - break; - case WAP_CO_MIME_PORT: - mimeType = WAP_CO_MIME_TYPE; - break; - default: - int start = index; - - // Skip text-string. - // Now we assume the mimetype is Extension-Media. - while (pdu[index++] != '\0') { - ; - } - mimeType = new String(pdu, start, index-start-1); - break; - } - - // XXX Skip the remainder of the header for now - int dataIndex = headerStartIndex + headerLength; - byte[] data; - if (pdu[headerStartIndex] == WAP_CO_MIME_PORT) - { - // because SMSDispatcher can't parse push headers "Content-Location" and - // X-Wap-Content-URI, so pass the whole push to CO application. - data = pdu; - } else - { - data = new byte[pdu.length - dataIndex]; - System.arraycopy(pdu, dataIndex, data, 0, data.length); - } - - // Notify listeners about the WAP PUSH - Intent intent = new Intent(Intents.WAP_PUSH_RECEIVED_ACTION); - intent.setType(mimeType); - intent.putExtra("transactionId", transactionId); - intent.putExtra("pduType", pduType); - intent.putExtra("data", data); - - if (mimeType.equals(MMS_MIME_TYPE)) { - mPhone.getContext().sendBroadcast( - intent, "android.permission.RECEIVE_MMS"); - } else { - mPhone.getContext().sendBroadcast( - intent, "android.permission.RECEIVE_WAP_PUSH"); - } - } /** * Send a multi-part text based SMS. @@ -913,7 +770,7 @@ final class SMSDispatcher extends Handler { sendSms(tracker); } } - + /** * Send a SMS * diff --git a/telephony/java/com/android/internal/telephony/gsm/ServiceStateTracker.java b/telephony/java/com/android/internal/telephony/gsm/ServiceStateTracker.java index f37d1eb..995173b 100644 --- a/telephony/java/com/android/internal/telephony/gsm/ServiceStateTracker.java +++ b/telephony/java/com/android/internal/telephony/gsm/ServiceStateTracker.java @@ -35,6 +35,7 @@ import android.database.ContentObserver; import android.os.AsyncResult; import android.os.Handler; import android.os.Message; +import android.os.PowerManager; import android.os.Registrant; import android.os.RegistrantList; import android.os.SystemClock; @@ -141,6 +142,9 @@ final class ServiceStateTracker extends Handler // Already sent the event-log for no gprs register private boolean mReportedGprsNoReg = false; + // Wake lock used while setting time of day. + private PowerManager.WakeLock mWakeLock; + private static final String WAKELOCK_TAG = "ServiceStateTracker"; // Keep track of SPN display rules, so we only broadcast intent if something changes. private String curSpn = null; @@ -230,7 +234,11 @@ final class ServiceStateTracker extends Handler cellLoc = new GsmCellLocation(); newCellLoc = new GsmCellLocation(); - cm.registerForAvailable(this, EVENT_RADIO_AVAILABLE, null); + PowerManager powerManager = + (PowerManager)phone.getContext().getSystemService(Context.POWER_SERVICE); + mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG); + + cm.registerForAvailable(this, EVENT_RADIO_AVAILABLE, null); cm.registerForRadioStateChanged(this, EVENT_RADIO_STATE_CHANGED, null); cm.registerForNetworkStateChanged(this, EVENT_NETWORK_STATE_CHANGED, null); @@ -1348,42 +1356,49 @@ final class ServiceStateTracker extends Handler return; } - if (getAutoTime()) { - long millisSinceNitzReceived - = SystemClock.elapsedRealtime() - nitzReceiveTime; + try { + mWakeLock.acquire(); - if (millisSinceNitzReceived < 0) { - // Sanity check: something is wrong - Log.i(LOG_TAG, "NITZ: not setting time, clock has rolled " - + "backwards since NITZ time was received, " - + nitz); - return; - } + if (getAutoTime()) { + long millisSinceNitzReceived + = SystemClock.elapsedRealtime() - nitzReceiveTime; + + if (millisSinceNitzReceived < 0) { + // Sanity check: something is wrong + Log.i(LOG_TAG, "NITZ: not setting time, clock has rolled " + + "backwards since NITZ time was received, " + + nitz); + return; + } - if (millisSinceNitzReceived > Integer.MAX_VALUE) { - // If the time is this far off, something is wrong > 24 days! - Log.i(LOG_TAG, "NITZ: not setting time, processing has taken " - + (millisSinceNitzReceived / (1000 * 60 * 60 * 24)) - + " days"); - return; - } + if (millisSinceNitzReceived > Integer.MAX_VALUE) { + // If the time is this far off, something is wrong > 24 days! + Log.i(LOG_TAG, "NITZ: not setting time, processing has taken " + + (millisSinceNitzReceived / (1000 * 60 * 60 * 24)) + + " days"); + return; + } - // Note: with range checks above, cast to int is safe - c.add(Calendar.MILLISECOND, (int)millisSinceNitzReceived); + // Note: with range checks above, cast to int is safe + c.add(Calendar.MILLISECOND, (int)millisSinceNitzReceived); - Log.i(LOG_TAG, "NITZ: Setting time of day to " + c.getTime() - + " NITZ receive delay(ms): " + millisSinceNitzReceived - + " gained(ms): " - + (c.getTimeInMillis() - System.currentTimeMillis()) - + " from " + nitz); + Log.i(LOG_TAG, "NITZ: Setting time of day to " + c.getTime() + + " NITZ receive delay(ms): " + millisSinceNitzReceived + + " gained(ms): " + + (c.getTimeInMillis() - System.currentTimeMillis()) + + " from " + nitz); - setAndBroadcastNetworkSetTime(c.getTimeInMillis()); - } - SystemProperties.set("gsm.nitz.time", String.valueOf(c.getTimeInMillis())); - saveNitzTime(c.getTimeInMillis()); - if (Config.LOGV) { - long end = SystemClock.elapsedRealtime(); - Log.v(LOG_TAG, "NITZ: end=" + end + " dur=" + (end - start)); + SystemClock.setCurrentTimeMillis(c.getTimeInMillis()); + Log.i(LOG_TAG, "NITZ: after Setting time of day"); + } + SystemProperties.set("gsm.nitz.time", String.valueOf(c.getTimeInMillis())); + saveNitzTime(c.getTimeInMillis()); + if (Config.LOGV) { + long end = SystemClock.elapsedRealtime(); + Log.v(LOG_TAG, "NITZ: end=" + end + " dur=" + (end - start)); + } + } finally { + mWakeLock.release(); } } catch (RuntimeException ex) { Log.e(LOG_TAG, "NITZ: Parsing NITZ time " + nitz, ex); diff --git a/tests/AndroidTests/AndroidManifest.xml b/tests/AndroidTests/AndroidManifest.xml index 36f7b9b..843d844 100644 --- a/tests/AndroidTests/AndroidManifest.xml +++ b/tests/AndroidTests/AndroidManifest.xml @@ -44,8 +44,6 @@ <uses-permission android:name="android.permission.CLEAR_APP_CACHE" /> <uses-permission android:name="android.permission.CLEAR_APP_USER_DATA" /> <uses-permission android:name="android.permission.GET_PACKAGE_SIZE" /> - <uses-permission android:name="android.permission.BLUETOOTH" /> - <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.WRITE_GSERVICES" /> diff --git a/tests/AndroidTests/src/com/android/unit_tests/BluetoothTest.java b/tests/AndroidTests/src/com/android/unit_tests/BluetoothTest.java deleted file mode 100644 index 0a60319..0000000 --- a/tests/AndroidTests/src/com/android/unit_tests/BluetoothTest.java +++ /dev/null @@ -1,484 +0,0 @@ -/* - * Copyright (C) 2007 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.unit_tests; - -import android.bluetooth.BluetoothA2dp; -import android.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothIntent; -import android.bluetooth.BluetoothClass; -import android.bluetooth.IBluetoothDeviceCallback; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.os.SystemProperties; -import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.LargeTest; -import android.test.suitebuilder.annotation.MediumTest; -import android.test.suitebuilder.annotation.SmallTest; -import android.util.Log; - -import junit.framework.Assert; - -import java.util.List; -import java.util.HashSet; - -public class BluetoothTest extends AndroidTestCase { - private static final String TAG = "BluetoothTest"; - - @MediumTest - public void testBluetoothSmokeTest() throws Exception { - - BluetoothDevice btDevice = - (BluetoothDevice)getContext().getSystemService(Context.BLUETOOTH_SERVICE); - - // TODO: Use a more reliable check to see if this product should - // support Bluetooth - see bug 988521 - boolean shouldSupportBluetooth = SystemProperties.get("ro.kernel.qemu").equals("0"); - - assertFalse(shouldSupportBluetooth && btDevice == null); - if (!shouldSupportBluetooth) { - Log.i(TAG, "Skipping test - this device does not have bluetooth."); - return; - } - - boolean bluetoothWasEnabled = btDevice.isEnabled(); - - if (bluetoothWasEnabled) { - Log.i(TAG, "Bluetooth already enabled"); - } else { - Log.i(TAG, "Enabling Bluetooth..."); - btDevice.enable(); - Log.i(TAG, "Bluetooth enabled"); - } - Assert.assertTrue(btDevice.isEnabled()); - - String myAddress = btDevice.getAddress(); - Assert.assertTrue(myAddress != null); - Log.i(TAG, "My Bluetooth Address is " + myAddress); - Assert.assertFalse(myAddress.equals("00:00:00:00:00:00")); - - if (!bluetoothWasEnabled) { - Log.i(TAG, "Disabling Bluetooth..."); - btDevice.disable(); - Log.i(TAG, "Bluetooth disabled"); - } - } - - private boolean listenA2dp = false; - private void listenA2dp() { - if (!listenA2dp) { - listenA2dp = true; - getContext().registerReceiver(new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - int state = intent.getIntExtra(BluetoothA2dp.SINK_STATE, -1); - int oldState = intent.getIntExtra(BluetoothA2dp.SINK_PREVIOUS_STATE, -1); - Log.e(TAG, "A2DP INTENT: state = " + state + " oldState = " + oldState); - } - }, new IntentFilter(BluetoothIntent.REMOTE_DEVICE_FOUND_ACTION)); - } - } - - @MediumTest - public void testA2dpSmokeTest() throws Exception { - listenA2dp(); - - BluetoothA2dp a2dp = new BluetoothA2dp(getContext()); - - List<String> sinks = a2dp.listConnectedSinks(); - Log.e(TAG, "listConnectedSinks()..."); - for (String sink : sinks) { - Log.e(TAG, sink + " state = " + a2dp.getSinkState(sink)); - } - } - - @MediumTest - public void testA2dpConnect() throws Exception { - listenA2dp(); - String address = SystemProperties.get("debug.a2dp.address", "<none>"); - BluetoothA2dp a2dp = new BluetoothA2dp(getContext()); - int result = a2dp.connectSink(address); - Log.e(TAG, "connectSink(" + address + ") = " + result); - } - - @MediumTest - public void testA2dpDisconnect() throws Exception { - listenA2dp(); - String address = SystemProperties.get("debug.a2dp.address", "<none>"); - BluetoothA2dp a2dp = new BluetoothA2dp(getContext()); - int result = a2dp.disconnectSink(address); - Log.e(TAG, "disconnectSink(" + address + ") = " + result); - } - - @MediumTest - public void testBluetoothEnabled() throws Exception { - BluetoothDevice device = - (BluetoothDevice)getContext().getSystemService(Context.BLUETOOTH_SERVICE); - if (device == null) { - Log.i(TAG, "Device not Bluetooth capable, skipping test"); - return; - } - - if (device.isEnabled()) { - Log.i(TAG, "isEnabled() = yes"); - } else { - Log.i(TAG, "isEnabled() = no"); - } - } - - @MediumTest - public void testEnableBluetooth() throws Exception { - BluetoothDevice device = - (BluetoothDevice)getContext().getSystemService(Context.BLUETOOTH_SERVICE); - if (device == null) { - Log.i(TAG, "Device not Bluetooth capable, skipping test"); - return; - } - device.enable(); - } - - @MediumTest - public void testEnableBluetoothWithCallback() throws Exception { - BluetoothDevice device = - (BluetoothDevice)getContext().getSystemService(Context.BLUETOOTH_SERVICE); - if (device == null) { - Log.i(TAG, "Device not Bluetooth capable, skipping test"); - return; - } - if (!device.enable(mCallback)) { - Log.e(TAG, "enable() failed"); - } - } - - @MediumTest - public void testDisableBluetooth() throws Exception { - BluetoothDevice device = - (BluetoothDevice)getContext().getSystemService(Context.BLUETOOTH_SERVICE); - if (device == null) { - Log.i(TAG, "Device not Bluetooth capable, skipping test"); - return; - } - device.disable(); - } - - @LargeTest - public void testDiscovery() throws Exception { - BluetoothDevice device = - (BluetoothDevice)getContext().getSystemService(Context.BLUETOOTH_SERVICE); - if (device == null) { - Log.i(TAG, "Device not Bluetooth capable, skipping test"); - return; - } - if (device.isEnabled()) { - getContext().registerReceiver((BroadcastReceiver)new TestDiscoveryReceiver(), - new IntentFilter(BluetoothIntent.REMOTE_DEVICE_FOUND_ACTION)); - Log.i(TAG, "Starting discovery..."); - String result = device.startDiscovery() ? "true" : "false"; - Log.i(TAG, "startDiscovery() = " + result); - } - } - - @LargeTest - public void testMultipleDiscovery() throws Exception { - BluetoothDevice device = - (BluetoothDevice)getContext().getSystemService(Context.BLUETOOTH_SERVICE); - if (device == null) { - Log.i(TAG, "Device not Bluetooth capable, skipping test"); - return; - } - if (device.isEnabled()) { - getContext().registerReceiver((BroadcastReceiver)new TestDiscoveryReceiver(), - new IntentFilter(BluetoothIntent.REMOTE_DEVICE_FOUND_ACTION)); - String result; - Log.i(TAG, "Starting multiple discovery..."); - for (int i = 0; i < 5; i++) { - result = device.startDiscovery() ? "true" : "false"; - Log.i(TAG, "startDiscovery() = " + result); - } - } - } - private class TestDiscoveryReceiver extends BroadcastReceiver { - @Override public void onReceive(Context context, Intent intent) { - String address = intent.getStringExtra(BluetoothIntent.ADDRESS); - int deviceClass = intent.getIntExtra(BluetoothIntent.CLASS, -1); - short rssi = intent.getShortExtra(BluetoothIntent.RSSI, (short)-1); - Log.i(TAG, "Discovered Device: " + address + " " + deviceClass + " " + rssi); - } - } - - private IBluetoothDeviceCallback mCallback = new IBluetoothDeviceCallback.Stub() { - public void onEnableResult(int res) { - String result = "unknown"; - switch (res) { - case BluetoothDevice.RESULT_SUCCESS: - result = "success"; - break; - case BluetoothDevice.RESULT_FAILURE: - result = "FAILURE"; - break; - } - Log.i(TAG, "onEnableResult(" + result + ")"); - } - public void onGetRemoteServiceChannelResult(String device, int channel) {} - }; - - @SmallTest - public void testCreateBond() throws Exception { - BluetoothDevice device = - (BluetoothDevice)getContext().getSystemService(Context.BLUETOOTH_SERVICE); - if (device == null) { - Log.i(TAG, "Device not Bluetooth capable, skipping test"); - return; - } - if (!device.createBond("01:23:45:67:89:AB")) { - Log.e(TAG, "createBonding() failed"); - } - } - - @SmallTest - public void testIsPeriodicDiscovery() throws Exception { - BluetoothDevice device = - (BluetoothDevice)getContext().getSystemService(Context.BLUETOOTH_SERVICE); - if (device == null) { - Log.i(TAG, "Device not Bluetooth capable, skipping test"); - return; - } - boolean ret = device.isPeriodicDiscovery(); - if (ret) { - Log.i(TAG, "isPeriodicDiscovery() = TRUE"); - } else { - Log.i(TAG, "isPeriodicDiscovery() = FALSE"); - } - } - - @LargeTest - public void testListBondings() throws Exception { - BluetoothDevice device = - (BluetoothDevice)getContext().getSystemService(Context.BLUETOOTH_SERVICE); - if (device == null) { - Log.i(TAG, "Device not Bluetooth capable, skipping test"); - return; - } - String[] addresses = device.listBonds(); - if (addresses == null) { - Log.i(TAG, "Bluetooth disabled"); - return; - } - for (String address : addresses) { - String name = device.getRemoteName(address); - Log.i(TAG, "BONDING: " + address + " (" + name + ")"); - } - } - - @LargeTest - public void testListAclConnections() throws Exception { - BluetoothDevice device = - (BluetoothDevice)getContext().getSystemService(Context.BLUETOOTH_SERVICE); - if (device == null) { - Log.i(TAG, "Device not Bluetooth capable, skipping test"); - return; - } - String[] addresses = device.listAclConnections(); - if (addresses == null) { - Log.i(TAG, "Bluetooth disabled"); - return; - } - for (String address : addresses) { - String name = device.getRemoteName(address); - Log.i(TAG, "CONNECTION: " + address + " (" + name + ")"); - } - } - - @LargeTest - public void testListRemoteDevices() throws Exception { - BluetoothDevice device = - (BluetoothDevice)getContext().getSystemService(Context.BLUETOOTH_SERVICE); - if (device == null) { - Log.i(TAG, "Device not Bluetooth capable, skipping test"); - return; - } - String[] addresses = device.listRemoteDevices(); - if (addresses == null) { - Log.i(TAG, "Bluetooth disabled"); - return; - } - for (String address : addresses) { - String name = device.getRemoteName(address); - Log.i(TAG, "KNOWN DEVICE: " + address + " (" + name + ")"); - } - } - - @MediumTest - public void testSetupBTIntentRecv() throws Exception { - BluetoothDevice device = - (BluetoothDevice)getContext().getSystemService(Context.BLUETOOTH_SERVICE); - if (device == null) { - Log.i(TAG, "Device not Bluetooth capable, skipping test"); - return; - } - if (device.isEnabled()) { - IntentFilter filter = new IntentFilter(BluetoothIntent.ENABLED_ACTION); - filter.addAction(BluetoothIntent.ENABLED_ACTION); - filter.addAction(BluetoothIntent.DISABLED_ACTION); - filter.addAction(BluetoothIntent.NAME_CHANGED_ACTION); - filter.addAction(BluetoothIntent.DISCOVERY_STARTED_ACTION); - filter.addAction(BluetoothIntent.DISCOVERY_COMPLETED_ACTION); - filter.addAction(BluetoothIntent.PAIRING_REQUEST_ACTION); - filter.addAction(BluetoothIntent.PAIRING_CANCEL_ACTION); - filter.addAction(BluetoothIntent.REMOTE_DEVICE_FOUND_ACTION); - filter.addAction(BluetoothIntent.REMOTE_DEVICE_DISAPPEARED_ACTION); - filter.addAction(BluetoothIntent.REMOTE_DEVICE_CLASS_UPDATED_ACTION); - filter.addAction(BluetoothIntent.REMOTE_DEVICE_CONNECTED_ACTION); - filter.addAction(BluetoothIntent.REMOTE_DEVICE_DISCONNECT_REQUESTED_ACTION); - filter.addAction(BluetoothIntent.REMOTE_DEVICE_DISCONNECTED_ACTION); - filter.addAction(BluetoothIntent.REMOTE_NAME_UPDATED_ACTION); - filter.addAction(BluetoothIntent.REMOTE_NAME_FAILED_ACTION); - filter.addAction(BluetoothIntent.BOND_STATE_CHANGED_ACTION); - filter.addAction(BluetoothIntent.HEADSET_STATE_CHANGED_ACTION); - getContext().registerReceiver( - (BroadcastReceiver)new BluetoothIntentReceiver(), filter); - Log.i(TAG, "Listening for BLUETOOTH INTENTS...."); - } else { - Log.e(TAG, "BT not enabled"); - } - } - - - private class BluetoothIntentReceiver extends BroadcastReceiver { - @Override public void onReceive(Context context, Intent intent) { - String msg = ""; - - String address = intent.getStringExtra(BluetoothIntent.ADDRESS); - if (address != null) { - msg += " address=" + address; - } - - int deviceClass = intent.getIntExtra(BluetoothIntent.CLASS, BluetoothClass.ERROR); - if (deviceClass != BluetoothClass.ERROR) { - msg += " class=" + deviceClass; - } - - int rssi = intent.getIntExtra(BluetoothIntent.RSSI, -1); - if (rssi != -1) { - msg += " rssi=" + rssi; - } - - String name = intent.getStringExtra(BluetoothIntent.NAME); - if (name != null) { - msg += " name=" + name; - } - - int state = intent.getIntExtra(BluetoothIntent.HEADSET_STATE, -10); - if (state != -10) { - msg += " headset state=" + state; - } - Log.i(TAG, "BLUETOOTH INTENT: " + intent.getAction() + msg); - } - } - - - private static final int[] ALL_SERVICE_CLASSES = new int[] { - BluetoothClass.Service.LIMITED_DISCOVERABILITY, - BluetoothClass.Service.POSITIONING, - BluetoothClass.Service.NETWORKING, - BluetoothClass.Service.RENDER, - BluetoothClass.Service.CAPTURE, - BluetoothClass.Service.OBJECT_TRANSFER, - BluetoothClass.Service.AUDIO, - BluetoothClass.Service.TELEPHONY, - BluetoothClass.Service.INFORMATION - }; - private void assertOnlyTheseServiceClassesAreSupported(int deviceClass, HashSet<Integer> serviceClasses) { - for (int serviceClassType : ALL_SERVICE_CLASSES) { - Assert.assertEquals(serviceClasses.contains(new Integer(serviceClassType)), - BluetoothClass.Service.hasService(deviceClass, serviceClassType)); - } - } - - @SmallTest - public void testDeviceClass() throws Exception { - // This test does not require bluetooth hardware - int deviceClass; - HashSet<Integer> serviceClasses; - - deviceClass = BluetoothClass.ERROR; // bogus class - serviceClasses = new HashSet<Integer>(); - assertOnlyTheseServiceClassesAreSupported(deviceClass, serviceClasses); - Assert.assertEquals(BluetoothClass.ERROR, BluetoothClass.Device.Major.getDeviceMajor(deviceClass)); - Assert.assertEquals(BluetoothClass.ERROR, BluetoothClass.Device.getDevice(deviceClass)); - - deviceClass = 0x10210C; // mac book pro - serviceClasses = new HashSet<Integer>(); - serviceClasses.add(BluetoothClass.Service.OBJECT_TRANSFER); - serviceClasses.add(BluetoothClass.Service.LIMITED_DISCOVERABILITY); - assertOnlyTheseServiceClassesAreSupported(deviceClass, serviceClasses); - Assert.assertEquals(BluetoothClass.Device.Major.COMPUTER, - BluetoothClass.Device.Major.getDeviceMajor(deviceClass)); - Assert.assertEquals(0x10C, BluetoothClass.Device.getDevice(deviceClass)); - - // mac book pro with some unused bits set. Expecting the same results - deviceClass = 0xFF10210F; - serviceClasses = new HashSet<Integer>(); - serviceClasses.add(BluetoothClass.Service.OBJECT_TRANSFER); - serviceClasses.add(BluetoothClass.Service.LIMITED_DISCOVERABILITY); - assertOnlyTheseServiceClassesAreSupported(deviceClass, serviceClasses); - Assert.assertEquals(BluetoothClass.Device.Major.COMPUTER, - BluetoothClass.Device.Major.getDeviceMajor(deviceClass)); - Assert.assertEquals(0x10C, BluetoothClass.Device.getDevice(deviceClass)); - - deviceClass = 0x3E0100; // droid.corp.google.com - serviceClasses = new HashSet<Integer>(); - serviceClasses.add(BluetoothClass.Service.AUDIO); - serviceClasses.add(BluetoothClass.Service.OBJECT_TRANSFER); - serviceClasses.add(BluetoothClass.Service.CAPTURE); - serviceClasses.add(BluetoothClass.Service.RENDER); - serviceClasses.add(BluetoothClass.Service.NETWORKING); - assertOnlyTheseServiceClassesAreSupported(deviceClass, serviceClasses); - Assert.assertEquals(BluetoothClass.Device.Major.COMPUTER, - BluetoothClass.Device.Major.getDeviceMajor(deviceClass)); - Assert.assertEquals(0x100, BluetoothClass.Device.getDevice(deviceClass)); - - deviceClass = 0x40020C; // Android - serviceClasses = new HashSet<Integer>(); - serviceClasses.add(BluetoothClass.Service.TELEPHONY); - assertOnlyTheseServiceClassesAreSupported(deviceClass, serviceClasses); - Assert.assertEquals(BluetoothClass.Device.Major.PHONE, BluetoothClass.Device.Major.getDeviceMajor(deviceClass)); - Assert.assertEquals(0x20C, BluetoothClass.Device.getDevice(deviceClass)); - - // Motorola T305 & Jabra BT125 & Jabra BT250V - // This seems to be a very common headset & handsfree device code - deviceClass = 0x200404; - serviceClasses = new HashSet<Integer>(); - serviceClasses.add(BluetoothClass.Service.AUDIO); - assertOnlyTheseServiceClassesAreSupported(deviceClass, serviceClasses); - Assert.assertEquals(BluetoothClass.Device.Major.AUDIO_VIDEO, - BluetoothClass.Device.Major.getDeviceMajor(deviceClass)); - Assert.assertEquals(BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET, - BluetoothClass.Device.getDevice(deviceClass)); - - // Audi UHV 0128 - deviceClass = 0x200408; - serviceClasses = new HashSet<Integer>(); - serviceClasses.add(BluetoothClass.Service.AUDIO); - assertOnlyTheseServiceClassesAreSupported(deviceClass, serviceClasses); - Assert.assertEquals(BluetoothClass.Device.Major.AUDIO_VIDEO, - BluetoothClass.Device.Major.getDeviceMajor(deviceClass)); - Assert.assertEquals(BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE, - BluetoothClass.Device.getDevice(deviceClass)); - } -} diff --git a/tools/aapt/Package.cpp b/tools/aapt/Package.cpp index 5d9e140..eb7d6f5 100644 --- a/tools/aapt/Package.cpp +++ b/tools/aapt/Package.cpp @@ -5,6 +5,7 @@ // #include "Main.h" #include "AaptAssets.h" +#include "ResourceTable.h" #include <utils.h> #include <utils/ZipFile.h> @@ -190,11 +191,20 @@ bail: ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<AaptAssets>& assets) { + ResourceFilter filter; + status_t status = filter.parse(bundle->getConfigurations()); + if (status != NO_ERROR) { + return -1; + } + ssize_t count = 0; const size_t N = assets->getGroupEntries().size(); for (size_t i=0; i<N; i++) { const AaptGroupEntry& ge = assets->getGroupEntries()[i]; + if (!filter.match(ge.toParams())) { + continue; + } ssize_t res = processAssets(bundle, zip, assets, ge); if (res < 0) { return res; diff --git a/wifi/java/android/net/wifi/WifiMonitor.java b/wifi/java/android/net/wifi/WifiMonitor.java index 1799560..fc750e2 100644 --- a/wifi/java/android/net/wifi/WifiMonitor.java +++ b/wifi/java/android/net/wifi/WifiMonitor.java @@ -28,7 +28,7 @@ import java.util.regex.Matcher; * Listens for events from the wpa_supplicant server, and passes them on * to the {@link WifiStateTracker} for handling. Runs in its own thread. * - * {@hide} + * @hide */ public class WifiMonitor { @@ -119,14 +119,8 @@ public class WifiMonitor { private final WifiStateTracker mWifiStateTracker; - private boolean supplicantConnected; - - private boolean oneShot; - public WifiMonitor(WifiStateTracker tracker) { mWifiStateTracker = tracker; - supplicantConnected = false; - oneShot = true; } public void startMonitoring() { @@ -144,10 +138,17 @@ public class WifiMonitor { public void run() { + if (connectToSupplicant()) { + // Send a message indicating that it is now possible to send commands + // to the supplicant + mWifiStateTracker.notifySupplicantConnection(); + } else { + mWifiStateTracker.notifySupplicantLost(); + return; + } + //noinspection InfiniteLoopStatement for (;;) { - ensureSupplicantConnection(); - String eventStr = WifiNative.waitForEvent(); if (eventStr == null) { @@ -214,42 +215,32 @@ public class WifiMonitor { handleSupplicantStateChange(eventData); } else if (event == DRIVER_STATE) { handleDriverEvent(eventData); + } else if (event == TERMINATING) { + mWifiStateTracker.notifySupplicantLost(); + // If supplicant is gone, exit the thread + break; } else { handleEvent(event, eventData); - // If supplicant is gone, exit the thread - if (event == TERMINATING) { - break; - } } } } - private void ensureSupplicantConnection() { - while (!supplicantConnected) { - boolean connected; + private boolean connectToSupplicant() { + int connectTries = 0; + + while (true) { synchronized (mWifiStateTracker) { - connected = WifiNative.connectToSupplicant(); - } - if (!connected) { - /* - * If we fail to connect on the very first attempt, send a message - * indicating a lost connection to the supplicant, so that the - * receiver can initialize to the proper state. - */ - if (oneShot) { - oneShot = false; - mWifiStateTracker.notifySupplicantLost(); + if (WifiNative.connectToSupplicant()) { + return true; } + } + if (connectTries++ < 3) { nap(5); } else { - supplicantConnected = true; - oneShot = false; - // Send a message indicating that it is now possible to send commands - // to the supplicant - mWifiStateTracker.notifySupplicantConnection(); - + break; } } + return false; } private void handlePasswordKeyMayBeIncorrect() { @@ -287,11 +278,6 @@ public class WifiMonitor { mWifiStateTracker.notifyScanResultsAvailable(); break; - case TERMINATING: - supplicantConnected = false; - mWifiStateTracker.notifySupplicantLost(); - break; - case UNKNOWN: break; } @@ -370,7 +356,7 @@ public class WifiMonitor { private static void nap(int secs) { try { Thread.sleep(secs * 1000); - } catch (InterruptedException e) { + } catch (InterruptedException ignore) { } } } |